diff --git a/Cargo.toml b/Cargo.toml index 692d90069e4aed5316eb66edc68cf731ae8b846f..78ba00ceae0c53cd137229f65ab659808c2791d9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,6 +30,7 @@ serde_json = { version = "1.0.127", features = ["preserve_order"] } color-print = { git = "https://gitlab.com/iago-lito/color-print", branch = "dev", version = "0.3.6" } color-print-proc-macro = { git = "https://gitlab.com/iago-lito/color-print", branch = "dev", version = "0.3.6" } csv = "1.3.0" +pathdiff = "0.2.1" [dev-dependencies] rand = "0.8.5" diff --git a/src/bin/aphid/main.rs b/src/bin/aphid/main.rs index 3ad4f38d32170c1d9910968c443c238772b8a595..d03727778af5d2a8e2eb118fb6c792a8643fc4ad 100644 --- a/src/bin/aphid/main.rs +++ b/src/bin/aphid/main.rs @@ -3,12 +3,14 @@ use std::{ fs::{self, File}, io::Write as IoWrite, path::{self, Path, PathBuf}, - process, time::Instant, + process, + time::Instant, }; use aphid::{ extract_local_triplet, imbalance, interner::{Interner, ResolvedSymbol, SpeciesSymbol}, + io::ToRelative, it_mean::SummedMean, ln_likelihood, optimize_likelihood, output::{self, detail, file}, @@ -98,8 +100,12 @@ fn run() -> Result<(), Error> { Ok(()) } -fn read_inputs(args: &args::Args, output_folder: &Path, interner: &mut Interner) -> Result<(Config, GenesForest), Error> { - let path = &args.config; +fn read_inputs( + args: &args::Args, + output_folder: &Path, + interner: &mut Interner, +) -> Result<(Config, GenesForest), Error> { + let path = &args.config.canonicalize()?; cprintln!("Read config from <b>{}</>.", path.display()); let config = Config::from_file(path, output_folder, interner)?; @@ -151,23 +157,26 @@ fn prepare_output(output: &Path, args: &args::Args) -> Result<(), Error> { match (output.exists(), args.force) { (true, true) => { cprintln!( - " <y>Removing</> existing folder: <b>{}</>.", + " <y>Replacing</> existing folder: <b>{}</>.", output.display() ); - fs::remove_dir_all(&output)?; + fs::remove_dir_all(output)?; } (true, false) => OutputExistsErr { path: &output }.fail()?, (false, _) => { cprintln!(" Creating empty folder: <b>{}</>.", output.display()); } }; - fs::create_dir_all(&output)?; + fs::create_dir_all(output)?; Ok(()) } fn write_config(output: &Path, config: &Config, interner: &Interner) -> Result<(), Error> { let path = output.join(file::CONFIG); - cprintln!(" Write full configuration to <b>{}</>.", path.display()); + cprintln!( + " Write full configuration to <b>{}</>.", + path.to_relative()?.display() + ); let mut file = File::create(path)?; writeln!(file, "{:#}", config.resolve(interner).json())?; Ok(()) @@ -375,7 +384,7 @@ fn write_detail(output: &Path, details: &[detail::Tree]) -> Result<(), Error> { let path = output.join(file::DETAIL); cprintln!( "Write full trees analysis/filtering detail to <b>{}</>.", - path.display() + path.to_relative()?.display() ); let mut file = File::create(path)?; writeln!(file, "{:#}", json!(details))?; @@ -384,7 +393,10 @@ fn write_detail(output: &Path, details: &[detail::Tree]) -> Result<(), Error> { fn write_csv(output: &Path, details: &[detail::Tree]) -> Result<(), Error> { let path = output.join(file::CSV); - cprintln!("Summarize scalar values to <b>{}</>.", path.display()); + cprintln!( + "Summarize scalar values to <b>{}</>.", + path.to_relative()?.display() + ); let file = File::create(path)?; let mut wtr = csv::Writer::from_writer(file); for detail in details { @@ -496,4 +508,6 @@ enum Error { Learn { source: aphid::learn::Error }, #[snafu(display("Could not serialize record to CSV:\n{source}"))] Csv { source: csv::Error }, + #[snafu(transparent)] + AphidIo { source: aphid::io::Error }, } diff --git a/src/config/deserialize.rs b/src/config/deserialize.rs index 18b2700f71a6091971ce81051a3698c54fd2042d..f44fd121916e085d03f79d4e1786db975eaf8891 100644 --- a/src/config/deserialize.rs +++ b/src/config/deserialize.rs @@ -144,7 +144,7 @@ pub(crate) struct BfgsConfig { } #[derive(Deserialize)] -#[serde(rename_all="snake_case")] +#[serde(rename_all = "snake_case")] pub(crate) enum RecordTrace { No, Global, diff --git a/src/io.rs b/src/io.rs index bebe8cede22218993c7e5c62fb38088f49a795b9..ecb6b4b5dbb5e0db2bf4ba8bd99b1be84014f4fa 100644 --- a/src/io.rs +++ b/src/io.rs @@ -1,6 +1,8 @@ // Reading from / writing to files on disk. use std::{ + borrow::Cow, + env, fs::File, io::{self, Read}, path::{Path, PathBuf}, @@ -25,6 +27,21 @@ pub fn canonicalize(path: &Path) -> Result<PathBuf, Error> { .with_context(|_| CanonicalizeErr { path }) } +// Display canonicalized paths relatively to current directory. +pub trait ToRelative { + fn to_relative(&self) -> Result<Cow<Path>, Error>; +} +impl ToRelative for Path { + fn to_relative(&self) -> Result<Cow<Path>, Error> { + let cwd = env::current_dir().with_context(|_| CurDirErr)?; + Ok(if let Some(diff) = pathdiff::diff_paths(self, cwd) { + Cow::Owned(diff) + } else { + Cow::Borrowed(self) + }) + } +} + #[derive(Debug, Snafu)] #[snafu(context(suffix(Err)))] pub enum Error { @@ -40,4 +57,6 @@ pub enum Error { source: std::io::Error, path: PathBuf, }, + #[snafu(display("Could not locate current directory: {source:?}"))] + CurDir { source: std::io::Error }, } diff --git a/src/learn.rs b/src/learn.rs index 217db857e43e408309996c7708f880f06e84ef18..02ec07a9e58a3f2c7c638ed181e0d93c0b9705a1 100644 --- a/src/learn.rs +++ b/src/learn.rs @@ -11,6 +11,7 @@ use tch::{Device, Tensor}; use crate::{ config::Search, + io::{self, ToRelative}, model::{ likelihood::{data_tensors, ln_likelihood_tensors}, parameters::GeneFlowTimes, @@ -151,13 +152,13 @@ pub fn optimize_likelihood( if let Some(main) = &search.bfgs.main_trace_path { cprintln!( " Recording main BFGS search trace at <b>{}</>.", - main.display() + main.to_relative()?.display() ); } if let Some(lins) = &search.bfgs.linsearch_trace_path { cprintln!( " Recording detailed Wolfe linear search traces at <b>{}</>.", - lins.display() + lins.to_relative()?.display() ); } // Log every step if a file was provided. @@ -177,7 +178,11 @@ pub fn optimize_likelihood( <g>{n_eval}</> evaluations and \ <g>{n_diff}</> differentiations." ); - cprintln!("<s>--</> Final location: <k,i>{}value 'gradient{}</>:\n{grad:#}", '<', '>'); + cprintln!( + "<s>--</> Final location: <k,i>{}value 'gradient{}</>:\n{grad:#}", + '<', + '>' + ); let parms: Parameters<f64> = (&scores).into(); let opt_lnl = -opt.best_loss(); @@ -255,4 +260,6 @@ pub enum Error { NonFiniteLikelihood { lnl: f64, parms: Parameters<f64> }, #[snafu(transparent)] Optim { source: OptimError }, + #[snafu(transparent)] + IO { source: io::Error }, } diff --git a/src/lib.rs b/src/lib.rs index ced4954e68919311934d4eda0e9efdf989bca24c..e07a4111bdd36d96c1ddda0ebc9129a2db59a7c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,7 +2,7 @@ pub mod config; mod gene_tree; pub mod genes_forest; pub mod interner; -mod io; +pub mod io; pub mod it_mean; pub mod learn; mod lexer; diff --git a/src/model/parameters.rs b/src/model/parameters.rs index 3bc67951797084790d89b9190f070eecd725e456..ae55e8f59c477120dd1183f456dd22f360d0fb50 100644 --- a/src/model/parameters.rs +++ b/src/model/parameters.rs @@ -120,16 +120,16 @@ impl<F: Num + Display + Sized + fmt::Debug> Display for Colored<'_, F> { p_ancient, gf_times, } = &self.0; - cwriteln!(f, " theta: <b>{theta:?}</>,")?; - cwriteln!(f, " tau_1: <b>{tau_1:?}</>,")?; - cwriteln!(f, " tau_2: <b>{tau_2:?}</>,")?; - cwriteln!(f, " p_ab: <b>{p_ab:?}</>,")?; - cwriteln!(f, " p_ac: <b>{p_ac:?}</>,")?; - cwriteln!(f, " p_bc: <b>{p_bc:?}</>,")?; - cwriteln!(f, " p_ancient: <b>{p_ancient:?}</>,")?; + cwriteln!(f, " theta: <c>{theta:?}</>,")?; + cwriteln!(f, " tau_1: <c>{tau_1:?}</>,")?; + cwriteln!(f, " tau_2: <c>{tau_2:?}</>,")?; + cwriteln!(f, " p_ab: <c>{p_ab:?}</>,")?; + cwriteln!(f, " p_ac: <c>{p_ac:?}</>,")?; + cwriteln!(f, " p_bc: <c>{p_bc:?}</>,")?; + cwriteln!(f, " p_ancient: <c>{p_ancient:?}</>,")?; writeln!(f, " gf_times: [")?; for t in &gf_times.0 { - cwriteln!(f, " <b>{t}</>,")?; + cwriteln!(f, " <c>{t}</>,")?; } writeln!(f, " ]")?; writeln!(f, "}}") diff --git a/src/output.rs b/src/output.rs index 9bebdcd15b652c6a5cdb200392c198990e138e57..42391064f1d4bded5470c8a102bfb91a18268cf6 100644 --- a/src/output.rs +++ b/src/output.rs @@ -1,8 +1,8 @@ // Use this module to specify the possible output(s) of aphid. mod config; -pub mod detail; pub mod csv; +pub mod detail; // Standard output filenames. pub mod file { @@ -12,4 +12,3 @@ pub mod file { pub const MAIN_TRACE: &str = "search_global.csv"; pub const LINSEARCH_TRACE: &str = "search_detail.csv"; } -