Skip to content

Commit 4ad08b5

Browse files
committed
feat(qio): update to qio
1 parent 94e2af2 commit 4ad08b5

File tree

1 file changed

+51
-238
lines changed
  • qiskit_scaleway/backends/qsim

1 file changed

+51
-238
lines changed

qiskit_scaleway/backends/qsim/job.py

Lines changed: 51 additions & 238 deletions
Original file line numberDiff line numberDiff line change
@@ -11,85 +11,31 @@
1111
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
14-
import json
15-
import io
16-
import collections
17-
18-
import numpy as np
19-
2014
from typing import (
21-
Any,
2215
List,
23-
Callable,
24-
Iterable,
25-
Mapping,
26-
Optional,
27-
Sequence,
28-
Tuple,
2916
Union,
30-
cast,
3117
Dict,
3218
)
3319

34-
from qiskit import qasm2, QuantumCircuit
20+
from qiskit import QuantumCircuit
3521
from qiskit.providers import JobError, JobStatus
3622
from qiskit.result import Result
3723
from qiskit.transpiler.passes import RemoveBarriers
38-
from qiskit.result.models import ExperimentResult, ExperimentResultData
3924

4025
from qiskit_scaleway.versions import USER_AGENT
4126
from qiskit_scaleway.backends import BaseJob
4227

43-
from scaleway_qaas_client.v1alpha1 import (
44-
QaaSClient,
45-
QaaSJobData,
46-
QaaSJobClientData,
47-
QaaSCircuitData,
48-
QaaSJobRunData,
49-
QaaSJobBackendData,
50-
QaaSCircuitSerializationFormat,
28+
from qio.core import (
29+
QuantumProgram,
30+
QuantumProgramSerializationFormat,
31+
QuantumProgramResult,
32+
QuantumComputationModel,
33+
QuantumComputationParameters,
34+
BackendData,
35+
ClientData,
5136
)
5237

53-
54-
def _tuple_of_big_endian_int(bit_groups: Iterable[Any]) -> Tuple[int, ...]:
55-
return tuple(_big_endian_bits_to_int(bits) for bits in bit_groups)
56-
57-
58-
def _big_endian_bits_to_int(bits: Iterable[Any]) -> int:
59-
result = 0
60-
61-
for e in bits:
62-
result <<= 1
63-
if e:
64-
result |= 1
65-
66-
return result
67-
68-
69-
def _unpack_digits(
70-
packed_digits: str,
71-
binary: bool,
72-
dtype: Optional[str],
73-
shape: Optional[Sequence[int]],
74-
) -> np.ndarray:
75-
if binary:
76-
dtype = cast(str, dtype)
77-
shape = cast(Sequence[int], shape)
78-
return _unpack_bits(packed_digits, dtype, shape)
79-
80-
buffer = io.BytesIO()
81-
buffer.write(bytes.fromhex(packed_digits))
82-
buffer.seek(0)
83-
digits = np.load(buffer, allow_pickle=False)
84-
buffer.close()
85-
86-
return digits
87-
88-
89-
def _unpack_bits(packed_bits: str, dtype: str, shape: Sequence[int]) -> np.ndarray:
90-
bits_bytes = bytes.fromhex(packed_bits)
91-
bits = np.unpackbits(np.frombuffer(bits_bytes, dtype=np.uint8))
92-
return bits[: np.prod(shape).item()].reshape(shape).astype(dtype)
38+
from scaleway_qaas_client.v1alpha1 import QaaSClient
9339

9440

9541
class QsimJob(BaseJob):
@@ -116,118 +62,69 @@ def submit(self, session_id: str) -> None:
11662
# Note 2: Qsim can only handle one circuit at a time
11763
circuit = RemoveBarriers()(self._circuits[0])
11864

119-
run_data = QaaSJobRunData(
120-
options={"shots": options.pop("shots")},
121-
circuits=[
122-
QaaSCircuitData(
123-
serialization_format=QaaSCircuitSerializationFormat.QASM_V2,
124-
circuit_serialization=qasm2.dumps(circuit),
125-
)
126-
],
127-
)
65+
programs = [
66+
QuantumProgram.from_qiskit_circuit(
67+
circuit, QuantumProgramSerializationFormat.QASM_V2
68+
)
69+
]
12870

12971
options.pop("circuit_memoization_size")
13072

131-
backend_data = QaaSJobBackendData(
73+
backend_data = BackendData(
13274
name=self.backend().name,
13375
version=self.backend().version,
13476
options=options,
77+
frozenset=True,
13578
)
13679

137-
client_data = QaaSJobClientData(
80+
client_data = ClientData(
13881
user_agent=USER_AGENT,
82+
frozenset=True,
13983
)
14084

141-
data = QaaSJobData.schema().dumps(
142-
QaaSJobData(
143-
backend=backend_data,
144-
run=run_data,
145-
client=client_data,
146-
)
147-
)
85+
computation_model_dict = QuantumComputationModel(
86+
programs=programs,
87+
backend=backend_data,
88+
client=client_data,
89+
frozenset=True,
90+
).to_dict()
91+
92+
computation_parameters_dict = QuantumComputationParameters(
93+
shots=options.pop("shots"),
94+
frozenset=True,
95+
).to_dict()
14896

14997
model = self._client.create_model(
150-
payload=data,
98+
payload=computation_model_dict,
15199
)
152100

153101
if not model:
154102
raise RuntimeError("Failed to push circuit data")
155103

156104
self._job_id = self._client.create_job(
157-
name=self._name, session_id=session_id, model_id=model.id
105+
name=self._name,
106+
session_id=session_id,
107+
model_id=model.id,
108+
parameters=computation_parameters_dict,
158109
).id
159110

160-
def __to_cirq_result(self, job_results) -> "cirq.Result":
111+
def __to_cirq_result(self, program_result: QuantumProgramResult) -> "cirq.Result":
161112
try:
162113
import cirq
163114
except:
164115
raise Exception("Cannot get Cirq result: Cirq not installed")
165116

166-
from cirq.study import ResultDict
167-
168-
if len(job_results) == 0:
169-
raise Exception("Empty result list")
170-
171-
payload = self._extract_payload_from_response(job_results[0])
172-
payload_dict = json.loads(payload)
173-
cirq_result = ResultDict._from_json_dict_(**payload_dict)
174-
175-
return cirq_result
176-
177-
def __to_qiskit_result(self, job_results):
178-
def __make_hex_from_result_array(result: Tuple):
179-
str_value = "".join(map(str, result))
180-
integer_value = int(str_value, 2)
181-
return hex(integer_value)
182-
183-
def __make_expresult_from_cirq_result(
184-
cirq_result: CirqResult,
185-
) -> ExperimentResult:
186-
hist = dict(
187-
cirq_result.multi_measurement_histogram(
188-
keys=cirq_result.measurements.keys()
189-
)
190-
)
191-
192-
return ExperimentResult(
193-
shots=cirq_result.repetitions,
194-
success=True,
195-
data=ExperimentResultData(
196-
counts={
197-
__make_hex_from_result_array(key): value
198-
for key, value in hist.items()
199-
},
200-
),
201-
)
202-
203-
def __make_result_from_payload(payload: str) -> Result:
204-
payload_dict = json.loads(payload)
205-
cirq_result = CirqResult._from_json_dict_(**payload_dict)
117+
return program_result.to_cirq_result()
206118

207-
return Result(
208-
backend_name=self.backend().name,
209-
backend_version=self.backend().version,
210-
job_id=self._job_id,
211-
qobj_id=", ".join(x.name for x in self._circuits),
212-
success=self.status() == JobStatus.DONE,
213-
results=[__make_expresult_from_cirq_result(cirq_result)],
214-
cirq_result=payload,
215-
)
216-
217-
qiskit_results = list(
218-
map(
219-
lambda r: __make_result_from_payload(
220-
self._extract_payload_from_response(r)
221-
),
222-
job_results,
223-
)
119+
def __to_qiskit_result(self, program_result: QuantumProgramResult) -> Result:
120+
return program_result.to_qiskit_result(
121+
backend_name=self.backend().name,
122+
backend_version=self.backend().version,
123+
job_id=self._job_id,
124+
qobj_id=", ".join(x.name for x in self._circuits),
125+
success=self.status() == JobStatus.DONE,
224126
)
225127

226-
if len(qiskit_results) == 1:
227-
return qiskit_results[0]
228-
229-
return qiskit_results
230-
231128
def result(
232129
self,
233130
timeout=None,
@@ -244,97 +141,13 @@ def result(
244141

245142
job_results = self._wait_for_result(timeout, fetch_interval)
246143

247-
return match.get(format, self.__to_qiskit_result)(job_results)
248-
249-
250-
class CirqResult:
251-
def __init__(
252-
self,
253-
*,
254-
measurements: Optional[Mapping[str, np.ndarray]] = None,
255-
records: Optional[Mapping[str, np.ndarray]] = None,
256-
) -> None:
257-
if measurements is None and records is None:
258-
measurements = {}
259-
records = {}
260-
self._params = None
261-
self._measurements = measurements
262-
self._records = records
263-
264-
@property
265-
def measurements(self) -> Mapping[str, np.ndarray]:
266-
if self._measurements is None:
267-
assert self._records is not None
268-
self._measurements = {}
269-
for key, data in self._records.items():
270-
reps, instances, qubits = data.shape
271-
if instances != 1:
272-
raise ValueError("Cannot extract 2D measurements for repeated keys")
273-
self._measurements[key] = data.reshape((reps, qubits))
274-
return self._measurements
144+
conv_method = match.get(format, self.__to_qiskit_result)
275145

276-
@property
277-
def records(self) -> Mapping[str, np.ndarray]:
278-
if self._records is None:
279-
assert self._measurements is not None
280-
self._records = {
281-
key: data[:, np.newaxis, :] for key, data in self._measurements.items()
282-
}
283-
return self._records
284-
285-
@property
286-
def repetitions(self) -> int:
287-
if self._records is not None:
288-
if not self._records:
289-
return 0
290-
return len(next(iter(self._records.values())))
291-
else:
292-
if not self._measurements:
293-
return 0
294-
return len(next(iter(self._measurements.values())))
295-
296-
def multi_measurement_histogram(
297-
self,
298-
*,
299-
keys: Iterable,
300-
fold_func: Callable = cast(Callable, _tuple_of_big_endian_int),
301-
) -> collections.Counter:
302-
fixed_keys = tuple(key for key in keys)
303-
samples: Iterable[Any] = zip(
304-
*(self.measurements[sub_key] for sub_key in fixed_keys)
305-
)
306-
307-
if len(fixed_keys) == 0:
308-
samples = [()] * self.repetitions
309-
310-
c: collections.Counter = collections.Counter()
311-
312-
for sample in samples:
313-
c[fold_func(sample)] += 1
314-
return c
315-
316-
@classmethod
317-
def _from_packed_records(cls, records, **kwargs):
318-
return cls(
319-
records={key: _unpack_digits(**val) for key, val in records.items()},
320-
**kwargs,
321-
)
322-
323-
@classmethod
324-
def _from_json_dict_(cls, **kwargs):
325-
if "measurements" in kwargs:
326-
measurements = kwargs["measurements"]
327-
return cls(
328-
params=None,
329-
measurements={
330-
key: _unpack_digits(**val) for key, val in measurements.items()
331-
},
146+
results = list(
147+
map(
148+
lambda r: conv_method(self._extract_payload_from_response(r)),
149+
job_results,
332150
)
333-
return cls._from_packed_records(records=kwargs["records"])
151+
)
334152

335-
@property
336-
def repetitions(self) -> int:
337-
if not self.records:
338-
return 0
339-
# Get the length quickly from one of the keyed results.
340-
return len(next(iter(self.records.values())))
153+
return results

0 commit comments

Comments
 (0)