Differentiating jobs¶

Many variational algorithms require computing the gradient of the cost function $$E(\vec{\theta}) = \langle \psi(\vec{\theta}) | H | \psi(\vec{\theta})\rangle. $$ The gradient can be used in gradient-based optimization methods. QLM jobs come with methods to compute the derivative of $E(\vec{\theta})$ automatically: differentiate and gradient.

differentiate returns the jobs allowing to compute the derivative of $E(\vec{\theta})$ with respect to a given variable. gradient returns the jobs allowing to compute the derivative of $E(\vec{\theta})$ with respect to all variables (in the form of a dictionary whose keys are the variable names).

Two methods for computing these derivatives are proposed: the shift-rule method and the Hadamard test method. The latter requires an ancilla qubit.

Simple, one-parameter example¶

In [1]:
from qat.lang.AQASM import CNOT, RX, H, Program

prog = Program()
qbits = prog.qalloc(2)
theta = prog.new_var(float, r"\theta")
H(qbits[0])
CNOT(qbits)
RX(theta)(qbits[1])
CNOT(qbits)

circ = prog.to_circ()
circ.display()
No description has been provided for this image

Let us now define a job corresponding to computing $E(\theta) = \langle \psi(\theta) | Z_0 | \psi(\theta)\rangle$ and its derivative:

In [2]:
from qat.core import Observable, Term
obs = Observable(2, pauli_terms=[Term(1, "Z", [0])])
job = circ.to_job(observable=obs)
jobs = job.differentiate(r"\theta")

for job_ in jobs:
    circ = job_.circuit
    circ.display()
No description has been provided for this image
No description has been provided for this image

With no additional arguments, differentiate uses the shift-rule method. To use the Hadamard test method:

In [3]:
jobs = job.differentiate(r"\theta", method="h-test")

for job_ in jobs:
    circ = job_.circuit
    circ.display()
No description has been provided for this image

More variables¶

Let us look at a slightly more involved example, with two variables:

In [4]:
prog = Program()
qbits = prog.qalloc(2)
theta = prog.new_var(float, r"\theta")
phi = prog.new_var(float, r"\phi")
H(qbits[0])
CNOT(qbits)
RX(theta)(qbits[1])
RX(phi)(qbits[0])
CNOT(qbits)

circ = prog.to_circ()
circ.display()
No description has been provided for this image
In [5]:
job = circ.to_job(observable=obs)
jobs = job.gradient()
for var in jobs.keys():
    print(var)
    for job_ in jobs[var]:
        circ = job_.circuit
        circ.display()
\phi
No description has been provided for this image
No description has been provided for this image
\theta
No description has been provided for this image
No description has been provided for this image
In [ ]: