Skip to content

Commit

Permalink
Merge pull request #770 from unitaryfund/patch-release-0.9.2
Browse files Browse the repository at this point in the history
Patch release 0.9.2
  • Loading branch information
andreamari authored Jun 30, 2021
2 parents 704ba56 + 3b8a76b commit e5fd404
Show file tree
Hide file tree
Showing 9 changed files with 278 additions and 18 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,17 @@
% # " - [Bug Fix]"
% # " - Fix the bug."

## Version 0.9.2 (June 30th, 2021)

This patch release fixes a Braket integration bug (gh-767).
It also adds an example about Clifford data regression in the documentation.

### All Changes

- Ensure short circuit warning is multi-platform (@andreamari gh-769).
- Add CDR example to docs + small change to `cdr.calculate_observable` (@rmlarose, gh-750).


## Version 0.9.1 (June 23rd, 2021)

This is a patch release to fix two bugs (gh-736, gh-737) related to the integration with optional packages.
Expand Down
2 changes: 1 addition & 1 deletion VERSION.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.9.1
0.9.2
210 changes: 210 additions & 0 deletions docs/source/examples/cdr_api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
---
jupytext:
text_representation:
extension: .md
format_name: myst
format_version: 0.13
jupytext_version: 1.11.3
kernelspec:
display_name: Python 3
language: python
name: python3
---

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

## Setup

+++

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

1. A quantum circuit to prepare a state $\rho$.
1. A quantum computer or noisy simulator to sample bitstrings from $\rho$.
1. One or more observables $O$ which specify what we wish to compute: $\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),
cirq.measure(a, b, key="z"),
)
circuit
```

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

```{code-cell} ipython3
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.

```{code-cell} ipython3
sample_bitstrings(circuit)
```

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

```{code-cell} ipython3
# 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.

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

```{code-cell} ipython3
cdr.execute.calculate_observable(
state_or_measurements=sample_bitstrings_simulator(circuit),
observable=obs,
)
```

### The noisy result

```{code-cell} ipython3
cdr.execute.calculate_observable(
state_or_measurements=sample_bitstrings(circuit),
observable=obs,
)
```

### The mitigated result

```{code-cell} ipython3
cdr.execute_with_cdr(
circuit=circuit,
executor=sample_bitstrings,
observables=[obs],
simulator=sample_bitstrings_simulator,
)
```

## 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=circuit,
executor=sample_bitstrings,
observables=[obs],
simulator=sample_bitstrings_simulator,
num_training_circuits=5,
)
```

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=circuit,
executor=sample_bitstrings,
observables=[obs],
simulator=sample_bitstrings_simulator,
fit_function=cdr.linear_fit_function_no_intercept,
)
```

### 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=circuit,
executor=sample_bitstrings,
observables=[obs],
simulator=sample_bitstrings_simulator,
scale_factors=(1, 3),
scale_noise=scaling.fold_gates_at_random,
)
```
1 change: 1 addition & 0 deletions docs/source/examples/examples.myst
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ ibmq-backends.rst
hamiltonians.rst
simple_landscape.rst
maxcut-demo.myst
cdr_api.md
test.myst
```
2 changes: 1 addition & 1 deletion mitiq/cdr/_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ def executor(
circuit.append(cirq.measure(*circuit.all_qubits(), key="z"))

result = cirq.DensityMatrixSimulator().run(circuit, repetitions=shots)
return {bin(k): v for k, v in result.histogram(key="z").items()}
return result.histogram(key="z")


def simulator(circuit: QPROGRAM, shots: int = 8192) -> MeasurementResult:
Expand Down
6 changes: 3 additions & 3 deletions mitiq/cdr/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import numpy as np

MeasurementResult = Union[Dict[bin, int], CounterType[bin]]
MeasurementResult = Union[Dict[int, int], CounterType[int]]


def calculate_observable(
Expand Down Expand Up @@ -50,7 +50,7 @@ def calculate_observable(
elif isinstance(state_or_measurements, (dict, Counter)):
probs = normalize_measurements(state_or_measurements)
observable_values = [
observable[i] * probs.get(bin(i), 0.0) for i in range(2 ** nqubits)
observable[i] * probs.get(i, 0.0) for i in range(2 ** nqubits)
]
else:
raise ValueError(
Expand All @@ -61,7 +61,7 @@ def calculate_observable(
return sum(np.real(observable_values))


def normalize_measurements(counts: MeasurementResult) -> Dict[bin, float]:
def normalize_measurements(counts: MeasurementResult) -> Dict[int, float]:
"""Normalizes the values of the MeasurementResult to get probabilities.
Args:
Expand Down
2 changes: 1 addition & 1 deletion mitiq/cdr/tests/test_execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
from mitiq.cdr._testing import simulator_statevector, simulator

# Observables.
sigma_z = np.diag(np.diag([1, -1]))
sigma_z = np.array([1, -1])


@pytest.mark.parametrize(
Expand Down
28 changes: 16 additions & 12 deletions mitiq/zne/inference.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
import numpy as np
from numpy.lib.polynomial import RankWarning
from scipy.optimize import curve_fit, OptimizeWarning
from cirq import Circuit

from mitiq import QPROGRAM
from mitiq.collector import Collector
from mitiq.interface import accept_any_qprogram_as_input


ExtrapolationResult = Union[
Expand Down Expand Up @@ -94,6 +96,16 @@ class ConvergenceWarning(Warning):
pass


@accept_any_qprogram_as_input
def _check_circuit_length(circuit: Circuit) -> None:
"""Raises a warning if the circuit is too short."""
if len(list(circuit.all_operations())) < 5:
warnings.warn(
"The input circuit is very short. "
"This may reduce the accuracy of noise scaling."
)


def mitiq_curve_fit(
ansatz: Callable[..., float],
scale_factors: Sequence[float],
Expand Down Expand Up @@ -518,15 +530,11 @@ def run(
self.reset()
self._batch_populate_instack()

_check_circuit_length(qp)

# Get all noise-scaled circuits to run
to_run = self._generate_circuits(qp, scale_noise, num_to_average)

if len(qp) < 5:
warnings.warn(
"The input circuit is very short. "
"This may reduce the accuracy of noise scaling."
)

# Get the list of keywords associated to each circuit in "to_run"
kwargs_list = self._get_keyword_args(num_to_average)

Expand Down Expand Up @@ -718,6 +726,8 @@ def run(
max_iterations: Maximum number of iterations (optional).
"""

_check_circuit_length(qp)

def scale_factor_to_expectation_value(
scale_factor: float, **exec_params: Any
) -> float:
Expand All @@ -729,12 +739,6 @@ def scale_factor_to_expectation_value(
expectation_values.append(executor(scaled_qp, **exec_params))
return np.average(expectation_values)

if len(qp) < 5:
warnings.warn(
"The input circuit is very short. "
"This may reduce the accuracy of noise scaling."
)

return self.run_classical(
scale_factor_to_expectation_value, max_iterations
)
Expand Down
Loading

0 comments on commit e5fd404

Please sign in to comment.