diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml new file mode 100644 index 0000000..47d89b6 --- /dev/null +++ b/.github/workflows/clippy.yml @@ -0,0 +1,65 @@ +name: rustfmt and clippy with annotations + +on: + push: + branches: + - "**" + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + fmt_lib: + name: rustfmt on parsimony lib + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1.0.6 + with: + profile: minimal + toolchain: stable + override: true + - run: rustup component add rustfmt + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --manifest-path ./parsimony/Cargo.toml --all -- --check + + clippy_check_lib: + name: clippy on parsimony lib + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: rustup component add clippy + - uses: actions-rs/clippy-check@v1.0.7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --manifest-path ./parsimony/Cargo.toml --all-features -- -D warnings + + fmt_bin: + name: rustfmt on indelMaP bin + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1.0.6 + with: + profile: minimal + toolchain: stable + override: true + - run: rustup component add rustfmt + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --manifest-path ./indelMaP/Cargo.toml --all -- --check + + clippy_check_bin: + name: clippy on indelMaP bin + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: rustup component add clippy + - uses: actions-rs/clippy-check@v1.0.7 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --manifest-path ./indelMaP/Cargo.toml --all-features -- -D warnings diff --git a/.github/workflows/test_coverage.yml b/.github/workflows/test_coverage.yml new file mode 100644 index 0000000..c58287f --- /dev/null +++ b/.github/workflows/test_coverage.yml @@ -0,0 +1,40 @@ +name: library tests with coverage + +on: + pull_request: + branches: [ "main" ] + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + test: + name: tests with coverage + runs-on: ubuntu-latest + env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: -Cinstrument-coverage -Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort + RUSTDOCFLAGS: -Cinstrument-coverage -Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1.0.6 + with: + toolchain: nightly + override: true + - name: Generate test results and coverage report + run: | + cargo install cargo2junit grcov; + rustup component add llvm-tools-preview; + cargo test --manifest-path ./parsimony/Cargo.toml -- -Z unstable-options --format json | cargo2junit > results.xml; + grcov . -s . --binary-path ./parsimony/target/debug/deps -t lcov --branch --ignore "**/*tests.rs" -o lcov.info; + - name: publish test results to github + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + files: results.xml + - name: upload coverage report to Codecov + uses: codecov/codecov-action@v3 + with: + files: lcov.info diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml new file mode 100644 index 0000000..e338571 --- /dev/null +++ b/.github/workflows/tests.yml @@ -0,0 +1,85 @@ +name: tests on stable + +on: + push: + branches: + - "**" + workflow_dispatch: + +env: + CARGO_TERM_COLOR: always + +jobs: + check_lib: + name: cargo check lib + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1.0.6 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: check + args: --manifest-path ./parsimony/Cargo.toml + + test_lib: + name: cargo test lib + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1.0.6 + with: + toolchain: nightly + override: true + - name: install cargo2junit + run: | + cargo install cargo2junit + - name: tests with cargo2junit + run: | + cargo test --manifest-path ./parsimony/Cargo.toml -- -Z unstable-options --format json --report-time | cargo2junit > results.xml + - name: Publish test results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + files: results.xml + + check_bin: + name: cargo check bin + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1.0.6 + with: + profile: minimal + toolchain: stable + override: true + - uses: actions-rs/cargo@v1 + with: + command: check + args: --manifest-path ./indelMaP/Cargo.toml + + test_bin: + name: cargo test bin + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1.0.6 + with: + toolchain: nightly + override: true + - name: install cargo2junit + run: | + cargo install cargo2junit + - name: tests with cargo2junit + run: | + cargo test --manifest-path ./indelMaP/Cargo.toml -- -Z unstable-options --format json --report-time | cargo2junit > results.xml + - name: Publish test results + uses: EnricoMi/publish-unit-test-result-action@v2 + if: always() + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + files: results.xml diff --git a/indelMaP/src/main.rs b/indelMaP/src/main.rs index 38820cc..ae0adea 100644 --- a/indelMaP/src/main.rs +++ b/indelMaP/src/main.rs @@ -10,7 +10,7 @@ use parsimony::parsimony_alignment::parsimony_costs::parsimony_costs_model::{ }; use phylo::alignment::{compile_alignment_representation, Alignment}; use phylo::io; -use phylo::phylo_info::{setup_phylogenetic_info, PhyloInfo}; +use phylo::phylo_info::{phyloinfo_from_files, PhyloInfo}; use phylo::sequences::{get_sequence_type, SequenceType}; use phylo::tree::{get_percentiles_rounded, NodeIdx}; use phylo::Rounding; @@ -98,7 +98,7 @@ fn main() -> Result<()> { info!("IndelMaP run started"); let cli = Cli::try_parse()?; info!("Successfully parsed the command line parameters"); - let info = setup_phylogenetic_info(cli.seq_file, cli.tree_file); + let info = phyloinfo_from_files(cli.seq_file, cli.tree_file); match info { Ok(info) => { let (alignment, scores) = match get_sequence_type(&info.sequences) { diff --git a/indelMaP/src/real_data_tests.rs b/indelMaP/src/real_data_tests.rs index d06e0ad..5346eb4 100644 --- a/indelMaP/src/real_data_tests.rs +++ b/indelMaP/src/real_data_tests.rs @@ -1,12 +1,12 @@ use crate::indel_map_align_protein_rounded; use parsimony::parsimony_alignment::parsimony_costs::parsimony_costs_model::GapMultipliers; -use phylo::phylo_info::setup_phylogenetic_info; +use phylo::phylo_info::phyloinfo_from_files; use phylo::Rounding; use std::path::PathBuf; #[test] fn align_HIV_example_wag() { - let info = setup_phylogenetic_info( + let info = phyloinfo_from_files( PathBuf::from("./data/HIV_subset.fas"), PathBuf::from("./data/HIV_subset.nwk"), ) diff --git a/parsimony/src/parsimony_alignment/parsimony_alignment_tests.rs b/parsimony/src/parsimony_alignment/parsimony_alignment_tests.rs index d15102a..b5d45c7 100644 --- a/parsimony/src/parsimony_alignment/parsimony_alignment_tests.rs +++ b/parsimony/src/parsimony_alignment/parsimony_alignment_tests.rs @@ -7,7 +7,7 @@ use crate::parsimony_alignment::{ parsimony_sets::get_parsimony_sets, }; use bio::io::fasta::Record; -use phylo::phylo_info::PhyloInfo; +use phylo::phylo_info::phyloinfo_from_sequences_tree; use phylo::sequences::SequenceType; use phylo::tree::{NodeIdx::Internal as I, NodeIdx::Leaf as L, Tree}; @@ -92,11 +92,11 @@ pub(crate) fn align_two_on_tree() { Record::with_attrs("A", None, b"AACT"), Record::with_attrs("A", None, b"AC"), ]; - let mut tree = Tree::new(&sequences.to_vec()); + let mut tree = Tree::new(&sequences).unwrap(); tree.add_parent(0, L(0), L(1), 1.0, 1.0); tree.complete = true; tree.create_postorder(); - let info = PhyloInfo::new(tree, sequences.to_vec()); + let info = phyloinfo_from_sequences_tree(&sequences, tree).unwrap(); let scoring = ParsimonyCostsSimple::new(mismatch_cost, gap_open_cost, gap_ext_cost); @@ -214,14 +214,14 @@ pub(crate) fn align_four_on_tree() { Record::with_attrs("D", None, b"GA"), ]; - let mut tree = Tree::new(&sequences.to_vec()); + let mut tree = Tree::new(&sequences).unwrap(); tree.add_parent(0, L(0), L(1), 1.0, 1.0); tree.add_parent(1, L(2), L(3), 1.0, 1.0); tree.add_parent(2, I(0), I(1), 1.0, 1.0); tree.complete = true; tree.create_postorder(); - let info = PhyloInfo::new(tree, sequences.to_vec()); + let info = phyloinfo_from_sequences_tree(&sequences, tree).unwrap(); let scoring = ParsimonyCostsSimple::new(c, a, b); diff --git a/parsimony/src/parsimony_alignment/parsimony_costs/parsimony_costs_model.rs b/parsimony/src/parsimony_alignment/parsimony_costs/parsimony_costs_model.rs index d931715..259f047 100644 --- a/parsimony/src/parsimony_alignment/parsimony_costs/parsimony_costs_model.rs +++ b/parsimony/src/parsimony_alignment/parsimony_costs/parsimony_costs_model.rs @@ -1,15 +1,21 @@ -use super::{BranchParsimonyCosts, ParsimonyCosts}; -use crate::{cmp_f64, f64_h, Result}; +use std::collections::HashMap; + use log::{debug, info}; -use nalgebra::{Const, DimMin, SMatrix}; +use nalgebra::{Const, DMatrix, DimMin}; use ordered_float::OrderedFloat; + +use phylo::evolutionary_models::EvolutionaryModel; use phylo::substitution_models::{ - dna_models, protein_models, DNASubstModel, ProteinSubstModel, SubstitutionModel, + dna_models::{nucleotide_index, DNASubstModel}, + protein_models::{aminoacid_index, ProteinSubstModel}, + SubstitutionModel, }; use phylo::Rounding; -use std::collections::HashMap; -type CostMatrix = SMatrix; +use crate::parsimony_alignment::{BranchParsimonyCosts, ParsimonyCosts}; +use crate::{cmp_f64, f64_h, Result}; + +type CostMatrix = DMatrix; #[derive(Clone, Debug, PartialEq)] pub struct ParsimonyCostsWModel { @@ -52,12 +58,12 @@ impl DNAParsCosts { "The scoring matrix entries will {}be rounded to the closest integer value.", if rounding.round { "" } else { "not " } ); - let model = DNASubstModel::new(model_name, model_params)?; + let model = DNASubstModel::new(model_name, model_params, false)?; let costs = generate_costs( - model, + &model, times, gap_mult, - dna_models::nucleotide_index(), + nucleotide_index(), zero_diag, rounding, ); @@ -84,12 +90,12 @@ impl ProteinParsCosts { "Setting up the parsimony scoring from the {} substitution model.", model_name ); - let model = ProteinSubstModel::new(model_name)?; + let model = ProteinSubstModel::new(model_name, &[], false)?; let costs = generate_costs( - model, + &model, times, gap_mult, - protein_models::aminoacid_index(), + aminoacid_index(), zero_diag, rounding, ); @@ -106,7 +112,7 @@ impl ProteinParsCosts { } fn generate_costs( - model: SubstitutionModel, + model: &SubstitutionModel, times: &[f64], gap_mult: &GapMultipliers, index: [i32; 255], @@ -180,7 +186,7 @@ pub(crate) struct BranchCostsWModel { avg_cost: f64, gap_open: f64, gap_ext: f64, - costs: CostMatrix, + costs: CostMatrix, } impl BranchParsimonyCosts for BranchCostsWModel { @@ -214,7 +220,11 @@ mod parsimony_costs_model_test { ParsimonyCosts, }, }; - use phylo::substitution_models::{protein_models, DNASubstModel, ProteinSubstModel}; + use phylo::evolutionary_models::EvolutionaryModel; + use phylo::substitution_models::{ + dna_models::DNASubstModel, + protein_models::{self, ProteinSubstModel}, + }; use phylo::Rounding; #[test] @@ -226,9 +236,9 @@ mod parsimony_costs_model_test { let avg_01 = 5.7675; let avg_07 = 4.0075; let times = [0.1, 0.7]; - let model = ProteinSubstModel::new("wag").unwrap(); + let model = ProteinSubstModel::new("wag", &[], false).unwrap(); let costs = generate_costs( - model, + &model, ×, &gap_mult, protein_models::aminoacid_index(), @@ -244,9 +254,9 @@ mod parsimony_costs_model_test { assert_eq!(branch_costs.costs.mean(), avg_07); assert_eq!(branch_costs.avg_cost, avg_07); - let model = ProteinSubstModel::new("wag").unwrap(); + let model = ProteinSubstModel::new("wag", &[], false).unwrap(); let costs = generate_costs( - model, + &model, ×, &gap_mult, protein_models::aminoacid_index(), @@ -330,9 +340,9 @@ mod parsimony_costs_model_test { let avg_01 = 2.25; let avg_07 = 1.75; let times = [0.1, 0.7]; - let model = DNASubstModel::new("jc69", &Vec::new()).unwrap(); + let model = DNASubstModel::new("jc69", &[], false).unwrap(); let costs = generate_costs( - model, + &model, ×, &gap_mult, protein_models::aminoacid_index(), @@ -347,9 +357,9 @@ mod parsimony_costs_model_test { let branch_costs = costs.get(&f64_h::from(0.7)).unwrap(); assert_eq!(branch_costs.costs.mean(), avg_07); assert_eq!(branch_costs.avg_cost, avg_07); - let model = DNASubstModel::new("jc69", &Vec::new()).unwrap(); + let model = DNASubstModel::new("jc69", &[], false).unwrap(); let costs = generate_costs( - model, + &model, ×, &gap_mult, protein_models::aminoacid_index(),