pyqrack-cpu 1.69.1__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.

Potentially problematic release.


This version of pyqrack-cpu might be problematic. Click here for more details.

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