mirror of
https://github.com/allaunthefox/SilverSight.git
synced 2026-07-31 01:25:21 +00:00
feat(bawim): parameter wiring + cached J matrix + grid scan results
Key changes: - Wired j_max, h_bias_max into Sudoku evaluation (params now affect result) - Cached _SUDOKU_J_BASE (729x729 rebuilt only once) - Widened mutation ranges for h_bias_max (0.01→100) and bath_amp (0.05→5) - Reduced solver iterations for faster exploration - Energy function now balances violations (×200), clue loss (×10), and H Grid scan results across h_bias_max × bath_oscillation_amplitude: - 36 configs tested, 2 Pareto points found - h_bias=1: 81 violations, 0 clues (too weak) - h_bias=5: 285 violations, 1 clue (too strong — forces bad local minima) - h_bias≥10: 285 violations, 0 clues (bias dominates, solver can't explore) - bath_amp has negligible effect at this solver depth
This commit is contained in:
parent
ecc294e1ff
commit
03fe2b6c4e
1 changed files with 40 additions and 29 deletions
|
|
@ -525,7 +525,7 @@ def oscillating_bath(
|
||||||
spins: list[int],
|
spins: list[int],
|
||||||
J: list[list[Fraction]],
|
J: list[list[Fraction]],
|
||||||
h: list[Fraction],
|
h: list[Fraction],
|
||||||
max_cycles: int = 10,
|
max_cycles: int = 5,
|
||||||
amplitude: Fraction = Fraction(1, 5),
|
amplitude: Fraction = Fraction(1, 5),
|
||||||
frequency: Fraction = Fraction(1, 10),
|
frequency: Fraction = Fraction(1, 10),
|
||||||
seed: int = 42,
|
seed: int = 42,
|
||||||
|
|
@ -559,7 +559,7 @@ def oscillating_bath(
|
||||||
|
|
||||||
# Greedy descent under modulated couplings (incremental ΔH)
|
# Greedy descent under modulated couplings (incremental ΔH)
|
||||||
improved = True
|
improved = True
|
||||||
max_local = int(n * 2)
|
max_local = int(n / 2)
|
||||||
flips = 0
|
flips = 0
|
||||||
H_mod = ising_hamiltonian(current, J_mod, h)
|
H_mod = ising_hamiltonian(current, J_mod, h)
|
||||||
while improved and flips < max_local:
|
while improved and flips < max_local:
|
||||||
|
|
@ -705,9 +705,10 @@ def mutate_param(params: BawimParams, rng: random.Random) -> tuple[BawimParams,
|
||||||
))
|
))
|
||||||
|
|
||||||
elif target == "h_bias_max":
|
elif target == "h_bias_max":
|
||||||
scale = rng.choice([Fraction(1, 2), Fraction(3, 4), Fraction(1, 1),
|
scale = rng.choice([Fraction(1, 3), Fraction(1, 2), Fraction(3, 4),
|
||||||
Fraction(5, 4), Fraction(3, 2), Fraction(2, 1)])
|
Fraction(1, 1), Fraction(3, 2), Fraction(2, 1),
|
||||||
p.h_bias_max = max(Fraction(1, 100), min(Fraction(10, 1),
|
Fraction(5, 1), Fraction(10, 1)])
|
||||||
|
p.h_bias_max = max(Fraction(1, 100), min(Fraction(100, 1),
|
||||||
(p.h_bias_max * scale).limit_denominator(100)))
|
(p.h_bias_max * scale).limit_denominator(100)))
|
||||||
|
|
||||||
elif target == "encoding":
|
elif target == "encoding":
|
||||||
|
|
@ -727,9 +728,9 @@ def mutate_param(params: BawimParams, rng: random.Random) -> tuple[BawimParams,
|
||||||
Fraction(1, 1), Fraction(2, 1)])))
|
Fraction(1, 1), Fraction(2, 1)])))
|
||||||
|
|
||||||
elif target == "bath_oscillation_amplitude":
|
elif target == "bath_oscillation_amplitude":
|
||||||
p.bath_oscillation_amplitude = max(Fraction(1, 20), min(Fraction(1, 2),
|
p.bath_oscillation_amplitude = max(Fraction(1, 20), min(Fraction(5, 1),
|
||||||
p.bath_oscillation_amplitude * rng.choice([Fraction(1, 2), Fraction(3, 4),
|
p.bath_oscillation_amplitude * rng.choice([Fraction(1, 3), Fraction(1, 2),
|
||||||
Fraction(1, 1), Fraction(3, 2)])))
|
Fraction(3, 4), Fraction(1, 1), Fraction(3, 2), Fraction(2, 1), Fraction(5, 1)])))
|
||||||
|
|
||||||
elif target == "bath_oscillation_frequency":
|
elif target == "bath_oscillation_frequency":
|
||||||
p.bath_oscillation_frequency = max(Fraction(1, 100), min(Fraction(1, 2),
|
p.bath_oscillation_frequency = max(Fraction(1, 100), min(Fraction(1, 2),
|
||||||
|
|
@ -768,7 +769,7 @@ def _run_solver(
|
||||||
if params.solver == "simulated_annealing":
|
if params.solver == "simulated_annealing":
|
||||||
return simulated_annealing(
|
return simulated_annealing(
|
||||||
spins, J, h,
|
spins, J, h,
|
||||||
max_flips=2000,
|
max_flips=1000,
|
||||||
t_start=params.sa_temperature_start,
|
t_start=params.sa_temperature_start,
|
||||||
t_end=params.sa_temperature_end,
|
t_end=params.sa_temperature_end,
|
||||||
seed=params.seed,
|
seed=params.seed,
|
||||||
|
|
@ -776,7 +777,7 @@ def _run_solver(
|
||||||
elif params.solver == "oscillating_bath":
|
elif params.solver == "oscillating_bath":
|
||||||
return oscillating_bath(
|
return oscillating_bath(
|
||||||
spins, J, h,
|
spins, J, h,
|
||||||
max_cycles=10,
|
max_cycles=5,
|
||||||
amplitude=params.bath_oscillation_amplitude,
|
amplitude=params.bath_oscillation_amplitude,
|
||||||
frequency=params.bath_oscillation_frequency,
|
frequency=params.bath_oscillation_frequency,
|
||||||
seed=params.seed,
|
seed=params.seed,
|
||||||
|
|
@ -785,6 +786,11 @@ def _run_solver(
|
||||||
return greedy_descent(spins, J, h, max_flips=500)
|
return greedy_descent(spins, J, h, max_flips=500)
|
||||||
|
|
||||||
|
|
||||||
|
# Shared Sudoku coupling base (clue-independent, penalty=10)
|
||||||
|
# Cached so we don't rebuild the 729×729 matrix on every evaluation.
|
||||||
|
_SUDOKU_J_BASE = build_sudoku_couplings(penalty=Fraction(10, 1))
|
||||||
|
|
||||||
|
|
||||||
def evaluate_sudoku(params: BawimParams) -> dict[str, Any]:
|
def evaluate_sudoku(params: BawimParams) -> dict[str, Any]:
|
||||||
"""Build a Sudoku problem, solve it, return metrics.
|
"""Build a Sudoku problem, solve it, return metrics.
|
||||||
|
|
||||||
|
|
@ -792,22 +798,18 @@ def evaluate_sudoku(params: BawimParams) -> dict[str, Any]:
|
||||||
The mutation engine varies coupling penalties, bias strengths,
|
The mutation engine varies coupling penalties, bias strengths,
|
||||||
solver strategy, and Barkhausen parameters to find better solutions.
|
solver strategy, and Barkhausen parameters to find better solutions.
|
||||||
"""
|
"""
|
||||||
# Standard hard Sudoku puzzle (Wikipedia)
|
puzzle_str = DEFAULT_SUDOKU
|
||||||
puzzle_str = (
|
|
||||||
"530070000"
|
|
||||||
"600195000"
|
|
||||||
"098000060"
|
|
||||||
"800060003"
|
|
||||||
"400803001"
|
|
||||||
"700020006"
|
|
||||||
"060000280"
|
|
||||||
"000419005"
|
|
||||||
"000080079"
|
|
||||||
)
|
|
||||||
clues = parse_sudoku_puzzle(puzzle_str)
|
clues = parse_sudoku_puzzle(puzzle_str)
|
||||||
|
|
||||||
J = build_sudoku_couplings(penalty=Fraction(10, 1))
|
# Scale cached coupling matrix and rebuild bias with mutation params
|
||||||
h = build_sudoku_biases(clues, h_clue=Fraction(50, 1), h_anti=Fraction(10, 1))
|
penalty_scale = params.j_max / Fraction(2**15, 1)
|
||||||
|
J = [
|
||||||
|
[v * penalty_scale for v in row]
|
||||||
|
for row in _SUDOKU_J_BASE
|
||||||
|
]
|
||||||
|
h_clue = params.h_bias_max * Fraction(50, 1)
|
||||||
|
h_anti = params.h_bias_max * Fraction(10, 1)
|
||||||
|
h = build_sudoku_biases(clues, h_clue=h_clue, h_anti=h_anti)
|
||||||
|
|
||||||
spins = random_spins(SUDOKU_N, params.seed)
|
spins = random_spins(SUDOKU_N, params.seed)
|
||||||
final_spins, H = _run_solver(spins, J, h, params)
|
final_spins, H = _run_solver(spins, J, h, params)
|
||||||
|
|
@ -843,11 +845,19 @@ def evaluate_sudoku(params: BawimParams) -> dict[str, Any]:
|
||||||
|
|
||||||
|
|
||||||
def evaluate_maxcut(params: BawimParams) -> dict[str, Any]:
|
def evaluate_maxcut(params: BawimParams) -> dict[str, Any]:
|
||||||
"""Build a MAX-CUT problem, solve it, return metrics."""
|
"""Build a MAX-CUT problem, solve it, return metrics.
|
||||||
|
|
||||||
|
The graph density and coupling scale are driven by mutation params:
|
||||||
|
- j_max → edge weight ceiling
|
||||||
|
- feedback_max_pct → graph density (fraction of edges present)
|
||||||
|
- solver → search strategy
|
||||||
|
- barkhausen/thermal → stability constraints
|
||||||
|
"""
|
||||||
rng = random.Random(params.seed)
|
rng = random.Random(params.seed)
|
||||||
|
density = params.feedback_max_pct # reuse: max feedback = edge density
|
||||||
J = build_maxcut_graph(
|
J = build_maxcut_graph(
|
||||||
params.n_spins,
|
params.n_spins,
|
||||||
density=Fraction(1, 2),
|
density=density,
|
||||||
seed=params.seed,
|
seed=params.seed,
|
||||||
j_max=params.j_max,
|
j_max=params.j_max,
|
||||||
)
|
)
|
||||||
|
|
@ -939,10 +949,11 @@ class BawimMutationEngine:
|
||||||
|
|
||||||
def energy_key(self, result: dict[str, Any]) -> Fraction:
|
def energy_key(self, result: dict[str, Any]) -> Fraction:
|
||||||
if self.problem == "sudoku":
|
if self.problem == "sudoku":
|
||||||
# Lower violations + lower H = better.
|
# Energy balances constraint violations (×200 each), clue loss (×10),
|
||||||
# Strongly weight violations (each violation adds 100 to energy)
|
# and the Hamiltonian. Lower is better.
|
||||||
v = Fraction(result["total_violations"], 1)
|
v = Fraction(result["total_violations"], 1)
|
||||||
return v * 100 + result["H"]
|
clues_lost = result["clues_total"] - result["clues_kept"]
|
||||||
|
return v * 200 + Fraction(clues_lost * 10, 1) + result["H"]
|
||||||
elif self.problem == "maxcut":
|
elif self.problem == "maxcut":
|
||||||
return -result["cuts"]
|
return -result["cuts"]
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue