pyqrack-complex128 2.29.4__tar.gz → 2.30.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 (32) hide show
  1. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/Makefile +1 -1
  2. {pyqrack_complex128-2.29.4/pyqrack_complex128.egg-info → pyqrack_complex128-2.30.0}/PKG-INFO +1 -1
  3. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/__init__.py +1 -0
  4. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/qrack_ace_backend.py +51 -4
  5. pyqrack_complex128-2.30.0/pyqrack/qrack_bridge.py +266 -0
  6. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/qrack_simulator.py +3 -3
  7. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0/pyqrack_complex128.egg-info}/PKG-INFO +1 -1
  8. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack_complex128.egg-info/SOURCES.txt +1 -0
  9. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/setup.py +1 -1
  10. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/LICENSE +0 -0
  11. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/MANIFEST.in +0 -0
  12. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/README.md +0 -0
  13. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyproject.toml +0 -0
  14. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/neuron_activation_fn.py +0 -0
  15. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/pauli.py +0 -0
  16. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/qrack_ace_mps_backend.py +0 -0
  17. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/qrack_circuit.py +0 -0
  18. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/qrack_near_clifford_qec_backend.py +0 -0
  19. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/qrack_neuron.py +0 -0
  20. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/qrack_neuron_torch_layer.py +0 -0
  21. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/qrack_stabilizer.py +0 -0
  22. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/qrack_system/__init__.py +0 -0
  23. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/qrack_system/qrack_system.py +0 -0
  24. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/quimb_circuit_type.py +0 -0
  25. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/stats/__init__.py +0 -0
  26. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/stats/load_quantized_data.py +0 -0
  27. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack/stats/quantize_by_range.py +0 -0
  28. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack_complex128.egg-info/dependency_links.txt +0 -0
  29. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack_complex128.egg-info/not-zip-safe +0 -0
  30. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack_complex128.egg-info/requires.txt +0 -0
  31. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/pyqrack_complex128.egg-info/top_level.txt +0 -0
  32. {pyqrack_complex128-2.29.4 → pyqrack_complex128-2.30.0}/setup.cfg +0 -0
@@ -30,7 +30,7 @@ build-deps:
30
30
  rm -rf pyqrack/qrack_system/qrack_cl_precompile
31
31
  ifneq ($(OS),Windows_NT)
32
32
  ifeq ($(QRACK_PRESENT),)
33
- git clone https://github.com/unitaryfund/qrack.git; cd qrack; git checkout 1ae704684a1ff692e6479fc85565f88dee582753; cd ..
33
+ git clone https://github.com/unitaryfund/qrack.git; cd qrack; git checkout 98b474070d3b4ee373d8645ee413f26f4b81c2b4; cd ..
34
34
  endif
35
35
  mkdir -p qrack/build
36
36
  ifeq ($(UNAME_S),Linux)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-complex128
3
- Version: 2.29.4
3
+ Version: 2.30.0
4
4
  Summary: pyqrack - Pure Python vm6502q/qrack Wrapper
5
5
  Home-page: https://github.com/vm6502q/pyqrack
6
6
  Author: Daniel Strano
@@ -9,6 +9,7 @@ from .quimb_circuit_type import QuimbCircuitType
9
9
  from .qrack_ace_backend import QrackAceBackend
10
10
  from .qrack_ace_mps_backend import QrackAceMPSBackend, QrackMPSPatch
11
11
  from .qrack_near_clifford_qec_backend import QrackNearCliffordQecBackend
12
+ from .qrack_bridge import QrackBridge
12
13
  from .qrack_circuit import QrackCircuit
13
14
  from .qrack_neuron import QrackNeuron
14
15
  from .qrack_neuron_torch_layer import (
@@ -429,6 +429,7 @@ class QrackAceBackend:
429
429
  # exactly mirroring how every other per-simulator allocation below
430
430
  # already guards for "nothing to allocate."
431
431
  has_boundary = self.use_crossbars and (boundary_count > 0)
432
+ self._crossbar_sim = boundary_sim_id if has_boundary else None
432
433
  if has_boundary:
433
434
  sim_counts.append(boundary_count)
434
435
  sim_count += 1
@@ -1603,6 +1604,54 @@ class QrackAceBackend:
1603
1604
 
1604
1605
  return connected, boundary
1605
1606
 
1607
+ def _update_lineage(self, lq1, hq1, lq2, hq2):
1608
+ # Lineage-tracking authority: a replica stays "clean" only while every
1609
+ # coupling it has taken part in was a REAL gate with a clean replica of
1610
+ # the partner. The authority for each participant becomes the clean
1611
+ # replicas that just met a clean partner replica in a shared simulator.
1612
+ # A replica with an unbroken real-gate history holds the qubit's full
1613
+ # coherent state, including entanglement accumulated across several
1614
+ # couplings -- e.g. the shared crossbar simulator, which holds a replica
1615
+ # of every boundary qubit, keeps a seam-to-seam subcircuit fully
1616
+ # coherent across every patch boundary. Only if no clean pair shares a
1617
+ # simulator does it fall back to the plain fresh-copy rule.
1618
+ if (not self.is_boundary_repetition_code) or self._in_gadget_capture or (lq1 is None) or (lq2 is None):
1619
+ return
1620
+ if (len(hq1) < 2) and (len(hq2) < 2):
1621
+ return
1622
+
1623
+ def clean(lq, hq):
1624
+ a = self._authority.get(lq) if len(hq) > 1 else None
1625
+ return set(range(len(hq))) if a is None else set(a)
1626
+
1627
+ c1, c2 = clean(lq1, hq1), clean(lq2, hq2)
1628
+ new1 = {i for i in c1 if any(hq1[i][0] == hq2[j][0] for j in c2)}
1629
+ new2 = {j for j in c2 if any(hq1[i][0] == hq2[j][0] for i in c1)}
1630
+ if not new1:
1631
+ sims2 = {b[0] for b in hq2}
1632
+ new1 = {i for i, b in enumerate(hq1) if b[0] in sims2}
1633
+ sims1 = {b[0] for b in hq1}
1634
+ new2 = {j for j, b in enumerate(hq2) if b[0] in sims1}
1635
+ # When both participants are boundary qubits, their crossbar replicas
1636
+ # (one shared simulator for every boundary qubit in the grid) are the
1637
+ # only copies guaranteed to see the WHOLE seam-to-seam history; copies
1638
+ # in a patch simulator can be left holding a pairwise correlation that
1639
+ # a later coupling elsewhere has made stale. So a clean crossbar pair
1640
+ # becomes the sole authority.
1641
+ cb = self._crossbar_sim
1642
+ if (cb is not None) and (len(hq1) > 1) and (len(hq2) > 1):
1643
+ i_cb = [i for i in new1 if hq1[i][0] == cb]
1644
+ j_cb = [j for j in new2 if hq2[j][0] == cb]
1645
+ if i_cb and j_cb:
1646
+ new1, new2 = set(i_cb), set(j_cb)
1647
+ for lq, hq, new in ((lq1, hq1, new1), (lq2, hq2, new2)):
1648
+ if len(hq) < 2 or not new:
1649
+ continue
1650
+ if len(new) == len(hq):
1651
+ self._authority.pop(lq, None)
1652
+ else:
1653
+ self._authority[lq] = new
1654
+
1606
1655
  def _set_authority(self, lq, hq, partner_hq):
1607
1656
  # Record which replica slot(s) of boundary qubit lq just received a
1608
1657
  # REAL coupling gate (they share a simulator with the partner). See
@@ -1658,8 +1707,7 @@ class QrackAceBackend:
1658
1707
  gate_fn, shadow_fn = self._get_gate(pauli, anti, sim_id)
1659
1708
  _, idx2 = hq2[i]
1660
1709
  gate_fn([idx1], idx2)
1661
- self._set_authority(lq1, hq1, hq2)
1662
- self._set_authority(lq2, hq2, hq1)
1710
+ self._update_lineage(lq1, hq1, lq2, hq2)
1663
1711
  return
1664
1712
 
1665
1713
  # Logical-level error-detection gadget: an earlier version of this
@@ -1761,8 +1809,7 @@ class QrackAceBackend:
1761
1809
  shadow_fn(b1, b2)
1762
1810
  shadow_targets.append(b2)
1763
1811
 
1764
- self._set_authority(lq1, hq1, hq2)
1765
- self._set_authority(lq2, hq2, hq1)
1812
+ self._update_lineage(lq1, hq1, lq2, hq2)
1766
1813
 
1767
1814
  if not self._in_gadget_capture:
1768
1815
  if pauli != Pauli.PauliZ:
@@ -0,0 +1,266 @@
1
+ # Bridged patching on QrackSimulator patches, recompressed through out_ket()/in_ket().
2
+ #
3
+ # By (Anthropic) Claude and Dan Strano
4
+ #
5
+ # Two-patch split: patch A = qubits [0, n // 2), patch B = the rest (first patch rounded down).
6
+ #
7
+ # Representation. Each patch is ONE QrackSimulator holding its data qubits (low indices) plus exactly
8
+ # b bond qubits (high indices), allocated and released as the bridge grows and shrinks. Bond j of
9
+ # patch A is paired with bond j of patch B. The represented state is both patch states contracted on
10
+ # the live bond pairs with the unnormalized virtual Bell projection sum_k <k|<k|:
11
+ #
12
+ # |Psi> ~ sum_k <k|psi_A> (x) <k|psi_B> (k over the 2^b bond basis states)
13
+ #
14
+ # i.e. with each patch's ket read as a (2^n_patch x 2^b) matrix M_A, M_B: Psi = M_A M_B^T.
15
+ # Rank chi = 2^b. Every in-patch gate is applied natively, once, to all chi branches at once.
16
+ #
17
+ # Cross-cut gates by gate teleportation through a fresh bond pair (exact, gates only):
18
+ # controlled-U from c (one patch) to t (the other):
19
+ # control patch: CNOT c -> bond_b (copy the control into the bond)
20
+ # target patch: H bond_b ; controlled-U bond_b -> t
21
+ # Contracting on (bond_b, bond_b) enforces equality, which is exactly controlled-U(c -> t).
22
+ # SWAP is 3 CNOTs; iSWAP is (S x S), CZ, SWAP; iSWAP^dagger is SWAP, CZ, (S^dag x S^dag).
23
+ #
24
+ # Recompression after each bridged two-qubit gate: out_ket() both patches, thin QR of each
25
+ # (2^n_patch x chi) matrix, SVD of the chi x chi core, keep the largest singular values (exact when
26
+ # only zeros are dropped; above max_bonds, greedy or Born-rule group selection), and in_ket() back.
27
+ # The singular values are absorbed into patch A, so patch B's bond columns stay orthonormal (B is
28
+ # isometric), which makes patch A's own marginal over its data qubits the exact marginal.
29
+ # Nothing larger than the kets themselves and a chi x chi core is ever formed: no density matrix,
30
+ # no (2^n x 2^n) object of any kind.
31
+ #
32
+ # Readout (exact for the stored state): sample patch A's data natively with its own measure_shots
33
+ # (exact because B is isometric); for each distinct outcome a, patch B's conditional state is
34
+ # M_B @ M_A[a, :], sampled in numpy from the exported B ket.
35
+ #
36
+ # fidelity_estimate multiplies the kept Schmidt weight (greedy) or sum_g p_g^2 (born) per cut.
37
+
38
+ import ctypes
39
+ import math
40
+
41
+ import numpy as np
42
+ from .qrack_simulator import QrackSimulator
43
+ from .qrack_system import Qrack
44
+
45
+ # Ket I/O straight into/out of numpy buffers (PyQrack's out_ket()/in_ket() round-trip through Python
46
+ # lists of complex, which dominated runtime). Same library calls, OutKet/InKet, without the copies.
47
+ _REAL = np.float32 if Qrack.fppow < 6 else np.float64
48
+ _CREAL = ctypes.c_float if Qrack.fppow < 6 else ctypes.c_double
49
+
50
+
51
+ def _out_ket(sim):
52
+ buf = np.empty(2 << sim.num_qubits(), dtype=_REAL)
53
+ Qrack.qrack_lib.OutKet(sim.sid, buf.ctypes.data_as(ctypes.POINTER(_CREAL)))
54
+ sim._throw_if_error()
55
+ return buf.view(np.complex64 if _REAL is np.float32 else np.complex128).astype(complex)
56
+
57
+
58
+ def _in_ket(sim, v):
59
+ buf = np.ascontiguousarray(np.asarray(v, dtype=complex).view(np.float64).astype(_REAL))
60
+ Qrack.qrack_lib.InKet(sim.sid, buf.ctypes.data_as(ctypes.POINTER(_CREAL)))
61
+ sim._throw_if_error()
62
+
63
+ _X = [0, 1, 1, 0]
64
+ _Y = [0, -1j, 1j, 0]
65
+ _Z = [1, 0, 0, -1]
66
+ _TOL = 1e-6 # relative singular-value floor treated as exactly zero (Qrack is single precision)
67
+
68
+
69
+ class QrackBridge:
70
+ def __init__(self, qubit_count, max_bonds=6, seed=None, truncation="greedy", _clone=None):
71
+ if _clone is not None:
72
+ src = _clone
73
+ self.n, self.qa, self.qb, self.side = src.n, src.qa, src.qb, src.side
74
+ self.max_bonds, self.truncation = src.max_bonds, src.truncation
75
+ self.sim = [s.clone() for s in src.sim]
76
+ self.b = src.b
77
+ self.fidelity_estimate, self.truncations, self.peak_bonds = src.fidelity_estimate, src.truncations, src.peak_bonds
78
+ self._defer = False
79
+ self.rng = np.random.default_rng(int(src.rng.integers(1 << 62)))
80
+ return
81
+ if truncation not in ("greedy", "born"):
82
+ raise ValueError("truncation must be 'greedy' or 'born'")
83
+ self.n = qubit_count
84
+ self.qa = list(range(qubit_count // 2))
85
+ self.qb = list(range(qubit_count // 2, qubit_count))
86
+ self.side = {q: (0, i) for i, q in enumerate(self.qa)}
87
+ self.side.update({q: (1, i) for i, q in enumerate(self.qb)})
88
+ self.max_bonds = max_bonds
89
+ self.truncation = truncation
90
+ self.sim = [QrackSimulator(len(self.qa)), QrackSimulator(len(self.qb))]
91
+ self.b = 0
92
+ self.fidelity_estimate = 1.0
93
+ self.truncations = 0
94
+ self.peak_bonds = 0
95
+ self._defer = False
96
+ self.rng = np.random.default_rng(seed)
97
+
98
+ def clone(self):
99
+ return QrackBridge(None, _clone=self)
100
+
101
+ def _nd(self, p):
102
+ return len(self.qa) if p == 0 else len(self.qb)
103
+
104
+ def _loc(self, q):
105
+ p, i = self.side[q]
106
+ return self.sim[p], p, i
107
+
108
+ # --- local gates ---------------------------------------------------------
109
+
110
+ def u(self, q, th, ph, lm):
111
+ s, _, i = self._loc(q)
112
+ s.u(i, th, ph, lm)
113
+
114
+ def x(self, q):
115
+ s, _, i = self._loc(q)
116
+ s.x(i)
117
+
118
+ def h(self, q):
119
+ s, _, i = self._loc(q)
120
+ s.h(i)
121
+
122
+ def s(self, q):
123
+ s, _, i = self._loc(q)
124
+ s.s(i)
125
+
126
+ def adjs(self, q):
127
+ s, _, i = self._loc(q)
128
+ s.adjs(i)
129
+
130
+ # --- controlled gates, local or bridged ----------------------------------
131
+
132
+ def _controlled(self, c, t, mtrx, anti):
133
+ sc, pc, ic = self._loc(c)
134
+ st, pt, it = self._loc(t)
135
+ if pc == pt:
136
+ (sc.macmtrx if anti else sc.mcmtrx)([ic], mtrx, it)
137
+ return
138
+ kc = self._nd(pc) + self.b
139
+ kt = self._nd(pt) + self.b
140
+ sc.allocate_qubit(kc)
141
+ st.allocate_qubit(kt)
142
+ (sc.macx if anti else sc.mcx)([ic], kc)
143
+ st.h(kt)
144
+ st.mcmtrx([kt], mtrx, it)
145
+ self.b += 1
146
+ self.peak_bonds = max(self.peak_bonds, self.b)
147
+ # Inside a SWAP-type gate, recompress exactly between its CNOTs; truncate once at its end.
148
+ self._compress(exact_only=self._defer)
149
+
150
+ def mcx(self, c, t): self._controlled(c[0], t, _X, False)
151
+ def mcy(self, c, t): self._controlled(c[0], t, _Y, False)
152
+ def mcz(self, c, t): self._controlled(c[0], t, _Z, False)
153
+ def macx(self, c, t): self._controlled(c[0], t, _X, True)
154
+ def macy(self, c, t): self._controlled(c[0], t, _Y, True)
155
+ def macz(self, c, t): self._controlled(c[0], t, _Z, True)
156
+
157
+ def cx(self, a, b): self.mcx([a], b)
158
+ def cy(self, a, b): self.mcy([a], b)
159
+ def cz(self, a, b): self.mcz([a], b)
160
+
161
+ def _composite(self, steps):
162
+ outer = self._defer
163
+ self._defer = True
164
+ for f in steps:
165
+ f()
166
+ self._defer = outer
167
+ if not outer:
168
+ self._compress()
169
+
170
+ def swap(self, a, b):
171
+ if self.side[a][0] == self.side[b][0]:
172
+ s, _, i = self._loc(a)
173
+ s.swap(i, self.side[b][1])
174
+ return
175
+ self._composite([lambda: self.mcx([a], b), lambda: self.mcx([b], a), lambda: self.mcx([a], b)])
176
+
177
+ def iswap(self, a, b):
178
+ if self.side[a][0] == self.side[b][0]:
179
+ s, _, i = self._loc(a)
180
+ s.iswap(i, self.side[b][1])
181
+ return
182
+ self._composite([lambda: self.s(a), lambda: self.s(b), lambda: self.mcz([a], b), lambda: self.swap(a, b)])
183
+
184
+ def adjiswap(self, a, b):
185
+ if self.side[a][0] == self.side[b][0]:
186
+ s, _, i = self._loc(a)
187
+ s.adjiswap(i, self.side[b][1])
188
+ return
189
+ self._composite([lambda: self.swap(a, b), lambda: self.mcz([a], b), lambda: self.adjs(a), lambda: self.adjs(b)])
190
+
191
+ # --- recompression through the kets --------------------------------------
192
+
193
+ def _matrices(self):
194
+ """Each patch ket as a (2^n_patch x 2^b) matrix over the live bonds (dead slots are |0>)."""
195
+ out = []
196
+ for p in (0, 1):
197
+ nd = self._nd(p)
198
+ v = _out_ket(self.sim[p])
199
+ out.append(v.reshape(1 << self.b, 1 << nd).T) # (data, bond): bond = high qubits
200
+ return out
201
+
202
+ def _store(self, p, mat, b_new):
203
+ nd = self._nd(p)
204
+ full = np.zeros((1 << b_new, 1 << nd), dtype=complex)
205
+ full[: mat.shape[1], :] = mat.T
206
+ v = full.reshape(-1)
207
+ v = v / np.linalg.norm(v)
208
+ self.sim[p] = QrackSimulator(nd + b_new)
209
+ _in_ket(self.sim[p], v)
210
+
211
+ def _compress(self, exact_only=False):
212
+ if self.b == 0:
213
+ return
214
+ ma, mb = self._matrices()
215
+ qa_, ra = np.linalg.qr(ma)
216
+ qb_, rb = np.linalg.qr(mb)
217
+ u, sv, vh = np.linalg.svd(ra @ rb.T)
218
+ if sv[0] <= 0:
219
+ return
220
+ live = int(np.sum(sv > _TOL * sv[0]))
221
+ b_exact = int(math.ceil(math.log2(max(live, 1))))
222
+ b_new = b_exact if exact_only else min(b_exact, self.max_bonds)
223
+ keep = min(live, 1 << b_new)
224
+ w = sv ** 2
225
+ if keep < live:
226
+ self.truncations += 1
227
+ if self.truncation == "greedy":
228
+ idx = np.arange(keep)
229
+ self.fidelity_estimate *= float(np.sum(w[:keep]) / np.sum(w[:live]))
230
+ else:
231
+ groups = [np.arange(i, min(i + keep, live)) for i in range(0, live, keep)]
232
+ pg = np.array([np.sum(w[g]) for g in groups]) / np.sum(w[:live])
233
+ idx = groups[int(self.rng.choice(len(groups), p=pg))]
234
+ self.fidelity_estimate *= float(np.sum(pg ** 2))
235
+ else:
236
+ idx = np.arange(keep)
237
+ new_a = (qa_ @ u[:, idx]) * sv[idx][None, :] # singular values absorbed into patch A
238
+ new_b = qb_ @ vh[idx, :].T # orthonormal columns: patch B isometric
239
+ self._store(0, new_a, b_new)
240
+ self._store(1, new_b, b_new)
241
+ self.b = b_new
242
+
243
+ # --- readout -----------------------------------------------------------------
244
+
245
+ def measure_shots(self, qubits, shots):
246
+ self._compress(exact_only=True) # canonical: patch B isometric
247
+ na, nb = len(self.qa), len(self.qb)
248
+ a_draws = self.sim[0].measure_shots(list(range(na)), shots) # exact marginal of patch A
249
+ ma, mb = self._matrices()
250
+ out = []
251
+ for a, cnt in zip(*np.unique(np.asarray(a_draws, dtype=np.int64), return_counts=True)):
252
+ amp = mb @ ma[a, :]
253
+ pb = np.abs(amp) ** 2
254
+ pb /= pb.sum()
255
+ for bidx in self.rng.choice(len(pb), size=int(cnt), p=pb):
256
+ full = int(a) | (int(bidx) << na)
257
+ out.append(sum(((full >> q) & 1) << j for j, q in enumerate(qubits)))
258
+ self.rng.shuffle(out)
259
+ return out
260
+
261
+ def out_ket(self):
262
+ """Dense contracted state (2^n), little-endian over logical qubits; for testing."""
263
+ ma, mb = self._matrices()
264
+ psi = (ma @ mb.T) # [a, b]
265
+ v = psi.T.reshape(-1) # index = a + 2^na * b
266
+ return v / np.linalg.norm(v)
@@ -452,7 +452,7 @@ class QrackSimulator:
452
452
  Qrack.qrack_lib.Exp(
453
453
  self.sid,
454
454
  len(b),
455
- QrackSimulator._ulonglong_byref(b),
455
+ QrackSimulator._int_byref(b),
456
456
  ctypes.c_double(ph),
457
457
  QrackSimulator._ulonglong_byref(q),
458
458
  )
@@ -971,7 +971,7 @@ class QrackSimulator:
971
971
  Qrack.qrack_lib.MCExp(
972
972
  self.sid,
973
973
  len(b),
974
- QrackSimulator._ulonglong_byref(b),
974
+ QrackSimulator._int_byref(b),
975
975
  ctypes.c_double(ph),
976
976
  len(cs),
977
977
  QrackSimulator._ulonglong_byref(cs),
@@ -3117,7 +3117,7 @@ class QrackSimulator:
3117
3117
  if len(b) != len(q):
3118
3118
  raise RuntimeError("Lengths of list parameters are mismatched.")
3119
3119
  result = Qrack.qrack_lib.JointEnsembleProbability(
3120
- self.sid, len(b), QrackSimulator._ulonglong_byref(b), q
3120
+ self.sid, len(b), QrackSimulator._int_byref(b), q
3121
3121
  )
3122
3122
  self._throw_if_error()
3123
3123
  return result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-complex128
3
- Version: 2.29.4
3
+ Version: 2.30.0
4
4
  Summary: pyqrack - Pure Python vm6502q/qrack Wrapper
5
5
  Home-page: https://github.com/vm6502q/pyqrack
6
6
  Author: Daniel Strano
@@ -9,6 +9,7 @@ pyqrack/neuron_activation_fn.py
9
9
  pyqrack/pauli.py
10
10
  pyqrack/qrack_ace_backend.py
11
11
  pyqrack/qrack_ace_mps_backend.py
12
+ pyqrack/qrack_bridge.py
12
13
  pyqrack/qrack_circuit.py
13
14
  pyqrack/qrack_near_clifford_qec_backend.py
14
15
  pyqrack/qrack_neuron.py
@@ -7,7 +7,7 @@ from setuptools import setup
7
7
  from setuptools.command.build_py import build_py
8
8
 
9
9
 
10
- VERSION = "2.29.4"
10
+ VERSION = "2.30.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')