1 /* Polyhedron class implementation
2 (non-inline widening-related member functions).
3 Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it>
4 Copyright (C) 2010-2016 BUGSENG srl (http://bugseng.com)
5
6 This file is part of the Parma Polyhedra Library (PPL).
7
8 The PPL is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12
13 The PPL is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software Foundation,
20 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA.
21
22 For the most up-to-date information see the Parma Polyhedra Library
23 site: http://bugseng.com/products/ppl/ . */
24
25 #include "ppl-config.h"
26 #include "Polyhedron_defs.hh"
27 #include "BHRZ03_Certificate_defs.hh"
28 #include "Rational_Box.hh"
29 #include "Scalar_Products_defs.hh"
30 #include "Scalar_Products_inlines.hh"
31 #include "assertions.hh"
32 #include <iostream>
33 #include <stdexcept>
34 #include <deque>
35
36 namespace PPL = Parma_Polyhedra_Library;
37
38 void
39 PPL::Polyhedron
select_CH78_constraints(const Polyhedron & y,Constraint_System & cs_selection) const40 ::select_CH78_constraints(const Polyhedron& y,
41 Constraint_System& cs_selection) const {
42 // Private method: the caller must ensure the following conditions.
43 PPL_ASSERT(topology() == y.topology()
44 && topology() == cs_selection.topology()
45 && space_dim == y.space_dim);
46 PPL_ASSERT(!marked_empty()
47 && !has_pending_constraints()
48 && generators_are_up_to_date());
49 PPL_ASSERT(!y.marked_empty()
50 && !y.has_something_pending()
51 && y.constraints_are_minimized());
52
53 // A constraint in `y.con_sys' is copied to `cs_selection'
54 // if it is satisfied by all the generators of `gen_sys'.
55
56 // Note: the loop index `i' goes upward to avoid reversing
57 // the ordering of the chosen constraints.
58 for (dimension_type i = 0, end = y.con_sys.num_rows(); i < end; ++i) {
59 const Constraint& c = y.con_sys[i];
60 if (gen_sys.satisfied_by_all_generators(c)) {
61 cs_selection.insert(c);
62 }
63 }
64 }
65
66 void
67 PPL::Polyhedron
select_H79_constraints(const Polyhedron & y,Constraint_System & cs_selected,Constraint_System & cs_not_selected) const68 ::select_H79_constraints(const Polyhedron& y,
69 Constraint_System& cs_selected,
70 Constraint_System& cs_not_selected) const {
71 // Private method: the caller must ensure the following conditions
72 // (beside the inclusion `y <= x').
73 PPL_ASSERT(topology() == y.topology()
74 && topology() == cs_selected.topology()
75 && topology() == cs_not_selected.topology());
76 PPL_ASSERT(space_dim == y.space_dim);
77 PPL_ASSERT(!marked_empty()
78 && !has_pending_generators()
79 && constraints_are_up_to_date());
80 PPL_ASSERT(!y.marked_empty()
81 && !y.has_something_pending()
82 && y.constraints_are_minimized()
83 && y.generators_are_up_to_date());
84
85 // FIXME: this is a workaround for NNC polyhedra.
86 if (!y.is_necessarily_closed()) {
87 // Force strong minimization of constraints.
88 y.strongly_minimize_constraints();
89 // Recompute generators (without compromising constraint minimization).
90 y.update_generators();
91 }
92
93 // Obtain a sorted copy of `y.sat_g'.
94 if (!y.sat_g_is_up_to_date()) {
95 y.update_sat_g();
96 }
97 Bit_Matrix tmp_sat_g = y.sat_g;
98 // Remove from `tmp_sat_g' the rows corresponding to tautologies
99 // (i.e., the positivity or epsilon-bounding constraints):
100 // this is needed in order to widen the polyhedron and not the
101 // corresponding homogenized polyhedral cone.
102 const Constraint_System& y_cs = y.con_sys;
103 const dimension_type old_num_rows = y_cs.num_rows();
104 dimension_type num_rows = old_num_rows;
105 for (dimension_type i = 0; i < num_rows; ++i) {
106 if (y_cs[i].is_tautological()) {
107 using std::swap;
108 --num_rows;
109 swap(tmp_sat_g[i], tmp_sat_g[num_rows]);
110 }
111 }
112 tmp_sat_g.remove_trailing_rows(old_num_rows - num_rows);
113 tmp_sat_g.sort_rows();
114
115 // A constraint in `con_sys' is copied to `cs_selected'
116 // if its behavior with respect to `y.gen_sys' is the same
117 // as that of another constraint in `y.con_sys'.
118 // otherwise it is copied to `cs_not_selected'.
119 // Namely, we check whether the saturation row `buffer'
120 // (built starting from the given constraint and `y.gen_sys')
121 // is a row of the saturation matrix `tmp_sat_g'.
122
123 // CHECKME: the following comment is only applicable when `y.gen_sys'
124 // is minimized. In that case, the comment suggests that it would be
125 // possible to use a fast (but incomplete) redundancy test based on
126 // the number of saturators in `buffer'.
127 // NOTE: If the considered constraint of `con_sys' does not
128 // satisfy the saturation rule (see Section \ref prelims), then
129 // it will not appear in the resulting constraint system,
130 // because `tmp_sat_g' is built starting from a minimized polyhedron.
131
132 // The size of `buffer' will reach sat.num_columns() bits.
133 Bit_Row buffer;
134 // Note: the loop index `i' goes upward to avoid reversing
135 // the ordering of the chosen constraints.
136 for (dimension_type i = 0, end = con_sys.num_rows(); i < end; ++i) {
137 const Constraint& ci = con_sys[i];
138 // The saturation row `buffer' is built considering
139 // the `i'-th constraint of the polyhedron `x' and
140 // all the generators of the polyhedron `y'.
141 buffer.clear();
142 for (dimension_type j = y.gen_sys.num_rows(); j-- > 0; ) {
143 const int sp_sgn = Scalar_Products::sign(ci, y.gen_sys[j]);
144 // We are assuming that `y <= x'.
145 PPL_ASSERT(sp_sgn >= 0
146 || (!is_necessarily_closed()
147 && ci.is_strict_inequality()
148 && y.gen_sys[j].is_point()));
149 if (sp_sgn > 0) {
150 buffer.set(j);
151 }
152 }
153 // We check whether `buffer' is a row of `tmp_sat_g',
154 // exploiting its sortedness in order to have faster comparisons.
155 if (tmp_sat_g.sorted_contains(buffer)) {
156 cs_selected.insert(ci);
157 }
158 else {
159 cs_not_selected.insert(ci);
160 }
161 }
162 }
163
164 void
H79_widening_assign(const Polyhedron & y,unsigned * tp)165 PPL::Polyhedron::H79_widening_assign(const Polyhedron& y, unsigned* tp) {
166 Polyhedron& x = *this;
167 // Topology compatibility check.
168 const Topology topol = x.topology();
169 if (topol != y.topology()) {
170 throw_topology_incompatible("H79_widening_assign(y)", "y", y);
171 // Dimension-compatibility check.
172 }
173 if (x.space_dim != y.space_dim) {
174 throw_dimension_incompatible("H79_widening_assign(y)", "y", y);
175 }
176 // Assume `y' is contained in or equal to `x'.
177 PPL_EXPECT_HEAVY(copy_contains(x, y));
178
179 // If any argument is zero-dimensional or empty,
180 // the H79-widening behaves as the identity function.
181 if (x.space_dim == 0 || x.marked_empty() || y.marked_empty()) {
182 return;
183 }
184
185 // `y.gen_sys' should be in minimal form and
186 // `y.sat_g' should be up-to-date.
187 if (y.is_necessarily_closed()) {
188 if (!y.minimize()) {
189 // `y' is empty: the result is `x'.
190 return;
191 }
192 }
193 else {
194 // Dealing with a NNC polyhedron.
195 // To obtain a correct reasoning when comparing
196 // the constraints of `x' with the generators of `y',
197 // we enforce the inclusion relation holding between
198 // the two NNC polyhedra `x' and `y' (i.e., `y <= x')
199 // to also hold for the corresponding eps-representations:
200 // this is obtained by intersecting the two eps-representations.
201 Polyhedron& yy = const_cast<Polyhedron&>(y);
202 yy.intersection_assign(x);
203 if (yy.is_empty()) {
204 // The result is `x'.
205 return;
206 }
207 }
208
209 // If we only have the generators of `x' and the dimensions of
210 // the two polyhedra are the same, we can compute the standard
211 // widening by using the specification in [CousotH78], therefore
212 // avoiding converting from generators to constraints.
213 if (x.has_pending_generators() || !x.constraints_are_up_to_date()) {
214 Constraint_System CH78_cs(topol);
215 x.select_CH78_constraints(y, CH78_cs);
216
217 if (CH78_cs.num_rows() == y.con_sys.num_rows()) {
218 // Having selected all the constraints, the result is `y'.
219 x = y;
220 return;
221 }
222 // Otherwise, check if `x' and `y' have the same dimension.
223 // Note that `y.con_sys' is minimized and `CH78_cs' has no redundant
224 // constraints, since it is a subset of the former.
225 else if (CH78_cs.num_equalities() == y.con_sys.num_equalities()) {
226 // Let `x' be defined by the constraints in `CH78_cs'.
227 Polyhedron CH78(topol, x.space_dim, UNIVERSE);
228 CH78.add_recycled_constraints(CH78_cs);
229
230 // Check whether we are using the widening-with-tokens technique
231 // and there still are tokens available.
232 if (tp != 0 && *tp > 0) {
233 // There are tokens available. If `CH78' is not a subset of `x',
234 // then it is less precise and we use one of the available tokens.
235 if (!x.contains(CH78)) {
236 --(*tp);
237 }
238 }
239 else {
240 // No tokens.
241 x.m_swap(CH78);
242 }
243 PPL_ASSERT_HEAVY(x.OK(true));
244 return;
245 }
246 }
247
248 // As the dimension of `x' is strictly greater than the dimension of `y',
249 // we have to compute the standard widening by selecting a subset of
250 // the constraints of `x'.
251 // `x.con_sys' is just required to be up-to-date, because:
252 // - if `x.con_sys' is unsatisfiable, then by assumption
253 // also `y' is empty, so that the resulting polyhedron is `x';
254 // - redundant constraints in `x.con_sys' do not affect the result
255 // of the widening, because if they are selected they will be
256 // redundant even in the result.
257 if (has_pending_generators()) {
258 process_pending_generators();
259 }
260 else if (!x.constraints_are_up_to_date()) {
261 x.update_constraints();
262 }
263 // Copy into `H79_cs' the constraints of `x' that are common to `y',
264 // according to the definition of the H79 widening.
265 Constraint_System H79_cs(topol);
266 Constraint_System x_minus_H79_cs(topol);
267 x.select_H79_constraints(y, H79_cs, x_minus_H79_cs);
268
269 if (x_minus_H79_cs.has_no_rows()) {
270 // We selected all of the constraints of `x',
271 // thus the result of the widening is `x'.
272 return;
273 }
274 else {
275 // We selected a strict subset of the constraints of `x'.
276 // NOTE: as `x.con_sys' was not necessarily in minimal form,
277 // this does not imply that the result strictly includes `x'.
278 // Let `H79' be defined by the constraints in `H79_cs'.
279 Polyhedron H79(topol, x.space_dim, UNIVERSE);
280 H79.add_recycled_constraints(H79_cs);
281
282 // Check whether we are using the widening-with-tokens technique
283 // and there still are tokens available.
284 if (tp != 0 && *tp > 0) {
285 // There are tokens available. If `H79' is not a subset of `x',
286 // then it is less precise and we use one of the available tokens.
287 if (!x.contains(H79)) {
288 --(*tp);
289 }
290 }
291 else {
292 // No tokens.
293 x.m_swap(H79);
294 }
295 PPL_ASSERT_HEAVY(x.OK(true));
296 }
297 }
298
299 void
limited_H79_extrapolation_assign(const Polyhedron & y,const Constraint_System & cs,unsigned * tp)300 PPL::Polyhedron::limited_H79_extrapolation_assign(const Polyhedron& y,
301 const Constraint_System& cs,
302 unsigned* tp) {
303 Polyhedron& x = *this;
304
305 const dimension_type cs_num_rows = cs.num_rows();
306 // If `cs' is empty, we fall back to ordinary, non-limited widening.
307 if (cs_num_rows == 0) {
308 x.H79_widening_assign(y, tp);
309 return;
310 }
311
312 // Topology compatibility check.
313 if (x.is_necessarily_closed()) {
314 if (!y.is_necessarily_closed()) {
315 throw_topology_incompatible("limited_H79_extrapolation_assign(y, cs)",
316 "y", y);
317 }
318 if (cs.has_strict_inequalities()) {
319 throw_topology_incompatible("limited_H79_extrapolation_assign(y, cs)",
320 "cs", cs);
321 }
322 }
323 else if (y.is_necessarily_closed()) {
324 throw_topology_incompatible("limited_H79_extrapolation_assign(y, cs)",
325 "y", y);
326 }
327 // Dimension-compatibility check.
328 if (x.space_dim != y.space_dim) {
329 throw_dimension_incompatible("limited_H79_extrapolation_assign(y, cs)",
330 "y", y);
331 }
332 // `cs' must be dimension-compatible with the two polyhedra.
333 const dimension_type cs_space_dim = cs.space_dimension();
334 if (x.space_dim < cs_space_dim) {
335 throw_dimension_incompatible("limited_H79_extrapolation_assign(y, cs)",
336 "cs", cs);
337 }
338 // Assume `y' is contained in or equal to `x'.
339 PPL_EXPECT_HEAVY(copy_contains(x, y));
340
341 if (y.marked_empty()) {
342 return;
343 }
344 if (x.marked_empty()) {
345 return;
346 }
347 // The limited H79-widening between two polyhedra in a
348 // zero-dimensional space is a polyhedron in a zero-dimensional
349 // space, too.
350 if (x.space_dim == 0) {
351 return;
352 }
353
354 if (!y.minimize()) {
355 // We have just discovered that `y' is empty.
356 return;
357 }
358
359 // Update the generators of `x': these are used to select,
360 // from the constraints in `cs', those that must be added
361 // to the resulting polyhedron.
362 if ((x.has_pending_constraints() && !x.process_pending_constraints())
363 || (!x.generators_are_up_to_date() && !x.update_generators())) {
364 // We have just discovered that `x' is empty.
365 return;
366 }
367
368 Constraint_System new_cs;
369 // The constraints to be added must be satisfied by all the
370 // generators of `x'. We can disregard `y' because `y <= x'.
371 const Generator_System& x_gen_sys = x.gen_sys;
372 // Iterate upwards here so as to keep the relative ordering of constraints.
373 // Not really an issue: just aesthetics.
374 for (dimension_type i = 0; i < cs_num_rows; ++i) {
375 const Constraint& c = cs[i];
376 if (x_gen_sys.satisfied_by_all_generators(c)) {
377 new_cs.insert(c);
378 }
379 }
380 x.H79_widening_assign(y, tp);
381 x.add_recycled_constraints(new_cs);
382 PPL_ASSERT_HEAVY(OK());
383 }
384
385 void
bounded_H79_extrapolation_assign(const Polyhedron & y,const Constraint_System & cs,unsigned * tp)386 PPL::Polyhedron::bounded_H79_extrapolation_assign(const Polyhedron& y,
387 const Constraint_System& cs,
388 unsigned* tp) {
389 Rational_Box x_box(*this, ANY_COMPLEXITY);
390 const Rational_Box y_box(y, ANY_COMPLEXITY);
391 x_box.CC76_widening_assign(y_box);
392 limited_H79_extrapolation_assign(y, cs, tp);
393 Constraint_System x_box_cs = x_box.constraints();
394 add_recycled_constraints(x_box_cs);
395 }
396
397 bool
398 PPL::Polyhedron
BHRZ03_combining_constraints(const Polyhedron & y,const BHRZ03_Certificate & y_cert,const Polyhedron & H79,const Constraint_System & x_minus_H79_cs)399 ::BHRZ03_combining_constraints(const Polyhedron& y,
400 const BHRZ03_Certificate& y_cert,
401 const Polyhedron& H79,
402 const Constraint_System& x_minus_H79_cs) {
403 Polyhedron& x = *this;
404 // It is assumed that `y <= x <= H79'.
405 PPL_ASSERT(x.topology() == y.topology()
406 && x.topology() == H79.topology()
407 && x.topology() == x_minus_H79_cs.topology());
408 PPL_ASSERT(x.space_dim == y.space_dim
409 && x.space_dim == H79.space_dim
410 && x.space_dim == x_minus_H79_cs.space_dimension());
411 PPL_ASSERT(!x.marked_empty() && !x.has_something_pending()
412 && x.constraints_are_minimized() && x.generators_are_minimized());
413 PPL_ASSERT(!y.marked_empty() && !y.has_something_pending()
414 && y.constraints_are_minimized() && y.generators_are_minimized());
415 PPL_ASSERT(!H79.marked_empty() && !H79.has_something_pending()
416 && H79.constraints_are_minimized() && H79.generators_are_minimized());
417
418 // We will choose from `x_minus_H79_cs' many subsets of constraints,
419 // that will be collected (one at a time) in `combining_cs'.
420 // For each group collected, we compute an average constraint,
421 // that will be stored in `new_cs'.
422
423 // There is no point in applying this technique when `x_minus_H79_cs'
424 // has one constraint at most (no ``new'' constraint can be computed).
425 const dimension_type x_minus_H79_cs_num_rows = x_minus_H79_cs.num_rows();
426 if (x_minus_H79_cs_num_rows <= 1) {
427 return false;
428 }
429
430 const Topology topol = x.topology();
431 Constraint_System combining_cs(topol);
432 Constraint_System new_cs(topol);
433
434 // Consider the points that belong to both `x.gen_sys' and `y.gen_sys'.
435 // For NNC polyhedra, the role of points is played by closure points.
436 const bool closed = x.is_necessarily_closed();
437 for (dimension_type i = y.gen_sys.num_rows(); i-- > 0; ) {
438 const Generator& g = y.gen_sys[i];
439 if ((g.is_point() && closed) || (g.is_closure_point() && !closed)) {
440 // If in `H79.con_sys' there is already an inequality constraint
441 // saturating this point, then there is no need to produce another
442 // constraint.
443 bool lies_on_the_boundary_of_H79 = false;
444 const Constraint_System& H79_cs = H79.con_sys;
445 for (dimension_type j = H79_cs.num_rows(); j-- > 0; ) {
446 const Constraint& c = H79_cs[j];
447 if (c.is_inequality() && Scalar_Products::sign(c, g) == 0) {
448 lies_on_the_boundary_of_H79 = true;
449 break;
450 }
451 }
452 if (lies_on_the_boundary_of_H79) {
453 continue;
454 }
455
456 // Consider all the constraints in `x_minus_H79_cs'
457 // that are saturated by the point `g'.
458 combining_cs.clear();
459 for (dimension_type j = x_minus_H79_cs_num_rows; j-- > 0; ) {
460 const Constraint& c = x_minus_H79_cs[j];
461 if (Scalar_Products::sign(c, g) == 0) {
462 combining_cs.insert(c);
463 }
464 }
465 // Build a new constraint by combining all the chosen constraints.
466 const dimension_type combining_cs_num_rows = combining_cs.num_rows();
467 if (combining_cs_num_rows > 0) {
468 if (combining_cs_num_rows == 1) {
469 // No combination is needed.
470 new_cs.insert(combining_cs[0]);
471 }
472 else {
473 Linear_Expression e(0);
474 bool strict_inequality = false;
475 for (dimension_type h = combining_cs_num_rows; h-- > 0; ) {
476 if (combining_cs[h].is_strict_inequality()) {
477 strict_inequality = true;
478 }
479 e += Linear_Expression(combining_cs[h].expression());
480 }
481
482 if (!e.all_homogeneous_terms_are_zero()) {
483 if (strict_inequality) {
484 new_cs.insert(e > 0);
485 }
486 else {
487 new_cs.insert(e >= 0);
488 }
489 }
490 }
491 }
492 }
493 }
494
495 // If none of the collected constraints strictly intersects `H79',
496 // then the technique was unsuccessful.
497 bool improves_upon_H79 = false;
498 const Poly_Con_Relation si = Poly_Con_Relation::strictly_intersects();
499 for (dimension_type i = new_cs.num_rows(); i-- > 0; ) {
500 if (H79.relation_with(new_cs[i]) == si) {
501 improves_upon_H79 = true;
502 break;
503 }
504 }
505 if (!improves_upon_H79) {
506 return false;
507 }
508
509 // The resulting polyhedron is obtained by adding the constraints
510 // in `new_cs' to polyhedron `H79'.
511 Polyhedron result = H79;
512 result.add_recycled_constraints(new_cs);
513 // Force minimization.
514 result.minimize();
515
516 // Check for stabilization with respect to `y_cert' and improvement
517 // over `H79'.
518 if (y_cert.is_stabilizing(result) && !result.contains(H79)) {
519 // The technique was successful.
520 x.m_swap(result);
521 PPL_ASSERT_HEAVY(x.OK(true));
522 return true;
523 }
524 else {
525 // The technique was unsuccessful.
526 return false;
527 }
528 }
529
530 bool
BHRZ03_evolving_points(const Polyhedron & y,const BHRZ03_Certificate & y_cert,const Polyhedron & H79)531 PPL::Polyhedron::BHRZ03_evolving_points(const Polyhedron& y,
532 const BHRZ03_Certificate& y_cert,
533 const Polyhedron& H79) {
534 Polyhedron& x = *this;
535 // It is assumed that `y <= x <= H79'.
536 PPL_ASSERT(x.topology() == y.topology()
537 && x.topology() == H79.topology());
538 PPL_ASSERT(x.space_dim == y.space_dim
539 && x.space_dim == H79.space_dim);
540 PPL_ASSERT(!x.marked_empty() && !x.has_something_pending()
541 && x.constraints_are_minimized() && x.generators_are_minimized());
542 PPL_ASSERT(!y.marked_empty() && !y.has_something_pending()
543 && y.constraints_are_minimized() && y.generators_are_minimized());
544 PPL_ASSERT(!H79.marked_empty() && !H79.has_something_pending()
545 && H79.constraints_are_minimized() && H79.generators_are_minimized());
546
547 // For each point in `x.gen_sys' that is not in `y',
548 // this technique tries to identify a set of rays that:
549 // - are included in polyhedron `H79';
550 // - when added to `y' will subsume the point.
551 Generator_System candidate_rays;
552
553 const dimension_type x_gen_sys_num_rows = x.gen_sys.num_rows();
554 const dimension_type y_gen_sys_num_rows = y.gen_sys.num_rows();
555 const bool closed = x.is_necessarily_closed();
556 for (dimension_type i = x_gen_sys_num_rows; i-- > 0; ) {
557 const Generator& g1 = x.gen_sys[i];
558 // For C polyhedra, we choose a point of `x.gen_sys'
559 // that is not included in `y'.
560 // In the case of NNC polyhedra, we can restrict attention to
561 // closure points (considering also points will only add redundancy).
562 if (((g1.is_point() && closed) || (g1.is_closure_point() && !closed))
563 && y.relation_with(g1) == Poly_Gen_Relation::nothing()) {
564 // For each point (resp., closure point) `g2' in `y.gen_sys',
565 // where `g1' and `g2' are different,
566 // build the candidate ray `g1 - g2'.
567 for (dimension_type j = y_gen_sys_num_rows; j-- > 0; ) {
568 const Generator& g2 = y.gen_sys[j];
569 if ((g2.is_point() && closed)
570 || (g2.is_closure_point() && !closed)) {
571 PPL_ASSERT(compare(g1, g2) != 0);
572 Generator ray_from_g2_to_g1 = g1;
573 ray_from_g2_to_g1.linear_combine(g2, 0);
574 candidate_rays.insert(ray_from_g2_to_g1);
575 }
576 }
577 }
578 }
579
580 // Be non-intrusive.
581 Polyhedron result = x;
582 result.add_recycled_generators(candidate_rays);
583 result.intersection_assign(H79);
584 // Force minimization.
585 result.minimize();
586
587 // Check for stabilization with respect to `y_cert' and improvement
588 // over `H79'.
589 if (y_cert.is_stabilizing(result) && !result.contains(H79)) {
590 // The technique was successful.
591 x.m_swap(result);
592 PPL_ASSERT_HEAVY(x.OK(true));
593 return true;
594 }
595 else {
596 // The technique was unsuccessful.
597 return false;
598 }
599 }
600
601 void
modify_according_to_evolution(Linear_Expression & ray,const Linear_Expression & x,const Linear_Expression & y)602 PPL::Polyhedron::modify_according_to_evolution(Linear_Expression& ray,
603 const Linear_Expression& x,
604 const Linear_Expression& y) {
605 PPL_DIRTY_TEMP_COEFFICIENT(tmp);
606 std::deque<bool> considered(x.space_dimension());
607 Linear_Expression::const_iterator x_end = x.end();
608 Linear_Expression::const_iterator y_end = y.end();
609 Linear_Expression::const_iterator y_k = y.begin();
610 for (Linear_Expression::const_iterator x_k = x.begin();
611 x_k != x_end; ++x_k) {
612 const Variable k_var = x_k.variable();
613 const dimension_type k = k_var.id();
614 if (considered[k]) {
615 continue;
616 }
617
618 while (y_k != y_end && y_k.variable().id() < k) {
619 ++y_k;
620 }
621
622 if (y_k == y_end) {
623 break;
624 }
625
626 const Variable y_k_var = y_k.variable();
627
628 // Note that y_k_var.id() may be greater than k.
629
630 Linear_Expression::const_iterator y_h = y_k;
631 // Do *not* increment y_h, since it may be after k already.
632 Linear_Expression::const_iterator x_h = x_k;
633 ++x_h;
634 for ( ; x_h != x_end; ++x_h) {
635 const dimension_type h = x_h.variable().id();
636 if (considered[h]) {
637 continue;
638 }
639
640 while (y_h != y_end && y_h.variable().id() < h) {
641 ++y_h;
642 }
643
644 // Note that y_h may be y_end, and y_h.variable().id() may not be k.
645
646 if (y_h != y_end && y_h.variable().id() == h) {
647 tmp = (*x_k) * (*y_h);
648 }
649 else {
650 tmp = 0;
651 }
652
653 if (y_k_var.id() == k) {
654 // The following line optimizes the computation of
655 // <CODE> tmp -= x[h] * y[k]; </CODE>
656 Parma_Polyhedra_Library::sub_mul_assign(tmp, *x_h, *y_k);
657 }
658
659 const int clockwise = sgn(tmp);
660 const int first_or_third_quadrant = sgn(*x_k) * sgn(*x_h);
661 switch (clockwise * first_or_third_quadrant) {
662 case -1:
663 ray.set_coefficient(k_var, Coefficient_zero());
664 considered[k] = true;
665 break;
666 case 1:
667 ray.set_coefficient(Variable(h), Coefficient_zero());
668 considered[h] = true;
669 break;
670 default:
671 break;
672 }
673 }
674 }
675 ray.normalize();
676 }
677
678 bool
BHRZ03_evolving_rays(const Polyhedron & y,const BHRZ03_Certificate & y_cert,const Polyhedron & H79)679 PPL::Polyhedron::BHRZ03_evolving_rays(const Polyhedron& y,
680 const BHRZ03_Certificate& y_cert,
681 const Polyhedron& H79) {
682 Polyhedron& x = *this;
683 // It is assumed that `y <= x <= H79'.
684 PPL_ASSERT(x.topology() == y.topology()
685 && x.topology() == H79.topology());
686 PPL_ASSERT(x.space_dim == y.space_dim
687 && x.space_dim == H79.space_dim);
688 PPL_ASSERT(!x.marked_empty() && !x.has_something_pending()
689 && x.constraints_are_minimized() && x.generators_are_minimized());
690 PPL_ASSERT(!y.marked_empty() && !y.has_something_pending()
691 && y.constraints_are_minimized() && y.generators_are_minimized());
692 PPL_ASSERT(!H79.marked_empty() && !H79.has_something_pending()
693 && H79.constraints_are_minimized() && H79.generators_are_minimized());
694
695 const dimension_type x_gen_sys_num_rows = x.gen_sys.num_rows();
696 const dimension_type y_gen_sys_num_rows = y.gen_sys.num_rows();
697
698 // Candidate rays are kept in a temporary generator system.
699 Generator_System candidate_rays;
700 for (dimension_type i = x_gen_sys_num_rows; i-- > 0; ) {
701 const Generator& x_g = x.gen_sys[i];
702 // We choose a ray of `x' that does not belong to `y'.
703 if (x_g.is_ray() && y.relation_with(x_g) == Poly_Gen_Relation::nothing()) {
704 for (dimension_type j = y_gen_sys_num_rows; j-- > 0; ) {
705 const Generator& y_g = y.gen_sys[j];
706 if (y_g.is_ray()) {
707 Generator new_ray(x_g);
708 // Modify `new_ray' according to the evolution of `x_g' with
709 // respect to `y_g'.
710 modify_according_to_evolution(new_ray.expr, x_g.expr, y_g.expr);
711 PPL_ASSERT(new_ray.OK());
712 candidate_rays.insert(new_ray);
713 }
714 }
715 }
716 }
717
718 // If there are no candidate rays, we cannot obtain stabilization.
719 if (candidate_rays.has_no_rows()) {
720 return false;
721 }
722
723 // Be non-intrusive.
724 Polyhedron result = x;
725 result.add_recycled_generators(candidate_rays);
726 result.intersection_assign(H79);
727 // Force minimization.
728 result.minimize();
729
730 // Check for stabilization with respect to `y' and improvement over `H79'.
731 if (y_cert.is_stabilizing(result) && !result.contains(H79)) {
732 // The technique was successful.
733 x.m_swap(result);
734 PPL_ASSERT_HEAVY(x.OK(true));
735 return true;
736 }
737 else {
738 // The technique was unsuccessful.
739 return false;
740 }
741 }
742
743 void
BHRZ03_widening_assign(const Polyhedron & y,unsigned * tp)744 PPL::Polyhedron::BHRZ03_widening_assign(const Polyhedron& y, unsigned* tp) {
745 Polyhedron& x = *this;
746 // Topology compatibility check.
747 if (x.topology() != y.topology()) {
748 throw_topology_incompatible("BHRZ03_widening_assign(y)", "y", y);
749 }
750 // Dimension-compatibility check.
751 if (x.space_dim != y.space_dim) {
752 throw_dimension_incompatible("BHRZ03_widening_assign(y)", "y", y);
753 }
754
755 // Assume `y' is contained in or equal to `x'.
756 PPL_EXPECT_HEAVY(copy_contains(x, y));
757
758 // If any argument is zero-dimensional or empty,
759 // the BHRZ03-widening behaves as the identity function.
760 if (x.space_dim == 0 || x.marked_empty() || y.marked_empty()) {
761 return;
762 }
763
764 // `y.con_sys' and `y.gen_sys' should be in minimal form.
765 if (!y.minimize()) {
766 // `y' is empty: the result is `x'.
767 return;
768 }
769 // `x.con_sys' and `x.gen_sys' should be in minimal form.
770 x.minimize();
771
772 // Compute certificate info for polyhedron `y'.
773 const BHRZ03_Certificate y_cert(y);
774
775 // If the iteration is stabilizing, the resulting polyhedron is `x'.
776 // At this point, also check if the two polyhedra are the same
777 // (exploiting the knowledge that `y <= x').
778 if (y_cert.is_stabilizing(x) || y.contains(x)) {
779 PPL_ASSERT_HEAVY(OK());
780 return;
781 }
782
783 // Here the iteration is not immediately stabilizing.
784 // If we are using the widening-with-tokens technique and
785 // there are tokens available, use one of them and return `x'.
786 if (tp != 0 && *tp > 0) {
787 --(*tp);
788 PPL_ASSERT_HEAVY(OK());
789 return;
790 }
791
792 // Copy into `H79_cs' the constraints that are common to `x' and `y',
793 // according to the definition of the H79 widening.
794 // The other ones are copied into `x_minus_H79_cs'.
795 const Topology topol = x.topology();
796 Constraint_System H79_cs(topol);
797 Constraint_System x_minus_H79_cs(topol);
798 x.select_H79_constraints(y, H79_cs, x_minus_H79_cs);
799
800 // We cannot have selected all of the rows, since otherwise
801 // the iteration should have been immediately stabilizing.
802 PPL_ASSERT(!x_minus_H79_cs.has_no_rows());
803 // Be careful to obtain the right space dimension
804 // (because `H79_cs' may be empty).
805 Polyhedron H79(topol, x.space_dim, UNIVERSE);
806 H79.add_recycled_constraints(H79_cs);
807 // Force minimization.
808 H79.minimize();
809
810 // NOTE: none of the following widening heuristics is intrusive:
811 // they will modify `x' only when returning successfully.
812 if (x.BHRZ03_combining_constraints(y, y_cert, H79, x_minus_H79_cs)) {
813 return;
814 }
815
816 PPL_ASSERT_HEAVY(H79.OK() && x.OK() && y.OK());
817
818 if (x.BHRZ03_evolving_points(y, y_cert, H79)) {
819 return;
820 }
821
822 PPL_ASSERT_HEAVY(H79.OK() && x.OK() && y.OK());
823
824 if (x.BHRZ03_evolving_rays(y, y_cert, H79)) {
825 return;
826 }
827
828 PPL_ASSERT_HEAVY(H79.OK() && x.OK() && y.OK());
829
830 // No previous technique was successful: fall back to the H79 widening.
831 x.m_swap(H79);
832 PPL_ASSERT_HEAVY(x.OK(true));
833
834 // The H79 widening is always stabilizing.
835 PPL_ASSERT(y_cert.is_stabilizing(x));
836 }
837
838 void
839 PPL::Polyhedron
limited_BHRZ03_extrapolation_assign(const Polyhedron & y,const Constraint_System & cs,unsigned * tp)840 ::limited_BHRZ03_extrapolation_assign(const Polyhedron& y,
841 const Constraint_System& cs,
842 unsigned* tp) {
843 Polyhedron& x = *this;
844 const dimension_type cs_num_rows = cs.num_rows();
845 // If `cs' is empty, we fall back to ordinary, non-limited widening.
846 if (cs_num_rows == 0) {
847 x.BHRZ03_widening_assign(y, tp);
848 return;
849 }
850
851 // Topology compatibility check.
852 if (x.is_necessarily_closed()) {
853 if (!y.is_necessarily_closed()) {
854 throw_topology_incompatible("limited_BHRZ03_extrapolation_assign(y, cs)",
855 "y", y);
856 }
857 if (cs.has_strict_inequalities()) {
858 throw_topology_incompatible("limited_BHRZ03_extrapolation_assign(y, cs)",
859 "cs", cs);
860 }
861 }
862 else if (y.is_necessarily_closed()) {
863 throw_topology_incompatible("limited_BHRZ03_extrapolation_assign(y, cs)",
864 "y", y);
865 }
866 // Dimension-compatibility check.
867 if (x.space_dim != y.space_dim) {
868 throw_dimension_incompatible("limited_BHRZ03_extrapolation_assign(y, cs)",
869 "y", y);
870 }
871 // `cs' must be dimension-compatible with the two polyhedra.
872 const dimension_type cs_space_dim = cs.space_dimension();
873 if (x.space_dim < cs_space_dim) {
874 throw_dimension_incompatible("limited_BHRZ03_extrapolation_assign(y, cs)",
875 "cs", cs);
876 }
877
878 // Assume `y' is contained in or equal to `x'.
879 PPL_EXPECT_HEAVY(copy_contains(x, y));
880
881 if (y.marked_empty()) {
882 return;
883 }
884 if (x.marked_empty()) {
885 return;
886 }
887
888 // The limited BHRZ03-widening between two polyhedra in a
889 // zero-dimensional space is a polyhedron in a zero-dimensional
890 // space, too.
891 if (x.space_dim == 0) {
892 return;
893 }
894
895 if (!y.minimize()) {
896 // We have just discovered that `y' is empty.
897 return;
898 }
899
900 // Update the generators of `x': these are used to select,
901 // from the constraints in `cs', those that must be added
902 // to the resulting polyhedron.
903 if ((x.has_pending_constraints() && !x.process_pending_constraints())
904 || (!x.generators_are_up_to_date() && !x.update_generators())) {
905 // We have just discovered that `x' is empty.
906 return;
907 }
908
909 Constraint_System new_cs;
910 // The constraints to be added must be satisfied by all the
911 // generators of `x'. We can disregard `y' because `y <= x'.
912 const Generator_System& x_gen_sys = x.gen_sys;
913 // Iterate upwards here so as to keep the relative ordering of constraints.
914 // Not really an issue: just aesthetics.
915 for (dimension_type i = 0; i < cs_num_rows; ++i) {
916 const Constraint& c = cs[i];
917 if (x_gen_sys.satisfied_by_all_generators(c)) {
918 new_cs.insert(c);
919 }
920 }
921 x.BHRZ03_widening_assign(y, tp);
922 x.add_recycled_constraints(new_cs);
923 PPL_ASSERT_HEAVY(OK());
924 }
925
926 void
927 PPL::Polyhedron
bounded_BHRZ03_extrapolation_assign(const Polyhedron & y,const Constraint_System & cs,unsigned * tp)928 ::bounded_BHRZ03_extrapolation_assign(const Polyhedron& y,
929 const Constraint_System& cs,
930 unsigned* tp) {
931 Rational_Box x_box(*this, ANY_COMPLEXITY);
932 const Rational_Box y_box(y, ANY_COMPLEXITY);
933 x_box.CC76_widening_assign(y_box);
934 limited_BHRZ03_extrapolation_assign(y, cs, tp);
935 Constraint_System x_box_cs = x_box.constraints();
936 add_recycled_constraints(x_box_cs);
937 }
938