1
//! To keep with the theory, we use capitalized variable names for sets of vertices.
2
//! Authors: Maurice Laveaux, Sjef van Loo, Erik de Vink and Tim A.C. Willemse
3
#![allow(nonstandard_style)]
4
#![allow(unused)]
5

            
6
use std::fmt;
7
use std::ops::Index;
8

            
9
use bitvec::order::Lsb0;
10
use bitvec::vec::BitVec;
11
use clap::ValueEnum;
12
use log::debug;
13
use log::trace;
14
use oxidd::BooleanFunction;
15
use oxidd::ManagerRef;
16
use oxidd::bdd::BDDFunction;
17
use oxidd::bdd::BDDManagerRef;
18
use oxidd::util::AllocResult;
19

            
20
use merc_symbolic::FormatConfigSet;
21
use merc_utilities::MercError;
22

            
23
use crate::PG;
24
use crate::Player;
25
use crate::Priority;
26
use crate::VariabilityParityGame;
27
use crate::VariabilityPredecessors;
28
use crate::VertexIndex;
29
use crate::combine;
30
use crate::x_and_not_x;
31

            
32
/// Utility to print a repeated static string a given number of times.
33
pub struct Repeat {
34
    s: &'static str,
35
    times: usize,
36
}
37

            
38
impl Repeat {
39
    /// Creates a new Repeat instance.
40
9218
    pub fn new(s: &'static str, times: usize) -> Self {
41
9218
        Self { s, times }
42
9218
    }
43
}
44

            
45
impl fmt::Display for Repeat {
46
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47
        for _ in 0..self.times {
48
            f.write_str(self.s)?;
49
        }
50
        Ok(())
51
    }
52
}
53

            
54
/// Variant of the Zielonka algorithm to use.
55
#[derive(ValueEnum, Debug, Clone, Copy, PartialEq, Eq)]
56
pub enum ZielonkaVariant {
57
    /// Product-based Zielonka variant.
58
    Product,
59
    /// Standard family-based Zielonka algorithm.
60
    Family,
61
    /// Left-optimised family-based Zielonka variant.
62
    FamilyOptimisedLeft,
63
}
64

            
65
/// Solves the given variability parity game using the specified Zielonka algorithm variant.
66
300
pub fn solve_variability_zielonka(
67
300
    manager_ref: &BDDManagerRef,
68
300
    game: &VariabilityParityGame,
69
300
    variant: ZielonkaVariant,
70
300
    alternative_solving: bool,
71
300
) -> Result<[Submap; 2], MercError> {
72
300
    debug_assert!(
73
300
        game.is_total(manager_ref)?,
74
        "Zielonka solver requires a total parity game"
75
    );
76

            
77
300
    let mut zielonka = VariabilityZielonkaSolver::new(manager_ref, game, alternative_solving);
78

            
79
    // Determine the initial set of vertices V
80
300
    let V = Submap::new(
81
300
        manager_ref.with_manager_shared(|manager| {
82
300
            if alternative_solving {
83
                BDDFunction::t(manager)
84
            } else {
85
300
                game.configuration().clone()
86
            }
87
300
        }),
88
300
        manager_ref.with_manager_shared(|manager| BDDFunction::f(manager)),
89
300
        game.num_of_vertices(),
90
    );
91

            
92
300
    let full_V = V.clone();
93
300
    let (W0, W1) = match variant {
94
200
        ZielonkaVariant::Family => zielonka.solve_recursive(V, 0)?,
95
100
        ZielonkaVariant::FamilyOptimisedLeft => zielonka.zielonka_family_optimised(V, 0)?,
96
        ZielonkaVariant::Product => {
97
            panic!("Product-based Zielonka is implemented in solve_product_zielonka");
98
        }
99
    };
100

            
101
300
    debug!("Performed {} recursive calls", zielonka.recursive_calls);
102
300
    if cfg!(debug_assertions) {
103
300
        zielonka.check_partition(&W0, &W1, &full_V)?;
104
    }
105

            
106
300
    let (W0, W1) = if alternative_solving {
107
        // Intersect the results with the game's configuration
108
        let config = game.configuration();
109
        (W0.and_function(config)?, W1.and_function(config)?)
110
    } else {
111
300
        (W0, W1)
112
    };
113

            
114
300
    Ok([W0, W1])
115
300
}
116

            
117
struct VariabilityZielonkaSolver<'a> {
118
    game: &'a VariabilityParityGame,
119

            
120
    manager_ref: &'a BDDManagerRef,
121

            
122
    /// Instead of solving the game only for the valid configurations, solve for
123
    /// all configurations and then restrict the result at the end.
124
    alternative_solving: bool,
125

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

            
129
    /// Keep track of the vertices in the temp_queue above in the attractor computation.
130
    temp_vertices: BitVec<usize, Lsb0>,
131

            
132
    /// Stores the predecessors of the game.
133
    predecessors: VariabilityPredecessors,
134

            
135
    /// Temporary storage for vertices per priority.
136
    priority_vertices: Vec<Vec<VertexIndex>>,
137

            
138
    /// The BDD function representing the empty configuration.
139
    false_bdd: BDDFunction,
140

            
141
    /// Keeps track of the total number of recursive calls.
142
    recursive_calls: usize,
143
}
144

            
145
impl<'a> VariabilityZielonkaSolver<'a> {
146
    /// Creates a new VariabilityZielonkaSolver for the given game.
147
300
    pub fn new(manager_ref: &'a BDDManagerRef, game: &'a VariabilityParityGame, alternative_solving: bool) -> Self {
148
        // Keep track of the vertices for each priority
149
300
        let mut priority_vertices = Vec::new();
150

            
151
6600
        for v in game.iter_vertices() {
152
6600
            let prio = game.priority(v);
153

            
154
7500
            while prio >= priority_vertices.len() {
155
900
                priority_vertices.push(Vec::new());
156
900
            }
157

            
158
6600
            priority_vertices[prio].push(v);
159
        }
160

            
161
300
        let false_bdd = manager_ref.with_manager_shared(|manager| BDDFunction::f(manager));
162

            
163
300
        Self {
164
300
            game,
165
300
            manager_ref,
166
300
            temp_queue: Vec::new(),
167
300
            temp_vertices: BitVec::repeat(false, game.num_of_vertices()),
168
300
            predecessors: VariabilityPredecessors::new(manager_ref, game),
169
300
            priority_vertices,
170
300
            recursive_calls: 0,
171
300
            alternative_solving,
172
300
            false_bdd,
173
300
        }
174
300
    }
175

            
176
    /// Solves the variability parity game for the given set of vertices V.
177
2174
    fn solve_recursive(&mut self, gamma: Submap, depth: usize) -> Result<(Submap, Submap), MercError> {
178
2174
        self.recursive_calls += 1;
179

            
180
        // For debugging mostly
181
2174
        let indent = Repeat::new(" ", depth);
182
2174
        let gamma_copy = gamma.clone();
183

            
184
        // 1. if \gamma == \epsilon then
185
2174
        if gamma.is_empty() {
186
788
            return Ok((gamma.clone(), gamma));
187
1386
        }
188

            
189
        // 5. m := max { p(v) | v in V && \gamma(v) \neq \emptyset }
190
1386
        let (highest_prio, lowest_prio) = self.get_highest_lowest_prio(&gamma);
191

            
192
        // 6. x := m mod 2
193
1386
        let x = Player::from_priority(&highest_prio);
194
1386
        let not_x = x.opponent();
195

            
196
        // 7. \mu := lambda v in V. bigcup { \gamma(v) | p(v) = m }
197
1386
        let mut mu = Submap::new(
198
1386
            self.manager_ref.with_manager_shared(|manager| BDDFunction::f(manager)),
199
1386
            self.false_bdd.clone(),
200
1386
            self.game.num_of_vertices(),
201
        );
202

            
203
10588
        for v in &self.priority_vertices[*highest_prio] {
204
10588
            mu.set(*v, gamma[*v].clone());
205
10588
        }
206

            
207
1386
        debug!(
208
            "|gamma| = {}, m = {}, l = {}, x = {}, |mu| = {}",
209
            gamma.number_of_non_empty(),
210
            highest_prio,
211
            lowest_prio,
212
            x,
213
            mu.number_of_non_empty()
214
        );
215

            
216
1386
        trace!("{indent}Vertices in gamma: {:?}", gamma);
217
1386
        trace!("{indent}Vertices in mu: {:?}", mu);
218
1386
        let alpha = self.attractor(x, &gamma, mu)?;
219
1386
        trace!("{indent}Vertices in alpha: {:?}", alpha);
220

            
221
        // 9. (omega'_0, omega'_1) := solve(\gamma \ \alpha)
222
1386
        debug!(
223
            "{indent}zielonka_family(gamma \\ alpha), |alpha| = {}",
224
            alpha.number_of_non_empty()
225
        );
226
1386
        let (omega1_0, omega1_1) = self.solve_recursive(gamma.clone().minus(&alpha.clone())?, depth + 1)?;
227

            
228
1386
        let (mut omega1_x, mut omega1_not_x) = x_and_not_x(omega1_0, omega1_1, x);
229
1386
        if omega1_not_x.is_empty() {
230
            // 11. omega_x := omega'_x \cup alpha
231
798
            omega1_x = omega1_x.or(&alpha)?;
232
            // 20. return (omega_0, omega_1)
233
798
            Ok(combine(omega1_x, omega1_not_x, x))
234
        } else {
235
            // 14. \beta := attr_notalpha(\omega'_notx)
236
588
            let beta = self.attractor(not_x, &gamma, omega1_not_x)?;
237
            // 15. (omega''_0, omega''_1) := solve(gamma \ beta)
238
588
            debug!(
239
                "{indent}solve_rec(gamma \\ beta), |beta| = {}",
240
                beta.number_of_non_empty()
241
            );
242
588
            trace!("{indent}Vertices in beta: {:?}", beta);
243

            
244
588
            let (mut omega2_0, mut omega2_1) = self.solve_recursive(gamma.minus(&beta)?, depth + 1)?;
245

            
246
            // 17. omega''_notx := omega''_notx \cup \beta
247
588
            let (omega2_x, mut omega2_not_x) = x_and_not_x(omega2_0, omega2_1, x);
248
588
            omega2_not_x = omega2_not_x.or(&beta)?;
249

            
250
            // 20. return (omega_0, omega_1)
251
588
            self.check_partition(&omega2_x, &omega2_not_x, &gamma_copy)?;
252
588
            Ok(combine(omega2_x, omega2_not_x, x))
253
        }
254
2174
    }
255

            
256
    /// Left-optimised Zielonka solver that has improved theoretical complexity, but might be slower in practice.
257
1094
    fn zielonka_family_optimised(&mut self, gamma: Submap, depth: usize) -> Result<(Submap, Submap), MercError> {
258
1094
        self.recursive_calls += 1;
259
1094
        let indent = Repeat::new(" ", depth);
260
1094
        let gamma_copy = gamma.clone();
261

            
262
        // 1. if \gamma == \epsilon then
263
1094
        if gamma.is_empty() {
264
            // 2. return (\epsilon, \epsilon)
265
395
            return Ok((gamma.clone(), gamma));
266
699
        }
267

            
268
        // 5. m := max { p(v) | v in V && \gamma(v) \neq \emptyset }
269
699
        let (highest_prio, lowest_prio) = self.get_highest_lowest_prio(&gamma);
270

            
271
        // 6. x := m mod 2
272
699
        let x = Player::from_priority(&highest_prio);
273
699
        let not_x = x.opponent();
274

            
275
        // 7. C := { c in \bigC | exists v in V : p(v) = m && c in \gamma(v) }
276
        // 8. \mu := lambda v in V. bigcup { \gamma(v) | p(v) = m }
277
699
        let mut mu = Submap::new(
278
699
            self.false_bdd.clone(),
279
699
            self.false_bdd.clone(),
280
699
            self.game.num_of_vertices(),
281
        );
282

            
283
699
        let mut C = self.false_bdd.clone();
284
5321
        for v in &self.priority_vertices[*highest_prio] {
285
5321
            mu.set(*v, gamma[*v].clone());
286
5321
            C = C.or(&gamma[*v])?;
287
        }
288

            
289
699
        debug!(
290
            "{indent}|gamma| = {}, m = {}, l = {}, x = {}, |mu| = {}",
291
            gamma.number_of_non_empty(),
292
            highest_prio,
293
            lowest_prio,
294
            x,
295
            mu.number_of_non_empty()
296
        );
297

            
298
        // 9. alpha := attr_x(\mu).
299
699
        trace!("{indent}gamma: {:?}", gamma);
300
699
        trace!("{indent}C: {}", FormatConfigSet(&C));
301
699
        let alpha = self.attractor(x, &gamma, mu)?;
302
699
        trace!("{indent}alpha: {:?}", alpha);
303

            
304
        // 10. (omega'_0, omega'_1) := solve(gamma \ alpha)
305
699
        debug!(
306
            "{indent}zielonka_family_opt(gamma \\ alpha) |alpha| = {}",
307
            alpha.number_of_non_empty()
308
        );
309
699
        let (omega1_0, omega1_1) = self.zielonka_family_optimised(gamma.clone().minus(&alpha)?, depth + 1)?;
310

            
311
        // omega_prime[not_x] restricted to (gamma \ C)
312
699
        let C_restricted = minus(
313
699
            &if !self.alternative_solving {
314
699
                self.manager_ref.with_manager_shared(|m| BDDFunction::t(m)).clone()
315
            } else {
316
                self.game.configuration().clone()
317
            },
318
699
            &C,
319
        )?;
320

            
321
699
        let (mut omega1_x, omega1_not_x) = x_and_not_x(omega1_0, omega1_1, x);
322
699
        let omega1_not_x_restricted = omega1_not_x.clone().minus_function(&C_restricted)?;
323

            
324
        // 10.
325
699
        if omega1_not_x_restricted.is_empty() {
326
            // 11. omega'_x := omega'_x \cup A
327
404
            omega1_x = omega1_x.or(&alpha)?;
328
404
            self.check_partition(&omega1_x, &omega1_not_x, &gamma_copy)?;
329

            
330
            // 22. return (omega_0, omega_1)
331
404
            Ok(combine(omega1_x, omega1_not_x, x))
332
        } else {
333
            // C' := { c in C | exists v: c in omega'_not_x(v) }
334
295
            let mut C1 = self.false_bdd.clone();
335
6490
            for (_v, func) in omega1_not_x.iter() {
336
6490
                C1 = C1.or(func)?;
337
            }
338
295
            C1 = C1.and(&C)?;
339

            
340
            // beta := attr_not_x(omega'_not_x | C')
341
295
            let C1_restricted = minus(
342
295
                &if self.alternative_solving {
343
                    self.manager_ref.with_manager_shared(|m| BDDFunction::t(m)).clone()
344
                } else {
345
295
                    self.game.configuration().clone()
346
                },
347
295
                &C1,
348
            )?;
349

            
350
295
            let omega1_not_x_restricted1 = omega1_not_x.clone().minus_function(&C1_restricted)?;
351
295
            trace!("{indent}omega'_notx_restricted: {:?}", omega1_not_x_restricted1);
352
295
            let alpha1 = self.attractor(not_x, &gamma, omega1_not_x_restricted1)?;
353
295
            trace!("{indent}alpha': {:?}", alpha1);
354

            
355
            // Solve on (gamma | C') \ alpha'
356
295
            let gamma_restricted = gamma.minus_function(&C1_restricted)?;
357

            
358
295
            debug!("{indent}zielonka_family_opt((gamma | C') \\ alpha')");
359
295
            let (omega2_0, omega2_1) = self.zielonka_family_optimised(gamma_restricted.minus(&alpha1)?, depth + 1)?;
360

            
361
            // 18. omega'_x := omega'_x\C' cup alpha\C' cup omega''_x
362
            // 19. omega_not_x := omega'_not_x\C' cup omega''_x cup beta
363
295
            let (omega2_x, omega2_not_x) = x_and_not_x(omega2_0, omega2_1, x);
364
295
            let omega1_x_restricted = omega1_x.minus_function(&C1)?;
365
295
            let omega1_not_x_restricted = omega1_not_x.minus_function(&C1)?;
366

            
367
295
            let alpha_restricted = alpha.minus_function(&C1)?;
368
295
            let omega2_x_result = omega2_x.or(&omega1_x_restricted.or(&alpha_restricted)?)?;
369
295
            let omega2_not_x_result = omega2_not_x.or(&omega1_not_x_restricted)?.or(&alpha1)?;
370

            
371
295
            debug!("{indent}return (omega''_0, omega''_1)");
372
295
            Ok(combine(omega2_x_result, omega2_not_x_result, x))
373
        }
374
1094
    }
375

            
376
    /// Computes the attractor for `player` to the set `A` within the set of vertices `gamma`.
377
    ///
378
    /// # Details
379
    ///
380
    /// The definition of the attractor is as follows:
381
    ///     Attrx,γ (β) = intersection { α ⊆ γ | ∀v ∈ V, c ∈ C: (c ∈ β(v) ⇒ c ∈ α(v)) ∧
382
    ///          (v ∈ Vx ∧ (∃w ∈ V : v c −→ γ w ∧ c ∈ α(w)) ⇒ c ∈ α(v)) ∧
383
    ///          (v ∈ V¯x ∧ (∀w ∈ V : v c −→ γ w ⇒ c ∈ α(w)) ⇒ c ∈ α(v)) }
384
    ///
385
    /// The relation to the implementation is not entirely straightforward. The player `x` is called alpha here, and A is the beta set.
386
2968
    fn attractor(&mut self, alpha: Player, gamma: &Submap, mut A: Submap) -> Result<Submap, MercError> {
387
        // 2. Queue Q := {v \in V | A(v) != \emptyset }
388
2968
        self.temp_vertices.fill(false);
389
14838
        for v in A.iter_vertices() {
390
14838
            self.temp_queue.push(v);
391
14838

            
392
14838
            // temp_vertices keeps track of which vertices are in the queue.
393
14838
            self.temp_vertices.set(*v, true);
394
14838
        }
395

            
396
        // 4. While Q not empty do
397
        // 5. w := Q.pop()
398
23831
        while let Some(w) = self.temp_queue.pop() {
399
20863
            self.temp_vertices.set(*w, false);
400

            
401
            // For every v \in Ew do
402
31260
            for (v, edge_guard) in self.predecessors.predecessors(w) {
403
31260
                let mut a = gamma[v].and(&A[w])?.and(edge_guard)?;
404

            
405
31260
                if a.satisfiable() {
406
                    // 7. if v in V_\alpha
407
24350
                    if self.game.owner(v) == alpha {
408
11390
                        // 8. a := gamma(v) \intersect \theta(v, w) \intersect A(w)
409
11390
                        // This assignment has already been computed above.
410
11390
                    } else {
411
                        // 10. a := gamma(v)
412
12960
                        a = gamma[v].clone();
413
                        // 11. for w' \in vE such that gamma(v) && theta(v, w') && \gamma(w') != \emptyset do
414
18097
                        for edge_w1 in self.game.outgoing_conf_edges(v) {
415
18097
                            let tmp = gamma[v].and(edge_w1.configuration())?.and(&gamma[edge_w1.to()])?;
416

            
417
18097
                            if tmp.satisfiable() {
418
                                // 12. a := a && ((C \ (theta(v, w') && \gamma(w'))) \cup A(w'))
419
17425
                                let tmp = edge_w1.configuration().and(&gamma[edge_w1.to()])?;
420

            
421
17425
                                a = a.and(&minus(self.game.configuration(), &tmp)?.or(&A[edge_w1.to()])?)?;
422
672
                            }
423
                        }
424
                    }
425

            
426
                    // 15. a \ A(v) != \emptyset
427
24350
                    if minus(&a, &A[v])?.satisfiable() {
428
                        // 16. A(v) := A(v) \cup a
429
6086
                        A.set(v, A[v].or(&a)?);
430

            
431
                        // 17. if v not in Q then Q.push(v)
432
6086
                        if !self.temp_vertices[*v] {
433
6025
                            self.temp_queue.push(v);
434
6025
                            self.temp_vertices.set(*v, true);
435
6025
                        }
436
18264
                    }
437
6910
                }
438
            }
439
        }
440

            
441
2968
        debug_assert!(
442
2968
            !self.temp_vertices.any(),
443
            "temp_vertices should be empty after attractor computation"
444
        );
445

            
446
2968
        Ok(A)
447
2968
    }
448

            
449
    /// Returns the highest and lowest priority in the given set of vertices V.
450
2085
    fn get_highest_lowest_prio(&self, V: &Submap) -> (Priority, Priority) {
451
2085
        let mut highest = usize::MIN;
452
2085
        let mut lowest = usize::MAX;
453

            
454
22026
        for v in V.iter_vertices() {
455
22026
            let prio = self.game.priority(v);
456
22026
            highest = highest.max(*prio);
457
22026
            lowest = lowest.min(*prio);
458
22026
        }
459

            
460
2085
        (Priority::new(highest), Priority::new(lowest))
461
2085
    }
462

            
463
    /// Checks that the sets W0 and W1 form a  partition w.r.t the submap V, i.e., their union is V and their intersection is empty.
464
1292
    fn check_partition(&self, W0: &Submap, W1: &Submap, V: &Submap) -> Result<(), MercError> {
465
18409
        for v in V.iter_vertices() {
466
18409
            let tmp = W0[v].or(&W1[v])?;
467

            
468
            // The union of both solutions should be the entire set of vertices.
469
18409
            debug_assert!(
470
18409
                tmp == V[v],
471
                "The union of both solutions should be the entire set of vertices, but vertex {v} is missing."
472
            );
473

            
474
18409
            debug_assert!(
475
18409
                !W0[v].and(&W1[v])?.satisfiable(),
476
                "The intersection of both solutions should be empty, but vertex {v} has non-empty intersection."
477
            );
478
        }
479

            
480
1292
        Ok(())
481
1292
    }
482
}
483

            
484
/// Returns the boolean set difference of two BDD functions: lhs \ rhs.
485
/// Implemented as lhs AND (NOT rhs).
486
166556
pub fn minus(lhs: &BDDFunction, rhs: &BDDFunction) -> AllocResult<BDDFunction> {
487
166556
    lhs.and(&rhs.not()?)
488
166556
}
489

            
490
/// A mapping from vertices to configurations.
491
#[derive(Clone, PartialEq, Eq)]
492
pub struct Submap {
493
    /// The mapping from vertex indices to BDD functions.
494
    mapping: Vec<BDDFunction>,
495

            
496
    /// Invariant: counts the number of non-empty positions in the mapping.
497
    non_empty_count: usize,
498

            
499
    /// The BDD function representing the empty configuration.
500
    false_bdd: BDDFunction,
501
}
502

            
503
impl Submap {
504
    /// Creates a new empty Submap for the given number of vertices.
505
2386
    fn new(initial: BDDFunction, false_bdd: BDDFunction, num_of_vertices: usize) -> Self {
506
        Self {
507
2386
            mapping: vec![initial.clone(); num_of_vertices],
508
2386
            false_bdd,
509
2386
            non_empty_count: if initial.satisfiable() {
510
300
                num_of_vertices // If the initial function is satisfiable, all entries are non-empty.
511
            } else {
512
2086
                0
513
            },
514
        }
515
2386
    }
516

            
517
    /// Returns an iterator over the vertices in the submap whose configuration is satisfiable.
518
6345
    pub fn iter_vertices(&self) -> impl Iterator<Item = VertexIndex> + '_ {
519
139590
        self.mapping.iter().enumerate().filter_map(|(i, func)| {
520
139590
            if func.satisfiable() {
521
55273
                Some(VertexIndex::new(i))
522
            } else {
523
84317
                None
524
            }
525
139590
        })
526
6345
    }
527

            
528
    /// Returns the number of non-empty entries in the submap.
529
    pub fn number_of_non_empty(&self) -> usize {
530
        self.non_empty_count
531
    }
532

            
533
    /// Sets the function for the given vertex index.
534
21996
    fn set(&mut self, index: VertexIndex, func: BDDFunction) {
535
21996
        let was_empty = !self.mapping[*index].satisfiable();
536
21996
        let is_empty = !func.satisfiable();
537

            
538
21996
        self.mapping[*index] = func;
539

            
540
        // Update the non-empty count invariant.
541
21996
        if was_empty && !is_empty {
542
15673
            self.non_empty_count += 1;
543
15673
        } else if !was_empty && is_empty {
544
            self.non_empty_count -= 1;
545
6323
        }
546
21996
    }
547

            
548
    /// Returns true iff the submap is empty.
549
5353
    fn is_empty(&self) -> bool {
550
5353
        self.non_empty_count == 0
551
5353
    }
552

            
553
    /// Returns the number of entries in the submap.
554
1
    fn len(&self) -> usize {
555
1
        self.mapping.len()
556
1
    }
557

            
558
    /// Clears the submap, setting all entries to the empty function.
559
    fn clear(&mut self) -> Result<(), MercError> {
560
        for func in self.mapping.iter_mut() {
561
            *func = self.false_bdd.clone();
562
        }
563
        self.non_empty_count = 0;
564

            
565
        Ok(())
566
    }
567

            
568
    /// Computes the difference between this submap and another submap.
569
2968
    fn minus(mut self, other: &Submap) -> Result<Submap, MercError> {
570
65296
        for (i, func) in self.mapping.iter_mut().enumerate() {
571
65296
            let was_satisfiable = func.satisfiable();
572
65296
            *func = minus(func, &other.mapping[i])?;
573
65296
            let is_satisfiable = func.satisfiable();
574

            
575
65296
            if was_satisfiable && !is_satisfiable {
576
19987
                self.non_empty_count -= 1;
577
45309
            }
578
        }
579

            
580
2968
        Ok(self)
581
2968
    }
582

            
583
    /// Computes the union between this submap and another submap.
584
2970
    fn or(mut self, other: &Submap) -> Result<Submap, MercError> {
585
65340
        for (i, func) in self.mapping.iter_mut().enumerate() {
586
65340
            let was_satisfiable = func.satisfiable();
587
65340
            *func = func.or(&other.mapping[i])?;
588
65340
            let is_satisfiable = func.satisfiable();
589

            
590
65340
            if !was_satisfiable && is_satisfiable {
591
14257
                self.non_empty_count += 1;
592
51083
            }
593
        }
594

            
595
2970
        Ok(self)
596
2970
    }
597

            
598
    /// Computes the intersection between this submap and another function.
599
    fn and_function(mut self, configuration: &BDDFunction) -> Result<Submap, MercError> {
600
        for (i, func) in self.mapping.iter_mut().enumerate() {
601
            let was_satisfiable = func.satisfiable();
602
            *func = func.and(configuration)?;
603
            let is_satisfiable = func.satisfiable();
604

            
605
            if was_satisfiable && !is_satisfiable {
606
                self.non_empty_count -= 1;
607
            }
608
        }
609

            
610
        Ok(self)
611
    }
612

            
613
    /// Computes the difference between this submap and another function.
614
2174
    fn minus_function(mut self, configuration: &BDDFunction) -> Result<Submap, MercError> {
615
47828
        for (i, func) in self.mapping.iter_mut().enumerate() {
616
47828
            let was_satisfiable = func.satisfiable();
617
47828
            *func = minus(func, configuration)?;
618
47828
            let is_satisfiable = func.satisfiable();
619

            
620
47828
            if was_satisfiable && !is_satisfiable {
621
4535
                self.non_empty_count -= 1;
622
43293
            }
623
        }
624

            
625
2174
        Ok(self)
626
2174
    }
627

            
628
    /// Returns an iterator over all entries.
629
295
    pub fn iter(&self) -> impl Iterator<Item = (VertexIndex, &BDDFunction)> {
630
295
        self.mapping
631
295
            .iter()
632
295
            .enumerate()
633
6490
            .map(|(i, func)| (VertexIndex::new(i), func))
634
295
    }
635
}
636

            
637
impl Index<VertexIndex> for Submap {
638
    type Output = BDDFunction;
639

            
640
293926
    fn index(&self, index: VertexIndex) -> &Self::Output {
641
293926
        &self.mapping[*index]
642
293926
    }
643
}
644

            
645
impl fmt::Debug for Submap {
646
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
647
        for (i, func) in self.mapping.iter().enumerate() {
648
            if func.satisfiable() {
649
                write!(f, " {} ({})", i, FormatConfigSet(func))?;
650
            }
651
        }
652
        Ok(())
653
    }
654
}
655

            
656
#[cfg(test)]
657
mod tests {
658
    use merc_macros::merc_test;
659
    use oxidd::BooleanFunction;
660
    use oxidd::Manager;
661
    use oxidd::ManagerRef;
662
    use oxidd::bdd::BDDFunction;
663
    use oxidd::util::AllocResult;
664

            
665
    use merc_utilities::random_test;
666

            
667
    use crate::PG;
668
    use crate::Submap;
669
    use crate::VertexIndex;
670
    use crate::ZielonkaVariant;
671
    use crate::project_variability_parity_games_iter;
672
    use crate::random_variability_parity_game;
673
    use crate::solve_variability_product_zielonka;
674
    use crate::solve_variability_zielonka;
675
    use crate::solve_zielonka;
676
    use crate::verify_variability_product_zielonka_solution;
677
    use crate::write_vpg;
678

            
679
    #[merc_test]
680
    #[cfg_attr(miri, ignore)] // Oxidd does not work with miri
681
    fn test_submap() {
682
        let manager_ref = oxidd::bdd::new_manager(2048, 1024, 1);
683
        let vars: Vec<BDDFunction> = manager_ref
684
1
            .with_manager_exclusive(|manager| {
685
3
                AllocResult::from_iter(manager.add_vars(3).map(|i| BDDFunction::var(manager, i)))
686
1
            })
687
            .expect("Could not create variables");
688

            
689
1
        let false_bdd = manager_ref.with_manager_shared(|manager| BDDFunction::f(manager));
690
        let mut submap = Submap::new(false_bdd.clone(), false_bdd, 3);
691

            
692
        assert_eq!(submap.len(), 3);
693
        assert_eq!(submap.non_empty_count, 0);
694
        submap.set(VertexIndex::new(1), vars[0].clone());
695

            
696
        assert_eq!(submap.non_empty_count, 1);
697
    }
698

            
699
    #[merc_test]
700
    #[cfg_attr(miri, ignore)] // Oxidd does not work with miri
701
    fn test_random_variability_parity_game_solve() {
702
100
        random_test(100, |rng| {
703
100
            let manager_ref = oxidd::bdd::new_manager(2048, 1024, 1);
704
100
            let vpg = random_variability_parity_game(&manager_ref, rng, true, 20, 3, 3, 3).unwrap();
705

            
706
            // write_vpg(&mut std::io::stdout(), &vpg).unwrap();
707

            
708
100
            let solution = solve_variability_zielonka(&manager_ref, &vpg, ZielonkaVariant::Family, false).unwrap();
709
100
            verify_variability_product_zielonka_solution(&vpg, &solution).unwrap();
710
100
        })
711
    }
712

            
713
    #[merc_test]
714
    #[cfg_attr(miri, ignore)] // Oxidd does not work with miri
715
    fn test_random_variability_parity_game_solve_optimised_left() {
716
100
        random_test(100, |rng| {
717
100
            let manager_ref = oxidd::bdd::new_manager(2048, 1024, 1);
718
100
            let vpg = random_variability_parity_game(&manager_ref, rng, true, 20, 3, 3, 3).unwrap();
719

            
720
            // write_vpg(&mut std::io::stdout(), &vpg).unwrap();
721

            
722
100
            let solution =
723
100
                solve_variability_zielonka(&manager_ref, &vpg, ZielonkaVariant::FamilyOptimisedLeft, false).unwrap();
724
100
            let solution_expected =
725
100
                solve_variability_zielonka(&manager_ref, &vpg, ZielonkaVariant::Family, false).unwrap();
726

            
727
100
            debug_assert_eq!(solution[0], solution_expected[0]);
728
100
            debug_assert_eq!(solution[1], solution_expected[1]);
729
100
        })
730
    }
731
}