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 = [OrderedCounter({'11': 100})]
sample from decomposed CNOT gate and 100 shots = [OrderedCounter({'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 = [OrderedCounter({'11': 100})]
sample cnot_evo = [OrderedCounter({'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_ee98ccbd225f4a59900c7a95c467127e cluster_9f3b577981e64d4abdce5576c0517d12 cluster_157e3fa8611643e7b546aeb1cbe17f67 cluster_4c7adc2ae16a4e0d8e9cf10639e5a857 cluster_6caaa85588f241bd97aff11d1f5fe938 cluster_7f8fca104c144173adbbec650f05649c cluster_08cf9c0f19a9433792e9595ff6cb4cd3 8afcd69fd9774295b8552c84fe928456 0 4dab6ffd8732482dae1e4cd0e936a0a9 HamEvo 8afcd69fd9774295b8552c84fe928456--4dab6ffd8732482dae1e4cd0e936a0a9 df51e50d32694955a73295c2ae6f65cf 1 d2f673cedda9470c91e36d7988c6e127 HamEvo 4dab6ffd8732482dae1e4cd0e936a0a9--d2f673cedda9470c91e36d7988c6e127 106d3b80c9b54f6b9b7b6ffe990c1b51 HamEvo d2f673cedda9470c91e36d7988c6e127--106d3b80c9b54f6b9b7b6ffe990c1b51 ac015cc14e30429d8de54c0e3d67ce71 X 106d3b80c9b54f6b9b7b6ffe990c1b51--ac015cc14e30429d8de54c0e3d67ce71 589fa191ad0140a3bd54fae3996565e2 HamEvo ac015cc14e30429d8de54c0e3d67ce71--589fa191ad0140a3bd54fae3996565e2 ab9647fc497d4591886f8658fca9cfda HamEvo 589fa191ad0140a3bd54fae3996565e2--ab9647fc497d4591886f8658fca9cfda 8e227db91b2344289d03e94902cac934 X ab9647fc497d4591886f8658fca9cfda--8e227db91b2344289d03e94902cac934 590f0cab44574633b6d25570fe7acd50 8e227db91b2344289d03e94902cac934--590f0cab44574633b6d25570fe7acd50 8ef64808efa848f0b4de5bfde8564249 HamEvo 590f0cab44574633b6d25570fe7acd50--8ef64808efa848f0b4de5bfde8564249 bf029057965b473fbe7d4303936a9c8d HamEvo 8ef64808efa848f0b4de5bfde8564249--bf029057965b473fbe7d4303936a9c8d bfcee75f120344fb9981006ccbf6dd87 bf029057965b473fbe7d4303936a9c8d--bfcee75f120344fb9981006ccbf6dd87 16288eabc77e4af5b8e9ed249099e375 bfcee75f120344fb9981006ccbf6dd87--16288eabc77e4af5b8e9ed249099e375 ca349293cc254206849f99f584ff7a98 aac4dac1b5db491680b5fcedf181b150 t = -3.14 df51e50d32694955a73295c2ae6f65cf--aac4dac1b5db491680b5fcedf181b150 dd5f7c8e0f6d4bc5a9628af18eab7f32 2 173ceb4c8faa4a11abbe94c32e1213aa t = 3.142 aac4dac1b5db491680b5fcedf181b150--173ceb4c8faa4a11abbe94c32e1213aa 96bacfc7a3514aa69d1ad30b85af35c8 t = -3.14 173ceb4c8faa4a11abbe94c32e1213aa--96bacfc7a3514aa69d1ad30b85af35c8 2975bc0b207846249dec17b7b2a487ea 96bacfc7a3514aa69d1ad30b85af35c8--2975bc0b207846249dec17b7b2a487ea caf604910b524644b6f488bbc2bc0ba6 t = 1.571 2975bc0b207846249dec17b7b2a487ea--caf604910b524644b6f488bbc2bc0ba6 7ec7e32d02604db893f189d16ffdf7ec t = 1.571 caf604910b524644b6f488bbc2bc0ba6--7ec7e32d02604db893f189d16ffdf7ec 6ebc70defa1545a89b9d740e23372953 7ec7e32d02604db893f189d16ffdf7ec--6ebc70defa1545a89b9d740e23372953 01d976434acf4d5286dc066b965e1b29 X 6ebc70defa1545a89b9d740e23372953--01d976434acf4d5286dc066b965e1b29 9809a747ed0e4d8e9e9b7096f30180c3 t = 1.571 01d976434acf4d5286dc066b965e1b29--9809a747ed0e4d8e9e9b7096f30180c3 1d11bb1e46674fc8b22783e85ee77267 t = 1.571 9809a747ed0e4d8e9e9b7096f30180c3--1d11bb1e46674fc8b22783e85ee77267 1455d69821d9453d8e1ade633340c3f2 X 1d11bb1e46674fc8b22783e85ee77267--1455d69821d9453d8e1ade633340c3f2 1455d69821d9453d8e1ade633340c3f2--ca349293cc254206849f99f584ff7a98 94bc2e240c134ee9b64641f91a66a34d f42ce20a7fb54a76bda60e28153c1844 dd5f7c8e0f6d4bc5a9628af18eab7f32--f42ce20a7fb54a76bda60e28153c1844 c1c839637cbf4179bedf008fc6178d28 f42ce20a7fb54a76bda60e28153c1844--c1c839637cbf4179bedf008fc6178d28 908acc455c1d46dab8d8d8e6979dce30 c1c839637cbf4179bedf008fc6178d28--908acc455c1d46dab8d8d8e6979dce30 8a8bb67140be4e3794decab9b8aa61e6 X 908acc455c1d46dab8d8d8e6979dce30--8a8bb67140be4e3794decab9b8aa61e6 a6e2e300a62a4c129150ea34088652e1 8a8bb67140be4e3794decab9b8aa61e6--a6e2e300a62a4c129150ea34088652e1 bd6cc7a817ad43edaeaf9e2074229add a6e2e300a62a4c129150ea34088652e1--bd6cc7a817ad43edaeaf9e2074229add 6013d1171a4d4206b0acb2457ab62949 X bd6cc7a817ad43edaeaf9e2074229add--6013d1171a4d4206b0acb2457ab62949 48684c9661564eb8bacc8fe59822e57a X 6013d1171a4d4206b0acb2457ab62949--48684c9661564eb8bacc8fe59822e57a 6dc40cf287d343439313cfcdd37d6106 48684c9661564eb8bacc8fe59822e57a--6dc40cf287d343439313cfcdd37d6106 af026582289144cb8d768a93b552fc74 6dc40cf287d343439313cfcdd37d6106--af026582289144cb8d768a93b552fc74 72c1ba691fbc4972b91cfa220de2b574 X af026582289144cb8d768a93b552fc74--72c1ba691fbc4972b91cfa220de2b574 72c1ba691fbc4972b91cfa220de2b574--94bc2e240c134ee9b64641f91a66a34d

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 = [OrderedCounter({'111': 100})]
sample cnot_dacq = [OrderedCounter({'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_80dd1dcee7f84bcb82caf65cfed12be9 cluster_4a86c450aa8d4e58bcb9eb0b2877983e 912d951f16fe4702a8507ce3a745afb7 0 2d8c7d0381934d42a5365a8488fe65ec X 912d951f16fe4702a8507ce3a745afb7--2d8c7d0381934d42a5365a8488fe65ec 4a81d99df1424399bcfc8404e4027878 1 fe8da295a8fb4605a65db708ab7f62c2 HamEvo 2d8c7d0381934d42a5365a8488fe65ec--fe8da295a8fb4605a65db708ab7f62c2 6703d8d40fb9471593884164dfee5bf7 X fe8da295a8fb4605a65db708ab7f62c2--6703d8d40fb9471593884164dfee5bf7 70606bd5b434436bbe78ef091c335904 6703d8d40fb9471593884164dfee5bf7--70606bd5b434436bbe78ef091c335904 09f78519d46b4461ba63c26b6dc61233 HamEvo 70606bd5b434436bbe78ef091c335904--09f78519d46b4461ba63c26b6dc61233 98be55fd0bab46bcba582233f483b006 09f78519d46b4461ba63c26b6dc61233--98be55fd0bab46bcba582233f483b006 32338bdde0a040e796bc511ba4c4587c 98be55fd0bab46bcba582233f483b006--32338bdde0a040e796bc511ba4c4587c 1fce1e5aca59437bbbf361189ee31974 de6ab620fa9440fab2daa0f9dc2e5b1f 4a81d99df1424399bcfc8404e4027878--de6ab620fa9440fab2daa0f9dc2e5b1f 92a505e77b144941b3a8a1c5f5773b24 2 5136826b9455451a85dc876736f0559d t = -0.50 de6ab620fa9440fab2daa0f9dc2e5b1f--5136826b9455451a85dc876736f0559d 1b5b67f9629e45b68b9ac5be283cc0a8 5136826b9455451a85dc876736f0559d--1b5b67f9629e45b68b9ac5be283cc0a8 80b86722b2414b31b0a6e24bb114dca0 X 1b5b67f9629e45b68b9ac5be283cc0a8--80b86722b2414b31b0a6e24bb114dca0 0a5bf795c8ed40599e68e4ae2acadeb0 t = -0.50 80b86722b2414b31b0a6e24bb114dca0--0a5bf795c8ed40599e68e4ae2acadeb0 25ab772462cb4cc68debc53f9b5bcdab X 0a5bf795c8ed40599e68e4ae2acadeb0--25ab772462cb4cc68debc53f9b5bcdab 25ab772462cb4cc68debc53f9b5bcdab--1fce1e5aca59437bbbf361189ee31974 f66350ecd3974b2c9064f84107c0c459 9e9dbdb840ac4f61a9a5b204f2daf46c X 92a505e77b144941b3a8a1c5f5773b24--9e9dbdb840ac4f61a9a5b204f2daf46c 745c648d73cb4fc0b0640835c408c0ae 9e9dbdb840ac4f61a9a5b204f2daf46c--745c648d73cb4fc0b0640835c408c0ae d391a033ae0d4e05baad88ce61c3b69b X 745c648d73cb4fc0b0640835c408c0ae--d391a033ae0d4e05baad88ce61c3b69b 2b3253a63557496283aeb0f3b87ba7e0 X d391a033ae0d4e05baad88ce61c3b69b--2b3253a63557496283aeb0f3b87ba7e0 68eed2ee65714006b729710b5033641e 2b3253a63557496283aeb0f3b87ba7e0--68eed2ee65714006b729710b5033641e 6175af373f0845758427772cdff189b3 X 68eed2ee65714006b729710b5033641e--6175af373f0845758427772cdff189b3 6175af373f0845758427772cdff189b3--f66350ecd3974b2c9064f84107c0c459

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_d46566d219724c89933340c656b88d45 cluster_8e73f36fba024f17a8f6a7217c39e55d 5d063d5eba714e62b86456a8499ebe74 0 753dfba418ba49c7998dd596cfc05241 X 5d063d5eba714e62b86456a8499ebe74--753dfba418ba49c7998dd596cfc05241 6a9b6dc84d414b63abed55b14c9a552e 1 70f4ac8214614dfcaaa3281a087db3fe HamEvo 753dfba418ba49c7998dd596cfc05241--70f4ac8214614dfcaaa3281a087db3fe a66b008f11e842789aabfe702b47233c X 70f4ac8214614dfcaaa3281a087db3fe--a66b008f11e842789aabfe702b47233c 0be0187db27d40948c241c04ae1fac5d a66b008f11e842789aabfe702b47233c--0be0187db27d40948c241c04ae1fac5d bfbdfe9068c3486c8442b7850bc32ca9 HamEvo 0be0187db27d40948c241c04ae1fac5d--bfbdfe9068c3486c8442b7850bc32ca9 88e0d326d743427196ff269487d61637 bfbdfe9068c3486c8442b7850bc32ca9--88e0d326d743427196ff269487d61637 c66efdf78775463290041b888872c7aa 88e0d326d743427196ff269487d61637--c66efdf78775463290041b888872c7aa 5d66044a82084034a7fc8b06aff86dc2 d495823b78b64620a6f48a78b746c83e 6a9b6dc84d414b63abed55b14c9a552e--d495823b78b64620a6f48a78b746c83e 0dc72fb48e374371a192ec07a580536d 2 94141d876d754aef9d6acca67d340404 t = -500. d495823b78b64620a6f48a78b746c83e--94141d876d754aef9d6acca67d340404 55ea222fc6804711a4832c1c213f8aed 94141d876d754aef9d6acca67d340404--55ea222fc6804711a4832c1c213f8aed cead30915eb0491881799c6b1113e62c X 55ea222fc6804711a4832c1c213f8aed--cead30915eb0491881799c6b1113e62c e103db8d7e6343b7bf689c0e987d6b84 t = -500. cead30915eb0491881799c6b1113e62c--e103db8d7e6343b7bf689c0e987d6b84 134e75ec705246e991a581527622503d X e103db8d7e6343b7bf689c0e987d6b84--134e75ec705246e991a581527622503d 134e75ec705246e991a581527622503d--5d66044a82084034a7fc8b06aff86dc2 e3619245f7a54b63855b2cee2e709801 a9e07945ea5541788227a0a043b144ce X 0dc72fb48e374371a192ec07a580536d--a9e07945ea5541788227a0a043b144ce c6c51570de8b44f5ab9d3f260c38a206 a9e07945ea5541788227a0a043b144ce--c6c51570de8b44f5ab9d3f260c38a206 fd00f8894ff94009bf17e9a3517686db X c6c51570de8b44f5ab9d3f260c38a206--fd00f8894ff94009bf17e9a3517686db b99cb3a07a27471a8674f91a8db26d23 X fd00f8894ff94009bf17e9a3517686db--b99cb3a07a27471a8674f91a8db26d23 b50f6dafc3664fe3845c1710335ff632 b99cb3a07a27471a8674f91a8db26d23--b50f6dafc3664fe3845c1710335ff632 a8de0ec892ca472ca1b6f5a86d5b6321 X b50f6dafc3664fe3845c1710335ff632--a8de0ec892ca472ca1b6f5a86d5b6321 a8de0ec892ca472ca1b6f5a86d5b6321--e3619245f7a54b63855b2cee2e709801

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