Creating observables

Observables

As mentioned in the job section, it is possible to construct a Job requiring the sampling of some observable on the final state produced by a quantum circuit. The Observable class provides a basic interface to declare observables.

from qat.core import Observable, Term

my_observable = Observable(4, # A 4 qubits observable
                           pauli_terms=[
                               Term(1., "ZZ", [0, 1]),
                               Term(4., "XZ", [2, 0]),
                               Term(3., "ZXZX", [0, 1, 2, 3])
                           ],
                           constant_coeff=23.)
print(my_observable)
23.0 * I^4 +
1.0 * (ZZ|[0, 1]) +
4.0 * (XZ|[2, 0]) +
3.0 * (ZXZX|[0, 1, 2, 3])

Observables operations

Observables can be added and multiplied by a scalar:

Observable basic arithmetic
from qat.core import Observable, Term

obs1 = Observable(2, pauli_terms=[Term(1., "ZZ", [0, 1])], constant_coeff=1.)
obs2 = Observable(2, pauli_terms=[Term(1., "X", [0])], constant_coeff=2.)

print(obs1 + obs2)
3.0 * I^2 +
1.0 * (ZZ|[0, 1]) +
1.0 * (X|[0])
from qat.core import Observable, Term

obs1 = Observable(2, pauli_terms=[Term(1., "ZZ", [0, 1])])

print(4 * obs1)
4.0 * (ZZ|[0, 1])

They can be composed via tensor product using the ^ __xor__ operator:

Observable composition
from qat.core import Observable, Term

obs1 = Observable(2, pauli_terms=[Term(1., "ZZ", [0, 1])])
obs2 = Observable(2, pauli_terms=[Term(1., "X", [0])])

print(obs1 ^ obs2)
1.0 * (ZZX|[0, 1, 2])

The commutator of two observables can be computed using the | __or__ operator:

Observable commutation
from qat.core import Observable, Term

obs1 = Observable(2, pauli_terms=[Term(1., "ZZ", [0, 1])])
obs2 = Observable(2, pauli_terms=[Term(1., "X", [0])])

print(obs1 | obs2)
2j * (YZ|[0, 1])

Single term observables

Simple observables containing only one Pauli terms can be created with the sigma_x, sigma_y and sigma_z class methods (or equivalently, x, y and z):

Shorthand single Pauli term notation
from qat.core import Observable

obs1 = Observable.sigma_x(0)
obs2 = Observable.sigma_y(1)
obs3 = Observable.sigma_z(0, nbqbits=2)

print("obs1:", obs1)
print("obs2:", obs2)
print("obs3:", obs3)
obs1: 1.0 * (X|[0])
obs2: 1.0 * (Y|[1])
obs3: 1.0 * (Z|[0])
from qat.core import Observable

obs1 = Observable.x(0)
obs2 = Observable.y(1)
obs3 = Observable.z(0, nbqbits=2)

print("obs1:", obs1)
print("obs2:", obs2)
print("obs3:", obs3)
obs1: 1.0 * (X|[0])
obs2: 1.0 * (Y|[1])
obs3: 1.0 * (Z|[0])

Of course those simple observable can be composed by using previously seen operators in order to get more complex ones.

from qat.core import Observable

obs1 = Observable.sigma_z(0)
obs2 = Observable.x(0)

print(((obs1 ^ obs2) + 3) * 2)
6.0 * I^2 +
2.0 * (ZX|[0, 1])

Using an observable

Once written, observables can be attached to a circuit to form an observable sampling job (see the job section for more examples):

from qat.core import Observable, Term

obs = Observable(2, pauli_terms=[Term(1., "ZZ", [0, 1])])
job = circuit.to_job(observable=obs, nbshots=2048)