1
#![forbid(unsafe_code)]
2

            
3
use rand::Rng;
4
use rand::RngExt;
5
use rand::distr::Uniform;
6

            
7
use merc_utilities::MercError;
8

            
9
use crate::LTS;
10
use crate::LabelledTransitionSystem;
11
use crate::LtsBuilderFast;
12
use crate::StateIndex;
13
use crate::TransitionLabel;
14
use crate::product_lts;
15

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

            
30
    // Synchronize on some of the labels.
31
2400
    let synchronized_labels: Vec<String> = (1..num_of_labels.min(3))
32
4800
        .map(|i| String::from_index(i as usize))
33
2400
        .collect();
34

            
35
2400
    components
36
2400
        .into_iter()
37
4800
        .reduce(|acc, lts| product_lts(&acc, &lts, Some(synchronized_labels.clone())))
38
2400
        .expect("At least one component should be present")
39
2400
}
40

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

            
60
    // Introduce lower case letters for the labels.
61
8300
    let mut labels: Vec<L> = Vec::new();
62
8300
    labels.push(L::tau_label()); // The initial hidden label, assumed to be index 0.
63
49400
    for i in 0..(num_of_labels - 1) {
64
49400
        labels.push(L::from_index(i as usize));
65
49400
    }
66

            
67
8300
    let mut builder = LtsBuilderFast::with_capacity(labels.clone(), Vec::new(), num_of_states);
68

            
69
953000
    for state_index in 0..num_of_states {
70
        // Introduce outgoing transitions for this state based on the desired out degree.
71
1204742
        for _ in 0..rng.random_range(0..outdegree) {
72
1177711
            // Pick a random label and state.
73
1177711
            let label = rng.random_range(0..num_of_labels);
74
1177711
            let to = rng.random_range(0..num_of_states);
75
1177711

            
76
1177711
            builder.add_transition(
77
1177711
                StateIndex::new(state_index),
78
1177711
                &labels[label as usize],
79
1177711
                StateIndex::new(to),
80
1177711
            );
81
1177711
        }
82
    }
83

            
84
    // Ensure there is at least one state (otherwise it would be an LTS without initial state).
85
8300
    builder.require_num_of_states(num_of_states.max(1));
86

            
87
8300
    builder.finish(StateIndex::new(rng.random_range(0..num_of_states)), true)
88
8300
}
89

            
90
/// Mutates the given LTS by randomly adding and removing transitions, and
91
/// changing the labels of some transitions. The number of mutations is
92
/// determined by the `num_of_mutations` parameter.
93
800
pub fn mutate_lts<L: LTS, R: Rng>(
94
800
    lts: &L,
95
800
    rng: &mut R,
96
800
    num_of_mutations: usize,
97
800
) -> Result<LabelledTransitionSystem<L::Label>, MercError> {
98
800
    let mut builder = LtsBuilderFast::new(lts.labels().to_vec(), Vec::new());
99
800
    builder.require_num_of_states(lts.num_of_states());
100

            
101
800
    let removed_transition = if num_of_mutations > 0 { rng.random_range(0..num_of_mutations) } else { 0 };
102

            
103
    // The indices of the transitions to remove.
104
800
    let to_remove = if lts.num_of_transitions() > 0 {
105
800
        let uniform = Uniform::new(0, lts.num_of_transitions())?;
106
800
        rng.sample_iter(uniform).take(removed_transition).collect::<Vec<_>>()
107
    } else {
108
        Vec::new()
109
    };
110

            
111
800
    let mut index = 0;
112
800000
    for state in lts.iter_states() {
113
800910
        for transition in lts.outgoing_transitions(state) {
114
            // Skip this transition if it is one of the transitions to remove.
115
800910
            if !to_remove.contains(&index) {
116
763289
                builder.add_transition(state, &lts.labels()[transition.label], transition.to);
117
763289
            }
118

            
119
800910
            index += 1;
120
        }
121
    }
122

            
123
    // Add new random transitions for the remaining mutations.
124
800
    let added_transitions = num_of_mutations - removed_transition;
125
800
    if lts.num_of_states() > 0 && !lts.labels().is_empty() {
126
800
        let state_uniform = Uniform::new(0, lts.num_of_states())?;
127
800
        let label_uniform = Uniform::new(0, lts.labels().len())?;
128
41152
        for _ in 0..added_transitions {
129
41152
            let from = StateIndex::new(rng.sample(state_uniform));
130
41152
            let label = rng.sample(label_uniform);
131
41152
            let to = StateIndex::new(rng.sample(state_uniform));
132
41152
            builder.add_transition(from, &lts.labels()[label], to);
133
41152
        }
134
    }
135

            
136
800
    Ok(builder.finish(lts.initial_state_index(), true))
137
800
}