pyqrack-cuda 1.55.4__tar.gz → 1.57.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.55.4/pyqrack_cuda.egg-info → pyqrack_cuda-1.57.0}/PKG-INFO +1 -1
  2. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/qrack_ace_backend.py +334 -241
  3. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/qrack_simulator.py +1 -1
  4. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0/pyqrack_cuda.egg-info}/PKG-INFO +1 -1
  5. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/setup.py +1 -1
  6. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/LICENSE +0 -0
  7. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/MANIFEST.in +0 -0
  8. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/Makefile +0 -0
  9. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/README.md +0 -0
  10. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyproject.toml +0 -0
  11. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/__init__.py +0 -0
  12. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/neuron_activation_fn.py +0 -0
  13. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/pauli.py +0 -0
  14. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/qrack_circuit.py +0 -0
  15. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/qrack_neuron.py +0 -0
  16. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/qrack_neuron_torch_layer.py +0 -0
  17. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/qrack_stabilizer.py +0 -0
  18. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/qrack_system/__init__.py +0 -0
  19. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/qrack_system/qrack_system.py +0 -0
  20. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/quimb_circuit_type.py +0 -0
  21. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/stats/__init__.py +0 -0
  22. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/stats/load_quantized_data.py +0 -0
  23. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack/stats/quantize_by_range.py +0 -0
  24. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack_cuda.egg-info/SOURCES.txt +0 -0
  25. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack_cuda.egg-info/dependency_links.txt +0 -0
  26. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack_cuda.egg-info/not-zip-safe +0 -0
  27. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack_cuda.egg-info/requires.txt +0 -0
  28. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/pyqrack_cuda.egg-info/top_level.txt +0 -0
  29. {pyqrack_cuda-1.55.4 → pyqrack_cuda-1.57.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cuda
3
- Version: 1.55.4
3
+ Version: 1.57.0
4
4
  Summary: pyqrack - Pure Python vm6502q/qrack Wrapper
5
5
  Home-page: https://github.com/vm6502q/pyqrack
6
6
  Author: Daniel Strano
@@ -168,16 +168,27 @@ class QrackAceBackend:
168
168
  def __init__(
169
169
  self,
170
170
  qubit_count=1,
171
- long_range_columns=2,
171
+ long_range_columns=5,
172
+ long_range_rows=2,
172
173
  is_transpose=False,
173
174
  isTensorNetwork=False,
175
+ isSchmidtDecomposeMulti=False,
176
+ isSchmidtDecompose=True,
174
177
  isStabilizerHybrid=False,
175
178
  isBinaryDecisionTree=False,
179
+ isPaged=True,
180
+ isCpuGpuHybrid=True,
181
+ isOpenCL=True,
182
+ isHostPointer=(
183
+ True if os.environ.get("PYQRACK_HOST_POINTER_DEFAULT_ON") else False
184
+ ),
185
+ noise=0,
176
186
  toClone=None,
177
187
  ):
178
188
  if toClone:
179
189
  qubit_count = toClone.num_qubits()
180
190
  long_range_columns = toClone.long_range_columns
191
+ long_range_rows = toClone.long_range_rows
181
192
  is_transpose = toClone.is_transpose
182
193
  if qubit_count < 0:
183
194
  qubit_count = 0
@@ -186,6 +197,7 @@ class QrackAceBackend:
186
197
 
187
198
  self._factor_width(qubit_count, is_transpose)
188
199
  self.long_range_columns = long_range_columns
200
+ self.long_range_rows = long_range_rows
189
201
  self.is_transpose = is_transpose
190
202
 
191
203
  fppow = 5
@@ -200,57 +212,86 @@ class QrackAceBackend:
200
212
 
201
213
  self._coupling_map = None
202
214
 
203
- # If there's only one or zero "False" columns,
215
+ # If there's only one or zero "False" columns or rows,
204
216
  # the entire simulator is connected, anyway.
205
217
  len_col_seq = long_range_columns + 1
206
- sim_count = (self._row_length + len_col_seq - 1) // len_col_seq
207
- if (long_range_columns + 1) >= self._row_length:
218
+ col_patch_count = (self._row_length + len_col_seq - 1) // len_col_seq
219
+ if (self._row_length < 3) or ((long_range_columns + 1) >= self._row_length):
208
220
  self._is_col_long_range = [True] * self._row_length
209
221
  else:
210
222
  col_seq = [True] * long_range_columns + [False]
211
- self._is_col_long_range = (col_seq * sim_count)[: self._row_length]
223
+ self._is_col_long_range = (col_seq * col_patch_count)[: self._row_length]
212
224
  if long_range_columns < self._row_length:
213
225
  self._is_col_long_range[-1] = False
226
+ len_row_seq = long_range_rows + 1
227
+ row_patch_count = (self._col_length + len_row_seq - 1) // len_row_seq
228
+ if (self._col_length < 3) or ((long_range_rows + 1) >= self._col_length):
229
+ self._is_row_long_range = [True] * self._col_length
230
+ else:
231
+ row_seq = [True] * long_range_rows + [False]
232
+ self._is_row_long_range = (row_seq * row_patch_count)[: self._col_length]
233
+ if long_range_rows < self._col_length:
234
+ self._is_row_long_range[-1] = False
235
+ sim_count = col_patch_count * row_patch_count
236
+ self._row_offset = 0
237
+ for r in range(self._col_length):
238
+ for c in self._is_col_long_range:
239
+ self._row_offset += 1 if c else 3
214
240
 
215
241
  self._qubit_dict = {}
216
- self._lhv_dict = {}
217
- self._hardware_offset = []
218
242
  sim_counts = [0] * sim_count
219
243
  sim_id = 0
220
244
  tot_qubits = 0
221
- for r in range(self._col_length):
245
+ for r in self._is_row_long_range:
222
246
  for c in self._is_col_long_range:
223
- self._hardware_offset.append(tot_qubits)
224
- if c:
225
- self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
226
- tot_qubits += 1
227
- sim_counts[sim_id] += 1
228
- else:
229
- self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
230
- tot_qubits += 1
231
- sim_counts[sim_id] += 1
232
-
233
- self._lhv_dict[tot_qubits] = LHVQubit(
234
- toClone=toClone._lhv_dict[tot_qubits] if toClone else None
247
+ qubit = [(sim_id, sim_counts[sim_id])]
248
+ sim_counts[sim_id] += 1
249
+
250
+ if (not c) or (not r):
251
+ t_sim_id = (sim_id + 1) % sim_count
252
+ qubit.append((t_sim_id, sim_counts[t_sim_id]))
253
+ sim_counts[t_sim_id] += 1
254
+
255
+ qubit.append(
256
+ LHVQubit(
257
+ toClone=(
258
+ toClone._qubit_dict[tot_qubits][2] if toClone else None
259
+ )
260
+ )
235
261
  )
236
- tot_qubits += 1
237
262
 
263
+ if (not c) and (not r):
264
+ t_sim_id = (sim_id + col_patch_count) % sim_count
265
+ qubit.append((t_sim_id, sim_counts[t_sim_id]))
266
+ sim_counts[t_sim_id] += 1
267
+
268
+ t_sim_id = (t_sim_id + 1) % sim_count
269
+ qubit.append((t_sim_id, sim_counts[t_sim_id]))
270
+ sim_counts[t_sim_id] += 1
271
+
272
+ if not c:
238
273
  sim_id = (sim_id + 1) % sim_count
239
- self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
240
- tot_qubits += 1
241
- sim_counts[sim_id] += 1
274
+
275
+ self._qubit_dict[tot_qubits] = qubit
276
+ tot_qubits += 1
242
277
 
243
278
  self.sim = []
244
279
  for i in range(sim_count):
245
- sim_counts[i] += 1
246
280
  self.sim.append(
247
281
  toClone.sim[i].clone()
248
282
  if toClone
249
283
  else QrackSimulator(
250
284
  sim_counts[i],
251
285
  isTensorNetwork=isTensorNetwork,
286
+ isSchmidtDecomposeMulti=isSchmidtDecomposeMulti,
287
+ isSchmidtDecompose=isSchmidtDecompose,
252
288
  isStabilizerHybrid=isStabilizerHybrid,
253
289
  isBinaryDecisionTree=isBinaryDecisionTree,
290
+ isPaged=isPaged,
291
+ isCpuGpuHybrid=isCpuGpuHybrid,
292
+ isOpenCL=isOpenCL,
293
+ isHostPointer=isHostPointer,
294
+ noise=noise,
254
295
  )
255
296
  )
256
297
 
@@ -347,57 +388,97 @@ class QrackAceBackend:
347
388
  self._qec_x(c)
348
389
 
349
390
  def _unpack(self, lq):
350
- offset = self._hardware_offset[lq]
391
+ return self._qubit_dict[lq]
351
392
 
352
- if self._is_col_long_range[lq % self._row_length]:
353
- return [self._qubit_dict[offset]]
393
+ def _get_qb_lhv_indices(self, hq):
394
+ qb = []
395
+ if len(hq) < 2:
396
+ qb = [0]
397
+ lhv = -1
398
+ elif len(hq) < 4:
399
+ qb = [0, 1]
400
+ lhv = 2
401
+ else:
402
+ qb = [0, 1, 3, 4]
403
+ lhv = 2
354
404
 
355
- return [
356
- self._qubit_dict[offset],
357
- self._lhv_dict[offset + 1],
358
- self._qubit_dict[offset + 2],
359
- ]
405
+ return qb, lhv
360
406
 
361
407
  def _correct(self, lq, phase=False):
362
- if self._is_col_long_range[lq % self._row_length] or (self._row_length == 2):
408
+ hq = self._unpack(lq)
409
+
410
+ if len(hq) == 1:
363
411
  return
364
412
 
365
- hq = self._unpack(lq)
413
+ qb, lhv = self._get_qb_lhv_indices(hq)
366
414
 
367
415
  if phase:
368
- b = hq[0]
369
- self.sim[b[0]].h(b[1])
370
- b = hq[1]
416
+ for q in qb:
417
+ b = hq[q]
418
+ self.sim[b[0]].h(b[1])
419
+ b = hq[lhv]
371
420
  b.h()
372
- b = hq[2]
373
- self.sim[b[0]].h(b[1])
374
421
 
375
- # RMS
376
- p = [
377
- self.sim[hq[0][0]].prob(hq[0][1]),
378
- hq[1].prob(),
379
- self.sim[hq[2][0]].prob(hq[2][1]),
380
- ]
381
- # Balancing suggestion from Elara (the custom OpenAI GPT)
382
- prms = math.sqrt((p[0] ** 2 + p[1] ** 2 + p[2] ** 2) / 3)
383
- qrms = math.sqrt(((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2) / 3)
384
- result = ((prms + (1 - qrms)) / 2) >= 0.5
385
- syndrome = [1 - p[0], 1 - p[1], 1 - p[2]] if result else [p[0], p[1], p[2]]
386
-
387
- for q in range(3):
388
- if syndrome[q] > (0.5 + self._epsilon):
389
- if q == 1:
390
- hq[q].x()
391
- else:
392
- self.sim[hq[q][0]].x(hq[q][1])
422
+ if len(hq) == 5:
423
+ # RMS
424
+ p = [
425
+ self.sim[hq[0][0]].prob(hq[0][1]),
426
+ self.sim[hq[1][0]].prob(hq[1][1]),
427
+ hq[2].prob(),
428
+ self.sim[hq[3][0]].prob(hq[3][1]),
429
+ self.sim[hq[4][0]].prob(hq[4][1]),
430
+ ]
431
+ # Balancing suggestion from Elara (the custom OpenAI GPT)
432
+ prms = math.sqrt(
433
+ (p[0] ** 2 + p[1] ** 2 + 3 * (p[2] ** 2) + p[3] ** 2 + p[4] ** 2) / 7
434
+ )
435
+ qrms = math.sqrt(
436
+ (
437
+ (1 - p[0]) ** 2
438
+ + (1 - p[1]) ** 2
439
+ + 3 * ((1 - p[2]) ** 2)
440
+ + (1 - p[3]) ** 2
441
+ + (1 - p[4]) ** 2
442
+ )
443
+ / 7
444
+ )
445
+ result = ((prms + (1 - qrms)) / 2) >= 0.5
446
+ syndrome = (
447
+ [1 - p[0], 1 - p[1], 1 - p[2], 1 - p[3], 1 - p[4]]
448
+ if result
449
+ else [p[0], p[1], p[2], p[3], p[4]]
450
+ )
451
+ for q in range(5):
452
+ if syndrome[q] > (0.5 + self._epsilon):
453
+ if q == 2:
454
+ hq[q].x()
455
+ else:
456
+ self.sim[hq[q][0]].x(hq[q][1])
457
+ else:
458
+ # RMS
459
+ p = [
460
+ self.sim[hq[0][0]].prob(hq[0][1]),
461
+ self.sim[hq[1][0]].prob(hq[1][1]),
462
+ hq[2].prob(),
463
+ ]
464
+ # Balancing suggestion from Elara (the custom OpenAI GPT)
465
+ prms = math.sqrt((p[0] ** 2 + p[1] ** 2 + p[2] ** 2) / 3)
466
+ qrms = math.sqrt(((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2) / 3)
467
+ result = ((prms + (1 - qrms)) / 2) >= 0.5
468
+ syndrome = [1 - p[0], 1 - p[1], 1 - p[2]] if result else [p[0], p[1], p[2]]
469
+ for q in range(3):
470
+ if syndrome[q] > (0.5 + self._epsilon):
471
+ if q == 2:
472
+ hq[q].x()
473
+ else:
474
+ self.sim[hq[q][0]].x(hq[q][1])
393
475
 
394
476
  if phase:
395
- b = hq[0]
396
- self.sim[b[0]].h(b[1])
397
- b = hq[1]
477
+ for q in qb:
478
+ b = hq[q]
479
+ self.sim[b[0]].h(b[1])
480
+ b = hq[lhv]
398
481
  b.h()
399
- b = hq[2]
400
- self.sim[b[0]].h(b[1])
401
482
 
402
483
  def u(self, lq, th, ph, lm):
403
484
  hq = self._unpack(lq)
@@ -406,12 +487,14 @@ class QrackAceBackend:
406
487
  self.sim[b[0]].u(b[1], th, ph, lm)
407
488
  return
408
489
 
409
- b = hq[0]
410
- self.sim[b[0]].u(b[1], th, ph, lm)
411
- b = hq[1]
490
+ qb, lhv = self._get_qb_lhv_indices(hq)
491
+
492
+ for q in qb:
493
+ b = hq[q]
494
+ self.sim[b[0]].u(b[1], th, ph, lm)
495
+
496
+ b = hq[lhv]
412
497
  b.u(th, ph, lm)
413
- b = hq[2]
414
- self.sim[b[0]].u(b[1], th, ph, lm)
415
498
 
416
499
  self._correct(lq, False)
417
500
  self._correct(lq, True)
@@ -423,17 +506,19 @@ class QrackAceBackend:
423
506
  self.sim[b[0]].r(p, th, b[1])
424
507
  return
425
508
 
426
- b = hq[0]
427
- self.sim[b[0]].r(p, th, b[1])
428
- b = hq[1]
509
+ qb, lhv = self._get_qb_lhv_indices(hq)
510
+
511
+ for q in qb:
512
+ b = hq[q]
513
+ self.sim[b[0]].r(p, th, b[1])
514
+
515
+ b = hq[lhv]
429
516
  if p == Pauli.PauliX:
430
517
  b.rx(th)
431
518
  elif p == Pauli.PauliY:
432
519
  b.ry(th)
433
520
  elif p == Pauli.PauliZ:
434
521
  b.rz(th)
435
- b = hq[2]
436
- self.sim[b[0]].r(p, th, b[1])
437
522
 
438
523
  if p != Pauli.PauliZ:
439
524
  self._correct(lq, False)
@@ -448,12 +533,16 @@ class QrackAceBackend:
448
533
  return
449
534
 
450
535
  self._correct(lq)
451
- b = hq[0]
452
- self.sim[b[0]].h(b[1])
453
- b = hq[1]
536
+
537
+ qb, lhv = self._get_qb_lhv_indices(hq)
538
+
539
+ for q in qb:
540
+ b = hq[q]
541
+ self.sim[b[0]].h(b[1])
542
+
543
+ b = hq[lhv]
454
544
  b.h()
455
- b = hq[2]
456
- self.sim[b[0]].h(b[1])
545
+
457
546
  self._correct(lq)
458
547
 
459
548
  def s(self, lq):
@@ -463,12 +552,14 @@ class QrackAceBackend:
463
552
  self.sim[b[0]].s(b[1])
464
553
  return
465
554
 
466
- b = hq[0]
467
- self.sim[b[0]].s(b[1])
468
- b = hq[1]
555
+ qb, lhv = self._get_qb_lhv_indices(hq)
556
+
557
+ for q in qb:
558
+ b = hq[q]
559
+ self.sim[b[0]].s(b[1])
560
+
561
+ b = hq[lhv]
469
562
  b.s()
470
- b = hq[2]
471
- self.sim[b[0]].s(b[1])
472
563
 
473
564
  def adjs(self, lq):
474
565
  hq = self._unpack(lq)
@@ -477,12 +568,14 @@ class QrackAceBackend:
477
568
  self.sim[b[0]].adjs(b[1])
478
569
  return
479
570
 
480
- b = hq[0]
481
- self.sim[b[0]].adjs(b[1])
482
- b = hq[1]
571
+ qb, lhv = self._get_qb_lhv_indices(hq)
572
+
573
+ for q in qb:
574
+ b = hq[q]
575
+ self.sim[b[0]].adjs(b[1])
576
+
577
+ b = hq[lhv]
483
578
  b.adjs()
484
- b = hq[2]
485
- self.sim[b[0]].adjs(b[1])
486
579
 
487
580
  def x(self, lq):
488
581
  hq = self._unpack(lq)
@@ -491,12 +584,14 @@ class QrackAceBackend:
491
584
  self.sim[b[0]].x(b[1])
492
585
  return
493
586
 
494
- b = hq[0]
495
- self.sim[b[0]].x(b[1])
496
- b = hq[1]
587
+ qb, lhv = self._get_qb_lhv_indices(hq)
588
+
589
+ for q in qb:
590
+ b = hq[q]
591
+ self.sim[b[0]].x(b[1])
592
+
593
+ b = hq[lhv]
497
594
  b.x()
498
- b = hq[2]
499
- self.sim[b[0]].x(b[1])
500
595
 
501
596
  def y(self, lq):
502
597
  hq = self._unpack(lq)
@@ -505,12 +600,14 @@ class QrackAceBackend:
505
600
  self.sim[b[0]].y(b[1])
506
601
  return
507
602
 
508
- b = hq[0]
509
- self.sim[b[0]].y(b[1])
510
- b = hq[1]
603
+ qb, lhv = self._get_qb_lhv_indices(hq)
604
+
605
+ for q in qb:
606
+ b = hq[q]
607
+ self.sim[b[0]].y(b[1])
608
+
609
+ b = hq[lhv]
511
610
  b.y()
512
- b = hq[2]
513
- self.sim[b[0]].y(b[1])
514
611
 
515
612
  def z(self, lq):
516
613
  hq = self._unpack(lq)
@@ -519,12 +616,14 @@ class QrackAceBackend:
519
616
  self.sim[b[0]].z(b[1])
520
617
  return
521
618
 
522
- b = hq[0]
523
- self.sim[b[0]].z(b[1])
524
- b = hq[1]
619
+ qb, lhv = self._get_qb_lhv_indices(hq)
620
+
621
+ for q in qb:
622
+ b = hq[q]
623
+ self.sim[b[0]].z(b[1])
624
+
625
+ b = hq[lhv]
525
626
  b.z()
526
- b = hq[2]
527
- self.sim[b[0]].z(b[1])
528
627
 
529
628
  def t(self, lq):
530
629
  hq = self._unpack(lq)
@@ -533,12 +632,14 @@ class QrackAceBackend:
533
632
  self.sim[b[0]].t(b[1])
534
633
  return
535
634
 
536
- b = hq[0]
537
- self.sim[b[0]].t(b[1])
538
- b = hq[1]
635
+ qb, lhv = self._get_qb_lhv_indices(hq)
636
+
637
+ for q in qb:
638
+ b = hq[q]
639
+ self.sim[b[0]].t(b[1])
640
+
641
+ b = hq[lhv]
539
642
  b.t()
540
- b = hq[2]
541
- self.sim[b[0]].t(b[1])
542
643
 
543
644
  def adjt(self, lq):
544
645
  hq = self._unpack(lq)
@@ -547,12 +648,14 @@ class QrackAceBackend:
547
648
  self.sim[b[0]].adjt(b[1])
548
649
  return
549
650
 
550
- b = hq[0]
551
- self.sim[b[0]].adjt(b[1])
552
- b = hq[1]
651
+ qb, lhv = self._get_qb_lhv_indices(hq)
652
+
653
+ for q in qb:
654
+ b = hq[q]
655
+ self.sim[b[0]].adjt(b[1])
656
+
657
+ b = hq[lhv]
553
658
  b.adjt()
554
- b = hq[2]
555
- self.sim[b[0]].adjt(b[1])
556
659
 
557
660
  def _get_gate(self, pauli, anti, sim_id):
558
661
  gate = None
@@ -573,10 +676,47 @@ class QrackAceBackend:
573
676
 
574
677
  return gate, shadow
575
678
 
576
- def _cpauli(self, lq1, lq2, anti, pauli):
577
- lq1_lr = self._is_col_long_range[lq1 % self._row_length]
578
- lq2_lr = self._is_col_long_range[lq2 % self._row_length]
679
+ def _get_connected(self, i, is_row):
680
+ long_range = self._is_row_long_range if is_row else self._is_col_long_range
681
+ length = self._col_length if is_row else self._row_length
682
+
683
+ connected = [i]
684
+ c = (i - 1) % length
685
+ while long_range[c] and (len(connected) < length):
686
+ connected.append(c)
687
+ c = (c - 1) % length
688
+ if len(connected) < length:
689
+ connected.append(c)
690
+ boundary = len(connected)
691
+ c = (i + 1) % length
692
+ while long_range[c] and (len(connected) < length):
693
+ connected.append(c)
694
+ c = (c + 1) % length
695
+ if len(connected) < length:
696
+ connected.append(c)
697
+
698
+ return connected, boundary
699
+
700
+ def _apply_coupling(self, pauli, anti, qb1, lhv1, hq1, qb2, lhv2, hq2, lq1_lr):
701
+ for q1 in qb1:
702
+ if q1 == lhv1:
703
+ continue
704
+ b1 = hq1[q1]
705
+ gate_fn, shadow_fn = self._get_gate(pauli, anti, b1[0])
706
+ for q2 in qb2:
707
+ if q2 == lhv2:
708
+ continue
709
+ b2 = hq2[q2]
710
+ if b1[0] == b2[0]:
711
+ gate_fn([b1[1]], b2[1])
712
+ elif (
713
+ lq1_lr
714
+ or (b1[1] == b2[1])
715
+ or ((len(qb1) == 2) and (b1[1] == (b2[1] & 1)))
716
+ ):
717
+ shadow_fn(b1, b2)
579
718
 
719
+ def _cpauli(self, lq1, lq2, anti, pauli):
580
720
  lq1_row = lq1 // self._row_length
581
721
  lq1_col = lq1 % self._row_length
582
722
  lq2_row = lq2 // self._row_length
@@ -585,6 +725,9 @@ class QrackAceBackend:
585
725
  hq1 = self._unpack(lq1)
586
726
  hq2 = self._unpack(lq2)
587
727
 
728
+ lq1_lr = len(hq1) == 1
729
+ lq2_lr = len(hq2) == 1
730
+
588
731
  if lq1_lr and lq2_lr:
589
732
  connected = (lq1_col == lq2_col) or (
590
733
  (self.long_range_columns + 1) >= self._row_length
@@ -607,80 +750,20 @@ class QrackAceBackend:
607
750
  shadow(b1, b2)
608
751
  return
609
752
 
610
- if self._row_length == 2:
611
- gate, shadow = self._get_gate(pauli, anti, hq1[2][0])
612
- gate([hq1[2][1]], hq2[0][1])
613
- gate, shadow = self._get_gate(pauli, anti, hq1[0][0])
614
- gate([hq1[0][1]], hq2[2][1])
615
- _cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
616
- return
617
-
618
- connected_cols = []
619
- c = (lq1_col - 1) % self._row_length
620
- while self._is_col_long_range[c] and (
621
- len(connected_cols) < (self._row_length - 1)
622
- ):
623
- connected_cols.append(c)
624
- c = (c - 1) % self._row_length
625
- if len(connected_cols) < (self._row_length - 1):
626
- connected_cols.append(c)
627
- boundary = len(connected_cols)
628
- c = (lq1_col + 1) % self._row_length
629
- while self._is_col_long_range[c] and (
630
- len(connected_cols) < (self._row_length - 1)
631
- ):
632
- connected_cols.append(c)
633
- c = (c + 1) % self._row_length
634
- if len(connected_cols) < (self._row_length - 1):
635
- connected_cols.append(c)
636
-
637
753
  self._correct(lq1)
638
754
 
639
- if (lq2_col in connected_cols) and (connected_cols.index(lq2_col) < boundary):
640
- # lq2_col < lq1_col
641
- gate, shadow = self._get_gate(pauli, anti, hq1[0][0])
642
- if lq1_lr:
643
- gate([hq1[0][1]], hq2[2][1])
644
- shadow(hq1[0], hq2[0])
645
- _cpauli_lhv(self.sim[hq1[0][0]].prob(hq1[0][1]), hq2[1], pauli, anti)
646
- elif lq2_lr:
647
- gate([hq1[0][1]], hq2[0][1])
648
- else:
649
- gate([hq1[0][1]], hq2[2][1])
650
- shadow(hq1[2], hq2[0])
651
- _cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
652
- elif lq2_col in connected_cols:
653
- # lq1_col < lq2_col
654
- gate, shadow = self._get_gate(pauli, anti, hq2[0][0])
655
- if lq1_lr:
656
- gate([hq1[0][1]], hq2[0][1])
657
- shadow(hq1[0], hq2[2])
658
- _cpauli_lhv(self.sim[hq1[0][0]].prob(hq1[0][1]), hq2[1], pauli, anti)
659
- elif lq2_lr:
660
- gate([hq1[2][1]], hq2[0][1])
661
- else:
662
- gate([hq1[2][1]], hq2[0][1])
663
- shadow(hq1[0], hq2[2])
664
- _cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
665
- elif lq1_col == lq2_col:
666
- # Both are in the same boundary column.
667
- b = hq1[0]
668
- gate, shadow = self._get_gate(pauli, anti, b[0])
669
- gate([b[1]], hq2[0][1])
670
- _cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
671
- b = hq1[2]
672
- gate, shadow = self._get_gate(pauli, anti, b[0])
673
- gate([b[1]], hq2[2][1])
674
- else:
675
- # The qubits have no quantum connection.
676
- gate, shadow = self._get_gate(pauli, anti, hq1[0][0])
677
- shadow(hq1[0], hq2[0])
678
- if lq1_lr:
679
- shadow(hq1[0], hq2[2])
680
- shadow(hq1[0], hq2[1])
681
- elif not lq2_lr:
682
- _cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
683
- shadow(hq1[2], hq2[2])
755
+ qb1, lhv1 = self._get_qb_lhv_indices(hq1)
756
+ qb2, lhv2 = self._get_qb_lhv_indices(hq2)
757
+ # Apply cross coupling on hardware qubits first
758
+ self._apply_coupling(pauli, anti, qb1, lhv1, hq1, qb2, lhv2, hq2, lq1_lr)
759
+ # Apply coupling to the local-hidden-variable target
760
+ if lhv2 >= 0:
761
+ _cpauli_lhv(
762
+ hq1[lhv1].prob() if lhv1 >= 0 else self.sim[hq1[0][0]].prob(hq1[0][1]),
763
+ hq2[lhv2],
764
+ pauli,
765
+ anti,
766
+ )
684
767
 
685
768
  self._correct(lq1, True)
686
769
  if pauli != Pauli.PauliZ:
@@ -772,14 +855,39 @@ class QrackAceBackend:
772
855
  return self.sim[b[0]].prob(b[1])
773
856
 
774
857
  self._correct(lq)
775
- b0 = hq[0]
776
- b1 = hq[1]
777
- b2 = hq[2]
778
- # RMS
779
- p = [self.sim[b0[0]].prob(b0[1]), b1.prob(), self.sim[b2[0]].prob(b2[1])]
780
- # Balancing suggestion from Elara (the custom OpenAI GPT)
781
- prms = math.sqrt((p[0] ** 2 + p[1] ** 2 + p[2] ** 2) / 3)
782
- qrms = math.sqrt(((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2) / 3)
858
+ if len(hq) == 5:
859
+ # RMS
860
+ p = [
861
+ self.sim[hq[0][0]].prob(hq[0][1]),
862
+ self.sim[hq[1][0]].prob(hq[1][1]),
863
+ hq[2].prob(),
864
+ self.sim[hq[3][0]].prob(hq[3][1]),
865
+ self.sim[hq[4][0]].prob(hq[4][1]),
866
+ ]
867
+ # Balancing suggestion from Elara (the custom OpenAI GPT)
868
+ prms = math.sqrt(
869
+ (p[0] ** 2 + p[1] ** 2 + 3 * (p[2] ** 2) + p[3] ** 2 + p[4] ** 2) / 7
870
+ )
871
+ qrms = math.sqrt(
872
+ (
873
+ (1 - p[0]) ** 2
874
+ + (1 - p[1]) ** 2
875
+ + 3 * ((1 - p[2]) ** 2)
876
+ + (1 - p[3]) ** 2
877
+ + (1 - p[4]) ** 2
878
+ )
879
+ / 7
880
+ )
881
+ else:
882
+ # RMS
883
+ p = [
884
+ self.sim[hq[0][0]].prob(hq[0][1]),
885
+ self.sim[hq[1][0]].prob(hq[1][1]),
886
+ hq[2].prob(),
887
+ ]
888
+ # Balancing suggestion from Elara (the custom OpenAI GPT)
889
+ prms = math.sqrt((p[0] ** 2 + p[1] ** 2 + p[2] ** 2) / 3)
890
+ qrms = math.sqrt(((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2) / 3)
783
891
 
784
892
  return (prms + (1 - qrms)) / 2
785
893
 
@@ -792,44 +900,47 @@ class QrackAceBackend:
792
900
  p = self.prob(lq)
793
901
  result = ((p + self._epsilon) >= 1) or (random.random() < p)
794
902
 
795
- b = hq[0]
796
- p = self.sim[b[0]].prob(b[1]) if result else (1 - self.sim[b[0]].prob(b[1]))
797
- if p < self._epsilon:
798
- if self.sim[b[0]].m(b[1]) != result:
799
- self.sim[b[0]].x(b[1])
800
- else:
801
- self.sim[b[0]].force_m(b[1], result)
903
+ qb, lhv = self._get_qb_lhv_indices(hq)
802
904
 
803
- b = hq[1]
905
+ for q in qb:
906
+ b = hq[q]
907
+ p = self.sim[b[0]].prob(b[1]) if result else (1 - self.sim[b[0]].prob(b[1]))
908
+ if p < self._epsilon:
909
+ if self.sim[b[0]].m(b[1]) != result:
910
+ self.sim[b[0]].x(b[1])
911
+ else:
912
+ self.sim[b[0]].force_m(b[1], result)
913
+
914
+ b = hq[lhv]
804
915
  b.reset()
805
916
  if result:
806
917
  b.x()
807
918
 
808
- b = hq[2]
809
- p = self.sim[b[0]].prob(b[1]) if result else (1 - self.sim[b[0]].prob(b[1]))
810
- if p < self._epsilon:
811
- if self.sim[b[0]].m(b[1]) != result:
812
- self.sim[b[0]].x(b[1])
813
- else:
814
- self.sim[b[0]].force_m(b[1], result)
815
-
816
919
  return result
817
920
 
818
- def force_m(self, lq, c):
921
+ def force_m(self, lq, result):
819
922
  hq = self._unpack(lq)
820
923
  if len(hq) < 2:
821
924
  b = hq[0]
822
- return self.sim[b[0]].force_m(b[1])
925
+ return self.sim[b[0]].force_m(b[1], result)
823
926
 
824
927
  self._correct(lq)
928
+
929
+ qb, lhv = self._get_qb_lhv_indices(hq)
930
+
931
+ for q in qb:
932
+ b = hq[q]
933
+ p = self.sim[b[0]].prob(b[1]) if result else (1 - self.sim[b[0]].prob(b[1]))
934
+ if p < self._epsilon:
935
+ if self.sim[b[0]].m(b[1]) != result:
936
+ self.sim[b[0]].x(b[1])
937
+ else:
938
+ self.sim[b[0]].force_m(b[1], result)
939
+
825
940
  b = hq[1]
826
941
  b.reset()
827
- if c:
942
+ if result:
828
943
  b.x()
829
- b = hq[0]
830
- self.sim[b[0]].force_m(b[1], c)
831
- b = hq[2]
832
- self.sim[b[0]].force_m(b[1], c)
833
944
 
834
945
  return c
835
946
 
@@ -1212,28 +1323,12 @@ class QrackAceBackend:
1212
1323
  return row * cols + col
1213
1324
 
1214
1325
  for col in range(cols):
1215
- connected_cols = [col]
1216
- c = (col - 1) % cols
1217
- while self._is_col_long_range[c] and (
1218
- len(connected_cols) < self._row_length
1219
- ):
1220
- connected_cols.append(c)
1221
- c = (c - 1) % cols
1222
- if len(connected_cols) < self._row_length:
1223
- connected_cols.append(c)
1224
- c = (col + 1) % cols
1225
- while self._is_col_long_range[c] and (
1226
- len(connected_cols) < self._row_length
1227
- ):
1228
- connected_cols.append(c)
1229
- c = (c + 1) % cols
1230
- if len(connected_cols) < self._row_length:
1231
- connected_cols.append(c)
1232
-
1326
+ connected_cols, _ = self._get_connected(col, False)
1233
1327
  for row in range(rows):
1328
+ connected_rows, _ = self._get_connected(row, False)
1234
1329
  a = logical_index(row, col)
1235
1330
  for c in connected_cols:
1236
- for r in range(0, rows):
1331
+ for r in connected_rows:
1237
1332
  b = logical_index(r, c)
1238
1333
  if a != b:
1239
1334
  coupling_map.add((a, b))
@@ -1259,9 +1354,7 @@ class QrackAceBackend:
1259
1354
  if is_long_a and is_long_b:
1260
1355
  continue # No noise on long-to-long
1261
1356
 
1262
- same_col = col_a == col_b
1263
-
1264
- if same_col:
1357
+ if (col_a == col_b) or (row_a == row_b):
1265
1358
  continue # No noise for same column
1266
1359
 
1267
1360
  if is_long_a or is_long_b:
@@ -50,7 +50,7 @@ class QrackSimulator:
50
50
  qubitCount=-1,
51
51
  cloneSid=-1,
52
52
  isTensorNetwork=True,
53
- isSchmidtDecomposeMulti=True,
53
+ isSchmidtDecomposeMulti=False,
54
54
  isSchmidtDecompose=True,
55
55
  isStabilizerHybrid=False,
56
56
  isBinaryDecisionTree=False,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cuda
3
- Version: 1.55.4
3
+ Version: 1.57.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.55.4"
10
+ VERSION = "1.57.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
File without changes