PPL  1.2
Polyhedron_chdims.cc
Go to the documentation of this file.
1 /* Polyhedron class implementation
2  (non-inline operators that may change the dimension of the vector space).
3  Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it>
4  Copyright (C) 2010-2016 BUGSENG srl (http://bugseng.com)
5 
6 This file is part of the Parma Polyhedra Library (PPL).
7 
8 The PPL is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by the
10 Free Software Foundation; either version 3 of the License, or (at your
11 option) any later version.
12 
13 The PPL is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17 
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software Foundation,
20 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA.
21 
22 For the most up-to-date information see the Parma Polyhedra Library
23 site: http://bugseng.com/products/ppl/ . */
24 
25 #include "ppl-config.h"
26 #include "Polyhedron_defs.hh"
27 #include "Variables_Set_defs.hh"
28 #include "assertions.hh"
29 
30 #define BE_LAZY 1
31 
32 namespace PPL = Parma_Polyhedra_Library;
33 
34 void
36  // The space dimension of the resulting polyhedron should not
37  // overflow the maximum allowed space dimension.
39  topology(),
40  "add_space_dimensions_and_embed(m)",
41  "adding m new space dimensions exceeds "
42  "the maximum allowed space dimension");
43 
44  // Adding no dimensions to any polyhedron is a no-op.
45  if (m == 0) {
46  return;
47  }
48 
49  // Adding dimensions to an empty polyhedron is obtained by adjusting
50  // `space_dim' and clearing `con_sys' (since it can contain the
51  // unsatisfiable constraint system of the wrong dimension).
52  if (marked_empty()) {
53  space_dim += m;
54  con_sys.clear();
55  return;
56  }
57 
58  // The case of a zero-dimensional space polyhedron.
59  if (space_dim == 0) {
60  // Since it is not empty, it has to be the universe polyhedron.
61  PPL_ASSERT(status.test_zero_dim_univ());
62  // We swap `*this' with a newly created
63  // universe polyhedron of dimension `m'.
64  Polyhedron ph(topology(), m, UNIVERSE);
65  m_swap(ph);
66  return;
67  }
68 
69  // To embed an n-dimension space polyhedron in a (n+m)-dimension space,
70  // we just add `m' zero-columns to the rows in the system of constraints;
71  // in contrast, the system of generators needs additional rows,
72  // corresponding to the vectors of the canonical basis
73  // for the added dimensions. That is, for each new dimension `x[k]'
74  // we add the line having that direction. This is done by invoking
75  // the function add_space_dimensions() giving the system of generators
76  // as the second argument.
79  // `sat_c' must be up to date for add_space_dimensions().
80  if (!sat_c_is_up_to_date()) {
81  update_sat_c();
82  }
83  // Adds rows and/or columns to both matrices.
84  // `add_space_dimensions' correctly handles pending constraints
85  // or generators.
87  }
88  else {
89  // Only constraints are up-to-date: no need to modify the generators.
91  }
92  }
93  else {
94  // Only generators are up-to-date: no need to modify the constraints.
95  PPL_ASSERT(generators_are_up_to_date());
97  }
98  // Update the space dimension.
99  space_dim += m;
100 
101  // Note: we do not check for satisfiability, because the system of
102  // constraints may be unsatisfiable.
103  PPL_ASSERT_HEAVY(OK());
104 }
105 
106 void
108  // The space dimension of the resulting polyhedron should not
109  // overflow the maximum allowed space dimension.
110  check_space_dimension_overflow(m, max_space_dimension() - space_dimension(),
111  topology(),
112  "add_space_dimensions_and_project(m)",
113  "adding m new space dimensions exceeds "
114  "the maximum allowed space dimension");
115 
116  // Adding no dimensions to any polyhedron is a no-op.
117  if (m == 0) {
118  return;
119  }
120 
121  // Adding dimensions to an empty polyhedron is obtained
122  // by merely adjusting `space_dim'.
123  if (marked_empty()) {
124  space_dim += m;
125  con_sys.clear();
126  return;
127  }
128 
129  if (space_dim == 0) {
130  PPL_ASSERT(status.test_zero_dim_univ() && gen_sys.has_no_rows());
131  // The system of generators for this polyhedron has only
132  // the origin as a point.
133  // In an NNC polyhedron, all points have to be accompanied
134  // by the corresponding closure points
135  // (this time, dimensions are automatically adjusted).
136  if (!is_necessarily_closed()) {
137  gen_sys.insert(Generator::zero_dim_closure_point());
138  }
139  gen_sys.insert(Generator::zero_dim_point());
140  gen_sys.adjust_topology_and_space_dimension(topology(), m);
141  set_generators_minimized();
142  space_dim = m;
143  PPL_ASSERT_HEAVY(OK());
144  return;
145  }
146 
147  // To project an n-dimension space polyhedron in a (n+m)-dimension space,
148  // we just add to the system of generators `m' zero-columns;
149  // In contrast, in the system of constraints, new rows are needed
150  // in order to avoid embedding the old polyhedron in the new space.
151  // Thus, for each new dimensions `x[k]', we add the constraint
152  // x[k] = 0: this is done by invoking the function add_space_dimensions()
153  // giving the system of constraints as the second argument.
154  if (constraints_are_up_to_date()) {
155  if (generators_are_up_to_date()) {
156  // `sat_g' must be up to date for add_space_dimensions().
157  if (!sat_g_is_up_to_date()) {
158  update_sat_g();
159  }
160  // Adds rows and/or columns to both matrices.
161  // `add_space_dimensions()' correctly handles pending constraints
162  // or generators.
163  add_space_dimensions(gen_sys, con_sys, sat_g, sat_c, m);
164  }
165  else {
166  // Only constraints are up-to-date: no need to modify the generators.
167  con_sys.add_universe_rows_and_space_dimensions(m);
168  }
169  }
170  else {
171  // Only generators are up-to-date: no need to modify the constraints.
172  PPL_ASSERT(generators_are_up_to_date());
173  gen_sys.set_space_dimension(gen_sys.space_dimension() + m);
174  }
175  // Now we update the space dimension.
176  space_dim += m;
177 
178  // Note: we do not check for satisfiability, because the system of
179  // constraints may be unsatisfiable.
180  PPL_ASSERT_HEAVY(OK());
181 }
182 
183 void
185  if (topology() != y.topology()) {
186  throw_topology_incompatible("concatenate_assign(y)", "y", y);
187  }
188 
189  // The space dimension of the resulting polyhedron should not
190  // overflow the maximum allowed space dimension.
191  const dimension_type added_columns = y.space_dim;
192  check_space_dimension_overflow(added_columns,
193  max_space_dimension() - space_dimension(),
194  topology(),
195  "concatenate_assign(y)",
196  "concatenation exceeds the maximum "
197  "allowed space dimension");
198 
199  // If `*this' or `y' are empty polyhedra, it is sufficient to adjust
200  // the dimension of the space.
201  if (marked_empty() || y.marked_empty()) {
202  space_dim += added_columns;
203  set_empty();
204  return;
205  }
206 
207  // If `y' is a non-empty 0-dim space polyhedron, the result is `*this'.
208  if (added_columns == 0) {
209  return;
210  }
211 
212  // If `*this' is a non-empty 0-dim space polyhedron, the result is `y'.
213  if (space_dim == 0) {
214  *this = y;
215  return;
216  }
217 
218  // TODO: this implementation is just an executable specification.
220 
221  // The constraints of `x' (possibly with pending rows) are required.
222  if (has_pending_generators()) {
223  process_pending_generators();
224  }
225  else if (!constraints_are_up_to_date()) {
226  update_constraints();
227  }
228 
229  // The matrix for the new system of constraints is obtained
230  // by leaving the old system of constraints in the upper left-hand side
231  // and placing the constraints of `cs' in the lower right-hand side.
232  // NOTE: here topologies agree, whereas dimensions may not agree.
233  const dimension_type added_rows = cs.num_rows();
234 
235  // We already dealt with the cases of an empty or zero-dim `y' polyhedron;
236  // also, `cs' contains the low-level constraints, at least.
237  PPL_ASSERT(added_rows > 0 && added_columns > 0);
238 
239  con_sys.set_space_dimension(con_sys.space_dimension() + added_columns);
240 
241  if (can_have_something_pending()) {
242  // Steal the constraints from `cs' and put them in `con_sys'
243  // using the right displacement for coefficients.
244  for (dimension_type i = 0; i < added_rows; ++i) {
245  cs.sys.rows[i].shift_space_dimensions(Variable(0), space_dim);
246  con_sys.insert_pending(cs.sys.rows[i], Recycle_Input());
247  }
248  cs.clear();
249 
250  // If `*this' can support pending constraints, then, since we have
251  // resized the system of constraints, we must also add to the generator
252  // system those lines corresponding to the newly added dimensions,
253  // because the non-pending parts of `con_sys' and `gen_sys' must still
254  // be a DD pair in minimal form.
255  gen_sys.add_universe_rows_and_space_dimensions(added_columns);
256  // Since we added new lines at the beginning of `x.gen_sys',
257  // we also have to adjust the saturation matrix `sat_c'.
258  // FIXME: if `sat_c' is not up-to-date, could not we directly update
259  // `sat_g' by resizing it and shifting its columns?
260  if (!sat_c_is_up_to_date()) {
261  sat_c.transpose_assign(sat_g);
262  set_sat_c_up_to_date();
263  }
264  clear_sat_g_up_to_date();
265  sat_c.resize(sat_c.num_rows() + added_columns, sat_c.num_columns());
266  // The old saturation rows are copied at the end of the matrix.
267  // The newly introduced lines saturate all the non-pending constraints,
268  // thus their saturation rows are made of zeroes.
269  using std::swap;
270  for (dimension_type i = sat_c.num_rows() - added_columns;
271  i-- > 0; ) {
272  swap(sat_c[i], sat_c[i+added_columns]);
273  }
274  // Since `added_rows > 0', we now have pending constraints.
275  set_constraints_pending();
276  }
277  else {
278  // Steal the constraints from `cs' and put them in `con_sys'
279  // using the right displacement for coefficients.
280  for (dimension_type i = 0; i < added_rows; ++i) {
281  cs.sys.rows[i].shift_space_dimensions(Variable(0), space_dim);
282  con_sys.insert(cs.sys.rows[i], Recycle_Input());
283  }
284 #if !BE_LAZY
285  con_sys.sort_rows();
286 #endif
287  clear_constraints_minimized();
288  clear_generators_up_to_date();
289  clear_sat_g_up_to_date();
290  clear_sat_c_up_to_date();
291  }
292  // Update space dimension.
293  space_dim += added_columns;
294 
295  // The system of constraints may be unsatisfiable,
296  // thus we do not check for satisfiability.
297  PPL_ASSERT_HEAVY(OK());
298 }
299 
300 void
302  // The removal of no dimensions from any polyhedron is a no-op.
303  // Note that this case also captures the only legal removal of
304  // dimensions from a polyhedron in a 0-dim space.
305  if (vars.empty()) {
306  PPL_ASSERT_HEAVY(OK());
307  return;
308  }
309 
310  // Dimension-compatibility check.
311  const dimension_type min_space_dim = vars.space_dimension();
312  if (space_dim < min_space_dim) {
313  throw_dimension_incompatible("remove_space_dimensions(vs)", min_space_dim);
314  }
315 
316  const dimension_type new_space_dim = space_dim - vars.size();
317 
318  // We need updated generators; note that keeping pending generators
319  // is useless because the constraints will be dropped anyway.
320  if (marked_empty()
321  || (has_something_pending() && !remove_pending_to_obtain_generators())
322  || (!generators_are_up_to_date() && !update_generators())) {
323  // Removing dimensions from the empty polyhedron:
324  // we clear `con_sys' since it could have contained the
325  // unsatisfiable constraint of the wrong dimension.
326  con_sys.clear();
327  // Update the space dimension.
328  space_dim = new_space_dim;
329  PPL_ASSERT_HEAVY(OK());
330  return;
331  }
332 
333  // When removing _all_ dimensions from a non-empty polyhedron,
334  // we obtain the zero-dimensional universe polyhedron.
335  if (new_space_dim == 0) {
336  set_zero_dim_univ();
337  return;
338  }
339 
340  gen_sys.remove_space_dimensions(vars);
341 
342  // Constraints are not up-to-date and generators are not minimized.
343  clear_constraints_up_to_date();
344  clear_generators_minimized();
345 
346  // Update the space dimension.
347  space_dim = new_space_dim;
348 
349  PPL_ASSERT_HEAVY(OK(true));
350 }
351 
352 void
354  // Dimension-compatibility check.
355  if (new_dimension > space_dim) {
356  throw_dimension_incompatible("remove_higher_space_dimensions(nd)",
357  new_dimension);
358  }
359 
360  // The removal of no dimensions from any polyhedron is a no-op.
361  // Note that this case also captures the only legal removal of
362  // dimensions from a polyhedron in a 0-dim space.
363  if (new_dimension == space_dim) {
364  PPL_ASSERT_HEAVY(OK());
365  return;
366  }
367 
368  // We need updated generators; note that keeping pending generators
369  // is useless because constraints will be dropped anyway.
370  if (marked_empty()
371  || (has_something_pending() && !remove_pending_to_obtain_generators())
372  || (!generators_are_up_to_date() && !update_generators())) {
373  // Removing dimensions from the empty polyhedron:
374  // just updates the space dimension.
375  space_dim = new_dimension;
376  con_sys.clear();
377  PPL_ASSERT_HEAVY(OK());
378  return;
379  }
380 
381  if (new_dimension == 0) {
382  // Removing all dimensions from a non-empty polyhedron:
383  // just return the zero-dimensional universe polyhedron.
384  set_zero_dim_univ();
385  return;
386  }
387 
388  gen_sys.set_space_dimension(new_dimension);
389 
390  // Constraints are not up-to-date and generators are not minimized.
391  clear_constraints_up_to_date();
392  clear_generators_minimized();
393 
394  // Update the space dimension.
395  space_dim = new_dimension;
396 
397  PPL_ASSERT_HEAVY(OK(true));
398 }
399 
400 void
402  // `var' should be one of the dimensions of the vector space.
403  if (var.space_dimension() > space_dim) {
404  throw_dimension_incompatible("expand_space_dimension(v, m)", "v", var);
405  }
406 
407  // The space dimension of the resulting polyhedron should not
408  // overflow the maximum allowed space dimension.
409  check_space_dimension_overflow(m, max_space_dimension() - space_dimension(),
410  topology(),
411  "expand_dimension(v, m)",
412  "adding m new space dimensions exceeds "
413  "the maximum allowed space dimension");
414 
415  // Nothing to do, if no dimensions must be added.
416  if (m == 0) {
417  return;
418  }
419 
420  // Keep track of the dimension before adding the new ones.
421  const dimension_type old_dim = space_dim;
422 
423  // Add the required new dimensions.
424  add_space_dimensions_and_embed(m);
425 
426  const Constraint_System& cs = constraints();
427  Constraint_System new_constraints(cs.topology());
429  cs_end = cs.end(); i != cs_end; ++i) {
430  const Constraint& c = *i;
431 
432  Coefficient_traits::const_reference coeff = c.coefficient(var);
433 
434  // If `c' does not constrain `var', skip it.
435  if (coeff == 0) {
436  continue;
437  }
438 
439  Constraint c_template = c;
440  c_template.expr.set_coefficient(var, Coefficient_zero());
441 
442  // Each relevant constraint results in `m' new constraints.
443  for (dimension_type dst_d = old_dim; dst_d < old_dim+m; ++dst_d) {
444  Constraint new_c = c_template;
445  add_mul_assign(new_c.expr, coeff, Variable(dst_d));
446  PPL_ASSERT(new_c.OK());
447  new_constraints.insert(new_c, Recycle_Input());
448  }
449  }
450  add_recycled_constraints(new_constraints);
451  PPL_ASSERT_HEAVY(OK());
452 }
453 
454 void
456  Variable dest) {
457  // TODO: this implementation is _really_ an executable specification.
458 
459  // `dest' should be one of the dimensions of the polyhedron.
460  if (dest.space_dimension() > space_dim) {
461  throw_dimension_incompatible("fold_space_dimensions(vs, v)", "v", dest);
462  }
463 
464  // The folding of no dimensions is a no-op.
465  if (vars.empty()) {
466  return;
467  }
468 
469  // All variables in `vars' should be dimensions of the polyhedron.
470  if (vars.space_dimension() > space_dim) {
471  throw_dimension_incompatible("fold_space_dimensions(vs, v)",
472  "vs.space_dimension()",
473  vars.space_dimension());
474  }
475 
476  // Moreover, `dest.id()' should not occur in `vars'.
477  if (vars.find(dest.id()) != vars.end()) {
478  throw_invalid_argument("fold_space_dimensions(vs, v)",
479  "v should not occur in vs");
480  }
481 
482  // All of the affine images we are going to compute are not invertible,
483  // hence we will need to compute the generators of the polyhedron.
484  // Since we keep taking copies, make sure that a single conversion
485  // from constraints to generators is computed.
486  (void) generators();
487  // Having generators, we now know if the polyhedron is empty:
488  // in that case, folding is equivalent to just removing space dimensions.
489  if (!marked_empty()) {
490  for (Variables_Set::const_iterator i = vars.begin(),
491  vs_end = vars.end(); i != vs_end; ++i) {
492  Polyhedron copy = *this;
493  copy.affine_image(dest, Linear_Expression(Variable(*i)));
494  poly_hull_assign(copy);
495  }
496  }
497  remove_space_dimensions(vars);
498  PPL_ASSERT_HEAVY(OK());
499 }
void add_space_dimensions_and_embed(dimension_type m)
Adds m new space dimensions and embeds the old polyhedron in the new vector space.
dimension_type space_dimension() const
Returns the dimension of the smallest vector space enclosing all the variables whose indexes are in t...
bool marked_empty() const
Returns true if the polyhedron is known to be empty.
dimension_type max_space_dimension()
Returns the maximum space dimension this library can handle.
A linear equality or inequality.
void swap(CO_Tree &x, CO_Tree &y)
size_t dimension_type
An unsigned integral type for representing space dimensions.
void set_space_dimension(dimension_type space_dim)
Sets the space dimension of the rows in the system to space_dim .
An std::set of variables' indexes.
Generator_System gen_sys
The system of generators.
void add_universe_rows_and_space_dimensions(dimension_type n)
Adds n rows and space dimensions to the system.
static const Generator & zero_dim_point()
Returns the origin of the zero-dimensional space .
void add_mul_assign(GMP_Integer &x, const GMP_Integer &y, const GMP_Integer &z)
void affine_image(Variable var, const Linear_Expression &expr, Coefficient_traits::const_reference denominator=Coefficient_one())
Assigns to *this the affine image of *this under the function mapping variable var to the affine expr...
bool sat_c_is_up_to_date() const
Returns true if the saturation matrix sat_c is up-to-date.
void add_space_dimensions_and_project(dimension_type m)
Adds m new space dimensions to the polyhedron and does not embed it in the new vector space...
void remove_space_dimensions(const Variables_Set &vars)
Removes all the specified dimensions from the vector space.
Topology topology() const
Returns the system topology.
void clear()
Removes all the constraints from the constraint system and sets its space dimension to 0...
const Constraint_System & constraints() const
Returns the system of constraints.
void set_coefficient(Variable v, Coefficient_traits::const_reference n)
Sets the coefficient of v in *this to n.
const_iterator begin() const
Returns the const_iterator pointing to the first constraint, if *this is not empty; otherwise...
Coefficient_traits::const_reference coefficient(Variable v) const
Returns the coefficient of v in *this.
dimension_type id() const
Returns the index of the Cartesian axis associated to the variable.
A dimension of the vector space.
The base class for convex polyhedra.
dimension_type space_dimension() const
Returns the dimension of the vector space enclosing *this.
Topology topology() const
Returns the topological kind of the polyhedron.
bool generators_are_up_to_date() const
Returns true if the system of generators is up-to-date.
Constraint_System con_sys
The system of constraints.
void remove_higher_space_dimensions(dimension_type new_dimension)
Removes the higher dimensions of the vector space so that the resulting space will have dimension new...
static dimension_type check_space_dimension_overflow(dimension_type dim, dimension_type max, const Topology topol, const char *method, const char *reason)
dimension_type space_dimension() const
Returns the dimension of the vector space enclosing *this.
bool constraints_are_up_to_date() const
Returns true if the system of constraints is up-to-date.
dimension_type check_space_dimension_overflow(const dimension_type dim, const dimension_type max, const char *domain, const char *method, const char *reason)
Definition: globals.cc:48
Bit_Matrix sat_g
The saturation matrix having generators on its columns.
const_iterator end() const
Returns the past-the-end const_iterator.
static dimension_type max_space_dimension()
Returns the maximum space dimension all kinds of Polyhedron can handle.
Status status
The status flags to keep track of the polyhedron's internal state.
static const Generator & zero_dim_closure_point()
Returns, as a closure point, the origin of the zero-dimensional space .
The universe element, i.e., the whole vector space.
Coefficient_traits::const_reference Coefficient_zero()
Returns a const reference to a Coefficient with value 0.
The entire library is confined to this namespace.
Definition: version.hh:61
dimension_type space_dim
The number of dimensions of the enclosing vector space.
void fold_space_dimensions(const Variables_Set &vars, Variable dest)
Folds the space dimensions in vars into dest.
bool OK(bool check_not_empty=false) const
Checks if all the invariants are satisfied.
void expand_space_dimension(Variable var, dimension_type m)
Creates m copies of the space dimension corresponding to var.
bool OK() const
Checks if all the invariants are satisfied.
Definition: Constraint.cc:467
void concatenate_assign(const Polyhedron &y)
Assigns to *this the concatenation of *this and y, taken in this order.
void update_sat_c() const
Updates sat_c using the updated constraints and generators.
Coefficient c
Definition: PIP_Tree.cc:64
Bit_Matrix sat_c
The saturation matrix having constraints on its columns.
dimension_type space_dimension() const
Returns the dimension of the vector space enclosing *this.
static void add_space_dimensions(Linear_System1 &sys1, Linear_System2 &sys2, Bit_Matrix &sat1, Bit_Matrix &sat2, dimension_type add_dim)
Adds new space dimensions to the given linear systems.
void m_swap(Polyhedron &y)
Swaps *this with polyhedron y. (*this and y can be dimension-incompatible.)