diff --git a/docs/source/qvm.rst b/docs/source/qvm.rst index ddf4e9ef8..ffd697216 100644 --- a/docs/source/qvm.rst +++ b/docs/source/qvm.rst @@ -3,25 +3,41 @@ The Quantum Computer ==================== +PyQuil is used to build Quil (Quantum Instruction Language) programs and execute them on simulated or real quantum devices. Quil is an opinionated +quantum instruction language: its basic belief is that in the near term quantum computers will +operate as coprocessors, working in concert with traditional CPUs. This means that Quil is designed to execute on +a Quantum Abstract Machine (QAM) that has a shared classical/quantum architecture at its core. + +A QAM must, therefore, implement certain abstract methods to manipulate classical and quantum states, such as loading +programs, writing to shared classical memory, and executing programs. + +The program execution itself is sent from pyQuil to quantum computer endpoints, which will be one of two options: + + - A Rigetti Quantum Virtual Machine (QVM) + - A Rigetti Quantum Processing Unit (QPU) + +Within pyQuil, there is a :py:class:`~pyquil.api.QVM` object and a :py:class:`~pyquil.api.QPU` object which use +the exposed APIs of the QVM and QPU servers, respectively. + +On this page, we'll learn a bit about the :ref:`QVM ` and :ref:`QPU `. Then we will +show you how to use them from pyQuil with a :ref:`quantum_computer`. + +For information on constructing quantum programs, please refer back to :ref:`basics`. + +.. _qvm_use: + The Quantum Virtual Machine (QVM) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ The Rigetti Quantum Virtual Machine is an implementation of the Quantum Abstract Machine from *A Practical Quantum Instruction Set Architecture*. [1]_ It is implemented in ANSI Common LISP and -executes programs specified in the Quantum Instruction Language (Quil). - -Quil is an opinionated quantum instruction language: its basic belief is that in the near term quantum computers will -operate as coprocessors, working in concert with traditional CPUs. This means that Quil is designed to execute on -a Quantum Abstract Machine that has a shared classical/quantum architecture at its core. +executes programs specified in Quil. The QVM is a wavefunction simulation of unitary evolution with classical control flow and shared quantum classical memory. -.. _qvm_use: - -Using the QVM -------------- -After :ref:`downloading the SDK `, the QVM is available on your local machine. You can initialize a local +The QVM is part of the Forest SDK, and it's available for you to use on your local machine. +After :ref:`downloading and installing the SDK `, you can initialize a local QVM server by typing ``qvm -S`` into your terminal. You should see the following message. .. code:: python @@ -36,43 +52,242 @@ QVM server by typing ``qvm -S`` into your terminal. You should see the following [2018-11-06 18:18:18] Starting server on port 5000. -For a detailed description of how to use the ``qvm`` from the command line, see :ref:`The QVM manual page `. +By default, the server is started on port 5000 on your local machine. Consequently, the endpoint which +the pyQuil :py:class:`~pyquil.api.QVM` will default to for the QVM address is ``"http://127.0.0.1:5000"``. When you +run your program, a pyQuil client will send a Quil program to the QVM server and wait for a response back. -Once the QVM is serving requests, we can run the following pyQuil program to get a ``QuantumComputer`` object which -will use the QVM. +It's also possible to use the QVM from the command line. You can write a Quil program in its own file: + +.. code:: python + + # example.quil + + DECLARE ro BIT[1] + RX(pi/2) 0 + CZ 0 1 + +and then execute it with the QVM directly from the command line: + +.. code:: python + + $ qvm -e < example.quil + + [2018-11-30 11:13:58] Reading program. + [2018-11-30 11:13:58] Allocating memory for QVM of 2 qubits. + [2018-11-30 11:13:58] Allocation completed in 4 ms. + [2018-11-30 11:13:58] Loading quantum program. + [2018-11-30 11:13:58] Executing quantum program. + [2018-11-30 11:13:58] Execution completed in 6 ms. + [2018-11-30 11:13:58] Printing 2-qubit state. + [2018-11-30 11:13:58] Amplitudes: + [2018-11-30 11:13:58] |00>: 0.0, P= 0.0% + [2018-11-30 11:13:58] |01>: 0.0-1.0i, P=100.0% + [2018-11-30 11:13:58] |10>: 0.0, P= 0.0% + [2018-11-30 11:13:58] |11>: 0.0, P= 0.0% + [2018-11-30 11:13:58] Classical memory (low -> high indexes): + [2018-11-30 11:13:58] ro: 1 0 + +For a detailed description of how to use the ``qvm`` from the command line, see :ref:`The QVM manual page ` or +type ``man qvm`` in your terminal. + +We also offer a Wavefunction Simulator (formerly a part of the :py:class:`~pyquil.api.QVM` object), +which allows users to contruct and inspect wavefunctions of quantum programs. Learn more +about the Wavefunction Simulator :ref:`here `. + +.. _qpu: + +The Quantum Processing Unit (QPU) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +To access a QPU endpoint, you will have to `sign up `_ for Quantum Cloud Services (QCS). +Documentation for getting started with your Quantum Machine Image (QMI) is found +`here `_. Using QCS, you will ``ssh`` into your QMI, and reserve a +QPU lattice for a particular time block. + +When your reservation begins, you will be authorized to access the QPU. A configuration file will be automatically +populated for you with the proper QPU endpoint for your reservation. Both your QMI and the QPU are located on premises, +giving you low latency access to the QPU server. That server accepts jobs in the form of ``BinaryExecutableRequest``s, +which is precisely what you get back when you compile your program in pyQuil and target the QPU (more on this soon). +This request contains all the information necessary to run your program on the control rack which sends and receives +waveforms from the QPU, so that you can receive classified readout results (``0``s and ``1``s). + +For information on available lattices, you can check out your dashboard at https://qcs.rigetti.com/dashboard after you've +been invited to QCS. + + +.. _quantum_computer: + +The ``QuantumComputer`` +~~~~~~~~~~~~~~~~~~~~~~~ + +The :py:class:`~pyquil.api.QuantumComputer` abstraction offered by pyQuil provides an easy access point to the most +critical objects used in pyQuil for building and executing your quantum programs. +We will cover the main methods and attributes on this page. +The `QuantumComputer API Reference `_ provides a reference for all of its methods and +options. + +At a high level, the :py:class:`~pyquil.api.QuantumComputer` wraps around our favorite quantum computing tools: + + - **A quantum abstract machine** ``.qam`` : this is our general purpose quantum computing device, + which implements the required abstract methods described :ref:`above `. It is implemented as a + :py:class:`~pyquil.api.QVM` or :py:class:`~pyquil.api.QPU` object in pyQuil. + - **A compiler** ``.compiler`` : this determines how we manipulate the Quil input to something more efficient when possible, + and then into a form which our QAM can accept as input. + - **A device** ``.device`` : this specifies the topology and Instruction Set Architecture (ISA) of + the targeted device by listing the supported 1Q and 2Q gates. + +When you instantiate a :py:class:`~pyquil.api.QuantumComputer` instance, these subcomponents will be compatible with +each other. So, if you get a ``QPU`` implementation for the ``.qam``, you will have a ``QPUCompiler`` for the +``.compiler``, and your ``.device`` will match the device used by the ``.compiler.`` + +The :py:class:`~pyquil.api.QuantumComputer` instance makes methods available which are built on the above objects. If +you need more fine grained controls for your work, you might try exploring what is offered by these objects. + +For more information on each of the above, check out the following pages: + + - `Compiler API Reference `_ + - :ref:`Quil Compiler docs ` + - `Device API Reference `_ + - :ref:`new_topology` + - `Quantum abstract machine (QAM) API Reference `_ + - `The Quil Whitepaper `_ which describes the QAM + +Instantiation +------------- + +A decent amount of information needs to be provided to initialize the ``compiler``, ``device``, and ``qam`` attributes, +much of which is already in your :ref:`config files <_advanced_usage>` (or provided reasonable defaults when running locally). +Typically, you will want a :py:class:`~pyquil.api.QuantumComputer` which either: + + - pertains to a real, available QPU device + - is a QVM but mimics the topology of a QPU + - is some generic QVM + +All of this can be accomplished with :py:func:`~pyquil.api.get_qc`. + +.. code:: python + + def get_qc(name: str, *, as_qvm: bool = None, noisy: bool = None, + connection: ForestConnection = None) -> QuantumComputer: + +.. code:: python + + from pyquil import get_qc + + # Get a QPU + qc = get_qc(QPU_LATTICE_NAME) # QPU_LATTICE_NAME is just a string naming the device + + # Get a QVM with the same topology as the QPU lattice + qc = get_qc(QPU_LATTICE_NAME, as_qvm=True) + # or, equivalently + qc = get_qc(f"{QPU_LATTICE_NAME}-qvm") + + # A fully connected QVM + number_of_qubits = 10 + qc = get_qc(f"{number_of_qubits}q-qvm") + +For now, you will have to join QCS to get ``QPU_LATTICE_NAME`` by running the +``qcs lattices`` command from your QMI. Access to the QPU is only possible from a QMI, during a booked reservation. +If this sounds unfamiliar, check out our `documentation for QCS `_ +and `join the waitlist `_. + +For more information about creating and adding your own noise models, check out :ref:`noise`. + +.. note:: + When connecting to a QVM locally (such as with ``get_qc(..., as_qvm=True)``) you'll have to set up the QVM + in :ref:`server mode `. + +Methods +------- + +Now that you have your ``qc``, there's a lot you can do with it. Most users will want to use ``compile``, ``run`` or +``run_and_measure``, and ``qubits`` very regularly. The general flow of use would look like this: .. code:: python from pyquil import get_qc, Program from pyquil.gates import * - qc = get_qc('9q-square-qvm') + qc = get_qc('9q-square-qvm') # not general to any number of qubits, 9q-square-qvm is special -One executes quantum programs on the QVM using the ``.run(...)`` method, intended to closely mirror how one will -execute programs on a real QPU. We also offer a Wavefunction Simulator (formerly a part of the ``QVM`` object), -which allows users to contruct and inspect wavefunctions of quantum programs. Learn more -about the Wavefunction Simulator :ref:`here `. For information on constructing quantum -programs, please refer back to :ref:`basics`. + qubits = qc.qubits() # this information comes from qc.device + p = Program() + # ... build program, potentially making use of the qubits list + + compiled_program = qc.compile(p) # this makes multiple calls to qc.compiler + + results = qc.run(compiled_program) # this makes multiple calls to qc.qam + +.. note:: + + In addition to a running QVM server, you will need a running ``quilc`` server to compile your program. Setting + up both of these is very easy, as explained :ref:`here `. + + +The ``.run_and_measure(...)`` method +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +This is the most high level way to run your program. With this method, you are **not** responsible for compiling your program +before running it, nor do you have to specify any ``MEASURE`` instructions; all qubits will get measured. + +.. code:: python + + from pyquil import Program, get_qc + from pyquil.gates import X + + qc = get_qc("8q-qvm") + + p = Program(X(0)) + + results = qc.run_and_measure(p, trials=5) + print(results) + +``trials`` specifies how many times to run this program. Let's see our results: + +.. parsed-literal:: + + {0: array([1, 1, 1, 1, 1]), + 1: array([0, 0, 0, 0, 0]), + 2: array([0, 0, 0, 0, 0]), + 3: array([0, 0, 0, 0, 0]), + 4: array([0, 0, 0, 0, 0]), + 5: array([0, 0, 0, 0, 0]), + 6: array([0, 0, 0, 0, 0]), + 7: array([0, 0, 0, 0, 0])} + +The return value is a dictionary from qubit index to results for all trials. +Every qubit in the lattice is measured for you, and as expected, qubit 0 has been flipped to the excited state +for each trial. The ``.run(...)`` method ------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^ -The ``.run(...)`` method takes in a compiled program. You are responsible for compiling your program before running it. -Remember to also start up a ``quilc`` compiler server, too, with ``quilc -S``. +The lower-level ``.run(...)`` method gives you more control over how you want to build and compile your program than +``.run_and_measure`` does. **You are responsible for compiling your program before running it.** +The above program would be written in this way to execute with ``run``: .. code:: python + from pyquil import Program, get_qc + from pyquil.gates import X, MEASURE + + qc = get_qc("8q-qvm") + p = Program() ro = p.declare('ro', 'BIT', 1) p += X(0) p += MEASURE(0, ro[0]) p += MEASURE(1, ro[1]) p.wrap_in_numshots_loop(5) + executable = qc.compile(p) - results = qc.run(executable) - print(results) + bitstrings = qc.run(executable) # .run takes in a compiled program, unlike .run_and_measure + print(bitstrings) -The results returned are a *list of lists of integers*. In the above case, that's +By specifying ``MEASURE`` ourselves, we will only get the results that we are interested in. To be completely equivalent +to the previous example, we would have to measure all eight qubits. + +The results returned is a *list of lists of integers*. In the above case, that's .. parsed-literal:: @@ -86,23 +301,62 @@ we use as our readout register. We see that the result of this program is that t the state of qubit 0, which should be ``1`` after an :math:`X`-gate. See :ref:`declaring_memory` and :ref:`measurement` for more details about declaring and accessing classical memory regions. -.. [1] https://arxiv.org/abs/1608.03355 +.. tip:: Get the results for qubit 0 with ``numpy.array(bitstrings)[:,0]``. + +.. _new_topology: + +Providing Your Own Device Topology +---------------------------------- + +It is simple to provide your own device topology as long as you can give your qubits each a number, +and specify which edges exist. Here is an example, using the topology of our 16Q chip (two octagons connected by a square): + +.. code:: python + + import networkx as nx + + from pyquil.device import NxDevice, gates_in_isa + from pyquil.noise import decoherence_noise_with_asymmetric_ro + + qubits = [0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 16, 17] # qubits are numbered by octagon + edges = [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7), (7, 0), # first octagon + (1, 16), (2, 15), # connections across the square + (10, 11), (11, 12), (13, 14), (14, 15), (15, 16), (16, 17), (10, 17)] # second octagon + + # Build the NX graph + topo = nx.from_edgelist(edges) + # You would uncomment the next line if you have disconnected qubits + # topo.add_nodes_from(qubits) + device = NxDevice(topo) + device.noise_model = decoherence_noise_with_asymmetric_ro(gates_in_isa(device.get_isa())) # Optional + +Now that you have your device, you could set ``qc.device`` and ``qc.compiler.device`` to point to your new device, +or use it to make new objects. Simulating the QPU using the QVM -------------------------------- -The QVM is a powerful tool for testing quantum programs before executing them on the QPU. +The :py:class:`~pyquil.api.QAM` methods are intended to be used in the same way, whether a QVM or QPU is being targeted. +Everywhere on this page, +you can swap out the type of the QAM (QVM <=> QPU) and you will still +get reasonable results back. As long as the topology of the devices are the same, programs compiled and ran on the QVM +will be able to run on the QPU and visa-versa. Since :py:class:`~pyquil.api.QuantumComputer` is built on the ``QAM`` +abstract class, its methods will also work for both QAM implementations. + +This makes the QVM a powerful tool for testing quantum programs before executing them on the QPU. .. code:: python - qc = get_qc("QuantumComputerName") - qc = get_qc("QuantumComputerName-qvm") + qpu = get_qc(QPU_LATTICE_NAME) + qvm = get_qc(QPU_LATTICE_NAME, as_qvm=True) -By simply providing ``-qvm`` in the device name, all programs executed on this QVM will, have the same topology as -the named QPU. To learn how to add noise models to your virtual ``QuantumComputer`` instance, check out +By simply providing ``as_qvm=True``, we get a QVM which will have the same topology as +the named QPU. It's a good idea to run your programs against the QVM before booking QPU time to iron out +bugs. To learn more about how to add noise models to your virtual ``QuantumComputer`` instance, check out :ref:`noise`. -The Quantum Processing Unit -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +In the next section, we will see how to use the Wavefunction Simulator aspect of the Rigetti QVM to inspect the full +wavefunction set up by a Quil program. + +.. [1] https://arxiv.org/abs/1608.03355 -*Coming soon*: Detailed information about how to use :py:func:`~pyquil.get_qc` to target a QPU.