1
use log::debug;
2

            
3
use crate::LabelledTransitionSystem;
4

            
5
/// A trait for partition refinement algorithms that expose the block number for
6
/// every state. Can be used to compute the quotient labelled transition system.
7
///
8
/// The invariants are that the union of all blocks is the original set, and
9
/// that each block contains distinct elements
10
pub trait Partition {
11
    /// Returns the block number for the given state.
12
    fn block_number(&self, state_index: usize) -> usize;
13

            
14
    /// Returns the number of blocks in the partition.
15
    fn num_of_blocks(&self) -> usize;
16

            
17
    /// Returns the number of elements in the partition.
18
    fn len(&self) -> usize;
19

            
20
    /// Returns whether the partition is empty.
21
    fn is_empty(&self) -> bool {
22
        self.len() == 0
23
    }
24

            
25
    /// Returns true iff the partitions are equal, runs in O(n^2)
26
111
    fn equal(&self, other: &impl Partition) -> bool {
27
        // Check that states in the same block, have a single (unique) number in
28
        // the other partition.
29
316401
        for block_index in 0..self.num_of_blocks() {
30
316401
            let mut other_block_index = None;
31

            
32
7686339186
            for state_index in (0..self.len()).filter(|&state_index| self.block_number(state_index) == block_index) {
33
564966
                match other_block_index {
34
316401
                    None => other_block_index = Some(other.block_number(state_index)),
35
248565
                    Some(other_block_index) => {
36
248565
                        if other.block_number(state_index) != other_block_index {
37
                            return false;
38
248565
                        }
39
                    }
40
                }
41
            }
42
        }
43

            
44
316401
        for block_index in 0..other.num_of_blocks() {
45
316401
            let mut other_block_index = None;
46

            
47
7686339186
            for state_index in (0..self.len()).filter(|&state_index| other.block_number(state_index) == block_index) {
48
564966
                match other_block_index {
49
316401
                    None => other_block_index = Some(self.block_number(state_index)),
50
248565
                    Some(other_block_index) => {
51
248565
                        if self.block_number(state_index) != other_block_index {
52
                            return false;
53
248565
                        }
54
                    }
55
                }
56
            }
57
        }
58

            
59
111
        true
60
111
    }
61
}
62

            
63
/// Returns a new LTS based on the given partition.
64
///
65
/// All states in a single block are replaced by a single representative state.
66
116
pub fn quotient_lts(
67
116
    lts: &LabelledTransitionSystem,
68
116
    partition: &impl Partition,
69
116
    eliminate_tau_loops: bool,
70
116
) -> LabelledTransitionSystem {
71
116
    let start = std::time::Instant::now();
72
116
    // Introduce the transitions based on the block numbers
73
116
    let mut transitions: Vec<(usize, usize, usize)> = Vec::default();
74

            
75
565011
    for state_index in lts.iter_states() {
76
980997
        for &(label, to) in lts.outgoing_transitions(state_index) {
77
980997
            let block = partition.block_number(state_index);
78
980997
            let to_block = partition.block_number(to);
79
980997

            
80
980997
            // If we eliminate tau loops then check if the 'to' and 'from' end up in the same block
81
980997
            if !(eliminate_tau_loops && lts.is_hidden_label(label) && block == to_block)
82
            {
83
980987
                debug_assert!(
84
980987
                    partition.block_number(state_index) < partition.num_of_blocks(),
85
                    "Quotienting assumes that the block numbers do not exceed the number of blocks"
86
                );
87

            
88
                // Make sure to keep the outgoing transitions sorted.
89
980987
                transitions.push((block, label, to_block));
90
10
            }
91
        }
92
    }
93

            
94
    // Remove duplicates.
95
116
    transitions.sort_unstable();
96
116
    transitions.dedup();
97
116

            
98
116
    let result = LabelledTransitionSystem::new(
99
116
        partition.block_number(lts.initial_state_index()),
100
116
        Some(partition.num_of_blocks()),
101
232
        || transitions.iter().cloned(),
102
116
        lts.labels().into(),
103
116
        lts.hidden_labels().into()
104
116
    );
105
116
    debug!("Time quotient: {:.3}s", start.elapsed().as_secs_f64());
106
116
    result
107
116
}