pyqrack-cuda 1.56.0__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.56.0/pyqrack_cuda.egg-info → pyqrack_cuda-1.57.0}/PKG-INFO +1 -1
  2. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/qrack_ace_backend.py +318 -240
  3. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0/pyqrack_cuda.egg-info}/PKG-INFO +1 -1
  4. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/setup.py +1 -1
  5. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/LICENSE +0 -0
  6. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/MANIFEST.in +0 -0
  7. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/Makefile +0 -0
  8. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/README.md +0 -0
  9. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyproject.toml +0 -0
  10. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/__init__.py +0 -0
  11. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/neuron_activation_fn.py +0 -0
  12. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/pauli.py +0 -0
  13. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/qrack_circuit.py +0 -0
  14. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/qrack_neuron.py +0 -0
  15. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/qrack_neuron_torch_layer.py +0 -0
  16. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/qrack_simulator.py +0 -0
  17. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/qrack_stabilizer.py +0 -0
  18. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/qrack_system/__init__.py +0 -0
  19. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/qrack_system/qrack_system.py +0 -0
  20. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/quimb_circuit_type.py +0 -0
  21. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/stats/__init__.py +0 -0
  22. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/stats/load_quantized_data.py +0 -0
  23. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack/stats/quantize_by_range.py +0 -0
  24. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack_cuda.egg-info/SOURCES.txt +0 -0
  25. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack_cuda.egg-info/dependency_links.txt +0 -0
  26. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack_cuda.egg-info/not-zip-safe +0 -0
  27. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack_cuda.egg-info/requires.txt +0 -0
  28. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.0}/pyqrack_cuda.egg-info/top_level.txt +0 -0
  29. {pyqrack_cuda-1.56.0 → 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.56.0
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,7 +168,8 @@ 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,
174
175
  isSchmidtDecomposeMulti=False,
@@ -187,6 +188,7 @@ class QrackAceBackend:
187
188
  if toClone:
188
189
  qubit_count = toClone.num_qubits()
189
190
  long_range_columns = toClone.long_range_columns
191
+ long_range_rows = toClone.long_range_rows
190
192
  is_transpose = toClone.is_transpose
191
193
  if qubit_count < 0:
192
194
  qubit_count = 0
@@ -195,6 +197,7 @@ class QrackAceBackend:
195
197
 
196
198
  self._factor_width(qubit_count, is_transpose)
197
199
  self.long_range_columns = long_range_columns
200
+ self.long_range_rows = long_range_rows
198
201
  self.is_transpose = is_transpose
199
202
 
200
203
  fppow = 5
@@ -209,45 +212,68 @@ class QrackAceBackend:
209
212
 
210
213
  self._coupling_map = None
211
214
 
212
- # If there's only one or zero "False" columns,
215
+ # If there's only one or zero "False" columns or rows,
213
216
  # the entire simulator is connected, anyway.
214
217
  len_col_seq = long_range_columns + 1
215
- sim_count = (self._row_length + len_col_seq - 1) // len_col_seq
216
- 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):
217
220
  self._is_col_long_range = [True] * self._row_length
218
221
  else:
219
222
  col_seq = [True] * long_range_columns + [False]
220
- 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]
221
224
  if long_range_columns < self._row_length:
222
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
223
240
 
224
241
  self._qubit_dict = {}
225
- self._lhv_dict = {}
226
- self._hardware_offset = []
227
242
  sim_counts = [0] * sim_count
228
243
  sim_id = 0
229
244
  tot_qubits = 0
230
- for r in range(self._col_length):
245
+ for r in self._is_row_long_range:
231
246
  for c in self._is_col_long_range:
232
- self._hardware_offset.append(tot_qubits)
233
- if c:
234
- self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
235
- tot_qubits += 1
236
- sim_counts[sim_id] += 1
237
- else:
238
- self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
239
- tot_qubits += 1
240
- sim_counts[sim_id] += 1
241
-
242
- self._lhv_dict[tot_qubits] = LHVQubit(
243
- 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
+ )
244
261
  )
245
- tot_qubits += 1
246
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:
247
273
  sim_id = (sim_id + 1) % sim_count
248
- self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
249
- tot_qubits += 1
250
- sim_counts[sim_id] += 1
274
+
275
+ self._qubit_dict[tot_qubits] = qubit
276
+ tot_qubits += 1
251
277
 
252
278
  self.sim = []
253
279
  for i in range(sim_count):
@@ -362,57 +388,97 @@ class QrackAceBackend:
362
388
  self._qec_x(c)
363
389
 
364
390
  def _unpack(self, lq):
365
- offset = self._hardware_offset[lq]
391
+ return self._qubit_dict[lq]
366
392
 
367
- if self._is_col_long_range[lq % self._row_length]:
368
- 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
369
404
 
370
- return [
371
- self._qubit_dict[offset],
372
- self._lhv_dict[offset + 1],
373
- self._qubit_dict[offset + 2],
374
- ]
405
+ return qb, lhv
375
406
 
376
407
  def _correct(self, lq, phase=False):
377
- 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:
378
411
  return
379
412
 
380
- hq = self._unpack(lq)
413
+ qb, lhv = self._get_qb_lhv_indices(hq)
381
414
 
382
415
  if phase:
383
- b = hq[0]
384
- self.sim[b[0]].h(b[1])
385
- b = hq[1]
416
+ for q in qb:
417
+ b = hq[q]
418
+ self.sim[b[0]].h(b[1])
419
+ b = hq[lhv]
386
420
  b.h()
387
- b = hq[2]
388
- self.sim[b[0]].h(b[1])
389
421
 
390
- # RMS
391
- p = [
392
- self.sim[hq[0][0]].prob(hq[0][1]),
393
- hq[1].prob(),
394
- self.sim[hq[2][0]].prob(hq[2][1]),
395
- ]
396
- # Balancing suggestion from Elara (the custom OpenAI GPT)
397
- prms = math.sqrt((p[0] ** 2 + p[1] ** 2 + p[2] ** 2) / 3)
398
- qrms = math.sqrt(((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2) / 3)
399
- result = ((prms + (1 - qrms)) / 2) >= 0.5
400
- syndrome = [1 - p[0], 1 - p[1], 1 - p[2]] if result else [p[0], p[1], p[2]]
401
-
402
- for q in range(3):
403
- if syndrome[q] > (0.5 + self._epsilon):
404
- if q == 1:
405
- hq[q].x()
406
- else:
407
- 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])
408
475
 
409
476
  if phase:
410
- b = hq[0]
411
- self.sim[b[0]].h(b[1])
412
- b = hq[1]
477
+ for q in qb:
478
+ b = hq[q]
479
+ self.sim[b[0]].h(b[1])
480
+ b = hq[lhv]
413
481
  b.h()
414
- b = hq[2]
415
- self.sim[b[0]].h(b[1])
416
482
 
417
483
  def u(self, lq, th, ph, lm):
418
484
  hq = self._unpack(lq)
@@ -421,12 +487,14 @@ class QrackAceBackend:
421
487
  self.sim[b[0]].u(b[1], th, ph, lm)
422
488
  return
423
489
 
424
- b = hq[0]
425
- self.sim[b[0]].u(b[1], th, ph, lm)
426
- 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]
427
497
  b.u(th, ph, lm)
428
- b = hq[2]
429
- self.sim[b[0]].u(b[1], th, ph, lm)
430
498
 
431
499
  self._correct(lq, False)
432
500
  self._correct(lq, True)
@@ -438,17 +506,19 @@ class QrackAceBackend:
438
506
  self.sim[b[0]].r(p, th, b[1])
439
507
  return
440
508
 
441
- b = hq[0]
442
- self.sim[b[0]].r(p, th, b[1])
443
- 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]
444
516
  if p == Pauli.PauliX:
445
517
  b.rx(th)
446
518
  elif p == Pauli.PauliY:
447
519
  b.ry(th)
448
520
  elif p == Pauli.PauliZ:
449
521
  b.rz(th)
450
- b = hq[2]
451
- self.sim[b[0]].r(p, th, b[1])
452
522
 
453
523
  if p != Pauli.PauliZ:
454
524
  self._correct(lq, False)
@@ -463,12 +533,16 @@ class QrackAceBackend:
463
533
  return
464
534
 
465
535
  self._correct(lq)
466
- b = hq[0]
467
- self.sim[b[0]].h(b[1])
468
- 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]
469
544
  b.h()
470
- b = hq[2]
471
- self.sim[b[0]].h(b[1])
545
+
472
546
  self._correct(lq)
473
547
 
474
548
  def s(self, lq):
@@ -478,12 +552,14 @@ class QrackAceBackend:
478
552
  self.sim[b[0]].s(b[1])
479
553
  return
480
554
 
481
- b = hq[0]
482
- self.sim[b[0]].s(b[1])
483
- 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]
484
562
  b.s()
485
- b = hq[2]
486
- self.sim[b[0]].s(b[1])
487
563
 
488
564
  def adjs(self, lq):
489
565
  hq = self._unpack(lq)
@@ -492,12 +568,14 @@ class QrackAceBackend:
492
568
  self.sim[b[0]].adjs(b[1])
493
569
  return
494
570
 
495
- b = hq[0]
496
- self.sim[b[0]].adjs(b[1])
497
- 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]
498
578
  b.adjs()
499
- b = hq[2]
500
- self.sim[b[0]].adjs(b[1])
501
579
 
502
580
  def x(self, lq):
503
581
  hq = self._unpack(lq)
@@ -506,12 +584,14 @@ class QrackAceBackend:
506
584
  self.sim[b[0]].x(b[1])
507
585
  return
508
586
 
509
- b = hq[0]
510
- self.sim[b[0]].x(b[1])
511
- 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]
512
594
  b.x()
513
- b = hq[2]
514
- self.sim[b[0]].x(b[1])
515
595
 
516
596
  def y(self, lq):
517
597
  hq = self._unpack(lq)
@@ -520,12 +600,14 @@ class QrackAceBackend:
520
600
  self.sim[b[0]].y(b[1])
521
601
  return
522
602
 
523
- b = hq[0]
524
- self.sim[b[0]].y(b[1])
525
- 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]
526
610
  b.y()
527
- b = hq[2]
528
- self.sim[b[0]].y(b[1])
529
611
 
530
612
  def z(self, lq):
531
613
  hq = self._unpack(lq)
@@ -534,12 +616,14 @@ class QrackAceBackend:
534
616
  self.sim[b[0]].z(b[1])
535
617
  return
536
618
 
537
- b = hq[0]
538
- self.sim[b[0]].z(b[1])
539
- 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]
540
626
  b.z()
541
- b = hq[2]
542
- self.sim[b[0]].z(b[1])
543
627
 
544
628
  def t(self, lq):
545
629
  hq = self._unpack(lq)
@@ -548,12 +632,14 @@ class QrackAceBackend:
548
632
  self.sim[b[0]].t(b[1])
549
633
  return
550
634
 
551
- b = hq[0]
552
- self.sim[b[0]].t(b[1])
553
- 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]
554
642
  b.t()
555
- b = hq[2]
556
- self.sim[b[0]].t(b[1])
557
643
 
558
644
  def adjt(self, lq):
559
645
  hq = self._unpack(lq)
@@ -562,12 +648,14 @@ class QrackAceBackend:
562
648
  self.sim[b[0]].adjt(b[1])
563
649
  return
564
650
 
565
- b = hq[0]
566
- self.sim[b[0]].adjt(b[1])
567
- 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]
568
658
  b.adjt()
569
- b = hq[2]
570
- self.sim[b[0]].adjt(b[1])
571
659
 
572
660
  def _get_gate(self, pauli, anti, sim_id):
573
661
  gate = None
@@ -588,10 +676,47 @@ class QrackAceBackend:
588
676
 
589
677
  return gate, shadow
590
678
 
591
- def _cpauli(self, lq1, lq2, anti, pauli):
592
- lq1_lr = self._is_col_long_range[lq1 % self._row_length]
593
- 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)
594
718
 
719
+ def _cpauli(self, lq1, lq2, anti, pauli):
595
720
  lq1_row = lq1 // self._row_length
596
721
  lq1_col = lq1 % self._row_length
597
722
  lq2_row = lq2 // self._row_length
@@ -600,6 +725,9 @@ class QrackAceBackend:
600
725
  hq1 = self._unpack(lq1)
601
726
  hq2 = self._unpack(lq2)
602
727
 
728
+ lq1_lr = len(hq1) == 1
729
+ lq2_lr = len(hq2) == 1
730
+
603
731
  if lq1_lr and lq2_lr:
604
732
  connected = (lq1_col == lq2_col) or (
605
733
  (self.long_range_columns + 1) >= self._row_length
@@ -622,80 +750,20 @@ class QrackAceBackend:
622
750
  shadow(b1, b2)
623
751
  return
624
752
 
625
- if self._row_length == 2:
626
- gate, shadow = self._get_gate(pauli, anti, hq1[2][0])
627
- gate([hq1[2][1]], hq2[0][1])
628
- gate, shadow = self._get_gate(pauli, anti, hq1[0][0])
629
- gate([hq1[0][1]], hq2[2][1])
630
- _cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
631
- return
632
-
633
- connected_cols = []
634
- c = (lq1_col - 1) % self._row_length
635
- while self._is_col_long_range[c] and (
636
- len(connected_cols) < (self._row_length - 1)
637
- ):
638
- connected_cols.append(c)
639
- c = (c - 1) % self._row_length
640
- if len(connected_cols) < (self._row_length - 1):
641
- connected_cols.append(c)
642
- boundary = len(connected_cols)
643
- c = (lq1_col + 1) % self._row_length
644
- while self._is_col_long_range[c] and (
645
- len(connected_cols) < (self._row_length - 1)
646
- ):
647
- connected_cols.append(c)
648
- c = (c + 1) % self._row_length
649
- if len(connected_cols) < (self._row_length - 1):
650
- connected_cols.append(c)
651
-
652
753
  self._correct(lq1)
653
754
 
654
- if (lq2_col in connected_cols) and (connected_cols.index(lq2_col) < boundary):
655
- # lq2_col < lq1_col
656
- gate, shadow = self._get_gate(pauli, anti, hq1[0][0])
657
- if lq1_lr:
658
- gate([hq1[0][1]], hq2[2][1])
659
- shadow(hq1[0], hq2[0])
660
- _cpauli_lhv(self.sim[hq1[0][0]].prob(hq1[0][1]), hq2[1], pauli, anti)
661
- elif lq2_lr:
662
- gate([hq1[0][1]], hq2[0][1])
663
- else:
664
- gate([hq1[0][1]], hq2[2][1])
665
- shadow(hq1[2], hq2[0])
666
- _cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
667
- elif lq2_col in connected_cols:
668
- # lq1_col < lq2_col
669
- gate, shadow = self._get_gate(pauli, anti, hq2[0][0])
670
- if lq1_lr:
671
- gate([hq1[0][1]], hq2[0][1])
672
- shadow(hq1[0], hq2[2])
673
- _cpauli_lhv(self.sim[hq1[0][0]].prob(hq1[0][1]), hq2[1], pauli, anti)
674
- elif lq2_lr:
675
- gate([hq1[2][1]], hq2[0][1])
676
- else:
677
- gate([hq1[2][1]], hq2[0][1])
678
- shadow(hq1[0], hq2[2])
679
- _cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
680
- elif lq1_col == lq2_col:
681
- # Both are in the same boundary column.
682
- b = hq1[0]
683
- gate, shadow = self._get_gate(pauli, anti, b[0])
684
- gate([b[1]], hq2[0][1])
685
- _cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
686
- b = hq1[2]
687
- gate, shadow = self._get_gate(pauli, anti, b[0])
688
- gate([b[1]], hq2[2][1])
689
- else:
690
- # The qubits have no quantum connection.
691
- gate, shadow = self._get_gate(pauli, anti, hq1[0][0])
692
- shadow(hq1[0], hq2[0])
693
- if lq1_lr:
694
- shadow(hq1[0], hq2[2])
695
- shadow(hq1[0], hq2[1])
696
- elif not lq2_lr:
697
- _cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
698
- 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
+ )
699
767
 
700
768
  self._correct(lq1, True)
701
769
  if pauli != Pauli.PauliZ:
@@ -787,14 +855,39 @@ class QrackAceBackend:
787
855
  return self.sim[b[0]].prob(b[1])
788
856
 
789
857
  self._correct(lq)
790
- b0 = hq[0]
791
- b1 = hq[1]
792
- b2 = hq[2]
793
- # RMS
794
- p = [self.sim[b0[0]].prob(b0[1]), b1.prob(), self.sim[b2[0]].prob(b2[1])]
795
- # Balancing suggestion from Elara (the custom OpenAI GPT)
796
- prms = math.sqrt((p[0] ** 2 + p[1] ** 2 + p[2] ** 2) / 3)
797
- 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)
798
891
 
799
892
  return (prms + (1 - qrms)) / 2
800
893
 
@@ -807,44 +900,47 @@ class QrackAceBackend:
807
900
  p = self.prob(lq)
808
901
  result = ((p + self._epsilon) >= 1) or (random.random() < p)
809
902
 
810
- b = hq[0]
811
- p = self.sim[b[0]].prob(b[1]) if result else (1 - self.sim[b[0]].prob(b[1]))
812
- if p < self._epsilon:
813
- if self.sim[b[0]].m(b[1]) != result:
814
- self.sim[b[0]].x(b[1])
815
- else:
816
- self.sim[b[0]].force_m(b[1], result)
903
+ qb, lhv = self._get_qb_lhv_indices(hq)
817
904
 
818
- 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]
819
915
  b.reset()
820
916
  if result:
821
917
  b.x()
822
918
 
823
- b = hq[2]
824
- p = self.sim[b[0]].prob(b[1]) if result else (1 - self.sim[b[0]].prob(b[1]))
825
- if p < self._epsilon:
826
- if self.sim[b[0]].m(b[1]) != result:
827
- self.sim[b[0]].x(b[1])
828
- else:
829
- self.sim[b[0]].force_m(b[1], result)
830
-
831
919
  return result
832
920
 
833
- def force_m(self, lq, c):
921
+ def force_m(self, lq, result):
834
922
  hq = self._unpack(lq)
835
923
  if len(hq) < 2:
836
924
  b = hq[0]
837
- return self.sim[b[0]].force_m(b[1])
925
+ return self.sim[b[0]].force_m(b[1], result)
838
926
 
839
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
+
840
940
  b = hq[1]
841
941
  b.reset()
842
- if c:
942
+ if result:
843
943
  b.x()
844
- b = hq[0]
845
- self.sim[b[0]].force_m(b[1], c)
846
- b = hq[2]
847
- self.sim[b[0]].force_m(b[1], c)
848
944
 
849
945
  return c
850
946
 
@@ -1227,28 +1323,12 @@ class QrackAceBackend:
1227
1323
  return row * cols + col
1228
1324
 
1229
1325
  for col in range(cols):
1230
- connected_cols = [col]
1231
- c = (col - 1) % cols
1232
- while self._is_col_long_range[c] and (
1233
- len(connected_cols) < self._row_length
1234
- ):
1235
- connected_cols.append(c)
1236
- c = (c - 1) % cols
1237
- if len(connected_cols) < self._row_length:
1238
- connected_cols.append(c)
1239
- c = (col + 1) % cols
1240
- while self._is_col_long_range[c] and (
1241
- len(connected_cols) < self._row_length
1242
- ):
1243
- connected_cols.append(c)
1244
- c = (c + 1) % cols
1245
- if len(connected_cols) < self._row_length:
1246
- connected_cols.append(c)
1247
-
1326
+ connected_cols, _ = self._get_connected(col, False)
1248
1327
  for row in range(rows):
1328
+ connected_rows, _ = self._get_connected(row, False)
1249
1329
  a = logical_index(row, col)
1250
1330
  for c in connected_cols:
1251
- for r in range(0, rows):
1331
+ for r in connected_rows:
1252
1332
  b = logical_index(r, c)
1253
1333
  if a != b:
1254
1334
  coupling_map.add((a, b))
@@ -1274,9 +1354,7 @@ class QrackAceBackend:
1274
1354
  if is_long_a and is_long_b:
1275
1355
  continue # No noise on long-to-long
1276
1356
 
1277
- same_col = col_a == col_b
1278
-
1279
- if same_col:
1357
+ if (col_a == col_b) or (row_a == row_b):
1280
1358
  continue # No noise for same column
1281
1359
 
1282
1360
  if is_long_a or is_long_b:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cuda
3
- Version: 1.56.0
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.56.0"
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