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 26, since only singular alphabetic labels
22
/// are used, because those are easier to read and understand.
23
5400
pub fn random_lts<L: TransitionLabel, R: Rng>(
24
5400
    rng: &mut R,
25
5400
    num_of_states: usize,
26
5400
    num_of_labels: u32,
27
5400
) -> LabelledTransitionSystem<L> {
28
5400
    assert!(
29
5400
        num_of_labels < 26,
30
        "Too many labels requested, we only support alphabetic labels."
31
    );
32

            
33
    // Introduce lower case letters for the labels.
34
5400
    let mut labels: Vec<L> = Vec::new();
35
5400
    labels.push(L::tau_label()); // The initial hidden label, assumed to be index 0.
36
14200
    for i in 0..(num_of_labels - 1) {
37
14200
        labels.push(L::from_index(i as usize));
38
14200
    }
39

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

            
42
2250000
    for state_index in 0..num_of_states {
43
        // Introduce outgoing transitions for this state based on the desired out degree.
44
3951010
        for _ in 0..rng.random_range(0..num_of_labels) {
45
3950025
            // Pick a random label and state.
46
3950025
            let label = rng.random_range(0..num_of_labels);
47
3950025
            let to = rng.random_range(0..num_of_states);
48
3950025

            
49
3950025
            builder
50
3950025
                .add_transition(
51
3950025
                    StateIndex::new(state_index),
52
3950025
                    &labels[label as usize],
53
3950025
                    StateIndex::new(to),
54
3950025
                )
55
3950025
                .expect("Adding transitions does not fail");
56
3950025
        }
57
    }
58

            
59
    // Ensure there is at least one state (otherwise it would be an LTS without initial state).
60
5400
    builder.require_num_of_states(num_of_states.max(1));
61

            
62
5400
    builder.finish(StateIndex::new(rng.random_range(0..num_of_states)), true)
63
5400
}
64

            
65
/// Mutates the given LTS by randomly adding and removing transitions, and
66
/// changing the labels of some transitions. The number of mutations is
67
/// determined by the `num_of_mutations` parameter.
68
1000
pub fn mutate_lts<L: LTS, R: Rng>(
69
1000
    lts: &L,
70
1000
    rng: &mut R,
71
1000
    num_of_mutations: usize,
72
1000
) -> Result<LabelledTransitionSystem<L::Label>, MercError> {
73
1000
    let mut builder = LtsBuilderFast::new(lts.labels().to_vec(), Vec::new());
74
1000
    builder.require_num_of_states(lts.num_of_states());
75

            
76
1000
    let removed_transition = if num_of_mutations > 0 {
77
1000
        rng.random_range(0..num_of_mutations)
78
    } else {
79
        0
80
    };
81

            
82
    // The indices of the transitions to remove.
83
1000
    let to_remove = if lts.num_of_transitions() > 0 {
84
1000
        let uniform = Uniform::new(0, lts.num_of_transitions())?;
85
1000
        rng.sample_iter(uniform).take(removed_transition).collect::<Vec<_>>()
86
    } else {
87
        Vec::new()
88
    };
89

            
90
1000
    let mut index = 0;
91
100000
    for state in lts.iter_states() {
92
100000
        for transition in lts.outgoing_transitions(state) {
93
            // Skip this transition if it is one of the transitions to remove.
94
99896
            if !to_remove.contains(&index) {
95
63481
                builder
96
63481
                    .add_transition(state, &lts.labels()[transition.label], transition.to)
97
63481
                    .expect("Adding transitions does not fail");
98
63481
            }
99

            
100
99896
            index += 1;
101
        }
102
    }
103

            
104
    // Add new random transitions for the remaining mutations.
105
1000
    let added_transitions = num_of_mutations - removed_transition;
106
1000
    if lts.num_of_states() > 0 && !lts.labels().is_empty() {
107
1000
        let state_uniform = Uniform::new(0, lts.num_of_states())?;
108
1000
        let label_uniform = Uniform::new(0, lts.labels().len())?;
109
50500
        for _ in 0..added_transitions {
110
50500
            let from = StateIndex::new(rng.sample(state_uniform));
111
50500
            let label = rng.sample(label_uniform);
112
50500
            let to = StateIndex::new(rng.sample(state_uniform));
113
50500
            builder.add_transition(from, &lts.labels()[label], to).unwrap();
114
50500
        }
115
    }
116

            
117
1000
    Ok(builder.finish(lts.initial_state_index(), true))
118
1000
}