use super::backend::{ConstraintCapabilities, ConstraintInstallation, NativeConstraint};
use super::{ObjectiveData, PenaltyMode};
use crate::constraints::{
CeilingConstraintData, CrossoverMonotonicityConstraintData, MinGainConstraintData,
SpacingConstraintData, viol_ceiling_from_spl, viol_min_gain_from_xs, viol_spacing_from_xs,
};
use crate::x2peq::x2spl;
pub fn install_constraints(
caps: ConstraintCapabilities,
obj: &mut ObjectiveData,
) -> ConstraintInstallation {
if caps.nonlinear_ineq {
obj.configure_penalties(PenaltyMode::Disabled);
ConstraintInstallation::Native(build_constraint_set(obj))
} else {
obj.configure_penalties(caps.fallback_penalty_mode);
ConstraintInstallation::Penalty
}
}
pub fn build_constraint_set(obj: &ObjectiveData) -> Vec<NativeConstraint> {
let mut out: Vec<NativeConstraint> = Vec::new();
if obj.max_db > 0.0 {
out.push(build_ceiling_constraint(obj));
}
if obj.min_db > 0.0 {
out.push(build_min_gain_constraint(obj));
}
if obj.min_spacing_oct > 0.0 {
out.push(build_spacing_constraint(obj));
}
out
}
pub fn build_crossover_monotonicity_constraint(obj: &ObjectiveData) -> Option<NativeConstraint> {
use crate::LossType;
if obj.loss_type != LossType::DriversFlat {
return None;
}
let drivers = obj.drivers_data.as_ref()?;
if drivers.drivers.len() < 2 {
return None;
}
let data = CrossoverMonotonicityConstraintData {
n_drivers: drivers.drivers.len(),
min_log_separation: 0.15,
};
Some(NativeConstraint {
label: "crossover_monotonicity",
fun: Box::new(move |x| {
let n_xovers = data.n_drivers - 1;
if n_xovers <= 1 {
return 0.0;
}
let xover_start = 2 * data.n_drivers;
let mut max_violation = f64::NEG_INFINITY;
for i in 0..(n_xovers - 1) {
let log_xover_i = x[xover_start + i];
let log_xover_i_plus_1 = x[xover_start + i + 1];
let violation = log_xover_i + data.min_log_separation - log_xover_i_plus_1;
if violation > max_violation {
max_violation = violation;
}
}
max_violation
}),
tol: 1e-6,
})
}
fn build_ceiling_constraint(obj: &ObjectiveData) -> NativeConstraint {
let data = CeilingConstraintData {
freqs: obj.freqs.clone(),
srate: obj.srate,
max_db: obj.max_db,
peq_model: obj.peq_model,
};
NativeConstraint {
label: "ceiling",
fun: Box::new(move |x| {
let peq_spl = x2spl(&data.freqs, x, data.srate, data.peq_model);
viol_ceiling_from_spl(&peq_spl, data.max_db, data.peq_model)
}),
tol: 1e-6,
}
}
fn build_min_gain_constraint(obj: &ObjectiveData) -> NativeConstraint {
let data = MinGainConstraintData {
min_db: obj.min_db,
peq_model: obj.peq_model,
};
NativeConstraint {
label: "min_gain",
fun: Box::new(move |x| viol_min_gain_from_xs(x, data.peq_model, data.min_db)),
tol: 1e-6,
}
}
fn build_spacing_constraint(obj: &ObjectiveData) -> NativeConstraint {
let data = SpacingConstraintData {
min_spacing_oct: obj.min_spacing_oct,
peq_model: obj.peq_model,
};
NativeConstraint {
label: "spacing",
fun: Box::new(move |x| viol_spacing_from_xs(x, data.peq_model, data.min_spacing_oct)),
tol: 1e-6,
}
}