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-10T14:29:31.653080
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_daf11696d4c14a89a13b6da72472b7f2
cluster_ec8c991dd26e47729be16119d85388ac
46637a7611d74c738ec0c0561ab6ee93
0
4eb7219c6b2a4a5291f1c60e32eb27d1
RX(theta₀)
46637a7611d74c738ec0c0561ab6ee93--4eb7219c6b2a4a5291f1c60e32eb27d1
ac300e8266754708a632f7fe1e7bab4a
1
955816a7298848c38fbab776378b958d
RY(theta₆)
4eb7219c6b2a4a5291f1c60e32eb27d1--955816a7298848c38fbab776378b958d
9a675b74294c45688ef0dbe08c52f1b0
RX(theta₁₂)
955816a7298848c38fbab776378b958d--9a675b74294c45688ef0dbe08c52f1b0
43a33e997bb84c0b84236dc8fa548455
9a675b74294c45688ef0dbe08c52f1b0--43a33e997bb84c0b84236dc8fa548455
156ffe4d230f480ea5f583422df3ab25
RX(theta₁₈)
43a33e997bb84c0b84236dc8fa548455--156ffe4d230f480ea5f583422df3ab25
b58c72d56aaf4fc6be663a3d4b15cc92
RY(theta₂₄)
156ffe4d230f480ea5f583422df3ab25--b58c72d56aaf4fc6be663a3d4b15cc92
6bdb03aad5b64859b8bc350a5b3f6d8c
RX(theta₃₀)
b58c72d56aaf4fc6be663a3d4b15cc92--6bdb03aad5b64859b8bc350a5b3f6d8c
8b03b5f4f0d94c598c93bb250b8fa33c
6bdb03aad5b64859b8bc350a5b3f6d8c--8b03b5f4f0d94c598c93bb250b8fa33c
1614b25b00d344f79cd21fb0e55447ba
8b03b5f4f0d94c598c93bb250b8fa33c--1614b25b00d344f79cd21fb0e55447ba
7470a1d62db24c2e960229ff7a63aea2
0dc4d993692a4bc499cd5e23bd70e7d2
RX(theta₁)
ac300e8266754708a632f7fe1e7bab4a--0dc4d993692a4bc499cd5e23bd70e7d2
65847a544dda4fcc8c58be77f5f3ab22
2
39546031817145c0ae95372dd72f8bf1
RY(theta₇)
0dc4d993692a4bc499cd5e23bd70e7d2--39546031817145c0ae95372dd72f8bf1
5845614894294436a3a36cbc98473345
RX(theta₁₃)
39546031817145c0ae95372dd72f8bf1--5845614894294436a3a36cbc98473345
512e0bcfe2fc45c3be9577be32548d4e
5845614894294436a3a36cbc98473345--512e0bcfe2fc45c3be9577be32548d4e
4e39b623fc4e4676a66d4379a5726c14
RX(theta₁₉)
512e0bcfe2fc45c3be9577be32548d4e--4e39b623fc4e4676a66d4379a5726c14
ac79e8f5192f46c4a07629567e99816c
RY(theta₂₅)
4e39b623fc4e4676a66d4379a5726c14--ac79e8f5192f46c4a07629567e99816c
425dadc97c934f288801498073de27c5
RX(theta₃₁)
ac79e8f5192f46c4a07629567e99816c--425dadc97c934f288801498073de27c5
fa4a6f294b9c4d90a85d79b94b50047b
425dadc97c934f288801498073de27c5--fa4a6f294b9c4d90a85d79b94b50047b
fa4a6f294b9c4d90a85d79b94b50047b--7470a1d62db24c2e960229ff7a63aea2
936bca0afc51491ca381045d564728b9
f494e79d545e4254ac5583a3470dc5f1
RX(theta₂)
65847a544dda4fcc8c58be77f5f3ab22--f494e79d545e4254ac5583a3470dc5f1
f2cf752857c342f892260dd92b3e05bb
3
87431dda6f2f4f869ff814624ec134ce
RY(theta₈)
f494e79d545e4254ac5583a3470dc5f1--87431dda6f2f4f869ff814624ec134ce
f016459ac51649989c51c8a3d8cfda36
RX(theta₁₄)
87431dda6f2f4f869ff814624ec134ce--f016459ac51649989c51c8a3d8cfda36
30c48e4134a3487d9444c11d5a88f328
HamEvo
f016459ac51649989c51c8a3d8cfda36--30c48e4134a3487d9444c11d5a88f328
9292704db9b046b3816c18148942bf43
RX(theta₂₀)
30c48e4134a3487d9444c11d5a88f328--9292704db9b046b3816c18148942bf43
eb384fd3b22941139dc07d1363e8c321
RY(theta₂₆)
9292704db9b046b3816c18148942bf43--eb384fd3b22941139dc07d1363e8c321
7454288827ac469b842f78824d953682
RX(theta₃₂)
eb384fd3b22941139dc07d1363e8c321--7454288827ac469b842f78824d953682
add0bb696efa4462b43fb36982bbdc05
HamEvo
7454288827ac469b842f78824d953682--add0bb696efa4462b43fb36982bbdc05
add0bb696efa4462b43fb36982bbdc05--936bca0afc51491ca381045d564728b9
ea4ff121bbd64c8a8107949c080a1f48
c9cd107d34f8447f8536d5bdf3204445
RX(theta₃)
f2cf752857c342f892260dd92b3e05bb--c9cd107d34f8447f8536d5bdf3204445
9614602f62594fd0902eb7ffdf060afc
4
24adc07f07b948378d7c85659213c6c4
RY(theta₉)
c9cd107d34f8447f8536d5bdf3204445--24adc07f07b948378d7c85659213c6c4
a6b9c41fcad045edb53b7320f605e1f2
RX(theta₁₅)
24adc07f07b948378d7c85659213c6c4--a6b9c41fcad045edb53b7320f605e1f2
f7ce995d09a84cfc9ff5e324a47f403a
t = theta_t₀
a6b9c41fcad045edb53b7320f605e1f2--f7ce995d09a84cfc9ff5e324a47f403a
a47eb912f3174747ad72b652ef25b2fa
RX(theta₂₁)
f7ce995d09a84cfc9ff5e324a47f403a--a47eb912f3174747ad72b652ef25b2fa
a9050960cec744a5826c2d4701523957
RY(theta₂₇)
a47eb912f3174747ad72b652ef25b2fa--a9050960cec744a5826c2d4701523957
9db9b5fad9e540fe98312b5fd9539df4
RX(theta₃₃)
a9050960cec744a5826c2d4701523957--9db9b5fad9e540fe98312b5fd9539df4
707ea4e2cae24312ae976dc6c3ed99b4
t = theta_t₁
9db9b5fad9e540fe98312b5fd9539df4--707ea4e2cae24312ae976dc6c3ed99b4
707ea4e2cae24312ae976dc6c3ed99b4--ea4ff121bbd64c8a8107949c080a1f48
2bb6a911d752467c932b000abb729586
eabc440b9c2a4cb49e4f753ae09abc2b
RX(theta₄)
9614602f62594fd0902eb7ffdf060afc--eabc440b9c2a4cb49e4f753ae09abc2b
f9a9017e778e4007b5e9d47135e1e931
5
0ff70af5e0e5477e92b82d83a34f5479
RY(theta₁₀)
eabc440b9c2a4cb49e4f753ae09abc2b--0ff70af5e0e5477e92b82d83a34f5479
a2ae695a4cb74226a6a46d6623d73338
RX(theta₁₆)
0ff70af5e0e5477e92b82d83a34f5479--a2ae695a4cb74226a6a46d6623d73338
5208847b35e84b459c19560cdf1d3842
a2ae695a4cb74226a6a46d6623d73338--5208847b35e84b459c19560cdf1d3842
b29ff033ef494e8cbb69040b11cd7ef4
RX(theta₂₂)
5208847b35e84b459c19560cdf1d3842--b29ff033ef494e8cbb69040b11cd7ef4
ae0175904a4d472499b6c57b3bb95572
RY(theta₂₈)
b29ff033ef494e8cbb69040b11cd7ef4--ae0175904a4d472499b6c57b3bb95572
5b71f7d9e0b349d7a4f016fc2744743e
RX(theta₃₄)
ae0175904a4d472499b6c57b3bb95572--5b71f7d9e0b349d7a4f016fc2744743e
1e62defcaafd4c1085fdbfea84f94ada
5b71f7d9e0b349d7a4f016fc2744743e--1e62defcaafd4c1085fdbfea84f94ada
1e62defcaafd4c1085fdbfea84f94ada--2bb6a911d752467c932b000abb729586
e7ec01c2c5bb421abb455a186186c17c
4bed592f626b4791855a625554eb833d
RX(theta₅)
f9a9017e778e4007b5e9d47135e1e931--4bed592f626b4791855a625554eb833d
b6bd054f6a4a440891da80c2d7f7e4ea
RY(theta₁₁)
4bed592f626b4791855a625554eb833d--b6bd054f6a4a440891da80c2d7f7e4ea
2f0909dec7794898a7df57979152e175
RX(theta₁₇)
b6bd054f6a4a440891da80c2d7f7e4ea--2f0909dec7794898a7df57979152e175
e7d4b3052b8a498c8127c0b1f071184e
2f0909dec7794898a7df57979152e175--e7d4b3052b8a498c8127c0b1f071184e
d8c2d4a0076f4bdd803e025539413875
RX(theta₂₃)
e7d4b3052b8a498c8127c0b1f071184e--d8c2d4a0076f4bdd803e025539413875
f39b6ed7f47a427e9dde2c06d861716b
RY(theta₂₉)
d8c2d4a0076f4bdd803e025539413875--f39b6ed7f47a427e9dde2c06d861716b
45ac444e341143268de0aac93168d9ca
RX(theta₃₅)
f39b6ed7f47a427e9dde2c06d861716b--45ac444e341143268de0aac93168d9ca
e12ea76ef5e544d9bb401a6b8ad3732a
45ac444e341143268de0aac93168d9ca--e12ea76ef5e544d9bb401a6b8ad3732a
e12ea76ef5e544d9bb401a6b8ad3732a--e7ec01c2c5bb421abb455a186186c17c
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-10T14:29:37.022289
image/svg+xml
Matplotlib v3.7.5, https://matplotlib.org/