use crate::compile::CompilerError;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SabreVf2PrepassConfig {
pub candidate_limit: usize,
pub call_limit: usize,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SabreConfig {
pub layout_trials: usize,
pub layout_assignment_budget: usize,
pub vf2_prepass: Option<SabreVf2PrepassConfig>,
pub refinement_iterations: usize,
pub routing_trials: usize,
pub seed: Option<u64>,
pub heuristic: SabreHeuristicConfig,
}
#[derive(Debug, Clone, PartialEq)]
pub struct SabreHeuristicConfig {
pub basic_weight: f64,
pub lookahead_weights: Vec<f64>,
pub decay_increment: Option<f64>,
pub decay_reset: usize,
pub attempt_limit: usize,
pub best_epsilon: f64,
}
impl Default for SabreHeuristicConfig {
fn default() -> Self {
Self {
basic_weight: 1.0,
lookahead_weights: vec![0.5, 0.25, 0.125, 0.0625, 0.03125],
decay_increment: Some(0.002),
decay_reset: 10,
attempt_limit: 1000,
best_epsilon: 1e-10,
}
}
}
impl Default for SabreConfig {
fn default() -> Self {
Self {
layout_trials: 10,
layout_assignment_budget: 1_000_000,
vf2_prepass: Some(SabreVf2PrepassConfig {
candidate_limit: 10,
call_limit: 1_000_000,
}),
refinement_iterations: 1,
routing_trials: 1,
seed: None,
heuristic: SabreHeuristicConfig::default(),
}
}
}
impl SabreConfig {
pub fn deterministic_seeded(seed: u64) -> Self {
Self {
layout_trials: 2,
layout_assignment_budget: 100_000,
vf2_prepass: Some(SabreVf2PrepassConfig {
candidate_limit: 10,
call_limit: 100_000,
}),
refinement_iterations: 1,
routing_trials: 1,
seed: Some(seed),
heuristic: SabreHeuristicConfig {
lookahead_weights: vec![0.5, 0.25, 0.125, 0.0625, 0.03125],
attempt_limit: 20,
..SabreHeuristicConfig::default()
},
}
}
pub fn validate(&self) -> Result<(), CompilerError> {
if self.routing_trials == 0 {
return Err(CompilerError::InvalidInput(
"sabre routing_trials must be greater than zero".to_string(),
));
}
self.heuristic.validate()
}
}
impl SabreHeuristicConfig {
pub(crate) fn validate(&self) -> Result<(), CompilerError> {
validate_weight(self.basic_weight, "sabre basic_weight")?;
for (index, weight) in self.lookahead_weights.iter().copied().enumerate() {
validate_weight(weight, &format!("sabre lookahead_weights[{index}]"))?;
}
if let Some(increment) = self.decay_increment {
validate_weight(increment, "sabre decay_increment")?;
if self.decay_reset == 0 {
return Err(CompilerError::InvalidInput(
"sabre decay_reset must be greater than zero when decay is enabled".to_string(),
));
}
}
if !(self.best_epsilon.is_finite() && self.best_epsilon >= 0.0) {
return Err(CompilerError::InvalidInput(
"sabre best_epsilon must be finite and non-negative".to_string(),
));
}
Ok(())
}
}
fn validate_weight(value: f64, name: &str) -> Result<(), CompilerError> {
if value.is_finite() && value >= 0.0 {
Ok(())
} else {
Err(CompilerError::InvalidInput(format!(
"{name} must be finite and non-negative"
)))
}
}
#[cfg(test)]
#[path = "heuristic_test.rs"]
mod heuristic_test;