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

n_qubits = 2

# CNOT gate
cnot_gate = CNOT(0, 1)

# CNOT decomposed
phi = torch.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=torch.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)
cluster_455661ff43c74065b468fe7f536f2576 cluster_c391cb6f2dc04dddb7e7f9acbaf4f981 cluster_9dfdc24d3de14c79920308ec03f445d3 cluster_124009c6cc9542a7a331fa7e11f92ad8 cluster_8d3d044aac4941d393a77f64acd4136e cluster_ee176fb005af404898dd4c3b5dccfaab cluster_6397ca89cc11430dad06951cbf5057e1 b3a81131395344bf9c7c82a5febc8a16 0 0fdf285bf14f4be1a3aaaf92b4759b45 HamEvo b3a81131395344bf9c7c82a5febc8a16--0fdf285bf14f4be1a3aaaf92b4759b45 d6289e5527c1488589fe469df93f78a6 1 1555bd55a345441096eb71ea243b7a8d HamEvo 0fdf285bf14f4be1a3aaaf92b4759b45--1555bd55a345441096eb71ea243b7a8d 8065f385554c4402a722c9be6a6aaefb HamEvo 1555bd55a345441096eb71ea243b7a8d--8065f385554c4402a722c9be6a6aaefb 6dd7720d78ce442283604006c193808e X 8065f385554c4402a722c9be6a6aaefb--6dd7720d78ce442283604006c193808e f10350e663ba40dbb0d9a8e5a38026be HamEvo 6dd7720d78ce442283604006c193808e--f10350e663ba40dbb0d9a8e5a38026be 27324a449df3436480e6858dcc8a1298 HamEvo f10350e663ba40dbb0d9a8e5a38026be--27324a449df3436480e6858dcc8a1298 2579172c4bbd4f15a4172f93c8719d23 X 27324a449df3436480e6858dcc8a1298--2579172c4bbd4f15a4172f93c8719d23 b6f5803d68b642db9915610f9f5b3dc6 2579172c4bbd4f15a4172f93c8719d23--b6f5803d68b642db9915610f9f5b3dc6 869506926c4e4f98a92fb3af035c5ed9 HamEvo b6f5803d68b642db9915610f9f5b3dc6--869506926c4e4f98a92fb3af035c5ed9 0047087464b14828af59c716e50aacbf HamEvo 869506926c4e4f98a92fb3af035c5ed9--0047087464b14828af59c716e50aacbf 2066a74c87324785a30e51c946e2837a 0047087464b14828af59c716e50aacbf--2066a74c87324785a30e51c946e2837a e3ab3f84b37140ae83c00ae170b5da8d 2066a74c87324785a30e51c946e2837a--e3ab3f84b37140ae83c00ae170b5da8d 6e41ff9c174641488449a4812cd8a00f 2974ee5c326d4f6fbaabd0ccb2f6f3da t = -3.142 d6289e5527c1488589fe469df93f78a6--2974ee5c326d4f6fbaabd0ccb2f6f3da 58f584fde52a40bbab7f319eab1a5ca5 2 19bf1b01cc8f41ec9784a5abd7dbc8a0 t = 3.142 2974ee5c326d4f6fbaabd0ccb2f6f3da--19bf1b01cc8f41ec9784a5abd7dbc8a0 22e88e6052894497b8157a431c7230f6 t = -3.142 19bf1b01cc8f41ec9784a5abd7dbc8a0--22e88e6052894497b8157a431c7230f6 e977d92dc7ba40ac987b4bfa9c3fec29 22e88e6052894497b8157a431c7230f6--e977d92dc7ba40ac987b4bfa9c3fec29 9d015ee4dc834de58cf33b1d72d628c2 t = 1.571 e977d92dc7ba40ac987b4bfa9c3fec29--9d015ee4dc834de58cf33b1d72d628c2 8d90a7d6c73f49e285dce15a62d2f832 t = 1.571 9d015ee4dc834de58cf33b1d72d628c2--8d90a7d6c73f49e285dce15a62d2f832 8e6434aceda94c20808cbc3b295844dc 8d90a7d6c73f49e285dce15a62d2f832--8e6434aceda94c20808cbc3b295844dc abcefea7a9524ae7b9860e23bde94d52 X 8e6434aceda94c20808cbc3b295844dc--abcefea7a9524ae7b9860e23bde94d52 c1ebafb977b54fa1a561555fe1c941cd t = 1.571 abcefea7a9524ae7b9860e23bde94d52--c1ebafb977b54fa1a561555fe1c941cd f189bdfd80c1456a89940e78ae9d4e7e t = 1.571 c1ebafb977b54fa1a561555fe1c941cd--f189bdfd80c1456a89940e78ae9d4e7e c557c09134b84dc7b6e3b5741c6e1632 X f189bdfd80c1456a89940e78ae9d4e7e--c557c09134b84dc7b6e3b5741c6e1632 c557c09134b84dc7b6e3b5741c6e1632--6e41ff9c174641488449a4812cd8a00f 30855f4da5444e538a8b60c3e3081a7a 5a1b0cdbfab047bda98fcf521525aa61 58f584fde52a40bbab7f319eab1a5ca5--5a1b0cdbfab047bda98fcf521525aa61 8c468407e634476d8b31b7348295633c 5a1b0cdbfab047bda98fcf521525aa61--8c468407e634476d8b31b7348295633c 4c754db40f584764a2646a09b14cbc81 8c468407e634476d8b31b7348295633c--4c754db40f584764a2646a09b14cbc81 515286d0d3284f07a4dc97d988d557f2 X 4c754db40f584764a2646a09b14cbc81--515286d0d3284f07a4dc97d988d557f2 efa6dc6f7cea4a18bee6394d9d3d5359 515286d0d3284f07a4dc97d988d557f2--efa6dc6f7cea4a18bee6394d9d3d5359 a12fc2fa34e74174bc9f22f5a8c45674 efa6dc6f7cea4a18bee6394d9d3d5359--a12fc2fa34e74174bc9f22f5a8c45674 29223d7f8b4f485d92febff9f0506879 X a12fc2fa34e74174bc9f22f5a8c45674--29223d7f8b4f485d92febff9f0506879 1d3c94dafa2b4a51be1d8b3013de89c0 X 29223d7f8b4f485d92febff9f0506879--1d3c94dafa2b4a51be1d8b3013de89c0 487f651d3d324bdeb4faa2558dd9b2fe 1d3c94dafa2b4a51be1d8b3013de89c0--487f651d3d324bdeb4faa2558dd9b2fe b516c1ff7b764b83bf29845e75bd15e7 487f651d3d324bdeb4faa2558dd9b2fe--b516c1ff7b764b83bf29845e75bd15e7 92c21006f08b450682f51052057dddf6 X b516c1ff7b764b83bf29845e75bd15e7--92c21006f08b450682f51052057dddf6 92c21006f08b450682f51052057dddf6--30855f4da5444e538a8b60c3e3081a7a

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)
cluster_6a8a7906141b49a796fd4df6b6c72a76 cluster_304fdc1ea35b4bf7abbabe2187e04879 3db0645a51e947c598d56f7b47b5fc01 0 4059ab7900c942879d69c3585965070f X 3db0645a51e947c598d56f7b47b5fc01--4059ab7900c942879d69c3585965070f a07a45b8f1654c4f8adabc26d156ba06 1 e7ce8eb86bc44f40acb2c5e4272cb499 HamEvo 4059ab7900c942879d69c3585965070f--e7ce8eb86bc44f40acb2c5e4272cb499 7b8450696b0649ed8921d9f85fa528c8 X e7ce8eb86bc44f40acb2c5e4272cb499--7b8450696b0649ed8921d9f85fa528c8 fc286096160246958e1ba16961e2314e 7b8450696b0649ed8921d9f85fa528c8--fc286096160246958e1ba16961e2314e 5e44cdb42e03414082f690955587b3db HamEvo fc286096160246958e1ba16961e2314e--5e44cdb42e03414082f690955587b3db 5e9e1fc985ac4de188a6fc15429721d8 5e44cdb42e03414082f690955587b3db--5e9e1fc985ac4de188a6fc15429721d8 2d6a9d3f027b4276a4db9ea2ca6ce7af 5e9e1fc985ac4de188a6fc15429721d8--2d6a9d3f027b4276a4db9ea2ca6ce7af c9450c5b2bd34677a312ed38623bb505 4085a52ea0694571bc144f3b48cc39da a07a45b8f1654c4f8adabc26d156ba06--4085a52ea0694571bc144f3b48cc39da 4c69ec3a47154eaa909487494672611e 2 d68148568390497695341dbfdb323d77 t = -0.500 4085a52ea0694571bc144f3b48cc39da--d68148568390497695341dbfdb323d77 8963f53a185f42a587c05117bd2a6ac4 d68148568390497695341dbfdb323d77--8963f53a185f42a587c05117bd2a6ac4 511a2b02ffd643709ed6913ff34d64e7 X 8963f53a185f42a587c05117bd2a6ac4--511a2b02ffd643709ed6913ff34d64e7 001bcff694a941728c193d1a74f3c17b t = -0.500 511a2b02ffd643709ed6913ff34d64e7--001bcff694a941728c193d1a74f3c17b dee068b7539d4abf87c1352e85dc2063 X 001bcff694a941728c193d1a74f3c17b--dee068b7539d4abf87c1352e85dc2063 dee068b7539d4abf87c1352e85dc2063--c9450c5b2bd34677a312ed38623bb505 a398b2be8b2943118da17beef2011bb8 e449a9fb1fc140cb939220eca8288419 X 4c69ec3a47154eaa909487494672611e--e449a9fb1fc140cb939220eca8288419 1abb371e3a354c2984dd3623565ddf7a e449a9fb1fc140cb939220eca8288419--1abb371e3a354c2984dd3623565ddf7a 67fa43deddc942729dcde708f783ec33 X 1abb371e3a354c2984dd3623565ddf7a--67fa43deddc942729dcde708f783ec33 20f07395dbaa4564b8cc86e5a1473a27 X 67fa43deddc942729dcde708f783ec33--20f07395dbaa4564b8cc86e5a1473a27 0f22436c4779457c85cc935d3871fd37 20f07395dbaa4564b8cc86e5a1473a27--0f22436c4779457c85cc935d3871fd37 98d78a87fd064f4db61fd8781d6aba92 X 0f22436c4779457c85cc935d3871fd37--98d78a87fd064f4db61fd8781d6aba92 98d78a87fd064f4db61fd8781d6aba92--a398b2be8b2943118da17beef2011bb8

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)
cluster_bae765a8a19246c1bcc94280d904edd5 cluster_9d0fa1887725433fa635e45d39cf03aa 9e6e59a8d5804bffa17447b7e061562a 0 f978870533c24f64a74d75374ea0d442 X 9e6e59a8d5804bffa17447b7e061562a--f978870533c24f64a74d75374ea0d442 0597095611bf4d98878ec0cf2e584e0c 1 6d3581be021f48eba95b764a9b1c1b99 HamEvo f978870533c24f64a74d75374ea0d442--6d3581be021f48eba95b764a9b1c1b99 3af77b711b8f49a4b83ca78f2d480b06 X 6d3581be021f48eba95b764a9b1c1b99--3af77b711b8f49a4b83ca78f2d480b06 28beac5c0d154dda985322a8ca36c386 3af77b711b8f49a4b83ca78f2d480b06--28beac5c0d154dda985322a8ca36c386 0debc44fbae24ce99613387a5bc07ee1 HamEvo 28beac5c0d154dda985322a8ca36c386--0debc44fbae24ce99613387a5bc07ee1 93b2b581e92647c695ad17af5eed188b 0debc44fbae24ce99613387a5bc07ee1--93b2b581e92647c695ad17af5eed188b 40d8271367f9476886c7fe28c10a9cf9 93b2b581e92647c695ad17af5eed188b--40d8271367f9476886c7fe28c10a9cf9 11e3140dc3c04e99a086db95cfcfe660 b5fdf204e58743adaba124bb0b09fbc2 0597095611bf4d98878ec0cf2e584e0c--b5fdf204e58743adaba124bb0b09fbc2 2ddfce40dc934c9686dad1e0d473bce9 2 fd6b56f030e4483a9e9324c92a564628 t = -500.000000000000 b5fdf204e58743adaba124bb0b09fbc2--fd6b56f030e4483a9e9324c92a564628 edcfcb40a3534684bdb058f71bf39429 fd6b56f030e4483a9e9324c92a564628--edcfcb40a3534684bdb058f71bf39429 aceb302d7ea04218b717731187355f64 X edcfcb40a3534684bdb058f71bf39429--aceb302d7ea04218b717731187355f64 5b8a19ad39e44fa9828a778a34c2a13e t = -500.000000000000 aceb302d7ea04218b717731187355f64--5b8a19ad39e44fa9828a778a34c2a13e b07cc07c314f46caaac9ba571ba4369f X 5b8a19ad39e44fa9828a778a34c2a13e--b07cc07c314f46caaac9ba571ba4369f b07cc07c314f46caaac9ba571ba4369f--11e3140dc3c04e99a086db95cfcfe660 d2cd9e5faf604a6093c93ceae4a62a9c 5d735426b2b545b881b883f325a34558 X 2ddfce40dc934c9686dad1e0d473bce9--5d735426b2b545b881b883f325a34558 83368752d35c477bae9d1e1279f85e05 5d735426b2b545b881b883f325a34558--83368752d35c477bae9d1e1279f85e05 6e780bcb6b914532badb1a097c68cc22 X 83368752d35c477bae9d1e1279f85e05--6e780bcb6b914532badb1a097c68cc22 c6da4170fca04d0fb7597603d4773101 X 6e780bcb6b914532badb1a097c68cc22--c6da4170fca04d0fb7597603d4773101 2fb2b25aa5c44f5d92a04263597a834d c6da4170fca04d0fb7597603d4773101--2fb2b25aa5c44f5d92a04263597a834d 841bec0602cd48ac93cc0b6251ff14be X 2fb2b25aa5c44f5d92a04263597a834d--841bec0602cd48ac93cc0b6251ff14be 841bec0602cd48ac93cc0b6251ff14be--d2cd9e5faf604a6093c93ceae4a62a9c

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