diff --git a/src/list/raxos/evolution/src/config.rs b/src/list/raxos/evolution/src/config.rs new file mode 100644 index 0000000..42f37f2 --- /dev/null +++ b/src/list/raxos/evolution/src/config.rs @@ -0,0 +1,12 @@ +pub struct Config { + pub n_points_per_scene: usize, + + /// The number of round to evolve. + pub n_round: usize, + + /// Number of variants to spawn for each contour + pub n_spawn: usize, + + /// The probability of moving a point, adding a point, or removing a point + pub variant_weight: (f64, f64, f64), +} diff --git a/src/list/raxos/evolution/src/contour.rs b/src/list/raxos/evolution/src/contour.rs index 375becb..1f04eb1 100644 --- a/src/list/raxos/evolution/src/contour.rs +++ b/src/list/raxos/evolution/src/contour.rs @@ -84,9 +84,9 @@ impl Contour { let y = rand::random::() * (y_high - y_low) + y_low; let p = Point::new(x, y); // println!("Move point {}/{l} {} to {}", i, p0, p); - if i == l - 1 { - println!("Move point {}/{l} {} to {}", i, p0, p); - } + // if i == l - 1 { + // println!("Move point {}/{l} {} to {}", i, p0, p); + // } points[i] = p; Self { points } } else if rand < prob_move + prob_add { @@ -107,39 +107,39 @@ impl Contour { let x = rand::random::() * (x_right - x_left) + x_left; - let (frm, to) = if i == 0 { - let frm = self.points[1]; - let to = self.points[0]; - (frm, to) - } else if i == l { - let fmt = self.points[l - 2]; - let to = self.points[l - 1]; - (fmt, to) - } else { - let frm = self.points[i - 1]; - let to = self.points[i]; - (frm, to) - }; - - let y = (to.y - frm.y) / (to.x - frm.x) * (x - frm.x) + frm.y; - - // let (y_low, y_high) = if i == 0 { - // let right_sibling = self.points[i].y; - // (right_sibling, (right_sibling * 1.5f64)) + // let (frm, to) = if i == 0 { + // let frm = self.points[1]; + // let to = self.points[0]; + // (frm, to) // } else if i == l { - // let last = self.points[i - 1].y; - // (0.0, last) + // let fmt = self.points[l - 2]; + // let to = self.points[l - 1]; + // (fmt, to) // } else { - // (self.points[i].y, self.points[i - 1].y) + // let frm = self.points[i - 1]; + // let to = self.points[i]; + // (frm, to) // }; - // let y = rand::random::() * (y_high - y_low) + y_low; + // + // let y = (to.y - frm.y) / (to.x - frm.x) * (x - frm.x) + frm.y; + + let (y_low, y_high) = if i == 0 { + let right_sibling = self.points[i].y; + (right_sibling, (right_sibling * 1.5f64)) + } else if i == l { + let last = self.points[i - 1].y; + (0.0, last) + } else { + (self.points[i].y, self.points[i - 1].y) + }; + let y = rand::random::() * (y_high - y_low) + y_low; let mut points = self.points.clone(); let p = Point::new(x, y); // println!("Add point {}/{l} at {}", i, p); - if i == l { - println!("Add point {}/{l} at {}", i, p); - } + // if i == l { + // println!("Add point {}/{l} at {}", i, p); + // } points.insert(i, p); Self { points } } else { @@ -153,9 +153,9 @@ impl Contour { let mut points = self.points.clone(); // println!("Remove point at {}/{l} {}", i, points[i]); - if i == l - 1 { - println!("Remove point at {}/{l} {}", i, points[i]); - } + // if i == l - 1 { + // println!("Remove point at {}/{l} {}", i, points[i]); + // } points.remove(i); Self { points } diff --git a/src/list/raxos/evolution/src/main.rs b/src/list/raxos/evolution/src/main.rs index 5e4e37b..1775648 100644 --- a/src/list/raxos/evolution/src/main.rs +++ b/src/list/raxos/evolution/src/main.rs @@ -1,32 +1,52 @@ use plotters::prelude::*; +use config::Config; use point::Point; use scene::Scene; use crate::contour::Contour; use crate::display_slice::DisplaySliceExt; +pub mod config; pub mod contour; pub mod display_slice; pub mod point; pub mod scene; pub struct Evolution { + config: Config, + scene: Scene, /// A normalized by each point of the scene normalized: Vec, + + /// Best found contour for each point in the Scene + contours: Vec, + + /// (point index, point conflict) + point_conflicts: Vec<(usize, usize)>, } impl Evolution { - pub fn new(scene: Scene) -> Self { + pub fn new(config: Config, scene: Scene, build_contour: impl Fn(&Point) -> Contour) -> Self { let normalized = scene .points .iter() .map(|point| scene.normalize(*point)) .collect::>(); - Self { scene, normalized } + let contours = scene.points.iter().map(build_contour).collect::>(); + + let point_conflicts = (0..scene.points.len()).map(|i| (i, 0)).collect::>(); + + Self { + config, + scene, + normalized, + contours, + point_conflicts, + } } pub fn points_len(&self) -> usize { @@ -38,7 +58,9 @@ impl Evolution { &self.normalized[i] } - /// Given two points in the scene, return `a` is below `b` by checking if `b` is above the contour + /// Given two points in the scene, return `a` is below `b`, + /// by checking if `b` is above the contour, + /// in the reference frame of `a`. pub fn is_below(&self, a: usize, b: usize, contour: &Contour) -> bool { let b_in_normalized_by_a = self.normalized[a].points[b]; contour.cross_product_x(&b_in_normalized_by_a) > 0f64 @@ -51,113 +73,91 @@ impl Evolution { /// Count the number of conflicting point pairs. /// - /// Return the total number of conflicts and list of conflict count for each point, - /// sorted by conflict count. - pub fn count_conflict(&self, contour: &Contour) -> (usize, Vec<(usize, usize)>) { + /// Return the total number of conflicts found for point `p`, using contour `contour`. + pub fn count_conflict(&self, p: usize, contour: &Contour) -> usize { contour.validate(); - let mut per_point_count = vec![0; self.points_len()]; - let mut conflicts = 0; for i in 0..self.points_len() { - for j in (i + 1)..self.points_len() { - if self.is_below(i, j, contour) && self.is_below(j, i, contour) - || self.is_above(i, j, contour) && self.is_above(j, i, contour) - { - per_point_count[i] += 1; - per_point_count[j] += 1; - conflicts += 1; - - println!( - "conflict: {} {} and {} {}", - i, self.scene.points[i], j, self.scene.points[j] - ); - - println!("normalize to a: {}", self.normalized[i].points[j]); - println!("normalize to b: {}", self.normalized[j].points[i]); - - panic!("wow") - } + if i == p { + continue; + } + + if self.is_below(p, i, contour) && self.is_below(i, p, &self.contours[i]) + || self.is_above(p, i, contour) && self.is_above(i, p, &self.contours[i]) + { + conflicts += 1; } } - let mut per_point_count = per_point_count - .into_iter() - .enumerate() - .map(|(point_index, count)| (count, point_index)) - .filter(|(count, _)| *count > 0) - .collect::>(); - per_point_count.sort_by(|a, b| b.0.cmp(&a.0)); - // println!( - // "conflict count: {} {:?}", - // per_point_count.len(), - // per_point_count - // ); - - (conflicts, per_point_count) + conflicts } - /// Eliminate the contours that have the most conflicts - pub fn eliminates(&self, contour: Vec) -> Vec { - let n = contour.len(); - - let mut contour_and_conflict = vec![]; - for c in contour { - let (conflicts, count_and_point_index) = self.count_conflict(&c); - contour_and_conflict.push((c, conflicts)); + /// Update the contour for the given point, find a better contour that has less conflicts with other points. + /// + /// Return the best contour found and the number of conflicts. + pub fn find_better_contour(&self, p: usize) -> (Contour, usize) { + let w = self.config.variant_weight; + let contour = self.contours[p].clone(); + let conflict = self.count_conflict(p, &contour); + + let mut best = (contour.clone(), conflict); + if best.1 == 0 { + return best; } + // println!("optimize: init: {} {}", best.1, best.0); - contour_and_conflict.sort_by(|a, b| a.1.cmp(&b.1)); - for x in contour_and_conflict.iter().take(10) { - println!("rank: {}: {}", x.1, x.0); + for _i in 0..self.config.n_spawn { + let new = contour.rand_update(w.0, w.1, w.2); + let conflict = self.count_conflict(p, &new); + + // Find the first better solution + if conflict < best.1 { + return (new, conflict); + } } - // Keep the first several contours that have less than 120% of the conflicts of the first contour - let first_rank = contour_and_conflict[0].1; + // println!("optimize: output: {} {}", best.1, best.0); + best + } + + pub fn evolve_one_round(&mut self) { + let mut next_generation = vec![]; + let mut point_conflict = vec![]; + for (p, _old_conflict) in self.point_conflicts.iter() { + let (new_contour, new_conflict) = self.find_better_contour(*p); + next_generation.push(new_contour); + point_conflict.push((*p, new_conflict)); + } - contour_and_conflict - .into_iter() - .take_while(|(c, r)| *r < first_rank * 120 / 100) - .take(8) - .map(|(c, _)| c) - .collect() + self.contours = next_generation; + point_conflict.sort_by(|a, b| b.1.cmp(&a.1)); + self.point_conflicts = point_conflict; + println!("point_conflicts: {:?}", self.point_conflicts); } - pub fn evolve(&self, contour: Contour, round: usize) -> Contour { - let n_spawn = 20; + pub fn evolve(&mut self) { + let mut t0 = std::time::Instant::now(); // make dir `output`: std::fs::create_dir_all("output/").unwrap(); let pref = "output/cc"; - let mut cc = vec![contour]; - - draw_contour(format!("{pref}-0000.png"), &self.scene, &cc[0]).unwrap(); + draw_contour(format!("{pref}-0000.png"), &self.scene, &self.contours).unwrap(); - for i in 1..=round { + for i in 1..=self.config.n_round { println!("round {}", i); - cc = self.eliminates(cc); + self.evolve_one_round(); - draw_contour(format!("{pref}-{i:0>4}.png"), &self.scene, &cc[0]).unwrap(); - - let new_cc = cc - .iter() - .cycle() - .map(|c| c.rand_update(7.0, 7.0, 7.0)) - .take(n_spawn) - .collect::>(); - cc.extend(new_cc); + if t0.elapsed().as_secs() > 1 { + t0 = std::time::Instant::now(); + draw_contour(format!("{pref}-{i:0>4}.png"), &self.scene, &self.contours).unwrap(); + } + // draw_contour(format!("{pref}-{i:0>4}.png"), &self.scene, &self.contours).unwrap(); } - - cc.remove(0) } } -pub struct Config { - pub n_scene: usize, - pub n_points_per_scene: usize, -} - fn main() -> Result<(), Box> { fn contour(points: impl IntoIterator) -> Contour { Contour::new(points.into_iter().map(Point::from)) @@ -165,33 +165,24 @@ fn main() -> Result<(), Box> { let c = contour([(0.5, 1.5), (1.0, 1.0), (1.5, 0.5)]); - let mut ps = (0..200) - .map(|_| { - let x = rand::random::() * 10f64; - let y = 1.0 / x; - (x, y) - }) - .chain([(1.0, 1.0)]) - .collect::>(); + let config = Config { + n_points_per_scene: 1000, + n_round: 1_000, + n_spawn: 1_000, + variant_weight: (5.0, 10.0, 5.0), + }; + let scene = Scene::rand_scene(10f64, 10f64, config.n_points_per_scene); - ps.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap_or(std::cmp::Ordering::Equal)); - println!("{:?}", ps); + let mut evolution = Evolution::new(config, scene, |_point| c.clone()); + evolution.evolve(); - let c = contour(ps); - - let scene = Scene::rand_scene(10f64, 10f64, 200); - - let evolution = Evolution::new(scene); - let got = evolution.evolve(c, 1_000); - - // draw_contour("contour.png", &scene, &c)?; Ok(()) } fn draw_contour( path: impl ToString, scene: &Scene, - contour: &Contour, + contours: &[Contour], ) -> Result<(), Box> { let path = path.to_string(); // 创建一个800x600像素的PNG文件 @@ -209,19 +200,25 @@ fn draw_contour( // 配置坐标轴 chart.configure_mesh().draw()?; - // Draw contour - let ps = contour - .points - .iter() - .map(|p| Circle::new((p.x, p.y), 5, &RED.mix(0.5))); - chart.draw_series(ps)?; - + // Scene let ps = scene .points .iter() .map(|p| Circle::new((p.x, p.y), 3, &BLUE.mix(0.5))); chart.draw_series(ps)?; + let l = contours.len() as f64; + for (i, contour) in contours.iter().enumerate() { + let color = HSLColor(i as f64 / l, 0.8, 0.5).mix(0.5); + + // Draw contour + let ps = contour + .points + .iter() + .map(|p| Circle::new((p.x, p.y), 3, &color.mix(0.5))); + chart.draw_series(ps)?; + } + root.present()?; Ok(())