diff --git a/pyquil/api/_qam.py b/pyquil/api/_qam.py index 701ba2109..a6dbf0d7f 100644 --- a/pyquil/api/_qam.py +++ b/pyquil/api/_qam.py @@ -21,7 +21,7 @@ from deprecated import deprecated import numpy as np from qcs_sdk import ExecutionData -from qcs_sdk.qpu import RawQPUReadoutData +from qcs_sdk.qpu import RawQPUReadoutData, MemoryValues from qcs_sdk.qvm import RawQVMReadoutData from pyquil.api._abstract_compiler import QuantumExecutable @@ -50,21 +50,6 @@ class QAMExecutionResult: to get at the data in a more convenient format. """ - def get_raw_readout_data(self) -> Union[RawQVMReadoutData, RawQPUReadoutData]: - """ - Get the raw result data. This will be a flattened structure derived - from :class:`qcs_sdk.qvm.QVMResultData` or :class:`qcs_sdk.qpu.QPUResultData` - depending on where the job was run. See their respective documentation - for more information on the data format. - - This property should be used when running programs that use features like - mid-circuit measurement and dynamic control flow on a QPU, since they can - produce irregular result shapes that don't necessarily fit in a - rectangular matrix. If the program was run on a QVM, or doesn't use those - features, consider using the ``register_map`` property instead. - """ - return self.data.result_data.to_raw_readout_data() - def get_register_map(self) -> Dict[str, Optional[np.ndarray]]: """ A mapping of a register name (ie. "ro") to a ``np.ndarray`` containing the values for the @@ -90,6 +75,33 @@ def get_register_map(self) -> Dict[str, Optional[np.ndarray]]: register_map = self.data.result_data.to_register_map() return {key: matrix.to_ndarray() for key, matrix in register_map.items()} + def get_raw_readout_data(self) -> Union[RawQVMReadoutData, RawQPUReadoutData]: + """ + Get the raw result data. This will be a flattened structure derived + from :class:`qcs_sdk.qvm.QVMResultData` or :class:`qcs_sdk.qpu.QPUResultData` + depending on where the job was run. See their respective documentation + for more information on the data format. + + This property should be used when running programs that use features like + mid-circuit measurement and dynamic control flow on a QPU, since they can + produce irregular result shapes that don't necessarily fit in a + rectangular matrix. If the program was run on a QVM, or doesn't use those + features, consider using the ``register_map`` property instead. + """ + return self.data.result_data.to_raw_readout_data() + + def get_memory_values(self) -> Mapping[str, Optional[MemoryValues]]: + """ + Get the final memory values for any memory region that was both read from + and written to during execution. This method will only return the final + value in memory after the job has completed. Because of this, memory + values should not be used to get readout data. Instead, use `get_register_map()` + or `get_raw_readout_data()`. + """ + if self.data.result_data.is_qpu(): + return self.data.result_data.to_qpu().memory_values + return {} + @property @deprecated( version="4.0.0", diff --git a/pyquil/api/_qpu.py b/pyquil/api/_qpu.py index a4815fef5..b8da74dbb 100644 --- a/pyquil/api/_qpu.py +++ b/pyquil/api/_qpu.py @@ -218,7 +218,7 @@ def get_result(self, execute_response: QPUExecuteResponse) -> QAMExecutionResult for mref, readout_name in execute_response._executable.ro_sources.items() if mref.name in execute_response._executable.memory_descriptors } - result_data = QPUResultData(mappings=mappings, readout_values=readout_values) + result_data = QPUResultData(mappings=mappings, readout_values=readout_values, memory_values=results.memory) result_data = ResultData(result_data) duration = None if results.execution_duration_microseconds is not None: diff --git a/test/unit/test_qpu.py b/test/unit/test_qpu.py index 8f9063371..dbca9d00e 100644 --- a/test/unit/test_qpu.py +++ b/test/unit/test_qpu.py @@ -8,6 +8,7 @@ from pyquil.api._abstract_compiler import EncryptedProgram from pyquil.quil import Program from qcs_sdk.qpu.api import Register, ExecutionResult, ExecutionResults +from qcs_sdk.qpu import MemoryValues from rpcq.messages import ParameterSpec from pyquil.quilatom import MemoryReference @@ -70,13 +71,15 @@ def test_qpu_execute( { "q0": ExecutionResult.from_register(Register.from_i32([1, 1, 1, 1])), "q1": ExecutionResult.from_register(Register.from_i32([1, 1, 1, 1])), - } + }, + {"stash": MemoryValues.from_binary([0, 1, 0, 1])}, ) result = qpu.get_result(execute_response) assert np.all(result.get_register_map()["ro"] == np.array([[1, 1], [1, 1], [1, 1], [1, 1]])) assert np.all(result.get_register_map()["ro"] == result.readout_data["ro"]) + assert result.get_memory_values() == {"stash": MemoryValues.from_binary([0, 1, 0, 1])} @patch("pyquil.api._qpu.retrieve_results") @@ -93,7 +96,8 @@ def test_qpu_execute_jagged_results( { "q0": ExecutionResult.from_register(Register.from_i32([1, 1])), "q1": ExecutionResult.from_register(Register.from_i32([1, 1, 1, 1])), - } + }, + {"stash": MemoryValues.from_binary([0, 1, 0, 1])}, ) result = qpu.get_result(execute_response) @@ -105,6 +109,7 @@ def test_qpu_execute_jagged_results( assert raw_readout_data.mappings == {"ro[0]": "q0", "ro[1]": "q1"} assert raw_readout_data.readout_values == {"q0": [1, 1], "q1": [1, 1, 1, 1]} + assert raw_readout_data.memory_values == {"stash": [0, 1, 0, 1]} class TestQPUExecutionOptions: @@ -133,7 +138,8 @@ def test_submit_with_class_options( { "q0": ExecutionResult.from_register(Register.from_i32([1, 1])), "q1": ExecutionResult.from_register(Register.from_i32([1, 1, 1, 1])), - } + }, + {"stash": MemoryValues.from_binary([0, 1, 0, 1])}, ) qpu.get_result(execute_response) @@ -168,7 +174,8 @@ def test_submit_with_options( { "q0": ExecutionResult.from_register(Register.from_i32([1, 1])), "q1": ExecutionResult.from_register(Register.from_i32([1, 1, 1, 1])), - } + }, + {"stash": MemoryValues.from_binary([0, 1, 0, 1])}, ) qpu.get_result(execute_response)