1
#![allow(nonstandard_style)]
2
//! To keep with the theory, we use capitalized variable names for sets of vertices.
3
//! Authors: Maurice Laveaux, Sjef van Loo, Erik de Vink and Tim A.C. Willemse
4
//!
5
//! Implements the standard Zielonka recursive solver for any parity game
6
//! implementing the [`crate::PG`] trait.
7

            
8
use core::fmt;
9
use std::ops::BitAnd;
10

            
11
use bitvec::bitvec;
12
use bitvec::order::Lsb0;
13
use bitvec::vec::BitVec;
14
use itertools::Itertools;
15
use log::debug;
16
use log::info;
17
use log::trace;
18
use oxidd::BooleanFunction;
19
use oxidd::bdd::BDDFunction;
20
use oxidd::util::OptBool;
21

            
22
use merc_symbolic::FormatConfig;
23
use merc_utilities::MercError;
24

            
25
use crate::PG;
26
use crate::ParityGame;
27
use crate::Player;
28
use crate::Predecessors;
29
use crate::Priority;
30
use crate::Repeat;
31
use crate::Submap;
32
use crate::VariabilityParityGame;
33
use crate::VertexIndex;
34
use crate::compute_reachable;
35
use crate::project_variability_parity_games_iter;
36

            
37
type Set = BitVec<usize, Lsb0>;
38

            
39
/// Solves the given parity game using the Zielonka algorithm.
40
853
pub fn solve_zielonka(game: &ParityGame) -> [Set; 2] {
41
853
    debug_assert!(game.is_total(), "Zielonka solver requires a total parity game");
42

            
43
853
    let mut V = bitvec![usize, Lsb0; 0; game.num_of_vertices()];
44
853
    V.set_elements(usize::MAX);
45
853
    let full_V = V.clone(); // Used for debugging.
46

            
47
853
    let mut zielonka = ZielonkaSolver::new(game);
48

            
49
853
    let (W0, W1) = zielonka.zielonka_rec(V, 0);
50

            
51
    // Check that the result is a valid partition
52
853
    debug!("Performed {} recursive calls", zielonka.recursive_calls);
53
853
    if cfg!(debug_assertions) {
54
853
        zielonka.check_partition(&W0, &W1, &full_V);
55
853
    }
56
853
    [W0, W1]
57
853
}
58

            
59
/// Solves the given variability parity game using the product-based Zielonka algorithm.
60
100
pub fn solve_variability_product_zielonka(
61
100
    vpg: &VariabilityParityGame,
62
100
) -> impl Iterator<Item = (Vec<OptBool>, BDDFunction, [Set; 2])> {
63
753
    project_variability_parity_games_iter(vpg).map(|result| {
64
753
        let (cube, bdd, pg) = result.expect("Projection should not fail");
65
753
        let (reachable_pg, projection) = compute_reachable(&pg);
66

            
67
753
        debug!("Solving projection on {}...", FormatConfig(&cube));
68

            
69
753
        let pg_solution = solve_zielonka(&reachable_pg);
70
753
        let mut new_solution = [
71
753
            bitvec![usize, Lsb0; 0; vpg.num_of_vertices()],
72
753
            bitvec![usize, Lsb0; 0; vpg.num_of_vertices()],
73
753
        ];
74
16566
        for v in pg.iter_vertices() {
75
16566
            if let Some(proj_v) = projection[*v] {
76
                // Vertex is reachable in the projection, set its solution
77
3691
                if pg_solution[0][proj_v] {
78
1851
                    new_solution[0].set(*v, true);
79
1851
                }
80
3691
                if pg_solution[1][proj_v] {
81
1840
                    new_solution[1].set(*v, true);
82
1851
                }
83
12875
            }
84
        }
85

            
86
753
        (cube, bdd, new_solution)
87
753
    })
88
100
}
89

            
90
/// Verifies that the solution obtained from the variability product-based Zielonka solver
91
/// is consistent with the solution of the variability parity game.
92
100
pub fn verify_variability_product_zielonka_solution(
93
100
    vpg: &VariabilityParityGame,
94
100
    solution: &[Submap; 2],
95
100
) -> Result<(), MercError> {
96
100
    info!("Verifying variability product-based Zielonka solution...");
97
753
    for (bits, cube, pg_solution) in solve_variability_product_zielonka(vpg) {
98
16566
        for v in vpg.iter_vertices() {
99
16566
            if pg_solution[0][*v] {
100
                // Won by Even
101
1851
                assert!(
102
1851
                    solution[0][v].and(&cube)?.satisfiable(),
103
                    "Projection {}, vertex {v} is won by even in the product, but not in the vpg",
104
                    FormatConfig(&bits)
105
                );
106
14715
            }
107

            
108
16566
            if pg_solution[1][*v] {
109
                // Won by Odd
110
1840
                assert!(
111
1840
                    solution[1][v].and(&cube)?.satisfiable(),
112
                    "Projection {}, vertex {v} is won by odd in the product, but not in the vpg",
113
                    FormatConfig(&bits)
114
                );
115
14726
            }
116
        }
117
    }
118

            
119
100
    Ok(())
120
100
}
121

            
122
struct ZielonkaSolver<'a> {
123
    game: &'a ParityGame,
124

            
125
    /// Reused temporary queue for attractor computation.
126
    temp_queue: Vec<VertexIndex>,
127

            
128
    /// Stores the predecessors of the game.
129
    predecessors: Predecessors,
130

            
131
    /// Temporary storage for vertices per priority.
132
    priority_vertices: Vec<Vec<VertexIndex>>,
133

            
134
    /// Keeps track of the total number of recursive calls.
135
    recursive_calls: usize,
136
}
137

            
138
impl ZielonkaSolver<'_> {
139
    /// Creates a new Zielonka solver for the given parity game.
140
853
    fn new<'a>(game: &'a ParityGame) -> ZielonkaSolver<'a> {
141
        // Keep track of the vertices for each priority
142
853
        let mut priority_vertices = Vec::new();
143

            
144
13691
        for v in game.iter_vertices() {
145
13691
            let prio = game.priority(v);
146

            
147
15996
            while prio >= priority_vertices.len() {
148
2305
                priority_vertices.push(Vec::new());
149
2305
            }
150

            
151
13691
            priority_vertices[prio].push(v);
152
        }
153

            
154
853
        ZielonkaSolver {
155
853
            game,
156
853
            predecessors: Predecessors::new(game),
157
853
            priority_vertices,
158
853
            temp_queue: Vec::new(),
159
853
            recursive_calls: 0,
160
853
        }
161
853
    }
162

            
163
    /// Recursively solves the parity game for the given set of vertices V.
164
5950
    fn zielonka_rec(&mut self, V: Set, depth: usize) -> (Set, Set) {
165
5950
        self.recursive_calls += 1;
166
5950
        let full_V = V.clone(); // Used for debugging
167
5950
        let indent = Repeat::new(" ", depth);
168

            
169
5950
        if !V.any() {
170
2298
            return (V.clone(), V);
171
3652
        }
172

            
173
3652
        let (highest_prio, lowest_prio) = self.get_highest_lowest_prio(&V);
174
3652
        let alpha = Player::from_priority(&highest_prio);
175
3652
        let not_alpha = alpha.opponent();
176

            
177
        // Collect the set U of vertices with the highest priority in V
178
3652
        let mut U = bitvec![usize, Lsb0; 0; self.game.num_of_vertices()];
179
41350
        for &v in self.priority_vertices[highest_prio].iter() {
180
41350
            if V[*v] {
181
22700
                U.set(*v, true);
182
22700
            }
183
        }
184

            
185
3652
        debug!(
186
            "{}|V| = {}, highest prio = {}, lowest prio = {}, player = {}, |U| = {}",
187
            indent,
188
            V.count_ones(),
189
            highest_prio,
190
            lowest_prio,
191
            alpha,
192
            U.count_ones()
193
        );
194
3652
        trace!("{}Vertices in U: {}", indent, DisplaySet(&U));
195

            
196
3652
        let A = self.attractor(alpha, &V, U);
197

            
198
3652
        trace!("{}Vertices in A: {}", indent, DisplaySet(&A));
199
3652
        debug!("{}zielonka(V \\ A) |A| = {}", indent, A.count_ones());
200
3652
        let (W1_0, W1_1) = self.zielonka_rec(V.clone().bitand(!A.clone()), depth + 1);
201

            
202
3652
        let (mut W1_alpha, W1_not_alpha) = x_and_not_x(W1_0, W1_1, alpha);
203

            
204
3652
        if !W1_not_alpha.any() {
205
2207
            W1_alpha |= A;
206
2207
            combine(W1_alpha, W1_not_alpha, alpha)
207
        } else {
208
1445
            let B = self.attractor(not_alpha, &V, W1_not_alpha);
209

            
210
1445
            trace!("{}Vertices in B: {}", indent, DisplaySet(&A));
211
1445
            debug!("{}zielonka(V \\ B)", indent);
212
1445
            let (W2_0, W2_1) = self.zielonka_rec(V.bitand(!B.clone()), depth + 1);
213

            
214
1445
            let (W2_alpha, mut W2_not_alpha) = x_and_not_x(W2_0, W2_1, alpha);
215

            
216
1445
            W2_not_alpha |= B;
217
1445
            self.check_partition(&W2_alpha, &W2_not_alpha, &full_V);
218
1445
            combine(W2_alpha, W2_not_alpha, alpha)
219
        }
220
5950
    }
221

            
222
    /// Computes the attractor for `alpha` to the set `U` within the vertices `V`.
223
5097
    fn attractor(&mut self, alpha: Player, V: &Set, mut A: Set) -> Set {
224
        // 2. Q = {v \in A}
225
5097
        self.temp_queue.clear();
226
43775
        for v in A.iter_ones() {
227
43775
            self.temp_queue.push(VertexIndex::new(v));
228
43775
        }
229

            
230
        // 4. While Q is not empty do
231
        // 5. w := Q.pop()
232
61308
        while let Some(w) = self.temp_queue.pop() {
233
            // For every u \in Ew do
234
82607
            for v in self.predecessors.predecessors(w) {
235
82607
                if V[*v] {
236
58132
                    let attracted = if self.game.owner(v) == alpha {
237
                        // v \in V and v in V_\alpha
238
17993
                        true
239
                    } else {
240
                        // Check if all successors of v are in the attractor
241
47350
                        self.game.outgoing_edges(v).all(|w_prime| !V[*w_prime] || A[*w_prime])
242
                    };
243

            
244
58132
                    if attracted && !A[*v] {
245
12436
                        A.set(*v, true);
246
12436
                        self.temp_queue.push(v);
247
45696
                    }
248
24475
                }
249
            }
250
        }
251

            
252
5097
        A
253
5097
    }
254

            
255
    /// Returns the highest and lowest priority in the given set of vertices V.
256
3652
    fn get_highest_lowest_prio(&self, V: &Set) -> (Priority, Priority) {
257
3652
        let mut highest = usize::MIN;
258
3652
        let mut lowest = usize::MAX;
259

            
260
71114
        for v in V.iter_ones() {
261
71114
            let prio = self.game.priority(VertexIndex::new(v));
262
71114
            highest = highest.max(*prio);
263
71114
            lowest = lowest.min(*prio);
264
71114
        }
265

            
266
3652
        (Priority::new(highest), Priority::new(lowest))
267
3652
    }
268

            
269
    /// Checks that the given solutions are a valid partition of the vertices in V
270
2298
    fn check_partition(&self, W0: &Set, W1: &Set, V: &Set) {
271
2298
        let intersection = W0.clone() & W1;
272
2298
        if intersection.any() {
273
            panic!(
274
                "The winning sets are not disjoint. Vertices in both sets: {}",
275
                intersection
276
            );
277
2298
        }
278

            
279
2298
        let both = W0.clone() | W1;
280
2298
        if both != *V {
281
            let missing = V.clone() & !both;
282
            panic!(
283
                "The winning sets do not cover all vertices. Missing vertices: {}",
284
                missing
285
            );
286
2298
        }
287
2298
    }
288
}
289

            
290
/// Returns the given pair ordered by player, left is alpha and right is not_alpha.
291
8065
pub fn x_and_not_x<U>(omega_0: U, omega_1: U, player: Player) -> (U, U) {
292
8065
    match player {
293
4078
        Player::Even => (omega_0, omega_1),
294
3987
        Player::Odd => (omega_1, omega_0),
295
    }
296
8065
}
297

            
298
/// Combines a pair of submaps ordered by player into a pair even, odd.
299
5737
pub fn combine<U>(omega_x: U, omega_not_x: U, player: Player) -> (U, U) {
300
5737
    match player {
301
3198
        Player::Even => (omega_x, omega_not_x),
302
2539
        Player::Odd => (omega_not_x, omega_x),
303
    }
304
5737
}
305

            
306
/// Helper struct to display a set of vertices.
307
struct DisplaySet<'a>(&'a Set);
308

            
309
impl fmt::Display for DisplaySet<'_> {
310
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
311
        write!(f, "{{{}}}", self.0.iter_ones().format(", "))
312
    }
313
}
314

            
315
#[cfg(test)]
316
mod tests {
317
    use merc_utilities::random_test;
318

            
319
    use crate::random_parity_game;
320
    use crate::solve_zielonka;
321

            
322
    #[test]
323
    #[cfg_attr(miri, ignore)] // Very slow under Miri
324
1
    fn test_random_parity_game_solve() {
325
100
        random_test(100, |rng| {
326
100
            let pg = random_parity_game(rng, true, 100, 5, 3);
327
100
            println!("{:?}", pg);
328

            
329
100
            solve_zielonka(&pg);
330
100
        })
331
1
    }
332
}