radiate_core/objectives/mod.rs
1pub mod front;
2pub mod optimize;
3pub mod pareto;
4pub mod score;
5
6pub use front::*;
7pub use optimize::*;
8pub use pareto::*;
9pub use score::*;
10
11// use crate::objectives::{Objective, Scored, pareto};
12// #[cfg(feature = "serde")]
13// use serde::{Deserialize, Serialize};
14// use std::{cmp::Ordering, hash::Hash, ops::Range, sync::Arc};
15
16// const DEFAULT_ENTROPY_BINS: usize = 20;
17
18// pub struct FrontAddResult {
19// pub added_count: usize,
20// pub removed_count: usize,
21// pub comparisons: usize,
22// pub filter_count: usize,
23// pub size: usize,
24// }
25
26// /// A `Front<T>` is a collection of `T`'s that are non-dominated with respect to each other.
27// /// This is useful for multi-objective optimization problems where the goal is to find
28// /// the best solutions that are not dominated by any other solution.
29// /// This results in what is called the Pareto front.
30// #[derive(Clone)]
31// #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32// pub struct Front<T>
33// where
34// T: Scored,
35// {
36// values: Vec<Arc<T>>,
37// range: Range<usize>,
38// objective: Objective,
39
40// // ---- scratch / cache (not part of logical state) ----
41// #[cfg_attr(feature = "serde", serde(skip))]
42// scratch_remove: Vec<usize>,
43
44// #[cfg_attr(feature = "serde", serde(skip))]
45// scratch_keep_idx: Vec<usize>,
46
47// // Flat score matrix: [n, m] row-major
48// #[cfg_attr(feature = "serde", serde(skip))]
49// scratch_scores: Vec<f32>,
50
51// // Crowding distance per item (len = n)
52// #[cfg_attr(feature = "serde", serde(skip))]
53// scratch_dist: Vec<f32>,
54
55// // Indices 0..n used for per-dimension sorts in crowding distance
56// #[cfg_attr(feature = "serde", serde(skip))]
57// scratch_order: Vec<usize>,
58// }
59
60// impl<T> Front<T>
61// where
62// T: Scored,
63// {
64// pub fn new(range: Range<usize>, objective: Objective) -> Self {
65// Front {
66// values: Vec::new(),
67// range,
68// objective: objective.clone(),
69// scratch_remove: Vec::new(),
70// scratch_keep_idx: Vec::new(),
71// scratch_scores: Vec::new(),
72// scratch_dist: Vec::new(),
73// scratch_order: Vec::new(),
74// }
75// }
76
77// pub fn range(&self) -> Range<usize> {
78// self.range.clone()
79// }
80
81// pub fn objective(&self) -> Objective {
82// self.objective.clone()
83// }
84
85// pub fn is_empty(&self) -> bool {
86// self.values.is_empty()
87// }
88
89// pub fn values(&self) -> &[Arc<T>] {
90// &self.values
91// }
92
93// pub fn crowding_distance(&self) -> Option<Vec<f32>> {
94// let scores = self
95// .values
96// .iter()
97// .filter_map(|s| s.score())
98// .collect::<Vec<_>>();
99
100// if scores.is_empty() {
101// return None;
102// }
103
104// Some(pareto::crowding_distance(&scores))
105// }
106
107// pub fn entropy(&self) -> Option<f32> {
108// let scores = self
109// .values
110// .iter()
111// .filter_map(|s| s.score())
112// .collect::<Vec<_>>();
113
114// if scores.is_empty() {
115// return None;
116// }
117
118// Some(pareto::entropy(&scores, DEFAULT_ENTROPY_BINS))
119// }
120
121// pub fn add_all(&mut self, items: Vec<T>) -> FrontAddResult
122// where
123// T: Eq + Hash + Clone + Send + Sync + 'static,
124// {
125// let mut updated = false;
126// let mut to_remove = Vec::new();
127// let mut added_count = 0;
128// let mut removed_count = 0;
129// let mut comparisons = 0;
130// let mut filter_count = 0;
131
132// for new_member in items.into_iter() {
133// let mut is_dominated = true;
134
135// for existing_val in self.values.iter() {
136// let equals = &new_member == existing_val.as_ref();
137// if self.dom_cmp(existing_val.as_ref(), &new_member) == Ordering::Greater || equals {
138// // If an existing value dominates the new value, return false
139// is_dominated = false;
140// comparisons += 1;
141// break;
142// } else if self.dom_cmp(&new_member, existing_val.as_ref()) == Ordering::Greater {
143// // If the new value dominates an existing value, continue checking
144// to_remove.push(Arc::clone(existing_val));
145// comparisons += 1;
146// continue;
147// }
148// }
149
150// if is_dominated {
151// updated = true;
152// self.values.push(Arc::new(new_member));
153// added_count += 1;
154// for rem in to_remove.drain(..) {
155// self.values.retain(|x| x.as_ref() != rem.as_ref());
156// removed_count += 1;
157// }
158// }
159
160// if updated && self.values.len() > self.range.end {
161// self.filter();
162// filter_count += 1;
163// }
164
165// to_remove.clear();
166// updated = false;
167// }
168
169// FrontAddResult {
170// added_count,
171// removed_count,
172// comparisons,
173// filter_count,
174// size: self.values.len(),
175// }
176// }
177
178// fn dom_cmp(&self, one: &T, two: &T) -> Ordering {
179// let one_score = one.score();
180// let two_score = two.score();
181
182// if one_score.is_none() || two_score.is_none() {
183// return Ordering::Equal;
184// }
185
186// if let (Some(one), Some(two)) = (one_score, two_score) {
187// if pareto::dominance(one, two, &self.objective) {
188// return Ordering::Greater;
189// } else if pareto::dominance(two, one, &self.objective) {
190// return Ordering::Less;
191// }
192// }
193
194// Ordering::Equal
195// }
196
197// fn filter(&mut self) {
198// if let Some(crowding_distances) = self.crowding_distance() {
199// let mut enumerated = crowding_distances.iter().enumerate().collect::<Vec<_>>();
200
201// enumerated.sort_unstable_by(|a, b| b.1.partial_cmp(a.1).unwrap_or(Ordering::Equal));
202
203// self.values = enumerated
204// .iter()
205// .take(self.range.start)
206// .map(|(i, _)| Arc::clone(&self.values[*i]))
207// .collect::<Vec<Arc<T>>>();
208// }
209// }
210// }
211
212// impl<T> Default for Front<T>
213// where
214// T: Scored,
215// {
216// fn default() -> Self {
217// Front::new(0..0, Objective::default())
218// }
219// }