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

Handle PrimitiveResult as result type #340

Merged
merged 2 commits into from
May 29, 2024
Merged
Changes from all 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
37 changes: 28 additions & 9 deletions pytket/extensions/qiskit/backends/ibm.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@
)
from warnings import warn

from qiskit.primitives import SamplerResult # type: ignore
import numpy as np

from qiskit.primitives import PrimitiveResult, SamplerResult # type: ignore


# RuntimeJob has no queue_position attribute, which is referenced
Expand All @@ -48,7 +50,7 @@
RuntimeJob,
)

from pytket.circuit import Circuit, OpType
from pytket.circuit import Bit, Circuit, OpType
from pytket.backends import Backend, CircuitNotRunError, CircuitStatus, ResultHandle
from pytket.backends.backendinfo import BackendInfo
from pytket.backends.backendresult import BackendResult
Expand Down Expand Up @@ -138,6 +140,12 @@ def _get_primitive_gates(gateset: Set[OpType]) -> Set[OpType]:
return gateset


def _int_from_readout(readout: np.ndarray) -> int:
# Weird mixture of big- and little-endian here.
n_bytes = len(readout)
return sum(int(x) << (8 * (n_bytes - 1 - i)) for i, x in enumerate(readout))


class IBMQBackend(Backend):
"""A backend for running circuits on remote IBMQ devices.

Expand Down Expand Up @@ -525,11 +533,14 @@ def process_circuits(

qcs, ppcirc_strs = [], []
for tkc in batch_chunk:
tkc1 = tkc.copy()
# Flatten bits to default register in lexicographic order:
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This solves the issue raised in this comment. There is still an issue with circuits having non-default classical register names, but this is an independent pre-existing issue and is not addressed in this PR.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just to make sure I understand: this does actually all work if pytket-qiskit is used to submit and retrieve the circuit no matter what the classical registers are named. Of course if one separately submits a circuit with non-default classical register names and tries to retrieve it with pytket-qiskit then there is an error... but that is indeed a different issue!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it works unless you then want to call get_counts() on the result with specific bits, using the cbits parameter to get_counts. In that case it will throw an error -- but that was already the case (separate issue).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks that makes sense!

tkc1.rename_units({bit: Bit(i) for i, bit in enumerate(tkc1.bits)})
if postprocess:
c0, ppcirc = prepare_circuit(tkc, allow_classical=False)
c0, ppcirc = prepare_circuit(tkc1, allow_classical=False)
ppcirc_rep = ppcirc.to_dict()
else:
c0, ppcirc_rep = tkc, None
c0, ppcirc_rep = tkc1, None
if simplify_initial:
SimplifyInitial(
allow_classical=False, create_all_qubits=True
Expand Down Expand Up @@ -614,11 +625,19 @@ def get_result(self, handle: ResultHandle, **kwargs: KwargTypes) -> BackendResul
sleep(10)

res = job.result(timeout=kwargs.get("timeout", None))
assert isinstance(res, SamplerResult)
for circ_index, (r, d) in enumerate(zip(res.quasi_dists, res.metadata)):
self._ibm_res_cache[(jobid, circ_index)] = Counter(
{n: int(0.5 + d["shots"] * p) for n, p in r.items()}
)
if isinstance(res, SamplerResult):
# TODO Is this code still reachable?
for circ_index, (r, d) in enumerate(zip(res.quasi_dists, res.metadata)):
self._ibm_res_cache[(jobid, circ_index)] = Counter(
{n: int(0.5 + d["shots"] * p) for n, p in r.items()}
)
else:
assert isinstance(res, PrimitiveResult)
for circ_index, pub_result in enumerate(res._pub_results):
readouts = pub_result.data.c.array
self._ibm_res_cache[(jobid, circ_index)] = Counter(
_int_from_readout(readout) for readout in readouts
)

counts = self._ibm_res_cache[cache_key] # Counter[int]
# Convert to `OutcomeArray`:
Expand Down