Skip to content
Merged
Show file tree
Hide file tree
Changes from 61 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
ea20069
Breaking: Update to use qcs-api-client (#1300)
erichulburd Mar 17, 2021
1dcb94f
Breaking: Migrate project to poetry
ameyer-rigetti Mar 17, 2021
52a1807
WIP: Support QVM asynchronous execution with compatibility layer
kalzoo Apr 28, 2021
be0cf49
Update: store parameter values on Program, not QAM
kalzoo Apr 29, 2021
94ba3e6
Tests: Update QVM Tests
kalzoo Apr 29, 2021
0cba876
WIP: refactor PyQVM to support new QAM API
kalzoo Apr 29, 2021
bda7d67
Update: QPU supports new QAM API
kalzoo Apr 29, 2021
32a6315
WIP: Improve compatibility interfaces and update tests to match
kalzoo May 19, 2021
23436fa
Fix: rebasing mistakes
kalzoo Jun 13, 2021
b56996f
Fix: incorrect test assertion
kalzoo Jun 14, 2021
b47187a
Fix: rebasing mistake
kalzoo Jun 14, 2021
347e0f0
Fix: tests
kalzoo Jun 14, 2021
262f078
Fix: PyQVM#execute
kalzoo Jun 14, 2021
54ffa39
Fix: QVM + experiment compatibility layer
kalzoo Jun 14, 2021
b7aed86
Fix: Program support for variable values
kalzoo Jun 14, 2021
bf828dc
Style: fix
kalzoo Jun 14, 2021
586155e
Fix: rebasing mistakes
kalzoo Jun 14, 2021
e0ee067
Rename compatibility tests
kalzoo Jun 16, 2021
186ba0c
Update: refactor program memory management
kalzoo Jun 16, 2021
dded7b7
Tests: update to use new API
kalzoo Jun 16, 2021
011d377
Docs: update
kalzoo Jun 16, 2021
f13e684
Docs: update changelog
kalzoo Jun 16, 2021
f7fc111
Docs: update changelog
kalzoo Jun 16, 2021
7f6d46a
Fix: remove duplicate Memory definition
kalzoo Jun 16, 2021
eb4f91a
Remove requirements.txt
kalzoo Jun 16, 2021
fd2f3f5
Docs: fix inline docs errors
kalzoo Jun 16, 2021
60bd2d8
Fix: stateful QAM leftovers
kalzoo Jun 16, 2021
95bf9b4
Fix: address PR feedback
kalzoo Jun 16, 2021
0b071d7
No code: address Program thread safety in docs
kalzoo Jun 16, 2021
b9f3d19
No code: fix docs
kalzoo Jun 16, 2021
17f21a7
Fix: QVM#get_results return shape
kalzoo Jun 16, 2021
6aaa270
Update: rename QAM.get_results() to QAM.get_result()
kalzoo Jun 16, 2021
8124327
Fix: QVMExecuteResponse
kalzoo Jun 16, 2021
1c82890
Fix: rename generic ExecuteResponse
kalzoo Jun 16, 2021
85886a3
Breaking: return execution results data as readout_data, not memory
kalzoo Jun 16, 2021
7822029
No code: update docs to reflect readout data
kalzoo Jun 17, 2021
cbcb387
Fix: remove QAM.experiment attribute
kalzoo Jun 17, 2021
f68cae9
Fix: make QPUCompiler.calibration_program thread-safe
kalzoo Jun 17, 2021
92875d3
CI: remove entry from mypy config
kalzoo Jun 17, 2021
4f210e8
Fix: type check failures
kalzoo Jun 17, 2021
54c47a7
Fix: move EngagementManager._lock to the instance
kalzoo Jun 18, 2021
271de7f
Style: fix
kalzoo Jun 18, 2021
9de7c88
No code: fix changelog
kalzoo Jun 21, 2021
330f49d
No code: fix e2e test
kalzoo Jun 21, 2021
38ffbbd
Fix: fix e2e tests
ameyer-rigetti Jun 22, 2021
d2daf19
No code: fix test mock
kalzoo Jun 23, 2021
969d96d
No code: update Readme example
kalzoo Jun 23, 2021
3520812
Fix: compatibility QuantumComputer
kalzoo Jun 23, 2021
f93c606
No code: fix example
kalzoo Jun 23, 2021
db1c94a
No code: add documentation on QAM.execute
kalzoo Jun 23, 2021
df6d355
Fix: replace threading.Lock with multiprocessing.Lock
kalzoo Jun 23, 2021
1a4c323
Merge branch 'rc' into 1330-async-execution
kalzoo Jun 23, 2021
f19354b
Fix: Program#copy
kalzoo Jun 23, 2021
d3ae981
No code: fix multiprocessing.Lock type checks
kalzoo Jun 23, 2021
87ec006
Fix: type check
kalzoo Jun 23, 2021
f816171
No code: fix autodoc toctree
kalzoo Jun 23, 2021
50260b8
Fix: style
kalzoo Jun 23, 2021
82fef98
No code: fix docs
kalzoo Jun 23, 2021
091cef7
No code: fix test assertion
kalzoo Jun 23, 2021
621c550
No code: restore original test random_seed
kalzoo Jun 23, 2021
46d442d
No code: allow QPU e2e test failure to allow for manual runs within r…
kalzoo Jun 23, 2021
24552ff
No code: use Rigetti fork of dephell
kalzoo Jun 23, 2021
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
3 changes: 3 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ Test Unit (3.7):

Test e2e QPU (3.7):
stage: test
allow_failure: true
image: python:3.7
coverage: '/TOTAL.*?(\d+)\%/'
script:
Expand All @@ -101,6 +102,7 @@ Test Unit (3.8):

Test e2e QPU (3.8):
stage: test
allow_failure: true
image: python:3.8
coverage: '/TOTAL.*?(\d+)\%/'
script:
Expand All @@ -127,6 +129,7 @@ Test Unit (3.9):

Test e2e QPU (3.9):
stage: test
allow_failure: true
image: python:3.9
coverage: '/TOTAL.*?(\d+)\%/'
script:
Expand Down
45 changes: 41 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,15 @@ Changelog

### Announcements

- Python 3.6 is no longer supported. Python 3.7, 3.8, and 3.9 are supported.
- pyQuil now directly supports the QCS API v1.0, offering you better performance and more
granular data about QCS quantum processors.

- Python 3.6 is no longer supported. Python 3.7, 3.8, and 3.9 are supported.

- `pyquil.compatibility.v2` provides a number of classes which support the pyQuil v2 API, such as
Comment thread
kalzoo marked this conversation as resolved.
`QuantumComputer` and `get_qc`; `pyquil.compatibility.v2.api` offers `QPU` and `QVM`. These may be
used to incrementally migrate from v2 to v3, but should not be relied on indefinitely, as the
underlying mechanics of these two versions continue to diverge in the future.
### Improvements and Changes

- Added support and documentation for concurrent compilation and execution (see "Advanced Usage" in docs)
Expand All @@ -24,8 +31,7 @@ Changelog
- `ForestConnection` and `ForestSession` have been removed. Connection information is now managed via `api.QCSClientConfiguration`
and `api.EngagementManager`.

- `QVMCompiler` now produces a `Program` instead of a `PyQuilExecutableResponse`. As a result, `QVM.load()` only
accepts a `Program`, and `QVM.requires_executable` has been removed.
- `QVMCompiler` now produces a `Program` instead of a `PyQuilExecutableResponse`.

- `QPU.get_version_info()` has been removed.

Expand Down Expand Up @@ -84,7 +90,7 @@ Changelog

- `get_qc()` no longer accepts `"9q-generic"` (deprecated). Use `"9q-square"` instead.

- Removed `QAM.read_from_memory_region()` (deprecated). Use `QAM.read_memory()` instead.
- Removed `QAM.read_from_memory_region()` (deprecated). Use `QAMExecutionResult.readout_data.get(region_name)` instead.

- Removed `local_qvm()` (deprecated). Use `local_forest_runtime()` instead.

Expand Down Expand Up @@ -119,6 +125,37 @@ Changelog
are passed to the QVM by `WavefunctionSimulator.run_and_measure()` and `WavefunctionSimulator.wavefunction()`.

- `noise.estimate_assignment_probs()` now accepts a `QuantumComputer` instead of `QVMConnection`.

- `QAM` and its subclasses (such as `QPU` and `QVM`) do not store any information specific to the state
of execution requests, and thus are safe to be used concurrently by different requests. `QAM.run`
is now composed of two intermediate calls:
- `QAM.execute` starts execution of the provided executable, returning an opaque handle.
- `QAM.get_result` uses the opaque handle returned by `execute` to retrieve the result values.

These new calls can be used to enqueue multiple programs for execution prior to retrieving
results for any of them. Note that this new pattern means that `QAM.load`, `QAM.reset`, and
`QAM.wait` no longer exist.

- `QAM.run` no longer accepts a `memory_map` argument. Memory values must be written onto
executable directly with `Program.write_memory()` and `EncryptedProgram.write_memory()` instead.

- `QuantumComputer`, `QAM`, `QPU`, and `QVM` are now safe to share across threads and processes,
as they no longer store request-related state.

- `PyQVM.execute` has been renamed to `PyQVM.execute_once` to execute a single program from start
to finish within the context of the existing `PyQVM` state. `PyQVM` is the only stateful `QAM`.
`PyQVM.execute` now implements `QAM.execute` and resets the `PyQVM` state prior to program execution.

- `QuantumComputer.experiment` has been renamed to `QuantumComputer.run_experiment`.

- Results returned from execution are now referred to as `readout_data` rather than `memory`, reflecting the reality
that the memory of the QAM is not currently exposed to the user. The exception to this rule is the stateful `PyQVM`,
whose state is maintained within the pyQuil process and whose memory _may truly be inspected._ For that,
`PyQVM.read_memory` remains available.

- `QuantumComputer.run` now returns a `QAMExecutionResult` rather than the readout data from the `ro` readout
Comment thread
kalzoo marked this conversation as resolved.
source. To access those same readout results, use `qc.run().readout_data.get('ro')`. This allows access to other
execution-related information and other readout sources.

- Simultaneous, rather than independent, random benchmark scores are passed to quilc as the gate fidelity for RX and RZ operations.

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,8 @@ ro = p.declare('ro', 'BIT', 2)
p += MEASURE(0, ro[0])
p += MEASURE(1, ro[1])
p.wrap_in_numshots_loop(10)
qvm.run(p).tolist()

qvm.run(p).readout_data['ro'].tolist()
```

The output of the above program should look something like the following,
Expand Down
31 changes: 14 additions & 17 deletions docs/source/advanced_usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,21 @@ locally):
Concurrency
~~~~~~~~~~~

Using pyQuil for concurrent programming is as simple as calling :py:func:`~pyquil.get_qc()` from within a given thread
or process, then using the returned :py:class:`~pyquil.api.QuantumComputer` as usual. While
:py:class:`~pyquil.api.QuantumComputer` objects as a whole are not safe to share between threads or processes (due to
state related to currently-running compilation or execution requests), some information they use is. Information related
to client configuration (:py:class:`~pyquil.api.QCSClientConfiguration`) and QPU auth
(:py:class:`~pyquil.api.EngagementManager`) can be safely extracted and shared among
:py:class:`~pyquil.api.QuantumComputer` instances, as shown below, to save your code from redundant disk reads and
auth-related HTTP requests.
:py:class:`~pyquil.api.QuantumComputer` objects are safe to share between threads or processes,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you completely sure this is true? What about the statefulness of the underlying AbstractCompiler (and possibly QAM)? Even though QuantumComputer is far less stateful than before, there are a few areas to keep in mind (e.g. QPUCompiler._calibration_program, QAM.experiment [doesn't look used though], etc.).

Most of these look to be set in the constructor and not mutated after, so they're probably OK, but might be worth another pass to see if any of these, and others, are at risk for data races.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great point - I had missed that. I've now removed that experiment attribute (you're right, it was unused, for this reason), and I've added a lock within the calibration_program property.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although, to add onto that, you bring up a good point - they're safe for use across threads, but that doesn't mean they're truly stateless and "safe" in every sense of the way that people can use them. It's really only that you can call their methods in different threads and expect it to work. So, to answer your question, it was my intent with this PR to make this true, but I am still not completely sure, given how Python works.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True, while much of the state is set in the ctor and is OK in most cases, users could still choose to mutate them in threads, even if our APIs don't do that naturally. If we wanted true thread safety, we'd need to put every property behind getters and setters that can lock/unlock appropriately. Moreover, that only works for non-mutable types, like ints, strings, etc. If you had a mutable property that wasn't naturally thread-safe, locking around a getter/setter would do no good if the item itself is getting mutated unsafely (i.e. qc.foo["x"] = 42, vs qc.foo = {...}). Disclaimer: not sure if dict was a good example, as they may be thread-safe for some operations, but I'm sure you get the point I'm illustrating.

enabling you to execute and retrieve results for multiple programs or parameter values at once.
Note that :py:class`~pyquil.Program` and :py:class`~pyquil.api.EncryptedProgram` are **not**
thread-safe, and should be copied (with ``copy()``) before use in a 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
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.

Using Multithreading
--------------------

Expand All @@ -58,16 +58,14 @@ Using Multithreading
from multiprocessing.pool import ThreadPool

from pyquil import get_qc, Program
from pyquil.api import EngagementManager, QCSClientConfiguration

from pyquil.api import QCSClientConfiguration

configuration = QCSClientConfiguration.load()
engagement_manager = EngagementManager(client_configuration=configuration)
qc = get_qc("Aspen-8", client_configuration=configuration)


def run(program: Program):
qc = get_qc("Aspen-8", client_configuration=configuration, engagement_manager=engagement_manager)
return qc.run(qc.compile(program))
return qc.run(qc.compile(program)).readout_data.get("ro")


programs = [Program("DECLARE ro BIT", "RX(pi) 0", "MEASURE 0 ro").wrap_in_numshots_loop(10)] * 20
Expand All @@ -86,16 +84,15 @@ Using Multiprocessing
from multiprocessing.pool import Pool

from pyquil import get_qc, Program
from pyquil.api import EngagementManager, QCSClientConfiguration
from pyquil.api import QCSClientConfiguration


configuration = QCSClientConfiguration.load()
engagement_manager = EngagementManager(client_configuration=configuration)
qc = get_qc("Aspen-8", client_configuration=configuration)


def run(program: Program):
qc = get_qc("Aspen-8", client_configuration=configuration, engagement_manager=engagement_manager)
return qc.run(qc.compile(program))
return qc.run(qc.compile(program)).readout_data.get("ro")


programs = [Program("DECLARE ro BIT", "RX(pi) 0", "MEASURE 0 ro").wrap_in_numshots_loop(10)] * 20
Expand Down
1 change: 0 additions & 1 deletion docs/source/apidocs/quantum_computer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ Quantum Computer

~QuantumComputer.run
~QuantumComputer.calibrate
~QuantumComputer.experiment
~QuantumComputer.run_symmetrized_readout
~QuantumComputer.qubits
~QuantumComputer.qubit_topology
Expand Down
9 changes: 7 additions & 2 deletions docs/source/basics.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ program on the Quantum Virtual Machine (QVM). We just have to add a few lines to
qc = get_qc('1q-qvm') # You can make any 'nq-qvm' this way for any reasonable 'n'
executable = qc.compile(p)
result = qc.run(executable)
print(result)
bitstrings = result.readout_data.get('ro')
print(bitstrings)

Congratulations! You just ran your program on the QVM. The returned value should be:

Expand Down Expand Up @@ -299,8 +300,12 @@ filled in for, say, 200 values between :math:`0` and :math:`2\pi`. We demonstrat
parametric_measurements = []

for theta in np.linspace(0, 2 * np.pi, 200):
# Set the desired parameter value in executable memory
executable.write_memory(region_name='theta', value=theta)

# Get the results of the run with the value we want to execute with
bitstrings = qc.run(executable, {'theta': [theta]})
bitstrings = qc.run(executable).readout_data.get("ro")

# Store our results
parametric_measurements.append(bitstrings)

Expand Down
12 changes: 6 additions & 6 deletions docs/source/intro.rst
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,7 @@ measurements. This functionality is emulated by :py:func:`QuantumComputer.run`.


qc = get_qc('9q-square-qvm')
print (qc.run(qc.compile(p)))
print (qc.run(qc.compile(p)).readout_data.get("ro"))


.. parsed-literal::
Expand All @@ -615,7 +615,7 @@ qubit before measurement then we obtain:
p += Program(X(0)) # Flip the qubit
p.measure(0, classical_register[0]) # Measure the qubit

print (qc.run(qc.compile(p)))
print (qc.run(qc.compile(p)).readout_data.get("ro"))


.. parsed-literal::
Expand All @@ -636,7 +636,7 @@ times then we always get the same outcome:
trials = 10
p.wrap_in_numshots_loop(shots=trials)

print (qc.run(qc.compile(p)))
print (qc.run(qc.compile(p)).readout_data.get("ro"))


.. parsed-literal::
Expand Down Expand Up @@ -705,7 +705,7 @@ extra power over regular bits.
p.wrap_in_numshots_loop(shots=10)

# We see probabilistic results of about half 1's and half 0's
print (qc.run(qc.compile(p)))
print (qc.run(qc.compile(p)).readout_data.get("ro"))


.. parsed-literal::
Expand Down Expand Up @@ -799,7 +799,7 @@ quantum operations to run.
p.measure(7, ro[7])

# Run and check register [7]
print (qc.run(qc.compile(p)))
print (qc.run(qc.compile(p)).readout_data.get("ro"))


.. parsed-literal::
Expand All @@ -824,7 +824,7 @@ increasing chance of halting, but that may run forever!
p.inst(X(0)).while_do(ro[0], inside_loop)

qc = get_qc('9q-square-qvm')
print (qc.run(qc.compile(p)))
print (qc.run(qc.compile(p)).readout_data.get("ro"))


.. parsed-literal::
Expand Down
89 changes: 86 additions & 3 deletions docs/source/migration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,89 @@ to the code affected. Most users should only need to make minimal changes.
If you've supplied ``PyquilConfig`` objects to functions (or used the ``QVM_URL`` and ``QUILC_URL`` environment variables)
to override configuration, see :ref:`pyquil_configuration`.

Lastly, pyQuil v3 relies on an updated authentication model. To get going smoothly, you should install the new `QCS CLI
<https://docs.rigetti.com/en/command-line-interface/command-line-interface>`_ and log in with it before using pyQuil v3
against real QPUs.
Authentication
--------------

pyQuil v3 relies on an updated authentication model. To get started, install the new `QCS CLI
<https://docs.rigetti.com/>`_ and
log in with it before using pyQuil v3 with QCS and live QPUs.


Parameters & Memory
-------------------

In order to give the user more control over and visibility into program execution, especially in
parallel, objects such as ``QuantumComputer``, ``QAM``, ``QPU``, and ``QVM`` are no longer stateful
with respect to individual programs. These objects are now safe to
share among different threads, so you can execute and retrieve results in parallel for even better
performance. (See :doc:`advanced_usage` for more information).

However, this required three small but important changes:

1. ``write_memory`` is no longer a method on ``QAM`` but rather on ``Program`` and ``EncryptedProgram``.
2. ``qc.run()`` no longer accepts a ``memory_map`` argument. All memory values must be set directly on the ``Program`` or ``EncryptedProgram`` using ``write_memory``.
3. ``QAM.load()``, ``QAM.wait()``, and ``QAM.reset()`` no longer exist, because the ``QAM`` no longer "stores" program state.

This means that you should now execute your programs using one of these options:

.. code:: python

qc = get_qc("Aspen-X")
program = Program()
theta = program.declare('theta', 'REAL')
program += RZ(theta, 0)
exe = qc.compile(program)

# Previously, we would have called ``qc.qam.write_memory`` instead
exe.write_memory(region_name='theta', value=np.pi)

# Option 1
result = qc.run(exe)

# Option 2
result = qc.qam.run(exe)

# Option 3
job = qc.qam.execute(exe)
Comment thread
ameyer-rigetti marked this conversation as resolved.
result = qc.qam.get_result(job)

# Run our program 10 times, enqueuing all the programs before retrieving results for any of them
jobs = [qc.qam.execute(exe) for _ in range(10)]
results = [qc.qam.get_result(job) for job in jobs]

Additionally, ``QuantumComputer.run()`` and ``QAM.run()`` now both return ``QAMExecutionResult``, rather
than the bitstrings which were read out following execution. So, to retrieve the bitstrings from region ``ro``
like you would have in pyQuil v2, query the readout data:

.. code:: python

result = qc.run(exe)
bitstrings = result.readout_data.get('ro')

Note that there is no "memory" in the result shape -- yet. Readout data is inherently collected and returned
differently than memory values are, and that nuance is no longer masked in pyQuil v3. All of the results
that, in pyQuil v2, you would have expected to retrieve using ``QAM.read_memory()`` are now present in
``QAMExecutionResult.readout_data``.

The notable exception to this is ``PyQVM``, which remains stateful, and whose memory you can still inspect using
``PyQVM.read_memory()``.


Compatibility Utilities
-----------------------

We understand that the changes above regarding parameters might cause difficulty in migration,
especially for large projects and lengthy scripts. So, to ease the migration path, we've added
the following utility classes which allow you to upgrade your pyQuil projects without having to
change any code.

.. code:: python

from pyquil.compatibility.v2 import get_qc, QuantumComputer
from pyquil.compatibility.v2.api import QAM, QVM, QPU

You can use these imported objects similarly how you use their counterparts in pyQuil v2.
Once you've verified that your scripts still work with v3, we recommend that you gradually convert
them to use the new versions of each object. This compatibility layer won't see any new
development, and without fully upgrading you'd miss out on all the new features to come in the
future.
Loading