1
use oxidd::bdd::BDDFunction;
2
use oxidd::bdd::BDDManagerRef;
3
use rand::Rng;
4

            
5
use merc_symbolic::create_variables;
6
use merc_symbolic::random_bdd;
7
use merc_utilities::MercError;
8

            
9
use crate::PG;
10
use crate::ParityGame;
11
use crate::Player;
12
use crate::Priority;
13
use crate::VariabilityParityGame;
14
use crate::VertexIndex;
15
use crate::make_vpg_total;
16

            
17
/// Creates a random parity game with the given number of vertices, priorities, and outdegree.
18
600
pub fn random_parity_game(
19
600
    rng: &mut impl Rng,
20
600
    make_total: bool,
21
600
    num_of_vertices: usize,
22
600
    num_of_priorities: usize,
23
600
    outdegree: usize,
24
600
) -> ParityGame {
25
600
    assert!(num_of_vertices > 0, "Parity game must have at least one vertex");
26
600
    assert!(num_of_priorities > 0, "Parity game must have at least one priority");
27

            
28
    // Randomly assign priorities to each vertex in range [0, num_of_priorities).
29
600
    let priority: Vec<Priority> = (0..num_of_vertices)
30
21000
        .map(|_| Priority::new(rng.random_range(0..num_of_priorities)))
31
600
        .collect();
32

            
33
    // Option 1: owner based on parity of priority; Option 2: random owner.
34
    // Mirror random_lts_monolithic style by using randomness.
35
600
    let owner: Vec<Player> = (0..num_of_vertices)
36
21000
        .map(|_| Player::from_index(rng.random_range(0..2)))
37
600
        .collect();
38

            
39
    // Build edges using a closure that can be iterated twice (as required by from_edges).
40
    // We generate a deterministic set by capturing a precomputed edge list.
41
600
    let mut edge_list: Vec<(VertexIndex, VertexIndex)> = Vec::with_capacity(num_of_vertices * outdegree);
42

            
43
21000
    for v in 0..num_of_vertices {
44
        // For each vertex, generate 0..outdegree outgoing edges.
45
25904
        for _ in 0..rng.random_range(0..outdegree) {
46
25904
            let to = rng.random_range(0..num_of_vertices);
47
25904
            edge_list.push((VertexIndex::new(v), VertexIndex::new(to)));
48
25904
        }
49
    }
50

            
51
    // Ensure at least the initial vertex exists.
52
600
    let initial_vertex = VertexIndex::new(0);
53

            
54
1200
    ParityGame::from_edges(initial_vertex, owner, priority, make_total, || {
55
1200
        edge_list.iter().cloned()
56
1200
    })
57
600
}
58

            
59
/// Creates a random parity game with the given number of vertices, priorities, and outdegree.
60
300
pub fn random_variability_parity_game(
61
300
    manager_ref: &BDDManagerRef,
62
300
    rng: &mut impl Rng,
63
300
    make_total: bool,
64
300
    num_of_vertices: usize,
65
300
    num_of_priorities: usize,
66
300
    outdegree: usize,
67
300
    number_of_variables: u32,
68
300
) -> Result<VariabilityParityGame, MercError> {
69
300
    let pg = random_parity_game(rng, make_total, num_of_vertices, num_of_priorities, outdegree);
70

            
71
    // Create random feature variables.
72
300
    let variables: Vec<BDDFunction> = create_variables(manager_ref, number_of_variables)?;
73

            
74
    // Overall configuration is the conjunction of all features (i.e., all features enabled).
75
300
    let configuration = random_bdd(manager_ref, rng, &variables)?;
76

            
77
    // Create random edge configurations.
78
300
    let mut edges_configuration: Vec<BDDFunction> = Vec::with_capacity(pg.num_of_edges());
79
300
    for _ in 0..pg.num_of_edges() {
80
6363
        edges_configuration.push(random_bdd(manager_ref, rng, &variables)?);
81
    }
82

            
83
300
    let result = VariabilityParityGame::new(pg, configuration, variables, edges_configuration);
84

            
85
300
    if make_total {
86
200
        make_vpg_total(manager_ref, &result)
87
    } else {
88
100
        Ok(result)
89
    }
90
300
}
91

            
92
#[cfg(test)]
93
mod tests {
94
    use merc_utilities::random_test;
95

            
96
    use crate::PG;
97
    use crate::random_parity_game;
98
    use crate::random_variability_parity_game;
99

            
100
    #[test]
101
1
    fn test_random_parity_game() {
102
100
        random_test(100, |rng| {
103
100
            let pg = random_parity_game(rng, false, 10, 5, 3);
104
100
            assert_eq!(pg.num_of_vertices(), 10);
105
100
        })
106
1
    }
107

            
108
    #[test]
109
    #[cfg_attr(miri, ignore)] // Oxidd does not work with miri
110
1
    fn test_random_variability_parity_game() {
111
100
        random_test(100, |rng| {
112
100
            let manager_ref = oxidd::bdd::new_manager(2048, 1024, 1);
113
100
            let vpg = random_variability_parity_game(&manager_ref, rng, false, 10, 5, 3, 3).unwrap();
114
100
            assert_eq!(vpg.num_of_vertices(), 10);
115
100
        })
116
1
    }
117
}