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)
%3 cluster_9e67f8b017834e2191b8b61de9a63531 cluster_4070c5aa614f41439f16d3d50245fe05 cluster_b7150e31e9b94c37bcc87ea59360334f cluster_86d8473c8a264ac4a2b18ef470b5deee cluster_da2d705f6e5f4225be31b650057f5616 cluster_a320f1ff8e21405daa3c7b67031abc45 cluster_7901257aff064e8e90c708f8dbda3f77 353f55c9e8684d18a80569992a2ad7ce 0 2e012bf539df4680bc07b8ec956a478c HamEvo 353f55c9e8684d18a80569992a2ad7ce--2e012bf539df4680bc07b8ec956a478c ac20c19e269f442094828e6ae1703218 1 2273838f65d44eaf86d452ef3cb0819d HamEvo 2e012bf539df4680bc07b8ec956a478c--2273838f65d44eaf86d452ef3cb0819d 004e7143fdcb47348aad1584533cacb4 HamEvo 2273838f65d44eaf86d452ef3cb0819d--004e7143fdcb47348aad1584533cacb4 ac08fe8d59a348719bad4874bb70673d X 004e7143fdcb47348aad1584533cacb4--ac08fe8d59a348719bad4874bb70673d c393b081a10347a6b60921ade15d8ec2 HamEvo ac08fe8d59a348719bad4874bb70673d--c393b081a10347a6b60921ade15d8ec2 df2abb3c6a8149da80e74e343a0cb916 HamEvo c393b081a10347a6b60921ade15d8ec2--df2abb3c6a8149da80e74e343a0cb916 28a8f66a1064415584a41e4c66d273f5 X df2abb3c6a8149da80e74e343a0cb916--28a8f66a1064415584a41e4c66d273f5 4e9e8cf8eb524189aed2a61612bdf21b 28a8f66a1064415584a41e4c66d273f5--4e9e8cf8eb524189aed2a61612bdf21b de2ce493460e490e8474cc36d0bb431b HamEvo 4e9e8cf8eb524189aed2a61612bdf21b--de2ce493460e490e8474cc36d0bb431b 537f3b47c65e4676a0e58a412a2d13cf HamEvo de2ce493460e490e8474cc36d0bb431b--537f3b47c65e4676a0e58a412a2d13cf 5a4866aba9974e6cb763b970b1b81dc6 537f3b47c65e4676a0e58a412a2d13cf--5a4866aba9974e6cb763b970b1b81dc6 85884d948a7641e6b801544e76372590 5a4866aba9974e6cb763b970b1b81dc6--85884d948a7641e6b801544e76372590 0a4525b7e37a46be9ff87f2fd6612af6 a2875309f40f4189a5129bc0ef00394e t = -3.142 ac20c19e269f442094828e6ae1703218--a2875309f40f4189a5129bc0ef00394e fff6f10accb74631a1cef1da20c58a3c 2 3626da1c2bce47f0adc72b6a3c762fb8 t = 3.142 a2875309f40f4189a5129bc0ef00394e--3626da1c2bce47f0adc72b6a3c762fb8 18765dfe1a0040c88301fd8cea35a61e t = -3.142 3626da1c2bce47f0adc72b6a3c762fb8--18765dfe1a0040c88301fd8cea35a61e d14fbd996522435097c43fb353ce6618 18765dfe1a0040c88301fd8cea35a61e--d14fbd996522435097c43fb353ce6618 eaa7c4d7dc674e48b36a1430aa3bddc4 t = 1.571 d14fbd996522435097c43fb353ce6618--eaa7c4d7dc674e48b36a1430aa3bddc4 e1c243bd23ef40d8a9e1d76b9fff5845 t = 1.571 eaa7c4d7dc674e48b36a1430aa3bddc4--e1c243bd23ef40d8a9e1d76b9fff5845 2f84c9c95a3843fca7a8c773a245c518 e1c243bd23ef40d8a9e1d76b9fff5845--2f84c9c95a3843fca7a8c773a245c518 0fbf700b45b4452d8df9349851226d0d X 2f84c9c95a3843fca7a8c773a245c518--0fbf700b45b4452d8df9349851226d0d b2e834bd312d4b188a8c07969c089911 t = 1.571 0fbf700b45b4452d8df9349851226d0d--b2e834bd312d4b188a8c07969c089911 8b595f7a86dd4eac9f443bb167c52783 t = 1.571 b2e834bd312d4b188a8c07969c089911--8b595f7a86dd4eac9f443bb167c52783 a8e221be0bb345c3baff44b4796c0518 X 8b595f7a86dd4eac9f443bb167c52783--a8e221be0bb345c3baff44b4796c0518 a8e221be0bb345c3baff44b4796c0518--0a4525b7e37a46be9ff87f2fd6612af6 b3293868c33f4ae6ad7eba1b5ef1a3d1 f699877f29eb40eb90da35ddb73effed fff6f10accb74631a1cef1da20c58a3c--f699877f29eb40eb90da35ddb73effed 2e08c5475b244f449fc04d18a1a71c73 f699877f29eb40eb90da35ddb73effed--2e08c5475b244f449fc04d18a1a71c73 a1afc89e45024a54b0ecb8a189e89255 2e08c5475b244f449fc04d18a1a71c73--a1afc89e45024a54b0ecb8a189e89255 8fdabc26ecf6485bbbb98a31c254258c X a1afc89e45024a54b0ecb8a189e89255--8fdabc26ecf6485bbbb98a31c254258c 49c924e5564d4bdf9b7b14e8338d09e1 8fdabc26ecf6485bbbb98a31c254258c--49c924e5564d4bdf9b7b14e8338d09e1 3460959fb49041509355d0bd087a81b8 49c924e5564d4bdf9b7b14e8338d09e1--3460959fb49041509355d0bd087a81b8 a6b4b350ee29484b8d287743108e9544 X 3460959fb49041509355d0bd087a81b8--a6b4b350ee29484b8d287743108e9544 42452e54af4a427296f24bbe5a6e47bd X a6b4b350ee29484b8d287743108e9544--42452e54af4a427296f24bbe5a6e47bd 29f80132d1844a9e97c3111e4f75a761 42452e54af4a427296f24bbe5a6e47bd--29f80132d1844a9e97c3111e4f75a761 efef884af49c4dc0a3045260dd41d89c 29f80132d1844a9e97c3111e4f75a761--efef884af49c4dc0a3045260dd41d89c 74f620030324418696980561c97de519 X efef884af49c4dc0a3045260dd41d89c--74f620030324418696980561c97de519 74f620030324418696980561c97de519--b3293868c33f4ae6ad7eba1b5ef1a3d1

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_b03e94ff72b04117a9a4b0ac3273e811 cluster_cee5fb4618554fe98b045c061fead2d8 b7e688a0e5784b9bbf00eb781bf1a2eb 0 86b53009dd3c4c7f8b73baa270c01a75 X b7e688a0e5784b9bbf00eb781bf1a2eb--86b53009dd3c4c7f8b73baa270c01a75 34ecedae48d944709bd23766e2363882 1 f1ddd3d856d142b49b5109fc460b8896 HamEvo 86b53009dd3c4c7f8b73baa270c01a75--f1ddd3d856d142b49b5109fc460b8896 a5ce2bbd2d7e4d30b4f757bd78849f38 X f1ddd3d856d142b49b5109fc460b8896--a5ce2bbd2d7e4d30b4f757bd78849f38 6c4c4211afed4f1283138f144e951caa a5ce2bbd2d7e4d30b4f757bd78849f38--6c4c4211afed4f1283138f144e951caa d17062f23e9a4f5c885963178faef260 HamEvo 6c4c4211afed4f1283138f144e951caa--d17062f23e9a4f5c885963178faef260 05d552183c4348a798dd6ae2793662c3 d17062f23e9a4f5c885963178faef260--05d552183c4348a798dd6ae2793662c3 990e7ab9e20a4b8e9baec2f67182c0ab 05d552183c4348a798dd6ae2793662c3--990e7ab9e20a4b8e9baec2f67182c0ab b85fc0630fe8451784d0f00b1abced96 138a584e538c443fbddced0f18a84da1 34ecedae48d944709bd23766e2363882--138a584e538c443fbddced0f18a84da1 d5cba93910814ec383791e1f8e15ccdb 2 30a3b36b01d24de186d11908b46d5d37 t = -0.500 138a584e538c443fbddced0f18a84da1--30a3b36b01d24de186d11908b46d5d37 b61b36afa6fa432ba34ed063c0672578 30a3b36b01d24de186d11908b46d5d37--b61b36afa6fa432ba34ed063c0672578 984398f142964a9f97cc3824e75f36cc X b61b36afa6fa432ba34ed063c0672578--984398f142964a9f97cc3824e75f36cc dd3755e42b934d6f9189c45409064ebc t = -0.500 984398f142964a9f97cc3824e75f36cc--dd3755e42b934d6f9189c45409064ebc cae886941e2b4064a8d0a108743573e6 X dd3755e42b934d6f9189c45409064ebc--cae886941e2b4064a8d0a108743573e6 cae886941e2b4064a8d0a108743573e6--b85fc0630fe8451784d0f00b1abced96 63f7e2a3ce7a47eaaa1fccb645eb4c46 335673fde159403881453806c12a3960 X d5cba93910814ec383791e1f8e15ccdb--335673fde159403881453806c12a3960 690bb1de1aa14338ae9b83057291da2b 335673fde159403881453806c12a3960--690bb1de1aa14338ae9b83057291da2b 5cd7cbf00ce54f1da9345537e47a9021 X 690bb1de1aa14338ae9b83057291da2b--5cd7cbf00ce54f1da9345537e47a9021 9a18fe355cd5484e89144a6331f28487 X 5cd7cbf00ce54f1da9345537e47a9021--9a18fe355cd5484e89144a6331f28487 3646f68506884230b8496cc2a67e34e8 9a18fe355cd5484e89144a6331f28487--3646f68506884230b8496cc2a67e34e8 e2b5d80c31d243a484270116feeeb14a X 3646f68506884230b8496cc2a67e34e8--e2b5d80c31d243a484270116feeeb14a e2b5d80c31d243a484270116feeeb14a--63f7e2a3ce7a47eaaa1fccb645eb4c46

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_3f52cf8c06784e109b6959baffb1748c cluster_52d6d63d2c9343cc959ffceb5205fce0 49e9bb69692543978c8f912284df8b86 0 ea852a67f5a54ec99e26cef5acf95851 X 49e9bb69692543978c8f912284df8b86--ea852a67f5a54ec99e26cef5acf95851 a1bb85737ac54ca3a474145372edacdc 1 e87d06505c0d48efbe7f0d37c26aaa95 HamEvo ea852a67f5a54ec99e26cef5acf95851--e87d06505c0d48efbe7f0d37c26aaa95 9b3310c2d4894e40980e6a6808538ae3 X e87d06505c0d48efbe7f0d37c26aaa95--9b3310c2d4894e40980e6a6808538ae3 ed961e7526ce4f558524d7e873c6f3bb 9b3310c2d4894e40980e6a6808538ae3--ed961e7526ce4f558524d7e873c6f3bb 2cb80247afc249d8a6b34d18485d5bae HamEvo ed961e7526ce4f558524d7e873c6f3bb--2cb80247afc249d8a6b34d18485d5bae c19a962784a44456863c9fb26c78d1b2 2cb80247afc249d8a6b34d18485d5bae--c19a962784a44456863c9fb26c78d1b2 d75d4b3d35384c139acb2494fd716ec9 c19a962784a44456863c9fb26c78d1b2--d75d4b3d35384c139acb2494fd716ec9 ed95312bf28c432cb773d34358eff2f0 fb4009e9958e468fa58c19af351a7f3d a1bb85737ac54ca3a474145372edacdc--fb4009e9958e468fa58c19af351a7f3d 36f97fed75a54a24a6727044ef388323 2 9680e89d88cd454bab8cb0f6c654c505 t = -500.000000000000 fb4009e9958e468fa58c19af351a7f3d--9680e89d88cd454bab8cb0f6c654c505 99667413cfba4eeab2dcd4adb21aed9a 9680e89d88cd454bab8cb0f6c654c505--99667413cfba4eeab2dcd4adb21aed9a 5662c029837e4f7a804a6213d021482e X 99667413cfba4eeab2dcd4adb21aed9a--5662c029837e4f7a804a6213d021482e 19e7e792003d416485afabd01fc32c6d t = -500.000000000000 5662c029837e4f7a804a6213d021482e--19e7e792003d416485afabd01fc32c6d 7b46c6e484494662969fc5bf43a469af X 19e7e792003d416485afabd01fc32c6d--7b46c6e484494662969fc5bf43a469af 7b46c6e484494662969fc5bf43a469af--ed95312bf28c432cb773d34358eff2f0 91f56ad1f29243db82d6a1dfb5c10633 9bb748cadbcc49548e29fbe9b45e31d5 X 36f97fed75a54a24a6727044ef388323--9bb748cadbcc49548e29fbe9b45e31d5 fe5488ebad524514b6e048e6e2b76f42 9bb748cadbcc49548e29fbe9b45e31d5--fe5488ebad524514b6e048e6e2b76f42 0769d8fbae784e25b5b2770e256731b2 X fe5488ebad524514b6e048e6e2b76f42--0769d8fbae784e25b5b2770e256731b2 6c6ffa6ae57443f192c3e1e1f1f550c2 X 0769d8fbae784e25b5b2770e256731b2--6c6ffa6ae57443f192c3e1e1f1f550c2 57713ec631fa4f8aa93b72f5f3c279df 6c6ffa6ae57443f192c3e1e1f1f550c2--57713ec631fa4f8aa93b72f5f3c279df 18a94e9e24a74da0921c3a7c06551a73 X 57713ec631fa4f8aa93b72f5f3c279df--18a94e9e24a74da0921c3a7c06551a73 18a94e9e24a74da0921c3a7c06551a73--91f56ad1f29243db82d6a1dfb5c10633

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