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