1
#![forbid(unsafe_code)]
2

            
3
use std::borrow::Borrow;
4
use std::collections::HashMap;
5
use std::fmt;
6
use std::hash::Hash;
7
use std::sync::Mutex;
8

            
9
use merc_collections::ByteCompressedVec;
10
use merc_collections::CompressedEntry;
11
use merc_utilities::MercError;
12

            
13
use crate::LabelIndex;
14
use crate::LabelledTransitionSystem;
15
use crate::StateIndex;
16
use crate::TransitionLabel;
17

            
18
/// A trait for building labelled transition systems incrementally.
19
///
20
/// # Details
21
///
22
/// Depending on the implementation this can be done in a memory efficient way,
23
/// or in a way that is optimized for speed. Alternatively, the resulting LTS is
24
/// immediately written to disk. The builder accumulates transitions using
25
/// `add_transition`, and once all transitions have been added, the labelled
26
/// transition system can be constructed with `finish`. An initial state can
27
/// also be specified during finalization.
28
pub trait LtsBuilder<L: TransitionLabel> {
29
    /// The result type of the builder once finalized.
30
    type LTS;
31

            
32
    /// Adds a transition to the builder. For efficiency reasons, we can use
33
    /// another type `Q` for the label.
34
    fn add_transition<Q>(&mut self, from: StateIndex, label: &Q, to: StateIndex) -> Result<(), MercError>
35
    where
36
        L: Borrow<Q>,
37
        Q: ?Sized + ToOwned<Owned = L> + Eq + Hash;
38

            
39
    /// Finalizes the builder and returns the constructed labelled transition system.
40
    fn finish(&mut self, initial_state: StateIndex) -> Result<Self::LTS, MercError>;
41

            
42
    /// Returns the number of transitions added to the builder.
43
    fn num_of_transitions(&self) -> usize;
44

            
45
    /// Returns the number of states added to the builder.
46
    fn num_of_states(&self) -> usize;
47

            
48
    /// Ensures the builder accounts for at least `num_states` states, so that
49
    /// states without incident transitions (such as an isolated initial state)
50
    /// are still reflected in the result.
51
    fn require_num_of_states(&mut self, num_states: usize);
52
}
53

            
54
/// A builder that discards all transitions, producing no output. This is useful
55
/// when an LTS only needs to be explored but the result is not required.
56
impl<L: TransitionLabel> LtsBuilder<L> for () {
57
    type LTS = ();
58

            
59
    fn add_transition<Q>(&mut self, _from: StateIndex, _label: &Q, _to: StateIndex) -> Result<(), MercError>
60
    where
61
        L: Borrow<Q>,
62
        Q: ?Sized + ToOwned<Owned = L> + Eq + Hash,
63
    {
64
        Ok(())
65
    }
66

            
67
    fn finish(&mut self, _initial_state: StateIndex) -> Result<Self::LTS, MercError> {
68
        Ok(())
69
    }
70

            
71
    fn num_of_transitions(&self) -> usize {
72
        0
73
    }
74

            
75
    fn num_of_states(&self) -> usize {
76
        0
77
    }
78

            
79
    fn require_num_of_states(&mut self, _num_states: usize) {}
80
}
81

            
82
/// A builder that additionally accepts transitions through a shared `&self`
83
/// reference, synchronising internally.
84
///
85
/// # Details
86
///
87
/// This is used by the parallel explorer, where several worker threads stream
88
/// transitions into a single builder at once. Implementors take care of their
89
/// own synchronisation, so callers need no external lock. The builder is
90
/// finalised through the [`LtsBuilder`] supertrait once exploration completes.
91
pub trait ConcurrentLtsBuilder<L: TransitionLabel>: LtsBuilder<L> + Sync {
92
    /// Adds a transition through a shared reference. See
93
    /// [`LtsBuilder::add_transition`].
94
    fn add_transition_shared<Q>(&self, from: StateIndex, label: &Q, to: StateIndex) -> Result<(), MercError>
95
    where
96
        L: Borrow<Q>,
97
        Q: ?Sized + ToOwned<Owned = L> + Eq + Hash;
98
}
99

            
100
/// The discarding builder also discards concurrent transitions.
101
impl<L: TransitionLabel> ConcurrentLtsBuilder<L> for () {
102
    fn add_transition_shared<Q>(&self, _from: StateIndex, _label: &Q, _to: StateIndex) -> Result<(), MercError>
103
    where
104
        L: Borrow<Q>,
105
        Q: ?Sized + ToOwned<Owned = L> + Eq + Hash,
106
    {
107
        Ok(())
108
    }
109
}
110

            
111
/// Adapts any [`LtsBuilder`] into a [`ConcurrentLtsBuilder`] by guarding it with
112
/// a `Mutex`.
113
///
114
/// Concurrent (`&self`) transitions simply lock the mutex and delegate to the
115
/// inner builder's [`LtsBuilder::add_transition`]; the single-threaded
116
/// (`&mut self`) operations access the builder without locking. This serialises
117
/// all writes, which is enough for builders that are cheap to write to (such as
118
/// [`crate::AutStream`]) while the expensive exploration happens outside the
119
/// lock.
120
pub struct MutexLtsBuilder<B> {
121
    inner: Mutex<B>,
122
}
123

            
124
impl<B> MutexLtsBuilder<B> {
125
    /// Wraps `builder` so it can be shared across worker threads.
126
    pub fn new(builder: B) -> MutexLtsBuilder<B> {
127
        MutexLtsBuilder {
128
            inner: Mutex::new(builder),
129
        }
130
    }
131

            
132
    /// Unwraps and returns the inner builder.
133
    pub fn into_inner(self) -> B {
134
        self.inner.into_inner().expect("MutexLtsBuilder mutex poisoned")
135
    }
136
}
137

            
138
impl<L: TransitionLabel, B: LtsBuilder<L>> LtsBuilder<L> for MutexLtsBuilder<B> {
139
    type LTS = B::LTS;
140

            
141
    fn add_transition<Q>(&mut self, from: StateIndex, label: &Q, to: StateIndex) -> Result<(), MercError>
142
    where
143
        L: Borrow<Q>,
144
        Q: ?Sized + ToOwned<Owned = L> + Eq + Hash,
145
    {
146
        // We hold `&mut self`, so the builder is exclusively ours and needs no lock.
147
        self.inner
148
            .get_mut()
149
            .expect("MutexLtsBuilder mutex poisoned")
150
            .add_transition(from, label, to)
151
    }
152

            
153
    fn finish(&mut self, initial_state: StateIndex) -> Result<Self::LTS, MercError> {
154
        self.inner
155
            .get_mut()
156
            .expect("MutexLtsBuilder mutex poisoned")
157
            .finish(initial_state)
158
    }
159

            
160
    fn num_of_transitions(&self) -> usize {
161
        self.inner
162
            .lock()
163
            .expect("MutexLtsBuilder mutex poisoned")
164
            .num_of_transitions()
165
    }
166

            
167
    fn num_of_states(&self) -> usize {
168
        self.inner
169
            .lock()
170
            .expect("MutexLtsBuilder mutex poisoned")
171
            .num_of_states()
172
    }
173

            
174
    fn require_num_of_states(&mut self, num_states: usize) {
175
        self.inner
176
            .get_mut()
177
            .expect("MutexLtsBuilder mutex poisoned")
178
            .require_num_of_states(num_states)
179
    }
180
}
181

            
182
impl<L: TransitionLabel, B: LtsBuilder<L> + Send> ConcurrentLtsBuilder<L> for MutexLtsBuilder<B> {
183
    fn add_transition_shared<Q>(&self, from: StateIndex, label: &Q, to: StateIndex) -> Result<(), MercError>
184
    where
185
        L: Borrow<Q>,
186
        Q: ?Sized + ToOwned<Owned = L> + Eq + Hash,
187
    {
188
        self.inner
189
            .lock()
190
            .expect("MutexLtsBuilder mutex poisoned")
191
            .add_transition(from, label, to)
192
    }
193
}
194

            
195
/// This struct helps in building a labelled transition system by accumulating
196
/// transitions in a memory efficient way.
197
///
198
/// # Details
199
///
200
/// Transitions can be added with `add_transition`, and once all transitions
201
/// have been added, the labelled transition system can be constructed with
202
/// `finish`. An initial state can also be specified during finalization.
203
///
204
pub struct LtsBuilderMem<L> {
205
    transition_from: ByteCompressedVec<StateIndex>,
206
    transition_labels: ByteCompressedVec<LabelIndex>,
207
    transition_to: ByteCompressedVec<StateIndex>,
208

            
209
    // This is used to keep track of the label to index mapping.
210
    labels_index: HashMap<L, LabelIndex>,
211
    labels: Vec<L>,
212

            
213
    /// The hidden labels that should be mapped to the hidden action.
214
    hidden_labels: Vec<String>,
215

            
216
    /// The number of states (derived from the transitions).
217
    num_of_states: usize,
218
}
219

            
220
impl<L: TransitionLabel> LtsBuilderMem<L> {
221
    /// Initializes a new empty builder.
222
503
    pub fn new(labels: Vec<L>, hidden_labels: Vec<String>) -> Self {
223
503
        Self::with_capacity(labels, hidden_labels, 0, 0, 0)
224
503
    }
225

            
226
    /// Initializes the builder with pre-allocated capacity for states and transitions. The number of labels
227
    /// can be used when labels are added dynamically.
228
6046
    pub fn with_capacity(
229
6046
        mut labels: Vec<L>,
230
6046
        hidden_labels: Vec<String>,
231
6046
        num_of_labels: usize,
232
6046
        num_of_states: usize,
233
6046
        num_of_transitions: usize,
234
6046
    ) -> Self {
235
        // Remove duplicates from the labels.
236
6046
        labels.sort();
237
6046
        labels.dedup();
238

            
239
        // Introduce the fixed 0-indexed tau label.
240
6046
        if let Some(tau_pos) = labels.iter().position(|l| l.is_tau_label()) {
241
            labels.swap(0, tau_pos);
242
6046
        } else {
243
6046
            labels.insert(0, L::tau_label());
244
6046
        }
245

            
246
        // Ensure that all hidden labels are mapped to the tau action.
247
6046
        let mut labels_index = HashMap::new();
248
6046
        labels_index.insert(L::tau_label(), LabelIndex::new(0));
249
6046
        for (index, label) in labels.iter().enumerate() {
250
6046
            if hidden_labels.iter().any(|l| label.matches_label(l)) {
251
                labels_index.insert(label.clone(), LabelIndex::new(0)); // Map hidden labels to tau
252
6046
            } else {
253
6046
                labels_index.insert(label.clone(), LabelIndex::new(index));
254
6046
            }
255
        }
256

            
257
6046
        Self {
258
6046
            transition_from: ByteCompressedVec::with_capacity(num_of_transitions, num_of_states.bytes_required()),
259
6046
            transition_labels: ByteCompressedVec::with_capacity(
260
6046
                num_of_transitions,
261
6046
                num_of_labels.max(labels.len()).bytes_required(),
262
6046
            ),
263
6046
            transition_to: ByteCompressedVec::with_capacity(num_of_transitions, num_of_states.bytes_required()),
264
6046
            labels_index,
265
6046
            labels,
266
6046
            hidden_labels,
267
6046
            num_of_states: 0,
268
6046
        }
269
6046
    }
270

            
271
    /// Returns an iterator over all transitions as (from, label, to) tuples.
272
12092
    fn iter(&self) -> impl Iterator<Item = (StateIndex, LabelIndex, StateIndex)> {
273
12092
        self.transition_from
274
12092
            .iter()
275
12092
            .zip(self.transition_labels.iter())
276
12092
            .zip(self.transition_to.iter())
277
21370908
            .map(|((from, label), to)| (from, label, to))
278
12092
    }
279
}
280

            
281
impl<L: TransitionLabel> LtsBuilder<L> for LtsBuilderMem<L> {
282
    type LTS = LabelledTransitionSystem<L>;
283

            
284
1062100
    fn add_transition<Q>(&mut self, from: StateIndex, label: &Q, to: StateIndex) -> Result<(), MercError>
285
1062100
    where
286
1062100
        L: Borrow<Q>,
287
1062100
        Q: ?Sized + ToOwned<Owned = L> + Eq + Hash,
288
    {
289
1062100
        let label_index = if let Some(&index) = self.labels_index.get(label) {
290
1058681
            index
291
        } else {
292
            // Label was not yet added, so add it to the labels and the index.
293
3419
            let label = label.to_owned();
294
3419
            let index = if self.hidden_labels.iter().any(|l| label.matches_label(l)) {
295
274
                LabelIndex::new(0) // Map hidden labels to tau
296
            } else {
297
3145
                let idx = LabelIndex::new(self.labels.len());
298
3145
                self.labels.push(label.clone());
299
3145
                idx
300
            };
301
3419
            self.labels_index.insert(label, index);
302
3419
            index
303
        };
304

            
305
1062100
        self.transition_from.push(from);
306
1062100
        self.transition_labels.push(label_index);
307
1062100
        self.transition_to.push(to);
308

            
309
        // Update the number of states.
310
1062100
        self.num_of_states = self.num_of_states.max(from.value() + 1).max(to.value() + 1);
311
1062100
        Ok(())
312
1062100
    }
313

            
314
6046
    fn finish(&mut self, initial_state: StateIndex) -> Result<Self::LTS, MercError> {
315
6046
        Ok(LabelledTransitionSystem::new(
316
6046
            initial_state,
317
6046
            Some(self.num_of_states),
318
12092
            || self.iter(),
319
6046
            self.labels.clone(),
320
        ))
321
6046
    }
322

            
323
859106
    fn num_of_transitions(&self) -> usize {
324
859106
        self.transition_from.len()
325
859106
    }
326

            
327
    fn num_of_states(&self) -> usize {
328
        self.num_of_states
329
    }
330

            
331
317
    fn require_num_of_states(&mut self, num_of_states: usize) {
332
317
        if num_of_states > self.num_of_states {
333
317
            self.num_of_states = num_of_states;
334
317
        }
335
317
    }
336
}
337

            
338
impl<Label: TransitionLabel> fmt::Debug for LtsBuilderMem<Label> {
339
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
340
        writeln!(f, "Transitions:")?;
341
        for (from, label, to) in self.iter() {
342
            writeln!(f, "    {:?} --[{:?}]-> {:?}", from, label, to)?;
343
        }
344
        Ok(())
345
    }
346
}