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 = [Counter({'11': 100})]
sample from decomposed CNOT gate and 100 shots = [Counter({'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 = [Counter({'11': 100})]
sample cnot_evo = [Counter({'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.00000000000000] 
   └── KronBlock(0,1)
       ├── N(1)
       └── N(0)
├── [mul: 1.00000000000000] 
   └── KronBlock(0,2)
       ├── N(2)
       └── N(0)
└── [mul: 1.00000000000000] 
    └── 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_0848345a536743a98547544c34e0ff30 cluster_1d6cc9001b194e0ca0673500edea1ea0 cluster_7e45e86357c0450998577af08a54934d cluster_7a1e5f414448432d982cea1b90068db3 cluster_fd086200116242a1919edef9fc3b86ac cluster_fcfebf2c114748ae9a9ca78de58ee57f cluster_7f4bbd7e4f754c2791906c50aaf89aa6 bc219b448e4744eab54bd443e2a93423 0 79cd2032f684446f81024b10f1cf434f HamEvo bc219b448e4744eab54bd443e2a93423--79cd2032f684446f81024b10f1cf434f d083c709701548efaf87a3a7bf7a984c 1 27e7ae603b67400da532569f46d5c58b HamEvo 79cd2032f684446f81024b10f1cf434f--27e7ae603b67400da532569f46d5c58b bcc8400e6da84e90baf590ddfaccabcb HamEvo 27e7ae603b67400da532569f46d5c58b--bcc8400e6da84e90baf590ddfaccabcb fdcd7e2853a946659d193bf68449890d X bcc8400e6da84e90baf590ddfaccabcb--fdcd7e2853a946659d193bf68449890d 07d3e3b229b04dc2b640aeb985899d68 HamEvo fdcd7e2853a946659d193bf68449890d--07d3e3b229b04dc2b640aeb985899d68 83093f0efd594002aa8134093ddedd81 HamEvo 07d3e3b229b04dc2b640aeb985899d68--83093f0efd594002aa8134093ddedd81 7a93b135c7354dbc8f342b63bdaff055 X 83093f0efd594002aa8134093ddedd81--7a93b135c7354dbc8f342b63bdaff055 c6da76228ae04a5492922728a5ea45b5 7a93b135c7354dbc8f342b63bdaff055--c6da76228ae04a5492922728a5ea45b5 df98b2d747634d3882aec18c3f77ae8e HamEvo c6da76228ae04a5492922728a5ea45b5--df98b2d747634d3882aec18c3f77ae8e 26211653f56f49a4a0e5978f6480a94a HamEvo df98b2d747634d3882aec18c3f77ae8e--26211653f56f49a4a0e5978f6480a94a fff7bceea5b6472899271d834cc5c21f 26211653f56f49a4a0e5978f6480a94a--fff7bceea5b6472899271d834cc5c21f 66cbb0b9aaa04d898f350eae75fb10a4 fff7bceea5b6472899271d834cc5c21f--66cbb0b9aaa04d898f350eae75fb10a4 69d147c95ee240abbdc972bdf15033ca b38ff0df75404263a4f5af55cb808c55 t = -3.142 d083c709701548efaf87a3a7bf7a984c--b38ff0df75404263a4f5af55cb808c55 bcb10d22719545a5a5e86126e23885de 2 f2372caae573454caa964c685ed4b671 t = 3.142 b38ff0df75404263a4f5af55cb808c55--f2372caae573454caa964c685ed4b671 ddb382e22dc84c2e8f48d8b151ee5300 t = -3.142 f2372caae573454caa964c685ed4b671--ddb382e22dc84c2e8f48d8b151ee5300 e0c7e76a3d3e44c5848f291291fe0f21 ddb382e22dc84c2e8f48d8b151ee5300--e0c7e76a3d3e44c5848f291291fe0f21 cd78c7e352574db08702ba57a6bae461 t = 1.571 e0c7e76a3d3e44c5848f291291fe0f21--cd78c7e352574db08702ba57a6bae461 db0a6f00dd9140ef8d27f7fa13e05fa4 t = 1.571 cd78c7e352574db08702ba57a6bae461--db0a6f00dd9140ef8d27f7fa13e05fa4 a7bc3a75b960422c9aa7030e31fcfd31 db0a6f00dd9140ef8d27f7fa13e05fa4--a7bc3a75b960422c9aa7030e31fcfd31 625147b21c6845a9b1eb78cfe6088264 X a7bc3a75b960422c9aa7030e31fcfd31--625147b21c6845a9b1eb78cfe6088264 93f104901f814d4987257e0467fe5892 t = 1.571 625147b21c6845a9b1eb78cfe6088264--93f104901f814d4987257e0467fe5892 4375e3666c91490fb2267fb0bcd838ec t = 1.571 93f104901f814d4987257e0467fe5892--4375e3666c91490fb2267fb0bcd838ec 35708486208249af93578ace7ac8299d X 4375e3666c91490fb2267fb0bcd838ec--35708486208249af93578ace7ac8299d 35708486208249af93578ace7ac8299d--69d147c95ee240abbdc972bdf15033ca b8454327c51a4bb28ae2c509fbf621aa d704faf4119948e0a8a45f3d0a58d238 bcb10d22719545a5a5e86126e23885de--d704faf4119948e0a8a45f3d0a58d238 79f383d953514ca69bffa105b3b1281a d704faf4119948e0a8a45f3d0a58d238--79f383d953514ca69bffa105b3b1281a b35f40132d3449d6be38c0957447df19 79f383d953514ca69bffa105b3b1281a--b35f40132d3449d6be38c0957447df19 678305a716264cda9e73c8e2460f8aa6 X b35f40132d3449d6be38c0957447df19--678305a716264cda9e73c8e2460f8aa6 ff91d633b5ca4f1180b4e366c3d42964 678305a716264cda9e73c8e2460f8aa6--ff91d633b5ca4f1180b4e366c3d42964 0fab8e957e9e483195712279597e04d8 ff91d633b5ca4f1180b4e366c3d42964--0fab8e957e9e483195712279597e04d8 7c59d455902e48338eca4fc5818c11b2 X 0fab8e957e9e483195712279597e04d8--7c59d455902e48338eca4fc5818c11b2 caf2b19947084ef2a39cc3de7548e8fe X 7c59d455902e48338eca4fc5818c11b2--caf2b19947084ef2a39cc3de7548e8fe 8ee43ebdd2eb4f2aa424380447464377 caf2b19947084ef2a39cc3de7548e8fe--8ee43ebdd2eb4f2aa424380447464377 0d1d5d41a0b1437797405e473262cff0 8ee43ebdd2eb4f2aa424380447464377--0d1d5d41a0b1437797405e473262cff0 b7416a54b849490fa81b365ac02eb770 X 0d1d5d41a0b1437797405e473262cff0--b7416a54b849490fa81b365ac02eb770 b7416a54b849490fa81b365ac02eb770--b8454327c51a4bb28ae2c509fbf621aa

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 = [Counter({'111': 100})]
sample cnot_dacq = [Counter({'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_6084477753f44c069193fd8adf55a14f cluster_850127677613457c8b4a606a2371d5af 8b1e69a6a3404a0498ccc218cbf2f239 0 e690259c40ce47d6af294b06b41a308c X 8b1e69a6a3404a0498ccc218cbf2f239--e690259c40ce47d6af294b06b41a308c a57bb8e9b7a349a7a8e54335b534dd2b 1 176e3e73487f4ab99183e94fc0996be0 HamEvo e690259c40ce47d6af294b06b41a308c--176e3e73487f4ab99183e94fc0996be0 a4ee5132081b424bb4a4d741156c9dc3 X 176e3e73487f4ab99183e94fc0996be0--a4ee5132081b424bb4a4d741156c9dc3 0e7433960ef24096a3da21a5e0600242 a4ee5132081b424bb4a4d741156c9dc3--0e7433960ef24096a3da21a5e0600242 07470f07d98d44ffbbe0896e06a7e70c HamEvo 0e7433960ef24096a3da21a5e0600242--07470f07d98d44ffbbe0896e06a7e70c 43025788843844e399137c70e5171a1e 07470f07d98d44ffbbe0896e06a7e70c--43025788843844e399137c70e5171a1e 92a4d2bae2c5479fa4551e309bec1584 43025788843844e399137c70e5171a1e--92a4d2bae2c5479fa4551e309bec1584 e8a43bce2ba04ddf86bfd7d258dd67fe 8f6b409cbcb34691b92e339ba5467ac4 a57bb8e9b7a349a7a8e54335b534dd2b--8f6b409cbcb34691b92e339ba5467ac4 26cc0d3923b44f7488b27f28f591321b 2 7febfe3230ab442394bd5ad592c55b7f t = -0.500 8f6b409cbcb34691b92e339ba5467ac4--7febfe3230ab442394bd5ad592c55b7f 39ffad46226b457da8d3013ce753547c 7febfe3230ab442394bd5ad592c55b7f--39ffad46226b457da8d3013ce753547c c4237e0c9576441aa15452411476ceb2 X 39ffad46226b457da8d3013ce753547c--c4237e0c9576441aa15452411476ceb2 ce97ea05947849a5900bc658f7e0bd1b t = -0.500 c4237e0c9576441aa15452411476ceb2--ce97ea05947849a5900bc658f7e0bd1b 0462205000284fd7ba1ed545765bef0b X ce97ea05947849a5900bc658f7e0bd1b--0462205000284fd7ba1ed545765bef0b 0462205000284fd7ba1ed545765bef0b--e8a43bce2ba04ddf86bfd7d258dd67fe 9adb36a8c3cc45e59700c2b37f9c337c da488daa88fa4412bd07b50c42e8fdbb X 26cc0d3923b44f7488b27f28f591321b--da488daa88fa4412bd07b50c42e8fdbb de695cbc11a54beaab66ee5337407f5d da488daa88fa4412bd07b50c42e8fdbb--de695cbc11a54beaab66ee5337407f5d ee06ba0b482d4a498e8667a0303619fd X de695cbc11a54beaab66ee5337407f5d--ee06ba0b482d4a498e8667a0303619fd 8bbfe6b3328040fa81dde2885abfd380 X ee06ba0b482d4a498e8667a0303619fd--8bbfe6b3328040fa81dde2885abfd380 9341184da27949ebbba7b6b78fd7c4ee 8bbfe6b3328040fa81dde2885abfd380--9341184da27949ebbba7b6b78fd7c4ee e41e381eac8c489da23e5b08e3386ccf X 9341184da27949ebbba7b6b78fd7c4ee--e41e381eac8c489da23e5b08e3386ccf e41e381eac8c489da23e5b08e3386ccf--9adb36a8c3cc45e59700c2b37f9c337c

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_8ba621dfca2b44c69a11ce0a7815ef5e cluster_7431cd94c1ea43a0b37ff439677ed7c5 054ebc77dbde4226a9adfaa68c392568 0 009a52e335c54f0fb35cae2233938b9c X 054ebc77dbde4226a9adfaa68c392568--009a52e335c54f0fb35cae2233938b9c 3694f1f1de5b437bbcabb459ca9be165 1 bc49f1e928544b6288d86b1f71d3ae2d HamEvo 009a52e335c54f0fb35cae2233938b9c--bc49f1e928544b6288d86b1f71d3ae2d ec86e02b719a417d9d4dd0c298f8f014 X bc49f1e928544b6288d86b1f71d3ae2d--ec86e02b719a417d9d4dd0c298f8f014 3d554ac687764450a8bd8e0b67fe3b23 ec86e02b719a417d9d4dd0c298f8f014--3d554ac687764450a8bd8e0b67fe3b23 7e7f3ec807ef443895de3d9901a7d659 HamEvo 3d554ac687764450a8bd8e0b67fe3b23--7e7f3ec807ef443895de3d9901a7d659 32b79f1f9759410386a152d6cf2914e9 7e7f3ec807ef443895de3d9901a7d659--32b79f1f9759410386a152d6cf2914e9 d0600df57a98470db2d056e17b598fff 32b79f1f9759410386a152d6cf2914e9--d0600df57a98470db2d056e17b598fff 546919ed5cd841e1ae17b9c0aeabe8e2 ec242cf5783c411abd8963827ae23210 3694f1f1de5b437bbcabb459ca9be165--ec242cf5783c411abd8963827ae23210 0b015b3c272f49eebce91ff9fcf34a05 2 bbc3a2e8794049e5b289c83220a086db t = -500.000000000000 ec242cf5783c411abd8963827ae23210--bbc3a2e8794049e5b289c83220a086db 4da851d1926845d3bbb46ef3192a8953 bbc3a2e8794049e5b289c83220a086db--4da851d1926845d3bbb46ef3192a8953 fa620e7657ee4b358a21878c92d0bc04 X 4da851d1926845d3bbb46ef3192a8953--fa620e7657ee4b358a21878c92d0bc04 febedfa7b37b46edba55d9ce0069fe7a t = -500.000000000000 fa620e7657ee4b358a21878c92d0bc04--febedfa7b37b46edba55d9ce0069fe7a b5fd04bd98ea4c33a33cee9a0a991b17 X febedfa7b37b46edba55d9ce0069fe7a--b5fd04bd98ea4c33a33cee9a0a991b17 b5fd04bd98ea4c33a33cee9a0a991b17--546919ed5cd841e1ae17b9c0aeabe8e2 45dc2d78d94e4c67a0d7875c560666e9 05fabf18a3cd4ccea987bc971c14d204 X 0b015b3c272f49eebce91ff9fcf34a05--05fabf18a3cd4ccea987bc971c14d204 ff77c6d06c7d4298821c5883585ce68c 05fabf18a3cd4ccea987bc971c14d204--ff77c6d06c7d4298821c5883585ce68c 8e70f03ee7cf4f978009149f1fe9925a X ff77c6d06c7d4298821c5883585ce68c--8e70f03ee7cf4f978009149f1fe9925a f40cf6e82e5d42b0bfecb9f3c4fa1cb8 X 8e70f03ee7cf4f978009149f1fe9925a--f40cf6e82e5d42b0bfecb9f3c4fa1cb8 d85a07520d8a4853ac091daa94138068 f40cf6e82e5d42b0bfecb9f3c4fa1cb8--d85a07520d8a4853ac091daa94138068 baa8f0586abf4adc940c6834aff835ef X d85a07520d8a4853ac091daa94138068--baa8f0586abf4adc940c6834aff835ef baa8f0586abf4adc940c6834aff835ef--45dc2d78d94e4c67a0d7875c560666e9

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