Run basic quantum information use cases in Qrunch

Goal

Run basic custom quantum information use cases in Qrunch, such as sampling bit-strings from a quantum circuit or estimating the expectation value of a Pauli string.

Note Many classes and methods used in this guide are not part of the public API and WILL change in the future.

Prerequisites

  • Working installation of Qrunch.

  • A sampler for sampling from quantum circuits. See Create a Sampler for details.

  • An estimator for estimating expectation values of quantum circuits. See Create an Estimator for details.

Steps

Create a quantum circuit

A quantum circuit in Qrunch is built with the Circuit class. The following example creates a two-qubit circuit, applies a Hadamard gate to the first qubit and a CNOT gate between the first and second qubits, preparing a Bell state.

from qrunch.quantum.circuits import Circuit
from qrunch.quantum.gates import CXGate, HGate

circuit = Circuit(num_qubits=2)
circuit.append(HGate(0))
circuit.append(CXGate(0, 1))

Many other gates are available in qrunch.quantum.gates, including single-qubit gates such as XGate, YGate, ZGate, SGate, HGate and the rotations RXGate, RYGate, RZGate, as well as two-qubit gates such as CXGate, CZGate and SWAPGate. Use circuit.append to add a single gate, or circuit.extend to add several at once.

from qrunch.quantum.gates import RXGate, RZGate, XGate

circuit = Circuit(num_qubits=2)
circuit.extend([XGate(0), RXGate(0.5, 1), RZGate(0.25, 0)])

# or in one go

circuit = Circuit(num_qubits=2, gates=[XGate(0), RXGate(0.5, 1), RZGate(0.25, 0)])

Sample from the circuit

To draw measurement outcomes, wrap the circuit in a MeasurementCircuit with the desired number of shots and pass it to a sampler. The default sampler is created with qc.sampler_creator().

import qrunch as qc
from qrunch.quantum.measurement.measurement_circuit import MeasurementCircuit

options = qc.options.MemoryRestrictedSimulatorOptions(max_number_of_amplitudes=None, use_real_numbers=False)
sampler = qc.sampler_creator().memory_restricted().with_options(options=options).create()

measurement = sampler.run(MeasurementCircuit(circuit, shots=1000))

print(measurement.to_binary_dict())   # e.g. {"00": 0.49, "11": 0.51}

The returned QuantumMeasurement exposes probabilities through to_binary_dict() and raw counts through as_counts(). For a Bell state the result concentrates on "00" and "11" with roughly equal weight. All defaults in qrunch are chemistry specific and might not work with a general quantum circuit. The sampler from the example above is a memory-restricted simulator with no memory restrictions and complex amplitudes that can be used for general quantum circuits. Any backend sampler should also work.

Estimate a Pauli string expectation value

To estimate \(\langle\psi|P|\psi\rangle\) for a Pauli operator \(P\), build the observable from the single-qubit Pauli operators X, Y and Z and pass it to an estimator. Operators are indexed by qubit and combined with multiplication and addition.

import qrunch as qc
from qrunch.quantum.operators.pauli import X, Y, Z

estimator = qc.estimator_creator().create()

# The two-qubit observable Z0 * Z1.
observable = Z(0) * Z(1)

expectation_value = estimator.run(observable, circuit, shots=1000)

print(expectation_value)         # estimated expectation value with error.
print(expectation_value.value)   # estimated expectation value
print(expectation_value.error)   # statistical error from shot noise

Passing shots=None performs an exact, noiseless state-vector estimation, while a positive integer emulates finite shot noise. Multiple observables can be estimated at once by passing a sequence, which returns one ExpectationValue per observable.

observables = [Z(0), Z(0) * Z(1), X(0) + X(1)]
expectation_values = estimator.run(observables, circuit, shots=None)

Not public API

The circuits, gates, and operators used in this guide are not part of the public API and WILL change in the future. A warning will be issued when importing them as a reminder. This warning can be turned off by setting the environment variable QRUNCH_NO_GUARD to true before importing Qrunch.

import os
os.environ["QRUNCH_NO_GUARD"] = "true"

This also turns off runtime type checking for all Qrunch public api.

Verify the result

  • For the Bell-state circuit, sampling should yield only "00" and "11" with probabilities near 0.5 each.

  • Z(0) * Z(1) should evaluate to 1.0 on the Bell state. With shots=None the value is exact.

Troubleshooting

  • ImportError: Check your environment path and reinstall qrunch.

  • Different qubit ordering: Bit-strings from to_binary_dict() are big-endian.

  • Large error bars: Increase shots or use shots=None for an exact result.

See also