PPL  1.2
Grid_chdims.cc
Go to the documentation of this file.
1 /* Grid 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 "Grid_defs.hh"
27 #include "Variables_Set_defs.hh"
28 #include "assertions.hh"
29 
30 namespace PPL = Parma_Polyhedra_Library;
31 
32 // Used for add_space_dimensions_and_embed.
33 void
36  const dimension_type dims) {
37  PPL_ASSERT(cgs.space_dimension() == gs.space_dimension());
38  PPL_ASSERT(dims > 0);
39 
40  const dimension_type old_modulus_index = cgs.space_dimension() + 1;
42 
44  dim_kinds.resize(old_modulus_index + dims, CON_VIRTUAL /* a.k.a. LINE */);
45  }
46 
48 }
49 
50 // Used for add_space_dimensions_and_project.
51 void
53  Congruence_System& cgs,
54  const dimension_type dims) {
55  PPL_ASSERT(cgs.space_dimension() == gs.space_dimension());
56  PPL_ASSERT(dims > 0);
57 
59 
60  // Add `dims' zero columns onto gs.
61  gs.set_space_dimension(space_dim + dims);
62 
63  normalize_divisors(gs);
64 
65  dim_kinds.resize(cgs.space_dimension() + 1, EQUALITY /* a.k.a GEN_VIRTUAL */);
66 }
67 
68 // (o is a point) y
69 //
70 // | | |
71 // => | | |
72 // | | |
73 // o---o---o-- x |---|---|-- x
74 // 0 1 2 3 4 5 0 1 2 3 4 5
75 // R^1 R^2
76 void
78  if (m == 0) {
79  return;
80  }
81 
82  // The space dimension of the resulting grid must be at most the
83  // maximum allowed space dimension.
84  check_space_dimension_overflow(m, max_space_dimension() - space_dimension(),
85  "PPL::Grid::",
86  "add_space_dimensions_and_embed(m)",
87  "adding m new space dimensions exceeds "
88  "the maximum allowed space dimension");
89 
90  // Adding dimensions to an empty grid is obtained by adjusting
91  // `space_dim' and clearing `con_sys' (since it can contain the
92  // integrality congruence of the current dimension).
93  if (marked_empty()) {
94  space_dim += m;
95  set_empty();
96  return;
97  }
98 
99  // The case of a zero-dimension space grid.
100  if (space_dim == 0) {
101  // Since it is not empty, it has to be the universe grid.
102  PPL_ASSERT(status.test_zero_dim_univ());
103  // Swap *this with a newly created `m'-dimensional universe grid.
104  Grid gr(m, UNIVERSE);
105  m_swap(gr);
106  return;
107  }
108 
109  // To embed an n-dimension space grid in a (n+m)-dimension space, we
110  // add `m' zero-columns to the rows in the system of congruences; in
111  // contrast, the system of generators needs additional rows,
112  // corresponding to the vectors of the canonical basis for the added
113  // dimensions. That is, for each new dimension we add the line
114  // having that direction. This is done by invoking the function
115  // add_space_dimensions().
116  if (congruences_are_up_to_date()) {
117  if (generators_are_up_to_date()) {
118  // Adds rows and/or columns to both matrices.
119  add_space_dimensions(con_sys, gen_sys, m);
120  }
121  else {
122  // Only congruences are up-to-date, so modify only them.
123  con_sys.set_space_dimension(con_sys.space_dimension() + m);
124  if (congruences_are_minimized()) {
125  dim_kinds.resize(con_sys.space_dimension() + 1, CON_VIRTUAL);
126  }
127  }
128  }
129  else {
130  // Only generators are up-to-date, so modify only them.
131  PPL_ASSERT(generators_are_up_to_date());
132  gen_sys.add_universe_rows_and_columns(m);
133  if (generators_are_minimized()) {
134  dim_kinds.resize(gen_sys.space_dimension() + 1, LINE);
135  }
136  }
137  // Update the space dimension.
138  space_dim += m;
139 
140  // Note: we do not check for satisfiability, because the system of
141  // congruences may be unsatisfiable.
142  PPL_ASSERT(OK());
143 }
144 
145 // (o is a point) y
146 //
147 //
148 // =>
149 //
150 // o---o---o-- x o---o---o-- x
151 // 0 1 2 3 4 5 0 1 2 3 4 5
152 // R^1 R^2
153 void
155  if (m == 0) {
156  return;
157  }
158 
159  // The space dimension of the resulting grid should be at most the
160  // maximum allowed space dimension.
161  check_space_dimension_overflow(m, max_space_dimension() - space_dimension(),
162  "PPL::Grid::",
163  "add_space_dimensions_and_project(m)",
164  "adding m new space dimensions exceeds "
165  "the maximum allowed space dimension");
166 
167  // Adding dimensions to an empty grid is obtained by merely
168  // adjusting `space_dim'.
169  if (marked_empty()) {
170  space_dim += m;
171  set_empty();
172  return;
173  }
174 
175  if (space_dim == 0) {
176  PPL_ASSERT(status.test_zero_dim_univ());
177  // Swap *this with a newly created `n'-dimensional universe grid.
178  Grid gr(m, UNIVERSE);
179  m_swap(gr);
180  return;
181  }
182 
183  // To project an n-dimension space grid in a (n+m)-dimension space,
184  // we just add to the system of generators `m' zero-columns; in
185  // contrast, in the system of congruences, new rows are needed in
186  // order to avoid embedding the old grid in the new space. Thus,
187  // for each new dimensions `x[k]', we add the constraint x[k] = 0;
188  // this is done by invoking the function add_space_dimensions()
189  // giving the system of constraints as the second argument.
190  if (congruences_are_up_to_date()) {
191  if (generators_are_up_to_date()) {
192  // Add rows and/or columns to both matrices.
193  add_space_dimensions(gen_sys, con_sys, m);
194  }
195  else {
196  // Only congruences are up-to-date so modify only them.
197  con_sys.add_unit_rows_and_space_dimensions(m);
198  if (congruences_are_minimized()) {
199  dim_kinds.resize(con_sys.space_dimension() + 1, EQUALITY);
200  }
201  }
202  }
203  else {
204  // Only generators are up-to-date so modify only them.
205  PPL_ASSERT(generators_are_up_to_date());
206 
207  // Add m zero columns onto gs.
208  gen_sys.set_space_dimension(space_dim + m);
209 
210  normalize_divisors(gen_sys);
211 
212  if (generators_are_minimized()) {
213  dim_kinds.resize(gen_sys.space_dimension() + 1, EQUALITY);
214  }
215  }
216  // Now update the space dimension.
217  space_dim += m;
218 
219  // Note: we do not check for satisfiability, because the system of
220  // congruences may be unsatisfiable.
221  PPL_ASSERT(OK());
222 }
223 
224 void
226  // The space dimension of the resulting grid must be at most the
227  // maximum allowed space dimension.
229  max_space_dimension() - space_dimension(),
230  "PPL::Grid::",
231  "concatenate_assign(y)",
232  "concatenation exceeds the maximum "
233  "allowed space dimension");
234 
235  const dimension_type added_columns = y.space_dim;
236 
237  // If `*this' or `y' are empty grids just adjust the space
238  // dimension.
239  if (marked_empty() || y.marked_empty()) {
240  space_dim += added_columns;
241  set_empty();
242  return;
243  }
244 
245  // If `y' is a universe 0-dim grid, the result is `*this'.
246  if (added_columns == 0) {
247  return;
248  }
249 
250  // If `*this' is a universe 0-dim space grid, the result is `y'.
251  if (space_dim == 0) {
252  *this = y;
253  return;
254  }
255 
256  if (!congruences_are_up_to_date()) {
257  update_congruences();
258  }
259 
260  con_sys.concatenate(y.congruences());
261 
262  space_dim += added_columns;
263 
264  clear_congruences_minimized();
265  clear_generators_up_to_date();
266 
267  // Check that the system is OK, taking into account that the system
268  // of congruences may now be empty.
269  PPL_ASSERT(OK());
270 }
271 
272 void
274  // The removal of no dimensions from any grid is a no-op. This case
275  // also captures the only legal removal of dimensions from a grid in
276  // a 0-dim space.
277  if (vars.empty()) {
278  PPL_ASSERT(OK());
279  return;
280  }
281 
282  // Dimension-compatibility check.
283  const dimension_type min_space_dim = vars.space_dimension();
284  if (space_dim < min_space_dim) {
285  throw_dimension_incompatible("remove_space_dimensions(vs)", min_space_dim);
286  }
287 
288  const dimension_type new_space_dim = space_dim - vars.size();
289 
290  if (marked_empty()
291  || (!generators_are_up_to_date() && !update_generators())) {
292  // Update the space dimension.
293  space_dim = new_space_dim;
294  set_empty();
295  PPL_ASSERT(OK());
296  return;
297  }
298 
299  // Removing _all_ dimensions from a non-empty grid obtains the
300  // zero-dimensional universe grid.
301  if (new_space_dim == 0) {
302  set_zero_dim_univ();
303  return;
304  }
305 
306  gen_sys.remove_space_dimensions(vars);
307 
308  clear_congruences_up_to_date();
309  clear_generators_minimized();
310 
311  // Update the space dimension.
312  space_dim = new_space_dim;
313 
314  PPL_ASSERT(OK(true));
315 }
316 
317 void
319  // Dimension-compatibility check.
320  if (new_dimension > space_dim) {
321  throw_dimension_incompatible("remove_higher_space_dimensions(nd)",
322  new_dimension);
323  }
324  // The removal of no dimensions from any grid is a no-op.
325  // Note that this case also captures the only legal removal of
326  // dimensions from a grid in a 0-dim space.
327  if (new_dimension == space_dim) {
328  PPL_ASSERT(OK());
329  return;
330  }
331 
332  if (is_empty()) {
333  // Removing dimensions from the empty grid just updates the space
334  // dimension.
335  space_dim = new_dimension;
336  set_empty();
337  PPL_ASSERT(OK());
338  return;
339  }
340 
341  if (new_dimension == 0) {
342  // Removing all dimensions from a non-empty grid just returns the
343  // zero-dimensional universe grid.
344  set_zero_dim_univ();
345  return;
346  }
347 
348  // Favor the generators, as is done by is_empty().
349  if (generators_are_up_to_date()) {
350  gen_sys.set_space_dimension(new_dimension);
351  if (generators_are_minimized()) {
352  // Count the actual number of rows that are now redundant.
353  dimension_type num_redundant = 0;
354  const dimension_type num_old_gs = space_dim - new_dimension;
355  for (dimension_type row = 0; row < num_old_gs; ++row) {
356  if (dim_kinds[row] != GEN_VIRTUAL) {
357  ++num_redundant;
358  }
359  }
360  if (num_redundant > 0) {
361  // Chop zero rows from end of system, to keep minimal form.
362  gen_sys.remove_trailing_rows(num_redundant);
363  gen_sys.unset_pending_rows();
364  }
365  dim_kinds.resize(new_dimension + 1);
366  // TODO: Consider if it is worth also preserving the congruences
367  // if they are also in minimal form.
368  }
369  clear_congruences_up_to_date();
370  // Extend the zero dim false congruence system to the appropriate
371  // dimension and then swap it with `con_sys'.
373  // Extra 2 columns for inhomogeneous term and modulus.
374  cgs.set_space_dimension(new_dimension + 2);
375  swap(con_sys, cgs);
376  }
377  else {
378  PPL_ASSERT(congruences_are_minimized());
379  con_sys.set_space_dimension(new_dimension);
380  // Count the actual number of rows that are now redundant.
381  dimension_type num_redundant = 0;
382  for (dimension_type row = space_dim; row > new_dimension; --row) {
383  if (dim_kinds[row] != CON_VIRTUAL) {
384  ++num_redundant;
385  }
386  }
387 
388  con_sys.remove_rows(0, num_redundant, true);
389  dim_kinds.erase(dim_kinds.begin() + (new_dimension + 1),
390  dim_kinds.end());
391 
392  clear_generators_up_to_date();
393  // Replace gen_sys with an empty system of the right dimension.
394  // Extra 2 columns for inhomogeneous term and modulus.
395  Grid_Generator_System gs(new_dimension + 2);
396  gen_sys.m_swap(gs);
397  }
398 
399  // Update the space dimension.
400  space_dim = new_dimension;
401 
402  PPL_ASSERT(OK(true));
403 }
404 
405 void
407  // `var' must be one of the dimensions of the vector space.
408  if (var.space_dimension() > space_dim) {
409  throw_dimension_incompatible("expand_space_dimension(v, m)", "v", var);
410  }
411 
412  // Adding 0 dimensions leaves the same grid.
413  if (m == 0) {
414  return;
415  }
416 
417  // The resulting space dimension must be at most the maximum.
418  check_space_dimension_overflow(m, max_space_dimension() - space_dimension(),
419  "PPL::Grid::",
420  "expand_space_dimension(v, m)",
421  "adding m new space dimensions exceeds "
422  "the maximum allowed space dimension");
423 
424  // Save the number of dimensions before adding new ones.
425  const dimension_type old_dim = space_dim;
426 
427  // Add the required new dimensions.
428  add_space_dimensions_and_embed(m);
429 
430  const Congruence_System& cgs = congruences();
431  Congruence_System new_congruences;
433  cgs_end = cgs.end(); i != cgs_end; ++i) {
434  const Congruence& cg = *i;
435 
436  Coefficient_traits::const_reference coeff = cg.coefficient(var);
437 
438  // Only consider congruences that constrain `var'.
439  if (coeff == 0) {
440  continue;
441  }
442 
443  Congruence cg_copy = cg;
444  cg_copy.expr.set_coefficient(var, Coefficient_zero());
445 
446  // Each relevant congruence results in `m' new congruences.
447  for (dimension_type dst_d = old_dim; dst_d < old_dim+m; ++dst_d) {
448  Congruence x = cg_copy;
449  add_mul_assign(x.expr, coeff, Variable(dst_d));
450  new_congruences.insert_verbatim(x, Recycle_Input());
451  }
452  }
453  add_recycled_congruences(new_congruences);
454  PPL_ASSERT(OK());
455 }
456 
457 void
459  // TODO: this implementation is _really_ an executable specification.
460 
461  // `dest' should be one of the dimensions of the grid.
462  if (dest.space_dimension() > space_dim) {
463  throw_dimension_incompatible("fold_space_dimensions(vs, v)", "v", dest);
464  }
465 
466  // Folding only has effect if dimensions are given.
467  if (vars.empty()) {
468  return;
469  }
470 
471  // All variables in `vars' must be dimensions of the grid.
472  if (vars.space_dimension() > space_dim) {
473  throw_dimension_incompatible("fold_space_dimensions(vs, v)",
474  "vs.space_dimension()",
475  vars.space_dimension());
476  }
477 
478  // Moreover, `dest.id()' must not occur in `vars'.
479  if (vars.find(dest.id()) != vars.end()) {
480  throw_invalid_argument("fold_space_dimensions(vs, v)",
481  "v should not occur in vs");
482  }
483  // All of the affine images we are going to compute are not invertible,
484  // hence we will need to compute the grid generators of the polyhedron.
485  // Since we keep taking copies, make sure that a single conversion
486  // from congruences to grid generators is computed.
487  (void) grid_generators();
488  // Having grid generators, we now know if the grid is empty:
489  // in that case, folding is equivalent to just removing space dimensions.
490  if (!marked_empty()) {
491  for (Variables_Set::const_iterator i = vars.begin(),
492  vs_end = vars.end(); i != vs_end; ++i) {
493  Grid copy = *this;
494  copy.affine_image(dest, Linear_Expression(Variable(*i)));
495  upper_bound_assign(copy);
496  }
497  }
498  remove_space_dimensions(vars);
499  PPL_ASSERT(OK());
500 }
void add_space_dimensions(Congruence_System &cgs, Grid_Generator_System &gs, dimension_type dims)
Adds new space dimensions to the given systems.
Definition: Grid_chdims.cc:34
void add_space_dimensions_and_embed(dimension_type m)
Adds m new space dimensions and embeds the old grid in the new vector space.
Definition: Grid_chdims.cc:77
dimension_type space_dimension() const
Returns the dimension of the smallest vector space enclosing all the variables whose indexes are in t...
void expand_space_dimension(Variable var, dimension_type m)
Creates m copies of the space dimension corresponding to var.
Definition: Grid_chdims.cc:406
dimension_type max_space_dimension()
Returns the maximum space dimension this library can handle.
void swap(CO_Tree &x, CO_Tree &y)
dimension_type space_dimension() const
Returns the dimension of the vector space enclosing *this.
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...
Definition: Grid_chdims.cc:318
size_t dimension_type
An unsigned integral type for representing space dimensions.
An std::set of variables' indexes.
const Congruence_System & congruences() const
Returns the system of congruences.
Definition: Grid_public.cc:300
bool set_space_dimension(dimension_type new_space_dim)
Sets the number of space dimensions to new_space_dim.
void add_mul_assign(GMP_Integer &x, const GMP_Integer &y, const GMP_Integer &z)
const_iterator end() const
Returns the past-the-end const_iterator.
void add_universe_rows_and_columns(dimension_type dims)
Adds dims rows and dims columns of zeroes to the matrix, initializing the added rows as in the univer...
void insert_verbatim(Congruence &cg, Recycle_Input)
Inserts in *this the congruence cg, stealing its contents and increasing the number of space dimensio...
void set_coefficient(Variable v, Coefficient_traits::const_reference n)
Sets the coefficient of v in *this to n.
void set_space_dimension(dimension_type space_dim)
Resizes the system to the specified space dimension.
dimension_type id() const
Returns the index of the Cartesian axis associated to the variable.
static const Congruence & zero_dim_false()
Returns a reference to the false (zero-dimension space) congruence .
A dimension of the vector space.
dimension_type space_dimension() const
Returns the dimension of the vector space enclosing *this.
dimension_type space_dimension() const
Returns the dimension of the vector space enclosing *this.
void remove_space_dimensions(const Variables_Set &vars)
Removes all the specified dimensions from the vector space.
Definition: Grid_chdims.cc:273
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
const_iterator begin() const
Returns the const_iterator pointing to the first congruence, if this is not empty; otherwise...
The universe element, i.e., the whole vector space.
dimension_type space_dim
The number of dimensions of the enclosing vector space.
Definition: Grid_defs.hh:1983
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
void fold_space_dimensions(const Variables_Set &vars, Variable dest)
Folds the space dimensions in vars into dest.
Definition: Grid_chdims.cc:458
bool marked_empty() const
Returns true if the grid is known to be empty.
Definition: Grid_inlines.hh:35
bool congruences_are_minimized() const
Returns true if the system of congruences is minimized.
Definition: Grid_inlines.hh:50
dimension_type space_dimension() const
Returns the dimension of the vector space enclosing *this.
void add_space_dimensions_and_project(dimension_type m)
Adds m new space dimensions to the grid and does not embed it in the new vector space.
Definition: Grid_chdims.cc:154
void upper_bound_assign(std::map< dimension_type, Linear_Form< FP_Interval_Type > > &ls1, const std::map< dimension_type, Linear_Form< FP_Interval_Type > > &ls2)
Coefficient_traits::const_reference coefficient(Variable v) const
Returns the coefficient of v in *this.
bool generators_are_minimized() const
Returns true if the system of generators is minimized.
Definition: Grid_inlines.hh:55
void concatenate_assign(const Grid &y)
Assigns to *this the concatenation of *this and y, taken in this order.
Definition: Grid_chdims.cc:225
void add_unit_rows_and_space_dimensions(dimension_type dims)
Adds dims rows and dims space dimensions to the matrix, initializing the added rows as in the unit co...
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 expre...