From 1cd524b367aefdbe29e56b82f25179f11c989c3a Mon Sep 17 00:00:00 2001 From: marquessv Date: Fri, 23 Jun 2023 14:33:57 -0700 Subject: [PATCH 1/6] feat!: When running against a QPU, a qcs_sdk.api.ConnectionStrategy can be used to configure how a connection established. --- pyquil/api/_qam.py | 9 ++++----- pyquil/api/_qpu.py | 22 ++++++++++++++++------ pyquil/api/_quantum_computer.py | 8 +++----- 3 files changed, 23 insertions(+), 16 deletions(-) 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..235971b6a 100644 --- a/pyquil/api/_qpu.py +++ b/pyquil/api/_qpu.py @@ -28,7 +28,7 @@ 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, ExecutionResult, ConnectionStrategy from qcs_sdk.qpu.rewrite_arithmetic import build_patch_values @@ -102,6 +102,7 @@ def alloc(spec: ParameterSpec) -> np.ndarray: class QPUExecuteResponse: job_id: str _executable: EncryptedProgram + connection_strategy: Optional[ConnectionStrategy] class QPU(QAM[QPUExecuteResponse]): @@ -113,7 +114,6 @@ def __init__( timeout: float = 10.0, client_configuration: Optional[QCSClient] = None, endpoint_id: Optional[str] = None, - use_gateway: bool = True, ) -> None: """ A connection to the QPU. @@ -136,18 +136,26 @@ def __init__( self._quantum_processor_id = quantum_processor_id self._endpoint_id = endpoint_id - self._use_gateway = use_gateway - @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, + connection_strategy: Optional[ConnectionStrategy] = None, + ) -> 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: + connection_strategy: An optional `ConnectionStrategy` enum that can be used + to configure how the job is submitted and retrieved from the QPU. If unset + the public gateway is used. """ executable = executable.copy() @@ -167,9 +175,10 @@ def execute(self, executable: QuantumExecutable, memory_map: Optional[MemoryMap] quantum_processor_id=self.quantum_processor_id, endpoint_id=self._endpoint_id, client=self._client_configuration, + connection_strategy=connection_strategy, ) - return QPUExecuteResponse(_executable=executable, job_id=job_id) + return QPUExecuteResponse(_executable=executable, job_id=job_id, connection_strategy=connection_strategy) def get_result(self, execute_response: QPUExecuteResponse) -> QAMExecutionResult: """ @@ -180,6 +189,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, + connection_strategy=execute_response.connection_strategy, ) ro_sources = execute_response._executable.ro_sources diff --git a/pyquil/api/_quantum_computer.py b/pyquil/api/_quantum_computer.py index 3b7584ddf..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]: """ @@ -794,7 +792,7 @@ def get_qc( .. _QCS API Docs: https://docs.api.qcs.rigetti.com/#tag/endpoints """ - client_configuration = QCSClient.load() + client_configuration = client_configuration or QCSClient.load() # 1. Parse name, check for redundant options, canonicalize names. prefix, qvm_type, noisy = _parse_name(name, as_qvm, noisy) From 9b13309230cfa739858aac66613597a09eede194 Mon Sep 17 00:00:00 2001 From: marquessv Date: Wed, 5 Jul 2023 10:30:40 -0700 Subject: [PATCH 2/6] switch to ExecutionOptions --- pyquil/api/_qpu.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/pyquil/api/_qpu.py b/pyquil/api/_qpu.py index 235971b6a..e68f140a1 100644 --- a/pyquil/api/_qpu.py +++ b/pyquil/api/_qpu.py @@ -28,7 +28,7 @@ MemoryReference, ) from qcs_sdk import QCSClient -from qcs_sdk.qpu.api import submit, retrieve_results, ExecutionResult, ConnectionStrategy +from qcs_sdk.qpu.api import submit, retrieve_results, ExecutionResult, ExecutionOptions from qcs_sdk.qpu.rewrite_arithmetic import build_patch_values @@ -102,7 +102,7 @@ def alloc(spec: ParameterSpec) -> np.ndarray: class QPUExecuteResponse: job_id: str _executable: EncryptedProgram - connection_strategy: Optional[ConnectionStrategy] + execution_options: Optional[ExecutionOptions] class QPU(QAM[QPUExecuteResponse]): @@ -145,7 +145,7 @@ def execute( self, executable: QuantumExecutable, memory_map: Optional[MemoryMap] = None, - connection_strategy: Optional[ConnectionStrategy] = None, + execution_options: Optional[ExecutionOptions] = None, ) -> QPUExecuteResponse: """ Enqueue a job for execution on the QPU. Returns a ``QPUExecuteResponse``, a @@ -153,9 +153,9 @@ def execute( results. :param: - connection_strategy: An optional `ConnectionStrategy` enum that can be used + execution_options: An optional `ExecutionOptions` enum that can be used to configure how the job is submitted and retrieved from the QPU. If unset - the public gateway is used. + `ExecutionOptions.default()` will be used. """ executable = executable.copy() @@ -175,10 +175,10 @@ def execute( quantum_processor_id=self.quantum_processor_id, endpoint_id=self._endpoint_id, client=self._client_configuration, - connection_strategy=connection_strategy, + execution_options=execution_options, ) - return QPUExecuteResponse(_executable=executable, job_id=job_id, connection_strategy=connection_strategy) + return QPUExecuteResponse(_executable=executable, job_id=job_id, execution_options=execution_options) def get_result(self, execute_response: QPUExecuteResponse) -> QAMExecutionResult: """ @@ -189,7 +189,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, - connection_strategy=execute_response.connection_strategy, + execution_options=execute_response.execution_options, ) ro_sources = execute_response._executable.ro_sources From e96c99400c89074b0b9988705842bd1680ad1c9d Mon Sep 17 00:00:00 2001 From: marquessv Date: Wed, 5 Jul 2023 10:31:12 -0700 Subject: [PATCH 3/6] re-export ExecutionOptions --- pyquil/api/__init__.py | 2 ++ 1 file changed, 2 insertions(+) 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 ( From b004ca0f174ee5029aa5830a0f92dd255828b707 Mon Sep 17 00:00:00 2001 From: marquessv Date: Fri, 7 Jul 2023 10:20:59 -0700 Subject: [PATCH 4/6] update qcs-sdk-python --- poetry.lock | 28 ++++++++++++++-------------- pyproject.toml | 2 +- 2 files changed, 15 insertions(+), 15 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 86817ceab..ad62b910f 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" From a08d5d9acb222a4806a8151ddd7386d3f6373039 Mon Sep 17 00:00:00 2001 From: marquessv Date: Fri, 7 Jul 2023 10:47:43 -0700 Subject: [PATCH 5/6] fix mypy errors --- pyquil/api/_qpu.py | 23 ++++++++++++++++++----- pyquil/api/_qvm.py | 3 ++- pyquil/api/_wavefunction_simulator.py | 4 +--- pyquil/pyqvm.py | 8 ++------ 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/pyquil/api/_qpu.py b/pyquil/api/_qpu.py index e68f140a1..12d75c76c 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, ExecutionOptions +from qcs_sdk.qpu.api import ( + submit, + retrieve_results, + ConnectionStrategy, + ExecutionResult, + ExecutionOptions, + ExecutionOptionsBuilder, +) from qcs_sdk.qpu.rewrite_arithmetic import build_patch_values @@ -114,6 +121,7 @@ def __init__( timeout: float = 10.0, client_configuration: Optional[QCSClient] = None, endpoint_id: Optional[str] = None, + execution_options: Optional[ExecutionOptions] = None, ) -> None: """ A connection to the QPU. @@ -134,7 +142,12 @@ 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 + 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: @@ -146,6 +159,7 @@ def execute( 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 @@ -173,9 +187,8 @@ def execute( 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, + execution_options=execution_options or self.execution_options, ) return QPUExecuteResponse(_executable=executable, job_id=job_id, execution_options=execution_options) 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``. From 37df964e7318a17901b6ed1f2c99b6483b65e3e7 Mon Sep 17 00:00:00 2001 From: Marquess Valdez Date: Fri, 7 Jul 2023 12:48:28 -0700 Subject: [PATCH 6/6] clarify docstring Co-authored-by: Kalan <22137047+kalzoo@users.noreply.github.com> --- pyquil/api/_qpu.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyquil/api/_qpu.py b/pyquil/api/_qpu.py index 12d75c76c..1c6bc44b1 100644 --- a/pyquil/api/_qpu.py +++ b/pyquil/api/_qpu.py @@ -168,8 +168,8 @@ def execute( :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 - `ExecutionOptions.default()` will 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()