pyqrack-cuda 1.49.9__tar.gz → 1.50.1__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 (29) hide show
  1. {pyqrack_cuda-1.49.9/pyqrack_cuda.egg-info → pyqrack_cuda-1.50.1}/PKG-INFO +1 -1
  2. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/qrack_ace_backend.py +295 -289
  3. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1/pyqrack_cuda.egg-info}/PKG-INFO +1 -1
  4. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/setup.py +1 -1
  5. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/LICENSE +0 -0
  6. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/MANIFEST.in +0 -0
  7. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/Makefile +0 -0
  8. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/README.md +0 -0
  9. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyproject.toml +0 -0
  10. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/__init__.py +0 -0
  11. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/neuron_activation_fn.py +0 -0
  12. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/pauli.py +0 -0
  13. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/qrack_circuit.py +0 -0
  14. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/qrack_neuron.py +0 -0
  15. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/qrack_neuron_torch_layer.py +0 -0
  16. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/qrack_simulator.py +0 -0
  17. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/qrack_stabilizer.py +0 -0
  18. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/qrack_system/__init__.py +0 -0
  19. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/qrack_system/qrack_system.py +0 -0
  20. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/quimb_circuit_type.py +0 -0
  21. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/stats/__init__.py +0 -0
  22. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/stats/load_quantized_data.py +0 -0
  23. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack/stats/quantize_by_range.py +0 -0
  24. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack_cuda.egg-info/SOURCES.txt +0 -0
  25. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack_cuda.egg-info/dependency_links.txt +0 -0
  26. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack_cuda.egg-info/not-zip-safe +0 -0
  27. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack_cuda.egg-info/requires.txt +0 -0
  28. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/pyqrack_cuda.egg-info/top_level.txt +0 -0
  29. {pyqrack_cuda-1.49.9 → pyqrack_cuda-1.50.1}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cuda
3
- Version: 1.49.9
3
+ Version: 1.50.1
4
4
  Summary: pyqrack - Pure Python vm6502q/qrack Wrapper
5
5
  Home-page: https://github.com/vm6502q/pyqrack
6
6
  Author: Daniel Strano
@@ -3,6 +3,7 @@
3
3
  # Use of this source code is governed by an MIT-style license that can be
4
4
  # found in the LICENSE file or at https://opensource.org/licenses/MIT.
5
5
  import math
6
+ import os
6
7
  import random
7
8
  import sys
8
9
  import time
@@ -48,7 +49,7 @@ class QrackAceBackend:
48
49
  def __init__(
49
50
  self,
50
51
  qubit_count=1,
51
- long_range_columns=2,
52
+ long_range_columns=-1,
52
53
  alternating_codes=True,
53
54
  reverse_row_and_col=False,
54
55
  isTensorNetwork=False,
@@ -56,8 +57,6 @@ class QrackAceBackend:
56
57
  isBinaryDecisionTree=False,
57
58
  toClone=None,
58
59
  ):
59
- if long_range_columns < 0:
60
- long_range_columns = 0
61
60
  if qubit_count < 0:
62
61
  qubit_count = 0
63
62
  if toClone:
@@ -65,43 +64,75 @@ class QrackAceBackend:
65
64
  long_range_columns = toClone.long_range_columns
66
65
 
67
66
  self._factor_width(qubit_count, reverse_row_and_col)
68
- self.alternating_codes = alternating_codes
67
+ if long_range_columns < 0:
68
+ long_range_columns = 3 if (self.row_length % 3) == 1 else 2
69
69
  self.long_range_columns = long_range_columns
70
- self._is_init = [False] * qubit_count
70
+
71
+ self.alternating_codes = alternating_codes
71
72
  self._coupling_map = None
72
73
 
73
74
  # If there's only one or zero "False" columns,
74
75
  # the entire simulator is connected, anyway.
76
+ len_col_seq = long_range_columns + 1
77
+ sim_count = (self.row_length + len_col_seq - 1) // len_col_seq
75
78
  if (long_range_columns + 1) >= self.row_length:
76
79
  self._is_col_long_range = [True] * self.row_length
77
80
  else:
78
81
  col_seq = [True] * long_range_columns + [False]
79
- len_col_seq = len(col_seq)
80
- self._is_col_long_range = (
81
- col_seq * ((self.row_length + len_col_seq - 1) // len_col_seq)
82
- )[: self.row_length]
82
+ self._is_col_long_range = (col_seq * sim_count)[: self.row_length]
83
83
  if long_range_columns < self.row_length:
84
84
  self._is_col_long_range[-1] = False
85
85
 
86
+ self._qubit_dict = {}
86
87
  self._hardware_offset = []
88
+ self._ancilla = [0] * sim_count
89
+ sim_counts = [0] * sim_count
90
+ sim_id = 0
87
91
  tot_qubits = 0
88
- for _ in range(self.col_length):
92
+ for r in range(self.col_length):
89
93
  for c in self._is_col_long_range:
90
94
  self._hardware_offset.append(tot_qubits)
91
- tot_qubits += 1 if c else 3
92
- self._ancilla = tot_qubits
93
- tot_qubits += 1
94
-
95
- self.sim = (
96
- toClone.sim.clone()
97
- if toClone
98
- else QrackSimulator(
99
- tot_qubits,
100
- isTensorNetwork=isTensorNetwork,
101
- isStabilizerHybrid=isStabilizerHybrid,
102
- isBinaryDecisionTree=isBinaryDecisionTree,
95
+ if c:
96
+ self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
97
+ tot_qubits += 1
98
+ sim_counts[sim_id] += 1
99
+ elif not self.alternating_codes or not (r & 1):
100
+ self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
101
+ tot_qubits += 1
102
+ sim_counts[sim_id] += 1
103
+ sim_id = (sim_id + 1) % sim_count
104
+ for _ in range(2):
105
+ self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
106
+ tot_qubits += 1
107
+ sim_counts[sim_id] += 1
108
+ else:
109
+ for _ in range(2):
110
+ self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
111
+ tot_qubits += 1
112
+ sim_counts[sim_id] += 1
113
+ sim_id = (sim_id + 1) % sim_count
114
+ self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
115
+ tot_qubits += 1
116
+ sim_counts[sim_id] += 1
117
+
118
+ self.sim = []
119
+ for i in range(sim_count):
120
+ self._ancilla[i] = sim_counts[i]
121
+ sim_counts[i] += 1
122
+ self.sim.append(
123
+ toClone.sim[i].clone()
124
+ if toClone
125
+ else QrackSimulator(
126
+ sim_counts[i],
127
+ isTensorNetwork=isTensorNetwork,
128
+ isStabilizerHybrid=isStabilizerHybrid,
129
+ isBinaryDecisionTree=isBinaryDecisionTree,
130
+ )
103
131
  )
104
- )
132
+
133
+ # You can still "monkey-patch" this, after the constructor.
134
+ if "QRACK_QUNIT_SEPARABILITY_THRESHOLD" not in os.environ:
135
+ self.sim[i].set_sdrp(0.03)
105
136
 
106
137
  def clone(self):
107
138
  return QrackAceBackend(toClone=self)
@@ -119,8 +150,8 @@ class QrackAceBackend:
119
150
  self.row_length = col_len if reverse else row_len
120
151
 
121
152
  def _ct_pair_prob(self, q1, q2):
122
- p1 = self.sim.prob(q1)
123
- p2 = self.sim.prob(q2)
153
+ p1 = self.sim[q1[0]].prob(q1[1])
154
+ p2 = self.sim[q2[0]].prob(q2[1])
124
155
 
125
156
  if p1 < p2:
126
157
  return p2, q1
@@ -130,96 +161,77 @@ class QrackAceBackend:
130
161
  def _cz_shadow(self, q1, q2):
131
162
  prob_max, t = self._ct_pair_prob(q1, q2)
132
163
  if prob_max > 0.5:
133
- self.sim.z(t)
164
+ self.sim[t[0]].z(t[1])
134
165
 
135
- def _anti_cz_shadow(self, q1, q2):
136
- self.sim.x(q1)
137
- self._cz_shadow(q1, q2)
138
- self.sim.x(q1)
166
+ def _anti_cz_shadow(self, c, t):
167
+ self.sim[c[0]].x(c[1])
168
+ self._cz_shadow(c, t)
169
+ self.sim[c[0]].x(c[1])
139
170
 
140
171
  def _cx_shadow(self, c, t):
141
- self.sim.h(t)
172
+ self.sim[t[0]].h(t[1])
142
173
  self._cz_shadow(c, t)
143
- self.sim.h(t)
174
+ self.sim[t[0]].h(t[1])
144
175
 
145
176
  def _anti_cx_shadow(self, c, t):
146
- self.sim.x(t)
177
+ self.sim[c[0]].x(c[1])
147
178
  self._cx_shadow(c, t)
148
- self.sim.x(t)
179
+ self.sim[c[0]].x(c[1])
149
180
 
150
181
  def _cy_shadow(self, c, t):
151
- self.sim.adjs(t)
182
+ self.sim[t[0]].adjs(t[1])
152
183
  self._cx_shadow(c, t)
153
- self.sim.s(t)
184
+ self.sim[t[0]].s(t[1])
154
185
 
155
186
  def _anti_cy_shadow(self, c, t):
156
- self.sim.x(t)
187
+ self.sim[c[0]].x(c[1])
157
188
  self._cy_shadow(c, t)
158
- self.sim.x(t)
189
+ self.sim[c[0]].x(c[1])
159
190
 
160
191
  def _ccz_shadow(self, c1, q2, q3):
161
- self.sim.mcx([q2], q3)
162
- self.sim.adjt(q3)
192
+ self.sim[q2[0]].mcx([q2[1]], q3[1])
193
+ self.sim[q3[0]].adjt(q3[1])
163
194
  self._cx_shadow(c1, q3)
164
- self.sim.t(q3)
165
- self.sim.mcx([q2], q3)
166
- self.sim.adjt(q3)
195
+ self.sim[q3[0]].t(q3[1])
196
+ self.sim[q2[0]].mcx([q2[1]], q3[1])
197
+ self.sim[q3[0]].adjt(q3[1])
167
198
  self._cx_shadow(c1, q3)
168
- self.sim.t(q3)
169
- self.sim.t(q2)
199
+ self.sim[q3[0]].t(q3[1])
200
+ self.sim[q2[0]].t(q2[1])
170
201
  self._cx_shadow(c1, q2)
171
- self.sim.adjt(q2)
172
- self.sim.t(c1)
202
+ self.sim[q2[0]].adjt(q2[1])
203
+ self.sim[c1[0]].t(c1[1])
173
204
  self._cx_shadow(c1, q2)
174
205
 
175
- def _ccx_shadow(self, c1, q2, q3):
176
- self.sim.h(q3)
177
- self._ccz_shadow(c1, q2, q3)
178
- self.sim.h(q3)
206
+ def _ccx_shadow(self, c1, q2, t):
207
+ self.sim[t[0]].h(t[1])
208
+ self._ccz_shadow(c1, q2, t)
209
+ self.sim[t[0]].h(t[1])
179
210
 
180
- def _unpack(self, lq, reverse=False):
211
+ def _unpack(self, lq):
181
212
  offset = self._hardware_offset[lq]
182
213
 
183
214
  if self._is_col_long_range[lq % self.row_length]:
184
- return [offset]
185
-
186
- return (
187
- [offset + 2, offset + 1, offset]
188
- if reverse
189
- else [offset, offset + 1, offset + 2]
190
- )
215
+ return [self._qubit_dict[offset]]
191
216
 
192
- def _encode(self, lq, hq, reverse=False):
193
- even_row = not ((lq // self.row_length) & 1)
194
- # Encode shadow-first
195
- if self._is_init[lq]:
196
- self._cx_shadow(hq[0], hq[2])
197
- if ((not self.alternating_codes) and reverse) or (even_row == reverse):
198
- self.sim.mcx([hq[2]], hq[1])
199
- else:
200
- self.sim.mcx([hq[0]], hq[1])
201
- self._is_init[lq] = True
217
+ return [
218
+ self._qubit_dict[offset],
219
+ self._qubit_dict[offset + 1],
220
+ self._qubit_dict[offset + 2],
221
+ ]
202
222
 
203
- def _decode(self, lq, hq, reverse=False):
204
- if not self._is_init[lq]:
223
+ def _encode_decode(self, lq, hq):
224
+ if len(hq) < 2:
205
225
  return
206
- even_row = not ((lq // self.row_length) & 1)
207
- if ((not self.alternating_codes) and reverse) or (even_row == reverse):
208
- # Decode entangled-first
209
- self.sim.mcx([hq[2]], hq[1])
210
- else:
211
- # Decode entangled-first
212
- self.sim.mcx([hq[0]], hq[1])
213
- self._cx_shadow(hq[0], hq[2])
214
-
215
- def _encode_decode_1qb(self, lq, hq):
216
- if not self.alternating_codes or not ((lq // self.row_length) & 1):
217
- self.sim.mcx([hq[0]], hq[1])
226
+ if hq[0][0] == hq[1][0]:
227
+ b0 = hq[0]
228
+ self.sim[b0[0]].mcx([b0[1]], hq[1][1])
218
229
  else:
219
- self.sim.mcx([hq[2]], hq[1])
230
+ b2 = hq[2]
231
+ self.sim[b2[0]].mcx([b2[1]], hq[1][1])
220
232
 
221
233
  def _correct(self, lq):
222
- if not self._is_init[lq]:
234
+ if self._is_col_long_range[lq % self.row_length]:
223
235
  return
224
236
  # We can't use true syndrome-based error correction,
225
237
  # because one of the qubits in the code is separated.
@@ -228,25 +240,31 @@ class QrackAceBackend:
228
240
 
229
241
  single_bit = 0
230
242
  other_bits = []
231
- if not self.alternating_codes or not ((lq // self.row_length) & 1):
243
+ hq = self._unpack(lq)
244
+ if hq[0][0] == hq[1][0]:
232
245
  single_bit = 2
233
246
  other_bits = [0, 1]
234
- else:
247
+ elif hq[1][0] == hq[2][0]:
235
248
  single_bit = 0
236
249
  other_bits = [1, 2]
250
+ else:
251
+ raise RuntimeError("Invalid boundary qubit!")
237
252
 
238
- hq = self._unpack(lq)
253
+ ancilla_sim = hq[other_bits[0]][0]
254
+ ancilla = self._ancilla[ancilla_sim]
239
255
 
240
- single_bit_value = self.sim.prob(hq[single_bit])
256
+ single_bit_value = self.sim[hq[single_bit][0]].prob(hq[single_bit][1])
241
257
  single_bit_polarization = max(single_bit_value, 1 - single_bit_value)
242
258
 
243
259
  # Suggestion from Elara (the custom OpenAI GPT):
244
260
  # Create phase parity tie before measurement.
245
- self._ccx_shadow(hq[single_bit], hq[other_bits[0]], self._ancilla)
246
- self.sim.mcx([hq[other_bits[1]]], self._ancilla)
247
- self.sim.force_m(self._ancilla, False)
261
+ # self._ccx_shadow(hq[single_bit], hq[other_bits[0]], [ancilla_sim, ancilla])
262
+ # self.sim[ancilla_sim].mcx([hq[other_bits[1]][1]], ancilla)
263
+ # self.sim[ancilla_sim].force_m(ancilla, False)
248
264
 
249
- samples = self.sim.measure_shots([hq[other_bits[0]], hq[other_bits[1]]], shots)
265
+ samples = self.sim[ancilla_sim].measure_shots(
266
+ [hq[other_bits[0]][1], hq[other_bits[1]][1]], shots
267
+ )
250
268
 
251
269
  syndrome_indices = (
252
270
  [other_bits[1], other_bits[0]]
@@ -296,31 +314,29 @@ class QrackAceBackend:
296
314
  error_bit = syndrome.index(max(syndrome))
297
315
  if error_bit == single_bit:
298
316
  # The stand-alone bit carries the error.
299
- self.sim.x(hq[error_bit])
317
+ self.sim[hq[error_bit][0]].x(hq[error_bit][1])
300
318
  else:
301
319
  # The coherent bits carry the error.
302
320
  force_syndrome = False
303
321
  # Form their syndrome.
304
- self.sim.mcx([hq[other_bits[0]]], self._ancilla)
305
- self.sim.mcx([hq[other_bits[1]]], self._ancilla)
322
+ self.sim[ancilla_sim].mcx([hq[other_bits[0]][1]], ancilla)
323
+ self.sim[ancilla_sim].mcx([hq[other_bits[1]][1]], ancilla)
306
324
  # Force the syndrome pathological
307
- self.sim.force_m(self._ancilla, True)
325
+ self.sim[ancilla_sim].force_m(ancilla, True)
308
326
  # Reset the ancilla.
309
- self.sim.x(self._ancilla)
327
+ self.sim[ancilla_sim].x(ancilla)
310
328
  # Correct the bit flip.
311
- self.sim.x(hq[error_bit])
329
+ self.sim[ancilla_sim].x(hq[error_bit][1])
312
330
 
313
331
  # There is no error.
314
332
  if force_syndrome:
315
333
  # Form the syndrome of the coherent bits.
316
- self.sim.mcx([hq[other_bits[0]]], self._ancilla)
317
- self.sim.mcx([hq[other_bits[1]]], self._ancilla)
334
+ self.sim[ancilla_sim].mcx([hq[other_bits[0]][1]], ancilla)
335
+ self.sim[ancilla_sim].mcx([hq[other_bits[1]][1]], ancilla)
318
336
  # Force the syndrome non-pathological.
319
- self.sim.force_m(self._ancilla, False)
337
+ self.sim[ancilla_sim].force_m(ancilla, False)
320
338
 
321
339
  def _correct_if_like_h(self, th, lq):
322
- if not self._is_init[lq]:
323
- return
324
340
  while th > math.pi:
325
341
  th -= 2 * math.pi
326
342
  while th <= -math.pi:
@@ -331,8 +347,9 @@ class QrackAceBackend:
331
347
 
332
348
  def u(self, lq, th, ph, lm):
333
349
  hq = self._unpack(lq)
334
- if self._is_col_long_range[lq % self.row_length]:
335
- self.sim.u(hq[0], th, ph, lm)
350
+ if len(hq) < 2:
351
+ b = hq[0]
352
+ self.sim[b[0]].u(b[1], th, ph, lm)
336
353
  return
337
354
 
338
355
  while ph > math.pi:
@@ -346,142 +363,150 @@ class QrackAceBackend:
346
363
 
347
364
  if not math.isclose(ph, -lm) and not math.isclose(abs(ph), math.pi / 2):
348
365
  # Produces/destroys superposition
349
- if self._is_init[lq]:
350
- self._correct_if_like_h(th, lq)
351
- self._encode_decode_1qb(lq, hq)
352
- self.sim.u(hq[0], th, ph, lm)
353
- self.sim.u(hq[2], th, ph, lm)
354
- if self._is_init[lq]:
355
- self._encode_decode_1qb(lq, hq)
356
- else:
357
- self._encode(lq, hq)
366
+ self._correct_if_like_h(th, lq)
367
+ self._encode_decode(lq, hq)
368
+ b = hq[0]
369
+ self.sim[b[0]].u(b[1], th, ph, lm)
370
+ b = hq[2]
371
+ self.sim[b[0]].u(b[1], th, ph, lm)
372
+ self._encode_decode(lq, hq)
358
373
  else:
359
374
  # Shouldn't produce/destroy superposition
360
375
  for b in hq:
361
- self.sim.u(b, th, ph, lm)
376
+ self.sim[b[0]].u(b[1], th, ph, lm)
362
377
 
363
378
  def r(self, p, th, lq):
364
379
  hq = self._unpack(lq)
365
- if self._is_col_long_range[lq % self.row_length]:
366
- self.sim.r(p, th, hq[0])
380
+ if len(hq) < 2:
381
+ b = hq[0]
382
+ self.sim[b[0]].r(p, th, b[1])
367
383
  return
368
384
 
369
385
  while th > math.pi:
370
386
  th -= 2 * math.pi
371
387
  while th <= -math.pi:
372
388
  th += 2 * math.pi
373
- if self._is_init[lq] and (p == Pauli.PauliY):
389
+ if p == Pauli.PauliY:
374
390
  self._correct_if_like_h(th, lq)
375
391
 
376
392
  if (p == Pauli.PauliZ) or math.isclose(abs(th), math.pi):
377
393
  # Doesn't produce/destroy superposition
378
394
  for b in hq:
379
- self.sim.r(p, th, b)
395
+ self.sim[b[0]].r(p, th, b[1])
380
396
  else:
381
397
  # Produces/destroys superposition
382
- if self._is_init[lq]:
383
- self._encode_decode_1qb(lq, hq)
384
- self.sim.r(p, th, hq[0])
385
- self.sim.r(p, th, hq[2])
386
- if self._is_init[lq]:
387
- self._encode_decode_1qb(lq, hq)
388
- else:
389
- self._encode(lq, hq)
398
+ self._encode_decode(lq, hq)
399
+ b = hq[0]
400
+ self.sim[b[0]].r(p, th, b[1])
401
+ b = hq[2]
402
+ self.sim[b[0]].r(p, th, b[1])
403
+ self._encode_decode(lq, hq)
390
404
 
391
405
  def h(self, lq):
392
406
  hq = self._unpack(lq)
393
- if self._is_col_long_range[lq % self.row_length]:
394
- self.sim.h(hq[0])
407
+ if len(hq) < 2:
408
+ b = hq[0]
409
+ self.sim[b[0]].h(b[1])
395
410
  return
396
411
 
397
- if self._is_init[lq]:
398
- self._correct(lq)
399
- self._encode_decode_1qb(lq, hq)
400
- self.sim.h(hq[0])
401
- self.sim.h(hq[2])
402
- if self._is_init[lq]:
403
- self._encode_decode_1qb(lq, hq)
404
- else:
405
- self._encode(lq, hq)
412
+ self._correct(lq)
413
+ self._encode_decode(lq, hq)
414
+ b = hq[0]
415
+ self.sim[b[0]].h(b[1])
416
+ b = hq[2]
417
+ self.sim[b[0]].h(b[1])
418
+ self._encode_decode(lq, hq)
406
419
 
407
420
  def s(self, lq):
408
421
  hq = self._unpack(lq)
409
- if self._is_col_long_range[lq % self.row_length]:
410
- self.sim.s(hq[0])
422
+ if len(hq) < 2:
423
+ b = hq[0]
424
+ self.sim[b[0]].s(b[1])
411
425
  return
412
426
 
413
427
  for b in hq:
414
- self.sim.s(b)
428
+ self.sim[b[0]].s(b[1])
415
429
 
416
430
  def adjs(self, lq):
417
431
  hq = self._unpack(lq)
418
- if self._is_col_long_range[lq % self.row_length]:
419
- self.sim.adjs(hq[0])
432
+ if len(hq) < 2:
433
+ b = hq[0]
434
+ self.sim[b[0]].adjs(b[1])
420
435
  return
421
436
 
422
437
  for b in hq:
423
- self.sim.adjs(b)
438
+ self.sim[b[0]].adjs(b[1])
424
439
 
425
440
  def x(self, lq):
426
441
  hq = self._unpack(lq)
427
- if self._is_col_long_range[lq % self.row_length]:
428
- self.sim.x(hq[0])
442
+ if len(hq) < 2:
443
+ b = hq[0]
444
+ self.sim[b[0]].x(b[1])
429
445
  return
430
446
 
431
447
  for b in hq:
432
- self.sim.x(b)
448
+ self.sim[b[0]].x(b[1])
433
449
 
434
450
  def y(self, lq):
435
451
  hq = self._unpack(lq)
436
- if self._is_col_long_range[lq % self.row_length]:
437
- self.sim.y(hq[0])
452
+ if len(hq) < 2:
453
+ b = hq[0]
454
+ self.sim[b[0]].y(b[1])
438
455
  return
439
456
 
440
457
  for b in hq:
441
- self.sim.y(b)
458
+ self.sim[b[0]].y(b[1])
442
459
 
443
460
  def z(self, lq):
444
461
  hq = self._unpack(lq)
445
- if self._is_col_long_range[lq % self.row_length]:
446
- self.sim.z(hq[0])
462
+ if len(hq) < 2:
463
+ b = hq[0]
464
+ self.sim[b[0]].z(b[1])
447
465
  return
448
466
 
449
467
  for b in hq:
450
- self.sim.z(b)
468
+ self.sim[b[0]].z(b[1])
451
469
 
452
470
  def t(self, lq):
453
471
  hq = self._unpack(lq)
454
- if self._is_col_long_range[lq % self.row_length]:
455
- self.sim.t(hq[0])
472
+ if len(hq) < 2:
473
+ b = hq[0]
474
+ self.sim[b[0]].t(b[1])
456
475
  return
457
476
 
458
477
  for b in hq:
459
- self.sim.t(b)
478
+ self.sim[b[0]].t(b[1])
460
479
 
461
480
  def adjt(self, lq):
462
481
  hq = self._unpack(lq)
463
- if self._is_col_long_range[lq % self.row_length]:
464
- self.sim.adjt(hq[0])
482
+ if len(hq) < 2:
483
+ b = hq[0]
484
+ self.sim[b[0]].adjt(b[1])
465
485
  return
466
486
 
467
487
  for b in hq:
468
- self.sim.adjt(b)
488
+ self.sim[b[0]].adjt(b[1])
469
489
 
470
- def _cpauli(self, lq1, lq2, anti, pauli):
490
+ def _get_gate(self, pauli, anti, sim_id):
471
491
  gate = None
472
492
  shadow = None
473
493
  if pauli == Pauli.PauliX:
474
- gate = self.sim.macx if anti else self.sim.mcx
494
+ gate = self.sim[sim_id].macx if anti else self.sim[sim_id].mcx
475
495
  shadow = self._anti_cx_shadow if anti else self._cx_shadow
476
496
  elif pauli == Pauli.PauliY:
477
- gate = self.sim.macy if anti else self.sim.mcy
497
+ gate = self.sim[sim_id].macy if anti else self.sim[sim_id].mcy
478
498
  shadow = self._anti_cy_shadow if anti else self._cy_shadow
479
499
  elif pauli == Pauli.PauliZ:
480
- gate = self.sim.macz if anti else self.sim.mcz
500
+ gate = self.sim[sim_id].macz if anti else self.sim[sim_id].mcz
481
501
  shadow = self._anti_cz_shadow if anti else self._cz_shadow
482
502
  else:
483
- return
503
+ raise RuntimeError(
504
+ "QrackAceBackend._get_gate() should never return identity!"
505
+ )
506
+
507
+ return gate, shadow
484
508
 
509
+ def _cpauli(self, lq1, lq2, anti, pauli):
485
510
  lq1_lr = self._is_col_long_range[lq1 % self.row_length]
486
511
  lq2_lr = self._is_col_long_range[lq2 % self.row_length]
487
512
 
@@ -492,125 +517,90 @@ class QrackAceBackend:
492
517
 
493
518
  connected_cols = []
494
519
  c = (lq1_col - 1) % self.row_length
495
- while self._is_col_long_range[c] and (len(connected_cols) < (self.row_length - 1)):
520
+ while self._is_col_long_range[c] and (
521
+ len(connected_cols) < (self.row_length - 1)
522
+ ):
496
523
  connected_cols.append(c)
497
524
  c = (c - 1) % self.row_length
498
- if len(connected_cols) < self.row_length:
525
+ if len(connected_cols) < (self.row_length - 1):
499
526
  connected_cols.append(c)
500
527
  boundary = len(connected_cols)
501
528
  c = (lq1_col + 1) % self.row_length
502
- while self._is_col_long_range[c] and (len(connected_cols) < (self.row_length - 1)):
529
+ while self._is_col_long_range[c] and (
530
+ len(connected_cols) < (self.row_length - 1)
531
+ ):
503
532
  connected_cols.append(c)
504
533
  c = (c + 1) % self.row_length
505
- if len(connected_cols) < self.row_length:
534
+ if len(connected_cols) < (self.row_length - 1):
506
535
  connected_cols.append(c)
507
536
 
537
+ hq1 = self._unpack(lq1)
538
+ hq2 = self._unpack(lq2)
539
+
508
540
  if lq1_lr and lq2_lr:
541
+ b1 = hq1[0]
542
+ b2 = hq2[0]
543
+ gate, shadow = self._get_gate(pauli, anti, b1[0])
509
544
  if lq2_col in connected_cols:
510
- gate(self._unpack(lq1), self._unpack(lq2)[0])
545
+ gate([b1[1]], b2[1])
511
546
  else:
512
- shadow(self._unpack(lq1)[0], self._unpack(lq2)[0])
547
+ shadow(b1, b2)
513
548
  return
514
549
 
515
550
  self._correct(lq1)
551
+ self._correct(lq2)
516
552
 
517
- if not self._is_init[lq1]:
518
- hq1 = self._unpack(lq1)
519
- hq2 = self._unpack(lq2)
520
- if lq1_lr:
521
- self._decode(lq2, hq2)
522
- gate(hq1, hq2[0])
523
- self._encode(lq2, hq2)
524
- elif lq2_lr:
525
- self._decode(lq1, hq1)
526
- gate([hq1[0]], hq2[0])
527
- self._encode(lq1, hq1)
528
- else:
529
- gate([hq1[0]], hq2[0])
530
- gate([hq1[1]], hq2[1])
531
- gate([hq1[2]], hq2[2])
532
-
533
- return
534
-
535
- hq1 = None
536
- hq2 = None
537
553
  if (lq2_col in connected_cols) and (connected_cols.index(lq2_col) < boundary):
554
+ # lq2_col < lq1_col
555
+ self._encode_decode(lq1, hq1)
556
+ self._encode_decode(lq2, hq2)
557
+ b = hq1[0]
538
558
  if lq1_lr:
539
- self._correct(lq2)
540
- hq1 = self._unpack(lq1)
541
- hq2 = self._unpack(lq2, False)
542
- self._decode(lq2, hq2, False)
543
- gate(hq1, hq2[0])
544
- self._encode(lq2, hq2, False)
559
+ self._get_gate(pauli, anti, hq1[0][0])[0]([b[1]], hq2[2][1])
545
560
  elif lq2_lr:
546
- hq1 = self._unpack(lq1, True)
547
- hq2 = self._unpack(lq2)
548
- self._decode(lq1, hq1, True)
549
- gate([hq1[0]], hq2[0])
550
- self._encode(lq1, hq1, True)
561
+ self._get_gate(pauli, anti, hq2[0][0])[0]([b[1]], hq2[0][1])
551
562
  else:
552
- self._correct(lq2)
553
- hq1 = self._unpack(lq1, True)
554
- hq2 = self._unpack(lq2, False)
555
- self._decode(lq1, hq1, True)
556
- self._decode(lq2, hq2, False)
557
- gate([hq1[0]], hq2[0])
558
- self._encode(lq2, hq2, False)
559
- self._encode(lq1, hq1, True)
563
+ self._get_gate(pauli, anti, b[0])[0]([b[1]], hq2[2][1])
564
+ self._encode_decode(lq2, hq2)
565
+ self._encode_decode(lq1, hq1)
560
566
  elif lq2_col in connected_cols:
567
+ # lq1_col < lq2_col
568
+ self._encode_decode(lq1, hq1)
569
+ self._encode_decode(lq2, hq2)
570
+ b = hq2[0]
561
571
  if lq1_lr:
562
- self._correct(lq2)
563
- hq2 = self._unpack(lq2, True)
564
- hq1 = self._unpack(lq1)
565
- self._decode(lq2, hq2, True)
566
- gate(hq1, hq2[0])
567
- self._encode(lq2, hq2, True)
572
+ self._get_gate(pauli, anti, hq1[0][0])[0]([hq1[0][1]], b[1])
568
573
  elif lq2_lr:
569
- hq2 = self._unpack(lq2)
570
- hq1 = self._unpack(lq1, False)
571
- self._decode(lq1, hq1, False)
572
- gate([hq1[0]], hq2[0])
573
- self._encode(lq1, hq1, False)
574
+ self._get_gate(pauli, anti, hq2[0][0])[0]([hq1[2][1]], b[1])
574
575
  else:
575
- self._correct(lq2)
576
- hq2 = self._unpack(lq2, True)
577
- hq1 = self._unpack(lq1, False)
578
- self._decode(lq2, hq2, True)
579
- self._decode(lq1, hq1, False)
580
- gate([hq1[0]], hq2[0])
581
- self._encode(lq1, hq1, False)
582
- self._encode(lq2, hq2, True)
576
+ self._get_gate(pauli, anti, b[0])[0]([hq1[2][1]], b[1])
577
+ self._encode_decode(lq2, hq2)
578
+ self._encode_decode(lq1, hq1)
583
579
  elif lq1_col == lq2_col:
584
- hq1 = self._unpack(lq1)
585
- hq2 = self._unpack(lq2)
586
- if lq1_lr:
587
- self._correct(lq2)
588
- self._decode(lq2, hq2)
589
- gate(hq1, hq2[0])
590
- self._encode(lq2, hq2)
591
- elif lq2_lr:
592
- self._decode(lq1, hq1)
593
- gate([hq1[0]], hq2[0])
594
- self._encode(lq1, hq1)
580
+ # Both are in the same boundary column.
581
+ b = hq1[0]
582
+ gate, shadow = self._get_gate(pauli, anti, b[0])
583
+ gate([b[1]], hq2[0][1])
584
+ b = hq1[2]
585
+ gate, shadow = self._get_gate(pauli, anti, b[0])
586
+ gate([b[1]], hq2[2][1])
587
+ if hq1[1][0] != hq2[1][0]:
588
+ shadow(hq1[1], hq2[1])
595
589
  else:
596
- gate([hq1[0]], hq2[0])
597
- if self.alternating_codes and ((lq2_row & 1) != (lq1_row & 1)):
598
- shadow(hq1[1], hq2[1])
599
- else:
600
- gate([hq1[1]], hq2[1])
601
- gate([hq1[2]], hq2[2])
590
+ b = hq1[1]
591
+ gate, shadow = self._get_gate(pauli, anti, b[0])
592
+ gate([b[1]], hq2[1][1])
602
593
  else:
603
- hq1 = self._unpack(lq1)
604
- hq2 = self._unpack(lq2)
594
+ # The qubits have no quantum connection.
595
+ gate, shadow = self._get_gate(pauli, anti, hq1[0][0])
605
596
  if lq1_lr:
606
- self._correct(lq2)
607
- self._decode(lq2, hq2)
597
+ self._encode_decode(lq2, hq2)
608
598
  shadow(hq1[0], hq2[0])
609
- self._encode(lq2, hq2)
599
+ self._encode_decode(lq2, hq2)
610
600
  elif lq2_lr:
611
- self._decode(lq1, hq1)
601
+ self._encode_decode(lq1, hq1)
612
602
  shadow(hq1[0], hq2[0])
613
- self._encode(lq1, hq1)
603
+ self._encode_decode(lq1, hq1)
614
604
  else:
615
605
  shadow(hq1[0], hq2[0])
616
606
  shadow(hq1[1], hq2[1])
@@ -694,12 +684,12 @@ class QrackAceBackend:
694
684
  self.swap(lq1, lq2)
695
685
 
696
686
  def m(self, lq):
697
- self._is_init[lq] = False
698
687
  hq = self._unpack(lq)
699
- if self._is_col_long_range[lq % self.row_length]:
700
- return self.sim.m(hq[0])
688
+ if len(hq) < 2:
689
+ b = hq[0]
690
+ return self.sim[b[0]].m(b[1])
701
691
 
702
- if not self.alternating_codes or not ((lq // self.row_length) & 1):
692
+ if hq[0][0] == hq[0][1]:
703
693
  single_bit = 2
704
694
  other_bits = [0, 1]
705
695
  else:
@@ -707,24 +697,27 @@ class QrackAceBackend:
707
697
  other_bits = [1, 2]
708
698
  # The syndrome of "other_bits" is guaranteed to be fixed, after this.
709
699
  self._correct(lq)
710
- syndrome = self.sim.m(hq[other_bits[0]])
711
- syndrome += self.sim.force_m(hq[other_bits[1]], bool(syndrome))
700
+ b = hq[other_bits[0]]
701
+ syndrome = self.sim[b[0]].m(b[1])
702
+ b = hq[other_bits[1]]
703
+ syndrome += self.sim[b[0]].force_m(b[1], bool(syndrome))
712
704
  # The two separable parts of the code are correlated,
713
705
  # but not non-locally, via entanglement.
714
706
  # Collapse the other separable part toward agreement.
715
- syndrome += self.sim.force_m(hq[single_bit], bool(syndrome))
707
+ b = hq[single_bit]
708
+ syndrome += self.sim[b[0]].force_m(b[1], bool(syndrome))
716
709
 
717
710
  return True if (syndrome > 1) else False
718
711
 
719
712
  def force_m(self, lq, c):
720
713
  hq = self._unpack(lq)
721
- self._is_init[lq] = False
722
- if self._is_col_long_range[lq % self.row_length]:
723
- return self.sim.force_m(hq[0])
714
+ if len(hq) < 2:
715
+ b = hq[0]
716
+ return self.sim[b[0]].force_m(b[1])
724
717
 
725
718
  self._correct(lq)
726
719
  for q in hq:
727
- self.sim.force_m(q, c)
720
+ self.sim[q[0]].force_m(q[1], c)
728
721
 
729
722
  return c
730
723
 
@@ -763,17 +756,20 @@ class QrackAceBackend:
763
756
 
764
757
  def prob(self, lq):
765
758
  hq = self._unpack(lq)
766
- if self._is_col_long_range[lq % self.row_length]:
767
- return self.sim.prob(hq[0])
759
+ if len(hq) < 2:
760
+ b = hq[0]
761
+ return self.sim[b[0]].prob(b[1])
768
762
 
769
763
  self._correct(lq)
770
764
  if not self.alternating_codes or not ((lq // self.row_length) & 1):
771
765
  other_bits = [0, 1]
772
766
  else:
773
767
  other_bits = [1, 2]
774
- self.sim.mcx([hq[other_bits[0]]], hq[other_bits[1]])
775
- result = self.sim.prob(hq[other_bits[0]])
776
- self.sim.mcx([hq[other_bits[0]]], hq[other_bits[1]])
768
+ b0 = hq[other_bits[0]]
769
+ b1 = hq[other_bits[1]]
770
+ self.sim[b0[0]].mcx([b0[1]], b1[1])
771
+ result = self.sim[b0[0]].prob(b0[1])
772
+ self.sim[b0[0]].mcx([b0[1]], b1[1])
777
773
 
778
774
  return result
779
775
 
@@ -1129,13 +1125,17 @@ class QrackAceBackend:
1129
1125
  for col in range(cols):
1130
1126
  connected_cols = [col]
1131
1127
  c = (col - 1) % cols
1132
- while self._is_col_long_range[c] and (len(connected_cols) < self.row_length):
1128
+ while self._is_col_long_range[c] and (
1129
+ len(connected_cols) < self.row_length
1130
+ ):
1133
1131
  connected_cols.append(c)
1134
1132
  c = (c - 1) % cols
1135
1133
  if len(connected_cols) < self.row_length:
1136
1134
  connected_cols.append(c)
1137
1135
  c = (col + 1) % cols
1138
- while self._is_col_long_range[c] and (len(connected_cols) < self.row_length):
1136
+ while self._is_col_long_range[c] and (
1137
+ len(connected_cols) < self.row_length
1138
+ ):
1139
1139
  connected_cols.append(c)
1140
1140
  c = (c + 1) % cols
1141
1141
  if len(connected_cols) < self.row_length:
@@ -1177,25 +1177,31 @@ class QrackAceBackend:
1177
1177
  continue # No noise for even-even or odd-odd within a boundary column
1178
1178
 
1179
1179
  if same_col:
1180
- x_cy = 1 - (1 - x)**2
1181
- x_swap = 1 - (1 - x)**3
1182
- noise_model.add_quantum_error(depolarizing_error(x, 2), 'cx', [a, b])
1183
- noise_model.add_quantum_error(depolarizing_error(x_cy, 2), 'cy', [a, b])
1184
- noise_model.add_quantum_error(depolarizing_error(x_cy, 2), 'cz', [a, b])
1185
- noise_model.add_quantum_error(depolarizing_error(x_swap, 2), 'swap', [a, b])
1180
+ x_cy = 1 - (1 - x) ** 2
1181
+ x_swap = 1 - (1 - x) ** 3
1182
+ noise_model.add_quantum_error(depolarizing_error(x, 2), "cx", [a, b])
1183
+ noise_model.add_quantum_error(depolarizing_error(x_cy, 2), "cy", [a, b])
1184
+ noise_model.add_quantum_error(depolarizing_error(x_cy, 2), "cz", [a, b])
1185
+ noise_model.add_quantum_error(
1186
+ depolarizing_error(x_swap, 2), "swap", [a, b]
1187
+ )
1186
1188
  elif is_long_a or is_long_b:
1187
- y_cy = 1 - (1 - y)**2
1188
- y_swap = 1 - (1 - y)**3
1189
- noise_model.add_quantum_error(depolarizing_error(y, 2), 'cx', [a, b])
1190
- noise_model.add_quantum_error(depolarizing_error(y_cy, 2), 'cy', [a, b])
1191
- noise_model.add_quantum_error(depolarizing_error(y_cy, 2), 'cz', [a, b])
1192
- noise_model.add_quantum_error(depolarizing_error(y_swap, 2), 'swap', [a, b])
1189
+ y_cy = 1 - (1 - y) ** 2
1190
+ y_swap = 1 - (1 - y) ** 3
1191
+ noise_model.add_quantum_error(depolarizing_error(y, 2), "cx", [a, b])
1192
+ noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cy", [a, b])
1193
+ noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cz", [a, b])
1194
+ noise_model.add_quantum_error(
1195
+ depolarizing_error(y_swap, 2), "swap", [a, b]
1196
+ )
1193
1197
  else:
1194
- y_cy = 1 - (1 - y)**2
1195
- y_swap = 1 - (1 - y)**3
1196
- noise_model.add_quantum_error(depolarizing_error(y_cy, 2), 'cx', [a, b])
1197
- noise_model.add_quantum_error(depolarizing_error(y_cy, 2), 'cy', [a, b])
1198
- noise_model.add_quantum_error(depolarizing_error(y_cy, 2), 'cz', [a, b])
1199
- noise_model.add_quantum_error(depolarizing_error(y_swap, 2), 'swap', [a, b])
1198
+ y_cy = 1 - (1 - y) ** 2
1199
+ y_swap = 1 - (1 - y) ** 3
1200
+ noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cx", [a, b])
1201
+ noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cy", [a, b])
1202
+ noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cz", [a, b])
1203
+ noise_model.add_quantum_error(
1204
+ depolarizing_error(y_swap, 2), "swap", [a, b]
1205
+ )
1200
1206
 
1201
1207
  return noise_model
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cuda
3
- Version: 1.49.9
3
+ Version: 1.50.1
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.49.9"
10
+ VERSION = "1.50.1"
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')
File without changes
File without changes
File without changes
File without changes
File without changes