pyqrack-cpu 1.75.0__py3-none-win_amd64.whl

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