use crate::cli::Args;
use crate::optim::ObjectiveData;
#[derive(Debug, Clone)]
pub struct ParetoFilter {
pub params: Vec<f64>,
pub flatness_loss: f64,
pub score_loss: Option<f64>,
pub num_filters: usize,
pub converged: bool,
}
pub fn pareto_optimization(
objective_data: &ObjectiveData,
args: &Args,
filter_counts: Vec<usize>,
) -> Vec<ParetoFilter> {
let mut pareto_front = Vec::new();
for &n_filters in &filter_counts {
let mut args_with_filters = args.clone();
args_with_filters.num_filters = n_filters;
let (lower_bounds, upper_bounds) = crate::workflow::setup_bounds(&args_with_filters);
let mut x =
crate::workflow::initial_guess(&args_with_filters, &lower_bounds, &upper_bounds);
let result = crate::optim::optimize_filters(
&mut x, &lower_bounds,
&upper_bounds,
objective_data.clone(),
&args_with_filters,
);
match result {
Ok((_, loss)) => {
pareto_front.push(ParetoFilter {
params: x,
flatness_loss: loss,
score_loss: None,
num_filters: n_filters,
converged: true,
});
}
Err((_, loss)) => {
pareto_front.push(ParetoFilter {
params: x,
flatness_loss: loss,
score_loss: None,
num_filters: n_filters,
converged: false,
});
}
}
}
pareto_front
}
pub fn extract_non_dominated(filters: &[ParetoFilter]) -> Vec<&ParetoFilter> {
let mut non_dominated = Vec::new();
for candidate in filters {
let mut is_dominated = false;
for other in filters {
if std::ptr::eq(other, candidate) {
continue;
}
let other_flat_le = other.flatness_loss <= candidate.flatness_loss;
let other_flat_lt = other.flatness_loss < candidate.flatness_loss;
let other_filters_le = other.num_filters <= candidate.num_filters;
let other_filters_lt = other.num_filters < candidate.num_filters;
if other_flat_le && other_filters_le && (other_flat_lt || other_filters_lt) {
is_dominated = true;
break;
}
}
if !is_dominated {
non_dominated.push(candidate);
}
}
non_dominated
}
pub fn print_pareto_front(filters: &[ParetoFilter]) {
log::info!("\nPareto-Optimal Filter Configurations:");
log::info!("=====================================");
log::info!("# | Filters | Flatness Loss | Converged");
log::info!("--+---------+---------------+-----------");
for (i, f) in filters.iter().enumerate() {
log::info!(
"{} | {:3} | {:12.6} | {}",
i + 1,
f.num_filters,
f.flatness_loss,
if f.converged { "Yes" } else { "No" }
);
}
log::info!("\nRecommendation: Choose the configuration with the fewest");
log::info!("filters that meets your loss tolerance threshold.");
}