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