1
use log::debug;
2
use log::info;
3
use log::trace;
4
use rustc_hash::FxBuildHasher;
5
use rustc_hash::FxHashSet;
6
use streaming_iterator::StreamingIterator;
7

            
8
use merc_collections::IndexedSet;
9
use merc_io::LargeFormatter;
10
use merc_io::TimeProgress;
11
use merc_ldd::Storage;
12
use merc_ldd::height;
13
use merc_ldd::iterators::iter;
14
use merc_ldd::len;
15
use merc_lts::LtsBuilder;
16
use merc_lts::StateIndex;
17
use merc_utilities::MercError;
18

            
19
use crate::SymbolicLTS;
20
use crate::TransitionGroup;
21

            
22
/// Converts a symbolic BDD LTS to an explicit LTS.
23
///
24
/// # Details
25
///
26
/// This basically applies the symbolic transitions to every state in the state
27
/// space, and constructs the explicit LTS.
28
201
pub fn convert_symbolic_lts<B: LtsBuilder<String>, L: SymbolicLTS>(
29
201
    storage: &mut Storage,
30
201
    output: &mut B,
31
201
    lts: &L,
32
201
) -> Result<B::LTS, MercError> {
33
    // Compute for every read and write index its position in the transition vector.
34
201
    let mut read_positions = Vec::new();
35
201
    let mut write_positions = Vec::new();
36

            
37
1010
    for group in lts.transition_groups() {
38
1010
        let (rpos, wpos) = compute_positions(group);
39
1010

            
40
1010
        read_positions.push(rpos);
41
1010
        write_positions.push(wpos);
42
1010
    }
43

            
44
1010
    for (group_index, group) in lts.transition_groups().iter().enumerate() {
45
1010
        debug!("Transition group {}: {:?}", group_index, group,);
46

            
47
1010
        debug!("  Read indices: {:?}", read_positions[group_index]);
48
1010
        debug!("  Write indices: {:?}", write_positions[group_index]);
49
    }
50

            
51
    // Total number of states for progress reporting.
52
201
    let total_number_of_states = len(storage, lts.states());
53
201
    info!(
54
        "Converting symbolic LTS to explicit LTS with {} states",
55
        LargeFormatter(total_number_of_states)
56
    );
57

            
58
201
    let state_progress = TimeProgress::new(
59
        move |number_of_states| {
60
            info!(
61
                "Added {} states to discovered ({}%)",
62
                LargeFormatter(number_of_states),
63
                number_of_states * 100 / total_number_of_states
64
            );
65
        },
66
        1,
67
    );
68

            
69
    // All states have been explored, so add them to the discovered set immediately.
70
201
    let mut discovered: IndexedSet<Vec<u32>, FxBuildHasher> = IndexedSet::new();
71
201
    let mut state_iter = iter(storage, lts.states());
72
35332
    while let Some(state) = state_iter.next() {
73
35131
        let (_, inserted) = discovered.insert(state.clone());
74
35131
        debug_assert!(inserted, "State space contains duplicate states");
75
35131
        state_progress.print(discovered.len())
76
    }
77

            
78
    // Total number of states for progress reporting.
79
201
    let progress = TimeProgress::new(
80
15
        move |(number_of_states, number_of_transitions)| {
81
15
            info!(
82
                "Explored {} states and {} transitions ({}%)",
83
                LargeFormatter(number_of_states),
84
                LargeFormatter(number_of_transitions),
85
                number_of_states * 100 / total_number_of_states
86
            );
87
15
        },
88
        1,
89
    );
90

            
91
    // Keep track of outgoing transitions to avoid duplicates.
92
201
    let mut outgoing = FxHashSet::default();
93

            
94
    // Avoid reallocations.
95
201
    let mut target = vec![0u32; height(storage, lts.states())];
96

            
97
201
    let mut state_iter = iter(storage, lts.states());
98
201
    let mut index: usize = 0;
99

            
100
35332
    while let Some(state) = state_iter.next() {
101
        // Find the index of this state, it was already added before.
102
35131
        let state_index = discovered
103
35131
            .index(state)
104
35131
            .ok_or("Found state that was not in the state set")?;
105

            
106
        // Apply every transition group to this state.
107
176025
        for (group_index, group) in lts.transition_groups().iter().enumerate() {
108
176025
            let mut iter_transitions = iter(storage, group.relation());
109
1897656
            'skip: while let Some(transition) = iter_transitions.next() {
110
                // Try to match the read parameters of this vector.
111
1721631
                for (index, i) in group.read_indices().iter().enumerate() {
112
1382830
                    if state[*i as usize] != transition[read_positions[group_index][index]] {
113
                        // If they do not match, skip this transition.
114
1094931
                        continue 'skip;
115
287899
                    }
116
                }
117

            
118
                // Apply the transition writes to the state vector.
119
626700
                target.clone_from_slice(state);
120
626700
                trace!("transition {:?}", transition);
121
3636292
                for (index, i) in group.write_indices().iter().enumerate() {
122
3636292
                    target[*i as usize] = transition[write_positions[group_index][index]];
123
3636292
                }
124

            
125
                // Find the action label.
126
626700
                let action_value = transition[group
127
626700
                    .action_label_index()
128
626700
                    .ok_or("Transition vector should at least have the action label")?];
129
626700
                let label = &lts.action_labels()[action_value as usize];
130

            
131
                // Find the target state index.
132
626700
                let target_index = discovered
133
626700
                    .index(&target)
134
626700
                    .ok_or("Found state that was not in the state set")?;
135
                // Include the action label in the dedup key: the same source/target pair can be
136
                // connected by transitions with different labels, and dropping any of them would
137
                // silently lose behavior.
138
626700
                if outgoing.insert((*state_index, action_value, *target_index)) {
139
626231
                    trace!(
140
                        " Found transition in {group_index} from {:?} to {:?} with label {:?}",
141
                        state, target, label
142
                    );
143

            
144
626231
                    output.add_transition(StateIndex::new(*state_index), label, StateIndex::new(*target_index))?;
145
469
                }
146
            }
147
        }
148

            
149
35131
        progress.print((index, output.num_of_transitions()));
150
35131
        index += 1;
151

            
152
        // Clear the outgoing set for the next state.
153
35131
        outgoing.clear();
154
    }
155

            
156
    // Find the initial state.
157
201
    let mut state_iter = iter(storage, lts.initial_state());
158
201
    let initial_state = state_iter.next().ok_or("Symbolic LTS has no initial state")?;
159

            
160
201
    let initial_state_index = discovered
161
201
        .index(initial_state)
162
201
        .ok_or("Initial state was not found in the discovered state set")?;
163

            
164
201
    output.finish(StateIndex::new(*initial_state_index))
165
201
}
166

            
167
/// Computes the positions of the read and write indices in the transition vector.
168
1010
fn compute_positions<G: TransitionGroup>(group: &G) -> (Vec<usize>, Vec<usize>) {
169
    // Ensure indices are non-decreasing; merge relies on sorted inputs.
170
1010
    debug_assert!(group.read_indices().is_sorted(), "read_indices must be sorted");
171
1010
    debug_assert!(group.write_indices().is_sorted(), "write_indices must be sorted");
172

            
173
1010
    let mut rpos = Vec::with_capacity(group.read_indices().len());
174
1010
    let mut wpos = Vec::with_capacity(group.write_indices().len());
175

            
176
1010
    let mut ri = 0usize;
177
1010
    let mut wi = 0usize;
178
1010
    let mut index = 0usize;
179

            
180
7440
    while ri < group.read_indices().len() && wi < group.write_indices().len() {
181
6430
        if group.read_indices()[ri] <= group.write_indices()[wi] {
182
3391
            rpos.push(index);
183
3391
            ri += 1;
184
3391
        } else {
185
3039
            wpos.push(index);
186
3039
            wi += 1;
187
3039
        }
188
6430
        index += 1;
189
    }
190

            
191
2015
    while ri < group.read_indices().len() {
192
1005
        rpos.push(index);
193
1005
        ri += 1;
194
1005
        index += 1;
195
1005
    }
196

            
197
2499
    while wi < group.write_indices().len() {
198
1489
        wpos.push(index);
199
1489
        wi += 1;
200
1489
        index += 1;
201
1489
    }
202
1010
    (rpos, wpos)
203
1010
}
204

            
205
#[cfg(test)]
206
mod tests {
207
    use merc_ldd::Storage;
208
    use merc_lts::LTS;
209
    use merc_lts::LtsBuilderMem;
210
    use merc_utilities::test_logger;
211

            
212
    use crate::read_symbolic_lts;
213

            
214
    use super::convert_symbolic_lts;
215

            
216
    #[test]
217
1
    fn test_convert_symbolic_lts() {
218
1
        test_logger();
219

            
220
1
        let input = include_bytes!("../../../examples/lts/abp.sym");
221

            
222
1
        let mut storage = Storage::new();
223
1
        let symbolic_lts = read_symbolic_lts(&mut storage, &input[..]).unwrap();
224

            
225
1
        let mut builder = LtsBuilderMem::new(Vec::new(), Vec::new());
226
1
        let lts = convert_symbolic_lts(&mut storage, &mut builder, &symbolic_lts).unwrap();
227

            
228
1
        debug_assert_eq!(lts.num_of_states(), 74);
229
1
        debug_assert_eq!(lts.num_of_transitions(), 92);
230
1
    }
231
}