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
1101
pub fn solve_zielonka(game: &impl PG) -> ([Set; 2], [Strategy; 2]) {
31
1101
    debug_assert!(game.is_total(), "Zielonka solver requires a total parity game");
32

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

            
38
1101
    let mut zielonka = ZielonkaSolver::new(game);
39

            
40
1101
    let (W0, S0, W1, S1) = zielonka.zielonka_rec(V, 0);
41

            
42
    // Check that the result is a valid partition
43
1101
    debug!("Performed {} recursive calls", zielonka.recursive_calls);
44
1101
    if cfg!(debug_assertions) {
45
1101
        check_partition(&W0, &W1, &full_V);
46
1101
    }
47
1101
    ([W0, W1], [S0, S1])
48
1101
}
49

            
50
struct ZielonkaSolver<'a, G: PG> {
51
    game: &'a G,
52

            
53
    /// Reused temporary queue for attractor computation.
54
    temp_queue: Vec<VertexIndex>,
55

            
56
    /// Stores the predecessors of the game.
57
    predecessors: Predecessors<'a>,
58

            
59
    /// Temporary storage for vertices per priority.
60
    priority_vertices: Vec<Vec<VertexIndex>>,
61

            
62
    /// Keeps track of the total number of recursive calls.
63
    recursive_calls: usize,
64
}
65

            
66
impl<G: PG> ZielonkaSolver<'_, G> {
67
    /// Creates a new Zielonka solver for the given parity game.
68
1101
    fn new<'a>(game: &'a G) -> ZielonkaSolver<'a, G> {
69
        // Keep track of the vertices for each priority
70
1101
        let mut priority_vertices = Vec::new();
71

            
72
3379
        for v in game.iter_vertices() {
73
3379
            let prio = game.priority(v);
74

            
75
5445
            while prio >= priority_vertices.len() {
76
2066
                priority_vertices.push(Vec::new());
77
2066
            }
78

            
79
3379
            priority_vertices[prio].push(v);
80
        }
81

            
82
1101
        ZielonkaSolver {
83
1101
            game,
84
1101
            predecessors: Predecessors::new(game),
85
1101
            priority_vertices,
86
1101
            temp_queue: Vec::new(),
87
1101
            recursive_calls: 0,
88
1101
        }
89
1101
    }
90

            
91
    /// Recursively solves the parity game for the given set of vertices V.
92
3753
    fn zielonka_rec(&mut self, V: Set, depth: usize) -> (Set, Strategy, Set, Strategy) {
93
3753
        self.recursive_calls += 1;
94
3753
        let full_V = V.clone(); // Used for debugging
95
3753
        let indent = Repeat::new(" ", depth);
96

            
97
3753
        if !V.any() {
98
1643
            return (V.clone(), Strategy::new(), V.clone(), Strategy::new());
99
2110
        }
100

            
101
2110
        let (highest_prio, lowest_prio) = self.get_highest_lowest_prio(&V);
102
2110
        let alpha = Player::from_priority(&highest_prio);
103
2110
        let not_alpha = alpha.opponent();
104

            
105
        // Collect the set U of vertices with the highest priority in V
106
2110
        let mut U = bitvec![usize, Lsb0; 0; self.game.num_of_vertices()];
107
4497
        for &v in self.priority_vertices[highest_prio].iter() {
108
4497
            if V[*v] {
109
3612
                U.set(*v, true);
110
3612
            }
111
        }
112

            
113
2110
        debug!(
114
            "{}|V| = {}, highest prio = {}, lowest prio = {}, player = {}, |U| = {}",
115
            indent,
116
            V.count_ones(),
117
            highest_prio,
118
            lowest_prio,
119
            alpha,
120
            U.count_ones()
121
        );
122
2110
        trace!("{}Vertices in U: {}", indent, DisplaySet(&U));
123

            
124
2110
        let (A, A_strategy) = self.attractor(alpha, &V, U);
125

            
126
2110
        trace!("{}Vertices in A: {}", indent, DisplaySet(&A));
127
2110
        debug!("{}zielonka(V \\ A) |A| = {}", indent, A.count_ones());
128
2110
        let (W1_0, S1_0, W1_1, S1_1) = self.zielonka_rec(V.clone().bitand(!A.clone()), depth + 1);
129

            
130
2110
        let (mut W1_alpha, mut S1_alpha, W1_not_alpha, _S1_not_alpha) =
131
2110
            x_and_not_x_strategy(W1_0, S1_0, W1_1, S1_1, alpha);
132

            
133
2110
        if !W1_not_alpha.any() {
134
1568
            W1_alpha |= A;
135
            // Combine the strategy from the attractor with the recursive strategy
136
1568
            S1_alpha = S1_alpha.combine(A_strategy);
137
1568
            combine_strategy(W1_alpha, S1_alpha, W1_not_alpha, Strategy::new(), alpha)
138
        } else {
139
542
            let (B, B_strategy) = self.attractor(not_alpha, &V, W1_not_alpha);
140

            
141
542
            trace!("{}Vertices in B: {}", indent, DisplaySet(&A));
142
542
            debug!("{}zielonka(V \\ B)", indent);
143
542
            let (W2_0, S2_0, W2_1, S2_1) = self.zielonka_rec(V.bitand(!B.clone()), depth + 1);
144

            
145
542
            let (W2_alpha, S2_alpha, mut W2_not_alpha, mut S2_not_alpha) =
146
542
                x_and_not_x_strategy(W2_0, S2_0, W2_1, S2_1, alpha);
147

            
148
542
            W2_not_alpha |= B;
149
            // Combine the strategy from the attractor with the recursive strategy
150
542
            S2_not_alpha = S2_not_alpha.combine(B_strategy);
151
542
            check_partition(&W2_alpha, &W2_not_alpha, &full_V);
152
542
            combine_strategy(W2_alpha, S2_alpha, W2_not_alpha, S2_not_alpha, alpha)
153
        }
154
3753
    }
155

            
156
    /// Computes the attractor for `alpha` to the set `U` within the vertices `V`.
157
2652
    fn attractor(&mut self, alpha: Player, V: &Set, mut A: Set) -> (Set, Strategy) {
158
        // 1. strategy := empty
159
2652
        let mut strategy = Strategy::new();
160

            
161
        // 2. Q = {v \in A}
162
2652
        self.temp_queue.clear();
163
4806
        for v in A.iter_ones() {
164
4806
            self.temp_queue.push(VertexIndex::new(v));
165
4806
        }
166

            
167
        // 4. While Q is not empty do
168
        // 5. w := Q.pop()
169
8905
        while let Some(w) = self.temp_queue.pop() {
170
            // For every u \in Ew do
171
8733
            for v in self.predecessors.predecessors(w) {
172
8733
                if V[*v] {
173
7330
                    let attracted = if self.game.owner(v) == alpha {
174
                        // v \in V and v in V_\alpha
175
2792
                        true
176
                    } else {
177
                        // Check if all successors of v are in the attractor
178
6087
                        self.game.outgoing_edges(v).all(|w_prime| !V[*w_prime] || A[*w_prime])
179
                    };
180

            
181
7330
                    if attracted && !A[*v] {
182
1447
                        if self.game.owner(v) == alpha {
183
838
                            strategy.set(v, w);
184
846
                        }
185

            
186
1447
                        A.set(*v, true);
187
1447
                        self.temp_queue.push(v);
188
5883
                    }
189
1403
                }
190
            }
191
        }
192

            
193
2652
        (A, strategy)
194
2652
    }
195

            
196
    /// Returns the highest and lowest priority in the given set of vertices V.
197
2110
    fn get_highest_lowest_prio(&self, V: &Set) -> (Priority, Priority) {
198
2110
        let mut highest = usize::MIN;
199
2110
        let mut lowest = usize::MAX;
200

            
201
6250
        for v in V.iter_ones() {
202
6250
            let prio = self.game.priority(VertexIndex::new(v));
203
6250
            highest = highest.max(*prio);
204
6250
            lowest = lowest.min(*prio);
205
6250
        }
206

            
207
2110
        (Priority::new(highest), Priority::new(lowest))
208
2110
    }
209
}
210

            
211
/// Checks that the given solutions are a valid partition of the vertices in V
212
1643
pub fn check_partition(W0: &Set, W1: &Set, V: &Set) {
213
1643
    let intersection = W0.clone() & W1;
214
1643
    if intersection.any() {
215
        panic!(
216
            "The winning sets are not disjoint. Vertices in both sets: {}",
217
            intersection
218
        );
219
1643
    }
220

            
221
1643
    let both = W0.clone() | W1;
222
1643
    if both != *V {
223
        let missing = V.clone() & !both;
224
        panic!(
225
            "The winning sets do not cover all vertices. Missing vertices: {}",
226
            missing
227
        );
228
1643
    }
229
1643
}
230

            
231
/// Returns the given pair ordered by player, left is alpha and right is not_alpha.
232
2866
pub fn x_and_not_x<U>(omega_0: U, omega_1: U, player: Player) -> (U, U) {
233
2866
    match player {
234
1491
        Player::Even => (omega_0, omega_1),
235
1375
        Player::Odd => (omega_1, omega_0),
236
    }
237
2866
}
238

            
239
/// Combines a pair of submaps ordered by player into a pair even, odd.
240
2028
pub fn combine<U>(omega_x: U, omega_not_x: U, player: Player) -> (U, U) {
241
2028
    match player {
242
1187
        Player::Even => (omega_x, omega_not_x),
243
841
        Player::Odd => (omega_not_x, omega_x),
244
    }
245
2028
}
246

            
247
/// Returns the given pair ordered by player, left is alpha and right is not_alpha.
248
2652
pub fn x_and_not_x_strategy<U, V>(
249
2652
    omega_0: U,
250
2652
    strategy_0: V,
251
2652
    omega_1: U,
252
2652
    strategy_1: V,
253
2652
    player: Player,
254
2652
) -> (U, V, U, V) {
255
2652
    match player {
256
1585
        Player::Even => (omega_0, strategy_0, omega_1, strategy_1),
257
1067
        Player::Odd => (omega_1, strategy_1, omega_0, strategy_0),
258
    }
259
2652
}
260

            
261
/// Combines a pair of submaps ordered by player into a pair even, odd.
262
2110
pub fn combine_strategy<U, V>(
263
2110
    omega_x: U,
264
2110
    strategy_x: V,
265
2110
    omega_not_x: U,
266
2110
    strategy_not_x: V,
267
2110
    player: Player,
268
2110
) -> (U, V, U, V) {
269
2110
    match player {
270
1307
        Player::Even => (omega_x, strategy_x, omega_not_x, strategy_not_x),
271
803
        Player::Odd => (omega_not_x, strategy_not_x, omega_x, strategy_x),
272
    }
273
2110
}
274

            
275
/// Helper struct to display a set of vertices.
276
struct DisplaySet<'a>(&'a Set);
277

            
278
impl fmt::Display for DisplaySet<'_> {
279
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280
        write!(f, "{{{}}}", self.0.iter_ones().format(", "))
281
    }
282
}