Skip to content

Commit 5749310

Browse files
The Inspiration Simplex: single working paper (construction, Alloy, signed compositions) (#292)
* Thinking Outside the Simplex: six semantics, two traps, two survivors - E15 bake-off: six negative-weight semantics under the ANTI_STRENGTH paired-ablation gate; caricature the sole selection-stage survivor - E16: full 20-pair caricature matrix (theory-uncorrelated) + magnitude ladder (restraint wins) - E17: search of the extended signed slice; champion (0.047, delta -0.134) reverses out of sample -> protocol gains an out-of-sample paired stage - E18: survival races; CMA-not-DE (65%, p=2e-12) and SA-not-PS (59%, p=1e-5) survive on weak hosts: negative weights repair weak hosts, spoil strong ones - Note rewritten around the complete arc; papers page blurb updated Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Merge into one working paper: The Inspiration Simplex Single working paper containing the construction, Alloy as the first worked example, the negative results, the global-optimization framing, and the signed-composition study (six semantics, two traps, two survivors, weak-host rule). Separate outside-simplex note retired; papers page, Alloy doc page and JSS citation updated. Further worked examples planned in later versions. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Twin-vs-Alloy claim made precise (54%/58% pairwise, discounts stated) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Succession match on fresh instances: Alloy holds Pre-registered head-to-head on the untouched demos at disguise seeds 5-9 (never used by any experiment): Alloy beats each caricature-prompt twin pairwise (57%/56%, p<=0.007), reversing the reused-suite edge and pricing the reuse. Twins keep the best mean ranks at the two smallest budgets. Paper updated with the verdict. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Second worked example: cache eviction (static blends fail, adaptive shares rediscover CAR) - cache_sim.py: six seeded trace families with the disguise analog (parameter randomization + key permutation), panel policies, and a reference ARC implementation - e21: static shares put every blend below every pure vertex despite ARC proving the interior rich; adaptive shares (one added instruction) yield a CLOCK-host artifact at 0.25 held-out vs ARC 0.19, implementing CAR's ghost-feedback design - E19/E20 fresh-instance matches: Alloy holds against the twins and the round-2 candidate - Paper gains the second-example section and abstract sentence Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Lint fix in e21b holdout harness Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Third worked example: distributional forecasting - forecast_sim.py: six seeded series families with affine disguise, panel of five, skaters laplace as reference champion; E22 static/adaptive rounds, holdout, novel families, sandwich/mixture couplings (e22*, e24) - Pre-registered prediction confirmed (adaptivity carries the third domain) - Paper gains the forecasting section: synthetic dominance, FRED reversal, and the artifact shipping in skaters as the markov_drift transform; abstract closes with both further examples; token-cap correction to the compile-failure figure Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent 5c92b97 commit 5749310

32 files changed

Lines changed: 3584 additions & 364 deletions

docs/algorithms/alloy.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ <h1>Alloy</h1>
255255
survived validation: on 29 problems never used in any selection step it had the best mean rank at
256256
every budget from 60 to 480 evaluations, beating each of six competitors (including CMA-ES) on
257257
64&ndash;77% of 580 instances. The full story, including what failed, is in the
258-
<a href="../papers/alloy.pdf">Alloy paper</a>.
258+
<a href="../papers/inspiration_simplex.pdf">Inspiration Simplex working paper</a>.
259259
</div>
260260

261261
<div class="coordinate-note">
@@ -314,7 +314,7 @@ <h2>Implementation Details</h2>
314314
<em>Reference: Cotton (2026), "Alloy: a machine-designed derivative-free optimizer"</em>
315315
</td>
316316
<td>
317-
<a href="../papers/alloy.pdf" class="link-button paper">Paper</a>
317+
<a href="../papers/inspiration_simplex.pdf" class="link-button paper">Paper</a>
318318
<a href="../papers.html" class="link-button info">All Papers</a>
319319
</td>
320320
</tr>
@@ -372,7 +372,7 @@ <h3>Performance characteristics</h3>
372372
<div id="moreContent" class="more-content">
373373
<h3>Related reading</h3>
374374
<ul>
375-
<li><a href="../papers/alloy.pdf">The Alloy paper</a> (how it was made, what worked, what failed)</li>
375+
<li><a href="../papers/inspiration_simplex.pdf">The Inspiration Simplex working paper</a> (how Alloy was made, what worked, what failed)</li>
376376
<li><a href="../papers/humpday-jss.pdf">The HumpDay software paper</a></li>
377377
<li><a href="nelder-mead.html">Nelder-Mead</a>, <a href="differential-evolution.html">Differential Evolution</a>, <a href="cma-evolution-strategy.html">CMA-ES</a>, <a href="pattern-search.html">Pattern Search</a>, <a href="simulated-annealing.html">Simulated Annealing</a> &mdash; the five ancestors</li>
378378
</ul>

docs/papers.html

Lines changed: 13 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -105,36 +105,22 @@ <h2><a href="papers/humpday-jss.pdf">HumpDay: derivative-free optimizers in pure
105105
</div>
106106

107107
<div class="paper">
108-
<h2><a href="papers/alloy.pdf">Alloy: a machine-designed derivative-free
109-
optimizer, and a full account of the search that found it</a></h2>
110-
<p class="abstract">Alloy was generated by a language model from a prompt
111-
requiring an equal blend of Nelder&ndash;Mead, Differential Evolution,
112-
CMA-ES, pattern search and simulated annealing, part by part. On
113-
twenty-nine problems never used in any selection step it has the best mean
114-
rank at every budget from 60 to 480 evaluations. The paper also reports
115-
what failed: the continuous search over blends never beat evaluating the
116-
obvious point, and a tuned generic template matched every selection score
117-
yet collapsed out of sample. Alloy ships in the package.</p>
118-
<p class="links"><a href="papers/alloy.pdf">PDF</a>
119-
<a href="algorithms.html">the algorithm</a>
108+
<h2><a href="papers/inspiration_simplex.pdf">The inspiration simplex: using
109+
derivative-free optimization in concept space to create new derivative-free
110+
optimizers</a></h2>
111+
<p class="abstract">Established algorithms at the vertices of a simplex; a
112+
language model turns each mixing recipe into a working program. First product:
113+
Alloy, best mean rank at every budget on twenty-nine held-out problems, shipped
114+
in the package. Extending recipes to negative weights, one of six semantics
115+
survives out of sample, with a rule: negative weights repair weak hosts and
116+
spoil strong ones. Failures reported throughout; further worked examples
117+
planned.</p>
118+
<p class="links"><a href="papers/inspiration_simplex.pdf">PDF</a>
119+
<a href="algorithms.html">Alloy</a>
120120
<span class="status">working paper</span></p>
121121
</div>
122122

123-
<div class="paper">
124-
<h2><a href="papers/outside_simplex.pdf">Thinking outside the simplex: signed
125-
compositions of optimization algorithms</a></h2>
126-
<p class="abstract">Recipes with negative weights: &minus;100% algorithm A,
127-
+200% algorithm B means build B, shadow A's proposal logic at zero cost, and
128-
steer away from where A would go. The best signed recipe beat every unsigned
129-
draw at selection; an ablation then reversed the story, since disabling the
130-
repulsion improved the same program. A record of the construction, the trap,
131-
and the protocol that avoids it: no signed recipe counts until it beats its
132-
own ablated twin.</p>
133-
<p class="links"><a href="papers/outside_simplex.pdf">PDF</a>
134-
<span class="status">working paper</span></p>
135-
</div>
136-
137-
<div class="paper">
123+
<div class="paper">
138124
<h2><a href="papers/benchmark_validity.pdf">Do synthetic benchmarks rank
139125
optimizers the way real problems do?</a></h2>
140126
<p class="abstract">Rank correlation between optimizer leaderboards on

docs/papers/alloy.pdf

-72 KB
Binary file not shown.

docs/papers/humpday-jss.pdf

124 Bytes
Binary file not shown.
88.1 KB
Binary file not shown.

docs/papers/outside_simplex.pdf

-48.4 KB
Binary file not shown.

papers/dfo_recommender/_oos.py

Whitespace-only changes.
Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
"""Cache-eviction benchmark for the second inspiration-simplex example.
2+
3+
Traces are seeded generators; the disguise analog randomizes each family's
4+
parameters and permutes key labels per seed, so a generated policy cannot
5+
succeed by memorising keys or magic constants tied to one workload.
6+
7+
Reference policies: LRU, LFU, FIFO, CLOCK, RANDOM (the panel) and ARC (the
8+
famous hand-designed interior point, reported but not part of the
9+
normalisation panel).
10+
11+
Score: miss-rate regret normalised against the panel on the same instance,
12+
0 matching the best panel member, 1 the worst.
13+
"""
14+
15+
from __future__ import annotations
16+
17+
import random
18+
from collections import OrderedDict, deque
19+
20+
21+
# ---------------------------------------------------------------- policies
22+
class LRU:
23+
def __init__(self, capacity):
24+
self.cap = capacity
25+
self.d = OrderedDict()
26+
27+
def access(self, key):
28+
if key in self.d:
29+
self.d.move_to_end(key)
30+
return True
31+
if len(self.d) >= self.cap:
32+
self.d.popitem(last=False)
33+
self.d[key] = True
34+
return False
35+
36+
37+
class LFU:
38+
def __init__(self, capacity):
39+
self.cap = capacity
40+
self.count = {}
41+
self.tick = 0
42+
self.last = {}
43+
44+
def access(self, key):
45+
self.tick += 1
46+
if key in self.count:
47+
self.count[key] += 1
48+
self.last[key] = self.tick
49+
return True
50+
if len(self.count) >= self.cap:
51+
victim = min(self.count, key=lambda k: (self.count[k], self.last[k]))
52+
del self.count[victim]
53+
del self.last[victim]
54+
self.count[key] = 1
55+
self.last[key] = self.tick
56+
return False
57+
58+
59+
class FIFO:
60+
def __init__(self, capacity):
61+
self.cap = capacity
62+
self.q = deque()
63+
self.s = set()
64+
65+
def access(self, key):
66+
if key in self.s:
67+
return True
68+
if len(self.q) >= self.cap:
69+
self.s.discard(self.q.popleft())
70+
self.q.append(key)
71+
self.s.add(key)
72+
return False
73+
74+
75+
class CLOCK:
76+
def __init__(self, capacity):
77+
self.cap = capacity
78+
self.keys = []
79+
self.ref = {}
80+
self.hand = 0
81+
82+
def access(self, key):
83+
if key in self.ref:
84+
self.ref[key] = 1
85+
return True
86+
if len(self.keys) >= self.cap:
87+
while True:
88+
k = self.keys[self.hand]
89+
if self.ref[k]:
90+
self.ref[k] = 0
91+
self.hand = (self.hand + 1) % len(self.keys)
92+
else:
93+
del self.ref[k]
94+
self.keys[self.hand] = key
95+
self.ref[key] = 1
96+
self.hand = (self.hand + 1) % len(self.keys)
97+
return False
98+
self.keys.append(key)
99+
self.ref[key] = 1
100+
return False
101+
102+
103+
class RANDOM:
104+
def __init__(self, capacity, seed=0):
105+
self.cap = capacity
106+
self.s = set()
107+
self.rng = random.Random(seed)
108+
109+
def access(self, key):
110+
if key in self.s:
111+
return True
112+
if len(self.s) >= self.cap:
113+
self.s.discard(self.rng.choice(list(self.s)))
114+
self.s.add(key)
115+
return False
116+
117+
118+
class ARC:
119+
"""Adaptive Replacement Cache (Megiddo & Modha, 2003)."""
120+
121+
def __init__(self, capacity):
122+
self.c = capacity
123+
self.p = 0
124+
self.t1 = OrderedDict()
125+
self.t2 = OrderedDict()
126+
self.b1 = OrderedDict()
127+
self.b2 = OrderedDict()
128+
129+
def _replace(self, in_b2):
130+
if self.t1 and (len(self.t1) > self.p or (in_b2 and len(self.t1) == self.p)):
131+
k, _ = self.t1.popitem(last=False)
132+
self.b1[k] = True
133+
else:
134+
k, _ = self.t2.popitem(last=False)
135+
self.b2[k] = True
136+
137+
def access(self, key):
138+
if key in self.t1:
139+
del self.t1[key]
140+
self.t2[key] = True
141+
return True
142+
if key in self.t2:
143+
self.t2.move_to_end(key)
144+
return True
145+
if key in self.b1:
146+
self.p = min(self.c, self.p + max(1, len(self.b2) // max(1, len(self.b1))))
147+
self._replace(False)
148+
del self.b1[key]
149+
self.t2[key] = True
150+
return False
151+
if key in self.b2:
152+
self.p = max(0, self.p - max(1, len(self.b1) // max(1, len(self.b2))))
153+
self._replace(True)
154+
del self.b2[key]
155+
self.t2[key] = True
156+
return False
157+
if len(self.t1) + len(self.b1) == self.c:
158+
if len(self.t1) < self.c:
159+
self.b1.popitem(last=False)
160+
self._replace(False)
161+
else:
162+
self.t1.popitem(last=False)
163+
elif len(self.t1) + len(self.t2) + len(self.b1) + len(self.b2) >= self.c:
164+
if len(self.t1) + len(self.t2) + len(self.b1) + len(self.b2) >= 2 * self.c:
165+
if self.b2:
166+
self.b2.popitem(last=False)
167+
elif self.b1:
168+
self.b1.popitem(last=False)
169+
if len(self.t1) + len(self.t2) >= self.c:
170+
self._replace(False)
171+
self.t1[key] = True
172+
return False
173+
174+
175+
PANEL = {"LRU": LRU, "LFU": LFU, "FIFO": FIFO, "CLOCK": CLOCK, "RANDOM": RANDOM}
176+
177+
178+
# ------------------------------------------------------------------ traces
179+
def _zipf_sampler(rng, n_keys, alpha):
180+
weights = [1.0 / (i + 1) ** alpha for i in range(n_keys)]
181+
total = sum(weights)
182+
cum = []
183+
acc = 0.0
184+
for w in weights:
185+
acc += w / total
186+
cum.append(acc)
187+
188+
def draw():
189+
u = rng.random()
190+
lo, hi = 0, n_keys - 1
191+
while lo < hi:
192+
mid = (lo + hi) // 2
193+
if cum[mid] < u:
194+
lo = mid + 1
195+
else:
196+
hi = mid
197+
return lo
198+
199+
return draw
200+
201+
202+
def trace_zipf(rng, length):
203+
draw = _zipf_sampler(rng, rng.randint(400, 1200), rng.uniform(0.7, 1.2))
204+
return [draw() for _ in range(length)]
205+
206+
207+
def trace_loop(rng, length):
208+
loop = rng.randint(150, 400)
209+
return [i % loop for i in range(length)]
210+
211+
212+
def trace_scan_mix(rng, length):
213+
draw = _zipf_sampler(rng, rng.randint(300, 800), rng.uniform(0.8, 1.2))
214+
out = []
215+
nxt = 10_000
216+
while len(out) < length:
217+
out.extend(draw() for _ in range(rng.randint(200, 500)))
218+
scan = rng.randint(150, 400)
219+
out.extend(range(nxt, nxt + scan))
220+
nxt += scan
221+
return out[:length]
222+
223+
224+
def trace_phase(rng, length):
225+
out = []
226+
while len(out) < length:
227+
ws = rng.sample(range(100_000), rng.randint(80, 250))
228+
out.extend(rng.choice(ws) for _ in range(rng.randint(500, 1500)))
229+
return out[:length]
230+
231+
232+
def trace_burst(rng, length):
233+
out = []
234+
recent = deque(maxlen=50)
235+
draw = _zipf_sampler(rng, 2000, 0.6)
236+
for _ in range(length):
237+
if recent and rng.random() < rng.uniform(0.4, 0.7):
238+
k = rng.choice(recent)
239+
else:
240+
k = draw()
241+
recent.append(k)
242+
out.append(k)
243+
return out
244+
245+
246+
def trace_mixture(rng, length):
247+
a = trace_zipf(random.Random(rng.random()), length)
248+
b = trace_loop(random.Random(rng.random()), length)
249+
return [(a[i] if rng.random() < 0.6 else b[i] + 1_000_000) for i in range(length)]
250+
251+
252+
FAMILIES = {
253+
"zipf": trace_zipf,
254+
"loop": trace_loop,
255+
"scan_mix": trace_scan_mix,
256+
"phase": trace_phase,
257+
"burst": trace_burst,
258+
"mixture": trace_mixture,
259+
}
260+
261+
TRACE_LEN = 20_000
262+
263+
264+
def make_instance(family, seed):
265+
"""Seeded, parameter-randomized, key-permuted instance."""
266+
import zlib
267+
268+
rng = random.Random(zlib.crc32(f"{family}:{seed}".encode()))
269+
raw = FAMILIES[family](rng, TRACE_LEN)
270+
perm = {}
271+
prng = random.Random(seed * 7919 + 13)
272+
trace = []
273+
for k in raw:
274+
if k not in perm:
275+
perm[k] = prng.randrange(10_000_000)
276+
trace.append(perm[k])
277+
capacity = rng.randint(64, 192)
278+
return trace, capacity
279+
280+
281+
def hit_rate(policy_cls, trace, capacity):
282+
p = policy_cls(capacity)
283+
hits = sum(1 for k in trace if p.access(k))
284+
return hits / len(trace)
285+
286+
287+
def score_policy(policy_cls, instances, panel_cache=None):
288+
"""Mean panel-normalised miss regret across instances (0 best, 1 worst)."""
289+
regrets = []
290+
for i, (trace, cap) in enumerate(instances):
291+
if panel_cache is not None:
292+
panel_miss = panel_cache[i]
293+
else:
294+
panel_miss = [1.0 - hit_rate(c, trace, cap) for c in PANEL.values()]
295+
try:
296+
cand = 1.0 - hit_rate(policy_cls, trace, cap)
297+
except Exception: # noqa: BLE001
298+
regrets.append(1.0)
299+
continue
300+
vals = [cand] + panel_miss
301+
mn, mx = min(vals), max(vals)
302+
regrets.append(0.0 if mx <= mn else (cand - mn) / (mx - mn))
303+
return sum(regrets) / len(regrets)
304+
305+
306+
def build_panel_cache(instances):
307+
return [
308+
[1.0 - hit_rate(c, trace, cap) for c in PANEL.values()]
309+
for (trace, cap) in instances
310+
]
311+
312+
313+
if __name__ == "__main__":
314+
insts = [make_instance(f, s) for f in FAMILIES for s in (0, 1)]
315+
cache = build_panel_cache(insts)
316+
for name, cls in list(PANEL.items()) + [("ARC", ARC)]:
317+
print(f"{name:8s} regret {score_policy(cls, insts, cache):.4f}")

0 commit comments

Comments
 (0)