{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Multi-Qubit Devices and Quantum Circuits\n", "This tutorial will cover two related topics:\n", "\n", "1. `ProccesorSpec` objects.\n", "\n", "These objects can be used to define the \"specification\" of a quantum computer (e.g., device connectivity, the gate-set etc), and which are geared towards multi-qubit devices. Currently, these are mostly encountered in `pyGSTi` as an input for generating randomized benchmarking experiments, but they will likely be used more widely in the future.\n", "\n", "2. `Circuit` objects.\n", "\n", "These objects represent quantum circuits. They are a more structured version of `Gatestring` objects, and they contain various methods that are useful for manipulating quantum circuits (e.g., simple depth compression) and interfacing `pyGSTi` with other quantum circuit standards (e.g., conversion to [OpenQasm](https://arxiv.org/abs/1707.03429)). Currently, they are mostly encountered in `pyGSTi` as the output of randomized benchmarking experiment generation functions, and with related circuit compiler functions.\n" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "from __future__ import print_function\n", "import pygsti # the main pyGSTi module" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Using a `ProcessorSpec` to specify a multi-qubit device.\n", "The `ProcessorSpec` object is designed to encapsulate the specification of a small to medium-scale quantum computer, and to hold a variety of useful things that can be derived from this information. The basic information that a `ProcessorSpec` is initialized via is:\n", "\n", "1. The number of qubits in the device, and, optionally, the labels of these qubits.\n", "\n", "2. The target gate-set of the device, as either unitary matrices or using names that point to in-built unitary matrices. E.g., 'Gcnot' is a shorthand for specifying a CNOT gate. Normally this will be the \"primitive\" gates of the device, although it may sometimes be useful to choose other gate-sets (it depends what you are then going to use the `ProcessorSpec` for). Currently only discrete gate-sets are supported. E.g., there is no way to specify an arbitrary $\\sigma_z$-rotation as one of the gates in the device. Parameterized gates will likely be supported in the future.\n", "\n", "3. The connectivity of the device.\n", "\n", "So let's create a `ProcessorSpec`.\n", "\n", "The number of qubits the device is for:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "nQubits = 4" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pick some names for the qubits. If not specified, the qubit labels default to 0, 1, 2, ..." ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "qubit_labels = ['Q0','Q1','Q2','Q3']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Pick a set of fundamental gates. These can be specified via in-built names,such as 'Gcnot' for a CNOT gate. The full set of in-built names is specified in the dictionary returned by `pygsti.tools.internalgates.get_standard_gatename_unitaries()`, and note that there is redundency in this set. E.g., 'Gi' is a identity gate but so is 'Gc0' (as one of the 24 1-qubit Cliffords named as 'Gci' for i = 0, 1, 2, ...)." ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "gate_names = ['Gi', # The idle gate\n", " 'Gxpi2', # A X rotation by pi/2\n", " 'Gypi2', # A Y rotation by pi/2\n", " 'Gzpi2', # A Z rotation by pi/2\n", " 'Gh', # The Hadamard gate\n", " 'Gcphase'] # The controlled-Z gate." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Additionally, we can define gates with user-specified names and actions, via a dictionary with keys that are strings (gate names) and values that are unitary matrices. For example, if you want to call the hadamard gate 'Ghad' we could do this here. The gate names should all start with a 'G', but are otherwise unrestricted. Here we'll leave this dictionary empty." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [], "source": [ "nonstd_gate_unitaries = {}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Specify the \"availability\" of gates: which qubits they can be applied to. When not specified for a gate, it is assumed that it can be applied to all dimension-appropriate sets of qubits. E.g., a 1-qubit gate will be assumed to be applicable to each qubit; a 2-qubit gate will be assumed to be applicable to all ordered pairs of qubits, etc.\n", "\n", "Let's make our device have ring connectivity:" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [], "source": [ "availability = {'Gcphase':[('Q0','Q1'),('Q1','Q2'),('Q2','Q3'),('Q3','Q0')]}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We then create a `ProcessorSpec` by handing it all of this information. This then generates a variety of auxillary information about the device from this input (e.g., optimal compilations for the Pauli operators and CNOT). The defaults here that haven't been specified will be ok for most purposes. But sometimes they will need to be changed to avoid slow ProcessorSpec initialization - fixes for these issues will likely be implemented in the future." ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "pspec = pygsti.obj.ProcessorSpec(nQubits, gate_names, nonstd_gate_unitaries=nonstd_gate_unitaries, \n", " availability=availability, qubit_labels=qubit_labels)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "`ProcessorSpec` objects are not particularly useful on their own. Currently, they are mostly used for interfacing with `Circuit` objects, in-built compilation algorithms, and the randomized benchmarking code. However, in the future we expect that they will be used for constructing circuits/gatestrings for other multi-qubit QCVV methods in `pyGSTi`." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Defining quantum circuits with the `Circuit` object" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We now introduce the `Circuit` objects. These are more a more structured version of a `GateString` object, and these objects can be easily converted between each other.\n", "### Initializing circuits\n", "\n", "First, we'll demonstrate different ways to initialize a circuit. Whenever you initialize a circuit it is necessary to specify the lines/wires/qubits the circuit is over. To do this either specify:\n", "- A `line_labels` list, which is names for the wires in the circuit.\n", "- The number of lines for the circuit, as `num_lines`, in which case the line labels default to integers starting at 0.\n", "\n", "We'll make a 2-qubit circuit and name our qubits 'Q0' and 'Q1'." ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "line_labels=['Q0','Q1']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Circuits do not know what the gates they contain are, in the sense that they do not know what unitaries the things in the circuit correpsond to, with one exception: they are initialized to know that a particular string corresponds to an identity/idle gate. This defaults to the 'I' string, but it can be useful to specify this as something else: often the idle identifier of a `ProcessorSpec`, as we do here." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [], "source": [ "identity=pspec.identity" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can then initialize an empty circuit:" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [], "source": [ "circuit = pygsti.obj.Circuit(line_labels=line_labels, identity=pspec.identity)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can print out a circuit in a basic string format.\n", "\n" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Qubit Q0 -----\n", "Qubit Q1 -----\n", "\n" ] } ], "source": [ "print(circuit)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We can find out various basic properties of the circuit." ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The circuit size is = 0\n", "The circuit depth is = 0\n", "The circuit multi-qubit-gate count is = 0\n" ] } ], "source": [ "print(\"The circuit size is = {}\".format(circuit.size()))\n", "print(\"The circuit depth is = {}\".format(circuit.depth()))\n", "print(\"The circuit multi-qubit-gate count is = {}\".format(circuit.multiQgate_count()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A circuit is essentially just a load of `Label` objects, that specify what gate is applied to each wire at each step. So to specify a non-empty circuit from scratch it is useful to import the `Label` object." ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [], "source": [ "from pygsti.baseobjs.label import Label as L # A shorthand for a Label" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A `Label` is basically just a string, corresponding to a gate name (e.g., 'Gcnot'), and a tuple, corresponding to the qubits the gate acts on. We can initialize a label by specifying these things:" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The gate's name: Gcnot\n", "The qubits the gate acts on: ('Q0', 'Q1')\n" ] } ], "source": [ "label_for_cnot_from_Q0_to_Q1 = L('Gcnot',('Q0','Q1'))\n", "print(\"The gate's name: \", label_for_cnot_from_Q0_to_Q1.name)\n", "print(\"The qubits the gate acts on: \", label_for_cnot_from_Q0_to_Q1.qubits)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Using labels, we can initializing a non-empty circuit. Below we create a circuit consisting of Hadamard gates and a controlled-Z gate over the two qubits 'Q0' and 'Q1'. For obvious reasons, the gates (i.e., the `Label` objects) in this list must act on the qubits in the circuit." ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [], "source": [ "gatestring = [L('Gh','Q0'),L('Gh','Q1'),L('Gcphase',('Q0','Q1')),L('Gh','Q0'),L('Gh','Q1')]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "From this gatestring list there is more than one way to create a circuit. By parallelizing the gates, or by doing them in sequence. Implementing the gates in sequence is the default." ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "A circuit created from a gatestring *without* parallelizing:\n", "\n", "Qubit Q0 ---|Gh|-| |-|●Q1|-|Gh|-| |---\n", "Qubit Q1 ---| |-|Gh|-|●Q0|-| |-|Gh|---\n", "\n", "The circuit size is = 6\n", "The circuit depth is = 5\n", "The circuit multi-qubit-gate count is = 1\n", "\n" ] } ], "source": [ "circuit1 = pygsti.obj.Circuit(gatestring=gatestring, line_labels=['Q0','Q1'], identity=pspec.identity)\n", "print(\"A circuit created from a gatestring *without* parallelizing:\",end='\\n\\n')\n", "print(circuit1)\n", "print(\"The circuit size is = {}\".format(circuit1.size()))\n", "print(\"The circuit depth is = {}\".format(circuit1.depth()))\n", "print(\"The circuit multi-qubit-gate count is = {}\".format(circuit1.multiQgate_count()),end='\\n\\n')" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "A circuit created from a gatestring *with* parallelizing:\n", "\n", "Qubit Q0 ---|Gh|-|●Q1|-|Gh|---\n", "Qubit Q1 ---|Gh|-|●Q0|-|Gh|---\n", "\n", "The circuit size is = 6\n", "The circuit depth is = 3\n", "The circuit multi-qubit-gate count is = 1\n" ] } ], "source": [ "circuit2 = pygsti.obj.Circuit(gatestring=gatestring, line_labels=['Q0','Q1'], parallelize=True, identity=pspec.identity)\n", "print(\"A circuit created from a gatestring *with* parallelizing:\",end='\\n\\n')\n", "print(circuit2)\n", "print(\"The circuit size is = {}\".format(circuit2.size()))\n", "print(\"The circuit depth is = {}\".format(circuit2.depth()))\n", "print(\"The circuit multi-qubit-gate count is = {}\".format(circuit2.multiQgate_count()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "To create a gatestring that can be converted to a circuit with an unambigious layer structure, these layers can be put into the gatestring as lists or tuples and `parallelize` is left as it's default (False)." ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "A circuit created from a gatestring *with* explicit layers:\n", "\n", "Qubit Q0 ---|Gh|-|●Q1|-|Gh|-| |---\n", "Qubit Q1 ---|Gh|-|●Q0|-| |-|Gh|---\n", "\n", "The circuit size is = 6\n", "The circuit depth is = 3\n", "The circuit multi-qubit-gate count is = 1\n" ] } ], "source": [ "gatestring = [[L('Gh','Q0'),L('Gh','Q1')],[L('Gcphase',('Q0','Q1')),],[L('Gh','Q0'),],[L('Gh','Q1'),]]\n", "\n", "circuit3 = pygsti.obj.Circuit(gatestring=gatestring, line_labels=['Q0','Q1'], identity=pspec.identity)\n", "print(\"A circuit created from a gatestring *with* explicit layers:\",end='\\n\\n')\n", "print(circuit3)\n", "print(\"The circuit size is = {}\".format(circuit2.size()))\n", "print(\"The circuit depth is = {}\".format(circuit2.depth()))\n", "print(\"The circuit multi-qubit-gate count is = {}\".format(circuit2.multiQgate_count()))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Reading in a circuit from file.\n", "Circuits can be saved in an unambigious string format using [...] to enclose each layer of the circuit. See the format in the imported file below for an example. We can import circuits from a text file, as we now demonstrate. \n", "\n", "Before doing so, we create a `ProcessorSpec` that will correspond to the device these circuits we're importing are for: they are 5-qubit circuits, containing 'Gcnot' gates (CNOT gates) and 'Gci' gates for i = 0, 1, 2, ..., which denote the 24 1-qubit Clifford gates." ] }, { "cell_type": "code", "execution_count": 19, "metadata": {}, "outputs": [], "source": [ "nQubits = 5\n", "gate_names = ['Gc'+format(i) for i in range(24)] + ['Gcnot']\n", "pspec2 = pygsti.obj.ProcessorSpec(nQubits, gate_names)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We first import the circuits as a list of `GateString` objects." ] }, { "cell_type": "code", "execution_count": 20, "metadata": {}, "outputs": [], "source": [ "gsList = pygsti.io.load_gatestring_list(\"tutorial_files/MyCircuits.txt\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We then turn them into circuit using the same method as above. It is still necessary to specify the line-labels and the identity element (if you want the circuit to know what the identity gate's name is), and these currently can't be stored in the same file as the circuits and auto-imported. " ] }, { "cell_type": "code", "execution_count": 21, "metadata": {}, "outputs": [], "source": [ "line_labels = [0,1,2,3,4]\n", "identity=pspec2.identity\n", "circuitList = [pygsti.obj.Circuit(gatestring=gs, line_labels=line_labels, identity=identity) for gs in gsList]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's look at the imported circuits" ] }, { "cell_type": "code", "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Qubit 0 -----\n", "Qubit 1 -----\n", "Qubit 2 -----\n", "Qubit 3 -----\n", "Qubit 4 -----\n", "\n", "Qubit 0 ---|Gc11|-| ⊕1 |-|Gc12|---\n", "Qubit 1 ---|Gc18|-| ●0 |-| ●2 |---\n", "Qubit 2 ---|Gc12|-|Gc22|-| ⊕1 |---\n", "Qubit 3 ---| ⊕4 |-|Gc23|-|Gc16|---\n", "Qubit 4 ---| ●3 |-|Gc22|-|Gc21|---\n", "\n", "Qubit 0 ---|Gc6|-|Gc4 |-|Gc5 |-|⊕1 |-|Gc15|-| ⊕1 |-|Gc13|---\n", "Qubit 1 ---| |-|Gc20|-|Gc23|-|●0 |-| ●2 |-| ●0 |-|Gc12|---\n", "Qubit 2 ---|●3 |-|Gc11|-|Gc12|-|Gc9|-| ⊕1 |-|Gc12|-|Gc13|---\n", "Qubit 3 ---|⊕2 |-|Gc13|-| ⊕4 |-|Gc2|-|Gc11|-| |-| ⊕4 |---\n", "Qubit 4 ---|Gc9|-|Gc17|-| ●3 |-|Gc1|-|Gc2 |-|Gc13|-| ●3 |---\n", "\n" ] } ], "source": [ "for c in circuitList:\n", " print(c)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Manipulating circuits\n", "\n", "In contrast to `GateString` objects, circuits are meant to be easily editable objects (at least until they are made static). There are only very simple methods for manipulating generic circuits, a couple of which are outlined below.\n", "\n", "We can do depth compression, whereby neighbouring 1-qubit gates are combined using the specified pair-wise relations, all gates are shifted as far forward as possible, and idle layers are deleted." ] }, { "cell_type": "code", "execution_count": 23, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "The circuit *before* depth-compression using the 1-qubit gate pair-wise relations:\n", "\n", "Qubit 0 ---|Gc6|-|Gc4 |-|Gc5 |-|⊕1 |-|Gc15|-| ⊕1 |-|Gc13|---\n", "Qubit 1 ---| |-|Gc20|-|Gc23|-|●0 |-| ●2 |-| ●0 |-|Gc12|---\n", "Qubit 2 ---|●3 |-|Gc11|-|Gc12|-|Gc9|-| ⊕1 |-|Gc12|-|Gc13|---\n", "Qubit 3 ---|⊕2 |-|Gc13|-| ⊕4 |-|Gc2|-|Gc11|-| |-| ⊕4 |---\n", "Qubit 4 ---|Gc9|-|Gc17|-| ●3 |-|Gc1|-|Gc2 |-|Gc13|-| ●3 |---\n", "\n", "The circuit *after* depth-compression using the 1-qubit gate pair-wise relations:\n", "\n", "Qubit 0 ---| |-| ⊕1 |-|Gc15|-| ⊕1 |-|Gc13|---\n", "Qubit 1 ---|Gc6 |-| ●0 |-| ●2 |-| ●0 |-|Gc12|---\n", "Qubit 2 ---| ●3 |-|Gc13|-| ⊕1 |-|Gc1 |-| |---\n", "Qubit 3 ---| ⊕2 |-|Gc13|-| ⊕4 |-|Gc4 |-| ⊕4 |---\n", "Qubit 4 ---|Gc20|-| |-| ●3 |-|Gc13|-| ●3 |---\n", "\n" ] } ], "source": [ "clifford_circuit = circuitList[2]\n", "print(\"The circuit *before* depth-compression using the 1-qubit gate pair-wise relations:\",end='\\n\\n')\n", "print(clifford_circuit)\n", "\n", "clifford_circuit.compress_depth(oneQgate_relations=pspec2.oneQgate_relations)\n", "\n", "print(\"The circuit *after* depth-compression using the 1-qubit gate pair-wise relations:\",end='\\n\\n')\n", "print(clifford_circuit)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Circuits have methods for things such as appending a circuit, insert a gate, changing gate library, adding idle wires, etc. Below we demonstrate inserting a layer." ] }, { "cell_type": "code", "execution_count": 24, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Qubit 0 ---| |-|●1 |-| ⊕1 |-|Gc15|-| ⊕1 |-|Gc13|---\n", "Qubit 1 ---|Gc6 |-|⊕0 |-| ●0 |-| ●2 |-| ●0 |-|Gc12|---\n", "Qubit 2 ---| ●3 |-| |-|Gc13|-| ⊕1 |-|Gc1 |-| |---\n", "Qubit 3 ---| ⊕2 |-| |-|Gc13|-| ⊕4 |-|Gc4 |-| ⊕4 |---\n", "Qubit 4 ---|Gc20|-| |-| |-| ●3 |-|Gc13|-| ●3 |---\n", "\n" ] } ], "source": [ "clifford_circuit.insert_layer([L('Gcnot',(0,1)),],1)\n", "print(clifford_circuit)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Converting circuits external formats\n", "\n", "`Circuit` objects can be easily converted to [OpenQasm](https://arxiv.org/abs/1707.03429) or [Quil](https://arxiv.org/pdf/1608.03355.pdf) strings, using the `convert_to_openqasm()` and `convert_to_quil()` methods. This conversion is automatic for circuits that containing only gates with name that are in-built into `pyGSTi` (see earlier and the docstring of `pygsti.tools.internalgates.get_standard_gatename_unitaries()`). This is with some exceptions in the case of Quil: currently not all of the in-built gate names can be converted to quil gate names automatically, but this will be fixed in the future. \n", "\n", "For other gate names (or even more crucially, if you have re-purposed any of the gate names that `pyGSTi` knows for a different unitary), the desired gate name conversation must be specified as an optional argument for both `convert_to_openqasm()` and `convert_to_quil()`. \n", "\n", "Circuits with qubit labels of the form 'Qi' or with integer labels are auto-converted to the corresponding integer. If this labelling convention is used but the mapping should be different, or if the qubit labelling in the circuit is not of one of these two forms, this mapping should also be handed to these conversion methods." ] }, { "cell_type": "code", "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OPENQASM 2.0;\n", "include \"qelib1.inc\";\n", "\n", "qreg q[2];\n", "creg cr[2];\n", "\n", "h q[0];\n", "h q[1];\n", "barrier q[0], q[1];\n", "cz q[0], q[1];\n", "barrier q[0], q[1];\n", "h q[0];\n", "h q[1];\n", "barrier q[0], q[1];\n", "measure q[0] -> cr[0];\n", "measure q[1] -> cr[1];\n", "\n" ] } ], "source": [ "openqasm = circuit2.convert_to_openqasm()\n", "print(openqasm)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Simulating circuits\n", "`pyGSTi` contains a range of simulators, which can be used to simulate circuits. First, let's pick a circuit to simulate." ] }, { "cell_type": "code", "execution_count": 26, "metadata": {}, "outputs": [], "source": [ "clifford_circuit = circuitList[2] " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Any `GateSet` object contains methods for generating probabilities from a string of gates, and these simulators can be applied to a circuit using the `simulate()` method of circuit. \n", "\n", "Error-free simulations of circuits can be achieved by passing the circuit one of the models contained within a `ProcessorSpec`, which are auto-generated when a `ProcessorSpec` is initialized. Simulations for imperfect gates require the user to build their own imperfect gateset (although there are also Pauli-error circuit simulators in the RB code). \n", "\n", "Here we'll do a perfect-circuit (efficient-in-qubit-number) Clifford simulation, using the 'clifford' model that is, by default, part of a ProcessorSpec." ] }, { "cell_type": "code", "execution_count": 27, "metadata": {}, "outputs": [], "source": [ "gateset = pspec2.models['clifford']" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Once we have a `GateSet`, the simulation is easy:" ] }, { "cell_type": "code", "execution_count": 28, "metadata": {}, "outputs": [], "source": [ "out = clifford_circuit.simulate(gateset=gateset)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The output is simply the outcome probabilities:" ] }, { "cell_type": "code", "execution_count": 29, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "OutcomeLabelDict([(('00100',), 0.12499999999999994), (('00111',), 0.12499999999999994), (('01100',), 0.12499999999999994), (('01111',), 0.12499999999999994), (('10100',), 0.12499999999999994), (('10111',), 0.12499999999999994), (('11100',), 0.12499999999999994), (('11111',), 0.12499999999999994)])\n" ] } ], "source": [ "print(out)" ] } ], "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.7.0" } }, "nbformat": 4, "nbformat_minor": 1 }