Skip to content

Commit

Permalink
QuantumComputer method to run TomographyExperiments (#1100)
Browse files Browse the repository at this point in the history
  • Loading branch information
karalekas authored Nov 22, 2019
1 parent 2f7e4b4 commit 032cd5e
Show file tree
Hide file tree
Showing 22 changed files with 1,254 additions and 223 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ Changelog

### Improvements and Changes

- There is a new `QuantumComputer.experiment` method for running a collection of
quantum programs as defined by a `TomographyExperiment`. These objects have a
main program body and a collection of state preparation and measurement
specifications, which capture the structure of many near-term applications
and algorithms like the variational quantum eigensolver (VQE). In addition,
the `TomographyExperiment` encodes information about symmetrization, active
qubit reset, and the number of shots to perform on the quantum backend (e.g.
the QVM or QPU). For more information check out the API documentation sections
on the [Quantum Computer](docs/source/apidocs/quantum_computer.rst) and on the
[Experiment Module](docs/source/apidocs/experiment.rst) (@karalekas, gh-1100).
- Type hints have been added to the `PauliTerm` class (@rht, gh-1075).
- The `rigetti/forest` Docker image now has less noisy output due to stdout and
stderr redirection to log files `entrypoint.sh` (@karalekas, gh-1105).
Expand Down
47 changes: 47 additions & 0 deletions docs/source/apidocs/experiment.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
Experiment
==========

The ``experiment`` module offers a schema and utilities for succinctly expressing commonly
used applications and algorithms in near-term quantum programming. A ``TomographyExperiment``
is intended to be consumed by the ``QuantumComputer.experiment`` method.

**NOTE**: When working with the `experiment` method, the following declared memory labels are
reserved:

- "preparation_alpha", "preparation_beta", and "preparation_gamma"
- "measurement_alpha", "measurement_beta", and "measurement_gamma"
- "symmetrization"
- "ro"

.. currentmodule:: pyquil.experiment

Schema
------

.. autoclass:: pyquil.experiment.TomographyExperiment

.. rubric:: Methods

.. autosummary::
:toctree: autogen
:template: autosumm.rst

~TomographyExperiment.get_meas_qubits
~TomographyExperiment.get_meas_registers
~TomographyExperiment.generate_experiment_program
~TomographyExperiment.build_setting_memory_map
~TomographyExperiment.build_symmetrization_memory_maps

.. autoclass:: SymmetrizationLevel

.. autoclass:: pyquil.experiment.ExperimentSetting

.. autoclass:: pyquil.experiment.ExperimentResult

Utilities
---------

.. autofunction:: pyquil.experiment.bitstrings_to_expectations
.. autofunction:: pyquil.experiment.merge_memory_map_lists
.. autofunction:: pyquil.experiment.read_json
.. autofunction:: pyquil.experiment.to_json
21 changes: 0 additions & 21 deletions docs/source/apidocs/operator_estimation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,6 @@ Operator Estimation

.. currentmodule:: pyquil.operator_estimation

Data structures
---------------

.. autosummary::
:toctree: autogen
:template: autosumm.rst

ExperimentSetting
TomographyExperiment
ExperimentResult


Methods
-------
Expand All @@ -26,13 +15,3 @@ Methods
group_experiments
measure_observables


Utilities
---------

.. autosummary::
:toctree: autogen
:template: autosumm.rst

to_json
read_json
1 change: 1 addition & 0 deletions docs/source/apidocs/quantum_computer.rst
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ Quantum Computer
:template: autosumm.rst

~QuantumComputer.run
~QuantumComputer.experiment
~QuantumComputer.run_and_measure
~QuantumComputer.run_symmetrized_readout
~QuantumComputer.qubits
Expand Down
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Contents
apidocs/noise
apidocs/operator_estimation
apidocs/visualization
apidocs/experiment

Indices and Tables
------------------
Expand Down
1 change: 1 addition & 0 deletions pyquil/api/_qam.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,5 +135,6 @@ def reset(self):
self._variables_shim = {}
self._executable = None
self._memory_results = defaultdict(lambda: None)
self._experiment = None

self.status = 'connected'
113 changes: 112 additions & 1 deletion pyquil/api/_quantum_computer.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
import socket
import warnings
from math import pi, log
from typing import List, Dict, Tuple, Iterator, Union
from typing import List, Dict, Tuple, Iterator, Mapping, Optional, Sequence, Union
import itertools

import subprocess
Expand All @@ -36,6 +36,8 @@
from pyquil.api._qpu import QPU
from pyquil.api._qvm import ForestConnection, QVM
from pyquil.device import AbstractDevice, NxDevice, gates_in_isa, ISA, Device
from pyquil.experiment import (ExperimentResult, TomographyExperiment, bitstrings_to_expectations,
merge_memory_map_lists)
from pyquil.gates import RX, MEASURE
from pyquil.noise import decoherence_noise_with_asymmetric_ro, NoiseModel
from pyquil.pyqvm import PyQVM
Expand Down Expand Up @@ -128,6 +130,115 @@ def run(self, executable: ExecutableDesignator,
.wait() \
.read_memory(region_name='ro')

@_record_call
def experiment(
self,
experiment: TomographyExperiment,
memory_map: Optional[Mapping[str, Sequence[Union[int, float]]]] = None
) -> List[ExperimentResult]:
"""
Run a ``TomographyExperiment`` on a QVM or QPU backend. A ``TomographyExperiment``
is composed of:
- A main ``Program`` body (or ansatz).
- A collection of ``ExperimentSetting`` objects, each of which encodes a particular
state preparation and measurement.
- A ``SymmetrizationLevel`` for enacting different readout symmetrization strategies.
- A number of shots to collect for each (unsymmetrized) ``ExperimentSetting``.
Because the main ``Program`` is static from run to run of a ``TomographyExperiment``, we
can leverage our platform's Parametric Compilation feature. This means that the ``Program``
can be compiled only once, and the various alterations due to state preparation,
measurement, and symmetrization can all be realized at runtime by providing a
``memory_map``. Thus, the steps in the ``experiment`` method are as follows:
1. Check to see if this ``TomographyExperiment`` has already been loaded into this
``QuantumComputer`` object. If so, skip to step 2. Otherwise, do the following:
a. Generate a parameterized program corresponding to the ``TomographyExperiment``
(see the ``TomographyExperiment.generate_experiment_program()`` method for more
details on how it changes the main body program to support state preparation,
measurement, and symmetrization).
b. Compile the parameterized program into a parametric (binary) executable, which
contains declared variables that can be assigned at runtime.
2. For each ``ExperimentSetting`` in the ``TomographyExperiment``, we repeat the
following:
a. Build a collection of memory maps that correspond to the various state
preparation, measurement, and symmetrization specifications.
b. Run the parametric executable on the QVM or QPU backend, providing the memory map
to assign variables at runtime.
c. Extract the desired statistics from the classified bitstrings that are produced
by the QVM or QPU backend, and package them in an ``ExperimentResult`` object.
3. Return the list of ``ExperimentResult`` objects.
This method is extremely useful shorthand for running near-term applications and algorithms,
which often have this ansatz + settings structure.
:param experiment: The ``TomographyExperiment`` to run.
:param memory_map: A dictionary mapping declared variables / parameters to their values.
The values are a list of floats or integers. Each float or integer corresponds to
a particular classical memory register. The memory map provided to the ``experiment``
method corresponds to variables in the main body program that we would like to change
at runtime (e.g. the variational parameters provided to the ansatz of the variational
quantum eigensolver).
:return: A list of ``ExperimentResult`` objects containing the statistics gathered
according to the specifications of the ``TomographyExperiment``.
"""
executable = self.qam._executable
# if this experiment was the last experiment run on this QuantumComputer,
# then use the executable that is already loaded into the object
if self.qam._experiment != experiment:
experiment_program = experiment.generate_experiment_program()
executable = self.compile(experiment_program)
self.qam._experiment = experiment

if memory_map is None:
memory_map = {}

results = []
for settings in experiment:
# TODO: add support for grouped ExperimentSettings
if len(settings) > 1:
raise ValueError('We only support length-1 settings for now.')
setting = settings[0]

qubits = setting.out_operator.get_qubits()
experiment_setting_memory_map = experiment.build_setting_memory_map(setting)
symmetrization_memory_maps = experiment.build_symmetrization_memory_maps(qubits)
merged_memory_maps = merge_memory_map_lists([experiment_setting_memory_map],
symmetrization_memory_maps)

all_bitstrings = []
# TODO: accomplish symmetrization via batch endpoint
for merged_memory_map in merged_memory_maps:
final_memory_map = {**memory_map, **merged_memory_map}
bitstrings = self.run(executable, memory_map=final_memory_map)

if 'symmetrization' in final_memory_map:
bitmask = np.array(np.array(final_memory_map['symmetrization']) / np.pi,
dtype=int)
bitstrings = np.bitwise_xor(bitstrings, bitmask)
all_bitstrings.append(bitstrings)
symmetrized_bitstrings = np.concatenate(all_bitstrings)

# TODO: support simultaneous observables via multiple correlations
joint_expectations = [experiment.get_meas_registers(qubits)]
expectations = bitstrings_to_expectations(symmetrized_bitstrings,
joint_expectations=joint_expectations)

# TODO: add calibration and correction
mean = np.mean(expectations).item()
std_err = (np.std(expectations, axis=0, ddof=1) / np.sqrt(len(expectations))).item()
result = ExperimentResult(setting=setting,
expectation=mean,
std_err=std_err,
total_counts=len(expectations))
results.append(result)
return results

@_record_call
def run_symmetrized_readout(self, program: Program, trials: int, symm_type: int = 3,
meas_qubits: List[int] = None) -> np.ndarray:
Expand Down
Empty file added pyquil/api/tests/__init__.py
Empty file.
95 changes: 95 additions & 0 deletions pyquil/api/tests/test_quantum_computer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import networkx as nx
import numpy as np

from pyquil import Program
from pyquil.api import QVM, QuantumComputer
from pyquil.device import NxDevice
from pyquil.experiment import ExperimentSetting, TomographyExperiment
from pyquil.gates import CNOT, H, RESET
from pyquil.paulis import sX, sY, sZ
from pyquil.tests.utils import DummyCompiler


def test_qc_expectation(forest):
device = NxDevice(nx.complete_graph(2))
qc = QuantumComputer(
name='testy!',
qam=QVM(connection=forest),
device=device,
compiler=DummyCompiler()
)

# bell state program
p = Program()
p += RESET()
p += H(0)
p += CNOT(0, 1)
p.wrap_in_numshots_loop(10)

# XX, YY, ZZ experiment
sx = ExperimentSetting(in_state=sZ(0) * sZ(1), out_operator=sX(0) * sX(1))
sy = ExperimentSetting(in_state=sZ(0) * sZ(1), out_operator=sY(0) * sY(1))
sz = ExperimentSetting(in_state=sZ(0) * sZ(1), out_operator=sZ(0) * sZ(1))

e = TomographyExperiment(settings=[sx, sy, sz], program=p)

results = qc.experiment(e)

# XX expectation value for bell state |00> + |11> is 1
assert np.isclose(results[0].expectation, 1)
assert np.isclose(results[0].std_err, 0)
assert results[0].total_counts == 40

# YY expectation value for bell state |00> + |11> is -1
assert np.isclose(results[1].expectation, -1)
assert np.isclose(results[1].std_err, 0)
assert results[1].total_counts == 40

# ZZ expectation value for bell state |00> + |11> is 1
assert np.isclose(results[2].expectation, 1)
assert np.isclose(results[2].std_err, 0)
assert results[2].total_counts == 40


def test_qc_expectation_larger_lattice(forest):
device = NxDevice(nx.complete_graph(4))
qc = QuantumComputer(
name='testy!',
qam=QVM(connection=forest),
device=device,
compiler=DummyCompiler()
)

q0 = 2
q1 = 3

# bell state program
p = Program()
p += RESET()
p += H(q0)
p += CNOT(q0, q1)
p.wrap_in_numshots_loop(10)

# XX, YY, ZZ experiment
sx = ExperimentSetting(in_state=sZ(q0) * sZ(q1), out_operator=sX(q0) * sX(q1))
sy = ExperimentSetting(in_state=sZ(q0) * sZ(q1), out_operator=sY(q0) * sY(q1))
sz = ExperimentSetting(in_state=sZ(q0) * sZ(q1), out_operator=sZ(q0) * sZ(q1))

e = TomographyExperiment(settings=[sx, sy, sz], program=p)

results = qc.experiment(e)

# XX expectation value for bell state |00> + |11> is 1
assert np.isclose(results[0].expectation, 1)
assert np.isclose(results[0].std_err, 0)
assert results[0].total_counts == 40

# YY expectation value for bell state |00> + |11> is -1
assert np.isclose(results[1].expectation, -1)
assert np.isclose(results[1].std_err, 0)
assert results[1].total_counts == 40

# ZZ expectation value for bell state |00> + |11> is 1
assert np.isclose(results[2].expectation, 1)
assert np.isclose(results[2].std_err, 0)
assert results[2].total_counts == 40
7 changes: 4 additions & 3 deletions pyquil/experiment/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from pyquil.experiment._main import (OperatorEncoder, SymmetrizationLevel, TomographyExperiment,
read_json, to_json)
from pyquil.experiment._result import ExperimentResult
from pyquil.experiment._main import OperatorEncoder, TomographyExperiment, read_json, to_json
from pyquil.experiment._memory import merge_memory_map_lists
from pyquil.experiment._result import ExperimentResult, bitstrings_to_expectations
from pyquil.experiment._setting import (_OneQState, _pauli_to_product_state, ExperimentSetting,
SIC0, SIC1, SIC2, SIC3, TensorProductState, minusX, minusY,
minusZ, plusX, plusY, plusZ, zeros_state)
from pyquil.experiment._symmetrization import SymmetrizationLevel
Loading

0 comments on commit 032cd5e

Please sign in to comment.