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-
2014from 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
3521from qiskit .providers import JobError , JobStatus
3622from qiskit .result import Result
3723from qiskit .transpiler .passes import RemoveBarriers
38- from qiskit .result .models import ExperimentResult , ExperimentResultData
3924
4025from qiskit_scaleway .versions import USER_AGENT
4126from 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
9541class 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