Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
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
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ Changelog

### Announcements

### Improvements and Changes

- `QAMExecutionResult` now includes `execution_duration_microseconds`, providing the amount of time
a job held exclusive hardware access. (@randall-fulton, #1436)

### Bugfixes

[v3.1.0](https://github.com/rigetti/pyquil/releases/tag/v3.1.0)
------------------------------------------------------------------------------------

### Announcements

- `setup.py` has been removed and will no longer be generated as part of the automated release process.

### Improvements and Changes
Expand Down
41 changes: 38 additions & 3 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ python = "^3.7"
numpy = "^1.20"
scipy = "^1.6.1"
lark = "^0.11.1"
rpcq = "^3.6.0"
rpcq = "^3.10.0"
networkx = "^2.5"
importlib-metadata = { version = "^3.7.3", python = "<3.8" }
qcs-api-client = ">=0.8.1,<0.21.0"
Expand Down
3 changes: 3 additions & 0 deletions pyquil/api/_qam.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ class QAMExecutionResult:
readout_data: Mapping[str, Optional[np.ndarray]] = field(default_factory=dict)
"""Readout data returned from the QAM, keyed on the name of the readout register or post-processing node."""

execution_duration_microseconds: Optional[int] = field(default=None)
"""Duration job held exclusive hardware access. Defaults to ``None`` when information is not available."""


class QAM(ABC, Generic[T]):
"""
Expand Down
26 changes: 11 additions & 15 deletions pyquil/api/_qpu.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,31 +177,27 @@ def get_result(self, execute_response: QPUExecuteResponse) -> QAMExecutionResult
"""
Retrieve results from execution on the QPU.
"""
raw_results = self._get_buffers(execute_response.job_id)
request = GetBuffersRequest(job_id=execute_response.job_id, wait=True)
results = self._qpu_client.get_execution_results(request)

ro_sources = execute_response._executable.ro_sources
decoded_buffers = {k: decode_buffer(v) for k, v in results.buffers.items()}

result_memory = {}
if raw_results is not None:
if decoded_buffers is not None:
extracted = _extract_memory_regions(
execute_response._executable.memory_descriptors, ro_sources, raw_results
execute_response._executable.memory_descriptors, ro_sources, decoded_buffers
)
for name, array in extracted.items():
result_memory[name] = array
elif not ro_sources:
result_memory["ro"] = np.zeros((0, 0), dtype=np.int64)

return QAMExecutionResult(executable=execute_response._executable, readout_data=result_memory)

def _get_buffers(self, job_id: str) -> Dict[str, np.ndarray]:
"""
Return the decoded result buffers for particular job_id.

:param job_id: Unique identifier for the job in question
:return: Decoded buffers or throw an error
"""
request = GetBuffersRequest(job_id=job_id, wait=True)
buffers = self._qpu_client.get_buffers(request).buffers
return {k: decode_buffer(v) for k, v in buffers.items()}
return QAMExecutionResult(
executable=execute_response._executable,
readout_data=result_memory,
execution_duration_microseconds=results.execution_duration_microseconds,
)

@classmethod
def _build_patch_values(cls, program: EncryptedProgram) -> Dict[str, List[Union[int, float]]]:
Expand Down
25 changes: 25 additions & 0 deletions pyquil/api/_qpu_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
from dataclasses import dataclass
from datetime import datetime
from typing import Dict, Optional, cast, Tuple, Union, List, Any
from attr import field

import rpcq
from dateutil.parser import parse as parsedate
Expand Down Expand Up @@ -93,6 +94,9 @@ class GetBuffersResponse:
buffers: Dict[str, BufferResponse]
"""Job buffers, by buffer name."""

execution_duration_microseconds: Optional[int] = field(default=None)
"Duration job held exclusive hardware access."


class QPUClient:
"""
Expand Down Expand Up @@ -156,6 +160,27 @@ def get_buffers(self, request: GetBuffersRequest) -> GetBuffersResponse:
}
)

def get_execution_results(self, request: GetBuffersRequest) -> GetBuffersResponse:
"""
Get job buffers and execution metadata.
"""
result = self._rpcq_request(
"get_execution_results",
job_id=request.job_id,
wait=request.wait,
)
return GetBuffersResponse(
buffers={
name: BufferResponse(
shape=cast(Tuple[int, int], tuple(val["shape"])),
dtype=val["dtype"],
data=val["data"],
)
for name, val in result.buffers.items()
},
execution_duration_microseconds=result.execution_duration_microseconds,
)

@retry(exceptions=TimeoutError, tries=2) # type: ignore
def _rpcq_request(self, method_name: str, *args: Any, **kwargs: Any) -> Any:
engagement = self._engagement_manager.get_engagement(
Expand Down
Loading