Skip to content

Commit

Permalink
Add include_foo arguments to qiskit result conversions
Browse files Browse the repository at this point in the history
And when we call those conversion methods from _AerBaseBackend.get_results(),
pass what we know about what the backend is expected to support.

This should mean that if we run anything on AerStateBackend or AerUnitaryBackend,
we don't get counts passed back even if the Qiskit result included them.
  • Loading branch information
isobelhooper committed Oct 24, 2024
1 parent df4940c commit c11e63c
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 10 deletions.
9 changes: 8 additions & 1 deletion pytket/extensions/qiskit/backends/aer.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,14 @@ def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResul
raise CircuitNotRunError(handle)

res = job.result()
backresults = qiskit_result_to_backendresult(res)
backresults = qiskit_result_to_backendresult(
res,
include_shots=self._supports_shots,
include_counts=self._supports_counts,
include_state=self._supports_state,
include_unitary=self._supports_unitary,
include_density_matrix=self._supports_density_matrix,
)
for circ_index, backres in enumerate(backresults):
self._cache[ResultHandle(jobid, circ_index, qubit_n, ppc)][
"result"
Expand Down
39 changes: 31 additions & 8 deletions pytket/extensions/qiskit/result_convert.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,17 @@ def _result_is_empty_shots(result: ExperimentResult) -> bool:
return False


# In some cases, Qiskit returns a result with fields we don't expect -
# for example, a circuit with classical bits run on AerStateBackend will
# return counts (whether or not there were measurements). The include_foo
# arguments should be set based on what the backend supports.
def qiskit_experimentresult_to_backendresult(
result: ExperimentResult,
include_counts: bool = True,
include_shots: bool = True,
include_state: bool = True,
include_unitary: bool = True,
include_density_matrix: bool = True,
) -> BackendResult:
if not result.success:
raise RuntimeError(result.status)
Expand All @@ -104,16 +113,16 @@ def qiskit_experimentresult_to_backendresult(

shots, counts, state, unitary, density_matrix = (None,) * 5
datadict = result.data.to_dict()
if _result_is_empty_shots(result):
if _result_is_empty_shots(result) and include_shots:
n_bits = len(c_bits) if c_bits else 0
shots = OutcomeArray.from_readouts(
np.zeros((result.shots, n_bits), dtype=np.uint8)
)
else:
if "memory" in datadict:
if "memory" in datadict and include_shots:
memory = datadict["memory"]
shots = _hex_to_outar(memory, width)
elif "counts" in datadict:
elif "counts" in datadict and include_counts:
qis_counts = datadict["counts"]
counts = Counter(
dict(
Expand All @@ -122,13 +131,13 @@ def qiskit_experimentresult_to_backendresult(
)
)

if "statevector" in datadict:
if "statevector" in datadict and include_state:
state = datadict["statevector"].reverse_qargs().data

if "unitary" in datadict:
if "unitary" in datadict and include_unitary:
unitary = datadict["unitary"].reverse_qargs().data

if "density_matrix" in datadict:
if "density_matrix" in datadict and include_density_matrix:
density_matrix = datadict["density_matrix"].reverse_qargs().data

return BackendResult(
Expand All @@ -143,9 +152,23 @@ def qiskit_experimentresult_to_backendresult(
)


def qiskit_result_to_backendresult(res: Result) -> Iterator[BackendResult]:
def qiskit_result_to_backendresult(
res: Result,
include_counts: bool = True,
include_shots: bool = True,
include_state: bool = True,
include_unitary: bool = True,
include_density_matrix: bool = True,
) -> Iterator[BackendResult]:
for result in res.results:
yield qiskit_experimentresult_to_backendresult(result)
yield qiskit_experimentresult_to_backendresult(
result,
include_counts,
include_shots,
include_state,
include_unitary,
include_density_matrix,
)


def backendresult_to_qiskit_resultdata(
Expand Down
7 changes: 6 additions & 1 deletion tests/qiskit_convert_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,12 +549,17 @@ def test_convert_result() -> None:
qc1.save_state()
qisk_result = simulator.run(qc1, shots=10).result()

tk_res = next(qiskit_result_to_backendresult(qisk_result))
# exclude counts from result (we don't expect them
# for the statevector sim after all)
tk_res = next(qiskit_result_to_backendresult(qisk_result, include_counts=False))

state = tk_res.get_state([Qubit("q2", 1), Qubit("q1", 0), Qubit("q2", 0)])
correct_state = np.zeros(1 << 3, dtype=complex)
correct_state[6] = 1 + 0j
assert compare_statevectors(state, correct_state)
# also check that we don't return counts in tket result
# even if the qiskit result includes them
assert tk_res._counts is None

# check measured
qc.measure(qr1[0], cr[0])
Expand Down

0 comments on commit c11e63c

Please sign in to comment.