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-03-13T17:13:55.176855
image/svg+xml
Matplotlib v3.10.1, 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_f5266b90ab5448a79797beba70bf4ff7
cluster_7bd7a35666a94dc5987f81a60ff0251f
98aa2c7fe3794176a47a5f5b68d12651
0
6cb851fedda642fbb56e73c2f2bed50b
RX(theta₀)
98aa2c7fe3794176a47a5f5b68d12651--6cb851fedda642fbb56e73c2f2bed50b
a56cf4c280e942a496aaf2be21597fb1
1
15f15c2844764a06855a49035ba4104e
RY(theta₆)
6cb851fedda642fbb56e73c2f2bed50b--15f15c2844764a06855a49035ba4104e
eef1937f71aa429c95384721d16b1521
RX(theta₁₂)
15f15c2844764a06855a49035ba4104e--eef1937f71aa429c95384721d16b1521
ef5d4961f9024cffb24ecf105000d995
eef1937f71aa429c95384721d16b1521--ef5d4961f9024cffb24ecf105000d995
9143b1f94b95487f9b6460cacaf838ce
RX(theta₁₈)
ef5d4961f9024cffb24ecf105000d995--9143b1f94b95487f9b6460cacaf838ce
4ec28930f2ae425a9a50ac947560edc9
RY(theta₂₄)
9143b1f94b95487f9b6460cacaf838ce--4ec28930f2ae425a9a50ac947560edc9
1f843a3cf0144fe39ddade3273b44ef4
RX(theta₃₀)
4ec28930f2ae425a9a50ac947560edc9--1f843a3cf0144fe39ddade3273b44ef4
a2dd19bfb8c143708f32eeb30bd6d9d7
1f843a3cf0144fe39ddade3273b44ef4--a2dd19bfb8c143708f32eeb30bd6d9d7
1f20487aa40b4179b3c26fb35059f5ae
a2dd19bfb8c143708f32eeb30bd6d9d7--1f20487aa40b4179b3c26fb35059f5ae
ccbc600357464199bbf7698f4a259f83
b6435ce271c64c76b8482300abe4889b
RX(theta₁)
a56cf4c280e942a496aaf2be21597fb1--b6435ce271c64c76b8482300abe4889b
8b1d6e73d7a34aa4a8f150e0628d1db5
2
cad4495022f84fe38e9cb800637f118a
RY(theta₇)
b6435ce271c64c76b8482300abe4889b--cad4495022f84fe38e9cb800637f118a
70690912655f45f6b201a909a23090b2
RX(theta₁₃)
cad4495022f84fe38e9cb800637f118a--70690912655f45f6b201a909a23090b2
9c3ffbe7252b4a208c3c791b65326589
70690912655f45f6b201a909a23090b2--9c3ffbe7252b4a208c3c791b65326589
321053bc19f843c28261782e40c5b5ac
RX(theta₁₉)
9c3ffbe7252b4a208c3c791b65326589--321053bc19f843c28261782e40c5b5ac
8db30f9d9e8241558d3a7023a0826c73
RY(theta₂₅)
321053bc19f843c28261782e40c5b5ac--8db30f9d9e8241558d3a7023a0826c73
86846ba5bf784da8a486b71536c37265
RX(theta₃₁)
8db30f9d9e8241558d3a7023a0826c73--86846ba5bf784da8a486b71536c37265
f971f8ae30f2441286b68812c449d831
86846ba5bf784da8a486b71536c37265--f971f8ae30f2441286b68812c449d831
f971f8ae30f2441286b68812c449d831--ccbc600357464199bbf7698f4a259f83
204264602b384edfa8991fc4908ac80c
d74c24d8904a4464bd8952710a582fc2
RX(theta₂)
8b1d6e73d7a34aa4a8f150e0628d1db5--d74c24d8904a4464bd8952710a582fc2
6c0a1475da25435c8d20a801f0b563cd
3
77e24859a9654bb9babddfbe5955f764
RY(theta₈)
d74c24d8904a4464bd8952710a582fc2--77e24859a9654bb9babddfbe5955f764
68dc08090d2241f58be4a56c5afb9222
RX(theta₁₄)
77e24859a9654bb9babddfbe5955f764--68dc08090d2241f58be4a56c5afb9222
360bb7aacb9047cd9d71655fc1080a25
HamEvo
68dc08090d2241f58be4a56c5afb9222--360bb7aacb9047cd9d71655fc1080a25
9ad985e135c845faa2015c1150a67c10
RX(theta₂₀)
360bb7aacb9047cd9d71655fc1080a25--9ad985e135c845faa2015c1150a67c10
efb2f1b917284a23882370e6848d1486
RY(theta₂₆)
9ad985e135c845faa2015c1150a67c10--efb2f1b917284a23882370e6848d1486
ae0c02919cba41d39cd65e597289ab3e
RX(theta₃₂)
efb2f1b917284a23882370e6848d1486--ae0c02919cba41d39cd65e597289ab3e
851da8781b4140a0a949ff5f5b0cd9c8
HamEvo
ae0c02919cba41d39cd65e597289ab3e--851da8781b4140a0a949ff5f5b0cd9c8
851da8781b4140a0a949ff5f5b0cd9c8--204264602b384edfa8991fc4908ac80c
cfb987a3d6ac421cbbac5867477b1dc9
08d24f15ee00451a982156925373165f
RX(theta₃)
6c0a1475da25435c8d20a801f0b563cd--08d24f15ee00451a982156925373165f
c0a2cfb6e2a746e0b5390dda0b423867
4
472de18fa08849b2863cb7becc20fc6b
RY(theta₉)
08d24f15ee00451a982156925373165f--472de18fa08849b2863cb7becc20fc6b
96b4429980a544e6a0a594f7cf7447b4
RX(theta₁₅)
472de18fa08849b2863cb7becc20fc6b--96b4429980a544e6a0a594f7cf7447b4
40a5cae89f71488f932f22e94c45b312
t = theta_t₀
96b4429980a544e6a0a594f7cf7447b4--40a5cae89f71488f932f22e94c45b312
69a67dad1da2435caecc27aa87551ead
RX(theta₂₁)
40a5cae89f71488f932f22e94c45b312--69a67dad1da2435caecc27aa87551ead
de97d0339d1341deacabf54abd162475
RY(theta₂₇)
69a67dad1da2435caecc27aa87551ead--de97d0339d1341deacabf54abd162475
671db70cedf845da9bc9f41699b5cc89
RX(theta₃₃)
de97d0339d1341deacabf54abd162475--671db70cedf845da9bc9f41699b5cc89
036e7851bbc246c395283ba9aefae727
t = theta_t₁
671db70cedf845da9bc9f41699b5cc89--036e7851bbc246c395283ba9aefae727
036e7851bbc246c395283ba9aefae727--cfb987a3d6ac421cbbac5867477b1dc9
ef3ef7603cb740cca8946ae84e5645ed
7f2d5a590db649ce980674de2e04fb56
RX(theta₄)
c0a2cfb6e2a746e0b5390dda0b423867--7f2d5a590db649ce980674de2e04fb56
52b7edc1b4994357819f290970cb6f28
5
6d267482cec8483aa1e7630a50ebb13d
RY(theta₁₀)
7f2d5a590db649ce980674de2e04fb56--6d267482cec8483aa1e7630a50ebb13d
9e0f135d75ec429a9bcfb08f6744054f
RX(theta₁₆)
6d267482cec8483aa1e7630a50ebb13d--9e0f135d75ec429a9bcfb08f6744054f
c0ba0e07bf8546eda55d595a1ab61294
9e0f135d75ec429a9bcfb08f6744054f--c0ba0e07bf8546eda55d595a1ab61294
dba29916a1b245e4ba38b32820f14911
RX(theta₂₂)
c0ba0e07bf8546eda55d595a1ab61294--dba29916a1b245e4ba38b32820f14911
3a6a3f12a13941e0a4d4c30b3de0a3d1
RY(theta₂₈)
dba29916a1b245e4ba38b32820f14911--3a6a3f12a13941e0a4d4c30b3de0a3d1
66697e266dc04d339a68698b0007b005
RX(theta₃₄)
3a6a3f12a13941e0a4d4c30b3de0a3d1--66697e266dc04d339a68698b0007b005
5204569052d149e6994c714a5ed6e56f
66697e266dc04d339a68698b0007b005--5204569052d149e6994c714a5ed6e56f
5204569052d149e6994c714a5ed6e56f--ef3ef7603cb740cca8946ae84e5645ed
5bdb1159d8da44dda240af84cdb0b47d
02ae9608745d4dbfa04704097c580f78
RX(theta₅)
52b7edc1b4994357819f290970cb6f28--02ae9608745d4dbfa04704097c580f78
d6bfd8fa9f504d97aee2a7f4f7f5734f
RY(theta₁₁)
02ae9608745d4dbfa04704097c580f78--d6bfd8fa9f504d97aee2a7f4f7f5734f
721e22b1589f467c949409f49b31e4fd
RX(theta₁₇)
d6bfd8fa9f504d97aee2a7f4f7f5734f--721e22b1589f467c949409f49b31e4fd
9d04e54e1f8342e8909e00ab2bbd8a57
721e22b1589f467c949409f49b31e4fd--9d04e54e1f8342e8909e00ab2bbd8a57
95b5f78430fb404c8ee8a32605101f5c
RX(theta₂₃)
9d04e54e1f8342e8909e00ab2bbd8a57--95b5f78430fb404c8ee8a32605101f5c
f63a39b88c6a47dab20a9b4693ce1a37
RY(theta₂₉)
95b5f78430fb404c8ee8a32605101f5c--f63a39b88c6a47dab20a9b4693ce1a37
cfdf86c30531407e8470e431346f63c1
RX(theta₃₅)
f63a39b88c6a47dab20a9b4693ce1a37--cfdf86c30531407e8470e431346f63c1
c13f0ca96a8947a7a7d18e856a95b891
cfdf86c30531407e8470e431346f63c1--c13f0ca96a8947a7a7d18e856a95b891
c13f0ca96a8947a7a7d18e856a95b891--5bdb1159d8da44dda240af84cdb0b47d
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-03-13T17:14:03.859883
image/svg+xml
Matplotlib v3.10.1, https://matplotlib.org/