|
PPL
0.12.1
|
00001 /* Polyhedron class implementation (non-inline public functions). 00002 Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it> 00003 Copyright (C) 2010-2012 BUGSENG srl (http://bugseng.com) 00004 00005 This file is part of the Parma Polyhedra Library (PPL). 00006 00007 The PPL is free software; you can redistribute it and/or modify it 00008 under the terms of the GNU General Public License as published by the 00009 Free Software Foundation; either version 3 of the License, or (at your 00010 option) any later version. 00011 00012 The PPL is distributed in the hope that it will be useful, but WITHOUT 00013 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 00015 for more details. 00016 00017 You should have received a copy of the GNU General Public License 00018 along with this program; if not, write to the Free Software Foundation, 00019 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA. 00020 00021 For the most up-to-date information see the Parma Polyhedra Library 00022 site: http://bugseng.com/products/ppl/ . */ 00023 00024 #include "ppl-config.h" 00025 #include "Polyhedron.defs.hh" 00026 #include "C_Polyhedron.defs.hh" 00027 #include "NNC_Polyhedron.defs.hh" 00028 #include "Scalar_Products.defs.hh" 00029 #include "MIP_Problem.defs.hh" 00030 #include "wrap_assign.hh" 00031 #include "assert.hh" 00032 #include <cstdlib> 00033 #include <iostream> 00034 00035 #ifndef ENSURE_SORTEDNESS 00036 #define ENSURE_SORTEDNESS 0 00037 #endif 00038 00039 namespace PPL = Parma_Polyhedra_Library; 00040 00041 PPL::dimension_type* PPL::Polyhedron::simplify_num_saturators_p = 0; 00042 00043 size_t PPL::Polyhedron::simplify_num_saturators_size = 0; 00044 00045 void 00046 PPL::Polyhedron::initialize() { 00047 PPL_ASSERT(simplify_num_saturators_p == 0); 00048 PPL_ASSERT(simplify_num_saturators_size == 0); 00049 simplify_num_saturators_p = new dimension_type[simplify_num_saturators_size]; 00050 } 00051 00052 void 00053 PPL::Polyhedron::finalize() { 00054 delete [] simplify_num_saturators_p; 00055 simplify_num_saturators_p = 0; 00056 simplify_num_saturators_size = 0; 00057 } 00058 00059 PPL::dimension_type 00060 PPL::Polyhedron::affine_dimension() const { 00061 if (is_empty()) 00062 return 0; 00063 00064 const Constraint_System& cs = minimized_constraints(); 00065 dimension_type d = space_dim; 00066 for (Constraint_System::const_iterator i = cs.begin(), 00067 cs_end = cs.end(); i != cs_end; ++i) 00068 if (i->is_equality()) 00069 --d; 00070 return d; 00071 } 00072 00073 const PPL::Constraint_System& 00074 PPL::Polyhedron::constraints() const { 00075 if (marked_empty()) { 00076 // We want `con_sys' to only contain the unsatisfiable constraint 00077 // of the appropriate dimension. 00078 if (con_sys.has_no_rows()) { 00079 // The 0-dim unsatisfiable constraint is extended to 00080 // the appropriate dimension and then stored in `con_sys'. 00081 Constraint_System unsat_cs = Constraint_System::zero_dim_empty(); 00082 unsat_cs.adjust_topology_and_space_dimension(topology(), space_dim); 00083 const_cast<Constraint_System&>(con_sys).m_swap(unsat_cs); 00084 } 00085 else { 00086 // Checking that `con_sys' contains the right thing. 00087 PPL_ASSERT(con_sys.space_dimension() == space_dim); 00088 PPL_ASSERT(con_sys.num_rows() == 1); 00089 PPL_ASSERT(con_sys[0].is_inconsistent()); 00090 } 00091 return con_sys; 00092 } 00093 00094 if (space_dim == 0) { 00095 // Zero-dimensional universe. 00096 PPL_ASSERT(con_sys.num_rows() == 0 && con_sys.num_columns() == 0); 00097 return con_sys; 00098 } 00099 00100 // If the polyhedron has pending generators, we process them to obtain 00101 // the constraints. No processing is needed if the polyhedron has 00102 // pending constraints. 00103 if (has_pending_generators()) 00104 process_pending_generators(); 00105 else if (!constraints_are_up_to_date()) 00106 update_constraints(); 00107 00108 // TODO: reconsider whether to really sort constraints at this stage. 00109 #if ENSURE_SORTEDNESS 00110 // We insist in returning a sorted system of constraints, 00111 // but sorting is useless if there are pending constraints. 00112 if (!has_pending_constraints()) 00113 obtain_sorted_constraints(); 00114 #endif 00115 return con_sys; 00116 } 00117 00118 const PPL::Constraint_System& 00119 PPL::Polyhedron::minimized_constraints() const { 00120 // `minimize()' or `strongly_minimize_constraints()' 00121 // will process any pending constraints or generators. 00122 if (is_necessarily_closed()) 00123 minimize(); 00124 else 00125 strongly_minimize_constraints(); 00126 return constraints(); 00127 } 00128 00129 const PPL::Generator_System& 00130 PPL::Polyhedron::generators() const { 00131 if (marked_empty()) { 00132 PPL_ASSERT(gen_sys.has_no_rows()); 00133 // We want `gen_sys' to have the appropriate space dimension, 00134 // even though it is an empty generator system. 00135 if (gen_sys.space_dimension() != space_dim) { 00136 Generator_System gs; 00137 gs.adjust_topology_and_space_dimension(topology(), space_dim); 00138 const_cast<Generator_System&>(gen_sys).m_swap(gs); 00139 } 00140 return gen_sys; 00141 } 00142 00143 if (space_dim == 0) { 00144 PPL_ASSERT(gen_sys.num_rows() == 0 && gen_sys.num_columns() == 0); 00145 return Generator_System::zero_dim_univ(); 00146 } 00147 00148 // If the polyhedron has pending constraints, we process them to obtain 00149 // the generators (we may discover that the polyhedron is empty). 00150 // No processing is needed if the polyhedron has pending generators. 00151 if ((has_pending_constraints() && !process_pending_constraints()) 00152 || (!generators_are_up_to_date() && !update_generators())) { 00153 // We have just discovered that `*this' is empty. 00154 PPL_ASSERT(gen_sys.has_no_rows()); 00155 // We want `gen_sys' to have the appropriate space dimension, 00156 // even though it is an empty generator system. 00157 if (gen_sys.space_dimension() != space_dim) { 00158 Generator_System gs; 00159 gs.adjust_topology_and_space_dimension(topology(), space_dim); 00160 const_cast<Generator_System&>(gen_sys).m_swap(gs); 00161 } 00162 return gen_sys; 00163 } 00164 00165 // TODO: reconsider whether to really sort generators at this stage. 00166 #if ENSURE_SORTEDNESS 00167 // We insist in returning a sorted system of generators, 00168 // but sorting is useless if there are pending generators. 00169 if (!has_pending_generators()) 00170 obtain_sorted_generators(); 00171 #else 00172 // In the case of an NNC polyhedron, if the generator system is fully 00173 // minimized (i.e., minimized and with no pending generator), then 00174 // return a sorted system of generators: this is needed so that the 00175 // const_iterator could correctly filter out the matched closure points. 00176 if (!is_necessarily_closed() 00177 && generators_are_minimized() && !has_pending_generators()) 00178 obtain_sorted_generators(); 00179 #endif 00180 return gen_sys; 00181 } 00182 00183 const PPL::Generator_System& 00184 PPL::Polyhedron::minimized_generators() const { 00185 // `minimize()' or `strongly_minimize_generators()' 00186 // will process any pending constraints or generators. 00187 if (is_necessarily_closed()) 00188 minimize(); 00189 else 00190 strongly_minimize_generators(); 00191 // Note: calling generators() on a strongly minimized NNC generator 00192 // system will also ensure sortedness, which is required to correctly 00193 // filter away the matched closure points. 00194 return generators(); 00195 } 00196 00197 PPL::Poly_Con_Relation 00198 PPL::Polyhedron::relation_with(const Constraint& c) const { 00199 // Dimension-compatibility check. 00200 if (space_dim < c.space_dimension()) 00201 throw_dimension_incompatible("relation_with(c)", "c", c); 00202 00203 if (marked_empty()) 00204 return Poly_Con_Relation::saturates() 00205 && Poly_Con_Relation::is_included() 00206 && Poly_Con_Relation::is_disjoint(); 00207 00208 if (space_dim == 0) { 00209 if (c.is_inconsistent()) 00210 if (c.is_strict_inequality() && c.inhomogeneous_term() == 0) 00211 // The constraint 0 > 0 implicitly defines the hyperplane 0 = 0; 00212 // thus, the zero-dimensional point also saturates it. 00213 return Poly_Con_Relation::saturates() 00214 && Poly_Con_Relation::is_disjoint(); 00215 else 00216 return Poly_Con_Relation::is_disjoint(); 00217 else if (c.is_equality() || c.inhomogeneous_term() == 0) 00218 return Poly_Con_Relation::saturates() 00219 && Poly_Con_Relation::is_included(); 00220 else 00221 // The zero-dimensional point saturates 00222 // neither the positivity constraint 1 >= 0, 00223 // nor the strict positivity constraint 1 > 0. 00224 return Poly_Con_Relation::is_included(); 00225 } 00226 00227 if ((has_pending_constraints() && !process_pending_constraints()) 00228 || (!generators_are_up_to_date() && !update_generators())) 00229 // The polyhedron is empty. 00230 return Poly_Con_Relation::saturates() 00231 && Poly_Con_Relation::is_included() 00232 && Poly_Con_Relation::is_disjoint(); 00233 00234 return gen_sys.relation_with(c); 00235 } 00236 00237 PPL::Poly_Gen_Relation 00238 PPL::Polyhedron::relation_with(const Generator& g) const { 00239 // Dimension-compatibility check. 00240 if (space_dim < g.space_dimension()) 00241 throw_dimension_incompatible("relation_with(g)", "g", g); 00242 00243 // The empty polyhedron cannot subsume a generator. 00244 if (marked_empty()) 00245 return Poly_Gen_Relation::nothing(); 00246 00247 // A universe polyhedron in a zero-dimensional space subsumes 00248 // all the generators of a zero-dimensional space. 00249 if (space_dim == 0) 00250 return Poly_Gen_Relation::subsumes(); 00251 00252 if (has_pending_generators()) 00253 process_pending_generators(); 00254 else if (!constraints_are_up_to_date()) 00255 update_constraints(); 00256 00257 return 00258 con_sys.satisfies_all_constraints(g) 00259 ? Poly_Gen_Relation::subsumes() 00260 : Poly_Gen_Relation::nothing(); 00261 } 00262 00263 PPL::Poly_Con_Relation 00264 PPL::Polyhedron::relation_with(const Congruence& cg) const { 00265 dimension_type cg_space_dim = cg.space_dimension(); 00266 // Dimension-compatibility check. 00267 if (space_dim < cg_space_dim) 00268 throw_dimension_incompatible("relation_with(cg)", "cg", cg); 00269 00270 if (cg.is_equality()) { 00271 const Constraint c(cg); 00272 return relation_with(c); 00273 } 00274 00275 if (marked_empty()) 00276 return Poly_Con_Relation::saturates() 00277 && Poly_Con_Relation::is_included() 00278 && Poly_Con_Relation::is_disjoint(); 00279 00280 if (space_dim == 0) { 00281 if (cg.is_inconsistent()) 00282 return Poly_Con_Relation::is_disjoint(); 00283 else 00284 return Poly_Con_Relation::saturates() 00285 && Poly_Con_Relation::is_included(); 00286 } 00287 00288 if ((has_pending_constraints() && !process_pending_constraints()) 00289 || (!generators_are_up_to_date() && !update_generators())) 00290 // The polyhedron is empty. 00291 return Poly_Con_Relation::saturates() 00292 && Poly_Con_Relation::is_included() 00293 && Poly_Con_Relation::is_disjoint(); 00294 00295 // Build the equality corresponding to the congruence (ignoring the modulus). 00296 Linear_Expression expr = Linear_Expression(cg); 00297 Constraint c(expr == 0); 00298 00299 // The polyhedron is non-empty so that there exists a point. 00300 // For an arbitrary generator point, compute the scalar product with 00301 // the equality. 00302 PPL_DIRTY_TEMP_COEFFICIENT(sp_point); 00303 for (Generator_System::const_iterator gs_i = gen_sys.begin(), 00304 gs_end = gen_sys.end(); gs_i != gs_end; ++gs_i) { 00305 if (gs_i->is_point()) { 00306 Scalar_Products::assign(sp_point, c, *gs_i); 00307 expr -= sp_point; 00308 break; 00309 } 00310 } 00311 00312 // Find two hyperplanes that satisfy the congruence and are near to 00313 // the generating point (so that the point lies on or between these 00314 // two hyperplanes). 00315 // Then use the relations between the polyhedron and the halfspaces 00316 // corresponding to the hyperplanes to determine the result. 00317 00318 // Compute the distance from the point to an hyperplane. 00319 const Coefficient& modulus = cg.modulus(); 00320 PPL_DIRTY_TEMP_COEFFICIENT(signed_distance); 00321 signed_distance = sp_point % modulus; 00322 if (signed_distance == 0) 00323 // The point is lying on the hyperplane. 00324 return relation_with(expr == 0); 00325 else 00326 // The point is not lying on the hyperplane. 00327 expr += signed_distance; 00328 00329 // Build first halfspace constraint. 00330 const bool positive = (signed_distance > 0); 00331 Constraint first_halfspace = positive ? (expr >= 0) : (expr <= 0); 00332 00333 Poly_Con_Relation first_rels = relation_with(first_halfspace); 00334 PPL_ASSERT(!first_rels.implies(Poly_Con_Relation::saturates()) 00335 && !first_rels.implies(Poly_Con_Relation::is_disjoint())); 00336 if (first_rels.implies(Poly_Con_Relation::strictly_intersects())) 00337 return Poly_Con_Relation::strictly_intersects(); 00338 00339 // Build second halfspace. 00340 if (positive) 00341 expr -= modulus; 00342 else 00343 expr += modulus; 00344 Constraint second_halfspace = positive ? (expr <= 0) : (expr >= 0); 00345 00346 PPL_ASSERT(first_rels == Poly_Con_Relation::is_included()); 00347 Poly_Con_Relation second_rels = relation_with(second_halfspace); 00348 PPL_ASSERT(!second_rels.implies(Poly_Con_Relation::saturates()) 00349 && !second_rels.implies(Poly_Con_Relation::is_disjoint())); 00350 if (second_rels.implies(Poly_Con_Relation::strictly_intersects())) 00351 return Poly_Con_Relation::strictly_intersects(); 00352 00353 PPL_ASSERT(second_rels == Poly_Con_Relation::is_included()); 00354 return Poly_Con_Relation::is_disjoint(); 00355 } 00356 00357 bool 00358 PPL::Polyhedron::is_universe() const { 00359 if (marked_empty()) 00360 return false; 00361 00362 if (space_dim == 0) 00363 return true; 00364 00365 if (!has_pending_generators() && constraints_are_up_to_date()) { 00366 // Search for a constraint that is not a tautology. 00367 for (dimension_type i = con_sys.num_rows(); i-- > 0; ) 00368 if (!con_sys[i].is_tautological()) 00369 return false; 00370 // All the constraints are tautologies. 00371 return true; 00372 } 00373 00374 PPL_ASSERT(!has_pending_constraints() && generators_are_up_to_date()); 00375 00376 // Try a fast-fail test. 00377 dimension_type num_lines = 0; 00378 dimension_type num_rays = 0; 00379 const dimension_type first_pending = gen_sys.first_pending_row(); 00380 for (dimension_type i = first_pending; i-- > 0; ) 00381 switch (gen_sys[i].type()) { 00382 case Generator::RAY: 00383 ++num_rays; 00384 break; 00385 case Generator::LINE: 00386 ++num_lines; 00387 break; 00388 default: 00389 break; 00390 } 00391 00392 if (has_pending_generators()) { 00393 // The non-pending part of `gen_sys' was minimized: 00394 // a success-first test is possible in this case. 00395 PPL_ASSERT(generators_are_minimized()); 00396 if (num_lines == space_dim) { 00397 PPL_ASSERT(num_rays == 0); 00398 return true; 00399 } 00400 PPL_ASSERT(num_lines < space_dim); 00401 // Now scan the pending generators. 00402 dimension_type num_pending_lines = 0; 00403 dimension_type num_pending_rays = 0; 00404 const dimension_type gs_num_rows = gen_sys.num_rows(); 00405 for (dimension_type i = first_pending; i < gs_num_rows; ++i) 00406 switch (gen_sys[i].type()) { 00407 case Generator::RAY: 00408 ++num_pending_rays; 00409 break; 00410 case Generator::LINE: 00411 ++num_pending_lines; 00412 break; 00413 default: 00414 break; 00415 } 00416 // If no pending rays and lines were found, 00417 // then it is not the universe polyhedron. 00418 if (num_pending_rays == 0 && num_pending_lines == 0) 00419 return false; 00420 // Factor away the lines already seen (to be on the safe side, 00421 // we assume they are all linearly independent). 00422 if (num_lines + num_pending_lines < space_dim) { 00423 const dimension_type num_dims_missing 00424 = space_dim - (num_lines + num_pending_lines); 00425 // In order to span an n dimensional space (where n = num_dims_missing), 00426 // at least n+1 rays are needed. 00427 if (num_rays + num_pending_rays <= num_dims_missing) 00428 return false; 00429 } 00430 } 00431 else { 00432 // There is nothing pending. 00433 if (generators_are_minimized()) { 00434 // The exact test is possible. 00435 PPL_ASSERT(num_rays == 0 || num_lines < space_dim); 00436 return num_lines == space_dim; 00437 } 00438 else 00439 // Only the fast-fail test can be computed: in order to span 00440 // an n dimensional space (where n = space_dim - num_lines), 00441 // at least n+1 rays are needed. 00442 if (num_lines < space_dim && num_lines + num_rays <= space_dim) 00443 return false; 00444 } 00445 00446 // We need the polyhedron in minimal form. 00447 if (has_pending_generators()) 00448 process_pending_generators(); 00449 else if (!constraints_are_minimized()) 00450 minimize(); 00451 if (is_necessarily_closed()) 00452 return con_sys.num_rows() == 1 00453 && con_sys[0].is_inequality() 00454 && con_sys[0].is_tautological(); 00455 else { 00456 // NNC polyhedron. 00457 if (con_sys.num_rows() != 2 00458 || con_sys[0].is_equality() 00459 || con_sys[1].is_equality()) 00460 return false; 00461 else { 00462 // If the system of constraints contains two rows that 00463 // are not equalities, we are sure that they are 00464 // epsilon constraints: in this case we know that 00465 // the polyhedron is universe. 00466 #ifndef NDEBUG 00467 obtain_sorted_constraints(); 00468 const Constraint& eps_leq_one = con_sys[0]; 00469 const Constraint& eps_geq_zero = con_sys[1]; 00470 const dimension_type eps_index = con_sys.num_columns() - 1; 00471 PPL_ASSERT(eps_leq_one[0] > 0 && eps_leq_one[eps_index] < 0 00472 && eps_geq_zero[0] == 0 && eps_geq_zero[eps_index] > 0); 00473 for (dimension_type i = 1; i < eps_index; ++i) 00474 PPL_ASSERT(eps_leq_one[i] == 0 && eps_geq_zero[i] == 0); 00475 #endif 00476 return true; 00477 } 00478 } 00479 } 00480 00481 bool 00482 PPL::Polyhedron::is_bounded() const { 00483 // A zero-dimensional or empty polyhedron is bounded. 00484 if (space_dim == 0 00485 || marked_empty() 00486 || (has_pending_constraints() && !process_pending_constraints()) 00487 || (!generators_are_up_to_date() && !update_generators())) 00488 return true; 00489 00490 // If the system of generators contains any line or a ray, 00491 // then the polyhedron is unbounded. 00492 for (dimension_type i = gen_sys.num_rows(); i-- > 0; ) 00493 if (gen_sys[i].is_line_or_ray()) 00494 return false; 00495 00496 // The system of generators is composed only by 00497 // points and closure points: the polyhedron is bounded. 00498 return true; 00499 } 00500 00501 bool 00502 PPL::Polyhedron::is_topologically_closed() const { 00503 // Necessarily closed polyhedra are trivially closed. 00504 if (is_necessarily_closed()) 00505 return true; 00506 // Any empty or zero-dimensional polyhedron is closed. 00507 if (marked_empty() 00508 || space_dim == 0 00509 || (has_something_pending() && !process_pending())) 00510 return true; 00511 00512 // At this point there are no pending constraints or generators. 00513 PPL_ASSERT(!has_something_pending()); 00514 00515 if (generators_are_minimized()) { 00516 // A polyhedron is closed if and only if all of its (non-redundant) 00517 // closure points are matched by a corresponding point. 00518 const dimension_type n_rows = gen_sys.num_rows(); 00519 const dimension_type n_lines = gen_sys.num_lines(); 00520 for (dimension_type i = n_rows; i-- > n_lines; ) { 00521 const Generator& gen_sys_i = gen_sys[i]; 00522 if (gen_sys_i.is_closure_point()) { 00523 bool gen_sys_i_has_no_matching_point = true; 00524 for (dimension_type j = n_rows; j-- > n_lines; ) { 00525 const Generator& gen_sys_j = gen_sys[j]; 00526 if (i != j 00527 && gen_sys_j.is_point() 00528 && gen_sys_i.is_matching_closure_point(gen_sys_j)) { 00529 gen_sys_i_has_no_matching_point = false; 00530 break; 00531 } 00532 } 00533 if (gen_sys_i_has_no_matching_point) 00534 return false; 00535 } 00536 } 00537 // All closure points are matched. 00538 return true; 00539 } 00540 00541 // A polyhedron is closed if, after strong minimization 00542 // of its constraint system, it has no strict inequalities. 00543 strongly_minimize_constraints(); 00544 return marked_empty() || !con_sys.has_strict_inequalities(); 00545 } 00546 00547 bool 00548 PPL::Polyhedron::contains_integer_point() const { 00549 // Any empty polyhedron does not contain integer points. 00550 if (marked_empty()) 00551 return false; 00552 00553 // A zero-dimensional, universe polyhedron has, by convention, an 00554 // integer point. 00555 if (space_dim == 0) 00556 return true; 00557 00558 // CHECKME: do we really want to call conversion to check for emptiness? 00559 if (has_pending_constraints() && !process_pending()) 00560 // Empty again. 00561 return false; 00562 00563 // FIXME: do also exploit info regarding rays and lines, if possible. 00564 // Is any integer point already available? 00565 PPL_ASSERT(!has_pending_constraints()); 00566 if (generators_are_up_to_date()) 00567 for (dimension_type i = gen_sys.num_rows(); i-- > 0; ) 00568 if (gen_sys[i].is_point() && gen_sys[i].divisor() == 1) 00569 return true; 00570 00571 const Constraint_System& cs = constraints(); 00572 #if 0 // TEMPORARILY DISABLED. 00573 MIP_Problem mip(space_dim, 00574 cs.begin(), cs.end(), 00575 Variables_Set(Variable(0), Variable(space_dim-1))); 00576 #else 00577 // FIXME: temporary workaround, to be removed as soon as the MIP 00578 // problem class will correctly and precisely handle 00579 // ((strict) in-) equality constraints having all integer variables. 00580 MIP_Problem mip(space_dim); 00581 mip.add_to_integer_space_dimensions(Variables_Set(Variable(0), 00582 Variable(space_dim-1))); 00583 PPL_DIRTY_TEMP_COEFFICIENT(homogeneous_gcd); 00584 PPL_DIRTY_TEMP_COEFFICIENT(gcd); 00585 PPL_DIRTY_TEMP(mpq_class, rational_inhomogeneous); 00586 PPL_DIRTY_TEMP_COEFFICIENT(tightened_inhomogeneous); 00587 for (Constraint_System::const_iterator cs_i = cs.begin(), 00588 cs_end = cs.end(); cs_i != cs_end; ++cs_i) { 00589 const Constraint& c = *cs_i; 00590 const Constraint::Type c_type = c.type(); 00591 const Coefficient& inhomogeneous = c.inhomogeneous_term(); 00592 if (c_type == Constraint::STRICT_INEQUALITY) { 00593 // CHECKME: should we change the behavior of Linear_Expression(c) ? 00594 // Compute the GCD of the coefficients of c 00595 // (disregarding the inhomogeneous term and the epsilon dimension). 00596 homogeneous_gcd = 0; 00597 for (dimension_type i = space_dim; i-- > 0; ) 00598 gcd_assign(homogeneous_gcd, 00599 homogeneous_gcd, c.coefficient(Variable(i))); 00600 if (homogeneous_gcd == 0) { 00601 // NOTE: since tautological constraints are already filtered away 00602 // by iterators, here we must have an inconsistent constraint. 00603 PPL_ASSERT(c.is_inconsistent()); 00604 return false; 00605 } 00606 Linear_Expression le; 00607 for (dimension_type i = space_dim; i-- > 0; ) 00608 le += (c.coefficient(Variable(i)) / homogeneous_gcd) * Variable(i); 00609 // Add the integer part of `inhomogeneous'. 00610 le += (inhomogeneous / homogeneous_gcd); 00611 // Further tighten the constraint if the inhomogeneous term 00612 // was integer, i.e., if `homogeneous_gcd' divides `inhomogeneous'. 00613 gcd_assign(gcd, homogeneous_gcd, inhomogeneous); 00614 if (gcd == homogeneous_gcd) 00615 le -= 1; 00616 mip.add_constraint(le >= 0); 00617 } 00618 else { 00619 // Equality or non-strict inequality. 00620 // If possible, avoid useless gcd computations. 00621 if (inhomogeneous == 0) 00622 // The inhomogeneous term cannot be tightened. 00623 mip.add_constraint(c); 00624 else { 00625 // Compute the GCD of the coefficients of c 00626 // (disregarding the inhomogeneous term) 00627 // to see whether or not the inhomogeneous term can be tightened. 00628 homogeneous_gcd = 0; 00629 for (dimension_type i = space_dim; i-- > 0; ) 00630 gcd_assign(homogeneous_gcd, 00631 homogeneous_gcd, c.coefficient(Variable(i))); 00632 if (homogeneous_gcd == 0) { 00633 // NOTE: since tautological constraints are already filtered away 00634 // by iterators, here we must have an inconsistent constraint. 00635 PPL_ASSERT(c.is_inconsistent()); 00636 return false; 00637 } 00638 else if (homogeneous_gcd == 1) 00639 // The normalized inhomogeneous term is integer: 00640 // add the constraint as-is. 00641 mip.add_constraint(c); 00642 else { 00643 PPL_ASSERT(homogeneous_gcd > 1); 00644 // Here the normalized inhomogeneous term is rational: 00645 // the constraint has to be tightened. 00646 #ifndef NDEBUG 00647 // `homogeneous_gcd' does not divide `inhomogeneous'. 00648 // FIXME: add a divisibility test for Coefficient. 00649 gcd_assign(gcd, homogeneous_gcd, inhomogeneous); 00650 PPL_ASSERT(gcd == 1); 00651 #endif 00652 if (c.type() == Constraint::EQUALITY) 00653 return false; 00654 // Extract the homogeneous part of the constraint. 00655 Linear_Expression le = Linear_Expression(c); 00656 le -= inhomogeneous; 00657 // Tighten the inhomogeneous term. 00658 assign_r(rational_inhomogeneous.get_num(), 00659 inhomogeneous, ROUND_NOT_NEEDED); 00660 assign_r(rational_inhomogeneous.get_den(), 00661 homogeneous_gcd, ROUND_NOT_NEEDED); 00662 // Note: canonicalization is not needed (as gcd == 1). 00663 PPL_ASSERT(is_canonical(rational_inhomogeneous)); 00664 assign_r(tightened_inhomogeneous, 00665 rational_inhomogeneous, ROUND_DOWN); 00666 tightened_inhomogeneous *= homogeneous_gcd; 00667 le += tightened_inhomogeneous; 00668 mip.add_constraint(le >= 0); 00669 } 00670 } 00671 } 00672 } 00673 #endif // TEMPORARY WORKAROUND. 00674 return mip.is_satisfiable(); 00675 } 00676 00677 bool 00678 PPL::Polyhedron::constrains(const Variable var) const { 00679 // `var' should be one of the dimensions of the polyhedron. 00680 const dimension_type var_space_dim = var.space_dimension(); 00681 if (space_dim < var_space_dim) 00682 throw_dimension_incompatible("constrains(v)", "v", var); 00683 00684 // An empty polyhedron constrains all variables. 00685 if (marked_empty()) 00686 return true; 00687 00688 if (generators_are_up_to_date() && !has_pending_constraints()) { 00689 // Since generators are up-to-date and there are no pending 00690 // constraints, the generator system (since it is well formed) 00691 // contains a point. Hence the polyhedron is not empty. 00692 if (constraints_are_up_to_date() && !has_pending_generators()) 00693 // Here a variable is constrained if and only if it is 00694 // syntactically constrained. 00695 goto syntactic_check; 00696 00697 if (generators_are_minimized()) { 00698 // Try a quick, incomplete check for the universe polyhedron: 00699 // a universe polyhedron constrains no variable. 00700 // Count the number of non-pending 00701 // (hence, linearly independent) lines. 00702 dimension_type num_lines = 0; 00703 const dimension_type first_pending = gen_sys.first_pending_row(); 00704 for (dimension_type i = first_pending; i-- > 0; ) 00705 if (gen_sys[i].is_line()) 00706 ++num_lines; 00707 00708 if (num_lines == space_dim) 00709 return false; 00710 } 00711 00712 // Scan generators: perhaps we will find a generator equivalent to 00713 // line(var) or a pair of generators equivalent to ray(-var) and 00714 // ray(var). 00715 bool have_positive_ray = false; 00716 bool have_negative_ray = false; 00717 const dimension_type var_id = var.id(); 00718 for (dimension_type i = gen_sys.num_rows(); i-- > 0; ) { 00719 const Generator& gen_sys_i = gen_sys[i]; 00720 if (gen_sys_i.is_line_or_ray()) { 00721 const Linear_Row& row = gen_sys_i; 00722 const int sign = sgn(row.coefficient(var_id)); 00723 if (sign != 0) { 00724 for (dimension_type j = space_dim+1; --j > 0; ) 00725 if (j != var_id && row[j] != 0) 00726 goto next; 00727 if (gen_sys_i.is_line()) 00728 return true; 00729 if (sign > 0) 00730 if (have_negative_ray) 00731 return true; 00732 else 00733 have_positive_ray = true; 00734 else if (have_positive_ray) 00735 return true; 00736 else 00737 have_negative_ray = true; 00738 } 00739 } 00740 next: 00741 ; 00742 } 00743 00744 // We are still here: at least we know that, since generators are 00745 // up-to-date and there are no pending constraints, then the 00746 // generator system (since it is well formed) contains a point. 00747 // Hence the polyhedron is not empty. 00748 if (has_pending_generators()) 00749 process_pending_generators(); 00750 else if (!constraints_are_up_to_date()) 00751 update_constraints(); 00752 goto syntactic_check; 00753 } 00754 00755 // We must minimize to detect emptiness and obtain constraints. 00756 if (!minimize()) 00757 return true; 00758 00759 syntactic_check: 00760 for (dimension_type i = con_sys.num_rows(); i-- > 0; ) 00761 if (con_sys[i].coefficient(var) != 0) 00762 return true; 00763 return false; 00764 } 00765 00766 bool 00767 PPL::Polyhedron::OK(bool check_not_empty) const { 00768 #ifndef NDEBUG 00769 using std::endl; 00770 using std::cerr; 00771 #endif 00772 00773 // The expected number of columns in the constraint and generator 00774 // systems, if they are not empty. 00775 const dimension_type poly_num_columns 00776 = space_dim + (is_necessarily_closed() ? 1U : 2U); 00777 00778 // Check whether the topologies of `con_sys' and `gen_sys' agree. 00779 if (con_sys.topology() != gen_sys.topology()) { 00780 #ifndef NDEBUG 00781 cerr << "Constraints and generators have different topologies!" 00782 << endl; 00783 #endif 00784 goto bomb; 00785 } 00786 00787 // Check whether the saturation matrices are well-formed. 00788 if (!sat_c.OK()) 00789 goto bomb; 00790 if (!sat_g.OK()) 00791 goto bomb; 00792 00793 // Check whether the status information is legal. 00794 if (!status.OK()) 00795 goto bomb; 00796 00797 if (marked_empty()) { 00798 if (check_not_empty) { 00799 // The caller does not want the polyhedron to be empty. 00800 #ifndef NDEBUG 00801 cerr << "Empty polyhedron!" << endl; 00802 #endif 00803 goto bomb; 00804 } 00805 00806 // An empty polyhedron is allowed if the system of constraints 00807 // either has no rows or only contains an unsatisfiable constraint 00808 // and if it has no pending constraints or generators. 00809 if (has_something_pending()) { 00810 #ifndef NDEBUG 00811 cerr << "The polyhedron is empty, " 00812 << "but it has something pending" << endl; 00813 #endif 00814 goto bomb; 00815 } 00816 if (con_sys.has_no_rows()) 00817 return true; 00818 else { 00819 if (con_sys.space_dimension() != space_dim) { 00820 #ifndef NDEBUG 00821 cerr << "The polyhedron is in a space of dimension " 00822 << space_dim 00823 << " while the system of constraints is in a space of dimension " 00824 << con_sys.space_dimension() 00825 << endl; 00826 #endif 00827 goto bomb; 00828 } 00829 if (con_sys.num_rows() != 1) { 00830 #ifndef NDEBUG 00831 cerr << "The system of constraints for an empty polyhedron " 00832 << "has more then one row" 00833 << endl; 00834 #endif 00835 goto bomb; 00836 } 00837 if (!con_sys[0].is_inconsistent()) { 00838 #ifndef NDEBUG 00839 cerr << "Empty polyhedron with a satisfiable system of constraints" 00840 << endl; 00841 #endif 00842 goto bomb; 00843 } 00844 // Here we have only one, inconsistent constraint. 00845 return true; 00846 } 00847 } 00848 00849 // A zero-dimensional, non-empty polyhedron is legal only if the 00850 // system of constraint `con_sys' and the system of generators 00851 // `gen_sys' have no rows. 00852 if (space_dim == 0) { 00853 if (has_something_pending()) { 00854 #ifndef NDEBUG 00855 cerr << "Zero-dimensional polyhedron with something pending" 00856 << endl; 00857 #endif 00858 goto bomb; 00859 } 00860 if (!con_sys.has_no_rows() || !gen_sys.has_no_rows()) { 00861 #ifndef NDEBUG 00862 cerr << "Zero-dimensional polyhedron with a non-empty" 00863 << endl 00864 << "system of constraints or generators." 00865 << endl; 00866 #endif 00867 goto bomb; 00868 } 00869 return true; 00870 } 00871 00872 // A polyhedron is defined by a system of constraints 00873 // or a system of generators: at least one of them must be up to date. 00874 if (!constraints_are_up_to_date() && !generators_are_up_to_date()) { 00875 #ifndef NDEBUG 00876 cerr << "Polyhedron not empty, not zero-dimensional" 00877 << endl 00878 << "and with neither constraints nor generators up-to-date!" 00879 << endl; 00880 #endif 00881 goto bomb; 00882 } 00883 00884 // Here we check if the size of the matrices is consistent. 00885 // Let us suppose that all the matrices are up-to-date; this means: 00886 // `con_sys' : number of constraints x poly_num_columns 00887 // `gen_sys' : number of generators x poly_num_columns 00888 // `sat_c' : number of generators x number of constraints 00889 // `sat_g' : number of constraints x number of generators. 00890 if (constraints_are_up_to_date()) { 00891 if (con_sys.num_columns() != poly_num_columns) { 00892 #ifndef NDEBUG 00893 cerr << "Incompatible size! (con_sys and space_dim)" 00894 << endl; 00895 #endif 00896 goto bomb; 00897 } 00898 if (sat_c_is_up_to_date()) 00899 if (con_sys.first_pending_row() != sat_c.num_columns()) { 00900 #ifndef NDEBUG 00901 cerr << "Incompatible size! (con_sys and sat_c)" 00902 << endl; 00903 #endif 00904 goto bomb; 00905 } 00906 if (sat_g_is_up_to_date()) 00907 if (con_sys.first_pending_row() != sat_g.num_rows()) { 00908 #ifndef NDEBUG 00909 cerr << "Incompatible size! (con_sys and sat_g)" 00910 << endl; 00911 #endif 00912 goto bomb; 00913 } 00914 if (generators_are_up_to_date()) 00915 if (con_sys.num_columns() != gen_sys.num_columns()) { 00916 #ifndef NDEBUG 00917 cerr << "Incompatible size! (con_sys and gen_sys)" 00918 << endl; 00919 #endif 00920 goto bomb; 00921 } 00922 } 00923 00924 if (generators_are_up_to_date()) { 00925 if (gen_sys.num_columns() != poly_num_columns) { 00926 #ifndef NDEBUG 00927 cerr << "Incompatible size! (gen_sys and space_dim)" 00928 << endl; 00929 #endif 00930 goto bomb; 00931 } 00932 if (sat_c_is_up_to_date()) 00933 if (gen_sys.first_pending_row() != sat_c.num_rows()) { 00934 #ifndef NDEBUG 00935 cerr << "Incompatible size! (gen_sys and sat_c)" 00936 << endl; 00937 #endif 00938 goto bomb; 00939 } 00940 if (sat_g_is_up_to_date()) 00941 if (gen_sys.first_pending_row() != sat_g.num_columns()) { 00942 #ifndef NDEBUG 00943 cerr << "Incompatible size! (gen_sys and sat_g)" 00944 << endl; 00945 #endif 00946 goto bomb; 00947 } 00948 00949 // Check if the system of generators is well-formed. 00950 if (!gen_sys.OK()) 00951 goto bomb; 00952 00953 if (gen_sys.first_pending_row() == 0) { 00954 #ifndef NDEBUG 00955 cerr << "Up-to-date generator system with all rows pending!" 00956 << endl; 00957 #endif 00958 goto bomb; 00959 } 00960 00961 // A non-empty system of generators describing a polyhedron 00962 // is valid if and only if it contains a point. 00963 if (!gen_sys.has_no_rows() && !gen_sys.has_points()) { 00964 #ifndef NDEBUG 00965 cerr << "Non-empty generator system declared up-to-date " 00966 << "has no points!" 00967 << endl; 00968 #endif 00969 goto bomb; 00970 } 00971 00972 #if 0 // To be activated when Status keeps strong minimization flags. 00973 //================================================= 00974 // TODO: this test is wrong in the general case. 00975 // However, such an invariant does hold for a 00976 // strongly-minimized Generator_System. 00977 // We will activate this test as soon as the Status 00978 // flags will be able to remember if a system is 00979 // strongly minimized. 00980 00981 // Checking that the number of closure points is always 00982 // greater than the number of points. 00983 if (!is_necessarily_closed()) { 00984 dimension_type num_points = 0; 00985 dimension_type num_closure_points = 0; 00986 dimension_type eps_index = gen_sys.num_columns() - 1; 00987 for (dimension_type i = gen_sys.num_rows(); i-- > 0; ) 00988 if (!gen_sys[i].is_line_or_ray()) 00989 if (gen_sys[i][eps_index] > 0) 00990 ++num_points; 00991 else 00992 ++num_closure_points; 00993 if (num_points > num_closure_points) { 00994 #ifndef NDEBUG 00995 cerr << "# POINTS > # CLOSURE_POINTS" << endl; 00996 #endif 00997 goto bomb; 00998 } 00999 } 01000 //================================================= 01001 #endif 01002 01003 if (generators_are_minimized()) { 01004 // If the system of generators is minimized, the number of 01005 // lines, rays and points of the polyhedron must be the same as 01006 // of a temporary, minimized one. If this does not happen then 01007 // the polyhedron is not OK. 01008 Constraint_System new_con_sys(topology()); 01009 Generator_System gs_without_pending = gen_sys; 01010 gs_without_pending.remove_trailing_rows(gs_without_pending.num_rows() 01011 - gen_sys.first_pending_row()); 01012 gs_without_pending.unset_pending_rows(); 01013 Generator_System copy_of_gen_sys = gs_without_pending; 01014 Bit_Matrix new_sat_c; 01015 minimize(false, copy_of_gen_sys, new_con_sys, new_sat_c); 01016 const dimension_type copy_num_lines = copy_of_gen_sys.num_lines(); 01017 if (gs_without_pending.num_rows() != copy_of_gen_sys.num_rows() 01018 || gs_without_pending.num_lines() != copy_num_lines 01019 || gs_without_pending.num_rays() != copy_of_gen_sys.num_rays()) { 01020 #ifndef NDEBUG 01021 cerr << "Generators are declared minimized, but they are not!\n" 01022 << "Here is the minimized form of the generators:\n"; 01023 copy_of_gen_sys.ascii_dump(cerr); 01024 cerr << endl; 01025 #endif 01026 goto bomb; 01027 } 01028 01029 // CHECKME : the following observation is not formally true 01030 // for a NNC_Polyhedron. But it may be true for its 01031 // representation ... 01032 01033 // If the corresponding polyhedral cone is _pointed_, then 01034 // a minimal system of generators is unique up to positive scaling. 01035 // We thus verify if the cone is pointed (i.e., there are no lines) 01036 // and, after normalizing and sorting a copy of the system `gen_sys' 01037 // of the polyhedron (we use a copy not to modify the polyhedron's 01038 // system) and the system `copy_of_gen_sys' that has been just 01039 // minimized, we check if the two matrices are identical. If 01040 // they are different it means that the generators of the 01041 // polyhedron are declared minimized, but they are not. 01042 if (copy_num_lines == 0) { 01043 copy_of_gen_sys.strong_normalize(); 01044 copy_of_gen_sys.sort_rows(); 01045 gs_without_pending.strong_normalize(); 01046 gs_without_pending.sort_rows(); 01047 if (copy_of_gen_sys != gs_without_pending) { 01048 #ifndef NDEBUG 01049 cerr << "Generators are declared minimized, but they are not!\n" 01050 << "(we are in the case:\n" 01051 << "dimension of lineality space equal to 0)\n" 01052 << "Here is the minimized form of the generators:\n"; 01053 copy_of_gen_sys.ascii_dump(cerr); 01054 cerr << endl; 01055 #endif 01056 goto bomb; 01057 } 01058 } 01059 } 01060 } 01061 01062 if (constraints_are_up_to_date()) { 01063 // Check if the system of constraints is well-formed. 01064 if (!con_sys.OK()) 01065 goto bomb; 01066 01067 if (con_sys.first_pending_row() == 0) { 01068 #ifndef NDEBUG 01069 cerr << "Up-to-date constraint system with all rows pending!" 01070 << endl; 01071 #endif 01072 goto bomb; 01073 } 01074 01075 // A non-empty system of constraints describing a polyhedron 01076 // must contain a constraint with a non-zero inhomogeneous term; 01077 // such a constraint corresponds to (a combination of other 01078 // constraints with): 01079 // -* the positivity constraint, for necessarily closed polyhedra; 01080 // -* the epsilon <= 1 constraint, for NNC polyhedra. 01081 bool no_positivity_constraint = true; 01082 for (dimension_type i = con_sys.num_rows(); i-- > 0; ) 01083 if (con_sys[i].inhomogeneous_term() != 0) { 01084 no_positivity_constraint = false; 01085 break; 01086 } 01087 if (no_positivity_constraint) { 01088 #ifndef NDEBUG 01089 cerr << "Non-empty constraint system has no positivity constraint" 01090 << endl; 01091 #endif 01092 goto bomb; 01093 } 01094 01095 if (!is_necessarily_closed()) { 01096 // A non-empty system of constraints describing a NNC polyhedron 01097 // must also contain a (combination of) the constraint epsilon >= 0, 01098 // i.e., a constraint with a positive epsilon coefficient. 01099 bool no_epsilon_geq_zero = true; 01100 const dimension_type eps_index = con_sys.num_columns() - 1; 01101 for (dimension_type i = con_sys.num_rows(); i-- > 0; ) 01102 if (con_sys[i][eps_index] > 0) { 01103 no_epsilon_geq_zero = false; 01104 break; 01105 } 01106 if (no_epsilon_geq_zero) { 01107 #ifndef NDEBUG 01108 cerr << "Non-empty constraint system for NNC polyhedron " 01109 << "has no epsilon >= 0 constraint" 01110 << endl; 01111 #endif 01112 goto bomb; 01113 } 01114 } 01115 01116 Constraint_System cs_without_pending = con_sys; 01117 cs_without_pending.remove_trailing_rows(cs_without_pending.num_rows() 01118 - con_sys.first_pending_row()); 01119 cs_without_pending.unset_pending_rows(); 01120 Constraint_System copy_of_con_sys = cs_without_pending; 01121 bool empty = false; 01122 if (check_not_empty || constraints_are_minimized()) { 01123 Generator_System new_gen_sys(topology()); 01124 Bit_Matrix new_sat_g; 01125 empty = minimize(true, copy_of_con_sys, new_gen_sys, new_sat_g); 01126 } 01127 01128 if (empty && check_not_empty) { 01129 #ifndef NDEBUG 01130 cerr << "Unsatisfiable system of constraints!" 01131 << endl; 01132 #endif 01133 goto bomb; 01134 } 01135 01136 if (constraints_are_minimized()) { 01137 // If the constraints are minimized, the number of equalities 01138 // and of inequalities of the system of the polyhedron must be 01139 // the same of the temporary minimized one. 01140 // If it does not happen, the polyhedron is not OK. 01141 if (cs_without_pending.num_rows() != copy_of_con_sys.num_rows() 01142 || cs_without_pending.num_equalities() 01143 != copy_of_con_sys.num_equalities()) { 01144 #ifndef NDEBUG 01145 cerr << "Constraints are declared minimized, but they are not!\n" 01146 << "Here is the minimized form of the constraints:\n"; 01147 copy_of_con_sys.ascii_dump(cerr); 01148 cerr << endl; 01149 #endif 01150 goto bomb; 01151 } 01152 // The system `copy_of_con_sys' has the form that is obtained 01153 // after applying methods gauss() and back_substitute(). 01154 // A system of constraints can be minimal even if it does not 01155 // have this form. So, to verify if the polyhedron is correct, 01156 // we copy the system `con_sys' in a temporary one and then 01157 // modify it using method simplify() (which calls both gauss() 01158 // and back_substitute()). 01159 // If the temporary system and `copy_of_con_sys' are different, 01160 // the polyhedron is not OK. 01161 copy_of_con_sys.strong_normalize(); 01162 copy_of_con_sys.sort_rows(); 01163 cs_without_pending.simplify(); 01164 cs_without_pending.strong_normalize(); 01165 cs_without_pending.sort_rows(); 01166 if (cs_without_pending != copy_of_con_sys) { 01167 #ifndef NDEBUG 01168 cerr << "Constraints are declared minimized, but they are not!\n" 01169 << "Here is the minimized form of the constraints:\n"; 01170 copy_of_con_sys.ascii_dump(cerr); 01171 cerr << endl; 01172 #endif 01173 goto bomb; 01174 } 01175 } 01176 } 01177 01178 if (sat_c_is_up_to_date()) 01179 for (dimension_type i = sat_c.num_rows(); i-- > 0; ) { 01180 const Generator tmp_gen = gen_sys[i]; 01181 const Bit_Row tmp_sat = sat_c[i]; 01182 for (dimension_type j = sat_c.num_columns(); j-- > 0; ) { 01183 const bool sat_j = (Scalar_Products::sign(con_sys[j], tmp_gen) == 0); 01184 if (sat_j == tmp_sat[j]) { 01185 #ifndef NDEBUG 01186 cerr << "sat_c is declared up-to-date, but it is not!" 01187 << endl; 01188 #endif 01189 goto bomb; 01190 } 01191 } 01192 } 01193 01194 if (sat_g_is_up_to_date()) 01195 for (dimension_type i = sat_g.num_rows(); i-- > 0; ) { 01196 const Constraint tmp_con = con_sys[i]; 01197 const Bit_Row tmp_sat = sat_g[i]; 01198 for (dimension_type j = sat_g.num_columns(); j-- > 0; ) { 01199 const bool sat_j = (Scalar_Products::sign(tmp_con, gen_sys[j]) == 0); 01200 if (sat_j == tmp_sat[j]) { 01201 #ifndef NDEBUG 01202 cerr << "sat_g is declared up-to-date, but it is not!" 01203 << endl; 01204 #endif 01205 goto bomb; 01206 } 01207 } 01208 } 01209 01210 if (has_pending_constraints()) { 01211 if (con_sys.num_pending_rows() == 0) { 01212 #ifndef NDEBUG 01213 cerr << "The polyhedron is declared to have pending constraints, " 01214 << "but con_sys has no pending rows!" 01215 << endl; 01216 #endif 01217 goto bomb; 01218 } 01219 } 01220 01221 if (has_pending_generators()) { 01222 if (gen_sys.num_pending_rows() == 0) { 01223 #ifndef NDEBUG 01224 cerr << "The polyhedron is declared to have pending generators, " 01225 << "but gen_sys has no pending rows!" 01226 << endl; 01227 #endif 01228 goto bomb; 01229 } 01230 } 01231 01232 return true; 01233 01234 bomb: 01235 #ifndef NDEBUG 01236 cerr << "Here is the guilty polyhedron:" 01237 << endl; 01238 ascii_dump(cerr); 01239 #endif 01240 return false; 01241 } 01242 01243 void 01244 PPL::Polyhedron::add_constraint(const Constraint& c) { 01245 // Topology-compatibility check. 01246 if (c.is_strict_inequality() && is_necessarily_closed()) { 01247 // Trivially true/false strict inequalities are legal. 01248 if (c.is_tautological()) 01249 return; 01250 if (c.is_inconsistent()) { 01251 set_empty(); 01252 return; 01253 } 01254 // Here c is a non-trivial strict inequality. 01255 throw_topology_incompatible("add_constraint(c)", "c", c); 01256 } 01257 01258 // Dimension-compatibility check: 01259 // the dimension of `c' can not be greater than space_dim. 01260 if (space_dim < c.space_dimension()) 01261 throw_dimension_incompatible("add_constraint(c)", "c", c); 01262 01263 if (!marked_empty()) 01264 refine_no_check(c); 01265 } 01266 01267 void 01268 PPL::Polyhedron::add_congruence(const Congruence& cg) { 01269 // Dimension-compatibility check: 01270 // the dimension of `cg' can not be greater than space_dim. 01271 if (space_dim < cg.space_dimension()) 01272 throw_dimension_incompatible("add_congruence(cg)", "cg", cg); 01273 01274 // Handle the case of proper congruences first. 01275 if (cg.is_proper_congruence()) { 01276 if (cg.is_tautological()) 01277 return; 01278 if (cg.is_inconsistent()) { 01279 set_empty(); 01280 return; 01281 } 01282 // Non-trivial and proper congruences are not allowed. 01283 throw_invalid_argument("add_congruence(cg)", 01284 "cg is a non-trivial, proper congruence"); 01285 } 01286 01287 PPL_ASSERT(cg.is_equality()); 01288 // Handle empty and 0-dim cases first. 01289 if (marked_empty()) 01290 return; 01291 if (space_dim == 0) { 01292 if (cg.is_inconsistent()) 01293 set_empty(); 01294 return; 01295 } 01296 01297 // Add the equality. 01298 Linear_Expression le(cg); 01299 Constraint c(le, Constraint::EQUALITY, NECESSARILY_CLOSED); 01300 // Enforce normalization. 01301 c.strong_normalize(); 01302 refine_no_check(c); 01303 } 01304 01305 void 01306 PPL::Polyhedron::add_generator(const Generator& g) { 01307 // Topology-compatibility check. 01308 if (g.is_closure_point() && is_necessarily_closed()) 01309 throw_topology_incompatible("add_generator(g)", "g", g); 01310 // Dimension-compatibility check: 01311 // the dimension of `g' can not be greater than space_dim. 01312 const dimension_type g_space_dim = g.space_dimension(); 01313 if (space_dim < g_space_dim) 01314 throw_dimension_incompatible("add_generator(g)", "g", g); 01315 01316 // Dealing with a zero-dimensional space polyhedron first. 01317 if (space_dim == 0) { 01318 // It is not possible to create 0-dim rays or lines. 01319 PPL_ASSERT(g.is_point() || g.is_closure_point()); 01320 // Closure points can only be inserted in non-empty polyhedra. 01321 if (marked_empty()) { 01322 if (g.type() != Generator::POINT) 01323 throw_invalid_generator("add_generator(g)", "g"); 01324 else 01325 set_zero_dim_univ(); 01326 } 01327 PPL_ASSERT_HEAVY(OK()); 01328 return; 01329 } 01330 01331 if (marked_empty() 01332 || (has_pending_constraints() && !process_pending_constraints()) 01333 || (!generators_are_up_to_date() && !update_generators())) { 01334 // Here the polyhedron is empty: 01335 // the specification says we can only insert a point. 01336 if (!g.is_point()) 01337 throw_invalid_generator("add_generator(g)", "g"); 01338 if (g.is_necessarily_closed() || !is_necessarily_closed()) { 01339 gen_sys.insert(g); 01340 // Since `gen_sys' was empty, after inserting `g' we have to resize 01341 // the system of generators to have the right dimension. 01342 gen_sys.adjust_topology_and_space_dimension(topology(), space_dim); 01343 if (!is_necessarily_closed()) { 01344 // In the NNC topology, each point has to be matched by 01345 // a corresponding closure point: 01346 // turn the just inserted point into the corresponding 01347 // (normalized) closure point. 01348 Generator& cp = gen_sys[gen_sys.num_rows() - 1]; 01349 cp[space_dim + 1] = 0; 01350 cp.normalize(); 01351 // Re-insert the point (which is already normalized). 01352 gen_sys.insert(g); 01353 } 01354 } 01355 else { 01356 // Note: here we have a _legal_ topology mismatch, 01357 // because `g' is NOT a closure point (it is a point!) 01358 // However, by barely invoking `gen_sys.insert(g)' we would 01359 // cause a change in the topology of `gen_sys', which is wrong. 01360 // Thus, we insert a "topology corrected" copy of `g'. 01361 const Linear_Expression nc_expr = Linear_Expression(g); 01362 gen_sys.insert(Generator::point(nc_expr, g.divisor())); 01363 // Since `gen_sys' was empty, after inserting `g' we have to resize 01364 // the system of generators to have the right dimension. 01365 gen_sys.adjust_topology_and_space_dimension(topology(), space_dim); 01366 } 01367 // No longer empty, generators up-to-date and minimized. 01368 clear_empty(); 01369 set_generators_minimized(); 01370 } 01371 else { 01372 PPL_ASSERT(generators_are_up_to_date()); 01373 const bool has_pending = can_have_something_pending(); 01374 if (g.is_necessarily_closed() || !is_necessarily_closed()) { 01375 // Since `gen_sys' is not empty, the topology and space dimension 01376 // of the inserted generator are automatically adjusted. 01377 if (has_pending) 01378 gen_sys.insert_pending(g); 01379 else 01380 gen_sys.insert(g); 01381 if (!is_necessarily_closed() && g.is_point()) { 01382 // In the NNC topology, each point has to be matched by 01383 // a corresponding closure point: 01384 // turn the just inserted point into the corresponding 01385 // (normalized) closure point. 01386 Generator& cp = gen_sys[gen_sys.num_rows() - 1]; 01387 cp[space_dim + 1] = 0; 01388 cp.normalize(); 01389 // Re-insert the point (which is already normalized). 01390 if (has_pending) 01391 gen_sys.insert_pending(g); 01392 else 01393 gen_sys.insert(g); 01394 } 01395 } 01396 else { 01397 PPL_ASSERT(!g.is_closure_point()); 01398 // Note: here we have a _legal_ topology mismatch, because 01399 // `g' is NOT a closure point. 01400 // However, by barely invoking `gen_sys.insert(g)' we would 01401 // cause a change in the topology of `gen_sys', which is wrong. 01402 // Thus, we insert a "topology corrected" copy of `g'. 01403 const Linear_Expression nc_expr = Linear_Expression(g); 01404 switch (g.type()) { 01405 case Generator::LINE: 01406 if (has_pending) 01407 gen_sys.insert_pending(Generator::line(nc_expr)); 01408 else 01409 gen_sys.insert(Generator::line(nc_expr)); 01410 break; 01411 case Generator::RAY: 01412 if (has_pending) 01413 gen_sys.insert_pending(Generator::ray(nc_expr)); 01414 else 01415 gen_sys.insert(Generator::ray(nc_expr)); 01416 break; 01417 case Generator::POINT: 01418 if (has_pending) 01419 gen_sys.insert_pending(Generator::point(nc_expr, g.divisor())); 01420 else 01421 gen_sys.insert(Generator::point(nc_expr, g.divisor())); 01422 break; 01423 case Generator::CLOSURE_POINT: 01424 PPL_UNREACHABLE; 01425 break; 01426 } 01427 } 01428 01429 if (has_pending) 01430 set_generators_pending(); 01431 else { 01432 // After adding the new generator, 01433 // constraints are no longer up-to-date. 01434 clear_generators_minimized(); 01435 clear_constraints_up_to_date(); 01436 } 01437 } 01438 PPL_ASSERT_HEAVY(OK()); 01439 } 01440 01441 void 01442 PPL::Polyhedron::add_recycled_constraints(Constraint_System& cs) { 01443 // Topology compatibility check. 01444 if (is_necessarily_closed() && cs.has_strict_inequalities()) { 01445 // We check if _all_ strict inequalities in cs are trivially false. 01446 // (The iterators already filter away trivially true constraints.) 01447 for (Constraint_System::const_iterator i = cs.begin(), 01448 i_end = cs.end(); i != i_end; ++i) { 01449 if (i->is_strict_inequality() 01450 && !i->is_inconsistent()) 01451 throw_topology_incompatible("add_recycled_constraints(cs)", 01452 "cs", cs); 01453 } 01454 // If we reach this point, all strict inequalities were inconsistent. 01455 set_empty(); 01456 return; 01457 } 01458 01459 // Dimension-compatibility check: 01460 // the dimension of `cs' can not be greater than space_dim. 01461 const dimension_type cs_space_dim = cs.space_dimension(); 01462 if (space_dim < cs_space_dim) 01463 throw_dimension_incompatible("add_recycled_constraints(cs)", "cs", cs); 01464 01465 // Adding no constraints is a no-op. 01466 if (cs.has_no_rows()) 01467 return; 01468 01469 if (space_dim == 0) { 01470 // In a 0-dimensional space the constraints are 01471 // tautologies (e.g., 0 == 0 or 1 >= 0 or 1 > 0) or 01472 // inconsistent (e.g., 1 == 0 or -1 >= 0 or 0 > 0). 01473 // In a system of constraints `begin()' and `end()' are equal 01474 // if and only if the system only contains tautologies. 01475 if (cs.begin() != cs.end()) 01476 // There is a constraint, it must be inconsistent, 01477 // the polyhedron is empty. 01478 status.set_empty(); 01479 return; 01480 } 01481 01482 if (marked_empty()) 01483 return; 01484 01485 // The constraints (possibly with pending rows) are required. 01486 if (has_pending_generators()) 01487 process_pending_generators(); 01488 else if (!constraints_are_up_to_date()) 01489 update_constraints(); 01490 01491 // Adjust `cs' to the right topology and space dimension. 01492 // NOTE: we already checked for topology compatibility. 01493 cs.adjust_topology_and_space_dimension(topology(), space_dim); 01494 01495 const bool adding_pending = can_have_something_pending(); 01496 01497 // Here we do not require `con_sys' to be sorted. 01498 // also, we _swap_ (instead of copying) the coefficients of `cs' 01499 // (which is not a const). 01500 const dimension_type old_num_rows = con_sys.num_rows(); 01501 const dimension_type cs_num_rows = cs.num_rows(); 01502 const dimension_type cs_num_columns = cs.num_columns(); 01503 con_sys.add_zero_rows(cs_num_rows, 01504 Linear_Row::Flags(topology(), 01505 Linear_Row::RAY_OR_POINT_OR_INEQUALITY)); 01506 for (dimension_type i = cs_num_rows; i-- > 0; ) { 01507 // NOTE: we cannot directly swap the rows, since they might have 01508 // different capacities (besides possibly having different sizes): 01509 // thus, we steal one coefficient at a time. 01510 Constraint& new_c = con_sys[old_num_rows + i]; 01511 Constraint& old_c = cs[i]; 01512 if (old_c.is_equality()) 01513 new_c.set_is_equality(); 01514 using std::swap; 01515 for (dimension_type j = cs_num_columns; j-- > 0; ) 01516 swap(new_c[j], old_c[j]); 01517 } 01518 01519 if (adding_pending) 01520 set_constraints_pending(); 01521 else { 01522 // The newly added ones are not pending constraints. 01523 con_sys.unset_pending_rows(); 01524 // They have been simply appended. 01525 con_sys.set_sorted(false); 01526 // Constraints are not minimized and generators are not up-to-date. 01527 clear_constraints_minimized(); 01528 clear_generators_up_to_date(); 01529 } 01530 // Note: the constraint system may have become unsatisfiable, thus 01531 // we do not check for satisfiability. 01532 PPL_ASSERT_HEAVY(OK()); 01533 } 01534 01535 void 01536 PPL::Polyhedron::add_constraints(const Constraint_System& cs) { 01537 // TODO: this is just an executable specification. 01538 Constraint_System cs_copy = cs; 01539 add_recycled_constraints(cs_copy); 01540 } 01541 01542 void 01543 PPL::Polyhedron::add_recycled_generators(Generator_System& gs) { 01544 // Topology compatibility check. 01545 if (is_necessarily_closed() && gs.has_closure_points()) 01546 throw_topology_incompatible("add_recycled_generators(gs)", "gs", gs); 01547 // Dimension-compatibility check: 01548 // the dimension of `gs' can not be greater than space_dim. 01549 const dimension_type gs_space_dim = gs.space_dimension(); 01550 if (space_dim < gs_space_dim) 01551 throw_dimension_incompatible("add_recycled_generators(gs)", "gs", gs); 01552 01553 // Adding no generators is a no-op. 01554 if (gs.has_no_rows()) 01555 return; 01556 01557 // Adding valid generators to a zero-dimensional polyhedron 01558 // transform it in the zero-dimensional universe polyhedron. 01559 if (space_dim == 0) { 01560 if (marked_empty() && !gs.has_points()) 01561 throw_invalid_generators("add_recycled_generators(gs)", "gs"); 01562 set_zero_dim_univ(); 01563 PPL_ASSERT_HEAVY(OK(true)); 01564 return; 01565 } 01566 01567 // Adjust `gs' to the right topology and dimensions. 01568 // NOTE: we already checked for topology compatibility. 01569 gs.adjust_topology_and_space_dimension(topology(), space_dim); 01570 // For NNC polyhedra, each point must be matched by 01571 // the corresponding closure point. 01572 if (!is_necessarily_closed()) 01573 gs.add_corresponding_closure_points(); 01574 01575 // The generators (possibly with pending rows) are required. 01576 if ((has_pending_constraints() && !process_pending_constraints()) 01577 || (!generators_are_up_to_date() && !minimize())) { 01578 // We have just discovered that `*this' is empty. 01579 // So `gs' must contain at least one point. 01580 if (!gs.has_points()) 01581 throw_invalid_generators("add_recycled_generators(gs)", "gs"); 01582 // The polyhedron is no longer empty and generators are up-to-date. 01583 using std::swap; 01584 swap(gen_sys, gs); 01585 if (gen_sys.num_pending_rows() > 0) { 01586 // Even though `gs' has pending generators, since the constraints 01587 // of the polyhedron are not up-to-date, the polyhedron cannot 01588 // have pending generators. By integrating the pending part 01589 // of `gen_sys' we may loose sortedness. 01590 gen_sys.unset_pending_rows(); 01591 gen_sys.set_sorted(false); 01592 } 01593 set_generators_up_to_date(); 01594 clear_empty(); 01595 PPL_ASSERT_HEAVY(OK()); 01596 return; 01597 } 01598 01599 const bool adding_pending = can_have_something_pending(); 01600 01601 // Here we do not require `gen_sys' to be sorted. 01602 // also, we _swap_ (instead of copying) the coefficients of `gs' 01603 // (which is not a const). 01604 const dimension_type old_num_rows = gen_sys.num_rows(); 01605 const dimension_type gs_num_rows = gs.num_rows(); 01606 const dimension_type gs_num_columns = gs.num_columns(); 01607 gen_sys.add_zero_rows(gs_num_rows, 01608 Linear_Row::Flags(topology(), 01609 Linear_Row::RAY_OR_POINT_OR_INEQUALITY)); 01610 for (dimension_type i = gs_num_rows; i-- > 0; ) { 01611 // NOTE: we cannot directly swap the rows, since they might have 01612 // different capacities (besides possibly having different sizes): 01613 // thus, we steal one coefficient at a time. 01614 Generator& new_g = gen_sys[old_num_rows + i]; 01615 Generator& old_g = gs[i]; 01616 if (old_g.is_line()) 01617 new_g.set_is_line(); 01618 using std::swap; 01619 for (dimension_type j = gs_num_columns; j-- > 0; ) 01620 swap(new_g[j], old_g[j]); 01621 } 01622 01623 if (adding_pending) 01624 set_generators_pending(); 01625 else { 01626 // The newly added ones are not pending generators. 01627 gen_sys.unset_pending_rows(); 01628 // They have been simply appended. 01629 gen_sys.set_sorted(false); 01630 // Constraints are not up-to-date and generators are not minimized. 01631 clear_constraints_up_to_date(); 01632 clear_generators_minimized(); 01633 } 01634 PPL_ASSERT_HEAVY(OK(true)); 01635 } 01636 01637 void 01638 PPL::Polyhedron::add_generators(const Generator_System& gs) { 01639 // TODO: this is just an executable specification. 01640 Generator_System gs_copy = gs; 01641 add_recycled_generators(gs_copy); 01642 } 01643 01644 void 01645 PPL::Polyhedron::add_congruences(const Congruence_System& cgs) { 01646 // Dimension-compatibility check. 01647 if (space_dim < cgs.space_dimension()) 01648 throw_dimension_incompatible("add_congruences(cgs)", "cgs", cgs); 01649 01650 Constraint_System cs; 01651 bool inserted = false; 01652 for (Congruence_System::const_iterator i = cgs.begin(), 01653 cgs_end = cgs.end(); i != cgs_end; ++i) { 01654 const Congruence& cg = *i; 01655 if (cg.is_equality()) { 01656 Linear_Expression le(cg); 01657 Constraint c(le, Constraint::EQUALITY, NECESSARILY_CLOSED); 01658 // Enforce normalization. 01659 c.strong_normalize(); 01660 // TODO: Consider stealing the row in c when adding it to cs. 01661 cs.insert(c); 01662 inserted = true; 01663 } 01664 else { 01665 PPL_ASSERT(cg.is_proper_congruence()); 01666 if (cg.is_inconsistent()) { 01667 set_empty(); 01668 return; 01669 } 01670 if (!cg.is_tautological()) 01671 throw_invalid_argument("add_congruences(cgs)", 01672 "cgs has a non-trivial, proper congruence"); 01673 } 01674 } 01675 // Only add cs if it contains something. 01676 if (inserted) 01677 add_recycled_constraints(cs); 01678 } 01679 01680 void 01681 PPL::Polyhedron::refine_with_constraint(const Constraint& c) { 01682 // Dimension-compatibility check. 01683 if (space_dim < c.space_dimension()) 01684 throw_dimension_incompatible("refine_with_constraint(c)", "c", c); 01685 // If the polyhedron is known to be empty, do nothing. 01686 if (!marked_empty()) 01687 refine_no_check(c); 01688 } 01689 01690 void 01691 PPL::Polyhedron::refine_with_congruence(const Congruence& cg) { 01692 // Dimension-compatibility check. 01693 if (space_dim < cg.space_dimension()) 01694 throw_dimension_incompatible("refine_with_congruence(cg)", "cg", cg); 01695 01696 // If the polyhedron is known to be empty, do nothing. 01697 if (marked_empty()) 01698 return; 01699 01700 // Dealing with a zero-dimensional space polyhedron first. 01701 if (space_dim == 0) { 01702 if (!cg.is_tautological()) 01703 set_empty(); 01704 return; 01705 } 01706 01707 if (cg.is_equality()) { 01708 Linear_Expression le(cg); 01709 Constraint c(le, Constraint::EQUALITY, NECESSARILY_CLOSED); 01710 // Enforce normalization. 01711 c.strong_normalize(); 01712 refine_no_check(c); 01713 } 01714 } 01715 01716 void 01717 PPL::Polyhedron::refine_with_constraints(const Constraint_System& cs) { 01718 // TODO: this is just an executable specification. 01719 01720 // Dimension-compatibility check. 01721 const dimension_type cs_space_dim = cs.space_dimension(); 01722 if (space_dim < cs_space_dim) 01723 throw_dimension_incompatible("refine_with_constraints(cs)a", 01724 "cs", cs); 01725 01726 // Adding no constraints is a no-op. 01727 if (cs.has_no_rows()) 01728 return; 01729 01730 if (space_dim == 0) { 01731 // In a 0-dimensional space the constraints are 01732 // tautologies (e.g., 0 == 0 or 1 >= 0 or 1 > 0) or 01733 // inconsistent (e.g., 1 == 0 or -1 >= 0 or 0 > 0). 01734 // In a system of constraints `begin()' and `end()' are equal 01735 // if and only if the system only contains tautologies. 01736 if (cs.begin() != cs.end()) 01737 // There is a constraint, it must be inconsistent, 01738 // the polyhedron is empty. 01739 status.set_empty(); 01740 return; 01741 } 01742 01743 if (marked_empty()) 01744 return; 01745 01746 // The constraints (possibly with pending rows) are required. 01747 if (has_pending_generators()) 01748 process_pending_generators(); 01749 else if (!constraints_are_up_to_date()) 01750 update_constraints(); 01751 01752 const bool adding_pending = can_have_something_pending(); 01753 for (dimension_type i = cs.num_rows(); i-- > 0; ) { 01754 const Constraint& c = cs[i]; 01755 01756 if (c.is_necessarily_closed() || !is_necessarily_closed()) 01757 // Since `con_sys' is not empty, the topology and space dimension 01758 // of the inserted constraint are automatically adjusted. 01759 if (adding_pending) 01760 con_sys.insert_pending(c); 01761 else 01762 con_sys.insert(c); 01763 else { 01764 // Here we know that *this is necessarily closed so even if c is 01765 // topologically closed, by barely invoking `con_sys.insert(c)' we 01766 // would cause a change in the topology of `con_sys', which is 01767 // wrong. Thus, we insert a topology closed and "topology 01768 // corrected" version of `c'. 01769 Linear_Expression nc_expr = Linear_Expression(c); 01770 if (c.is_equality()) 01771 if (adding_pending) 01772 con_sys.insert_pending(nc_expr == 0); 01773 else 01774 con_sys.insert(nc_expr == 0); 01775 else 01776 if (adding_pending) 01777 con_sys.insert_pending(nc_expr >= 0); 01778 else 01779 con_sys.insert(nc_expr >= 0); 01780 } 01781 } 01782 01783 if (adding_pending) 01784 set_constraints_pending(); 01785 else { 01786 // Constraints are not minimized and generators are not up-to-date. 01787 clear_constraints_minimized(); 01788 clear_generators_up_to_date(); 01789 } 01790 01791 // Note: the constraint system may have become unsatisfiable, thus 01792 // we do not check for satisfiability. 01793 PPL_ASSERT_HEAVY(OK()); 01794 } 01795 01796 void 01797 PPL::Polyhedron::refine_with_congruences(const Congruence_System& cgs) { 01798 // Dimension-compatibility check. 01799 if (space_dim < cgs.space_dimension()) 01800 throw_dimension_incompatible("refine_with_congruences(cgs)", "cgs", cgs); 01801 01802 Constraint_System cs; 01803 bool inserted = false; 01804 for (Congruence_System::const_iterator i = cgs.begin(), 01805 cgs_end = cgs.end(); i != cgs_end; ++i) { 01806 if (i->is_equality()) { 01807 Linear_Expression le(*i); 01808 Constraint c(le, Constraint::EQUALITY, NECESSARILY_CLOSED); 01809 // Enforce normalization. 01810 c.strong_normalize(); 01811 // TODO: Consider stealing the row in c when adding it to cs. 01812 cs.insert(c); 01813 inserted = true; 01814 } 01815 else if (i->is_inconsistent()) { 01816 set_empty(); 01817 return; 01818 } 01819 } 01820 // Only add cgs if congruences were inserted into cgs, as the 01821 // dimension of cs must be at most that of the polyhedron. 01822 if (inserted) 01823 add_recycled_constraints(cs); 01824 } 01825 01826 void 01827 PPL::Polyhedron::unconstrain(const Variable var) { 01828 // Dimension-compatibility check. 01829 if (space_dim < var.space_dimension()) 01830 throw_dimension_incompatible("unconstrain(var)", var.space_dimension()); 01831 01832 // Do something only if the polyhedron is non-empty. 01833 if (marked_empty() 01834 || (has_pending_constraints() && !process_pending_constraints()) 01835 || (!generators_are_up_to_date() && !update_generators())) 01836 // Empty: do nothing. 01837 return; 01838 01839 PPL_ASSERT(generators_are_up_to_date()); 01840 // Since `gen_sys' is not empty, the topology and space dimension 01841 // of the inserted generator are automatically adjusted. 01842 if (can_have_something_pending()) { 01843 gen_sys.insert_pending(Generator::line(var)); 01844 set_generators_pending(); 01845 } 01846 else { 01847 gen_sys.insert(Generator::line(var)); 01848 // After adding the new generator, 01849 // constraints are no longer up-to-date. 01850 clear_generators_minimized(); 01851 clear_constraints_up_to_date(); 01852 } 01853 PPL_ASSERT_HEAVY(OK(true)); 01854 } 01855 01856 void 01857 PPL::Polyhedron::unconstrain(const Variables_Set& vars) { 01858 // The cylindrification with respect to no dimensions is a no-op. 01859 // This case also captures the only legal cylindrification 01860 // of a polyhedron in a 0-dim space. 01861 if (vars.empty()) 01862 return; 01863 01864 // Dimension-compatibility check. 01865 const dimension_type min_space_dim = vars.space_dimension(); 01866 if (space_dim < min_space_dim) 01867 throw_dimension_incompatible("unconstrain(vs)", min_space_dim); 01868 01869 // Do something only if the polyhedron is non-empty. 01870 if (marked_empty() 01871 || (has_pending_constraints() && !process_pending_constraints()) 01872 || (!generators_are_up_to_date() && !update_generators())) 01873 // Empty: do nothing. 01874 return; 01875 01876 PPL_ASSERT(generators_are_up_to_date()); 01877 // Since `gen_sys' is not empty, the topology and space dimension 01878 // of the inserted generators are automatically adjusted. 01879 Variables_Set::const_iterator vsi = vars.begin(); 01880 Variables_Set::const_iterator vsi_end = vars.end(); 01881 if (can_have_something_pending()) { 01882 for ( ; vsi != vsi_end; ++vsi) 01883 gen_sys.insert_pending(Generator::line(Variable(*vsi))); 01884 set_generators_pending(); 01885 } 01886 else { 01887 for ( ; vsi != vsi_end; ++vsi) 01888 gen_sys.insert(Generator::line(Variable(*vsi))); 01889 // After adding the new generators, 01890 // constraints are no longer up-to-date. 01891 clear_generators_minimized(); 01892 clear_constraints_up_to_date(); 01893 } 01894 PPL_ASSERT_HEAVY(OK(true)); 01895 } 01896 01897 void 01898 PPL::Polyhedron::intersection_assign(const Polyhedron& y) { 01899 Polyhedron& x = *this; 01900 // Topology compatibility check. 01901 if (x.topology() != y.topology()) 01902 throw_topology_incompatible("intersection_assign(y)", "y", y); 01903 // Dimension-compatibility check. 01904 if (x.space_dim != y.space_dim) 01905 throw_dimension_incompatible("intersection_assign(y)", "y", y); 01906 01907 // If one of the two polyhedra is empty, the intersection is empty. 01908 if (x.marked_empty()) 01909 return; 01910 if (y.marked_empty()) { 01911 x.set_empty(); 01912 return; 01913 } 01914 01915 // If both polyhedra are zero-dimensional, 01916 // then at this point they are necessarily non-empty, 01917 // so that their intersection is non-empty too. 01918 if (x.space_dim == 0) 01919 return; 01920 01921 // Both systems of constraints have to be up-to-date, 01922 // possibly having pending constraints. 01923 if (x.has_pending_generators()) 01924 x.process_pending_generators(); 01925 else if (!x.constraints_are_up_to_date()) 01926 x.update_constraints(); 01927 01928 if (y.has_pending_generators()) 01929 y.process_pending_generators(); 01930 else if (!y.constraints_are_up_to_date()) 01931 y.update_constraints(); 01932 01933 // Here both systems are up-to-date and possibly have pending constraints 01934 // (but they cannot have pending generators). 01935 PPL_ASSERT(!x.has_pending_generators() && x.constraints_are_up_to_date()); 01936 PPL_ASSERT(!y.has_pending_generators() && y.constraints_are_up_to_date()); 01937 01938 // If `x' can support pending constraints, 01939 // the constraints of `y' are added as pending constraints of `x'. 01940 if (x.can_have_something_pending()) { 01941 x.con_sys.add_pending_rows(y.con_sys); 01942 x.set_constraints_pending(); 01943 } 01944 else { 01945 // `x' cannot support pending constraints. 01946 // If both constraint systems are (fully) sorted, then we can 01947 // merge them; otherwise we simply add the second to the first. 01948 if (x.con_sys.is_sorted() 01949 && y.con_sys.is_sorted() && !y.has_pending_constraints()) 01950 x.con_sys.merge_rows_assign(y.con_sys); 01951 else 01952 x.con_sys.add_rows(y.con_sys); 01953 // Generators are no longer up-to-date and constraints are no 01954 // longer minimized. 01955 x.clear_generators_up_to_date(); 01956 x.clear_constraints_minimized(); 01957 } 01958 PPL_ASSERT_HEAVY(x.OK() && y.OK()); 01959 } 01960 01961 namespace { 01962 01963 struct Ruled_Out_Pair { 01964 PPL::dimension_type constraint_index; 01965 PPL::dimension_type num_ruled_out; 01966 }; 01967 01968 struct Ruled_Out_Less_Than { 01969 bool operator()(const Ruled_Out_Pair& x, 01970 const Ruled_Out_Pair& y) const { 01971 return x.num_ruled_out > y.num_ruled_out; 01972 } 01973 }; 01974 01975 bool 01976 add_to_system_and_check_independence(PPL::Linear_System& eq_sys, 01977 const PPL::Linear_Row& eq) { 01978 // Check if eq is linearly independent from eq_sys. 01979 PPL_ASSERT(eq.is_line_or_equality()); 01980 eq_sys.insert(eq); 01981 const PPL::dimension_type eq_sys_num_rows = eq_sys.num_rows(); 01982 const PPL::dimension_type rank = eq_sys.gauss(eq_sys_num_rows); 01983 if (rank == eq_sys_num_rows) 01984 // eq is linearly independent from eq_sys. 01985 return true; 01986 else { 01987 // eq is not linearly independent from eq_sys. 01988 PPL_ASSERT(rank == eq_sys_num_rows - 1); 01989 eq_sys.remove_trailing_rows(1); 01990 return false; 01991 } 01992 } 01993 01994 /* 01995 Modifies the vector of pointers \p ineqs_p, setting to 0 those entries 01996 that point to redundant inequalities or masked equalities. 01997 The redundancy test is based on saturation matrix \p sat and 01998 on knowing that there exists \p rank non-redundant equalities 01999 (they are implicit, i.e., not explicitly listed in \p ineqs_p). 02000 */ 02001 void 02002 drop_redundant_inequalities(std::vector<const PPL::Constraint*>& ineqs_p, 02003 const PPL::Topology topology, 02004 const PPL::Bit_Matrix& sat, 02005 const PPL::dimension_type rank) { 02006 using namespace Parma_Polyhedra_Library; 02007 const dimension_type num_rows = ineqs_p.size(); 02008 PPL_ASSERT(num_rows > 0); 02009 // `rank' is the rank of the (implicit) system of equalities. 02010 const dimension_type space_dim = ineqs_p[0]->space_dimension(); 02011 PPL_ASSERT(space_dim > 0 && space_dim >= rank); 02012 const dimension_type num_coefficients 02013 = space_dim + ((topology == NECESSARILY_CLOSED) ? 0U : 1U); 02014 const dimension_type min_sat = num_coefficients - rank; 02015 const dimension_type num_cols_sat = sat.num_columns(); 02016 02017 // Perform quick redundancy test based on the number of saturators. 02018 for (dimension_type i = num_rows; i-- > 0; ) { 02019 if (sat[i].empty()) 02020 // Masked equalities are redundant. 02021 ineqs_p[i] = 0; 02022 else { 02023 const dimension_type num_sat = num_cols_sat - sat[i].count_ones(); 02024 if (num_sat < min_sat) 02025 ineqs_p[i] = 0; 02026 } 02027 } 02028 02029 // Re-examine remaining inequalities. 02030 // Iteration index `i' is _intentionally_ increasing. 02031 for (dimension_type i = 0; i < num_rows; ++i) { 02032 if (ineqs_p[i] != 0) { 02033 for (dimension_type j = 0; j < num_rows; ++j) { 02034 bool strict_subset; 02035 if (ineqs_p[j] != 0 && i != j 02036 && subset_or_equal(sat[j], sat[i], strict_subset)) { 02037 if (strict_subset) { 02038 ineqs_p[i] = 0; 02039 break; 02040 } 02041 else 02042 // Here `sat[j] == sat[i]'. 02043 ineqs_p[j] = 0; 02044 } 02045 } 02046 } 02047 } 02048 } 02049 02050 } // namespace 02051 02052 bool 02053 PPL::Polyhedron::simplify_using_context_assign(const Polyhedron& y) { 02054 Polyhedron& x = *this; 02055 // Topology compatibility check. 02056 if (x.topology() != y.topology()) 02057 throw_topology_incompatible("simplify_using_context_assign(y)", "y", y); 02058 // Dimension-compatibility check. 02059 if (x.space_dim != y.space_dim) 02060 throw_dimension_incompatible("simplify_using_context_assign(y)", "y", y); 02061 02062 // Filter away the zero-dimensional case. 02063 if (x.space_dim == 0) { 02064 if (y.is_empty()) { 02065 x.set_zero_dim_univ(); 02066 return false; 02067 } 02068 else 02069 return !x.is_empty(); 02070 } 02071 02072 // If `y' is empty, the biggest enlargement for `x' is the universe. 02073 if (!y.minimize()) { 02074 Polyhedron ph(x.topology(), x.space_dim, UNIVERSE); 02075 m_swap(ph); 02076 return false; 02077 } 02078 02079 // If `x' is empty, the intersection is empty. 02080 if (!x.minimize()) { 02081 // Search for a constraint of `y' that is not a tautology. 02082 PPL_ASSERT(!y.has_pending_generators() && y.constraints_are_up_to_date()); 02083 for (dimension_type i = y.con_sys.num_rows(); i-- > 0; ) { 02084 const Constraint& y_con_sys_i = y.con_sys[i]; 02085 if (!y_con_sys_i.is_tautological()) { 02086 // Found: we obtain a constraint `c' contradicting the one we 02087 // found, and assign to `x' the polyhedron `ph' with `c' as 02088 // the only constraint. 02089 Polyhedron ph(x.topology(), x.space_dim, UNIVERSE); 02090 Linear_Expression le(y_con_sys_i); 02091 switch (y_con_sys_i.type()) { 02092 case Constraint::EQUALITY: 02093 ph.refine_no_check(le == 1); 02094 break; 02095 case Constraint::NONSTRICT_INEQUALITY: 02096 ph.refine_no_check(le <= -1); 02097 break; 02098 case Constraint::STRICT_INEQUALITY: 02099 ph.refine_no_check(le == 0); 02100 break; 02101 } 02102 m_swap(ph); 02103 PPL_ASSERT_HEAVY(OK()); 02104 return false; 02105 } 02106 } 02107 // `y' is the universe: `x' cannot be enlarged. 02108 return false; 02109 } 02110 02111 PPL_ASSERT(x.constraints_are_minimized() 02112 && !x.has_something_pending() 02113 && y.generators_are_minimized() 02114 && !y.has_something_pending()); 02115 const Constraint_System& x_cs = x.con_sys; 02116 const dimension_type x_cs_num_rows = x_cs.num_rows(); 02117 const Generator_System& y_gs = y.gen_sys; 02118 02119 // Record into `redundant_by_y' the info about which constraints of 02120 // `x' are redundant in the context `y'. Count the number of 02121 // redundancies found. 02122 std::vector<bool> redundant_by_y(x_cs_num_rows, false); 02123 dimension_type num_redundant_by_y = 0; 02124 for (dimension_type i = 0; i < x_cs_num_rows; ++i) 02125 if (y_gs.satisfied_by_all_generators(x_cs[i])) { 02126 redundant_by_y[i] = true; 02127 ++num_redundant_by_y; 02128 } 02129 02130 Constraint_System result_cs; 02131 02132 if (num_redundant_by_y < x_cs_num_rows) { 02133 // Some constraints were not identified as redundant (yet?). 02134 const Constraint_System& y_cs = y.con_sys; 02135 const dimension_type y_cs_num_rows = y_cs.num_rows(); 02136 // Compute into `z' the minimized intersection of `x' and `y'. 02137 const bool x_first = (x_cs_num_rows > y_cs_num_rows); 02138 Polyhedron z(x_first ? x : y); 02139 if (x_first) 02140 z.add_constraints(y_cs); 02141 else { 02142 // Only copy (and then recycle) the non-redundant constraints. 02143 Constraint_System tmp_cs; 02144 for (dimension_type i = 0; i < x_cs_num_rows; ++i) { 02145 if (!redundant_by_y[i]) 02146 tmp_cs.insert(x_cs[i]); 02147 } 02148 z.add_recycled_constraints(tmp_cs); 02149 } 02150 if (!z.minimize()) { 02151 // The objective function is the default, zero. 02152 // We do not care about minimization or maximization, since 02153 // we are only interested in satisfiability. 02154 MIP_Problem lp; 02155 if (x.is_necessarily_closed()) { 02156 lp.add_space_dimensions_and_embed(x.space_dim); 02157 lp.add_constraints(y_cs); 02158 } 02159 else { 02160 // KLUDGE: temporarily mark `y_cs' if it was necessarily 02161 // closed, so that we can interpret the epsilon dimension as a 02162 // standard dimension. Be careful to reset the topology of `cs' 02163 // even on exceptional execution path. 02164 const_cast<Constraint_System&>(y_cs).set_necessarily_closed(); 02165 try { 02166 lp.add_space_dimensions_and_embed(x.space_dim+1); 02167 lp.add_constraints(y_cs); 02168 const_cast<Constraint_System&>(y_cs).set_not_necessarily_closed(); 02169 } 02170 catch (...) { 02171 const_cast<Constraint_System&>(y_cs).set_not_necessarily_closed(); 02172 throw; 02173 } 02174 } 02175 // We apply the following heuristics here: constraints of `x' that 02176 // are not made redundant by `y' are added to `lp' depending on 02177 // the number of generators of `y' they rule out (the more generators 02178 // they rule out, the sooner they are added). Of course, as soon 02179 // as `lp' becomes unsatisfiable, we stop adding. 02180 std::vector<Ruled_Out_Pair> 02181 ruled_out_vec(x_cs_num_rows - num_redundant_by_y); 02182 for (dimension_type i = 0, j = 0; i < x_cs_num_rows; ++i) { 02183 if (!redundant_by_y[i]) { 02184 const Constraint& c = x_cs[i]; 02185 Topology_Adjusted_Scalar_Product_Sign sps(c); 02186 dimension_type num_ruled_out_generators = 0; 02187 for (Generator_System::const_iterator k = y_gs.begin(), 02188 y_gs_end = y_gs.end(); k != y_gs_end; ++k) { 02189 const Generator& g = *k; 02190 const int sp_sign = sps(g, c); 02191 if (x.is_necessarily_closed()) { 02192 if (g.is_line()) { 02193 // Lines must saturate the constraint. 02194 if (sp_sign != 0) 02195 goto ruled_out; 02196 } 02197 else { 02198 // `g' is either a ray, a point or a closure point. 02199 if (c.is_inequality()) { 02200 // `c' is a non-strict inequality. 02201 if (sp_sign < 0) 02202 goto ruled_out; 02203 } 02204 else 02205 // `c' is an equality. 02206 if (sp_sign != 0) 02207 goto ruled_out; 02208 } 02209 } 02210 else 02211 // The topology is not necessarily closed. 02212 switch (g.type()) { 02213 case Generator::LINE: 02214 // Lines must saturate the constraint. 02215 if (sp_sign != 0) 02216 goto ruled_out; 02217 break; 02218 case Generator::POINT: 02219 // Have to perform the special test when dealing with 02220 // a strict inequality. 02221 switch (c.type()) { 02222 case Constraint::EQUALITY: 02223 if (sp_sign != 0) 02224 goto ruled_out; 02225 break; 02226 case Constraint::NONSTRICT_INEQUALITY: 02227 if (sp_sign < 0) 02228 goto ruled_out; 02229 break; 02230 case Constraint::STRICT_INEQUALITY: 02231 if (sp_sign <= 0) 02232 goto ruled_out; 02233 break; 02234 } 02235 break; 02236 case Generator::RAY: 02237 // Intentionally fall through. 02238 case Generator::CLOSURE_POINT: 02239 if (c.is_inequality()) { 02240 // Constraint `c' is either a strict or a non-strict 02241 // inequality. 02242 if (sp_sign < 0) 02243 goto ruled_out; 02244 } 02245 else 02246 // Constraint `c' is an equality. 02247 if (sp_sign != 0) 02248 goto ruled_out; 02249 break; 02250 } 02251 02252 // If we reach this point, `g' satisfies `c'. 02253 continue; 02254 ruled_out: 02255 ++num_ruled_out_generators; 02256 } 02257 ruled_out_vec[j].constraint_index = i; 02258 ruled_out_vec[j].num_ruled_out = num_ruled_out_generators; 02259 ++j; 02260 } 02261 } 02262 std::sort(ruled_out_vec.begin(), ruled_out_vec.end(), 02263 Ruled_Out_Less_Than()); 02264 02265 for (std::vector<Ruled_Out_Pair>::const_iterator 02266 j = ruled_out_vec.begin(), 02267 ruled_out_vec_end = ruled_out_vec.end(); 02268 j != ruled_out_vec_end; 02269 ++j) { 02270 const Constraint& c = x_cs[j->constraint_index]; 02271 result_cs.insert(c); 02272 lp.add_constraint(c); 02273 MIP_Problem_Status status = lp.solve(); 02274 if (status == UNFEASIBLE_MIP_PROBLEM) { 02275 Polyhedron result_ph(x.topology(), x.space_dim, UNIVERSE); 02276 result_ph.add_constraints(result_cs); 02277 x.m_swap(result_ph); 02278 PPL_ASSERT_HEAVY(x.OK()); 02279 return false; 02280 } 02281 } 02282 // Cannot exit from here. 02283 PPL_UNREACHABLE; 02284 } 02285 else { 02286 // Here `z' is not empty and minimized. 02287 PPL_ASSERT(z.constraints_are_minimized() 02288 && z.generators_are_minimized() 02289 && !z.has_something_pending()); 02290 const Constraint_System& z_cs = z.con_sys; 02291 const Generator_System& z_gs = z.gen_sys; 02292 const dimension_type z_gs_num_rows = z_gs.num_rows(); 02293 02294 // Compute the number of equalities in x_cs, y_cs and z_cs 02295 // (exploiting minimal form knowledge). 02296 dimension_type x_cs_num_eq = 0; 02297 while (x_cs[x_cs_num_eq].is_equality()) 02298 ++x_cs_num_eq; 02299 dimension_type y_cs_num_eq = 0; 02300 while (y_cs[y_cs_num_eq].is_equality()) 02301 ++y_cs_num_eq; 02302 dimension_type z_cs_num_eq = 0; 02303 while (z_cs[z_cs_num_eq].is_equality()) 02304 ++z_cs_num_eq; 02305 PPL_ASSERT(x_cs_num_eq <= z_cs_num_eq && y_cs_num_eq <= z_cs_num_eq); 02306 02307 // Identify non-redundant equalities. 02308 Constraint_System non_redundant_eq; 02309 dimension_type num_non_redundant_eq = 0; 02310 const dimension_type needed_non_redundant_eq = z_cs_num_eq - y_cs_num_eq; 02311 Linear_System eqs(x.topology()); 02312 if (needed_non_redundant_eq > 0) { 02313 // Populate eqs with the equalities from y. 02314 for (dimension_type i = 0; i < y_cs_num_eq; ++i) 02315 eqs.insert(y_cs[i]); 02316 // Try to find another `needed_non_redundant_eq' linear independent 02317 // equalities among those from x. 02318 for (dimension_type i = 0; i < x_cs_num_eq; ++i) { 02319 const Constraint& x_cs_i = x_cs[i]; 02320 if (add_to_system_and_check_independence(eqs, x_cs_i)) { 02321 // x_cs_i is linear independent. 02322 non_redundant_eq.insert(x_cs_i); 02323 ++num_non_redundant_eq; 02324 if (num_non_redundant_eq == needed_non_redundant_eq) 02325 // Already found all the needed equalities. 02326 break; 02327 } 02328 } 02329 // NOTE: if num_non_redundant_eq < needed_non_redundant_eq 02330 // then we haven't found all the needed equalities yet: 02331 // this means that some inequalities from x actually holds 02332 // as "masked" equalities in the context of y. 02333 PPL_ASSERT(eqs.num_rows() <= z_cs_num_eq); 02334 PPL_ASSERT(num_non_redundant_eq <= needed_non_redundant_eq); 02335 PPL_ASSERT(z_cs_num_eq - eqs.num_rows() 02336 == needed_non_redundant_eq - num_non_redundant_eq); 02337 } 02338 02339 // Identify non-redundant inequalities. 02340 // Avoid useless copies (no modifications are needed). 02341 std::vector<const Constraint*> non_redundant_ineq_p; 02342 // Fill non_redundant_ineq_p with (pointers to) inequalities 02343 // from y_cs ... 02344 for (dimension_type i = y_cs_num_eq; i < y_cs_num_rows; ++i) 02345 non_redundant_ineq_p.push_back(&y_cs[i]); 02346 // ... and (pointers to) non-redundant inequalities from x_cs. 02347 for (dimension_type i = x_cs_num_eq; i < x_cs_num_rows; ++i) 02348 if (!redundant_by_y[i]) 02349 non_redundant_ineq_p.push_back(&x_cs[i]); 02350 02351 const dimension_type non_redundant_ineq_p_size 02352 = non_redundant_ineq_p.size(); 02353 const dimension_type y_cs_num_ineq = y_cs_num_rows - y_cs_num_eq; 02354 02355 // Compute saturation info. 02356 const dimension_type sat_num_rows = non_redundant_ineq_p_size; 02357 Bit_Matrix sat(sat_num_rows, z_gs_num_rows); 02358 for (dimension_type i = sat_num_rows; i-- > 0; ) { 02359 const Constraint& non_redundant_ineq_i = *(non_redundant_ineq_p[i]); 02360 Bit_Row& sat_i = sat[i]; 02361 for (dimension_type j = z_gs_num_rows; j-- > 0; ) 02362 if (Scalar_Products::sign(non_redundant_ineq_i, z_gs[j]) != 0) 02363 sat_i.set(j); 02364 if (sat_i.empty() && num_non_redundant_eq < needed_non_redundant_eq) { 02365 // `non_redundant_ineq_i' is actually masking an equality 02366 // and we are still looking for some masked inequalities. 02367 // Iteration goes downwards, so the inequality comes from x_cs. 02368 PPL_ASSERT(i >= y_cs_num_ineq); 02369 // Check if the equality is independent in eqs. 02370 Linear_Row masked_eq = Linear_Row(non_redundant_ineq_i); 02371 masked_eq.set_is_line_or_equality(); 02372 masked_eq.sign_normalize(); 02373 if (add_to_system_and_check_independence(eqs, masked_eq)) { 02374 // It is independent: add the _inequality_ to non_redundant_eq. 02375 non_redundant_eq.insert(non_redundant_ineq_i); 02376 ++num_non_redundant_eq; 02377 } 02378 } 02379 } 02380 // Here we have already found all the needed (masked) equalities. 02381 PPL_ASSERT(num_non_redundant_eq == needed_non_redundant_eq); 02382 02383 drop_redundant_inequalities(non_redundant_ineq_p, x.topology(), 02384 sat, z_cs_num_eq); 02385 02386 // Place the non-redundant (masked) equalities into result_cs. 02387 result_cs.m_swap(non_redundant_eq); 02388 // Add to result_cs the non-redundant inequalities from x_cs, 02389 // i.e., those having indices no smaller than y_cs_num_ineq. 02390 for (dimension_type i = y_cs_num_ineq; 02391 i < non_redundant_ineq_p_size; 02392 ++i) 02393 if (non_redundant_ineq_p[i] != 0) 02394 result_cs.insert(*non_redundant_ineq_p[i]); 02395 } 02396 } 02397 02398 Polyhedron result_ph(x.topology(), x.space_dim, UNIVERSE); 02399 result_ph.add_recycled_constraints(result_cs); 02400 x.m_swap(result_ph); 02401 PPL_ASSERT_HEAVY(x.OK()); 02402 return true; 02403 } 02404 02405 void 02406 PPL::Polyhedron::poly_hull_assign(const Polyhedron& y) { 02407 Polyhedron& x = *this; 02408 // Topology compatibility check. 02409 if (x.topology() != y.topology()) 02410 throw_topology_incompatible("poly_hull_assign(y)", "y", y); 02411 // Dimension-compatibility check. 02412 if (x.space_dim != y.space_dim) 02413 throw_dimension_incompatible("poly_hull_assign(y)", "y", y); 02414 02415 // The poly-hull of a polyhedron `p' with an empty polyhedron is `p'. 02416 if (y.marked_empty()) 02417 return; 02418 if (x.marked_empty()) { 02419 x = y; 02420 return; 02421 } 02422 02423 // If both polyhedra are zero-dimensional, 02424 // then at this point they are necessarily universe polyhedra, 02425 // so that their poly-hull is the universe polyhedron too. 02426 if (x.space_dim == 0) 02427 return; 02428 02429 // Both systems of generators have to be up-to-date, 02430 // possibly having pending generators. 02431 if ((x.has_pending_constraints() && !x.process_pending_constraints()) 02432 || (!x.generators_are_up_to_date() && !x.update_generators())) { 02433 // Discovered `x' empty when updating generators. 02434 x = y; 02435 return; 02436 } 02437 if ((y.has_pending_constraints() && !y.process_pending_constraints()) 02438 || (!y.generators_are_up_to_date() && !y.update_generators())) 02439 // Discovered `y' empty when updating generators. 02440 return; 02441 02442 // Here both systems are up-to-date and possibly have pending generators 02443 // (but they cannot have pending constraints). 02444 PPL_ASSERT(!x.has_pending_constraints() && x.generators_are_up_to_date()); 02445 PPL_ASSERT(!y.has_pending_constraints() && y.generators_are_up_to_date()); 02446 02447 // If `x' can support pending generators, 02448 // the generators of `y' are added as pending generators of `x'. 02449 if (x.can_have_something_pending()) { 02450 x.gen_sys.add_pending_rows(y.gen_sys); 02451 x.set_generators_pending(); 02452 } 02453 else { 02454 // `x' cannot support pending generators. 02455 // If both generator systems are (fully) sorted, then we can merge 02456 // them; otherwise we simply add the second to the first. 02457 if (x.gen_sys.is_sorted() 02458 && y.gen_sys.is_sorted() && !y.has_pending_generators()) 02459 x.gen_sys.merge_rows_assign(y.gen_sys); 02460 else 02461 x.gen_sys.add_rows(y.gen_sys); 02462 // Constraints are no longer up-to-date 02463 // and generators are no longer minimized. 02464 x.clear_constraints_up_to_date(); 02465 x.clear_generators_minimized(); 02466 } 02467 // At this point both `x' and `y' are not empty. 02468 PPL_ASSERT_HEAVY(x.OK(true) && y.OK(true)); 02469 } 02470 02471 void 02472 PPL::Polyhedron::poly_difference_assign(const Polyhedron& y) { 02473 Polyhedron& x = *this; 02474 // Topology compatibility check. 02475 if (x.topology() != y.topology()) 02476 throw_topology_incompatible("poly_difference_assign(y)", "y", y); 02477 // Dimension-compatibility check. 02478 if (x.space_dim != y.space_dim) 02479 throw_dimension_incompatible("poly_difference_assign(y)", "y", y); 02480 02481 // The difference of a polyhedron `p' and an empty polyhedron is `p'. 02482 if (y.marked_empty()) 02483 return; 02484 // The difference of an empty polyhedron and of a polyhedron `p' is empty. 02485 if (x.marked_empty()) 02486 return; 02487 02488 // If both polyhedra are zero-dimensional, 02489 // then at this point they are necessarily universe polyhedra, 02490 // so that their difference is empty. 02491 if (x.space_dim == 0) { 02492 x.set_empty(); 02493 return; 02494 } 02495 02496 // TODO: This is just an executable specification. 02497 // Have to find a more efficient method. 02498 02499 if (y.contains(x)) { 02500 x.set_empty(); 02501 return; 02502 } 02503 02504 // Being lazy here is only harmful. 02505 // `minimize()' will process any pending constraints or generators. 02506 if (!y.minimize()) 02507 return; 02508 x.minimize(); 02509 02510 Polyhedron new_polyhedron(topology(), x.space_dim, EMPTY); 02511 02512 const Constraint_System& y_cs = y.constraints(); 02513 for (Constraint_System::const_iterator i = y_cs.begin(), 02514 y_cs_end = y_cs.end(); i != y_cs_end; ++i) { 02515 const Constraint& c = *i; 02516 PPL_ASSERT(!c.is_tautological()); 02517 PPL_ASSERT(!c.is_inconsistent()); 02518 // If the polyhedron `x' is included in the polyhedron defined by 02519 // `c', then `c' can be skipped, as adding its complement to `x' 02520 // would result in the empty polyhedron. Moreover, if we operate 02521 // on C-polyhedra and `c' is a non-strict inequality, c _must_ be 02522 // skipped for otherwise we would obtain a result that is less 02523 // precise than the poly-difference. 02524 if (x.relation_with(c).implies(Poly_Con_Relation::is_included())) 02525 continue; 02526 Polyhedron z = x; 02527 const Linear_Expression e = Linear_Expression(c); 02528 switch (c.type()) { 02529 case Constraint::NONSTRICT_INEQUALITY: 02530 if (is_necessarily_closed()) 02531 z.refine_no_check(e <= 0); 02532 else 02533 z.refine_no_check(e < 0); 02534 break; 02535 case Constraint::STRICT_INEQUALITY: 02536 z.refine_no_check(e <= 0); 02537 break; 02538 case Constraint::EQUALITY: 02539 if (is_necessarily_closed()) 02540 // We have already filtered out the case 02541 // when `x' is included in `y': the result is `x'. 02542 return; 02543 else { 02544 Polyhedron w = x; 02545 w.refine_no_check(e < 0); 02546 new_polyhedron.poly_hull_assign(w); 02547 z.refine_no_check(e > 0); 02548 } 02549 break; 02550 } 02551 new_polyhedron.poly_hull_assign(z); 02552 } 02553 *this = new_polyhedron; 02554 02555 PPL_ASSERT_HEAVY(OK()); 02556 } 02557 02558 void 02559 PPL::Polyhedron:: 02560 affine_image(const Variable var, 02561 const Linear_Expression& expr, 02562 Coefficient_traits::const_reference denominator) { 02563 // The denominator cannot be zero. 02564 if (denominator == 0) 02565 throw_invalid_argument("affine_image(v, e, d)", "d == 0"); 02566 02567 // Dimension-compatibility checks. 02568 // The dimension of `expr' should not be greater than the dimension 02569 // of `*this'. 02570 if (space_dim < expr.space_dimension()) 02571 throw_dimension_incompatible("affine_image(v, e, d)", "e", expr); 02572 // `var' should be one of the dimensions of the polyhedron. 02573 const dimension_type var_space_dim = var.space_dimension(); 02574 if (space_dim < var_space_dim) 02575 throw_dimension_incompatible("affine_image(v, e, d)", "v", var); 02576 02577 if (marked_empty()) 02578 return; 02579 02580 if (expr.coefficient(var) != 0) { 02581 // The transformation is invertible: 02582 // minimality and saturators are preserved, so that 02583 // pending rows, if present, are correctly handled. 02584 if (generators_are_up_to_date()) { 02585 // Generator_System::affine_image() requires the third argument 02586 // to be a positive Coefficient. 02587 if (denominator > 0) 02588 gen_sys.affine_image(var_space_dim, expr, denominator); 02589 else 02590 gen_sys.affine_image(var_space_dim, -expr, -denominator); 02591 } 02592 if (constraints_are_up_to_date()) { 02593 // To build the inverse transformation, 02594 // after copying and negating `expr', 02595 // we exchange the roles of `expr[var_space_dim]' and `denominator'. 02596 Linear_Expression inverse; 02597 if (expr[var_space_dim] > 0) { 02598 inverse = -expr; 02599 inverse[var_space_dim] = denominator; 02600 con_sys.affine_preimage(var_space_dim, inverse, expr[var_space_dim]); 02601 } 02602 else { 02603 // The new denominator is negative: we negate everything once 02604 // more, as Constraint_System::affine_preimage() requires the 02605 // third argument to be positive. 02606 inverse = expr; 02607 inverse[var_space_dim] = denominator; 02608 neg_assign(inverse[var_space_dim]); 02609 con_sys.affine_preimage(var_space_dim, inverse, -expr[var_space_dim]); 02610 } 02611 } 02612 } 02613 else { 02614 // The transformation is not invertible. 02615 // We need an up-to-date system of generators. 02616 if (has_something_pending()) 02617 remove_pending_to_obtain_generators(); 02618 else if (!generators_are_up_to_date()) 02619 minimize(); 02620 if (!marked_empty()) { 02621 // Generator_System::affine_image() requires the third argument 02622 // to be a positive Coefficient. 02623 if (denominator > 0) 02624 gen_sys.affine_image(var_space_dim, expr, denominator); 02625 else 02626 gen_sys.affine_image(var_space_dim, -expr, -denominator); 02627 02628 clear_constraints_up_to_date(); 02629 clear_generators_minimized(); 02630 clear_sat_c_up_to_date(); 02631 clear_sat_g_up_to_date(); 02632 } 02633 } 02634 PPL_ASSERT_HEAVY(OK()); 02635 } 02636 02637 02638 void 02639 PPL::Polyhedron:: 02640 affine_preimage(const Variable var, 02641 const Linear_Expression& expr, 02642 Coefficient_traits::const_reference denominator) { 02643 // The denominator cannot be zero. 02644 if (denominator == 0) 02645 throw_invalid_argument("affine_preimage(v, e, d)", "d == 0"); 02646 02647 // Dimension-compatibility checks. 02648 // The dimension of `expr' should not be greater than the dimension 02649 // of `*this'. 02650 if (space_dim < expr.space_dimension()) 02651 throw_dimension_incompatible("affine_preimage(v, e, d)", "e", expr); 02652 // `var' should be one of the dimensions of the polyhedron. 02653 const dimension_type var_space_dim = var.space_dimension(); 02654 if (space_dim < var_space_dim) 02655 throw_dimension_incompatible("affine_preimage(v, e, d)", "v", var); 02656 02657 if (marked_empty()) 02658 return; 02659 02660 if (expr.coefficient(var) != 0) { 02661 // The transformation is invertible: 02662 // minimality and saturators are preserved. 02663 if (constraints_are_up_to_date()) { 02664 // Constraint_System::affine_preimage() requires the third argument 02665 // to be a positive Coefficient. 02666 if (denominator > 0) 02667 con_sys.affine_preimage(var_space_dim, expr, denominator); 02668 else 02669 con_sys.affine_preimage(var_space_dim, -expr, -denominator); 02670 } 02671 if (generators_are_up_to_date()) { 02672 // To build the inverse transformation, 02673 // after copying and negating `expr', 02674 // we exchange the roles of `expr[var_space_dim]' and `denominator'. 02675 Linear_Expression inverse; 02676 if (expr[var_space_dim] > 0) { 02677 inverse = -expr; 02678 inverse[var_space_dim] = denominator; 02679 gen_sys.affine_image(var_space_dim, inverse, expr[var_space_dim]); 02680 } 02681 else { 02682 // The new denominator is negative: 02683 // we negate everything once more, as Generator_System::affine_image() 02684 // requires the third argument to be positive. 02685 inverse = expr; 02686 inverse[var_space_dim] = denominator; 02687 neg_assign(inverse[var_space_dim]); 02688 gen_sys.affine_image(var_space_dim, inverse, -expr[var_space_dim]); 02689 } 02690 } 02691 } 02692 else { 02693 // The transformation is not invertible. 02694 // We need an up-to-date system of constraints. 02695 if (has_something_pending()) 02696 remove_pending_to_obtain_constraints(); 02697 else if (!constraints_are_up_to_date()) 02698 minimize(); 02699 // Constraint_System::affine_preimage() requires the third argument 02700 // to be a positive Coefficient. 02701 if (denominator > 0) 02702 con_sys.affine_preimage(var_space_dim, expr, denominator); 02703 else 02704 con_sys.affine_preimage(var_space_dim, -expr, -denominator); 02705 // Generators, minimality and saturators are no longer valid. 02706 clear_generators_up_to_date(); 02707 clear_constraints_minimized(); 02708 clear_sat_c_up_to_date(); 02709 clear_sat_g_up_to_date(); 02710 } 02711 PPL_ASSERT_HEAVY(OK()); 02712 } 02713 02714 void 02715 PPL::Polyhedron:: 02716 bounded_affine_image(const Variable var, 02717 const Linear_Expression& lb_expr, 02718 const Linear_Expression& ub_expr, 02719 Coefficient_traits::const_reference denominator) { 02720 // The denominator cannot be zero. 02721 if (denominator == 0) 02722 throw_invalid_argument("bounded_affine_image(v, lb, ub, d)", "d == 0"); 02723 02724 // Dimension-compatibility checks. 02725 // `var' should be one of the dimensions of the polyhedron. 02726 const dimension_type var_space_dim = var.space_dimension(); 02727 if (space_dim < var_space_dim) 02728 throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)", 02729 "v", var); 02730 // The dimension of `lb_expr' and `ub_expr' should not be 02731 // greater than the dimension of `*this'. 02732 const dimension_type lb_space_dim = lb_expr.space_dimension(); 02733 if (space_dim < lb_space_dim) 02734 throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)", 02735 "lb", lb_expr); 02736 const dimension_type ub_space_dim = ub_expr.space_dimension(); 02737 if (space_dim < ub_space_dim) 02738 throw_dimension_incompatible("bounded_affine_image(v, lb, ub, d)", 02739 "ub", ub_expr); 02740 02741 // Any image of an empty polyhedron is empty. 02742 if (marked_empty()) 02743 return; 02744 02745 // Check whether `var' occurs in `lb_expr' and/or `ub_expr'. 02746 if (lb_expr.coefficient(var) == 0) { 02747 // Here `var' may only occur in `ub_expr'. 02748 generalized_affine_image(var, 02749 LESS_OR_EQUAL, 02750 ub_expr, 02751 denominator); 02752 if (denominator > 0) 02753 refine_no_check(lb_expr <= denominator*var); 02754 else 02755 refine_no_check(denominator*var <= lb_expr); 02756 } 02757 else if (ub_expr.coefficient(var) == 0) { 02758 // Here `var' only occurs in `lb_expr'. 02759 generalized_affine_image(var, 02760 GREATER_OR_EQUAL, 02761 lb_expr, 02762 denominator); 02763 if (denominator > 0) 02764 refine_no_check(denominator*var <= ub_expr); 02765 else 02766 refine_no_check(ub_expr <= denominator*var); 02767 } 02768 else { 02769 // Here `var' occurs in both `lb_expr' and `ub_expr'. 02770 // To ease the computation, we add an additional dimension. 02771 const Variable new_var(space_dim); 02772 add_space_dimensions_and_embed(1); 02773 // Constrain the new dimension to be equal to `ub_expr'. 02774 refine_no_check(denominator*new_var == ub_expr); 02775 // Apply the affine lower bound. 02776 generalized_affine_image(var, 02777 GREATER_OR_EQUAL, 02778 lb_expr, 02779 denominator); 02780 if (!marked_empty()) 02781 // Now apply the affine upper bound, as recorded in `new_var'. 02782 refine_no_check(new_var >= var); 02783 // Remove the temporarily added dimension. 02784 remove_higher_space_dimensions(space_dim-1); 02785 } 02786 PPL_ASSERT_HEAVY(OK()); 02787 } 02788 02789 void 02790 PPL::Polyhedron:: 02791 bounded_affine_preimage(const Variable var, 02792 const Linear_Expression& lb_expr, 02793 const Linear_Expression& ub_expr, 02794 Coefficient_traits::const_reference denominator) { 02795 // The denominator cannot be zero. 02796 if (denominator == 0) 02797 throw_invalid_argument("bounded_affine_preimage(v, lb, ub, d)", "d == 0"); 02798 02799 // Dimension-compatibility checks. 02800 // `var' should be one of the dimensions of the polyhedron. 02801 const dimension_type var_space_dim = var.space_dimension(); 02802 if (space_dim < var_space_dim) 02803 throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)", 02804 "v", var); 02805 // The dimension of `lb_expr' and `ub_expr' should not be 02806 // greater than the dimension of `*this'. 02807 const dimension_type lb_space_dim = lb_expr.space_dimension(); 02808 if (space_dim < lb_space_dim) 02809 throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)", 02810 "lb", lb_expr); 02811 const dimension_type ub_space_dim = ub_expr.space_dimension(); 02812 if (space_dim < ub_space_dim) 02813 throw_dimension_incompatible("bounded_affine_preimage(v, lb, ub, d)", 02814 "ub", ub_expr); 02815 02816 // Any preimage of an empty polyhedron is empty. 02817 if (marked_empty()) 02818 return; 02819 02820 // Check whether `var' occurs in neither `lb_expr' nor `ub_expr'. 02821 if (lb_expr.coefficient(var) == 0 && ub_expr.coefficient(var) == 0) { 02822 if (denominator > 0) { 02823 refine_no_check(lb_expr <= denominator*var); 02824 refine_no_check(denominator*var <= ub_expr); 02825 } 02826 else { 02827 refine_no_check(ub_expr <= denominator*var); 02828 refine_no_check(denominator*var <= lb_expr); 02829 } 02830 unconstrain(var); 02831 } 02832 else { 02833 // Here `var' occurs in `lb_expr' or `ub_expr'. 02834 // To ease the computation, add an additional dimension. 02835 const Variable new_var(space_dim); 02836 add_space_dimensions_and_embed(1); 02837 // Swap dimensions `var' and `new_var'. 02838 std::vector<dimension_type> swapping_cycle; 02839 swapping_cycle.push_back(var_space_dim); 02840 swapping_cycle.push_back(space_dim); 02841 swapping_cycle.push_back(0); 02842 if (constraints_are_up_to_date()) 02843 con_sys.permute_columns(swapping_cycle); 02844 if (generators_are_up_to_date()) 02845 gen_sys.permute_columns(swapping_cycle); 02846 // Constrain the new dimension as dictated by `lb_expr' and `ub_expr'. 02847 // (we force minimization because we will need the generators). 02848 if (denominator > 0) { 02849 refine_no_check(lb_expr <= denominator*new_var); 02850 refine_no_check(denominator*new_var <= ub_expr); 02851 } 02852 else { 02853 refine_no_check(ub_expr <= denominator*new_var); 02854 refine_no_check(denominator*new_var <= lb_expr); 02855 } 02856 // Remove the temporarily added dimension. 02857 remove_higher_space_dimensions(space_dim-1); 02858 } 02859 PPL_ASSERT_HEAVY(OK()); 02860 } 02861 02862 void 02863 PPL::Polyhedron:: 02864 generalized_affine_image(const Variable var, 02865 const Relation_Symbol relsym, 02866 const Linear_Expression& expr, 02867 Coefficient_traits::const_reference denominator) { 02868 // The denominator cannot be zero. 02869 if (denominator == 0) 02870 throw_invalid_argument("generalized_affine_image(v, r, e, d)", "d == 0"); 02871 02872 // Dimension-compatibility checks. 02873 // The dimension of `expr' should not be greater than the dimension 02874 // of `*this'. 02875 if (space_dim < expr.space_dimension()) 02876 throw_dimension_incompatible("generalized_affine_image(v, r, e, d)", 02877 "e", expr); 02878 // `var' should be one of the dimensions of the polyhedron. 02879 const dimension_type var_space_dim = var.space_dimension(); 02880 if (space_dim < var_space_dim) 02881 throw_dimension_incompatible("generalized_affine_image(v, r, e, d)", 02882 "v", var); 02883 02884 // Strict relation symbols are only admitted for NNC polyhedra. 02885 if (is_necessarily_closed() 02886 && (relsym == LESS_THAN || relsym == GREATER_THAN)) 02887 throw_invalid_argument("generalized_affine_image(v, r, e, d)", 02888 "r is a strict relation symbol"); 02889 // The relation symbol cannot be a disequality. 02890 if (relsym == NOT_EQUAL) 02891 throw_invalid_argument("generalized_affine_image(v, r, e, d)", 02892 "r is the disequality relation symbol"); 02893 02894 // First compute the affine image. 02895 affine_image(var, expr, denominator); 02896 02897 if (relsym == EQUAL) 02898 // The affine relation is indeed an affine function. 02899 return; 02900 02901 // Any image of an empty polyhedron is empty. 02902 // Note: DO check for emptiness here, as we will later add a ray. 02903 if (is_empty()) 02904 return; 02905 02906 switch (relsym) { 02907 case LESS_OR_EQUAL: 02908 add_generator(ray(-var)); 02909 break; 02910 case GREATER_OR_EQUAL: 02911 add_generator(ray(var)); 02912 break; 02913 case LESS_THAN: 02914 // Intentionally fall through. 02915 case GREATER_THAN: 02916 { 02917 // The relation symbol is strict. 02918 PPL_ASSERT(!is_necessarily_closed()); 02919 // While adding the ray, we minimize the generators 02920 // in order to avoid adding too many redundant generators later. 02921 add_generator(ray((relsym == GREATER_THAN) ? var : -var)); 02922 minimize(); 02923 // We split each point of the generator system into two generators: 02924 // a closure point, having the same coordinates of the given point, 02925 // and another point, having the same coordinates for all but the 02926 // `var' dimension, which is displaced along the direction of the 02927 // newly introduced ray. 02928 const dimension_type eps_index = space_dim + 1; 02929 for (dimension_type i = gen_sys.num_rows(); i-- > 0; ) 02930 if (gen_sys[i].is_point()) { 02931 // Add a `var'-displaced copy of `gen_sys[i]' to the generator system. 02932 gen_sys.add_row(gen_sys[i]); 02933 if (relsym == GREATER_THAN) 02934 ++gen_sys[gen_sys.num_rows()-1][var_space_dim]; 02935 else 02936 --gen_sys[gen_sys.num_rows()-1][var_space_dim]; 02937 // Transform `gen_sys[i]' into a closure point. 02938 gen_sys[i][eps_index] = 0; 02939 } 02940 clear_constraints_up_to_date(); 02941 clear_generators_minimized(); 02942 gen_sys.set_sorted(false); 02943 clear_sat_c_up_to_date(); 02944 clear_sat_g_up_to_date(); 02945 } 02946 break; 02947 default: 02948 // The EQUAL and NOT_EQUAL cases have been already dealt with. 02949 PPL_UNREACHABLE; 02950 break; 02951 } 02952 PPL_ASSERT_HEAVY(OK()); 02953 } 02954 02955 void 02956 PPL::Polyhedron:: 02957 generalized_affine_preimage(const Variable var, 02958 const Relation_Symbol relsym, 02959 const Linear_Expression& expr, 02960 Coefficient_traits::const_reference denominator) { 02961 // The denominator cannot be zero. 02962 if (denominator == 0) 02963 throw_invalid_argument("generalized_affine_preimage(v, r, e, d)", 02964 "d == 0"); 02965 02966 // Dimension-compatibility checks. 02967 // The dimension of `expr' should not be greater than the dimension 02968 // of `*this'. 02969 if (space_dim < expr.space_dimension()) 02970 throw_dimension_incompatible("generalized_affine_preimage(v, r, e, d)", 02971 "e", expr); 02972 // `var' should be one of the dimensions of the polyhedron. 02973 const dimension_type var_space_dim = var.space_dimension(); 02974 if (space_dim < var_space_dim) 02975 throw_dimension_incompatible("generalized_affine_preimage(v, r, e, d)", 02976 "v", var); 02977 02978 // Strict relation symbols are only admitted for NNC polyhedra. 02979 if (is_necessarily_closed() 02980 && (relsym == LESS_THAN || relsym == GREATER_THAN)) 02981 throw_invalid_argument("generalized_affine_preimage(v, r, e, d)", 02982 "r is a strict relation symbol"); 02983 // The relation symbol cannot be a disequality. 02984 if (relsym == NOT_EQUAL) 02985 throw_invalid_argument("generalized_affine_preimage(v, r, e, d)", 02986 "r is the disequality relation symbol"); 02987 02988 // Check whether the affine relation is indeed an affine function. 02989 if (relsym == EQUAL) { 02990 affine_preimage(var, expr, denominator); 02991 return; 02992 } 02993 02994 // Compute the reversed relation symbol to simplify later coding. 02995 Relation_Symbol reversed_relsym; 02996 switch (relsym) { 02997 case LESS_THAN: 02998 reversed_relsym = GREATER_THAN; 02999 break; 03000 case LESS_OR_EQUAL: 03001 reversed_relsym = GREATER_OR_EQUAL; 03002 break; 03003 case GREATER_OR_EQUAL: 03004 reversed_relsym = LESS_OR_EQUAL; 03005 break; 03006 case GREATER_THAN: 03007 reversed_relsym = LESS_THAN; 03008 break; 03009 default: 03010 // The EQUAL and NOT_EQUAL cases have been already dealt with. 03011 PPL_UNREACHABLE; 03012 return; 03013 } 03014 03015 // Check whether the preimage of this affine relation can be easily 03016 // computed as the image of its inverse relation. 03017 const Coefficient& var_coefficient = expr.coefficient(var); 03018 if (var_coefficient != 0) { 03019 Linear_Expression inverse_expr 03020 = expr - (denominator + var_coefficient) * var; 03021 PPL_DIRTY_TEMP_COEFFICIENT(inverse_denominator); 03022 neg_assign(inverse_denominator, var_coefficient); 03023 Relation_Symbol inverse_relsym 03024 = (sgn(denominator) == sgn(inverse_denominator)) 03025 ? relsym : reversed_relsym; 03026 generalized_affine_image(var, inverse_relsym, inverse_expr, 03027 inverse_denominator); 03028 return; 03029 } 03030 03031 // Here `var_coefficient == 0', so that the preimage cannot 03032 // be easily computed by inverting the affine relation. 03033 // Shrink the polyhedron by adding the constraint induced 03034 // by the affine relation. 03035 const Relation_Symbol corrected_relsym 03036 = (denominator > 0) ? relsym : reversed_relsym; 03037 switch (corrected_relsym) { 03038 case LESS_THAN: 03039 refine_no_check(denominator*var < expr); 03040 break; 03041 case LESS_OR_EQUAL: 03042 refine_no_check(denominator*var <= expr); 03043 break; 03044 case GREATER_OR_EQUAL: 03045 refine_no_check(denominator*var >= expr); 03046 break; 03047 case GREATER_THAN: 03048 refine_no_check(denominator*var > expr); 03049 break; 03050 default: 03051 // The EQUAL and NOT_EQUAL cases have been already dealt with. 03052 PPL_UNREACHABLE; 03053 break; 03054 } 03055 unconstrain(var); 03056 PPL_ASSERT_HEAVY(OK()); 03057 } 03058 03059 void 03060 PPL::Polyhedron::generalized_affine_image(const Linear_Expression& lhs, 03061 const Relation_Symbol relsym, 03062 const Linear_Expression& rhs) { 03063 // Dimension-compatibility checks. 03064 // The dimension of `lhs' should not be greater than the dimension 03065 // of `*this'. 03066 dimension_type lhs_space_dim = lhs.space_dimension(); 03067 if (space_dim < lhs_space_dim) 03068 throw_dimension_incompatible("generalized_affine_image(e1, r, e2)", 03069 "e1", lhs); 03070 // The dimension of `rhs' should not be greater than the dimension 03071 // of `*this'. 03072 const dimension_type rhs_space_dim = rhs.space_dimension(); 03073 if (space_dim < rhs_space_dim) 03074 throw_dimension_incompatible("generalized_affine_image(e1, r, e2)", 03075 "e2", rhs); 03076 03077 // Strict relation symbols are only admitted for NNC polyhedra. 03078 if (is_necessarily_closed() 03079 && (relsym == LESS_THAN || relsym == GREATER_THAN)) 03080 throw_invalid_argument("generalized_affine_image(e1, r, e2)", 03081 "r is a strict relation symbol"); 03082 // The relation symbol cannot be a disequality. 03083 if (relsym == NOT_EQUAL) 03084 throw_invalid_argument("generalized_affine_image(e1, r, e2)", 03085 "r is the disequality relation symbol"); 03086 03087 // Any image of an empty polyhedron is empty. 03088 if (marked_empty()) 03089 return; 03090 03091 // Compute the actual space dimension of `lhs', 03092 // i.e., the highest dimension having a non-zero coefficient in `lhs'. 03093 for ( ; lhs_space_dim > 0; lhs_space_dim--) 03094 if (lhs.coefficient(Variable(lhs_space_dim - 1)) != 0) 03095 break; 03096 // If all variables have a zero coefficient, then `lhs' is a constant: 03097 // we can simply add the constraint `lhs relsym rhs'. 03098 if (lhs_space_dim == 0) { 03099 switch (relsym) { 03100 case LESS_THAN: 03101 refine_no_check(lhs < rhs); 03102 break; 03103 case LESS_OR_EQUAL: 03104 refine_no_check(lhs <= rhs); 03105 break; 03106 case EQUAL: 03107 refine_no_check(lhs == rhs); 03108 break; 03109 case GREATER_OR_EQUAL: 03110 refine_no_check(lhs >= rhs); 03111 break; 03112 case GREATER_THAN: 03113 refine_no_check(lhs > rhs); 03114 break; 03115 case NOT_EQUAL: 03116 // The NOT_EQUAL case has been already dealt with. 03117 PPL_UNREACHABLE; 03118 break; 03119 } 03120 return; 03121 } 03122 03123 // Gather in `new_lines' the collections of all the lines having 03124 // the direction of variables occurring in `lhs'. 03125 // While at it, check whether or not there exists a variable 03126 // occurring in both `lhs' and `rhs'. 03127 Generator_System new_lines; 03128 bool lhs_vars_intersects_rhs_vars = false; 03129 for (dimension_type i = lhs_space_dim; i-- > 0; ) 03130 if (lhs.coefficient(Variable(i)) != 0) { 03131 new_lines.insert(line(Variable(i))); 03132 if (rhs.coefficient(Variable(i)) != 0) 03133 lhs_vars_intersects_rhs_vars = true; 03134 } 03135 03136 if (lhs_vars_intersects_rhs_vars) { 03137 // Some variables in `lhs' also occur in `rhs'. 03138 // To ease the computation, we add an additional dimension. 03139 const Variable new_var(space_dim); 03140 add_space_dimensions_and_embed(1); 03141 03142 // Constrain the new dimension to be equal to the right hand side. 03143 // (check for emptiness because we will add lines). 03144 refine_no_check(new_var == rhs); 03145 if (!is_empty()) { 03146 // Existentially quantify the variables in the left hand side. 03147 add_recycled_generators(new_lines); 03148 03149 // Constrain the new dimension so that it is related to 03150 // the left hand side as dictated by `relsym' 03151 // (we force minimization because we will need the generators). 03152 switch (relsym) { 03153 case LESS_THAN: 03154 refine_no_check(lhs < new_var); 03155 break; 03156 case LESS_OR_EQUAL: 03157 refine_no_check(lhs <= new_var); 03158 break; 03159 case EQUAL: 03160 refine_no_check(lhs == new_var); 03161 break; 03162 case GREATER_OR_EQUAL: 03163 refine_no_check(lhs >= new_var); 03164 break; 03165 case GREATER_THAN: 03166 refine_no_check(lhs > new_var); 03167 break; 03168 case NOT_EQUAL: 03169 // The NOT_EQUAL case has been already dealt with. 03170 PPL_UNREACHABLE; 03171 break; 03172 } 03173 } 03174 // Remove the temporarily added dimension. 03175 remove_higher_space_dimensions(space_dim-1); 03176 } 03177 else { 03178 // `lhs' and `rhs' variables are disjoint: 03179 // there is no need to add a further dimension. 03180 03181 // Any image of an empty polyhedron is empty. 03182 // Note: DO check for emptiness here, as we will add lines. 03183 if (is_empty()) 03184 return; 03185 03186 // Existentially quantify the variables in the left hand side. 03187 add_recycled_generators(new_lines); 03188 03189 // Constrain the left hand side expression so that it is related to 03190 // the right hand side expression as dictated by `relsym'. 03191 switch (relsym) { 03192 case LESS_THAN: 03193 refine_no_check(lhs < rhs); 03194 break; 03195 case LESS_OR_EQUAL: 03196 refine_no_check(lhs <= rhs); 03197 break; 03198 case EQUAL: 03199 refine_no_check(lhs == rhs); 03200 break; 03201 case GREATER_OR_EQUAL: 03202 refine_no_check(lhs >= rhs); 03203 break; 03204 case GREATER_THAN: 03205 refine_no_check(lhs > rhs); 03206 break; 03207 case NOT_EQUAL: 03208 // The NOT_EQUAL case has been already dealt with. 03209 PPL_UNREACHABLE; 03210 break; 03211 } 03212 } 03213 PPL_ASSERT_HEAVY(OK()); 03214 } 03215 03216 void 03217 PPL::Polyhedron::generalized_affine_preimage(const Linear_Expression& lhs, 03218 const Relation_Symbol relsym, 03219 const Linear_Expression& rhs) { 03220 // Dimension-compatibility checks. 03221 // The dimension of `lhs' should not be greater than the dimension 03222 // of `*this'. 03223 dimension_type lhs_space_dim = lhs.space_dimension(); 03224 if (space_dim < lhs_space_dim) 03225 throw_dimension_incompatible("generalized_affine_preimage(e1, r, e2)", 03226 "e1", lhs); 03227 // The dimension of `rhs' should not be greater than the dimension 03228 // of `*this'. 03229 const dimension_type rhs_space_dim = rhs.space_dimension(); 03230 if (space_dim < rhs_space_dim) 03231 throw_dimension_incompatible("generalized_affine_preimage(e1, r, e2)", 03232 "e2", rhs); 03233 03234 // Strict relation symbols are only admitted for NNC polyhedra. 03235 if (is_necessarily_closed() 03236 && (relsym == LESS_THAN || relsym == GREATER_THAN)) 03237 throw_invalid_argument("generalized_affine_preimage(e1, r, e2)", 03238 "r is a strict relation symbol"); 03239 // The relation symbol cannot be a disequality. 03240 if (relsym == NOT_EQUAL) 03241 throw_invalid_argument("generalized_affine_preimage(e1, r, e2)", 03242 "r is the disequality relation symbol"); 03243 03244 // Any preimage of an empty polyhedron is empty. 03245 if (marked_empty()) 03246 return; 03247 03248 // Compute the actual space dimension of `lhs', 03249 // i.e., the highest dimension having a non-zero coefficient in `lhs'. 03250 for ( ; lhs_space_dim > 0; lhs_space_dim--) 03251 if (lhs.coefficient(Variable(lhs_space_dim - 1)) != 0) 03252 break; 03253 03254 // If all variables have a zero coefficient, then `lhs' is a constant: 03255 // in this case, preimage and image happen to be the same. 03256 if (lhs_space_dim == 0) { 03257 generalized_affine_image(lhs, relsym, rhs); 03258 return; 03259 } 03260 03261 // Gather in `new_lines' the collections of all the lines having 03262 // the direction of variables occurring in `lhs'. 03263 // While at it, check whether or not there exists a variable 03264 // occurring in both `lhs' and `rhs'. 03265 Generator_System new_lines; 03266 bool lhs_vars_intersects_rhs_vars = false; 03267 for (dimension_type i = lhs_space_dim; i-- > 0; ) 03268 if (lhs.coefficient(Variable(i)) != 0) { 03269 new_lines.insert(line(Variable(i))); 03270 if (rhs.coefficient(Variable(i)) != 0) 03271 lhs_vars_intersects_rhs_vars = true; 03272 } 03273 03274 if (lhs_vars_intersects_rhs_vars) { 03275 // Some variables in `lhs' also occur in `rhs'. 03276 // To ease the computation, we add an additional dimension. 03277 const Variable new_var(space_dim); 03278 add_space_dimensions_and_embed(1); 03279 03280 // Constrain the new dimension to be equal to `lhs' 03281 // (also check for emptiness because we have to add lines). 03282 refine_no_check(new_var == lhs); 03283 if (!is_empty()) { 03284 // Existentially quantify the variables in the left hand side. 03285 add_recycled_generators(new_lines); 03286 03287 // Constrain the new dimension so that it is related to 03288 // the right hand side as dictated by `relsym'. 03289 switch (relsym) { 03290 case LESS_THAN: 03291 refine_no_check(new_var < rhs); 03292 break; 03293 case LESS_OR_EQUAL: 03294 refine_no_check(new_var <= rhs); 03295 break; 03296 case EQUAL: 03297 refine_no_check(new_var == rhs); 03298 break; 03299 case GREATER_OR_EQUAL: 03300 refine_no_check(new_var >= rhs); 03301 break; 03302 case GREATER_THAN: 03303 refine_no_check(new_var > rhs); 03304 break; 03305 case NOT_EQUAL: 03306 // The NOT_EQUAL case has been already dealt with. 03307 PPL_UNREACHABLE; 03308 break; 03309 } 03310 } 03311 // Remove the temporarily added dimension. 03312 remove_higher_space_dimensions(space_dim-1); 03313 } 03314 else { 03315 // `lhs' and `rhs' variables are disjoint: 03316 // there is no need to add a further dimension. 03317 03318 // Constrain the left hand side expression so that it is related to 03319 // the right hand side expression as dictated by `relsym'. 03320 switch (relsym) { 03321 case LESS_THAN: 03322 refine_no_check(lhs < rhs); 03323 break; 03324 case LESS_OR_EQUAL: 03325 refine_no_check(lhs <= rhs); 03326 break; 03327 case EQUAL: 03328 refine_no_check(lhs == rhs); 03329 break; 03330 case GREATER_OR_EQUAL: 03331 refine_no_check(lhs >= rhs); 03332 break; 03333 case GREATER_THAN: 03334 refine_no_check(lhs > rhs); 03335 break; 03336 case NOT_EQUAL: 03337 // The NOT_EQUAL case has been already dealt with. 03338 PPL_UNREACHABLE; 03339 break; 03340 } 03341 // Any image of an empty polyhedron is empty. 03342 // Note: DO check for emptiness here, as we will add lines. 03343 if (is_empty()) 03344 return; 03345 // Existentially quantify all the variables occurring in `lhs'. 03346 add_recycled_generators(new_lines); 03347 } 03348 PPL_ASSERT_HEAVY(OK()); 03349 } 03350 03351 void 03352 PPL::Polyhedron::time_elapse_assign(const Polyhedron& y) { 03353 Polyhedron& x = *this; 03354 // Topology compatibility check. 03355 if (x.topology() != y.topology()) 03356 throw_topology_incompatible("time_elapse_assign(y)", "y", y); 03357 // Dimension-compatibility checks. 03358 if (x.space_dim != y.space_dim) 03359 throw_dimension_incompatible("time_elapse_assign(y)", "y", y); 03360 03361 // Dealing with the zero-dimensional case. 03362 if (x.space_dim == 0) { 03363 if (y.marked_empty()) 03364 x.set_empty(); 03365 return; 03366 } 03367 03368 // If either one of `x' or `y' is empty, the result is empty too. 03369 if (x.marked_empty() || y.marked_empty() 03370 || (x.has_pending_constraints() && !x.process_pending_constraints()) 03371 || (!x.generators_are_up_to_date() && !x.update_generators()) 03372 || (y.has_pending_constraints() && !y.process_pending_constraints()) 03373 || (!y.generators_are_up_to_date() && !y.update_generators())) { 03374 x.set_empty(); 03375 return; 03376 } 03377 03378 // At this point both generator systems are up-to-date, 03379 // possibly containing pending generators. 03380 Generator_System gs = y.gen_sys; 03381 const dimension_type old_gs_num_rows = gs.num_rows(); 03382 dimension_type gs_num_rows = old_gs_num_rows; 03383 03384 if (!x.is_necessarily_closed()) { 03385 // `x' and `y' are NNC polyhedra. 03386 for (dimension_type i = gs_num_rows; i-- > 0; ) 03387 switch (gs[i].type()) { 03388 case Generator::POINT: 03389 // The points of `gs' can be erased, 03390 // since their role can be played by closure points. 03391 --gs_num_rows; 03392 using std::swap; 03393 swap(gs[i], gs[gs_num_rows]); 03394 break; 03395 case Generator::CLOSURE_POINT: 03396 { 03397 Generator& cp = gs[i]; 03398 // If it is the origin, erase it. 03399 if (cp.all_homogeneous_terms_are_zero()) { 03400 --gs_num_rows; 03401 using std::swap; 03402 swap(cp, gs[gs_num_rows]); 03403 } 03404 // Otherwise, transform the closure point into a ray. 03405 else { 03406 cp[0] = 0; 03407 // Enforce normalization. 03408 cp.normalize(); 03409 } 03410 } 03411 break; 03412 case Generator::RAY: 03413 case Generator::LINE: 03414 // For rays and lines, nothing to be done. 03415 break; 03416 } 03417 } 03418 else { 03419 // `x' and `y' are C polyhedra. 03420 for (dimension_type i = gs_num_rows; i-- > 0; ) { 03421 // For rays and lines, nothing to be done. 03422 if (gs[i].is_point()) { 03423 Generator& p = gs[i]; 03424 // If it is the origin, erase it. 03425 if (p.all_homogeneous_terms_are_zero()) { 03426 --gs_num_rows; 03427 using std::swap; 03428 swap(p, gs[gs_num_rows]); 03429 } 03430 // Otherwise, transform the point into a ray. 03431 else { 03432 p[0] = 0; 03433 // Enforce normalization. 03434 p.normalize(); 03435 } 03436 } 03437 } 03438 } 03439 // If it was present, erase the origin point or closure point, 03440 // which cannot be transformed into a valid ray or line. 03441 // For NNC polyhedra, also erase all the points of `gs', 03442 // whose role can be played by the closure points. 03443 // These have been previously moved to the end of `gs'. 03444 gs.remove_trailing_rows(old_gs_num_rows - gs_num_rows); 03445 gs.unset_pending_rows(); 03446 03447 // `gs' may now have no rows. 03448 // Namely, this happens when `y' was the singleton polyhedron 03449 // having the origin as the one and only point. 03450 // In such a case, the resulting polyhedron is equal to `x'. 03451 if (gs_num_rows == 0) 03452 return; 03453 03454 // If the polyhedron can have something pending, we add `gs' 03455 // to `gen_sys' as pending rows 03456 if (x.can_have_something_pending()) { 03457 x.gen_sys.add_pending_rows(gs); 03458 x.set_generators_pending(); 03459 } 03460 // Otherwise, the two systems are merged. 03461 // `Linear_System::merge_rows_assign()' requires both systems to be sorted. 03462 else { 03463 if (!x.gen_sys.is_sorted()) 03464 x.gen_sys.sort_rows(); 03465 gs.sort_rows(); 03466 x.gen_sys.merge_rows_assign(gs); 03467 // Only the system of generators is up-to-date. 03468 x.clear_constraints_up_to_date(); 03469 x.clear_generators_minimized(); 03470 } 03471 PPL_ASSERT_HEAVY(x.OK(true) && y.OK(true)); 03472 } 03473 03474 bool 03475 PPL::Polyhedron::frequency(const Linear_Expression& expr, 03476 Coefficient& freq_n, Coefficient& freq_d, 03477 Coefficient& val_n, Coefficient& val_d) const { 03478 // The dimension of `expr' must be at most the dimension of *this. 03479 if (space_dim < expr.space_dimension()) 03480 throw_dimension_incompatible("frequency(e, ...)", "e", expr); 03481 03482 // If the `expr' has a constant value, then the frequency 03483 // `freq_n' is 0. Otherwise the values for \p expr are not discrete 03484 // and we return false. 03485 03486 // Space dimension is 0: if empty, then return false; 03487 // otherwise the frequency is 1 and the value is 0. 03488 if (space_dim == 0) { 03489 if (is_empty()) 03490 return false; 03491 freq_n = 0; 03492 freq_d = 1; 03493 val_n = expr.inhomogeneous_term(); 03494 val_d = 1; 03495 return true; 03496 } 03497 03498 // For an empty polyhedron, we simply return false. 03499 if (marked_empty() 03500 || (has_pending_constraints() && !process_pending_constraints()) 03501 || (!generators_are_up_to_date() && !update_generators())) 03502 return false; 03503 03504 // The polyhedron has updated, possibly pending generators. 03505 // The following loop will iterate through the generator 03506 // to see if `expr' has a constant value. 03507 PPL_DIRTY_TEMP(mpq_class, value); 03508 03509 // True if we have no other candidate value to compare with. 03510 bool first_candidate = true; 03511 03512 PPL_DIRTY_TEMP_COEFFICIENT(sp); 03513 PPL_DIRTY_TEMP(mpq_class, candidate); 03514 for (dimension_type i = gen_sys.num_rows(); i-- > 0; ) { 03515 const Generator& gen_sys_i = gen_sys[i]; 03516 Scalar_Products::homogeneous_assign(sp, expr, gen_sys_i); 03517 // Lines and rays in `*this' can cause `expr' to be non-constant. 03518 if (gen_sys_i.is_line_or_ray()) { 03519 const int sp_sign = sgn(sp); 03520 if (sp_sign != 0) 03521 // `expr' is unbounded in `*this'. 03522 return false; 03523 } 03524 else { 03525 // We have a point or a closure point. 03526 PPL_ASSERT(gen_sys_i.is_point() || gen_sys_i.is_closure_point()); 03527 // Notice that we are ignoring the constant term in `expr' here. 03528 // We will add it to the value if there is a constant value. 03529 assign_r(candidate.get_num(), sp, ROUND_NOT_NEEDED); 03530 assign_r(candidate.get_den(), gen_sys_i[0], ROUND_NOT_NEEDED); 03531 candidate.canonicalize(); 03532 if (first_candidate) { 03533 // We have a (new) candidate value. 03534 first_candidate = false; 03535 value = candidate; 03536 } 03537 else if (candidate != value) 03538 return false; 03539 } 03540 } 03541 03542 // Add in the constant term in `expr'. 03543 PPL_DIRTY_TEMP(mpz_class, n); 03544 assign_r(n, expr.inhomogeneous_term(), ROUND_NOT_NEEDED); 03545 value += n; 03546 // FIXME: avoid these temporaries, if possible. 03547 // This can be done adding an `assign' function working on native 03548 // and checked or an operator= that have on one side a checked and 03549 // on the other a native or checked. 03550 // The reason why now we can't use operator= is the fact that we 03551 // still can have Coefficient defined to mpz_class (and not 03552 // Checked_Number<mpz_class>). 03553 val_n = Coefficient(value.get_num()); 03554 val_d = Coefficient(value.get_den()); 03555 03556 freq_n = 0; 03557 freq_d = 1; 03558 return true; 03559 } 03560 03561 void 03562 PPL::Polyhedron::topological_closure_assign() { 03563 // Necessarily closed polyhedra are trivially closed. 03564 if (is_necessarily_closed()) 03565 return; 03566 // Any empty or zero-dimensional polyhedron is closed. 03567 if (marked_empty() || space_dim == 0) 03568 return; 03569 03570 // The computation can be done using constraints or generators. 03571 // If we use constraints, we will change them, so that having pending 03572 // constraints would be useless. If we use generators, we add generators, 03573 // so that having pending generators still makes sense. 03574 03575 // Process any pending constraints. 03576 if (has_pending_constraints() && !process_pending_constraints()) 03577 return; 03578 03579 // Use constraints only if they are available and 03580 // there are no pending generators. 03581 if (!has_pending_generators() && constraints_are_up_to_date()) { 03582 const dimension_type eps_index = space_dim + 1; 03583 bool changed = false; 03584 // Transform all strict inequalities into non-strict ones. 03585 for (dimension_type i = con_sys.num_rows(); i-- > 0; ) { 03586 Constraint& c = con_sys[i]; 03587 if (c[eps_index] < 0 && !c.is_tautological()) { 03588 c[eps_index] = 0; 03589 // Enforce normalization. 03590 c.normalize(); 03591 changed = true; 03592 } 03593 } 03594 if (changed) { 03595 con_sys.insert(Constraint::epsilon_leq_one()); 03596 con_sys.set_sorted(false); 03597 // After changing the system of constraints, the generators 03598 // are no longer up-to-date and the constraints are no longer 03599 // minimized. 03600 clear_generators_up_to_date(); 03601 clear_constraints_minimized(); 03602 } 03603 } 03604 else { 03605 // Here we use generators, possibly keeping constraints. 03606 PPL_ASSERT(generators_are_up_to_date()); 03607 // Add the corresponding point to each closure point. 03608 gen_sys.add_corresponding_points(); 03609 if (can_have_something_pending()) 03610 set_generators_pending(); 03611 else { 03612 // We cannot have pending generators; this also implies 03613 // that generators may have lost their sortedness. 03614 gen_sys.unset_pending_rows(); 03615 gen_sys.set_sorted(false); 03616 // Constraints are not up-to-date and generators are not minimized. 03617 clear_constraints_up_to_date(); 03618 clear_generators_minimized(); 03619 } 03620 } 03621 PPL_ASSERT_HEAVY(OK()); 03622 } 03623 03625 bool 03626 PPL::operator==(const Polyhedron& x, const Polyhedron& y) { 03627 // If the two polyhedra are topology-incompatible or dimension-incompatible, 03628 // then they cannot be the same polyhedron. 03629 if (x.topology() != y.topology() || x.space_dim != y.space_dim) 03630 return false; 03631 03632 if (x.marked_empty()) 03633 return y.is_empty(); 03634 else if (y.marked_empty()) 03635 return x.is_empty(); 03636 else if (x.space_dim == 0) 03637 return true; 03638 03639 switch (x.quick_equivalence_test(y)) { 03640 case Polyhedron::TVB_TRUE: 03641 return true; 03642 03643 case Polyhedron::TVB_FALSE: 03644 return false; 03645 03646 default: 03647 if (x.is_included_in(y)) 03648 if (x.marked_empty()) 03649 return y.is_empty(); 03650 else 03651 return y.is_included_in(x); 03652 else 03653 return false; 03654 } 03655 } 03656 03657 bool 03658 PPL::Polyhedron::contains(const Polyhedron& y) const { 03659 const Polyhedron& x = *this; 03660 03661 // Topology compatibility check. 03662 if (x.topology() != y.topology()) 03663 throw_topology_incompatible("contains(y)", "y", y); 03664 03665 // Dimension-compatibility check. 03666 if (x.space_dim != y.space_dim) 03667 throw_dimension_incompatible("contains(y)", "y", y); 03668 03669 if (y.marked_empty()) 03670 return true; 03671 else if (x.marked_empty()) 03672 return y.is_empty(); 03673 else if (y.space_dim == 0) 03674 return true; 03675 else if (x.quick_equivalence_test(y) == Polyhedron::TVB_TRUE) 03676 return true; 03677 else 03678 return y.is_included_in(x); 03679 } 03680 03681 bool 03682 PPL::Polyhedron::is_disjoint_from(const Polyhedron& y) const { 03683 Polyhedron z = *this; 03684 z.intersection_assign(y); 03685 return z.is_empty(); 03686 } 03687 03688 void 03689 PPL::Polyhedron::ascii_dump(std::ostream& s) const { 03690 s << "space_dim " << space_dim << "\n"; 03691 status.ascii_dump(s); 03692 s << "\ncon_sys (" 03693 << (constraints_are_up_to_date() ? "" : "not_") 03694 << "up-to-date)" 03695 << "\n"; 03696 con_sys.ascii_dump(s); 03697 s << "\ngen_sys (" 03698 << (generators_are_up_to_date() ? "" : "not_") 03699 << "up-to-date)" 03700 << "\n"; 03701 gen_sys.ascii_dump(s); 03702 s << "\nsat_c\n"; 03703 sat_c.ascii_dump(s); 03704 s << "\nsat_g\n"; 03705 sat_g.ascii_dump(s); 03706 s << "\n"; 03707 } 03708 03709 PPL_OUTPUT_DEFINITIONS(Polyhedron) 03710 03711 bool 03712 PPL::Polyhedron::ascii_load(std::istream& s) { 03713 std::string str; 03714 03715 if (!(s >> str) || str != "space_dim") 03716 return false; 03717 03718 if (!(s >> space_dim)) 03719 return false; 03720 03721 if (!status.ascii_load(s)) 03722 return false; 03723 03724 if (!(s >> str) || str != "con_sys") 03725 return false; 03726 03727 if (!(s >> str) || (str != "(not_up-to-date)" && str != "(up-to-date)")) 03728 return false; 03729 03730 if (!con_sys.ascii_load(s)) 03731 return false; 03732 03733 if (!(s >> str) || str != "gen_sys") 03734 return false; 03735 03736 if (!(s >> str) || (str != "(not_up-to-date)" && str != "(up-to-date)")) 03737 return false; 03738 03739 if (!gen_sys.ascii_load(s)) 03740 return false; 03741 03742 if (!(s >> str) || str != "sat_c") 03743 return false; 03744 03745 if (!sat_c.ascii_load(s)) 03746 return false; 03747 03748 if (!(s >> str) || str != "sat_g") 03749 return false; 03750 03751 if (!sat_g.ascii_load(s)) 03752 return false; 03753 03754 // Check invariants. 03755 PPL_ASSERT_HEAVY(OK()); 03756 return true; 03757 } 03758 03759 PPL::memory_size_type 03760 PPL::Polyhedron::external_memory_in_bytes() const { 03761 return 03762 con_sys.external_memory_in_bytes() 03763 + gen_sys.external_memory_in_bytes() 03764 + sat_c.external_memory_in_bytes() 03765 + sat_g.external_memory_in_bytes(); 03766 } 03767 03768 void 03769 PPL::Polyhedron::wrap_assign(const Variables_Set& vars, 03770 Bounded_Integer_Type_Width w, 03771 Bounded_Integer_Type_Representation r, 03772 Bounded_Integer_Type_Overflow o, 03773 const Constraint_System* cs_p, 03774 unsigned complexity_threshold, 03775 bool wrap_individually) { 03776 if (is_necessarily_closed()) 03777 Implementation::wrap_assign(static_cast<C_Polyhedron&>(*this), 03778 vars, w, r, o, cs_p, 03779 complexity_threshold, wrap_individually, 03780 "C_Polyhedron"); 03781 else 03782 Implementation::wrap_assign(static_cast<NNC_Polyhedron&>(*this), 03783 vars, w, r, o, cs_p, 03784 complexity_threshold, wrap_individually, 03785 "NNC_Polyhedron"); 03786 } 03787 03789 std::ostream& 03790 PPL::IO_Operators::operator<<(std::ostream& s, const Polyhedron& ph) { 03791 if (ph.is_empty()) 03792 s << "false"; 03793 else 03794 s << ph.minimized_constraints(); 03795 return s; 03796 }