| //! Program to write all inputs from a generator to a file, then invoke a Julia script to plot |
| //! them. Output is in `target/plots`. |
| //! |
| //! Requires Julia with the `CairoMakie` dependency. |
| //! |
| //! Note that running in release mode by default generates a _lot_ more datapoints, which |
| //! causes plotting to be extremely slow (some simplification to be done in the script). |
| |
| use std::fmt::Write as _; |
| use std::io::{BufWriter, Write}; |
| use std::path::Path; |
| use std::process::Command; |
| use std::{env, fs}; |
| |
| use libm_test::generate::spaced::SpacedInput; |
| use libm_test::generate::{edge_cases, spaced}; |
| use libm_test::{CheckBasis, CheckCtx, GeneratorKind, MathOp, op}; |
| |
| const JL_PLOT: &str = "examples/plot_file.jl"; |
| |
| fn main() { |
| let manifest_env = env::var("CARGO_MANIFEST_DIR").unwrap(); |
| let manifest_dir = Path::new(&manifest_env); |
| let out_dir = manifest_dir.join("../../target/plots"); |
| if !out_dir.exists() { |
| fs::create_dir(&out_dir).unwrap(); |
| } |
| |
| let jl_script = manifest_dir.join(JL_PLOT); |
| let mut config = format!(r#"out_dir = "{}""#, out_dir.display()); |
| config.write_str("\n\n").unwrap(); |
| |
| // Plot a few domains with some functions that use them. |
| plot_one_operator::<op::sqrtf::Routine>(&out_dir, &mut config); |
| plot_one_operator::<op::cosf::Routine>(&out_dir, &mut config); |
| plot_one_operator::<op::cbrtf::Routine>(&out_dir, &mut config); |
| |
| let config_path = out_dir.join("config.toml"); |
| fs::write(&config_path, config).unwrap(); |
| |
| // The script expects a path to `config.toml` to be passed as its only argument |
| let mut cmd = Command::new("julia"); |
| if cfg!(optimizations_enabled) { |
| cmd.arg("-O3"); |
| } |
| cmd.arg(jl_script).arg(config_path); |
| |
| println!("launching script... {cmd:?}"); |
| cmd.status().unwrap(); |
| } |
| |
| /// Run multiple generators for a single operator. |
| fn plot_one_operator<Op>(out_dir: &Path, config: &mut String) |
| where |
| Op: MathOp<FTy = f32, RustArgs = (f32,)>, |
| Op::RustArgs: SpacedInput<Op>, |
| { |
| let mut ctx = CheckCtx::new(Op::IDENTIFIER, CheckBasis::Mpfr, GeneratorKind::Spaced); |
| plot_one_generator( |
| out_dir, |
| &ctx, |
| "logspace", |
| config, |
| spaced::get_test_cases::<Op>(&ctx).0, |
| ); |
| ctx.gen_kind = GeneratorKind::EdgeCases; |
| plot_one_generator( |
| out_dir, |
| &ctx, |
| "edge_cases", |
| config, |
| edge_cases::get_test_cases::<Op>(&ctx).0, |
| ); |
| } |
| |
| /// Plot the output of a single generator. |
| fn plot_one_generator( |
| out_dir: &Path, |
| ctx: &CheckCtx, |
| gen_name: &str, |
| config: &mut String, |
| generator: impl Iterator<Item = (f32,)>, |
| ) { |
| let fn_name = ctx.base_name_str; |
| let text_file = out_dir.join(format!("input-{fn_name}-{gen_name}.txt")); |
| |
| let f = fs::File::create(&text_file).unwrap(); |
| let mut w = BufWriter::new(f); |
| let mut count = 0u64; |
| |
| for input in generator { |
| writeln!(w, "{:e}", input.0).unwrap(); |
| count += 1; |
| } |
| |
| w.flush().unwrap(); |
| println!("generated {count} inputs for {fn_name}-{gen_name}"); |
| |
| writeln!( |
| config, |
| r#"[[input]] |
| function = "{fn_name}" |
| generator = "{gen_name}" |
| input_file = "{}" |
| "#, |
| text_file.to_str().unwrap() |
| ) |
| .unwrap() |
| } |