pyqrack-complex128 1.78.3__py3-none-macosx_14_0_arm64.whl → 1.81.0__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.
Potentially problematic release.
This version of pyqrack-complex128 might be problematic. Click here for more details.
- pyqrack/__init__.py +0 -1
- pyqrack/qrack_ace_backend.py +16 -46
- pyqrack/qrack_circuit.py +2 -6
- pyqrack/qrack_neuron.py +43 -36
- pyqrack/qrack_neuron_torch_layer.py +132 -235
- pyqrack/qrack_simulator.py +82 -98
- pyqrack/qrack_system/qrack_lib/{libqrack_pinvoke.9.34.5.dylib → libqrack_pinvoke.10.0.0.dylib} +0 -0
- pyqrack/qrack_system/qrack_lib/libqrack_pinvoke.dylib +0 -0
- pyqrack/qrack_system/qrack_system.py +68 -59
- pyqrack/stats/load_quantized_data.py +1 -3
- pyqrack/stats/quantize_by_range.py +2 -6
- {pyqrack_complex128-1.78.3.dist-info → pyqrack_complex128-1.81.0.dist-info}/METADATA +3 -1
- pyqrack_complex128-1.81.0.dist-info/RECORD +23 -0
- pyqrack_complex128-1.78.3.dist-info/RECORD +0 -23
- {pyqrack_complex128-1.78.3.dist-info → pyqrack_complex128-1.81.0.dist-info}/LICENSE +0 -0
- {pyqrack_complex128-1.78.3.dist-info → pyqrack_complex128-1.81.0.dist-info}/WHEEL +0 -0
- {pyqrack_complex128-1.78.3.dist-info → pyqrack_complex128-1.81.0.dist-info}/top_level.txt +0 -0
pyqrack/__init__.py
CHANGED
pyqrack/qrack_ace_backend.py
CHANGED
|
@@ -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
|
-
|
|
1267
|
-
)
|
|
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
|
@@ -31,7 +31,9 @@ class QrackNeuron:
|
|
|
31
31
|
simulator(QrackSimulator): Simulator instance for all synaptic clefts of the neuron
|
|
32
32
|
controls(list(int)): Indices of all "control" qubits, for neuron input
|
|
33
33
|
target(int): Index of "target" qubit, for neuron output
|
|
34
|
-
|
|
34
|
+
activation_fn(NeuronActivationFn): Activation function choice
|
|
35
|
+
alpha(float): Activation function parameter, if required
|
|
36
|
+
angles(list[ctypes.c_float]): (or c_double) Memory for neuron prediction angles
|
|
35
37
|
"""
|
|
36
38
|
|
|
37
39
|
def _get_error(self):
|
|
@@ -48,7 +50,6 @@ class QrackNeuron:
|
|
|
48
50
|
target,
|
|
49
51
|
activation_fn=NeuronActivationFn.Sigmoid,
|
|
50
52
|
alpha=1.0,
|
|
51
|
-
tolerance=sys.float_info.epsilon,
|
|
52
53
|
_init=True,
|
|
53
54
|
):
|
|
54
55
|
self.simulator = simulator
|
|
@@ -56,7 +57,7 @@ class QrackNeuron:
|
|
|
56
57
|
self.target = target
|
|
57
58
|
self.activation_fn = activation_fn
|
|
58
59
|
self.alpha = alpha
|
|
59
|
-
self.
|
|
60
|
+
self.angles = QrackNeuron._real1_byref([0.0] * (1 << len(controls)))
|
|
60
61
|
|
|
61
62
|
if not _init:
|
|
62
63
|
return
|
|
@@ -66,9 +67,6 @@ class QrackNeuron:
|
|
|
66
67
|
len(controls),
|
|
67
68
|
QrackNeuron._ulonglong_byref(controls),
|
|
68
69
|
target,
|
|
69
|
-
activation_fn,
|
|
70
|
-
alpha,
|
|
71
|
-
tolerance,
|
|
72
70
|
)
|
|
73
71
|
|
|
74
72
|
self._throw_if_error()
|
|
@@ -91,11 +89,9 @@ class QrackNeuron:
|
|
|
91
89
|
self.simulator,
|
|
92
90
|
self.controls,
|
|
93
91
|
self.target,
|
|
94
|
-
self.activation_fn,
|
|
95
|
-
self.alpha,
|
|
96
|
-
self.tolerance,
|
|
97
92
|
)
|
|
98
|
-
|
|
93
|
+
result.nid = Qrack.qrack_lib.clone_qneuron(self.simulator.sid)
|
|
94
|
+
result.angles = self.angles[:]
|
|
99
95
|
self._throw_if_error()
|
|
100
96
|
return result
|
|
101
97
|
|
|
@@ -110,6 +106,27 @@ class QrackNeuron:
|
|
|
110
106
|
return (ctypes.c_float * len(a))(*a)
|
|
111
107
|
return (ctypes.c_double * len(a))(*a)
|
|
112
108
|
|
|
109
|
+
def set_simulator(self, s):
|
|
110
|
+
"""Set the neuron simulator
|
|
111
|
+
|
|
112
|
+
Set the simulator used by this neuron
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
s(QrackSimulator): The simulator to use
|
|
116
|
+
|
|
117
|
+
Raises:
|
|
118
|
+
RuntimeError: QrackSimulator raised an exception.
|
|
119
|
+
"""
|
|
120
|
+
Qrack.qrack_lib.set_qneuron_sim(
|
|
121
|
+
self.nid,
|
|
122
|
+
s.sid,
|
|
123
|
+
len(self.controls),
|
|
124
|
+
QrackNeuron._ulonglong_byref(self.controls),
|
|
125
|
+
self.target,
|
|
126
|
+
)
|
|
127
|
+
self._throw_if_error()
|
|
128
|
+
self.simulator = s
|
|
129
|
+
|
|
113
130
|
def set_angles(self, a):
|
|
114
131
|
"""Directly sets the neuron parameters.
|
|
115
132
|
|
|
@@ -127,8 +144,7 @@ class QrackNeuron:
|
|
|
127
144
|
raise ValueError(
|
|
128
145
|
"Angles 'a' in QrackNeuron.set_angles() must contain at least (2 ** len(self.controls)) elements."
|
|
129
146
|
)
|
|
130
|
-
|
|
131
|
-
self._throw_if_error()
|
|
147
|
+
self.angles = QrackNeuron._real1_byref(a)
|
|
132
148
|
|
|
133
149
|
def get_angles(self):
|
|
134
150
|
"""Directly gets the neuron parameters.
|
|
@@ -139,10 +155,7 @@ class QrackNeuron:
|
|
|
139
155
|
Raises:
|
|
140
156
|
RuntimeError: QrackNeuron C++ library raised an exception.
|
|
141
157
|
"""
|
|
142
|
-
|
|
143
|
-
Qrack.qrack_lib.get_qneuron_angles(self.nid, ket)
|
|
144
|
-
self._throw_if_error()
|
|
145
|
-
return list(ket)
|
|
158
|
+
return list(self.angles)
|
|
146
159
|
|
|
147
160
|
def set_alpha(self, a):
|
|
148
161
|
"""Set the neuron 'alpha' parameter.
|
|
@@ -151,13 +164,8 @@ class QrackNeuron:
|
|
|
151
164
|
parameter that is applied as a power to its angles, before
|
|
152
165
|
learning and prediction. This makes the activation function
|
|
153
166
|
sharper (or less sharp).
|
|
154
|
-
|
|
155
|
-
Raises:
|
|
156
|
-
RuntimeError: QrackNeuron C++ library raised an exception.
|
|
157
167
|
"""
|
|
158
168
|
self.alpha = a
|
|
159
|
-
Qrack.qrack_lib.set_qneuron_alpha(self.nid, a)
|
|
160
|
-
self._throw_if_error()
|
|
161
169
|
|
|
162
170
|
def set_activation_fn(self, f):
|
|
163
171
|
"""Sets the activation function of this QrackNeuron
|
|
@@ -165,13 +173,8 @@ class QrackNeuron:
|
|
|
165
173
|
Nonlinear activation functions can be important to neural net
|
|
166
174
|
applications, like DNN. The available activation functions are
|
|
167
175
|
enumerated in `NeuronActivationFn`.
|
|
168
|
-
|
|
169
|
-
Raises:
|
|
170
|
-
RuntimeError: QrackNeuron C++ library raised an exception.
|
|
171
176
|
"""
|
|
172
177
|
self.activation_fn = f
|
|
173
|
-
Qrack.qrack_lib.set_qneuron_activation_fn(self.nid, f)
|
|
174
|
-
self._throw_if_error()
|
|
175
178
|
|
|
176
179
|
def predict(self, e=True, r=True):
|
|
177
180
|
"""Predict based on training
|
|
@@ -190,7 +193,7 @@ class QrackNeuron:
|
|
|
190
193
|
Raises:
|
|
191
194
|
RuntimeError: QrackNeuron C++ library raised an exception.
|
|
192
195
|
"""
|
|
193
|
-
result = Qrack.qrack_lib.qneuron_predict(self.nid, e, r)
|
|
196
|
+
result = Qrack.qrack_lib.qneuron_predict(self.nid, self.angles, e, r, self.activation_fn, self.alpha)
|
|
194
197
|
self._throw_if_error()
|
|
195
198
|
return result
|
|
196
199
|
|
|
@@ -206,7 +209,7 @@ class QrackNeuron:
|
|
|
206
209
|
Raises:
|
|
207
210
|
RuntimeError: QrackNeuron C++ library raised an exception.
|
|
208
211
|
"""
|
|
209
|
-
result = Qrack.qrack_lib.qneuron_unpredict(self.nid, e)
|
|
212
|
+
result = Qrack.qrack_lib.qneuron_unpredict(self.nid, self.angles, e, self.activation_fn, self.alpha)
|
|
210
213
|
self._throw_if_error()
|
|
211
214
|
return result
|
|
212
215
|
|
|
@@ -222,7 +225,7 @@ class QrackNeuron:
|
|
|
222
225
|
Raises:
|
|
223
226
|
RuntimeError: QrackNeuron C++ library raised an exception.
|
|
224
227
|
"""
|
|
225
|
-
Qrack.qrack_lib.qneuron_learn_cycle(self.nid, e)
|
|
228
|
+
Qrack.qrack_lib.qneuron_learn_cycle(self.nid, self.angles, e, self.activation_fn, self.alpha)
|
|
226
229
|
self._throw_if_error()
|
|
227
230
|
|
|
228
231
|
def learn(self, eta, e=True, r=True):
|
|
@@ -241,7 +244,7 @@ class QrackNeuron:
|
|
|
241
244
|
Raises:
|
|
242
245
|
RuntimeError: QrackNeuron C++ library raised an exception.
|
|
243
246
|
"""
|
|
244
|
-
Qrack.qrack_lib.qneuron_learn(self.nid, eta, e, r)
|
|
247
|
+
Qrack.qrack_lib.qneuron_learn(self.nid, self.angles, eta, e, r, self.activation_fn, self.alpha)
|
|
245
248
|
self._throw_if_error()
|
|
246
249
|
|
|
247
250
|
def learn_permutation(self, eta, e=True, r=True):
|
|
@@ -260,12 +263,12 @@ class QrackNeuron:
|
|
|
260
263
|
Raises:
|
|
261
264
|
RuntimeError: QrackNeuron C++ library raised an exception.
|
|
262
265
|
"""
|
|
263
|
-
Qrack.qrack_lib.qneuron_learn_permutation(self.nid, eta, e, r)
|
|
266
|
+
Qrack.qrack_lib.qneuron_learn_permutation(self.nid, self.angles, eta, e, r, self.activation_fn, self.alpha)
|
|
264
267
|
self._throw_if_error()
|
|
265
268
|
|
|
266
269
|
@staticmethod
|
|
267
270
|
def quantile_bounds(vec, bits):
|
|
268
|
-
"""
|
|
271
|
+
"""Calculate vector quantile bounds
|
|
269
272
|
|
|
270
273
|
This is a static helper method to calculate the quantile
|
|
271
274
|
bounds of 2 ** bits worth of quantiles.
|
|
@@ -283,11 +286,15 @@ class QrackNeuron:
|
|
|
283
286
|
n = len(vec)
|
|
284
287
|
vec_sorted = sorted(vec)
|
|
285
288
|
|
|
286
|
-
return
|
|
289
|
+
return (
|
|
290
|
+
[vec_sorted[0]]
|
|
291
|
+
+ [vec_sorted[(k * n) // bins] for k in range(1, bins)]
|
|
292
|
+
+ [vec_sorted[-1]]
|
|
293
|
+
)
|
|
287
294
|
|
|
288
295
|
@staticmethod
|
|
289
296
|
def discretize(vec, bounds):
|
|
290
|
-
"""
|
|
297
|
+
"""Discretize vector by quantile bounds
|
|
291
298
|
|
|
292
299
|
This is a static helper method to discretize a numerical
|
|
293
300
|
vector according to quantile bounds calculated by the
|
|
@@ -317,7 +324,7 @@ class QrackNeuron:
|
|
|
317
324
|
|
|
318
325
|
@staticmethod
|
|
319
326
|
def flatten_and_transpose(arr):
|
|
320
|
-
"""
|
|
327
|
+
"""Flatten and transpose feature matrix
|
|
321
328
|
|
|
322
329
|
This is a static helper method to convert a multi-feature
|
|
323
330
|
bit-row matrix to an observation-row matrix with flat
|
|
@@ -333,7 +340,7 @@ class QrackNeuron:
|
|
|
333
340
|
|
|
334
341
|
@staticmethod
|
|
335
342
|
def bin_endpoints_average(bounds):
|
|
336
|
-
"""
|
|
343
|
+
"""Bin endpoints average
|
|
337
344
|
|
|
338
345
|
This is a static helper method that accepts the output
|
|
339
346
|
bins from quantile_bounds() and returns the average points
|