src/main.rs

changeset 37
d7cd14b8ccc0
parent 33
556afeaa34bf
child 42
271fba635bce
equal deleted inserted replaced
34:aa6129697116 37:d7cd14b8ccc0
9 #![allow(confusable_idents)] 9 #![allow(confusable_idents)]
10 10
11 mod manifold; 11 mod manifold;
12 mod fb; 12 mod fb;
13 mod cube; 13 mod cube;
14 mod cylinder;
14 mod dist; 15 mod dist;
15 mod zero; 16 mod zero;
16 mod scaled; 17 mod scaled;
18 mod newton;
17 19
18 use serde::{Serialize, Deserialize}; 20 use serde::{Serialize, Deserialize};
19 use alg_tools::logger::Logger; 21 use alg_tools::logger::Logger;
20 use alg_tools::tabledump::{TableDump, write_csv}; 22 use alg_tools::tabledump::{TableDump, write_csv};
21 use alg_tools::error::DynError; 23 use alg_tools::error::DynError;
27 use alg_tools::mapping::Mapping; 29 use alg_tools::mapping::Mapping;
28 use image::{ImageFormat, ImageBuffer, Rgb}; 30 use image::{ImageFormat, ImageBuffer, Rgb};
29 31
30 use dist::{DistTo, DistToSquaredDiv2}; 32 use dist::{DistTo, DistToSquaredDiv2};
31 use fb::{forward_backward, IterInfo, Desc, Prox}; 33 use fb::{forward_backward, IterInfo, Desc, Prox};
32 use manifold::EmbeddedManifoldPoint; 34 use manifold::{EmbeddedManifoldPoint, ManifoldPoint, FacedManifoldPoint};
33 use cube::*; 35 use cube::OnCube;
34 use Face::*; 36 use cylinder::{CylCoords, Cylinder, CylinderConfig, OnCylinder, normalise_angle};
35 #[allow(unused_imports)] 37 #[allow(unused_imports)]
36 use zero::ZeroFn; 38 use zero::ZeroFn;
37 use scaled::Scaled; 39 use scaled::Scaled;
38 40
41 /// Location for saving results
42 static PREFIX : &str = "res";
43
39 /// Program entry point 44 /// Program entry point
40 fn main() { 45 fn main() {
41 simple_cube_test().unwrap() 46 simple_cube_test(format!("{PREFIX}/cube").as_str()).unwrap();
47 simple_cylinder_test(format!("{PREFIX}/cylinder").as_str()).unwrap();
42 } 48 }
43 49
44 /// Helper structure for saving a point on a cube into a CSV file 50 /// Helper structure for saving a point on a cube into a CSV file
45 #[derive(Serialize,Deserialize,Debug)] 51 #[derive(Serialize,Deserialize,Debug)]
46 struct CSVPoint { 52 struct CSVPoint<Face> {
47 face : Face, 53 face : Face,
48 x : f64, 54 x : f64,
49 y : f64, 55 y : f64,
50 z : f64 56 z : f64
51 } 57 }
52 58
53 impl From<&OnCube> for CSVPoint { 59 impl<M> From<&M> for CSVPoint<M::Face>
54 fn from(point : &OnCube) -> Self { 60 where M : EmbeddedManifoldPoint<EmbeddedCoords = Loc<f64, 3>> + FacedManifoldPoint {
61 fn from(point : &M) -> Self {
55 let Loc([x,y,z]) = point.embedded_coords(); 62 let Loc([x,y,z]) = point.embedded_coords();
56 let face = point.face(); 63 let face = point.face();
57 CSVPoint { face, x, y, z } 64 CSVPoint { face, x, y, z }
58 } 65 }
59 } 66 }
60 67
61 /// Helper structure for saving the log into a CSV file 68 /// Helper structure for saving a point on a cylinder into a CSV file
62 #[derive(Serialize,Deserialize,Debug)] 69 #[derive(Serialize,Deserialize,Debug)]
63 struct CSVLog { 70 struct CSVCylPoint<Face> {
64 iter : usize,
65 value : f64,
66 // serde is junk
67 //#[serde(flatten)]
68 //point : CSVPoint
69 face : Face, 71 face : Face,
70 x : f64, 72 angle : f64,
71 y : f64, 73 r : f64,
72 z : f64 74 z : f64
73 } 75 }
74 76
75 77 impl<'a> From<&'a crate::cylinder::OnCylinder<'a>> for CSVCylPoint<crate::cylinder::Face> {
76 /// Location for saving results 78 fn from(point : &'a OnCylinder<'a>) -> Self {
77 static PREFIX : &str = "res"; 79 let CylCoords {r, angle, z} = point.cyl_coords();
80 let face = point.face();
81 CSVCylPoint { face, r, angle, z }
82 }
83 }
78 84
79 /// A simple test on the cube 85 /// A simple test on the cube
80 fn simple_cube_test() -> DynError { 86 fn simple_cube_test(dir : &str) -> DynError {
87 use crate::cube::Face;
88 use crate::cube::Face::*;
81 89
82 let points = [ 90 let points = [
83 OnCube::new(F1, Loc([0.5, 0.7])), 91 OnCube::new(F1, Loc([0.5, 0.7])),
84 OnCube::new(F2, Loc([0.3, 0.5])), 92 OnCube::new(F2, Loc([0.3, 0.5])),
85 OnCube::new(F4, Loc([0.9, 0.9])), 93 OnCube::new(F4, Loc([0.9, 0.9])),
88 OnCube::new(F3, Loc([0.3, 0.7])), 96 OnCube::new(F3, Loc([0.3, 0.7])),
89 ]; 97 ];
90 98
91 let origin = OnCube::new(F4, Loc([0.5, 0.5])); 99 let origin = OnCube::new(F4, Loc([0.5, 0.5]));
92 100
93 write_points(format!("{PREFIX}/data"), points.iter())?; 101 std::fs::create_dir_all(dir)?;
94 write_points(format!("{PREFIX}/origin"), std::iter::once(&origin))?; 102 write_csv(points.iter().map(CSVPoint::from), format!("{dir}/data.csv"))?;
103 write_csv(std::iter::once(&origin).map(CSVPoint::from), format!("{dir}/origin.csv"))?;
95 104
96 let f = Sum::new(points.into_iter().map(DistToSquaredDiv2)); 105 let f = Sum::new(points.into_iter().map(DistToSquaredDiv2));
97 //let g = ZeroFn::new(); 106 //let g = ZeroFn::new();
98 let g = Scaled::new(0.5, DistTo(origin)); 107 let g = Scaled::new(0.5, DistTo(origin));
99 let τ = 0.1; 108 let τ = 0.1;
100 109
101 std::fs::create_dir_all(PREFIX)?;
102 for face in Face::all() { 110 for face in Face::all() {
103 write_face_csv(format!("{PREFIX}/{face}"), face, 32, |x| f.apply(x) + g.apply(x))?; 111 write_cube_face_csv(format!("{dir}/{face}"), face, 32, |x| f.apply(x) + g.apply(x))?;
104 } 112 }
105 write_face_imgs(128, |x| f.apply(x) + g.apply(x))?; 113 write_cube_face_imgs(dir, 128, |x| f.apply(x) + g.apply(x))?;
106 114
107 run_and_save("x1", &f, &g, OnCube::new(F3, Loc([0.1, 0.7])), τ)?; 115 /// Helper structure for saving the log into a CSV file
108 run_and_save("x2", &f, &g, OnCube::new(F2, Loc([0.3, 0.1])), τ)?; 116 #[derive(Serialize,Deserialize,Debug)]
109 run_and_save("x3", &f, &g, OnCube::new(F6, Loc([0.6, 0.2])), τ) 117 struct CSVLog<Face> {
110 } 118 iter : usize,
111 119 value : f64,
112 /// Runs [forward_backward] and saves the results. 120 // serde is junk
113 pub fn run_and_save<F, G>( 121 //#[serde(flatten)]
114 name : &str, 122 //point : CSVPoint
115 f : &F, 123 face : Face,
116 g : &G, 124 x : f64,
117 x : OnCube, 125 y : f64,
118 τ : f64, 126 z : f64
119 ) -> DynError 127 }
120 where F : Desc<OnCube> + Mapping<OnCube, Codomain = f64>, 128
121 G : Prox<OnCube> + Mapping<OnCube, Codomain = f64> {
122
123 let mut logger = Logger::new();
124 let logmap = |iter, IterInfo { value, point } : IterInfo<OnCube>| { 129 let logmap = |iter, IterInfo { value, point } : IterInfo<OnCube>| {
125 let CSVPoint {x , y, z, face} = CSVPoint::from(&point); 130 let CSVPoint {x , y, z, face} = CSVPoint::from(&point);
126 CSVLog { 131 CSVLog {
127 iter, value, //point : CSVPoint::from(&point) 132 iter, value, //point : CSVPoint::from(&point)
128 x, y, z, face 133 x, y, z, face
129 } 134 }
130 }; 135 };
136
137 run_and_save(dir, "x1", &f, &g, OnCube::new(F3, Loc([0.1, 0.7])), τ, logmap)?;
138 run_and_save(dir, "x2", &f, &g, OnCube::new(F2, Loc([0.3, 0.1])), τ, logmap)?;
139 run_and_save(dir, "x3", &f, &g, OnCube::new(F6, Loc([0.6, 0.2])), τ, logmap)
140 }
141
142
143 /// A simple test on the cube
144 fn simple_cylinder_test(dir : &str) -> DynError {
145 let π = f64::PI;
146
147 let cyl = Cylinder {
148 radius : 1.0,
149 height : 1.0,
150 config : CylinderConfig { newton_iters : 100 },
151 //Default::default(),
152 };
153
154 let points = [
155 cyl.on_side(π/2.0, 0.4),
156 cyl.on_side(π*3.0/2.0, -0.1),
157 cyl.on_side(π, 0.2),
158 cyl.on_side(-π/5.0, -0.2),
159 cyl.on_side(π*2.0/3.0, -0.45),
160 cyl.on_top(π*3.0/4.0, 0.7),
161 cyl.on_bottom(π*5.0/4.0, 0.3),
162 ];
163
164 let origin = cyl.on_side(0.0, 0.0);
165
166 std::fs::create_dir_all(dir)?;
167 write_csv(points.iter().map(CSVCylPoint::from), format!("{dir}/data.csv"))?;
168 write_csv(std::iter::once(&origin).map(CSVCylPoint::from), format!("{dir}/origin.csv"))?;
169
170 let f = Sum::new(points.into_iter().map(DistToSquaredDiv2));
171 let g = Scaled::new(4.0, DistTo(origin));
172 let τ = 0.1;
173
174 write_cylinder_faces_csv(dir, &cyl, 32, 32, 32, |x| f.apply(x) + g.apply(x))?;
175
176 /// Helper structure for saving the log into a CSV file
177 #[derive(Serialize,Deserialize,Debug)]
178 struct CSVLog<Face> {
179 iter : usize,
180 value : f64,
181 // serde is junk
182 //#[serde(flatten)]
183 //point : CSVCylPoint
184 face : Face,
185 angle : f64,
186 r : f64,
187 z : f64,
188 }
189
190 let logmap = |iter, IterInfo { value, point } : IterInfo<OnCylinder<'_>>| {
191 let CSVCylPoint {r, angle, z, face} = CSVCylPoint::from(&point);
192 CSVLog {
193 iter, value, //point : CSVPoint::from(&point)
194 r, angle, z, face
195 }
196 };
197
198 run_and_save(dir, "x1", &f, &g, cyl.on_top(0.0, 0.0), τ, logmap)?;
199 run_and_save(dir, "x2", &f, &g, cyl.on_side(-π*2.0/5.0, 0.0), τ, logmap)?;
200 run_and_save(dir, "x3", &f, &g, cyl.on_bottom(-π*0.5, 0.8), τ, logmap)
201
202 }
203
204
205 /// Runs [forward_backward] and saves the results.
206 pub fn run_and_save<F, G, M, I>(
207 dir : &str,
208 name : &str,
209 f : &F,
210 g : &G,
211 x : M,
212 τ : f64,
213 logmap : impl Fn(usize, IterInfo<M>) -> I
214 ) -> DynError
215 where M : ManifoldPoint
216 + EmbeddedManifoldPoint<EmbeddedCoords = Loc<f64, 3>>
217 + FacedManifoldPoint,
218 F : Desc<M> + Mapping<M, Codomain = f64>,
219 G : Prox<M> + Mapping<M, Codomain = f64>,
220 I : Serialize {
221
222 let mut logger = Logger::new();
131 let iter = AlgIteratorOptions{ 223 let iter = AlgIteratorOptions{
132 max_iter : 20, 224 max_iter : 20,
133 verbose_iter : Verbose::Every(1), 225 verbose_iter : Verbose::Every(1),
134 .. Default::default() 226 .. Default::default()
135 }.mapped(logmap) 227 }.mapped(logmap)
136 .into_log(&mut logger); 228 .into_log(&mut logger);
137 229
138 let x̂ = forward_backward(f, g, x, τ, iter); 230 let x̂ = forward_backward(f, g, x, τ, iter);
139 println!("result = {}\n{:?}", x̂.embedded_coords(), &x̂); 231 println!("result = {}\n{:?}", x̂.embedded_coords(), &x̂);
140 232
141 logger.write_csv(format!("{PREFIX}/{name}_log.csv"))?; 233 logger.write_csv(format!("{dir}/{name}_log.csv"))?;
142 234
143 Ok(()) 235 Ok(())
144 } 236 }
145 237
146 /// Writes the values of `f` on `face` of a [`OnCube`] into a PNG file 238 /// Writes the values of `f` on `face` of a [`OnCube`] into a PNG file
147 /// with resolution `n × n`. 239 /// with resolution `n × n`.
148 fn write_face_imgs(n : usize, mut f : impl FnMut(&OnCube) -> f64) -> DynError { 240 fn write_cube_face_imgs(
241 dir : &str,
242 n : usize,
243 mut f : impl FnMut(&OnCube) -> f64
244 ) -> DynError {
245 use crate::cube::Face;
246
149 let grid = LinSpace { 247 let grid = LinSpace {
150 start : Loc([0.0, 0.0]), 248 start : Loc([0.0, 0.0]),
151 end : Loc([1.0, 1.0]), 249 end : Loc([1.0, 1.0]),
152 count : [n, n] 250 count : [n, n]
153 }; 251 };
173 //let rgb = [(50.0*t).cos(), (20.0*t).sin(), (3.0*t).cos()]; 271 //let rgb = [(50.0*t).cos(), (20.0*t).sin(), (3.0*t).cos()];
174 let rgb = [1.0-t, 1.0-t, 1.0]; 272 let rgb = [1.0-t, 1.0-t, 1.0];
175 *p = Rgb(rgb.map(|v| (v*(u8::RANGE_MAX as f64)) as u8)) 273 *p = Rgb(rgb.map(|v| (v*(u8::RANGE_MAX as f64)) as u8))
176 }); 274 });
177 275
178 img.save_with_format(format!("{PREFIX}/{face}.png"), ImageFormat::Png)?; 276 img.save_with_format(format!("{dir}/{face}.png"), ImageFormat::Png)?;
179 } 277 }
180 278
181 Ok(()) 279 Ok(())
182 } 280 }
183 281
184 /// Writes the values of `f` on `face` of a [`OnCube`] into a CSV file 282 /// Writes the values of `f` on `face` of a [`OnCube`] into a CSV file
185 /// with resolution `n × n`. 283 /// with resolution `n × n`.
186 fn write_face_csv(filename : String, face : Face, n : usize, mut f : impl FnMut(&OnCube) -> f64) -> DynError { 284 fn write_cube_face_csv(
285 filename : String,
286 face : crate::cube::Face,
287 n : usize,
288 mut f : impl FnMut(&OnCube) -> f64
289 ) -> DynError {
187 290
188 #[derive(Serialize)] 291 #[derive(Serialize)]
189 struct CSVFace { u : f64, v : f64, value : f64 } 292 struct CSVFace { u : f64, v : f64, value : f64 }
190 293
191 let grid = LinSpace { 294 let grid = LinSpace {
200 write_csv(data, format!("{filename}.csv"))?; 303 write_csv(data, format!("{filename}.csv"))?;
201 304
202 Ok(()) 305 Ok(())
203 } 306 }
204 307
205 /// Writes a list of points on a [`OnCube`] into a CSV file 308 /// Writes the values of `f` on the faces of a `cylinder`.
206 fn write_points<'a, I : Iterator<Item=&'a OnCube>>(filename : String, list : I) -> DynError { 309 fn write_cylinder_faces_csv<'a>(
207 310 dir : &str,
208 write_csv(list.map(CSVPoint::from), format!("{filename}.csv"))?; 311 cyl : &'a Cylinder,
209 Ok(()) 312 n_height : usize,
210 } 313 n_radius : usize,
211 314 n_angle : usize,
315 mut f : impl FnMut(&OnCylinder<'a>) -> f64
316 ) -> DynError {
317 let π = f64::PI;
318
319 // Side front is [a, b] for the TikZ configuration.
320 let a = -π*5.0/6.0;
321 let b = π/6.0;
322
323 #[derive(Serialize)]
324 struct CSVFace { angle : f64, /*cos_angle : f64, sin_angle : f64,*/ v : f64, value : f64 }
325
326 let mkf = |angle, v, value| CSVFace {
327 angle,
328 //cos_angle : angle.cos(),
329 //sin_angle : angle.sin(),
330 v,
331 value
332 };
333
334 let side_half_grid = LinSpace {
335 start : Loc([0.0, cyl.bottom_z()]),
336 end : Loc([π, cyl.top_z()]),
337 count : [n_angle / 2, n_height]
338 };
339
340 let side_grid = LinSpace {
341 start : Loc([0.0, cyl.bottom_z()]),
342 end : Loc([2.0*π, cyl.top_z()]),
343 count : [n_angle, n_height]
344 };
345 let cap_grid = LinSpace {
346 start : Loc([0.0, 0.0]),
347 end : Loc([2.0*π, cyl.radius]),
348 count : [n_angle, n_radius]
349 };
350
351 let side_front = side_grid.into_iter()
352 .map(|Loc([angle, v])| mkf(angle, v, f(&cyl.on_side(angle, v))));
353 write_csv(side_front, format!("{dir}/side.csv"))?;
354
355 let side_front = side_half_grid.into_iter()
356 .map(|Loc([angle, v])| (normalise_angle(angle + a), v))
357 .map(|(angle, v)| mkf(angle, v, f(&cyl.on_side(angle, v))));
358 write_csv(side_front, format!("{dir}/side_front.csv"))?;
359
360 let side_back = side_half_grid.into_iter()
361 .map(|Loc([angle, v])| (normalise_angle(angle + b), v))
362 .map(|(angle, v)| mkf(angle + π, v, f(&cyl.on_side(angle + π, v))));
363 write_csv(side_back, format!("{dir}/side_back.csv"))?;
364
365 let top = cap_grid.into_iter()
366 .map(|Loc([angle, v])| mkf(angle, v, f(&cyl.on_top(angle, v))));
367 write_csv(top, format!("{dir}/top.csv"))?;
368
369 let bottom = cap_grid.into_iter()
370 .map(|Loc([angle, v])| mkf(angle, v, f(&cyl.on_bottom(angle, v))));
371 write_csv(bottom, format!("{dir}/bottom.csv"))
372
373 }
374

mercurial