1
#![forbid(unsafe_code)]
2

            
3
use std::collections::HashMap;
4
use std::collections::HashSet;
5
use std::fmt;
6

            
7
use merc_collections::ByteCompressedVec;
8
use merc_collections::CompressedEntry;
9
use merc_collections::CompressedVecMetrics;
10
use merc_collections::bytevec;
11
use merc_io::LargeFormatter;
12
use merc_utilities::MercError;
13

            
14
use crate::LTS;
15
use crate::LabelIndex;
16
use crate::StateIndex;
17
use crate::Transition;
18
use crate::TransitionLabel;
19

            
20
/// Represents a labelled transition system consisting of states with directed
21
/// labelled transitions between them.
22
///
23
/// # Details
24
///
25
/// Uses byte compressed vectors to store the states and their outgoing
26
/// transitions efficiently in memory.
27
#[derive(PartialEq, Eq, Clone)]
28
pub struct LabelledTransitionSystem<Label> {
29
    /// Encodes the states and their outgoing transitions.
30
    states: ByteCompressedVec<usize>,
31
    transition_labels: ByteCompressedVec<LabelIndex>,
32
    transition_to: ByteCompressedVec<StateIndex>,
33

            
34
    /// Keeps track of the labels for every index, and which of them are hidden.
35
    labels: Vec<Label>,
36

            
37
    /// The index of the initial state.
38
    initial_state: StateIndex,
39
}
40

            
41
impl<Label: TransitionLabel> LabelledTransitionSystem<Label> {
42
    /// Creates a new labelled transition system with the given transitions,
43
    /// labels, and hidden labels.
44
    ///
45
    /// The initial state is the state with the given index. `num_of_states` is
46
    /// the number of states in the LTS, if known. If it is not known, pass
47
    /// `None`. However, in that case the number of states will be determined
48
    /// based on the maximum state index in the transitions. And all states that
49
    /// do not have any outgoing transitions will simply be created as deadlock
50
    /// states.
51
21331
    pub fn new<I, F>(
52
21331
        initial_state: StateIndex,
53
21331
        num_of_states: Option<usize>,
54
21331
        mut transition_iter: F,
55
21331
        labels: Vec<Label>,
56
21331
    ) -> LabelledTransitionSystem<Label>
57
21331
    where
58
21331
        F: FnMut() -> I,
59
21331
        I: Iterator<Item = (StateIndex, LabelIndex, StateIndex)>,
60
    {
61
21331
        let mut states = ByteCompressedVec::new();
62
21331
        if let Some(num_of_states) = num_of_states {
63
21328
            states.resize_with(num_of_states, Default::default);
64
21328
        }
65

            
66
        // Count the number of transitions for every state
67
21331
        let mut num_of_transitions = 0;
68
16928108
        for (from, _, to) in transition_iter() {
69
            // Ensure that the states vector is large enough.
70
16928108
            if states.len() <= *from.max(to) {
71
7
                states.resize_with(*from.max(to) + 1, || 0);
72
16928101
            }
73

            
74
16928108
            states.update(*from, |start| *start += 1);
75
16928108
            num_of_transitions += 1;
76

            
77
16928108
            if let Some(num_of_states) = num_of_states {
78
16928096
                debug_assert!(
79
16928096
                    *from < num_of_states && *to < num_of_states,
80
                    "State index out of bounds: from {:?}, to {:?}, num_of_states {}",
81
                    from,
82
                    to,
83
                    num_of_states
84
                );
85
12
            }
86
        }
87

            
88
21331
        if initial_state.value() >= states.len() {
89
1204
            // Ensure that the initial state is a valid state (and all states before it exist).
90
1204
            states.resize_with(initial_state.value() + 1, Default::default);
91
20127
        }
92

            
93
        // Track the number of transitions before every state.
94
5131327
        states.fold(0, |count, start| {
95
5131327
            let result = count + *start;
96
5131327
            *start = count;
97
5131327
            result
98
5131327
        });
99

            
100
        // Place the transitions, and increment the end for every state.
101
21331
        let mut transition_labels = bytevec![LabelIndex::new(labels.len()); num_of_transitions];
102
21331
        let mut transition_to = bytevec![StateIndex::new(states.len()); num_of_transitions];
103
16928108
        for (from, label, to) in transition_iter() {
104
16928108
            states.update(*from, |start| {
105
16928108
                transition_labels.set(*start, label);
106
16928108
                transition_to.set(*start, to);
107
16928108
                *start += 1
108
16928108
            });
109
        }
110

            
111
        // Reset the offset.
112
5131327
        states.fold(0, |previous, start| {
113
5131327
            let result = *start;
114
5131327
            *start = previous;
115
5131327
            result
116
5131327
        });
117

            
118
        // Add the sentinel state.
119
21331
        states.push(transition_labels.len());
120

            
121
21331
        LabelledTransitionSystem::from_raw_parts(initial_state, states, transition_labels, transition_to, labels)
122
21331
    }
123

            
124
    /// Constructs a LTS by a successor function for every state.
125
    pub fn with_successors<F, I>(
126
        initial_state: StateIndex,
127
        num_of_states: usize,
128
        labels: Vec<Label>,
129
        mut successors: F,
130
    ) -> Self
131
    where
132
        F: FnMut(StateIndex) -> I,
133
        I: Iterator<Item = (LabelIndex, StateIndex)>,
134
    {
135
        let mut states = ByteCompressedVec::new();
136
        states.resize_with(num_of_states, Default::default);
137

            
138
        let mut transition_labels = ByteCompressedVec::with_capacity(num_of_states, 16usize.bytes_required());
139
        let mut transition_to = ByteCompressedVec::with_capacity(num_of_states, num_of_states.bytes_required());
140

            
141
        for state_index in 0..num_of_states {
142
            let state_index = StateIndex::new(state_index);
143
            states.update(*state_index, |entry| {
144
                *entry = transition_labels.len();
145
            });
146

            
147
            for (label, to) in successors(state_index) {
148
                transition_labels.push(label);
149
                transition_to.push(to);
150
            }
151
        }
152

            
153
        // Add the sentinel state.
154
        states.push(transition_labels.len());
155

            
156
        Self::from_raw_parts(initial_state, states, transition_labels, transition_to, labels)
157
    }
158

            
159
    /// Consumes the current LTS and merges it with another one, returning the merged LTS.
160
    ///
161
    /// # Details
162
    ///
163
    /// Internally this works by offsetting the state indices of the other LTS by the number of states
164
    /// in the current LTS, and combining the action labels. The offset is returned such that
165
    /// can find the states of the other LTS in the merged LTS as the initial state of the other LTS.
166
4409
    fn merge_disjoint_impl<L: LTS<Label = Label>>(mut self, other: &L) -> (Self, StateIndex) {
167
        // Determine the combination of action labels, keeping a map from label to
168
        // its index so membership checks stay O(1) instead of scanning the vector.
169
4409
        let mut all_labels = self.labels().to_vec();
170
4409
        let mut label_indices: HashMap<Label, LabelIndex> = all_labels
171
4409
            .iter()
172
4409
            .enumerate()
173
15153
            .map(|(i, label)| (label.clone(), LabelIndex::new(i)))
174
4409
            .collect();
175

            
176
15156
        for label in other.labels() {
177
15156
            if !label_indices.contains_key(label) {
178
3
                label_indices.insert(label.clone(), LabelIndex::new(all_labels.len()));
179
3
                all_labels.push(label.clone());
180
15153
            }
181
        }
182

            
183
4409
        let total_number_of_states = self.num_of_states() + other.num_of_states();
184

            
185
        // Reserve space for the right LTS.
186
4409
        self.states
187
4409
            .reserve(other.num_of_states(), total_number_of_states.bytes_required());
188
4409
        self.transition_labels
189
4409
            .reserve(other.num_of_transitions(), all_labels.len().bytes_required());
190
4409
        self.transition_to
191
4409
            .reserve(other.num_of_transitions(), total_number_of_states.bytes_required());
192

            
193
4409
        let offset = self.num_of_states();
194

            
195
        // Remove the sentinel state temporarily. This breaks the state invariant, but we will add it back later.
196
4409
        self.states.pop();
197

            
198
        // Add vertices for the other LTS that are offset by the number of states in self
199
554768
        for state_index in other.iter_states() {
200
            // Add a new state for every state in the other LTS
201
554768
            self.states.push(self.num_of_transitions());
202
1663199
            for transition in other.outgoing_transitions(state_index) {
203
1663199
                // Add the transitions of the other LTS, offsetting the state indices
204
1663199
                self.transition_to.push(StateIndex::new(transition.to.value() + offset));
205
1663199

            
206
1663199
                // Map the label to the new index in all_labels
207
1663199
                let label_name = &other.labels()[transition.label.value()];
208
1663199
                self.transition_labels
209
1663199
                    .push(*label_indices.get(label_name).expect("Label should exist in all_labels"));
210
1663199
            }
211
        }
212

            
213
        // Add back the sentinel state
214
4409
        self.states.push(self.num_of_transitions());
215
4409
        debug_assert_eq!(self.num_of_states(), total_number_of_states);
216

            
217
4409
        (
218
4409
            Self::from_raw_parts(
219
4409
                self.initial_state,
220
4409
                self.states,
221
4409
                self.transition_labels,
222
4409
                self.transition_to,
223
4409
                all_labels,
224
4409
            ),
225
4409
            StateIndex::new(offset + other.initial_state_index().value()),
226
4409
        )
227
4409
    }
228

            
229
    /// Creates a labelled transition system from another one, given the permutation of state indices.
230
    ///
231
    /// The permutation maps old state indices to new state indices, i.e.,
232
    /// `permutation(old) = new`. The transition arrays are rebuilt so that
233
    /// transitions are contiguous per new state index, and all transition
234
    /// targets are updated to reference the new state indices.
235
5319
    pub fn new_from_permutation<P>(lts: Self, permutation: P) -> Self
236
5319
    where
237
5319
        P: Fn(StateIndex) -> StateIndex + Copy,
238
    {
239
        // Build the inverse permutation: inverse[new_index] = old_index
240
5319
        let mut inverse = vec![StateIndex::new(0); lts.num_of_states()];
241
1731811
        for state_index in lts.iter_states() {
242
1731811
            inverse[*permutation(state_index)] = state_index;
243
1731811
        }
244

            
245
        // Rebuild transition arrays in the order of the new state indices.
246
5319
        let mut states = ByteCompressedVec::new();
247
5319
        let mut transition_labels = ByteCompressedVec::new();
248
5319
        let mut transition_to = ByteCompressedVec::new();
249

            
250
1731811
        for old_index in &inverse {
251
1731811
            states.push(transition_labels.len());
252

            
253
1731811
            let start = lts.states.index(**old_index);
254
1731811
            let end = lts.states.index(**old_index + 1);
255

            
256
1769638
            for i in start..end {
257
1769638
                transition_labels.push(lts.transition_labels.index(i));
258
1769638
                transition_to.push(permutation(lts.transition_to.index(i)));
259
1769638
            }
260
        }
261

            
262
        // Add the sentinel state.
263
5319
        states.push(transition_labels.len());
264

            
265
5319
        Self::from_raw_parts(
266
5319
            permutation(lts.initial_state),
267
5319
            states,
268
5319
            transition_labels,
269
5319
            transition_to,
270
5319
            lts.labels,
271
        )
272
5319
    }
273

            
274
    /// Consumes the LTS and relabels its transition labels according to the
275
    /// given mapping.
276
    ///
277
    /// Note that this only relabels the visible labels, since the hidden label
278
    /// must be kept consistent.
279
    pub fn relabel<L, F>(self, labelling: F) -> Result<LabelledTransitionSystem<L>, MercError>
280
    where
281
        F: Fn(Label) -> Result<L, MercError>,
282
        L: TransitionLabel,
283
    {
284
        let new_labels: Vec<L> = self
285
            .labels
286
            .into_iter()
287
            .enumerate()
288
            .map(|(index, label)| {
289
                if index == 0 {
290
                    Ok(L::tau_label())
291
                } else {
292
                    labelling(label)
293
                }
294
            })
295
            .collect::<Result<_, _>>()?;
296

            
297
        Ok(LabelledTransitionSystem::from_raw_parts(
298
            self.initial_state,
299
            self.states,
300
            self.transition_labels,
301
            self.transition_to,
302
            new_labels,
303
        ))
304
    }
305

            
306
    /// A [Self::relabel] variant that also applies to the tau label. This is
307
    /// useful when the tau label cannot be constructed from `L::tau_label()`.
308
2
    pub fn relabel_all<L, F>(self, labelling: F) -> Result<LabelledTransitionSystem<L>, MercError>
309
2
    where
310
2
        F: Fn(Label) -> Result<L, MercError>,
311
2
        L: TransitionLabel,
312
    {
313
2
        let new_labels: Vec<L> = self.labels.into_iter().map(labelling).collect::<Result<_, _>>()?;
314

            
315
2
        Ok(LabelledTransitionSystem::from_raw_parts(
316
2
            self.initial_state,
317
2
            self.states,
318
2
            self.transition_labels,
319
2
            self.transition_to,
320
2
            new_labels,
321
2
        ))
322
2
    }
323

            
324
    /// Constructs a [LabelledTransitionSystem] directly from its raw internal arrays.
325
    ///
326
    /// The `states` array must contain one entry per state holding the start offset of that
327
    /// state's transitions in the transition arrays, plus a sentinel entry at the end equal
328
    /// to the total number of transitions. `transition_labels` and `transition_to` must have
329
    /// equal length and all indices they contain must be in bounds.
330
    ///
331
    /// # Panics
332
    ///
333
    /// Panics (in debug mode) if the invariants of the internal representation are violated.
334
343230
    pub fn from_raw_parts(
335
343230
        initial_state: StateIndex,
336
343230
        states: ByteCompressedVec<usize>,
337
343230
        transition_labels: ByteCompressedVec<LabelIndex>,
338
343230
        transition_to: ByteCompressedVec<StateIndex>,
339
343230
        labels: Vec<Label>,
340
343230
    ) -> Self {
341
343230
        let lts = LabelledTransitionSystem {
342
343230
            initial_state,
343
343230
            states,
344
343230
            transition_labels,
345
343230
            transition_to,
346
343230
            labels,
347
343230
        };
348
343230
        lts.assert_valid();
349
343230
        lts
350
343230
    }
351

            
352
    /// Checks that the internal representation satisfies all structural invariants.
353
343230
    pub fn assert_valid(&self) {
354
343230
        let num_states = self.num_of_states();
355
343230
        let num_transitions = self.num_of_transitions();
356

            
357
343230
        debug_assert!(
358
343230
            !self.states.is_empty(),
359
            "states array must have at least one entry (the sentinel)"
360
        );
361

            
362
343230
        debug_assert!(
363
343230
            self.initial_state.value() < num_states,
364
            "initial_state {:?} is out of bounds (num_states: {})",
365
            self.initial_state,
366
            num_states
367
        );
368

            
369
343230
        debug_assert_eq!(
370
343230
            self.states.index(num_states),
371
            num_transitions,
372
            "sentinel value must equal the number of transitions"
373
        );
374

            
375
343230
        debug_assert_eq!(
376
343230
            self.transition_labels.len(),
377
343230
            self.transition_to.len(),
378
            "transition_labels and transition_to must have equal length"
379
        );
380

            
381
343230
        assert!(
382
343230
            self.labels
383
343230
                .first()
384
343230
                .expect("At least one label (the hidden label) must be provided")
385
343230
                .is_tau_label(),
386
            "The first label must be the hidden label."
387
        );
388

            
389
88670277
        for i in 0..num_states {
390
88670277
            debug_assert!(
391
88670277
                self.states.index(i) <= self.states.index(i + 1),
392
                "state {i} has offset {} which is greater than successor offset {}",
393
                self.states.index(i),
394
                self.states.index(i + 1)
395
            );
396
        }
397

            
398
113050079
        for i in 0..num_transitions {
399
113050079
            let label = self.transition_labels.index(i);
400
113050079
            debug_assert!(
401
113050079
                label.value() < self.labels.len(),
402
                "transition {i} references label index {} which is out of bounds (num_labels: {})",
403
                label.value(),
404
                self.labels.len()
405
            );
406

            
407
113050079
            let to = self.transition_to.index(i);
408
113050079
            debug_assert!(
409
113050079
                to.value() < num_states,
410
                "transition {i} references target state {} which is out of bounds (num_states: {})",
411
                to.value(),
412
                num_states
413
            );
414
        }
415

            
416
88670277
        for state in 0..num_states {
417
88670277
            let start = self.states.index(state);
418
88670277
            let end = self.states.index(state + 1);
419
88670277
            let mut seen = HashSet::with_capacity(end.saturating_sub(start));
420

            
421
113050079
            for i in start..end {
422
113050079
                let edge = (
423
113050079
                    self.transition_labels.index(i).value(),
424
113050079
                    self.transition_to.index(i).value(),
425
113050079
                );
426

            
427
113050079
                debug_assert!(
428
113050079
                    seen.insert(edge),
429
                    "state {state} has duplicate outgoing transition with label {} to state {}",
430
                    edge.0,
431
                    edge.1
432
                );
433
            }
434
        }
435
343230
    }
436

            
437
    /// Returns metrics about the LTS.
438
    pub fn metrics(&self) -> LtsMetrics {
439
        LtsMetrics {
440
            num_of_states: self.num_of_states(),
441
            num_of_labels: self.num_of_labels(),
442
            num_of_transitions: self.num_of_transitions(),
443
            state_metrics: self.states.metrics(),
444
            transition_labels_metrics: self.transition_labels.metrics(),
445
            transition_to_metrics: self.transition_to.metrics(),
446
        }
447
    }
448
}
449

            
450
impl<L: TransitionLabel> LTS for LabelledTransitionSystem<L> {
451
    type Label = L;
452

            
453
28334
    fn initial_state_index(&self) -> StateIndex {
454
28334
        self.initial_state
455
28334
    }
456

            
457
216394188
    fn outgoing_transitions(&self, state_index: StateIndex) -> impl Iterator<Item = Transition> + '_ {
458
216394188
        let start = self.states.index(*state_index);
459
216394188
        let end = self.states.index(*state_index + 1);
460

            
461
216394188
        (start..end).map(move |i| Transition {
462
228039585
            label: self.transition_labels.index(i),
463
228039585
            to: self.transition_to.index(i),
464
228039585
        })
465
216394188
    }
466

            
467
1670786
    fn iter_states(&self) -> impl Iterator<Item = StateIndex> + '_ {
468
1670786
        (0..self.num_of_states()).map(StateIndex::new)
469
1670786
    }
470

            
471
61165746
    fn num_of_states(&self) -> usize {
472
        // Remove the sentinel state.
473
61165746
        self.states.len() - 1
474
61165746
    }
475

            
476
4783012
    fn num_of_labels(&self) -> usize {
477
4783012
        self.labels.len()
478
4783012
    }
479

            
480
7150959
    fn num_of_transitions(&self) -> usize {
481
7150959
        self.transition_labels.len()
482
7150959
    }
483

            
484
4732011
    fn labels(&self) -> &[Self::Label] {
485
4732011
        &self.labels
486
4732011
    }
487

            
488
474662954
    fn is_hidden_label(&self, label_index: LabelIndex) -> bool {
489
474662954
        label_index.value() == 0
490
474662954
    }
491

            
492
4409
    fn merge_disjoint<T: LTS<Label = Self::Label>>(self, other: &T) -> (Self, StateIndex) {
493
4409
        self.merge_disjoint_impl(other)
494
4409
    }
495
}
496

            
497
/// Metrics for a labelled transition system.
498
#[derive(Debug, Clone)]
499
pub struct LtsMetrics {
500
    /// The number of states in the LTS.
501
    pub num_of_states: usize,
502
    pub state_metrics: CompressedVecMetrics,
503
    /// The number of transitions in the LTS.
504
    pub num_of_transitions: usize,
505
    pub transition_labels_metrics: CompressedVecMetrics,
506
    pub transition_to_metrics: CompressedVecMetrics,
507
    /// The number of action labels in the LTS.
508
    pub num_of_labels: usize,
509
}
510

            
511
impl fmt::Display for LtsMetrics {
512
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
513
        // Print some information about the LTS.
514
        writeln!(f, "Number of states: {}", LargeFormatter(self.num_of_states))?;
515
        writeln!(f, "Number of action labels: {}", LargeFormatter(self.num_of_labels))?;
516
        writeln!(
517
            f,
518
            "Number of transitions: {}\n",
519
            LargeFormatter(self.num_of_transitions)
520
        )?;
521
        writeln!(f, "Memory usage:")?;
522
        writeln!(f, "States {}", self.state_metrics)?;
523
        writeln!(f, "Transition labels {}", self.transition_labels_metrics)?;
524
        write!(f, "Transition to {}", self.transition_to_metrics)
525
    }
526
}
527

            
528
/// Checks that two LTSs are equivalent, for testing purposes.
529
#[cfg(test)]
530
300
pub fn check_equivalent<L: LTS>(lts: &L, lts_read: &L) {
531
300
    println!("LTS labels: {:?}", lts.labels());
532
300
    println!("Read LTS labels: {:?}", lts_read.labels());
533

            
534
    // If labels are not used, the number of labels may be less. So find a remapping of old labels to new labels.
535
300
    let mapping = lts
536
300
        .labels()
537
300
        .iter()
538
1800
        .map(|label| lts_read.labels().iter().position(|l| l == label))
539
300
        .collect::<Vec<_>>();
540

            
541
    // Print the mapping
542
900
    for (i, m) in mapping.iter().enumerate() {
543
900
        println!("Label {} mapped to {:?}", i, m);
544
900
    }
545

            
546
300
    assert_eq!(lts.num_of_transitions(), lts_read.num_of_transitions());
547

            
548
    // Check that all the outgoing transitions are the same.
549
300000
    for state_index in lts.iter_states() {
550
300000
        let transitions: Vec<_> = lts.outgoing_transitions(state_index).collect();
551
300000
        let transitions_read: Vec<_> = if state_index.value() < lts_read.num_of_states() {
552
299993
            lts_read.outgoing_transitions(state_index).collect()
553
        } else {
554
            // Treat as deadlock if state_index is out of bounds
555
7
            Vec::new()
556
        };
557

            
558
        // Check that transitions are the same, modulo label remapping.
559
300438
        transitions.iter().for_each(|t| {
560
300438
            let mapped_label = mapping[t.label.value()].unwrap_or_else(|| panic!("Label {} should be found", t.label));
561
300438
            assert!(
562
300438
                transitions_read
563
300438
                    .iter()
564
400496
                    .any(|tr| tr.to == t.to && tr.label.value() == mapped_label)
565
            );
566
300438
        });
567
    }
568
300
}
569

            
570
#[cfg(test)]
571
mod tests {
572
    use merc_io::DumpFiles;
573
    use merc_utilities::random_test;
574

            
575
    use crate::LTS;
576
    use crate::num_reachable_states;
577
    use crate::random_lts;
578
    use crate::write_aut;
579

            
580
    #[test]
581
    #[cfg_attr(miri, ignore)] // Miri is too slow
582
1
    fn test_random_labelled_transition_system_merge_disjoint() {
583
100
        random_test(100, |rng| {
584
100
            let files = DumpFiles::new("test_random_merge_disjoint");
585

            
586
100
            let left = random_lts::<String, _>(rng, 1000, 20);
587
100
            files.dump("left.aut", |w| write_aut(w, &left)).unwrap();
588

            
589
100
            let right = random_lts::<String, _>(rng, 1000, 20);
590
100
            files.dump("right.aut", |w| write_aut(w, &right)).unwrap();
591

            
592
100
            let (merged, right_initial) = left.clone().merge_disjoint(&right);
593
100
            files.dump("merged.aut", |w| write_aut(w, &merged)).unwrap();
594

            
595
100
            assert_eq!(
596
100
                num_reachable_states(&left, left.initial_state_index()),
597
100
                num_reachable_states(&merged, merged.initial_state_index()),
598
                "The left LTS should be fully reachable in the merged LTS"
599
            );
600
100
            assert_eq!(
601
100
                num_reachable_states(&right, right.initial_state_index()),
602
100
                num_reachable_states(&merged, right_initial),
603
                "The right LTS should be fully reachable in the merged LTS"
604
            );
605
100
        });
606
1
    }
607
}