Skip to main content

forge_engine/runtime/
stabilizer.rs

1use crate::config::ForgeConfig;
2use crate::error::ForgeResult;
3use crate::runtime::novelty::AttemptPhase;
4
5pub struct Stabilizer {
6    inner: stabilizer_core::Stabilizer,
7}
8
9/// Per-attempt overrides produced by the stabilizer.
10#[derive(Debug, Clone)]
11pub struct AttemptOverrides {
12    pub phase: AttemptPhase,
13    pub delta_amplitude: f64,
14    pub force_family: Option<String>,
15    pub force_minimal_diff: bool,
16    pub weight_factor: f64,
17}
18
19impl Stabilizer {
20    pub fn new(config: &ForgeConfig) -> Self {
21        let delta_policy = stabilizer_core::DeltaPolicy::new(
22            config.novelty.delta_amp_default,
23            config.novelty.delta_amp_stabilize1,
24            config.novelty.delta_amp_stabilize2,
25            config.novelty.delta_amp_clamp,
26        );
27        Self {
28            inner: stabilizer_core::Stabilizer::new(
29                delta_policy,
30                stabilizer_core::StabilizerConfig {
31                    force_family: config.stabilization.stabilize1_force_family.clone(),
32                    force_minimal_diff: config.stabilization.stabilize2_force_minimal_diff,
33                    stabilize_weight_factor: config.stabilization.increase_stabilize_weight_factor,
34                },
35            ),
36        }
37    }
38
39    /// Get the next attempt's overrides.
40    ///
41    /// Panics in debug builds if called out of order.
42    /// Returns error in release builds.
43    pub fn next_attempt(&mut self) -> ForgeResult<AttemptOverrides> {
44        let next = self.inner.next_attempt()?;
45        Ok(AttemptOverrides {
46            phase: next.phase,
47            delta_amplitude: next.delta_amplitude,
48            force_family: next.force_family,
49            force_minimal_diff: next.force_minimal_diff,
50            weight_factor: next.weight_factor,
51        })
52    }
53
54    /// Check if more attempts remain.
55    pub fn has_next(&self) -> bool {
56        self.inner.has_next()
57    }
58
59    /// Reset the stabilizer for a new run.
60    pub fn reset(&mut self) {
61        self.inner.reset();
62    }
63
64    /// Get the current phase index (for logging).
65    pub fn current_index(&self) -> usize {
66        self.inner.current_index()
67    }
68}