Wed, 06 Nov 2024 14:55:47 -0500
Test changes and more saving
| 13 | 1 | /*! |
| 2 | Optimisation on non-Riemannian manifolds. | |
| 3 | */ | |
| 4 | 4 | |
| 5 | // We use unicode. We would like to use much more of it than Rust allows. | |
| 6 | // Live with it. Embrace it. | |
| 7 | #![allow(uncommon_codepoints)] | |
| 8 | #![allow(mixed_script_confusables)] | |
| 9 | #![allow(confusable_idents)] | |
| 1 | 10 | |
| 19 | 11 | mod manifold; |
| 12 | mod fb; | |
| 13 | mod cube; | |
| 14 | mod dist; | |
| 15 | mod zero; | |
| 16 | mod scaled; | |
| 17 | ||
| 20 | 18 | use serde::{Serialize, Deserialize}; |
| 16 | 19 | use alg_tools::logger::Logger; |
| 20 | use alg_tools::tabledump::{TableDump, write_csv}; | |
| 21 | use alg_tools::error::DynError; | |
| 22 | use alg_tools::lingrid::LinSpace; | |
| 23 | use alg_tools::loc::Loc; | |
| 24 | use alg_tools::types::*; | |
| 25 | use alg_tools::mapping::{Sum, Apply}; | |
| 26 | use alg_tools::iterate::{AlgIteratorOptions, AlgIteratorFactory, Verbose}; | |
| 27 | use image::{ImageFormat, ImageBuffer, Rgb}; | |
| 28 | ||
| 19 | 29 | use dist::{DistTo, DistToSquaredDiv2}; |
| 12 | 30 | use fb::{forward_backward, IterInfo}; |
| 7 | 31 | use manifold::EmbeddedManifoldPoint; |
| 11 | 32 | use cube::*; |
| 16 | 33 | use Face::*; |
| 19 | 34 | #[allow(unused_imports)] |
| 16 | 35 | use zero::ZeroFn; |
| 19 | 36 | use scaled::Scaled; |
| 1 | 37 | |
| 13 | 38 | /// Program entry point |
| 1 | 39 | fn main() { |
| 12 | 40 | simple_cube_test().unwrap() |
| 7 | 41 | } |
| 1 | 42 | |
| 20 | 43 | /// Helper structure for saving a point on a cube into a CSV file |
| 44 | #[derive(Serialize,Deserialize,Debug)] | |
| 45 | struct CSVPoint { | |
| 12 | 46 | face : Face, |
| 47 | x : f64, | |
| 48 | y : f64, | |
| 49 | z : f64 | |
| 50 | } | |
| 51 | ||
| 20 | 52 | impl From<&OnCube> for CSVPoint { |
| 53 | fn from(point : &OnCube) -> Self { | |
| 54 | let Loc([x,y,z]) = point.embedded_coords(); | |
| 55 | let face = point.face(); | |
| 56 | CSVPoint { face, x, y, z } | |
| 57 | } | |
| 58 | } | |
| 59 | ||
| 60 | /// Helper structure for saving the log into a CSV file | |
| 61 | #[derive(Serialize,Deserialize,Debug)] | |
| 62 | struct CSVLog { | |
| 63 | iter : usize, | |
| 64 | value : f64, | |
| 65 | // serde is junk | |
| 66 | //#[serde(flatten)] | |
| 67 | //point : CSVPoint | |
| 68 | face : Face, | |
| 69 | x : f64, | |
| 70 | y : f64, | |
| 71 | z : f64 | |
| 72 | } | |
| 73 | ||
| 74 | ||
| 13 | 75 | /// Location for saving results |
| 12 | 76 | static PREFIX : &str = "res"; |
| 77 | ||
| 78 | /// A simple test on the cube | |
| 79 | fn simple_cube_test() -> DynError { | |
| 7 | 80 | |
| 81 | let points = [ | |
| 9 | 82 | //OnCube::new(F1, Loc([0.5, 0.5])), |
| 83 | //OnCube::new(F2, Loc([0.5, 0.5])), | |
| 84 | //OnCube::new(F4, Loc([0.1, 0.1])), | |
| 20 | 85 | OnCube::new(F1, Loc([0.5, 0.7])), |
| 86 | OnCube::new(F2, Loc([0.3, 0.5])), | |
| 87 | OnCube::new(F4, Loc([0.9, 0.9])), | |
| 88 | OnCube::new(F6, Loc([0.4, 0.3])), | |
| 89 | OnCube::new(F4, Loc([0.3, 0.7])), | |
| 90 | OnCube::new(F3, Loc([0.3, 0.7])), | |
| 7 | 91 | ]; |
| 92 | ||
| 20 | 93 | let origin = OnCube::new(F4, Loc([0.5, 0.5])); |
| 94 | ||
| 95 | write_points(format!("{PREFIX}/data"), points.iter())?; | |
| 96 | write_points(format!("{PREFIX}/origin"), std::iter::once(&origin))?; | |
| 97 | ||
| 7 | 98 | //let x = points[0].clone(); |
| 9 | 99 | // OnCube::new(F3, Loc([0.5, 0.5])); goes to opposite side |
| 20 | 100 | let x = OnCube::new(F3, Loc([0.1, 0.7])); |
| 7 | 101 | let f = Sum::new(points.into_iter().map(DistToSquaredDiv2)); |
| 19 | 102 | //let g = ZeroFn::new(); |
| 20 | 103 | let g = Scaled::new(0.5, DistTo(origin)); |
| 104 | let τ = 0.05; | |
| 12 | 105 | |
| 106 | let mut logger = Logger::new(); | |
| 107 | let logmap = |iter, IterInfo { value, point } : IterInfo<OnCube>| { | |
| 20 | 108 | let CSVPoint {x , y, z, face} = CSVPoint::from(&point); |
| 109 | CSVLog { | |
| 110 | iter, value, //point : CSVPoint::from(&point) | |
| 111 | x, y, z, face | |
| 112 | } | |
| 12 | 113 | }; |
| 7 | 114 | let iter = AlgIteratorOptions{ |
| 115 | max_iter : 100, | |
| 116 | verbose_iter : Verbose::Every(1), | |
| 117 | .. Default::default() | |
| 12 | 118 | }.mapped(logmap) |
| 119 | .into_log(&mut logger); | |
| 7 | 120 | |
| 121 | let x̂ = forward_backward(&f, &g, x, τ, iter); | |
| 122 | println!("result = {}\n{:?}", x̂.embedded_coords(), &x̂); | |
| 11 | 123 | |
| 12 | 124 | std::fs::create_dir_all(PREFIX)?; |
| 125 | ||
| 126 | logger.write_csv(format!("{PREFIX}/log.txt"))?; | |
| 127 | ||
| 11 | 128 | for face in Face::all() { |
| 20 | 129 | write_face_csv(format!("{PREFIX}/{face}"), face, 32, |x| f.apply(x) + g.apply(x))?; |
| 16 | 130 | write_face_img(format!("{PREFIX}/{face}"), face, 128, |x| f.apply(x) + g.apply(x))?; |
| 11 | 131 | } |
| 12 | 132 | |
| 133 | Ok(()) | |
| 1 | 134 | } |
| 11 | 135 | |
| 12 | 136 | /// Writes the values of `f` on `face` of a [`OnCube`] into a PNG file |
| 137 | /// with resolution `n × n`. | |
| 16 | 138 | fn write_face_img(filename : String, face : Face, n : usize, mut f : impl FnMut(&OnCube) -> f64) -> DynError { |
| 11 | 139 | let mut img = ImageBuffer::new(n as u32, n as u32); |
| 140 | let grid = LinSpace { | |
| 141 | start : Loc([0.0, 0.0]), | |
| 142 | end : Loc([1.0, 1.0]), | |
| 143 | count : [n, n] | |
| 144 | }; | |
| 145 | let rawdata : Vec<_> = grid.into_iter() | |
| 146 | .map(|x| f(&OnCube::new(face, x))) | |
| 147 | .collect(); | |
| 148 | let a = rawdata.iter().copied().reduce(f64::max).unwrap(); | |
| 149 | img.pixels_mut() | |
| 150 | .zip(rawdata) | |
| 151 | .for_each(|(p, v)| { | |
| 152 | let t = v/a; | |
| 153 | let rgb = [1.0-t, 1.0-t, 1.0]; | |
| 154 | *p = Rgb(rgb.map(|v| (v*(u8::RANGE_MAX as f64)) as u8)) | |
| 155 | }); | |
| 156 | ||
| 12 | 157 | img.save_with_format(format!("{filename}.png"), ImageFormat::Png)?; |
| 158 | ||
| 159 | Ok(()) | |
| 11 | 160 | } |
| 16 | 161 | |
| 162 | /// Writes the values of `f` on `face` of a [`OnCube`] into a CSV file | |
| 163 | /// with resolution `n × n`. | |
| 164 | fn write_face_csv(filename : String, face : Face, n : usize, mut f : impl FnMut(&OnCube) -> f64) -> DynError { | |
| 165 | ||
| 166 | #[derive(Serialize)] | |
| 167 | struct CSVFace { u : f64, v : f64, value : f64 } | |
| 168 | ||
| 169 | let grid = LinSpace { | |
| 170 | start : Loc([0.0, 0.0]), | |
| 171 | end : Loc([1.0, 1.0]), | |
| 172 | count : [n, n] | |
| 173 | }; | |
| 174 | ||
| 175 | let data = grid.into_iter() | |
| 176 | .map(|p@Loc([u,v])| CSVFace{ u, v, value : f(&OnCube::new(face, p)) }); | |
| 177 | ||
| 178 | write_csv(data, format!("{filename}.csv"))?; | |
| 179 | ||
| 180 | Ok(()) | |
| 181 | } | |
| 20 | 182 | |
| 183 | /// Writes a list of points on a [`OnCube`] into a CSV file | |
| 184 | fn write_points<'a, I : Iterator<Item=&'a OnCube>>(filename : String, list : I) -> DynError { | |
| 185 | ||
| 186 | write_csv(list.map(CSVPoint::from), format!("{filename}.csv"))?; | |
| 187 | Ok(()) | |
| 188 | } | |
| 189 |