pa_types/
cost.rs

1use serde::{Deserialize, Serialize};
2
3use crate::I;
4
5/// The non-negative cost of an alignment.
6pub type Cost = i32;
7
8/// The score of an alignment. Can be positive or negative.
9pub type Score = i32;
10
11/// Different cost models.
12/// All values must be non-negative.
13#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone, Copy)]
14#[cfg_attr(feature = "clap", derive(clap::Args))]
15pub struct CostModel {
16    /// Substitution cost, (> 0)
17    ///
18    /// TODO: Support `-infinity` to disallow substitutions entirely.
19    #[cfg_attr(feature = "clap", clap(long, default_value_t = 1, value_name = "COST"))]
20    pub sub: Cost,
21    /// Gap open cost (>= 0)
22    ///
23    /// When 0, gap cost is linear.
24    #[cfg_attr(feature = "clap", clap(long, default_value_t = 0, value_name = "COST"))]
25    pub open: Cost,
26    /// Gap extend cost (> 0)
27    #[cfg_attr(feature = "clap", clap(long, default_value_t = 1, value_name = "COST"))]
28    pub extend: Cost,
29}
30
31impl CostModel {
32    pub fn unit() -> Self {
33        Self {
34            sub: 1,
35            open: 0,
36            extend: 1,
37        }
38    }
39    pub fn is_unit(&self) -> bool {
40        self == &Self::unit()
41    }
42    pub fn linear(sub: Cost, indel: Cost) -> Self {
43        Self {
44            sub,
45            open: 0,
46            extend: indel,
47        }
48    }
49    pub fn is_linear(&self) -> bool {
50        self.open == 0
51    }
52    pub fn affine(sub: Cost, open: Cost, extend: Cost) -> Self {
53        Self { sub, open, extend }
54    }
55    pub fn is_affine(&self) -> bool {
56        self.open > 0
57    }
58
59    /// The cost of a substitution.
60    pub fn sub(&self) -> Cost {
61        self.sub
62    }
63    /// The cost of a substitution, or None if not allowed.
64    pub fn maybe_sub(&self) -> Option<Cost> {
65        Some(self.sub)
66    }
67    /// The cost of an insertion of given length.
68    pub fn ins(&self, len: I) -> Cost {
69        self.open + len * self.extend
70    }
71    /// The cost of a deletion of given length.
72    pub fn del(&self, len: I) -> Cost {
73        self.open + len * self.extend
74    }
75}
76
77#[derive(Debug, Eq, PartialEq, Clone, Copy)]
78pub struct ScoreModel {
79    /// > 0
80    pub r#match: Score,
81    /// < 0
82    pub sub: Score,
83    /// <= 0
84    pub open: Score,
85    /// < 0
86    pub extend: Score,
87
88    pub factor: i32,
89}
90
91impl ScoreModel {
92    const OFFSET: i32 = 1;
93
94    pub fn from_costs(cm: CostModel) -> Self {
95        let factor;
96        if cm.sub > 2 && cm.extend > 1 {
97            factor = 1;
98        } else if cm.sub == 1 {
99            factor = 3;
100        } else {
101            factor = 2;
102        }
103
104        Self {
105            r#match: Self::OFFSET * 2,
106            // < 0
107            sub: -cm.sub * factor + Self::OFFSET * 2,
108            // <= 0
109            open: -cm.open * factor,
110            // < 0
111            extend: -cm.extend * factor + Self::OFFSET,
112            factor,
113        }
114    }
115
116    pub fn global_cost(&self, score: Score, a_len: usize, b_len: usize) -> Cost {
117        let path_len = (a_len + b_len) as i32;
118        let s = -score + path_len * Self::OFFSET;
119        // assert!(
120        //     s % self.factor == 0,
121        //     "path_len {path_len}: Score {score} reduced to cost {s} which is not a multiple of {}",
122        //     self.factor
123        // );
124        s / self.factor
125    }
126}