Qdislib — 05 · Observables

So far we measured <ZZZZZ>. Qdislib accepts any per-qubit Pauli string over I, X, Y, Z, and weighted sums of them (a Hamiltonian).

[ ]:
from qibo import models, gates


def example_circuit():
    """A small 5-qubit circuit: cuttable (CZ chain), non-trivial <ZZZZZ> ~= 0.44."""
    circuit = models.Circuit(5)
    circuit.add(gates.RY(0, 0.8))
    circuit.add(gates.CZ(0, 1))
    circuit.add(gates.RY(1, 0.5))
    circuit.add(gates.CZ(1, 2))
    circuit.add(gates.RY(2, 0.6))
    circuit.add(gates.CZ(2, 3))
    circuit.add(gates.RY(3, 0.4))
    circuit.add(gates.CZ(3, 4))
    circuit.add(gates.RX(4, 0.3))
    return circuit


circuit = example_circuit()
print(circuit.draw())
[ ]:
import Qdislib as qd

circuit = example_circuit()
cut = qd.find_cut(circuit, gate_cut=True, wire_cut=False)

Pauli strings

Z measures in the computational basis; I ignores a qubit (including one on a cut — the reconstruction handles it); X/Y apply the basis rotation for you. Compare each to the exact value.

[ ]:
for obs in ["ZZZZZ", "ZIZIZ", "XZIZI", "XZZZY"]:
    value = qd.gate_cutting(example_circuit(), cut, observables=obs, shots=20000)
    exact = qd.analytical_solution(example_circuit(), obs)
    print(f"{obs}:  cut={value:+.3f}   exact={exact:+.3f}")

Sampling variance. Each cut multiplies the estimator’s variance (the quasiprobability sampling overhead), and X/Y observables add more on top, so measurements involving them need more shots to converge — the estimate stays unbiased, it just gets noisier. Bump shots (or make fewer cuts) if a value looks off.

Identity on a cut qubit. An I on a qubit that a gate cut passes through is the subtle case: there is no basis measurement on that qubit to fix the sign of each reconstruction term, so Qdislib recovers the sign from the cut’s own mid-circuit measurement. You don’t do anything special — it still returns the exact value — but it’s why measuring identities across a cut works at all.

Weighted sums (Hamiltonians)

Pass a list of (coefficient, pauli) pairs or a {pauli: coefficient} mapping; Qdislib evaluates cᵢ Pᵢ> = Σ cᵢ <Pᵢ> term by term.

[ ]:
hamiltonian = [(0.5, "ZZIII"), (-1.0, "IIZZI"), (0.25, "ZIZII")]
value = qd.gate_cutting(example_circuit(), cut, observables=hamiltonian, shots=20000)
exact = qd.analytical_solution(example_circuit(), hamiltonian)
print(f"<H>:  cut={value:+.3f}   exact={exact:+.3f}")

The same observables= argument works for wire_cutting, estimate and the three-step *_subcircuits workflow.


Next: 06 · Sampling the output distribution.