Fitting a function with a Hamiltonian ansatz
In the analog QCL tutorial we used analog blocks to learn a function of interest. The analog blocks are a direct abstraction of device execution with global addressing. However, we may want to directly program an Hamiltonian-level ansatz to have a finer control on our model. In Qadence this can easily be done through digital-analog programs. In this tutorial we will solve a simple QCL problem with this approach.
Setting up the problem
The example problem considered is to fit a function of interest in a specified range. Below we define and plot the function \(f(x)=x^5\) .
import torch
import matplotlib.pyplot as plt
# Function to fit:
def f ( x ):
return x ** 5
xmin = - 1.0
xmax = 1.0
n_test = 100
x_test = torch . linspace ( xmin , xmax , steps = n_test )
y_test = f ( x_test )
plt . plot ( x_test , y_test )
plt . xlim (( - 1.1 , 1.1 ))
plt . ylim (( - 1.1 , 1.1 ))
2025-05-26T11:54:27.233702
image/svg+xml
Matplotlib v3.10.3, https://matplotlib.org/
Digital-Analog Ansatz
We start by defining the register of qubits. The topology we use now will define the interactions in the entangling Hamiltonian. As an example, we can define a rectangular lattice with 6 qubits.
from qadence import Register
reg = Register . rectangular_lattice (
qubits_row = 3 ,
qubits_col = 2 ,
)
Inspired by the Ising interaction mode of Rydberg atoms, we can now define an interaction Hamiltonian as \(\mathcal{H}_{ij}=\frac{1}{r_{ij}^6}N_iN_j\) , where \(N_i=(1/2)(I_i-Z_i)\) is the number operator and and \(r_{ij}\) is the distance between qubits \(i\) and \(j\) . We can easily instatiate this interaction Hamiltonian from the register information :
from qadence import N , add
def h_ij ( i : int , j : int ):
return N ( i ) @N ( j )
h_int = add ( h_ij ( * edge ) / r ** 6 for edge , r in reg . edge_distances . items ())
To build the digital-analog ansatz we can make use of the standard hea
function by specifying we want to use the Strategy.SDAQC
and passing the Hamiltonian we created as the entangler, as see in the QML constructors tutorial . The entangling operation will be replaced by the evolution of this Hamiltonian HamEvo(h_int, t)
, where the time parameter t
is considered to be a variational parameter at each layer.
from qadence import hea , Strategy , RX , RY
depth = 2
da_ansatz = hea (
n_qubits = reg . n_qubits ,
depth = depth ,
operations = [ RX , RY , RX ],
entangler = h_int ,
strategy = Strategy . SDAQC ,
)
print ( html_string ( da_ansatz ))
%3
cluster_ca81672ba2aa45df801d71f8820e78c6
cluster_8cbe0955deb944e19fd9264e67fecf51
2701af28b0064a5384400d3a30b26cc2
0
e979c5492ecd4b54a4f8147f7e22ed4c
RX(theta₀)
2701af28b0064a5384400d3a30b26cc2--e979c5492ecd4b54a4f8147f7e22ed4c
d2bd206109a54383a60d7fc7199ddba9
1
20e996a313564647b7ea533023b77e32
RY(theta₆)
e979c5492ecd4b54a4f8147f7e22ed4c--20e996a313564647b7ea533023b77e32
c23eb00f7844458ead4c732524fc2a8d
RX(theta₁₂)
20e996a313564647b7ea533023b77e32--c23eb00f7844458ead4c732524fc2a8d
068bf29d550d4fb699e24b4405fb93f3
c23eb00f7844458ead4c732524fc2a8d--068bf29d550d4fb699e24b4405fb93f3
ded76728226d4fefbf8b4d1760b7b009
RX(theta₁₈)
068bf29d550d4fb699e24b4405fb93f3--ded76728226d4fefbf8b4d1760b7b009
7c429b5e464c45469f556b5d368e2623
RY(theta₂₄)
ded76728226d4fefbf8b4d1760b7b009--7c429b5e464c45469f556b5d368e2623
45518f88580c4085b9f9ba04f4a6e2dd
RX(theta₃₀)
7c429b5e464c45469f556b5d368e2623--45518f88580c4085b9f9ba04f4a6e2dd
299d5c5077ec49e4a0edceae90bdfa00
45518f88580c4085b9f9ba04f4a6e2dd--299d5c5077ec49e4a0edceae90bdfa00
5365676b4c2c4d5d9562cb0652d1d624
299d5c5077ec49e4a0edceae90bdfa00--5365676b4c2c4d5d9562cb0652d1d624
952ba115508f40f1ad6f4d977fde1373
ce6f69f8c12a432a9fa77ef2da7b1472
RX(theta₁)
d2bd206109a54383a60d7fc7199ddba9--ce6f69f8c12a432a9fa77ef2da7b1472
1f02401d2cdc4d32b256ae63f5a93d19
2
3dbfca7c616e48a580aa5160128d0b79
RY(theta₇)
ce6f69f8c12a432a9fa77ef2da7b1472--3dbfca7c616e48a580aa5160128d0b79
3a50483c377048eca62b3e73e16f8d80
RX(theta₁₃)
3dbfca7c616e48a580aa5160128d0b79--3a50483c377048eca62b3e73e16f8d80
cc69fbba3cc2422eac0ee488a401ba4b
3a50483c377048eca62b3e73e16f8d80--cc69fbba3cc2422eac0ee488a401ba4b
fb267e0f224e4c18a710ff0e6be54b0c
RX(theta₁₉)
cc69fbba3cc2422eac0ee488a401ba4b--fb267e0f224e4c18a710ff0e6be54b0c
502a9975b0d64661a6450ef8c7f56d81
RY(theta₂₅)
fb267e0f224e4c18a710ff0e6be54b0c--502a9975b0d64661a6450ef8c7f56d81
39c71969581f4b8a94395aa61e0219d2
RX(theta₃₁)
502a9975b0d64661a6450ef8c7f56d81--39c71969581f4b8a94395aa61e0219d2
0739299dc222477eb60c480cc91346dd
39c71969581f4b8a94395aa61e0219d2--0739299dc222477eb60c480cc91346dd
0739299dc222477eb60c480cc91346dd--952ba115508f40f1ad6f4d977fde1373
fc222e771bf5440398726fc94757699f
89226113074849fa93864e9bcc62ebd3
RX(theta₂)
1f02401d2cdc4d32b256ae63f5a93d19--89226113074849fa93864e9bcc62ebd3
71bf03d3abdc4587a8e8abc4ca05d788
3
c71ccbf5fe7747d19f49072c15e88f04
RY(theta₈)
89226113074849fa93864e9bcc62ebd3--c71ccbf5fe7747d19f49072c15e88f04
c7fb12a1e1a74bb3a6d9ac3316eee627
RX(theta₁₄)
c71ccbf5fe7747d19f49072c15e88f04--c7fb12a1e1a74bb3a6d9ac3316eee627
b29dd1779a3c45dbbc1031e7b8ea7c5e
HamEvo
c7fb12a1e1a74bb3a6d9ac3316eee627--b29dd1779a3c45dbbc1031e7b8ea7c5e
1f87717d635549d891f74b0f7e95f62d
RX(theta₂₀)
b29dd1779a3c45dbbc1031e7b8ea7c5e--1f87717d635549d891f74b0f7e95f62d
1e98925093da465698102ccbe62eb023
RY(theta₂₆)
1f87717d635549d891f74b0f7e95f62d--1e98925093da465698102ccbe62eb023
ce748a4f712d46edb83028aefaae57df
RX(theta₃₂)
1e98925093da465698102ccbe62eb023--ce748a4f712d46edb83028aefaae57df
3ceb5a3751ed4d79ba7654bf8abf4efe
HamEvo
ce748a4f712d46edb83028aefaae57df--3ceb5a3751ed4d79ba7654bf8abf4efe
3ceb5a3751ed4d79ba7654bf8abf4efe--fc222e771bf5440398726fc94757699f
b2b27600ae404638bb2bfffb7f718630
80e6194dc5744a25a4844be0ef1e7b7e
RX(theta₃)
71bf03d3abdc4587a8e8abc4ca05d788--80e6194dc5744a25a4844be0ef1e7b7e
4e8808b405ed4eea9c4125fec877710c
4
0755516f8241407a85976ee6ce4dcded
RY(theta₉)
80e6194dc5744a25a4844be0ef1e7b7e--0755516f8241407a85976ee6ce4dcded
0f81f1e4dd524bab9e55cf4b3f0d9cd6
RX(theta₁₅)
0755516f8241407a85976ee6ce4dcded--0f81f1e4dd524bab9e55cf4b3f0d9cd6
deb35613eada4a00952f510559f638ee
t = theta_t₀
0f81f1e4dd524bab9e55cf4b3f0d9cd6--deb35613eada4a00952f510559f638ee
e239a3d3f3544bc385a43c5ed6ec104e
RX(theta₂₁)
deb35613eada4a00952f510559f638ee--e239a3d3f3544bc385a43c5ed6ec104e
d2f357912bb34afda6a40a94b9fdbbb5
RY(theta₂₇)
e239a3d3f3544bc385a43c5ed6ec104e--d2f357912bb34afda6a40a94b9fdbbb5
eb0562640a184300854d89681f6354a3
RX(theta₃₃)
d2f357912bb34afda6a40a94b9fdbbb5--eb0562640a184300854d89681f6354a3
74d89d6cc7ea4fa7a8e11f3e536351fb
t = theta_t₁
eb0562640a184300854d89681f6354a3--74d89d6cc7ea4fa7a8e11f3e536351fb
74d89d6cc7ea4fa7a8e11f3e536351fb--b2b27600ae404638bb2bfffb7f718630
d136e0f0d05e48c8a3a7aab22ec4a723
3567633cacc846f0954a52f6d03ed99a
RX(theta₄)
4e8808b405ed4eea9c4125fec877710c--3567633cacc846f0954a52f6d03ed99a
08a5624be5a34ff8b89ad9a71fb306b7
5
ff5401e339f14d9ba1ea708e73422c04
RY(theta₁₀)
3567633cacc846f0954a52f6d03ed99a--ff5401e339f14d9ba1ea708e73422c04
de6ea1afd34d4832aeb7acc9b03c4de9
RX(theta₁₆)
ff5401e339f14d9ba1ea708e73422c04--de6ea1afd34d4832aeb7acc9b03c4de9
67ec538ca3f44b5180eb7897e75c6d83
de6ea1afd34d4832aeb7acc9b03c4de9--67ec538ca3f44b5180eb7897e75c6d83
edfa2dde45ee4792a3e6a239e61c5fb9
RX(theta₂₂)
67ec538ca3f44b5180eb7897e75c6d83--edfa2dde45ee4792a3e6a239e61c5fb9
bba9b113b2e049a5a769677e8189211e
RY(theta₂₈)
edfa2dde45ee4792a3e6a239e61c5fb9--bba9b113b2e049a5a769677e8189211e
8628c9f746fd4f958cf5c4856932649a
RX(theta₃₄)
bba9b113b2e049a5a769677e8189211e--8628c9f746fd4f958cf5c4856932649a
236eeb39618342f6b2761e82bcf9b384
8628c9f746fd4f958cf5c4856932649a--236eeb39618342f6b2761e82bcf9b384
236eeb39618342f6b2761e82bcf9b384--d136e0f0d05e48c8a3a7aab22ec4a723
c706f7fdad704cc9be160f5c46e583be
a2a32d77b9ba48288460127b180ac560
RX(theta₅)
08a5624be5a34ff8b89ad9a71fb306b7--a2a32d77b9ba48288460127b180ac560
39c84044fa994ca89cb9a4c15f3618a8
RY(theta₁₁)
a2a32d77b9ba48288460127b180ac560--39c84044fa994ca89cb9a4c15f3618a8
0651007f49984a60976e26e94d66e08a
RX(theta₁₇)
39c84044fa994ca89cb9a4c15f3618a8--0651007f49984a60976e26e94d66e08a
640694cd5ca64a79a5f2560991670e9e
0651007f49984a60976e26e94d66e08a--640694cd5ca64a79a5f2560991670e9e
93cab68fca6249e9b29ff1286f486434
RX(theta₂₃)
640694cd5ca64a79a5f2560991670e9e--93cab68fca6249e9b29ff1286f486434
67009309c57145ed8135beb65d70843b
RY(theta₂₉)
93cab68fca6249e9b29ff1286f486434--67009309c57145ed8135beb65d70843b
0ca9a1ad00884e1f9e59b2bea4efbe29
RX(theta₃₅)
67009309c57145ed8135beb65d70843b--0ca9a1ad00884e1f9e59b2bea4efbe29
97b6590b8e9649febab021d68000e3f4
0ca9a1ad00884e1f9e59b2bea4efbe29--97b6590b8e9649febab021d68000e3f4
97b6590b8e9649febab021d68000e3f4--c706f7fdad704cc9be160f5c46e583be
Creating the QuantumModel
The rest of the procedure is the same as any other Qadence workflow. We start by defining a feature map for input encoding and an observable for output decoding.
from qadence import feature_map , BasisSet , ReuploadScaling
from qadence import Z , I
fm = feature_map (
n_qubits = reg . n_qubits ,
param = "x" ,
fm_type = BasisSet . CHEBYSHEV ,
reupload_scaling = ReuploadScaling . TOWER ,
)
# Total magnetization
observable = add ( Z ( i ) for i in range ( reg . n_qubits ))
And we have all the ingredients to initialize the QuantumModel
:
from qadence import QuantumCircuit , QuantumModel
circuit = QuantumCircuit ( reg , fm , da_ansatz )
model = QuantumModel ( circuit , observable = observable )
Training the model
We can now train the model. We use a set of 20 equally spaced training points.
# Chebyshev FM does not accept x = -1, 1
xmin = - 0.99
xmax = 0.99
n_train = 20
x_train = torch . linspace ( xmin , xmax , steps = n_train )
y_train = f ( x_train )
# Initial model prediction
y_pred_initial = model . expectation ({ "x" : x_test }) . detach ()
And we use a simple custom training loop.
criterion = torch . nn . MSELoss ()
optimizer = torch . optim . Adam ( model . parameters (), lr = 0.1 )
n_epochs = 200
def loss_fn ( x_train , y_train ):
out = model . expectation ({ "x" : x_train })
loss = criterion ( out . squeeze (), y_train )
return loss
for i in range ( n_epochs ):
optimizer . zero_grad ()
loss = loss_fn ( x_train , y_train )
loss . backward ()
optimizer . step ()
Results
Finally we can plot the resulting trained model.
y_pred_final = model . expectation ({ "x" : x_test }) . detach ()
plt . plot ( x_test , y_pred_initial , label = "Initial prediction" )
plt . plot ( x_test , y_pred_final , label = "Final prediction" )
plt . scatter ( x_train , y_train , label = "Training points" )
plt . xlabel ( "x" )
plt . ylabel ( "f(x)" )
plt . legend ()
plt . xlim (( - 1.1 , 1.1 ))
plt . ylim (( - 1.1 , 1.1 ))
2025-05-26T11:54:35.528435
image/svg+xml
Matplotlib v3.10.3, https://matplotlib.org/