Skip to content

CNOT with interacting qubits

Digital-analog quantum computing focuses on using single qubit digital gates combined with more complex and device-dependent analog interactions to represent quantum programs. This paradigm has been shown to be universal for quantum computation1. However, while this approach may have advantages when adapting quantum programs to real devices, known quantum algorithms are very often expressed in a fully digital paradigm. As such, it is also important to have concrete ways to transform from one paradigm to another.

This tutorial will exemplify the DAQC transformation starting with the representation of a simple digital CNOT using the universality of the Ising Hamiltonian2.

CNOT with CPHASE

Let's look at a single example of how the digital-analog transformation can be used to perform a CNOT on two qubits inside a register of globally interacting qubits.

First, note that the CNOT can be decomposed with two Hadamard and a CPHASE gate with \(\phi=\pi\):

import torch
from qadence import chain, sample, product_state

from qadence.draw import display
from qadence import X, I, Z, H, N, CPHASE, CNOT, HamEvo, PI

n_qubits = 2

# CNOT gate
cnot_gate = CNOT(0, 1)

# CNOT decomposed
phi = PI
cnot_decomp = chain(H(1), CPHASE(0, 1, phi), H(1))

init_state = product_state("10")
sample from CNOT gate and 100 shots = [OrderedCounter({'11': 100})]
sample from decomposed CNOT gate and 100 shots = [OrderedCounter({'11': 100})]

The CPHASE matrix is diagonal, and can be implemented by exponentiating an Ising-like Hamiltonian, or generator,

\[\text{CPHASE}(i,j,\phi)=\text{exp}\left(-i\phi \mathcal{H}_\text{CP}(i, j)\right)\]
\[\begin{aligned} \mathcal{H}_\text{CP}&=-\frac{1}{4}(I_i-Z_i)(I_j-Z_j)\\ &=-N_iN_j \end{aligned}\]

where the number operator \(N_i = \frac{1}{2}(I_i-Z_i)=\hat{n}_i\) is used, leading to an Ising-like interaction \(\hat{n}_i\hat{n}_j\) realisable in neutral-atom systems. Let's rebuild the CNOT using this evolution.

from qadence import kron, block_to_tensor

# Hamiltonian for the CPHASE gate
h_cphase = (-1.0) * kron(N(0), N(1))

# Exponentiating and time-evolving the Hamiltonian until t=phi.
cphase_evo = HamEvo(h_cphase, phi)

# Check that we have the CPHASE gate:
cphase_matrix = block_to_tensor(CPHASE(0, 1, phi))
cphase_evo_matrix = block_to_tensor(cphase_evo)
cphase_matrix == cphase_evo_matrix: True

Now that the CPHASE generator is checked, it can be applied to the CNOT:

# CNOT with Hamiltonian Evolution
cnot_evo = chain(
    H(1),
    cphase_evo,
    H(1)
)

# Initialize state to check CNOTs sample outcomes.
init_state = product_state("10")
sample cnot_gate = [OrderedCounter({'11': 100})]
sample cnot_evo = [OrderedCounter({'11': 100})]

Thus, a CNOT gate can be created by combining a few single-qubit gates together with a two-qubit Ising interaction between the control and the target qubit which is the essence of the Ising transform proposed in the seminal DAQC paper2 for \(ZZ\) interactions. In Qadence, both \(ZZ\) and \(NN\) interactions are supported.

CNOT in an interacting system of three qubits

Consider a simple experimental setup with \(n=3\) interacting qubits laid out in a triangular grid. For the sake of simplicity, all qubits interact with each other with an \(NN\)-Ising interaction of constant strength \(g_\text{int}\). The Hamiltonian for the system can be written by summing interaction terms over all pairs:

\[\mathcal{H}_\text{sys}=\sum_{i=0}^{n}\sum_{j=0}^{i-1}g_\text{int}N_iN_j,\]

which in this case leads to only three interaction terms,

\[\mathcal{H}_\text{sys}=g_\text{int}(N_0N_1+N_1N_2+N_0N_2)\]

This generator can be easily built in Qadence:

from qadence import add, kron
n_qubits = 3

# Interaction strength.
g_int = 1.0

# Build a list of interactions.
interaction_list = []
for i in range(n_qubits):
    for j in range(i):
        interaction_list.append(g_int * kron(N(i), N(j)))

h_sys = add(*interaction_list)
h_sys = AddBlock(0,1,2)
├── [mul: 1.000] 
   └── KronBlock(0,1)
       ├── N(1)
       └── N(0)
├── [mul: 1.000] 
   └── KronBlock(0,2)
       ├── N(2)
       └── N(0)
└── [mul: 1.000] 
    └── KronBlock(1,2)
        ├── N(2)
        └── N(1)

Now let's consider that the experimental system is fixed, and qubits can not be isolated one from another. The options are:

  • Turn on or off the global system Hamiltonian.
  • Perform local single-qubit rotations.

To perform a fully digital CNOT(0,1), the interacting control on qubit 0 and target on qubit 1 must be isolated from the third one to implement the gate directly. While this can be achieved for a three-qubit system, it becomes experimentally untractable when scaling the qubit count.

However, this is not the case within the digital-analog paradigm. In fact, the two qubit Ising interaction required for the CNOT can be represented with a combination of the global system Hamiltonian and a specific set of single-qubit rotations. Full details about this transformation are to be found in the DAQC paper2 but a more succint yet in-depth description takes place in the next section. It is conveniently available in Qadence by calling the daqc_transform function.

In the most general sense, the daqc_transform function will return a circuit that represents the evolution of a target Hamiltonian \(\mathcal{H}_\text{target}\) (here the unitary of the gate) until a specified time \(t_f\) by using only the evolution of a build Hamiltonian \(\mathcal{H}_\text{build}\) (here \(\mathcal{H}_\text{sys}\)) together with local \(X\)-gates. In Qadence, daqc_transform is applicable for \(\mathcal{H}_\text{target}\) and \(\mathcal{H}_\text{build}\) composed only of \(ZZ\)- or \(NN\)-interactions. These generators are parsed by the daqc_transform function and the appropriate type is automatically determined together with the appropriate single-qubit detunings and global phases.

Let's apply it for the CNOT implementation:

from qadence import daqc_transform, Strategy

# Settings for the target CNOT operation
i = 0  # Control qubit
j = 1  # Target qubit
k = 2  # The extra qubit

# Define the target CNOT operation
# by composing with identity on the extra qubit.
cnot_target = kron(CNOT(i, j), I(k))

# The two-qubit NN-Ising interaction term for the CPHASE
h_int = (-1.0) * kron(N(i), N(j))

# Transforming the two-qubit Ising interaction using only our system Hamiltonian
transformed_ising = daqc_transform(
    n_qubits=3,        # Total number of qubits in the transformation
    gen_target=h_int,  # The target Ising generator
    t_f=PI,            # The target evolution time
    gen_build=h_sys,   # The building block Ising generator to be used
    strategy=Strategy.SDAQC,   # Currently only sDAQC is implemented
    ignore_global_phases=False  # Global phases from mapping between Z and N
)

# display(transformed_ising)
%3 cluster_f25184f503de484ab7a6853c4660d1b4 cluster_1359ba9bc13b44c4afc8adf48837d06e cluster_326c2b571c1a4921960dcfa5e83ce6ee cluster_52b2780e2c994ccbb2499912fae49ef6 cluster_9a505eb789df4f0494c484233ebbc638 cluster_f9df323b0ca347edbc72207b16f58313 cluster_ac2871fe02a347258f1160e9380b61bd d5844530a1d04c1f9b3b58ef078c8d73 0 153e39b336d64077a31874d2adbdd6b8 HamEvo d5844530a1d04c1f9b3b58ef078c8d73--153e39b336d64077a31874d2adbdd6b8 ad49e3a3e85a42e4af97fda61c5058ca 1 4cab5b932f6b44f19080ca1238558794 HamEvo 153e39b336d64077a31874d2adbdd6b8--4cab5b932f6b44f19080ca1238558794 b923894e76644275901d963c9c5f90c5 HamEvo 4cab5b932f6b44f19080ca1238558794--b923894e76644275901d963c9c5f90c5 e01056836ff840d8862f2805f1fac961 X b923894e76644275901d963c9c5f90c5--e01056836ff840d8862f2805f1fac961 299b5884dff5410d93f180f27ee02e25 HamEvo e01056836ff840d8862f2805f1fac961--299b5884dff5410d93f180f27ee02e25 8be527909f3245948cf0bbd12de45ae4 HamEvo 299b5884dff5410d93f180f27ee02e25--8be527909f3245948cf0bbd12de45ae4 fe6397ba6c9a44288c56f7d1d5a0859b X 8be527909f3245948cf0bbd12de45ae4--fe6397ba6c9a44288c56f7d1d5a0859b 69966674c93747debafe08b56fc24f98 fe6397ba6c9a44288c56f7d1d5a0859b--69966674c93747debafe08b56fc24f98 05b1d76ea61241988c34ab3b9b62b12a HamEvo 69966674c93747debafe08b56fc24f98--05b1d76ea61241988c34ab3b9b62b12a 9aae7a9d242a4e45be4800b0f35070b6 HamEvo 05b1d76ea61241988c34ab3b9b62b12a--9aae7a9d242a4e45be4800b0f35070b6 69f344a7d29740f7bcc4c0bf05a77abb 9aae7a9d242a4e45be4800b0f35070b6--69f344a7d29740f7bcc4c0bf05a77abb 76f82cb2f5c1430e97aaa683427d6d08 69f344a7d29740f7bcc4c0bf05a77abb--76f82cb2f5c1430e97aaa683427d6d08 3fbee8cd832247179216f1f52187ba7e 150610eafec542789ce6dac393239fa7 t = -3.14 ad49e3a3e85a42e4af97fda61c5058ca--150610eafec542789ce6dac393239fa7 966b185577df49d4bf4e0807de681db8 2 c3f0369b76384f168df13ecf3562f84e t = 3.142 150610eafec542789ce6dac393239fa7--c3f0369b76384f168df13ecf3562f84e d44834804668415c9dc078768a5d0faa t = -3.14 c3f0369b76384f168df13ecf3562f84e--d44834804668415c9dc078768a5d0faa d95aba65522747f0ad35ee5e900df602 d44834804668415c9dc078768a5d0faa--d95aba65522747f0ad35ee5e900df602 df1439a7f4a94177a5608f7465778c56 t = 1.571 d95aba65522747f0ad35ee5e900df602--df1439a7f4a94177a5608f7465778c56 b8a851109479419eb424fb0ae751ce4a t = 1.571 df1439a7f4a94177a5608f7465778c56--b8a851109479419eb424fb0ae751ce4a 794b6b797af249e7b6b78ea6d16217f7 b8a851109479419eb424fb0ae751ce4a--794b6b797af249e7b6b78ea6d16217f7 90d7ca89110843b2948a1edf55806594 X 794b6b797af249e7b6b78ea6d16217f7--90d7ca89110843b2948a1edf55806594 23788d468906483eb554d9abcaa592f1 t = 1.571 90d7ca89110843b2948a1edf55806594--23788d468906483eb554d9abcaa592f1 c8e33462c8c447768b3d74067863f9cc t = 1.571 23788d468906483eb554d9abcaa592f1--c8e33462c8c447768b3d74067863f9cc 14a4242e9b514796a8fb7ae54cf37587 X c8e33462c8c447768b3d74067863f9cc--14a4242e9b514796a8fb7ae54cf37587 14a4242e9b514796a8fb7ae54cf37587--3fbee8cd832247179216f1f52187ba7e 941db546a4ea421ab12aeea4875f172f 8c813b17a55943e3bde3918f148d7961 966b185577df49d4bf4e0807de681db8--8c813b17a55943e3bde3918f148d7961 f77eba7d431d47a39a2c07b37775a07f 8c813b17a55943e3bde3918f148d7961--f77eba7d431d47a39a2c07b37775a07f c43aad4114884ddc9d8188799c445790 f77eba7d431d47a39a2c07b37775a07f--c43aad4114884ddc9d8188799c445790 8173ef64504746e391c127ff9489b16e X c43aad4114884ddc9d8188799c445790--8173ef64504746e391c127ff9489b16e 59e161f422c64bae8e150b3a0e09ed05 8173ef64504746e391c127ff9489b16e--59e161f422c64bae8e150b3a0e09ed05 6a9f3c0a4adc425282f97e2e5976789c 59e161f422c64bae8e150b3a0e09ed05--6a9f3c0a4adc425282f97e2e5976789c 21c018d12e0d43e28d4e01be74aea4e4 X 6a9f3c0a4adc425282f97e2e5976789c--21c018d12e0d43e28d4e01be74aea4e4 474fd4ae1d794dfa93117691f3264029 X 21c018d12e0d43e28d4e01be74aea4e4--474fd4ae1d794dfa93117691f3264029 ce28dbfa1c0e4e84a863aa23a4a6f5ee 474fd4ae1d794dfa93117691f3264029--ce28dbfa1c0e4e84a863aa23a4a6f5ee b1c4f89fae764d1793e452f3969c6637 ce28dbfa1c0e4e84a863aa23a4a6f5ee--b1c4f89fae764d1793e452f3969c6637 c7241f301f394a86b5e2fe8bfd9d87c8 X b1c4f89fae764d1793e452f3969c6637--c7241f301f394a86b5e2fe8bfd9d87c8 c7241f301f394a86b5e2fe8bfd9d87c8--941db546a4ea421ab12aeea4875f172f

The output circuit displays three groups of system Hamiltonian evolutions which account for global-phases and single-qubit detunings related to the mapping between the \(Z\) and \(N\) operators. Optionally, global phases can be ignored.

In general, the mapping of a \(n\)-qubit Ising Hamiltonian to another will require at most \(n(n-1)\) evolutions. The transformed circuit performs these evolutions for specific times that are computed from the solution of a linear system of equations involving the set of interactions in the target and build Hamiltonians.

In this case, the mapping is exact when using the step-wise DAQC strategy (Strategy.SDAQC) available in Qadence. In banged DAQC (Strategy.BDAQC) the mapping is approximate, but easier to implement on a physical device with always-on interactions such as neutral-atom systems.

Just as before, the transformed Ising circuit can be checked to exactly recover the CPHASE gate:

# CPHASE on (i, j), Identity on third qubit:
cphase_matrix = block_to_tensor(kron(CPHASE(i, j, phi), I(k)))

# CPHASE using the transformed circuit:
cphase_evo_matrix = block_to_tensor(transformed_ising)

# Check that it implements the CPHASE.
# Will fail if global phases are ignored.
cphase_matrix == cphase_evo_matrix : True

The CNOT gate can now finally be built:

from qadence import equivalent_state, run, sample

cnot_daqc = chain(
    H(j),
    transformed_ising,
    H(j)
)

# And finally apply the CNOT on a specific 3-qubit initial state:
init_state = product_state("101")

# Check we get an equivalent wavefunction
wf_cnot = run(n_qubits, block=cnot_target, state=init_state)
wf_daqc = run(n_qubits, block=cnot_daqc, state=init_state)

# Visualize the CNOT bit-flip in samples.
wf_cnot == wf_dacq : True
sample cnot_target = [OrderedCounter({'111': 100})]
sample cnot_dacq = [OrderedCounter({'111': 100})]

As one can see, a CNOT operation has been succesfully implemented on the desired target qubits by using only the global system as the building block Hamiltonian and single-qubit rotations. Decomposing a single digital gate into an Ising Hamiltonian serves as a proof of principle for the potential of this technique to represent universal quantum computation.

Technical details on the DAQC transformation

  • The mapping between target generator and final circuit is performed by solving a linear system of size \(n(n-1)\) where \(n\) is the number of qubits, so it can be computed efficiently (i.e., with a polynomial cost in the number of qubits).
  • The linear system to be solved is actually not invertible for \(n=4\) qubits. This is very specific edge case requiring a workaround, that is currently not yet implemented.
  • As mentioned, the final circuit has at most \(n(n-1)\) slices, so there is at most a quadratic overhead in circuit depth.

Finally, and most important to its usage:

  • The target Hamiltonian should be sufficiently represented in the building block Hamiltonian.

To illustrate this point, consider the following target and build Hamiltonians:

# Interaction between qubits 0 and 1
gen_target = 1.0 * (Z(0) @ Z(1))

# Fixed interaction between qubits 1 and 2, and customizable between 0 and 1
def gen_build(g_int):
    return g_int * (Z(0) @ Z(1)) + 1.0 * (Z(1) @ Z(2))

And now we perform the DAQC transform by setting g_int=1.0, exactly matching the target Hamiltonian:

transformed_ising = daqc_transform(
    n_qubits=3,
    gen_target=gen_target,
    t_f=1.0,
    gen_build=gen_build(g_int=1.0),
)

# display(transformed_ising)
%3 cluster_a0ec4c1f23a24706b714a173b4405900 cluster_e59339ed3f30466dbc2cfb38da2ccb11 011d96e0ad5249b49749779a75a0d2c2 0 6a64b2aefd114cb09565bead0ea9b254 X 011d96e0ad5249b49749779a75a0d2c2--6a64b2aefd114cb09565bead0ea9b254 1e99d7a7b1ad485fb1f8be994754b12c 1 6a81566eb6ab4664a163dec12a48e416 HamEvo 6a64b2aefd114cb09565bead0ea9b254--6a81566eb6ab4664a163dec12a48e416 3794ff96ca1843c787b27b1abe3eee1b X 6a81566eb6ab4664a163dec12a48e416--3794ff96ca1843c787b27b1abe3eee1b 34dda5888b1c4760b1a305fde7419861 3794ff96ca1843c787b27b1abe3eee1b--34dda5888b1c4760b1a305fde7419861 f20cbbf417f74644b33bfd46614a7905 HamEvo 34dda5888b1c4760b1a305fde7419861--f20cbbf417f74644b33bfd46614a7905 f04ab5dcef1747779e607404c78c9627 f20cbbf417f74644b33bfd46614a7905--f04ab5dcef1747779e607404c78c9627 beea44b559ea4cf68dad74e77cd91e06 f04ab5dcef1747779e607404c78c9627--beea44b559ea4cf68dad74e77cd91e06 370679d5c71848428368d38d7221dde4 17619ae585d94b6a8b1e7c1748ba6b8f 1e99d7a7b1ad485fb1f8be994754b12c--17619ae585d94b6a8b1e7c1748ba6b8f 13480598af2d4b37bec4e2a228cffda0 2 54b51477ab544ad39c02b6a56d32b70a t = -0.50 17619ae585d94b6a8b1e7c1748ba6b8f--54b51477ab544ad39c02b6a56d32b70a c913f9e938134ce89bfd642df8d8ffea 54b51477ab544ad39c02b6a56d32b70a--c913f9e938134ce89bfd642df8d8ffea 41fc75aaf8164d999e9442fb6b600538 X c913f9e938134ce89bfd642df8d8ffea--41fc75aaf8164d999e9442fb6b600538 d6136baa648543e58697abc258a1ad2b t = -0.50 41fc75aaf8164d999e9442fb6b600538--d6136baa648543e58697abc258a1ad2b 66e98c27eaa549f8ae1ae020ba78447e X d6136baa648543e58697abc258a1ad2b--66e98c27eaa549f8ae1ae020ba78447e 66e98c27eaa549f8ae1ae020ba78447e--370679d5c71848428368d38d7221dde4 b5ca557a42f14a3ca150946c57c582e6 c5c34811630944e2a95e57c25c4e9d66 X 13480598af2d4b37bec4e2a228cffda0--c5c34811630944e2a95e57c25c4e9d66 a39109cc888e4f30ab4f33f83bc90775 c5c34811630944e2a95e57c25c4e9d66--a39109cc888e4f30ab4f33f83bc90775 2bbf52fd573646b6b9f1d078e4d47bd1 X a39109cc888e4f30ab4f33f83bc90775--2bbf52fd573646b6b9f1d078e4d47bd1 558e10ddb0fc4b0e895cfd477cc95ce5 X 2bbf52fd573646b6b9f1d078e4d47bd1--558e10ddb0fc4b0e895cfd477cc95ce5 7d7e1ff35bc247b7b076a3d99308c8d9 558e10ddb0fc4b0e895cfd477cc95ce5--7d7e1ff35bc247b7b076a3d99308c8d9 f1cbebd120c241a2a0ba729e5e8a5c68 X 7d7e1ff35bc247b7b076a3d99308c8d9--f1cbebd120c241a2a0ba729e5e8a5c68 f1cbebd120c241a2a0ba729e5e8a5c68--b5ca557a42f14a3ca150946c57c582e6

Now, if the interaction between qubits 0 and 1 is weakened in the build Hamiltonian:

transformed_ising = daqc_transform(
    n_qubits=3,
    gen_target=gen_target,
    t_f=1.0,
    gen_build=gen_build(g_int=0.001),
)

# display(transformed_ising)
%3 cluster_080c80bd41fb4482b433f526b71190b4 cluster_f7ba2a11109d47cb8299da71e81f9401 fc4e1d415e1c4833addf4ead25b9a732 0 797b9f41f37940b0a78205f6b99595a5 X fc4e1d415e1c4833addf4ead25b9a732--797b9f41f37940b0a78205f6b99595a5 3c307c48227342fa84c77df0d190e7c6 1 24f216f551514388a824f021296a1180 HamEvo 797b9f41f37940b0a78205f6b99595a5--24f216f551514388a824f021296a1180 96183b7c86f54b6382bf4526e22ad599 X 24f216f551514388a824f021296a1180--96183b7c86f54b6382bf4526e22ad599 f77885ae72e7490d8380c38a12b63829 96183b7c86f54b6382bf4526e22ad599--f77885ae72e7490d8380c38a12b63829 81968d1a282a4ae3a315f783033003fd HamEvo f77885ae72e7490d8380c38a12b63829--81968d1a282a4ae3a315f783033003fd 55609d906b214b02841395570b8b7550 81968d1a282a4ae3a315f783033003fd--55609d906b214b02841395570b8b7550 46069258712e4e0987b175c53f41c43f 55609d906b214b02841395570b8b7550--46069258712e4e0987b175c53f41c43f 37ef6ca1f292463e8554f812ae56c9a7 4bd44b04592b4981a2ce5c0c585dadc1 3c307c48227342fa84c77df0d190e7c6--4bd44b04592b4981a2ce5c0c585dadc1 ef40df51711346c0b4cc632e776a03eb 2 e3737c9b4a14471493d52bad32e503e3 t = -500. 4bd44b04592b4981a2ce5c0c585dadc1--e3737c9b4a14471493d52bad32e503e3 df0ec47224e64255b0f55fde04bb4e2a e3737c9b4a14471493d52bad32e503e3--df0ec47224e64255b0f55fde04bb4e2a 222a85f2a174471ab06e483bf797ab23 X df0ec47224e64255b0f55fde04bb4e2a--222a85f2a174471ab06e483bf797ab23 48db01ff0303475db9a4b83e4939c882 t = -500. 222a85f2a174471ab06e483bf797ab23--48db01ff0303475db9a4b83e4939c882 de84704b8da74f11a234fb1a77c7b262 X 48db01ff0303475db9a4b83e4939c882--de84704b8da74f11a234fb1a77c7b262 de84704b8da74f11a234fb1a77c7b262--37ef6ca1f292463e8554f812ae56c9a7 c54985cc0370487b9b595c665100248f bca1523bb902442392a2549cb8084b86 X ef40df51711346c0b4cc632e776a03eb--bca1523bb902442392a2549cb8084b86 c24fe8eadbdc4e8fbeb1140908d66eda bca1523bb902442392a2549cb8084b86--c24fe8eadbdc4e8fbeb1140908d66eda 0c8b9a27a4cd435da8ce9ae34bda1b1a X c24fe8eadbdc4e8fbeb1140908d66eda--0c8b9a27a4cd435da8ce9ae34bda1b1a 1712c43328d34df6829265d62f528c7c X 0c8b9a27a4cd435da8ce9ae34bda1b1a--1712c43328d34df6829265d62f528c7c 19d9ac05af2f4d41a227fab4254ae319 1712c43328d34df6829265d62f528c7c--19d9ac05af2f4d41a227fab4254ae319 a26eff8b39c84e2a8607d791c3eb215d X 19d9ac05af2f4d41a227fab4254ae319--a26eff8b39c84e2a8607d791c3eb215d a26eff8b39c84e2a8607d791c3eb215d--c54985cc0370487b9b595c665100248f

The times slices using the build Hamiltonian need now to evolve for much longer to represent the same interaction since it is not sufficiently represented in the building block Hamiltonian.

In the limit where that interaction is not present, the transform will not work:

try:
    transformed_ising = daqc_transform(
        n_qubits=3,
        gen_target=gen_target,
        t_f=1.0,
        gen_build=gen_build(g_int = 0.0),
    )
except ValueError as error:
    print("Error:", error)
Error: Incompatible interactions between target and build Hamiltonians.

References