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.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_280121e65e624d76bf222d89d7b28227 cluster_2361479bc74c409da6d128d2bab381d5 cluster_4152b5dfad244d10abfab22a5b7134e3 cluster_9bc6046c721945cc8915ecf88b9f63fc cluster_9d6dda27c42a46aeb2ca61777e3dc859 cluster_48c0b8d2fd0d4f579aa4dbed7293aa35 cluster_749ecfc32e7945e494cd5a838f3ee72a 483057023d8b48759dff8a582d8dd043 0 63d3e71af39c4876a10c3ef569f246b0 HamEvo 483057023d8b48759dff8a582d8dd043--63d3e71af39c4876a10c3ef569f246b0 9e91188b52354888abbff62f7feb1f22 1 2b7a8525d9ba4b2bab8d60db445d2bf3 HamEvo 63d3e71af39c4876a10c3ef569f246b0--2b7a8525d9ba4b2bab8d60db445d2bf3 3fd2262d093f41e2b172770bfabbe377 HamEvo 2b7a8525d9ba4b2bab8d60db445d2bf3--3fd2262d093f41e2b172770bfabbe377 6524df61840542a98234dbb6536d902c X 3fd2262d093f41e2b172770bfabbe377--6524df61840542a98234dbb6536d902c 6b1def381f9b45b58bf15e5120d98cc2 HamEvo 6524df61840542a98234dbb6536d902c--6b1def381f9b45b58bf15e5120d98cc2 509479ed43094cdfa5b45baed563a96f HamEvo 6b1def381f9b45b58bf15e5120d98cc2--509479ed43094cdfa5b45baed563a96f 2ca2618a5ec74e0ab266796ff1a2185d X 509479ed43094cdfa5b45baed563a96f--2ca2618a5ec74e0ab266796ff1a2185d 541861c476424b12b8d3835ec91a4091 2ca2618a5ec74e0ab266796ff1a2185d--541861c476424b12b8d3835ec91a4091 0480afeb4b4443d19a99e5423b60154d HamEvo 541861c476424b12b8d3835ec91a4091--0480afeb4b4443d19a99e5423b60154d 596c885156274f348f2926217ed800d4 HamEvo 0480afeb4b4443d19a99e5423b60154d--596c885156274f348f2926217ed800d4 57d198f489c94a63915dabcb02aa33ca 596c885156274f348f2926217ed800d4--57d198f489c94a63915dabcb02aa33ca e262e04c416c403eb6b0b291d44cb68f 57d198f489c94a63915dabcb02aa33ca--e262e04c416c403eb6b0b291d44cb68f ca3ff4ed3b9b47618640fa3be280f333 a2f9571802ca48978d5140bd2d0045fe t = -3.14 9e91188b52354888abbff62f7feb1f22--a2f9571802ca48978d5140bd2d0045fe 0d56bfb57ac0488582b7f2b18dc480b8 2 854a2547ccbd44c5910022567ef2e098 t = 3.142 a2f9571802ca48978d5140bd2d0045fe--854a2547ccbd44c5910022567ef2e098 683f2256f43347068a1a46dfd3d9d950 t = -3.14 854a2547ccbd44c5910022567ef2e098--683f2256f43347068a1a46dfd3d9d950 498f4df0d42b48eca3ccee69aa87daed 683f2256f43347068a1a46dfd3d9d950--498f4df0d42b48eca3ccee69aa87daed a08d68fa75ae4744b63c326fc748220d t = 1.571 498f4df0d42b48eca3ccee69aa87daed--a08d68fa75ae4744b63c326fc748220d 95be5490d3a840dab953bd31edafe2ec t = 1.571 a08d68fa75ae4744b63c326fc748220d--95be5490d3a840dab953bd31edafe2ec 2e33e52506c94b6cab9e88eb47be444c 95be5490d3a840dab953bd31edafe2ec--2e33e52506c94b6cab9e88eb47be444c d3ea20e9551d465bbaf1474ae95dafaf X 2e33e52506c94b6cab9e88eb47be444c--d3ea20e9551d465bbaf1474ae95dafaf 441440a293df404e9b564beb1dda57a8 t = 1.571 d3ea20e9551d465bbaf1474ae95dafaf--441440a293df404e9b564beb1dda57a8 70ba3402324048228e2970ddb6a670a2 t = 1.571 441440a293df404e9b564beb1dda57a8--70ba3402324048228e2970ddb6a670a2 79d5f0b00e7d483eab845853f49f3c21 X 70ba3402324048228e2970ddb6a670a2--79d5f0b00e7d483eab845853f49f3c21 79d5f0b00e7d483eab845853f49f3c21--ca3ff4ed3b9b47618640fa3be280f333 e8b6c9206acd414eb99c74f4e071304d bbea800458684ed1bab90b5edaf56339 0d56bfb57ac0488582b7f2b18dc480b8--bbea800458684ed1bab90b5edaf56339 357eff7c52934a1cb65c2e75fb89b6a9 bbea800458684ed1bab90b5edaf56339--357eff7c52934a1cb65c2e75fb89b6a9 baf841832fe749f28799cece03d262f9 357eff7c52934a1cb65c2e75fb89b6a9--baf841832fe749f28799cece03d262f9 4f6ed3f721074f3cbece3179193d9ee0 X baf841832fe749f28799cece03d262f9--4f6ed3f721074f3cbece3179193d9ee0 8c7744ff47264450a7bdb1b2670159c7 4f6ed3f721074f3cbece3179193d9ee0--8c7744ff47264450a7bdb1b2670159c7 9526f76b9c2c47d8be00a76f7436b4b4 8c7744ff47264450a7bdb1b2670159c7--9526f76b9c2c47d8be00a76f7436b4b4 cfbfd99efffc47bf8291ef28ee56ab27 X 9526f76b9c2c47d8be00a76f7436b4b4--cfbfd99efffc47bf8291ef28ee56ab27 2fbbd651bdf54b11ad80b602bfaf721d X cfbfd99efffc47bf8291ef28ee56ab27--2fbbd651bdf54b11ad80b602bfaf721d 83bb87838a224f77a7a88ed90f302edd 2fbbd651bdf54b11ad80b602bfaf721d--83bb87838a224f77a7a88ed90f302edd 3fc30d59e3cc4a188ed9a852a6a6882b 83bb87838a224f77a7a88ed90f302edd--3fc30d59e3cc4a188ed9a852a6a6882b f53512ea3dec45ec8288dfea97b42b20 X 3fc30d59e3cc4a188ed9a852a6a6882b--f53512ea3dec45ec8288dfea97b42b20 f53512ea3dec45ec8288dfea97b42b20--e8b6c9206acd414eb99c74f4e071304d

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_6350c1face5e421da573dc96f7e42d4a cluster_d7c73eae82594bfc8b062416a9166dc2 51ac2cad286c4bd7b537e73f9ad9458f 0 a3bc6e9c8e3141daa940f9549d9f65b3 X 51ac2cad286c4bd7b537e73f9ad9458f--a3bc6e9c8e3141daa940f9549d9f65b3 def4995f29e2460eba33400d0cc980c2 1 0210a07b2cf648a8ade6e40054ff58d3 HamEvo a3bc6e9c8e3141daa940f9549d9f65b3--0210a07b2cf648a8ade6e40054ff58d3 831d154147584bf5bc67d43366170add X 0210a07b2cf648a8ade6e40054ff58d3--831d154147584bf5bc67d43366170add 344b180749f4482083de3349a1d33456 831d154147584bf5bc67d43366170add--344b180749f4482083de3349a1d33456 7c23493eaf294d7193e39508ffb3ed65 HamEvo 344b180749f4482083de3349a1d33456--7c23493eaf294d7193e39508ffb3ed65 8466f88a1b52461fba710a8377557e41 7c23493eaf294d7193e39508ffb3ed65--8466f88a1b52461fba710a8377557e41 eeff056d5c3142bd82410612082070c5 8466f88a1b52461fba710a8377557e41--eeff056d5c3142bd82410612082070c5 2520e3e0cd16417a89e974877d2695ee abc3ee231d514f078f9c3041e3188cd2 def4995f29e2460eba33400d0cc980c2--abc3ee231d514f078f9c3041e3188cd2 47d3bd366e8749659059b851daeb46af 2 983720960b314d7892dc788705ca469b t = -0.50 abc3ee231d514f078f9c3041e3188cd2--983720960b314d7892dc788705ca469b d41e51f60ed24ea0a8578ff7b364d595 983720960b314d7892dc788705ca469b--d41e51f60ed24ea0a8578ff7b364d595 2e2bdc9a258844aeb6647ae74fc1ab9c X d41e51f60ed24ea0a8578ff7b364d595--2e2bdc9a258844aeb6647ae74fc1ab9c 15be57643203435fb6a7eeb8cf5d77ec t = -0.50 2e2bdc9a258844aeb6647ae74fc1ab9c--15be57643203435fb6a7eeb8cf5d77ec a34b92877e0246c9ac304f0dffa0febc X 15be57643203435fb6a7eeb8cf5d77ec--a34b92877e0246c9ac304f0dffa0febc a34b92877e0246c9ac304f0dffa0febc--2520e3e0cd16417a89e974877d2695ee 0a1d82be38b9448aa401937e71ab2556 4070d27d88444b62ad08cd7e31dc9771 X 47d3bd366e8749659059b851daeb46af--4070d27d88444b62ad08cd7e31dc9771 bff34b40ca4442adb0314e3cad76d08c 4070d27d88444b62ad08cd7e31dc9771--bff34b40ca4442adb0314e3cad76d08c b1ff9dae5ff944c98cfcd0d5bedcfc4d X bff34b40ca4442adb0314e3cad76d08c--b1ff9dae5ff944c98cfcd0d5bedcfc4d 61c8032500e149328eb00f40bb0e5c50 X b1ff9dae5ff944c98cfcd0d5bedcfc4d--61c8032500e149328eb00f40bb0e5c50 6339aff629ee45e1be2881edd3bbca28 61c8032500e149328eb00f40bb0e5c50--6339aff629ee45e1be2881edd3bbca28 423c225090df4b43838a1d2ba2e4cea9 X 6339aff629ee45e1be2881edd3bbca28--423c225090df4b43838a1d2ba2e4cea9 423c225090df4b43838a1d2ba2e4cea9--0a1d82be38b9448aa401937e71ab2556

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_74508cfe8c664dce8e5ed5a6968e1514 cluster_24d8ed34a1e74b12b4239b2747926ae8 9114e75c7c074019a29f9db95e21c50d 0 63f73a52875b48cf9386d95d99fac62a X 9114e75c7c074019a29f9db95e21c50d--63f73a52875b48cf9386d95d99fac62a bf65bfa650af4876969edf4eba8fc296 1 0b511eaeded04aa9add1a5f6c37dfbb1 HamEvo 63f73a52875b48cf9386d95d99fac62a--0b511eaeded04aa9add1a5f6c37dfbb1 c6d07994818e42fd81d40c8909db5158 X 0b511eaeded04aa9add1a5f6c37dfbb1--c6d07994818e42fd81d40c8909db5158 972fc03712d04e248335b1ba08c5948a c6d07994818e42fd81d40c8909db5158--972fc03712d04e248335b1ba08c5948a c57aa9daf1fc48cb8a12568936c8dc4e HamEvo 972fc03712d04e248335b1ba08c5948a--c57aa9daf1fc48cb8a12568936c8dc4e bc4d94b88860498882aa1fe027cd3068 c57aa9daf1fc48cb8a12568936c8dc4e--bc4d94b88860498882aa1fe027cd3068 983fb6978eb1443ea7d19527dbdf6e82 bc4d94b88860498882aa1fe027cd3068--983fb6978eb1443ea7d19527dbdf6e82 a5546306dee745e89072b5864a8335df 78c432dfb86248a3a769c95e44b43559 bf65bfa650af4876969edf4eba8fc296--78c432dfb86248a3a769c95e44b43559 7299dadb75fe40eca9ab924ce00fdc3b 2 464317952bd84b34b05a30eadea657ae t = -500. 78c432dfb86248a3a769c95e44b43559--464317952bd84b34b05a30eadea657ae 92d2a15c23ec4176aa64f96cfe1d1c24 464317952bd84b34b05a30eadea657ae--92d2a15c23ec4176aa64f96cfe1d1c24 e401191c3c9442e09c2fdab8d2124abc X 92d2a15c23ec4176aa64f96cfe1d1c24--e401191c3c9442e09c2fdab8d2124abc 753d98586d7d44e3aaa645bd367e83c3 t = -500. e401191c3c9442e09c2fdab8d2124abc--753d98586d7d44e3aaa645bd367e83c3 3905302f30d747a09203c5325a735a66 X 753d98586d7d44e3aaa645bd367e83c3--3905302f30d747a09203c5325a735a66 3905302f30d747a09203c5325a735a66--a5546306dee745e89072b5864a8335df 43cb3b318d2046fe8f62a380fb0f316e c69e58ba17274c4ba7950878b493cfc0 X 7299dadb75fe40eca9ab924ce00fdc3b--c69e58ba17274c4ba7950878b493cfc0 ce39349d6cf2479481b32cc940c50852 c69e58ba17274c4ba7950878b493cfc0--ce39349d6cf2479481b32cc940c50852 010e520eb25f4967b98939a524554cbc X ce39349d6cf2479481b32cc940c50852--010e520eb25f4967b98939a524554cbc d305ec83fde0475db9bd591e1126f52d X 010e520eb25f4967b98939a524554cbc--d305ec83fde0475db9bd591e1126f52d 04913c7aab874765a48a9fd0d182042a d305ec83fde0475db9bd591e1126f52d--04913c7aab874765a48a9fd0d182042a eb8d5d8848f345a7a370f5f6897b085d X 04913c7aab874765a48a9fd0d182042a--eb8d5d8848f345a7a370f5f6897b085d eb8d5d8848f345a7a370f5f6897b085d--43cb3b318d2046fe8f62a380fb0f316e

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