pyqrack 1.29.0__py3-none-win_amd64.whl → 1.72.5__py3-none-win_amd64.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 +13 -6
- pyqrack/neuron_activation_fn.py +2 -2
- pyqrack/pauli.py +1 -1
- pyqrack/qrack_ace_backend.py +1544 -0
- pyqrack/qrack_circuit.py +212 -100
- pyqrack/qrack_neuron.py +27 -9
- pyqrack/qrack_neuron_torch_layer.py +170 -0
- pyqrack/qrack_simulator.py +870 -310
- pyqrack/qrack_stabilizer.py +58 -0
- pyqrack/qrack_system/qrack_cl_precompile/qrack_cl_precompile.exe +0 -0
- pyqrack/qrack_system/qrack_lib/qrack_pinvoke.dll +0 -0
- pyqrack/qrack_system/qrack_system.py +228 -135
- pyqrack/quimb_circuit_type.py +1 -1
- pyqrack/stats/__init__.py +6 -0
- pyqrack/stats/load_quantized_data.py +35 -0
- pyqrack/stats/quantize_by_range.py +56 -0
- {pyqrack-1.29.0.dist-info → pyqrack-1.72.5.dist-info}/LICENSE +1 -1
- pyqrack-1.72.5.dist-info/METADATA +82 -0
- pyqrack-1.72.5.dist-info/RECORD +22 -0
- {pyqrack-1.29.0.dist-info → pyqrack-1.72.5.dist-info}/WHEEL +1 -1
- pyqrack/util/__init__.py +0 -8
- pyqrack/util/convert_qiskit_circuit_to_qasm_experiment.py +0 -61
- pyqrack-1.29.0.dist-info/METADATA +0 -61
- pyqrack-1.29.0.dist-info/RECORD +0 -17
- {pyqrack-1.29.0.dist-info → pyqrack-1.72.5.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# (C) Daniel Strano and the Qrack contributors 2017-2025. All rights reserved.
|
|
2
|
+
#
|
|
3
|
+
# Initial draft by Elara (OpenAI custom GPT)
|
|
4
|
+
# Refined and architecturally clarified by Dan Strano
|
|
5
|
+
#
|
|
6
|
+
# Use of this source code is governed by an MIT-style license that can be
|
|
7
|
+
# found in the LICENSE file or at https://opensource.org/licenses/MIT.
|
|
8
|
+
|
|
9
|
+
_IS_TORCH_AVAILABLE = True
|
|
10
|
+
try:
|
|
11
|
+
import torch
|
|
12
|
+
import torch.nn as nn
|
|
13
|
+
from torch.autograd import Function
|
|
14
|
+
except ImportError:
|
|
15
|
+
_IS_TORCH_AVAILABLE = False
|
|
16
|
+
|
|
17
|
+
from .qrack_neuron import QrackNeuron
|
|
18
|
+
from .neuron_activation_fn import NeuronActivationFn
|
|
19
|
+
|
|
20
|
+
from itertools import chain, combinations
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# From https://stackoverflow.com/questions/1482308/how-to-get-all-subsets-of-a-set-powerset#answer-1482316
|
|
24
|
+
def powerset(iterable):
|
|
25
|
+
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3,) (1,2,3)"
|
|
26
|
+
s = list(iterable)
|
|
27
|
+
return chain.from_iterable(combinations(s, r) for r in range(len(s) + 1))
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class QrackTorchNeuron(nn.Module if _IS_TORCH_AVAILABLE else object):
|
|
31
|
+
"""Torch wrapper for QrackNeuron
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
neuron(QrackNeuron): QrackNeuron backing this torch wrapper
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
def __init__(self, neuron: QrackNeuron):
|
|
38
|
+
super().__init__()
|
|
39
|
+
self.neuron = neuron
|
|
40
|
+
|
|
41
|
+
def forward(self, x):
|
|
42
|
+
neuron = self.neuron
|
|
43
|
+
neuron.predict(True, False)
|
|
44
|
+
|
|
45
|
+
return neuron.simulator.prob(neuron.target)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class QrackNeuronFunction(Function if _IS_TORCH_AVAILABLE else object):
|
|
49
|
+
"""Static forward/backward/apply functions for QrackTorchNeuron"""
|
|
50
|
+
|
|
51
|
+
@staticmethod
|
|
52
|
+
def forward(ctx, neuron):
|
|
53
|
+
# Save for backward
|
|
54
|
+
ctx.neuron = neuron
|
|
55
|
+
|
|
56
|
+
init_prob = neuron.simulator.prob(neuron.target)
|
|
57
|
+
neuron.predict(True, False)
|
|
58
|
+
final_prob = neuron.simulator.prob(neuron.target)
|
|
59
|
+
ctx.delta = final_prob - init_prob
|
|
60
|
+
|
|
61
|
+
return (
|
|
62
|
+
torch.tensor([ctx.delta], dtype=torch.float32)
|
|
63
|
+
if _IS_TORCH_AVAILABLE
|
|
64
|
+
else ctx.delta
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def backward(ctx, grad_output):
|
|
69
|
+
neuron = ctx.neuron
|
|
70
|
+
|
|
71
|
+
pre_unpredict = neuron.simulator.prob(neuron.output_id)
|
|
72
|
+
neuron.unpredict()
|
|
73
|
+
post_unpredict = neuron.simulator.prob(neuron.output_id)
|
|
74
|
+
reverse_delta = pre_unpredict - post_unpredict
|
|
75
|
+
|
|
76
|
+
grad = reverse_delta - ctx.delta
|
|
77
|
+
|
|
78
|
+
return (
|
|
79
|
+
torch.tensor([grad], dtype=torch.float32) if _IS_TORCH_AVAILABLE else grad
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class QrackNeuronTorchLayer(nn.Module if _IS_TORCH_AVAILABLE else object):
|
|
84
|
+
"""Torch layer wrapper for QrackNeuron (with power set of neurons between inputs and outputs)"""
|
|
85
|
+
|
|
86
|
+
def __init__(
|
|
87
|
+
self,
|
|
88
|
+
simulator,
|
|
89
|
+
input_indices,
|
|
90
|
+
output_indices,
|
|
91
|
+
activation=int(NeuronActivationFn.Generalized_Logistic),
|
|
92
|
+
parameters=None,
|
|
93
|
+
):
|
|
94
|
+
"""
|
|
95
|
+
Initialize a QrackNeuron layer for PyTorch with a power set of neurons connecting inputs to outputs.
|
|
96
|
+
The inputs and outputs must take the form of discrete, binary features (loaded manually into the backing QrackSimulator)
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
sim (QrackSimulator): Simulator into which predictor features are loaded
|
|
100
|
+
input_indices (list[int]): List of input bits
|
|
101
|
+
output_indices (list[int]): List of output bits
|
|
102
|
+
activation (int): Integer corresponding to choice of activation function from NeuronActivationFn
|
|
103
|
+
parameters (list[float]): (Optional) Flat list of initial neuron parameters, corresponding to little-endian basis states of power set of input indices, repeated for each output index (with empty set being constant bias)
|
|
104
|
+
"""
|
|
105
|
+
super(QrackNeuronTorchLayer, self).__init__()
|
|
106
|
+
self.simulator = simulator
|
|
107
|
+
self.input_indices = input_indices
|
|
108
|
+
self.output_indices = output_indices
|
|
109
|
+
self.activation = NeuronActivationFn(activation)
|
|
110
|
+
self.fn = (
|
|
111
|
+
QrackNeuronFunction.apply
|
|
112
|
+
if _IS_TORCH_AVAILABLE
|
|
113
|
+
else lambda x: QrackNeuronFunction.forward(object(), x)
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
# Create neurons from all powerset input combinations, projecting to coherent output qubits
|
|
117
|
+
self.neurons = nn.ModuleList(
|
|
118
|
+
[
|
|
119
|
+
QrackTorchNeuron(
|
|
120
|
+
QrackNeuron(simulator, list(input_subset), output_id, activation)
|
|
121
|
+
)
|
|
122
|
+
for input_subset in powerset(input_indices)
|
|
123
|
+
for output_id in output_indices
|
|
124
|
+
]
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Set Qrack's internal parameters:
|
|
128
|
+
param_count = 0
|
|
129
|
+
for neuron_wrapper in self.neurons:
|
|
130
|
+
neuron = neuron_wrapper.neuron
|
|
131
|
+
p_count = 1 << len(neuron.controls)
|
|
132
|
+
neuron.set_angles(
|
|
133
|
+
parameters[param_count : (param_count + p_count + 1)]
|
|
134
|
+
if parameters
|
|
135
|
+
else ([0.0] * p_count)
|
|
136
|
+
)
|
|
137
|
+
param_count += p_count
|
|
138
|
+
|
|
139
|
+
self.weights = nn.ParameterList()
|
|
140
|
+
for pid in range(param_count):
|
|
141
|
+
self.weights.append(
|
|
142
|
+
nn.Parameter(torch.tensor(parameters[pid] if parameters else 0.0))
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
def forward(self, _):
|
|
146
|
+
# Assume quantum outputs should overwrite the simulator state
|
|
147
|
+
for output_id in self.output_indices:
|
|
148
|
+
if self.simulator.m(output_id):
|
|
149
|
+
self.simulator.x(output_id)
|
|
150
|
+
self.simulator.h(output_id)
|
|
151
|
+
|
|
152
|
+
# Set Qrack's internal parameters:
|
|
153
|
+
param_count = 0
|
|
154
|
+
for neuron_wrapper in self.neurons:
|
|
155
|
+
neuron = neuron_wrapper.neuron
|
|
156
|
+
p_count = 1 << len(neuron.controls)
|
|
157
|
+
angles = [
|
|
158
|
+
w.item() for w in self.weights[param_count : (param_count + p_count)]
|
|
159
|
+
]
|
|
160
|
+
neuron.set_angles(angles)
|
|
161
|
+
param_count += p_count
|
|
162
|
+
|
|
163
|
+
# Assume quantum inputs already loaded into simulator state
|
|
164
|
+
for neuron_wrapper in self.neurons:
|
|
165
|
+
self.fn(neuron_wrapper.neuron)
|
|
166
|
+
|
|
167
|
+
# These are classical views over quantum state; simulator still maintains full coherence
|
|
168
|
+
outputs = [self.simulator.prob(output_id) for output_id in self.output_indices]
|
|
169
|
+
|
|
170
|
+
return torch.tensor(outputs, dtype=torch.float32)
|