| 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 }; |
| 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 |