1
use oxidd::ldd::LDDFunction;
2
use oxidd::ldd::LDDManagerRef;
3
use rand::Rng;
4
use rand::RngExt;
5
use rand::seq::IndexedRandom;
6
use rand::seq::IteratorRandom;
7

            
8
use merc_aterm::ATermString;
9
use merc_data::DataExpression;
10
use merc_data::DataSpecification;
11
use merc_data::DataVariable;
12
use merc_lts::LtsMultiAction;
13
use merc_lts::TransitionLabel;
14
use merc_utilities::MercError;
15
use merc_utilities::Timing;
16

            
17
use crate::SummandGroup;
18
use crate::SymbolicLPS;
19
use crate::SymbolicLts;
20
use crate::from_iter;
21
use crate::random_vector_set;
22
use crate::reachability;
23

            
24
/// Generates random symbolic LTSs for testing purposes.
25
400
pub fn random_symbolic_lts<R: Rng>(
26
400
    rng: &mut R,
27
400
    manager: &LDDManagerRef,
28
400
    num_state_variables: usize,
29
400
    num_action_labels: usize,
30
400
) -> Result<SymbolicLts, MercError> {
31
400
    let num_of_values = 5usize;
32

            
33
400
    let states = random_vector_set(rng, 100, num_state_variables, num_of_values as u32);
34
400
    let initial_state = states.iter().choose(rng).ok_or("at least one state generated")?;
35

            
36
400
    let mut action_labels = Vec::new();
37
2000
    for i in 0..num_action_labels {
38
2000
        action_labels.push(LtsMultiAction::from_index(i));
39
2000
    }
40

            
41
400
    let mut parameter_values = Vec::new();
42
400
    let value = DataExpression::from_string("1")?;
43
4000
    for _ in 0..num_state_variables {
44
4000
        parameter_values.push(Vec::from_iter(std::iter::repeat_n(value.clone(), num_of_values)));
45
4000
    }
46

            
47
400
    let parameters = (0..num_state_variables)
48
4000
        .map(|i| DataVariable::new(ATermString::new(format!("p{i}"))))
49
400
        .collect::<Vec<_>>();
50

            
51
400
    let mut summand_groups = Vec::new();
52
400
    for _ in 0..5 {
53
        // random_range panics on an empty range, so guard against zero parameters.
54
2000
        let num_of_read_variables = if parameters.is_empty() {
55
            0
56
        } else {
57
2000
            rng.random_range(0..parameters.len())
58
        };
59
2000
        let read_parameters = parameters
60
2000
            .sample(rng, num_of_read_variables)
61
2000
            .cloned()
62
2000
            .collect::<Vec<_>>();
63

            
64
2000
        let num_of_write_variables = if parameters.is_empty() {
65
            0
66
        } else {
67
2000
            rng.random_range(0..parameters.len())
68
        };
69
2000
        let write_parameters = parameters
70
2000
            .sample(rng, num_of_write_variables)
71
2000
            .cloned()
72
2000
            .collect::<Vec<_>>();
73

            
74
2000
        let relation = random_vector_set(rng, 10, read_parameters.len() + write_parameters.len() + 1, 5);
75
2000
        let relation_bdd = from_iter(manager, relation.iter());
76

            
77
2000
        summand_groups.push(SummandGroup::new(
78
2000
            manager,
79
2000
            &parameters,
80
2000
            read_parameters,
81
2000
            write_parameters,
82
2000
            relation_bdd,
83
        )?)
84
    }
85

            
86
400
    let initial_state_ldd = from_iter(manager, std::iter::once(initial_state));
87

            
88
    // Explore via a lightweight wrapper so SymbolicLts is only constructed at the end
89
    // with the actual reachable state set.
90
400
    let mut lps = ExplorationLps {
91
400
        initial_state: initial_state_ldd.clone(),
92
400
        summand_groups,
93
400
    };
94
400
    let reachable = reachability(manager, &mut lps, &Timing::new())?;
95

            
96
400
    Ok(SymbolicLts::new(
97
400
        DataSpecification::default(),
98
400
        reachable,
99
400
        initial_state_ldd,
100
400
        lps.summand_groups,
101
400
        action_labels,
102
400
        parameter_values,
103
400
    ))
104
400
}
105

            
106
/// Minimal [SymbolicLPS] wrapper used during exploration before the final [SymbolicLts] is built.
107
struct ExplorationLps {
108
    initial_state: LDDFunction,
109
    summand_groups: Vec<SummandGroup>,
110
}
111

            
112
impl SymbolicLPS for ExplorationLps {
113
    type Group = SummandGroup;
114

            
115
800
    fn initial_state(&self) -> &LDDFunction {
116
800
        &self.initial_state
117
800
    }
118

            
119
    fn transition_groups(&self) -> &[Self::Group] {
120
        &self.summand_groups
121
    }
122

            
123
1423
    fn transition_groups_mut(&mut self) -> &mut [Self::Group] {
124
1423
        &mut self.summand_groups
125
1423
    }
126

            
127
400
    fn create_context(&self) {}
128
}