This notebook introduces the Observable class that allows to describe, manipulate and sample observables over quantum states produced by circuits.
We will take as example a simple observable that counts the number of ones in a quantum state over 5 qubits.
This observable can be written as:
$$ O = \Sigma_i (1 - \sigma_z^i)/2 $$An observable is initialized with the number of qubits it acts on:
from qat.core import Observable, Term
nbqbits = 5
one_count = Observable(nbqbits)
New Pauli terms can be added to the observable.
First, we need to write our observable $O$ as a sum of weighted Pauli operators:
$$ O = N/2 - \Sigma_i \frac{1}{2}\sigma_z^i $$# The sigma Z terms:
for i in range(nbqbits):
one_count.add_term(Term(-0.5, "Z", [i]))
# And the constant term:
one_count.constant_coeff += nbqbits/2
We can print our observable to check if it is correct
print(one_count)
Lets build a simple circuit and approximate the expectation of our observable over its final state.
Because PyLinalg does not natively supports observable sampling, we will use an intermediate plugin ObservableSplitter
in order to split the observable carrying job into a collection of basic sampling jobs.
from qat.lang.AQASM import Program, X, CNOT, RX
prog_2_ones = Program()
qbits = prog_2_ones.qalloc(nbqbits)
prog_2_ones.apply(X, qbits[0])
prog_2_ones.apply(CNOT, qbits[0], qbits[2])
circ_2_ones = prog_2_ones.to_circ()
from qat.qpus import PyLinalg
from qat.plugins import ObservableSplitter
qpu = ObservableSplitter() | PyLinalg()
job = circ_2_ones.to_job("OBS", observable=one_count, nbshots=30)
print("Number of ones:", qpu.submit(job).value)
Now with a less obvious circuit:
prog = Program()
qbits = prog.qalloc(5)
for i, qb in enumerate(qbits):
prog.apply(RX(0.324 * i), qb)
circ = prog.to_circ()
job = circ.to_job("OBS", observable=one_count, nbshots=30)
print("Number of ones:", qpu.submit(job).value)
Of course, we can reduce the deviation of this result by increasing the number of samples:
job = circ.to_job("OBS", observable=one_count, nbshots=1000)
print("Number of ones:", qpu.submit(job).value)
Or even compute the exact value of the observable using an "infinite" number of shots
job = circ.to_job("OBS", observable=one_count)
print("Exact number of ones:", qpu.submit(job).value)