1
#![forbid(unsafe_code)]
2

            
3
use rand::Rng;
4

            
5
use crate::LabelledTransitionSystem;
6
use crate::LtsBuilderFast;
7
use crate::StateIndex;
8
use crate::TransitionLabel;
9
use crate::product_lts;
10

            
11
/// Generates a random LTS with the desired number of states, labels and out
12
/// degree by composing three smaller random LTSs using the synchronous product.
13
/// This is often a more realistic structure than fully random LTSs, but
14
/// otherwise see [`random_lts_monolithic`].
15
1700
pub fn random_lts(
16
1700
    rng: &mut impl Rng,
17
1700
    num_of_states: usize,
18
1700
    num_of_labels: u32,
19
1700
    outdegree: usize,
20
1700
) -> LabelledTransitionSystem<String> {
21
1700
    let components: Vec<LabelledTransitionSystem<String>> = (0..3)
22
5100
        .map(|_| random_lts_monolithic(rng, num_of_states, num_of_labels, outdegree))
23
1700
        .collect();
24

            
25
    // Synchronize on some of the labels.
26
1700
    let synchronized_labels: Vec<String> = (1..num_of_labels.min(3))
27
3400
        .map(|i| String::from_index(i as usize))
28
1700
        .collect();
29

            
30
1700
    components
31
1700
        .into_iter()
32
3400
        .reduce(|acc, lts| product_lts(&acc, &lts, Some(synchronized_labels.clone())))
33
1700
        .expect("At least one component should be present")
34
1700
}
35

            
36
/// Generates a monolithic LTS with the desired number of states, labels, out
37
/// degree and in degree for all the states. Uses the given TransitionLabel type
38
/// to generate the transition labels.
39
///
40
/// # Details
41
///
42
/// The number of labels is limited to 26, since only singular alphabetic labels
43
/// are used, because those are easier to read and understand.
44
5400
pub fn random_lts_monolithic<L: TransitionLabel>(
45
5400
    rng: &mut impl Rng,
46
5400
    num_of_states: usize,
47
5400
    num_of_labels: u32,
48
5400
    outdegree: usize,
49
5400
) -> LabelledTransitionSystem<L> {
50
5400
    assert!(
51
5400
        num_of_labels < 26,
52
        "Too many labels requested, we only support alphabetic labels."
53
    );
54

            
55
    // Introduce lower case letters for the labels.
56
5400
    let mut labels: Vec<L> = Vec::new();
57
5400
    labels.push(L::tau_label()); // The initial hidden label, assumed to be index 0.
58
19800
    for i in 0..(num_of_labels - 1) {
59
19800
        labels.push(L::from_index(i as usize));
60
19800
    }
61

            
62
5400
    let mut builder = LtsBuilderFast::with_capacity(labels.clone(), Vec::new(), num_of_states);
63

            
64
75600
    for state_index in 0..num_of_states {
65
        // Introduce outgoing transitions for this state based on the desired out degree.
66
340773
        for _ in 0..rng.random_range(0..outdegree) {
67
337824
            // Pick a random label and state.
68
337824
            let label = rng.random_range(0..num_of_labels);
69
337824
            let to = rng.random_range(0..num_of_states);
70
337824

            
71
337824
            builder.add_transition(
72
337824
                StateIndex::new(state_index),
73
337824
                &labels[label as usize],
74
337824
                StateIndex::new(to),
75
337824
            );
76
337824
        }
77
    }
78

            
79
5400
    if builder.num_of_states() == 0 {
80
25
        // Ensure there is at least one state (otherwise it would be an LTS without initial state).
81
25
        builder.require_num_of_states(1);
82
5375
    }
83

            
84
5400
    builder.finish(StateIndex::new(0), true)
85
5400
}
86

            
87
#[cfg(test)]
88
mod tests {
89
    use super::*;
90

            
91
    use test_log::test;
92

            
93
    use merc_utilities::random_test;
94

            
95
    #[test]
96
    fn random_lts_test() {
97
100
        random_test(100, |rng| {
98
            // This test only checks the assertions of an LTS internally.
99
100
            let _lts = random_lts(rng, 10, 3, 3);
100
100
        });
101
    }
102
}