{ "cells": [ { "cell_type": "markdown", "metadata": { "id": "DkA0Fobtb9dM" }, "source": [ "##### Copyright 2022 The Cirq Developers" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "cellView": "form", "execution": { "iopub.execute_input": "2024-07-19T09:40:42.617711Z", "iopub.status.busy": "2024-07-19T09:40:42.617158Z", "iopub.status.idle": "2024-07-19T09:40:42.621561Z", "shell.execute_reply": "2024-07-19T09:40:42.620849Z" }, "id": "tUshu7YfcAAW" }, "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": "7zgataJVe0mU" }, "source": [ "# Noisy Simulation" ] }, { "cell_type": "markdown", "metadata": { "id": "HYkRhx2pe2XX" }, "source": [ "
\n",
" ![]() | \n",
" \n",
" ![]() | \n",
" \n",
" ![]() | \n",
" \n",
" ![]() | \n",
"
0: ───BitAndPhaseFlip(0.05)───\n", "\n", "1: ───BitAndPhaseFlip(0.05)───\n", "\n", "2: ───BitAndPhaseFlip(0.05)───" ], "text/plain": [ "0: ───BitAndPhaseFlip(0.05)───\n", "\n", "1: ───BitAndPhaseFlip(0.05)───\n", "\n", "2: ───BitAndPhaseFlip(0.05)───" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "\"\"\"Example of using a custom channel in a circuit.\"\"\"\n", "circuit = cirq.Circuit(bit_phase_flip.on_each(*cirq.LineQubit.range(3)))\n", "circuit" ] }, { "cell_type": "markdown", "metadata": { "id": "46eb7a01e30d" }, "source": [ "Note: If a custom channel does not have a mixture, it should instead define the `_kraus_` magic method to return a sequence of Kraus operators (as `numpy.ndarray`s). Defining a `_has_kraus_` method which returns `True` is optional but recommended." ] }, { "cell_type": "markdown", "metadata": { "id": "b537c43e078c" }, "source": [ "This method of defining custom channels is the most general, but simple channels such as the custom `BitAndPhaseFlipChannel` can also be created directly from a `Gate` with the convenient `Gate.with_probability` method." ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "execution": { "iopub.execute_input": "2024-07-19T09:41:00.366263Z", "iopub.status.busy": "2024-07-19T09:41:00.365685Z", "iopub.status.idle": "2024-07-19T09:41:00.369013Z", "shell.execute_reply": "2024-07-19T09:41:00.368364Z" }, "id": "0937f8dad808" }, "outputs": [], "source": [ "\"\"\"Create a channel with Gate.with_probability.\"\"\"\n", "channel = cirq.Y.with_probability(probability=0.05)" ] }, { "cell_type": "markdown", "metadata": { "id": "0461f377ac46" }, "source": [ "This produces the same mixture as the custom `BitAndPhaseFlip` channel above." ] }, { "cell_type": "code", "execution_count": 15, "metadata": { "execution": { "iopub.execute_input": "2024-07-19T09:41:00.372233Z", "iopub.status.busy": "2024-07-19T09:41:00.371711Z", "iopub.status.idle": "2024-07-19T09:41:00.376105Z", "shell.execute_reply": "2024-07-19T09:41:00.375453Z" }, "id": "f0a85b33680a" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "With probability 0.05, apply\n", " [[0.+0.j 0.-1.j]\n", " [0.+1.j 0.+0.j]]\n", "\n", "With probability 0.95, apply\n", " [[1. 0.]\n", " [0. 1.]]\n", "\n" ] } ], "source": [ "for prob, kraus in cirq.mixture(channel):\n", " print(f\"With probability {prob}, apply\\n\", kraus, end=\"\\n\\n\")" ] }, { "cell_type": "markdown", "metadata": { "id": "c73ec3e63e23" }, "source": [ "Note that the order of Kraus operators is reversed from above, but this of course does not affect the action of the channel." ] }, { "cell_type": "markdown", "metadata": { "id": "96a696b543f7" }, "source": [ "## Simulating noisy circuits" ] }, { "cell_type": "markdown", "metadata": { "id": "d51d993ae869" }, "source": [ "### Density matrix simulation" ] }, { "cell_type": "markdown", "metadata": { "id": "38dfcb60ef79" }, "source": [ "The `cirq.DensityMatrixSimulator` can simulate any noisy circuit (i.e., can apply any quantum channel) because it stores the full density matrix $\\rho$. This simulation strategy updates the state $\\rho$ by directly applying the Kraus operators of each quantum channel." ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "execution": { "iopub.execute_input": "2024-07-19T09:41:00.379690Z", "iopub.status.busy": "2024-07-19T09:41:00.379164Z", "iopub.status.idle": "2024-07-19T09:41:00.387366Z", "shell.execute_reply": "2024-07-19T09:41:00.386674Z" }, "id": "a2973a8faef0" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Simulating circuit:\n", "(0, 0): ───X───AD(0.1)───\n", "\n", "Final density matrix:\n", "[[0.1 +0.j 0. +0.j]\n", " [0. +0.j 0.90000004+0.j]]\n" ] } ], "source": [ "\"\"\"Simulating a circuit with the density matrix simulator.\"\"\"\n", "# Get a circuit.\n", "qbit = cirq.GridQubit(0, 0)\n", "circuit = cirq.Circuit(cirq.X(qbit), cirq.amplitude_damp(0.1).on(qbit))\n", "\n", "# Display it.\n", "print(\"Simulating circuit:\")\n", "print(circuit)\n", "\n", "# Simulate with the density matrix simulator.\n", "dsim = cirq.DensityMatrixSimulator()\n", "rho = dsim.simulate(circuit).final_density_matrix\n", "\n", "# Display the final density matrix.\n", "print(\"\\nFinal density matrix:\")\n", "print(rho)" ] }, { "cell_type": "markdown", "metadata": { "id": "ac8a991a426b" }, "source": [ "Note that the density matrix simulator supports the `run` method which only gives access to measurements as well as the `simulate` method (used above) which gives access to the full density matrix." ] }, { "cell_type": "markdown", "metadata": { "id": "0c659bb20098" }, "source": [ "### Monte Carlo wavefunction simulation" ] }, { "cell_type": "markdown", "metadata": { "id": "6f3caf9cea92" }, "source": [ "Noisy circuits with arbitrary channels can also be simulated with the `cirq.Simulator`. When simulating such a channel, a single Kraus operator is randomly sampled (according to the probability distribution) and applied to the wavefunction. This method is known as \"Monte Carlo (wavefunction) simulation\" or \"quantum trajectories.\"\n", "\n", "Note: For channels which do not support the `cirq.mixture` protocol, the probability of applying each Kraus operator depends on the state. In contrast, for channels which do support the `cirq.mixture` protocol, the probability of applying each Kraus operator is independent of the state." ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "execution": { "iopub.execute_input": "2024-07-19T09:41:00.390834Z", "iopub.status.busy": "2024-07-19T09:41:00.390395Z", "iopub.status.idle": "2024-07-19T09:41:00.397265Z", "shell.execute_reply": "2024-07-19T09:41:00.396601Z" }, "id": "d80a2bc7ce14" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Simulating circuit:\n", "Q: ───BF(0.5)───\n", "\n", "Final wavefunction:\n", "|1⟩\n" ] } ], "source": [ "\"\"\"Simulating a noisy circuit via Monte Carlo simulation.\"\"\"\n", "# Get a circuit.\n", "qbit = cirq.NamedQubit(\"Q\")\n", "circuit = cirq.Circuit(cirq.bit_flip(p=0.5).on(qbit))\n", "\n", "# Display it.\n", "print(\"Simulating circuit:\")\n", "print(circuit)\n", "\n", "# Simulate with the cirq.Simulator.\n", "sim = cirq.Simulator()\n", "psi = sim.simulate(circuit).dirac_notation()\n", "\n", "# Display the final wavefunction.\n", "print(\"\\nFinal wavefunction:\")\n", "print(psi)" ] }, { "cell_type": "markdown", "metadata": { "id": "4c485bbc1dfd" }, "source": [ "To see that the output is stochastic, you can run the cell above multiple times. Since $p = 0.5$ in the bit-flip channel, you should get $|0\\rangle$ roughly half the time and $|1\\rangle$ roughly half the time. The `run` method with many repetitions can also be used to see this behavior." ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "execution": { "iopub.execute_input": "2024-07-19T09:41:00.400495Z", "iopub.status.busy": "2024-07-19T09:41:00.399988Z", "iopub.status.idle": "2024-07-19T09:41:00.483476Z", "shell.execute_reply": "2024-07-19T09:41:00.482735Z" }, "id": "8143ae0c7a34" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Counter({1: 59, 0: 41})\n" ] } ], "source": [ "\"\"\"Example of Monte Carlo wavefunction simulation with the `run` method.\"\"\"\n", "circuit = cirq.Circuit(cirq.bit_flip(p=0.5).on(qbit), cirq.measure(qbit))\n", "res = sim.run(circuit, repetitions=100)\n", "print(res.histogram(key=qbit))" ] }, { "cell_type": "markdown", "metadata": { "id": "0f123f0e3c55" }, "source": [ "## Adding noise to circuits" ] }, { "cell_type": "markdown", "metadata": { "id": "473ac025a226" }, "source": [ "Often circuits are defined with just unitary operations, but we want to simulate them with noise. There are several methods for inserting noise in Cirq." ] }, { "cell_type": "markdown", "metadata": { "id": "d0de2c475f12" }, "source": [ "For any circuit, the `with_noise` method can be called to insert a channel after every moment." ] }, { "cell_type": "code", "execution_count": 19, "metadata": { "execution": { "iopub.execute_input": "2024-07-19T09:41:00.487367Z", "iopub.status.busy": "2024-07-19T09:41:00.486721Z", "iopub.status.idle": "2024-07-19T09:41:00.497577Z", "shell.execute_reply": "2024-07-19T09:41:00.496876Z" }, "id": "568530c8707b" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Circuit without noise:\n", " ┌──┐\n", "0: ───@───X─────×────\n", " │ │ │\n", "1: ───@───┼────S┼────\n", " │ │\n", "2: ───Z───@─────×────\n", " └──┘\n", "\n", "Circuit with noise:\n", " ┌──┐\n", "0: ───@───D(0.01)[
0: ───H───D(0.01)[<virtual>]───\n", "\n", "1: ───H───D(0.01)[<virtual>]───" ], "text/plain": [ "0: ───H───D(0.01)[