brush-paint-opt: tighten axis bounds to prevent smear-basin

Wider ranges let the optimizer find a degenerate local minimum:
brush_radius_factor=0.43 + pen_lift_penalty=50 + min_score_factor=0.046
+ polish_search_factor=1.95. This config covered every letter but at
~8× repaint per pixel — visually a fuzzy halo'd mess.

Bounds now:
  brush_radius_factor   0.40-1.50  →  0.70-1.20  (close to original guess)
  brush_radius_percentile 0.70-1.00 → 0.85-1.00
  pen_lift_penalty      0-200      →  0-25       (cap repaint aggression)
  pen_lift_reach        0.5-16     →  1.0-6.0
  min_score_factor      0.0-0.30   →  0.05-0.30  (walker must demand progress)
  polish_search_factor  0.10-4.0   →  0.30-1.20  (polish stays brush-local)
  polish_iters          0-12       →  0-4
  lookahead_steps       1-12       →  2-6
This commit is contained in:
Mitchell Hansen
2026-05-06 21:18:09 -07:00
parent e172d5703e
commit 75edb76681

View File

@@ -35,16 +35,21 @@ pub struct Axis {
}
pub fn default_axes() -> Vec<Axis> {
// Bounds tightened to keep the search inside a sane neighbourhood
// around the original brush-sizing guess. Wider ranges let the
// optimizer find a "smear" basin (tiny brush + huge pen-lift +
// low score gate) that covers the corpus by repainting every
// pixel 7-8× — visually awful even though all metrics pass.
vec![
Axis { name: "brush_radius_factor", lo: 0.40, hi: 1.50, is_int: false,
Axis { name: "brush_radius_factor", lo: 0.70, hi: 1.20, is_int: false,
set: |p, v| p.brush_radius_factor = v, get: |p| p.brush_radius_factor },
Axis { name: "brush_radius_percentile", lo: 0.70, hi: 1.00, is_int: false,
Axis { name: "brush_radius_percentile", lo: 0.85, hi: 1.00, is_int: false,
set: |p, v| p.brush_radius_percentile = v, get: |p| p.brush_radius_percentile },
Axis { name: "brush_radius_offset_px", lo: 0.0, hi: 1.0, is_int: false,
set: |p, v| p.brush_radius_offset_px = v, get: |p| p.brush_radius_offset_px },
Axis { name: "polish_iters", lo: 0.0, hi: 12.0, is_int: true,
Axis { name: "polish_iters", lo: 0.0, hi: 4.0, is_int: true,
set: |p, v| p.polish_iters = v as u32, get: |p| p.polish_iters as f32 },
Axis { name: "polish_search_factor", lo: 0.10, hi: 4.0, is_int: false,
Axis { name: "polish_search_factor", lo: 0.30, hi: 1.20, is_int: false,
set: |p, v| p.polish_search_factor = v, get: |p| p.polish_search_factor },
Axis { name: "bg_penalty", lo: 0.0, hi: 20.0, is_int: false,
set: |p, v| p.bg_penalty = v, get: |p| p.bg_penalty },
@@ -54,21 +59,21 @@ pub fn default_axes() -> Vec<Axis> {
set: |p, v| p.overpaint_penalty = v, get: |p| p.overpaint_penalty },
Axis { name: "step_size_factor", lo: 0.20, hi: 0.90, is_int: false,
set: |p, v| p.step_size_factor = v, get: |p| p.step_size_factor },
Axis { name: "lookahead_steps", lo: 1.0, hi: 12.0, is_int: true,
Axis { name: "lookahead_steps", lo: 2.0, hi: 6.0, is_int: true,
set: |p, v| p.lookahead_steps = v as usize, get: |p| p.lookahead_steps as f32 },
Axis { name: "n_directions", lo: 8.0, hi: 64.0, is_int: true,
set: |p, v| p.n_directions = v as usize, get: |p| p.n_directions as f32 },
Axis { name: "momentum_weight", lo: 0.0, hi: 2.0, is_int: false,
set: |p, v| p.momentum_weight = v, get: |p| p.momentum_weight },
Axis { name: "min_score_factor", lo: 0.0, hi: 0.30, is_int: false,
Axis { name: "min_score_factor", lo: 0.05, hi: 0.30, is_int: false,
set: |p, v| p.min_score_factor = v, get: |p| p.min_score_factor },
Axis { name: "back_dir_cutoff", lo: -0.95, hi: -0.3, is_int: false,
set: |p, v| p.back_dir_cutoff = v, get: |p| p.back_dir_cutoff },
Axis { name: "min_component_factor", lo: 0.10, hi: 1.50, is_int: false,
set: |p, v| p.min_component_factor = v, get: |p| p.min_component_factor },
Axis { name: "pen_lift_penalty", lo: 0.0, hi: 200.0, is_int: false,
Axis { name: "pen_lift_penalty", lo: 0.0, hi: 25.0, is_int: false,
set: |p, v| p.pen_lift_penalty = v, get: |p| p.pen_lift_penalty },
Axis { name: "pen_lift_reach", lo: 0.5, hi: 16.0, is_int: false,
Axis { name: "pen_lift_reach", lo: 1.0, hi: 6.0, is_int: false,
set: |p, v| p.pen_lift_reach = v, get: |p| p.pen_lift_reach },
Axis { name: "output_rdp_eps", lo: 0.0, hi: 2.0, is_int: false,
set: |p, v| p.output_rdp_eps = v, get: |p| p.output_rdp_eps },