Skip to content

Commit

Permalink
CDR Tutorial with Qrack (#2451)
Browse files Browse the repository at this point in the history
* New example md file from ipynb

* New thumbnail for tutorial

* Add CDR with Qrack to Examples TOC

* Match thumbnail to tutorial

* Update md file to install Qrack

* Restart kernel if qrack needs to be install

* Add pyqrack to for building docs

* Removed pip install but added information in markdown

* Set the latest pyqrack

* Update with PYQRACK_SHARED_LIB_PATH

* Build Qrack for docs build remove  PYQRACK_SHARED_LIB_PATH

* Installing openCL per suggestion

* Adding sudo to apt calls

* Move apt call out of requirements

* Update apt format

* Put apt info back into docs build

* Cleaned up links to be relative and removed unnecessary line

* Remove unnecessary sentence about density matrices

Co-authored-by: nate stemen <[email protected]>

* Update execute_with_cdr to full function call

Co-authored-by: nate stemen <[email protected]>

* Remove unnecessary code explanation

Co-authored-by: nate stemen <[email protected]>

* Remove observable=None from execute_with_cdr

Co-authored-by: nate stemen <[email protected]>

* Update random_state and conclusion

* Add section explaining executors

* Added final sentences with other references and contact info

---------

Co-authored-by: nate stemen <[email protected]>
  • Loading branch information
bdg221 and natestemen committed Aug 5, 2024
1 parent aa1235d commit a8f88cb
Show file tree
Hide file tree
Showing 7 changed files with 195 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .github/workflows/docs-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ jobs:

- name: Build and test Sphinx docs
run: |
sudo apt update
sudo apt install ocl-icd-opencl-dev
export BQSKIT_DOC_CHECK_OVERRIDE=1
export PYDEVD_DISABLE_FILE_VALIDATION=1
make docs
Expand Down
2 changes: 2 additions & 0 deletions .readthedocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ build:
os: ubuntu-22.04
tools:
python: "3.12"
apt_packages:
- ocl-icd-opencl-dev

# Build documentation in the docs/ directory with Sphinx
sphinx:
Expand Down
Binary file added docs/source/_thumbnails/cdr-qrack.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ def get_incollection_template(self, e):
"examples/quantum_simulation_scars_ibmq": "_static/qmbs_ibmq.png",
"examples/zne_logical_rb_cirq_stim": "_static/mitiq_stim_logo.png",
"examples/quantum_simulation_1d_ising": "_static/quantum_simulation.png",
"examples/cdr_qrack": "_static/cdr-qrack.png",
# default images if no thumbnail is specified
"examples/*": "_static/mitiq-logo.png",
}
187 changes: 187 additions & 0 deletions docs/source/examples/cdr_qrack.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
---
jupytext:
formats: ipynb,md:myst
text_representation:
extension: .md
format_name: myst
format_version: 0.13
jupytext_version: 1.16.3
kernelspec:
display_name: Python 3
language: python
name: python3
---

# CDR with Qrack as Near-Clifford Simulator

In this tutorial, [Clifford Data Regression](../guide/cdr.md) (CDR) is used with [Qrack](https://qrack.readthedocs.io/en/latest/) and a Qiskit fake backend.

+++

## Setup

To start, relevant modules and libraries are imported. Please ensure that the following Python modules are installed: `mitiq`, `numpy`, `pyqrack`, `cirq`, `qiskit`

+++

```{note}
In the code below the environmental variable, `QRACK_MAX_CPU_QB`, is set to `-1`. This enviroment variable sets the maximum on how many qubits can be allocated on a single QEngineCPU instance. More information can be found on the [Qrack README page](https://github.com/unitaryfund/qrack?tab=readme-ov-file#maximum-allocation-guard).
```

```{code-cell}
import numpy as np
import collections
import os
import warnings
warnings.simplefilter("ignore", np.ComplexWarning)
from pyqrack import QrackSimulator, QrackCircuit
os.environ["QRACK_MAX_CPU_QB"]="-1"
import mitiq.interface.mitiq_qiskit
from mitiq.interface.mitiq_cirq import compute_density_matrix
from mitiq import cdr, Observable, PauliString
import cirq
from qiskit.providers.fake_provider import Fake5QV1
```

## Sample Circuit

This sample circuit includes Clifford gates (`H`, `CNOT`, `RX`) and non-Clifford gates (`RZ`).

```{code-cell}
a, b = cirq.LineQubit.range(2)
circuit = cirq.Circuit(
cirq.H.on(a), # Clifford
cirq.H.on(b), # Clifford
cirq.rz(1.75).on(a),
cirq.rz(2.31).on(b),
cirq.CNOT.on(a, b), # Clifford
cirq.rz(-1.17).on(b),
cirq.rz(3.23).on(a),
cirq.rx(np.pi / 2).on(a), # Clifford
cirq.rx(np.pi / 2).on(b), # Clifford
)
# CDR works better if the circuit is not too short. So we increase its depth.
circuit = 5 * circuit
print(circuit)
```

## Devices and Near-Clifford Simulator

During CDR, near-Clifford representations of the original circuit are generated. Those near-Clifford representations are run with a near-Clifford simulator (Qrack Near-Clifford Simulator) and also on the quantum device (or noisy simulator, like Qiskit Fake Backend.) In this example, we also want to show how the unmitigated and mitigated results compare to the exact results, so we will use another simulator (Cirq Simulator) to generate the ideal results.

+++

### Qrack Near-Clifford Simulator

Especially when using CDR at scale, it is important to use an efficient Near-Clifford circuit simulator. In this example, Qrack will be configured and used as the Near-Clifford Simulator. The `qrack_simulate` method accepts a Cirq circuit and the number of shots as parameters. The Qrack simulator is then called and the expectation value for `00` is returned.

Since CDR includes the generation of near-Clifford circuits representing the original circuit, a near-Clifford simulator is ideal for efficiently simulating the circuit. Again, this is particularly important when using CDR at scale.

```{code-cell}
def qrack_simulate(circuit: cirq.Circuit, shots=1000) -> float:
"""Returns the expectation value of 00 from the state prepared by the circuit
executed without noise by Qrack configured as a near-Clifford simulator.
"""
# Cirq -> Qiskit circuit
qiskit_circ = mitiq.interface.mitiq_qiskit.to_qiskit(circuit)
# Qiskit -> Qrack circuit
qcircuit = QrackCircuit.in_from_qiskit_circuit(qiskit_circ)
# Setup the Qrack simulator and run it
qsim = QrackSimulator(qiskit_circ.width(), isStabilizerHybrid=True, isTensorNetwork=False, isSchmidtDecomposeMulti=False, isSchmidtDecompose=False, isOpenCL=False)
qcircuit.run(qsim)
# Use shot measurements to return the expectation value of 00
results = qsim.measure_shots(q=list(range(qiskit_circ.width())), s=shots)
results = dict(collections.Counter(results))
for key, value in results.items():
results[key] = value / shots
return results[0]
```

### Qiskit Fake Backend

CDR requires the use of a quantum device or a noisy simulator. The near-Clifford circuits that are generated from the original circuit are executed on the quantum device or noisy simulator in order to compare against the simulated results.

In this example, a Qiskit 5 Qubit Fake Backend is used. The [Fake5QV1](https://docs.quantum.ibm.com/api/qiskit/qiskit.providers.fake_provider.Fake5QV1) uses configurations and noise settings taken previously from the 5 qubit IBM Quantum Yorktown device. The `qiskit_noisy` function takes the Cirq circuit and uses Mitiq to change it into a Qiskit circuit. After adding measurements, the circuit is run on the fake backend. The expectation value for `00` is then returned.

```{code-cell}
# Use Qiskit's Fave5QV1 as a noisy simulator
def qiskit_noisy(circuit: cirq.Circuit, shots=1000):
"""Execute the input circuit and return the expectation value of |00..0><00..0|"""
# Cirq -> Qiskit circuit
qiskit_circ = mitiq.interface.mitiq_qiskit.to_qiskit(circuit)
# Add measurement gates to the circuit
qiskit_circ.measure_all()
# Setup the fake backend and run the circuit
noisy_backend = Fake5QV1()
job = noisy_backend.run(qiskit_circ, shots=shots)
# Use the resulting counts to return the expectation value of 00
counts = job.result().get_counts()
ret_val = counts[qiskit_circ.num_qubits * "0"] / shots
return ret_val
```

### Cirq Simulator for exact result

The `compute_density_matrix` is the Cirq density matrix simulator with a Mitiq wrapper. It is used to obtain the exact `00` expectation value. This is then used to determine the accuracy of the mitigated and unmitigated reuslts.

```{code-cell}
def cirq_simulate(circuit: cirq.Circuit) -> np.ndarray:
"""Returns Tr[ρ |0⟩⟨0|] where ρ is the state prepared by the circuit
executed without depolarizing noise.
"""
res = compute_density_matrix(circuit, noise_level=(0.0,))
return res[0, 0].real
```

## Executing CDR

With the different executor functions defined for running the Qrack, Qiskit, and Cirq simulators, the `mitiq.cdr.execute_with_cdr` function can now be called.

```{code-cell}
ideal_expval = cirq_simulate(circuit).round(5)
print(f"Ideal expectation value from Cirq Simulator: {ideal_expval:.3f}")
unmitigated_expval = qiskit_noisy(circuit)
print(f"Unmitigated expectation value from Qiskit Fake backend: {unmitigated_expval:.3f}")
mitigated_expval = cdr.execute_with_cdr(
circuit,
qiskit_noisy,
simulator=qrack_simulate,
random_state=0,
)
print(f"Mitigated expectation value with Mitiq CDR: {mitigated_expval:.3f}\n")
```

## Conclusion
By learning from the near-Clifford circuits that resemble the original circuit, CDR is able to apply the noise information learned to the original circuit. From the example, you can see that the mitigated expectation value is closer to the ideal value.

The improvement factor with CDR can be seen below to highlight the mitigated results that can be provided with this technique.

```{code-cell}
unmitigated_error = abs(unmitigated_expval - ideal_expval)
mitigated_error = abs(mitigated_expval - ideal_expval)
print(f"Unmitigated Error: {unmitigated_error:.3f}")
print(f"Mitigated Error: {mitigated_error:.3f}")
improvement_factor = unmitigated_error / mitigated_error
print(f"Improvement factor with CDR: {improvement_factor:.2f}")
```

To learn more about CDR, please check the [User Guide](../guide/cdr.md) or the [ZNE and CDR with Cirq: 1D Ising Simulation](./quantum_simulation_1d_ising.md) example. If you have any questions, please do not hesitate to open a [Github discussion](https://github.com/unitaryfund/mitiq/discussions) or reach out to the Mitiq team on [Discord](http://discord.unitary.fund).
3 changes: 2 additions & 1 deletion docs/source/examples/examples.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Examples

Below you can find a gallery of tutorials applying Zero Noise Extrapolation (ZNE), Probabilistic Error Cancellation (PEC), and Digital Dynamical Decoupling (DDD) with Mitiq:
Below you can find a gallery of tutorials applying Zero Noise Extrapolation (ZNE), Probabilistic Error Cancellation (PEC), Clifford Data Regression (CDR), and Digital Dynamical Decoupling (DDD) with Mitiq:

```{nbgallery}
ZNE Calibration with Qiskit <calibration-tutorial.md>
Expand Down Expand Up @@ -32,6 +32,7 @@ Classical Shadows with Cirq: State Reconstruction and Observable Estimation <sha
Robust Shadow Estimation with Cirq: Pauli Twirling Calibration of Classical Shadows <rshadows_tutorial.md>
DDD with Cirq: Mirror circuits <ddd_tutorial.md>
DDD with Qiskit: GHZ circuits <ddd_on_ibmq_ghz.md>
CDR with Qrack as Near-Clifford Simulator <cdr_qrack.md>
GGI Summer School ZNE Hands-On Tutorial <ggi_summer_school_unsolved.md>
Composing techniques: REM + ZNE <combine_rem_zne.md>
Composing techniques: DDD + ZNE <combine_ddd_zne.md>
Expand Down
1 change: 1 addition & 0 deletions requirements/requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ bqskit==1.1.1
seaborn==0.13.0
stim==1.13.0
stimcirq==1.13.0
pyqrack==1.30.0

0 comments on commit a8f88cb

Please sign in to comment.