use crate::errors::QlResult;
use crate::math::array::Array;
use crate::math::optimization::costfunction::CostFunction;
use crate::math::optimization::projection::Projection;
use crate::types::Real;
pub struct ProjectedCostFunction<'a> {
cost_function: &'a dyn CostFunction,
projection: Projection,
}
impl<'a> ProjectedCostFunction<'a> {
pub fn new(
cost_function: &'a dyn CostFunction,
parameter_values: &Array,
fix_parameters: Vec<bool>,
) -> QlResult<Self> {
Ok(ProjectedCostFunction {
cost_function,
projection: Projection::new(parameter_values, fix_parameters)?,
})
}
pub fn project(&self, parameters: &Array) -> Array {
self.projection.project(parameters)
}
pub fn include(&self, projected_parameters: &Array) -> Array {
self.projection.include(projected_parameters)
}
}
impl CostFunction for ProjectedCostFunction<'_> {
fn values(&self, free_parameters: &Array) -> Array {
self.cost_function
.values(&self.projection.include(free_parameters))
}
fn value(&self, free_parameters: &Array) -> Real {
self.cost_function
.value(&self.projection.include(free_parameters))
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Identity;
impl CostFunction for Identity {
fn values(&self, x: &Array) -> Array {
x.clone()
}
}
#[test]
fn project_and_include_delegate_to_the_projection() {
let cost = Identity;
let pcf = ProjectedCostFunction::new(
&cost,
&Array::from([10.0, 20.0, 30.0]),
vec![true, false, true],
)
.unwrap();
assert_eq!(
pcf.project(&Array::from([10.0, 20.0, 30.0])),
Array::from([20.0])
);
assert_eq!(
pcf.include(&Array::from([99.0])),
Array::from([10.0, 99.0, 30.0])
);
}
#[test]
fn value_and_values_delegate_over_the_reassembled_vector() {
let cost = Identity;
let pcf = ProjectedCostFunction::new(
&cost,
&Array::from([10.0, 20.0, 30.0]),
vec![true, false, true],
)
.unwrap();
assert_eq!(
pcf.values(&Array::from([99.0])),
Array::from([10.0, 99.0, 30.0])
);
let expected = ((10.0 * 10.0 + 99.0 * 99.0 + 30.0 * 30.0) / 3.0_f64).sqrt();
assert!((pcf.value(&Array::from([99.0])) - expected).abs() < 1e-12);
}
#[test]
fn new_rejects_an_all_fixed_split() {
let cost = Identity;
let result = ProjectedCostFunction::new(&cost, &Array::from([1.0, 2.0]), vec![true, true]);
assert!(result.is_err());
assert_eq!(result.err().unwrap().message(), "numberOfFreeParameters==0");
}
}