From 5a9da6d52d7551de26de5aee5090336b0d367579 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Thu, 29 Feb 2024 15:44:47 -0800 Subject: [PATCH] add to interface and update docs --- docs/source/programs_and_gates.rst | 22 +++++++++++----------- pyquil/api/_qam.py | 19 ++++++++++++++++++- pyquil/api/_qpu.py | 4 ++-- pyquil/api/_qvm.py | 13 ++++++++++++- pyquil/pyqvm.py | 9 ++++++++- 5 files changed, 51 insertions(+), 16 deletions(-) diff --git a/docs/source/programs_and_gates.rst b/docs/source/programs_and_gates.rst index 70dde8be8..8ec73e19b 100644 --- a/docs/source/programs_and_gates.rst +++ b/docs/source/programs_and_gates.rst @@ -361,21 +361,21 @@ filled in for, say, 200 values between :math:`0` and :math:`2\pi`. We demonstrat .. testcode:: parametric - # Somewhere to store each list of results - parametric_measurements = [] + # Generate a memory map for each set of parameters we want to execute with + memory_maps = [{"theta": [theta] for theta in np.linspace(0, 2 * np.pi, 200)} - for theta in np.linspace(0, 2 * np.pi, 200): - # Set the desired parameter value in executable memory - memory_map = {"theta": [theta]} + # Begin batch execution of the program using each set of parameters. + # This returns a list of references to each job, the length and order of which correspond to the memory maps we + # pass in. + handles = qc.execute_with_memory_map_batch(executable, memory_maps) - # Get the results of the run with the value we want to execute with - bitstrings = qc.run(executable, memory_map=memory_map).get_register_map().get("ro") + # Use the handles to gather the results + parametric_measurements = [qc.get_result(handles) for handle in handles] - # Store our results - parametric_measurements.append(bitstrings) +.. note:: -In the example here, if you called ``qc.run(executable)`` and didn't specify ``'theta'``, the program would apply -``RZ(0, qubit)`` for every execution. + :py:meth:`~QAM.run` and :py:meth:`~QAM.execute` both support executing a program a single memory map. We chose + :py:meth:`~QAM.batch_execute_with_memory_map` for this example since we had multiple sets of parameters to run. .. note:: diff --git a/pyquil/api/_qam.py b/pyquil/api/_qam.py index 02ebe2c8a..0986acd59 100644 --- a/pyquil/api/_qam.py +++ b/pyquil/api/_qam.py @@ -15,7 +15,7 @@ ############################################################################## from abc import ABC, abstractmethod from dataclasses import dataclass -from typing import Any, Generic, Mapping, Optional, TypeVar, Sequence, Union, Dict +from typing import Any, Generic, Mapping, Optional, TypeVar, Sequence, Union, Dict, List, Iterable from datetime import timedelta from deprecated import deprecated @@ -144,6 +144,23 @@ def execute( region for the run. """ + @abstractmethod + def execute_with_memory_map_batch( + self, + executable: QuantumExecutable, + memory_maps: Iterable[MemoryMap], + **kwargs: Any, + ) -> List[T]: + """ + Execute a QuantumExecutable with one or more memory_maps, returning a handles to be used to retrieve results. + + How these programs are batched and executed is determined by the executor. See their respective documentation + for details. + + Returns a list of ``QAMExecutionResult``, which can be used to fetch + results in ``QAM#get_result``. + """ + @abstractmethod def get_result(self, execute_response: T) -> QAMExecutionResult: """ diff --git a/pyquil/api/_qpu.py b/pyquil/api/_qpu.py index f5903e5a5..b179fc9ba 100644 --- a/pyquil/api/_qpu.py +++ b/pyquil/api/_qpu.py @@ -16,7 +16,7 @@ from dataclasses import dataclass from collections import defaultdict from datetime import timedelta -from typing import Any, Dict, Optional, Union, List +from typing import Any, Dict, Optional, Union, List, Iterable import numpy as np from numpy.typing import NDArray @@ -185,7 +185,7 @@ def execute( def execute_with_memory_map_batch( self, executable: QuantumExecutable, - memory_maps: List[MemoryMap], + memory_maps: Iterable[MemoryMap], execution_options: Optional[ExecutionOptions] = None, ) -> List[QPUExecuteResponse]: """ diff --git a/pyquil/api/_qvm.py b/pyquil/api/_qvm.py index 2dc3de2e6..02ddcd950 100644 --- a/pyquil/api/_qvm.py +++ b/pyquil/api/_qvm.py @@ -14,7 +14,7 @@ # limitations under the License. ############################################################################## from dataclasses import dataclass -from typing import Any, Optional, Sequence, Tuple, Dict +from typing import Any, Optional, Sequence, Tuple, Dict, List, Iterable import numpy as np @@ -127,6 +127,17 @@ def connect(self) -> None: except ConnectionError: raise QVMNotRunning(f"No QVM server running at {self._client.qvm_url}") from ConnectionError + def execute_with_memory_map_batch( + self, executable: QuantumExecutable, memory_maps: Iterable[MemoryMap], **__: Any + ) -> List[QVMExecuteResponse]: + """ + Executes a single program on the QVM with multiple memory maps. + + This method is a convenience wrapper around QVM#execute and isn't more efficient than making multiple seperate + requests to the QVM. + """ + return [self.execute(executable, memory_map) for memory_map in memory_maps] + def execute( self, executable: QuantumExecutable, diff --git a/pyquil/pyqvm.py b/pyquil/pyqvm.py index 9dfcd1657..dcb970284 100644 --- a/pyquil/pyqvm.py +++ b/pyquil/pyqvm.py @@ -15,7 +15,7 @@ ############################################################################## import logging from abc import ABC, abstractmethod -from typing import Dict, List, Optional, Sequence, Type, Union, Any +from typing import Dict, List, Optional, Sequence, Type, Union, Any, Iterable import numpy as np from numpy.random.mtrand import RandomState @@ -221,6 +221,13 @@ def _extract_defined_gates(self) -> None: raise NotImplementedError("PyQVM does not support DEFGATE ... AS MATRIX | PAULI-SUM.") self.defined_gates[dg.name] = dg.matrix + def execute_with_memory_map_batch( + self, executable: QuantumExecutable, memory_maps: Iterable[MemoryMap], **__: Any + ) -> List["PyQVM"]: + raise NotImplementedError( + "PyQVM does not support batch execution as the state of the instance is reset at the start of each execute." + ) + def execute(self, executable: QuantumExecutable, memory_map: Optional[MemoryMap] = None, **__: Any) -> "PyQVM": """ Execute a program on the PyQVM. Note that the state of the instance is reset on each