Skip to main content

ebi_objects/ebi_objects/
language_of_alignments.rs

1#[cfg(any(test, feature = "testactivities"))]
2use crate::activity_key::has_activity_key::TestActivityKey;
3use crate::{
4    Activity, ActivityKey, ActivityKeyTranslator, Exportable, Importable, Infoable,
5    TranslateActivityKey,
6    constants::ebi_object::EbiObject,
7    ebi_objects::labelled_petri_net::TransitionIndex,
8    line_reader::LineReader,
9    traits::importable::{ImporterParameter, ImporterParameterValues, from_string},
10};
11use anyhow::{Context, Result, anyhow};
12use ebi_derive::ActivityKey;
13use std::fmt::Display;
14
15pub const HEADER: &str = "language of alignments";
16
17#[derive(ActivityKey, Clone)]
18pub struct LanguageOfAlignments {
19    pub activity_key: ActivityKey,
20    pub alignments: Vec<Vec<Move>>,
21}
22
23impl LanguageOfAlignments {
24    pub fn new(activity_key: ActivityKey) -> Self {
25        Self {
26            activity_key: activity_key,
27            alignments: vec![],
28        }
29    }
30
31    pub fn push(&mut self, alignment: Vec<Move>) {
32        self.alignments.push(alignment);
33    }
34
35    pub fn append(&mut self, alignments: &mut Vec<Vec<Move>>) {
36        self.alignments.append(alignments);
37    }
38
39    pub fn get(&self, index: usize) -> Option<&Vec<Move>> {
40        self.alignments.get(index)
41    }
42
43    pub fn get_activity_key(&self) -> &ActivityKey {
44        &self.activity_key
45    }
46
47    pub fn get_activity_key_mut(&mut self) -> &mut ActivityKey {
48        &mut self.activity_key
49    }
50
51    pub fn sort(&mut self) {
52        self.alignments.sort();
53    }
54}
55
56impl TranslateActivityKey for LanguageOfAlignments {
57    fn translate_using_activity_key(&mut self, to_activity_key: &mut ActivityKey) {
58        let translator = ActivityKeyTranslator::new(&self.activity_key, to_activity_key);
59        self.alignments.iter_mut().for_each(|alignment| {
60            alignment.iter_mut().for_each(|activity| match activity {
61                Move::SynchronousMove(activity, _)
62                | Move::LogMove(activity)
63                | Move::ModelMove(activity, _) => {
64                    *activity = translator.translate_activity(&activity)
65                }
66                _ => {}
67            })
68        });
69        self.activity_key = to_activity_key.clone();
70    }
71}
72
73impl Exportable for LanguageOfAlignments {
74    fn export_from_object(object: EbiObject, f: &mut dyn std::io::Write) -> Result<()> {
75        match object {
76            EbiObject::LanguageOfAlignments(alignments) => alignments.export(f),
77            EbiObject::StochasticLanguageOfAlignments(sali) => Into::<Self>::into(sali).export(f),
78            _ => Err(anyhow!("Cannot export as language of alignments.")),
79        }
80    }
81
82    fn export(&self, f: &mut dyn std::io::Write) -> Result<()> {
83        Ok(write!(f, "{}", self)?)
84    }
85}
86
87impl Display for LanguageOfAlignments {
88    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89        writeln!(f, "{}", HEADER)?;
90
91        writeln!(f, "# number of alignments\n{}", self.alignments.len())?;
92
93        for (i, moves) in self.alignments.iter().enumerate() {
94            writeln!(f, "# alignment {}", i)?;
95            writeln!(f, "# number of moves\n{}", moves.len())?;
96
97            for (j, movee) in moves.iter().enumerate() {
98                writeln!(f, "# move {}", j)?;
99
100                match movee {
101                    Move::LogMove(activity) => {
102                        writeln!(f, "log move")?;
103                        writeln!(
104                            f,
105                            "label {}",
106                            self.activity_key.get_activity_label(activity)
107                        )?;
108                    }
109                    Move::ModelMove(activity, transition) => {
110                        writeln!(f, "model move")?;
111                        writeln!(
112                            f,
113                            "label {}",
114                            self.activity_key.get_activity_label(activity)
115                        )?;
116                        writeln!(f, "{}", transition)?;
117                    }
118                    Move::SynchronousMove(activity, transition) => {
119                        writeln!(f, "synchronous move")?;
120                        writeln!(
121                            f,
122                            "label {}",
123                            self.activity_key.get_activity_label(activity)
124                        )?;
125                        writeln!(f, "{}", transition)?;
126                    }
127                    Move::SilentMove(transition) => {
128                        writeln!(f, "silent move")?;
129                        writeln!(f, "{}", transition)?;
130                    }
131                };
132            }
133        }
134
135        write!(f, "")
136    }
137}
138
139impl Importable for LanguageOfAlignments {
140    const FILE_FORMAT_SPECIFICATION_LATEX: &str = "A language of alignments is a line-based structure. Lines starting with a \\# are ignored.
141    This first line is exactly `language of alignments'.
142    The second line is the number of alignments in the language.
143    For each alignment, the first line contains the number of moves in the alignment.
144    Then, each move is given as either 
145    \\begin{itemize}
146        \\item `synchronous move', followed by a line with the word `label' followed by a space and the activity label, which is followed with a line with the index of the involved transition.
147        \\item `silent move', followed by a line with the index of the silent transition.
148        \\item `log move', followed by a line with the word `label', then a space, and then the activity label.
149        \\item `model move', followed by a line with the word `label' followed by a space and the activity label, which is followed with a line with the index of the involved ransition.
150    \\end{itemize}
151    Note that the Semantics trait of Ebi, which is what most alignment computations use, requires that every final marking is a deadlock.
152    Consequently, an implicit silent transition may be added by the Semantics trait that is not in the model.
153    
154    For instance:
155    \\lstinputlisting[language=ebilines, style=boxed]{../testfiles/aa-ab-ba.ali}";
156
157    const IMPORTER_PARAMETERS: &[ImporterParameter] = &[];
158
159    fn import_as_object(
160        reader: &mut dyn std::io::BufRead,
161        parameter_values: &ImporterParameterValues,
162    ) -> Result<EbiObject> {
163        Ok(EbiObject::LanguageOfAlignments(Self::import(
164            reader,
165            parameter_values,
166        )?))
167    }
168
169    fn import(
170        reader: &mut dyn std::io::BufRead,
171        _: &ImporterParameterValues,
172    ) -> anyhow::Result<Self>
173    where
174        Self: Sized,
175    {
176        let mut lreader = LineReader::new(reader);
177        let mut activity_key = ActivityKey::new();
178
179        let head = lreader
180            .next_line_string()
181            .with_context(|| format!("failed to read header, which should be {}", HEADER))?;
182        if head != HEADER {
183            return Err(anyhow!(
184                "first line should be exactly `{}`, but found `{}`",
185                HEADER,
186                lreader.get_last_line()
187            ));
188        }
189
190        let number_of_alignments = lreader
191            .next_line_index()
192            .context("failed to read number of alignments")?;
193
194        let mut alignments = Vec::new();
195        for alignment_number in 0..number_of_alignments {
196            let mut moves = vec![];
197
198            let number_of_moves = lreader.next_line_index().with_context(|| {
199                format!(
200                    "failed to read number of moves in alignemnt {}",
201                    alignment_number
202                )
203            })?;
204
205            for move_number in 0..number_of_moves {
206                //read type of move
207                let move_type_line = lreader.next_line_string().with_context(|| {
208                    format!(
209                        "failed to read type of move {} of alignment {}",
210                        move_number, alignment_number
211                    )
212                })?;
213                if move_type_line.trim_start().starts_with("log move") {
214                    //read a log move
215                    let label_line = lreader.next_line_string().with_context(|| {
216                        format!(
217                            "failed to read label of log move {} of alignment {}",
218                            move_number, alignment_number
219                        )
220                    })?;
221                    if label_line.trim_start().starts_with("label ") {
222                        let label = label_line[6..].to_string();
223                        let activity = activity_key.process_activity(&label);
224                        moves.push(Move::LogMove(activity));
225                    } else {
226                        return Err(anyhow!("Line must have a label"));
227                    }
228                } else if move_type_line.trim_start().starts_with("model move") {
229                    //read the label
230                    let label_line = lreader.next_line_string().with_context(|| {
231                        format!(
232                            "failed to read label of model move {} of alignment {}",
233                            move_number, alignment_number
234                        )
235                    })?;
236                    let activity = if label_line.trim_start().starts_with("label ") {
237                        let label = label_line.trim_start()[6..].to_string();
238                        let activity = activity_key.process_activity(&label);
239                        activity
240                    } else {
241                        return Err(anyhow!("Line must have a label"));
242                    };
243
244                    //read the transition
245                    let transition = lreader.next_line_index().with_context(|| {
246                        format!(
247                            "failed to read transition of move {} in alignemnt {}",
248                            move_number, alignment_number
249                        )
250                    })?;
251
252                    moves.push(Move::ModelMove(activity, transition));
253                } else if move_type_line.trim_start().starts_with("synchronous move") {
254                    //read the label
255                    let label_line = lreader.next_line_string().with_context(|| {
256                        format!(
257                            "failed to read label of synchronous move {} of alignment {}",
258                            move_number, alignment_number
259                        )
260                    })?;
261                    if label_line.trim_start().starts_with("label ") {
262                        let label = label_line.trim_start()[6..].to_string();
263                        let activity = activity_key.process_activity(&label);
264
265                        //read the transition
266                        let transition = lreader.next_line_index().with_context(|| {
267                            format!(
268                                "failed to read transition of move {} in alignemnt {}",
269                                move_number, alignment_number
270                            )
271                        })?;
272
273                        moves.push(Move::SynchronousMove(activity, transition));
274                    } else {
275                        return Err(anyhow!("Line must have a label"));
276                    }
277                } else if move_type_line.trim_start().starts_with("silent move") {
278                    //read the transition
279                    let transition = lreader.next_line_index().with_context(|| {
280                        format!(
281                            "failed to read transition of move {} in alignemnt {}",
282                            move_number, alignment_number
283                        )
284                    })?;
285
286                    moves.push(Move::SilentMove(transition));
287                } else {
288                    return Err(anyhow!(
289                        "Type of log move {} of alignment {} is not recognised.",
290                        move_number,
291                        alignment_number
292                    ));
293                }
294            }
295
296            alignments.push(moves);
297        }
298
299        Ok(Self {
300            activity_key: activity_key,
301            alignments: alignments,
302        })
303    }
304}
305from_string!(LanguageOfAlignments);
306
307impl Infoable for LanguageOfAlignments {
308    fn info(&self, f: &mut impl std::io::Write) -> Result<()> {
309        writeln!(f, "Number of alignments\t\t{}", self.alignments.len())?;
310
311        writeln!(f, "")?;
312        self.get_activity_key().info(f)?;
313
314        Ok(writeln!(f, "")?)
315    }
316}
317
318#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Clone)]
319pub enum Move {
320    LogMove(Activity),
321    ModelMove(Activity, TransitionIndex),
322    SynchronousMove(Activity, TransitionIndex),
323    SilentMove(TransitionIndex),
324}
325
326impl Move {
327    pub fn get_transition(&self) -> Option<TransitionIndex> {
328        match self {
329            Move::LogMove(_) => None,
330            Move::ModelMove(_, transition)
331            | Move::SilentMove(transition)
332            | Move::SynchronousMove(_, transition) => Some(*transition),
333        }
334    }
335}
336
337#[cfg(any(test, feature = "testactivities"))]
338impl TestActivityKey for LanguageOfAlignments {
339    fn test_activity_key(&self) {
340        self.alignments.iter().for_each(|alignment| {
341            alignment.iter().for_each(|activity| match activity {
342                Move::SynchronousMove(activity, _)
343                | Move::LogMove(activity)
344                | Move::ModelMove(activity, _) => {
345                    use crate::HasActivityKey;
346
347                    self.activity_key().assert_activity_is_of_key(activity)
348                }
349                Move::SilentMove(_) => {}
350            })
351        });
352    }
353}