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_5c0ddf1edf70487e89ee10ff00f50926 cluster_62337d5e63914b5e8b51687c4555aef3 cluster_630dbeba38864c18be7abbae996c9530 cluster_2f24dc39541a46c88412adeb12f518e7 cluster_f6f76edf7fc0435ab7036bb05524265a cluster_67c1f10559bf4363aec707dedf62a4fb cluster_5584783e364840a290c67397493e710d 4beb5fc1c5274d149693a5022786aa0c 0 97c37914a36c47c1b49df410916efd7d HamEvo 4beb5fc1c5274d149693a5022786aa0c--97c37914a36c47c1b49df410916efd7d 6a5de2e557704f45b52f9775ca2ef099 1 853930aaa5de480bb4e4d50f2db3ac59 HamEvo 97c37914a36c47c1b49df410916efd7d--853930aaa5de480bb4e4d50f2db3ac59 d0791d0bc9a54756910bee119f93c95d HamEvo 853930aaa5de480bb4e4d50f2db3ac59--d0791d0bc9a54756910bee119f93c95d 700618139b7d419b874072a0aa757fbb X d0791d0bc9a54756910bee119f93c95d--700618139b7d419b874072a0aa757fbb 6e5c022371504177a79207b6dbbabba6 HamEvo 700618139b7d419b874072a0aa757fbb--6e5c022371504177a79207b6dbbabba6 2059c1ca3bf54ff2922b4907bc8aa677 HamEvo 6e5c022371504177a79207b6dbbabba6--2059c1ca3bf54ff2922b4907bc8aa677 a87cdc0e10574a5fab3a043946d75e21 X 2059c1ca3bf54ff2922b4907bc8aa677--a87cdc0e10574a5fab3a043946d75e21 42ea692eefb74accb191a494ccb59ad5 a87cdc0e10574a5fab3a043946d75e21--42ea692eefb74accb191a494ccb59ad5 b823000823ca4532b41ec9ee972844cb HamEvo 42ea692eefb74accb191a494ccb59ad5--b823000823ca4532b41ec9ee972844cb 5255d499f69d45dba2744a17f752e2c3 HamEvo b823000823ca4532b41ec9ee972844cb--5255d499f69d45dba2744a17f752e2c3 f25085e55b2d4a4c9d784caa075ee0b9 5255d499f69d45dba2744a17f752e2c3--f25085e55b2d4a4c9d784caa075ee0b9 581a40d6b063456ba187daea350603cf f25085e55b2d4a4c9d784caa075ee0b9--581a40d6b063456ba187daea350603cf 1663dd7bc0cf42b7bc215c428cf18147 a170e5c2b73540b697538e019049733c t = -3.14 6a5de2e557704f45b52f9775ca2ef099--a170e5c2b73540b697538e019049733c 85aa2435c25f421e88b8ec1d072cbec7 2 4fe9cd3e22f9448a9d28cb1d44388f5e t = 3.142 a170e5c2b73540b697538e019049733c--4fe9cd3e22f9448a9d28cb1d44388f5e f04209906e834d4fbd4491c946b4dcc3 t = -3.14 4fe9cd3e22f9448a9d28cb1d44388f5e--f04209906e834d4fbd4491c946b4dcc3 c6a47041a4cd412bbaa5a175d2d9cedc f04209906e834d4fbd4491c946b4dcc3--c6a47041a4cd412bbaa5a175d2d9cedc 04d3f4be8eba473f9fb47410c708c8ab t = 1.571 c6a47041a4cd412bbaa5a175d2d9cedc--04d3f4be8eba473f9fb47410c708c8ab 843ef50b99a24cff8e155f724a4c4ccd t = 1.571 04d3f4be8eba473f9fb47410c708c8ab--843ef50b99a24cff8e155f724a4c4ccd daa85fdf9f464dc1bea03d4960cf0bfa 843ef50b99a24cff8e155f724a4c4ccd--daa85fdf9f464dc1bea03d4960cf0bfa 8274f63a13b940149226ffaa1d2ecdff X daa85fdf9f464dc1bea03d4960cf0bfa--8274f63a13b940149226ffaa1d2ecdff 0deacec63687455b8db30e192f838889 t = 1.571 8274f63a13b940149226ffaa1d2ecdff--0deacec63687455b8db30e192f838889 2999e7dc6fda4c5288fafaf8afa93ea2 t = 1.571 0deacec63687455b8db30e192f838889--2999e7dc6fda4c5288fafaf8afa93ea2 37b16a20b486484cbb01d8038713e587 X 2999e7dc6fda4c5288fafaf8afa93ea2--37b16a20b486484cbb01d8038713e587 37b16a20b486484cbb01d8038713e587--1663dd7bc0cf42b7bc215c428cf18147 47f6d7e0e92f4aa08ff3c3575877cb4d 173bd896854347929820f4887912423a 85aa2435c25f421e88b8ec1d072cbec7--173bd896854347929820f4887912423a 063cff633ef943aeb174f7634ebdb8d1 173bd896854347929820f4887912423a--063cff633ef943aeb174f7634ebdb8d1 ca8acad6f61143e08fd99d1718e89f98 063cff633ef943aeb174f7634ebdb8d1--ca8acad6f61143e08fd99d1718e89f98 d03a5bfd3e794a349a46b851141833a8 X ca8acad6f61143e08fd99d1718e89f98--d03a5bfd3e794a349a46b851141833a8 f3caeb1586e44eeba301a60276672d48 d03a5bfd3e794a349a46b851141833a8--f3caeb1586e44eeba301a60276672d48 e79df9309bba40f3b5dfcaa415f0bf8f f3caeb1586e44eeba301a60276672d48--e79df9309bba40f3b5dfcaa415f0bf8f ecaf167e926049ce9383346f05c20fb4 X e79df9309bba40f3b5dfcaa415f0bf8f--ecaf167e926049ce9383346f05c20fb4 3bb595a9f24b43ab9c091e4afe973108 X ecaf167e926049ce9383346f05c20fb4--3bb595a9f24b43ab9c091e4afe973108 303ae884e5434037b05a69b2e13507cb 3bb595a9f24b43ab9c091e4afe973108--303ae884e5434037b05a69b2e13507cb f786812df7924160b99d1e2cbf02a5ce 303ae884e5434037b05a69b2e13507cb--f786812df7924160b99d1e2cbf02a5ce 93c4be4a6ea4465681b0dd1a0c428ea2 X f786812df7924160b99d1e2cbf02a5ce--93c4be4a6ea4465681b0dd1a0c428ea2 93c4be4a6ea4465681b0dd1a0c428ea2--47f6d7e0e92f4aa08ff3c3575877cb4d

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_3c64bc9f074b4ae780a3c3b00e582c85 cluster_f662fd6a14d1444292fdfdb26d90d4f3 87ce5d97875543779ea38c49b067a896 0 3828f173e7584cbcbdc25d3fec61b184 X 87ce5d97875543779ea38c49b067a896--3828f173e7584cbcbdc25d3fec61b184 cd7908800acb42c6b62e3d392463be1e 1 af95d553fd45452481923456f1d51b64 HamEvo 3828f173e7584cbcbdc25d3fec61b184--af95d553fd45452481923456f1d51b64 157eb6eeb277460fbe24252fa93b5289 X af95d553fd45452481923456f1d51b64--157eb6eeb277460fbe24252fa93b5289 018f3aad9437455bbfc5ad85843c3899 157eb6eeb277460fbe24252fa93b5289--018f3aad9437455bbfc5ad85843c3899 c62889f9953d4222b827eb18f16af250 HamEvo 018f3aad9437455bbfc5ad85843c3899--c62889f9953d4222b827eb18f16af250 aa49548edf7041078917211adb82d894 c62889f9953d4222b827eb18f16af250--aa49548edf7041078917211adb82d894 cbc97b6671964ba0ae1ddf991d539b3a aa49548edf7041078917211adb82d894--cbc97b6671964ba0ae1ddf991d539b3a f8fae5a6e925420e92022c6c1f78358d 10afd17c042449cf92138b63c8f23d6f cd7908800acb42c6b62e3d392463be1e--10afd17c042449cf92138b63c8f23d6f b4168e8ee7a64eefaffda823bcaf3f84 2 bc5631d37a64402d86c7b6d0a99dd820 t = -0.50 10afd17c042449cf92138b63c8f23d6f--bc5631d37a64402d86c7b6d0a99dd820 44515482e5d64bb0a826b98b5c1aae34 bc5631d37a64402d86c7b6d0a99dd820--44515482e5d64bb0a826b98b5c1aae34 ce4a3df5844442a095902afa2f1ee1e0 X 44515482e5d64bb0a826b98b5c1aae34--ce4a3df5844442a095902afa2f1ee1e0 ccba045a408d48fdbcf7ac261273f229 t = -0.50 ce4a3df5844442a095902afa2f1ee1e0--ccba045a408d48fdbcf7ac261273f229 e3f41f6cf5054fb5ab6d5dc8a6aea744 X ccba045a408d48fdbcf7ac261273f229--e3f41f6cf5054fb5ab6d5dc8a6aea744 e3f41f6cf5054fb5ab6d5dc8a6aea744--f8fae5a6e925420e92022c6c1f78358d 88da1d63640b4210865e2e7aa13adb9c 63d7b9365af34fd2b85647330dd23edb X b4168e8ee7a64eefaffda823bcaf3f84--63d7b9365af34fd2b85647330dd23edb d87a58e7be0e47d6bd2e9750f6a8dd5a 63d7b9365af34fd2b85647330dd23edb--d87a58e7be0e47d6bd2e9750f6a8dd5a 604e32610ba24e3892a0ee83032d40e2 X d87a58e7be0e47d6bd2e9750f6a8dd5a--604e32610ba24e3892a0ee83032d40e2 f5d363ab990a4405b94491795524726f X 604e32610ba24e3892a0ee83032d40e2--f5d363ab990a4405b94491795524726f 08c33f51ec6f4e318b54e0b33818a647 f5d363ab990a4405b94491795524726f--08c33f51ec6f4e318b54e0b33818a647 dff0a792d52e44a4b30584f3e2e2e5fb X 08c33f51ec6f4e318b54e0b33818a647--dff0a792d52e44a4b30584f3e2e2e5fb dff0a792d52e44a4b30584f3e2e2e5fb--88da1d63640b4210865e2e7aa13adb9c

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_4b937ca2b4f7497aa5265da9cbf3ee7c cluster_8176b5bca4b44754a2af3de33f1e2879 33b55d3689e14a2ea202add6b4a1ac33 0 5e0f7305d94c49b9ad3b455cdd396dfe X 33b55d3689e14a2ea202add6b4a1ac33--5e0f7305d94c49b9ad3b455cdd396dfe 87a8d4b07bed431ca64252e97310a094 1 ef474d8986b449f5a85d3606b0e2663d HamEvo 5e0f7305d94c49b9ad3b455cdd396dfe--ef474d8986b449f5a85d3606b0e2663d 33054284184f4fa18052c012b93d2c1c X ef474d8986b449f5a85d3606b0e2663d--33054284184f4fa18052c012b93d2c1c 5fcb86362c544a24ae2daba81c54e6c9 33054284184f4fa18052c012b93d2c1c--5fcb86362c544a24ae2daba81c54e6c9 446543b9cf24422199f3fe9036e98af5 HamEvo 5fcb86362c544a24ae2daba81c54e6c9--446543b9cf24422199f3fe9036e98af5 6ab375b292704c878784a7502fe399a7 446543b9cf24422199f3fe9036e98af5--6ab375b292704c878784a7502fe399a7 08fe97e7afe74134a619ddf3f4e61cc8 6ab375b292704c878784a7502fe399a7--08fe97e7afe74134a619ddf3f4e61cc8 49e7d5b543d74aa6aaa93e25702c25e2 c6d457aa37b340c9b746ea51521a24c1 87a8d4b07bed431ca64252e97310a094--c6d457aa37b340c9b746ea51521a24c1 d89575a138a94f51960eca36f96e4cb2 2 fb4924aabfa64829995330a838514698 t = -500. c6d457aa37b340c9b746ea51521a24c1--fb4924aabfa64829995330a838514698 974532ed22fe49058289a2ee491c9ba8 fb4924aabfa64829995330a838514698--974532ed22fe49058289a2ee491c9ba8 c58081f2fc024f9b97c4a6a8d640ef2d X 974532ed22fe49058289a2ee491c9ba8--c58081f2fc024f9b97c4a6a8d640ef2d a0b6e264b36c46b0953e650a67c94e01 t = -500. c58081f2fc024f9b97c4a6a8d640ef2d--a0b6e264b36c46b0953e650a67c94e01 2b7d8405b3da4456995b054b424a26fc X a0b6e264b36c46b0953e650a67c94e01--2b7d8405b3da4456995b054b424a26fc 2b7d8405b3da4456995b054b424a26fc--49e7d5b543d74aa6aaa93e25702c25e2 5405efa81ed241939fe8f930dc806221 cd4dfa286b5d4651bbc1f82724fe6807 X d89575a138a94f51960eca36f96e4cb2--cd4dfa286b5d4651bbc1f82724fe6807 95a2e73209784f788e246dcbf390de67 cd4dfa286b5d4651bbc1f82724fe6807--95a2e73209784f788e246dcbf390de67 201f0170fead497d84007721a2874ac7 X 95a2e73209784f788e246dcbf390de67--201f0170fead497d84007721a2874ac7 7a44536d8131464793ac3c2d75085ddc X 201f0170fead497d84007721a2874ac7--7a44536d8131464793ac3c2d75085ddc bc619f3efc4d4c5589681b36ae8b4245 7a44536d8131464793ac3c2d75085ddc--bc619f3efc4d4c5589681b36ae8b4245 ee4a90eeba5d418ea8122b3f95a2ff89 X bc619f3efc4d4c5589681b36ae8b4245--ee4a90eeba5d418ea8122b3f95a2ff89 ee4a90eeba5d418ea8122b3f95a2ff89--5405efa81ed241939fe8f930dc806221

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