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 ))
2024-07-12T10:06:53.103030
image/svg+xml
Matplotlib v3.7.5, 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_e2f408ef23eb4e82b1c9ccb470fce5df
cluster_07fa0d2496394171bd246c89ec1da56f
37262a2348df4151a2bc096bfcfa53f3
0
26c7989b649e4f63bc31ea217c2b6174
RX(theta₀)
37262a2348df4151a2bc096bfcfa53f3--26c7989b649e4f63bc31ea217c2b6174
6816b07681264ffebb715ba61273c5b1
1
4c5f25fd4fde47fba94013d96b5ba34e
RY(theta₆)
26c7989b649e4f63bc31ea217c2b6174--4c5f25fd4fde47fba94013d96b5ba34e
2f430cf16d1c44efb595ddd247525707
RX(theta₁₂)
4c5f25fd4fde47fba94013d96b5ba34e--2f430cf16d1c44efb595ddd247525707
df39e7cb56e44a0aa599d4addccda58a
2f430cf16d1c44efb595ddd247525707--df39e7cb56e44a0aa599d4addccda58a
8ede012a19584ad3865adda8f03b9de8
RX(theta₁₈)
df39e7cb56e44a0aa599d4addccda58a--8ede012a19584ad3865adda8f03b9de8
2fb7738ed78a4107a4620e4d05714d3a
RY(theta₂₄)
8ede012a19584ad3865adda8f03b9de8--2fb7738ed78a4107a4620e4d05714d3a
e0f573f84d094f4e9505cc207c1dda18
RX(theta₃₀)
2fb7738ed78a4107a4620e4d05714d3a--e0f573f84d094f4e9505cc207c1dda18
1f977ee48c924fa7ad2dab65d2a36c62
e0f573f84d094f4e9505cc207c1dda18--1f977ee48c924fa7ad2dab65d2a36c62
10526797de4346deb7a7a7a5bef78795
1f977ee48c924fa7ad2dab65d2a36c62--10526797de4346deb7a7a7a5bef78795
a617a722253342afb86508e7732eab5b
38b32059f0664f8a9e0fb081ad20a416
RX(theta₁)
6816b07681264ffebb715ba61273c5b1--38b32059f0664f8a9e0fb081ad20a416
47746df8fedf4b8faea85a313f96c36d
2
f804c74671dc4283b848111c6055ccb6
RY(theta₇)
38b32059f0664f8a9e0fb081ad20a416--f804c74671dc4283b848111c6055ccb6
a04570d2127f49129b239bd7ae6b1a42
RX(theta₁₃)
f804c74671dc4283b848111c6055ccb6--a04570d2127f49129b239bd7ae6b1a42
614723577cde450d873b684e68433e5b
a04570d2127f49129b239bd7ae6b1a42--614723577cde450d873b684e68433e5b
804d2ed8a1924ffe86862144388f8306
RX(theta₁₉)
614723577cde450d873b684e68433e5b--804d2ed8a1924ffe86862144388f8306
2cfcb179f16445958430ace77f50bbbd
RY(theta₂₅)
804d2ed8a1924ffe86862144388f8306--2cfcb179f16445958430ace77f50bbbd
1aa71a9105ef4229951b9b7336bff482
RX(theta₃₁)
2cfcb179f16445958430ace77f50bbbd--1aa71a9105ef4229951b9b7336bff482
cea2b6d74b9843989a9fbde1572fea87
1aa71a9105ef4229951b9b7336bff482--cea2b6d74b9843989a9fbde1572fea87
cea2b6d74b9843989a9fbde1572fea87--a617a722253342afb86508e7732eab5b
c687d01639c14805a7358f8f74b6078b
efb9b692a38545238cc0f89e10144b29
RX(theta₂)
47746df8fedf4b8faea85a313f96c36d--efb9b692a38545238cc0f89e10144b29
f51d4911c5b04a6bac0028e39bb851f7
3
1609445772364f78b1eb880ae1fb65b8
RY(theta₈)
efb9b692a38545238cc0f89e10144b29--1609445772364f78b1eb880ae1fb65b8
b758e7e43eed419fb01763432b3695e2
RX(theta₁₄)
1609445772364f78b1eb880ae1fb65b8--b758e7e43eed419fb01763432b3695e2
1af34f00b7e44604918abc9aec41a951
HamEvo
b758e7e43eed419fb01763432b3695e2--1af34f00b7e44604918abc9aec41a951
4525d1fed826451db6dbccbaa65db8a9
RX(theta₂₀)
1af34f00b7e44604918abc9aec41a951--4525d1fed826451db6dbccbaa65db8a9
916655ccedea42639283ed7afffe1589
RY(theta₂₆)
4525d1fed826451db6dbccbaa65db8a9--916655ccedea42639283ed7afffe1589
73560748ef264ce38436d9985a3f7f7d
RX(theta₃₂)
916655ccedea42639283ed7afffe1589--73560748ef264ce38436d9985a3f7f7d
bbf44d354ffa49a1b3faeca59ba7b7db
HamEvo
73560748ef264ce38436d9985a3f7f7d--bbf44d354ffa49a1b3faeca59ba7b7db
bbf44d354ffa49a1b3faeca59ba7b7db--c687d01639c14805a7358f8f74b6078b
b108278e9c8747ff8de020c47ab4834e
799f4a16d37e45d4939e08c9a486c2e3
RX(theta₃)
f51d4911c5b04a6bac0028e39bb851f7--799f4a16d37e45d4939e08c9a486c2e3
1b6af807efef40b39b81166cbe939aab
4
cda1b208c2a449e08235a0de16ccef11
RY(theta₉)
799f4a16d37e45d4939e08c9a486c2e3--cda1b208c2a449e08235a0de16ccef11
846d041acf8d4d22b33d2b2b25ab5879
RX(theta₁₅)
cda1b208c2a449e08235a0de16ccef11--846d041acf8d4d22b33d2b2b25ab5879
f4c790b8a5c24d94aa9929d750d7168e
t = theta_t₀
846d041acf8d4d22b33d2b2b25ab5879--f4c790b8a5c24d94aa9929d750d7168e
d37112954b6b489c980b1501099361d4
RX(theta₂₁)
f4c790b8a5c24d94aa9929d750d7168e--d37112954b6b489c980b1501099361d4
9108647a854f4b2f8ef488d73906e732
RY(theta₂₇)
d37112954b6b489c980b1501099361d4--9108647a854f4b2f8ef488d73906e732
26b88ef615c648b893c3083f8288f61d
RX(theta₃₃)
9108647a854f4b2f8ef488d73906e732--26b88ef615c648b893c3083f8288f61d
411e43e2cf714d2ba0f5cf608722f86f
t = theta_t₁
26b88ef615c648b893c3083f8288f61d--411e43e2cf714d2ba0f5cf608722f86f
411e43e2cf714d2ba0f5cf608722f86f--b108278e9c8747ff8de020c47ab4834e
2e85d84e88b94150b14477bb4afbff3b
e7ca558eb08b4dfb84be132fb2ef86f4
RX(theta₄)
1b6af807efef40b39b81166cbe939aab--e7ca558eb08b4dfb84be132fb2ef86f4
bc8f1ba7a5194d08a30ada02015926d4
5
17a464d65fd144949c60191f97816631
RY(theta₁₀)
e7ca558eb08b4dfb84be132fb2ef86f4--17a464d65fd144949c60191f97816631
06078cbcd1184d16b7d6dbd50711f1f8
RX(theta₁₆)
17a464d65fd144949c60191f97816631--06078cbcd1184d16b7d6dbd50711f1f8
65f25801fd2048d2a28f9dca46967cdf
06078cbcd1184d16b7d6dbd50711f1f8--65f25801fd2048d2a28f9dca46967cdf
b11a616f39c048dd909d29bc296a7128
RX(theta₂₂)
65f25801fd2048d2a28f9dca46967cdf--b11a616f39c048dd909d29bc296a7128
cc6b12b27a8e4038a06105837b435e76
RY(theta₂₈)
b11a616f39c048dd909d29bc296a7128--cc6b12b27a8e4038a06105837b435e76
cbd099f5fb8b4dd9bb5c5065a3f66d3f
RX(theta₃₄)
cc6b12b27a8e4038a06105837b435e76--cbd099f5fb8b4dd9bb5c5065a3f66d3f
45a27e8653754c1494ae40c1c382c619
cbd099f5fb8b4dd9bb5c5065a3f66d3f--45a27e8653754c1494ae40c1c382c619
45a27e8653754c1494ae40c1c382c619--2e85d84e88b94150b14477bb4afbff3b
253d7abe6b1c498591f3b13d9dd6fb01
20c664e2d0e544d693bfbe51b0182396
RX(theta₅)
bc8f1ba7a5194d08a30ada02015926d4--20c664e2d0e544d693bfbe51b0182396
77a2208e7a0048a9ba7926f6806fdaa2
RY(theta₁₁)
20c664e2d0e544d693bfbe51b0182396--77a2208e7a0048a9ba7926f6806fdaa2
1bde9386bf52444f847d4fadb1b4e4b1
RX(theta₁₇)
77a2208e7a0048a9ba7926f6806fdaa2--1bde9386bf52444f847d4fadb1b4e4b1
74ddde4c01fb4972abf40473c074b953
1bde9386bf52444f847d4fadb1b4e4b1--74ddde4c01fb4972abf40473c074b953
d138b20f6ec148559f38f5b24294ed4b
RX(theta₂₃)
74ddde4c01fb4972abf40473c074b953--d138b20f6ec148559f38f5b24294ed4b
dacabb64df3f4fa2a3399f2485cc2bb8
RY(theta₂₉)
d138b20f6ec148559f38f5b24294ed4b--dacabb64df3f4fa2a3399f2485cc2bb8
a7ce4799d7ec4c21a44bfa27f33f9bf4
RX(theta₃₅)
dacabb64df3f4fa2a3399f2485cc2bb8--a7ce4799d7ec4c21a44bfa27f33f9bf4
5f2fefc5f2af48d2856748c9de32bac8
a7ce4799d7ec4c21a44bfa27f33f9bf4--5f2fefc5f2af48d2856748c9de32bac8
5f2fefc5f2af48d2856748c9de32bac8--253d7abe6b1c498591f3b13d9dd6fb01
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 ))
2024-07-12T10:06:58.639052
image/svg+xml
Matplotlib v3.7.5, https://matplotlib.org/