Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Final memory values are now available on QAMExecutionResults #1703

Merged
merged 4 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 28 additions & 16 deletions pyquil/api/_qam.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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]]:
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
"""
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()`.
"""
kalzoo marked this conversation as resolved.
Show resolved Hide resolved
if self.data.result_data.is_qpu():
return self.data.result_data.to_qpu().memory_values
return {}

@property
@deprecated(
version="4.0.0",
Expand Down
2 changes: 1 addition & 1 deletion pyquil/api/_qpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
15 changes: 11 additions & 4 deletions test/unit/test_qpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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")
Expand All @@ -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)
Expand All @@ -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:
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Loading