Thu, 01 Dec 2022 23:37:14 +0200
README fine-tuning and build.rs for uglifying it for rustdoc.
Cargo.lock | file | annotate | diff | comparison | revisions | |
Cargo.toml | file | annotate | diff | comparison | revisions | |
README.md | file | annotate | diff | comparison | revisions | |
build.rs | file | annotate | diff | comparison | revisions | |
misc/cargo-d | file | annotate | diff | comparison | revisions | |
misc/doc_alias.sh | file | annotate | diff | comparison | revisions | |
src/main.rs | file | annotate | diff | comparison | revisions |
--- a/Cargo.lock Thu Dec 01 23:46:09 2022 +0200 +++ b/Cargo.lock Thu Dec 01 23:37:14 2022 +0200 @@ -29,6 +29,15 @@ checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" [[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + +[[package]] name = "alg_tools" version = "0.1.0" dependencies = [ @@ -926,6 +935,7 @@ "poloto", "rand", "rand_distr", + "regex", "rgb", "serde", "serde_json", @@ -1058,12 +1068,29 @@ ] [[package]] +name = "regex" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076559ef8e241f2ae3479e36f97bd5741c0330689e217ad51ce2c76808b868a" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] name = "regex-automata" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" [[package]] +name = "regex-syntax" +version = "0.6.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" + +[[package]] name = "rgb" version = "0.8.34" source = "registry+https://github.com/rust-lang/crates.io-index"
--- a/Cargo.toml Thu Dec 01 23:46:09 2022 +0200 +++ b/Cargo.toml Thu Dec 01 23:37:14 2022 +0200 @@ -41,5 +41,8 @@ serde_json = "~1.0.85" chrono = { version = "~0.4.23", features = ["alloc", "std", "serde"] } +[build-dependencies] +regex = "~1.7.0" + [profile.release] debug = true
--- a/README.md Thu Dec 01 23:46:09 2022 +0200 +++ b/README.md Thu Dec 01 23:37:14 2022 +0200 @@ -1,42 +1,95 @@ -# pointsource_algs +# Proximal methods for point source localisation: the implementation -This repository contains [Rust][] codes for the manuscript “_Proximal methods for point source localisation_” by Tuomo Valkonen ⟨tuomov@iki.fi⟩. -It concerns solution of problems of the type +This package contains [Rust][] codes for the manuscript “_Proximal methods for +point source localisation_” by Tuomo Valkonen ⟨tuomov@iki.fi⟩. It concerns +solution of problems of the type $$ \min_{μ ∈ ℳ(Ω)}~ F(x) + λ \|μ\|_{ℳ(Ω)} + δ_{≥ 0}(x), $$ -where $F(x)=\frac12\|Ax-b\|_2^2$ and $A \in 𝕃(ℳ(Ω); ℝ^m)$, and $ℳ(Ω)$ is the space of Radon measures on the (rectangular) domain $Ω ⊂ ℝ^n$. +where $F$ is a data term, and $ℳ(Ω)$ is the space of Radon measures on the +(rectangular) domain $Ω ⊂ ℝ^n$. Implemented are $F(x)=\frac12\|Ax-b\|_2^2$ and +$F(x)=\|Ax-b\|_1$ for the forward operator $A \in 𝕃(ℳ(Ω); ℝ^m)$ modelling a +simple sensor grid. For the 2-norm-squared data term implemented are the +algorithms μFB, μFISTA, and μPDPS from the aforementioned manuscript along with +comparison relaxed and fully corrective conditional gradient methods from the +literature. For the 1-norm data term only the μPDPS is applicable. ## Installation and usage -First install the Install [Rust][] compiler and `cargo`. -Also install the [GNU Scientific Library][gsl]. On a Mac with [Homebrew][] -installed, the latter can be done with -```sh -$ brew install gsl +### Installing dependencies + +Most dependencies are managed by the Cargo build system of [Rust][]. You will +only need to install the “nightly” Rust compiler and the +[GNU Scientific Library][gsl] manually. At the time of writing this README, +[alg_tools][] also needs to be downloaded separately. + +1. Install the [Rust][] infrastructure (including Cargo) with [rustup][]. +2. Install a “nightly” release of the Rust compiler. With rustup, installed in + the previous step, this can be done with + ```console + rustup toolchain install nightly + ``` +3. Install [GNU Scientific Library][gsl]. On a Mac with [Homebrew] installed, + this can be done with + ```console + brew install gsl + ``` + For other operating systems, suggestions are available in the + [rust-GSL crate documentation][rust-GSL]. + On Windows, you will likely 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. + + [rustup]: https://rustup.rs + [alg_tools]: https://tuomov.iki.fi/software/alg_tools/ + [Rust]: https://www.rust-lang.org/ + [rust-GSL]: https://docs.rs/GSL/6.0.0/rgsl/ + [gsl]: https://www.gnu.org/software/gsl/ + [Homebrew]: https://brew.sh + +### Building and running the experiments + +To compile the code and run the experiments in the manuscript, use +```console +cargo run --release ``` -Then download [alg_tools][] and unpack it under the same directory as this package. -To compile the code and run the experiments in the manuscript, use -```sh -$ cargo run --release -``` +When doing this for the first time, several dependencies will be downloaded. The `--release` flag is required to build optimised high performance code. Without that flag the performance will be significantly worse. -## Documentation +Alternatively, you may build the executable with +```console +cargo build --release +``` +and then run it with +``` +target/release/pointsource_algs +``` -The integrated documentation may be built and opened with -```sh -$ carg doc # build dependency docs -$ . misc/doc-alias.sh # load KaTeX helper macro -$ cargo-d --open # build and open KaTeX-aware docs for this crate +### Documentation + +Use the `--help` option to get an extensive listing of command line options. +If using `cargo` to run the executable, you have to pass any arguments to this +program after a double-dash: +```console +cargo run --release -- --help ``` -The `cargo-d` alias ensures that KaTeX mathematics is rendered in the generated documentation. `Rustdoc` is obsolete rubbish that does not support modern markdown featues, so `cargo doc` does not render mathematics. Instead an ugly workaround is needed. + +## Internals - [alg_tools]: https://tuomov.iki.fi/software/alg_tools/ - [Rust]: https://www.rust-lang.org/ - [gsl]: https://www.gnu.org/software/gsl/ - [Homebrew]: https://brew.sh +If you are interested in the program internals, the integrated source code +documentation may be built and opened with +```console +cargo doc # build dependency docs +misc/cargo-d --open # build and open KaTeX-aware docs for this crate +``` +The `cargo-d` script ensures that KaTeX mathematics is rendered in the +generated documentation. Unfortunately, `rustdoc`, akin to Rust largely itself, +is stuck in 80's 7-bit gringo ASCII world, and does not support modern markdown +features, such as mathematics. Instead, to render mathematics, an ugly +workaround is needed together with lots of painful raw HTML escapes in the +documentation. -
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/build.rs Thu Dec 01 23:37:14 2022 +0200 @@ -0,0 +1,38 @@ +use std::env; +use regex::{Regex, Captures}; + +fn proc<A : AsRef<str>>(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() { + 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 + // workaround to process the math on the fly. + + println!("cargo:rerun-if-changed=README.md"); + + 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)); + // Remove the instructions for building the documentation + let readme_cut = Regex::new("## Internals(.*|\n)*") + .unwrap() + .replace_all(&readme_uglified, ""); + + std::fs::write(out_dir + "/README_uglified.md", readme_cut.as_bytes()) + .expect("Error saving uglified README"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/misc/cargo-d Thu Dec 01 23:37:14 2022 +0200 @@ -0,0 +1,4 @@ +#!/bin/sh +RUSTDOCFLAGS="--html-in-header misc/katex-header.html" cargo d --no-deps "$@" + + \ No newline at end of file
--- a/misc/doc_alias.sh Thu Dec 01 23:46:09 2022 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,5 +0,0 @@ -# source this file. use cargo rustdoc or cargo d --no-deps no build the documentation. -echo 'Creating cargo-d alias' -alias cargo-d='RUSTDOCFLAGS="--html-in-header misc/katex-header.html" BROWSER=/Applications/Firefox.app/Contents/MacOS/firefox-bin cargo d --no-deps' - - \ No newline at end of file
--- a/src/main.rs Thu Dec 01 23:46:09 2022 +0200 +++ b/src/main.rs Thu Dec 01 23:37:14 2022 +0200 @@ -1,15 +1,16 @@ // The main documentation is in the README. -#![doc = include_str!("../README.md")] +// We need to uglify it in build.rs because rustdoc is stuck in the past. +#![doc = include_str!(concat!(env!("OUT_DIR"), "/README_uglified.md"))] // We use unicode. We would like to use much more of it than Rust allows. // Live with it. Embrace it. #![allow(uncommon_codepoints)] #![allow(mixed_script_confusables)] #![allow(confusable_idents)] -// Linear operators may be writtten e.g. as `opA` for a resemblance -// to mathematical convention. +// Linear operators may be written e.g. as `opA`, to keep the capital letters of mathematical +// convention while referring to the type (trait) of the operator as `A`. #![allow(non_snake_case)] -// We need the drain filter for inertial prune +// We need the drain filter for inertial prune. #![feature(drain_filter)] use clap::Parser;