Classical Presets¶
This tutorial shows the three classical presets (Boltzmann, Fast, Gsa (Tsallis)) and the single run surface.
All three occupy the same five component slots; only the concrete Cool, Neigh, and Move objects differ.
Reference
The typed component algebra and the reduction of the three Generalized-SA families to the five slots are documented in the reference paper (see [[file:../used_by.org][used_by]]) and in the reproducibility package (SymPy witnesses of the four limit cases).
Prerequisites¶
pip install anneal(orpixi -e pythoninside the workspace).An objective callable
f(x: ndarray) -> float(values only for these presets).Box bounds (NumPy arrays, any dimension).
pip install anneal
Step 1: One surface, three presets¶
The call is identical. Only the preset object changes.
import numpy as np
from anneal import Boltzmann, Fast, Gsa, run
def rosenbrock(x):
"""Classic 2-D Rosenbrock (separable in a transformed coordinate)."""
return (1.0 - x[0])**2 + 100.0 * (x[1] - x[0]**2)**2
low = np.array([-5.0, -5.0])
high = np.array([5.0, 5.0])
h_b = run(rosenbrock, low, high,
Boltzmann(t_init=5.0, sigma=0.5),
n_epochs=20, steps_per_epoch=100, seed=42)
print("Boltzmann:", h_b.best_pos, h_b.best_val)
h_f = run(rosenbrock, low, high,
Fast(t_init=5.0, gamma=0.5),
n_epochs=20, steps_per_epoch=100, seed=42)
print("Fast:", h_f.best_pos, h_f.best_val)
h_g = run(rosenbrock, low, high,
Gsa(t_init=5.0, q_v=2.5, q_a=1.5),
n_epochs=20, steps_per_epoch=100, seed=42)
print("Gsa:", h_g.best_pos, h_g.best_val)
Expected: three different trajectories, all reaching a point near (1,1) with value near 0 (global min is 0 at (1,1)).
The histories contain epochs (list of EpochLine with best_val, accept_rate, T etc.).
Step 2: What the call actually does (slot view)¶
Objis the userrosenbrock(only thing that knows F).Coolis supplied by the preset (logarithmic for Boltzmann, reciprocal for Fast,Tsallispower forGsa (Tsallis)).NeighandMovetogether realize the proposal shape at the current T (Gaussian scale for Boltzmann, Cauchy for Fast,Tsallisvisit forGsa (Tsallis)).Acceptis always Metropolis (orTsallisaccept forGsa (Tsallis)) with the noise-aware compensated path when the crate was built with the finite-precision feature.
Because all three presets implement the same Sampler trait, run (the orchestrator) is oblivious to which family is inside.
Step 3: Inspect the returned history¶
print("Epochs recorded:", len(h_b.epochs))
print("Last epoch best:", h_b.epochs[-1].best_val)
print("Global best val:", h_b.best_val)
The History also carries the final state and per-epoch diagnostics that the Bayesian mixer later consumes.
Why this works¶
The algebra guarantees that any correct filling of the five slots produces a chain whose best-so-far is monotone (L3 + L4) and whose neighbors are symmetric (L1) with support covering the box (L2). The three classical families are three different fillings that the literature already proved converge; the Rust implementation factors them so that the orchestrator loop, the device kernel, the noise-aware Accept, and the QMC polish are shared verbatim.