pyqrack-complex128 1.78.3__py3-none-macosx_14_0_arm64.whl → 1.80.2__py3-none-macosx_14_0_arm64.whl

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.
pyqrack/__init__.py CHANGED
@@ -13,7 +13,6 @@ from .qrack_neuron_torch_layer import (
13
13
  QrackNeuronTorch,
14
14
  QrackNeuronTorchFunction,
15
15
  QrackNeuronTorchLayer,
16
- QrackNeuronTorchLayerFunction,
17
16
  )
18
17
  from .qrack_simulator import QrackSimulator
19
18
  from .qrack_stabilizer import QrackStabilizer
@@ -224,9 +224,7 @@ class QrackAceBackend:
224
224
  isPaged=True,
225
225
  isCpuGpuHybrid=True,
226
226
  isOpenCL=True,
227
- isHostPointer=(
228
- True if os.environ.get("PYQRACK_HOST_POINTER_DEFAULT_ON") else False
229
- ),
227
+ isHostPointer=(True if os.environ.get("PYQRACK_HOST_POINTER_DEFAULT_ON") else False),
230
228
  noise=0,
231
229
  toClone=None,
232
230
  ):
@@ -294,11 +292,7 @@ class QrackAceBackend:
294
292
  sim_counts[t_sim_id] += 1
295
293
 
296
294
  qubit.append(
297
- LHVQubit(
298
- toClone=(
299
- toClone._qubits[tot_qubits][2] if toClone else None
300
- )
301
- )
295
+ LHVQubit(toClone=(toClone._qubits[tot_qubits][2] if toClone else None))
302
296
  )
303
297
 
304
298
  if (not c) and (not r):
@@ -545,9 +539,7 @@ class QrackAceBackend:
545
539
  self.sim[hq[4][0]].prob(hq[4][1]),
546
540
  ]
547
541
  # Balancing suggestion from Elara (the custom OpenAI GPT)
548
- prms = math.sqrt(
549
- (p[0] ** 2 + p[1] ** 2 + 3 * (p[2] ** 2) + p[3] ** 2 + p[4] ** 2) / 7
550
- )
542
+ prms = math.sqrt((p[0] ** 2 + p[1] ** 2 + 3 * (p[2] ** 2) + p[3] ** 2 + p[4] ** 2) / 7)
551
543
  qrms = math.sqrt(
552
544
  (
553
545
  (1 - p[0]) ** 2
@@ -860,9 +852,7 @@ class QrackAceBackend:
860
852
  gate = self.sim[sim_id].macz if anti else self.sim[sim_id].mcz
861
853
  shadow = self._anti_cz_shadow if anti else self._cz_shadow
862
854
  else:
863
- raise RuntimeError(
864
- "QrackAceBackend._get_gate() should never return identity!"
865
- )
855
+ raise RuntimeError("QrackAceBackend._get_gate() should never return identity!")
866
856
 
867
857
  return gate, shadow
868
858
 
@@ -899,11 +889,7 @@ class QrackAceBackend:
899
889
  b2 = hq2[q2]
900
890
  if b1[0] == b2[0]:
901
891
  gate_fn([b1[1]], b2[1])
902
- elif (
903
- lq1_lr
904
- or (b1[1] == b2[1])
905
- or ((len(qb1) == 2) and (b1[1] == (b2[1] & 1)))
906
- ):
892
+ elif lq1_lr or (b1[1] == b2[1]) or ((len(qb1) == 2) and (b1[1] == (b2[1] & 1))):
907
893
  shadow_fn(b1, b2)
908
894
 
909
895
  def _cpauli(self, lq1, lq2, anti, pauli):
@@ -1033,9 +1019,7 @@ class QrackAceBackend:
1033
1019
  self.sim[hq[4][0]].prob(hq[4][1]),
1034
1020
  ]
1035
1021
  # Balancing suggestion from Elara (the custom OpenAI GPT)
1036
- prms = math.sqrt(
1037
- (p[0] ** 2 + p[1] ** 2 + 3 * (p[2] ** 2) + p[3] ** 2 + p[4] ** 2) / 7
1038
- )
1022
+ prms = math.sqrt((p[0] ** 2 + p[1] ** 2 + 3 * (p[2] ** 2) + p[3] ** 2 + p[4] ** 2) / 7)
1039
1023
  qrms = math.sqrt(
1040
1024
  (
1041
1025
  (1 - p[0]) ** 2
@@ -1187,17 +1171,11 @@ class QrackAceBackend:
1187
1171
  (-1 * float(operation.params[1])) + math.pi / 2,
1188
1172
  )
1189
1173
  elif name == "rx":
1190
- self._sim.r(
1191
- Pauli.PauliX, float(operation.params[0]), operation.qubits[0]._index
1192
- )
1174
+ self._sim.r(Pauli.PauliX, float(operation.params[0]), operation.qubits[0]._index)
1193
1175
  elif name == "ry":
1194
- self._sim.r(
1195
- Pauli.PauliY, float(operation.params[0]), operation.qubits[0]._index
1196
- )
1176
+ self._sim.r(Pauli.PauliY, float(operation.params[0]), operation.qubits[0]._index)
1197
1177
  elif name == "rz":
1198
- self._sim.r(
1199
- Pauli.PauliZ, float(operation.params[0]), operation.qubits[0]._index
1200
- )
1178
+ self._sim.r(Pauli.PauliZ, float(operation.params[0]), operation.qubits[0]._index)
1201
1179
  elif name == "h":
1202
1180
  self._sim.h(operation.qubits[0]._index)
1203
1181
  elif name == "x":
@@ -1262,9 +1240,9 @@ class QrackAceBackend:
1262
1240
  cregbit = clbit
1263
1241
 
1264
1242
  regbit = 1 << cregbit
1265
- self._classical_register = (
1266
- self._classical_register & (~regbit)
1267
- ) | (qubit_outcome << cregbit)
1243
+ self._classical_register = (self._classical_register & (~regbit)) | (
1244
+ qubit_outcome << cregbit
1245
+ )
1268
1246
 
1269
1247
  elif name == "bfunc":
1270
1248
  mask = int(operation.mask, 16)
@@ -1379,9 +1357,7 @@ class QrackAceBackend:
1379
1357
  if operation.name == "id" or operation.name == "barrier":
1380
1358
  continue
1381
1359
 
1382
- if is_initializing and (
1383
- (operation.name == "measure") or (operation.name == "reset")
1384
- ):
1360
+ if is_initializing and ((operation.name == "measure") or (operation.name == "reset")):
1385
1361
  continue
1386
1362
 
1387
1363
  is_initializing = False
@@ -1439,9 +1415,7 @@ class QrackAceBackend:
1439
1415
  self._sample_cregbits = []
1440
1416
 
1441
1417
  if self._sample_measure and (len(self._sample_qubits) > 0):
1442
- _data = self._add_sample_measure(
1443
- self._sample_qubits, self._sample_clbits, self._shots
1444
- )
1418
+ _data = self._add_sample_measure(self._sample_qubits, self._sample_clbits, self._shots)
1445
1419
 
1446
1420
  del self._sim
1447
1421
 
@@ -1532,17 +1506,13 @@ class QrackAceBackend:
1532
1506
  noise_model.add_quantum_error(depolarizing_error(y, 2), "cx", [a, b])
1533
1507
  noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cy", [a, b])
1534
1508
  noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cz", [a, b])
1535
- noise_model.add_quantum_error(
1536
- depolarizing_error(y_swap, 2), "swap", [a, b]
1537
- )
1509
+ noise_model.add_quantum_error(depolarizing_error(y_swap, 2), "swap", [a, b])
1538
1510
  else:
1539
1511
  y_cy = 1 - (1 - y) ** 2
1540
1512
  y_swap = 1 - (1 - y) ** 3
1541
1513
  noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cx", [a, b])
1542
1514
  noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cy", [a, b])
1543
1515
  noise_model.add_quantum_error(depolarizing_error(y_cy, 2), "cz", [a, b])
1544
- noise_model.add_quantum_error(
1545
- depolarizing_error(y_swap, 2), "swap", [a, b]
1546
- )
1516
+ noise_model.add_quantum_error(depolarizing_error(y_swap, 2), "swap", [a, b])
1547
1517
 
1548
1518
  return noise_model
pyqrack/qrack_circuit.py CHANGED
@@ -507,9 +507,7 @@ class QrackCircuit:
507
507
  )
508
508
  if circuit_type == QuimbCircuitType.Circuit
509
509
  else (
510
- qtn.CircuitDense(
511
- N=qcirc.num_qubits, psi0=psi0, gate_opts=gate_opts, tags=tags
512
- )
510
+ qtn.CircuitDense(N=qcirc.num_qubits, psi0=psi0, gate_opts=gate_opts, tags=tags)
513
511
  if circuit_type == QuimbCircuitType.CircuitDense
514
512
  else qtn.CircuitMPS(
515
513
  N=qcirc.num_qubits,
@@ -540,9 +538,7 @@ class QrackCircuit:
540
538
  return tcirc
541
539
 
542
540
  @staticmethod
543
- def file_to_tensorcircuit(
544
- filename, inputs=None, circuit_params=None, binding_params=None
545
- ):
541
+ def file_to_tensorcircuit(filename, inputs=None, circuit_params=None, binding_params=None):
546
542
  """Convert an output file to a TensorCircuit circuit
547
543
 
548
544
  Reads in an (optimized) circuit from a file named
pyqrack/qrack_neuron.py CHANGED
@@ -110,6 +110,21 @@ class QrackNeuron:
110
110
  return (ctypes.c_float * len(a))(*a)
111
111
  return (ctypes.c_double * len(a))(*a)
112
112
 
113
+ def set_simulator(self, s):
114
+ """Set the neuron simulator
115
+
116
+ Set the simulator used by this neuron
117
+
118
+ Args:
119
+ s(QrackSimulator): The simulator to use
120
+
121
+ Raises:
122
+ RuntimeError: QrackSimulator raised an exception.
123
+ """
124
+ Qrack.qrack_lib.set_qneuron_sim(self.nid, s.sid)
125
+ self._throw_if_error()
126
+ self.simulator = s
127
+
113
128
  def set_angles(self, a):
114
129
  """Directly sets the neuron parameters.
115
130
 
@@ -265,7 +280,7 @@ class QrackNeuron:
265
280
 
266
281
  @staticmethod
267
282
  def quantile_bounds(vec, bits):
268
- """ Calculate vector quantile bounds
283
+ """Calculate vector quantile bounds
269
284
 
270
285
  This is a static helper method to calculate the quantile
271
286
  bounds of 2 ** bits worth of quantiles.
@@ -283,11 +298,15 @@ class QrackNeuron:
283
298
  n = len(vec)
284
299
  vec_sorted = sorted(vec)
285
300
 
286
- return [vec_sorted[0]] + [vec_sorted[(k * n) // bins] for k in range(1, bins)] + [vec_sorted[-1]]
301
+ return (
302
+ [vec_sorted[0]]
303
+ + [vec_sorted[(k * n) // bins] for k in range(1, bins)]
304
+ + [vec_sorted[-1]]
305
+ )
287
306
 
288
307
  @staticmethod
289
308
  def discretize(vec, bounds):
290
- """ Discretize vector by quantile bounds
309
+ """Discretize vector by quantile bounds
291
310
 
292
311
  This is a static helper method to discretize a numerical
293
312
  vector according to quantile bounds calculated by the
@@ -317,7 +336,7 @@ class QrackNeuron:
317
336
 
318
337
  @staticmethod
319
338
  def flatten_and_transpose(arr):
320
- """ Flatten and transpose feature matrix
339
+ """Flatten and transpose feature matrix
321
340
 
322
341
  This is a static helper method to convert a multi-feature
323
342
  bit-row matrix to an observation-row matrix with flat
@@ -333,7 +352,7 @@ class QrackNeuron:
333
352
 
334
353
  @staticmethod
335
354
  def bin_endpoints_average(bounds):
336
- """ Bin endpoints average
355
+ """Bin endpoints average
337
356
 
338
357
  This is a static helper method that accepts the output
339
358
  bins from quantile_bounds() and returns the average points
@@ -1,4 +1,4 @@
1
- # (C) Daniel Strano and the Qrack contributors 2017-2025. All rights reserved.
1
+ # (C) Daniel Strano and the Qrack contributors 2017-2026. All rights reserved.
2
2
  #
3
3
  # Initial draft by Elara (OpenAI custom GPT)
4
4
  # Refined and architecturally clarified by Dan Strano
@@ -8,6 +8,7 @@
8
8
 
9
9
  import itertools
10
10
  import math
11
+ import random
11
12
  import sys
12
13
 
13
14
  _IS_TORCH_AVAILABLE = True
@@ -24,99 +25,84 @@ from .qrack_simulator import QrackSimulator
24
25
  from .neuron_activation_fn import NeuronActivationFn
25
26
 
26
27
 
27
- # Should be safe for 16-bit
28
- angle_eps = math.pi * (2 ** -8)
28
+ # Parameter-shift rule
29
+ param_shift_eps = math.pi / 2
30
+ # Neuron angle initialization
31
+ init_phi = math.asin(0.5)
29
32
 
30
33
 
31
- if not _IS_TORCH_AVAILABLE:
32
- class TorchContextMock(object):
33
- def __init__(self):
34
- pass
35
-
36
- def save_for_backward(self, *args):
37
- self.saved_tensors = args
38
-
39
34
  class QrackNeuronTorchFunction(Function if _IS_TORCH_AVAILABLE else object):
40
35
  """Static forward/backward/apply functions for QrackNeuronTorch"""
41
36
 
42
- if not _IS_TORCH_AVAILABLE:
43
- @staticmethod
44
- def apply(x, neuron_wrapper):
45
- return forward(TorchContextMock(), x, neuron_wrapper)
46
-
47
37
  @staticmethod
48
- def forward(ctx, x, neuron_wrapper):
49
- ctx.neuron_wrapper = neuron_wrapper
38
+ def forward(ctx, x, neuron):
39
+ ctx.neuron = neuron
40
+ ctx.simulator = neuron.simulator
50
41
  ctx.save_for_backward(x)
51
- neuron = neuron_wrapper.neuron
52
42
 
53
- angles = (x.detach().cpu().numpy() if x.requires_grad else x.numpy()) if _IS_TORCH_AVAILABLE else x
43
+ # Baseline probability BEFORE applying this neuron's unitary
44
+ pre_prob = neuron.simulator.prob(neuron.target)
45
+
46
+ angles = x.detach().cpu().numpy() if x.requires_grad else x.numpy()
54
47
  neuron.set_angles(angles)
55
48
  neuron.predict(True, False)
49
+
50
+ # Probability AFTER applying this neuron's unitary
56
51
  post_prob = neuron.simulator.prob(neuron.target)
57
- if _IS_TORCH_AVAILABLE:
58
- post_prob = torch.tensor([post_prob], dtype=torch.float32, device=x.device)
52
+ ctx.post_prob = post_prob
53
+
54
+ delta = math.asin(post_prob) - math.asin(pre_prob)
55
+ ctx.delta = delta
59
56
 
60
- return post_prob
57
+ # Return shape: (1,)
58
+ return x.new_tensor([delta])
61
59
 
62
60
  @staticmethod
63
- def _backward(x, neuron_wrapper):
64
- neuron = neuron_wrapper.neuron
65
- angles = (x.detach().cpu().numpy() if x.requires_grad else x.numpy()) if _IS_TORCH_AVAILABLE else x
61
+ def backward(ctx, grad_output):
62
+ (x,) = ctx.saved_tensors
63
+ neuron = ctx.neuron
64
+ neuron.set_simulator(ctx.simulator)
65
+ post_prob = ctx.post_prob
66
+
67
+ angles = x.detach().cpu().numpy() if x.requires_grad else x.numpy()
66
68
 
67
- # Uncompute
69
+ # Restore simulator to state BEFORE this neuron's unitary
68
70
  neuron.set_angles(angles)
69
71
  neuron.unpredict()
70
72
  pre_sim = neuron.simulator
71
- pre_prob = pre_sim.prob(neuron.target)
72
73
 
73
- param_count = 1 << len(neuron.controls)
74
- delta = [0.0] * param_count
75
- for param in range(param_count):
76
- angle = angles[param]
74
+ grad_x = torch.zeros_like(x)
77
75
 
78
- # x + angle_eps
79
- angles[param] = angle + angle_eps
76
+ for i in range(x.shape[0]):
77
+ angle = angles[i]
78
+
79
+ # θ + π/2
80
+ angles[i] = angle + param_shift_eps
80
81
  neuron.set_angles(angles)
81
82
  neuron.simulator = pre_sim.clone()
82
83
  neuron.predict(True, False)
83
84
  p_plus = neuron.simulator.prob(neuron.target)
84
85
 
85
- # x - angle_eps
86
- angles[param] = angle - angle_eps
86
+ # θ π/2
87
+ angles[i] = angle - param_shift_eps
87
88
  neuron.set_angles(angles)
88
89
  neuron.simulator = pre_sim.clone()
89
90
  neuron.predict(True, False)
90
91
  p_minus = neuron.simulator.prob(neuron.target)
91
92
 
92
- # Central difference
93
- delta[param] = (p_plus - p_minus) / (2 * angle_eps)
93
+ # Parameter-shift gradient
94
+ grad_x[i] = 0.5 * (p_plus - p_minus)
94
95
 
95
- angles[param] = angle
96
+ angles[i] = angle
96
97
 
97
- neuron.simulator = pre_sim
98
+ # Restore simulator
99
+ neuron.set_simulator(pre_sim)
98
100
 
99
- if _IS_TORCH_AVAILABLE:
100
- delta = torch.tensor(delta, dtype=torch.float32, device=x.device)
101
+ # Apply chain rule and upstream gradient
102
+ grad_x *= grad_output[0] / math.sqrt(max(1.0 - post_prob * post_prob, 1e-6))
101
103
 
102
- return delta
104
+ return grad_x, None
103
105
 
104
- @staticmethod
105
- def backward(ctx, grad_output):
106
- (x,) = ctx.saved_tensors
107
- neuron_wrapper = ctx.neuron_wrapper
108
- delta = _backward(x, neuron_wrapper, grad_output)
109
- if _IS_TORCH_AVAILABLE:
110
- # grad_output: (O,)
111
- # delta: (O, I)
112
- grad_input = torch.matmul(grad_output, delta) # result: (I,)
113
- else:
114
- grad_input = [
115
- sum(o * d for o, d in zip(grad_output, col))
116
- for col in zip(*delta)
117
- ]
118
-
119
- return grad_input, None
120
106
 
121
107
  class QrackNeuronTorch(nn.Module if _IS_TORCH_AVAILABLE else object):
122
108
  """Torch wrapper for QrackNeuron
@@ -125,12 +111,13 @@ class QrackNeuronTorch(nn.Module if _IS_TORCH_AVAILABLE else object):
125
111
  neuron(QrackNeuron): QrackNeuron backing this torch wrapper
126
112
  """
127
113
 
128
- def __init__(self, neuron):
114
+ def __init__(self, neuron, x):
129
115
  super().__init__()
130
116
  self.neuron = neuron
117
+ self.weights = nn.Parameter(x)
131
118
 
132
- def forward(self, x):
133
- return QrackNeuronTorchFunction.apply(x, self.neuron)
119
+ def forward(self):
120
+ return QrackNeuronTorchFunction.apply(self.weights, self.neuron)
134
121
 
135
122
 
136
123
  class QrackNeuronTorchLayer(nn.Module if _IS_TORCH_AVAILABLE else object):
@@ -156,7 +143,9 @@ class QrackNeuronTorchLayer(nn.Module if _IS_TORCH_AVAILABLE else object):
156
143
  lowest_combo_count=0,
157
144
  highest_combo_count=2,
158
145
  activation=int(NeuronActivationFn.Generalized_Logistic),
146
+ dtype=torch.float if _IS_TORCH_AVAILABLE else float,
159
147
  parameters=None,
148
+ **kwargs
160
149
  ):
161
150
  """
162
151
  Initialize a QrackNeuron layer for PyTorch with a power set of neurons connecting inputs to outputs.
@@ -175,186 +164,83 @@ class QrackNeuronTorchLayer(nn.Module if _IS_TORCH_AVAILABLE else object):
175
164
  super(QrackNeuronTorchLayer, self).__init__()
176
165
  if hidden_qubits is None:
177
166
  hidden_qubits = highest_combo_count
178
- self.simulator = QrackSimulator(input_qubits + hidden_qubits + output_qubits)
167
+ self.simulator = QrackSimulator(input_qubits + hidden_qubits + output_qubits, **kwargs)
179
168
  self.simulators = []
180
169
  self.input_indices = list(range(input_qubits))
181
170
  self.hidden_indices = list(range(input_qubits, input_qubits + hidden_qubits))
182
- self.output_indices = list(range(input_qubits + hidden_qubits, input_qubits + hidden_qubits + output_qubits))
171
+ self.output_indices = list(
172
+ range(input_qubits + hidden_qubits, input_qubits + hidden_qubits + output_qubits)
173
+ )
183
174
  self.activation = NeuronActivationFn(activation)
175
+ self.dtype = dtype
184
176
  self.apply_fn = QrackNeuronTorchFunction.apply
185
- self.backward_fn = QrackNeuronTorchFunction._backward
186
177
 
187
178
  # Create neurons from all input combinations, projecting to coherent output qubits
188
- neurons = [
189
- QrackNeuronTorch(
190
- QrackNeuron(self.simulator, input_subset, output_id, activation)
191
- )
192
- for output_id in self.output_indices
193
- for k in range(lowest_combo_count, highest_combo_count + 1)
194
- for input_subset in itertools.combinations(self.input_indices + self.hidden_indices, k)
195
- ]
196
- self.neurons = nn.ModuleList(neurons) if _IS_TORCH_AVAILABLE else neurons
197
-
198
- # Set Qrack's internal parameters:
199
- if parameters:
200
- param_count = 0
201
- self.weights = nn.ParameterList() if _IS_TORCH_AVAILABLE else []
202
- for neuron_wrapper in self.neurons:
203
- neuron = neuron_wrapper.neuron
204
- p_count = 1 << len(neuron.controls)
205
- neuron.set_angles(parameters[param_count : (param_count + p_count)])
206
- self.weights.append(
207
- nn.Parameter(torch.tensor(parameters[param_count : (param_count + p_count)]))
208
- if _IS_TORCH_AVAILABLE else parameters[param_count : (param_count + p_count)]
209
- )
210
- param_count += p_count
211
- else:
212
- self.weights = nn.ParameterList() if _IS_TORCH_AVAILABLE else []
213
- for neuron_wrapper in self.neurons:
214
- neuron = neuron_wrapper.neuron
215
- p_count = 1 << len(neuron.controls)
216
- self.weights.append(nn.Parameter(torch.zeros(p_count)) if _IS_TORCH_AVAILABLE else ([0.0] * p_count))
179
+ neurons = []
180
+ param_count = 0
181
+ for output_id in self.output_indices:
182
+ for k in range(lowest_combo_count, highest_combo_count + 1):
183
+ for input_subset in itertools.combinations(self.input_indices, k):
184
+ p_count = 1 << len(input_subset)
185
+ angles = (
186
+ (
187
+ torch.tensor(
188
+ parameters[param_count : (param_count + p_count)], dtype=dtype
189
+ )
190
+ if parameters
191
+ else torch.zeros(p_count, dtype=dtype)
192
+ )
193
+ )
194
+ neurons.append(
195
+ QrackNeuronTorch(
196
+ QrackNeuron(self.simulator, input_subset, output_id, activation), angles
197
+ )
198
+ )
199
+ param_count += p_count
200
+ self.neurons = nn.ModuleList(neurons)
217
201
 
218
202
  def forward(self, x):
219
- return QrackNeuronTorchLayerFunction.apply(x, self)
203
+ B = x.shape[0]
204
+ x = x.view(B, -1)
220
205
 
206
+ self.simulators.clear()
221
207
 
222
- class QrackNeuronTorchLayerFunction(Function if _IS_TORCH_AVAILABLE else object):
223
- """Static forward/backward/apply functions for QrackNeuronTorch"""
208
+ self.simulator.reset_all()
209
+ # Prepare hidden predictors
210
+ for hidden_id in self.hidden_indices:
211
+ self.simulator.h(hidden_id)
212
+ # Prepare a maximally uncertain output state.
213
+ for output_id in self.output_indices:
214
+ self.simulator.h(output_id)
224
215
 
225
- @staticmethod
226
- def forward(ctx, x, neuron_layer):
227
- # Save for backward
228
- ctx.save_for_backward(x)
229
- ctx.neuron_layer = neuron_layer
230
-
231
- input_indices = neuron_layer.input_indices
232
- hidden_indices = neuron_layer.hidden_indices
233
- output_indices = neuron_layer.output_indices
234
- simulators = neuron_layer.simulators
235
- weights = neuron_layer.weights
236
-
237
- if _IS_TORCH_AVAILABLE:
238
- B = x.shape[0]
239
- x = x.view(B, -1)
240
- else:
241
- B = len(x)
242
-
243
- simulators.clear()
244
- if _IS_TORCH_AVAILABLE:
245
- for b in range(B):
246
- simulator = neuron_layer.simulator.clone()
247
- simulators.append(simulator)
248
- for q, input_id in enumerate(input_indices):
249
- simulator.r(Pauli.PauliY, math.pi * x[b, q].item(), q)
250
- else:
251
- for b in range(B):
252
- simulator = neuron_layer.simulator.clone()
253
- simulators.append(simulator)
254
- for q, input_id in enumerate(input_indices):
255
- simulator.r(Pauli.PauliY, math.pi * x[b][q], q)
256
-
257
- y = [([0.0] * len(output_indices)) for _ in range(B)]
216
+ # Group neurons by output target once
217
+ by_out = {out: [] for out in self.output_indices}
218
+ for neuron_wrapper in self.neurons:
219
+ by_out[neuron_wrapper.neuron.target].append(neuron_wrapper)
220
+
221
+ batch_rows = []
258
222
  for b in range(B):
259
- simulator = simulators[b]
260
- # Prepare a maximally uncertain output state.
261
- for output_id in output_indices:
262
- simulator.h(output_id)
263
- # Prepare hidden predictors
264
- for h in hidden_indices:
265
- simulator.h(h)
223
+ simulator = self.simulator.clone()
224
+ self.simulators.append(simulator)
266
225
 
267
- # Set Qrack's internal parameters:
268
- for idx, neuron_wrapper in enumerate(neuron_layer.neurons):
269
- neuron_wrapper.neuron.simulator = simulator
270
- neuron_layer.apply_fn(weights[idx], neuron_wrapper)
226
+ for q, input_id in enumerate(self.input_indices):
227
+ simulator.r(Pauli.PauliY, math.pi * x[b, q].item(), input_id)
271
228
 
272
- for q, output_id in enumerate(output_indices):
273
- y[b][q] = simulator.prob(output_id)
229
+ row = []
230
+ for out in self.output_indices:
231
+ phi = torch.tensor(init_phi, device=x.device, dtype=x.dtype)
274
232
 
275
- if _IS_TORCH_AVAILABLE:
276
- y = torch.tensor(y, dtype=torch.float32, device=x.device)
233
+ for neuron_wrapper in by_out[out]:
234
+ neuron_wrapper.neuron.set_simulator(simulator)
235
+ phi += self.apply_fn(
236
+ neuron_wrapper.weights,
237
+ neuron_wrapper.neuron
238
+ ).squeeze()
277
239
 
278
- return y
240
+ # Convert angle back to probability
241
+ p = torch.clamp(torch.sin(phi), min=0.0)
242
+ row.append(p)
279
243
 
280
- @staticmethod
281
- def backward(ctx, grad_output):
282
- (x,) = ctx.saved_tensors
283
- neuron_layer = ctx.neuron_layer
284
-
285
- input_indices = neuron_layer.input_indices
286
- hidden_indices = neuron_layer.hidden_indices
287
- output_indices = neuron_layer.output_indices
288
- simulators = neuron_layer.simulators
289
- neurons = neuron_layer.neurons
290
- backward_fn = neuron_layer.backward_fn
291
-
292
- input_count = len(input_indices)
293
- output_count = len(output_indices)
294
-
295
- if _IS_TORCH_AVAILABLE:
296
- B = x.shape[0]
297
- x = x.view(B, -1)
298
- else:
299
- B = len(x)
300
-
301
- # Uncompute prediction
302
- if _IS_TORCH_AVAILABLE:
303
- delta = torch.zeros((B, output_count, input_count), dtype=torch.float32, device=x.device)
304
- for b in range(B):
305
- simulator = simulators[b]
306
- for neuron_wrapper in neurons:
307
- neuron = neuron_wrapper.neuron
308
- neuron.simulator = simulator
309
- angles = torch.tensor(neuron.get_angles(), dtype=torch.float32, device=x.device, requires_grad=True)
310
- o = output_indices.index(neuron.target)
311
- neuron_grad = backward_fn(angles, neuron_wrapper)
312
- for idx, c in enumerate(neuron.controls):
313
- if c not in input_indices:
314
- continue
315
- i = input_indices.index(c)
316
- delta[b, o, i] += neuron_grad[idx]
317
- else:
318
- delta = [[[0.0] * input_count for _ in range(output_count)] for _ in range(B)]
319
- for b in range(B):
320
- simulator = simulators[b]
321
- for neuron_wrapper in neurons:
322
- neuron = neuron_wrapper.neuron
323
- neuron.simulator = simulator
324
- angles = neuron.get_angles()
325
- o = output_indices.index(neuron.target)
326
- neuron_grad = backward_fn(angles, neuron_wrapper)
327
- for idx, c in enumerate(neuron.controls):
328
- if c not in input_indices:
329
- continue
330
- i = input_indices.index(c)
331
- delta[b][o][i] += neuron_grad[idx]
332
-
333
- # Uncompute output state prep
334
- for simulator in simulators:
335
- for output_id in output_indices:
336
- simulator.h(output_id)
337
- for h in hidden_indices:
338
- simulator.h(output_id)
339
-
340
- if _IS_TORCH_AVAILABLE:
341
- for b in range(B):
342
- simulator = simulators[b]
343
- for q, input_id in enumerate(input_indices):
344
- simulator.r(Pauli.PauliY, -math.pi * x[b, q].item(), q)
345
- else:
346
- for b in range(B):
347
- simulator = simulators[b]
348
- for q, input_id in enumerate(input_indices):
349
- simulator.r(Pauli.PauliY, -math.pi * x[b][q].item(), q)
350
-
351
- if _IS_TORCH_AVAILABLE:
352
- grad_input = torch.matmul(grad_output.view(B, 1, -1), delta).view_as(x)
353
- else:
354
- grad_input = [[0.0] * output_count for _ in range(B)]
355
- for b in range(B):
356
- for o in range(output_indices):
357
- for i in range(input_indices):
358
- grad_input[b][o] += grad_output[b][o] * delta[b][o][i]
359
-
360
- return grad_input, None
244
+ batch_rows.append(torch.stack(row))
245
+
246
+ return torch.stack(batch_rows)
@@ -57,9 +57,7 @@ class QrackSimulator:
57
57
  isPaged=True,
58
58
  isCpuGpuHybrid=True,
59
59
  isOpenCL=True,
60
- isHostPointer=(
61
- True if os.environ.get("PYQRACK_HOST_POINTER_DEFAULT_ON") else False
62
- ),
60
+ isHostPointer=(True if os.environ.get("PYQRACK_HOST_POINTER_DEFAULT_ON") else False),
63
61
  isSparse=False,
64
62
  noise=0,
65
63
  pyzxCircuit=None,
@@ -100,7 +98,7 @@ class QrackSimulator:
100
98
  isCpuGpuHybrid,
101
99
  isOpenCL,
102
100
  isHostPointer,
103
- isSparse
101
+ isSparse,
104
102
  )
105
103
 
106
104
  self._throw_if_error()
@@ -593,7 +591,11 @@ class QrackSimulator:
593
591
  "2x2 matrix 'm' in QrackSimulator.mcmtrx() must contain at least 4 elements."
594
592
  )
595
593
  Qrack.qrack_lib.MCMtrx(
596
- self.sid, len(c), QrackSimulator._ulonglong_byref(c), QrackSimulator._complex_byref(m), q
594
+ self.sid,
595
+ len(c),
596
+ QrackSimulator._ulonglong_byref(c),
597
+ QrackSimulator._complex_byref(m),
598
+ q,
597
599
  )
598
600
  self._throw_if_error()
599
601
 
@@ -771,7 +773,11 @@ class QrackSimulator:
771
773
  "2x2 matrix 'm' in QrackSimulator.macmtrx() must contain at least 4 elements."
772
774
  )
773
775
  Qrack.qrack_lib.MACMtrx(
774
- self.sid, len(c), QrackSimulator._ulonglong_byref(c), QrackSimulator._complex_byref(m), q
776
+ self.sid,
777
+ len(c),
778
+ QrackSimulator._ulonglong_byref(c),
779
+ QrackSimulator._complex_byref(m),
780
+ q,
775
781
  )
776
782
  self._throw_if_error()
777
783
 
@@ -796,7 +802,12 @@ class QrackSimulator:
796
802
  "2x2 matrix 'm' in QrackSimulator.ucmtrx() must contain at least 4 elements."
797
803
  )
798
804
  Qrack.qrack_lib.UCMtrx(
799
- self.sid, len(c), QrackSimulator._ulonglong_byref(c), QrackSimulator._complex_byref(m), q, p
805
+ self.sid,
806
+ len(c),
807
+ QrackSimulator._ulonglong_byref(c),
808
+ QrackSimulator._complex_byref(m),
809
+ q,
810
+ p,
800
811
  )
801
812
  self._throw_if_error()
802
813
 
@@ -820,7 +831,11 @@ class QrackSimulator:
820
831
  "Multiplex matrix 'm' in QrackSimulator.multiplex1_mtrx() must contain at least (4 * 2 ** len(c)) elements."
821
832
  )
822
833
  Qrack.qrack_lib.Multiplex1Mtrx(
823
- self.sid, len(c), QrackSimulator._ulonglong_byref(c), q, QrackSimulator._complex_byref(m)
834
+ self.sid,
835
+ len(c),
836
+ QrackSimulator._ulonglong_byref(c),
837
+ q,
838
+ QrackSimulator._complex_byref(m),
824
839
  )
825
840
  self._throw_if_error()
826
841
 
@@ -978,9 +993,7 @@ class QrackSimulator:
978
993
  Raises:
979
994
  RuntimeError: QrackSimulator raised an exception.
980
995
  """
981
- Qrack.qrack_lib.FSim(
982
- self.sid, ctypes.c_double(th), ctypes.c_double(ph), qi1, qi2
983
- )
996
+ Qrack.qrack_lib.FSim(self.sid, ctypes.c_double(th), ctypes.c_double(ph), qi1, qi2)
984
997
  self._throw_if_error()
985
998
 
986
999
  def cswap(self, c, qi1, qi2):
@@ -1831,7 +1844,10 @@ class QrackSimulator:
1831
1844
  )
1832
1845
 
1833
1846
  Qrack.qrack_lib.Hash(
1834
- self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._to_ubyte(len(q), t)
1847
+ self.sid,
1848
+ len(q),
1849
+ QrackSimulator._ulonglong_byref(q),
1850
+ QrackSimulator._to_ubyte(len(q), t),
1835
1851
  )
1836
1852
  self._throw_if_error()
1837
1853
 
@@ -2316,7 +2332,9 @@ class QrackSimulator:
2316
2332
  amp_count = 1 << len(q)
2317
2333
  sqr_amp_count = amp_count * amp_count
2318
2334
  flat_rdm = QrackSimulator._qrack_complex_byref([complex(0, 0)] * sqr_amp_count)
2319
- Qrack.qrack_lib.OutReducedDensityMatrix(self.sid, len(q), QrackSimulator._ulonglong_byref(q), flat_rdm)
2335
+ Qrack.qrack_lib.OutReducedDensityMatrix(
2336
+ self.sid, len(q), QrackSimulator._ulonglong_byref(q), flat_rdm
2337
+ )
2320
2338
  self._throw_if_error()
2321
2339
  return [complex(r, i) for r, i in QrackSimulator._pairwise(flat_rdm)]
2322
2340
 
@@ -2541,7 +2559,11 @@ class QrackSimulator:
2541
2559
  raise RuntimeError("factorized_expectation argument lengths do not match.")
2542
2560
  m = max([(x.bit_length() + 63) // 64 for x in c])
2543
2561
  result = Qrack.qrack_lib.FactorizedExpectation(
2544
- self.sid, len(q), QrackSimulator._ulonglong_byref(q), m, QrackSimulator._to_ulonglong(m, c)
2562
+ self.sid,
2563
+ len(q),
2564
+ QrackSimulator._ulonglong_byref(q),
2565
+ m,
2566
+ QrackSimulator._to_ulonglong(m, c),
2545
2567
  )
2546
2568
  self._throw_if_error()
2547
2569
  return result
@@ -2566,12 +2588,15 @@ class QrackSimulator:
2566
2588
  Expectation value
2567
2589
  """
2568
2590
  if (len(q) << 1) != len(c):
2569
- raise RuntimeError(
2570
- "factorized_expectation_rdm argument lengths do not match."
2571
- )
2591
+ raise RuntimeError("factorized_expectation_rdm argument lengths do not match.")
2572
2592
  m = max([(x.bit_length() + 63) // 64 for x in c])
2573
2593
  result = Qrack.qrack_lib.FactorizedExpectationRdm(
2574
- self.sid, len(q), QrackSimulator._ulonglong_byref(q), m, QrackSimulator._to_ulonglong(m, c), r
2594
+ self.sid,
2595
+ len(q),
2596
+ QrackSimulator._ulonglong_byref(q),
2597
+ m,
2598
+ QrackSimulator._to_ulonglong(m, c),
2599
+ r,
2575
2600
  )
2576
2601
  self._throw_if_error()
2577
2602
  return result
@@ -2594,9 +2619,7 @@ class QrackSimulator:
2594
2619
  Expectation value
2595
2620
  """
2596
2621
  if (len(q) << 1) != len(c):
2597
- raise RuntimeError(
2598
- "factorized_expectation_rdm argument lengths do not match."
2599
- )
2622
+ raise RuntimeError("factorized_expectation_rdm argument lengths do not match.")
2600
2623
  result = Qrack.qrack_lib.FactorizedExpectationFp(
2601
2624
  self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._real1_byref(c)
2602
2625
  )
@@ -2623,9 +2646,7 @@ class QrackSimulator:
2623
2646
  Expectation value
2624
2647
  """
2625
2648
  if (len(q) << 1) != len(c):
2626
- raise RuntimeError(
2627
- "factorized_expectation_fp_rdm argument lengths do not match."
2628
- )
2649
+ raise RuntimeError("factorized_expectation_fp_rdm argument lengths do not match.")
2629
2650
  result = Qrack.qrack_lib.FactorizedExpectationFpRdm(
2630
2651
  self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._real1_byref(c), r
2631
2652
  )
@@ -2836,7 +2857,11 @@ class QrackSimulator:
2836
2857
  raise RuntimeError("factorized_variance argument lengths do not match.")
2837
2858
  m = max([(x.bit_length() + 63) // 64 for x in c])
2838
2859
  result = Qrack.qrack_lib.FactorizedVariance(
2839
- self.sid, len(q), QrackSimulator._ulonglong_byref(q), m, QrackSimulator._to_ulonglong(m, c)
2860
+ self.sid,
2861
+ len(q),
2862
+ QrackSimulator._ulonglong_byref(q),
2863
+ m,
2864
+ QrackSimulator._to_ulonglong(m, c),
2840
2865
  )
2841
2866
  self._throw_if_error()
2842
2867
  return result
@@ -2864,7 +2889,12 @@ class QrackSimulator:
2864
2889
  raise RuntimeError("factorized_variance_rdm argument lengths do not match.")
2865
2890
  m = max([(x.bit_length() + 63) // 64 for x in c])
2866
2891
  result = Qrack.qrack_lib.FactorizedVarianceRdm(
2867
- self.sid, len(q), QrackSimulator._ulonglong_byref(q), m, QrackSimulator._to_ulonglong(m, c), r
2892
+ self.sid,
2893
+ len(q),
2894
+ QrackSimulator._ulonglong_byref(q),
2895
+ m,
2896
+ QrackSimulator._to_ulonglong(m, c),
2897
+ r,
2868
2898
  )
2869
2899
  self._throw_if_error()
2870
2900
  return result
@@ -2914,9 +2944,7 @@ class QrackSimulator:
2914
2944
  variance
2915
2945
  """
2916
2946
  if (len(q) << 1) != len(c):
2917
- raise RuntimeError(
2918
- "factorized_variance_fp_rdm argument lengths do not match."
2919
- )
2947
+ raise RuntimeError("factorized_variance_fp_rdm argument lengths do not match.")
2920
2948
  result = Qrack.qrack_lib.FactorizedVarianceFpRdm(
2921
2949
  self.sid, len(q), QrackSimulator._ulonglong_byref(q), QrackSimulator._real1_byref(c), r
2922
2950
  )
@@ -3551,9 +3579,7 @@ class QrackSimulator:
3551
3579
  "swap",
3552
3580
  ]
3553
3581
  try:
3554
- circ = transpile(
3555
- clifford_circ, basis_gates=basis_gates, optimization_level=2
3556
- )
3582
+ circ = transpile(clifford_circ, basis_gates=basis_gates, optimization_level=2)
3557
3583
  except:
3558
3584
  circ = clifford_circ
3559
3585
 
@@ -3649,9 +3675,7 @@ class QrackSimulator:
3649
3675
  )
3650
3676
  elif op.name == "h":
3651
3677
  non_clifford = np.matmul(
3652
- np.array(
3653
- [[sqrt1_2, sqrt1_2], [sqrt1_2, -sqrt1_2]], np.complex128
3654
- ),
3678
+ np.array([[sqrt1_2, sqrt1_2], [sqrt1_2, -sqrt1_2]], np.complex128),
3655
3679
  non_clifford,
3656
3680
  )
3657
3681
  elif op.name == "x":
@@ -3759,9 +3783,7 @@ class QrackSimulator:
3759
3783
  j += 1
3760
3784
  continue
3761
3785
 
3762
- if (q1 == i) and (
3763
- (op.name == "cx") or (op.name == "cy") or (op.name == "cz")
3764
- ):
3786
+ if (q1 == i) and ((op.name == "cx") or (op.name == "cy") or (op.name == "cz")):
3765
3787
  if np.isclose(np.abs(non_clifford[0][1]), 0) and np.isclose(
3766
3788
  np.abs(non_clifford[1][0]), 0
3767
3789
  ):
@@ -3827,9 +3849,7 @@ class QrackSimulator:
3827
3849
  elif op.name == "h":
3828
3850
  non_clifford = np.matmul(
3829
3851
  non_clifford,
3830
- np.array(
3831
- [[sqrt1_2, sqrt1_2], [sqrt1_2, -sqrt1_2]], np.complex128
3832
- ),
3852
+ np.array([[sqrt1_2, sqrt1_2], [sqrt1_2, -sqrt1_2]], np.complex128),
3833
3853
  )
3834
3854
  elif op.name == "x":
3835
3855
  non_clifford = np.matmul(
@@ -4019,12 +4039,8 @@ class QrackSimulator:
4019
4039
  qasm = qasm3.dumps(circ)
4020
4040
  except:
4021
4041
  qasm = circ.qasm()
4022
- qasm = qasm.replace(
4023
- "qreg q[" + str(circ.width()) + "];", "qreg q[" + str(width) + "];"
4024
- )
4025
- highest_index = max(
4026
- [int(x) for x in re.findall(r"\[(.*?)\]", qasm) if x.isdigit()]
4027
- )
4042
+ qasm = qasm.replace("qreg q[" + str(circ.width()) + "];", "qreg q[" + str(width) + "];")
4043
+ highest_index = max([int(x) for x in re.findall(r"\[(.*?)\]", qasm) if x.isdigit()])
4028
4044
  if highest_index != width:
4029
4045
  qasm = qasm.replace(
4030
4046
  "qreg q[" + str(width) + "];", "qreg q[" + str(highest_index) + "];"
@@ -4139,17 +4155,11 @@ class QrackSimulator:
4139
4155
  (-1 * float(operation.params[1])) + math.pi / 2,
4140
4156
  )
4141
4157
  elif name == "rx":
4142
- self._sim.r(
4143
- Pauli.PauliX, float(operation.params[0]), operation.qubits[0]._index
4144
- )
4158
+ self._sim.r(Pauli.PauliX, float(operation.params[0]), operation.qubits[0]._index)
4145
4159
  elif name == "ry":
4146
- self._sim.r(
4147
- Pauli.PauliY, float(operation.params[0]), operation.qubits[0]._index
4148
- )
4160
+ self._sim.r(Pauli.PauliY, float(operation.params[0]), operation.qubits[0]._index)
4149
4161
  elif name == "rz":
4150
- self._sim.r(
4151
- Pauli.PauliZ, float(operation.params[0]), operation.qubits[0]._index
4152
- )
4162
+ self._sim.r(Pauli.PauliZ, float(operation.params[0]), operation.qubits[0]._index)
4153
4163
  elif name == "h":
4154
4164
  self._sim.h(operation.qubits[0]._index)
4155
4165
  elif name == "x":
@@ -4201,21 +4211,13 @@ class QrackSimulator:
4201
4211
  float(operation.params[2]),
4202
4212
  )
4203
4213
  elif name == "cx":
4204
- self._sim.mcx(
4205
- [q._index for q in operation.qubits[0:1]], operation.qubits[1]._index
4206
- )
4214
+ self._sim.mcx([q._index for q in operation.qubits[0:1]], operation.qubits[1]._index)
4207
4215
  elif name == "cy":
4208
- self._sim.mcy(
4209
- [q._index for q in operation.qubits[0:1]], operation.qubits[1]._index
4210
- )
4216
+ self._sim.mcy([q._index for q in operation.qubits[0:1]], operation.qubits[1]._index)
4211
4217
  elif name == "cz":
4212
- self._sim.mcz(
4213
- [q._index for q in operation.qubits[0:1]], operation.qubits[1]._index
4214
- )
4218
+ self._sim.mcz([q._index for q in operation.qubits[0:1]], operation.qubits[1]._index)
4215
4219
  elif name == "ch":
4216
- self._sim.mch(
4217
- [q._index for q in operation.qubits[0:1]], operation.qubits[1]._index
4218
- )
4220
+ self._sim.mch([q._index for q in operation.qubits[0:1]], operation.qubits[1]._index)
4219
4221
  elif name == "cp":
4220
4222
  self._sim.mcmtrx(
4221
4223
  [q._index for q in operation.qubits[0:1]],
@@ -4241,34 +4243,20 @@ class QrackSimulator:
4241
4243
  operation.qubits[1]._index,
4242
4244
  )
4243
4245
  elif name == "dcx":
4244
- self._sim.mcx(
4245
- [q._index for q in operation.qubits[0:1]], operation.qubits[1]._index
4246
- )
4246
+ self._sim.mcx([q._index for q in operation.qubits[0:1]], operation.qubits[1]._index)
4247
4247
  self._sim.mcx(operation.qubits[1:2]._index, operation.qubits[0]._index)
4248
4248
  elif name == "ccx":
4249
- self._sim.mcx(
4250
- [q._index for q in operation.qubits[0:2]], operation.qubits[2]._index
4251
- )
4249
+ self._sim.mcx([q._index for q in operation.qubits[0:2]], operation.qubits[2]._index)
4252
4250
  elif name == "ccy":
4253
- self._sim.mcy(
4254
- [q._index for q in operation.qubits[0:2]], operation.qubits[2]._index
4255
- )
4251
+ self._sim.mcy([q._index for q in operation.qubits[0:2]], operation.qubits[2]._index)
4256
4252
  elif name == "ccz":
4257
- self._sim.mcz(
4258
- [q._index for q in operation.qubits[0:2]], operation.qubits[2]._index
4259
- )
4253
+ self._sim.mcz([q._index for q in operation.qubits[0:2]], operation.qubits[2]._index)
4260
4254
  elif name == "mcx":
4261
- self._sim.mcx(
4262
- [q._index for q in operation.qubits[0:-1]], operation.qubits[-1]._index
4263
- )
4255
+ self._sim.mcx([q._index for q in operation.qubits[0:-1]], operation.qubits[-1]._index)
4264
4256
  elif name == "mcy":
4265
- self._sim.mcy(
4266
- [q._index for q in operation.qubits[0:-1]], operation.qubits[-1]._index
4267
- )
4257
+ self._sim.mcy([q._index for q in operation.qubits[0:-1]], operation.qubits[-1]._index)
4268
4258
  elif name == "mcz":
4269
- self._sim.mcz(
4270
- [q._index for q in operation.qubits[0:-1]], operation.qubits[-1]._index
4271
- )
4259
+ self._sim.mcz([q._index for q in operation.qubits[0:-1]], operation.qubits[-1]._index)
4272
4260
  elif name == "swap":
4273
4261
  self._sim.swap(operation.qubits[0]._index, operation.qubits[1]._index)
4274
4262
  elif name == "iswap":
@@ -4320,9 +4308,9 @@ class QrackSimulator:
4320
4308
  cregbit = clbit
4321
4309
 
4322
4310
  regbit = 1 << cregbit
4323
- self._classical_register = (
4324
- self._classical_register & (~regbit)
4325
- ) | (qubit_outcome << cregbit)
4311
+ self._classical_register = (self._classical_register & (~regbit)) | (
4312
+ qubit_outcome << cregbit
4313
+ )
4326
4314
 
4327
4315
  elif name == "bfunc":
4328
4316
  mask = int(operation.mask, 16)
@@ -4437,9 +4425,7 @@ class QrackSimulator:
4437
4425
  if operation.name == "id" or operation.name == "barrier":
4438
4426
  continue
4439
4427
 
4440
- if is_initializing and (
4441
- (operation.name == "measure") or (operation.name == "reset")
4442
- ):
4428
+ if is_initializing and ((operation.name == "measure") or (operation.name == "reset")):
4443
4429
  continue
4444
4430
 
4445
4431
  is_initializing = False
@@ -4497,9 +4483,7 @@ class QrackSimulator:
4497
4483
  self._sample_cregbits = []
4498
4484
 
4499
4485
  if self._sample_measure and (len(self._sample_qubits) > 0):
4500
- _data = self._add_sample_measure(
4501
- self._sample_qubits, self._sample_clbits, self._shots
4502
- )
4486
+ _data = self._add_sample_measure(self._sample_qubits, self._sample_clbits, self._shots)
4503
4487
 
4504
4488
  del self._sim
4505
4489
 
@@ -28,13 +28,9 @@ class QrackSystem:
28
28
  elif _platform == "win32":
29
29
  shared_lib_path = os.path.dirname(__file__) + "/qrack_lib/qrack_pinvoke.dll"
30
30
  elif _platform == "darwin":
31
- shared_lib_path = (
32
- os.path.dirname(__file__) + "/qrack_lib/libqrack_pinvoke.dylib"
33
- )
31
+ shared_lib_path = os.path.dirname(__file__) + "/qrack_lib/libqrack_pinvoke.dylib"
34
32
  else:
35
- shared_lib_path = (
36
- os.path.dirname(__file__) + "/qrack_lib/libqrack_pinvoke.so"
37
- )
33
+ shared_lib_path = os.path.dirname(__file__) + "/qrack_lib/libqrack_pinvoke.so"
38
34
 
39
35
  try:
40
36
  self.qrack_lib = CDLL(shared_lib_path)
@@ -50,9 +46,7 @@ class QrackSystem:
50
46
  self.qrack_lib = CDLL(shared_lib_path)
51
47
  except Exception as e:
52
48
  if _platform == "win32":
53
- shared_lib_path = (
54
- "C:/Program Files (x86)/Qrack/bin/qrack_pinvoke.dll"
55
- )
49
+ shared_lib_path = "C:/Program Files (x86)/Qrack/bin/qrack_pinvoke.dll"
56
50
  elif _platform == "darwin":
57
51
  shared_lib_path = "/usr/lib/qrack/libqrack_pinvoke.dylib"
58
52
  else:
@@ -97,12 +91,22 @@ class QrackSystem:
97
91
  self.qrack_lib.InKet.argtypes = [c_ulonglong, POINTER(c_float)]
98
92
  self.qrack_lib.OutKet.argtypes = [c_ulonglong, POINTER(c_float)]
99
93
  self.qrack_lib.OutProbs.argtypes = [c_ulonglong, POINTER(c_float)]
100
- self.qrack_lib.OutReducedDensityMatrix.argtypes = [c_ulonglong, c_ulonglong, POINTER(c_ulonglong), POINTER(c_float)]
94
+ self.qrack_lib.OutReducedDensityMatrix.argtypes = [
95
+ c_ulonglong,
96
+ c_ulonglong,
97
+ POINTER(c_ulonglong),
98
+ POINTER(c_float),
99
+ ]
101
100
  else:
102
101
  self.qrack_lib.InKet.argtypes = [c_ulonglong, POINTER(c_double)]
103
102
  self.qrack_lib.OutKet.argtypes = [c_ulonglong, POINTER(c_double)]
104
103
  self.qrack_lib.OutProbs.argtypes = [c_ulonglong, POINTER(c_double)]
105
- self.qrack_lib.OutReducedDensityMatrix.argtypes = [c_ulonglong, c_ulonglong, POINTER(c_ulonglong), POINTER(c_double)]
104
+ self.qrack_lib.OutReducedDensityMatrix.argtypes = [
105
+ c_ulonglong,
106
+ c_ulonglong,
107
+ POINTER(c_ulonglong),
108
+ POINTER(c_double),
109
+ ]
106
110
 
107
111
  self.qrack_lib.init.restype = c_ulonglong
108
112
  self.qrack_lib.init.argtypes = []
@@ -129,7 +133,7 @@ class QrackSystem:
129
133
  c_bool,
130
134
  c_bool,
131
135
  c_bool,
132
- c_bool
136
+ c_bool,
133
137
  ]
134
138
 
135
139
  self.qrack_lib.init_count_stabilizer.restype = c_ulonglong
@@ -1251,6 +1255,9 @@ class QrackSystem:
1251
1255
  self.qrack_lib.destroy_qneuron.restype = None
1252
1256
  self.qrack_lib.destroy_qneuron.argtypes = [c_ulonglong]
1253
1257
 
1258
+ self.qrack_lib.set_qneuron_sim.restype = None
1259
+ self.qrack_lib.set_qneuron_sim.argtypes = [c_ulonglong, c_ulonglong]
1260
+
1254
1261
  self.qrack_lib.set_qneuron_angles.restype = None
1255
1262
  self.qrack_lib.get_qneuron_angles.restype = None
1256
1263
 
@@ -20,9 +20,7 @@ def load_data(sim, index_bits, value_bits, data):
20
20
  Value error: Length of value_bits does not match data feature column count!
21
21
  """
22
22
  if (len(data) > 0) and (len(value_bits) != len(data[0])):
23
- raise ValueError(
24
- "Length of value_bits does not match data feature column count!"
25
- )
23
+ raise ValueError("Length of value_bits does not match data feature column count!")
26
24
 
27
25
  for i in range(index_bits):
28
26
  if sim.m(i):
@@ -28,9 +28,7 @@ try:
28
28
  min_val, max_val = data[:, i].min(), data[:, i].max()
29
29
  thresholds = np.linspace(min_val, max_val, n_bins + 1)[1:-1]
30
30
  bins = np.digitize(data[:, i], bins=thresholds)
31
- bin_bits = ((bins[:, None] & (1 << np.arange(bits)[::-1])) > 0).astype(
32
- int
33
- )
31
+ bin_bits = ((bins[:, None] & (1 << np.arange(bits)[::-1])) > 0).astype(int)
34
32
  new_features.append(bin_bits)
35
33
  else:
36
34
  new_features.append(data[:, i][:, None]) # Keep original
@@ -51,6 +49,4 @@ except ImportError:
51
49
  Returns:
52
50
  np.ndarray: Transformed data with selected features replaced by binary bit columns.
53
51
  """
54
- raise NotImplementedError(
55
- "You must have numpy installed to use quantize_by_percentile()!"
56
- )
52
+ raise NotImplementedError("You must have numpy installed to use quantize_by_percentile()!")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyqrack-complex128
3
- Version: 1.78.3
3
+ Version: 1.80.2
4
4
  Summary: pyqrack - Pure Python vm6502q/qrack Wrapper
5
5
  Home-page: https://github.com/vm6502q/pyqrack
6
6
  Author: Daniel Strano
@@ -32,6 +32,8 @@ Classifier: Programming Language :: Python :: 3.9
32
32
  Classifier: Programming Language :: Python :: 3.10
33
33
  Classifier: Programming Language :: Python :: 3.11
34
34
  Classifier: Programming Language :: Python :: 3.12
35
+ Classifier: Programming Language :: Python :: 3.13
36
+ Classifier: Programming Language :: Python :: 3.14
35
37
  Classifier: Topic :: Scientific/Engineering :: Quantum Computing
36
38
  Description-Content-Type: text/markdown
37
39
  License-File: LICENSE
@@ -0,0 +1,23 @@
1
+ pyqrack/__init__.py,sha256=P-J0gPYsYfeoBdJA0EqGwrmGwM0-hyI6pqb0Qd40Rpg,790
2
+ pyqrack/neuron_activation_fn.py,sha256=fQTTFfsvwcot_43Vopacot47IV2Rxk8pelUyuzwpXPs,593
3
+ pyqrack/pauli.py,sha256=wg500wDOwdIU4lEVJoMmjtbAdmtakZYzLPjdzC2rwUQ,654
4
+ pyqrack/qrack_ace_backend.py,sha256=-LSEX-M_hg-17LL27rLh8fpwuJaMEQeQpXzYoiuC21A,49143
5
+ pyqrack/qrack_circuit.py,sha256=jcelcoRrAWJBs9OrjBKkCba1MEIUcXeXDMKH3iwOl2Y,19778
6
+ pyqrack/qrack_neuron.py,sha256=TQl3jNFu5E41yuILBQIeSeagr4mRAPakKufFvniJOOc,12219
7
+ pyqrack/qrack_neuron_torch_layer.py,sha256=zAcB_xGnCGKZQSJQLth-nyYob-ewdOs4EEmms6lr4nw,9715
8
+ pyqrack/qrack_simulator.py,sha256=41nQEjC8IRhPz2_McpH9G-1T7V1aFK82RRKLxt1oJeo,148879
9
+ pyqrack/qrack_stabilizer.py,sha256=O-7VJ9Vw4h25PK_kesSjIqHXGSo8lLrQLIyGgmzG7Co,2124
10
+ pyqrack/quimb_circuit_type.py,sha256=Sk-Tmn38kUYmAkJJ75btWuhYZyTXOOezmowFhfdiGDc,621
11
+ pyqrack/qrack_system/__init__.py,sha256=-oZ9dsb1hixsnrkUJRY_C5DzQ_l6MtifF_Z465BgqV4,334
12
+ pyqrack/qrack_system/qrack_system.py,sha256=2Me1K7z74dzx1u5wcLpNK8mVrgn3aA1fCoj4jcf2guQ,44155
13
+ pyqrack/qrack_system/qrack_cl_precompile/qrack_cl_precompile,sha256=oVv74H7l9G0k6YTYFqYWygQ9XV-xt7IcyPwZlpudbuw,36224
14
+ pyqrack/qrack_system/qrack_lib/libqrack_pinvoke.9.35.1.dylib,sha256=NiRG8bQF2Ve0A4Up9jux6sf3krHOCbl8v1VENOg7cn8,3660640
15
+ pyqrack/qrack_system/qrack_lib/libqrack_pinvoke.dylib,sha256=NiRG8bQF2Ve0A4Up9jux6sf3krHOCbl8v1VENOg7cn8,3660640
16
+ pyqrack/stats/__init__.py,sha256=Hla85my2fY_roR9lIjGBVpEG7ySOTMwjWa8D6-kgCnY,276
17
+ pyqrack/stats/load_quantized_data.py,sha256=10-ZULlnSX45br7fDtJuiCzcMTAL4eI3QyhSENXZjgM,1162
18
+ pyqrack/stats/quantize_by_range.py,sha256=zPLMNuuyzMPvB-K9Ukc-RjzIzwCsZkzuT2gZneCeCBg,2210
19
+ pyqrack_complex128-1.80.2.dist-info/LICENSE,sha256=HxB-7SaWTuewAk1nz-3_3FUD6QhgX73kNT_taKVUTq8,1069
20
+ pyqrack_complex128-1.80.2.dist-info/METADATA,sha256=H-Lr2s3JeqIj0Z29Bnuke42tFeoTMaShiVSRdQgOjvA,5992
21
+ pyqrack_complex128-1.80.2.dist-info/WHEEL,sha256=BM14wstXunLvpW3hePaerLq1eZfN4NnYgS3CIXR7_AM,106
22
+ pyqrack_complex128-1.80.2.dist-info/top_level.txt,sha256=YE_3q9JTGRLMilNg2tGP1y7uU-Dx8PDao2OhwoIbv8E,8
23
+ pyqrack_complex128-1.80.2.dist-info/RECORD,,
@@ -1,23 +0,0 @@
1
- pyqrack/__init__.py,sha256=m5hflZS-sICsMPlwri4H6x8CF0ZEa0WBAVrna89SyTk,825
2
- pyqrack/neuron_activation_fn.py,sha256=fQTTFfsvwcot_43Vopacot47IV2Rxk8pelUyuzwpXPs,593
3
- pyqrack/pauli.py,sha256=wg500wDOwdIU4lEVJoMmjtbAdmtakZYzLPjdzC2rwUQ,654
4
- pyqrack/qrack_ace_backend.py,sha256=D5scJXQuqbBKTPr5ycDr5wpxdk6TEwz7fhZ5YCcsTL8,49677
5
- pyqrack/qrack_circuit.py,sha256=UvdcmF5GZQEf5pLL8fxqEofLgms3yBBWFPwAC-pVh5I,19830
6
- pyqrack/qrack_neuron.py,sha256=XanXEaY1sZvTpFmMUlaL41pxFwiQjAeS3V82ugmwyqc,11786
7
- pyqrack/qrack_neuron_torch_layer.py,sha256=DjESsSiNLFW96a3MRc4H9mKQ96o51R2O9-q2rYD6Oyc,14805
8
- pyqrack/qrack_simulator.py,sha256=5Io-UHx11faJfgiMtUciqEGtMNN_RBvcwgyk8lBfkDw,149271
9
- pyqrack/qrack_stabilizer.py,sha256=O-7VJ9Vw4h25PK_kesSjIqHXGSo8lLrQLIyGgmzG7Co,2124
10
- pyqrack/quimb_circuit_type.py,sha256=Sk-Tmn38kUYmAkJJ75btWuhYZyTXOOezmowFhfdiGDc,621
11
- pyqrack/qrack_system/__init__.py,sha256=-oZ9dsb1hixsnrkUJRY_C5DzQ_l6MtifF_Z465BgqV4,334
12
- pyqrack/qrack_system/qrack_system.py,sha256=q6kjSCjTvuh5sLru-tUoCoGtD8Y3tL3J2yQahXuEQ1s,43976
13
- pyqrack/qrack_system/qrack_cl_precompile/qrack_cl_precompile,sha256=oVv74H7l9G0k6YTYFqYWygQ9XV-xt7IcyPwZlpudbuw,36224
14
- pyqrack/qrack_system/qrack_lib/libqrack_pinvoke.9.34.5.dylib,sha256=UflibXjUSaWJZd2d1vY_y3IvwnE-B12SnkCoeB7A5FM,3660608
15
- pyqrack/qrack_system/qrack_lib/libqrack_pinvoke.dylib,sha256=UflibXjUSaWJZd2d1vY_y3IvwnE-B12SnkCoeB7A5FM,3660608
16
- pyqrack/stats/__init__.py,sha256=Hla85my2fY_roR9lIjGBVpEG7ySOTMwjWa8D6-kgCnY,276
17
- pyqrack/stats/load_quantized_data.py,sha256=z12u9F7Nt3P-i44nY1xxvso_klS6WIHS3iqq7R2_lqE,1184
18
- pyqrack/stats/quantize_by_range.py,sha256=UM0_7jJDdQ7g30cR3UQAxkbzkqrmsy1oUfqg0h11FUY,2270
19
- pyqrack_complex128-1.78.3.dist-info/LICENSE,sha256=HxB-7SaWTuewAk1nz-3_3FUD6QhgX73kNT_taKVUTq8,1069
20
- pyqrack_complex128-1.78.3.dist-info/METADATA,sha256=M0lvm5jPx1sbFvZnNH6yW24rrXzW3jKJdLvNXqb2nDk,5890
21
- pyqrack_complex128-1.78.3.dist-info/WHEEL,sha256=BM14wstXunLvpW3hePaerLq1eZfN4NnYgS3CIXR7_AM,106
22
- pyqrack_complex128-1.78.3.dist-info/top_level.txt,sha256=YE_3q9JTGRLMilNg2tGP1y7uU-Dx8PDao2OhwoIbv8E,8
23
- pyqrack_complex128-1.78.3.dist-info/RECORD,,