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::trace;
17

            
18
use crate::PG;
19
use crate::Player;
20
use crate::Pred;
21
use crate::Predecessors;
22
use crate::Priority;
23
use crate::Repeat;
24
use crate::Strat;
25
use crate::Strategy;
26
use crate::VertexIndex;
27

            
28
/// The type for a set of vertices.
29
pub type Set = BitVec<usize, Lsb0>;
30

            
31
/// Solves the given parity game using the Zielonka algorithm.
32
///
33
/// If `compute_strategy` is true, also computes the winning strategy for both
34
/// players. Otherwise, returns `None` strategies.
35
1846
pub fn solve_zielonka<G: PG>(game: &G, compute_strategy: bool) -> ([Set; 2], Option<[Strategy; 2]>) {
36
1846
    if compute_strategy {
37
100
        let (solution, strategy) = solve_zielonka_impl::<G, Strategy>(game);
38
100
        (solution, Some(strategy))
39
    } else {
40
1746
        let (solution, _) = solve_zielonka_impl::<G, ()>(game);
41
1746
        (solution, None)
42
    }
43
1846
}
44

            
45
/// Solves the given parity game using the Zielonka algorithm, computing a
46
/// strategy representation of type `S`.
47
1846
fn solve_zielonka_impl<G: PG, S: Strat>(game: &G) -> ([Set; 2], [S; 2]) {
48
1846
    debug_assert!(game.is_total(), "Zielonka solver requires a total parity game");
49

            
50
    // Initial set of vertices V = all vertices
51
1846
    let mut V = bitvec![usize, Lsb0; 0; game.num_of_vertices()];
52
1846
    V.set_elements(usize::MAX);
53
1846
    let full_V = V.clone(); // Used for debugging.
54

            
55
1846
    let mut zielonka = ZielonkaSolver::<_, S>::new(game);
56

            
57
1846
    let (W0, S0, W1, S1) = zielonka.zielonka_rec(V, 0);
58

            
59
    // Check that the result is a valid partition
60
1846
    debug!("Performed {} recursive calls", zielonka.recursive_calls);
61
1846
    if cfg!(debug_assertions) {
62
1846
        check_partition(&W0, &W1, &full_V);
63
1846
    }
64

            
65
1846
    ([W0, W1], [S0, S1])
66
1846
}
67

            
68
struct ZielonkaSolver<'a, G: PG, S: Strat> {
69
    game: &'a G,
70

            
71
    /// Reused temporary queue for attractor computation.
72
    temp_queue: Vec<VertexIndex>,
73

            
74
    /// Stores the predecessors of the game.
75
    predecessors: Predecessors<'a>,
76

            
77
    /// Temporary storage for vertices per priority.
78
    priority_vertices: Vec<Vec<VertexIndex>>,
79

            
80
    /// Keeps track of the total number of recursive calls.
81
    recursive_calls: usize,
82

            
83
    /// The `S` is not actually stored in the struct, but we need to keep track of the type for the recursive calls.
84
    _strategy: std::marker::PhantomData<S>,
85
}
86

            
87
impl<G: PG, S: Strat> ZielonkaSolver<'_, G, S> {
88
    /// Creates a new Zielonka solver for the given parity game.
89
1846
    fn new<'a>(game: &'a G) -> ZielonkaSolver<'a, G, S> {
90
        // Keep track of the vertices for each priority
91
1846
        let mut priority_vertices = Vec::new();
92

            
93
58205
        for v in game.iter_vertices() {
94
58205
            let prio = game.priority(v);
95

            
96
62551
            while prio >= priority_vertices.len() {
97
4346
                priority_vertices.push(Vec::new());
98
4346
            }
99

            
100
58205
            priority_vertices[prio].push(v);
101
        }
102

            
103
1846
        ZielonkaSolver {
104
1846
            game,
105
1846
            predecessors: Predecessors::new(game),
106
1846
            priority_vertices,
107
1846
            temp_queue: Vec::new(),
108
1846
            recursive_calls: 0,
109
1846
            _strategy: std::marker::PhantomData,
110
1846
        }
111
1846
    }
112

            
113
    /// Recursively solves the parity game for the given set of vertices V.
114
    ///
115
    /// # Details
116
    ///
117
    /// The strategy computation is taken from the following paper:
118
    ///
119
    /// >  Oliver Friedmann. Recursive algorithm for parity games requires exponential time. RAIRO Theor. Informatics Appl. 45(4): 449-457 (2011) [DOI](https://doi.org/10.1051/ita/2011124).
120
11423
    fn zielonka_rec(&mut self, V: Set, depth: usize) -> (Set, S, Set, S) {
121
11423
        self.recursive_calls += 1;
122
11423
        let full_V = V.clone(); // Used for debugging
123
11423
        let indent = Repeat::new(" ", depth);
124

            
125
11423
        if !V.any() {
126
4435
            return (V.clone(), S::new(), V.clone(), S::new());
127
6988
        }
128

            
129
6988
        let (highest_prio, lowest_prio) = self.get_highest_lowest_prio(&V);
130
6988
        let alpha = Player::from_priority(&highest_prio);
131
6988
        let not_alpha = alpha.opponent();
132

            
133
        // Collect the set U of vertices with the highest priority in V
134
6988
        let mut U = bitvec![usize, Lsb0; 0; self.game.num_of_vertices()];
135
113552
        for &v in self.priority_vertices[highest_prio].iter() {
136
113552
            if V[*v] {
137
65108
                U.set(*v, true);
138
65108
            }
139
        }
140

            
141
6988
        debug!(
142
            "{}|V| = {}, highest prio = {}, lowest prio = {}, player = {}, |U| = {}",
143
            indent,
144
            V.count_ones(),
145
            highest_prio,
146
            lowest_prio,
147
            alpha,
148
            U.count_ones()
149
        );
150
6988
        trace!("{}Vertices in U: {}", indent, DisplaySet(&U));
151

            
152
6988
        let U_clone = U.clone();
153

            
154
6988
        let (A, A_strategy) = self.attractor(alpha, &V, U);
155

            
156
6988
        trace!("{}Vertices in A: {}", indent, DisplaySet(&A));
157
6988
        debug!("{}zielonka(V \\ A) |A| = {}", indent, A.count_ones());
158
6988
        let (W1_0, S1_0, W1_1, S1_1) = self.zielonka_rec(V.clone().bitand(!A.clone()), depth + 1);
159

            
160
6988
        let (mut W1_alpha, mut S1_alpha, W1_not_alpha, S1_not_alpha) =
161
6988
            x_and_not_x_strategy(W1_0, S1_0, W1_1, S1_1, alpha);
162

            
163
6988
        if !W1_not_alpha.any() {
164
4399
            W1_alpha |= A;
165
            // Combine the strategy from the attractor with the recursive strategy.
166
4399
            S1_alpha = S1_alpha
167
4399
                .union(A_strategy)
168
4399
                .extend_arbitrary(self.game, &U_clone, &V, alpha);
169
4399
            combine_with_strategy(W1_alpha, S1_alpha, W1_not_alpha, S::new(), alpha)
170
        } else {
171
2589
            let (B, B_strategy) = self.attractor(not_alpha, &V, W1_not_alpha);
172

            
173
2589
            trace!("{}Vertices in B: {}", indent, DisplaySet(&B));
174
2589
            debug!("{}zielonka(V \\ B)", indent);
175
2589
            let (W2_0, S2_0, W2_1, S2_1) = self.zielonka_rec(V.bitand(!B.clone()), depth + 1);
176

            
177
2589
            let (W2_alpha, S2_alpha, mut W2_not_alpha, mut S2_not_alpha) =
178
2589
                x_and_not_x_strategy(W2_0, S2_0, W2_1, S2_1, alpha);
179

            
180
2589
            W2_not_alpha |= B;
181
            // Combine the strategy from the attractor with the recursive strategy
182
2589
            S2_not_alpha = S2_not_alpha.union(B_strategy).union(S1_not_alpha);
183

            
184
2589
            check_partition(&W2_alpha, &W2_not_alpha, &full_V);
185
2589
            combine_with_strategy(W2_alpha, S2_alpha, W2_not_alpha, S2_not_alpha, alpha)
186
        }
187
11423
    }
188

            
189
    /// Computes the attractor for `alpha` to the set `U` within the vertices `V`.
190
9577
    fn attractor(&mut self, alpha: Player, V: &Set, mut A: Set) -> (Set, S) {
191
        // 1. strategy := empty
192
9577
        let mut strategy = S::new();
193

            
194
        // 2. Q = {v \in A}
195
9577
        self.temp_queue.clear();
196
107358
        for v in A.iter_ones() {
197
107358
            self.temp_queue.push(VertexIndex::new(v));
198
107358
        }
199

            
200
        // 4. While Q is not empty do
201
        // 5. w := Q.pop()
202
168302
        while let Some(w) = self.temp_queue.pop() {
203
            // For every u \in Ew do
204
218262
            for v in self.predecessors.predecessors(w) {
205
218262
                if V[*v] {
206
173011
                    let attracted = if self.game.owner(v) == alpha {
207
                        // v \in V and v in V_\alpha
208
64984
                        true
209
                    } else {
210
                        // Check if all successors of v are in the attractor
211
135960
                        self.game.outgoing_edges(v).all(|edge| !V[*edge.to()] || A[*edge.to()])
212
                    };
213

            
214
173011
                    if attracted && !A[*v] {
215
51367
                        if self.game.owner(v) == alpha {
216
29630
                            strategy.set(v, w);
217
29630
                        }
218

            
219
51367
                        A.set(*v, true);
220
51367
                        self.temp_queue.push(v);
221
121644
                    }
222
45251
                }
223
            }
224
        }
225

            
226
9577
        (A, strategy)
227
9577
    }
228

            
229
    /// Returns the highest and lowest priority in the given set of vertices V.
230
6988
    fn get_highest_lowest_prio(&self, V: &Set) -> (Priority, Priority) {
231
6988
        let mut highest = usize::MIN;
232
6988
        let mut lowest = usize::MAX;
233

            
234
173574
        for v in V.iter_ones() {
235
173574
            let prio = self.game.priority(VertexIndex::new(v));
236
173574
            highest = highest.max(*prio);
237
173574
            lowest = lowest.min(*prio);
238
173574
        }
239

            
240
6988
        (Priority::new(highest), Priority::new(lowest))
241
6988
    }
242
}
243

            
244
/// Checks that the given solutions are a valid partition of the vertices in V
245
4635
pub fn check_partition(W0: &Set, W1: &Set, V: &Set) {
246
4635
    let intersection = W0.clone() & W1;
247
4635
    if intersection.any() {
248
        panic!(
249
            "The winning sets are not disjoint. Vertices in both sets: {}",
250
            intersection
251
        );
252
4635
    }
253

            
254
4635
    let both = W0.clone() | W1;
255
4635
    if both != *V {
256
        let missing = V.clone() & !both;
257
        panic!(
258
            "The winning sets do not cover all vertices. Missing vertices: {}",
259
            missing
260
        );
261
4635
    }
262
4635
}
263

            
264
/// Returns the given pair ordered by player, left is alpha and right is not_alpha.
265
2799
pub fn x_and_not_x<U>(omega_0: U, omega_1: U, player: Player) -> (U, U) {
266
2799
    match player {
267
1412
        Player::Even => (omega_0, omega_1),
268
1387
        Player::Odd => (omega_1, omega_0),
269
    }
270
2799
}
271

            
272
/// Combines a pair of submaps ordered by player into a pair even, odd.
273
1966
pub fn combine<U>(omega_x: U, omega_not_x: U, player: Player) -> (U, U) {
274
1966
    match player {
275
1131
        Player::Even => (omega_x, omega_not_x),
276
835
        Player::Odd => (omega_not_x, omega_x),
277
    }
278
1966
}
279

            
280
/// Returns the given pair ordered by player, left is alpha and right is not_alpha.
281
9577
pub fn x_and_not_x_strategy<U, V>(
282
9577
    omega_0: U,
283
9577
    strategy_0: V,
284
9577
    omega_1: U,
285
9577
    strategy_1: V,
286
9577
    player: Player,
287
9577
) -> (U, V, U, V) {
288
9577
    match player {
289
3709
        Player::Even => (omega_0, strategy_0, omega_1, strategy_1),
290
5868
        Player::Odd => (omega_1, strategy_1, omega_0, strategy_0),
291
    }
292
9577
}
293

            
294
/// Combines a pair of submaps ordered by player into a pair even, odd.
295
6988
pub fn combine_with_strategy<U, V>(
296
6988
    omega_x: U,
297
6988
    strategy_x: V,
298
6988
    omega_not_x: U,
299
6988
    strategy_not_x: V,
300
6988
    player: Player,
301
6988
) -> (U, V, U, V) {
302
6988
    match player {
303
3041
        Player::Even => (omega_x, strategy_x, omega_not_x, strategy_not_x),
304
3947
        Player::Odd => (omega_not_x, strategy_not_x, omega_x, strategy_x),
305
    }
306
6988
}
307

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

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

            
317
#[cfg(test)]
318
mod tests {
319
    use merc_io::DumpFiles;
320
    use merc_utilities::random_test;
321

            
322
    use crate::random_parity_game;
323
    use crate::verify_solution;
324
    use crate::write_pg;
325

            
326
    #[test]
327
    #[cfg_attr(miri, ignore)] // Miri is too slow for this test.
328
1
    fn test_random_zielonka_solver() {
329
100
        random_test(100, |rng| {
330
100
            let mut files = DumpFiles::new("test_random_zielonka_solver");
331
100
            let game = random_parity_game(rng, true, 100, 5, 3);
332

            
333
100
            files.dump("input.pg", |writer| write_pg(writer, &game)).unwrap();
334

            
335
100
            let (solution, strategy) = super::solve_zielonka(&game, true);
336

            
337
100
            verify_solution(&game, &solution, &strategy.unwrap());
338
100
        });
339
1
    }
340
}