@@ -219,18 +219,133 @@ def get_interpreter() -> Interpreter:
219
219
return _interpreter
220
220
221
221
222
- def eval (source : str ) -> Any :
222
+ class StateDump :
223
+ """
224
+ A state dump returned from the Q# interpreter.
225
+ """
226
+
227
+ """
228
+ The number of allocated qubits at the time of the dump.
229
+ """
230
+ qubit_count : int
231
+
232
+ __inner : dict
233
+ __data : StateDumpData
234
+
235
+ def __init__ (self , data : StateDumpData ):
236
+ self .__data = data
237
+ self .__inner = data .get_dict ()
238
+ self .qubit_count = data .qubit_count
239
+
240
+ def __getitem__ (self , index : int ) -> complex :
241
+ return self .__inner .__getitem__ (index )
242
+
243
+ def __iter__ (self ):
244
+ return self .__inner .__iter__ ()
245
+
246
+ def __len__ (self ) -> int :
247
+ return len (self .__inner )
248
+
249
+ def __repr__ (self ) -> str :
250
+ return self .__data .__repr__ ()
251
+
252
+ def __str__ (self ) -> str :
253
+ return self .__data .__str__ ()
254
+
255
+ def _repr_markdown_ (self ) -> str :
256
+ return self .__data ._repr_markdown_ ()
257
+
258
+ def check_eq (
259
+ self , state : Union [Dict [int , complex ], List [complex ]], tolerance : float = 1e-10
260
+ ) -> bool :
261
+ """
262
+ Checks if the state dump is equal to the given state. This is not mathematical equality,
263
+ as the check ignores global phase.
264
+
265
+ :param state: The state to check against, provided either as a dictionary of state indices to complex amplitudes,
266
+ or as a list of real amplitudes.
267
+ :param tolerance: The tolerance for the check. Defaults to 1e-10.
268
+ """
269
+ phase = None
270
+ # Convert a dense list of real amplitudes to a dictionary of state indices to complex amplitudes
271
+ if isinstance (state , list ):
272
+ state = {i : val for i , val in enumerate (state )}
273
+ # Filter out zero states from the state dump and the given state based on tolerance
274
+ state = {k : v for k , v in state .items () if abs (v ) > tolerance }
275
+ inner_state = {k : v for k , v in self .__inner .items () if abs (v ) > tolerance }
276
+ if len (state ) != len (inner_state ):
277
+ return False
278
+ for key in state :
279
+ if key not in inner_state :
280
+ return False
281
+ if phase is None :
282
+ # Calculate the phase based on the first state pair encountered.
283
+ # Every pair of states after this must have the same phase for the states to be equivalent.
284
+ phase = inner_state [key ] / state [key ]
285
+ elif abs (phase - inner_state [key ] / state [key ]) > tolerance :
286
+ # This pair of states does not have the same phase,
287
+ # within tolerance, so the equivalence check fails.
288
+ return False
289
+ return True
290
+
291
+ def as_dense_state (self ) -> List [complex ]:
292
+ """
293
+ Returns the state dump as a dense list of complex amplitudes. This will include zero amplitudes.
294
+ """
295
+ return [self .__inner .get (i , complex (0 )) for i in range (2 ** self .qubit_count )]
296
+
297
+
298
+ class ShotResult (TypedDict ):
299
+ """
300
+ A single result of a shot.
301
+ """
302
+
303
+ events : List [Output ]
304
+ result : Any
305
+ messages : List [str ]
306
+ matrices : List [Output ]
307
+ dumps : List [StateDump ]
308
+
309
+
310
+ def eval (
311
+ source : str ,
312
+ * ,
313
+ save_events : bool = False ,
314
+ ) -> Any :
223
315
"""
224
316
Evaluates Q# source code.
225
317
226
318
Output is printed to console.
227
319
228
320
:param source: The Q# source code to evaluate.
229
- :returns value: The value returned by the last statement in the source code.
321
+ :param save_events: If true, all output will be saved and returned. If false, they will be printed.
322
+ :returns value: The value returned by the last statement in the source code or the saved output if `save_events` is true.
230
323
:raises QSharpError: If there is an error evaluating the source code.
231
324
"""
232
325
ipython_helper ()
233
326
327
+ results : ShotResult = {
328
+ "events" : [],
329
+ "result" : None ,
330
+ "messages" : [],
331
+ "matrices" : [],
332
+ "dumps" : [],
333
+ }
334
+
335
+ def on_save_events (output : Output ) -> None :
336
+ # Append the output to the last shot's output list
337
+ if output .is_matrix ():
338
+ results ["events" ].append (output )
339
+ results ["matrices" ].append (output )
340
+ elif output .is_state_dump ():
341
+ state_dump = StateDump (output .state_dump ())
342
+ results ["events" ].append (state_dump )
343
+ results ["dumps" ].append (state_dump )
344
+ elif output .is_message ():
345
+ stringified = str (output )
346
+ results ["events" ].append (stringified )
347
+ results ["messages" ].append (stringified )
348
+
234
349
def callback (output : Output ) -> None :
235
350
if _in_jupyter :
236
351
try :
@@ -244,21 +359,17 @@ def callback(output: Output) -> None:
244
359
telemetry_events .on_eval ()
245
360
start_time = monotonic ()
246
361
247
- results = get_interpreter ().interpret (source , callback )
362
+ results ["result" ] = get_interpreter ().interpret (
363
+ source , on_save_events if save_events else callback
364
+ )
248
365
249
366
durationMs = (monotonic () - start_time ) * 1000
250
367
telemetry_events .on_eval_end (durationMs )
251
368
252
- return results
253
-
254
-
255
- class ShotResult (TypedDict ):
256
- """
257
- A single result of a shot.
258
- """
259
-
260
- events : List [Output ]
261
- result : Any
369
+ if save_events :
370
+ return results
371
+ else :
372
+ return results ["result" ]
262
373
263
374
264
375
def run (
@@ -315,9 +426,17 @@ def print_output(output: Output) -> None:
315
426
def on_save_events (output : Output ) -> None :
316
427
# Append the output to the last shot's output list
317
428
results [- 1 ]["events" ].append (output )
429
+ if output .is_matrix ():
430
+ results [- 1 ]["matrices" ].append (output )
431
+ elif output .is_state_dump ():
432
+ results [- 1 ]["dumps" ].append (StateDump (output .state_dump ()))
433
+ elif output .is_message ():
434
+ results [- 1 ]["messages" ].append (str (output ))
318
435
319
436
for shot in range (shots ):
320
- results .append ({"result" : None , "events" : []})
437
+ results .append (
438
+ {"result" : None , "events" : [], "messages" : [], "matrices" : [], "dumps" : []}
439
+ )
321
440
run_results = get_interpreter ().run (
322
441
entry_expr ,
323
442
on_save_events if save_events else print_output ,
@@ -482,82 +601,6 @@ def set_classical_seed(seed: Optional[int]) -> None:
482
601
get_interpreter ().set_classical_seed (seed )
483
602
484
603
485
- class StateDump :
486
- """
487
- A state dump returned from the Q# interpreter.
488
- """
489
-
490
- """
491
- The number of allocated qubits at the time of the dump.
492
- """
493
- qubit_count : int
494
-
495
- __inner : dict
496
- __data : StateDumpData
497
-
498
- def __init__ (self , data : StateDumpData ):
499
- self .__data = data
500
- self .__inner = data .get_dict ()
501
- self .qubit_count = data .qubit_count
502
-
503
- def __getitem__ (self , index : int ) -> complex :
504
- return self .__inner .__getitem__ (index )
505
-
506
- def __iter__ (self ):
507
- return self .__inner .__iter__ ()
508
-
509
- def __len__ (self ) -> int :
510
- return len (self .__inner )
511
-
512
- def __repr__ (self ) -> str :
513
- return self .__data .__repr__ ()
514
-
515
- def __str__ (self ) -> str :
516
- return self .__data .__str__ ()
517
-
518
- def _repr_markdown_ (self ) -> str :
519
- return self .__data ._repr_markdown_ ()
520
-
521
- def check_eq (
522
- self , state : Union [Dict [int , complex ], List [complex ]], tolerance : float = 1e-10
523
- ) -> bool :
524
- """
525
- Checks if the state dump is equal to the given state. This is not mathematical equality,
526
- as the check ignores global phase.
527
-
528
- :param state: The state to check against, provided either as a dictionary of state indices to complex amplitudes,
529
- or as a list of real amplitudes.
530
- :param tolerance: The tolerance for the check. Defaults to 1e-10.
531
- """
532
- phase = None
533
- # Convert a dense list of real amplitudes to a dictionary of state indices to complex amplitudes
534
- if isinstance (state , list ):
535
- state = {i : state [i ] for i in range (len (state ))}
536
- # Filter out zero states from the state dump and the given state based on tolerance
537
- state = {k : v for k , v in state .items () if abs (v ) > tolerance }
538
- inner_state = {k : v for k , v in self .__inner .items () if abs (v ) > tolerance }
539
- if len (state ) != len (inner_state ):
540
- return False
541
- for key in state :
542
- if key not in inner_state :
543
- return False
544
- if phase is None :
545
- # Calculate the phase based on the first state pair encountered.
546
- # Every pair of states after this must have the same phase for the states to be equivalent.
547
- phase = inner_state [key ] / state [key ]
548
- elif abs (phase - inner_state [key ] / state [key ]) > tolerance :
549
- # This pair of states does not have the same phase,
550
- # within tolerance, so the equivalence check fails.
551
- return False
552
- return True
553
-
554
- def as_dense_state (self ) -> List [complex ]:
555
- """
556
- Returns the state dump as a dense list of complex amplitudes. This will include zero amplitudes.
557
- """
558
- return [self .__inner .get (i , complex (0 )) for i in range (2 ** self .qubit_count )]
559
-
560
-
561
604
def dump_machine () -> StateDump :
562
605
"""
563
606
Returns the sparse state vector of the simulator as a StateDump object.
0 commit comments