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_d793e2c8f22e4ce599767ea3a93e9770 cluster_6725fa266ee04af2ab0455151e067b0c cluster_a7956a3454984014889bc05c71e78754 cluster_940a4713caad430c8e1b53764d3f5006 cluster_0cb93661ae0a4ea59beceebeabbb25d6 cluster_d79bff24d3fb4309bc72958f88ea144e cluster_80c23edca0da4b7094638f8f57b4a383 0efe7d12a623440290d696978b4460f0 0 db09583f81e240659b503887d7adc14f HamEvo 0efe7d12a623440290d696978b4460f0--db09583f81e240659b503887d7adc14f 03e4289d771643c29d8e6f1722378165 1 87d6e53aa7654e5dbb4b677ecc14ceea HamEvo db09583f81e240659b503887d7adc14f--87d6e53aa7654e5dbb4b677ecc14ceea a842870397044db3b2c4677897706551 HamEvo 87d6e53aa7654e5dbb4b677ecc14ceea--a842870397044db3b2c4677897706551 f008d01d38b14daa8d5baffe43f5e31d X a842870397044db3b2c4677897706551--f008d01d38b14daa8d5baffe43f5e31d cfc628768b95465d8891889e02bb67f9 HamEvo f008d01d38b14daa8d5baffe43f5e31d--cfc628768b95465d8891889e02bb67f9 d590b8d3cb9b49f9a4b99ed0632f56ae HamEvo cfc628768b95465d8891889e02bb67f9--d590b8d3cb9b49f9a4b99ed0632f56ae 4e15a1160fb44717b3f459c1b3eee9f0 X d590b8d3cb9b49f9a4b99ed0632f56ae--4e15a1160fb44717b3f459c1b3eee9f0 ee164c7d52c04040960bd2649139fdc6 4e15a1160fb44717b3f459c1b3eee9f0--ee164c7d52c04040960bd2649139fdc6 fa65c955ea0c4a1da16c4398da24e7c2 HamEvo ee164c7d52c04040960bd2649139fdc6--fa65c955ea0c4a1da16c4398da24e7c2 a22821fc0080455499ade96d36ce4d84 HamEvo fa65c955ea0c4a1da16c4398da24e7c2--a22821fc0080455499ade96d36ce4d84 6f0bbb32d5be4beeaa41f6ab6e2fc14f a22821fc0080455499ade96d36ce4d84--6f0bbb32d5be4beeaa41f6ab6e2fc14f 99260559bfd34f08998c968b78a5f472 6f0bbb32d5be4beeaa41f6ab6e2fc14f--99260559bfd34f08998c968b78a5f472 634a111ae967435d8f14b359c868cd4c 5418f3390b4e4c228f5974b97c9316db t = -3.142 03e4289d771643c29d8e6f1722378165--5418f3390b4e4c228f5974b97c9316db affa2a8a1af94f5a84b04d061c938324 2 bd31dfc69882472b93e31453f9abdc96 t = 3.142 5418f3390b4e4c228f5974b97c9316db--bd31dfc69882472b93e31453f9abdc96 05d15a4f3a6d4bdcb77f4ea7d3882b03 t = -3.142 bd31dfc69882472b93e31453f9abdc96--05d15a4f3a6d4bdcb77f4ea7d3882b03 df466fe2727c4ac2aed2f80a858043a4 05d15a4f3a6d4bdcb77f4ea7d3882b03--df466fe2727c4ac2aed2f80a858043a4 df8c3769044c41dab98f99325a70997e t = 1.571 df466fe2727c4ac2aed2f80a858043a4--df8c3769044c41dab98f99325a70997e ec32076d96c441df8892a470e199da1e t = 1.571 df8c3769044c41dab98f99325a70997e--ec32076d96c441df8892a470e199da1e 6362ac13a918424a917fb0a9c693d702 ec32076d96c441df8892a470e199da1e--6362ac13a918424a917fb0a9c693d702 345e0a7446474b92a3196ba22169ec9c X 6362ac13a918424a917fb0a9c693d702--345e0a7446474b92a3196ba22169ec9c a4ee8062ad6d4f93a6feba41095dbd5a t = 1.571 345e0a7446474b92a3196ba22169ec9c--a4ee8062ad6d4f93a6feba41095dbd5a 841a8cb51bfc4c11bf1a32a6004d3d72 t = 1.571 a4ee8062ad6d4f93a6feba41095dbd5a--841a8cb51bfc4c11bf1a32a6004d3d72 fbd666b789fe4c51ac63bdc969cf9dc5 X 841a8cb51bfc4c11bf1a32a6004d3d72--fbd666b789fe4c51ac63bdc969cf9dc5 fbd666b789fe4c51ac63bdc969cf9dc5--634a111ae967435d8f14b359c868cd4c 6870c4eafa3244b9a5ef4bea64d17697 1cba5068ccf141ac89951e22bcacad6f affa2a8a1af94f5a84b04d061c938324--1cba5068ccf141ac89951e22bcacad6f 0a97b2cf4d9a47cf9adab084be0b476f 1cba5068ccf141ac89951e22bcacad6f--0a97b2cf4d9a47cf9adab084be0b476f 33aa46a0a62c4f5e94da2c76749b4f70 0a97b2cf4d9a47cf9adab084be0b476f--33aa46a0a62c4f5e94da2c76749b4f70 101eec0a08114ffb8e5fa31104ccb383 X 33aa46a0a62c4f5e94da2c76749b4f70--101eec0a08114ffb8e5fa31104ccb383 b4102da024c748be88e6a10c8d839725 101eec0a08114ffb8e5fa31104ccb383--b4102da024c748be88e6a10c8d839725 2f333bab415b4934b09da72a6a7bb2d6 b4102da024c748be88e6a10c8d839725--2f333bab415b4934b09da72a6a7bb2d6 4dfaafc277824b09a586107df2db7db7 X 2f333bab415b4934b09da72a6a7bb2d6--4dfaafc277824b09a586107df2db7db7 f869b0e6b9574d59bcf101723e6729d4 X 4dfaafc277824b09a586107df2db7db7--f869b0e6b9574d59bcf101723e6729d4 d34385e90f8e40fd92a3abcc324323c2 f869b0e6b9574d59bcf101723e6729d4--d34385e90f8e40fd92a3abcc324323c2 b9673e5da22d4c1a9bcad2011a5c4392 d34385e90f8e40fd92a3abcc324323c2--b9673e5da22d4c1a9bcad2011a5c4392 417e39bddb254666bf4726f2553591d3 X b9673e5da22d4c1a9bcad2011a5c4392--417e39bddb254666bf4726f2553591d3 417e39bddb254666bf4726f2553591d3--6870c4eafa3244b9a5ef4bea64d17697

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_bb8f3740967e40f49c2012e340d248f2 cluster_c6ef3081f3544da0b36402e2e0193e87 40cd6f5fa08f408bad91e42f3a826bbb 0 6671d4791e2641b997bdda1673d4aa81 X 40cd6f5fa08f408bad91e42f3a826bbb--6671d4791e2641b997bdda1673d4aa81 3791209ee7e546df8ab41fc3d457a09b 1 6aa06b2d592949dd9e13d31811865fdd HamEvo 6671d4791e2641b997bdda1673d4aa81--6aa06b2d592949dd9e13d31811865fdd 0dde2d7eb4944eed8ab1d40e3c0a2dfc X 6aa06b2d592949dd9e13d31811865fdd--0dde2d7eb4944eed8ab1d40e3c0a2dfc f6e9184976b448fbad891c317a69a50e 0dde2d7eb4944eed8ab1d40e3c0a2dfc--f6e9184976b448fbad891c317a69a50e 9387895f5275486aae56670bd6f565db HamEvo f6e9184976b448fbad891c317a69a50e--9387895f5275486aae56670bd6f565db f2e61e6f3a6747c39ef2c7692f5bb282 9387895f5275486aae56670bd6f565db--f2e61e6f3a6747c39ef2c7692f5bb282 734004f723db4feebc2271f2a50d6b8d f2e61e6f3a6747c39ef2c7692f5bb282--734004f723db4feebc2271f2a50d6b8d 16117b479cb143bc8fc14053a0c76663 e37094482ea243459a1cdd86baf4161e 3791209ee7e546df8ab41fc3d457a09b--e37094482ea243459a1cdd86baf4161e d5b6a235c86943e297afc72122d64c70 2 2f0443068570411cb0ddb02843866ccc t = -0.500 e37094482ea243459a1cdd86baf4161e--2f0443068570411cb0ddb02843866ccc 83f39928076044c782a0c92fa290d40d 2f0443068570411cb0ddb02843866ccc--83f39928076044c782a0c92fa290d40d 2512b93420c841f1ba19e0764b9df75a X 83f39928076044c782a0c92fa290d40d--2512b93420c841f1ba19e0764b9df75a 898b872982fe491fbcdbc1db4188ae80 t = -0.500 2512b93420c841f1ba19e0764b9df75a--898b872982fe491fbcdbc1db4188ae80 d3c48d84aad2494fb9a7c28555b4a7fd X 898b872982fe491fbcdbc1db4188ae80--d3c48d84aad2494fb9a7c28555b4a7fd d3c48d84aad2494fb9a7c28555b4a7fd--16117b479cb143bc8fc14053a0c76663 2fa821d6ac6a4b4fa0ac5538f66fb255 4da3c336890d4f6391515ee28d1d9878 X d5b6a235c86943e297afc72122d64c70--4da3c336890d4f6391515ee28d1d9878 700295c85cc94d2d8ee8f2c16cee8e12 4da3c336890d4f6391515ee28d1d9878--700295c85cc94d2d8ee8f2c16cee8e12 88d7e39f56ea4501b9f647967b1c67c0 X 700295c85cc94d2d8ee8f2c16cee8e12--88d7e39f56ea4501b9f647967b1c67c0 44ecdc3218bb4858989a35932dcf851b X 88d7e39f56ea4501b9f647967b1c67c0--44ecdc3218bb4858989a35932dcf851b a805fbde57c54307a0ba354672496e6c 44ecdc3218bb4858989a35932dcf851b--a805fbde57c54307a0ba354672496e6c ed28034e7b9346699edaa9c262ae98dc X a805fbde57c54307a0ba354672496e6c--ed28034e7b9346699edaa9c262ae98dc ed28034e7b9346699edaa9c262ae98dc--2fa821d6ac6a4b4fa0ac5538f66fb255

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_6db4d99a05bf44aa9fa4db214d962ff9 cluster_de7f75da50e14f448a9676e15783de82 6399add413e64109bc4a4f2089ba2cb9 0 d2e15c63bc274996b6e19166d6310a26 X 6399add413e64109bc4a4f2089ba2cb9--d2e15c63bc274996b6e19166d6310a26 1c84a1c38ca544bcad03336343fc39ad 1 ad6801940cea4b4fb88bff49c65db54a HamEvo d2e15c63bc274996b6e19166d6310a26--ad6801940cea4b4fb88bff49c65db54a 75c6ca0e9be3442cad66dcb465e77e01 X ad6801940cea4b4fb88bff49c65db54a--75c6ca0e9be3442cad66dcb465e77e01 d5ed5e3add3a4fc3bdebfb11e4720436 75c6ca0e9be3442cad66dcb465e77e01--d5ed5e3add3a4fc3bdebfb11e4720436 363e754da18842748ac6feea1df4241d HamEvo d5ed5e3add3a4fc3bdebfb11e4720436--363e754da18842748ac6feea1df4241d c691377042014f659d04b5f7bbde4dba 363e754da18842748ac6feea1df4241d--c691377042014f659d04b5f7bbde4dba e0468a2efd744b528965807eb88c9df0 c691377042014f659d04b5f7bbde4dba--e0468a2efd744b528965807eb88c9df0 bca1eb6f28584cd0947069d2d528cd59 b2eb1021f5fa4037910a56880b53c42d 1c84a1c38ca544bcad03336343fc39ad--b2eb1021f5fa4037910a56880b53c42d d332c91b1c95493c919bc5d89843ada5 2 92a68a609ed547e0803056d06c12becc t = -500.000000000000 b2eb1021f5fa4037910a56880b53c42d--92a68a609ed547e0803056d06c12becc 43d3117ad98746d48de68387e689652c 92a68a609ed547e0803056d06c12becc--43d3117ad98746d48de68387e689652c d82a202186f844c8aff684c26cb26241 X 43d3117ad98746d48de68387e689652c--d82a202186f844c8aff684c26cb26241 e42687ccd11b46c3b81825786f4ad387 t = -500.000000000000 d82a202186f844c8aff684c26cb26241--e42687ccd11b46c3b81825786f4ad387 6199b561274c465d854128c0aaa84240 X e42687ccd11b46c3b81825786f4ad387--6199b561274c465d854128c0aaa84240 6199b561274c465d854128c0aaa84240--bca1eb6f28584cd0947069d2d528cd59 695a3531df8a410da0bf3f1bfc4e4de1 4ec17a9391fc4d15b2b9608f0bd61f03 X d332c91b1c95493c919bc5d89843ada5--4ec17a9391fc4d15b2b9608f0bd61f03 ef06e96cb47548b8a0350d3673bc204c 4ec17a9391fc4d15b2b9608f0bd61f03--ef06e96cb47548b8a0350d3673bc204c 868cc13ff0b34443a222a4d77250c50c X ef06e96cb47548b8a0350d3673bc204c--868cc13ff0b34443a222a4d77250c50c 075b385458e44568922c8f44c2c51f2b X 868cc13ff0b34443a222a4d77250c50c--075b385458e44568922c8f44c2c51f2b 890493a05eff43eaaa7b07cff6a47a69 075b385458e44568922c8f44c2c51f2b--890493a05eff43eaaa7b07cff6a47a69 5be2fd00300c449bb6cd2bf76948d702 X 890493a05eff43eaaa7b07cff6a47a69--5be2fd00300c449bb6cd2bf76948d702 5be2fd00300c449bb6cd2bf76948d702--695a3531df8a410da0bf3f1bfc4e4de1

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