Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

V4 - Update remaining documentation #1652

Merged
merged 17 commits into from
Sep 15, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ The 4.0 release of pyQuil migrates its core functionality into Rigetti's latest
- Removed the `compatibility.v2` sub-package.
- Removed the `EngagementManager` class as RPCQ is no longer used.
- Python 3.7 is no longer supported.
- The environment variable overrides for `quilc` and `QVM` URLs have been renamed to `QCS_APPLICATIONS_QUILC_URL` and `QCS_APPLICATIONS_QVM_URL`, respectively.
- The environment variable overrides for `quilc` and `QVM` URLs have been renamed to `QCS_SETTINGS_APPLICATIONS_QUILC_URL` and `QCS_SETTINGS_APPLICATIONS_QVM_URL`, respectively.
- The `QuantumComputer`'s `run` method now takes an optional `memory_map` parameter. This mapping takes memory region names to a list of values to use for a run. This replaces the ability to use `write_memory` on `Program`s.
- `Program` and instructions have been re-written using the `quil` package. Much of the API remains the same, with the following exceptions:
- `SwapPhase` has been renamed to `SwapPhases`
Expand All @@ -35,6 +35,7 @@ The 4.0 release of pyQuil migrates its core functionality into Rigetti's latest
- The `JumpConditional` base class has been removed, use `JumpWhen` and/or `JumpUnless` directly instead.
- The `Program` class automatically sorts `DECLARE` instructions to the top of the Program when converting to Quil.
- `FenceAll` is now a subclass of `Fence`. This can be impactful if you are doing something like `isinstance(instruction, Fence)` since that will now match `Fence` and `FenceAll`. If the difference between the two is important, check for `FenceAll` first. You can also check if the `qubits` property is empty, which implies a `FenceAll` instruction.
- The `RawInstr` class has been removed. All Quil instructions should be supported by either parsing them with the `Program` class, or constructing them with an instruction class. If you were using `RawInstr` for QASM2.0 transpilation, use the new `transpile_qasm_2` method on `AbstractCompiler`.

### Features

Expand All @@ -54,6 +55,7 @@ installation, perform diagnostics checks, and return a summary.
- The `Include` class for `INCLUDE` instructions.
- The `DefCircuit` class `DEFCIRCUIT` instructions.
- The `Program.copy` method now performs a deep copy.
- The `AbstractCompiler` class now has a method `transpile_qasm_2` method for transpiling QASM2.0 programs to Quil.

### Deprecations

Expand Down
120 changes: 84 additions & 36 deletions docs/source/advanced_usage.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
.. _advanced_usage:

Advanced Usage
==============
Advanced usage
==============

.. note::
Expand All @@ -10,30 +11,32 @@ Advanced Usage

.. _pyquil_configuration:

pyQuil Configuration
~~~~~~~~~~~~~~~~~~~~
********************
pyQuil configuration
********************

:py:class:`~pyquil.api.QCSClientConfiguration` instructs pyQuil on how to connect with the
components needed to compile and run programs (quilc, QVMs, and QCS). Any APIs that take a configuration object
as input (e.g. :py:func:`~pyquil.get_qc()`) typically do so optionally, so that a default configuration can be loaded
:py:class:`~pyquil.api.QCSClient` instructs pyQuil on how to connect with the components needed to compile and run
programs (``quilc``, ``qvm``, and QCS). Any APIs that take a configuration object as input
(e.g. :py:func:`~pyquil.api.get_qc`) typically do so optionally, so that a default configuration can be loaded
for you if one is not provided. You can override this default configuration by either instantiating your own
:py:class:`~pyquil.api.QCSClientConfiguration` object and providing it as input to the function in question,
:py:class:`~pyquil.api.QCSClient` object and providing it as input to the function in question,
or by setting the ``QCS_SETTINGS_FILE_PATH`` and/or ``QCS_SECRETS_FILE_PATH`` environment variables to have
pyQuil load its settings and secrets from specific locations. By default, configuration will be loaded from
``$HOME/.qcs/settings.toml`` and ``$HOME/.qcs/secrets.toml``.

Additionally, you can override whichever QVM and quilc URLs are loaded from ``settings.toml``
(``profiles.<profile>.applications.pyquil.qvm_url`` and ``profiles.<profile>.applications.pyquil.quilc_url`` fields)
by setting the ``QCS_SETTINGS_APPLICATIONS_PYQUIL_QVM_URL`` and/or ``QCS_SETTINGS_APPLICATIONS_QUILC_URL``
by setting the ``QCS_SETTINGS_APPLICATIONS_QVM_URL`` and/or ``QCS_SETTINGS_APPLICATIONS_QUILC_URL``
environment variables. If these URLs are missing from ``settings.toml`` and are not set by environment variables,
the following defaults will be used (as they correspond to the default behavior of the QVM and quilc when running
locally):

- QVM URL: ``http://127.0.0.1:5000``
- quilc URL: ``tcp://127.0.0.1:5555``

**************
Multithreading
~~~~~~~~~~~~~~
**************

:py:class:`~pyquil.api.QuantumComputer` objects are safe to share between threads, enabling you to execute and retrieve
results for multiple programs or parameter values at once. Note that :py:class:`~pyquil.Program` and
Expand All @@ -43,13 +46,16 @@ concurrent context.
.. note::
The QVM processes incoming requests in parallel, while a QPU may process them sequentially or in parallel
(depending on the qubits used). If you encounter timeouts while trying to run large numbers of programs against a
QPU, try increasing the ``execution_timeout`` parameter on calls to :py:func:`~pyquil.get_qc()` (specified in
QPU, try increasing the ``execution_timeout`` parameter on calls to :py:func:`~pyquil.get_qc` (specified in
seconds).

.. note::
We suggest running jobs with a minimum of 2x parallelism, so that the QVM or QPU
is fully occupied while your program runs and no time is wasted in between jobs.

.. note::
Because pyQuil does not currently have an ``asyncio`` API it is recommended to use ``ThreadPool``\s.

Below is an example that demonstrates how to use pyQuil in a multithreading scenario:

.. code:: python
Expand Down Expand Up @@ -81,8 +87,9 @@ Below is an example that demonstrates how to use pyQuil in a multithreading scen
print(f"Results for program {i}:\n{result}\n")


Alternative QPU Endpoints
~~~~~~~~~~~~~~~~~~~~~~~~~
*************************
Alternative QPU endpoints
*************************

Rigetti QCS supports alternative endpoints for access to a QPU architecture, useful for very particular cases.
Generally, this is useful to call "mock" or test endpoints, which simulate the results of execution for the
Expand All @@ -95,18 +102,19 @@ of the sites where ``quantum_processor_id`` is used:
.. code:: python

# Option 1
qc = get_qc("Aspen-9", endpoint_id="my_endpoint")
qc = get_qc("Aspen-M-3", endpoint_id="my_endpoint")

# Option 2
qam = QPU(quantum_processor_id="Aspen-9", endpoint_id="my_endpoint")
qam = QPU(quantum_processor_id="Aspen-M-3", endpoint_id="my_endpoint")

After doing so, for all intents and purposes - compilation, optimization, etc - your program will behave the same
as when using "default" endpoint for a given quantum processor, except that it will be executed by an
alternate QCS service, and the results of execution should not be treated as correct or meaningful.


Using Qubit Placeholders
~~~~~~~~~~~~~~~~~~~~~~~~
************************
Using qubit placeholders
************************

.. note::
The functionality provided inline by ``QubitPlaceholders`` is similar to writing a function which returns a
Expand All @@ -128,7 +136,7 @@ In pyQuil, we typically use integers to identify qubits
However, when running on real, near-term QPUs we care about what
particular physical qubits our program will run on. In fact, we may want
to run the same program on an assortment of different qubits. This is
where using ``QubitPlaceholder``\ s comes in.
where using ``QubitPlaceholder``\s comes in.

.. testsetup:: placeholders

Expand All @@ -154,9 +162,11 @@ where using ``QubitPlaceholder``\ s comes in.
H Placeholder(QubitPlaceholder(0x600002DEB5B0))
CNOT Placeholder(QubitPlaceholder(0x600002DEB5B0)) Placeholder(QubitPlaceholder(0x600002DEABB0))

..
Could not make this a doctest because it would keep failing. ``doctest`` is supposed to match the
exception text if one occurs, but instead it seemed to fail the test because an exception happened.
Addressing qubits
=================

If your program uses ``QubitPlaceholder``\s, the placeholders must be resolved before your program can
be run. If you try to run a program with unresolved placeholders, you will get an error:

.. code:: python

Expand All @@ -166,10 +176,9 @@ where using ``QubitPlaceholder``\ s comes in.

RuntimeError: Qubit q4402789176 has not been assigned an index


Instead, you must explicitly map the placeholders to physical qubits. By
default, the function ``address_qubits`` will address qubits from 0 to
N.
default, the function :py:func:`~pyquil.quil.address_qubits` will address qubits from 0 to
N, skipping indices that are already used in the program.

.. testcode:: placeholders

Expand All @@ -195,13 +204,44 @@ The real power comes into play when you provide an explicit mapping:
H 14
CNOT 14 19

As an alternative to a mapping, you can consider using :py:meth:`~pyquil.quil.Program.resolve_placeholders_with_custom_resolvers`.
This method accepts any function that takes a placeholder as an argument, and returns a fixed value for that placeholder (or
``None``, if you want it to remain unresolved).

.. testsetup:: placeholders

from typing import Optional
from pyquil import Program, get_qc
from pyquil.gates import H, CNOT
from pyquil.quilatom import QubitPlaceholder

.. testcode:: placeholders

q0 = QubitPlaceholder()
q1 = QubitPlaceholder()
p = Program(H(q0), CNOT(q0, q1))
qc = get_qc("2q-qvm")

def qubit_resolver(placeholder: QubitPlaceholder) -> Optional[int]:
if placeholder == q0:
return 0
if placeholder == q1:
return None

p.resolve_placeholders_with_custom_resolvers(qubit_resolver=qubit_resolver)
print(p)

.. testoutput:: placeholders

H 0
CNOT 0 Placeholder(...)

Register
--------
Requesting a register of qubit placeholders
===========================================

Usually, your algorithm will use an assortment of qubits. You can use
the convenience function ``QubitPlaceholder.register()`` to request a
list of qubits to build your program.
the convenience function :py:meth:`~pyquil.quilatom.QubitPlaceholder.register` to request a
register of qubits to build your program.

.. testsetup:: register

Expand All @@ -228,17 +268,20 @@ list of qubits to build your program.
H 12
H 14

Classical Control Flow
~~~~~~~~~~~~~~~~~~~~~~
**********************
Classical control flow
**********************

.. note::
Here are a couple quick examples that show how much richer a Quil program
can be with classical control flow.

Classical control flow is not yet supported on the QPU.
.. warning::
Dynamic control flow can have unexpected effects on readout data. See :ref:`accessing_raw_execution_data` for more information.

While loops
===========

Here are a couple quick examples that show how much richer a Quil program
can be with classical control flow. In this first example, we create a while
loop by following these steps:
In this first example, we create a while loop by following these steps:

1. Declare a register called ``flag_register`` to use as a boolean test for looping.

Expand All @@ -251,6 +294,8 @@ loop by following these steps:

4. Use the :py:func:`~pyquil.quil.Program.while_do` method to add control flow.

5. Call :py:meth:`~pyquil.quil.Program.resolve_label_placeholders` to resolve the label placeholders inserted by ``while_do``.

.. testcode:: control-flow

from pyquil import Program
Expand Down Expand Up @@ -295,6 +340,9 @@ classical register. There are several classical commands that can be used in th
- ``MOVE`` which moves the value of a classical bit at one classical address into another
- ``EXCHANGE`` which swaps the value of two classical bits

If, then
========

In this next example, we show how to do conditional branching in the
form of the traditional ``if`` construct as in many programming
languages. Much like the last example, we construct programs for each
Expand Down Expand Up @@ -378,8 +426,9 @@ We can run this program a few times to see what we get in the readout register `
[0]]


**********************
Pauli Operator Algebra
~~~~~~~~~~~~~~~~~~~~~~
**********************

Many algorithms require manipulating sums of Pauli combinations, such as
:math:`\sigma = \frac{1}{2}I - \frac{3}{4}X_0Y_1Z_3 + (5-2i)Z_1X_2,` where
Expand Down Expand Up @@ -482,4 +531,3 @@ To take it one step further, you can use :ref:`parametric_compilation` with ``ex
theta = prog.declare('theta', 'REAL')
prog += exponential_map(ham)(theta)


Loading
Loading