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.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=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_a9f3f3a5e87f4688a6279f23c8e07afc cluster_c53acfaa35524e43a98cb5f35e95604e cluster_e037ff00a7bc41a9a1c1eca4be55fc78 cluster_c7c0b7aa248b4dc1819870af3ad26715 cluster_5295a5cb25d6428e9f321ee49fd75ec0 cluster_24b2c41399714cd6b2319ffa7775bdf7 cluster_66fd441447b144bdb9e1a133c6cced2e 7767da4e75ba49378390e8aa52cea501 0 70ecc6ba00544c61b4398fccda770e13 HamEvo 7767da4e75ba49378390e8aa52cea501--70ecc6ba00544c61b4398fccda770e13 f2479f1ed3d444ef91df4086eeed1596 1 5762464a975b442bb89b2b83c933a336 HamEvo 70ecc6ba00544c61b4398fccda770e13--5762464a975b442bb89b2b83c933a336 e88452cc057747df825e9e152573b57e HamEvo 5762464a975b442bb89b2b83c933a336--e88452cc057747df825e9e152573b57e b286d5c21eac41dda6415ff044caca5d X e88452cc057747df825e9e152573b57e--b286d5c21eac41dda6415ff044caca5d d5cd4c12e0e84c92977a34125057414d HamEvo b286d5c21eac41dda6415ff044caca5d--d5cd4c12e0e84c92977a34125057414d 773f4ffce88646f39bccfbcfceb9b788 HamEvo d5cd4c12e0e84c92977a34125057414d--773f4ffce88646f39bccfbcfceb9b788 9e01d4e416d24a7ca5c0e5210acc2d08 X 773f4ffce88646f39bccfbcfceb9b788--9e01d4e416d24a7ca5c0e5210acc2d08 cb379b81dac1431397d33626fc64c973 9e01d4e416d24a7ca5c0e5210acc2d08--cb379b81dac1431397d33626fc64c973 9d038401812540c29e1a1017179a39c9 HamEvo cb379b81dac1431397d33626fc64c973--9d038401812540c29e1a1017179a39c9 eebd24168e094e9cb63bb071abfdfa91 HamEvo 9d038401812540c29e1a1017179a39c9--eebd24168e094e9cb63bb071abfdfa91 77db9f2c53e64050ad6120ff878249ea eebd24168e094e9cb63bb071abfdfa91--77db9f2c53e64050ad6120ff878249ea 59188c616b4f4b9caa16980a0b5d9ac4 77db9f2c53e64050ad6120ff878249ea--59188c616b4f4b9caa16980a0b5d9ac4 6cca18e6c58c4ff29ea2166ff8b03a5d 23e3da830d9c4a91a4a440519d648872 t = -3.142 f2479f1ed3d444ef91df4086eeed1596--23e3da830d9c4a91a4a440519d648872 2e45471a915349498c9542d898c018ec 2 4219a7ccf4e846648a22133076c9d8ed t = 3.142 23e3da830d9c4a91a4a440519d648872--4219a7ccf4e846648a22133076c9d8ed 63c139a716e1438a8c916f8386bcdad9 t = -3.142 4219a7ccf4e846648a22133076c9d8ed--63c139a716e1438a8c916f8386bcdad9 a317d3e24fac4935aae3813c95a481c2 63c139a716e1438a8c916f8386bcdad9--a317d3e24fac4935aae3813c95a481c2 f508383fb6cd4ff6a3911e8beda60a8d t = 1.571 a317d3e24fac4935aae3813c95a481c2--f508383fb6cd4ff6a3911e8beda60a8d 71582ea505c042a3ab92883cdfa74e69 t = 1.571 f508383fb6cd4ff6a3911e8beda60a8d--71582ea505c042a3ab92883cdfa74e69 6ae4e68d4b424136bbf70d043744466f 71582ea505c042a3ab92883cdfa74e69--6ae4e68d4b424136bbf70d043744466f 2e4741e04dc34ecfbd5ed5c56c58e068 X 6ae4e68d4b424136bbf70d043744466f--2e4741e04dc34ecfbd5ed5c56c58e068 3120079ed9034d859b77c9549923605a t = 1.571 2e4741e04dc34ecfbd5ed5c56c58e068--3120079ed9034d859b77c9549923605a 72aaf13dff5c4a58b170ee558ff0904d t = 1.571 3120079ed9034d859b77c9549923605a--72aaf13dff5c4a58b170ee558ff0904d 8d54e593b6074034a6f6ef84aa9370c4 X 72aaf13dff5c4a58b170ee558ff0904d--8d54e593b6074034a6f6ef84aa9370c4 8d54e593b6074034a6f6ef84aa9370c4--6cca18e6c58c4ff29ea2166ff8b03a5d 4d7d12a66cd84373bd35e183b9c8ca4c ad96b633ad0a428fb4e2f31e0fcff6c8 2e45471a915349498c9542d898c018ec--ad96b633ad0a428fb4e2f31e0fcff6c8 0f9ba027c6ad4146b507c1029089c9ed ad96b633ad0a428fb4e2f31e0fcff6c8--0f9ba027c6ad4146b507c1029089c9ed 93c9714ec91c4b499697c26f3b5dcd3f 0f9ba027c6ad4146b507c1029089c9ed--93c9714ec91c4b499697c26f3b5dcd3f 325c507d11434a559303eed4cd078ed9 X 93c9714ec91c4b499697c26f3b5dcd3f--325c507d11434a559303eed4cd078ed9 375e09acde81439b97bdf0cff582e6a5 325c507d11434a559303eed4cd078ed9--375e09acde81439b97bdf0cff582e6a5 7c2210f6057b49459cd443c50c12effa 375e09acde81439b97bdf0cff582e6a5--7c2210f6057b49459cd443c50c12effa eac36e0476154aadb533548207863f5e X 7c2210f6057b49459cd443c50c12effa--eac36e0476154aadb533548207863f5e 71b3b0038c724e38a504ef7f53154fc5 X eac36e0476154aadb533548207863f5e--71b3b0038c724e38a504ef7f53154fc5 e42ac9997712492984fe654f746bdd23 71b3b0038c724e38a504ef7f53154fc5--e42ac9997712492984fe654f746bdd23 67b43874cf7242a2a4fe71fb5e10b75e e42ac9997712492984fe654f746bdd23--67b43874cf7242a2a4fe71fb5e10b75e d124723517db4e58b0c208c50429ca11 X 67b43874cf7242a2a4fe71fb5e10b75e--d124723517db4e58b0c208c50429ca11 d124723517db4e58b0c208c50429ca11--4d7d12a66cd84373bd35e183b9c8ca4c

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_93859e33028e458db1c5901434f8b077 cluster_b0d9d91b4cdf4b1b94bccf5950f1b3e9 1a3ed4a609dd4cbba68d28eefab3d62e 0 6429553c8783426aace378c2c1862a54 X 1a3ed4a609dd4cbba68d28eefab3d62e--6429553c8783426aace378c2c1862a54 2e5662c6c625415bb4c06781d4d13ce4 1 3ca9505c2d9444a08ee2a92bfde6081b HamEvo 6429553c8783426aace378c2c1862a54--3ca9505c2d9444a08ee2a92bfde6081b 67c52cdb33364fcf9117f102a27a8500 X 3ca9505c2d9444a08ee2a92bfde6081b--67c52cdb33364fcf9117f102a27a8500 0910dd6804c048ad8ec880e9ea642d45 67c52cdb33364fcf9117f102a27a8500--0910dd6804c048ad8ec880e9ea642d45 35f35be477654f3e9a5b65de329c083a HamEvo 0910dd6804c048ad8ec880e9ea642d45--35f35be477654f3e9a5b65de329c083a 70f95eb0b7f842ad92e3263d132a1965 35f35be477654f3e9a5b65de329c083a--70f95eb0b7f842ad92e3263d132a1965 e15a385e92d14a03a2de36326ac1528f 70f95eb0b7f842ad92e3263d132a1965--e15a385e92d14a03a2de36326ac1528f 3abb91810b8a48adb92e24753f856a7e 5f611637500840fd829443d8a106da2a 2e5662c6c625415bb4c06781d4d13ce4--5f611637500840fd829443d8a106da2a 58728d393b964ae3b16b3aa06bec87b9 2 6ba798b32d264cc289646f0f6e5946b3 t = -0.500 5f611637500840fd829443d8a106da2a--6ba798b32d264cc289646f0f6e5946b3 c47750343d6e49b2a7778bdf1293f1e5 6ba798b32d264cc289646f0f6e5946b3--c47750343d6e49b2a7778bdf1293f1e5 41715ee1368e4ce3b5fd5e3909b35c4c X c47750343d6e49b2a7778bdf1293f1e5--41715ee1368e4ce3b5fd5e3909b35c4c 9e3df3ee5df54b87afcf7706ac5a6049 t = -0.500 41715ee1368e4ce3b5fd5e3909b35c4c--9e3df3ee5df54b87afcf7706ac5a6049 146973bd25984d02bf8a8c16b71ffed5 X 9e3df3ee5df54b87afcf7706ac5a6049--146973bd25984d02bf8a8c16b71ffed5 146973bd25984d02bf8a8c16b71ffed5--3abb91810b8a48adb92e24753f856a7e 73d169ae65a9459f9abd7f61ce2f8202 8faac5354a134ca795bc61437e4f5b96 X 58728d393b964ae3b16b3aa06bec87b9--8faac5354a134ca795bc61437e4f5b96 0e775dfc3a2c41a483cf6b688a95b6b5 8faac5354a134ca795bc61437e4f5b96--0e775dfc3a2c41a483cf6b688a95b6b5 3ba5af293bc345db9c33ef59c3c5412b X 0e775dfc3a2c41a483cf6b688a95b6b5--3ba5af293bc345db9c33ef59c3c5412b 4ffb3ff6b8404042ac258de696560c4a X 3ba5af293bc345db9c33ef59c3c5412b--4ffb3ff6b8404042ac258de696560c4a 5bd9c569ad7a42e6bc9e45ee54393c12 4ffb3ff6b8404042ac258de696560c4a--5bd9c569ad7a42e6bc9e45ee54393c12 8cbc97d747094f679e8655d91b855b82 X 5bd9c569ad7a42e6bc9e45ee54393c12--8cbc97d747094f679e8655d91b855b82 8cbc97d747094f679e8655d91b855b82--73d169ae65a9459f9abd7f61ce2f8202

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_987056f94bce4b3a9b994cc6b37c23b3 cluster_fc4ae28bb898429c9543ff75f36876db f315dc06754c4432a8252bf1c807292a 0 75452384fc00495f9a736d0682a3fc19 X f315dc06754c4432a8252bf1c807292a--75452384fc00495f9a736d0682a3fc19 e5247a08af7640008300c17fd9559097 1 5891660be0a343a884da5d893c870c1f HamEvo 75452384fc00495f9a736d0682a3fc19--5891660be0a343a884da5d893c870c1f 89e198b26e154036bf09132a6b8df7a1 X 5891660be0a343a884da5d893c870c1f--89e198b26e154036bf09132a6b8df7a1 61806d913c0145aab7ca2d70293f3e7b 89e198b26e154036bf09132a6b8df7a1--61806d913c0145aab7ca2d70293f3e7b b3184ae39baa4a4b85f38d7d787cd549 HamEvo 61806d913c0145aab7ca2d70293f3e7b--b3184ae39baa4a4b85f38d7d787cd549 aacc0fceecea40ea922da78ad2b0e0a8 b3184ae39baa4a4b85f38d7d787cd549--aacc0fceecea40ea922da78ad2b0e0a8 d9549c0b3ce34161b05ee476df2a8a2f aacc0fceecea40ea922da78ad2b0e0a8--d9549c0b3ce34161b05ee476df2a8a2f 7fc3a7ba0edb473fab5fedd8a70dd34e 926d4b4c9dc54184b60f9de256817f58 e5247a08af7640008300c17fd9559097--926d4b4c9dc54184b60f9de256817f58 3981f979336e4dbea0038080f6cb5646 2 33387b9c96b34a6ea9cd46ac9d13ddc1 t = -500.000000000000 926d4b4c9dc54184b60f9de256817f58--33387b9c96b34a6ea9cd46ac9d13ddc1 bbfe5a93b81f4b7593ef92640ce7af7d 33387b9c96b34a6ea9cd46ac9d13ddc1--bbfe5a93b81f4b7593ef92640ce7af7d 46b2584857d042518b5cb6e2f0d54244 X bbfe5a93b81f4b7593ef92640ce7af7d--46b2584857d042518b5cb6e2f0d54244 5a7fa3e70e2b4b1bb91fef60ad4dbb00 t = -500.000000000000 46b2584857d042518b5cb6e2f0d54244--5a7fa3e70e2b4b1bb91fef60ad4dbb00 8cb10678f32a4cdb936d66e63187875b X 5a7fa3e70e2b4b1bb91fef60ad4dbb00--8cb10678f32a4cdb936d66e63187875b 8cb10678f32a4cdb936d66e63187875b--7fc3a7ba0edb473fab5fedd8a70dd34e c99d2948273249989c41f9618ed8b54b e1e0eca8d8534127a3694e67b0f33ccc X 3981f979336e4dbea0038080f6cb5646--e1e0eca8d8534127a3694e67b0f33ccc 4c409ac1e796427083f67a4bdae45bc8 e1e0eca8d8534127a3694e67b0f33ccc--4c409ac1e796427083f67a4bdae45bc8 33b1c714d6d945fcb1b248d6037e1f44 X 4c409ac1e796427083f67a4bdae45bc8--33b1c714d6d945fcb1b248d6037e1f44 bb72a5b03d0f4e25b80e02c9b985f9a8 X 33b1c714d6d945fcb1b248d6037e1f44--bb72a5b03d0f4e25b80e02c9b985f9a8 dcecfdb8778943cf8a8893e8a71929ca bb72a5b03d0f4e25b80e02c9b985f9a8--dcecfdb8778943cf8a8893e8a71929ca 170e8e094aae49b09d03c77eebb3cbc3 X dcecfdb8778943cf8a8893e8a71929ca--170e8e094aae49b09d03c77eebb3cbc3 170e8e094aae49b09d03c77eebb3cbc3--c99d2948273249989c41f9618ed8b54b

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