diff -r aa6129697116 -r d7cd14b8ccc0 src/main.rs --- a/src/main.rs Thu Nov 07 16:52:05 2024 -0500 +++ b/src/main.rs Wed Dec 04 23:19:46 2024 -0500 @@ -11,9 +11,11 @@ mod manifold; mod fb; mod cube; +mod cylinder; mod dist; mod zero; mod scaled; +mod newton; use serde::{Serialize, Deserialize}; use alg_tools::logger::Logger; @@ -29,55 +31,61 @@ use dist::{DistTo, DistToSquaredDiv2}; use fb::{forward_backward, IterInfo, Desc, Prox}; -use manifold::EmbeddedManifoldPoint; -use cube::*; -use Face::*; +use manifold::{EmbeddedManifoldPoint, ManifoldPoint, FacedManifoldPoint}; +use cube::OnCube; +use cylinder::{CylCoords, Cylinder, CylinderConfig, OnCylinder, normalise_angle}; #[allow(unused_imports)] use zero::ZeroFn; use scaled::Scaled; +/// Location for saving results +static PREFIX : &str = "res"; + /// Program entry point fn main() { - simple_cube_test().unwrap() + simple_cube_test(format!("{PREFIX}/cube").as_str()).unwrap(); + simple_cylinder_test(format!("{PREFIX}/cylinder").as_str()).unwrap(); } /// Helper structure for saving a point on a cube into a CSV file #[derive(Serialize,Deserialize,Debug)] -struct CSVPoint { +struct CSVPoint { face : Face, x : f64, y : f64, z : f64 } -impl From<&OnCube> for CSVPoint { - fn from(point : &OnCube) -> Self { +impl From<&M> for CSVPoint +where M : EmbeddedManifoldPoint> + FacedManifoldPoint { + fn from(point : &M) -> Self { let Loc([x,y,z]) = point.embedded_coords(); let face = point.face(); CSVPoint { face, x, y, z } } } -/// Helper structure for saving the log into a CSV file +/// Helper structure for saving a point on a cylinder into a CSV file #[derive(Serialize,Deserialize,Debug)] -struct CSVLog { - iter : usize, - value : f64, - // serde is junk - //#[serde(flatten)] - //point : CSVPoint +struct CSVCylPoint { face : Face, - x : f64, - y : f64, + angle : f64, + r : f64, z : f64 } - -/// Location for saving results -static PREFIX : &str = "res"; +impl<'a> From<&'a crate::cylinder::OnCylinder<'a>> for CSVCylPoint { + fn from(point : &'a OnCylinder<'a>) -> Self { + let CylCoords {r, angle, z} = point.cyl_coords(); + let face = point.face(); + CSVCylPoint { face, r, angle, z } + } +} /// A simple test on the cube -fn simple_cube_test() -> DynError { +fn simple_cube_test(dir : &str) -> DynError { + use crate::cube::Face; + use crate::cube::Face::*; let points = [ OnCube::new(F1, Loc([0.5, 0.7])), @@ -90,37 +98,34 @@ let origin = OnCube::new(F4, Loc([0.5, 0.5])); - write_points(format!("{PREFIX}/data"), points.iter())?; - write_points(format!("{PREFIX}/origin"), std::iter::once(&origin))?; + std::fs::create_dir_all(dir)?; + write_csv(points.iter().map(CSVPoint::from), format!("{dir}/data.csv"))?; + write_csv(std::iter::once(&origin).map(CSVPoint::from), format!("{dir}/origin.csv"))?; let f = Sum::new(points.into_iter().map(DistToSquaredDiv2)); //let g = ZeroFn::new(); let g = Scaled::new(0.5, DistTo(origin)); let τ = 0.1; - std::fs::create_dir_all(PREFIX)?; for face in Face::all() { - write_face_csv(format!("{PREFIX}/{face}"), face, 32, |x| f.apply(x) + g.apply(x))?; + write_cube_face_csv(format!("{dir}/{face}"), face, 32, |x| f.apply(x) + g.apply(x))?; } - write_face_imgs(128, |x| f.apply(x) + g.apply(x))?; - - run_and_save("x1", &f, &g, OnCube::new(F3, Loc([0.1, 0.7])), τ)?; - run_and_save("x2", &f, &g, OnCube::new(F2, Loc([0.3, 0.1])), τ)?; - run_and_save("x3", &f, &g, OnCube::new(F6, Loc([0.6, 0.2])), τ) -} + write_cube_face_imgs(dir, 128, |x| f.apply(x) + g.apply(x))?; -/// Runs [forward_backward] and saves the results. -pub fn run_and_save( - name : &str, - f : &F, - g : &G, - x : OnCube, - τ : f64, -) -> DynError -where F : Desc + Mapping, - G : Prox + Mapping { - - let mut logger = Logger::new(); + /// Helper structure for saving the log into a CSV file + #[derive(Serialize,Deserialize,Debug)] + struct CSVLog { + iter : usize, + value : f64, + // serde is junk + //#[serde(flatten)] + //point : CSVPoint + face : Face, + x : f64, + y : f64, + z : f64 + } + let logmap = |iter, IterInfo { value, point } : IterInfo| { let CSVPoint {x , y, z, face} = CSVPoint::from(&point); CSVLog { @@ -128,6 +133,93 @@ x, y, z, face } }; + + run_and_save(dir, "x1", &f, &g, OnCube::new(F3, Loc([0.1, 0.7])), τ, logmap)?; + run_and_save(dir, "x2", &f, &g, OnCube::new(F2, Loc([0.3, 0.1])), τ, logmap)?; + run_and_save(dir, "x3", &f, &g, OnCube::new(F6, Loc([0.6, 0.2])), τ, logmap) +} + + +/// A simple test on the cube +fn simple_cylinder_test(dir : &str) -> DynError { + let π = f64::PI; + + let cyl = Cylinder { + radius : 1.0, + height : 1.0, + config : CylinderConfig { newton_iters : 100 }, + //Default::default(), + }; + + let points = [ + cyl.on_side(π/2.0, 0.4), + cyl.on_side(π*3.0/2.0, -0.1), + cyl.on_side(π, 0.2), + cyl.on_side(-π/5.0, -0.2), + cyl.on_side(π*2.0/3.0, -0.45), + cyl.on_top(π*3.0/4.0, 0.7), + cyl.on_bottom(π*5.0/4.0, 0.3), + ]; + + let origin = cyl.on_side(0.0, 0.0); + + std::fs::create_dir_all(dir)?; + write_csv(points.iter().map(CSVCylPoint::from), format!("{dir}/data.csv"))?; + write_csv(std::iter::once(&origin).map(CSVCylPoint::from), format!("{dir}/origin.csv"))?; + + let f = Sum::new(points.into_iter().map(DistToSquaredDiv2)); + let g = Scaled::new(4.0, DistTo(origin)); + let τ = 0.1; + + write_cylinder_faces_csv(dir, &cyl, 32, 32, 32, |x| f.apply(x) + g.apply(x))?; + + /// Helper structure for saving the log into a CSV file + #[derive(Serialize,Deserialize,Debug)] + struct CSVLog { + iter : usize, + value : f64, + // serde is junk + //#[serde(flatten)] + //point : CSVCylPoint + face : Face, + angle : f64, + r : f64, + z : f64, + } + + let logmap = |iter, IterInfo { value, point } : IterInfo>| { + let CSVCylPoint {r, angle, z, face} = CSVCylPoint::from(&point); + CSVLog { + iter, value, //point : CSVPoint::from(&point) + r, angle, z, face + } + }; + + run_and_save(dir, "x1", &f, &g, cyl.on_top(0.0, 0.0), τ, logmap)?; + run_and_save(dir, "x2", &f, &g, cyl.on_side(-π*2.0/5.0, 0.0), τ, logmap)?; + run_and_save(dir, "x3", &f, &g, cyl.on_bottom(-π*0.5, 0.8), τ, logmap) + +} + + +/// Runs [forward_backward] and saves the results. +pub fn run_and_save( + dir : &str, + name : &str, + f : &F, + g : &G, + x : M, + τ : f64, + logmap : impl Fn(usize, IterInfo) -> I +) -> DynError +where M : ManifoldPoint + + EmbeddedManifoldPoint> + + FacedManifoldPoint, + F : Desc + Mapping, + G : Prox + Mapping, + I : Serialize { + + let mut logger = Logger::new(); let iter = AlgIteratorOptions{ max_iter : 20, verbose_iter : Verbose::Every(1), @@ -138,14 +230,20 @@ let x̂ = forward_backward(f, g, x, τ, iter); println!("result = {}\n{:?}", x̂.embedded_coords(), &x̂); - logger.write_csv(format!("{PREFIX}/{name}_log.csv"))?; + logger.write_csv(format!("{dir}/{name}_log.csv"))?; Ok(()) } /// Writes the values of `f` on `face` of a [`OnCube`] into a PNG file /// with resolution `n × n`. -fn write_face_imgs(n : usize, mut f : impl FnMut(&OnCube) -> f64) -> DynError { +fn write_cube_face_imgs( + dir : &str, + n : usize, + mut f : impl FnMut(&OnCube) -> f64 +) -> DynError { + use crate::cube::Face; + let grid = LinSpace { start : Loc([0.0, 0.0]), end : Loc([1.0, 1.0]), @@ -175,7 +273,7 @@ *p = Rgb(rgb.map(|v| (v*(u8::RANGE_MAX as f64)) as u8)) }); - img.save_with_format(format!("{PREFIX}/{face}.png"), ImageFormat::Png)?; + img.save_with_format(format!("{dir}/{face}.png"), ImageFormat::Png)?; } Ok(()) @@ -183,7 +281,12 @@ /// Writes the values of `f` on `face` of a [`OnCube`] into a CSV file /// with resolution `n × n`. -fn write_face_csv(filename : String, face : Face, n : usize, mut f : impl FnMut(&OnCube) -> f64) -> DynError { +fn write_cube_face_csv( + filename : String, + face : crate::cube::Face, + n : usize, + mut f : impl FnMut(&OnCube) -> f64 +) -> DynError { #[derive(Serialize)] struct CSVFace { u : f64, v : f64, value : f64 } @@ -202,10 +305,70 @@ Ok(()) } -/// Writes a list of points on a [`OnCube`] into a CSV file -fn write_points<'a, I : Iterator>(filename : String, list : I) -> DynError { +/// Writes the values of `f` on the faces of a `cylinder`. +fn write_cylinder_faces_csv<'a>( + dir : &str, + cyl : &'a Cylinder, + n_height : usize, + n_radius : usize, + n_angle : usize, + mut f : impl FnMut(&OnCylinder<'a>) -> f64 +) -> DynError { + let π = f64::PI; + + // Side front is [a, b] for the TikZ configuration. + let a = -π*5.0/6.0; + let b = π/6.0; + + #[derive(Serialize)] + struct CSVFace { angle : f64, /*cos_angle : f64, sin_angle : f64,*/ v : f64, value : f64 } + + let mkf = |angle, v, value| CSVFace { + angle, + //cos_angle : angle.cos(), + //sin_angle : angle.sin(), + v, + value + }; + + let side_half_grid = LinSpace { + start : Loc([0.0, cyl.bottom_z()]), + end : Loc([π, cyl.top_z()]), + count : [n_angle / 2, n_height] + }; - write_csv(list.map(CSVPoint::from), format!("{filename}.csv"))?; - Ok(()) + let side_grid = LinSpace { + start : Loc([0.0, cyl.bottom_z()]), + end : Loc([2.0*π, cyl.top_z()]), + count : [n_angle, n_height] + }; + let cap_grid = LinSpace { + start : Loc([0.0, 0.0]), + end : Loc([2.0*π, cyl.radius]), + count : [n_angle, n_radius] + }; + + let side_front = side_grid.into_iter() + .map(|Loc([angle, v])| mkf(angle, v, f(&cyl.on_side(angle, v)))); + write_csv(side_front, format!("{dir}/side.csv"))?; + + let side_front = side_half_grid.into_iter() + .map(|Loc([angle, v])| (normalise_angle(angle + a), v)) + .map(|(angle, v)| mkf(angle, v, f(&cyl.on_side(angle, v)))); + write_csv(side_front, format!("{dir}/side_front.csv"))?; + + let side_back = side_half_grid.into_iter() + .map(|Loc([angle, v])| (normalise_angle(angle + b), v)) + .map(|(angle, v)| mkf(angle + π, v, f(&cyl.on_side(angle + π, v)))); + write_csv(side_back, format!("{dir}/side_back.csv"))?; + + let top = cap_grid.into_iter() + .map(|Loc([angle, v])| mkf(angle, v, f(&cyl.on_top(angle, v)))); + write_csv(top, format!("{dir}/top.csv"))?; + + let bottom = cap_grid.into_iter() + .map(|Loc([angle, v])| mkf(angle, v, f(&cyl.on_bottom(angle, v)))); + write_csv(bottom, format!("{dir}/bottom.csv")) + }