---
jupytext:
  text_representation:
    extension: .myst
    format_name: myst
    format_version: 0.13
    jupytext_version: 1.11.4
kernelspec:
  display_name: Python 3
  language: python
  name: python3
---
(label-cdr-example)=
# Clifford data regression API

+++

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

```{code-cell} ipython3
import warnings
warnings.filterwarnings("ignore")

import numpy as np

import cirq
from mitiq import cdr, Observable, PauliString
```

## Setup

+++

To use CDR, we call `cdr.execute_with_cdr` with four "ingredients":

1. A quantum circuit to prepare a state $\rho$.
1. A quantum computer or noisy simulator to return a `mitiq.QuantumResult` from $\rho$.
1. An observable $O$ which specifies what we wish to compute via $\text{Tr} [ \rho O ]$.
1. 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}\}$.**

```{code-cell} ipython3
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),
)
circuit
```

### (2) Executor

+++

The executor inputs a circuit and returns a `mitiq.QuantumResult`. Typically this function will send the circuit to a quantum computer and wait for the results. Here for sake of example we use a simulator that adds single-qubit depolarizing noise after each moment and returns the final density matrix.

```{code-cell} ipython3
from mitiq.interface.mitiq_cirq import compute_density_matrix

compute_density_matrix(circuit).round(3)
```

### (3) Observable

+++

The observables $O$ indicates what we wish to compute via $\text{Tr} [ \rho O ]$.

```{code-cell} ipython3
# Observable to measure.
obs = Observable(PauliString("ZZ"), PauliString("X", coeff=-1.75))
print(obs)
```

### (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 should also return a `QuantumResult`.

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, and we use the same density matrix simulator as above but without noise.

```{code-cell} ipython3
def simulate(circuit: cirq.Circuit) -> np.ndarray:
    return compute_density_matrix(circuit, noise_level=(0.0,))


simulate(circuit).round(3)
```

## 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

```{code-cell} ipython3
obs.expectation(circuit, simulate).real
```

### The noisy result

```{code-cell} ipython3
obs.expectation(circuit, compute_density_matrix).real
```

### The mitigated result

```{code-cell} ipython3
cdr.execute_with_cdr(
    circuit,
    compute_density_matrix,
    observable=obs,
    simulator=simulate,
).real
```

## 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.

```{code-cell} ipython3
cdr.execute_with_cdr(
    circuit,
    compute_density_matrix,
    observable=obs,
    simulator=simulate,
    num_training_circuits=20,
).real
```

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

+++

### Fit function

```{code-cell} ipython3
cdr.execute_with_cdr(
    circuit,
    compute_density_matrix,
    observable=obs,
    simulator=simulate,
    fit_function=cdr.linear_fit_function_no_intercept,
).real
```

### Variable noise CDR

+++

The circuit + training circuits can also be run at different noise scale factors to implement [variable noise Clifford data regression](https://arxiv.org/abs/2011.01157).

```{code-cell} ipython3
from mitiq.zne import scaling

cdr.execute_with_cdr(
    circuit,
    compute_density_matrix,
    observable=obs,
    simulator=simulate,
    scale_factors=(3,),
).real
```
