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::LtsBuilder;
12
use crate::LtsBuilderFast;
13
use crate::StateIndex;
14
use crate::TransitionLabel;
15

            
16
/// Generates a random LTS with the desired number of states and labels. Uses
17
/// the given [crate::TransitionLabel] type to generate the transition labels.
18
///
19
/// # Details
20
///
21
/// The number of labels is limited to the range `1..26`, since only singular
22
/// alphabetic labels are used, because those are easier to read and understand.
23
/// The hidden label occupies index 0, so at least one label is always required.
24
///
25
/// # Panics
26
///
27
/// Panics if `num_of_states == 0` (an LTS needs an initial state) or if
28
/// `num_of_labels` is not in `1..26`.
29
5403
pub fn random_lts<L: TransitionLabel, R: Rng>(
30
5403
    rng: &mut R,
31
5403
    num_of_states: usize,
32
5403
    num_of_labels: u32,
33
5403
) -> LabelledTransitionSystem<L> {
34
5403
    assert!(
35
5403
        (1..26).contains(&num_of_labels),
36
        "Number of labels must be in the range 1..26, since we only support alphabetic labels."
37
    );
38
5403
    assert!(
39
5403
        num_of_states > 0,
40
        "An LTS requires at least one state for the initial state."
41
    );
42

            
43
    // Introduce lower case letters for the labels.
44
5403
    let mut labels: Vec<L> = Vec::new();
45
5403
    labels.push(L::tau_label()); // The initial hidden label, assumed to be index 0.
46
14206
    for i in 0..(num_of_labels - 1) {
47
14206
        labels.push(L::from_index(i as usize));
48
14206
    }
49

            
50
5403
    let mut builder = LtsBuilderFast::with_capacity(labels.clone(), Vec::new(), num_of_states);
51

            
52
2250018
    for state_index in 0..num_of_states {
53
        // Introduce outgoing transitions for this state based on the desired out degree.
54
3950617
        for _ in 0..rng.random_range(0..num_of_labels) {
55
3950617
            // Pick a random label and state.
56
3950617
            let label = rng.random_range(0..num_of_labels);
57
3950617
            let to = rng.random_range(0..num_of_states);
58
3950617

            
59
3950617
            builder
60
3950617
                .add_transition(
61
3950617
                    StateIndex::new(state_index),
62
3950617
                    &labels[label as usize],
63
3950617
                    StateIndex::new(to),
64
3950617
                )
65
3950617
                .expect("Adding transitions does not fail");
66
3950617
        }
67
    }
68

            
69
    // Ensure deadlock states without outgoing transitions are still counted.
70
5403
    builder.require_num_of_states(num_of_states);
71

            
72
5403
    builder.finish(StateIndex::new(rng.random_range(0..num_of_states)), true)
73
5403
}
74

            
75
/// Mutates the given LTS by randomly adding and removing transitions, and
76
/// changing the labels of some transitions. The number of mutations is
77
/// determined by the `num_of_mutations` parameter.
78
1000
pub fn mutate_lts<L: LTS, R: Rng>(
79
1000
    lts: &L,
80
1000
    rng: &mut R,
81
1000
    num_of_mutations: usize,
82
1000
) -> Result<LabelledTransitionSystem<L::Label>, MercError> {
83
1000
    let mut builder = LtsBuilderFast::new(lts.labels().to_vec(), Vec::new());
84
1000
    builder.require_num_of_states(lts.num_of_states());
85

            
86
1000
    let removed_transition = if num_of_mutations > 0 {
87
1000
        rng.random_range(0..num_of_mutations)
88
    } else {
89
        0
90
    };
91

            
92
    // The indices of the transitions to remove.
93
1000
    let to_remove = if lts.num_of_transitions() > 0 {
94
1000
        let uniform = Uniform::new(0, lts.num_of_transitions())?;
95
1000
        rng.sample_iter(uniform).take(removed_transition).collect::<Vec<_>>()
96
    } else {
97
        Vec::new()
98
    };
99

            
100
1000
    let mut index = 0;
101
100000
    for state in lts.iter_states() {
102
100548
        for transition in lts.outgoing_transitions(state) {
103
            // Skip this transition if it is one of the transitions to remove.
104
100548
            if !to_remove.contains(&index) {
105
64944
                builder
106
64944
                    .add_transition(state, &lts.labels()[transition.label], transition.to)
107
64944
                    .expect("Adding transitions does not fail");
108
64944
            }
109

            
110
100548
            index += 1;
111
        }
112
    }
113

            
114
    // Add new random transitions for the remaining mutations.
115
1000
    let added_transitions = num_of_mutations - removed_transition;
116
1000
    if lts.num_of_states() > 0 && !lts.labels().is_empty() {
117
1000
        let state_uniform = Uniform::new(0, lts.num_of_states())?;
118
1000
        let label_uniform = Uniform::new(0, lts.labels().len())?;
119
51840
        for _ in 0..added_transitions {
120
51840
            let from = StateIndex::new(rng.sample(state_uniform));
121
51840
            let label = rng.sample(label_uniform);
122
51840
            let to = StateIndex::new(rng.sample(state_uniform));
123
51840
            builder.add_transition(from, &lts.labels()[label], to).unwrap();
124
51840
        }
125
    }
126

            
127
1000
    Ok(builder.finish(lts.initial_state_index(), true))
128
1000
}