pyqrack-cuda 1.44.0__tar.gz → 1.44.2__tar.gz

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.
Files changed (30) hide show
  1. {pyqrack_cuda-1.44.0/pyqrack_cuda.egg-info → pyqrack_cuda-1.44.2}/PKG-INFO +1 -1
  2. pyqrack_cuda-1.44.2/pyqrack/qrack_ace_backend.py +602 -0
  3. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/qrack_simulator.py +1 -1
  4. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2/pyqrack_cuda.egg-info}/PKG-INFO +1 -1
  5. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/setup.py +1 -1
  6. pyqrack_cuda-1.44.0/pyqrack/qrack_ace_backend.py +0 -261
  7. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/LICENSE +0 -0
  8. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/MANIFEST.in +0 -0
  9. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/Makefile +0 -0
  10. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/README.md +0 -0
  11. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyproject.toml +0 -0
  12. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/__init__.py +0 -0
  13. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/neuron_activation_fn.py +0 -0
  14. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/pauli.py +0 -0
  15. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/qrack_circuit.py +0 -0
  16. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/qrack_neuron.py +0 -0
  17. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/qrack_neuron_torch_layer.py +0 -0
  18. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/qrack_stabilizer.py +0 -0
  19. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/qrack_system/__init__.py +0 -0
  20. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/qrack_system/qrack_system.py +0 -0
  21. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/quimb_circuit_type.py +0 -0
  22. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/stats/__init__.py +0 -0
  23. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/stats/load_quantized_data.py +0 -0
  24. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack/stats/quantize_by_range.py +0 -0
  25. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack_cuda.egg-info/SOURCES.txt +0 -0
  26. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack_cuda.egg-info/dependency_links.txt +0 -0
  27. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack_cuda.egg-info/not-zip-safe +0 -0
  28. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack_cuda.egg-info/requires.txt +0 -0
  29. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/pyqrack_cuda.egg-info/top_level.txt +0 -0
  30. {pyqrack_cuda-1.44.0 → pyqrack_cuda-1.44.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cuda
3
- Version: 1.44.0
3
+ Version: 1.44.2
4
4
  Summary: pyqrack - Pure Python vm6502q/qrack Wrapper
5
5
  Home-page: https://github.com/vm6502q/pyqrack
6
6
  Author: Daniel Strano
@@ -0,0 +1,602 @@
1
+ # (C) Daniel Strano and the Qrack contributors 2017-2025. All rights reserved.
2
+ #
3
+ # Use of this source code is governed by an MIT-style license that can be
4
+ # found in the LICENSE file or at https://opensource.org/licenses/MIT.
5
+ import math
6
+ import random
7
+ import sys
8
+ import time
9
+
10
+ from .qrack_simulator import QrackSimulator
11
+ from .pauli import Pauli
12
+
13
+
14
+ _IS_QISKIT_AVAILABLE = True
15
+ try:
16
+ from qiskit.circuit.quantumcircuit import QuantumCircuit
17
+ from qiskit.compiler import transpile
18
+ from qiskit.quantum_info.operators.symplectic.clifford import Clifford
19
+ except ImportError:
20
+ _IS_QISKIT_AVAILABLE = False
21
+
22
+
23
+ class QrackAceBackend:
24
+ """A back end for elided quantum error correction
25
+
26
+ This back end uses elided repetition code on a nearest-neighbor topology to emulate
27
+ a utility-scale superconducting chip quantum computer in very little memory.
28
+
29
+ The backend was originally designed assuming a 2D qubit grid like 2019 Sycamore.
30
+ However, it quickly became apparent that users can basically design their own
31
+ connectivity topologies, without breaking the concept. (Not all will work equally well.)
32
+
33
+ Attributes:
34
+ sim(QrackSimulator): Corresponding simulator.
35
+ """
36
+
37
+ def __init__(
38
+ self,
39
+ qubit_count=-1,
40
+ toClone=None
41
+ ):
42
+ self.sim = toClone.sim.clone() if toClone else QrackSimulator(3 * qubit_count)
43
+
44
+
45
+ def _ct_pair_prob(self, q1, q2):
46
+ p1 = self.sim.prob(q1)
47
+ p2 = self.sim.prob(q2)
48
+
49
+ if p1 < p2:
50
+ return p2, q1
51
+
52
+ return p1, q2
53
+
54
+
55
+ def _cz_shadow(self, q1, q2):
56
+ prob_max, t = self._ct_pair_prob(q1, q2)
57
+ if prob_max > 0.5:
58
+ self.sim.z(t)
59
+
60
+
61
+ def _anti_cz_shadow(self, q1, q2):
62
+ self.sim.x(q1)
63
+ self._cz_shadow(q1, q2)
64
+ self.sim.x(q1)
65
+
66
+
67
+ def _cx_shadow(self, c, t):
68
+ self.sim.h(t)
69
+ self._cz_shadow(c, t)
70
+ self.sim.h(t)
71
+
72
+
73
+ def _anti_cx_shadow(self, c, t):
74
+ self.sim.x(t)
75
+ self._cx_shadow(c, t)
76
+ self.sim.x(t)
77
+
78
+
79
+ def _cy_shadow(self, c, t):
80
+ self.sim.adjs(t)
81
+ self._cx_shadow(c, t)
82
+ self.sim.s(t)
83
+
84
+
85
+ def _anti_cy_shadow(self, c, t):
86
+ self.sim.x(t)
87
+ self._cy_shadow(c, t)
88
+ self.sim.x(t)
89
+
90
+
91
+ def _unpack(self, lq, reverse = False):
92
+ return [3 * lq + 2, 3 * lq + 1, 3 * lq] if reverse else [3 * lq, 3 * lq + 1, 3 * lq + 2]
93
+
94
+
95
+ def _encode(self, hq, reverse = False):
96
+ if reverse:
97
+ self._cx_shadow(hq[0], hq[1])
98
+ self.sim.mcx([hq[1]], hq[2])
99
+ else:
100
+ self.sim.mcx([hq[0]], hq[1])
101
+ self._cx_shadow(hq[1], hq[2])
102
+
103
+
104
+ def _decode(self, hq, reverse = False):
105
+ if reverse:
106
+ self.sim.mcx([hq[1]], hq[2])
107
+ self._cx_shadow(hq[0], hq[1])
108
+ else:
109
+ self._cx_shadow(hq[1], hq[2])
110
+ self.sim.mcx([hq[0]], hq[1])
111
+
112
+
113
+ def u(self, th, ph, lm, lq):
114
+ hq = self._unpack(lq)
115
+ self._decode(hq)
116
+ self.sim.u(hq[0], th, ph, lm)
117
+ self._encode(hq)
118
+
119
+
120
+ def r(self, p, th, lq):
121
+ hq = self._unpack(lq)
122
+ self._decode(hq)
123
+ self.sim.r(p, th, hq[0])
124
+ self._encode(hq)
125
+
126
+
127
+ def s(self, lq):
128
+ hq = self._unpack(lq)
129
+ self._decode(hq)
130
+ self.sim.s(hq[0])
131
+ self._encode(hq)
132
+
133
+
134
+ def adjs(self, lq):
135
+ hq = self._unpack(lq)
136
+ self._decode(hq)
137
+ self.sim.adjs(hq[0])
138
+ self._encode(hq)
139
+
140
+
141
+ def x(self, lq):
142
+ hq = self._unpack(lq)
143
+ self._decode(hq)
144
+ self.sim.x(hq[0])
145
+ self._encode(hq)
146
+
147
+
148
+ def y(self, lq):
149
+ hq = self._unpack(lq)
150
+ self._decode(hq)
151
+ self.sim.y(hq[0])
152
+ self._encode(hq)
153
+
154
+
155
+ def z(self, lq):
156
+ hq = self._unpack(lq)
157
+ self._decode(hq)
158
+ self.sim.z(hq[0])
159
+ self._encode(hq)
160
+
161
+
162
+ def h(self, lq):
163
+ hq = self._unpack(lq)
164
+ self._decode(hq)
165
+ self.sim.h(hq[0])
166
+ self._encode(hq)
167
+
168
+
169
+ def t(self, lq):
170
+ hq = self._unpack(lq)
171
+ self._decode(hq)
172
+ self.sim.t(hq[0])
173
+ self._encode(hq)
174
+
175
+
176
+ def adjt(self, lq):
177
+ hq = self._unpack(lq)
178
+ self._decode(hq)
179
+ self.sim.adjt(hq[0])
180
+ self._encode(hq)
181
+
182
+
183
+ def _cpauli(self, lq1, lq2, anti, pauli):
184
+ gate = None
185
+ if pauli == Pauli.PauliX:
186
+ gate = self.sim.macx if anti else self.sim.mcx
187
+ elif pauli == Pauli.PauliY:
188
+ gate = self.sim.macy if anti else self.sim.mcy
189
+ elif pauli == Pauli.PauliZ:
190
+ gate = self.sim.macz if anti else self.sim.mcz
191
+ else:
192
+ return
193
+
194
+ if (lq2 == (lq1 + 1)) or (lq1 == (lq2 + 1)):
195
+ hq1 = self._unpack(lq1, True)
196
+ hq2 = self._unpack(lq2, False)
197
+ self._decode(hq1, True)
198
+ self._decode(hq2, False)
199
+ gate([hq1[0]], hq2[0])
200
+ self._encode(hq2, False)
201
+ self._encode(hq1, True)
202
+ else:
203
+ hq1 = self._unpack(lq1)
204
+ hq2 = self._unpack(lq2)
205
+ gate([hq1[0]], hq2[0])
206
+ gate([hq1[1]], hq2[1])
207
+ gate([hq1[2]], hq2[2])
208
+
209
+
210
+ def cx(self, lq1, lq2):
211
+ self._cpauli(lq1, lq2, False, Pauli.PauliX)
212
+
213
+
214
+ def cy(self, lq1, lq2):
215
+ self._cpauli(lq1, lq2, False, Pauli.PauliY)
216
+
217
+
218
+ def cz(self, lq1, lq2):
219
+ self._cpauli(lq1, lq2, False, Pauli.PauliZ)
220
+
221
+
222
+ def acx(self, lq1, lq2):
223
+ self._cpauli(lq1, lq2, True, Pauli.PauliX)
224
+
225
+
226
+ def acy(self, lq1, lq2):
227
+ self._cpauli(lq1, lq2, True, Pauli.PauliY)
228
+
229
+
230
+ def acz(self, lq1, lq2):
231
+ self._cpauli(lq1, lq2, True, Pauli.PauliZ)
232
+
233
+
234
+ def swap(self, lq1, lq2):
235
+ self.cx(lq1, lq2)
236
+ self.cx(lq2, lq1)
237
+ self.cx(lq1, lq2)
238
+
239
+
240
+ def iswap(self, lq1, lq2):
241
+ self.swap(lq1, lq2)
242
+ self.cz(lq1, lq2)
243
+ self.s(lq1)
244
+ self.s(lq2)
245
+
246
+
247
+ def adjiswap(self, lq1, lq2):
248
+ self.adjs(lq2)
249
+ self.adjs(lq1)
250
+ self.cz(lq1, lq2)
251
+ self.swap(lq1, lq2)
252
+
253
+
254
+ def m(self, lq):
255
+ hq = self._unpack(lq)
256
+ syndrome = 0
257
+ bits = []
258
+ for q in hq:
259
+ bits.append(self.sim.m(q))
260
+ if bits[-1]:
261
+ syndrome += 1
262
+ result = True if (syndrome > 1) else False
263
+ for i in range(len(hq)):
264
+ if bits[i] != result:
265
+ self.sim.x(hq[i])
266
+
267
+ return result
268
+
269
+
270
+ def m_all(self):
271
+ result = 0
272
+ for lq in range(self.sim.num_qubits() // 3):
273
+ result <<= 1
274
+ if self.m(lq):
275
+ result |= 1
276
+
277
+ return result
278
+
279
+
280
+ def measure_shots(self, q, s):
281
+ _q = []
282
+ for i in q:
283
+ _q.append(3 * i)
284
+ _q.append(3 * i + 1)
285
+ _q.append(3 * i + 2)
286
+
287
+ samples = self.sim.measure_shots(_q, s)
288
+
289
+ results = []
290
+ for sample in samples:
291
+ logical_sample = 0
292
+ for i in range(len(q)):
293
+ logical_sample <<= 1
294
+ bit_count = 0
295
+ for _ in range(3):
296
+ if sample & 1:
297
+ bit_count += 1
298
+ sample >>= 1
299
+ if bit_count > 1:
300
+ logical_sample |= 1
301
+ results.append(logical_sample)
302
+
303
+ return results
304
+
305
+
306
+ def _apply_op(self, operation):
307
+ name = operation.name
308
+
309
+ if (name == 'id') or (name == 'barrier'):
310
+ # Skip measurement logic
311
+ return
312
+
313
+ conditional = getattr(operation, 'conditional', None)
314
+ if isinstance(conditional, int):
315
+ conditional_bit_set = (self._classical_register >> conditional) & 1
316
+ if not conditional_bit_set:
317
+ return
318
+ elif conditional is not None:
319
+ mask = int(conditional.mask, 16)
320
+ if mask > 0:
321
+ value = self._classical_memory & mask
322
+ while (mask & 0x1) == 0:
323
+ mask >>= 1
324
+ value >>= 1
325
+ if value != int(conditional.val, 16):
326
+ return
327
+
328
+ if (name == 'u1') or (name == 'p'):
329
+ self._sim.u(0, 0, float(operation.params[0]), operation.qubits[0]._index)
330
+ elif name == 'u2':
331
+ self._sim.u(
332
+ math.pi / 2,
333
+ float(operation.params[0]),
334
+ float(operation.params[1]),
335
+ operation.qubits[0]._index
336
+ )
337
+ elif (name == 'u3') or (name == 'u'):
338
+ self._sim.u(
339
+ float(operation.params[0]),
340
+ float(operation.params[1]),
341
+ float(operation.params[2]),
342
+ operation.qubits[0]._index
343
+ )
344
+ elif name == 'r':
345
+ self._sim.u(
346
+ float(operation.params[0]),
347
+ float(operation.params[1]) - math.pi / 2,
348
+ (-1 * float(operation.params[1])) + math.pi / 2,
349
+ operation.qubits[0]._index
350
+ )
351
+ elif name == 'rx':
352
+ self._sim.r(Pauli.PauliX, float(operation.params[0]), operation.qubits[0]._index)
353
+ elif name == 'ry':
354
+ self._sim.r(Pauli.PauliY, float(operation.params[0]), operation.qubits[0]._index)
355
+ elif name == 'rz':
356
+ self._sim.r(Pauli.PauliZ, float(operation.params[0]), operation.qubits[0]._index)
357
+ elif name == 'h':
358
+ self._sim.h(operation.qubits[0]._index)
359
+ elif name == 'x':
360
+ self._sim.x(operation.qubits[0]._index)
361
+ elif name == 'y':
362
+ self._sim.y(operation.qubits[0]._index)
363
+ elif name == 'z':
364
+ self._sim.z(operation.qubits[0]._index)
365
+ elif name == 's':
366
+ self._sim.s(operation.qubits[0]._index)
367
+ elif name == 'sdg':
368
+ self._sim.adjs(operation.qubits[0]._index)
369
+ elif name == 't':
370
+ self._sim.t(operation.qubits[0]._index)
371
+ elif name == 'tdg':
372
+ self._sim.adjt(operation.qubits[0]._index)
373
+ elif name == 'cx':
374
+ self._sim.cx(operation.qubits[0]._index, operation.qubits[1]._index)
375
+ elif name == 'cy':
376
+ self._sim.cy(operation.qubits[0]._index, operation.qubits[1]._index)
377
+ elif name == 'cz':
378
+ self._sim.cz(operation.qubits[0]._index, operation.qubits[1]._index)
379
+ elif name == 'dcx':
380
+ self._sim.mcx(operation.qubits[0]._index, operation.qubits[1]._index)
381
+ self._sim.mcx(operation.qubits[1]._index, operation.qubits[0]._index)
382
+ elif name == 'swap':
383
+ self._sim.swap(operation.qubits[0]._index, operation.qubits[1]._index)
384
+ elif name == 'iswap':
385
+ self._sim.iswap(operation.qubits[0]._index, operation.qubits[1]._index)
386
+ elif name == 'iswap_dg':
387
+ self._sim.adjiswap(operation.qubits[0]._index, operation.qubits[1]._index)
388
+ elif name == 'reset':
389
+ qubits = operation.qubits
390
+ for qubit in qubits:
391
+ if self._sim.m(qubit._index):
392
+ self._sim.x(qubit._index)
393
+ elif name == 'measure':
394
+ qubits = operation.qubits
395
+ clbits = operation.clbits
396
+ cregbits = (
397
+ operation.register
398
+ if hasattr(operation, 'register')
399
+ else len(operation.qubits) * [-1]
400
+ )
401
+
402
+ self._sample_qubits += qubits
403
+ self._sample_clbits += clbits
404
+ self._sample_cregbits += cregbits
405
+
406
+ if not self._sample_measure:
407
+ for index in range(len(qubits)):
408
+ qubit_outcome = self._sim.m(qubits[index]._index)
409
+
410
+ clbit = clbits[index]
411
+ clmask = 1 << clbit
412
+ self._classical_memory = (self._classical_memory & (~clmask)) | (
413
+ qubit_outcome << clbit
414
+ )
415
+
416
+ cregbit = cregbits[index]
417
+ if cregbit < 0:
418
+ cregbit = clbit
419
+
420
+ regbit = 1 << cregbit
421
+ self._classical_register = (
422
+ self._classical_register & (~regbit)
423
+ ) | (qubit_outcome << cregbit)
424
+
425
+ elif name == 'bfunc':
426
+ mask = int(operation.mask, 16)
427
+ relation = operation.relation
428
+ val = int(operation.val, 16)
429
+
430
+ cregbit = operation.register
431
+ cmembit = operation.memory if hasattr(operation, 'memory') else None
432
+
433
+ compared = (self._classical_register & mask) - val
434
+
435
+ if relation == '==':
436
+ outcome = compared == 0
437
+ elif relation == '!=':
438
+ outcome = compared != 0
439
+ elif relation == '<':
440
+ outcome = compared < 0
441
+ elif relation == '<=':
442
+ outcome = compared <= 0
443
+ elif relation == '>':
444
+ outcome = compared > 0
445
+ elif relation == '>=':
446
+ outcome = compared >= 0
447
+ else:
448
+ raise QrackError('Invalid boolean function relation.')
449
+
450
+ # Store outcome in register and optionally memory slot
451
+ regbit = 1 << cregbit
452
+ self._classical_register = (self._classical_register & (~regbit)) | (
453
+ int(outcome) << cregbit
454
+ )
455
+ if cmembit is not None:
456
+ membit = 1 << cmembit
457
+ self._classical_memory = (self._classical_memory & (~membit)) | (
458
+ int(outcome) << cmembit
459
+ )
460
+ else:
461
+ err_msg = 'QrackAceBackend encountered unrecognized operation "{0}"'
462
+ raise RuntimeError(err_msg.format(operation))
463
+
464
+ def _add_sample_measure(self, sample_qubits, sample_clbits, num_samples):
465
+ """Generate data samples from current statevector.
466
+
467
+ Taken almost straight from the terra source code.
468
+
469
+ Args:
470
+ measure_params (list): List of (qubit, clbit) values for
471
+ measure instructions to sample.
472
+ num_samples (int): The number of data samples to generate.
473
+
474
+ Returns:
475
+ list: A list of data values in hex format.
476
+ """
477
+ # Get unique qubits that are actually measured
478
+ measure_qubit = [qubit for qubit in sample_qubits]
479
+ measure_clbit = [clbit for clbit in sample_clbits]
480
+
481
+ # Sample and convert to bit-strings
482
+ if num_samples == 1:
483
+ sample = self._sim.m_all()
484
+ result = 0
485
+ for index in range(len(measure_qubit)):
486
+ qubit = measure_qubit[index]._index
487
+ qubit_outcome = (sample >> qubit) & 1
488
+ result |= qubit_outcome << index
489
+ measure_results = [result]
490
+ else:
491
+ measure_results = self._sim.measure_shots([q._index for q in measure_qubit], num_samples)
492
+
493
+ data = []
494
+ for sample in measure_results:
495
+ for index in range(len(measure_qubit)):
496
+ qubit_outcome = (sample >> index) & 1
497
+ clbit = measure_clbit[index]._index
498
+ clmask = 1 << clbit
499
+ self._classical_memory = (self._classical_memory & (~clmask)) | (
500
+ qubit_outcome << clbit
501
+ )
502
+
503
+ data.append(bin(self._classical_memory)[2:].zfill(self.num_qubits()))
504
+
505
+ return data
506
+
507
+ def run_qiskit_circuit(self, experiment, shots=1):
508
+ if not _IS_QISKIT_AVAILABLE:
509
+ raise RuntimeError(
510
+ "Before trying to run_qiskit_circuit() with QrackAceBackend, you must install Qiskit!"
511
+ )
512
+
513
+ instructions = []
514
+ if isinstance(experiment, QuantumCircuit):
515
+ instructions = experiment.data
516
+ else:
517
+ raise RuntimeError('Unrecognized "run_input" argument specified for run().')
518
+
519
+ self._shots = shots
520
+ self._sample_qubits = []
521
+ self._sample_clbits = []
522
+ self._sample_cregbits = []
523
+ self._sample_measure = True
524
+ _data = []
525
+ shotLoopMax = 1
526
+
527
+ is_initializing = True
528
+ boundary_start = -1
529
+
530
+ for opcount in range(len(instructions)):
531
+ operation = instructions[opcount]
532
+
533
+ if operation.name == 'id' or operation.name == 'barrier':
534
+ continue
535
+
536
+ if is_initializing and (
537
+ (operation.name == 'measure') or (operation.name == 'reset')
538
+ ):
539
+ continue
540
+
541
+ is_initializing = False
542
+
543
+ if (operation.name == 'measure') or (operation.name == 'reset'):
544
+ if boundary_start == -1:
545
+ boundary_start = opcount
546
+
547
+ if (boundary_start != -1) and (operation.name != 'measure'):
548
+ shotsPerLoop = 1
549
+ shotLoopMax = self._shots
550
+ self._sample_measure = False
551
+ break
552
+
553
+ preamble_memory = 0
554
+ preamble_register = 0
555
+ preamble_sim = None
556
+
557
+ if self._sample_measure or boundary_start <= 0:
558
+ boundary_start = 0
559
+ self._sample_measure = True
560
+ shotsPerLoop = self._shots
561
+ shotLoopMax = 1
562
+ else:
563
+ boundary_start -= 1
564
+ if boundary_start > 0:
565
+ self._sim = self
566
+ self._classical_memory = 0
567
+ self._classical_register = 0
568
+
569
+ for operation in instructions[:boundary_start]:
570
+ self._apply_op(operation)
571
+
572
+ preamble_memory = self._classical_memory
573
+ preamble_register = self._classical_register
574
+ preamble_sim = self._sim
575
+
576
+ for shot in range(shotLoopMax):
577
+ if preamble_sim is None:
578
+ self._sim = self
579
+ self._classical_memory = 0
580
+ self._classical_register = 0
581
+ else:
582
+ self._sim = QrackAceBackend(toClone=preamble_sim)
583
+ self._classical_memory = preamble_memory
584
+ self._classical_register = preamble_register
585
+
586
+ for operation in instructions[boundary_start:]:
587
+ self._apply_op(operation)
588
+
589
+ if not self._sample_measure and (len(self._sample_qubits) > 0):
590
+ _data += [bin(self._classical_memory)[2:].zfill(self.num_qubits())]
591
+ self._sample_qubits = []
592
+ self._sample_clbits = []
593
+ self._sample_cregbits = []
594
+
595
+ if self._sample_measure and (len(self._sample_qubits) > 0):
596
+ _data = self._add_sample_measure(
597
+ self._sample_qubits, self._sample_clbits, self._shots
598
+ )
599
+
600
+ del self._sim
601
+
602
+ return _data
@@ -3843,7 +3843,7 @@ class QrackSimulator:
3843
3843
 
3844
3844
  if not self._sample_measure:
3845
3845
  for index in range(len(qubits)):
3846
- qubit_outcome = self._sim.m(qubits[index])
3846
+ qubit_outcome = self._sim.m(qubits[index]._index)
3847
3847
 
3848
3848
  clbit = clbits[index]
3849
3849
  clmask = 1 << clbit
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cuda
3
- Version: 1.44.0
3
+ Version: 1.44.2
4
4
  Summary: pyqrack - Pure Python vm6502q/qrack Wrapper
5
5
  Home-page: https://github.com/vm6502q/pyqrack
6
6
  Author: Daniel Strano
@@ -7,7 +7,7 @@ from setuptools import setup
7
7
  from setuptools.command.build_py import build_py
8
8
 
9
9
 
10
- VERSION = "1.44.0"
10
+ VERSION = "1.44.2"
11
11
 
12
12
  # Read long description from README.
13
13
  README_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.md')
@@ -1,261 +0,0 @@
1
- # (C) Daniel Strano and the Qrack contributors 2017-2025. All rights reserved.
2
- #
3
- # Use of this source code is governed by an MIT-style license that can be
4
- # found in the LICENSE file or at https://opensource.org/licenses/MIT.
5
- import math
6
- import random
7
- import sys
8
- import time
9
-
10
- from .qrack_simulator import QrackSimulator
11
- from .pauli import Pauli
12
-
13
-
14
- class QrackAceBackend:
15
- """A back end for elided quantum error correction
16
-
17
- This back end uses elided repetition code on a nearest-neighbor topology to emulate
18
- a utility-scale superconducting chip quantum computer in very little memory.
19
-
20
- The backend was originally designed assuming a 2D qubit grid like 2019 Sycamore.
21
- However, it quickly became apparent that users can basically design their own
22
- connectivity topologies, without breaking the concept. (Not all will work equally well.)
23
-
24
- Attributes:
25
- sim(QrackSimulator): Corresponding simulator.
26
- """
27
-
28
- def __init__(
29
- self,
30
- qubit_count=-1,
31
- ):
32
- self.sim = QrackSimulator(3 * qubit_count)
33
-
34
-
35
- def _ct_pair_prob(self, q1, q2):
36
- p1 = self.sim.prob(q1)
37
- p2 = self.sim.prob(q2)
38
-
39
- if p1 < p2:
40
- return p2, q1
41
-
42
- return p1, q2
43
-
44
-
45
- def _cz_shadow(self, q1, q2):
46
- prob_max, t = self._ct_pair_prob(q1, q2)
47
- if prob_max > 0.5:
48
- self.sim.z(t)
49
-
50
-
51
- def _anti_cz_shadow(self, q1, q2):
52
- self.sim.x(q1)
53
- self._cz_shadow(q1, q2)
54
- self.sim.x(q1)
55
-
56
-
57
- def _cx_shadow(self, c, t):
58
- self.sim.h(t)
59
- self._cz_shadow(c, t)
60
- self.sim.h(t)
61
-
62
-
63
- def _anti_cx_shadow(self, c, t):
64
- self.sim.x(t)
65
- self._cx_shadow(c, t)
66
- self.sim.x(t)
67
-
68
-
69
- def _cy_shadow(self, c, t):
70
- self.sim.adjs(t)
71
- self._cx_shadow(c, t)
72
- self.sim.s(t)
73
-
74
-
75
- def _anti_cy_shadow(self, c, t):
76
- self.sim.x(t)
77
- self._cy_shadow(c, t)
78
- self.sim.x(t)
79
-
80
-
81
- def _unpack(self, lq, reverse = False):
82
- return [3 * lq + 2, 3 * lq + 1, 3 * lq] if reverse else [3 * lq, 3 * lq + 1, 3 * lq + 2]
83
-
84
-
85
- def _encode(self, hq, reverse = False):
86
- if reverse:
87
- self._cx_shadow(hq[0], hq[1])
88
- self.sim.mcx([hq[1]], hq[2])
89
- else:
90
- self.sim.mcx([hq[0]], hq[1])
91
- self._cx_shadow(hq[1], hq[2])
92
-
93
-
94
- def _decode(self, hq, reverse = False):
95
- if reverse:
96
- self.sim.mcx([hq[1]], hq[2])
97
- self._cx_shadow(hq[0], hq[1])
98
- else:
99
- self._cx_shadow(hq[1], hq[2])
100
- self.sim.mcx([hq[0]], hq[1])
101
-
102
-
103
- def u(self, th, ph, lm, lq):
104
- hq = self._unpack(lq)
105
- self._decode(hq)
106
- self.sim.u(hq[0], th, ph, lm)
107
- self._encode(hq)
108
-
109
-
110
- def s(self, lq):
111
- hq = self._unpack(lq)
112
- self._decode(hq)
113
- self.sim.s(hq[0])
114
- self._encode(hq)
115
-
116
-
117
- def adjs(self, lq):
118
- hq = self._unpack(lq)
119
- self._decode(hq)
120
- self.sim.adjs(hq[0])
121
- self._encode(hq)
122
-
123
-
124
- def x(self, lq):
125
- hq = self._unpack(lq)
126
- self._decode(hq)
127
- self.sim.x(hq[0])
128
- self._encode(hq)
129
-
130
-
131
- def y(self, lq):
132
- hq = self._unpack(lq)
133
- self._decode(hq)
134
- self.sim.y(hq[0])
135
- self._encode(hq)
136
-
137
-
138
- def z(self, lq):
139
- hq = self._unpack(lq)
140
- self._decode(hq)
141
- self.sim.z(hq[0])
142
- self._encode(hq)
143
-
144
-
145
- def h(self, lq):
146
- hq = self._unpack(lq)
147
- self._decode(hq)
148
- self.sim.h(hq[0])
149
- self._encode(hq)
150
-
151
-
152
- def t(self, lq):
153
- hq = self._unpack(lq)
154
- self._decode(hq)
155
- self.sim.t(hq[0])
156
- self._encode(hq)
157
-
158
-
159
- def adjt(self, lq):
160
- hq = self._unpack(lq)
161
- self._decode(hq)
162
- self.sim.adjt(hq[0])
163
- self._encode(hq)
164
-
165
-
166
- def _cpauli(self, lq1, lq2, anti, pauli):
167
- gate = None
168
- if pauli == Pauli.PauliX:
169
- gate = self.sim.macx if anti else self.sim.mcx
170
- elif pauli == Pauli.PauliY:
171
- gate = self.sim.macy if anti else self.sim.mcy
172
- elif pauli == Pauli.PauliZ:
173
- gate = self.sim.macz if anti else self.sim.mcz
174
- else:
175
- return
176
-
177
- if (lq2 == (lq1 + 1)) or (lq1 == (lq2 + 1)):
178
- hq1 = self._unpack(lq1, True)
179
- hq2 = self._unpack(lq2, False)
180
- self._decode(hq1, True)
181
- self._decode(hq2, False)
182
- gate([hq1[0]], hq2[0])
183
- self._encode(hq2, False)
184
- self._encode(hq1, True)
185
- else:
186
- hq1 = self._unpack(lq1)
187
- hq2 = self._unpack(lq2)
188
- gate([hq1[0]], hq2[0])
189
- gate([hq1[1]], hq2[1])
190
- gate([hq1[2]], hq2[2])
191
-
192
-
193
- def cx(self, lq1, lq2):
194
- self._cpauli(lq1, lq2, False, Pauli.PauliX)
195
-
196
-
197
- def cy(self, lq1, lq2):
198
- self._cpauli(lq1, lq2, False, Pauli.PauliY)
199
-
200
-
201
- def cz(self, lq1, lq2):
202
- self._cpauli(lq1, lq2, False, Pauli.PauliZ)
203
-
204
-
205
- def acx(self, lq1, lq2):
206
- self._cpauli(lq1, lq2, True, Pauli.PauliX)
207
-
208
-
209
- def acy(self, lq1, lq2):
210
- self._cpauli(lq1, lq2, True, Pauli.PauliY)
211
-
212
-
213
- def acz(self, lq1, lq2):
214
- self._cpauli(lq1, lq2, True, Pauli.PauliZ)
215
-
216
-
217
- def swap(self, lq1, lq2):
218
- self.cx(lq1, lq2)
219
- self.cx(lq2, lq1)
220
- self.cx(lq1, lq2)
221
-
222
-
223
- def iswap(self, lq1, lq2):
224
- self.swap(lq1, lq2)
225
- self.cz(lq1, lq2)
226
- self.s(lq1)
227
- self.s(lq2)
228
-
229
-
230
- def adjiswap(self, lq1, lq2):
231
- self.adjs(lq2)
232
- self.adjs(lq1)
233
- self.cz(lq1, lq2)
234
- self.swap(lq1, lq2)
235
-
236
-
237
- def m(self, lq):
238
- hq = self._unpack(lq)
239
- syndrome = 0
240
- bits = []
241
- for q in hq:
242
- bits.append(self.sim.m(q))
243
- if bits[-1]:
244
- syndrome += 1
245
- result = True if (syndrome > 1) else False
246
- for i in range(len(hq)):
247
- if bits[i] != result:
248
- self.sim.x(hq[i])
249
-
250
- return result
251
-
252
-
253
- def m_all(self):
254
- result = 0
255
- for lq in range(self.sim.num_qubits() // 3):
256
- result <<= 1
257
- if self.m(lq):
258
- result |= 1
259
-
260
- return result
261
-
File without changes
File without changes
File without changes
File without changes
File without changes