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