{"cells":[{"cell_type":"markdown","metadata":{},"source":["# Comparison of the simulators available through TKET\n","\n","**Download this notebook - {nb-download}`comparing_simulators.ipynb`**"]},{"cell_type":"markdown","metadata":{},"source":["In this tutorial, we will focus on:
\n","- exploring the wide array of simulators available through the extension modules for `pytket`;
\n","- comparing their unique features and capabilities."]},{"cell_type":"markdown","metadata":{},"source":["This example assumes the reader is familiar with the basics of circuit construction and evaluation.
\n","
\n","To run every option in this example, you will need `pytket`, `pytket-qiskit`, `pytket-pyquil`, `pytket-qulacs`, and `pytket-projectq`.
\n","
\n","With the number of simulator `Backend`s available across the `pytket` extension modules, we are often asked why to use one over another. Surely, any two simulators are equivalent if they are able to sample the circuits in the same way, right? Not quite. In this notebook we go through each of the simulators in turn and describe what sets them apart from others and how to make use of any unique features.
\n","
\n","But first, to demonstrate the significant overlap in functionality, we'll just give some examples of common usage for different types of backends."]},{"cell_type":"markdown","metadata":{},"source":["## Sampling simulator usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerBackend"]},{"cell_type":"markdown","metadata":{},"source":["Define a circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3, 3)\n","c.Ry(0.7, 0)\n","c.CX(0, 1)\n","c.X(2)\n","c.measure_all()"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=2000)\n","counts = backend.get_result(handle).get_counts()\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## Statevector simulator usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerStateBackend"]},{"cell_type":"markdown","metadata":{},"source":["Build a quantum state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.H(0).CX(0, 1)\n","c.Rz(0.3, 0)\n","c.Rz(-0.3, 1)\n","c.Ry(0.8, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Examine the statevector:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerStateBackend()\n","c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c)\n","state = backend.get_result(handle).get_state()\n","print(state)"]},{"cell_type":"markdown","metadata":{},"source":["## Expectation value usage"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit, Qubit\n","from pytket.extensions.qiskit import AerBackend, AerStateBackend\n","from pytket.pauli import Pauli, QubitPauliString\n","from pytket.utils.operators import QubitPauliOperator"]},{"cell_type":"markdown","metadata":{},"source":["Build a quantum state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.H(0).CX(0, 1)\n","c.Rz(0.3, 0)\n","c.Rz(-0.3, 1)\n","c.Ry(0.8, 2)"]},{"cell_type":"markdown","metadata":{},"source":["Define the measurement operator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["xxi = QubitPauliString({Qubit(0): Pauli.X, Qubit(1): Pauli.X})\n","zzz = QubitPauliString({Qubit(0): Pauli.Z, Qubit(1): Pauli.Z, Qubit(2): Pauli.Z})\n","op = QubitPauliOperator({xxi: -1.8, zzz: 0.7})"]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerBackend()\n","c = backend.get_compiled_circuit(c)\n","exp = backend.get_operator_expectation_value(c, op)\n","print(exp)"]},{"cell_type":"markdown","metadata":{},"source":["## `AerBackend`"]},{"cell_type":"markdown","metadata":{},"source":["`AerBackend` wraps up the `qasm_simulator` from the Qiskit Aer package. It supports an extremely flexible set of circuits and uses many effective simulation methods making it a great all-purpose sampling simulator.
\n","
\n","Unique features:
\n","- supports mid-circuit measurement and OpenQASM-style conditional gates;
\n","- encompasses a variety of underlying simulation methods and automatically selects the best one for each circuit (including statevector, density matrix, (extended) stabilizer and matrix product state);
\n","- can be provided with a `qiskit.providers.Aer.noise.NoiseModel` on instantiation to perform a noisy simulation."]},{"cell_type":"markdown","metadata":{},"source":["Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerBackend\n","from itertools import combinations\n","from qiskit_aer.noise import NoiseModel, depolarizing_error"]},{"cell_type":"markdown","metadata":{},"source":["Quantum teleportation circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit()\n","alice = c.add_q_register(\"a\", 2)\n","bob = c.add_q_register(\"b\", 1)\n","data = c.add_c_register(\"d\", 2)\n","final = c.add_c_register(\"f\", 1)"]},{"cell_type":"markdown","metadata":{},"source":["Start in an interesting state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.Rx(0.3, alice[0])"]},{"cell_type":"markdown","metadata":{},"source":["Set up a Bell state between Alice and Bob:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.H(alice[1]).CX(alice[1], bob[0])"]},{"cell_type":"markdown","metadata":{},"source":["Measure Alice's qubits in the Bell basis:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.CX(alice[0], alice[1]).H(alice[0])\n","c.Measure(alice[0], data[0])\n","c.Measure(alice[1], data[1])"]},{"cell_type":"markdown","metadata":{},"source":["Correct Bob's qubit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=1)\n","c.X(bob[0], condition_bits=[data[0], data[1]], condition_value=3)\n","c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=2)\n","c.Z(bob[0], condition_bits=[data[0], data[1]], condition_value=3)"]},{"cell_type":"markdown","metadata":{},"source":["Measure Bob's qubit to observe the interesting state:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c.Measure(bob[0], final[0])"]},{"cell_type":"markdown","metadata":{},"source":["Set up a noisy simulator:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["model = NoiseModel()\n","dep_err = depolarizing_error(0.04, 2)\n","for i, j in combinations(range(3), r=2):\n"," model.add_quantum_error(dep_err, [\"cx\"], [i, j])\n"," model.add_quantum_error(dep_err, [\"cx\"], [j, i])\n","backend = AerBackend(noise_model=model)"]},{"cell_type":"markdown","metadata":{},"source":["Run circuit:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = backend.get_compiled_circuit(c)\n","handle = backend.process_circuit(c, n_shots=2000)\n","result = backend.get_result(handle)\n","counts = result.get_counts([final[0]])\n","print(counts)"]},{"cell_type":"markdown","metadata":{},"source":["## `AerStateBackend`"]},{"cell_type":"markdown","metadata":{},"source":["`AerStateBackend` provides access to Qiskit Aer's `statevector_simulator`. It supports a similarly large gate set and has competitive speed for statevector simulations.
\n","
\n","Useful features:
\n","- no dependency on external executables, making it easy to install and run on any computer;
\n","- support for fast expectation value calculations according to `QubitPauliString`s or `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `AerUnitaryBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Finishing the set of simulators from Qiskit Aer, `AerUnitaryBackend` captures the `unitary_simulator`, allowing for the entire unitary of a pure quantum process to be calculated. This is especially useful for testing small subcircuits that will be used many times in a larger computation.
\n","
\n","Unique features:
\n","- provides the full unitary matrix for a pure quantum circuit."]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["from pytket import Circuit\n","from pytket.extensions.qiskit import AerUnitaryBackend\n","from pytket.predicates import NoClassicalControlPredicate"]},{"cell_type":"markdown","metadata":{},"source":["Define a simple quantum incrementer:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["c = Circuit(3)\n","c.CCX(2, 1, 0)\n","c.CX(2, 1)\n","c.X(2)"]},{"cell_type":"markdown","metadata":{},"source":["Examine the unitary:"]},{"cell_type":"code","execution_count":null,"metadata":{},"outputs":[],"source":["backend = AerUnitaryBackend()\n","c = backend.get_compiled_circuit(c)\n","result = backend.run_circuit(c)\n","unitary = result.get_unitary()\n","print(unitary.round(1).real)"]},{"cell_type":"markdown","metadata":{},"source":["## `ForestBackend`"]},{"cell_type":"markdown","metadata":{},"source":["Whilst it can, with suitable credentials, be used to access the Rigetti QPUs, the `ForestBackend` also features a simulator mode which turns it into a noiseless sampling simulator that matches the constraints of the simulated device (e.g. the same gate set, restricted connectivity, measurement model, etc.). This is useful when playing around with custom compilation strategies to ensure that your final circuits are suitable to run on the device and for checking that your overall program works fine before you invest in reserving a QPU.
\n","
\n","Unique features:
\n","- faithful recreation of the circuit constraints of Rigetti QPUs."]},{"cell_type":"markdown","metadata":{},"source":["If trying to use the `ForestBackend` locally (i.e. not on a Rigetti QMI), you will need to have `quilc` and `qvm` running as separate processes in server mode. One easy way of doing this is with `docker` (see the `quilc` and `qvm` documentation for alternative methods of running them):
\n","`docker run --rm -it -p 5555:5555 rigetti/quilc -R`
\n","`docker run --rm -it -p 5000:5000 rigetti/qvm -S`"]},{"cell_type":"markdown","metadata":{},"source":["## `ForestStateBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The Rigetti `pyquil` package also provides the `WavefunctionSimulator`, which we present as the `ForestStateBackend`. Functionally, it is very similar to the `AerStateBackend` so can be used interchangeably. It does require that `quilc` and `qvm` are running as separate processes when not running on a Rigetti QMI.
\n","
\n","Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["Run on the backend:"]},{"cell_type":"markdown","metadata":{},"source":["## `QulacsBackend`"]},{"cell_type":"markdown","metadata":{},"source":["The `QulacsBackend` is an all-purpose simulator with both sampling and statevector modes, using the basic CPU simulator from Qulacs.
\n","
\n","Unique features:
\n","- supports both sampling (shots/counts) and complete statevector outputs."]},{"cell_type":"markdown","metadata":{},"source":["Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]},{"cell_type":"markdown","metadata":{},"source":["## `QulacsGPUBackend`"]},{"cell_type":"markdown","metadata":{},"source":["If the GPU version of Qulacs is installed, the `QulacsGPUBackend` will use that to benefit from even faster speeds. It is very easy to get started with using a GPU, as it only requires a CUDA installation and the `qulacs-gpu` package from `pip`. Functionally, it is identical to the `QulacsBackend`, but potentially faster if you have GPU resources available.
\n","
\n","Unique features:
\n","- GPU support for very fast simulation."]},{"cell_type":"markdown","metadata":{},"source":["## `ProjectQBackend`"]},{"cell_type":"markdown","metadata":{},"source":["ProjectQ is a popular quantum circuit simulator, thanks to its availability and ease of use. It provides a similar level of performance and features to `AerStateBackend`.
\n","
\n","Useful features:
\n","- support for fast expectation value calculations according to `QubitPauliString`s or Hermitian `QubitPauliOperator`s."]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.6.4"}},"nbformat":4,"nbformat_minor":2}