pyqrack-cpu 1.32.7__py3-none-manylinux_2_31_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

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