1
use oxidd::BooleanFunction;
2
use oxidd::Manager;
3
use oxidd::ManagerRef;
4
use oxidd::bdd::BDDFunction;
5
use oxidd::bdd::BDDManagerRef;
6
use rand::Rng;
7

            
8
use merc_symbolic::random_bdd;
9
use merc_utilities::MercError;
10
use rand::RngExt;
11

            
12
use crate::PG;
13
use crate::ParityGame;
14
use crate::ParityGameBuilder;
15
use crate::Player;
16
use crate::Priority;
17
use crate::VariabilityParityGame;
18
use crate::VertexIndex;
19
use crate::make_vpg_total;
20

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

            
32
1200
    let mut builder = ParityGameBuilder::new(VertexIndex::new(0));
33

            
34
48000
    for v in 0..num_of_vertices {
35
48000
        let priority = Priority::new(rng.random_range(0..num_of_priorities));
36
48000
        let owner = Player::from_index(rng.random_range(0..2));
37
48000
        builder.add_vertex(VertexIndex::new(v), owner, priority);
38
48000
    }
39
    
40
    // For each vertex, generate 0..outdegree outgoing edges.
41
48000
    for v in 0..num_of_vertices {
42
67570
        for _ in 0..rng.random_range(0..outdegree) {
43
67570
            let to = rng.random_range(0..num_of_vertices);
44
67570
            builder.add_edge(VertexIndex::new(v), VertexIndex::new(to));
45
67570
        }
46
    }
47

            
48
1200
    builder.finish(make_total, true)
49
1200
}
50

            
51
/// Creates a random parity game with the given number of vertices, priorities, and outdegree.
52
400
pub fn random_variability_parity_game<R: Rng>(
53
400
    manager_ref: &BDDManagerRef,
54
400
    rng: &mut R,
55
400
    make_total: bool,
56
400
    num_of_vertices: usize,
57
400
    num_of_priorities: usize,
58
400
    outdegree: usize,
59
400
    number_of_variables: u32,
60
400
) -> Result<VariabilityParityGame, MercError> {
61
400
    let pg = random_parity_game(rng, make_total, num_of_vertices, num_of_priorities, outdegree);
62

            
63
    // Check for existing variables.
64
400
    if manager_ref.with_manager_shared(|manager| manager.num_vars()) != 0 {
65
        return Err("BDD manager must not contain any variables yet".into());
66
400
    }
67

            
68
    // Create random feature variables.
69
400
    let variables = manager_ref.with_manager_exclusive(|manager| -> Result<Vec<BDDFunction>, MercError> {
70
400
        Ok(manager
71
400
            .add_vars(number_of_variables)
72
1200
            .map(|i| BDDFunction::var(manager, i))
73
400
            .collect::<Result<Vec<_>, _>>()?)
74
400
    })?;
75

            
76
    // Overall configuration is the conjunction of all features (i.e., all features enabled).
77
400
    let configuration = random_bdd(manager_ref, rng, &variables, 10)?;
78

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

            
85
400
    let result = VariabilityParityGame::new(pg, configuration, variables, edges_configuration);
86

            
87
400
    if make_total {
88
300
        make_vpg_total(manager_ref, &result)
89
    } else {
90
100
        Ok(result)
91
    }
92
400
}
93

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

            
98
    use crate::PG;
99
    use crate::random_parity_game;
100
    use crate::random_variability_parity_game;
101

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

            
110
    #[test]
111
1
    fn test_random_total_parity_game() {
112
100
        random_test(100, |rng| {
113
100
            let pg = random_parity_game(rng, true, 10, 5, 3);
114
100
            assert_eq!(pg.num_of_vertices(), 10);
115
100
            assert!(pg.is_total(), "Generated parity game should be total");
116
100
        })
117
1
    }
118

            
119
    #[test]
120
    #[cfg_attr(miri, ignore)] // Oxidd does not work with miri
121
1
    fn test_random_variability_parity_game() {
122
100
        random_test(100, |rng| {
123
100
            let manager_ref = oxidd::bdd::new_manager(2048, 1024, 1);
124
100
            let vpg = random_variability_parity_game(&manager_ref, rng, false, 10, 5, 3, 3).unwrap();
125
100
            assert_eq!(vpg.num_of_vertices(), 10);
126
100
        })
127
1
    }
128

            
129
    #[test]
130
    #[cfg_attr(miri, ignore)] // Oxidd does not work with miri
131
1
    fn test_random_total_variability_parity_game() {
132
100
        random_test(100, |rng| {
133
100
            let manager_ref = oxidd::bdd::new_manager(2048, 1024, 1);
134
100
            let vpg = random_variability_parity_game(&manager_ref, rng, true, 10, 5, 3, 3).unwrap();
135
100
            assert!(
136
100
                vpg.num_of_vertices() >= 10 && vpg.num_of_vertices() <= 12,
137
                "At least 10 vertices, with 2 additional vertices for totality"
138
            );
139
100
            assert!(
140
100
                vpg.is_total(&manager_ref).unwrap(),
141
                "Generated variability parity game should be total"
142
            );
143
100
        })
144
1
    }
145
}