mCRL2
Loading...
Searching...
No Matches
PriorityPromotionSolver.cpp
Go to the documentation of this file.
1// Author(s): Maurice Laveaux
2// Copyright (c) 2016-2016 Eindhoven University of Technology
3//
4// Distributed under the Boost Software License, Version 1.0.
5// (See accompanying file LICENSE_1_0.txt or copy at
6// http://www.boost.org/LICENSE_1_0.txt)
7
9
11
14{}
15
17{
18 // Important note, instead of actually repeatly removing dominions from
19 // the game. The game is kept the same but the substrategy is used to
20 // determine which vertices still are not solved. This is done
21 // because make_subgame allocates new memory repeately and parity games
22 // can be huge.
23 StaticGraph& graph = const_cast<StaticGraph&>(game_.graph());
24
25 // Initialize the initial state top = (priority_function, [NO_VERTEX], prio)
26 std::vector<priority_t> region_function(graph.V(), 0);
27 ParityGame::Strategy strategy(graph.V(), NO_VERTEX);
28
29 // Due to cache locality the new approach will be slightly slower. Also
30 // a magic region is used to indicate in region_function that some vertex is
31 // computed.
32 m_unsolved = std::vector<verti>(graph.V(), 0);
33
34 // The lowest priority in the game (the highest number).
35 priority_t lowestRegion = 0;
36
37 // Set region_function to the original priorities and initialize the mapping
38 for (verti v = 0; v < graph.V(); ++v) {
39 region_function[v] = game().priority(v);
40 m_unsolved[v] = v;
41
42 lowestRegion = std::max(lowestRegion, game().priority(v));
43 }
44
45 // Initialize all regions that have some vertices to true
46 m_regions = std::vector<verti>(lowestRegion + 1, 0);
47 for (priority_t region : region_function) {
48 ++m_regions[region];
49 }
50
51 // Find the lowest priority in the game
52 priority_t prio = nextPriority(region_function, 0);
53
54 // The searcher: The algorithm was tail recursive so can also be written as iteration.
55 while (true) {
56 query(region_function, strategy, prio);
57
58 if (isOpen(region_function, prio, true)) {
59 mCRL2log(mcrl2::log::debug) << "Newly computed region is open in the subgame, with p = " << prio << std::endl;
60 printRegion(region_function, prio);
61
62 // Keep the new region_function and substrategy, but go to the next priority
63 prio = nextPriority(region_function, prio + 1);
64 }
65 else {
66 if (!isOpen(region_function, prio, false)) {
67
68 // Make sure we can use todo without problems.
69 assert(m_todo.empty());
70
71 // This is a dominion D in the whole game, compute the attractor
72 // for this region.
73 for (verti v : m_unsolved) {
74 if (region_function[v] == prio) {
75 m_todo.push_back(v);
76 }
77 }
78
79 computeAttractor(region_function, strategy, prio, m_todo, false);
80
81 // Remove the dominion from the game and keep the unsolved vertices, also reset
82 // lower priorities and set region of prio to the COMPUTED_REGION.
83 mCRL2log(mcrl2::log::debug) << "Found the dominion D, with p = " << prio << std::endl;
84 printRegion(region_function, prio);
85
86 // Reset the unsolved set and remove all regions, also add one dominion to statistics
87 m_unsolved.clear();
88 m_regions.assign(m_regions.size(), 0);
90
91 for (verti v = 0; v < game().graph().V(); ++v) {
92 if (region_function[v] == prio) {
93 // Assign a special region indicating that its solved.
94 region_function[v] = COMPUTED_REGION;
95 }
96 else if (region_function[v] != COMPUTED_REGION) {
97 region_function[v] = game().priority(v);
98 strategy[v] = NO_VERTEX;
99
100 // Add the not solved vertices to the unsolved set and add vertices to their region.
101 m_unsolved.push_back(v);
102 ++m_regions[game().priority(v)];
103 }
104 }
105
106
107 if (m_unsolved.empty()) {
108 break; // Stop the algorithm, as all the vertices were solved.
109 }
110
111 // Reset the game and find the lowest priority in the game
112 prio = nextPriority(region_function, 0);
113 }
114 else {
115 // The game is a dominion, but only in the subgame, so promote its priority.
116 mCRL2log(mcrl2::log::debug) << "Promoted dominion D, with p = " << prio << " to ";
117 prio = promoteSubDominion(region_function, strategy, prio);
118 mCRL2log(mcrl2::log::debug) << prio << std::endl;
119 printRegion(region_function, prio);
120 }
121 }
122 }
123
124 mCRL2log(mcrl2::log::verbose) << m_dominions << " dominions found, and " << m_promotions << " promotions required" << std::endl;
125
126 return strategy;
127}
128
129void PriorityPromotionSolver::query(std::vector<priority_t>& region_function,
130 ParityGame::Strategy& strategy,
131 priority_t prio)
132{
133 // Make sure nothing else is stored in the todo
134 assert(m_todo.empty());
135
136 // R* = region_function^-1(prio), this results in the todo for the attractor
137 // computation, the initial set essentially.
138 for (verti v : m_unsolved)
139 {
140 if (region_function[v] == prio) {
141 m_todo.push_back(v);
142 }
143 }
144
145 // (region_function[R -> prio], strategy*) <- computeAttractor_G(todo, strategy
146 // restriced to todo)
147 computeAttractor(region_function, strategy, prio, m_todo, true);
148}
149
150void PriorityPromotionSolver::computeAttractor(std::vector<priority_t>& region_function,
151 ParityGame::Strategy& strategy,
152 priority_t prio,
153 std::deque<verti>& todo,
154 bool inSubgraph)
155{
156 // Name some variables for easier access
157 const StaticGraph &graph = game().graph();
158 const ParityGame::Player alpha = (ParityGame::Player)(prio % 2);
159
160 // O(V): Compute the attractor set to the alpha-region:
161 while (!todo.empty()) {
162 const verti w = todo.front();
163 todo.pop_front();
164
165 // Check all predecessors v of w:
166 for (StaticGraph::const_iterator it = graph.pred_begin(w);
167 it != graph.pred_end(w); ++it) {
168 const verti v = *it;
169
170 // Skip predecessors that are already in the attractor set, also skip
171 // vertices outside the subgame G >= prio. Or vertices that are computed
172 if (region_function[v] == prio || region_function[v] == COMPUTED_REGION || (inSubgraph && region_function[v] < prio)) continue;
173
174 if (game().player(v) == alpha) {
175 // sigma(v) = w, a valid strategy for alpha is to pick a successor in A
176 strategy[v] = w;
177 }
178 else {
179
180 // Check if all successors (w, x) subset A, thus if they end up in a vertex with prio.
181 bool isSubset = true;
182 for (StaticGraph::const_iterator it = graph.succ_begin(v);
183 it != graph.succ_end(v); ++it) {
184 verti x = *it;
185
186 // Skip vertices that are not considered in the subgraph G >= prio
187 // or that already belong to COMPUTED_REGION
188 if (region_function[x] == prio || region_function[x] == COMPUTED_REGION) {
189 continue;
190 }
191
192 // Either only take vertices in G >= prio or all when inSubgraph is false
193 if (region_function[x] > prio || !inSubgraph) {
194 isSubset = false; break;
195 }
196 }
197
198 // opponent controls vertex
199 if (isSubset) {
200 // For opponent controlled vertices no strategy exists, so
201 // every possible outgoing edge is losing.
202 strategy[v] = NO_VERTEX;
203 } else {
204 continue; // not in the attractor set yet!
205 }
206 }
207
208 // Add a vertex to their new region and remove from the old one
209 --m_regions[region_function[v]];
210 ++m_regions[prio];
211
212 // When this part is reached, all liberties of v are gone or v belongs
213 // to alpha, so add vertex v to the attractor set:
214 region_function[v] = prio;
215 todo.push_back(v);
216
217 }
218 }
219
220 // R \ domain(tau restricted to R*), essentially vertices in R belonging to
221 // alpha where no strategy is defined yet. These can pick an arbritrary
222 // successor that can reach R \ R*, these already have an attraction
223 // strategy so that is always fine.
224 for (verti v : m_unsolved)
225 {
226 if (region_function[v] == prio && game().player(v) == alpha && strategy[v] == NO_VERTEX) {
227 for (StaticGraph::const_iterator it = graph.succ_begin(v); it != graph.succ_end(v); ++it) {
228 const verti w = *it;
229
230 if (region_function[w] == prio) {
231 // There exists some (v, w) in E such that w belongs to R (has r[w] == prio).
232 strategy[v] = w;
233 }
234 }
235 }
236 }
237
238 // TODO: Check conflict in the paper.
239 // In the lemma: by associating every position v in Stay(R) \ domain(strategy)
240 // with an arbitrary successor in R. So a vertex without a strategy can
241 // pick a successor in R.
242 //
243 // Later on: R \ domain(strategy restricted to R*), R* is region without
244 // attractor. So this is still the same, but then it should take a
245 // vertex in R \ R*. So it has to be outside of the region, this cannot
246 // be checked easily at this point as that information (in todo) is lost.
247}
248
249bool PriorityPromotionSolver::isOpen(std::vector<priority_t>& region_function,
250 priority_t prio,
251 bool inSubgraph)
252{
253 // Name some variables for easier access
254 const StaticGraph &graph = game().graph();
255 ParityGame::Player alpha = (ParityGame::Player)(prio % 2);
256
257 // O(V): Loop over unsolved vertices and find vertices belonging to region with prio.
258 for (verti v : m_unsolved) {
259 if (region_function[v] == prio) {
260 // If the vertex belong to the opponent
261 if (game().player(v) != alpha) {
262 // For all (v, u) in E, u should belong to R
263 for (StaticGraph::const_iterator it = graph.succ_begin(v);
264 it != graph.succ_end(v); ++it)
265 {
266 const verti u = *it;
267
268 // There is an edge from opponent to a vertex in the subgraph or in the whole graph
269 if (region_function[u] != COMPUTED_REGION
270 && ((inSubgraph && region_function[u] > prio)
271 || (!inSubgraph && region_function[u] != prio))) {
272 return true;
273 }
274 }
275 }
276 else {
277 // If there exists a (v, u) to R its closed
278 bool isOpen = true;
279
280 for (StaticGraph::const_iterator it = graph.succ_begin(v);
281 it != graph.succ_end(v); ++it)
282 {
283 const verti u = *it;
284
285 // There is an edge from a player to the region
286 if (region_function[u] == prio) {
287 isOpen = false; break;
288 }
289 }
290
291 if (isOpen) {
292 return true;
293 }
294 }
295 }
296 }
297
298 return false;
299}
300
301priority_t PriorityPromotionSolver::promoteSubDominion(std::vector<priority_t>& region_function,
302 ParityGame::Strategy& strategy,
303 priority_t prio)
304{
305 const StaticGraph &graph = game().graph();
306 ParityGame::Player alpha = (ParityGame::Player)(prio % 2);
307
308 // O(V): It is only a dominion in the subgraph, determine highest p < prio
309 // that opponent can escape to
310 priority_t promotion = 0;
311
312 // This is referred to as r* = bep(R, r) in the paper (best escape priority).
313 // For every opponent vertex this is the lowest priority (highest value in
314 // min-prio games) that it can reach. The region it can reach belongs to alpha,
315 // otherwise it would be attracted in some earlier state.
316 for (verti v : m_unsolved) {
317 if (region_function[v] == prio && game().player(v) != alpha) {
318 // For all (v, u) in E collect the highest priority smaller then prio that opponent can flee to.
319 for (StaticGraph::const_iterator it = graph.succ_begin(v);
320 it != graph.succ_end(v); ++it) {
321 const verti u = *it;
322
323 if (region_function[u] < prio) {
324 promotion = std::max(promotion, region_function[u]);
325 }
326 }
327 }
328 }
329
330 ++m_promotions;
331
332 // Here the prio region is promoted to the new priority and all lower positions
333 // are reset.
334 for (verti v : m_unsolved) {
335 // Promote the current region to the promotion priority
336 if (region_function[v] == prio) {
337 // Remove the vertex from the old region
338 --m_regions[region_function[v]];
339
340 region_function[v] = promotion;
341
342 // Add the vertex to the new region.
343 ++m_regions[region_function[v]];
344 }
345 else if (region_function[v] > promotion) {
346 // Remove the vertex from the old region
347 --m_regions[region_function[v]];
348
349 // Reset all vertices lower to the original priorities, remove the strategy.
350 region_function[v] = game().priority(v);
351 strategy[v] = NO_VERTEX;
352
353 // Add the vertex to the new region.
354 ++m_regions[region_function[v]];
355 }
356 }
357
358 return promotion;
359}
360
361void PriorityPromotionSolver::printRegion(std::vector<priority_t>& region_function,
362 priority_t prio)
363{
364 // This costs O(V) so only enable this in debug, prints all vertices v where region_function[v] == prio.
365 if (mCRL2logEnabled(mcrl2::log::debug)) {
366 mCRL2log(mcrl2::log::debug) << "alpha-region[" << prio << "] = { ";
367 bool first = true;
368 for (verti v : m_unsolved)
369 {
370 if (region_function[v] == prio) {
371 if (!first) {
373 }
374
376 first = false;
377 }
378
379 }
380 mCRL2log(mcrl2::log::debug) << " }" << std::endl;
381 }
382}
383
384priority_t PriorityPromotionSolver::nextPriority(std::vector<priority_t>& /* region_function */,
385 priority_t prio)
386{
387 // Starting from the current priority, find the next region that exists
388 // greater or equal to rio. This should never go out of bounds as the lowest
389 // region will always be dominion.
390 while (m_regions[prio] == 0)
391 {
392 ++prio;
393 assert(prio < m_regions.size());
394 }
395
396 return prio;
397}
398
400 const verti* /* vertex_map */,
401 verti /* vertex_map_size*/)
402{
403 return new PriorityPromotionSolver(game);
404}
std::size_t verti
type used to number vertices
Definition Graph.h:24
#define NO_VERTEX
Definition Graph.h:27
std::size_t priority_t
Definition ParityGame.h:32
player_t
Definition ParityGame.h:35
const priority_t COMPUTED_REGION
const ParityGame & game() const
const ParityGame & game_
Game being solved.
priority_t priority(verti v) const
Definition ParityGame.h:259
Player player(verti v) const
Definition ParityGame.h:262
player_t Player
Definition ParityGame.h:87
const StaticGraph & graph() const
Definition ParityGame.h:256
std::vector< verti > Strategy
Definition ParityGame.h:97
ParityGameSolver * create(const ParityGame &game, const verti *vertex_map, verti vertex_map_size)
Returns a new PriorityPromotionSolver instance.
std::vector< verti > m_regions
Count the number of vertices per region, to speed up nextPriority.
priority_t nextPriority(std::vector< priority_t > &region_function, priority_t prio)
void printRegion(std::vector< priority_t > &region_function, priority_t prio)
Print the vertices with region_function[v] equal to prio, representing the region.
verti m_dominions
The number of promotions required.
std::vector< verti > m_unsolved
Stores a list of vertices not yet solved by the algorithm.
std::deque< verti > m_todo
This is a reused queue with vertices to compute the attractor set from.
bool isOpen(std::vector< priority_t > &region_function, priority_t prio, bool inSubgraph)
priority_t promoteSubDominion(std::vector< priority_t > &region_function, ParityGame::Strategy &strategy, priority_t prio)
void query(std::vector< priority_t > &region_function, std::vector< verti > &strategy, priority_t prio)
PriorityPromotionSolver(const ParityGame &game)
void computeAttractor(std::vector< priority_t > &region_function, ParityGame::Strategy &strategy, priority_t prio, std::deque< verti > &todo, bool inSubgraph)
const_iterator succ_end(verti v) const
Definition Graph.h:193
const_iterator succ_begin(verti v) const
Definition Graph.h:188
verti V() const
Definition Graph.h:179
const_iterator pred_begin(verti v) const
Definition Graph.h:198
const_iterator pred_end(verti v) const
Definition Graph.h:203
const verti * const_iterator
Definition Graph.h:66
#define mCRL2log(LEVEL)
mCRL2log(LEVEL) provides the stream used to log.
Definition logger.h:391
@ verbose
Definition logger.h:37