pyqrack-cpu 1.82.0__py3-none-macosx_15_0_arm64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,4464 @@
1
+ # (C) Daniel Strano and the Qrack contributors 2017-2025. All rights reserved.
2
+ #
3
+ # Use of this source code is governed by an MIT-style license that can be
4
+ # found in the LICENSE file or at https://opensource.org/licenses/MIT.
5
+
6
+ import copy
7
+ import ctypes
8
+ import math
9
+ import os
10
+ import re
11
+ from .qrack_system import Qrack
12
+ from .pauli import Pauli
13
+
14
+ _IS_QISKIT_AVAILABLE = True
15
+ try:
16
+ from qiskit.circuit.quantumcircuit import QuantumCircuit
17
+ from qiskit.compiler import transpile
18
+ from qiskit.quantum_info.operators.symplectic.clifford import Clifford
19
+ except ImportError:
20
+ _IS_QISKIT_AVAILABLE = False
21
+
22
+ try:
23
+ from qiskit import qasm3
24
+ except ImportError:
25
+ pass
26
+
27
+ _IS_NUMPY_AVAILABLE = True
28
+ try:
29
+ import numpy as np
30
+ except:
31
+ _IS_NUMPY_AVAILABLE = False
32
+
33
+
34
+ class QrackSimulator:
35
+ """Interface for all the Qrack functionality.
36
+
37
+ Attributes:
38
+ sid(int): Corresponding simulator id.
39
+ """
40
+
41
+ def _get_error(self):
42
+ return Qrack.qrack_lib.get_error(self.sid)
43
+
44
+ def _throw_if_error(self):
45
+ if self._get_error() != 0:
46
+ raise RuntimeError("QrackSimulator C++ library raised exception.")
47
+
48
+ def __init__(
49
+ self,
50
+ qubitCount=-1,
51
+ cloneSid=-1,
52
+ isTensorNetwork=True,
53
+ isSchmidtDecomposeMulti=False,
54
+ isSchmidtDecompose=True,
55
+ isStabilizerHybrid=False,
56
+ isBinaryDecisionTree=False,
57
+ isPaged=True,
58
+ isCpuGpuHybrid=True,
59
+ isOpenCL=True,
60
+ isHostPointer=(True if os.environ.get("PYQRACK_HOST_POINTER_DEFAULT_ON") else False),
61
+ isSparse=False,
62
+ noise=0,
63
+ pyzxCircuit=None,
64
+ qiskitCircuit=None,
65
+ ):
66
+ self.sid = None
67
+
68
+ if pyzxCircuit is not None:
69
+ qubitCount = pyzxCircuit.qubits
70
+ elif qiskitCircuit is not None and qubitCount < 0:
71
+ raise RuntimeError(
72
+ "Must specify qubitCount with qiskitCircuit parameter in QrackSimulator constructor!"
73
+ )
74
+
75
+ if qubitCount > -1 and cloneSid > -1:
76
+ raise RuntimeError(
77
+ "Cannot clone a QrackSimulator and specify its qubit length at the same time, in QrackSimulator constructor!"
78
+ )
79
+
80
+ self.is_pure_stabilizer = False
81
+
82
+ if cloneSid > -1:
83
+ self.sid = Qrack.qrack_lib.init_clone(cloneSid)
84
+ else:
85
+ if qubitCount < 0:
86
+ qubitCount = 0
87
+
88
+ self.sid = Qrack.qrack_lib.init_count_type(
89
+ qubitCount,
90
+ isTensorNetwork,
91
+ isSchmidtDecomposeMulti,
92
+ isSchmidtDecompose,
93
+ isStabilizerHybrid,
94
+ isBinaryDecisionTree,
95
+ isPaged,
96
+ (noise > 0),
97
+ isCpuGpuHybrid,
98
+ isOpenCL,
99
+ isHostPointer,
100
+ isSparse,
101
+ )
102
+
103
+ self._throw_if_error()
104
+
105
+ if noise > 0:
106
+ self.set_noise_parameter(noise)
107
+
108
+ if pyzxCircuit is not None:
109
+ self.run_pyzx_gates(pyzxCircuit.gates)
110
+ elif qiskitCircuit is not None:
111
+ self.run_qiskit_circuit(qiskitCircuit)
112
+
113
+ def __del__(self):
114
+ if self.sid is not None:
115
+ Qrack.qrack_lib.destroy(self.sid)
116
+ self.sid = None
117
+
118
+ @staticmethod
119
+ def _int_byref(a):
120
+ return (ctypes.c_int * len(a))(*a)
121
+
122
+ @staticmethod
123
+ def _ulonglong_byref(a):
124
+ return (ctypes.c_ulonglong * len(a))(*a)
125
+
126
+ @staticmethod
127
+ def _longlong_byref(a):
128
+ return (ctypes.c_longlong * len(a))(*a)
129
+
130
+ @staticmethod
131
+ def _double_byref(a):
132
+ return (ctypes.c_double * len(a))(*a)
133
+
134
+ @staticmethod
135
+ def _complex_byref(a):
136
+ t = [(c.real, c.imag) for c in a]
137
+ return QrackSimulator._double_byref([float(item) for sublist in t for item in sublist])
138
+
139
+ @staticmethod
140
+ def _real1_byref(a):
141
+ # This needs to be c_double, if PyQrack is built with fp64.
142
+ if Qrack.fppow < 6:
143
+ return (ctypes.c_float * len(a))(*a)
144
+ return (ctypes.c_double * len(a))(*a)
145
+
146
+ @staticmethod
147
+ def _bool_byref(a):
148
+ return (ctypes.c_bool * len(a))(*a)
149
+
150
+ @staticmethod
151
+ def _qrack_complex_byref(a):
152
+ t = [(c.real, c.imag) for c in a]
153
+ return QrackSimulator._real1_byref([float(item) for sublist in t for item in sublist])
154
+
155
+ @staticmethod
156
+ def _to_ubyte(nv, v):
157
+ c = math.floor((nv - 1) / 8) + 1
158
+ b = (ctypes.c_ubyte * (c * (1 << nv)))()
159
+ n = 0
160
+ for u in v:
161
+ for _ in range(c):
162
+ b[n] = u & 0xFF
163
+ u >>= 8
164
+ n += 1
165
+
166
+ return b
167
+
168
+ @staticmethod
169
+ def _to_ulonglong(m, v):
170
+ b = (ctypes.c_ulonglong * (m * len(v)))()
171
+ n = 0
172
+ for u in v:
173
+ for _ in range(m):
174
+ b[n] = u & 0xFFFFFFFFFFFFFFFF
175
+ u >>= 64
176
+ n += 1
177
+
178
+ return b
179
+
180
+ # See https://stackoverflow.com/questions/5389507/iterating-over-every-two-elements-in-a-list#answer-30426000
181
+ @staticmethod
182
+ def _pairwise(it):
183
+ it = iter(it)
184
+ while True:
185
+ try:
186
+ yield next(it), next(it)
187
+ except StopIteration:
188
+ # no more elements in the iterator
189
+ return
190
+
191
+ # non-quantum
192
+ def seed(self, s):
193
+ Qrack.qrack_lib.seed(self.sid, s)
194
+ self._throw_if_error()
195
+
196
+ def set_concurrency(self, p):
197
+ """Set the CPU parallel thread count"""
198
+ Qrack.qrack_lib.set_concurrency(self.sid, p)
199
+ self._throw_if_error()
200
+
201
+ def set_device(self, d):
202
+ """Set the GPU device ID"""
203
+ Qrack.qrack_lib.set_device(self.sid, d)
204
+ self._throw_if_error()
205
+
206
+ def set_device_list(self, d):
207
+ """Set the GPU device ID"""
208
+ Qrack.qrack_lib.set_device_list(self.sid, len(d), QrackSimulator._longlong_byref(d))
209
+ self._throw_if_error()
210
+
211
+ def clone(self):
212
+ return QrackSimulator(cloneSid=self.sid)
213
+
214
+ # standard gates
215
+
216
+ ## single-qubits gates
217
+ def x(self, q):
218
+ """Applies X gate.
219
+
220
+ Applies the Pauli “X” operator to the qubit at position “q.”
221
+ The Pauli “X” operator is equivalent to a logical “NOT.”
222
+
223
+ Args:
224
+ q: the qubit number on which the gate is applied to.
225
+
226
+ Raises:
227
+ RuntimeError: QrackSimulator raised an exception.
228
+ """
229
+ Qrack.qrack_lib.X(self.sid, q)
230
+ self._throw_if_error()
231
+
232
+ def y(self, q):
233
+ """Applies Y gate.
234
+
235
+ Applies the Pauli “Y” operator to the qubit at “q.”
236
+ The Pauli “Y” operator is equivalent to a logical “NOT" with
237
+ permutation phase.
238
+
239
+ Args:
240
+ q: the qubit number on which the gate is applied to.
241
+
242
+ Raises:
243
+ RuntimeError: QrackSimulator raised an exception.
244
+ """
245
+ Qrack.qrack_lib.Y(self.sid, q)
246
+ self._throw_if_error()
247
+
248
+ def z(self, q):
249
+ """Applies Z gate.
250
+
251
+ Applies the Pauli “Z” operator to the qubit at “q.”
252
+ The Pauli “Z” operator flips the phase of `|1>`
253
+
254
+ Args:
255
+ q: the qubit number on which the gate is applied to.
256
+
257
+ Raises:
258
+ RuntimeError: QrackSimulator raised an exception.
259
+ """
260
+ Qrack.qrack_lib.Z(self.sid, q)
261
+ self._throw_if_error()
262
+
263
+ def h(self, q):
264
+ """Applies H gate.
265
+
266
+ Applies the Hadarmard operator to the qubit at “q.”
267
+
268
+ Args:
269
+ q: the qubit number on which the gate is applied to.
270
+
271
+ Raises:
272
+ RuntimeError: QrackSimulator raised an exception.
273
+ """
274
+ Qrack.qrack_lib.H(self.sid, q)
275
+ self._throw_if_error()
276
+
277
+ def s(self, q):
278
+ """Applies S gate.
279
+
280
+ Applies the 1/4 phase rotation to the qubit at “q.”
281
+
282
+ Args:
283
+ q: the qubit number on which the gate is applied to.
284
+
285
+ Raises:
286
+ RuntimeError: QrackSimulator raised an exception.
287
+ """
288
+ Qrack.qrack_lib.S(self.sid, q)
289
+ self._throw_if_error()
290
+
291
+ def t(self, q):
292
+ """Applies T gate.
293
+
294
+ Applies the 1/8 phase rotation to the qubit at “q.”
295
+
296
+ Args:
297
+ q: the qubit number on which the gate is applied to.
298
+
299
+ Raises:
300
+ RuntimeError: QrackSimulator raised an exception.
301
+ """
302
+ Qrack.qrack_lib.T(self.sid, q)
303
+ self._throw_if_error()
304
+
305
+ def adjs(self, q):
306
+ """Adjoint of S gate
307
+
308
+ Applies the gate equivalent to the inverse of S gate.
309
+
310
+ Args:
311
+ q: the qubit number on which the gate is applied to.
312
+
313
+ Raises:
314
+ RuntimeError: QrackSimulator raised an exception.
315
+ """
316
+ Qrack.qrack_lib.AdjS(self.sid, q)
317
+ self._throw_if_error()
318
+
319
+ def adjt(self, q):
320
+ """Adjoint of T gate
321
+
322
+ Applies the gate equivalent to the inverse of T gate.
323
+
324
+ Args:
325
+ q: the qubit number on which the gate is applied to.
326
+
327
+ Raises:
328
+ RuntimeError: QrackSimulator raised an exception.
329
+ """
330
+ Qrack.qrack_lib.AdjT(self.sid, q)
331
+ self._throw_if_error()
332
+
333
+ def u(self, q, th, ph, la):
334
+ """General unitary gate.
335
+
336
+ Applies a gate guaranteed to be unitary.
337
+ Spans all possible single bit unitary gates.
338
+
339
+ `U(theta, phi, lambda) = RZ(phi + pi/2)RX(theta)RZ(lambda - pi/2)`
340
+
341
+ Args:
342
+ q: the qubit number on which the gate is applied to.
343
+ th: theta
344
+ ph: phi
345
+ la: lambda
346
+
347
+ Raises:
348
+ RuntimeError: QrackSimulator raised an exception.
349
+ """
350
+ Qrack.qrack_lib.U(
351
+ self.sid, q, ctypes.c_double(th), ctypes.c_double(ph), ctypes.c_double(la)
352
+ )
353
+ self._throw_if_error()
354
+
355
+ def mtrx(self, m, q):
356
+ """Operation from matrix.
357
+
358
+ Applies arbitrary operation defined by the given matrix.
359
+
360
+ Args:
361
+ m: row-major complex list representing the operator.
362
+ q: the qubit number on which the gate is applied to.
363
+
364
+ Raises:
365
+ ValueError: 2x2 matrix 'm' in QrackSimulator.mtrx() must contain at least 4 elements.
366
+ RuntimeError: QrackSimulator raised an exception.
367
+ """
368
+ if len(m) < 4:
369
+ raise ValueError(
370
+ "2x2 matrix 'm' in QrackSimulator.mtrx() must contain at least 4 elements."
371
+ )
372
+ Qrack.qrack_lib.Mtrx(self.sid, QrackSimulator._complex_byref(m), q)
373
+ self._throw_if_error()
374
+
375
+ def r(self, b, ph, q):
376
+ """Rotation gate.
377
+
378
+ Rotate the qubit along the given pauli basis by the given angle.
379
+
380
+
381
+ Args:
382
+ b: Pauli basis
383
+ ph: rotation angle
384
+ q: the qubit number on which the gate is applied to
385
+
386
+ Raises:
387
+ RuntimeError: QrackSimulator raised an exception.
388
+ """
389
+ Qrack.qrack_lib.R(self.sid, ctypes.c_ulonglong(b), ctypes.c_double(ph), q)
390
+ self._throw_if_error()
391
+
392
+ def exp(self, b, ph, q):
393
+ """Arbitrary exponentiation
394
+
395
+ `exp(b, theta) = e^{i*theta*[b_0 . b_1 ...]}`
396
+ where `.` is the tensor product.
397
+
398
+
399
+ Args:
400
+ b: Pauli basis
401
+ ph: coefficient of exponentiation
402
+ q: the qubit number on which the gate is applied to
403
+
404
+ Raises:
405
+ RuntimeError: QrackSimulator raised an exception.
406
+ """
407
+ if len(b) != len(q):
408
+ raise RuntimeError("Lengths of list parameters are mismatched.")
409
+ Qrack.qrack_lib.Exp(
410
+ self.sid,
411
+ len(b),
412
+ QrackSimulator._ulonglong_byref(b),
413
+ ctypes.c_double(ph),
414
+ QrackSimulator._ulonglong_byref(q),
415
+ )
416
+ self._throw_if_error()
417
+
418
+ ## multi-qubit gates
419
+ def mcx(self, c, q):
420
+ """Multi-controlled X gate
421
+
422
+ If all controlled qubits are `|1>` then the target qubit is flipped.
423
+
424
+ Args:
425
+ c: list of controlled qubits.
426
+ q: target qubit.
427
+
428
+ Raises:
429
+ RuntimeError: QrackSimulator raised an exception.
430
+ """
431
+ Qrack.qrack_lib.MCX(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
432
+ self._throw_if_error()
433
+
434
+ def mcy(self, c, q):
435
+ """Multi-controlled Y gate
436
+
437
+ If all controlled qubits are `|1>` then the Pauli "Y" gate is applied to
438
+ the target qubit.
439
+
440
+ Args:
441
+ c: list of controlled qubits.
442
+ q: target qubit.
443
+
444
+ Raises:
445
+ RuntimeError: QrackSimulator raised an exception.
446
+ """
447
+ Qrack.qrack_lib.MCY(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
448
+ self._throw_if_error()
449
+
450
+ def mcz(self, c, q):
451
+ """Multi-controlled Z gate
452
+
453
+ If all controlled qubits are `|1>` then the Pauli "Z" gate is applied to
454
+ the target qubit.
455
+
456
+ Args:
457
+ c: list of controlled qubits.
458
+ q: target qubit.
459
+
460
+ Raises:
461
+ RuntimeError: QrackSimulator raised an exception.
462
+ """
463
+ Qrack.qrack_lib.MCZ(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
464
+ self._throw_if_error()
465
+
466
+ def mch(self, c, q):
467
+ """Multi-controlled H gate
468
+
469
+ If all controlled qubits are `|1>` then the Hadarmard gate is applied to
470
+ the target qubit.
471
+
472
+ Args:
473
+ c: list of controlled qubits.
474
+ q: target qubit.
475
+
476
+ Raises:
477
+ RuntimeError: QrackSimulator raised an exception.
478
+ """
479
+ Qrack.qrack_lib.MCH(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
480
+ self._throw_if_error()
481
+
482
+ def mcs(self, c, q):
483
+ """Multi-controlled S gate
484
+
485
+ If all controlled qubits are `|1>` then the "S" gate is applied to
486
+ the target qubit.
487
+
488
+ Args:
489
+ c: list of controlled qubits.
490
+ q: target qubit.
491
+
492
+ Raises:
493
+ RuntimeError: QrackSimulator raised an exception.
494
+ """
495
+ Qrack.qrack_lib.MCS(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
496
+ self._throw_if_error()
497
+
498
+ def mct(self, c, q):
499
+ """Multi-controlled T gate
500
+
501
+ If all controlled qubits are `|1>` then the "T" gate is applied to
502
+ the target qubit.
503
+
504
+ Args:
505
+ c: list of controlled qubits.
506
+ q: target qubit.
507
+
508
+ Raises:
509
+ RuntimeError: QrackSimulator raised an exception.
510
+ """
511
+ Qrack.qrack_lib.MCT(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
512
+ self._throw_if_error()
513
+
514
+ def mcadjs(self, c, q):
515
+ """Multi-controlled adjs gate
516
+
517
+ If all controlled qubits are `|1>` then the adjs gate is applied to
518
+ the target qubit.
519
+
520
+ Args:
521
+ c: list of controlled qubits.
522
+ q: target qubit.
523
+
524
+ Raises:
525
+ RuntimeError: QrackSimulator raised an exception.
526
+ """
527
+ Qrack.qrack_lib.MCAdjS(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
528
+ self._throw_if_error()
529
+
530
+ def mcadjt(self, c, q):
531
+ """Multi-controlled adjt gate
532
+
533
+ If all controlled qubits are `|1>` then the adjt gate is applied to
534
+ the target qubit.
535
+
536
+ Args:
537
+ c: list of controlled qubits.
538
+ q: target qubit.
539
+
540
+ Raises:
541
+ RuntimeError: QrackSimulator raised an exception.
542
+ """
543
+ Qrack.qrack_lib.MCAdjT(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
544
+ self._throw_if_error()
545
+
546
+ def mcu(self, c, q, th, ph, la):
547
+ """Multi-controlled arbitraty unitary
548
+
549
+ If all controlled qubits are `|1>` then the unitary gate described by
550
+ parameters is applied to the target qubit.
551
+
552
+ Args:
553
+ c: list of controlled qubits.
554
+ q: target qubit.
555
+ th: theta
556
+ ph: phi
557
+ la: lambda
558
+
559
+ Raises:
560
+ RuntimeError: QrackSimulator raised an exception.
561
+ """
562
+ Qrack.qrack_lib.MCU(
563
+ self.sid,
564
+ len(c),
565
+ QrackSimulator._ulonglong_byref(c),
566
+ q,
567
+ ctypes.c_double(th),
568
+ ctypes.c_double(ph),
569
+ ctypes.c_double(la),
570
+ )
571
+ self._throw_if_error()
572
+
573
+ def mcmtrx(self, c, m, q):
574
+ """Multi-controlled arbitrary operator
575
+
576
+ If all controlled qubits are `|1>` then the arbitrary operation by
577
+ parameters is applied to the target qubit.
578
+
579
+ Args:
580
+ c: list of controlled qubits
581
+ m: row-major complex list representing the operator.
582
+ q: target qubit
583
+
584
+ Raises:
585
+ ValueError: 2x2 matrix 'm' in QrackSimulator.mcmtrx() must contain at least 4 elements.
586
+ RuntimeError: QrackSimulator raised an exception.
587
+ """
588
+ if len(m) < 4:
589
+ raise ValueError(
590
+ "2x2 matrix 'm' in QrackSimulator.mcmtrx() must contain at least 4 elements."
591
+ )
592
+ Qrack.qrack_lib.MCMtrx(
593
+ self.sid,
594
+ len(c),
595
+ QrackSimulator._ulonglong_byref(c),
596
+ QrackSimulator._complex_byref(m),
597
+ q,
598
+ )
599
+ self._throw_if_error()
600
+
601
+ def macx(self, c, q):
602
+ """Anti multi-controlled X gate
603
+
604
+ If all controlled qubits are `|0>` then the target qubit is flipped.
605
+
606
+ Args:
607
+ c: list of controlled qubits.
608
+ q: target qubit.
609
+
610
+ Raises:
611
+ RuntimeError: QrackSimulator raised an exception.
612
+ """
613
+ Qrack.qrack_lib.MACX(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
614
+ self._throw_if_error()
615
+
616
+ def macy(self, c, q):
617
+ """Anti multi-controlled Y gate
618
+
619
+ If all controlled qubits are `|0>` then the Pauli "Y" gate is applied to
620
+ the target qubit.
621
+
622
+ Args:
623
+ c: list of controlled qubits.
624
+ q: target qubit.
625
+
626
+ Raises:
627
+ RuntimeError: QrackSimulator raised an exception.
628
+ """
629
+ Qrack.qrack_lib.MACY(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
630
+ self._throw_if_error()
631
+
632
+ def macz(self, c, q):
633
+ """Anti multi-controlled Z gate
634
+
635
+ If all controlled qubits are `|0>` then the Pauli "Z" gate is applied to
636
+ the target qubit.
637
+
638
+ Args:
639
+ c: list of controlled qubits.
640
+ q: target qubit.
641
+
642
+ Raises:
643
+ RuntimeError: QrackSimulator raised an exception.
644
+ """
645
+ Qrack.qrack_lib.MACZ(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
646
+ self._throw_if_error()
647
+
648
+ def mach(self, c, q):
649
+ """Anti multi-controlled H gate
650
+
651
+ If all controlled qubits are `|0>` then the Hadarmard gate is applied to
652
+ the target qubit.
653
+
654
+ Args:
655
+ c: list of controlled qubits.
656
+ q: target qubit.
657
+
658
+ Raises:
659
+ RuntimeError: QrackSimulator raised an exception.
660
+ """
661
+ Qrack.qrack_lib.MACH(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
662
+ self._throw_if_error()
663
+
664
+ def macs(self, c, q):
665
+ """Anti multi-controlled S gate
666
+
667
+ If all controlled qubits are `|0>` then the "S" gate is applied to
668
+ the target qubit.
669
+
670
+ Args:
671
+ c: list of controlled qubits.
672
+ q: target qubit.
673
+
674
+ Raises:
675
+ RuntimeError: QrackSimulator raised an exception.
676
+ """
677
+ Qrack.qrack_lib.MACS(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
678
+ self._throw_if_error()
679
+
680
+ def mact(self, c, q):
681
+ """Anti multi-controlled T gate
682
+
683
+ If all controlled qubits are `|0>` then the "T" gate is applied to
684
+ the target qubit.
685
+
686
+ Args:
687
+ c: list of controlled qubits.
688
+ q: target qubit.
689
+
690
+ Raises:
691
+ RuntimeError: QrackSimulator raised an exception.
692
+ """
693
+ Qrack.qrack_lib.MACT(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
694
+ self._throw_if_error()
695
+
696
+ def macadjs(self, c, q):
697
+ """Anti multi-controlled adjs gate
698
+
699
+ If all controlled qubits are `|0>` then the adjs gate is applied to
700
+ the target qubit.
701
+
702
+ Args:
703
+ c: list of controlled qubits.
704
+ q: target qubit.
705
+
706
+ Raises:
707
+ RuntimeError: QrackSimulator raised an exception.
708
+ """
709
+ Qrack.qrack_lib.MACAdjS(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
710
+ self._throw_if_error()
711
+
712
+ def macadjt(self, c, q):
713
+ """Anti multi-controlled adjt gate
714
+
715
+ If all controlled qubits are `|0>` then the adjt gate is applied to
716
+ the target qubit.
717
+
718
+ Args:
719
+ c: list of controlled qubits.
720
+ q: target qubit.
721
+
722
+ Raises:
723
+ RuntimeError: QrackSimulator raised an exception.
724
+ """
725
+ Qrack.qrack_lib.MACAdjT(self.sid, len(c), QrackSimulator._ulonglong_byref(c), q)
726
+ self._throw_if_error()
727
+
728
+ def macu(self, c, q, th, ph, la):
729
+ """Anti multi-controlled arbitraty unitary
730
+
731
+ If all controlled qubits are `|0>` then the unitary gate described by
732
+ parameters is applied to the target qubit.
733
+
734
+ Args:
735
+ c: list of controlled qubits.
736
+ q: target qubit.
737
+ th: theta
738
+ ph: phi
739
+ la: lambda
740
+
741
+ Raises:
742
+ RuntimeError: QrackSimulator raised an exception.
743
+ """
744
+ Qrack.qrack_lib.MACU(
745
+ self.sid,
746
+ len(c),
747
+ QrackSimulator._ulonglong_byref(c),
748
+ q,
749
+ ctypes.c_double(th),
750
+ ctypes.c_double(ph),
751
+ ctypes.c_double(la),
752
+ )
753
+ self._throw_if_error()
754
+
755
+ def macmtrx(self, c, m, q):
756
+ """Anti multi-controlled arbitraty operator
757
+
758
+ If all controlled qubits are `|0>` then the arbitrary operation by
759
+ parameters is applied to the target qubit.
760
+
761
+ Args:
762
+ c: list of controlled qubits.
763
+ m: row-major complex matrix which defines the operator.
764
+ q: target qubit.
765
+
766
+ Raises:
767
+ ValueError: 2x2 matrix 'm' in QrackSimulator.macmtrx() must contain at least 4 elements.
768
+ RuntimeError: QrackSimulator raised an exception.
769
+ """
770
+ if len(m) < 4:
771
+ raise ValueError(
772
+ "2x2 matrix 'm' in QrackSimulator.macmtrx() must contain at least 4 elements."
773
+ )
774
+ Qrack.qrack_lib.MACMtrx(
775
+ self.sid,
776
+ len(c),
777
+ QrackSimulator._ulonglong_byref(c),
778
+ QrackSimulator._complex_byref(m),
779
+ q,
780
+ )
781
+ self._throw_if_error()
782
+
783
+ def ucmtrx(self, c, m, q, p):
784
+ """Multi-controlled arbitrary operator with arbitrary controls
785
+
786
+ If all control qubits match 'p' permutation by bit order, then the arbitrary
787
+ operation by parameters is applied to the target qubit.
788
+
789
+ Args:
790
+ c: list of control qubits
791
+ m: row-major complex list representing the operator.
792
+ q: target qubit
793
+ p: permutation of list of control qubits
794
+
795
+ Raises:
796
+ ValueError: 2x2 matrix 'm' in QrackSimulator.ucmtrx() must contain at least 4 elements.
797
+ RuntimeError: QrackSimulator raised an exception.
798
+ """
799
+ if len(m) < 4:
800
+ raise ValueError(
801
+ "2x2 matrix 'm' in QrackSimulator.ucmtrx() must contain at least 4 elements."
802
+ )
803
+ Qrack.qrack_lib.UCMtrx(
804
+ self.sid,
805
+ len(c),
806
+ QrackSimulator._ulonglong_byref(c),
807
+ QrackSimulator._complex_byref(m),
808
+ q,
809
+ p,
810
+ )
811
+ self._throw_if_error()
812
+
813
+ def multiplex1_mtrx(self, c, q, m):
814
+ """Multiplex gate
815
+
816
+ A multiplex gate with a single target and an arbitrary number of
817
+ controls.
818
+
819
+ Args:
820
+ c: list of controlled qubits.
821
+ m: row-major complex matrix which defines the operator.
822
+ q: target qubit.
823
+
824
+ Raises:
825
+ ValueError: Multiplex matrix 'm' in QrackSimulator.multiplex1_mtrx() must contain at least 4 elements.
826
+ RuntimeError: QrackSimulator raised an exception.
827
+ """
828
+ if len(m) < ((1 << len(c)) * 4):
829
+ raise ValueError(
830
+ "Multiplex matrix 'm' in QrackSimulator.multiplex1_mtrx() must contain at least (4 * 2 ** len(c)) elements."
831
+ )
832
+ Qrack.qrack_lib.Multiplex1Mtrx(
833
+ self.sid,
834
+ len(c),
835
+ QrackSimulator._ulonglong_byref(c),
836
+ q,
837
+ QrackSimulator._complex_byref(m),
838
+ )
839
+ self._throw_if_error()
840
+
841
+ def mx(self, q):
842
+ """Multi X-gate
843
+
844
+ Applies the Pauli “X” operator on all qubits.
845
+
846
+ Args:
847
+ q: list of qubits to apply X on.
848
+
849
+ Raises:
850
+ RuntimeError: QrackSimulator raised an exception.
851
+ """
852
+ Qrack.qrack_lib.MX(self.sid, len(q), QrackSimulator._ulonglong_byref(q))
853
+ self._throw_if_error()
854
+
855
+ def my(self, q):
856
+ """Multi Y-gate
857
+
858
+ Applies the Pauli “Y” operator on all qubits.
859
+
860
+ Args:
861
+ q: list of qubits to apply Y on.
862
+
863
+ Raises:
864
+ RuntimeError: QrackSimulator raised an exception.
865
+ """
866
+ Qrack.qrack_lib.MY(self.sid, len(q), QrackSimulator._ulonglong_byref(q))
867
+ self._throw_if_error()
868
+
869
+ def mz(self, q):
870
+ """Multi Z-gate
871
+
872
+ Applies the Pauli “Z” operator on all qubits.
873
+
874
+ Args:
875
+ q: list of qubits to apply Z on.
876
+
877
+ Raises:
878
+ RuntimeError: QrackSimulator raised an exception.
879
+ """
880
+ Qrack.qrack_lib.MZ(self.sid, len(q), QrackSimulator._ulonglong_byref(q))
881
+ self._throw_if_error()
882
+
883
+ def mcr(self, b, ph, c, q):
884
+ """Multi-controlled arbitrary rotation.
885
+
886
+ If all controlled qubits are `|1>` then the arbitrary rotation by
887
+ parameters is applied to the target qubit.
888
+
889
+ Args:
890
+ b: Pauli basis
891
+ ph: coefficient of exponentiation.
892
+ c: list of controlled qubits.
893
+ q: the qubit number on which the gate is applied to.
894
+
895
+ Raises:
896
+ RuntimeError: QrackSimulator raised an exception.
897
+ """
898
+ Qrack.qrack_lib.MCR(
899
+ self.sid,
900
+ ctypes.c_ulonglong(b),
901
+ ctypes.c_double(ph),
902
+ len(c),
903
+ QrackSimulator._ulonglong_byref(c),
904
+ q,
905
+ )
906
+ self._throw_if_error()
907
+
908
+ def mcexp(self, b, ph, cs, q):
909
+ """Multi-controlled arbitrary exponentiation
910
+
911
+ If all controlled qubits are `|1>` then the target qubit is
912
+ exponentiated an pauli basis basis with coefficient.
913
+
914
+ Args:
915
+ b: Pauli basis
916
+ ph: coefficient of exponentiation.
917
+ q: the qubit number on which the gate is applied to.
918
+
919
+ Raises:
920
+ RuntimeError: QrackSimulator raised an exception.
921
+ """
922
+ if len(b) != len(q):
923
+ raise RuntimeError("Lengths of list parameters are mismatched.")
924
+ Qrack.qrack_lib.MCExp(
925
+ self.sid,
926
+ len(b),
927
+ QrackSimulator._ulonglong_byref(b),
928
+ ctypes.c_double(ph),
929
+ len(cs),
930
+ QrackSimulator._ulonglong_byref(cs),
931
+ QrackSimulator._ulonglong_byref(q),
932
+ )
933
+ self._throw_if_error()
934
+
935
+ def swap(self, qi1, qi2):
936
+ """Swap Gate
937
+
938
+ Swaps the qubits at two given positions.
939
+
940
+ Args:
941
+ qi1: First position of qubit.
942
+ qi2: Second position of qubit.
943
+
944
+ Raises:
945
+ RuntimeError: QrackSimulator raised an exception.
946
+ """
947
+ Qrack.qrack_lib.SWAP(self.sid, qi1, qi2)
948
+ self._throw_if_error()
949
+
950
+ def iswap(self, qi1, qi2):
951
+ """Swap Gate with phase.
952
+
953
+ Swaps the qubits at two given positions.
954
+ If the bits are different then there is additional phase of `i`.
955
+
956
+ Args:
957
+ qi1: First position of qubit.
958
+ qi2: Second position of qubit.
959
+
960
+ Raises:
961
+ RuntimeError: QrackSimulator raised an exception.
962
+ """
963
+ Qrack.qrack_lib.ISWAP(self.sid, qi1, qi2)
964
+ self._throw_if_error()
965
+
966
+ def adjiswap(self, qi1, qi2):
967
+ """Swap Gate with phase.
968
+
969
+ Swaps the qubits at two given positions.
970
+ If the bits are different then there is additional phase of `-i`.
971
+
972
+ Args:
973
+ qi1: First position of qubit.
974
+ qi2: Second position of qubit.
975
+
976
+ Raises:
977
+ RuntimeError: QrackSimulator raised an exception.
978
+ """
979
+ Qrack.qrack_lib.AdjISWAP(self.sid, qi1, qi2)
980
+ self._throw_if_error()
981
+
982
+ def fsim(self, th, ph, qi1, qi2):
983
+ """Fsim gate.
984
+
985
+ The 2-qubit “fSim” gate
986
+ Useful in the simulation of particles with fermionic statistics
987
+
988
+ Args:
989
+ qi1: First position of qubit.
990
+ qi2: Second position of qubit.
991
+
992
+ Raises:
993
+ RuntimeError: QrackSimulator raised an exception.
994
+ """
995
+ Qrack.qrack_lib.FSim(self.sid, ctypes.c_double(th), ctypes.c_double(ph), qi1, qi2)
996
+ self._throw_if_error()
997
+
998
+ def cswap(self, c, qi1, qi2):
999
+ """Controlled-swap Gate
1000
+
1001
+ Swaps the qubits at two given positions if the control qubits are `|1>`
1002
+
1003
+ Args:
1004
+ c: list of controlled qubits.
1005
+ qi1: First position of qubit.
1006
+ qi2: Second position of qubit.
1007
+
1008
+ Raises:
1009
+ RuntimeError: QrackSimulator raised an exception.
1010
+ """
1011
+ Qrack.qrack_lib.CSWAP(self.sid, len(c), QrackSimulator._ulonglong_byref(c), qi1, qi2)
1012
+ self._throw_if_error()
1013
+
1014
+ def acswap(self, c, qi1, qi2):
1015
+ """Anti controlled-swap Gate
1016
+
1017
+ Swaps the qubits at two given positions if the control qubits are `|0>`
1018
+
1019
+ Args:
1020
+ c: list of controlled qubits.
1021
+ qi1: First position of qubit.
1022
+ qi2: Second position of qubit.
1023
+
1024
+ Raises:
1025
+ RuntimeError: QrackSimulator raised an exception.
1026
+ """
1027
+ Qrack.qrack_lib.ACSWAP(self.sid, len(c), QrackSimulator._ulonglong_byref(c), qi1, qi2)
1028
+ self._throw_if_error()
1029
+
1030
+ # standard operations
1031
+ def m(self, q):
1032
+ """Measurement gate
1033
+
1034
+ Measures the qubit at "q" and returns Boolean value.
1035
+ This operator is not unitary & is probabilistic in nature.
1036
+
1037
+ Args:
1038
+ q: qubit to measure
1039
+
1040
+ Raises:
1041
+ RuntimeError: QrackSimulator raised an exception.
1042
+
1043
+ Returns:
1044
+ Measurement result.
1045
+ """
1046
+ result = Qrack.qrack_lib.M(self.sid, q)
1047
+ self._throw_if_error()
1048
+ return result
1049
+
1050
+ def force_m(self, q, r):
1051
+ """Force-Measurement gate
1052
+
1053
+ Acts as if the measurement is applied and the result obtained is `r`
1054
+
1055
+ Args:
1056
+ q: qubit to measure
1057
+ r: the required result
1058
+
1059
+ Raises:
1060
+ RuntimeError: QrackSimulator raised an exception.
1061
+
1062
+ Returns:
1063
+ Measurement result.
1064
+ """
1065
+ result = Qrack.qrack_lib.ForceM(self.sid, q, r)
1066
+ self._throw_if_error()
1067
+ return result
1068
+
1069
+ def m_all(self):
1070
+ """Measure-all gate
1071
+
1072
+ Measures measures all qubits.
1073
+ This operator is not unitary & is probabilistic in nature.
1074
+
1075
+ Raises:
1076
+ RuntimeError: QrackSimulator raised an exception.
1077
+
1078
+ Returns:
1079
+ Measurement result of all qubits.
1080
+ """
1081
+ num_q = self.num_qubits()
1082
+ num_words = (num_q + 63) // 64
1083
+ _r = (ctypes.c_ulonglong * num_words)()
1084
+ Qrack.qrack_lib.MAllLong(self.sid, _r)
1085
+ self._throw_if_error()
1086
+ r = 0
1087
+ for w in range(num_words):
1088
+ r <<= 64
1089
+ r |= _r[w]
1090
+ return r
1091
+
1092
+ def measure_pauli(self, b, q):
1093
+ """Pauli Measurement gate
1094
+
1095
+ Measures the qubit at "q" with the given pauli basis.
1096
+ This operator is not unitary & is probabilistic in nature.
1097
+
1098
+ Args:
1099
+ b: Pauli basis
1100
+ q: qubit to measure
1101
+
1102
+ Raises:
1103
+ RuntimeError: QrackSimulator raised an exception.
1104
+
1105
+ Returns:
1106
+ Measurement result.
1107
+ """
1108
+ if len(b) != len(q):
1109
+ raise RuntimeError("Lengths of list parameters are mismatched.")
1110
+ result = Qrack.qrack_lib.Measure(
1111
+ self.sid, len(b), QrackSimulator._int_byref(b), QrackSimulator._ulonglong_byref(q)
1112
+ )
1113
+ self._throw_if_error()
1114
+ return result
1115
+
1116
+ def measure_shots(self, q, s):
1117
+ """Multi-shot measurement operator
1118
+
1119
+ Measures the qubit at "q" with the given pauli basis.
1120
+ This operator is not unitary & is probabilistic in nature.
1121
+
1122
+ Args:
1123
+ q: list of qubits to measure
1124
+ s: number of shots
1125
+
1126
+ Raises:
1127
+ RuntimeError: QrackSimulator raised an exception.
1128
+
1129
+ Returns:
1130
+ list of measurement result.
1131
+ """
1132
+ m = QrackSimulator._ulonglong_byref([0] * s)
1133
+ Qrack.qrack_lib.MeasureShots(self.sid, len(q), QrackSimulator._ulonglong_byref(q), s, m)
1134
+ self._throw_if_error()
1135
+ return [m[i] for i in range(s)]
1136
+
1137
+ def reset_all(self):
1138
+ """Reset gate
1139
+
1140
+ Resets all qubits to `|0>`
1141
+
1142
+ Raises:
1143
+ RuntimeError: QrackSimulator raised an exception.
1144
+ """
1145
+ Qrack.qrack_lib.ResetAll(self.sid)
1146
+ self._throw_if_error()
1147
+
1148
+ # arithmetic-logic-unit (ALU)
1149
+ @staticmethod
1150
+ def _split_longs(a):
1151
+ """Split operation
1152
+
1153
+ Splits the given integer into 64 bit numbers.
1154
+
1155
+
1156
+ Args:
1157
+ a: number to split
1158
+
1159
+ Raises:
1160
+ RuntimeError: QrackSimulator raised an exception.
1161
+
1162
+ Returns:
1163
+ list of split numbers.
1164
+ """
1165
+ aParts = []
1166
+ if a == 0:
1167
+ aParts.append(0)
1168
+ while a > 0:
1169
+ aParts.append(a & 0xFFFFFFFFFFFFFFFF)
1170
+ a = a >> 64
1171
+ return aParts
1172
+
1173
+ @staticmethod
1174
+ def _split_longs_2(a, m):
1175
+ """Split simultanoues operation
1176
+
1177
+ Splits 2 integers into same number of 64 bit numbers.
1178
+
1179
+ Args:
1180
+ a: first number to split
1181
+ m: second number to split
1182
+
1183
+ Raises:
1184
+ RuntimeError: QrackSimulator raised an exception.
1185
+
1186
+ Returns:
1187
+ pair of lists of split numbers.
1188
+ """
1189
+ aParts = []
1190
+ mParts = []
1191
+ if a == 0 and m == 0:
1192
+ aParts.append(0)
1193
+ mParts.append(0)
1194
+ while a > 0 or m > 0:
1195
+ aParts.append(a & 0xFFFFFFFFFFFFFFFF)
1196
+ a = a >> 64
1197
+ mParts.append(m & 0xFFFFFFFFFFFFFFFF)
1198
+ m = m >> 64
1199
+ return aParts, mParts
1200
+
1201
+ def add(self, a, q):
1202
+ """Add integer to qubit
1203
+
1204
+ Adds the given integer to the given set of qubits.
1205
+
1206
+ Args:
1207
+ a: first number to split
1208
+ q: list of qubits to add the number
1209
+
1210
+ Raises:
1211
+ RuntimeError: QrackSimulator raised an exception.
1212
+ """
1213
+ aParts = QrackSimulator._split_longs(a)
1214
+ Qrack.qrack_lib.ADD(
1215
+ self.sid,
1216
+ len(aParts),
1217
+ QrackSimulator._ulonglong_byref(aParts),
1218
+ len(q),
1219
+ QrackSimulator._ulonglong_byref(q),
1220
+ )
1221
+ self._throw_if_error()
1222
+
1223
+ def sub(self, a, q):
1224
+ """Subtract integer to qubit
1225
+
1226
+ Subtracts the given integer to the given set of qubits.
1227
+
1228
+ Args:
1229
+ a: first number to split
1230
+ q: list of qubits to subtract the number
1231
+
1232
+ Raises:
1233
+ RuntimeError: QrackSimulator raised an exception.
1234
+ """
1235
+ aParts = QrackSimulator._split_longs(a)
1236
+ Qrack.qrack_lib.SUB(
1237
+ self.sid,
1238
+ len(aParts),
1239
+ QrackSimulator._ulonglong_byref(aParts),
1240
+ len(q),
1241
+ QrackSimulator._ulonglong_byref(q),
1242
+ )
1243
+ self._throw_if_error()
1244
+
1245
+ def adds(self, a, s, q):
1246
+ """Signed Addition integer to qubit
1247
+
1248
+ Signed Addition of the given integer to the given set of qubits,
1249
+ if there is an overflow the resultant will become negative.
1250
+
1251
+ Args:
1252
+ a: number to add
1253
+ s: qubit to store overflow
1254
+ q: list of qubits to add the number
1255
+
1256
+ Raises:
1257
+ RuntimeError: QrackSimulator raised an exception.
1258
+ """
1259
+ aParts = QrackSimulator._split_longs(a)
1260
+ Qrack.qrack_lib.ADDS(
1261
+ self.sid,
1262
+ len(aParts),
1263
+ QrackSimulator._ulonglong_byref(aParts),
1264
+ s,
1265
+ len(q),
1266
+ QrackSimulator._ulonglong_byref(q),
1267
+ )
1268
+ self._throw_if_error()
1269
+
1270
+ def subs(self, a, s, q):
1271
+ """Subtract integer to qubit
1272
+
1273
+ Subtracts the given integer to the given set of qubits,
1274
+ if there is an overflow the resultant will become negative.
1275
+
1276
+ Args:
1277
+ a: number to subtract
1278
+ s: qubit to store overflow
1279
+ q: list of qubits to subtract the number
1280
+
1281
+ Raises:
1282
+ RuntimeError: QrackSimulator raised an exception.
1283
+ """
1284
+ aParts = QrackSimulator._split_longs(a)
1285
+ Qrack.qrack_lib.SUBS(
1286
+ self.sid,
1287
+ len(aParts),
1288
+ QrackSimulator._ulonglong_byref(aParts),
1289
+ s,
1290
+ len(q),
1291
+ QrackSimulator._ulonglong_byref(q),
1292
+ )
1293
+ self._throw_if_error()
1294
+
1295
+ def mul(self, a, q, o):
1296
+ """Multiplies integer to qubit
1297
+
1298
+ Multiplies the given integer to the given set of qubits.
1299
+ Carry register is required for maintaining the unitary nature of
1300
+ operation and must be as long as the input qubit register.
1301
+
1302
+ Args:
1303
+ a: number to multiply
1304
+ q: list of qubits to multiply the number
1305
+ o: carry register
1306
+
1307
+ Raises:
1308
+ RuntimeError: QrackSimulator raised an exception.
1309
+ RuntimeError: QrackStabilizer cannot mul()! (Create a QrackSimulator instead.)
1310
+ """
1311
+ if self.is_pure_stabilizer:
1312
+ raise RuntimeError(
1313
+ "QrackStabilizer cannot mul()! (Create a QrackSimulator instead.)"
1314
+ )
1315
+
1316
+ if len(q) != len(o):
1317
+ raise RuntimeError("Lengths of list parameters are mismatched.")
1318
+ aParts = QrackSimulator._split_longs(a)
1319
+ Qrack.qrack_lib.MUL(
1320
+ self.sid,
1321
+ len(aParts),
1322
+ QrackSimulator._ulonglong_byref(aParts),
1323
+ len(q),
1324
+ QrackSimulator._ulonglong_byref(q),
1325
+ QrackSimulator._ulonglong_byref(o),
1326
+ )
1327
+ self._throw_if_error()
1328
+
1329
+ def div(self, a, q, o):
1330
+ """Divides qubit by integer
1331
+
1332
+ 'Divides' the given qubits by the integer.
1333
+ (This is rather the adjoint of mul().)
1334
+ Carry register is required for maintaining the unitary nature of
1335
+ operation.
1336
+
1337
+ Args:
1338
+ a: integer to divide by
1339
+ q: qubits to divide
1340
+ o: carry register
1341
+
1342
+ Raises:
1343
+ RuntimeError: QrackSimulator raised an exception.
1344
+ RuntimeError: QrackStabilizer cannot div()! (Create a QrackSimulator instead.)
1345
+ """
1346
+ if self.is_pure_stabilizer:
1347
+ raise RuntimeError(
1348
+ "QrackStabilizer cannot div()! (Create a QrackSimulator instead.)"
1349
+ )
1350
+
1351
+ if len(q) != len(o):
1352
+ raise RuntimeError("Lengths of list parameters are mismatched.")
1353
+ aParts = QrackSimulator._split_longs(a)
1354
+ Qrack.qrack_lib.DIV(
1355
+ self.sid,
1356
+ len(aParts),
1357
+ QrackSimulator._ulonglong_byref(aParts),
1358
+ len(q),
1359
+ QrackSimulator._ulonglong_byref(q),
1360
+ QrackSimulator._ulonglong_byref(o),
1361
+ )
1362
+ self._throw_if_error()
1363
+
1364
+ def muln(self, a, m, q, o):
1365
+ """Modulo Multiplication
1366
+
1367
+ Modulo Multiplication of the given integer to the given set of qubits
1368
+ Out-of-place register is required to store the resultant.
1369
+
1370
+ Args:
1371
+ a: number to multiply
1372
+ m: modulo number
1373
+ q: list of qubits to multiply the number
1374
+ o: carry register
1375
+
1376
+ Raises:
1377
+ RuntimeError: QrackSimulator raised an exception.
1378
+ """
1379
+ if len(q) != len(o):
1380
+ raise RuntimeError("Lengths of list parameters are mismatched.")
1381
+ aParts, mParts = QrackSimulator._split_longs_2(a, m)
1382
+ Qrack.qrack_lib.MULN(
1383
+ self.sid,
1384
+ len(aParts),
1385
+ QrackSimulator._ulonglong_byref(aParts),
1386
+ QrackSimulator._ulonglong_byref(mParts),
1387
+ len(q),
1388
+ QrackSimulator._ulonglong_byref(q),
1389
+ QrackSimulator._ulonglong_byref(o),
1390
+ )
1391
+ self._throw_if_error()
1392
+
1393
+ def divn(self, a, m, q, o):
1394
+ """Modulo Division
1395
+
1396
+ 'Modulo Division' of the given set of qubits by the given integer
1397
+ (This is rather the adjoint of muln().)
1398
+ Out-of-place register is required to retrieve the resultant.
1399
+
1400
+ Args:
1401
+ a: integer by which qubit will be divided
1402
+ m: modulo integer
1403
+ q: qubits to divide
1404
+ o: carry register
1405
+
1406
+ Raises:
1407
+ RuntimeError: QrackSimulator raised an exception.
1408
+ """
1409
+ if len(q) != len(o):
1410
+ raise RuntimeError("Lengths of list parameters are mismatched.")
1411
+ aParts, mParts = QrackSimulator._split_longs_2(a, m)
1412
+ Qrack.qrack_lib.DIVN(
1413
+ self.sid,
1414
+ len(aParts),
1415
+ QrackSimulator._ulonglong_byref(aParts),
1416
+ QrackSimulator._ulonglong_byref(mParts),
1417
+ len(q),
1418
+ QrackSimulator._ulonglong_byref(q),
1419
+ QrackSimulator._ulonglong_byref(o),
1420
+ )
1421
+ self._throw_if_error()
1422
+
1423
+ def pown(self, a, m, q, o):
1424
+ """Modulo Power
1425
+
1426
+ Raises the qubit to the power `a` to which `mod m` is applied to.
1427
+ Out-of-place register is required to store the resultant.
1428
+
1429
+ Args:
1430
+ a: number in power
1431
+ m: modulo number
1432
+ q: list of qubits to exponentiate
1433
+ o: out-of-place register
1434
+
1435
+ Raises:
1436
+ RuntimeError: QrackSimulator raised an exception.
1437
+ RuntimeError: QrackStabilizer cannot pown()! (Create a QrackSimulator instead.)
1438
+ """
1439
+ if self.is_pure_stabilizer:
1440
+ raise RuntimeError(
1441
+ "QrackStabilizer cannot pown()! (Create a QrackSimulator instead.)"
1442
+ )
1443
+
1444
+ if len(q) != len(o):
1445
+ raise RuntimeError("Lengths of list parameters are mismatched.")
1446
+ aParts, mParts = QrackSimulator._split_longs_2(a, m)
1447
+ Qrack.qrack_lib.POWN(
1448
+ self.sid,
1449
+ len(aParts),
1450
+ QrackSimulator._ulonglong_byref(aParts),
1451
+ QrackSimulator._ulonglong_byref(mParts),
1452
+ len(q),
1453
+ QrackSimulator._ulonglong_byref(q),
1454
+ QrackSimulator._ulonglong_byref(o),
1455
+ )
1456
+ self._throw_if_error()
1457
+
1458
+ def mcadd(self, a, c, q):
1459
+ """Controlled-add
1460
+
1461
+ Adds the given integer to the given set of qubits if all controlled
1462
+ qubits are `|1>`.
1463
+
1464
+ Args:
1465
+ a: number to add.
1466
+ c: list of controlled qubits.
1467
+ q: list of qubits to add the number
1468
+
1469
+ Raises:
1470
+ RuntimeError: QrackSimulator raised an exception.
1471
+ """
1472
+ aParts = QrackSimulator._split_longs(a)
1473
+ Qrack.qrack_lib.MCADD(
1474
+ self.sid,
1475
+ len(aParts),
1476
+ QrackSimulator._ulonglong_byref(aParts),
1477
+ len(c),
1478
+ QrackSimulator._ulonglong_byref(c),
1479
+ len(q),
1480
+ QrackSimulator._ulonglong_byref(q),
1481
+ )
1482
+ self._throw_if_error()
1483
+
1484
+ def mcsub(self, a, c, q):
1485
+ """Controlled-subtract
1486
+
1487
+ Subtracts the given integer to the given set of qubits if all controlled
1488
+ qubits are `|1>`.
1489
+
1490
+ Args:
1491
+ a: number to subtract.
1492
+ c: list of controlled qubits.
1493
+ q: list of qubits to add the number
1494
+
1495
+ Raises:
1496
+ RuntimeError: QrackSimulator raised an exception.
1497
+ """
1498
+ aParts = QrackSimulator._split_longs(a)
1499
+ Qrack.qrack_lib.MCSUB(
1500
+ self.sid,
1501
+ len(aParts),
1502
+ QrackSimulator._ulonglong_byref(aParts),
1503
+ len(c),
1504
+ QrackSimulator._ulonglong_byref(c),
1505
+ len(q),
1506
+ QrackSimulator._ulonglong_byref(q),
1507
+ )
1508
+ self._throw_if_error()
1509
+
1510
+ def mcmul(self, a, c, q, o):
1511
+ """Controlled-multiply
1512
+
1513
+ Multiplies the given integer to the given set of qubits if all controlled
1514
+ qubits are `|1>`.
1515
+ Carry register is required for maintaining the unitary nature of
1516
+ operation.
1517
+
1518
+ Args:
1519
+ a: number to multiply
1520
+ c: list of controlled qubits.
1521
+ q: list of qubits to add the number
1522
+ o: carry register
1523
+
1524
+ Raises:
1525
+ RuntimeError: QrackSimulator raised an exception.
1526
+ RuntimeError: QrackStabilizer cannot mcmul()! (Create a QrackSimulator instead.)
1527
+ """
1528
+ if self.is_pure_stabilizer:
1529
+ raise RuntimeError(
1530
+ "QrackStabilizer cannot mcmul()! (Create a QrackSimulator instead.)"
1531
+ )
1532
+
1533
+ if len(q) != len(o):
1534
+ raise RuntimeError("Lengths of list parameters are mismatched.")
1535
+ aParts = QrackSimulator._split_longs(a)
1536
+ Qrack.qrack_lib.MCMUL(
1537
+ self.sid,
1538
+ len(aParts),
1539
+ QrackSimulator._ulonglong_byref(aParts),
1540
+ len(c),
1541
+ QrackSimulator._ulonglong_byref(c),
1542
+ len(q),
1543
+ QrackSimulator._ulonglong_byref(q),
1544
+ )
1545
+ self._throw_if_error()
1546
+
1547
+ def mcdiv(self, a, c, q, o):
1548
+ """Controlled-divide.
1549
+
1550
+ 'Divides' the given qubits by the integer if all controlled
1551
+ qubits are `|1>`.
1552
+ (This is rather the adjoint of mcmul().)
1553
+ Carry register is required for maintaining the unitary nature of
1554
+ operation.
1555
+
1556
+ Args:
1557
+ a: number to divide by
1558
+ c: list of controlled qubits.
1559
+ q: qubits to divide
1560
+ o: carry register
1561
+
1562
+ Raises:
1563
+ RuntimeError: QrackSimulator raised an exception.
1564
+ RuntimeError: QrackStabilizer cannot mcdiv()! (Create a QrackSimulator instead.)
1565
+ """
1566
+ if self.is_pure_stabilizer:
1567
+ raise RuntimeError(
1568
+ "QrackStabilizer cannot mcdiv()! (Create a QrackSimulator instead.)"
1569
+ )
1570
+
1571
+ if len(q) != len(o):
1572
+ raise RuntimeError("Lengths of list parameters are mismatched.")
1573
+ aParts = QrackSimulator._split_longs(a)
1574
+ Qrack.qrack_lib.MCDIV(
1575
+ self.sid,
1576
+ len(aParts),
1577
+ QrackSimulator._ulonglong_byref(aParts),
1578
+ len(c),
1579
+ QrackSimulator._ulonglong_byref(c),
1580
+ len(q),
1581
+ QrackSimulator._ulonglong_byref(q),
1582
+ )
1583
+ self._throw_if_error()
1584
+
1585
+ def mcmuln(self, a, c, m, q, o):
1586
+ """Controlled-modulo multiplication
1587
+
1588
+ Modulo multiplication of the given integer to the given set of qubits
1589
+ if all controlled qubits are `|1>`.
1590
+ Out-of-place register is required to store the resultant.
1591
+
1592
+ Args:
1593
+ a: number to multiply
1594
+ c: list of controlled qubits.
1595
+ m: modulo number
1596
+ q: list of qubits to add the number
1597
+ o: out-of-place output register
1598
+
1599
+ Raises:
1600
+ RuntimeError: QrackSimulator raised an exception.
1601
+ """
1602
+ if len(q) != len(o):
1603
+ raise RuntimeError("Lengths of list parameters are mismatched.")
1604
+ aParts, mParts = QrackSimulator._split_longs_2(a, m)
1605
+ Qrack.qrack_lib.MCMULN(
1606
+ self.sid,
1607
+ len(aParts),
1608
+ QrackSimulator._ulonglong_byref(aParts),
1609
+ len(c),
1610
+ QrackSimulator._ulonglong_byref(c),
1611
+ QrackSimulator._ulonglong_byref(mParts),
1612
+ len(q),
1613
+ QrackSimulator._ulonglong_byref(q),
1614
+ QrackSimulator._ulonglong_byref(o),
1615
+ )
1616
+ self._throw_if_error()
1617
+
1618
+ def mcdivn(self, a, c, m, q, o):
1619
+ """Controlled-divide.
1620
+
1621
+ Modulo division of the given qubits by the given number if all
1622
+ controlled qubits are `|1>`.
1623
+ (This is rather the adjoint of mcmuln().)
1624
+ Out-of-place register is required to retrieve the resultant.
1625
+
1626
+ Args:
1627
+ a: number to divide by
1628
+ c: list of controlled qubits.
1629
+ m: modulo number
1630
+ q: qubits to divide
1631
+ o: carry register
1632
+
1633
+ Raises:
1634
+ RuntimeError: QrackSimulator raised an exception.
1635
+ """
1636
+ if len(q) != len(o):
1637
+ raise RuntimeError("Lengths of list parameters are mismatched.")
1638
+ aParts, mParts = QrackSimulator._split_longs_2(a, m)
1639
+ Qrack.qrack_lib.MCDIVN(
1640
+ self.sid,
1641
+ len(aParts),
1642
+ QrackSimulator._ulonglong_byref(aParts),
1643
+ len(c),
1644
+ QrackSimulator._ulonglong_byref(c),
1645
+ QrackSimulator._ulonglong_byref(mParts),
1646
+ len(q),
1647
+ QrackSimulator._ulonglong_byref(q),
1648
+ QrackSimulator._ulonglong_byref(o),
1649
+ )
1650
+ self._throw_if_error()
1651
+
1652
+ def mcpown(self, a, c, m, q, o):
1653
+ """Controlled-modulo Power
1654
+
1655
+ Raises the qubit to the power `a` to which `mod m` is applied to if
1656
+ all the controlled qubits are set to `|1>`.
1657
+ Out-of-place register is required to store the resultant.
1658
+
1659
+ Args:
1660
+ a: number in power
1661
+ c: control qubits
1662
+ m: modulo number
1663
+ q: list of qubits to exponentiate
1664
+ o: out-of-place register
1665
+
1666
+ Raises:
1667
+ RuntimeError: QrackSimulator raised an exception.
1668
+ RuntimeError: QrackStabilizer cannot mcpown()! (Create a QrackSimulator instead.)
1669
+ """
1670
+ if self.is_pure_stabilizer:
1671
+ raise RuntimeError(
1672
+ "QrackStabilizer cannot mcpown()! (Create a QrackSimulator instead.)"
1673
+ )
1674
+
1675
+ if len(q) != len(o):
1676
+ raise RuntimeError("Lengths of list parameters are mismatched.")
1677
+ aParts, mParts = QrackSimulator._split_longs_2(a, m)
1678
+ Qrack.qrack_lib.MCPOWN(
1679
+ self.sid,
1680
+ len(aParts),
1681
+ QrackSimulator._ulonglong_byref(aParts),
1682
+ len(c),
1683
+ QrackSimulator._ulonglong_byref(c),
1684
+ QrackSimulator._ulonglong_byref(mParts),
1685
+ len(q),
1686
+ QrackSimulator._ulonglong_byref(q),
1687
+ QrackSimulator._ulonglong_byref(o),
1688
+ )
1689
+ self._throw_if_error()
1690
+
1691
+ def lda(self, qi, qv, t):
1692
+ """Load Accumalator
1693
+
1694
+ Quantum counterpart for LDA from MOS-6502 assembly. `t` must be of
1695
+ the length `2 ** len(qi)`. It loads each list entry index of t into
1696
+ the qi register and each list entry value into the qv register.
1697
+
1698
+ Args:
1699
+ qi: qubit register for index
1700
+ qv: qubit register for value
1701
+ t: list of values
1702
+
1703
+ Raises:
1704
+ RuntimeError: QrackSimulator raised an exception.
1705
+ RuntimeError: QrackStabilizer cannot lda()! (Create a QrackSimulator instead.)
1706
+ """
1707
+ if self.is_pure_stabilizer:
1708
+ raise RuntimeError(
1709
+ "QrackStabilizer cannot lda()! (Create a QrackSimulator instead.)"
1710
+ )
1711
+
1712
+ Qrack.qrack_lib.LDA(
1713
+ self.sid,
1714
+ len(qi),
1715
+ QrackSimulator._ulonglong_byref(qi),
1716
+ len(qv),
1717
+ QrackSimulator._ulonglong_byref(qv),
1718
+ QrackSimulator._to_ubyte(len(qv), t),
1719
+ )
1720
+ self._throw_if_error()
1721
+
1722
+ def adc(self, s, qi, qv, t):
1723
+ """Add with Carry
1724
+
1725
+ Quantum counterpart for ADC from MOS-6502 assembly. `t` must be of
1726
+ the length `2 ** len(qi)`.
1727
+
1728
+ Args:
1729
+ qi: qubit register for index
1730
+ qv: qubit register for value
1731
+ t: list of values
1732
+
1733
+ Raises:
1734
+ RuntimeError: QrackSimulator raised an exception.
1735
+ RuntimeError: QrackStabilizer cannot adc()! (Create a QrackSimulator instead.)
1736
+ """
1737
+ if self.is_pure_stabilizer:
1738
+ raise RuntimeError(
1739
+ "QrackStabilizer cannot adc()! (Create a QrackSimulator instead.)"
1740
+ )
1741
+
1742
+ Qrack.qrack_lib.ADC(
1743
+ self.sid,
1744
+ s,
1745
+ len(qi),
1746
+ QrackSimulator._ulonglong_byref(qi),
1747
+ len(qv),
1748
+ QrackSimulator._ulonglong_byref(qv),
1749
+ QrackSimulator._to_ubyte(len(qv), t),
1750
+ )
1751
+ self._throw_if_error()
1752
+
1753
+ def sbc(self, s, qi, qv, t):
1754
+ """Subtract with Carry
1755
+
1756
+ Quantum counterpart for SBC from MOS-6502 assembly. `t` must be of
1757
+ the length `2 ** len(qi)`
1758
+
1759
+ Args:
1760
+ qi: qubit register for index
1761
+ qv: qubit register for value
1762
+ t: list of values
1763
+
1764
+ Raises:
1765
+ RuntimeError: QrackSimulator raised an exception.
1766
+ RuntimeError: QrackStabilizer cannot sbc()! (Create a QrackSimulator instead.)
1767
+ """
1768
+ if self.is_pure_stabilizer:
1769
+ raise RuntimeError(
1770
+ "QrackStabilizer cannot sbc()! (Create a QrackSimulator instead.)"
1771
+ )
1772
+
1773
+ Qrack.qrack_lib.SBC(
1774
+ self.sid,
1775
+ s,
1776
+ len(qi),
1777
+ QrackSimulator._ulonglong_byref(qi),
1778
+ len(qv),
1779
+ QrackSimulator._ulonglong_byref(qv),
1780
+ QrackSimulator._to_ubyte(len(qv), t),
1781
+ )
1782
+ self._throw_if_error()
1783
+
1784
+ def hash(self, q, t):
1785
+ """Hash function
1786
+
1787
+ Replicates the behaviour of LDA without the index register.
1788
+ For the operation to be unitary, the entries present in `t` must be
1789
+ unique, and the length of `t` must be `2 ** len(qi)`.
1790
+
1791
+
1792
+ Args:
1793
+ q: qubit register for value
1794
+ t: list of values
1795
+
1796
+ Raises:
1797
+ RuntimeError: QrackSimulator raised an exception.
1798
+ RuntimeError: QrackStabilizer cannot hash()! (Create a QrackSimulator instead.)
1799
+ """
1800
+ if self.is_pure_stabilizer:
1801
+ raise RuntimeError(
1802
+ "QrackStabilizer cannot hash()! (Create a QrackSimulator instead.)"
1803
+ )
1804
+
1805
+ Qrack.qrack_lib.Hash(
1806
+ self.sid,
1807
+ len(q),
1808
+ QrackSimulator._ulonglong_byref(q),
1809
+ QrackSimulator._to_ubyte(len(q), t),
1810
+ )
1811
+ self._throw_if_error()
1812
+
1813
+ # boolean logic gates
1814
+ def qand(self, qi1, qi2, qo):
1815
+ """Logical AND
1816
+
1817
+ Logical AND of 2 qubits whose result is stored in the target qubit.
1818
+
1819
+ Args:
1820
+ qi1: qubit 1
1821
+ qi2: qubit 2
1822
+ qo: target qubit
1823
+
1824
+ Raises:
1825
+ RuntimeError: QrackSimulator raised an exception.
1826
+ """
1827
+ Qrack.qrack_lib.AND(self.sid, qi1, qi2, qo)
1828
+ self._throw_if_error()
1829
+
1830
+ def qor(self, qi1, qi2, qo):
1831
+ """Logical OR
1832
+
1833
+ Logical OR of 2 qubits whose result is stored in the target qubit.
1834
+
1835
+ Args:
1836
+ qi1: qubit 1
1837
+ qi2: qubit 2
1838
+ qo: target qubit
1839
+
1840
+ Raises:
1841
+ RuntimeError: QrackSimulator raised an exception.
1842
+ """
1843
+ Qrack.qrack_lib.OR(self.sid, qi1, qi2, qo)
1844
+ self._throw_if_error()
1845
+
1846
+ def qxor(self, qi1, qi2, qo):
1847
+ """Logical XOR
1848
+
1849
+ Logical exlusive-OR of 2 qubits whose result is stored in the target
1850
+ qubit.
1851
+
1852
+ Args:
1853
+ qi1: qubit 1
1854
+ qi2: qubit 2
1855
+ qo: target qubit
1856
+
1857
+ Raises:
1858
+ RuntimeError: QrackSimulator raised an exception.
1859
+ """
1860
+ Qrack.qrack_lib.XOR(self.sid, qi1, qi2, qo)
1861
+ self._throw_if_error()
1862
+
1863
+ def qnand(self, qi1, qi2, qo):
1864
+ """Logical NAND
1865
+
1866
+ Logical NAND of 2 qubits whose result is stored in the target
1867
+ qubit.
1868
+
1869
+ Args:
1870
+ qi1: qubit 1
1871
+ qi2: qubit 2
1872
+ qo: target qubit
1873
+
1874
+ Raises:
1875
+ RuntimeError: QrackSimulator raised an exception.
1876
+ """
1877
+ Qrack.qrack_lib.NAND(self.sid, qi1, qi2, qo)
1878
+ self._throw_if_error()
1879
+
1880
+ def qnor(self, qi1, qi2, qo):
1881
+ """Logical NOR
1882
+
1883
+ Logical NOR of 2 qubits whose result is stored in the target
1884
+ qubit.
1885
+
1886
+ Args:
1887
+ qi1: qubit 1
1888
+ qi2: qubit 2
1889
+ qo: target qubit
1890
+
1891
+ Raises:
1892
+ RuntimeError: QrackSimulator raised an exception.
1893
+ """
1894
+ Qrack.qrack_lib.NOR(self.sid, qi1, qi2, qo)
1895
+ self._throw_if_error()
1896
+
1897
+ def qxnor(self, qi1, qi2, qo):
1898
+ """Logical XOR
1899
+
1900
+ Logical exlusive-NOR of 2 qubits whose result is stored in the target
1901
+ qubit.
1902
+
1903
+ Args:
1904
+ qi1: qubit 1
1905
+ qi2: qubit 2
1906
+ qo: target qubit
1907
+
1908
+ Raises:
1909
+ RuntimeError: QrackSimulator raised an exception.
1910
+ """
1911
+ Qrack.qrack_lib.XNOR(self.sid, qi1, qi2, qo)
1912
+ self._throw_if_error()
1913
+
1914
+ def cland(self, ci, qi, qo):
1915
+ """Classical AND
1916
+
1917
+ Logical AND with one qubit and one classical bit whose result is
1918
+ stored in target qubit.
1919
+
1920
+ Args:
1921
+ qi1: qubit 1
1922
+ qi2: qubit 2
1923
+ qo: target qubit
1924
+
1925
+ Raises:
1926
+ RuntimeError: QrackSimulator raised an exception.
1927
+ """
1928
+ Qrack.qrack_lib.CLAND(self.sid, ci, qi, qo)
1929
+ self._throw_if_error()
1930
+
1931
+ def clor(self, ci, qi, qo):
1932
+ """Classical OR
1933
+
1934
+ Logical OR with one qubit and one classical bit whose result is
1935
+ stored in target qubit.
1936
+
1937
+ Args:
1938
+ qi1: qubit 1
1939
+ qi2: qubit 2
1940
+ qo: target qubit
1941
+
1942
+ Raises:
1943
+ RuntimeError: QrackSimulator raised an exception.
1944
+ """
1945
+ Qrack.qrack_lib.CLOR(self.sid, ci, qi, qo)
1946
+ self._throw_if_error()
1947
+
1948
+ def clxor(self, ci, qi, qo):
1949
+ """Classical XOR
1950
+
1951
+ Logical exlusive-OR with one qubit and one classical bit whose result is
1952
+ stored in target qubit.
1953
+
1954
+ Args:
1955
+ qi1: qubit 1
1956
+ qi2: qubit 2
1957
+ qo: target qubit
1958
+
1959
+ Raises:
1960
+ RuntimeError: QrackSimulator raised an exception.
1961
+ """
1962
+ Qrack.qrack_lib.CLXOR(self.sid, ci, qi, qo)
1963
+ self._throw_if_error()
1964
+
1965
+ def clnand(self, ci, qi, qo):
1966
+ """Classical NAND
1967
+
1968
+ Logical NAND with one qubit and one classical bit whose result is
1969
+ stored in target qubit.
1970
+
1971
+ Args:
1972
+ qi1: qubit 1
1973
+ qi2: qubit 2
1974
+ qo: target qubit
1975
+
1976
+ Raises:
1977
+ RuntimeError: QrackSimulator raised an exception.
1978
+ """
1979
+ Qrack.qrack_lib.CLNAND(self.sid, ci, qi, qo)
1980
+ self._throw_if_error()
1981
+
1982
+ def clnor(self, ci, qi, qo):
1983
+ """Classical NOR
1984
+
1985
+ Logical NOR with one qubit and one classical bit whose result is
1986
+ stored in target qubit.
1987
+
1988
+ Args:
1989
+ qi1: qubit 1
1990
+ qi2: qubit 2
1991
+ qo: target qubit
1992
+
1993
+ Raises:
1994
+ RuntimeError: QrackSimulator raised an exception.
1995
+ """
1996
+ Qrack.qrack_lib.CLNOR(self.sid, ci, qi, qo)
1997
+ self._throw_if_error()
1998
+
1999
+ def clxnor(self, ci, qi, qo):
2000
+ """Classical XNOR
2001
+
2002
+ Logical exlusive-NOR with one qubit and one classical bit whose result is
2003
+ stored in target qubit.
2004
+
2005
+ Args:
2006
+ qi1: qubit 1
2007
+ qi2: qubit 2
2008
+ qo: target qubit
2009
+
2010
+ Raises:
2011
+ RuntimeError: QrackSimulator raised an exception.
2012
+ """
2013
+ Qrack.qrack_lib.CLXNOR(self.sid, ci, qi, qo)
2014
+ self._throw_if_error()
2015
+
2016
+ # Particular Quantum Circuits
2017
+
2018
+ ## fourier transform
2019
+ def qft(self, qs):
2020
+ """Quantum Fourier Transform
2021
+
2022
+ Applies Quantum Fourier Transform on the list of qubits provided.
2023
+
2024
+ Args:
2025
+ qs: list of qubits
2026
+
2027
+ Raises:
2028
+ RuntimeError: QrackSimulator raised an exception.
2029
+ """
2030
+ Qrack.qrack_lib.QFT(self.sid, len(qs), QrackSimulator._ulonglong_byref(qs))
2031
+ self._throw_if_error()
2032
+
2033
+ def iqft(self, qs):
2034
+ """Inverse-quantum Fourier Transform
2035
+
2036
+ Applies Inverse-quantum Fourier Transform on the list of qubits
2037
+ provided.
2038
+
2039
+ Args:
2040
+ qs: list of qubits
2041
+
2042
+ Raises:
2043
+ RuntimeError: QrackSimulator raised an exception.
2044
+ """
2045
+ Qrack.qrack_lib.IQFT(self.sid, len(qs), QrackSimulator._ulonglong_byref(qs))
2046
+ self._throw_if_error()
2047
+
2048
+ # pseudo-quantum
2049
+
2050
+ ## allocate and release
2051
+ def allocate_qubit(self, qid):
2052
+ """Allocate Qubit
2053
+
2054
+ Allocate 1 new qubit with the given qubit ID.
2055
+
2056
+ Args:
2057
+ qid: qubit id
2058
+
2059
+ Raises:
2060
+ RuntimeError: QrackSimulator raised an exception.
2061
+ """
2062
+ Qrack.qrack_lib.allocateQubit(self.sid, qid)
2063
+ self._throw_if_error()
2064
+
2065
+ def release(self, q):
2066
+ """Release Qubit
2067
+
2068
+ Release qubit given by the given qubit ID.
2069
+
2070
+ Args:
2071
+ q: qubit id
2072
+
2073
+ Raises:
2074
+ RuntimeError: QrackSimulator raised an exception.
2075
+
2076
+ Returns:
2077
+ If the qubit was in `|0>` state with small tolerance.
2078
+ """
2079
+ result = Qrack.qrack_lib.release(self.sid, q)
2080
+ self._throw_if_error()
2081
+ return result
2082
+
2083
+ def num_qubits(self):
2084
+ """Get Qubit count
2085
+
2086
+ Returns the qubit count of the simulator.
2087
+
2088
+ Args:
2089
+ q: qubit id
2090
+
2091
+ Raises:
2092
+ RuntimeError: QrackSimulator raised an exception.
2093
+
2094
+ Returns:
2095
+ Qubit count of the simulator
2096
+ """
2097
+ result = Qrack.qrack_lib.num_qubits(self.sid)
2098
+ self._throw_if_error()
2099
+ return result
2100
+
2101
+ ## schmidt decomposition
2102
+ def compose(self, other, q):
2103
+ """Compose qubits
2104
+
2105
+ Compose quantum description of given qubit with the current system.
2106
+
2107
+ Args:
2108
+ q: qubit id
2109
+
2110
+ Raises:
2111
+ RuntimeError: QrackSimulator raised an exception.
2112
+ """
2113
+ Qrack.qrack_lib.Compose(self.sid, other.sid, QrackSimulator._ulonglong_byref(q))
2114
+ self._throw_if_error()
2115
+
2116
+ def decompose(self, q):
2117
+ """Decompose system
2118
+
2119
+ Factorize a set of contiguous bits with minimal fidelity loss.
2120
+
2121
+ Args:
2122
+ q: qubit id
2123
+
2124
+ Raises:
2125
+ RuntimeError: QrackSimulator raised an exception.
2126
+
2127
+ Returns:
2128
+ Decomposed subsystem simulator.
2129
+ """
2130
+ other = QrackSimulator()
2131
+ Qrack.qrack_lib.destroy(other.sid)
2132
+ l = len(q)
2133
+ other.sid = Qrack.qrack_lib.Decompose(self.sid, l, QrackSimulator._ulonglong_byref(q))
2134
+ self._throw_if_error()
2135
+ return other
2136
+
2137
+ def dispose(self, q):
2138
+ """Dispose qubits
2139
+
2140
+ Factorize a set of contiguous bits with minimal fidelity loss,
2141
+ and discard the separable bits.
2142
+
2143
+ Args:
2144
+ q: qubit
2145
+
2146
+ Raises:
2147
+ RuntimeError: QrackSimulator raised an exception.
2148
+ """
2149
+ l = len(q)
2150
+ Qrack.qrack_lib.Dispose(self.sid, l, QrackSimulator._ulonglong_byref(q))
2151
+ self._throw_if_error()
2152
+
2153
+ ## miscellaneous
2154
+ def dump_ids(self):
2155
+ """Dump all IDs
2156
+
2157
+ Dump all IDs from the selected simulator ID into the callback.
2158
+
2159
+ Returns:
2160
+ List of ids
2161
+ """
2162
+ global ids_list
2163
+ global ids_list_index
2164
+ ids_list = [0] * self.num_qubits()
2165
+ ids_list_index = 0
2166
+ Qrack.qrack_lib.DumpIds(self.sid, self.dump_ids_callback)
2167
+ return ids_list
2168
+
2169
+ @ctypes.CFUNCTYPE(None, ctypes.c_ulonglong)
2170
+ def dump_ids_callback(i):
2171
+ """C callback function"""
2172
+ global ids_list
2173
+ global ids_list_index
2174
+ ids_list[ids_list_index] = i
2175
+ ids_list_index = ids_list_index + 1
2176
+
2177
+ def dump(self):
2178
+ """Dump state vector
2179
+
2180
+ Dump state vector from the selected simulator ID into the callback.
2181
+
2182
+ Returns:
2183
+ State vector list
2184
+ """
2185
+ global state_vec_list
2186
+ global state_vec_list_index
2187
+ global state_vec_probability
2188
+ state_vec_list = [complex(0, 0)] * (1 << self.num_qubits())
2189
+ state_vec_list_index = 0
2190
+ state_vec_probability = 0
2191
+ Qrack.qrack_lib.Dump(self.sid, self.dump_callback)
2192
+ return state_vec_list
2193
+
2194
+ @ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_double, ctypes.c_double)
2195
+ def dump_callback(r, i):
2196
+ """C callback function"""
2197
+ global state_vec_list
2198
+ global state_vec_list_index
2199
+ global state_vec_probability
2200
+ state_vec_list[state_vec_list_index] = complex(r, i)
2201
+ state_vec_list_index = state_vec_list_index + 1
2202
+ state_vec_probability = state_vec_probability + (r * r) + (i * i)
2203
+ if (1.0 - state_vec_probability) <= (7.0 / 3 - 4.0 / 3 - 1):
2204
+ return False
2205
+ return True
2206
+
2207
+ def in_ket(self, ket):
2208
+ """Set state vector
2209
+
2210
+ Set state vector for the selected simulator ID.
2211
+ Warning: State vector is not always the internal representation leading
2212
+ to sub-optimal performance of the method.
2213
+
2214
+ Args:
2215
+ ket: the state vector to which simulator will be set
2216
+
2217
+ Raises:
2218
+ RuntimeError: QrackSimulator raised an exception.
2219
+ """
2220
+ Qrack.qrack_lib.InKet(self.sid, QrackSimulator._qrack_complex_byref(ket))
2221
+ self._throw_if_error()
2222
+
2223
+ def out_ket(self):
2224
+ """Get state vector
2225
+
2226
+ Returns the raw state vector of the simulator.
2227
+ Warning: State vector is not always the internal representation leading
2228
+ to sub-optimal performance of the method.
2229
+
2230
+ Raises:
2231
+ RuntimeError: QrackSimulator raised an exception.
2232
+
2233
+ Returns:
2234
+ list representing the state vector.
2235
+ """
2236
+ amp_count = 1 << self.num_qubits()
2237
+ ket = QrackSimulator._qrack_complex_byref([complex(0, 0)] * amp_count)
2238
+ Qrack.qrack_lib.OutKet(self.sid, ket)
2239
+ self._throw_if_error()
2240
+ return [complex(r, i) for r, i in QrackSimulator._pairwise(ket)]
2241
+
2242
+ def out_probs(self):
2243
+ """Get basis dimension probabilities
2244
+
2245
+ Returns the probabilities of each basis dimension in the state vector
2246
+ of the simulator.
2247
+
2248
+ Raises:
2249
+ RuntimeError: QrackSimulator raised an exception.
2250
+
2251
+ Returns:
2252
+ list representing the basis dimension probabilities.
2253
+ """
2254
+ prob_count = 1 << self.num_qubits()
2255
+ probs = QrackSimulator._real1_byref([0.0] * prob_count)
2256
+ Qrack.qrack_lib.OutProbs(self.sid, probs)
2257
+ self._throw_if_error()
2258
+ return list(probs)
2259
+
2260
+ def out_rdm(self, q):
2261
+ """Get reduced density matrix
2262
+
2263
+ Returns the raw reduced density matrix of the simulator, for the qubit list.
2264
+ Warning: State vector or is not always the internal representation leading
2265
+ to sub-optimal performance of the method.
2266
+
2267
+ Raises:
2268
+ RuntimeError: QrackSimulator raised an exception.
2269
+
2270
+ Returns:
2271
+ flat list structure representing the reduced density matrix.
2272
+ """
2273
+ amp_count = 1 << len(q)
2274
+ sqr_amp_count = amp_count * amp_count
2275
+ flat_rdm = QrackSimulator._qrack_complex_byref([complex(0, 0)] * sqr_amp_count)
2276
+ Qrack.qrack_lib.OutReducedDensityMatrix(
2277
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), flat_rdm
2278
+ )
2279
+ self._throw_if_error()
2280
+ return [complex(r, i) for r, i in QrackSimulator._pairwise(flat_rdm)]
2281
+
2282
+ def highest_prob_perm(self):
2283
+ """Get the permutation (bit string) with the highest probability
2284
+
2285
+ Returns the single highest-probability bit string in the Hilbert space
2286
+
2287
+ Raises:
2288
+ RuntimeError: QrackSimulator raised an exception.
2289
+
2290
+ Returns:
2291
+ Highest probability dimension index
2292
+ """
2293
+ num_q = self.num_qubits()
2294
+ num_words = (num_q + 63) // 64
2295
+ _r = (ctypes.c_ulonglong * num_words)()
2296
+ Qrack.qrack_lib.HighestProbAll(self.sid, _r)
2297
+ self._throw_if_error()
2298
+ r = 0
2299
+ for w in range(num_words):
2300
+ r <<= 64
2301
+ r |= _r[w]
2302
+ return r
2303
+
2304
+ def highest_n_prob_perm(self, n):
2305
+ """Get the top n permutations (bit strings) with the highest probability
2306
+
2307
+ Returns the top n highest-probability bit strings in the Hilbert space
2308
+
2309
+ Raises:
2310
+ RuntimeError: QrackSimulator raised an exception.
2311
+
2312
+ Returns:
2313
+ Top n highest probability dimension indices
2314
+ """
2315
+ num_q = self.num_qubits()
2316
+ num_words = (num_q + 63) // 64
2317
+ _r = (ctypes.c_ulonglong * (num_words * n))()
2318
+ Qrack.qrack_lib.HighestProbAllN(self.sid, n, _r)
2319
+ self._throw_if_error()
2320
+ r = [0] * n
2321
+ for i in range(n):
2322
+ r[i] = 0
2323
+ for w in range(num_words):
2324
+ r[i] <<= 64
2325
+ r[i] |= _r[(i * num_words) + w]
2326
+ return r
2327
+
2328
+ def prob_all(self, q):
2329
+ """Probabilities of all subset permutations
2330
+
2331
+ Get the probabilities of all permutations of the subset.
2332
+
2333
+ Args:
2334
+ q: list of qubit ids
2335
+
2336
+ Raises:
2337
+ RuntimeError: QrackSimulator raised an exception.
2338
+
2339
+ Returns:
2340
+ list representing the state vector.
2341
+ """
2342
+ probs = QrackSimulator._real1_byref([0.0] * (1 << len(q)))
2343
+ Qrack.qrack_lib.ProbAll(self.sid, len(q), QrackSimulator._ulonglong_byref(q), probs)
2344
+ self._throw_if_error()
2345
+ return list(probs)
2346
+
2347
+ def prob(self, q):
2348
+ """Probability of `|1>`
2349
+
2350
+ Get the probability that a qubit is in the `|1>` state.
2351
+
2352
+ Args:
2353
+ q: qubit id
2354
+
2355
+ Raises:
2356
+ RuntimeError: QrackSimulator raised an exception.
2357
+
2358
+ Returns:
2359
+ probability of qubit being in `|1>`
2360
+ """
2361
+ result = Qrack.qrack_lib.Prob(self.sid, q)
2362
+ self._throw_if_error()
2363
+ return result
2364
+
2365
+ def prob_rdm(self, q):
2366
+ """Probability of `|1>`, (tracing out the reduced
2367
+ density matrix without stabilizer ancillary qubits)
2368
+
2369
+ Get the probability that a qubit is in the `|1>` state.
2370
+
2371
+ Args:
2372
+ q: qubit id
2373
+
2374
+ Raises:
2375
+ RuntimeError: QrackSimulator raised an exception.
2376
+
2377
+ Returns:
2378
+ probability of qubit being in `|1>`
2379
+ """
2380
+ result = Qrack.qrack_lib.ProbRdm(self.sid, q)
2381
+ self._throw_if_error()
2382
+ return result
2383
+
2384
+ def prob_perm(self, q, c):
2385
+ """Probability of permutation
2386
+
2387
+ Get the probability that the qubit IDs in "q" have the truth values
2388
+ in "c", directly corresponding by list index.
2389
+
2390
+ Args:
2391
+ q: list of qubit ids
2392
+ c: list of qubit truth values bools
2393
+
2394
+ Raises:
2395
+ RuntimeError: QrackSimulator raised an exception.
2396
+
2397
+ Returns:
2398
+ probability that each qubit in "q[i]" has corresponding truth
2399
+ value in "c[i]", at once
2400
+ """
2401
+
2402
+ if len(q) != len(c):
2403
+ raise RuntimeError("prob_perm argument lengths do not match.")
2404
+ result = Qrack.qrack_lib.PermutationProb(
2405
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._bool_byref(c)
2406
+ )
2407
+ self._throw_if_error()
2408
+ return result
2409
+
2410
+ def prob_perm_rdm(self, q, c, r=True):
2411
+ """Probability of permutation, (tracing out the reduced
2412
+ density matrix without stabilizer ancillary qubits)
2413
+
2414
+ Get the probability that the qubit IDs in "q" have the truth
2415
+ values in "c", directly corresponding by list index.
2416
+
2417
+ Args:
2418
+ q: list of qubit ids
2419
+ c: list of qubit truth values bools
2420
+ r: round Rz gates down from T^(1/2)
2421
+
2422
+ Raises:
2423
+ RuntimeError: QrackSimulator raised an exception.
2424
+
2425
+ Returns:
2426
+ probability that each qubit in "q[i]" has corresponding truth
2427
+ value in "c[i]", at once
2428
+ """
2429
+
2430
+ if len(q) != len(c):
2431
+ raise RuntimeError("prob_perm argument lengths do not match.")
2432
+ result = Qrack.qrack_lib.PermutationProbRdm(
2433
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._bool_byref(c), r
2434
+ )
2435
+ self._throw_if_error()
2436
+ return result
2437
+
2438
+ def permutation_expectation(self, q):
2439
+ """Permutation expectation value
2440
+
2441
+ Get the permutation expectation value, based upon the order of
2442
+ input qubits.
2443
+
2444
+ Args:
2445
+ q: qubits, from low to high
2446
+
2447
+ Raises:
2448
+ RuntimeError: QrackSimulator raised an exception.
2449
+
2450
+ Returns:
2451
+ Expectation value
2452
+ """
2453
+ result = Qrack.qrack_lib.PermutationExpectation(
2454
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q)
2455
+ )
2456
+ self._throw_if_error()
2457
+ return result
2458
+
2459
+ def permutation_expectation_rdm(self, q, r=True):
2460
+ """Permutation expectation value, (tracing out the reduced
2461
+ density matrix without stabilizer ancillary qubits)
2462
+
2463
+ Get the permutation expectation value, based upon the order of
2464
+ input qubits.
2465
+
2466
+ Args:
2467
+ q: qubits, from low to high
2468
+ r: round Rz gates down from T^(1/2)
2469
+
2470
+ Raises:
2471
+ RuntimeError: QrackSimulator raised an exception.
2472
+
2473
+ Returns:
2474
+ Expectation value
2475
+ """
2476
+ result = Qrack.qrack_lib.PermutationExpectationRdm(
2477
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), r
2478
+ )
2479
+ self._throw_if_error()
2480
+ return result
2481
+
2482
+ def factorized_expectation(self, q, c):
2483
+ """Factorized expectation value
2484
+
2485
+ Get the factorized expectation value, where each entry
2486
+ in "c" is an expectation value for corresponding "q"
2487
+ being false, then true, repeated for each in "q".
2488
+
2489
+ Args:
2490
+ q: qubits, from low to high
2491
+ c: qubit falsey/truthy values, from low to high
2492
+
2493
+ Raises:
2494
+ RuntimeError: QrackSimulator raised an exception.
2495
+
2496
+ Returns:
2497
+ Expectation value
2498
+ """
2499
+ if (len(q) << 1) != len(c):
2500
+ raise RuntimeError("factorized_expectation argument lengths do not match.")
2501
+ m = max([(x.bit_length() + 63) // 64 for x in c])
2502
+ result = Qrack.qrack_lib.FactorizedExpectation(
2503
+ self.sid,
2504
+ len(q),
2505
+ QrackSimulator._ulonglong_byref(q),
2506
+ m,
2507
+ QrackSimulator._to_ulonglong(m, c),
2508
+ )
2509
+ self._throw_if_error()
2510
+ return result
2511
+
2512
+ def factorized_expectation_rdm(self, q, c, r=True):
2513
+ """Factorized expectation value, (tracing out the reduced
2514
+ density matrix without stabilizer ancillary qubits)
2515
+
2516
+ Get the factorized expectation value, where each entry
2517
+ in "c" is an expectation value for corresponding "q"
2518
+ being false, then true, repeated for each in "q".
2519
+
2520
+ Args:
2521
+ q: qubits, from low to high
2522
+ c: qubit falsey/truthy values, from low to high
2523
+ r: round Rz gates down from T^(1/2)
2524
+
2525
+ Raises:
2526
+ RuntimeError: QrackSimulator raised an exception.
2527
+
2528
+ Returns:
2529
+ Expectation value
2530
+ """
2531
+ if (len(q) << 1) != len(c):
2532
+ raise RuntimeError("factorized_expectation_rdm argument lengths do not match.")
2533
+ m = max([(x.bit_length() + 63) // 64 for x in c])
2534
+ result = Qrack.qrack_lib.FactorizedExpectationRdm(
2535
+ self.sid,
2536
+ len(q),
2537
+ QrackSimulator._ulonglong_byref(q),
2538
+ m,
2539
+ QrackSimulator._to_ulonglong(m, c),
2540
+ r,
2541
+ )
2542
+ self._throw_if_error()
2543
+ return result
2544
+
2545
+ def factorized_expectation_fp(self, q, c):
2546
+ """Factorized expectation value (floating-point)
2547
+
2548
+ Get the factorized expectation value, where each entry
2549
+ in "c" is an expectation value for corresponding "q"
2550
+ being false, then true, repeated for each in "q".
2551
+
2552
+ Args:
2553
+ q: qubits, from low to high
2554
+ c: qubit falsey/truthy values, from low to high
2555
+
2556
+ Raises:
2557
+ RuntimeError: QrackSimulator raised an exception.
2558
+
2559
+ Returns:
2560
+ Expectation value
2561
+ """
2562
+ if (len(q) << 1) != len(c):
2563
+ raise RuntimeError("factorized_expectation_rdm argument lengths do not match.")
2564
+ result = Qrack.qrack_lib.FactorizedExpectationFp(
2565
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._real1_byref(c)
2566
+ )
2567
+ self._throw_if_error()
2568
+ return result
2569
+
2570
+ def factorized_expectation_fp_rdm(self, q, c, r=True):
2571
+ """Factorized expectation value, (tracing out the reduced
2572
+ density matrix without stabilizer ancillary qubits)
2573
+
2574
+ Get the factorized expectation value, where each entry
2575
+ in "c" is an expectation value for corresponding "q"
2576
+ being false, then true, repeated for each in "q".
2577
+
2578
+ Args:
2579
+ q: qubits, from low to high
2580
+ c: qubit falsey/truthy values, from low to high
2581
+ r: round Rz gates down from T^(1/2)
2582
+
2583
+ Raises:
2584
+ RuntimeError: QrackSimulator raised an exception.
2585
+
2586
+ Returns:
2587
+ Expectation value
2588
+ """
2589
+ if (len(q) << 1) != len(c):
2590
+ raise RuntimeError("factorized_expectation_fp_rdm argument lengths do not match.")
2591
+ result = Qrack.qrack_lib.FactorizedExpectationFpRdm(
2592
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._real1_byref(c), r
2593
+ )
2594
+ self._throw_if_error()
2595
+ return result
2596
+
2597
+ def unitary_expectation(self, q, b):
2598
+ """3-parameter unitary tensor product expectation value
2599
+
2600
+ Get the single-qubit (3-parameter) operator
2601
+ expectation value for the array of qubits and bases.
2602
+
2603
+ Args:
2604
+ q: qubits, from low to high
2605
+ b: 3-parameter, single-qubit, unitary bases (flat over wires)
2606
+
2607
+ Raises:
2608
+ RuntimeError: QrackSimulator raised an exception.
2609
+
2610
+ Returns:
2611
+ Expectation value
2612
+ """
2613
+ if (3 * len(q)) != len(b):
2614
+ raise RuntimeError("unitary_expectation argument lengths do not match.")
2615
+ result = Qrack.qrack_lib.UnitaryExpectation(
2616
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._real1_byref(b)
2617
+ )
2618
+ self._throw_if_error()
2619
+ return result
2620
+
2621
+ def matrix_expectation(self, q, b):
2622
+ """Single-qubit operator tensor product expectation value
2623
+
2624
+ Get the single-qubit (3-parameter) operator
2625
+ expectation value for the array of qubits and bases.
2626
+
2627
+ Args:
2628
+ q: qubits, from low to high
2629
+ b: single-qubit (2x2) operator unitary bases (flat over wires)
2630
+
2631
+ Raises:
2632
+ RuntimeError: QrackSimulator raised an exception.
2633
+
2634
+ Returns:
2635
+ Expectation value
2636
+ """
2637
+ if (len(q) << 2) != len(b):
2638
+ raise RuntimeError("matrix_expectation argument lengths do not match.")
2639
+ result = Qrack.qrack_lib.MatrixExpectation(
2640
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._complex_byref(b)
2641
+ )
2642
+ self._throw_if_error()
2643
+ return result
2644
+
2645
+ def unitary_expectation_eigenval(self, q, b, e):
2646
+ """3-parameter unitary tensor product expectation value
2647
+
2648
+ Get the single-qubit (3-parameter) operator
2649
+ expectation value for the array of qubits and bases.
2650
+
2651
+ Args:
2652
+ q: qubits, from low to high
2653
+ b: 3-parameter, single-qubit, unitary bases (flat over wires)
2654
+
2655
+ Raises:
2656
+ RuntimeError: QrackSimulator raised an exception.
2657
+
2658
+ Returns:
2659
+ Expectation value
2660
+ """
2661
+ if (3 * len(q)) != len(b):
2662
+ raise RuntimeError(
2663
+ "unitary_expectation_eigenval qubit and basis argument lengths do not match."
2664
+ )
2665
+ if (len(q) << 1) != len(e):
2666
+ raise RuntimeError(
2667
+ "unitary_expectation_eigenval qubit and eigenvalue argument lengths do not match."
2668
+ )
2669
+ result = Qrack.qrack_lib.UnitaryExpectationEigenVal(
2670
+ self.sid,
2671
+ len(q),
2672
+ QrackSimulator._ulonglong_byref(q),
2673
+ QrackSimulator._real1_byref(b),
2674
+ QrackSimulator._real1_byref(e),
2675
+ )
2676
+ self._throw_if_error()
2677
+ return result
2678
+
2679
+ def matrix_expectation_eigenval(self, q, b, e):
2680
+ """Single-qubit operator tensor product expectation value
2681
+
2682
+ Get the single-qubit (3-parameter) operator
2683
+ expectation value for the array of qubits and bases.
2684
+
2685
+ Args:
2686
+ q: qubits, from low to high
2687
+ b: single-qubit (2x2) operator unitary bases (flat over wires)
2688
+
2689
+ Raises:
2690
+ RuntimeError: QrackSimulator raised an exception.
2691
+
2692
+ Returns:
2693
+ Expectation value
2694
+ """
2695
+ if (len(q) << 2) != len(b):
2696
+ raise RuntimeError(
2697
+ "matrix_expectation_eigenval qubit and basis argument lengths do not match."
2698
+ )
2699
+ if (len(q) << 1) != len(e):
2700
+ raise RuntimeError(
2701
+ "matrix_expectation_eigenval qubit and eigenvalue argument lengths do not match."
2702
+ )
2703
+ result = Qrack.qrack_lib.MatrixExpectationEigenVal(
2704
+ self.sid,
2705
+ len(q),
2706
+ QrackSimulator._ulonglong_byref(q),
2707
+ QrackSimulator._complex_byref(b),
2708
+ QrackSimulator._real1_byref(e),
2709
+ )
2710
+ self._throw_if_error()
2711
+ return result
2712
+
2713
+ def pauli_expectation(self, q, b):
2714
+ """Pauli tensor product expectation value
2715
+
2716
+ Get the Pauli tensor product expectation value,
2717
+ where each entry in "b" is a Pauli observable for
2718
+ corresponding "q", as the product for each in "q".
2719
+
2720
+ Args:
2721
+ q: qubits, from low to high
2722
+ b: qubit Pauli bases
2723
+
2724
+ Raises:
2725
+ RuntimeError: QrackSimulator raised an exception.
2726
+
2727
+ Returns:
2728
+ Expectation value
2729
+ """
2730
+ if len(q) != len(b):
2731
+ raise RuntimeError("pauli_expectation argument lengths do not match.")
2732
+ result = Qrack.qrack_lib.PauliExpectation(
2733
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._ulonglong_byref(b)
2734
+ )
2735
+ self._throw_if_error()
2736
+ return result
2737
+
2738
+ def variance(self, q):
2739
+ """Variance of probabilities of all subset permutations
2740
+
2741
+ Get the overall variance of probabilities of all
2742
+ permutations of the subset.
2743
+
2744
+ Args:
2745
+ q: list of qubit ids
2746
+
2747
+ Raises:
2748
+ RuntimeError: QrackSimulator raised an exception.
2749
+
2750
+ Returns:
2751
+ float variance
2752
+ """
2753
+ result = Qrack.qrack_lib.Variance(self.sid, len(q), QrackSimulator._ulonglong_byref(q))
2754
+ self._throw_if_error()
2755
+ return result
2756
+
2757
+ def variance_rdm(self, q, r=True):
2758
+ """Permutation variance, (tracing out the reduced
2759
+ density matrix without stabilizer ancillary qubits)
2760
+
2761
+ Get the permutation variance, based upon the order of
2762
+ input qubits.
2763
+
2764
+ Args:
2765
+ q: qubits, from low to high
2766
+ r: round Rz gates down from T^(1/2)
2767
+
2768
+ Raises:
2769
+ RuntimeError: QrackSimulator raised an exception.
2770
+
2771
+ Returns:
2772
+ variance
2773
+ """
2774
+ result = Qrack.qrack_lib.VarianceRdm(
2775
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), r
2776
+ )
2777
+ self._throw_if_error()
2778
+ return result
2779
+
2780
+ def factorized_variance(self, q, c):
2781
+ """Factorized variance
2782
+
2783
+ Get the factorized variance, where each entry
2784
+ in "c" is an variance for corresponding "q"
2785
+ being false, then true, repeated for each in "q".
2786
+
2787
+ Args:
2788
+ q: qubits, from low to high
2789
+ c: qubit falsey/truthy values, from low to high
2790
+
2791
+ Raises:
2792
+ RuntimeError: QrackSimulator raised an exception.
2793
+
2794
+ Returns:
2795
+ variance
2796
+ """
2797
+ if (len(q) << 1) != len(c):
2798
+ raise RuntimeError("factorized_variance argument lengths do not match.")
2799
+ m = max([(x.bit_length() + 63) // 64 for x in c])
2800
+ result = Qrack.qrack_lib.FactorizedVariance(
2801
+ self.sid,
2802
+ len(q),
2803
+ QrackSimulator._ulonglong_byref(q),
2804
+ m,
2805
+ QrackSimulator._to_ulonglong(m, c),
2806
+ )
2807
+ self._throw_if_error()
2808
+ return result
2809
+
2810
+ def factorized_variance_rdm(self, q, c, r=True):
2811
+ """Factorized variance, (tracing out the reduced
2812
+ density matrix without stabilizer ancillary qubits)
2813
+
2814
+ Get the factorized variance, where each entry
2815
+ in "c" is an variance for corresponding "q"
2816
+ being false, then true, repeated for each in "q".
2817
+
2818
+ Args:
2819
+ q: qubits, from low to high
2820
+ c: qubit falsey/truthy values, from low to high
2821
+ r: round Rz gates down from T^(1/2)
2822
+
2823
+ Raises:
2824
+ RuntimeError: QrackSimulator raised an exception.
2825
+
2826
+ Returns:
2827
+ variance
2828
+ """
2829
+ if (len(q) << 1) != len(c):
2830
+ raise RuntimeError("factorized_variance_rdm argument lengths do not match.")
2831
+ m = max([(x.bit_length() + 63) // 64 for x in c])
2832
+ result = Qrack.qrack_lib.FactorizedVarianceRdm(
2833
+ self.sid,
2834
+ len(q),
2835
+ QrackSimulator._ulonglong_byref(q),
2836
+ m,
2837
+ QrackSimulator._to_ulonglong(m, c),
2838
+ r,
2839
+ )
2840
+ self._throw_if_error()
2841
+ return result
2842
+
2843
+ def factorized_variance_fp(self, q, c):
2844
+ """Factorized variance (floating-point)
2845
+
2846
+ Get the factorized variance, where each entry
2847
+ in "c" is an variance for corresponding "q"
2848
+ being false, then true, repeated for each in "q".
2849
+
2850
+ Args:
2851
+ q: qubits, from low to high
2852
+ c: qubit falsey/truthy values, from low to high
2853
+
2854
+ Raises:
2855
+ RuntimeError: QrackSimulator raised an exception.
2856
+
2857
+ Returns:
2858
+ variance
2859
+ """
2860
+ if (len(q) << 1) != len(c):
2861
+ raise RuntimeError("factorized_variance_rdm argument lengths do not match.")
2862
+ result = Qrack.qrack_lib.FactorizedVarianceFp(
2863
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._real1_byref(c)
2864
+ )
2865
+ self._throw_if_error()
2866
+ return result
2867
+
2868
+ def factorized_variance_fp_rdm(self, q, c, r=True):
2869
+ """Factorized variance, (tracing out the reduced
2870
+ density matrix without stabilizer ancillary qubits)
2871
+
2872
+ Get the factorized variance, where each entry
2873
+ in "c" is an variance for corresponding "q"
2874
+ being false, then true, repeated for each in "q".
2875
+
2876
+ Args:
2877
+ q: qubits, from low to high
2878
+ c: qubit falsey/truthy values, from low to high
2879
+ r: round Rz gates down from T^(1/2)
2880
+
2881
+ Raises:
2882
+ RuntimeError: QrackSimulator raised an exception.
2883
+
2884
+ Returns:
2885
+ variance
2886
+ """
2887
+ if (len(q) << 1) != len(c):
2888
+ raise RuntimeError("factorized_variance_fp_rdm argument lengths do not match.")
2889
+ result = Qrack.qrack_lib.FactorizedVarianceFpRdm(
2890
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._real1_byref(c), r
2891
+ )
2892
+ self._throw_if_error()
2893
+ return result
2894
+
2895
+ def unitary_variance(self, q, b):
2896
+ """3-parameter unitary tensor product variance
2897
+
2898
+ Get the single-qubit (3-parameter) operator
2899
+ variance for the array of qubits and bases.
2900
+
2901
+ Args:
2902
+ q: qubits, from low to high
2903
+ b: 3-parameter, single-qubit, unitary bases (flat over wires)
2904
+
2905
+ Raises:
2906
+ RuntimeError: QrackSimulator raised an exception.
2907
+
2908
+ Returns:
2909
+ variance
2910
+ """
2911
+ if (3 * len(q)) != len(b):
2912
+ raise RuntimeError("unitary_variance argument lengths do not match.")
2913
+ result = Qrack.qrack_lib.UnitaryVariance(
2914
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._real1_byref(b)
2915
+ )
2916
+ self._throw_if_error()
2917
+ return result
2918
+
2919
+ def matrix_variance(self, q, b):
2920
+ """Single-qubit operator tensor product variance
2921
+
2922
+ Get the single-qubit (3-parameter) operator
2923
+ variance for the array of qubits and bases.
2924
+
2925
+ Args:
2926
+ q: qubits, from low to high
2927
+ b: single-qubit (2x2) operator unitary bases (flat over wires)
2928
+
2929
+ Raises:
2930
+ RuntimeError: QrackSimulator raised an exception.
2931
+
2932
+ Returns:
2933
+ variance
2934
+ """
2935
+ if (len(q) << 2) != len(b):
2936
+ raise RuntimeError("matrix_variance argument lengths do not match.")
2937
+ result = Qrack.qrack_lib.MatrixVariance(
2938
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._complex_byref(b)
2939
+ )
2940
+ self._throw_if_error()
2941
+ return result
2942
+
2943
+ def unitary_variance_eigenval(self, q, b, e):
2944
+ """3-parameter unitary tensor product variance
2945
+
2946
+ Get the single-qubit (3-parameter) operator
2947
+ variance for the array of qubits and bases.
2948
+
2949
+ Args:
2950
+ q: qubits, from low to high
2951
+ b: 3-parameter, single-qubit, unitary bases (flat over wires)
2952
+
2953
+ Raises:
2954
+ RuntimeError: QrackSimulator raised an exception.
2955
+
2956
+ Returns:
2957
+ variance
2958
+ """
2959
+ if (3 * len(q)) != len(b):
2960
+ raise RuntimeError(
2961
+ "unitary_variance_eigenval qubit and basis argument lengths do not match."
2962
+ )
2963
+ if (len(q) << 1) != len(e):
2964
+ raise RuntimeError(
2965
+ "unitary_variance_eigenval qubit and eigenvalue argument lengths do not match."
2966
+ )
2967
+ result = Qrack.qrack_lib.UnitaryVarianceEigenVal(
2968
+ self.sid,
2969
+ len(q),
2970
+ QrackSimulator._ulonglong_byref(q),
2971
+ QrackSimulator._real1_byref(b),
2972
+ QrackSimulator._real1_byref(e),
2973
+ )
2974
+ self._throw_if_error()
2975
+ return result
2976
+
2977
+ def matrix_variance_eigenval(self, q, b, e):
2978
+ """Single-qubit operator tensor product variance
2979
+
2980
+ Get the single-qubit (3-parameter) operator
2981
+ variance for the array of qubits and bases.
2982
+
2983
+ Args:
2984
+ q: qubits, from low to high
2985
+ b: single-qubit (2x2) operator unitary bases (flat over wires)
2986
+
2987
+ Raises:
2988
+ RuntimeError: QrackSimulator raised an exception.
2989
+
2990
+ Returns:
2991
+ variance
2992
+ """
2993
+ if (len(q) << 2) != len(b):
2994
+ raise RuntimeError(
2995
+ "matrix_variance_eigenval qubit and basis argument lengths do not match."
2996
+ )
2997
+ if (len(q) << 1) != len(e):
2998
+ raise RuntimeError(
2999
+ "matrix_variance_eigenval qubit and eigenvalue argument lengths do not match."
3000
+ )
3001
+ result = Qrack.qrack_lib.MatrixVarianceEigenVal(
3002
+ self.sid,
3003
+ len(q),
3004
+ QrackSimulator._ulonglong_byref(q),
3005
+ QrackSimulator._complex_byref(b),
3006
+ QrackSimulator._real1_byref(e),
3007
+ )
3008
+ self._throw_if_error()
3009
+ return result
3010
+
3011
+ def pauli_variance(self, q, b):
3012
+ """Pauli tensor product variance
3013
+
3014
+ Get the Pauli tensor product variance,
3015
+ where each entry in "b" is a Pauli observable for
3016
+ corresponding "q", as the product for each in "q".
3017
+
3018
+ Args:
3019
+ q: qubits, from low to high
3020
+ b: qubit Pauli bases
3021
+
3022
+ Raises:
3023
+ RuntimeError: QrackSimulator raised an exception.
3024
+
3025
+ Returns:
3026
+ variance
3027
+ """
3028
+ if len(q) != len(b):
3029
+ raise RuntimeError("pauli_variance argument lengths do not match.")
3030
+ result = Qrack.qrack_lib.PauliVariance(
3031
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._ulonglong_byref(b)
3032
+ )
3033
+ self._throw_if_error()
3034
+ return result
3035
+
3036
+ def joint_ensemble_probability(self, b, q):
3037
+ """Ensemble probability
3038
+
3039
+ Find the joint probability for all specified qubits under the
3040
+ respective Pauli basis transformations.
3041
+
3042
+ Args:
3043
+ b: pauli basis
3044
+ q: specified qubits
3045
+
3046
+ Raises:
3047
+ RuntimeError: QrackSimulator raised an exception.
3048
+
3049
+ Returns:
3050
+ Variance
3051
+ """
3052
+ if len(b) != len(q):
3053
+ raise RuntimeError("Lengths of list parameters are mismatched.")
3054
+ result = Qrack.qrack_lib.JointEnsembleProbability(
3055
+ self.sid, len(b), QrackSimulator._ulonglong_byref(b), q
3056
+ )
3057
+ self._throw_if_error()
3058
+ return result
3059
+
3060
+ def phase_parity(self, la, q):
3061
+ """Phase to odd parity
3062
+
3063
+ Applies `e^(i*la)` phase factor to all combinations of bits with
3064
+ odd parity, based upon permutations of qubits.
3065
+
3066
+ Args:
3067
+ la: phase
3068
+ q: specified qubits
3069
+
3070
+ Raises:
3071
+ RuntimeError: QrackSimulator raised an exception.
3072
+ RuntimeError: QrackStabilizer cannot phase_parity()! (Create a QrackSimulator instead.)
3073
+ """
3074
+ if self.is_pure_stabilizer:
3075
+ raise RuntimeError(
3076
+ "QrackStabilizer cannot phase_parity()! (Create a QrackSimulator instead.)"
3077
+ )
3078
+
3079
+ Qrack.qrack_lib.PhaseParity(
3080
+ self.sid, ctypes.c_double(la), len(q), QrackSimulator._ulonglong_byref(q)
3081
+ )
3082
+ self._throw_if_error()
3083
+
3084
+ def phase_root_n(self, n, q):
3085
+ """Phase to root n
3086
+
3087
+ Applies `-2 * math.pi / (2**N)` phase rotation to each qubit.
3088
+
3089
+ Args:
3090
+ n: Phase root
3091
+ q: specified qubits
3092
+
3093
+ Raises:
3094
+ RuntimeError: QrackSimulator raised an exception.
3095
+ RuntimeError: QrackStabilizer cannot phase_root_n()! (Create a QrackSimulator instead.)
3096
+ """
3097
+ if self.is_pure_stabilizer:
3098
+ raise RuntimeError(
3099
+ "QrackStabilizer cannot phase_root_n()! (Create a QrackSimulator instead.)"
3100
+ )
3101
+
3102
+ Qrack.qrack_lib.PhaseRootN(self.sid, n, len(q), QrackSimulator._ulonglong_byref(q))
3103
+ self._throw_if_error()
3104
+
3105
+ def try_separate_1qb(self, qi1):
3106
+ """Manual seperation
3107
+
3108
+ Exposes manual control for schmidt decomposition which attempts to
3109
+ decompose the qubit with possible performance improvement
3110
+
3111
+ Args:
3112
+ qi1: qubit to be decomposed
3113
+
3114
+ Raises:
3115
+ RuntimeError: QrackSimulator raised an exception.
3116
+
3117
+ Returns:
3118
+ State of the qubit.
3119
+ """
3120
+ result = Qrack.qrack_lib.TrySeparate1Qb(self.sid, qi1)
3121
+ self._throw_if_error()
3122
+ return result
3123
+
3124
+ def try_separate_2qb(self, qi1, qi2):
3125
+ """Manual two-qubits seperation
3126
+
3127
+ two-qubits counterpart of `try_separate_1qb`.
3128
+
3129
+ Args:
3130
+ qi1: first qubit to be decomposed
3131
+ qi2: second qubit to be decomposed
3132
+
3133
+ Raises:
3134
+ Runtimeerror: QrackSimulator raised an exception.
3135
+
3136
+ Returns:
3137
+ State of both the qubits.
3138
+ """
3139
+ result = Qrack.qrack_lib.TrySeparate2Qb(self.sid, qi1, qi2)
3140
+ self._throw_if_error()
3141
+ return result
3142
+
3143
+ def try_separate_tolerance(self, qs, t):
3144
+ """Manual multi-qubits seperation
3145
+
3146
+ Multi-qubits counterpart of `try_separate_1qb`.
3147
+
3148
+ Args:
3149
+ qs: list of qubits to be decomposed
3150
+ t: allowed tolerance
3151
+
3152
+ Raises:
3153
+ Runtimeerror: QrackSimulator raised an exception.
3154
+
3155
+ Returns:
3156
+ State of all the qubits.
3157
+ """
3158
+ result = Qrack.qrack_lib.TrySeparateTol(
3159
+ self.sid, len(qs), QrackSimulator._ulonglong_byref(qs), t
3160
+ )
3161
+ self._throw_if_error()
3162
+ return result
3163
+
3164
+ def separate(self, qs):
3165
+ """Force manual multi-qubits seperation
3166
+
3167
+ Force separation as per `try_separate_tolerance`.
3168
+
3169
+ Args:
3170
+ qs: list of qubits to be decomposed
3171
+
3172
+ Raises:
3173
+ Runtimeerror: QrackSimulator raised an exception.
3174
+ """
3175
+ result = Qrack.qrack_lib.Separate(self.sid, len(qs), QrackSimulator._ulonglong_byref(qs))
3176
+ self._throw_if_error()
3177
+
3178
+ def get_unitary_fidelity(self):
3179
+ """Get fidelity estimate
3180
+
3181
+ When using "Schmidt decomposition rounding parameter" ("SDRP")
3182
+ approximate simulation, QrackSimulator() can make an excellent
3183
+ estimate of its overall fidelity at any time, tested against a
3184
+ nearest-neighbor variant of quantum volume circuits.
3185
+
3186
+ Resetting the fidelity calculation to 1.0 happens automatically
3187
+ when calling `mall` are can be done manually with
3188
+ `reset_unitary_fidelity()`.
3189
+
3190
+ Raises:
3191
+ RuntimeError: QrackSimulator raised an exception.
3192
+
3193
+ Returns:
3194
+ Fidelity estimate
3195
+ """
3196
+ result = Qrack.qrack_lib.GetUnitaryFidelity(self.sid)
3197
+ self._throw_if_error()
3198
+ return result
3199
+
3200
+ def reset_unitary_fidelity(self):
3201
+ """Reset fidelity estimate
3202
+
3203
+ When using "Schmidt decomposition rounding parameter" ("SDRP")
3204
+ approximate simulation, QrackSimulator() can make an excellent
3205
+ estimate of its overall fidelity at any time, tested against a
3206
+ nearest-neighbor variant of quantum volume circuits.
3207
+
3208
+ Resetting the fidelity calculation to 1.0 happens automatically
3209
+ when calling `m_all` or can be done manually with
3210
+ `reset_unitary_fidelity()`.
3211
+
3212
+ Raises:
3213
+ RuntimeError: QrackSimulator raised an exception.
3214
+ """
3215
+ Qrack.qrack_lib.ResetUnitaryFidelity(self.sid)
3216
+ self._throw_if_error()
3217
+
3218
+ def set_sdrp(self, sdrp):
3219
+ """Set "Schmidt decomposition rounding parameter"
3220
+
3221
+ When using "Schmidt decomposition rounding parameter" ("SDRP")
3222
+ approximate simulation, QrackSimulator() can make an excellent
3223
+ estimate of its overall fidelity at any time, tested against a
3224
+ nearest-neighbor variant of quantum volume circuits.
3225
+
3226
+ Resetting the fidelity calculation to 1.0 happens automatically
3227
+ when calling `m_all` or can be done manually with
3228
+ `reset_unitary_fidelity()`.
3229
+
3230
+ Raises:
3231
+ RuntimeError: QrackSimulator raised an exception.
3232
+ """
3233
+ Qrack.qrack_lib.SetSdrp(self.sid, sdrp)
3234
+ self._throw_if_error()
3235
+
3236
+ def set_ncrp(self, ncrp):
3237
+ """Set "Near-Clifford rounding parameter"
3238
+
3239
+ When using "near-Clifford rounding parameter" ("NCRP")
3240
+ approximate simulation, QrackSimulator() can make an excellent
3241
+ estimate of its overall fidelity after measurement, tested against
3242
+ a nearest-neighbor variant of quantum volume circuits.
3243
+
3244
+ Resetting the fidelity calculation to 1.0 happens automatically
3245
+ when calling `m_all` or can be done manually with
3246
+ `reset_unitary_fidelity()`.
3247
+
3248
+ Raises:
3249
+ RuntimeError: QrackSimulator raised an exception.
3250
+ """
3251
+ Qrack.qrack_lib.SetNcrp(self.sid, ncrp)
3252
+ self._throw_if_error()
3253
+
3254
+ def set_reactive_separate(self, irs):
3255
+ """Set reactive separation option
3256
+
3257
+ If reactive separation is available, then this method turns it off/on.
3258
+ Note that reactive separation is on by default.
3259
+
3260
+ Args:
3261
+ irs: is aggresively separable
3262
+
3263
+ Raises:
3264
+ RuntimeError: QrackSimulator raised an exception.
3265
+ """
3266
+ Qrack.qrack_lib.SetReactiveSeparate(self.sid, irs)
3267
+ self._throw_if_error()
3268
+
3269
+ def set_t_injection(self, iti):
3270
+ """Set t-injection option
3271
+
3272
+ If t-injection is available, then this method turns it off/on.
3273
+ Note that t-injection is on by default.
3274
+
3275
+ Args:
3276
+ iti: use "reverse t-injection gadget"
3277
+
3278
+ Raises:
3279
+ RuntimeError: QrackSimulator raised an exception.
3280
+ """
3281
+ Qrack.qrack_lib.SetTInjection(self.sid, iti)
3282
+ self._throw_if_error()
3283
+
3284
+ def set_noise_parameter(self, np):
3285
+ """Set noise parameter option
3286
+
3287
+ If noisy simulation is on, then this set the depolarization
3288
+ parameter per qubit per gate. (Default is 0.01.)
3289
+
3290
+ Args:
3291
+ np: depolarizing noise parameter
3292
+
3293
+ Raises:
3294
+ RuntimeError: QrackSimulator raised an exception.
3295
+ """
3296
+ Qrack.qrack_lib.SetNoiseParameter(self.sid, np)
3297
+ self._throw_if_error()
3298
+
3299
+ def set_ace_max_qb(self, qb):
3300
+ """Set "automatic circuit elision" (ACE) max qubits
3301
+
3302
+ If isSchmidtDecompose=True, maximum entangled subsytem size
3303
+ of this simulator will be capped to 'qb', and entangling
3304
+ gates that would exceed that size are replaced with gate
3305
+ shadows.
3306
+
3307
+ Args:
3308
+ qb: maximum subsystem qubits
3309
+
3310
+ Raises:
3311
+ RuntimeError: QrackSimulator raised an exception.
3312
+ """
3313
+ Qrack.qrack_lib.SetAceMaxQb(self.sid, qb)
3314
+ self._throw_if_error()
3315
+
3316
+ def set_sparse_ace_max_mb(self, mb):
3317
+ """Set sparse "automatic circuit elision" (ACE) max memory
3318
+
3319
+ If isSchmidtDecompose=True, isSparse=True, and
3320
+ isOpenCL=False, maximum subsytem size memory MB of this
3321
+ simulator will be capped to 'mb', and entangling gates
3322
+ that would exceed that size are replaced with gate
3323
+ shadows.
3324
+
3325
+ Args:
3326
+ mb: maximum subsystem memory in MB
3327
+
3328
+ Raises:
3329
+ RuntimeError: QrackSimulator raised an exception.
3330
+ """
3331
+ Qrack.qrack_lib.SetSparseAceMaxMb(self.sid, mb)
3332
+ self._throw_if_error()
3333
+
3334
+ def normalize(self):
3335
+ """Normalize the state
3336
+
3337
+ This should never be necessary to use unless
3338
+ decompose() is "abused." ("Abusing" decompose()
3339
+ might lead to efficient entanglement-breaking
3340
+ channels, though.)
3341
+
3342
+ Raises:
3343
+ RuntimeError: QrackSimulator raised an exception.
3344
+ """
3345
+ Qrack.qrack_lib.Normalize(self.sid)
3346
+ self._throw_if_error()
3347
+
3348
+ def out_to_file(self, filename):
3349
+ """Output state to file (stabilizer only!)
3350
+
3351
+ Outputs the hybrid stabilizer state to file.
3352
+
3353
+ Args:
3354
+ filename: Name of file
3355
+ """
3356
+ Qrack.qrack_lib.qstabilizer_out_to_file(self.sid, filename.encode("utf-8"))
3357
+ self._throw_if_error()
3358
+
3359
+ def in_from_file(
3360
+ filename,
3361
+ is_binary_decision_tree=False,
3362
+ is_paged=True,
3363
+ is_cpu_gpu_hybrid=False,
3364
+ is_opencl=True,
3365
+ is_host_pointer=False,
3366
+ is_noisy=False,
3367
+ ):
3368
+ """Input state from file (stabilizer only!)
3369
+
3370
+ Reads in a hybrid stabilizer state from file.
3371
+
3372
+ Args:
3373
+ filename: Name of file
3374
+ """
3375
+ qb_count = 1
3376
+ with open(filename) as f:
3377
+ qb_count = int(f.readline())
3378
+ out = QrackSimulator(
3379
+ qubitCount=qb_count,
3380
+ isTensorNetwork=False,
3381
+ isSchmidtDecomposeMulti=False,
3382
+ isSchmidtDecompose=False,
3383
+ isStabilizerHybrid=True,
3384
+ isBinaryDecisionTree=is_binary_decision_tree,
3385
+ isPaged=is_paged,
3386
+ isCpuGpuHybrid=is_cpu_gpu_hybrid,
3387
+ isOpenCL=is_opencl,
3388
+ isHostPointer=is_host_pointer,
3389
+ isNoisy=is_noisy,
3390
+ )
3391
+ Qrack.qrack_lib.qstabilizer_in_from_file(out.sid, filename.encode("utf-8"))
3392
+ out._throw_if_error()
3393
+
3394
+ return out
3395
+
3396
+ def file_to_qiskit_circuit(filename, is_hardware_encoded=False):
3397
+ """Convert an output state file to a Qiskit circuit
3398
+
3399
+ Reads in an (optimized) circuit from a file named
3400
+ according to the "filename" parameter and outputs
3401
+ a Qiskit circuit.
3402
+
3403
+ Args:
3404
+ filename: Name of file
3405
+
3406
+ Raises:
3407
+ RuntimeErorr: Before trying to file_to_qiskit_circuit() with
3408
+ QrackCircuit, you must install Qiskit, numpy, and math!
3409
+ """
3410
+ if not (_IS_QISKIT_AVAILABLE and _IS_NUMPY_AVAILABLE):
3411
+ raise RuntimeError(
3412
+ "Before trying to file_to_qiskit_circuit() with QrackCircuit, you must install Qiskit, numpy, and math!"
3413
+ )
3414
+
3415
+ lines = []
3416
+ with open(filename, "r") as file:
3417
+ lines = file.readlines()
3418
+
3419
+ logical_qubits = int(lines[0])
3420
+ stabilizer_qubits = int(lines[1])
3421
+
3422
+ stabilizer_count = int(lines[2])
3423
+
3424
+ clifford_circ = None
3425
+ line_number = 3
3426
+ for i in range(stabilizer_count):
3427
+ shard_map_size = int(lines[line_number])
3428
+ line_number += 1
3429
+
3430
+ shard_map = {}
3431
+ for j in range(shard_map_size):
3432
+ line = lines[line_number].split()
3433
+ line_number += 1
3434
+ shard_map[int(line[0])] = int(line[1])
3435
+
3436
+ line_number += 1
3437
+ tableau = []
3438
+ row_count = shard_map_size << 1
3439
+ for line in lines[line_number : (line_number + row_count)]:
3440
+ bits = line.split()
3441
+ if len(bits) != (row_count + 1):
3442
+ raise QrackException("Invalid Qrack hybrid stabilizer file!")
3443
+ row = []
3444
+ for b in range(row_count):
3445
+ row.append(bool(int(bits[b])))
3446
+ row.append(bool((int(bits[-1]) >> 1) & 1))
3447
+ tableau.append(row)
3448
+ line_number += shard_map_size << 1
3449
+ tableau = np.array(tableau, bool)
3450
+
3451
+ clifford = Clifford(tableau, validate=False, copy=False)
3452
+ clifford_circ = clifford.to_circuit()
3453
+ clifford_circ = QrackSimulator._reorder_qubits(clifford_circ, shard_map)
3454
+
3455
+ non_clifford_gates = []
3456
+ g = 0
3457
+ for line in lines[line_number:]:
3458
+ i = 0
3459
+ tokens = line.split()
3460
+ op = np.zeros((2, 2), dtype=complex)
3461
+ row = []
3462
+ for _ in range(2):
3463
+ amp = tokens[i].replace("(", "").replace(")", "").split(",")
3464
+ row.append(float(amp[0]) + float(amp[1]) * 1j)
3465
+ i = i + 1
3466
+ l = math.sqrt(np.real(row[0] * np.conj(row[0]) + row[1] * np.conj(row[1])))
3467
+ op[0][0] = row[0] / l
3468
+ op[0][1] = row[1] / l
3469
+
3470
+ if np.abs(op[0][0] - row[0]) > 1e-5:
3471
+ print("Warning: gate ", str(g), " might not be unitary!")
3472
+ if np.abs(op[0][1] - row[1]) > 1e-5:
3473
+ print("Warning: gate ", str(g), " might not be unitary!")
3474
+
3475
+ row = []
3476
+ for _ in range(2):
3477
+ amp = tokens[i].replace("(", "").replace(")", "").split(",")
3478
+ row.append(float(amp[0]) + float(amp[1]) * 1j)
3479
+ i = i + 1
3480
+ l = math.sqrt(np.real(row[0] * np.conj(row[0]) + row[1] * np.conj(row[1])))
3481
+ op[1][0] = row[0] / l
3482
+ op[1][1] = row[1] / l
3483
+
3484
+ ph = np.real(np.log(np.linalg.det(op)) / 1j)
3485
+
3486
+ op[1][0] = -np.exp(1j * ph) * np.conj(op[0][1])
3487
+ op[1][1] = np.exp(1j * ph) * np.conj(op[0][0])
3488
+
3489
+ if np.abs(op[1][0] - row[0]) > 1e-5:
3490
+ print("Warning: gate ", str(g), " might not be unitary!")
3491
+ if np.abs(op[1][1] - row[1]) > 1e-5:
3492
+ print("Warning: gate ", str(g), " might not be unitary!")
3493
+
3494
+ non_clifford_gates.append(op)
3495
+ g = g + 1
3496
+
3497
+ basis_gates = [
3498
+ "rz",
3499
+ "h",
3500
+ "x",
3501
+ "y",
3502
+ "z",
3503
+ "sx",
3504
+ "sxdg",
3505
+ "s",
3506
+ "sdg",
3507
+ "t",
3508
+ "tdg",
3509
+ "cx",
3510
+ "cy",
3511
+ "cz",
3512
+ "swap",
3513
+ ]
3514
+ try:
3515
+ circ = transpile(clifford_circ, basis_gates=basis_gates, optimization_level=2)
3516
+ except:
3517
+ circ = clifford_circ
3518
+
3519
+ for i in range(len(non_clifford_gates)):
3520
+ circ.unitary(non_clifford_gates[i], [i])
3521
+
3522
+ if is_hardware_encoded:
3523
+ for i in range(logical_qubits, stabilizer_qubits, 2):
3524
+ circ.h(i + 1)
3525
+ circ.cz(i, i + 1)
3526
+ circ.h(i + 1)
3527
+
3528
+ return circ
3529
+
3530
+ def _reorder_qubits(circuit, mapping):
3531
+ """
3532
+ Reorders qubits in the circuit according to the given mapping using SWAP gates.
3533
+ (Thanks to "Elara," an OpenAI GPT, for this implementation)
3534
+
3535
+ Parameters:
3536
+ - circuit (QuantumCircuit): The circuit to modify.
3537
+ - mapping (dict): Dictionary mapping internal qubit indices to logical qubit indices.
3538
+
3539
+ Returns:
3540
+ - QuantumCircuit: The modified circuit with qubits reordered.
3541
+ """
3542
+ swaps = []
3543
+
3544
+ # Determine swaps to fix the order
3545
+ for logical_index in sorted(mapping):
3546
+ internal_index = mapping[logical_index]
3547
+ if logical_index != internal_index:
3548
+ swaps.append((logical_index, internal_index))
3549
+ # Update the reverse mapping for subsequent swaps
3550
+ mapping[logical_index] = logical_index
3551
+ mapping[internal_index] = internal_index
3552
+
3553
+ # Apply the swaps to the circuit
3554
+ for qubit1, qubit2 in swaps:
3555
+ circuit.swap(qubit1, qubit2)
3556
+
3557
+ return circuit
3558
+
3559
+ def file_to_optimized_qiskit_circuit(filename):
3560
+ """Convert an output state file to a Qiskit circuit
3561
+
3562
+ Reads in a circuit from a file named according to the "filename"
3563
+ parameter and outputs a 'hyper-optimized' Qiskit circuit that
3564
+ favors maximum reduction in gate count and depth at the potential
3565
+ expense of additional non-Clifford gates. (Ancilla qubits are
3566
+ left included in the output, though they probably have no gates.)
3567
+
3568
+ Args:
3569
+ filename: Name of file
3570
+
3571
+ Raises:
3572
+ RuntimeErorr: Before trying to file_to_qiskit_circuit() with
3573
+ QrackCircuit, you must install Qiskit, numpy, and math!
3574
+ """
3575
+ circ = QrackSimulator.file_to_qiskit_circuit(filename)
3576
+
3577
+ width = 0
3578
+ with open(filename, "r", encoding="utf-8") as file:
3579
+ width = int(file.readline())
3580
+
3581
+ sqrt_pi = np.sqrt(1j)
3582
+ sqrt_ni = np.sqrt(-1j)
3583
+ sqrt1_2 = 1 / math.sqrt(2)
3584
+ ident = np.eye(2, dtype=np.complex128)
3585
+ # passable_gates = ["unitary", "rz", "h", "x", "y", "z", "sx", "sxdg", "s", "sdg", "t", "tdg"]
3586
+
3587
+ passed_swaps = []
3588
+ for i in range(0, circ.width()):
3589
+ # We might trace out swap, but we want to maintain the iteration order of qubit channels.
3590
+ non_clifford = np.copy(ident)
3591
+ j = 0
3592
+ while j < len(circ.data):
3593
+ op = circ.data[j].operation
3594
+ qubits = circ.data[j].qubits
3595
+ if len(qubits) > 2:
3596
+ raise RuntimeError(
3597
+ "Something went wrong while optimizing circuit! (Found a gate with 3 or more qubits)"
3598
+ )
3599
+ q1 = circ.find_bit(qubits[0])[0]
3600
+ if (len(qubits) < 2) and (q1 == i):
3601
+ if op.name == "unitary":
3602
+ non_clifford = np.matmul(op.params[0], non_clifford)
3603
+ elif op.name == "rz":
3604
+ lm = float(op.params[0])
3605
+ non_clifford = np.matmul(
3606
+ [[np.exp(-1j * lm / 2), 0], [0, np.exp(1j * lm / 2)]],
3607
+ non_clifford,
3608
+ )
3609
+ elif op.name == "h":
3610
+ non_clifford = np.matmul(
3611
+ np.array([[sqrt1_2, sqrt1_2], [sqrt1_2, -sqrt1_2]], np.complex128),
3612
+ non_clifford,
3613
+ )
3614
+ elif op.name == "x":
3615
+ non_clifford = np.matmul(
3616
+ np.array([[0, 1], [1, 0]], np.complex128), non_clifford
3617
+ )
3618
+ elif op.name == "y":
3619
+ non_clifford = np.matmul(
3620
+ np.array([[0, -1j], [1j, 0]], np.complex128), non_clifford
3621
+ )
3622
+ elif op.name == "z":
3623
+ non_clifford = np.matmul(
3624
+ np.array([[1, 0], [0, -1]], np.complex128), non_clifford
3625
+ )
3626
+ elif op.name == "sx":
3627
+ non_clifford = np.matmul(
3628
+ np.array(
3629
+ [
3630
+ [(1 + 1j) / 2, (1 - 1j) / 2],
3631
+ [(1 - 1j) / 2, (1 + 1j) / 2],
3632
+ ],
3633
+ np.complex128,
3634
+ ),
3635
+ non_clifford,
3636
+ )
3637
+ elif op.name == "sxdg":
3638
+ non_clifford = np.matmul(
3639
+ np.array(
3640
+ [
3641
+ [(1 - 1j) / 2, (1 + 1j) / 2],
3642
+ [(1 + 1j) / 2, (1 - 1j) / 2],
3643
+ ],
3644
+ np.complex128,
3645
+ ),
3646
+ non_clifford,
3647
+ )
3648
+ elif op.name == "sy":
3649
+ non_clifford = np.matmul(
3650
+ np.array(
3651
+ [
3652
+ [(1 + 1j) / 2, -(1 + 1j) / 2],
3653
+ [(1 + 1j) / 2, (1 + 1j) / 2],
3654
+ ],
3655
+ np.complex128,
3656
+ ),
3657
+ non_clifford,
3658
+ )
3659
+ elif op.name == "sydg":
3660
+ non_clifford = np.matmul(
3661
+ np.array(
3662
+ [
3663
+ [(1 - 1j) / 2, (1 - 1j) / 2],
3664
+ [(-1 + 1j) / 2, (1 - 1j) / 2],
3665
+ ],
3666
+ np.complex128,
3667
+ ),
3668
+ non_clifford,
3669
+ )
3670
+ elif op.name == "s":
3671
+ non_clifford = np.matmul(
3672
+ np.array([[1, 0], [0, 1j]], np.complex128), non_clifford
3673
+ )
3674
+ elif op.name == "sdg":
3675
+ non_clifford = np.matmul(
3676
+ np.array([[1, 0], [0, -1j]], np.complex128), non_clifford
3677
+ )
3678
+ elif op.name == "t":
3679
+ non_clifford = np.matmul(
3680
+ np.array([[1, 0], [0, sqrt_pi]], np.complex128),
3681
+ non_clifford,
3682
+ )
3683
+ elif op.name == "tdg":
3684
+ non_clifford = np.matmul(
3685
+ np.array([[1, 0], [0, sqrt_ni]], np.complex128),
3686
+ non_clifford,
3687
+ )
3688
+ else:
3689
+ raise RuntimeError(
3690
+ "Something went wrong while optimizing circuit! (Dropped a single-qubit gate.)"
3691
+ )
3692
+
3693
+ del circ.data[j]
3694
+ continue
3695
+
3696
+ if len(qubits) < 2:
3697
+ j += 1
3698
+ continue
3699
+
3700
+ q2 = circ.find_bit(qubits[1])[0]
3701
+
3702
+ if (i != q1) and (i != q2):
3703
+ j += 1
3704
+ continue
3705
+
3706
+ if op.name == "swap":
3707
+ i = q2 if i == q1 else q1
3708
+
3709
+ if circ.data[j] in passed_swaps:
3710
+ passed_swaps.remove(circ.data[j])
3711
+ del circ.data[j]
3712
+ continue
3713
+
3714
+ passed_swaps.append(circ.data[j])
3715
+
3716
+ j += 1
3717
+ continue
3718
+
3719
+ if (q1 == i) and ((op.name == "cx") or (op.name == "cy") or (op.name == "cz")):
3720
+ if np.isclose(np.abs(non_clifford[0][1]), 0) and np.isclose(
3721
+ np.abs(non_clifford[1][0]), 0
3722
+ ):
3723
+ # If we're not buffering anything but phase, the blocking gate has no effect, and we're safe to continue.
3724
+ del circ.data[j]
3725
+ continue
3726
+
3727
+ if np.isclose(np.abs(non_clifford[0][0]), 0) and np.isclose(
3728
+ np.abs(non_clifford[1][1]), 0
3729
+ ):
3730
+ # If we're buffering full negation (plus phase), the control qubit can be dropped.
3731
+ c = QuantumCircuit(circ.qubits)
3732
+ if op.name == "cx":
3733
+ c.x(qubits[1])
3734
+ elif op.name == "cy":
3735
+ c.y(qubits[1])
3736
+ else:
3737
+ c.z(qubits[1])
3738
+ circ.data[j] = copy.deepcopy(c.data[0])
3739
+
3740
+ j += 1
3741
+ continue
3742
+
3743
+ if np.allclose(non_clifford, ident):
3744
+ # No buffer content to write to circuit definition
3745
+ non_clifford = np.copy(ident)
3746
+ break
3747
+
3748
+ # We're blocked, so we insert our buffer at this place in the circuit definition.
3749
+ c = QuantumCircuit(circ.qubits)
3750
+ c.unitary(non_clifford, qubits[0])
3751
+ circ.data.insert(j, copy.deepcopy(c.data[0]))
3752
+
3753
+ non_clifford = np.copy(ident)
3754
+ break
3755
+
3756
+ if (j == len(circ.data)) and not np.allclose(non_clifford, ident):
3757
+ # We're at the end of the wire, so add the buffer gate.
3758
+ circ.unitary(non_clifford, i)
3759
+
3760
+ passed_swaps.clear()
3761
+ for i in range(width, circ.width()):
3762
+ # We might trace out swap, but we want to maintain the iteration order of qubit channels.
3763
+ non_clifford = np.copy(ident)
3764
+ j = len(circ.data) - 1
3765
+ while j >= 0:
3766
+ op = circ.data[j].operation
3767
+ qubits = circ.data[j].qubits
3768
+ if len(qubits) > 2:
3769
+ raise RuntimeError(
3770
+ "Something went wrong while optimizing circuit! (Found a gate with 3 or more qubits.)"
3771
+ )
3772
+ q1 = circ.find_bit(qubits[0])[0]
3773
+ if (len(qubits) < 2) and (q1 == i):
3774
+ if op.name == "unitary":
3775
+ non_clifford = np.matmul(non_clifford, op.params[0])
3776
+ elif op.name == "rz":
3777
+ lm = float(op.params[0])
3778
+ non_clifford = np.matmul(
3779
+ non_clifford,
3780
+ [[np.exp(-1j * lm / 2), 0], [0, np.exp(1j * lm / 2)]],
3781
+ )
3782
+ elif op.name == "h":
3783
+ non_clifford = np.matmul(
3784
+ non_clifford,
3785
+ np.array([[sqrt1_2, sqrt1_2], [sqrt1_2, -sqrt1_2]], np.complex128),
3786
+ )
3787
+ elif op.name == "x":
3788
+ non_clifford = np.matmul(
3789
+ non_clifford, np.array([[0, 1], [1, 0]], np.complex128)
3790
+ )
3791
+ elif op.name == "y":
3792
+ non_clifford = np.matmul(
3793
+ non_clifford, np.array([[0, -1j], [1j, 0]], np.complex128)
3794
+ )
3795
+ elif op.name == "z":
3796
+ non_clifford = np.matmul(
3797
+ non_clifford, np.array([[1, 0], [0, -1]], np.complex128)
3798
+ )
3799
+ elif op.name == "sx":
3800
+ non_clifford = np.matmul(
3801
+ non_clifford,
3802
+ np.array(
3803
+ [
3804
+ [(1 + 1j) / 2, (1 - 1j) / 2],
3805
+ [(1 - 1j) / 2, (1 + 1j) / 2],
3806
+ ],
3807
+ np.complex128,
3808
+ ),
3809
+ )
3810
+ elif op.name == "sxdg":
3811
+ non_clifford = np.matmul(
3812
+ non_clifford,
3813
+ np.array(
3814
+ [
3815
+ [(1 - 1j) / 2, (1 + 1j) / 2],
3816
+ [(1 + 1j) / 2, (1 - 1j) / 2],
3817
+ ],
3818
+ np.complex128,
3819
+ ),
3820
+ )
3821
+ elif op.name == "sy":
3822
+ non_clifford = np.matmul(
3823
+ non_clifford,
3824
+ np.array(
3825
+ [
3826
+ [(1 + 1j) / 2, -(1 + 1j) / 2],
3827
+ [(1 + 1j) / 2, (1 + 1j) / 2],
3828
+ ],
3829
+ np.complex128,
3830
+ ),
3831
+ )
3832
+ elif op.name == "sydg":
3833
+ non_clifford = np.matmul(
3834
+ non_clifford,
3835
+ np.array(
3836
+ [
3837
+ [(1 - 1j) / 2, (1 - 1j) / 2],
3838
+ [(-1 + 1j) / 2, (1 - 1j) / 2],
3839
+ ],
3840
+ np.complex128,
3841
+ ),
3842
+ )
3843
+ elif op.name == "s":
3844
+ non_clifford = np.matmul(
3845
+ non_clifford, np.array([[1, 0], [0, 1j]], np.complex128)
3846
+ )
3847
+ elif op.name == "sdg":
3848
+ non_clifford = np.matmul(
3849
+ non_clifford, np.array([[1, 0], [0, -1j]], np.complex128)
3850
+ )
3851
+ elif op.name == "t":
3852
+ non_clifford = np.matmul(
3853
+ non_clifford,
3854
+ np.array([[1, 0], [0, sqrt_pi]], np.complex128),
3855
+ )
3856
+ elif op.name == "tdg":
3857
+ non_clifford = np.matmul(
3858
+ non_clifford,
3859
+ np.array([[1, 0], [0, sqrt_ni]], np.complex128),
3860
+ )
3861
+ else:
3862
+ raise RuntimeError(
3863
+ "Something went wrong while optimizing circuit! (Dropped a single-qubit gate.)"
3864
+ )
3865
+
3866
+ del circ.data[j]
3867
+ j -= 1
3868
+ continue
3869
+
3870
+ if len(qubits) < 2:
3871
+ j -= 1
3872
+ continue
3873
+
3874
+ q2 = circ.find_bit(qubits[1])[0]
3875
+
3876
+ if (i != q1) and (i != q2):
3877
+ j -= 1
3878
+ continue
3879
+
3880
+ if (op.name == "swap") and (q1 >= width) and (q2 >= width):
3881
+ i = q2 if i == q1 else q1
3882
+ if circ.data[j] in passed_swaps:
3883
+ passed_swaps.remove(circ.data[j])
3884
+ del circ.data[j]
3885
+ else:
3886
+ passed_swaps.append(circ.data[j])
3887
+
3888
+ j -= 1
3889
+ continue
3890
+
3891
+ if (
3892
+ (q1 == i)
3893
+ and ((op.name == "cx") or (op.name == "cy") or (op.name == "cz"))
3894
+ and (
3895
+ np.isclose(np.abs(non_clifford[0][1]), 0)
3896
+ and np.isclose(np.abs(non_clifford[1][0]), 0)
3897
+ )
3898
+ ):
3899
+ # If we're not buffering anything but phase, this commutes with control, and we're safe to continue.
3900
+ j -= 1
3901
+ continue
3902
+
3903
+ if (q1 == i) and (op.name == "cx"):
3904
+ orig_instr = circ.data[j]
3905
+ del circ.data[j]
3906
+
3907
+ # We're replacing CNOT with CNOT in the opposite direction plus four H gates
3908
+ rep = QuantumCircuit(circ.qubits)
3909
+ rep.h(qubits[0])
3910
+ circ.data.insert(j, copy.deepcopy(rep.data[0]))
3911
+ rep.h(qubits[1])
3912
+ circ.data.insert(j, copy.deepcopy(rep.data[1]))
3913
+ rep.cx(qubits[1], qubits[0])
3914
+ circ.data.insert(j, copy.deepcopy(rep.data[2]))
3915
+ rep.h(qubits[0])
3916
+ circ.data.insert(j, copy.deepcopy(rep.data[3]))
3917
+ rep.h(qubits[1])
3918
+ circ.data.insert(j, copy.deepcopy(rep.data[4]))
3919
+
3920
+ j += 4
3921
+ continue
3922
+
3923
+ if (q1 == i) or (op.name != "cx"):
3924
+ if np.allclose(non_clifford, ident):
3925
+ # No buffer content to write to circuit definition
3926
+ break
3927
+
3928
+ # We're blocked, so we insert our buffer at this place in the circuit definition.
3929
+ c = QuantumCircuit(circ.qubits)
3930
+ c.unitary(non_clifford, qubits[0])
3931
+ circ.data.insert(j + 1, copy.deepcopy(c.data[0]))
3932
+
3933
+ break
3934
+
3935
+ # Re-injection branch (apply gadget to target)
3936
+ to_inject = np.matmul(
3937
+ non_clifford, np.array([[sqrt1_2, sqrt1_2], [sqrt1_2, -sqrt1_2]])
3938
+ )
3939
+ if np.allclose(to_inject, ident):
3940
+ # No buffer content to write to circuit definition
3941
+ del circ.data[j]
3942
+ j -= 1
3943
+ continue
3944
+
3945
+ c = QuantumCircuit(circ.qubits)
3946
+ c.unitary(to_inject, qubits[0])
3947
+ circ.data.insert(j, copy.deepcopy(c.data[0]))
3948
+ j -= 1
3949
+
3950
+ basis_gates = [
3951
+ "u",
3952
+ "rz",
3953
+ "h",
3954
+ "x",
3955
+ "y",
3956
+ "z",
3957
+ "sx",
3958
+ "sxdg",
3959
+ "s",
3960
+ "sdg",
3961
+ "t",
3962
+ "tdg",
3963
+ "cx",
3964
+ "cy",
3965
+ "cz",
3966
+ "swap",
3967
+ ]
3968
+ circ = transpile(circ, basis_gates=basis_gates, optimization_level=2)
3969
+
3970
+ # Eliminate unused ancillae
3971
+ try:
3972
+ qasm = qasm3.dumps(circ)
3973
+ except:
3974
+ qasm = circ.qasm()
3975
+ qasm = qasm.replace("qreg q[" + str(circ.width()) + "];", "qreg q[" + str(width) + "];")
3976
+ highest_index = max([int(x) for x in re.findall(r"\[(.*?)\]", qasm) if x.isdigit()])
3977
+ if highest_index != width:
3978
+ qasm = qasm.replace(
3979
+ "qreg q[" + str(width) + "];", "qreg q[" + str(highest_index) + "];"
3980
+ )
3981
+
3982
+ orig_circ = circ
3983
+ try:
3984
+ circ = QuantumCircuit.from_qasm_str(qasm)
3985
+ except:
3986
+ circ = orig_circ
3987
+
3988
+ return circ
3989
+
3990
+ def _apply_pyzx_op(self, gate):
3991
+ if gate.name == "XPhase":
3992
+ self.r(Pauli.PauliX, math.pi * gate.phase, gate.target)
3993
+ elif gate.name == "ZPhase":
3994
+ self.r(Pauli.PauliZ, math.pi * gate.phase, gate.target)
3995
+ elif gate.name == "Z":
3996
+ self.z(gate.target)
3997
+ elif gate.name == "S":
3998
+ self.s(gate.target)
3999
+ elif gate.name == "T":
4000
+ self.t(gate.target)
4001
+ elif gate.name == "NOT":
4002
+ self.x(gate.target)
4003
+ elif gate.name == "HAD":
4004
+ self.h(gate.target)
4005
+ elif gate.name == "CNOT":
4006
+ self.mcx([gate.control], gate.target)
4007
+ elif gate.name == "CZ":
4008
+ self.mcz([gate.control], gate.target)
4009
+ elif gate.name == "CX":
4010
+ self.h(gate.control)
4011
+ self.mcx([gate.control], gate.target)
4012
+ self.h(gate.control)
4013
+ elif gate.name == "SWAP":
4014
+ self.swap(gate.control, gate.target)
4015
+ elif gate.name == "CRZ":
4016
+ self.mcr(Pauli.PauliZ, math.pi * gate.phase, [gate.control], gate.target)
4017
+ elif gate.name == "CHAD":
4018
+ self.mch([gate.control], gate.target)
4019
+ elif gate.name == "ParityPhase":
4020
+ self.phase_parity(math.pi * gate.phase, gate.targets)
4021
+ elif gate.name == "FSim":
4022
+ self.fsim(gate.theta, gate.phi, gate.control, gate.target)
4023
+ elif gate.name == "CCZ":
4024
+ self.mcz([gate.ctrl1, gate.ctrl2], gate.target)
4025
+ elif gate.name == "Tof":
4026
+ self.mcx([gate.ctrl1, gate.ctrl2], gate.target)
4027
+ self._throw_if_error()
4028
+
4029
+ def run_pyzx_gates(self, gates):
4030
+ """PYZX Gates
4031
+
4032
+ Converts PYZX gates to `QRackSimulator` and immediately executes them.
4033
+
4034
+ Args:
4035
+ gates: list of PYZX gates
4036
+
4037
+ Raises:
4038
+ RuntimeError: QrackSimulator raised an exception.
4039
+ """
4040
+ for gate in gates:
4041
+ self._apply_pyzx_op(gate)
4042
+
4043
+ def _apply_op(self, operation):
4044
+ name = operation.name
4045
+
4046
+ if (name == "id") or (name == "barrier"):
4047
+ # Skip measurement logic
4048
+ return
4049
+
4050
+ conditional = getattr(operation, "conditional", None)
4051
+ if isinstance(conditional, int):
4052
+ conditional_bit_set = (self._classical_register >> conditional) & 1
4053
+ if not conditional_bit_set:
4054
+ return
4055
+ elif conditional is not None:
4056
+ mask = int(conditional.mask, 16)
4057
+ if mask > 0:
4058
+ value = self._classical_memory & mask
4059
+ while (mask & 0x1) == 0:
4060
+ mask >>= 1
4061
+ value >>= 1
4062
+ if value != int(conditional.val, 16):
4063
+ return
4064
+
4065
+ if (name == "u1") or (name == "p"):
4066
+ self._sim.u(operation.qubits[0]._index, 0, 0, float(operation.params[0]))
4067
+ elif name == "u2":
4068
+ self._sim.u(
4069
+ operation.qubits[0]._index,
4070
+ math.pi / 2,
4071
+ float(operation.params[0]),
4072
+ float(operation.params[1]),
4073
+ )
4074
+ elif (name == "u3") or (name == "u"):
4075
+ self._sim.u(
4076
+ operation.qubits[0]._index,
4077
+ float(operation.params[0]),
4078
+ float(operation.params[1]),
4079
+ float(operation.params[2]),
4080
+ )
4081
+ elif (name == "unitary") and (len(operation.qubits) == 1):
4082
+ self._sim.mtrx(operation.params[0].flatten(), operation.qubits[0]._index)
4083
+ elif name == "r":
4084
+ self._sim.u(
4085
+ operation.qubits[0]._index,
4086
+ float(operation.params[0]),
4087
+ float(operation.params[1]) - math.pi / 2,
4088
+ (-1 * float(operation.params[1])) + math.pi / 2,
4089
+ )
4090
+ elif name == "rx":
4091
+ self._sim.r(Pauli.PauliX, float(operation.params[0]), operation.qubits[0]._index)
4092
+ elif name == "ry":
4093
+ self._sim.r(Pauli.PauliY, float(operation.params[0]), operation.qubits[0]._index)
4094
+ elif name == "rz":
4095
+ self._sim.r(Pauli.PauliZ, float(operation.params[0]), operation.qubits[0]._index)
4096
+ elif name == "h":
4097
+ self._sim.h(operation.qubits[0]._index)
4098
+ elif name == "x":
4099
+ self._sim.x(operation.qubits[0]._index)
4100
+ elif name == "y":
4101
+ self._sim.y(operation.qubits[0]._index)
4102
+ elif name == "z":
4103
+ self._sim.z(operation.qubits[0]._index)
4104
+ elif name == "s":
4105
+ self._sim.s(operation.qubits[0]._index)
4106
+ elif name == "sdg":
4107
+ self._sim.adjs(operation.qubits[0]._index)
4108
+ elif name == "sx":
4109
+ self._sim.mtrx(
4110
+ [(1 + 1j) / 2, (1 - 1j) / 2, (1 - 1j) / 2, (1 + 1j) / 2],
4111
+ operation.qubits[0]._index,
4112
+ )
4113
+ elif name == "sxdg":
4114
+ self._sim.mtrx(
4115
+ [(1 - 1j) / 2, (1 + 1j) / 2, (1 + 1j) / 2, (1 - 1j) / 2],
4116
+ operation.qubits[0]._index,
4117
+ )
4118
+ elif name == "t":
4119
+ self._sim.t(operation.qubits[0]._index)
4120
+ elif name == "tdg":
4121
+ self._sim.adjt(operation.qubits[0]._index)
4122
+ elif name == "cu1":
4123
+ self._sim.mcu(
4124
+ [q._index for q in operation.qubits[0:1]],
4125
+ operation.qubits[1]._index,
4126
+ 0,
4127
+ 0,
4128
+ float(operation.params[0]),
4129
+ )
4130
+ elif name == "cu2":
4131
+ self._sim.mcu(
4132
+ [q._index for q in operation.qubits[0:1]],
4133
+ operation.qubits[1]._index,
4134
+ math.pi / 2,
4135
+ float(operation.params[0]),
4136
+ float(operation.params[1]),
4137
+ )
4138
+ elif (name == "cu3") or (name == "cu"):
4139
+ self._sim.mcu(
4140
+ [q._index for q in operation.qubits[0:1]],
4141
+ operation.qubits[1]._index,
4142
+ float(operation.params[0]),
4143
+ float(operation.params[1]),
4144
+ float(operation.params[2]),
4145
+ )
4146
+ elif name == "cx":
4147
+ self._sim.mcx([q._index for q in operation.qubits[0:1]], operation.qubits[1]._index)
4148
+ elif name == "cy":
4149
+ self._sim.mcy([q._index for q in operation.qubits[0:1]], operation.qubits[1]._index)
4150
+ elif name == "cz":
4151
+ self._sim.mcz([q._index for q in operation.qubits[0:1]], operation.qubits[1]._index)
4152
+ elif name == "ch":
4153
+ self._sim.mch([q._index for q in operation.qubits[0:1]], operation.qubits[1]._index)
4154
+ elif name == "cp":
4155
+ self._sim.mcmtrx(
4156
+ [q._index for q in operation.qubits[0:1]],
4157
+ [
4158
+ 1,
4159
+ 0,
4160
+ 0,
4161
+ math.cos(float(operation.params[0]))
4162
+ + 1j * math.sin(float(operation.params[0])),
4163
+ ],
4164
+ operation.qubits[1]._index,
4165
+ )
4166
+ elif name == "csx":
4167
+ self._sim.mcmtrx(
4168
+ [q._index for q in operation.qubits[0:1]],
4169
+ [(1 + 1j) / 2, (1 - 1j) / 2, (1 - 1j) / 2, (1 + 1j) / 2],
4170
+ operation.qubits[1]._index,
4171
+ )
4172
+ elif name == "csxdg":
4173
+ self._sim.mcmtrx(
4174
+ [q._index for q in operation.qubits[0:1]],
4175
+ [(1 - 1j) / 2, (1 + 1j) / 2, (1 + 1j) / 2, (1 - 1j) / 2],
4176
+ operation.qubits[1]._index,
4177
+ )
4178
+ elif name == "dcx":
4179
+ self._sim.mcx([q._index for q in operation.qubits[0:1]], operation.qubits[1]._index)
4180
+ self._sim.mcx(operation.qubits[1:2]._index, operation.qubits[0]._index)
4181
+ elif name == "ccx":
4182
+ self._sim.mcx([q._index for q in operation.qubits[0:2]], operation.qubits[2]._index)
4183
+ elif name == "ccy":
4184
+ self._sim.mcy([q._index for q in operation.qubits[0:2]], operation.qubits[2]._index)
4185
+ elif name == "ccz":
4186
+ self._sim.mcz([q._index for q in operation.qubits[0:2]], operation.qubits[2]._index)
4187
+ elif name == "mcx":
4188
+ self._sim.mcx([q._index for q in operation.qubits[0:-1]], operation.qubits[-1]._index)
4189
+ elif name == "mcy":
4190
+ self._sim.mcy([q._index for q in operation.qubits[0:-1]], operation.qubits[-1]._index)
4191
+ elif name == "mcz":
4192
+ self._sim.mcz([q._index for q in operation.qubits[0:-1]], operation.qubits[-1]._index)
4193
+ elif name == "swap":
4194
+ self._sim.swap(operation.qubits[0]._index, operation.qubits[1]._index)
4195
+ elif name == "iswap":
4196
+ self._sim.iswap(operation.qubits[0]._index, operation.qubits[1]._index)
4197
+ elif name == "iswap_dg":
4198
+ self._sim.adjiswap(operation.qubits[0]._index, operation.qubits[1]._index)
4199
+ elif name == "cswap":
4200
+ self._sim.cswap(
4201
+ [q._index for q in operation.qubits[0:1]],
4202
+ operation.qubits[1]._index,
4203
+ operation.qubits[2]._index,
4204
+ )
4205
+ elif name == "mcswap":
4206
+ self._sim.cswap(
4207
+ [q._index for q in operation.qubits[:-2]],
4208
+ operation.qubits[-2]._index,
4209
+ operation.qubits[-1]._index,
4210
+ )
4211
+ elif name == "reset":
4212
+ qubits = operation.qubits
4213
+ for qubit in qubits:
4214
+ if self._sim.m(qubit._index):
4215
+ self._sim.x(qubit._index)
4216
+ elif name == "measure":
4217
+ qubits = operation.qubits
4218
+ clbits = operation.clbits
4219
+ cregbits = (
4220
+ operation.register
4221
+ if hasattr(operation, "register")
4222
+ else len(operation.qubits) * [-1]
4223
+ )
4224
+
4225
+ self._sample_qubits += qubits
4226
+ self._sample_clbits += clbits
4227
+ self._sample_cregbits += cregbits
4228
+
4229
+ if not self._sample_measure:
4230
+ for index in range(len(qubits)):
4231
+ qubit_outcome = self._sim.m(qubits[index]._index)
4232
+
4233
+ clbit = clbits[index]
4234
+ clmask = 1 << clbit
4235
+ self._classical_memory = (self._classical_memory & (~clmask)) | (
4236
+ qubit_outcome << clbit
4237
+ )
4238
+
4239
+ cregbit = cregbits[index]
4240
+ if cregbit < 0:
4241
+ cregbit = clbit
4242
+
4243
+ regbit = 1 << cregbit
4244
+ self._classical_register = (self._classical_register & (~regbit)) | (
4245
+ qubit_outcome << cregbit
4246
+ )
4247
+
4248
+ elif name == "bfunc":
4249
+ mask = int(operation.mask, 16)
4250
+ relation = operation.relation
4251
+ val = int(operation.val, 16)
4252
+
4253
+ cregbit = operation.register
4254
+ cmembit = operation.memory if hasattr(operation, "memory") else None
4255
+
4256
+ compared = (self._classical_register & mask) - val
4257
+
4258
+ if relation == "==":
4259
+ outcome = compared == 0
4260
+ elif relation == "!=":
4261
+ outcome = compared != 0
4262
+ elif relation == "<":
4263
+ outcome = compared < 0
4264
+ elif relation == "<=":
4265
+ outcome = compared <= 0
4266
+ elif relation == ">":
4267
+ outcome = compared > 0
4268
+ elif relation == ">=":
4269
+ outcome = compared >= 0
4270
+ else:
4271
+ raise QrackError("Invalid boolean function relation.")
4272
+
4273
+ # Store outcome in register and optionally memory slot
4274
+ regbit = 1 << cregbit
4275
+ self._classical_register = (self._classical_register & (~regbit)) | (
4276
+ int(outcome) << cregbit
4277
+ )
4278
+ if cmembit is not None:
4279
+ membit = 1 << cmembit
4280
+ self._classical_memory = (self._classical_memory & (~membit)) | (
4281
+ int(outcome) << cmembit
4282
+ )
4283
+ else:
4284
+ err_msg = 'QrackSimulator encountered unrecognized operation "{0}"'
4285
+ raise RuntimeError(err_msg.format(operation))
4286
+
4287
+ def _add_sample_measure(self, sample_qubits, sample_clbits, num_samples):
4288
+ """Generate data samples from current statevector.
4289
+
4290
+ Taken almost straight from the terra source code.
4291
+
4292
+ Args:
4293
+ measure_params (list): List of (qubit, clbit) values for
4294
+ measure instructions to sample.
4295
+ num_samples (int): The number of data samples to generate.
4296
+
4297
+ Returns:
4298
+ list: A list of data values in hex format.
4299
+ """
4300
+ # Get unique qubits that are actually measured
4301
+ measure_qubit = [qubit for qubit in sample_qubits]
4302
+ measure_clbit = [clbit for clbit in sample_clbits]
4303
+
4304
+ # Sample and convert to bit-strings
4305
+ if num_samples == 1:
4306
+ sample = self._sim.m_all()
4307
+ result = 0
4308
+ for index in range(len(measure_qubit)):
4309
+ qubit = measure_qubit[index]._index
4310
+ qubit_outcome = (sample >> qubit) & 1
4311
+ result |= qubit_outcome << index
4312
+ measure_results = [result]
4313
+ else:
4314
+ measure_results = self._sim.measure_shots(
4315
+ [q._index for q in measure_qubit], num_samples
4316
+ )
4317
+
4318
+ data = []
4319
+ for sample in measure_results:
4320
+ for index in range(len(measure_qubit)):
4321
+ qubit_outcome = (sample >> index) & 1
4322
+ clbit = measure_clbit[index]._index
4323
+ clmask = 1 << clbit
4324
+ self._classical_memory = (self._classical_memory & (~clmask)) | (
4325
+ qubit_outcome << clbit
4326
+ )
4327
+
4328
+ data.append(bin(self._classical_memory)[2:].zfill(self.num_qubits()))
4329
+
4330
+ return data
4331
+
4332
+ def run_qiskit_circuit(self, experiment, shots=1):
4333
+ if not _IS_QISKIT_AVAILABLE:
4334
+ raise RuntimeError(
4335
+ "Before trying to run_qiskit_circuit() with QrackSimulator, you must install Qiskit!"
4336
+ )
4337
+
4338
+ instructions = []
4339
+ if isinstance(experiment, QuantumCircuit):
4340
+ instructions = experiment.data
4341
+ else:
4342
+ raise RuntimeError('Unrecognized "run_input" argument specified for run().')
4343
+
4344
+ self._shots = shots
4345
+ self._sample_qubits = []
4346
+ self._sample_clbits = []
4347
+ self._sample_cregbits = []
4348
+ self._sample_measure = True
4349
+ _data = []
4350
+ shotLoopMax = 1
4351
+
4352
+ is_initializing = True
4353
+ boundary_start = -1
4354
+
4355
+ for opcount in range(len(instructions)):
4356
+ operation = instructions[opcount]
4357
+
4358
+ if operation.name == "id" or operation.name == "barrier":
4359
+ continue
4360
+
4361
+ if is_initializing and ((operation.name == "measure") or (operation.name == "reset")):
4362
+ continue
4363
+
4364
+ is_initializing = False
4365
+
4366
+ if (operation.name == "measure") or (operation.name == "reset"):
4367
+ if boundary_start == -1:
4368
+ boundary_start = opcount
4369
+
4370
+ if (boundary_start != -1) and (operation.name != "measure"):
4371
+ shotsPerLoop = 1
4372
+ shotLoopMax = self._shots
4373
+ self._sample_measure = False
4374
+ break
4375
+
4376
+ preamble_memory = 0
4377
+ preamble_register = 0
4378
+ preamble_sim = None
4379
+
4380
+ if self._sample_measure or boundary_start <= 0:
4381
+ boundary_start = 0
4382
+ self._sample_measure = True
4383
+ shotsPerLoop = self._shots
4384
+ shotLoopMax = 1
4385
+ else:
4386
+ boundary_start -= 1
4387
+ if boundary_start > 0:
4388
+ self._sim = self
4389
+ self._classical_memory = 0
4390
+ self._classical_register = 0
4391
+
4392
+ for operation in instructions[:boundary_start]:
4393
+ self._apply_op(operation)
4394
+
4395
+ preamble_memory = self._classical_memory
4396
+ preamble_register = self._classical_register
4397
+ preamble_sim = self._sim
4398
+
4399
+ for shot in range(shotLoopMax):
4400
+ if preamble_sim is None:
4401
+ self._sim = self
4402
+ self._classical_memory = 0
4403
+ self._classical_register = 0
4404
+ else:
4405
+ self._sim = QrackSimulator(cloneSid=preamble_sim.sid)
4406
+ self._classical_memory = preamble_memory
4407
+ self._classical_register = preamble_register
4408
+
4409
+ for operation in instructions[boundary_start:]:
4410
+ self._apply_op(operation)
4411
+
4412
+ if not self._sample_measure and (len(self._sample_qubits) > 0):
4413
+ _data += [bin(self._classical_memory)[2:].zfill(self.num_qubits())]
4414
+ self._sample_qubits = []
4415
+ self._sample_clbits = []
4416
+ self._sample_cregbits = []
4417
+
4418
+ if self._sample_measure and (len(self._sample_qubits) > 0):
4419
+ _data = self._add_sample_measure(self._sample_qubits, self._sample_clbits, self._shots)
4420
+
4421
+ del self._sim
4422
+
4423
+ return _data
4424
+
4425
+ @staticmethod
4426
+ def get_qiskit_basis_gates():
4427
+ return [
4428
+ "id",
4429
+ "u",
4430
+ "u1",
4431
+ "u2",
4432
+ "u3",
4433
+ "r",
4434
+ "rx",
4435
+ "ry",
4436
+ "rz",
4437
+ "h",
4438
+ "x",
4439
+ "y",
4440
+ "z",
4441
+ "s",
4442
+ "sdg",
4443
+ "sx",
4444
+ "sxdg",
4445
+ "p",
4446
+ "t",
4447
+ "tdg",
4448
+ "cu",
4449
+ "cu1",
4450
+ "cu3",
4451
+ "cx",
4452
+ "cy",
4453
+ "cz",
4454
+ "ch",
4455
+ "cp",
4456
+ "csx",
4457
+ "ccx",
4458
+ "ccz",
4459
+ "swap",
4460
+ "iswap",
4461
+ "cswap",
4462
+ "reset",
4463
+ "measure",
4464
+ ]