{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "cellView": "form", "execution": { "iopub.execute_input": "2024-08-16T10:15:15.462243Z", "iopub.status.busy": "2024-08-16T10:15:15.461712Z", "iopub.status.idle": "2024-08-16T10:15:15.465856Z", "shell.execute_reply": "2024-08-16T10:15:15.465197Z" }, "id": "a7f65e34937b" }, "outputs": [], "source": [ "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n", "# you may not use this file except in compliance with the License.\n", "# You may obtain a copy of the License at\n", "#\n", "# https://www.apache.org/licenses/LICENSE-2.0\n", "#\n", "# Unless required by applicable law or agreed to in writing, software\n", "# distributed under the License is distributed on an \"AS IS\" BASIS,\n", "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n", "# See the License for the specific language governing permissions and\n", "# limitations under the License." ] }, { "cell_type": "markdown", "metadata": { "id": "c005a2665283" }, "source": [ "# Representing noise" ] }, { "cell_type": "markdown", "metadata": { "id": "HYkRhx2pe2XX" }, "source": [ "\n", " \n", " \n", " \n", " \n", "
\n", " View on QuantumAI\n", " \n", " Run in Google Colab\n", " \n", " View source on GitHub\n", " \n", " Download notebook\n", "
" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:15.469236Z", "iopub.status.busy": "2024-08-16T10:15:15.468733Z", "iopub.status.idle": "2024-08-16T10:15:32.840090Z", "shell.execute_reply": "2024-08-16T10:15:32.839139Z" }, "id": "d063c007c647" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "installing cirq...\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "\u001b[31mERROR: pip's dependency resolver does not currently take into account all the packages that are installed. This behaviour is the source of the following dependency conflicts.\r\n", "tensorflow-metadata 1.15.0 requires protobuf<4.21,>=3.20.3; python_version < \"3.11\", but you have protobuf 4.25.4 which is incompatible.\u001b[0m\u001b[31m\r\n", "\u001b[0m" ] }, { "name": "stdout", "output_type": "stream", "text": [ "installed cirq.\n" ] } ], "source": [ "try:\n", " import cirq\n", "except ImportError:\n", " print(\"installing cirq...\")\n", " !pip install --quiet cirq\n", " print(\"installed cirq.\")\n", " import cirq" ] }, { "cell_type": "markdown", "metadata": { "id": "755ba122d550" }, "source": [ "This doc assumes you have already read the [noisy simulation](../simulate/noisy_simulation.ipynb) tutorial.\n", "\n", "Cirq provides several built-in tools for representing noise at multiple levels:\n", "- Channels, to insert as individual noisy operators\n", "- `cirq.NoiseModel`s, for applying noise to entire circuits\n", "- `cirq.MeasurementGate` parameters, for changing measurements results\n", "\n", "This doc describes these options and the types of real-world noise they can be used to represent." ] }, { "cell_type": "markdown", "metadata": { "id": "efe093cfa38e" }, "source": [ "## Channels\n", "\n", "Errors in hardware can be broadly separated into two categories: _coherent_ and _incoherent_.\n", "\n", "**Coherent** errors apply a reversible (but unknown) transformation, such as making every $Z$ gate instead behave as $Z^{1.01}$. This can be represented by inserting [gates](../build/gates.ipynb) into the intended circuit.\n", "\n", "**Incoherent** errors cause [decoherence](https://en.wikipedia.org/wiki/Quantum_decoherence#Non-unitary_modelling_examples) of the quantum state, and are irreversible as a result. This is equivalent to applying an operation with some probability $0 < P < 1$, and can be represented with Cirq \"channels\". [`ops/common_channels.py`](https://github.com/quantumlib/Cirq/blob/main/cirq-core/cirq/ops/common_channels.py) defines channels for some of the most common incoherent errors, which are described below." ] }, { "cell_type": "markdown", "metadata": { "id": "775a6df1be6d" }, "source": [ "### Bit flip\n", "\n", "`cirq.BitFlipChannel` (or `cirq.bit_flip`) is equivalent to applying `cirq.X` with a given probability. This channel is best used to represent state-agnostic bit flip errors in the body of a circuit." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:32.844848Z", "iopub.status.busy": "2024-08-16T10:15:32.844136Z", "iopub.status.idle": "2024-08-16T10:15:33.620649Z", "shell.execute_reply": "2024-08-16T10:15:33.619890Z" }, "id": "60f9f3b43759" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Counter({0: 795, 1: 205})\n" ] } ], "source": [ "q0 = cirq.LineQubit(0)\n", "circuit = cirq.Circuit(\n", " cirq.bit_flip(p=0.2).on(q0),\n", " cirq.measure(q0, key='result')\n", ")\n", "result = cirq.Simulator(seed=0).run(circuit, repetitions=1000)\n", "print(result.histogram(key='result'))" ] }, { "cell_type": "markdown", "metadata": { "id": "b526c2d39fe2" }, "source": [ "For bit flips which depend on the qubit state, see [Amplitude damping](#amplitude-damping).\n", "\n", "For measurement error that doesn't affect the quantum state, see [Invert mask](#invert-mask)." ] }, { "cell_type": "markdown", "metadata": { "id": "352e1b654056" }, "source": [ "### Amplitude damping\n", "\n", "`cirq.AmplitudeDampingChannel` (or `cirq.amplitude_damp`) performs a $|1\\rangle \\rightarrow |0\\rangle$ transformation with some probability `gamma`, leaving the existing $|0\\rangle$ state alone. This channel is best used to represent an idealized form of energy dissipation, where qubits decay from $|1\\rangle$ to $|0\\rangle$." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:33.624720Z", "iopub.status.busy": "2024-08-16T10:15:33.624104Z", "iopub.status.idle": "2024-08-16T10:15:34.068618Z", "shell.execute_reply": "2024-08-16T10:15:34.067899Z" }, "id": "2e8b3868bf6c" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Counter({1: 795, 0: 205})\n" ] } ], "source": [ "q0 = cirq.LineQubit(0)\n", "circuit = cirq.Circuit(\n", " cirq.X(q0),\n", " cirq.amplitude_damp(gamma=0.2).on(q0),\n", " cirq.measure(q0, key='result')\n", ")\n", "result = cirq.Simulator(seed=0).run(circuit, repetitions=1000)\n", "print(result.histogram(key='result'))" ] }, { "cell_type": "markdown", "metadata": { "id": "352e1b654056" }, "source": [ "For state-agnostic bit flips, see [Bit flip](#bit-flip)." ] }, { "cell_type": "markdown", "metadata": { "id": "352e1b654056" }, "source": [ "### Generalized amplitude damping\n", "\n", "`cirq.GeneralizedAmplitudeDampingChannel` (or `cirq.generalized_amplitude_damp`) is a generalized version of `AmplitudeDampingChannel`. It represent a more realistic bidirectional energy dissipation, in which qubits experience not only decay but also spontaneous excitation. In this channel, `gamma` represents the probability of energy transfer (excitation OR decay) and a new parameter `p` gives the probability that the environment is excited.\n", "\n", "This is equivalent to excitation with probability `(1-p) * gamma` and decay with probability `p * gamma`." ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:34.072380Z", "iopub.status.busy": "2024-08-16T10:15:34.071809Z", "iopub.status.idle": "2024-08-16T10:15:34.923835Z", "shell.execute_reply": "2024-08-16T10:15:34.923038Z" }, "id": "dd677fbd36df" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Starting in |0): Counter({0: 824, 1: 176})\n", "Starting in |1): Counter({1: 956, 0: 44})\n" ] } ], "source": [ "q0, q1 = cirq.LineQubit.range(2)\n", "circuit = cirq.Circuit(\n", " cirq.X(q1),\n", " cirq.generalized_amplitude_damp(gamma=0.2, p=0.2).on_each(q0, q1),\n", " cirq.measure(q0, key='result_0'),\n", " cirq.measure(q1, key='result_1'),\n", ")\n", "result = cirq.Simulator(seed=0).run(circuit, repetitions=1000)\n", "print(\"Starting in |0):\", result.histogram(key='result_0'))\n", "print(\"Starting in |1):\", result.histogram(key='result_1'))" ] }, { "cell_type": "markdown", "metadata": { "id": "2b57b0db415c" }, "source": [ "### Phase flip or damping\n", "\n", "`cirq.PhaseFlipChannel` (or `cirq.phase_flip`) is equivalent to applying `cirq.Z` with a given probability `p`. This channel is best used to represent state-agnostic phase flip errors in the body of a circuit." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:34.927702Z", "iopub.status.busy": "2024-08-16T10:15:34.927037Z", "iopub.status.idle": "2024-08-16T10:15:35.808194Z", "shell.execute_reply": "2024-08-16T10:15:35.807402Z" }, "id": "0fd69edd7133" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Phase flip: Counter({0: 795, 1: 205})\n" ] } ], "source": [ "q0 = cirq.LineQubit(0)\n", "circuit = cirq.Circuit(\n", " cirq.H(q0),\n", " cirq.phase_flip(p=0.2).on(q0),\n", " cirq.H(q0),\n", " cirq.measure(q0, key='result')\n", ")\n", "result = cirq.Simulator(seed=0).run(circuit, repetitions=1000)\n", "print(\"Phase flip:\", result.histogram(key='result'))" ] }, { "cell_type": "markdown", "metadata": { "id": "2b57b0db415c" }, "source": [ "`cirq.PhaseDampingChannel` (or `cirq.phase_damp`) is a different way of expressing the same behavior: for any given value of `p`, `PhaseFlipChannel(p=p)` is equivalent to `PhaseDampingChannel(gamma=(1-(2*p-1)**2))`." ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:35.812333Z", "iopub.status.busy": "2024-08-16T10:15:35.811806Z", "iopub.status.idle": "2024-08-16T10:15:36.344342Z", "shell.execute_reply": "2024-08-16T10:15:36.343610Z" }, "id": "665f6c0b5956" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "gamma=0.64\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Phase damp: Counter({0: 777, 1: 223})\n" ] } ], "source": [ "q0 = cirq.LineQubit(0)\n", "# Convert p=0.2 to gamma\n", "p = 0.2\n", "gamma = 1 - (2 * p - 1) ** 2\n", "print(f\"{gamma=}\")\n", "circuit = cirq.Circuit(\n", " cirq.H(q0),\n", " cirq.phase_damp(gamma=gamma).on(q0),\n", " cirq.H(q0),\n", " cirq.measure(q0, key='result')\n", ")\n", "result = cirq.Simulator(seed=0).run(circuit, repetitions=1000)\n", "print(\"Phase damp:\", result.histogram(key='result'))" ] }, { "cell_type": "markdown", "metadata": { "id": "c64aa7e18262" }, "source": [ "Note that the results differ despite the same seed and equivalent circuits. This is due to the channels having different operators, which interact differently with Cirq's RNG." ] }, { "cell_type": "markdown", "metadata": { "id": "f45f3d04073b" }, "source": [ "### Depolarization\n", "\n", "`cirq.DepolarizingChannel` (or `cirq.depolarize`) is equivalent to applying a randomly-selected Pauli operator to the target qubits. The identity is applied with probability `1-p`; all other Pauli operators have an equal probability `p / (4**n-1)` of being selected. This channel is best used for representing uniformly-distributed decoherence of the target qubit(s) across all Pauli channels." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:36.348138Z", "iopub.status.busy": "2024-08-16T10:15:36.347524Z", "iopub.status.idle": "2024-08-16T10:15:38.766378Z", "shell.execute_reply": "2024-08-16T10:15:38.765600Z" }, "id": "bc2911a4bf81" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X basis: Counter({0: 872, 1: 128})\n", "Y basis: Counter({0: 862, 1: 138})\n", "Z basis: Counter({0: 892, 1: 108})\n" ] } ], "source": [ "q0, q1, q2 = cirq.LineQubit.range(3)\n", "circuit = cirq.Circuit(\n", " cirq.H(q0), # initialize X basis\n", " cirq.H(q1), # initialize Y basis\n", " cirq.S(q1),\n", " cirq.depolarize(p=0.2).on_each(q0, q1, q2),\n", " cirq.H(q0), # return to Z-basis\n", " cirq.S(q1) ** -1,\n", " cirq.H(q1),\n", " cirq.measure(q0, key='result_0'),\n", " cirq.measure(q1, key='result_1'),\n", " cirq.measure(q2, key='result_2'),\n", ")\n", "result = cirq.Simulator(seed=0).run(circuit, repetitions=1000)\n", "# All basis states are equally affected.\n", "print(\"X basis:\", result.histogram(key='result_0'))\n", "print(\"Y basis:\", result.histogram(key='result_1'))\n", "print(\"Z basis:\", result.histogram(key='result_2'))" ] }, { "cell_type": "markdown", "metadata": { "id": "f45f3d04073b" }, "source": [ "For noise in just the X or Z channels, see [Bit flip](#bit-flip) or [Phase flip](#phase-flip-or-damping) respectively.\n", "\n", "### Asymmetric depolarization\n", "\n", "`cirq.AsymmetricDepolarizingChannel` (or `cirq.asymmetric_depolarize`) is a generalized version of `DepolarizingChannel` which accepts separate probabilities for X, Y, and Z error. It is best used instead of `DepolarizingChannel` when there is a known, nontrivial discrepancy between the different Pauli error modes." ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:38.770364Z", "iopub.status.busy": "2024-08-16T10:15:38.769737Z", "iopub.status.idle": "2024-08-16T10:15:41.148662Z", "shell.execute_reply": "2024-08-16T10:15:41.147923Z" }, "id": "e24b120ea02a" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "X basis: Counter({0: 764, 1: 236})\n", "Y basis: Counter({0: 800, 1: 200})\n", "Z basis: Counter({0: 950, 1: 50})\n" ] } ], "source": [ "q0, q1, q2 = cirq.LineQubit.range(3)\n", "asym_depol = cirq.asymmetric_depolarize(p_x=0, p_y=0.05, p_z=0.2)\n", "circuit = cirq.Circuit(\n", " cirq.H(q0), # initialize X basis\n", " cirq.H(q1), # initialize Y basis\n", " cirq.S(q1),\n", " asym_depol.on_each(q0, q1, q2),\n", " cirq.H(q0), # return to Z-basis\n", " cirq.S(q1) ** -1,\n", " cirq.H(q1),\n", " cirq.measure(q0, key='result_0'),\n", " cirq.measure(q1, key='result_1'),\n", " cirq.measure(q2, key='result_2'),\n", ")\n", "result = cirq.Simulator(seed=0).run(circuit, repetitions=1000)\n", "# Basis states are only affected by error in other bases.\n", "print(\"X basis:\", result.histogram(key='result_0'))\n", "print(\"Y basis:\", result.histogram(key='result_1'))\n", "print(\"Z basis:\", result.histogram(key='result_2'))" ] }, { "cell_type": "markdown", "metadata": { "id": "066ad7cc19f6" }, "source": [ "### Reset\n", "\n", "`cirq.Reset` forces a qubit into the $|0\\rangle$ state. This is not a noise channel, but rather a hardware operation which commonly consists of measuring the qubit and applying `X` as needed to return it to the $|0\\rangle$ state." ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:41.152871Z", "iopub.status.busy": "2024-08-16T10:15:41.152328Z", "iopub.status.idle": "2024-08-16T10:15:42.194029Z", "shell.execute_reply": "2024-08-16T10:15:42.193282Z" }, "id": "b1e207466867" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Counter({0: 1000})\n" ] } ], "source": [ "q0 = cirq.LineQubit(0)\n", "circuit = cirq.Circuit(\n", " cirq.bit_flip(p=0.2).on(q0),\n", " cirq.reset(q0),\n", " cirq.measure(q0, key='result')\n", ")\n", "result = cirq.Simulator(seed=0).run(circuit, repetitions=1000)\n", "print(result.histogram(key='result'))" ] }, { "cell_type": "markdown", "metadata": { "id": "baf9cefb1aac" }, "source": [ "### Custom channels\n", "\n", "`cirq.MixedUnitaryChannel` (in [`ops/mixed_unitary_channel.py`](https://github.com/quantumlib/Cirq/blob/main/cirq-core/cirq/ops/mixed_unitary_channel.py)) is a customizable channel which can represent any probabilistic mixture of unitary operators. It accepts an optional measurement key to capture which operator was selected." ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:42.197793Z", "iopub.status.busy": "2024-08-16T10:15:42.197242Z", "iopub.status.idle": "2024-08-16T10:15:42.212701Z", "shell.execute_reply": "2024-08-16T10:15:42.211997Z" }, "id": "293264af084d" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "op_num=00001000001000000001\n", "result=00001000001000000001\n" ] } ], "source": [ "q0 = cirq.LineQubit(0)\n", "# equivalent to cirq.bit_flip(p=0.2)\n", "my_channel = cirq.MixedUnitaryChannel(\n", " [(0.8, cirq.unitary(cirq.I)), (0.2, cirq.unitary(cirq.X))],\n", " key='op_num',\n", ")\n", "circuit = cirq.Circuit(\n", " my_channel.on(q0),\n", " cirq.measure(q0, key='result')\n", ")\n", "result = cirq.Simulator(seed=0).run(circuit, repetitions=20)\n", "# `op_num` and `result` are always equal.\n", "print(result)" ] }, { "cell_type": "markdown", "metadata": { "id": "baf9cefb1aac" }, "source": [ "`cirq.KrausChannel` (in [`ops/kraus_channel.py`](https://github.com/quantumlib/Cirq/blob/main/cirq-core/cirq/ops/kraus_channel.py)) is similar, but supports non-unitary operators." ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:42.216421Z", "iopub.status.busy": "2024-08-16T10:15:42.215789Z", "iopub.status.idle": "2024-08-16T10:15:42.232978Z", "shell.execute_reply": "2024-08-16T10:15:42.232272Z" }, "id": "5259ad5c61c5" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "op_num=11111110011100111011\n", "result=11111110011100111011\n" ] } ], "source": [ "import numpy as np\n", "\n", "q0 = cirq.LineQubit(0)\n", "# equivalent to cirq.amplitude_damp(gamma=0.2)\n", "gamma = 0.2\n", "my_channel = cirq.KrausChannel(\n", " [\n", " np.array([[0, np.sqrt(gamma)], [0, 0]]), # decay |1) -> |0)\n", " np.array([[1, 0], [0, np.sqrt(1-gamma)]]), # stay in |1)\n", " ],\n", " key='op_num',\n", ")\n", "circuit = cirq.Circuit(\n", " cirq.X(q0),\n", " my_channel.on(q0),\n", " cirq.measure(q0, key='result')\n", ")\n", "result = cirq.Simulator(seed=0).run(circuit, repetitions=20)\n", "# `op_num` and `result` are always equal.\n", "print(result)" ] }, { "cell_type": "markdown", "metadata": { "id": "baf9cefb1aac" }, "source": [ "In general, prefer one of the other built-in channels if your use case supports it, as those channels can occasionally be optimized in ways that do not generalize to these channels.\n", "\n", "Prefer `MixedUnitaryChannel` if your channel has a mix-of-unitaries description, as it can be simulated more efficiently than `KrausChannel`." ] }, { "cell_type": "markdown", "metadata": { "id": "b005820d6151" }, "source": [ "## NoiseModels\n", "\n", "Built-in `cirq.NoiseModel` types do not have a shared home like channels, but a couple of commonly-used types are listed here. For more complex experiments, it is often useful to define your own `NoiseModel` subclasses; refer to [`devices/noise_model.py`](https://github.com/quantumlib/Cirq/blob/main/cirq-core/cirq/devices/noise_model.py) to learn more." ] }, { "cell_type": "markdown", "metadata": { "id": "ec10d57ac6cf" }, "source": [ "### Constant noise\n", "\n", "`cirq.ConstantQubitNoiseModel` (in [`devices/noise_model.py`](https://github.com/quantumlib/Cirq/blob/main/cirq-core/cirq/devices/noise_model.py)) is a simple model which will insert the given gate after every operation in the target circuit. When \"trivially converting\" gates to `NoiseModel`s, this is the model that is used, but it isn't particularly representative of any real-world noise." ] }, { "cell_type": "code", "execution_count": 13, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:42.236556Z", "iopub.status.busy": "2024-08-16T10:15:42.236021Z", "iopub.status.idle": "2024-08-16T10:15:42.260256Z", "shell.execute_reply": "2024-08-16T10:15:42.259562Z" }, "id": "73bdc200ea10" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0: ───I───X[]───M('result_0')───X[]───M('result_1')───X[]───\n", "First measure: Counter({1: 20})\n", "Second measure: Counter({0: 20})\n" ] } ], "source": [ "q0 = cirq.LineQubit(0)\n", "circuit = cirq.Circuit(\n", " cirq.I(q0),\n", " cirq.measure(q0, key='result_0'),\n", " cirq.measure(q0, key='result_1'),\n", ")\n", "# Applies noise after every gate, even measurements.\n", "noisy_circuit = circuit.with_noise(cirq.X)\n", "print(noisy_circuit)\n", "result = cirq.Simulator(seed=0).run(noisy_circuit, repetitions=20)\n", "print(\"First measure:\", result.histogram(key='result_0'))\n", "print(\"Second measure:\", result.histogram(key='result_1'))" ] }, { "cell_type": "markdown", "metadata": { "id": "ec10d57ac6cf" }, "source": [ "Avoid using this model except for simple tests, as different gates (particularly `cirq.MeasurementGate`) usually have different error." ] }, { "cell_type": "markdown", "metadata": { "id": "7a247831019f" }, "source": [ "### Insertion noise\n", "\n", "`cirq.devices.InsertionNoiseModel` (in [`devices/insertion_noise_model.py`](https://github.com/quantumlib/Cirq/blob/main/cirq-core/cirq/devices/insertion_noise_model.py)) inspects the circuit for operations matching user-specified identifiers, and inserts the corresponding noise operations after matching operations. This noise model is useful for applying specific noise to specific gates - for example, adding different depolarizing error to 1- and 2-qubit gates." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:42.263660Z", "iopub.status.busy": "2024-08-16T10:15:42.263205Z", "iopub.status.idle": "2024-08-16T10:15:43.018810Z", "shell.execute_reply": "2024-08-16T10:15:43.018036Z" }, "id": "aafbb3e3ac05" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "0: ───I───X───BF(0.2)───M('result')───\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ "Counter({1: 795, 0: 205})\n" ] } ], "source": [ "from cirq.devices import InsertionNoiseModel\n", "\n", "q0 = cirq.LineQubit(0)\n", "circuit = cirq.Circuit(\n", " cirq.I(q0),\n", " cirq.X(q0),\n", " cirq.measure(q0, key='result'),\n", ")\n", "# Apply bitflip noise after each X gate.\n", "target_op = cirq.OpIdentifier(cirq.XPowGate, q0)\n", "insert_op = cirq.bit_flip(p=0.2).on(q0)\n", "noise_model = InsertionNoiseModel(\n", " ops_added={target_op: insert_op},\n", " require_physical_tag=False, # For use outside calibration-to-noise\n", ")\n", "noisy_circuit = circuit.with_noise(noise_model)\n", "print(noisy_circuit)\n", "result = cirq.Simulator(seed=0).run(noisy_circuit, repetitions=1000)\n", "print(result.histogram(key='result'))" ] }, { "cell_type": "markdown", "metadata": { "id": "8f5fa4a5f6b3" }, "source": [ "`InsertionNoiseModel` is primarily used in the calibration-to-noise pipeline, but can be used elsewhere by setting `require_physical_tag=False`, as seen above." ] }, { "cell_type": "markdown", "metadata": { "id": "aa2dd361a7fe" }, "source": [ "## Measurement parameters\n", "\n", "`cirq.MeasurementGate` provides parameters for error which occurs in the classical measurement step instead of in the quantum state, which can be useful for accelerating simulations.\n", "\n", "### Invert mask\n", "\n", "The `invert_mask` field is a simple list of booleans indicating bits to flip in the final output. This can represent simple bitflip error in measurement, or a correction for that error." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:43.022674Z", "iopub.status.busy": "2024-08-16T10:15:43.022039Z", "iopub.status.idle": "2024-08-16T10:15:43.032185Z", "shell.execute_reply": "2024-08-16T10:15:43.031515Z" }, "id": "94f0818ade6a" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Counter({0: 1000})\n" ] } ], "source": [ "q0 = cirq.LineQubit(0)\n", "circuit = cirq.Circuit(\n", " cirq.X(q0),\n", " cirq.measure(q0, key='result', invert_mask=[True])\n", ")\n", "result = cirq.Simulator(seed=0).run(circuit, repetitions=1000)\n", "print(result.histogram(key='result'))" ] }, { "cell_type": "markdown", "metadata": { "id": "aa2dd361a7fe" }, "source": [ "### Confusion map\n", "\n", "The `confusion_map` field maps qubit tuples to confusion matrices for those qubits. The confusion matrix for two qubits is:\n", "\n", "$$\\begin{bmatrix}\n", "Pr(00|00) & Pr(01|00) & Pr(10|00) & Pr(11|00) \\\\\n", "Pr(00|01) & Pr(01|01) & Pr(10|01) & Pr(11|01) \\\\\n", "Pr(00|10) & Pr(01|10) & Pr(10|10) & Pr(11|10) \\\\\n", "Pr(00|11) & Pr(01|11) & Pr(10|11) & Pr(11|11)\n", "\\end{bmatrix}$$\n", "\n", "where `Pr(ij|pq)` is the probability of observing `ij` if state `pq` was prepared; a `2**n`-square confusion matrix can be provided for any grouping of N qubits." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2024-08-16T10:15:43.035513Z", "iopub.status.busy": "2024-08-16T10:15:43.034982Z", "iopub.status.idle": "2024-08-16T10:15:43.084753Z", "shell.execute_reply": "2024-08-16T10:15:43.084098Z" }, "id": "8b8679f784b3" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Counter({1: 787, 0: 213})\n" ] } ], "source": [ "import numpy as np\n", "\n", "q0 = cirq.LineQubit(0)\n", "# 10% chance to report |0) as |1), 20% chance to report |1) as |0).\n", "cmap = {(0,): np.array([[0.9, 0.1], [0.2, 0.8]])}\n", "circuit = cirq.Circuit(\n", " cirq.X(q0),\n", " cirq.measure(q0, key='result', confusion_map=cmap)\n", ")\n", "result = cirq.Simulator(seed=0).run(circuit, repetitions=1000)\n", "print(result.histogram(key='result'))" ] }, { "cell_type": "markdown", "metadata": { "id": "aa2dd361a7fe" }, "source": [ "This can be used for representing more complex errors in measurement, including probabilistic error on individual qubits and correlated error across multiple qubits." ] } ], "metadata": { "colab": { "name": "representing_noise.ipynb", "toc_visible": true }, "kernelspec": { "display_name": "Python 3", "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.10.14" } }, "nbformat": 4, "nbformat_minor": 0 }