{ "cells": [ { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%matplotlib inline" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n# Simulator: Introduction\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "|True-Q| offers a highly versatile built-in simulator that allows\nusers to simulate arbitrary circuits with a wide variety of noise models and options\nfor noise model customization.\n\nIn this example, we go through the basic features of the |True-Q| simulator.\nWe begin by instantiating a circuit to play with:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import matplotlib.pyplot as plt\nimport numpy as np\n\nimport trueq as tq\nimport trueq.simulation as tqs\n\ncircuit = tq.Circuit([{0: tq.Gate.x, 1: tq.Gate.y}, {(0, 1): tq.Gate.cz}])\ncircuit.measure_all()\ncircuit.draw()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Simulator Basics\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In its simplest configuration, a (noiseless) simulator can be instantiated from\n|True-Q|'s :py:class:~trueq.Simulator class as follows:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "sim = tq.Simulator()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can use it to simulate the final state of the circuit, with all initial states\nprepared as $|0\\rangle$ by default. Since the simulator is noiseless, the\noutput state is a pure state; a noisy simulator will generally return a density matrix\ninstead.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "sim.state(circuit).mat()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "If we want to force the output to be a density matrix, we can use:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "sim.state(circuit).upgrade().mat()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also use it to compute the overall action of the circuit. Since the simulator\nis currently noiseless, the output is a unitary matrix; a noisy simulator will return\na superoperator (in the rowstacked basis).\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "tq.plot_mat(sim.operator(circuit).mat())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Finally, we can use a simulator to populate the :py:attr:~trueq.Circuit.results\nattribute of the circuit. Currently, this attribute is just an empty dictionary:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "circuit.results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "But after calling the :py:meth:~trueq.Simulator.run method, it has\n:py:attr:~trueq.Circuit.results that are randomly sampled bitstrings from the final\nstate of a state simulation:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "sim.run(circuit, n_shots=100)\ncircuit.results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "In this case, all 100 shots end in the 11 state because the final state is a\ncomputational eigenstate.\n\nWhen re-running the simulation, it will overwrite the existing\n:py:attr:~trueq.Circuit.results unless\notherwise specified. Another option is to call the simulator's\n:py:meth:~trueq.Simulator.sample method which returns a :py:class:~trueq.Results\nobject directly without affecting the circuit's :py:attr:~trueq.Circuit.results\nattribute:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "results = sim.sample(circuit, n_shots=100)\nresults" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Adding Noise Sources\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We construct a noisy simulator by appending noise sources to a noiseless simulator.\nThe example below demonstrates this using two of |True-Q|\\'s built-in noise sources:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Add an overrotation noise, which causes single qubit gates to be simulated as U^1.02\nsim.add_overrotation(single_sys=0.02)\n\n# Add a depolarizing noise source at a rate of 0.8% per acted-on qubit per cycle\nsim.add_depolarizing(p=0.008)\n\n# Note that noisy simulators can be constructed as one-liners\nother_sim = tq.Simulator().add_overrotation(single_sys=0.02).add_depolarizing(p=0.008)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "

#### Note

Note that simulation is cycle-based.\n Each noise source is called to add noise to the quantum state (or to the\n superoperator if :py:meth:~trueq.Simulator.operator is called) for each cycle\n in a circuit. The order in which noise sources are applied is dictated by the\n order in which they were added to the simulator.

\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, since the simulator applies a depolarizing noise source, we get a density\noperator rather than a pure state:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "tq.plot_mat(sim.state(circuit).mat())" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "After calling the :py:meth:~trueq.Simulator.run method (overwriting the\n:py:attr:~trueq.Circuit.results from above), we end up with noisy outcomes:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "sim.run(circuit, n_shots=100, overwrite=True)\ncircuit.results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can also specify infinite shots to get the expectation values of each bitstring:\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "sim.run(circuit, n_shots=np.inf, overwrite=True)\ncircuit.results" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Built-in Noise Sources\n\nThe simulator supports a series of common noise models, such as:\n\n#. :py:meth:~trueq.Simulator.add_depolarizing\\: This method adds depolarizing noise\n to every location where a gate acts. It takes the depolarizing parameter p as\n an argument.\n#. :py:meth:~trueq.Simulator.add_stochastic_pauli\\: This method adds stochastic\n Pauli noise to every location where a gate acts. It takes as parameters the\n probabilities of each Pauli error for the noise channel, px, py and\n pz.\n#. :py:meth:~trueq.Simulator.add_overrotation\\: This method adds an overrotation to\n every single- and/or two-qubit gate. It takes as parameters the single_sys\n angle which specifies how much the single-qudit gates are under/overrotated, and\n the multi_sys parameter, which specifies how much the two-qudit gates are\n under/overrotated.\n#. :py:meth:~trueq.Simulator.add_relaxation\\: This method adds amplitude damping\n ($T1$\\) and/or dephasing ($T2$\\) to every location where a gate acts.\n This method takes as arguments the noise parameters t1 and t2 and the\n amount of time t_single (t_multi) a single- (multi-)qubit gate takes.\n#. :py:meth:~trueq.Simulator.add_kraus\\: This method allows for the creation of a\n custom noise source specified through Kraus operators and takes as input a list\n of operators specifed in matrix-form.\n\nFor a full list of built-in noise sources, check out the :py:class:~trueq.Simulator\nAPI reference.\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Restricting Noise via Conditional Noise Sources\n\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Every noise source has a property called match, which can be used to create a\nconditional noise source. A match can specify specific qubits, operations,\ncycles, or even more specific properties. Refer to the children of\n:py:class:~trueq.simulation.match.Match for more details.\n\n" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# initialize a simulator with different dephasing rates on the qubits\nbitflip = lambda p: [np.sqrt(1 - p) * np.eye(2), np.sqrt(p) * np.fliplr(np.eye(2))]\nsim0 = tq.Simulator()\nsim0.add_kraus(bitflip(0.05), match=tqs.LabelMatch(0))\nsim0.add_kraus(bitflip(0.09), match=tqs.LabelMatch(1))\n\n# initialize a simulator that targets only a specific gate\nxmatch = tqs.GateMatch(tq.Gate.x)\nsim1 = tq.Simulator().add_kraus(bitflip(0.15), match=xmatch)\n\n# initialize a simulator that targets only specific gates on specific labels\nsim2 = tq.Simulator()\ngate_label_match = tqs.LabelMatch((1, 2)) & tqs.GateMatch([tq.Gate.y, tq.Gate.s])\nsim2.add_kraus(bitflip(0.1), match=gate_label_match)\n\n# plot the final states\nplt.figure(figsize=(10, 3))\ntq.plot_mat(sim0.state(circuit).mat(), ax=plt.subplot(131))\ntq.plot_mat(sim1.state(circuit).mat(), ax=plt.subplot(132))\ntq.plot_mat(sim2.state(circuit).mat(), ax=plt.subplot(133))" ] } ], "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.9.12" } }, "nbformat": 4, "nbformat_minor": 0 }