{ "metadata": { "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.9.2" }, "orig_nbformat": 2, "kernelspec": { "name": "python392jvsc74a57bd0916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1", "display_name": "Python 3.9.2 64-bit" }, "metadata": { "interpreter": { "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" } } }, "nbformat": 4, "nbformat_minor": 2, "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "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." ] }, { "source": [ "# Noise simulation in qsimcirq" ], "cell_type": "markdown", "metadata": {} }, { "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": "markdown", "metadata": {} }, { "source": [ "Noisy gates in Cirq are represented by `Channel`s, which can act as one of a set of gates depending on the state of the circuit. The [Cirq tutorial on noise](https://quantumai.google/cirq/noise) explains how to construct these objects and add them to your circuits.\n", "\n", "## Setup\n", "\n", "Install the Cirq and qsimcirq packages:" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "try:\n", " import cirq\n", "except ImportError:\n", " !pip install cirq --quiet\n", " import cirq\n", "\n", "try:\n", " import qsimcirq\n", "except ImportError:\n", " !pip install qsimcirq --quiet\n", " import qsimcirq" ] }, { "source": [ "It is possible to simulate channels with density matrices, which combine all possible channel behaviors, but the overhead is steep: a density matrix requires O(4^N) storage for N qubits.\n", "\n", "In qsimcirq, noisy circuits are instead simulated as \"trajectories\": the behavior of each `Channel` is determined probabilistically at runtime. This permits much larger simulations at the cost of only capturing one such \"trajectory\" per execution.\n", "\n", "## Performance\n", "\n", "Noisy circuits tend to be more expensive to simulate than their noiseless equivalents, but qsim is optimized to avoid these overheads when possible. In particular, the less incoherent noise (i.e. non-unitary effects) that a `Channel` has, the closer its performance will be to the noiseless case for a single repetition.\n", "\n", "Simulating many repetitions of a noisy circuit requires executing the entire circuit once for each repetition due to the nondeterministic nature of noisy operations." ], "cell_type": "markdown", "metadata": {} }, { "source": [ "## Constructing noisy circuits\n", "\n", "Cirq provides a number of tools for constructing noisy circuits. For the purpose of this tutorial, we will focus on two common types of noise: T1 (\"amplitude damping\") and T2 (\"phase damping\"). These can be created in Cirq with `cirq.amplitude_damp` and `cirq.phase_damp`, as shown below:" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "q0, q1 = cirq.LineQubit.range(2)\n", "\n", "circuit = cirq.Circuit(\n", " # Perform a Hadamard on both qubits\n", " cirq.H(q0), cirq.H(q1),\n", " # Apply amplitude damping to q0 with probability 0.1\n", " cirq.amplitude_damp(gamma=0.1).on(q0),\n", " # Apply phase damping to q1 with probability 0.1\n", " cirq.phase_damp(gamma=0.1).on(q1),\n", ")" ] }, { "source": [ "## Simulating noisy circuits\n", "\n", "Simulating this circuit works exactly the same as simulating a noiseless circuit: simply construct a simulator object and simulate. `QSimSimulator` will automatically switch over to the noisy simulator if it detects noise (i.e. `Channel`s) in your circuit." ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "[0.52631575+0.j 0.49930704+0.j 0.49930704+0.j 0.47368425+0.j]\n" ] } ], "source": [ "qsim_simulator = qsimcirq.QSimSimulator()\n", "results = qsim_simulator.simulate(circuit)\n", "print(results.final_state_vector)" ] }, { "source": [ "It's important to note that unlike density-matrix simulations, this result (from a single repetition) is stochastic in nature. Running the circuit multiple times may yield different results, but each result generated is a possible outcome of the provided circuit." ], "cell_type": "markdown", "metadata": {} }, { "source": [ "## Other simulation modes\n", "\n", "Noisy circuit simulation in qsimcirq supports all of the same simulation modes as the noiseless simulator, including:\n", "\n", "### Measurement Sampling" ], "cell_type": "markdown", "metadata": {} }, { "source": [ "# Simulate measuring at the end of the circuit.\n", "measured_circuit = circuit + cirq.measure(q0, q1, key='m')\n", "measure_results = qsim_simulator.run(measured_circuit, repetitions=5)\n", "print(measure_results)" ], "cell_type": "code", "metadata": {}, "execution_count": 5, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "m=01101, 00100\n" ] } ] }, { "source": [ "### Amplitude evaluation" ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "[(0.5263157486915588+0j), (0.4993070363998413+0j)]\n" ] } ], "source": [ "# Calculate only the amplitudes of the |00) and |01) states.\n", "amp_results = qsim_simulator.compute_amplitudes(\n", " circuit, bitstrings=[0b00, 0b01])\n", "print(amp_results)" ] }, { "source": [ "### Expectation values\n", "\n", "Expectation values can only be estimated from trajectories, but the accuracy of these estimates can be increased by simulating the circuit additional times. This is demonstrated below." ], "cell_type": "markdown", "metadata": {} }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [ { "output_type": "stream", "name": "stdout", "text": [ "[(0.13789467364549637+0j), (0.9386972016096116+0j)]\n" ] } ], "source": [ "# Set the \"noisy repetitions\" to 100.\n", "# This parameter only affects expectation value calculations.\n", "options = {'r': 100}\n", "# Also set the random seed to get reproducible results.\n", "ev_simulator = qsimcirq.QSimSimulator(qsim_options=options, seed=1)\n", "# Define observables to measure: for q0 and for q1.\n", "pauli_sum1 = cirq.Z(q0)\n", "pauli_sum2 = cirq.X(q1)\n", "# Calculate expectation values for the given observables.\n", "ev_results = ev_simulator.simulate_expectation_values(\n", " circuit,\n", " observables=[pauli_sum1, pauli_sum2],\n", ")\n", "print(ev_results)" ] }, { "source": [ "The output is a list of expectation values, one for each observable." ], "cell_type": "markdown", "metadata": {} } ] }