pyqrack-cuda 1.50.0__tar.gz → 1.51.0__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-1.51.0}/Makefile +1 -1
  2. {pyqrack_cuda-1.50.0/pyqrack_cuda.egg-info → pyqrack_cuda-1.51.0}/PKG-INFO +1 -1
  3. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/qrack_ace_backend.py +303 -300
  4. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/qrack_simulator.py +6 -0
  5. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/qrack_system/qrack_system.py +3 -0
  6. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0/pyqrack_cuda.egg-info}/PKG-INFO +1 -1
  7. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/setup.py +1 -1
  8. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/LICENSE +0 -0
  9. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/MANIFEST.in +0 -0
  10. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/README.md +0 -0
  11. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyproject.toml +0 -0
  12. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/__init__.py +0 -0
  13. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/neuron_activation_fn.py +0 -0
  14. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/pauli.py +0 -0
  15. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/qrack_circuit.py +0 -0
  16. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/qrack_neuron.py +0 -0
  17. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/qrack_neuron_torch_layer.py +0 -0
  18. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/qrack_stabilizer.py +0 -0
  19. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/qrack_system/__init__.py +0 -0
  20. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/quimb_circuit_type.py +0 -0
  21. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/stats/__init__.py +0 -0
  22. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/stats/load_quantized_data.py +0 -0
  23. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack/stats/quantize_by_range.py +0 -0
  24. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack_cuda.egg-info/SOURCES.txt +0 -0
  25. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack_cuda.egg-info/dependency_links.txt +0 -0
  26. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack_cuda.egg-info/not-zip-safe +0 -0
  27. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack_cuda.egg-info/requires.txt +0 -0
  28. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/pyqrack_cuda.egg-info/top_level.txt +0 -0
  29. {pyqrack_cuda-1.50.0 → pyqrack_cuda-1.51.0}/setup.cfg +0 -0
@@ -18,7 +18,7 @@ help:
18
18
  build-deps:
19
19
  ifneq ($(OS),Windows_NT)
20
20
  ifeq ($(QRACK_PRESENT),)
21
- git clone https://github.com/unitaryfund/qrack.git; cd qrack; git checkout 065d4575ca82be0398ed726de59899c16500581f; cd ..
21
+ git clone https://github.com/unitaryfund/qrack.git; cd qrack; git checkout f0738286f49fb0ffaeb46d6e5d2294dbfc7f6138; cd ..
22
22
  endif
23
23
  mkdir -p qrack/build
24
24
  ifeq ($(UNAME_S),Linux)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cuda
3
- Version: 1.50.0
3
+ Version: 1.51.0
4
4
  Summary: pyqrack - Pure Python vm6502q/qrack Wrapper
5
5
  Home-page: https://github.com/vm6502q/pyqrack
6
6
  Author: Daniel Strano
@@ -38,8 +38,12 @@ class QrackAceBackend:
38
38
  without breaking the concept. (Not all will work equally well.) For maximum flexibility, set
39
39
  "alternating_codes=False". (For best performance on Sycamore-like topologies,leave it "True.")
40
40
 
41
+ Consider distributing the different "patches" to different GPUs with self.sim[sim_id].set_device(gpu_id)!
42
+ (If you have 3+ patches, maybe your discrete GPU can do multiple patches in the time it takes an Intel HD
43
+ to do one patch worth of work!)
44
+
41
45
  Attributes:
42
- sim(QrackSimulator): Corresponding simulator.
46
+ sim(QrackSimulator): Array of simulators corresponding to "patches" between boundary rows.
43
47
  alternating_codes(bool): Alternate repetition code elision by index?
44
48
  row_length(int): Qubits per row.
45
49
  col_length(int): Qubits per column.
@@ -69,45 +73,70 @@ class QrackAceBackend:
69
73
  self.long_range_columns = long_range_columns
70
74
 
71
75
  self.alternating_codes = alternating_codes
72
- self._is_init = [False] * qubit_count
73
76
  self._coupling_map = None
74
77
 
75
78
  # If there's only one or zero "False" columns,
76
79
  # the entire simulator is connected, anyway.
80
+ len_col_seq = long_range_columns + 1
81
+ sim_count = (self.row_length + len_col_seq - 1) // len_col_seq
77
82
  if (long_range_columns + 1) >= self.row_length:
78
83
  self._is_col_long_range = [True] * self.row_length
79
84
  else:
80
85
  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]
86
+ self._is_col_long_range = (col_seq * sim_count)[: self.row_length]
85
87
  if long_range_columns < self.row_length:
86
88
  self._is_col_long_range[-1] = False
87
89
 
90
+ self._qubit_dict = {}
88
91
  self._hardware_offset = []
92
+ self._ancilla = [0] * sim_count
93
+ sim_counts = [0] * sim_count
94
+ sim_id = 0
89
95
  tot_qubits = 0
90
- for _ in range(self.col_length):
96
+ for r in range(self.col_length):
91
97
  for c in self._is_col_long_range:
92
98
  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,
99
+ if c:
100
+ self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
101
+ tot_qubits += 1
102
+ sim_counts[sim_id] += 1
103
+ elif not self.alternating_codes or not (r & 1):
104
+ self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
105
+ tot_qubits += 1
106
+ sim_counts[sim_id] += 1
107
+ sim_id = (sim_id + 1) % sim_count
108
+ for _ in range(2):
109
+ self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
110
+ tot_qubits += 1
111
+ sim_counts[sim_id] += 1
112
+ else:
113
+ for _ in range(2):
114
+ self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
115
+ tot_qubits += 1
116
+ sim_counts[sim_id] += 1
117
+ sim_id = (sim_id + 1) % sim_count
118
+ self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
119
+ tot_qubits += 1
120
+ sim_counts[sim_id] += 1
121
+
122
+ self.sim = []
123
+ for i in range(sim_count):
124
+ self._ancilla[i] = sim_counts[i]
125
+ sim_counts[i] += 1
126
+ self.sim.append(
127
+ toClone.sim[i].clone()
128
+ if toClone
129
+ else QrackSimulator(
130
+ sim_counts[i],
131
+ isTensorNetwork=isTensorNetwork,
132
+ isStabilizerHybrid=isStabilizerHybrid,
133
+ isBinaryDecisionTree=isBinaryDecisionTree,
134
+ )
105
135
  )
106
- )
107
136
 
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)
137
+ # You can still "monkey-patch" this, after the constructor.
138
+ if "QRACK_QUNIT_SEPARABILITY_THRESHOLD" not in os.environ:
139
+ self.sim[i].set_sdrp(0.03)
111
140
 
112
141
  def clone(self):
113
142
  return QrackAceBackend(toClone=self)
@@ -125,8 +154,8 @@ class QrackAceBackend:
125
154
  self.row_length = col_len if reverse else row_len
126
155
 
127
156
  def _ct_pair_prob(self, q1, q2):
128
- p1 = self.sim.prob(q1)
129
- p2 = self.sim.prob(q2)
157
+ p1 = self.sim[q1[0]].prob(q1[1])
158
+ p2 = self.sim[q2[0]].prob(q2[1])
130
159
 
131
160
  if p1 < p2:
132
161
  return p2, q1
@@ -136,96 +165,87 @@ class QrackAceBackend:
136
165
  def _cz_shadow(self, q1, q2):
137
166
  prob_max, t = self._ct_pair_prob(q1, q2)
138
167
  if prob_max > 0.5:
139
- self.sim.z(t)
168
+ self.sim[t[0]].z(t[1])
140
169
 
141
- def _anti_cz_shadow(self, q1, q2):
142
- self.sim.x(q1)
143
- self._cz_shadow(q1, q2)
144
- self.sim.x(q1)
170
+ def _anti_cz_shadow(self, c, t):
171
+ self.sim[c[0]].x(c[1])
172
+ self._cz_shadow(c, t)
173
+ self.sim[c[0]].x(c[1])
145
174
 
146
175
  def _cx_shadow(self, c, t):
147
- self.sim.h(t)
176
+ self.sim[t[0]].h(t[1])
148
177
  self._cz_shadow(c, t)
149
- self.sim.h(t)
178
+ self.sim[t[0]].h(t[1])
150
179
 
151
180
  def _anti_cx_shadow(self, c, t):
152
- self.sim.x(t)
181
+ self.sim[c[0]].x(c[1])
153
182
  self._cx_shadow(c, t)
154
- self.sim.x(t)
183
+ self.sim[c[0]].x(c[1])
155
184
 
156
185
  def _cy_shadow(self, c, t):
157
- self.sim.adjs(t)
186
+ self.sim[t[0]].adjs(t[1])
158
187
  self._cx_shadow(c, t)
159
- self.sim.s(t)
188
+ self.sim[t[0]].s(t[1])
160
189
 
161
190
  def _anti_cy_shadow(self, c, t):
162
- self.sim.x(t)
191
+ self.sim[c[0]].x(c[1])
163
192
  self._cy_shadow(c, t)
164
- self.sim.x(t)
193
+ self.sim[c[0]].x(c[1])
165
194
 
166
195
  def _ccz_shadow(self, c1, q2, q3):
167
- self.sim.mcx([q2], q3)
168
- self.sim.adjt(q3)
196
+ self.sim[q2[0]].mcx([q2[1]], q3[1])
197
+ self.sim[q3[0]].adjt(q3[1])
169
198
  self._cx_shadow(c1, q3)
170
- self.sim.t(q3)
171
- self.sim.mcx([q2], q3)
172
- self.sim.adjt(q3)
199
+ self.sim[q3[0]].t(q3[1])
200
+ self.sim[q2[0]].mcx([q2[1]], q3[1])
201
+ self.sim[q3[0]].adjt(q3[1])
173
202
  self._cx_shadow(c1, q3)
174
- self.sim.t(q3)
175
- self.sim.t(q2)
203
+ self.sim[q3[0]].t(q3[1])
204
+ self.sim[q2[0]].t(q2[1])
176
205
  self._cx_shadow(c1, q2)
177
- self.sim.adjt(q2)
178
- self.sim.t(c1)
206
+ self.sim[q2[0]].adjt(q2[1])
207
+ self.sim[c1[0]].t(c1[1])
179
208
  self._cx_shadow(c1, q2)
180
209
 
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)
210
+ def _ccx_shadow(self, c1, q2, t):
211
+ self.sim[t[0]].h(t[1])
212
+ self._ccz_shadow(c1, q2, t)
213
+ self.sim[t[0]].h(t[1])
185
214
 
186
- def _unpack(self, lq, reverse=False):
215
+ def _unpack(self, lq):
187
216
  offset = self._hardware_offset[lq]
188
217
 
189
218
  if self._is_col_long_range[lq % self.row_length]:
190
- return [offset]
219
+ return [self._qubit_dict[offset]]
191
220
 
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
221
+ return [
222
+ self._qubit_dict[offset],
223
+ self._qubit_dict[offset + 1],
224
+ self._qubit_dict[offset + 2],
225
+ ]
208
226
 
209
- def _decode(self, lq, hq, reverse=False):
210
- if not self._is_init[lq]:
227
+ def _encode_decode(self, lq, hq):
228
+ if len(hq) < 2:
211
229
  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])
230
+ if hq[0][0] == hq[1][0]:
231
+ b0 = hq[0]
232
+ self.sim[b0[0]].mcx([b0[1]], hq[1][1])
216
233
  else:
217
- # Decode entangled-first
218
- self.sim.mcx([hq[0]], hq[1])
219
- self._cx_shadow(hq[0], hq[2])
234
+ b2 = hq[2]
235
+ self.sim[b2[0]].mcx([b2[1]], hq[1][1])
220
236
 
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])
224
- else:
225
- self.sim.mcx([hq[2]], hq[1])
237
+ def _encode_decode_half(self, lq, hq, toward_0):
238
+ if len(hq) < 2:
239
+ return
240
+ if toward_0 and (hq[0][0] == hq[1][0]):
241
+ b0 = hq[0]
242
+ self.sim[b0[0]].mcx([b0[1]], hq[1][1])
243
+ elif not toward_0 and (hq[2][0] == hq[1][0]):
244
+ b2 = hq[2]
245
+ self.sim[b2[0]].mcx([b2[1]], hq[1][1])
226
246
 
227
247
  def _correct(self, lq):
228
- if not self._is_init[lq]:
248
+ if self._is_col_long_range[lq % self.row_length]:
229
249
  return
230
250
  # We can't use true syndrome-based error correction,
231
251
  # because one of the qubits in the code is separated.
@@ -234,25 +254,31 @@ class QrackAceBackend:
234
254
 
235
255
  single_bit = 0
236
256
  other_bits = []
237
- if not self.alternating_codes or not ((lq // self.row_length) & 1):
257
+ hq = self._unpack(lq)
258
+ if hq[0][0] == hq[1][0]:
238
259
  single_bit = 2
239
260
  other_bits = [0, 1]
240
- else:
261
+ elif hq[1][0] == hq[2][0]:
241
262
  single_bit = 0
242
263
  other_bits = [1, 2]
264
+ else:
265
+ raise RuntimeError("Invalid boundary qubit!")
243
266
 
244
- hq = self._unpack(lq)
267
+ ancilla_sim = hq[other_bits[0]][0]
268
+ ancilla = self._ancilla[ancilla_sim]
245
269
 
246
- single_bit_value = self.sim.prob(hq[single_bit])
270
+ single_bit_value = self.sim[hq[single_bit][0]].prob(hq[single_bit][1])
247
271
  single_bit_polarization = max(single_bit_value, 1 - single_bit_value)
248
272
 
249
273
  # Suggestion from Elara (the custom OpenAI GPT):
250
274
  # 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)
275
+ self._ccx_shadow(hq[single_bit], hq[other_bits[0]], [ancilla_sim, ancilla])
276
+ self.sim[ancilla_sim].mcx([hq[other_bits[1]][1]], ancilla)
277
+ self.sim[ancilla_sim].force_m(ancilla, False)
254
278
 
255
- samples = self.sim.measure_shots([hq[other_bits[0]], hq[other_bits[1]]], shots)
279
+ samples = self.sim[ancilla_sim].measure_shots(
280
+ [hq[other_bits[0]][1], hq[other_bits[1]][1]], shots
281
+ )
256
282
 
257
283
  syndrome_indices = (
258
284
  [other_bits[1], other_bits[0]]
@@ -302,31 +328,29 @@ class QrackAceBackend:
302
328
  error_bit = syndrome.index(max(syndrome))
303
329
  if error_bit == single_bit:
304
330
  # The stand-alone bit carries the error.
305
- self.sim.x(hq[error_bit])
331
+ self.sim[hq[error_bit][0]].x(hq[error_bit][1])
306
332
  else:
307
333
  # The coherent bits carry the error.
308
334
  force_syndrome = False
309
335
  # Form their syndrome.
310
- self.sim.mcx([hq[other_bits[0]]], self._ancilla)
311
- self.sim.mcx([hq[other_bits[1]]], self._ancilla)
336
+ self.sim[ancilla_sim].mcx([hq[other_bits[0]][1]], ancilla)
337
+ self.sim[ancilla_sim].mcx([hq[other_bits[1]][1]], ancilla)
312
338
  # Force the syndrome pathological
313
- self.sim.force_m(self._ancilla, True)
339
+ self.sim[ancilla_sim].force_m(ancilla, True)
314
340
  # Reset the ancilla.
315
- self.sim.x(self._ancilla)
341
+ self.sim[ancilla_sim].x(ancilla)
316
342
  # Correct the bit flip.
317
- self.sim.x(hq[error_bit])
343
+ self.sim[ancilla_sim].x(hq[error_bit][1])
318
344
 
319
345
  # There is no error.
320
346
  if force_syndrome:
321
347
  # 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)
348
+ self.sim[ancilla_sim].mcx([hq[other_bits[0]][1]], ancilla)
349
+ self.sim[ancilla_sim].mcx([hq[other_bits[1]][1]], ancilla)
324
350
  # Force the syndrome non-pathological.
325
- self.sim.force_m(self._ancilla, False)
351
+ self.sim[ancilla_sim].force_m(ancilla, False)
326
352
 
327
353
  def _correct_if_like_h(self, th, lq):
328
- if not self._is_init[lq]:
329
- return
330
354
  while th > math.pi:
331
355
  th -= 2 * math.pi
332
356
  while th <= -math.pi:
@@ -337,8 +361,9 @@ class QrackAceBackend:
337
361
 
338
362
  def u(self, lq, th, ph, lm):
339
363
  hq = self._unpack(lq)
340
- if self._is_col_long_range[lq % self.row_length]:
341
- self.sim.u(hq[0], th, ph, lm)
364
+ if len(hq) < 2:
365
+ b = hq[0]
366
+ self.sim[b[0]].u(b[1], th, ph, lm)
342
367
  return
343
368
 
344
369
  while ph > math.pi:
@@ -352,142 +377,148 @@ class QrackAceBackend:
352
377
 
353
378
  if not math.isclose(ph, -lm) and not math.isclose(abs(ph), math.pi / 2):
354
379
  # 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)
380
+ self._encode_decode(lq, hq)
381
+ b = hq[0]
382
+ self.sim[b[0]].u(b[1], th, ph, lm)
383
+ b = hq[2]
384
+ self.sim[b[0]].u(b[1], th, ph, lm)
385
+ self._encode_decode(lq, hq)
386
+ self._correct_if_like_h(th, lq)
364
387
  else:
365
388
  # Shouldn't produce/destroy superposition
366
389
  for b in hq:
367
- self.sim.u(b, th, ph, lm)
390
+ self.sim[b[0]].u(b[1], th, ph, lm)
368
391
 
369
392
  def r(self, p, th, lq):
370
393
  hq = self._unpack(lq)
371
- if self._is_col_long_range[lq % self.row_length]:
372
- self.sim.r(p, th, hq[0])
394
+ if len(hq) < 2:
395
+ b = hq[0]
396
+ self.sim[b[0]].r(p, th, b[1])
373
397
  return
374
398
 
375
399
  while th > math.pi:
376
400
  th -= 2 * math.pi
377
401
  while th <= -math.pi:
378
402
  th += 2 * math.pi
379
- if self._is_init[lq] and (p == Pauli.PauliY):
380
- self._correct_if_like_h(th, lq)
381
-
382
403
  if (p == Pauli.PauliZ) or math.isclose(abs(th), math.pi):
383
404
  # Doesn't produce/destroy superposition
384
405
  for b in hq:
385
- self.sim.r(p, th, b)
406
+ self.sim[b[0]].r(p, th, b[1])
386
407
  else:
387
408
  # 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)
409
+ self._encode_decode(lq, hq)
410
+ b = hq[0]
411
+ self.sim[b[0]].r(p, th, b[1])
412
+ b = hq[2]
413
+ self.sim[b[0]].r(p, th, b[1])
414
+ self._encode_decode(lq, hq)
415
+ self._correct_if_like_h(th, lq)
396
416
 
397
417
  def h(self, lq):
398
418
  hq = self._unpack(lq)
399
- if self._is_col_long_range[lq % self.row_length]:
400
- self.sim.h(hq[0])
419
+ if len(hq) < 2:
420
+ b = hq[0]
421
+ self.sim[b[0]].h(b[1])
401
422
  return
402
423
 
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)
424
+ self._encode_decode(lq, hq)
425
+ b = hq[0]
426
+ self.sim[b[0]].h(b[1])
427
+ b = hq[2]
428
+ self.sim[b[0]].h(b[1])
429
+ self._encode_decode(lq, hq)
430
+ self._correct(lq)
412
431
 
413
432
  def s(self, lq):
414
433
  hq = self._unpack(lq)
415
- if self._is_col_long_range[lq % self.row_length]:
416
- self.sim.s(hq[0])
434
+ if len(hq) < 2:
435
+ b = hq[0]
436
+ self.sim[b[0]].s(b[1])
417
437
  return
418
438
 
419
439
  for b in hq:
420
- self.sim.s(b)
440
+ self.sim[b[0]].s(b[1])
421
441
 
422
442
  def adjs(self, lq):
423
443
  hq = self._unpack(lq)
424
- if self._is_col_long_range[lq % self.row_length]:
425
- self.sim.adjs(hq[0])
444
+ if len(hq) < 2:
445
+ b = hq[0]
446
+ self.sim[b[0]].adjs(b[1])
426
447
  return
427
448
 
428
449
  for b in hq:
429
- self.sim.adjs(b)
450
+ self.sim[b[0]].adjs(b[1])
430
451
 
431
452
  def x(self, lq):
432
453
  hq = self._unpack(lq)
433
- if self._is_col_long_range[lq % self.row_length]:
434
- self.sim.x(hq[0])
454
+ if len(hq) < 2:
455
+ b = hq[0]
456
+ self.sim[b[0]].x(b[1])
435
457
  return
436
458
 
437
459
  for b in hq:
438
- self.sim.x(b)
460
+ self.sim[b[0]].x(b[1])
439
461
 
440
462
  def y(self, lq):
441
463
  hq = self._unpack(lq)
442
- if self._is_col_long_range[lq % self.row_length]:
443
- self.sim.y(hq[0])
464
+ if len(hq) < 2:
465
+ b = hq[0]
466
+ self.sim[b[0]].y(b[1])
444
467
  return
445
468
 
446
469
  for b in hq:
447
- self.sim.y(b)
470
+ self.sim[b[0]].y(b[1])
448
471
 
449
472
  def z(self, lq):
450
473
  hq = self._unpack(lq)
451
- if self._is_col_long_range[lq % self.row_length]:
452
- self.sim.z(hq[0])
474
+ if len(hq) < 2:
475
+ b = hq[0]
476
+ self.sim[b[0]].z(b[1])
453
477
  return
454
478
 
455
479
  for b in hq:
456
- self.sim.z(b)
480
+ self.sim[b[0]].z(b[1])
457
481
 
458
482
  def t(self, lq):
459
483
  hq = self._unpack(lq)
460
- if self._is_col_long_range[lq % self.row_length]:
461
- self.sim.t(hq[0])
484
+ if len(hq) < 2:
485
+ b = hq[0]
486
+ self.sim[b[0]].t(b[1])
462
487
  return
463
488
 
464
489
  for b in hq:
465
- self.sim.t(b)
490
+ self.sim[b[0]].t(b[1])
466
491
 
467
492
  def adjt(self, lq):
468
493
  hq = self._unpack(lq)
469
- if self._is_col_long_range[lq % self.row_length]:
470
- self.sim.adjt(hq[0])
494
+ if len(hq) < 2:
495
+ b = hq[0]
496
+ self.sim[b[0]].adjt(b[1])
471
497
  return
472
498
 
473
499
  for b in hq:
474
- self.sim.adjt(b)
500
+ self.sim[b[0]].adjt(b[1])
475
501
 
476
- def _cpauli(self, lq1, lq2, anti, pauli):
502
+ def _get_gate(self, pauli, anti, sim_id):
477
503
  gate = None
478
504
  shadow = None
479
505
  if pauli == Pauli.PauliX:
480
- gate = self.sim.macx if anti else self.sim.mcx
506
+ gate = self.sim[sim_id].macx if anti else self.sim[sim_id].mcx
481
507
  shadow = self._anti_cx_shadow if anti else self._cx_shadow
482
508
  elif pauli == Pauli.PauliY:
483
- gate = self.sim.macy if anti else self.sim.mcy
509
+ gate = self.sim[sim_id].macy if anti else self.sim[sim_id].mcy
484
510
  shadow = self._anti_cy_shadow if anti else self._cy_shadow
485
511
  elif pauli == Pauli.PauliZ:
486
- gate = self.sim.macz if anti else self.sim.mcz
512
+ gate = self.sim[sim_id].macz if anti else self.sim[sim_id].mcz
487
513
  shadow = self._anti_cz_shadow if anti else self._cz_shadow
488
514
  else:
489
- return
515
+ raise RuntimeError(
516
+ "QrackAceBackend._get_gate() should never return identity!"
517
+ )
490
518
 
519
+ return gate, shadow
520
+
521
+ def _cpauli(self, lq1, lq2, anti, pauli):
491
522
  lq1_lr = self._is_col_long_range[lq1 % self.row_length]
492
523
  lq2_lr = self._is_col_long_range[lq2 % self.row_length]
493
524
 
@@ -498,129 +529,95 @@ class QrackAceBackend:
498
529
 
499
530
  connected_cols = []
500
531
  c = (lq1_col - 1) % self.row_length
501
- while self._is_col_long_range[c] and (len(connected_cols) < (self.row_length - 1)):
532
+ while self._is_col_long_range[c] and (
533
+ len(connected_cols) < (self.row_length - 1)
534
+ ):
502
535
  connected_cols.append(c)
503
536
  c = (c - 1) % self.row_length
504
537
  if len(connected_cols) < (self.row_length - 1):
505
538
  connected_cols.append(c)
506
539
  boundary = len(connected_cols)
507
540
  c = (lq1_col + 1) % self.row_length
508
- while self._is_col_long_range[c] and (len(connected_cols) < (self.row_length - 1)):
541
+ while self._is_col_long_range[c] and (
542
+ len(connected_cols) < (self.row_length - 1)
543
+ ):
509
544
  connected_cols.append(c)
510
545
  c = (c + 1) % self.row_length
511
546
  if len(connected_cols) < (self.row_length - 1):
512
547
  connected_cols.append(c)
513
548
 
549
+ hq1 = self._unpack(lq1)
550
+ hq2 = self._unpack(lq2)
551
+
514
552
  if lq1_lr and lq2_lr:
553
+ b1 = hq1[0]
554
+ b2 = hq2[0]
555
+ gate, shadow = self._get_gate(pauli, anti, b1[0])
515
556
  if lq2_col in connected_cols:
516
- gate(self._unpack(lq1), self._unpack(lq2)[0])
517
- else:
518
- shadow(self._unpack(lq1)[0], self._unpack(lq2)[0])
519
- return
520
-
521
- self._correct(lq1)
522
-
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)
557
+ gate([b1[1]], b2[1])
534
558
  else:
535
- gate([hq1[0]], hq2[0])
536
- gate([hq1[1]], hq2[1])
537
- gate([hq1[2]], hq2[2])
538
-
559
+ shadow(b1, b2)
539
560
  return
540
561
 
541
- hq1 = None
542
- hq2 = None
543
562
  if (lq2_col in connected_cols) and (connected_cols.index(lq2_col) < boundary):
563
+ # lq2_col < lq1_col
564
+ self._encode_decode_half(lq1, hq1, True)
565
+ self._encode_decode_half(lq2, hq2, False)
566
+ b = hq1[0]
544
567
  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)
568
+ self._get_gate(pauli, anti, hq1[0][0])[0]([b[1]], hq2[2][1])
551
569
  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)
570
+ self._get_gate(pauli, anti, hq2[0][0])[0]([b[1]], hq2[0][1])
557
571
  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)
572
+ self._get_gate(pauli, anti, b[0])[0]([b[1]], hq2[2][1])
573
+ self._encode_decode_half(lq2, hq2, False)
574
+ self._encode_decode_half(lq1, hq1, True)
566
575
  elif lq2_col in connected_cols:
576
+ # lq1_col < lq2_col
577
+ self._encode_decode_half(lq1, hq1, False)
578
+ self._encode_decode_half(lq2, hq2, True)
579
+ b = hq2[0]
567
580
  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)
581
+ self._get_gate(pauli, anti, hq1[0][0])[0]([hq1[0][1]], b[1])
574
582
  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)
583
+ self._get_gate(pauli, anti, hq2[0][0])[0]([hq1[2][1]], b[1])
580
584
  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)
585
+ self._get_gate(pauli, anti, b[0])[0]([hq1[2][1]], b[1])
586
+ self._encode_decode_half(lq2, hq2, True)
587
+ self._encode_decode_half(lq1, hq1, False)
589
588
  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)
601
- 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])
589
+ # Both are in the same boundary column.
590
+ self._encode_decode(lq1, hq1)
591
+ self._encode_decode(lq2, hq2)
592
+ b = hq1[0]
593
+ gate, shadow = self._get_gate(pauli, anti, b[0])
594
+ gate([b[1]], hq2[0][1])
595
+ b = hq1[2]
596
+ gate, shadow = self._get_gate(pauli, anti, b[0])
597
+ gate([b[1]], hq2[2][1])
608
598
  else:
609
- hq1 = self._unpack(lq1)
610
- hq2 = self._unpack(lq2)
599
+ # The qubits have no quantum connection.
600
+ gate, shadow = self._get_gate(pauli, anti, hq1[0][0])
611
601
  if lq1_lr:
612
- self._correct(lq2)
613
- self._decode(lq2, hq2)
614
- shadow(hq1[0], hq2[0])
615
- self._encode(lq2, hq2)
602
+ connected01 = (hq2[0][0] == hq2[1][0])
603
+ self._encode_decode(lq2, hq2)
604
+ shadow(hq1[0], hq2[0] if connected01 else hq2[2])
605
+ self._encode_decode(lq2, hq2)
616
606
  elif lq2_lr:
617
- self._decode(lq1, hq1)
618
- shadow(hq1[0], hq2[0])
619
- self._encode(lq1, hq1)
607
+ connected01 = (hq1[0][0] == hq1[1][0])
608
+ self._encode_decode(lq1, hq1)
609
+ shadow(hq1[0] if connected01 else hq1[2], hq2[0])
610
+ self._encode_decode(lq1, hq1)
620
611
  else:
612
+ self._encode_decode(lq1, hq1)
613
+ self._encode_decode(lq2, hq2)
621
614
  shadow(hq1[0], hq2[0])
622
- shadow(hq1[1], hq2[1])
623
615
  shadow(hq1[2], hq2[2])
616
+ self._encode_decode(lq2, hq2)
617
+ self._encode_decode(lq1, hq1)
618
+
619
+ self._correct(lq1)
620
+ self._correct(lq2)
624
621
 
625
622
  def cx(self, lq1, lq2):
626
623
  self._cpauli(lq1, lq2, False, Pauli.PauliX)
@@ -700,12 +697,12 @@ class QrackAceBackend:
700
697
  self.swap(lq1, lq2)
701
698
 
702
699
  def m(self, lq):
703
- self._is_init[lq] = False
704
700
  hq = self._unpack(lq)
705
- if self._is_col_long_range[lq % self.row_length]:
706
- return self.sim.m(hq[0])
701
+ if len(hq) < 2:
702
+ b = hq[0]
703
+ return self.sim[b[0]].m(b[1])
707
704
 
708
- if not self.alternating_codes or not ((lq // self.row_length) & 1):
705
+ if hq[0][0] == hq[0][1]:
709
706
  single_bit = 2
710
707
  other_bits = [0, 1]
711
708
  else:
@@ -713,24 +710,27 @@ class QrackAceBackend:
713
710
  other_bits = [1, 2]
714
711
  # The syndrome of "other_bits" is guaranteed to be fixed, after this.
715
712
  self._correct(lq)
716
- syndrome = self.sim.m(hq[other_bits[0]])
717
- syndrome += self.sim.force_m(hq[other_bits[1]], bool(syndrome))
713
+ b = hq[other_bits[0]]
714
+ syndrome = self.sim[b[0]].m(b[1])
715
+ b = hq[other_bits[1]]
716
+ syndrome += self.sim[b[0]].force_m(b[1], bool(syndrome))
718
717
  # The two separable parts of the code are correlated,
719
718
  # but not non-locally, via entanglement.
720
719
  # Collapse the other separable part toward agreement.
721
- syndrome += self.sim.force_m(hq[single_bit], bool(syndrome))
720
+ b = hq[single_bit]
721
+ syndrome += self.sim[b[0]].force_m(b[1], bool(syndrome))
722
722
 
723
723
  return True if (syndrome > 1) else False
724
724
 
725
725
  def force_m(self, lq, c):
726
726
  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])
727
+ if len(hq) < 2:
728
+ b = hq[0]
729
+ return self.sim[b[0]].force_m(b[1])
730
730
 
731
731
  self._correct(lq)
732
732
  for q in hq:
733
- self.sim.force_m(q, c)
733
+ self.sim[q[0]].force_m(q[1], c)
734
734
 
735
735
  return c
736
736
 
@@ -769,17 +769,20 @@ class QrackAceBackend:
769
769
 
770
770
  def prob(self, lq):
771
771
  hq = self._unpack(lq)
772
- if self._is_col_long_range[lq % self.row_length]:
773
- return self.sim.prob(hq[0])
772
+ if len(hq) < 2:
773
+ b = hq[0]
774
+ return self.sim[b[0]].prob(b[1])
774
775
 
775
776
  self._correct(lq)
776
- if not self.alternating_codes or not ((lq // self.row_length) & 1):
777
+ if hq[0][0] == hq[1][0]:
777
778
  other_bits = [0, 1]
778
779
  else:
779
780
  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]])
781
+ b0 = hq[other_bits[0]]
782
+ b1 = hq[other_bits[1]]
783
+ self.sim[b0[0]].mcx([b0[1]], b1[1])
784
+ result = self.sim[b0[0]].prob(b0[1])
785
+ self.sim[b0[0]].mcx([b0[1]], b1[1])
783
786
 
784
787
  return result
785
788
 
@@ -1135,13 +1138,17 @@ class QrackAceBackend:
1135
1138
  for col in range(cols):
1136
1139
  connected_cols = [col]
1137
1140
  c = (col - 1) % cols
1138
- while self._is_col_long_range[c] and (len(connected_cols) < self.row_length):
1141
+ while self._is_col_long_range[c] and (
1142
+ len(connected_cols) < self.row_length
1143
+ ):
1139
1144
  connected_cols.append(c)
1140
1145
  c = (c - 1) % cols
1141
1146
  if len(connected_cols) < self.row_length:
1142
1147
  connected_cols.append(c)
1143
1148
  c = (col + 1) % cols
1144
- while self._is_col_long_range[c] and (len(connected_cols) < self.row_length):
1149
+ while self._is_col_long_range[c] and (
1150
+ len(connected_cols) < self.row_length
1151
+ ):
1145
1152
  connected_cols.append(c)
1146
1153
  c = (c + 1) % cols
1147
1154
  if len(connected_cols) < self.row_length:
@@ -1177,31 +1184,27 @@ class QrackAceBackend:
1177
1184
  continue # No noise on long-to-long
1178
1185
 
1179
1186
  same_col = col_a == col_b
1180
- even_odd = (row_a % 2) != (row_b % 2)
1181
-
1182
- if same_col and not even_odd:
1183
- continue # No noise for even-even or odd-odd within a boundary column
1184
1187
 
1185
1188
  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])
1192
- 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
+ continue # No noise for same column
1190
+
1191
+ if is_long_a or is_long_b:
1192
+ y_cy = 1 - (1 - y) ** 2
1193
+ y_swap = 1 - (1 - y) ** 3
1194
+ noise_model.add_quantum_error(depolarizing_error(y, 2), "cx", [a, b])
1195
+ noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cy", [a, b])
1196
+ noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cz", [a, b])
1197
+ noise_model.add_quantum_error(
1198
+ depolarizing_error(y_swap, 2), "swap", [a, b]
1199
+ )
1199
1200
  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])
1201
+ y_cy = 1 - (1 - y) ** 2
1202
+ y_swap = 1 - (1 - y) ** 3
1203
+ noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cx", [a, b])
1204
+ noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cy", [a, b])
1205
+ noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cz", [a, b])
1206
+ noise_model.add_quantum_error(
1207
+ depolarizing_error(y_swap, 2), "swap", [a, b]
1208
+ )
1206
1209
 
1207
1210
  return noise_model
@@ -181,9 +181,15 @@ class QrackSimulator:
181
181
  self._throw_if_error()
182
182
 
183
183
  def set_concurrency(self, p):
184
+ """ Set the CPU parallel thread count"""
184
185
  Qrack.qrack_lib.set_concurrency(self.sid, p)
185
186
  self._throw_if_error()
186
187
 
188
+ def set_device(self, d):
189
+ """ Set the GPU device ID"""
190
+ Qrack.qrack_lib.set_device(self.sid, d)
191
+ self._throw_if_error()
192
+
187
193
  def clone(self):
188
194
  return QrackSimulator(cloneSid=self.sid)
189
195
 
@@ -143,6 +143,9 @@ class QrackSystem:
143
143
  self.qrack_lib.set_concurrency.restype = None
144
144
  self.qrack_lib.set_concurrency.argtypes = [c_ulonglong, c_ulonglong]
145
145
 
146
+ self.qrack_lib.set_device.restype = None
147
+ self.qrack_lib.set_device.argtypes = [c_ulonglong, c_ulonglong]
148
+
146
149
  # pseudo-quantum
147
150
 
148
151
  self.qrack_lib.ProbAll.restype = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cuda
3
- Version: 1.50.0
3
+ Version: 1.51.0
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.51.0"
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