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_6586ce6330c44b9abe0e76f0fff855a5 cluster_72c747556cf84ec9ba40fc5443771fe8 cluster_1762f212899744f780fbc36209b31c01 cluster_9c57185d808e43819ee02e195940ba07 cluster_0e0fb04cab4a4cd0928abff119708777 cluster_16d11ee72458488986b0a9535457e78e cluster_557045eb25c9405f9d02a85a46e646b4 8e5690aa441847198a665077e51c93a3 0 0923fdea61ec4bebbae5ded2f29f54d1 HamEvo 8e5690aa441847198a665077e51c93a3--0923fdea61ec4bebbae5ded2f29f54d1 37148f298a814ff89f872b08e1755fb6 1 96ecf78bb7a949d68c11275a61f693f8 HamEvo 0923fdea61ec4bebbae5ded2f29f54d1--96ecf78bb7a949d68c11275a61f693f8 4200878369e9466f80c4575d570e8b5c HamEvo 96ecf78bb7a949d68c11275a61f693f8--4200878369e9466f80c4575d570e8b5c a9a29391a4ca4a4cadcf0cf203ad72ac X 4200878369e9466f80c4575d570e8b5c--a9a29391a4ca4a4cadcf0cf203ad72ac d1155f080f63429cbfe40c9b8e122f9d HamEvo a9a29391a4ca4a4cadcf0cf203ad72ac--d1155f080f63429cbfe40c9b8e122f9d 11bb6737a0104f27a822e39669d54a25 HamEvo d1155f080f63429cbfe40c9b8e122f9d--11bb6737a0104f27a822e39669d54a25 3450a6ac7a6540749dbb9002ac176aae X 11bb6737a0104f27a822e39669d54a25--3450a6ac7a6540749dbb9002ac176aae 6e51fca567e04073bd6701b40b4ae975 3450a6ac7a6540749dbb9002ac176aae--6e51fca567e04073bd6701b40b4ae975 1cbb277599e042c0b2ddac6eab8f8619 HamEvo 6e51fca567e04073bd6701b40b4ae975--1cbb277599e042c0b2ddac6eab8f8619 a0af3d353aed4746b31e98f8dec4369f HamEvo 1cbb277599e042c0b2ddac6eab8f8619--a0af3d353aed4746b31e98f8dec4369f cea88b1521264e1ea4710e41e22688b2 a0af3d353aed4746b31e98f8dec4369f--cea88b1521264e1ea4710e41e22688b2 84df277f2e2b4aaa8b56638c6accf619 cea88b1521264e1ea4710e41e22688b2--84df277f2e2b4aaa8b56638c6accf619 cff1683e948f4286bae86f4dea769e33 3727bcb8951244aeafb01544acf0a31f t = -3.14 37148f298a814ff89f872b08e1755fb6--3727bcb8951244aeafb01544acf0a31f 1a858263fba847048a54dcfe594b1e53 2 72b0628d423645da86809238eeba0ae3 t = 3.142 3727bcb8951244aeafb01544acf0a31f--72b0628d423645da86809238eeba0ae3 2733f8042e7642669b1613dec1ba085a t = -3.14 72b0628d423645da86809238eeba0ae3--2733f8042e7642669b1613dec1ba085a 686b14422f1b46258e03f7bf0cfc9e44 2733f8042e7642669b1613dec1ba085a--686b14422f1b46258e03f7bf0cfc9e44 9c234a9221fd422a810e46da07be13b2 t = 1.571 686b14422f1b46258e03f7bf0cfc9e44--9c234a9221fd422a810e46da07be13b2 3cf0bab41d9f459e88816a8997f73402 t = 1.571 9c234a9221fd422a810e46da07be13b2--3cf0bab41d9f459e88816a8997f73402 b963a3e0bad64f88b346d94b8ffff819 3cf0bab41d9f459e88816a8997f73402--b963a3e0bad64f88b346d94b8ffff819 20a9239662ae42e18fe38715c67a4086 X b963a3e0bad64f88b346d94b8ffff819--20a9239662ae42e18fe38715c67a4086 0eae66cfcf934b43bf2b1aa433fc9f55 t = 1.571 20a9239662ae42e18fe38715c67a4086--0eae66cfcf934b43bf2b1aa433fc9f55 47d1148227244bb1a27c13accc2dd42f t = 1.571 0eae66cfcf934b43bf2b1aa433fc9f55--47d1148227244bb1a27c13accc2dd42f cfaaa23c376d4d1a853e1a7095a05e6f X 47d1148227244bb1a27c13accc2dd42f--cfaaa23c376d4d1a853e1a7095a05e6f cfaaa23c376d4d1a853e1a7095a05e6f--cff1683e948f4286bae86f4dea769e33 d9789b846f55400681a5897c5ff60f07 cc4f2e3989a34c38a133677101bb3e55 1a858263fba847048a54dcfe594b1e53--cc4f2e3989a34c38a133677101bb3e55 4d2d6530055a4780bfa1db3aa7edfbaf cc4f2e3989a34c38a133677101bb3e55--4d2d6530055a4780bfa1db3aa7edfbaf 9fc63ca5ed1341febf2eff83f84ef5c3 4d2d6530055a4780bfa1db3aa7edfbaf--9fc63ca5ed1341febf2eff83f84ef5c3 df94338079b2482f9385d235b9da60e5 X 9fc63ca5ed1341febf2eff83f84ef5c3--df94338079b2482f9385d235b9da60e5 da9e851ad5ec48a0aa8bc73657091d9b df94338079b2482f9385d235b9da60e5--da9e851ad5ec48a0aa8bc73657091d9b f9a0712cf1834fa7b5dc48058b1814fd da9e851ad5ec48a0aa8bc73657091d9b--f9a0712cf1834fa7b5dc48058b1814fd 6a6f246540834f19920f24bbc51d774b X f9a0712cf1834fa7b5dc48058b1814fd--6a6f246540834f19920f24bbc51d774b 3598592c205641fab61fef9aa6c07b42 X 6a6f246540834f19920f24bbc51d774b--3598592c205641fab61fef9aa6c07b42 2d9aa8880762485996e655d1017a8d6f 3598592c205641fab61fef9aa6c07b42--2d9aa8880762485996e655d1017a8d6f a746cbe7390d4dd68d190f044c72a820 2d9aa8880762485996e655d1017a8d6f--a746cbe7390d4dd68d190f044c72a820 c0e990eed1cf40e5b2b0e0d936a0a8da X a746cbe7390d4dd68d190f044c72a820--c0e990eed1cf40e5b2b0e0d936a0a8da c0e990eed1cf40e5b2b0e0d936a0a8da--d9789b846f55400681a5897c5ff60f07

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_d25c37f5eca7408fb7605cd1411556dc cluster_66825200f90244ab96994b69325ddd6b 506db13dfc754f62bad91371778bc67d 0 f488d6b8d5a64e588103e24e5609a3ea X 506db13dfc754f62bad91371778bc67d--f488d6b8d5a64e588103e24e5609a3ea 1199bae3e618444394ed0d42e197675b 1 86d3bea448fa4f7db4d34f042159169e HamEvo f488d6b8d5a64e588103e24e5609a3ea--86d3bea448fa4f7db4d34f042159169e fd1c25bb3d374cc382ca79635c1ae6d9 X 86d3bea448fa4f7db4d34f042159169e--fd1c25bb3d374cc382ca79635c1ae6d9 310baad3814041c396d621e2f4ceba4f fd1c25bb3d374cc382ca79635c1ae6d9--310baad3814041c396d621e2f4ceba4f 726997c37da04fc1a860a85442ee17ad HamEvo 310baad3814041c396d621e2f4ceba4f--726997c37da04fc1a860a85442ee17ad 456a7c5b565641728f9a17a254d1da10 726997c37da04fc1a860a85442ee17ad--456a7c5b565641728f9a17a254d1da10 5879ad540c464934be7243a94b3eb822 456a7c5b565641728f9a17a254d1da10--5879ad540c464934be7243a94b3eb822 79e8a8b9767844f8a7ded80e1cf96edc 44ed76ef890c4bab891bcadd193859b5 1199bae3e618444394ed0d42e197675b--44ed76ef890c4bab891bcadd193859b5 ceba51d72951445bb49c79a9a0d88e80 2 a52764c971a7467eb2e250371c5d38e2 t = -0.50 44ed76ef890c4bab891bcadd193859b5--a52764c971a7467eb2e250371c5d38e2 6f0386946d1642438fcd787a348f4ed7 a52764c971a7467eb2e250371c5d38e2--6f0386946d1642438fcd787a348f4ed7 6b56188cc25d4b7f966eb6a660a37984 X 6f0386946d1642438fcd787a348f4ed7--6b56188cc25d4b7f966eb6a660a37984 efee97f7a67742acbf27585a57435ea1 t = -0.50 6b56188cc25d4b7f966eb6a660a37984--efee97f7a67742acbf27585a57435ea1 21f4da7a13454c31b8c22cbe8b59d66d X efee97f7a67742acbf27585a57435ea1--21f4da7a13454c31b8c22cbe8b59d66d 21f4da7a13454c31b8c22cbe8b59d66d--79e8a8b9767844f8a7ded80e1cf96edc 77ae6f75474e4cd08d2f252d40c0966a 322f1907566e434eab94319ad2236a90 X ceba51d72951445bb49c79a9a0d88e80--322f1907566e434eab94319ad2236a90 65f90817db6249c59a726f66f298b1cb 322f1907566e434eab94319ad2236a90--65f90817db6249c59a726f66f298b1cb 7218a16a725144528993d924a92b2b47 X 65f90817db6249c59a726f66f298b1cb--7218a16a725144528993d924a92b2b47 df2871369330461f8aa05f5161b37148 X 7218a16a725144528993d924a92b2b47--df2871369330461f8aa05f5161b37148 d60fe1712047467d91b42df220bc1e4c df2871369330461f8aa05f5161b37148--d60fe1712047467d91b42df220bc1e4c 63a7e5e4537143b38c98fa50df6211d4 X d60fe1712047467d91b42df220bc1e4c--63a7e5e4537143b38c98fa50df6211d4 63a7e5e4537143b38c98fa50df6211d4--77ae6f75474e4cd08d2f252d40c0966a

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_19b3ac56aa244377b603fa1ac20b9976 cluster_6502c17968e84eb291eee3568b7bc257 de07fcf9d9494a59b4c41dbe6368fec6 0 701ad047772446fb941de2b68e3c2fc8 X de07fcf9d9494a59b4c41dbe6368fec6--701ad047772446fb941de2b68e3c2fc8 b6269d1e21404ed0bd48fb1afb2ca4c4 1 da3a6bb078c84b16bdfc22227f1f5d12 HamEvo 701ad047772446fb941de2b68e3c2fc8--da3a6bb078c84b16bdfc22227f1f5d12 7e818de4b4b94df9b672b0b1b3db42de X da3a6bb078c84b16bdfc22227f1f5d12--7e818de4b4b94df9b672b0b1b3db42de b5a0fbd3e77a44ea8684c7d5ff67823c 7e818de4b4b94df9b672b0b1b3db42de--b5a0fbd3e77a44ea8684c7d5ff67823c 506f2c3fd6a44d30b6de9b6f44653c4e HamEvo b5a0fbd3e77a44ea8684c7d5ff67823c--506f2c3fd6a44d30b6de9b6f44653c4e 36a89845196e48a280480e1f839756a1 506f2c3fd6a44d30b6de9b6f44653c4e--36a89845196e48a280480e1f839756a1 8e2e3d61cb0d44589111900d69bbe6df 36a89845196e48a280480e1f839756a1--8e2e3d61cb0d44589111900d69bbe6df 9ae30dc124104db88014b15c51590ce9 b7d0aff3bf4248bbaa48bfee830238e5 b6269d1e21404ed0bd48fb1afb2ca4c4--b7d0aff3bf4248bbaa48bfee830238e5 0e6ce38764ba4e1faa11a8aef13fea15 2 120e184875a14b148dc5dceb1e51eb80 t = -500. b7d0aff3bf4248bbaa48bfee830238e5--120e184875a14b148dc5dceb1e51eb80 d37b49a324814928a7c6abf6b925e1cc 120e184875a14b148dc5dceb1e51eb80--d37b49a324814928a7c6abf6b925e1cc 94cffeef1e264833a68160a72f024e6d X d37b49a324814928a7c6abf6b925e1cc--94cffeef1e264833a68160a72f024e6d c924ccb50d0f4a1183fca16dcd7a8141 t = -500. 94cffeef1e264833a68160a72f024e6d--c924ccb50d0f4a1183fca16dcd7a8141 b14eb1bf44e2407bbd281f698770d662 X c924ccb50d0f4a1183fca16dcd7a8141--b14eb1bf44e2407bbd281f698770d662 b14eb1bf44e2407bbd281f698770d662--9ae30dc124104db88014b15c51590ce9 0022458f467c432589e377e5759d4d30 ca64e28b57194898939d588ff31a2761 X 0e6ce38764ba4e1faa11a8aef13fea15--ca64e28b57194898939d588ff31a2761 036ec20e8d7d427ea96e7bf914367921 ca64e28b57194898939d588ff31a2761--036ec20e8d7d427ea96e7bf914367921 5018e4092a524a5bab81a18c0ede98d2 X 036ec20e8d7d427ea96e7bf914367921--5018e4092a524a5bab81a18c0ede98d2 4307647add7a41b4ac6c905de8f027d0 X 5018e4092a524a5bab81a18c0ede98d2--4307647add7a41b4ac6c905de8f027d0 db819ed187054d6cbcc735ae9613063e 4307647add7a41b4ac6c905de8f027d0--db819ed187054d6cbcc735ae9613063e 560cb814771a46bcb4763ca0dd656776 X db819ed187054d6cbcc735ae9613063e--560cb814771a46bcb4763ca0dd656776 560cb814771a46bcb4763ca0dd656776--0022458f467c432589e377e5759d4d30

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