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::Predecessors;
21
use crate::Priority;
22
use crate::Repeat;
23
use crate::Strategy;
24
use crate::VertexIndex;
25

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

            
29
/// Solves the given parity game using the Zielonka algorithm.
30
/// 
31
/// If `compute_strategy` is true, also computes the winning strategy for both
32
/// players. Otherwise, returns `None` strategies.
33
1032
pub fn solve_zielonka<G: PG>(game: &G, compute_strategy: bool) -> ([Set; 2], Option<[Strategy; 2]>) {
34
1032
    debug_assert!(game.is_total(), "Zielonka solver requires a total parity game");
35

            
36
    // Initial set of vertices V = all vertices
37
1032
    let mut V = bitvec![usize, Lsb0; 0; game.num_of_vertices()];
38
1032
    V.set_elements(usize::MAX);
39
1032
    let full_V = V.clone(); // Used for debugging.
40

            
41
1032
    let mut zielonka = ZielonkaSolver::new(game, compute_strategy);
42

            
43
1032
    let (W0, S0, W1, S1) = zielonka.zielonka_rec(V, 0);
44

            
45
    // Check that the result is a valid partition
46
1032
    debug!("Performed {} recursive calls", zielonka.recursive_calls);
47
1032
    if cfg!(debug_assertions) {
48
1032
        check_partition(&W0, &W1, &full_V);
49
1032
    }
50

            
51
1032
    if compute_strategy {
52
        // One of the strategies must be `Some`, but it could also be both.
53
100
        debug_assert!(S0.is_some() || S1.is_some(), "At least one strategy should be computed if compute_strategy is true");
54
100
        let S0 = S0.unwrap_or_default();
55
100
        let S1 = S1.unwrap_or_default();
56

            
57
100
        ([W0, W1], Some([S0, S1]))
58
    } else {
59
932
        ([W0, W1], None)
60
    }
61
1032
}
62

            
63
struct ZielonkaSolver<'a, G: PG> {
64
    game: &'a G,
65

            
66
    /// Reused temporary queue for attractor computation.
67
    temp_queue: Vec<VertexIndex>,
68

            
69
    /// Stores the predecessors of the game.
70
    predecessors: Predecessors<'a>,
71

            
72
    /// Temporary storage for vertices per priority.
73
    priority_vertices: Vec<Vec<VertexIndex>>,
74

            
75
    /// Keeps track of the total number of recursive calls.
76
    recursive_calls: usize,
77

            
78
    /// If true, also computes the winning strategy for both players. Otherwise, returns `None` strategies.
79
    compute_strategy: bool,
80
}
81

            
82
impl<G: PG> ZielonkaSolver<'_, G> {
83
    /// Creates a new Zielonka solver for the given parity game.
84
1032
    fn new<'a>(game: &'a G, compute_strategy: bool) -> ZielonkaSolver<'a, G> {
85
        // Keep track of the vertices for each priority
86
1032
        let mut priority_vertices = Vec::new();
87

            
88
37202
        for v in game.iter_vertices() {
89
37202
            let prio = game.priority(v);
90

            
91
39703
            while prio >= priority_vertices.len() {
92
2501
                priority_vertices.push(Vec::new());
93
2501
            }
94

            
95
37202
            priority_vertices[prio].push(v);
96
        }
97

            
98
1032
        ZielonkaSolver {
99
1032
            game,
100
1032
            predecessors: Predecessors::new(game),
101
1032
            priority_vertices,
102
1032
            temp_queue: Vec::new(),
103
1032
            recursive_calls: 0,
104
1032
            compute_strategy,
105
1032
        }
106
1032
    }
107

            
108
    /// Recursively solves the parity game for the given set of vertices V.
109
    /// 
110
    /// # Details
111
    /// 
112
    /// The strategy computation is taken from the following paper:
113
    /// 
114
    /// >  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).
115
6888
    fn zielonka_rec(&mut self, V: Set, depth: usize) -> (Set, Option<Strategy>, Set, Option<Strategy>) {
116
6888
        self.recursive_calls += 1;
117
6888
        let full_V = V.clone(); // Used for debugging
118
6888
        let indent = Repeat::new(" ", depth);
119

            
120
6888
        if !V.any() {
121
2647
            return (V.clone(), None, V.clone(), None);
122
4241
        }
123

            
124
4241
        let (highest_prio, lowest_prio) = self.get_highest_lowest_prio(&V);
125
4241
        let alpha = Player::from_priority(&highest_prio);
126
4241
        let not_alpha = alpha.opponent();
127

            
128
        // Collect the set U of vertices with the highest priority in V
129
4241
        let mut U = bitvec![usize, Lsb0; 0; self.game.num_of_vertices()];
130
79507
        for &v in self.priority_vertices[highest_prio].iter() {
131
79507
            if V[*v] {
132
44609
                U.set(*v, true);
133
44609
            }
134
        }
135

            
136
4241
        debug!(
137
            "{}|V| = {}, highest prio = {}, lowest prio = {}, player = {}, |U| = {}",
138
            indent,
139
            V.count_ones(),
140
            highest_prio,
141
            lowest_prio,
142
            alpha,
143
            U.count_ones()
144
        );
145
4241
        trace!("{}Vertices in U: {}", indent, DisplaySet(&U));
146

            
147
        // This copy of U is only necessary whenever a strategy is computed, see below.
148
4241
        let U_clone = if self.compute_strategy {
149
1717
            U.clone()
150
        } else {
151
2524
            Set::default()
152
        };
153

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

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

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

            
163
4241
        if !W1_not_alpha.any() {
164
2626
            W1_alpha |= A;
165
            // Combine the strategy from the attractor with the recursive strategy.
166
2626
            S1_alpha = if self.compute_strategy { self.union_strategies(S1_alpha, A_strategy).map_or_else(
167
                || {
168
                    Some(Strategy::new().extend_arbitrary(self.game, &U_clone, &V, alpha))
169
                },
170
913
                |s| Some(s.extend_arbitrary(self.game, &U_clone, &V, alpha)),
171
            ) } else {
172
                // Ignore strategy
173
1713
                None
174
            };
175
2626
            combine_with_strategy(W1_alpha, S1_alpha, W1_not_alpha, None, alpha)
176
        } else {
177
1615
            let (B, B_strategy) = self.attractor(not_alpha, &V, W1_not_alpha);
178

            
179
1615
            trace!("{}Vertices in B: {}", indent, DisplaySet(&B));
180
1615
            debug!("{}zielonka(V \\ B)", indent);
181
1615
            let (W2_0, S2_0, W2_1, S2_1) = self.zielonka_rec(V.bitand(!B.clone()), depth + 1);
182

            
183
1615
            let (W2_alpha, S2_alpha, mut W2_not_alpha, mut S2_not_alpha) =
184
1615
                x_and_not_x_strategy(W2_0, S2_0, W2_1, S2_1, alpha);
185

            
186
1615
            W2_not_alpha |= B;
187
            // Combine the strategy from the attractor with the recursive strategy
188
1615
            S2_not_alpha = self.union_strategies(self.union_strategies(S2_not_alpha, B_strategy), S1_not_alpha);
189

            
190
1615
            check_partition(&W2_alpha, &W2_not_alpha, &full_V);
191
1615
            combine_with_strategy(W2_alpha, S2_alpha, W2_not_alpha, S2_not_alpha, alpha)
192
        }
193
6888
    }
194

            
195
    /// Computes the attractor for `alpha` to the set `U` within the vertices `V`.
196
5856
    fn attractor(&mut self, alpha: Player, V: &Set, mut A: Set) -> (Set, Option<Strategy>) {
197
        // 1. strategy := empty
198
5856
        let mut strategy = if self.compute_strategy {
199
2521
            Some(Strategy::new())
200
        } else {
201
3335
            None
202
        };
203

            
204
        // 2. Q = {v \in A}
205
5856
        self.temp_queue.clear();
206
74427
        for v in A.iter_ones() {
207
74427
            self.temp_queue.push(VertexIndex::new(v));
208
74427
        }
209

            
210
        // 4. While Q is not empty do
211
        // 5. w := Q.pop()
212
112903
        while let Some(w) = self.temp_queue.pop() {
213
            // For every u \in Ew do
214
149572
            for v in self.predecessors.predecessors(w) {
215
149572
                if V[*v] {
216
114871
                    let attracted = if self.game.owner(v) == alpha {
217
                        // v \in V and v in V_\alpha
218
44819
                        true
219
                    } else {
220
                        // Check if all successors of v are in the attractor
221
87202
                        self.game.outgoing_edges(v).all(|edge| !V[*edge.to()] || A[*edge.to()])
222
                    };
223

            
224
114871
                    if attracted && !A[*v] {
225
32620
                        if let Some(s) = strategy.as_mut() && self.game.owner(v) == alpha {
226
6167
                            s.set(v, w);
227
26453
                        }
228

            
229
32620
                        A.set(*v, true);
230
32620
                        self.temp_queue.push(v);
231
82251
                    }
232
34701
                }
233
            }
234
        }
235

            
236
5856
        (A, strategy)
237
5856
    }
238

            
239
    /// Returns the highest and lowest priority in the given set of vertices V.
240
4241
    fn get_highest_lowest_prio(&self, V: &Set) -> (Priority, Priority) {
241
4241
        let mut highest = usize::MIN;
242
4241
        let mut lowest = usize::MAX;
243

            
244
121841
        for v in V.iter_ones() {
245
121841
            let prio = self.game.priority(VertexIndex::new(v));
246
121841
            highest = highest.max(*prio);
247
121841
            lowest = lowest.min(*prio);
248
121841
        }
249

            
250
4241
        (Priority::new(highest), Priority::new(lowest))
251
4241
    }
252

            
253
    /// Combines two (possibly empty) strategies by overwriting the mappings in
254
    /// the first strategy with those in the second strategy, if they exist.
255
4143
    fn union_strategies(&self, strategy1: Option<Strategy>, strategy2: Option<Strategy>) -> Option<Strategy> {
256
4143
        if let Some(s1) = strategy1 {
257
1204
            if let Some(s2) = strategy2 {
258
1204
                Some(s1.union(s2))
259
            } else {
260
                Some(s1)
261
            }
262
2939
        } else { strategy2 }
263
4143
    }
264
}
265

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

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

            
286
/// Returns the given pair ordered by player, left is alpha and right is not_alpha.
287
2473
pub fn x_and_not_x<U>(omega_0: U, omega_1: U, player: Player) -> (U, U) {
288
2473
    match player {
289
1248
        Player::Even => (omega_0, omega_1),
290
1225
        Player::Odd => (omega_1, omega_0),
291
    }
292
2473
}
293

            
294
/// Combines a pair of submaps ordered by player into a pair even, odd.
295
1734
pub fn combine<U>(omega_x: U, omega_not_x: U, player: Player) -> (U, U) {
296
1734
    match player {
297
995
        Player::Even => (omega_x, omega_not_x),
298
739
        Player::Odd => (omega_not_x, omega_x),
299
    }
300
1734
}
301

            
302
/// Returns the given pair ordered by player, left is alpha and right is not_alpha.
303
5856
pub fn x_and_not_x_strategy<U, V>(
304
5856
    omega_0: U,
305
5856
    strategy_0: V,
306
5856
    omega_1: U,
307
5856
    strategy_1: V,
308
5856
    player: Player,
309
5856
) -> (U, V, U, V) {
310
5856
    match player {
311
2472
        Player::Even => (omega_0, strategy_0, omega_1, strategy_1),
312
3384
        Player::Odd => (omega_1, strategy_1, omega_0, strategy_0),
313
    }
314
5856
}
315

            
316
/// Combines a pair of submaps ordered by player into a pair even, odd.
317
4241
pub fn combine_with_strategy<U, V>(
318
4241
    omega_x: U,
319
4241
    strategy_x: Option<V>,
320
4241
    omega_not_x: U,
321
4241
    strategy_not_x: Option<V>,
322
4241
    player: Player,
323
4241
) -> (U, Option<V>, U, Option<V>) {
324
4241
    match player {
325
1997
        Player::Even => (omega_x, strategy_x, omega_not_x, strategy_not_x),
326
2244
        Player::Odd => (omega_not_x, strategy_not_x, omega_x, strategy_x),
327
    }
328
4241
}
329

            
330
/// Helper struct to display a set of vertices.
331
struct DisplaySet<'a>(&'a Set);
332

            
333
impl fmt::Display for DisplaySet<'_> {
334
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
335
        write!(f, "{{{}}}", self.0.iter_ones().format(", "))
336
    }
337
}
338

            
339
#[cfg(test)]
340
mod tests {
341
    use merc_io::DumpFiles;
342
    use merc_utilities::random_test;
343

            
344
    use crate::random_parity_game;
345
    use crate::verify_solution;
346
    use crate::write_pg;
347

            
348
    #[test]
349
    #[cfg_attr(miri, ignore)] // Miri is too slow for this test.
350
1
    fn test_random_zielonka_solver() {
351
100
        random_test(100, |rng| {
352
100
            let mut files = DumpFiles::new("test_random_zielonka_solver");
353
100
            let game = random_parity_game(rng, true, 100, 5, 3);
354

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

            
357
100
            let (solution, strategy) = super::solve_zielonka(&game, true);
358

            
359
100
            verify_solution(&game, &solution, &strategy.unwrap());
360
100
        });
361
1
    }
362
}