Clifford data regression API

This example shows how to use Clifford data regression (CDR) by means of a simple example.

import warnings
warnings.filterwarnings("ignore")

import numpy as np

import cirq
from mitiq import cdr

Setup

To use CDR, we call cdr.execute_with_cdr with four necessary “ingredients”:

  1. A quantum circuit to prepare a state \(\rho\).

  2. A quantum computer or noisy simulator to sample bitstrings from \(\rho\).

  3. One or more observables \(O\) which specify what we wish to compute: \(\text{Tr} [ \rho O ]\).

  4. A near-Clifford (classical) circuit simulator.

(1) Circuit

The circuit can be specified as any quantum circuit supported by Mitiq but must be compiled into the gateset \(\{ \sqrt{X}, Z, \text{CNOT}\}\).

a, b = cirq.LineQubit.range(2)
circuit = cirq.Circuit(
    cirq.rx(0.1).on(a),
    cirq.rx(-0.72).on(b),
    cirq.rz(0.4).on(a),
    cirq.rz(0.2).on(b),
    cirq.CNOT.on(a, b),
    cirq.rx(-0.1).on(b),
    cirq.rz(-0.23).on(a),
    cirq.CNOT.on(b, a),
    cirq.rx(-0.112).on(a),
    cirq.measure(a, b, key="z"),
)
circuit
0: ───Rx(0.032π)────Rz(0.127π)───@───Rz(-0.073π)───X───Rx(-0.036π)───M('z')───
                                 │                 │                 │
1: ───Rx(-0.229π)───Rz(0.064π)───X───Rx(-0.032π)───@─────────────────M────────

(2) Executor

The executor inputs a circuit and returns a dictionary or counter of computational basis measurements. The return type must be Dict[int, int] or Counter[int].

Typically this function will send the circuit to a quantum computer and wait for the results. Here for sake of example we use a noisy simulator.

from typing import Counter


def sample_bitstrings(circ: cirq.Circuit, shots: int = 1000, noise: float = 0.01) -> Counter[int]:
    # Add depolarizing noise to emulate a noisy quantum processor!
    circuit = circ.with_noise(cirq.depolarize(noise))
    
    return cirq.DensityMatrixSimulator().run(circuit, repetitions=shots).histogram(key="z")

An example of calling this function is shown below.

sample_bitstrings(circuit)
Counter({0: 770, 3: 168, 2: 31, 1: 31})

(3) Observable(s)

The observables \(O\) indicate what we wish to compute via \(\text{Tr} [ \rho O ]\) and must be specified as “diagonal” (one-dimensional) NumPy arrays.

# Observable(s) to measure.
z = np.diag([1, -1])
obs = np.diag(np.kron(z, z))

(4) (Near-clifford) Simulator

The CDR method creates a set of “training circuits” which are related to the input circuit and are efficiently simulable. These circuits are simulated on a classical (noiseless) simulator to collect data for regression. The simulator return type must be the same as the executor return type.

To use CDR at scale, an efficient near-Clifford circuit simulator must be specified. In this example, the circuit is small enough to use any classical simulator.

def sample_bitstrings_simulator(circuit, shots: int = 1000) -> Counter:
    return sample_bitstrings(circuit, shots=shots, noise=0.0)

Results

Now we can run CDR. We first compute the noiseless result then the noisy result to compare to the mitigated result from CDR.

The noiseless result

cdr.execute.calculate_observable(
    state_or_measurements=sample_bitstrings_simulator(circuit),
    observable=obs,
)
0.992

The noisy result

cdr.execute.calculate_observable(
    state_or_measurements=sample_bitstrings(circuit),
    observable=obs,
)
0.886

The mitigated result

cdr.execute_with_cdr(
    circuit=circuit,
    executor=sample_bitstrings,
    observables=[obs],
    simulator=sample_bitstrings_simulator,
)
[0.9879999999999999]

Additional options

In addition to the four necessary arguments shown above, there are additional parameters in CDR.

Training circuits

One option is how many circuits are in the training set (default is 10). This can be changed as follows.

cdr.execute_with_cdr(
    circuit=circuit,
    executor=sample_bitstrings,
    observables=[obs],
    simulator=sample_bitstrings_simulator,
    num_training_circuits=5,
)
[0.988]

Another option is which fit function to use for regresstion (default is cdr.linear_fit_function).

Fit function

cdr.execute_with_cdr(
    circuit=circuit,
    executor=sample_bitstrings,
    observables=[obs],
    simulator=sample_bitstrings_simulator,
    fit_function=cdr.linear_fit_function_no_intercept,
)
[0.982]

Variable noise CDR

The circuit + training circuits can also be run at different noise scale factors to implement Variable noise Clifford data regression.

from mitiq.zne import scaling

cdr.execute_with_cdr(
    circuit=circuit,
    executor=sample_bitstrings,
    observables=[obs],
    simulator=sample_bitstrings_simulator,
    scale_factors=(1, 3),
    scale_noise=scaling.fold_gates_at_random,
)
[1.0224632036521384]