pyqrack-cuda 1.56.0__tar.gz → 1.57.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.56.0/pyqrack_cuda.egg-info → pyqrack_cuda-1.57.1}/PKG-INFO +1 -1
  2. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/qrack_ace_backend.py +317 -261
  3. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1/pyqrack_cuda.egg-info}/PKG-INFO +1 -1
  4. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/setup.py +1 -1
  5. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/LICENSE +0 -0
  6. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/MANIFEST.in +0 -0
  7. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/Makefile +0 -0
  8. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/README.md +0 -0
  9. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyproject.toml +0 -0
  10. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/__init__.py +0 -0
  11. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/neuron_activation_fn.py +0 -0
  12. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/pauli.py +0 -0
  13. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/qrack_circuit.py +0 -0
  14. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/qrack_neuron.py +0 -0
  15. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/qrack_neuron_torch_layer.py +0 -0
  16. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/qrack_simulator.py +0 -0
  17. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/qrack_stabilizer.py +0 -0
  18. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/qrack_system/__init__.py +0 -0
  19. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/qrack_system/qrack_system.py +0 -0
  20. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/quimb_circuit_type.py +0 -0
  21. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/stats/__init__.py +0 -0
  22. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/stats/load_quantized_data.py +0 -0
  23. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack/stats/quantize_by_range.py +0 -0
  24. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack_cuda.egg-info/SOURCES.txt +0 -0
  25. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack_cuda.egg-info/dependency_links.txt +0 -0
  26. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack_cuda.egg-info/not-zip-safe +0 -0
  27. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack_cuda.egg-info/requires.txt +0 -0
  28. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/pyqrack_cuda.egg-info/top_level.txt +0 -0
  29. {pyqrack_cuda-1.56.0 → pyqrack_cuda-1.57.1}/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.1
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,102 +725,23 @@ class QrackAceBackend:
600
725
  hq1 = self._unpack(lq1)
601
726
  hq2 = self._unpack(lq2)
602
727
 
603
- if lq1_lr and lq2_lr:
604
- connected = (lq1_col == lq2_col) or (
605
- (self.long_range_columns + 1) >= self._row_length
606
- )
607
- c = (lq1_col - 1) % self._row_length
608
- while not connected and self._is_col_long_range[c]:
609
- connected = lq2_col == c
610
- c = (c - 1) % self._row_length
611
- c = (lq1_col + 1) % self._row_length
612
- while not connected and self._is_col_long_range[c]:
613
- connected = lq2_col == c
614
- c = (c + 1) % self._row_length
615
-
616
- b1 = hq1[0]
617
- b2 = hq2[0]
618
- gate, shadow = self._get_gate(pauli, anti, b1[0])
619
- if connected:
620
- gate([b1[1]], b2[1])
621
- else:
622
- shadow(b1, b2)
623
- return
624
-
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)
728
+ lq1_lr = len(hq1) == 1
729
+ lq2_lr = len(hq2) == 1
651
730
 
652
731
  self._correct(lq1)
653
732
 
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])
733
+ qb1, lhv1 = self._get_qb_lhv_indices(hq1)
734
+ qb2, lhv2 = self._get_qb_lhv_indices(hq2)
735
+ # Apply cross coupling on hardware qubits first
736
+ self._apply_coupling(pauli, anti, qb1, lhv1, hq1, qb2, lhv2, hq2, lq1_lr)
737
+ # Apply coupling to the local-hidden-variable target
738
+ if lhv2 >= 0:
739
+ _cpauli_lhv(
740
+ hq1[lhv1].prob() if lhv1 >= 0 else self.sim[hq1[0][0]].prob(hq1[0][1]),
741
+ hq2[lhv2],
742
+ pauli,
743
+ anti,
744
+ )
699
745
 
700
746
  self._correct(lq1, True)
701
747
  if pauli != Pauli.PauliZ:
@@ -787,14 +833,39 @@ class QrackAceBackend:
787
833
  return self.sim[b[0]].prob(b[1])
788
834
 
789
835
  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)
836
+ if len(hq) == 5:
837
+ # RMS
838
+ p = [
839
+ self.sim[hq[0][0]].prob(hq[0][1]),
840
+ self.sim[hq[1][0]].prob(hq[1][1]),
841
+ hq[2].prob(),
842
+ self.sim[hq[3][0]].prob(hq[3][1]),
843
+ self.sim[hq[4][0]].prob(hq[4][1]),
844
+ ]
845
+ # Balancing suggestion from Elara (the custom OpenAI GPT)
846
+ prms = math.sqrt(
847
+ (p[0] ** 2 + p[1] ** 2 + 3 * (p[2] ** 2) + p[3] ** 2 + p[4] ** 2) / 7
848
+ )
849
+ qrms = math.sqrt(
850
+ (
851
+ (1 - p[0]) ** 2
852
+ + (1 - p[1]) ** 2
853
+ + 3 * ((1 - p[2]) ** 2)
854
+ + (1 - p[3]) ** 2
855
+ + (1 - p[4]) ** 2
856
+ )
857
+ / 7
858
+ )
859
+ else:
860
+ # RMS
861
+ p = [
862
+ self.sim[hq[0][0]].prob(hq[0][1]),
863
+ self.sim[hq[1][0]].prob(hq[1][1]),
864
+ hq[2].prob(),
865
+ ]
866
+ # Balancing suggestion from Elara (the custom OpenAI GPT)
867
+ prms = math.sqrt((p[0] ** 2 + p[1] ** 2 + p[2] ** 2) / 3)
868
+ qrms = math.sqrt(((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2) / 3)
798
869
 
799
870
  return (prms + (1 - qrms)) / 2
800
871
 
@@ -807,44 +878,47 @@ class QrackAceBackend:
807
878
  p = self.prob(lq)
808
879
  result = ((p + self._epsilon) >= 1) or (random.random() < p)
809
880
 
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)
881
+ qb, lhv = self._get_qb_lhv_indices(hq)
817
882
 
818
- b = hq[1]
883
+ for q in qb:
884
+ b = hq[q]
885
+ p = self.sim[b[0]].prob(b[1]) if result else (1 - self.sim[b[0]].prob(b[1]))
886
+ if p < self._epsilon:
887
+ if self.sim[b[0]].m(b[1]) != result:
888
+ self.sim[b[0]].x(b[1])
889
+ else:
890
+ self.sim[b[0]].force_m(b[1], result)
891
+
892
+ b = hq[lhv]
819
893
  b.reset()
820
894
  if result:
821
895
  b.x()
822
896
 
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
897
  return result
832
898
 
833
- def force_m(self, lq, c):
899
+ def force_m(self, lq, result):
834
900
  hq = self._unpack(lq)
835
901
  if len(hq) < 2:
836
902
  b = hq[0]
837
- return self.sim[b[0]].force_m(b[1])
903
+ return self.sim[b[0]].force_m(b[1], result)
838
904
 
839
905
  self._correct(lq)
906
+
907
+ qb, lhv = self._get_qb_lhv_indices(hq)
908
+
909
+ for q in qb:
910
+ b = hq[q]
911
+ p = self.sim[b[0]].prob(b[1]) if result else (1 - self.sim[b[0]].prob(b[1]))
912
+ if p < self._epsilon:
913
+ if self.sim[b[0]].m(b[1]) != result:
914
+ self.sim[b[0]].x(b[1])
915
+ else:
916
+ self.sim[b[0]].force_m(b[1], result)
917
+
840
918
  b = hq[1]
841
919
  b.reset()
842
- if c:
920
+ if result:
843
921
  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
922
 
849
923
  return c
850
924
 
@@ -1227,28 +1301,12 @@ class QrackAceBackend:
1227
1301
  return row * cols + col
1228
1302
 
1229
1303
  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
-
1304
+ connected_cols, _ = self._get_connected(col, False)
1248
1305
  for row in range(rows):
1306
+ connected_rows, _ = self._get_connected(row, False)
1249
1307
  a = logical_index(row, col)
1250
1308
  for c in connected_cols:
1251
- for r in range(0, rows):
1309
+ for r in connected_rows:
1252
1310
  b = logical_index(r, c)
1253
1311
  if a != b:
1254
1312
  coupling_map.add((a, b))
@@ -1274,9 +1332,7 @@ class QrackAceBackend:
1274
1332
  if is_long_a and is_long_b:
1275
1333
  continue # No noise on long-to-long
1276
1334
 
1277
- same_col = col_a == col_b
1278
-
1279
- if same_col:
1335
+ if (col_a == col_b) or (row_a == row_b):
1280
1336
  continue # No noise for same column
1281
1337
 
1282
1338
  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.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.56.0"
10
+ VERSION = "1.57.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