1
//! Authors: Maurice Laveaux and Sjef van Loo
2

            
3
use std::fmt;
4

            
5
use delegate::delegate;
6
use oxidd::BooleanFunction;
7
use oxidd::ManagerRef;
8
use oxidd::bdd::BDDFunction;
9
use oxidd::bdd::BDDManagerRef;
10

            
11
use merc_symbolic::FormatConfigSet;
12
use merc_utilities::MercError;
13

            
14
use crate::PG;
15
use crate::ParityGame;
16
use crate::Player;
17
use crate::Priority;
18
use crate::VertexIndex;
19
use crate::minus;
20

            
21
/// A variability parity game is an extension of a parity game where each edge is
22
/// associated with a BDD function representing the configurations in which the
23
/// edge is enabled.
24
///
25
/// # Details
26
///
27
/// This is also a max-priority parity game. There is also a configuration set associated
28
/// with the variability parity game, representing the overall configurations.
29
pub struct VariabilityParityGame {
30
    /// The underlying normal parity game.
31
    game: ParityGame,
32

            
33
    /// The overall configurations for the variability parity game.
34
    configuration: BDDFunction,
35

            
36
    /// The variables used in the configuration BDD.
37
    variables: Vec<BDDFunction>,
38

            
39
    /// Every edge has an associated BDD function representing the configurations
40
    /// in which the edge is enabled.
41
    edges_configuration: Vec<BDDFunction>,
42
}
43

            
44
/// Represents an edge in the parity game along with its configuration BDD.
45
pub struct Edge<'a> {
46
    to: VertexIndex,
47
    configuration: &'a BDDFunction,
48
}
49

            
50
impl<'a> Edge<'a> {
51
    /// Returns the target vertex of the edge.
52
100249
    pub fn to(&self) -> VertexIndex {
53
100249
        self.to
54
100249
    }
55

            
56
    /// Returns the configuration BDD associated with the edge.
57
89876
    pub fn configuration(&self) -> &BDDFunction {
58
89876
        self.configuration
59
89876
    }
60
}
61

            
62
impl VariabilityParityGame {
63
    /// Construct a new variability parity game from an iterator over edges.
64
301
    pub fn new(
65
301
        parity_game: ParityGame,
66
301
        configuration: BDDFunction,
67
301
        variables: Vec<BDDFunction>,
68
301
        edges_configuration: Vec<BDDFunction>,
69
301
    ) -> Self {
70
        // Check that the sizes are consistent
71
301
        debug_assert_eq!(
72
301
            edges_configuration.len(),
73
301
            parity_game.num_of_edges(),
74
            "There should be a configuration BDD for every edge"
75
        );
76

            
77
301
        Self {
78
301
            game: parity_game,
79
301
            configuration,
80
301
            variables,
81
301
            edges_configuration,
82
301
        }
83
301
    }
84

            
85
    /// Constructs a new variability parity game from an iterator over edges.
86
    ///
87
    /// The vertices are given by their owner and priority.
88
    /// The `edges` iterator should yield tuples of the form (from, configuration, to).
89
202
    pub fn from_edges<F, I>(
90
202
        manager_ref: &BDDManagerRef,
91
202
        initial_vertex: VertexIndex,
92
202
        owner: Vec<Player>,
93
202
        priority: Vec<Priority>,
94
202
        configuration: BDDFunction,
95
202
        variables: Vec<BDDFunction>,
96
202
        mut edges: F,
97
202
    ) -> Self
98
202
    where
99
202
        F: FnMut() -> I,
100
202
        I: Iterator<Item = (VertexIndex, BDDFunction, VertexIndex)>,
101
    {
102
202
        let num_of_vertices = owner.len();
103
202
        debug_assert_eq!(
104
202
            priority.len(),
105
            num_of_vertices,
106
            "Owner and priority vectors should have the same length"
107
        );
108

            
109
202
        let mut vertices = Vec::new();
110
202
        vertices.resize_with(num_of_vertices, Default::default);
111
202
        debug_assert!(
112
202
            initial_vertex.value() < num_of_vertices,
113
            "Initial vertex index {} out of bounds {num_of_vertices}",
114
            initial_vertex.value()
115
        );
116

            
117
        // Count the number of transitions for every state
118
202
        let mut num_of_edges = 0;
119
6477
        for (from, _, to) in edges() {
120
            // Ensure that the states vector is large enough.
121
6477
            if vertices.len() <= *from.max(to) {
122
                vertices.resize_with(*from.max(to) + 1, || 0);
123
6477
            }
124

            
125
6477
            vertices[*from] += 1;
126
6477
            num_of_edges += 1;
127

            
128
6477
            debug_assert!(
129
6477
                *from < num_of_vertices && *to < num_of_vertices,
130
                "Vertex index out of bounds: from {:?}, to {:?}, num_of_vertices {}",
131
                from,
132
                to,
133
                num_of_vertices
134
            );
135
        }
136

            
137
202
        if initial_vertex.value() >= vertices.len() {
138
            // Ensure that the initial state is a valid state (and all states before it exist).
139
            vertices.resize_with(initial_vertex.value() + 1, Default::default);
140
202
        }
141

            
142
        // Sets the offset for every state into the edge arrays.
143
4448
        vertices.iter_mut().fold(0, |count, start| {
144
4448
            let result = count + *start;
145
4448
            *start = count;
146
4448
            result
147
4448
        });
148

            
149
        // Place the transitions, and increment the end for every state.
150
202
        let mut edges_to = vec![VertexIndex::new(0); num_of_edges];
151
202
        let mut edges_configuration =
152
202
            manager_ref.with_manager_shared(|manager| vec![BDDFunction::f(manager); num_of_edges]);
153
6477
        for (from, config, to) in edges() {
154
6477
            let start = &mut vertices[*from];
155
6477
            edges_to[*start] = to;
156
6477
            edges_configuration[*start] = config;
157
6477
            *start += 1;
158
6477
        }
159

            
160
        // Reset the offset to the start.
161
4448
        vertices.iter_mut().fold(0, |previous, start| {
162
4448
            let result = *start;
163
4448
            *start = previous;
164
4448
            result
165
4448
        });
166

            
167
202
        vertices.push(num_of_edges); // Sentinel vertex
168

            
169
202
        Self {
170
202
            game: ParityGame::new(initial_vertex, owner, priority, vertices, edges_to),
171
202
            configuration,
172
202
            variables,
173
202
            edges_configuration,
174
202
        }
175
202
    }
176

            
177
    /// Returns an iterator over the outgoing edges of the given vertex.
178
53349
    pub fn outgoing_conf_edges(&self, state_index: VertexIndex) -> impl Iterator<Item = Edge<'_>> + '_ {
179
53349
        let start = self.game.vertices()[*state_index];
180
53349
        let end = self.game.vertices()[*state_index + 1];
181
53349
        self.edges_configuration[start..end]
182
53349
            .iter()
183
53349
            .zip(self.game.edges_to()[start..end].iter())
184
76657
            .map(|(configuration, &to)| Edge { to, configuration })
185
53349
    }
186

            
187
    /// Returns true iff the parity game is total, checks all vertices have at least one outgoing edge.
188
301
    pub fn is_total(&self, manager_ref: &BDDManagerRef) -> Result<bool, MercError> {
189
        // Check that every vertex has at least one outgoing edge.
190
6606
        for v in self.iter_vertices() {
191
6606
            if self.outgoing_edges(v).next().is_none() {
192
1
                return Ok(false);
193
6605
            }
194
        }
195

            
196
        // Check that the configurations of the outgoing edges cover the overall configuration.
197
6600
        for v in self.iter_vertices() {
198
            // Compute the disjunction of all outgoing edge configurations.
199
6600
            let covered = self.outgoing_conf_edges(v).try_fold(
200
6600
                manager_ref.with_manager_shared(|manager| BooleanFunction::f(manager)),
201
9611
                |acc: BDDFunction, edge| acc.or(edge.configuration()),
202
            )?;
203

            
204
            // If there are configurations not covered by the outgoing edges, the game is not total.
205
6600
            if minus(&self.configuration, &covered)?.satisfiable() {
206
                return Ok(false);
207
6600
            }
208
        }
209

            
210
300
        Ok(true)
211
301
    }
212

            
213
    /// Returns the overall configuration BDD of the variability parity game.
214
18321
    pub fn configuration(&self) -> &BDDFunction {
215
18321
        &self.configuration
216
18321
    }
217

            
218
    /// Returns the variables used in the configuration BDD.
219
301
    pub fn variables(&self) -> &Vec<BDDFunction> {
220
301
        &self.variables
221
301
    }
222

            
223
    /// Returns the owners of the vertices in the variability parity game.
224
954
    pub(crate) fn owners(&self) -> &Vec<Player> {
225
954
        self.game.owners()
226
954
    }
227

            
228
    /// Returns the priorities of the vertices in the variability parity game.
229
954
    pub(crate) fn priorities(&self) -> &Vec<Priority> {
230
954
        self.game.priorities()
231
954
    }
232
}
233

            
234
impl PG for VariabilityParityGame {
235
    delegate! {
236
        to self.game {
237
955
            fn initial_vertex(&self) -> VertexIndex;
238
4593
            fn num_of_vertices(&self) -> usize;
239
600
            fn num_of_edges(&self) -> usize;
240
3208
            fn iter_vertices(&self) -> impl Iterator<Item = VertexIndex> + '_;
241
24373
            fn owner(&self, vertex: VertexIndex) -> Player;
242
28649
            fn priority(&self, vertex: VertexIndex) -> Priority;
243
6629
            fn outgoing_edges(&self, state_index: VertexIndex) -> impl Iterator<Item = VertexIndex> + '_;
244
        }
245
    }
246
}
247

            
248
impl fmt::Display for VariabilityParityGame {
249
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250
        writeln!(f, "Variability Parity Game:")?;
251
        writeln!(f, "Configuration BDD: {}", FormatConfigSet(self.configuration()))?;
252

            
253
        for v in self.iter_vertices() {
254
            write!(
255
                f,
256
                "v{}: {:?}, priority={}, edges=[",
257
                v,
258
                self.owner(v).to_index(),
259
                self.priority(v)
260
            )?;
261

            
262
            let mut first = true;
263
            for edge in self.outgoing_conf_edges(v) {
264
                if !first {
265
                    write!(f, ", ")?;
266
                }
267
                write!(f, "(v{}, {})", edge.to(), FormatConfigSet(edge.configuration()))?;
268
                first = false;
269
            }
270

            
271
            writeln!(f, "]")?;
272
        }
273

            
274
        Ok(())
275
    }
276
}