From fc402d17f7c04fe5ec5633c57fa8166572eb6017 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 7 Jul 2023 12:55:31 -0700 Subject: [PATCH] feat!: Use `ExecutionOptions` parameter to configure how jobs are submitted and retrieved from a QPU. This replaces the `use_gateway` flag on `QCSClient.load()` has been removed. (#1598) --- poetry.lock | 28 +++++++++--------- pyproject.toml | 2 +- pyquil/api/__init__.py | 2 ++ pyquil/api/_qam.py | 9 +++--- pyquil/api/_qpu.py | 41 +++++++++++++++++++++------ pyquil/api/_quantum_computer.py | 6 ++-- pyquil/api/_qvm.py | 3 +- pyquil/api/_wavefunction_simulator.py | 4 +-- pyquil/pyqvm.py | 8 ++---- 9 files changed, 60 insertions(+), 43 deletions(-) diff --git a/poetry.lock b/poetry.lock index 6cee5e794..7fb50bccd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -2012,24 +2012,24 @@ cffi = {version = "*", markers = "implementation_name == \"pypy\""} [[package]] name = "qcs-sdk-python" -version = "0.9.1" +version = "0.10.0" description = "Python interface for the QCS Rust SDK" category = "main" optional = false python-versions = "*" files = [ - {file = "qcs_sdk_python-0.9.1-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:8360b382cfc8882975eb8e3cc3a6e0685898bb9193d8387aa54ba050521dfc41"}, - {file = "qcs_sdk_python-0.9.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d96dcdca3019f02612d8b1dd81f4cd8611ef891806286e6b2a7940de96718c50"}, - {file = "qcs_sdk_python-0.9.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ddef79ee3999b1b5d76f8f96b0c23c14ab1e1ab3b2eb3b5b96e24b6afb01261"}, - {file = "qcs_sdk_python-0.9.1-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:85bd7d7733f6bb0842dccd9d3fe628069760500ac398e2109fe80cfca1a24ca7"}, - {file = "qcs_sdk_python-0.9.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c2d98d1621b11e86c00774ec9cf5fd99c34b13286498ff57c1b5a472d5f4d9c"}, - {file = "qcs_sdk_python-0.9.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a87317d65d14e9e7029692fb7c3ec129b2ff12195d2d5df63cb5771eeeb26011"}, - {file = "qcs_sdk_python-0.9.1-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:ab8ba469ecf61b3ad22a437d94fa938a53dcafdf97d6daf1d1126be859c18fbe"}, - {file = "qcs_sdk_python-0.9.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9afbb9cbc2321e14a23bf75a0d897c91b9e387a5c2905677bbe939a8773edd71"}, - {file = "qcs_sdk_python-0.9.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63732afb1007ce25e940190e94af063cd7382ae845d903fc3e235453bf40e820"}, - {file = "qcs_sdk_python-0.9.1-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:a34585ae0b2f2e00ecaae3501f7b80ee870ac90c74830741289cfb4e36ea4656"}, - {file = "qcs_sdk_python-0.9.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:544fb455e721775f0f191e38bf616b8eafb5bbd96ec2650b9e11d185f1d6b194"}, - {file = "qcs_sdk_python-0.9.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:75f0d7699de17489226cc5aca6126b0906c0e966d2f38a261d7be3ea0742b0bd"}, + {file = "qcs_sdk_python-0.10.0-cp310-cp310-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:fbbe007b85758241552d171b6abda5849cab9c04dd2fd6025926bd8bae6e2405"}, + {file = "qcs_sdk_python-0.10.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a23ec2be1ea44a5d48b0cd777744aa7aa63620915528b6c3efadcda2cec3910a"}, + {file = "qcs_sdk_python-0.10.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6153449a68ab7b72e0de620581c127b5ad43cb873e0cd7cefa825873f165fbba"}, + {file = "qcs_sdk_python-0.10.0-cp311-cp311-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:8c60499157b0f54ea587633815b81515afd7026550e20ec4232e2bb19e44d23a"}, + {file = "qcs_sdk_python-0.10.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9213179a5173c2069fbbe7e63c5ce660e4f006fba01531767c38153311ffbbfc"}, + {file = "qcs_sdk_python-0.10.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e64427ad54d4a6d49f30c5d43a4c99f3a47573a667df0b8c147c64a45f162448"}, + {file = "qcs_sdk_python-0.10.0-cp38-cp38-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:0c5bdf19d0082b28984593c56e0fdd67d698430804cc931378c81b142663ff23"}, + {file = "qcs_sdk_python-0.10.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:76eaec611b7f7cdb25e9e88e7115ba55359853eb879166b957768d0ea04c78cf"}, + {file = "qcs_sdk_python-0.10.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35adf044edda13b3afc56a93e75a941e7ca5c518eab1845dcbba4c009701d35e"}, + {file = "qcs_sdk_python-0.10.0-cp39-cp39-macosx_10_9_x86_64.macosx_11_0_arm64.macosx_10_9_universal2.whl", hash = "sha256:2a0f6ab21758dfbf7cdd276f4a8bd2fef6eaed299dbb1d940d7a8eda6097b448"}, + {file = "qcs_sdk_python-0.10.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7526791e0c188c67c85d15423a2e58fd3bd04269d22d5c8b014f89dc7fed68de"}, + {file = "qcs_sdk_python-0.10.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21243e24b10429b4d5a719627e3001a1ba41e6a58d7301262293f29c1bbc1bca"}, ] [[package]] @@ -2737,4 +2737,4 @@ latex = ["ipython"] [metadata] lock-version = "2.0" python-versions = "^3.8,<4.0" -content-hash = "bae1a3bd69a2c4b48775ffb81af8fc9fe56b714c30243decd8cff15e78aeb4ad" +content-hash = "a2372deb1254a6dbca694ec8e08d350e41b81b8d56417a02eb2c41a1e4e757b2" diff --git a/pyproject.toml b/pyproject.toml index bf08dea0f..740a87998 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -27,7 +27,7 @@ rpcq = "^3.10.0" pydantic = "^1.10.7" networkx = ">=2.5" importlib-metadata = { version = ">=3.7.3,<5", python = "<3.8" } -qcs-sdk-python = "0.9.1" +qcs-sdk-python = "0.10.0" quil = "0.1.1" tenacity = "^8.2.2" types-python-dateutil = "^2.8.19" diff --git a/pyquil/api/__init__.py b/pyquil/api/__init__.py index 588071a0b..e25406e9f 100644 --- a/pyquil/api/__init__.py +++ b/pyquil/api/__init__.py @@ -21,6 +21,7 @@ "AbstractCompiler", "BenchmarkConnection", "EncryptedProgram", + "ExecutionOptions", "get_qc", "list_quantum_computers", "local_forest_runtime", @@ -40,6 +41,7 @@ ] from qcs_sdk import QCSClient +from qcs_sdk.qpu.api import ExecutionOptions from pyquil.api._benchmark import BenchmarkConnection from pyquil.api._compiler import ( diff --git a/pyquil/api/_qam.py b/pyquil/api/_qam.py index 19eb556a5..49c5b3dea 100644 --- a/pyquil/api/_qam.py +++ b/pyquil/api/_qam.py @@ -15,7 +15,7 @@ ############################################################################## from abc import ABC, abstractmethod from dataclasses import dataclass, field -from typing import Generic, Mapping, Optional, TypeVar, Sequence, Union +from typing import Any, Generic, Mapping, Optional, TypeVar, Sequence, Union import numpy as np from pyquil.api._abstract_compiler import QuantumExecutable @@ -55,6 +55,7 @@ def execute( self, executable: QuantumExecutable, memory_map: Optional[MemoryMap] = None, + **kwargs: Any, ) -> T: """ Run an executable on a QAM, returning a handle to be used to retrieve @@ -74,11 +75,9 @@ def get_result(self, execute_response: T) -> QAMExecutionResult: """ def run( - self, - executable: QuantumExecutable, - memory_map: Optional[MemoryMap] = None, + self, executable: QuantumExecutable, memory_map: Optional[MemoryMap] = None, **kwargs: Any ) -> QAMExecutionResult: """ Run an executable to completion on the QAM. """ - return self.get_result(self.execute(executable, memory_map)) + return self.get_result(self.execute(executable, memory_map, **kwargs)) diff --git a/pyquil/api/_qpu.py b/pyquil/api/_qpu.py index 3b011f294..1c6bc44b1 100644 --- a/pyquil/api/_qpu.py +++ b/pyquil/api/_qpu.py @@ -15,7 +15,7 @@ ############################################################################## from dataclasses import dataclass from collections import defaultdict -from typing import Dict, Optional, Union +from typing import Any, Dict, Optional, Union import numpy as np from numpy.typing import NDArray @@ -28,7 +28,14 @@ MemoryReference, ) from qcs_sdk import QCSClient -from qcs_sdk.qpu.api import submit, retrieve_results, ExecutionResult +from qcs_sdk.qpu.api import ( + submit, + retrieve_results, + ConnectionStrategy, + ExecutionResult, + ExecutionOptions, + ExecutionOptionsBuilder, +) from qcs_sdk.qpu.rewrite_arithmetic import build_patch_values @@ -102,6 +109,7 @@ def alloc(spec: ParameterSpec) -> np.ndarray: class QPUExecuteResponse: job_id: str _executable: EncryptedProgram + execution_options: Optional[ExecutionOptions] class QPU(QAM[QPUExecuteResponse]): @@ -113,7 +121,7 @@ def __init__( timeout: float = 10.0, client_configuration: Optional[QCSClient] = None, endpoint_id: Optional[str] = None, - use_gateway: bool = True, + execution_options: Optional[ExecutionOptions] = None, ) -> None: """ A connection to the QPU. @@ -134,20 +142,34 @@ def __init__( self._last_results: Dict[str, np.ndarray] = {} self._memory_results: Dict[str, Optional[np.ndarray]] = defaultdict(lambda: None) self._quantum_processor_id = quantum_processor_id - self._endpoint_id = endpoint_id - - self._use_gateway = use_gateway + if execution_options is None: + execution_options_builder = ExecutionOptionsBuilder.default() + if endpoint_id is not None: + execution_options_builder.connection_strategy(ConnectionStrategy.endpoint_id(endpoint_id)) + execution_options = execution_options_builder.build() + self.execution_options = execution_options @property def quantum_processor_id(self) -> str: """ID of quantum processor targeted.""" return self._quantum_processor_id - def execute(self, executable: QuantumExecutable, memory_map: Optional[MemoryMap] = None) -> QPUExecuteResponse: + def execute( + self, + executable: QuantumExecutable, + memory_map: Optional[MemoryMap] = None, + execution_options: Optional[ExecutionOptions] = None, + **__: Any, + ) -> QPUExecuteResponse: """ Enqueue a job for execution on the QPU. Returns a ``QPUExecuteResponse``, a job descriptor which should be passed directly to ``QPU.get_result`` to retrieve results. + + :param: + execution_options: An optional `ExecutionOptions` enum that can be used + to configure how the job is submitted and retrieved from the QPU. If unset, + an appropriate default will be used. """ executable = executable.copy() @@ -165,11 +187,11 @@ def execute(self, executable: QuantumExecutable, memory_map: Optional[MemoryMap] program=executable.program, patch_values=patch_values, quantum_processor_id=self.quantum_processor_id, - endpoint_id=self._endpoint_id, client=self._client_configuration, + execution_options=execution_options or self.execution_options, ) - return QPUExecuteResponse(_executable=executable, job_id=job_id) + return QPUExecuteResponse(_executable=executable, job_id=job_id, execution_options=execution_options) def get_result(self, execute_response: QPUExecuteResponse) -> QAMExecutionResult: """ @@ -180,6 +202,7 @@ def get_result(self, execute_response: QPUExecuteResponse) -> QAMExecutionResult job_id=execute_response.job_id, quantum_processor_id=self.quantum_processor_id, client=self._client_configuration, + execution_options=execute_response.execution_options, ) ro_sources = execute_response._executable.ro_sources diff --git a/pyquil/api/_quantum_computer.py b/pyquil/api/_quantum_computer.py index 821849243..5e157ccf1 100644 --- a/pyquil/api/_quantum_computer.py +++ b/pyquil/api/_quantum_computer.py @@ -126,9 +126,7 @@ def to_compiler_isa(self) -> CompilerISA: return self.compiler.quantum_processor.to_compiler_isa() def run( - self, - executable: QuantumExecutable, - memory_map: Optional[MemoryMap] = None, + self, executable: QuantumExecutable, memory_map: Optional[MemoryMap] = None, **kwargs: Any ) -> QAMExecutionResult: """ Run a quil executable. All parameters in the executable must have values applied using @@ -139,7 +137,7 @@ def run( region for the run. :return: execution result including readout data. """ - return self.qam.run(executable, memory_map) + return self.qam.run(executable, memory_map, **kwargs) def calibrate(self, experiment: Experiment) -> List[ExperimentResult]: """ diff --git a/pyquil/api/_qvm.py b/pyquil/api/_qvm.py index 8c5706158..534bf3a0c 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 Mapping, Optional, Sequence, Tuple +from typing import Any, Mapping, Optional, Sequence, Tuple import numpy as np @@ -123,6 +123,7 @@ def execute( self, executable: QuantumExecutable, memory_map: Optional[MemoryMap] = None, + **__: Any, ) -> QVMExecuteResponse: """ Synchronously execute the input program to completion. diff --git a/pyquil/api/_wavefunction_simulator.py b/pyquil/api/_wavefunction_simulator.py index d11578c4f..324566f4e 100644 --- a/pyquil/api/_wavefunction_simulator.py +++ b/pyquil/api/_wavefunction_simulator.py @@ -202,9 +202,7 @@ def run_and_measure( trials, qubits, ) - measured_qubits = qvm.api.run_and_measure( - request, options=QVMOptions(timeout_seconds=self.timeout) # type: ignore[call-arg] - ) + measured_qubits = qvm.api.run_and_measure(request, options=QVMOptions(timeout_seconds=self.timeout)) return np.asarray(measured_qubits) @staticmethod diff --git a/pyquil/pyqvm.py b/pyquil/pyqvm.py index f5a831d2c..8efc6851f 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 +from typing import Dict, List, Optional, Sequence, Type, Union, Any import numpy as np from numpy.random.mtrand import RandomState @@ -221,11 +221,7 @@ 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( - self, - executable: QuantumExecutable, - memory_map: Optional[MemoryMap] = None, - ) -> "PyQVM": + 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 call to ``execute``.