use crate::{Objective, ObjectiveContext};
pub struct WeightedObjective<T> {
objectives: Vec<(Box<dyn Objective<T>>, f64)>,
}
impl<T> WeightedObjective<T> {
pub fn new() -> Self {
Self {
objectives: Vec::new(),
}
}
pub fn add(mut self, objective: Box<dyn Objective<T>>, weight: f64) -> Self {
self.objectives.push((objective, weight));
self
}
}
impl<T> Default for WeightedObjective<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Send + Sync> Objective<T> for WeightedObjective<T> {
fn score(&self, candidate: &T, context: &ObjectiveContext) -> f64 {
if self.objectives.is_empty() {
return 0.0;
}
let mut weighted_sum = 0.0;
let mut weight_sum = 0.0;
for (objective, weight) in &self.objectives {
let w = *weight;
if !w.is_finite() || w <= 0.0 {
continue;
}
let score = objective.score(candidate, context);
if !score.is_finite() {
continue;
}
weighted_sum += score * w;
weight_sum += w;
}
if weight_sum > 0.0 {
weighted_sum / weight_sum
} else {
0.0
}
}
fn name(&self) -> &str {
"WeightedObjective"
}
}
pub struct PriorityObjective<T> {
objectives: Vec<(Box<dyn Objective<T>>, f64)>,
fallback: f64,
}
impl<T> PriorityObjective<T> {
pub fn new() -> Self {
Self {
objectives: Vec::new(),
fallback: 0.0,
}
}
pub fn add(mut self, objective: Box<dyn Objective<T>>, threshold: f64) -> Self {
self.objectives.push((objective, threshold));
self
}
pub fn with_fallback(mut self, score: f64) -> Self {
self.fallback = score;
self
}
}
impl<T> Default for PriorityObjective<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Send + Sync> Objective<T> for PriorityObjective<T> {
fn score(&self, candidate: &T, context: &ObjectiveContext) -> f64 {
for (objective, threshold) in &self.objectives {
let score = objective.score(candidate, context);
if score.is_finite() && score >= *threshold {
return score;
}
}
self.fallback
}
fn name(&self) -> &str {
"PriorityObjective"
}
}
pub struct ConsensusObjective<T> {
objectives: Vec<Box<dyn Objective<T>>>,
threshold: f64,
}
impl<T> ConsensusObjective<T> {
pub fn new(threshold: f64) -> Self {
Self {
objectives: Vec::new(),
threshold,
}
}
pub fn with_objective(mut self, objective: Box<dyn Objective<T>>) -> Self {
self.objectives.push(objective);
self
}
}
impl<T: Send + Sync> Objective<T> for ConsensusObjective<T> {
fn score(&self, candidate: &T, context: &ObjectiveContext) -> f64 {
if self.objectives.is_empty() {
return 0.0;
}
let mut log_sum = 0.0f64;
let n = self.objectives.len();
for objective in &self.objectives {
let score = objective.score(candidate, context);
if !score.is_finite() || score < self.threshold {
return 0.0;
}
if score <= 0.0 {
return 0.0;
}
log_sum += score.ln();
}
(log_sum / n as f64).exp()
}
fn name(&self) -> &str {
"ConsensusObjective"
}
}
pub struct UnionObjective<T> {
objectives: Vec<Box<dyn Objective<T>>>,
}
impl<T> UnionObjective<T> {
pub fn new() -> Self {
Self {
objectives: Vec::new(),
}
}
pub fn with_objective(mut self, objective: Box<dyn Objective<T>>) -> Self {
self.objectives.push(objective);
self
}
}
impl<T> Default for UnionObjective<T> {
fn default() -> Self {
Self::new()
}
}
impl<T: Send + Sync> Objective<T> for UnionObjective<T> {
fn score(&self, candidate: &T, context: &ObjectiveContext) -> f64 {
self.objectives
.iter()
.map(|obj| obj.score(candidate, context))
.filter(|s| s.is_finite())
.fold(0.0f64, |a, b| a.max(b))
}
fn name(&self) -> &str {
"UnionObjective"
}
}
pub struct NegateObjective<T> {
inner: Box<dyn Objective<T>>,
}
impl<T> NegateObjective<T> {
pub fn new(inner: Box<dyn Objective<T>>) -> Self {
Self { inner }
}
}
impl<T: Send + Sync> Objective<T> for NegateObjective<T> {
fn score(&self, candidate: &T, context: &ObjectiveContext) -> f64 {
1.0 - self.inner.score(candidate, context)
}
fn name(&self) -> &str {
"NegateObjective"
}
}
pub struct ScaleObjective<O> {
inner: O,
factor: f64,
}
impl<O> ScaleObjective<O> {
pub fn new(inner: O, factor: f64) -> Self {
Self { inner, factor }
}
}
impl<T, O: Objective<T>> Objective<T> for ScaleObjective<O> {
fn score(&self, candidate: &T, context: &ObjectiveContext) -> f64 {
self.inner.score(candidate, context) * self.factor
}
fn name(&self) -> &str {
"ScaleObjective"
}
}
#[cfg(test)]
#[path = "compose_tests.rs"]
mod tests;