softopt 0.6.2__tar.gz → 0.6.4__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.
- {softopt-0.6.2/softopt.egg-info → softopt-0.6.4}/PKG-INFO +21 -1
- {softopt-0.6.2 → softopt-0.6.4}/README.md +20 -0
- {softopt-0.6.2 → softopt-0.6.4}/pyproject.toml +1 -1
- {softopt-0.6.2 → softopt-0.6.4}/softopt/__init__.py +1 -1
- {softopt-0.6.2 → softopt-0.6.4}/softopt/compile.py +27 -0
- {softopt-0.6.2 → softopt-0.6.4}/softopt/numpy_optimizer.py +28 -11
- {softopt-0.6.2 → softopt-0.6.4/softopt.egg-info}/PKG-INFO +21 -1
- {softopt-0.6.2 → softopt-0.6.4}/softopt.egg-info/SOURCES.txt +1 -0
- softopt-0.6.4/tests/test_exact_d2.py +47 -0
- {softopt-0.6.2 → softopt-0.6.4}/LICENSE +0 -0
- {softopt-0.6.2 → softopt-0.6.4}/setup.cfg +0 -0
- {softopt-0.6.2 → softopt-0.6.4}/softopt/soft_number.py +0 -0
- {softopt-0.6.2 → softopt-0.6.4}/softopt/torch_optimizer.py +0 -0
- {softopt-0.6.2 → softopt-0.6.4}/softopt.egg-info/dependency_links.txt +0 -0
- {softopt-0.6.2 → softopt-0.6.4}/softopt.egg-info/requires.txt +0 -0
- {softopt-0.6.2 → softopt-0.6.4}/softopt.egg-info/top_level.txt +0 -0
- {softopt-0.6.2 → softopt-0.6.4}/tests/test_basic.py +0 -0
- {softopt-0.6.2 → softopt-0.6.4}/tests/test_causal.py +0 -0
- {softopt-0.6.2 → softopt-0.6.4}/tests/test_compile.py +0 -0
- {softopt-0.6.2 → softopt-0.6.4}/tests/test_measured_slope.py +0 -0
- {softopt-0.6.2 → softopt-0.6.4}/tests/test_soft_number.py +0 -0
- {softopt-0.6.2 → softopt-0.6.4}/tests/test_torch.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: softopt
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
4
4
|
Summary: A standalone optimizer for problems with a known computation graph, built on Klein–Maimon soft-number calculus
|
|
5
5
|
Author: Ido Angel
|
|
6
6
|
License: MIT
|
|
@@ -120,8 +120,18 @@ Your model needs to be built from arithmetic and the elementary functions the so
|
|
|
120
120
|
|
|
121
121
|
The one thing to avoid is converting a traced value back to a plain number mid-model — `float(x)`, or `np.array(params)` on the parameter list, both silently discard the derivative. `soft_compile`'s verification catches this and says so.
|
|
122
122
|
|
|
123
|
+
**Parameter scale.** SoftOpt's correction moves each parameter by at most 0.5 per step, a natural size for angles and other parameters of order 1. If your parameters live on a very different scale (pixel offsets of 0.03, prices in the thousands), rescale them to roughly ±1, or set the bound to match: `SoftOpt(n, g_delta, lr=..., newton_lo=-b, newton_hi=b)`.
|
|
124
|
+
|
|
123
125
|
If you'd rather write the derivative function yourself — because your model lives in a simulator SoftOpt can't trace, or because you want the speed of a hand-tuned implementation — pass any `g_delta(theta, delta)` that returns the exact directional derivative, and skip `soft_compile` entirely.
|
|
124
126
|
|
|
127
|
+
### Exact curvature (new in 0.6.4)
|
|
128
|
+
|
|
129
|
+
The Newton correction needs two numbers along the probe direction: the slope D1 and the curvature D2. Since 0.6.4 both come from **one pass of your model on nested soft numbers**, with no step size. Each parameter enters as `θ + δ·ε₁ + δ·ε₂`, where each soft axis obeys the book's `ε² = 0`; the mixed `ε₁ε₂` part of the result is D2 exactly (Lemma 6.1 applied twice). `soft_compile` sets this up for you, and `SoftOpt` uses it automatically.
|
|
130
|
+
|
|
131
|
+
A hand-written `g_delta` can opt in the same way by carrying an attribute `g_delta.d1_d2 = lambda theta, delta: (D1, D2)`. Without it, D2 is a central finite difference of your exact `g_delta` (`h_fd`, the 0.6.3 behaviour, unchanged). `exact_d2=False` forces the finite difference.
|
|
132
|
+
|
|
133
|
+
On the H4 benchmark (FakeFez, 10 seeds) the exact-curvature engine (`benchmarks/vqe/soft_su2_exact.py`, `h4_exact_d2.py`) gives the same result as the published one: 400 vs 409 mHa at step 100 (p=0.56), both 10/10 against Adam with 67% of the gap closed. `examples/soft_tensor_demo.py` shows that the same nested construction on tensors reproduces SoftOptTorch's nested `torch.func.jvp` to rounding (4e-16).
|
|
134
|
+
|
|
125
135
|
## Where the slope comes from: `slope="model"` or `slope="measured"`
|
|
126
136
|
|
|
127
137
|
Every correction is a Newton step, `-slope / curvature`, taken along one direction. The curvature always comes from your model. The slope has two possible sources:
|
|
@@ -204,6 +214,16 @@ All results below use IBM's `FakeFez` noise model via Qiskit + Aer, or realistic
|
|
|
204
214
|
|
|
205
215
|
Both run SoftOpt with its default settings on IBM's `FakeFez` noise model: `benchmarks/vqe/h4_real_molecule.py`, `benchmarks/vqe/h2_vs_cobyla.py`.
|
|
206
216
|
|
|
217
|
+
**Quantum machine learning**
|
|
218
|
+
|
|
219
|
+
| Test | Baseline | Result | Win rate |
|
|
220
|
+
|---|---|---|---|
|
|
221
|
+
| Variational quantum classifier (Iris, 4 qubits, 24 parameters), same budget of 300 readings, 20 seeds | Adam | 25% lower training loss; test accuracy on the device 70.0% vs 66.3% | 20/20 (loss), 15/20 + 3 ties (accuracy) |
|
|
222
|
+
| Variational quantum classifier (breast cancer, 8 qubits, 48 parameters), same budget, 10 seeds | Adam | 6% lower training loss; test accuracy on the device 90.0% vs 88.3% | 10/10 (loss), 7/10 + 2 ties (accuracy) |
|
|
223
|
+
| Same two classifiers | COBYLA | lower training loss (Iris 16/20, cancer 10/10); test accuracy tied | 16/20, 10/10 (loss) |
|
|
224
|
+
|
|
225
|
+
`benchmarks/qml/vqc.py --dataset iris|cancer`
|
|
226
|
+
|
|
207
227
|
**Quantum control**
|
|
208
228
|
|
|
209
229
|
| Domain | Improvement | Win rate |
|
|
@@ -94,8 +94,18 @@ Your model needs to be built from arithmetic and the elementary functions the so
|
|
|
94
94
|
|
|
95
95
|
The one thing to avoid is converting a traced value back to a plain number mid-model — `float(x)`, or `np.array(params)` on the parameter list, both silently discard the derivative. `soft_compile`'s verification catches this and says so.
|
|
96
96
|
|
|
97
|
+
**Parameter scale.** SoftOpt's correction moves each parameter by at most 0.5 per step, a natural size for angles and other parameters of order 1. If your parameters live on a very different scale (pixel offsets of 0.03, prices in the thousands), rescale them to roughly ±1, or set the bound to match: `SoftOpt(n, g_delta, lr=..., newton_lo=-b, newton_hi=b)`.
|
|
98
|
+
|
|
97
99
|
If you'd rather write the derivative function yourself — because your model lives in a simulator SoftOpt can't trace, or because you want the speed of a hand-tuned implementation — pass any `g_delta(theta, delta)` that returns the exact directional derivative, and skip `soft_compile` entirely.
|
|
98
100
|
|
|
101
|
+
### Exact curvature (new in 0.6.4)
|
|
102
|
+
|
|
103
|
+
The Newton correction needs two numbers along the probe direction: the slope D1 and the curvature D2. Since 0.6.4 both come from **one pass of your model on nested soft numbers**, with no step size. Each parameter enters as `θ + δ·ε₁ + δ·ε₂`, where each soft axis obeys the book's `ε² = 0`; the mixed `ε₁ε₂` part of the result is D2 exactly (Lemma 6.1 applied twice). `soft_compile` sets this up for you, and `SoftOpt` uses it automatically.
|
|
104
|
+
|
|
105
|
+
A hand-written `g_delta` can opt in the same way by carrying an attribute `g_delta.d1_d2 = lambda theta, delta: (D1, D2)`. Without it, D2 is a central finite difference of your exact `g_delta` (`h_fd`, the 0.6.3 behaviour, unchanged). `exact_d2=False` forces the finite difference.
|
|
106
|
+
|
|
107
|
+
On the H4 benchmark (FakeFez, 10 seeds) the exact-curvature engine (`benchmarks/vqe/soft_su2_exact.py`, `h4_exact_d2.py`) gives the same result as the published one: 400 vs 409 mHa at step 100 (p=0.56), both 10/10 against Adam with 67% of the gap closed. `examples/soft_tensor_demo.py` shows that the same nested construction on tensors reproduces SoftOptTorch's nested `torch.func.jvp` to rounding (4e-16).
|
|
108
|
+
|
|
99
109
|
## Where the slope comes from: `slope="model"` or `slope="measured"`
|
|
100
110
|
|
|
101
111
|
Every correction is a Newton step, `-slope / curvature`, taken along one direction. The curvature always comes from your model. The slope has two possible sources:
|
|
@@ -178,6 +188,16 @@ All results below use IBM's `FakeFez` noise model via Qiskit + Aer, or realistic
|
|
|
178
188
|
|
|
179
189
|
Both run SoftOpt with its default settings on IBM's `FakeFez` noise model: `benchmarks/vqe/h4_real_molecule.py`, `benchmarks/vqe/h2_vs_cobyla.py`.
|
|
180
190
|
|
|
191
|
+
**Quantum machine learning**
|
|
192
|
+
|
|
193
|
+
| Test | Baseline | Result | Win rate |
|
|
194
|
+
|---|---|---|---|
|
|
195
|
+
| Variational quantum classifier (Iris, 4 qubits, 24 parameters), same budget of 300 readings, 20 seeds | Adam | 25% lower training loss; test accuracy on the device 70.0% vs 66.3% | 20/20 (loss), 15/20 + 3 ties (accuracy) |
|
|
196
|
+
| Variational quantum classifier (breast cancer, 8 qubits, 48 parameters), same budget, 10 seeds | Adam | 6% lower training loss; test accuracy on the device 90.0% vs 88.3% | 10/10 (loss), 7/10 + 2 ties (accuracy) |
|
|
197
|
+
| Same two classifiers | COBYLA | lower training loss (Iris 16/20, cancer 10/10); test accuracy tied | 16/20, 10/10 (loss) |
|
|
198
|
+
|
|
199
|
+
`benchmarks/qml/vqc.py --dataset iris|cancer`
|
|
200
|
+
|
|
181
201
|
**Quantum control**
|
|
182
202
|
|
|
183
203
|
| Domain | Improvement | Win rate |
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "softopt"
|
|
7
|
-
version = "0.6.
|
|
7
|
+
version = "0.6.4"
|
|
8
8
|
description = "A standalone optimizer for problems with a known computation graph, built on Klein–Maimon soft-number calculus"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -61,6 +61,33 @@ def soft_compile(model_fn, n_params=None, verify=True, verify_tol=1e-5):
|
|
|
61
61
|
)
|
|
62
62
|
return result.a
|
|
63
63
|
|
|
64
|
+
def d1_d2(theta, delta):
|
|
65
|
+
"""Exact D1 = f'(theta).delta and D2 = delta^T f''(theta) delta from ONE pass of the model
|
|
66
|
+
on nested soft numbers: each parameter is t + eps2*d + eps1*d, built as
|
|
67
|
+
SoftNumber(SoftNumber(0, d), SoftNumber(d, t)). Each axis obeys the book's eps^2 = 0; the
|
|
68
|
+
mixed eps1*eps2 component of the result is D2 (Lemma 6.1 applied twice). No step size."""
|
|
69
|
+
soft = [SoftNumber(SoftNumber(0.0, float(d)), SoftNumber(float(d), float(t)))
|
|
70
|
+
for t, d in zip(theta, delta)]
|
|
71
|
+
r = model_fn(soft)
|
|
72
|
+
if not (isinstance(r, SoftNumber) and isinstance(r.a, SoftNumber)):
|
|
73
|
+
raise TypeError("the model did not propagate nested soft numbers")
|
|
74
|
+
D1, D2 = float(r.a.b), float(r.a.a)
|
|
75
|
+
if not (np.isfinite(D1) and np.isfinite(D2)):
|
|
76
|
+
raise FloatingPointError(f"non-finite slope/curvature ({D1}, {D2}) at this point")
|
|
77
|
+
return D1, D2
|
|
78
|
+
|
|
79
|
+
# Attach the exact second-order path; SoftOpt uses it automatically when present.
|
|
80
|
+
# If the model cannot carry nested soft numbers, fall back silently to the first-order g_delta.
|
|
81
|
+
try:
|
|
82
|
+
_t = np.random.default_rng(1).normal(0.0, 0.5, n_params) if n_params else None
|
|
83
|
+
if _t is not None:
|
|
84
|
+
d1, _ = d1_d2(_t, np.ones(n_params))
|
|
85
|
+
if abs(d1 - g_delta(_t, np.ones(n_params))) > 1e-9 * max(1.0, abs(d1)):
|
|
86
|
+
raise ValueError
|
|
87
|
+
g_delta.d1_d2 = d1_d2
|
|
88
|
+
except Exception:
|
|
89
|
+
pass
|
|
90
|
+
|
|
64
91
|
if verify:
|
|
65
92
|
if n_params is None:
|
|
66
93
|
raise ValueError("pass n_params to verify the compiled model, "
|
|
@@ -8,9 +8,14 @@ known model (circuit, projection, physical law). `soft_compile` generates
|
|
|
8
8
|
this for you from a model written in ordinary Python.
|
|
9
9
|
|
|
10
10
|
The correction is a bounded Newton step, t* = -D1 / D2 along a direction delta.
|
|
11
|
-
D2 (the curvature) always comes from your model
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
D2 (the curvature) always comes from your model. When g_delta comes from
|
|
12
|
+
`soft_compile` (or carries a `d1_d2(theta, delta) -> (D1, D2)` attribute), D2 is
|
|
13
|
+
EXACT: one pass of the model on nested soft numbers (two soft axes, each with
|
|
14
|
+
eps^2 = 0; the mixed term is D2) -- no step size. For a hand-written g_delta
|
|
15
|
+
without that attribute, D2 is a central finite difference OF the exact
|
|
16
|
+
soft-number derivative, controlled by h_fd (the 0.6.3 behaviour).
|
|
17
|
+
|
|
18
|
+
D1 (the slope) has two possible sources, set by `slope`:
|
|
14
19
|
|
|
15
20
|
slope="model" (default) D1 is the model's exact directional derivative
|
|
16
21
|
along a fresh random direction. Robust to very noisy
|
|
@@ -33,7 +38,7 @@ import numpy as np
|
|
|
33
38
|
class SoftOpt:
|
|
34
39
|
def __init__(self, n_params, g_delta, lr=0.02, betas=(0.9, 0.999), eps=1e-8,
|
|
35
40
|
newton_lo=-0.5, newton_hi=0.5, eta_fallback=0.3,
|
|
36
|
-
h_fd=1e-4, seed=None, slope="model", _test_frozen_constant=1.0):
|
|
41
|
+
h_fd=1e-4, seed=None, slope="model", exact_d2="auto", _test_frozen_constant=1.0):
|
|
37
42
|
if slope not in ("model", "measured"):
|
|
38
43
|
raise ValueError(f'slope must be "model" or "measured", got {slope!r}')
|
|
39
44
|
self.slope = slope
|
|
@@ -45,6 +50,11 @@ class SoftOpt:
|
|
|
45
50
|
self.newton_hi = newton_hi
|
|
46
51
|
self.eta_fallback = eta_fallback
|
|
47
52
|
self.h_fd = h_fd
|
|
53
|
+
if exact_d2 not in ("auto", True, False):
|
|
54
|
+
raise ValueError('exact_d2 must be "auto", True or False')
|
|
55
|
+
self._d1_d2 = getattr(g_delta, "d1_d2", None) if exact_d2 else None
|
|
56
|
+
if exact_d2 is True and self._d1_d2 is None:
|
|
57
|
+
raise ValueError("exact_d2=True needs a g_delta with a d1_d2 attribute (e.g. from soft_compile)")
|
|
48
58
|
self.m = None
|
|
49
59
|
self.v = None
|
|
50
60
|
self.t = 0
|
|
@@ -113,13 +123,17 @@ class SoftOpt:
|
|
|
113
123
|
if self._ablation_rng is None:
|
|
114
124
|
self._ablation_rng = np.random.default_rng(
|
|
115
125
|
self.rng.integers(0, 2**63 - 1))
|
|
126
|
+
if self._d1_d2 is not None: # exact: nested soft numbers, one pass
|
|
127
|
+
D1_model, D2_real = self._d1_d2(theta_after_adam, delta)
|
|
128
|
+
else: # finite difference of the exact g_delta
|
|
129
|
+
D1_model = None
|
|
130
|
+
gp = self.g_delta(theta_after_adam + self.h_fd * delta, delta)
|
|
131
|
+
gm = self.g_delta(theta_after_adam - self.h_fd * delta, delta)
|
|
132
|
+
D2_real = (gp - gm) / (2 * self.h_fd)
|
|
116
133
|
if self.slope == "measured":
|
|
117
134
|
D1 = float(measured_slope) # from the real system
|
|
118
135
|
else:
|
|
119
|
-
D1 = self.g_delta(theta_after_adam, delta)
|
|
120
|
-
gp = self.g_delta(theta_after_adam + self.h_fd * delta, delta)
|
|
121
|
-
gm = self.g_delta(theta_after_adam - self.h_fd * delta, delta)
|
|
122
|
-
D2_real = (gp - gm) / (2 * self.h_fd)
|
|
136
|
+
D1 = D1_model if D1_model is not None else self.g_delta(theta_after_adam, delta)
|
|
123
137
|
|
|
124
138
|
if _test_magnitude_source in (None, 'real'):
|
|
125
139
|
D2_used = D2_real
|
|
@@ -129,9 +143,12 @@ class SoftOpt:
|
|
|
129
143
|
theta_f = _test_foreign_sampler(self._ablation_rng)
|
|
130
144
|
delta_f = (self._ablation_rng.choice([-1.0, 1.0], size=n)
|
|
131
145
|
if _test_magnitude_source == 'foreign_point_and_dir' else delta)
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
146
|
+
if self._d1_d2 is not None:
|
|
147
|
+
D2_used = abs(self._d1_d2(theta_f, delta_f)[1])
|
|
148
|
+
else:
|
|
149
|
+
gp_f = self.g_delta(theta_f + self.h_fd * delta_f, delta_f)
|
|
150
|
+
gm_f = self.g_delta(theta_f - self.h_fd * delta_f, delta_f)
|
|
151
|
+
D2_used = abs((gp_f - gm_f) / (2 * self.h_fd))
|
|
135
152
|
else:
|
|
136
153
|
raise ValueError(_test_magnitude_source)
|
|
137
154
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: softopt
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.4
|
|
4
4
|
Summary: A standalone optimizer for problems with a known computation graph, built on Klein–Maimon soft-number calculus
|
|
5
5
|
Author: Ido Angel
|
|
6
6
|
License: MIT
|
|
@@ -120,8 +120,18 @@ Your model needs to be built from arithmetic and the elementary functions the so
|
|
|
120
120
|
|
|
121
121
|
The one thing to avoid is converting a traced value back to a plain number mid-model — `float(x)`, or `np.array(params)` on the parameter list, both silently discard the derivative. `soft_compile`'s verification catches this and says so.
|
|
122
122
|
|
|
123
|
+
**Parameter scale.** SoftOpt's correction moves each parameter by at most 0.5 per step, a natural size for angles and other parameters of order 1. If your parameters live on a very different scale (pixel offsets of 0.03, prices in the thousands), rescale them to roughly ±1, or set the bound to match: `SoftOpt(n, g_delta, lr=..., newton_lo=-b, newton_hi=b)`.
|
|
124
|
+
|
|
123
125
|
If you'd rather write the derivative function yourself — because your model lives in a simulator SoftOpt can't trace, or because you want the speed of a hand-tuned implementation — pass any `g_delta(theta, delta)` that returns the exact directional derivative, and skip `soft_compile` entirely.
|
|
124
126
|
|
|
127
|
+
### Exact curvature (new in 0.6.4)
|
|
128
|
+
|
|
129
|
+
The Newton correction needs two numbers along the probe direction: the slope D1 and the curvature D2. Since 0.6.4 both come from **one pass of your model on nested soft numbers**, with no step size. Each parameter enters as `θ + δ·ε₁ + δ·ε₂`, where each soft axis obeys the book's `ε² = 0`; the mixed `ε₁ε₂` part of the result is D2 exactly (Lemma 6.1 applied twice). `soft_compile` sets this up for you, and `SoftOpt` uses it automatically.
|
|
130
|
+
|
|
131
|
+
A hand-written `g_delta` can opt in the same way by carrying an attribute `g_delta.d1_d2 = lambda theta, delta: (D1, D2)`. Without it, D2 is a central finite difference of your exact `g_delta` (`h_fd`, the 0.6.3 behaviour, unchanged). `exact_d2=False` forces the finite difference.
|
|
132
|
+
|
|
133
|
+
On the H4 benchmark (FakeFez, 10 seeds) the exact-curvature engine (`benchmarks/vqe/soft_su2_exact.py`, `h4_exact_d2.py`) gives the same result as the published one: 400 vs 409 mHa at step 100 (p=0.56), both 10/10 against Adam with 67% of the gap closed. `examples/soft_tensor_demo.py` shows that the same nested construction on tensors reproduces SoftOptTorch's nested `torch.func.jvp` to rounding (4e-16).
|
|
134
|
+
|
|
125
135
|
## Where the slope comes from: `slope="model"` or `slope="measured"`
|
|
126
136
|
|
|
127
137
|
Every correction is a Newton step, `-slope / curvature`, taken along one direction. The curvature always comes from your model. The slope has two possible sources:
|
|
@@ -204,6 +214,16 @@ All results below use IBM's `FakeFez` noise model via Qiskit + Aer, or realistic
|
|
|
204
214
|
|
|
205
215
|
Both run SoftOpt with its default settings on IBM's `FakeFez` noise model: `benchmarks/vqe/h4_real_molecule.py`, `benchmarks/vqe/h2_vs_cobyla.py`.
|
|
206
216
|
|
|
217
|
+
**Quantum machine learning**
|
|
218
|
+
|
|
219
|
+
| Test | Baseline | Result | Win rate |
|
|
220
|
+
|---|---|---|---|
|
|
221
|
+
| Variational quantum classifier (Iris, 4 qubits, 24 parameters), same budget of 300 readings, 20 seeds | Adam | 25% lower training loss; test accuracy on the device 70.0% vs 66.3% | 20/20 (loss), 15/20 + 3 ties (accuracy) |
|
|
222
|
+
| Variational quantum classifier (breast cancer, 8 qubits, 48 parameters), same budget, 10 seeds | Adam | 6% lower training loss; test accuracy on the device 90.0% vs 88.3% | 10/10 (loss), 7/10 + 2 ties (accuracy) |
|
|
223
|
+
| Same two classifiers | COBYLA | lower training loss (Iris 16/20, cancer 10/10); test accuracy tied | 16/20, 10/10 (loss) |
|
|
224
|
+
|
|
225
|
+
`benchmarks/qml/vqc.py --dataset iris|cancer`
|
|
226
|
+
|
|
207
227
|
**Quantum control**
|
|
208
228
|
|
|
209
229
|
| Domain | Improvement | Win rate |
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for the exact-curvature path (0.6.4): nested soft numbers give D2 with no step size, and a
|
|
3
|
+
hand-written g_delta without d1_d2 keeps the 0.6.3 finite-difference behaviour bit for bit.
|
|
4
|
+
"""
|
|
5
|
+
import numpy as np
|
|
6
|
+
from softopt import soft_compile, SoftOpt
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def _model(p):
|
|
10
|
+
x, y, z = p
|
|
11
|
+
return np.cos(x) * np.sin(y) + x * x * z + np.exp(0.3 * y) * z
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def test_soft_compile_attaches_exact_d1_d2():
|
|
15
|
+
g = soft_compile(_model, n_params=3)
|
|
16
|
+
assert hasattr(g, 'd1_d2')
|
|
17
|
+
rng = np.random.default_rng(0)
|
|
18
|
+
for _ in range(10):
|
|
19
|
+
t, d = rng.normal(0, 1, 3), rng.choice([-1.0, 1.0], 3)
|
|
20
|
+
D1, D2 = g.d1_d2(t, d)
|
|
21
|
+
assert np.isclose(D1, g(t, d), rtol=1e-12)
|
|
22
|
+
h = 1e-5
|
|
23
|
+
D2_fd = (g(t + h * d, d) - g(t - h * d, d)) / (2 * h)
|
|
24
|
+
assert np.isclose(D2, D2_fd, rtol=1e-6, atol=1e-8)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def test_hand_written_g_delta_unchanged():
|
|
28
|
+
g = soft_compile(_model, n_params=3)
|
|
29
|
+
hand = lambda t, d: g(t, d) # no d1_d2 attribute -> finite difference
|
|
30
|
+
a = SoftOpt(3, hand, lr=0.02, seed=1)
|
|
31
|
+
b = SoftOpt(3, g, lr=0.02, seed=1, exact_d2=False)
|
|
32
|
+
ta = tb = np.array([0.3, -0.2, 0.5])
|
|
33
|
+
for _ in range(20):
|
|
34
|
+
gr = np.array([g(ta, e) for e in np.eye(3)])
|
|
35
|
+
ta, tb = a.step(ta, gr), b.step(tb, gr)
|
|
36
|
+
assert np.array_equal(ta, tb)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def test_exact_and_fd_paths_agree():
|
|
40
|
+
g = soft_compile(_model, n_params=3)
|
|
41
|
+
a = SoftOpt(3, g, lr=0.02, seed=2) # exact (auto)
|
|
42
|
+
b = SoftOpt(3, g, lr=0.02, seed=2, exact_d2=False)
|
|
43
|
+
ta = tb = np.array([0.3, -0.2, 0.5])
|
|
44
|
+
for _ in range(50):
|
|
45
|
+
ta = a.step(ta, np.array([g(ta, e) for e in np.eye(3)]))
|
|
46
|
+
tb = b.step(tb, np.array([g(tb, e) for e in np.eye(3)]))
|
|
47
|
+
assert np.allclose(ta, tb, atol=1e-6)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|