# HG changeset patch # User Tuomo Valkonen # Date 1772123923 18000 # Node ID 4f468d35fa29d3152b6f932ea82afc208bf94b92 # Parent 9738b51d90d7481bc3291fa0535a799eb7cd0951 General forward operators, separation of measures into own crate, and other architecture improvements to support the pointsource_pde crate. diff -r 9738b51d90d7 -r 4f468d35fa29 .cargo/config.toml --- a/.cargo/config.toml Sun Apr 27 15:03:51 2025 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,2 +0,0 @@ -[target.'cfg(all(target_os = "macos"))'] -rustflags = ["-L", "/opt/homebrew/include"] diff -r 9738b51d90d7 -r 4f468d35fa29 .gitignore --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/.gitignore Thu Feb 26 11:38:43 2026 -0500 @@ -0,0 +1,1 @@ +.hgignore \ No newline at end of file diff -r 9738b51d90d7 -r 4f468d35fa29 .hgignore --- a/.hgignore Sun Apr 27 15:03:51 2025 -0500 +++ b/.hgignore Thu Feb 26 11:38:43 2026 -0500 @@ -1,6 +1,9 @@ -^target/ -^debug_out/ -^pointsource.._.*\.txt +syntax:glob +out/ +test/ +target/ +debug_out/ +**/pointsource??_*.txt flamegraph.svg DEADJOE -.*\.orig +**/*.orig diff -r 9738b51d90d7 -r 4f468d35fa29 Cargo.lock --- a/Cargo.lock Sun Apr 27 15:03:51 2025 -0500 +++ b/Cargo.lock Thu Feb 26 11:38:43 2026 -0500 @@ -45,6 +45,7 @@ "num-traits", "numeric_literals", "rayon", + "rustc_version", "serde", "serde_json", "simba", @@ -379,9 +380,7 @@ [[package]] name = "float_extras" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b22b70f8649ea2315955f1a36d964b0e4da482dfaa5f0d04df0d1fb7c338ab7a" +version = "0.1.7" dependencies = [ "libc", ] @@ -394,16 +393,113 @@ [[package]] name = "getrandom" -version = "0.2.10" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "wasi", + "r-efi", + "wasip2", ] [[package]] +name = "glam" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "333928d5eb103c5d4050533cec0384302db6be8ef7d3cebd30ec6a35350353da" + +[[package]] +name = "glam" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3abb554f8ee44336b72d522e0a7fe86a29e09f839a36022fa869a7dfe941a54b" + +[[package]] +name = "glam" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4126c0479ccf7e8664c36a2d719f5f2c140fbb4f9090008098d2c291fa5b3f16" + +[[package]] +name = "glam" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01732b97afd8508eee3333a541b9f7610f454bb818669e66e90f5f57c93a776" + +[[package]] +name = "glam" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525a3e490ba77b8e326fb67d4b44b4bd2f920f44d4cc73ccec50adc68e3bee34" + +[[package]] +name = "glam" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b8509e6791516e81c1a630d0bd7fbac36d2fa8712a9da8662e716b52d5051ca" + +[[package]] +name = "glam" +version = "0.20.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f43e957e744be03f5801a55472f593d43fabdebf25a4585db250f04d86b1675f" + +[[package]] +name = "glam" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "518faa5064866338b013ff9b2350dc318e14cc4fcd6cb8206d7e7c9886c98815" + +[[package]] +name = "glam" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f597d56c1bd55a811a1be189459e8fad2bbc272616375602443bdfb37fa774" + +[[package]] +name = "glam" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e4afd9ad95555081e109fe1d21f2a30c691b5f0919c67dfa690a2e1eb6bd51c" + +[[package]] +name = "glam" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5418c17512bdf42730f9032c74e1ae39afc408745ebb2acf72fbc4691c17945" + +[[package]] +name = "glam" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" + +[[package]] +name = "glam" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" + +[[package]] +name = "glam" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "779ae4bf7e8421cf91c0b3b64e7e8b40b862fba4d393f59150042de7c4965a94" + +[[package]] +name = "glam" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8babf46d4c1c9d92deac9f7be466f76dfc4482b6452fc5024b5e8daf6ffeb3ee" + +[[package]] +name = "glam" +version = "0.30.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd47b05dddf0005d850e5644cae7f2b14ac3df487979dbfff3b56f20b1a6ae46" + +[[package]] name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -517,9 +613,9 @@ [[package]] name = "libc" -version = "0.2.149" +version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "libm" @@ -550,6 +646,17 @@ ] [[package]] +name = "measures" +version = "0.1.0" +dependencies = [ + "alg_tools", + "nalgebra", + "numeric_literals", + "regex", + "serde", +] + +[[package]] name = "memchr" version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -557,11 +664,27 @@ [[package]] name = "nalgebra" -version = "0.33.2" +version = "0.34.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26aecdf64b707efd1310e3544d709c5c0ac61c13756046aaaba41be5c4f66a3b" +checksum = "c4d5b3eff5cd580f93da45e64715e8c20a3996342f1e466599cf7a267a0c2f5f" dependencies = [ "approx", + "glam 0.14.0", + "glam 0.15.2", + "glam 0.16.0", + "glam 0.17.3", + "glam 0.18.0", + "glam 0.19.0", + "glam 0.20.5", + "glam 0.21.3", + "glam 0.22.0", + "glam 0.23.0", + "glam 0.24.2", + "glam 0.25.0", + "glam 0.27.0", + "glam 0.28.0", + "glam 0.29.3", + "glam 0.30.9", "matrixmultiply", "nalgebra-macros", "num-complex", @@ -574,9 +697,9 @@ [[package]] name = "nalgebra-macros" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" +checksum = "973e7178a678cfd059ccec50887658d482ce16b0aa9da3888ddeab5cd5eb4889" dependencies = [ "proc-macro2", "quote", @@ -676,9 +799,9 @@ [[package]] name = "once_cell" -version = "1.20.2" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "paste" @@ -688,9 +811,9 @@ [[package]] name = "pkg-config" -version = "0.3.27" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "pointsource_algs" @@ -705,6 +828,7 @@ "cpu-time", "float_extras", "itertools", + "measures", "nalgebra", "num-traits", "numeric_literals", @@ -714,6 +838,7 @@ "serde", "serde_json", "serde_with", + "thiserror", ] [[package]] @@ -747,21 +872,26 @@ ] [[package]] -name = "rand" -version = "0.8.5" +name = "r-efi" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "libc", "rand_chacha", "rand_core", ] [[package]] name = "rand_chacha" -version = "0.3.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", "rand_core", @@ -769,18 +899,18 @@ [[package]] name = "rand_core" -version = "0.6.4" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" dependencies = [ "getrandom", ] [[package]] name = "rand_distr" -version = "0.4.3" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" dependencies = [ "num-traits", "rand", @@ -842,6 +972,15 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] name = "rustix" version = "0.38.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -870,6 +1009,12 @@ ] [[package]] +name = "semver" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" + +[[package]] name = "serde" version = "1.0.189" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -988,6 +1133,26 @@ ] [[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.93", +] + +[[package]] name = "time" version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -1058,10 +1223,13 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "wasip2" +version = "1.0.1+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] [[package]] name = "wasm-bindgen" @@ -1296,3 +1464,9 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" diff -r 9738b51d90d7 -r 4f468d35fa29 Cargo.toml --- a/Cargo.toml Sun Apr 27 15:03:51 2025 -0500 +++ b/Cargo.toml Thu Feb 26 11:38:43 2026 -0500 @@ -17,36 +17,52 @@ "pdps", "fista", "frank-wolfe", - "conditional gradient" + "conditional gradient", ] categories = ["mathematics", "science", "computer-vision"] [dependencies.alg_tools] version = "~0.4.0-dev" path = "../alg_tools" -default-features = false +default-features = false features = ["nightly"] +[dependencies.measures] +version = "~0.1.0" +path = "../measures" + [dependencies] serde = { version = "1.0", features = ["derive"] } num-traits = { version = "~0.2.14", features = ["std"] } -rand = "~0.8.5" +rand = "~0.9.2" colored = "~2.1.0" -rand_distr = "~0.4.3" -nalgebra = { version = "~0.33.0", features = ["rand-no-std"] } +rand_distr = "~0.5.1" +nalgebra = { version = "~0.34.0", features = ["rand-no-std"] } itertools = "~0.13.0" numeric_literals = "~0.2.0" GSL = "~7.0.0" -float_extras = "~0.1.6" +float_extras = { path = "../float_extras"} clap = { version = "~4.5.0", features = ["derive", "unicode", "wrap_help"] } -cpu-time = "~1.0.0" +cpu-time = "1.0.0" serde_json = "~1.0.85" chrono = { version = "~0.4.23", features = ["alloc", "std", "serde"] } anyhow = "1.0.95" serde_with = { version = "3.11.0", features = ["macros"] } +thiserror = "2.0.12" + +[features] +default = [] [build-dependencies] regex = "~1.11.0" [profile.release] debug = true + +[lib] +name = "pointsource_algs" +path = "src/lib.rs" + +[[bin]] +name = "pointsource_experiments" +path = "src/main.rs" diff -r 9738b51d90d7 -r 4f468d35fa29 README.md --- a/README.md Sun Apr 27 15:03:51 2025 -0500 +++ b/README.md Thu Feb 26 11:38:43 2026 -0500 @@ -36,8 +36,9 @@ brew install gsl ``` For other operating systems, suggestions are available in the [rust-GSL] - crate documentation. You may need to pass extra `RUSTFLAGS` options to - Cargo in the following steps to locate the library. + crate documentation. If not correctly installed, you may need to pass + extra `RUSTFLAGS` options to Cargo in the following steps to locate the + library. 4. Download [alg_tools] and unpack it under the same directory as this package. @@ -61,7 +62,7 @@ When doing this for the first time, several dependencies will be downloaded. Now you can run the default set of experiments with ``` -pointsource_algs -o results +pointsource_experiments -o results ``` The `-o results` option tells `pointsource_algs` to write results in the `results` directory. The option is required. @@ -71,7 +72,7 @@ cargo run --release -- -o results ``` The double-dash separates the options for the Cargo build system -and `pointsource_algs`. +and `pointsource_experiments`. ### Documentation diff -r 9738b51d90d7 -r 4f468d35fa29 build.rs --- a/build.rs Sun Apr 27 15:03:51 2025 -0500 +++ b/build.rs Thu Feb 26 11:38:43 2026 -0500 @@ -1,20 +1,39 @@ +use regex::{Captures, Regex}; use std::env; -use regex::{Regex, Captures}; -fn proc>(re : &str, str : A) -> String { - let need_to_escape = Regex::new(r"([_*\\])").unwrap(); - Regex::new(re).unwrap().replace_all(str.as_ref(), |caps : &Captures| { - format!("{}{}{}", - caps.get(1).unwrap().as_str(), - need_to_escape.replace_all(caps.get(2).unwrap().as_str(), "\\$1"), - caps.get(3).unwrap().as_str() - ) - }).to_string() +fn main() { + process_readme(); + // Does not seem to be needed now. + //discover_gsl(); } -fn main() { +/* +/// Discover how to link to gsl, as the gsl crate does not provide this information +fn discover_gsl() { + pkg_config::Config::new().probe("gsl").unwrap(); +} +*/ + +/// `\`-escape `_`, `*`, and ´\\` in matches of `re` within `str`. +fn proc>(re: &str, str: A) -> String { + let need_to_escape = Regex::new(r"([_*\\])").unwrap(); + Regex::new(re) + .unwrap() + .replace_all(str.as_ref(), |caps: &Captures| { + format!( + "{}{}{}", + caps.get(1).unwrap().as_str(), + need_to_escape.replace_all(caps.get(2).unwrap().as_str(), "\\$1"), + caps.get(3).unwrap().as_str() + ) + }) + .to_string() +} + +/// Process the README for inclusion in documentation +fn process_readme() { let out_dir = env::var("OUT_DIR").unwrap(); - + // Since rust is stuck in 80's 7-bit gringo ASCII world, so that rustdoc does not support // markdown KaTeX mathematics, we have to process the README to include horrible horrible // horrible escapes for the math, and then use an vomit-inducingly ugly javasccript @@ -22,12 +41,13 @@ println!("cargo:rerun-if-changed=README.md"); - let readme = std::fs::read_to_string("README.md") - .expect("Error reading README"); + let readme = std::fs::read_to_string("README.md").expect("Error reading README"); // Escape _, *, and \ in equations. - let readme_uglified = proc(r"(?m)([^$]\$)([^$]+)(\$[^$])", - proc(r"([^$]\$\$)([^$]+)(\$\$[^$])", readme)); + let readme_uglified = proc( + r"(?m)([^$]\$)([^$]+)(\$[^$])", + proc(r"([^$]\$\$)([^$]+)(\$\$[^$])", readme), + ); // Remove the instructions for building the documentation let readme_cut = Regex::new("## Internals(.*|\n)*") .unwrap() diff -r 9738b51d90d7 -r 4f468d35fa29 rustfmt.toml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/rustfmt.toml Thu Feb 26 11:38:43 2026 -0500 @@ -0,0 +1,3 @@ +overflow_delimited_expr = true +struct_lit_width = 80 + diff -r 9738b51d90d7 -r 4f468d35fa29 src/dataterm.rs --- a/src/dataterm.rs Sun Apr 27 15:03:51 2025 -0500 +++ b/src/dataterm.rs Thu Feb 26 11:38:43 2026 -0500 @@ -2,30 +2,30 @@ Basid definitions for data terms */ -use numeric_literals::replace_float_literals; +//use numeric_literals::replace_float_literals; -use alg_tools::euclidean::Euclidean; -use alg_tools::linops::GEMV; -pub use alg_tools::norms::L1; -use alg_tools::norms::Norm; -use alg_tools::instance::{Instance, Space}; +use alg_tools::convex::Norm222; +//use alg_tools::euclidean::Euclidean; +//use alg_tools::instance::{Instance, Space}; +//use alg_tools::linops::GEMV; +use alg_tools::mapping::DataTerm; +use alg_tools::norms::{NormMapping, L1}; -use crate::types::*; -pub use crate::types::L2Squared; -use crate::measures::RNDM; +//use crate::types::*; +/* /// Calculates the residual $Aμ-b$. #[replace_float_literals(F::cast_from(literal))] pub(crate) fn calculate_residual< - X : Space, - I : Instance, - F : Float, - V : Euclidean + Clone, - A : GEMV, + X: Space, + I: Instance, + F: Float, + V: Euclidean + Clone, + A: GEMV, >( - μ : I, - opA : &A, - b : &V + μ: I, + opA: &A, + b: &V, ) -> V { let mut r = b.clone(); opA.gemv(&mut r, 1.0, μ, -1.0); @@ -35,60 +35,24 @@ /// Calculates the residual $A(μ+μ_delta)-b$. #[replace_float_literals(F::cast_from(literal))] pub(crate) fn calculate_residual2< - F : Float, - X : Space, - I : Instance, - J : Instance, - V : Euclidean + Clone, - A : GEMV, + F: Float, + X: Space, + I: Instance, + J: Instance, + V: Euclidean + Clone, + A: GEMV, >( - μ : I, - μ_delta : J, - opA : &A, - b : &V + μ: I, + μ_delta: J, + opA: &A, + b: &V, ) -> V { let mut r = b.clone(); opA.gemv(&mut r, 1.0, μ, -1.0); opA.gemv(&mut r, 1.0, μ_delta, 1.0); r } - - -/// Trait for data terms -#[replace_float_literals(F::cast_from(literal))] -pub trait DataTerm { - /// Calculates $F(y)$, where $F$ is the data fidelity. - fn calculate_fit(&self, _residual : &V) -> F; +*/ - /// Calculates $F(Aμ-b)$, where $F$ is the data fidelity. - fn calculate_fit_op, Codomain = V>>( - &self, - μ : I, - opA : &A, - b : &V - ) -> F - where - V : Euclidean + Clone, - I : Instance>, - { - let r = calculate_residual(μ, opA, b); - self.calculate_fit(&r) - } -} - -impl, const N : usize> -DataTerm -for L2Squared { - fn calculate_fit(&self, residual : &V) -> F { - residual.norm2_squared_div2() - } -} - - -impl + Norm, const N : usize> -DataTerm -for L1 { - fn calculate_fit(&self, residual : &V) -> F { - residual.norm(L1) - } -} +pub type L1DataTerm = DataTerm>; +pub type QuadraticDataTerm = DataTerm>; diff -r 9738b51d90d7 -r 4f468d35fa29 src/experiments.rs --- a/src/experiments.rs Sun Apr 27 15:03:51 2025 -0500 +++ b/src/experiments.rs Thu Feb 26 11:38:43 2026 -0500 @@ -3,37 +3,30 @@ */ //use numeric_literals::replace_float_literals; -use serde::{Serialize, Deserialize}; -use clap::ValueEnum; -use std::collections::HashMap; -use std::hash::{Hash, Hasher}; -use std::collections::hash_map::DefaultHasher; - +use crate::kernels::SupportProductFirst as Prod; +use crate::kernels::*; +use crate::run::{DefaultAlgorithm, ExperimentBiased, ExperimentV2, Named, RunnableExperiment}; +use crate::types::*; +use crate::{AlgorithmOverrides, ExperimentSetup}; use alg_tools::bisection_tree::*; use alg_tools::error::DynResult; use alg_tools::norms::Linfinity; - -use crate::{ExperimentOverrides, AlgorithmOverrides}; -use crate::kernels::*; -use crate::kernels::SupportProductFirst as Prod; -use crate::types::*; -use crate::run::{ - RunnableExperiment, - ExperimentV2, - ExperimentBiased, - Named, - DefaultAlgorithm, -}; -//use crate::fb::FBGenericConfig; -use crate::rand_distr::{SerializableNormal, SaltAndPepper}; +use clap::{Parser, ValueEnum}; +use serde::{Deserialize, Serialize}; +use std::collections::hash_map::DefaultHasher; +use std::collections::HashMap; +use std::hash::{Hash, Hasher}; +//use crate::fb::InsertionConfig; +use crate::rand_distr::{SaltAndPepper, SerializableNormal}; use crate::regularisation::Regularisation; use alg_tools::euclidean::Euclidean; use alg_tools::instance::Instance; use alg_tools::mapping::Mapping; use alg_tools::operator_arithmetic::{MappingSum, Weighted}; +use itertools::Itertools; +use serde_with::skip_serializing_none; /// Experiments shorthands, to be used with the command line parser - #[derive(ValueEnum, Debug, Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)] #[allow(non_camel_case_types)] pub enum DefaultExperiment { @@ -69,49 +62,77 @@ Experiment2D_TV_Fast, } +/// Command line experiment setup overrides +#[skip_serializing_none] +#[derive(Parser, Debug, Serialize, Deserialize, Default, Clone, Hash)] +pub struct DefaultExperimentSetup { + /// List of experiments to perform + #[arg(value_name = "EXPERIMENT")] + experiments: Vec, + + #[arg(long)] + /// Regularisation parameter override. + /// + /// Only use if running just a single experiment, as different experiments have different + /// regularisation parameters. + alpha: Option, + + #[arg(long)] + /// Gaussian noise variance override + variance: Option, + + #[arg(long, value_names = &["MAGNITUDE", "PROBABILITY"])] + /// Salt and pepper noise override. + salt_and_pepper: Option>, + + #[arg(long)] + /// Noise seed + noise_seed: Option, +} + macro_rules! make_float_constant { ($name:ident = $value:expr) => { #[derive(Debug, Copy, Eq, PartialEq, Clone, Serialize, Deserialize)] #[serde(into = "float")] struct $name; - impl Into for $name { + impl Into for $name { #[inline] - fn into(self) -> float { $value } + fn into(self) -> float { + $value + } } impl Constant for $name { type Type = float; - fn value(&self) -> float { $value } + fn value(&self) -> float { + $value + } } - } + }; } /// Ground-truth measure spike locations and magnitudes for 1D experiments -static MU_TRUE_1D_BASIC : [(float, float); 4] = [ - (0.10, 10.0), - (0.30, 2.0), - (0.70, 3.0), - (0.80, 5.0) -]; +static MU_TRUE_1D_BASIC: [(float, float); 4] = + [(0.10, 10.0), (0.30, 2.0), (0.70, 3.0), (0.80, 5.0)]; /// Ground-truth measure spike locations and magnitudes for 2D experiments -static MU_TRUE_2D_BASIC : [([float; 2], float); 4] = [ +static MU_TRUE_2D_BASIC: [([float; 2], float); 4] = [ ([0.15, 0.15], 10.0), ([0.75, 0.45], 2.0), ([0.80, 0.50], 4.0), - ([0.30, 0.70], 5.0) + ([0.30, 0.70], 5.0), ]; /// The $\{0,1\}$-valued characteristic function of a ball as a [`Mapping`]. -#[derive(Debug,Copy,Clone,Serialize,PartialEq)] -struct BallCharacteristic { - pub center : Loc, - pub radius : F, +#[derive(Debug, Copy, Clone, Serialize, PartialEq)] +struct BallCharacteristic { + pub center: Loc, + pub radius: F, } -impl Mapping> for BallCharacteristic { - type Codomain =F; +impl Mapping> for BallCharacteristic { + type Codomain = F; - fn apply>>(&self, i : I) -> F { + fn apply>>(&self, i: I) -> F { if self.center.dist2(i) <= self.radius { F::ONE } else { @@ -120,25 +141,52 @@ } } +/// Trait for customising the experiments available from the command line +impl ExperimentSetup for DefaultExperimentSetup { + type FloatType = f64; + + fn runnables(&self) -> DynResult>>> { + self.experiments + .iter() + .unique() + .map(|e| e.get_experiment(self)) + .try_collect() + } +} + //#[replace_float_literals(F::cast_from(literal))] impl DefaultExperiment { + // fn default_list() -> Vec { + // use DefaultExperiment::*; + // [ + // Experiment1D, + // Experiment1DFast, + // Experiment2D, + // Experiment2DFast, + // Experiment1D_L1, + // ] + // .into() + // } + /// Convert the experiment shorthand into a runnable experiment configuration. - pub fn get_experiment(&self, cli : &ExperimentOverrides) -> DynResult>> { - let name = "pointsource".to_string() - + self.to_possible_value().unwrap().get_name(); + fn get_experiment( + &self, + cli: &DefaultExperimentSetup, + ) -> DynResult>> { + let name = "pointsource".to_string() + self.to_possible_value().unwrap().get_name(); let kernel_plot_width = 0.2; - const BASE_SEED : u64 = 915373234; + const BASE_SEED: u64 = 915373234; - const N_SENSORS_1D : usize = 100; - make_float_constant!(SensorWidth1D = 0.4/(N_SENSORS_1D as float)); + const N_SENSORS_1D: usize = 100; + make_float_constant!(SensorWidth1D = 0.4 / (N_SENSORS_1D as float)); - const N_SENSORS_2D : usize = 16; - make_float_constant!(SensorWidth2D = 0.4/(N_SENSORS_2D as float)); + const N_SENSORS_2D: usize = 16; + make_float_constant!(SensorWidth2D = 0.4 / (N_SENSORS_2D as float)); - const N_SENSORS_2D_MORE : usize = 32; - make_float_constant!(SensorWidth2DMore = 0.4/(N_SENSORS_2D_MORE as float)); + //const N_SENSORS_2D_MORE: usize = 32; + //make_float_constant!(SensorWidth2DMore = 0.4 / (N_SENSORS_2D_MORE as float)); make_float_constant!(Variance1 = 0.05.powi(2)); make_float_constant!(CutOff1 = 0.15); @@ -160,45 +208,43 @@ // .. Default::default() // } // ); - let sliding_fb_cut_gaussian = (DefaultAlgorithm::SlidingFB, - AlgorithmOverrides { - theta0 : Some(0.3), - .. Default::default() - } - ); + let sliding_fb_cut_gaussian = (DefaultAlgorithm::SlidingFB, AlgorithmOverrides { + theta0: Some(0.3), + ..Default::default() + }); // let higher_cpos = |alg| (alg, // AlgorithmOverrides { // transport_tolerance_pos : Some(1000.0), // .. Default::default() // } // ); - let higher_cpos_merging = |alg| (alg, - AlgorithmOverrides { - transport_tolerance_pos : Some(1000.0), - merge : Some(true), - fitness_merging : Some(true), - .. Default::default() - } - ); - let higher_cpos_merging_steptune = |alg| (alg, - AlgorithmOverrides { - transport_tolerance_pos : Some(1000.0), - theta0 : Some(0.3), - merge : Some(true), - fitness_merging : Some(true), - .. Default::default() - } - ); - let much_higher_cpos_merging_steptune = |alg| (alg, - AlgorithmOverrides { - transport_tolerance_pos : Some(10000.0), - sigma0 : Some(0.15), - theta0 : Some(0.3), - merge : Some(true), - fitness_merging : Some(true), - .. Default::default() - } - ); + let higher_cpos_merging = |alg| { + (alg, AlgorithmOverrides { + transport_tolerance_pos: Some(1000.0), + merge: Some(true), + fitness_merging: Some(true), + ..Default::default() + }) + }; + let higher_cpos_merging_steptune = |alg| { + (alg, AlgorithmOverrides { + transport_tolerance_pos: Some(1000.0), + theta0: Some(0.3), + merge: Some(true), + fitness_merging: Some(true), + ..Default::default() + }) + }; + let much_higher_cpos_merging_steptune = |alg| { + (alg, AlgorithmOverrides { + transport_tolerance_pos: Some(10000.0), + sigma0: Some(0.15), + theta0: Some(0.3), + merge: Some(true), + fitness_merging: Some(true), + ..Default::default() + }) + }; // We add a hash of the experiment name to the configured // noise seed to not use the same noise for different experiments. let mut h = DefaultHasher::new(); @@ -210,238 +256,284 @@ use DefaultExperiment::*; Ok(match self { Experiment1D => { - let base_spread = Gaussian { variance : Variance1 }; - let spread_cutoff = BallIndicator { r : CutOff1, exponent : Linfinity }; - Box::new(Named { name, data : ExperimentV2 { - domain : [[0.0, 1.0]].into(), - sensor_count : [N_SENSORS_1D], - regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.08)), - noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.2))?, - dataterm : DataTerm::L2Squared, - μ_hat : MU_TRUE_1D_BASIC.into(), - sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, - spread : Prod(spread_cutoff, base_spread), - kernel : Prod(AutoConvolution(spread_cutoff), base_spread), - kernel_plot_width, - noise_seed, - default_merge_radius, - algorithm_overrides: HashMap::from([ - sliding_fb_cut_gaussian, - higher_cpos_merging(DefaultAlgorithm::RadonFB), - higher_cpos_merging(DefaultAlgorithm::RadonSlidingFB), - ]), - }}) - }, + let base_spread = Gaussian { variance: Variance1 }; + let spread_cutoff = BallIndicator { r: CutOff1, exponent: Linfinity }; + Box::new(Named { + name, + data: ExperimentV2 { + domain: [[0.0, 1.0]].into(), + sensor_count: [N_SENSORS_1D], + regularisation: Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.08)), + noise_distr: SerializableNormal::new(0.0, cli.variance.unwrap_or(0.2))?, + dataterm: DataTermType::L222, + μ_hat: MU_TRUE_1D_BASIC.into(), + sensor: BallIndicator { r: SensorWidth1D, exponent: Linfinity }, + spread: Prod(spread_cutoff, base_spread), + kernel: Prod(AutoConvolution(spread_cutoff), base_spread), + kernel_plot_width, + noise_seed, + default_merge_radius, + algorithm_overrides: HashMap::from([ + sliding_fb_cut_gaussian, + higher_cpos_merging(DefaultAlgorithm::RadonFB), + higher_cpos_merging(DefaultAlgorithm::RadonSlidingFB), + ]), + }, + }) + } Experiment1DFast => { - let base_spread = HatConv { radius : Hat1 }; - Box::new(Named { name, data : ExperimentV2 { - domain : [[0.0, 1.0]].into(), - sensor_count : [N_SENSORS_1D], - regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.06)), - noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.2))?, - dataterm : DataTerm::L2Squared, - μ_hat : MU_TRUE_1D_BASIC.into(), - sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, - spread : base_spread, - kernel : base_spread, - kernel_plot_width, - noise_seed, - default_merge_radius, - algorithm_overrides: HashMap::from([ - higher_cpos_merging(DefaultAlgorithm::RadonFB), - higher_cpos_merging(DefaultAlgorithm::RadonSlidingFB), - ]), - }}) - }, + let base_spread = HatConv { radius: Hat1 }; + Box::new(Named { + name, + data: ExperimentV2 { + domain: [[0.0, 1.0]].into(), + sensor_count: [N_SENSORS_1D], + regularisation: Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.06)), + noise_distr: SerializableNormal::new(0.0, cli.variance.unwrap_or(0.2))?, + dataterm: DataTermType::L222, + μ_hat: MU_TRUE_1D_BASIC.into(), + sensor: BallIndicator { r: SensorWidth1D, exponent: Linfinity }, + spread: base_spread, + kernel: base_spread, + kernel_plot_width, + noise_seed, + default_merge_radius, + algorithm_overrides: HashMap::from([ + higher_cpos_merging(DefaultAlgorithm::RadonFB), + higher_cpos_merging(DefaultAlgorithm::RadonSlidingFB), + ]), + }, + }) + } Experiment2D => { - let base_spread = Gaussian { variance : Variance1 }; - let spread_cutoff = BallIndicator { r : CutOff1, exponent : Linfinity }; - Box::new(Named { name, data : ExperimentV2 { - domain : [[0.0, 1.0]; 2].into(), - sensor_count : [N_SENSORS_2D; 2], - regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.19)), - noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.25))?, - dataterm : DataTerm::L2Squared, - μ_hat : MU_TRUE_2D_BASIC.into(), - sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, - spread : Prod(spread_cutoff, base_spread), - kernel : Prod(AutoConvolution(spread_cutoff), base_spread), - kernel_plot_width, - noise_seed, - default_merge_radius, - algorithm_overrides: HashMap::from([ - sliding_fb_cut_gaussian, - higher_cpos_merging(DefaultAlgorithm::RadonFB), - higher_cpos_merging(DefaultAlgorithm::RadonSlidingFB), - ]), - }}) - }, + let base_spread = Gaussian { variance: Variance1 }; + let spread_cutoff = BallIndicator { r: CutOff1, exponent: Linfinity }; + Box::new(Named { + name, + data: ExperimentV2 { + domain: [[0.0, 1.0]; 2].into(), + sensor_count: [N_SENSORS_2D; 2], + regularisation: Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.19)), + noise_distr: SerializableNormal::new(0.0, cli.variance.unwrap_or(0.25))?, + dataterm: DataTermType::L222, + μ_hat: MU_TRUE_2D_BASIC.into(), + sensor: BallIndicator { r: SensorWidth2D, exponent: Linfinity }, + spread: Prod(spread_cutoff, base_spread), + kernel: Prod(AutoConvolution(spread_cutoff), base_spread), + kernel_plot_width, + noise_seed, + default_merge_radius, + algorithm_overrides: HashMap::from([ + sliding_fb_cut_gaussian, + higher_cpos_merging(DefaultAlgorithm::RadonFB), + higher_cpos_merging(DefaultAlgorithm::RadonSlidingFB), + ]), + }, + }) + } Experiment2DFast => { - let base_spread = HatConv { radius : Hat1 }; - Box::new(Named { name, data : ExperimentV2 { - domain : [[0.0, 1.0]; 2].into(), - sensor_count : [N_SENSORS_2D; 2], - regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.12)), - noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.15))?, //0.25 - dataterm : DataTerm::L2Squared, - μ_hat : MU_TRUE_2D_BASIC.into(), - sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, - spread : base_spread, - kernel : base_spread, - kernel_plot_width, - noise_seed, - default_merge_radius, - algorithm_overrides: HashMap::from([ - higher_cpos_merging(DefaultAlgorithm::RadonFB), - higher_cpos_merging(DefaultAlgorithm::RadonSlidingFB), - ]), - }}) - }, - Experiment1D_L1 => { - let base_spread = Gaussian { variance : Variance1 }; - let spread_cutoff = BallIndicator { r : CutOff1, exponent : Linfinity }; - Box::new(Named { name, data : ExperimentV2 { - domain : [[0.0, 1.0]].into(), - sensor_count : [N_SENSORS_1D], - regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.1)), - noise_distr : SaltAndPepper::new( - cli.salt_and_pepper.as_ref().map_or(0.6, |v| v[0]), - cli.salt_and_pepper.as_ref().map_or(0.4, |v| v[1]) - )?, - dataterm : DataTerm::L1, - μ_hat : MU_TRUE_1D_BASIC.into(), - sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, - spread : Prod(spread_cutoff, base_spread), - kernel : Prod(AutoConvolution(spread_cutoff), base_spread), - kernel_plot_width, - noise_seed, - default_merge_radius, - algorithm_overrides: HashMap::new(), - }}) - }, - Experiment1D_L1_Fast => { - let base_spread = HatConv { radius : Hat1 }; - Box::new(Named { name, data : ExperimentV2 { - domain : [[0.0, 1.0]].into(), - sensor_count : [N_SENSORS_1D], - regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.12)), - noise_distr : SaltAndPepper::new( - cli.salt_and_pepper.as_ref().map_or(0.6, |v| v[0]), - cli.salt_and_pepper.as_ref().map_or(0.4, |v| v[1]) - )?, - dataterm : DataTerm::L1, - μ_hat : MU_TRUE_1D_BASIC.into(), - sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, - spread : base_spread, - kernel : base_spread, - kernel_plot_width, - noise_seed, - default_merge_radius, - algorithm_overrides: HashMap::new(), - }}) - }, - Experiment2D_L1 => { - let base_spread = Gaussian { variance : Variance1 }; - let spread_cutoff = BallIndicator { r : CutOff1, exponent : Linfinity }; - Box::new(Named { name, data : ExperimentV2 { - domain : [[0.0, 1.0]; 2].into(), - sensor_count : [N_SENSORS_2D; 2], - regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.35)), - noise_distr : SaltAndPepper::new( - cli.salt_and_pepper.as_ref().map_or(0.8, |v| v[0]), - cli.salt_and_pepper.as_ref().map_or(0.2, |v| v[1]) - )?, - dataterm : DataTerm::L1, - μ_hat : MU_TRUE_2D_BASIC.into(), - sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, - spread : Prod(spread_cutoff, base_spread), - kernel : Prod(AutoConvolution(spread_cutoff), base_spread), - kernel_plot_width, - noise_seed, - default_merge_radius, - algorithm_overrides: HashMap::from([ - ]), - }}) - }, - Experiment2D_L1_Fast => { - let base_spread = HatConv { radius : Hat1 }; - Box::new(Named { name, data : ExperimentV2 { - domain : [[0.0, 1.0]; 2].into(), - sensor_count : [N_SENSORS_2D; 2], - regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.40)), - noise_distr : SaltAndPepper::new( - cli.salt_and_pepper.as_ref().map_or(0.8, |v| v[0]), - cli.salt_and_pepper.as_ref().map_or(0.2, |v| v[1]) - )?, - dataterm : DataTerm::L1, - μ_hat : MU_TRUE_2D_BASIC.into(), - sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, - spread : base_spread, - kernel : base_spread, - kernel_plot_width, - noise_seed, - default_merge_radius, - algorithm_overrides: HashMap::from([ - ]), - }}) - }, - Experiment1D_TV_Fast => { - let base_spread = HatConv { radius : HatBias }; - Box::new(Named { name, data : ExperimentBiased { - λ : 0.02, - bias : MappingSum::new([ - Weighted::new(1.0, BallCharacteristic{ center : 0.3.into(), radius : 0.2 }), - Weighted::new(0.5, BallCharacteristic{ center : 0.6.into(), radius : 0.3 }), - ]), - base : ExperimentV2 { - domain : [[0.0, 1.0]].into(), - sensor_count : [N_SENSORS_1D], - regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.2)), - noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.1))?, - dataterm : DataTerm::L2Squared, - μ_hat : MU_TRUE_1D_BASIC.into(), - sensor : BallIndicator { r : SensorWidth1D, exponent : Linfinity }, - spread : base_spread, - kernel : base_spread, + let base_spread = HatConv { radius: Hat1 }; + Box::new(Named { + name, + data: ExperimentV2 { + domain: [[0.0, 1.0]; 2].into(), + sensor_count: [N_SENSORS_2D; 2], + regularisation: Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.12)), + noise_distr: SerializableNormal::new(0.0, cli.variance.unwrap_or(0.15))?, //0.25 + dataterm: DataTermType::L222, + μ_hat: MU_TRUE_2D_BASIC.into(), + sensor: BallIndicator { r: SensorWidth2D, exponent: Linfinity }, + spread: base_spread, + kernel: base_spread, kernel_plot_width, noise_seed, default_merge_radius, algorithm_overrides: HashMap::from([ - higher_cpos_merging_steptune(DefaultAlgorithm::RadonForwardPDPS), - higher_cpos_merging_steptune(DefaultAlgorithm::RadonSlidingPDPS), + higher_cpos_merging(DefaultAlgorithm::RadonFB), + higher_cpos_merging(DefaultAlgorithm::RadonSlidingFB), ]), }, - }}) - }, - Experiment2D_TV_Fast => { - let base_spread = HatConv { radius : Hat1 }; - Box::new(Named { name, data : ExperimentBiased { - λ : 0.005, - bias : MappingSum::new([ - Weighted::new(1.0, BallCharacteristic{ center : [0.3, 0.3].into(), radius : 0.2 }), - Weighted::new(0.5, BallCharacteristic{ center : [0.6, 0.6].into(), radius : 0.3 }), - ]), - base : ExperimentV2 { - domain : [[0.0, 1.0]; 2].into(), - sensor_count : [N_SENSORS_2D; 2], - regularisation : Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.06)), - noise_distr : SerializableNormal::new(0.0, cli.variance.unwrap_or(0.15))?, //0.25 - dataterm : DataTerm::L2Squared, - μ_hat : MU_TRUE_2D_BASIC.into(), - sensor : BallIndicator { r : SensorWidth2D, exponent : Linfinity }, - spread : base_spread, - kernel : base_spread, + }) + } + Experiment1D_L1 => { + let base_spread = Gaussian { variance: Variance1 }; + let spread_cutoff = BallIndicator { r: CutOff1, exponent: Linfinity }; + Box::new(Named { + name, + data: ExperimentV2 { + domain: [[0.0, 1.0]].into(), + sensor_count: [N_SENSORS_1D], + regularisation: Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.1)), + noise_distr: SaltAndPepper::new( + cli.salt_and_pepper.as_ref().map_or(0.6, |v| v[0]), + cli.salt_and_pepper.as_ref().map_or(0.4, |v| v[1]), + )?, + dataterm: DataTermType::L1, + μ_hat: MU_TRUE_1D_BASIC.into(), + sensor: BallIndicator { r: SensorWidth1D, exponent: Linfinity }, + spread: Prod(spread_cutoff, base_spread), + kernel: Prod(AutoConvolution(spread_cutoff), base_spread), + kernel_plot_width, + noise_seed, + default_merge_radius, + algorithm_overrides: HashMap::new(), + }, + }) + } + Experiment1D_L1_Fast => { + let base_spread = HatConv { radius: Hat1 }; + Box::new(Named { + name, + data: ExperimentV2 { + domain: [[0.0, 1.0]].into(), + sensor_count: [N_SENSORS_1D], + regularisation: Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.12)), + noise_distr: SaltAndPepper::new( + cli.salt_and_pepper.as_ref().map_or(0.6, |v| v[0]), + cli.salt_and_pepper.as_ref().map_or(0.4, |v| v[1]), + )?, + dataterm: DataTermType::L1, + μ_hat: MU_TRUE_1D_BASIC.into(), + sensor: BallIndicator { r: SensorWidth1D, exponent: Linfinity }, + spread: base_spread, + kernel: base_spread, + kernel_plot_width, + noise_seed, + default_merge_radius, + algorithm_overrides: HashMap::new(), + }, + }) + } + Experiment2D_L1 => { + let base_spread = Gaussian { variance: Variance1 }; + let spread_cutoff = BallIndicator { r: CutOff1, exponent: Linfinity }; + Box::new(Named { + name, + data: ExperimentV2 { + domain: [[0.0, 1.0]; 2].into(), + sensor_count: [N_SENSORS_2D; 2], + regularisation: Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.35)), + noise_distr: SaltAndPepper::new( + cli.salt_and_pepper.as_ref().map_or(0.8, |v| v[0]), + cli.salt_and_pepper.as_ref().map_or(0.2, |v| v[1]), + )?, + dataterm: DataTermType::L1, + μ_hat: MU_TRUE_2D_BASIC.into(), + sensor: BallIndicator { r: SensorWidth2D, exponent: Linfinity }, + spread: Prod(spread_cutoff, base_spread), + kernel: Prod(AutoConvolution(spread_cutoff), base_spread), kernel_plot_width, noise_seed, default_merge_radius, - algorithm_overrides: HashMap::from([ - much_higher_cpos_merging_steptune(DefaultAlgorithm::RadonForwardPDPS), - much_higher_cpos_merging_steptune(DefaultAlgorithm::RadonSlidingPDPS), + algorithm_overrides: HashMap::from([]), + }, + }) + } + Experiment2D_L1_Fast => { + let base_spread = HatConv { radius: Hat1 }; + Box::new(Named { + name, + data: ExperimentV2 { + domain: [[0.0, 1.0]; 2].into(), + sensor_count: [N_SENSORS_2D; 2], + regularisation: Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.40)), + noise_distr: SaltAndPepper::new( + cli.salt_and_pepper.as_ref().map_or(0.8, |v| v[0]), + cli.salt_and_pepper.as_ref().map_or(0.2, |v| v[1]), + )?, + dataterm: DataTermType::L1, + μ_hat: MU_TRUE_2D_BASIC.into(), + sensor: BallIndicator { r: SensorWidth2D, exponent: Linfinity }, + spread: base_spread, + kernel: base_spread, + kernel_plot_width, + noise_seed, + default_merge_radius, + algorithm_overrides: HashMap::from([]), + }, + }) + } + Experiment1D_TV_Fast => { + let base_spread = HatConv { radius: HatBias }; + Box::new(Named { + name, + data: ExperimentBiased { + λ: 0.02, + bias: MappingSum::new([ + Weighted::new(1.0, BallCharacteristic { + center: 0.3.into(), + radius: 0.2, + }), + Weighted::new(0.5, BallCharacteristic { + center: 0.6.into(), + radius: 0.3, + }), ]), + base: ExperimentV2 { + domain: [[0.0, 1.0]].into(), + sensor_count: [N_SENSORS_1D], + regularisation: Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.2)), + noise_distr: SerializableNormal::new(0.0, cli.variance.unwrap_or(0.1))?, + dataterm: DataTermType::L222, + μ_hat: MU_TRUE_1D_BASIC.into(), + sensor: BallIndicator { r: SensorWidth1D, exponent: Linfinity }, + spread: base_spread, + kernel: base_spread, + kernel_plot_width, + noise_seed, + default_merge_radius, + algorithm_overrides: HashMap::from([ + higher_cpos_merging_steptune(DefaultAlgorithm::RadonForwardPDPS), + higher_cpos_merging_steptune(DefaultAlgorithm::RadonSlidingPDPS), + ]), + }, }, - }}) - }, + }) + } + Experiment2D_TV_Fast => { + let base_spread = HatConv { radius: Hat1 }; + Box::new(Named { + name, + data: ExperimentBiased { + λ: 0.005, + bias: MappingSum::new([ + Weighted::new(1.0, BallCharacteristic { + center: [0.3, 0.3].into(), + radius: 0.2, + }), + Weighted::new(0.5, BallCharacteristic { + center: [0.6, 0.6].into(), + radius: 0.3, + }), + ]), + base: ExperimentV2 { + domain: [[0.0, 1.0]; 2].into(), + sensor_count: [N_SENSORS_2D; 2], + regularisation: Regularisation::NonnegRadon(cli.alpha.unwrap_or(0.06)), + noise_distr: SerializableNormal::new( + 0.0, + cli.variance.unwrap_or(0.15), + )?, //0.25 + dataterm: DataTermType::L222, + μ_hat: MU_TRUE_2D_BASIC.into(), + sensor: BallIndicator { r: SensorWidth2D, exponent: Linfinity }, + spread: base_spread, + kernel: base_spread, + kernel_plot_width, + noise_seed, + default_merge_radius, + algorithm_overrides: HashMap::from([ + much_higher_cpos_merging_steptune( + DefaultAlgorithm::RadonForwardPDPS, + ), + much_higher_cpos_merging_steptune( + DefaultAlgorithm::RadonSlidingPDPS, + ), + ]), + }, + }, + }) + } }) } } - diff -r 9738b51d90d7 -r 4f468d35fa29 src/fb.rs --- a/src/fb.rs Sun Apr 27 15:03:51 2025 -0500 +++ b/src/fb.rs Thu Feb 26 11:38:43 2026 -0500 @@ -74,37 +74,34 @@