Gröbner basis project
Codebase for research into Gröbner basis computation
skeleton.cpp
1 
9 /*****************************************************************************\
10 * This file is part of DynGB. *
11 * *
12 * DynGB is free software: you can redistribute it and/or modify *
13 * it under the terms of the GNU General Public License as published by *
14 * the Free Software Foundation, either version 2 of the License, or *
15 * (at your option) any later version. *
16 * *
17 * Foobar is distributed in the hope that it will be useful, *
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
20 * GNU General Public License for more details. *
21 * *
22 * You should have received a copy of the GNU General Public License *
23 * along with DynGB. If not, see <http://www.gnu.org/licenses/>. *
24 \*****************************************************************************/
25 
26 #ifndef SKELETON_C
27 #define SKELETON_C
28 
29 #include <iostream>
30 #include <cstdlib>
31 #include <algorithm>
32 using std::set_union;
33 
34 #include "system_constants.hpp"
35 
36 #include "goda.hpp"
37 #include "skeleton.hpp"
38 
39 edge::edge(const ray &first_ray, const ray &second_ray)
40  : first(first_ray), second(second_ray)
41 {
42  if (first < second)
43  first.swap(second);
44 }
45 
46 edge::edge(const edge &old_edge)
47  : first(old_edge.first), second(old_edge.second)
48 {
49  // nothing to do
50 }
51 
52 ostream & operator<<(ostream & ostr, const edge &e)
53 {
54  ostr << "{ " << e.first << " , " << e.second << " }";
55  return ostr;
56 }
57 
58 bool operator < (const edge &first, const edge &second)
59 {
60  bool result = true;
61  if (first.first < second.first)
62  {
63  // do nothing
64  }
65  else if (second.first < first.first)
66  result = false;
67  else // first entries equal; look at second
68  if (first.second < second.second)
69  {
70  // do nothing
71  }
72  else
73  result = false;
74  return result;
75 }
76 
77 edge & edge::operator=(const edge &other)
78 {
79  if (!(*this == other))
80  {
81  first = other.first;
82  second = other.second;
83  }
84  return *this;
85 }
86 
87 void skeleton::common_initialization(NVAR_TYPE dimension)
88 {
89  //cout << "creating skeleton with dimension " << dimension << endl;
90  dim = dimension;
91  // initialize the constraints
92  CONSTR_TYPE * constr_coords = new CONSTR_TYPE[dim];
93  for (NVAR_TYPE i = 0; i < dim; ++i) constr_coords[i] = 0;
94  // add the constraint xi >= 0 for each i = 0, ..., dim - 1
95  for (NVAR_TYPE i = 0; i < dim; ++i)
96  {
97  constr_coords[i] = 1;
98  constraints.push_back(constraint(dim, constr_coords));
99  constr_coords[i] = 0;
100  }
101  delete [] constr_coords;
102  // initialize the rays, one for each axis
103  for (NVAR_TYPE i = 0; i < dim; ++i)
104  {
105  ray new_ray(dim, i);
106  rays.insert(new_ray);
107  }
108  // initialize the edges
109  // currently, all the rays are adjacent
110  for (auto riter = rays.begin(); riter != rays.end(); ++riter)
111  for (auto siter = rays.begin(); siter != rays.end(); ++siter)
112  if (*riter != *siter)
113  {
114  edge new_edge(*riter, *siter);
115  edges.insert(new_edge);
116  }
117 }
118 
119 skeleton::skeleton(NVAR_TYPE dimension)
120 {
121  common_initialization(dimension);
122 }
123 
124 skeleton::skeleton(NVAR_TYPE dimension, vector<constraint> &constraints)
125  //: skeleton(dimension)
126 {
127  common_initialization(dimension);
128  solve(constraints);
129 }
130 
132  : constraints(old_skeleton.constraints),
133  edges(old_skeleton.edges), dim(old_skeleton.dim)
134 
135 {
136  rays = old_skeleton.rays;
137  // nothing more to do
138 }
139 
140 bool skeleton::copy(const LP_Solver * other) {
141  const skeleton * old_skeleton = dynamic_cast<const skeleton *>(other);
142  if (old_skeleton != nullptr) {
143  constraints.clear(); rays.clear(); edges.clear();
144  for (const constraint & c : old_skeleton->constraints)
145  constraints.push_back(c);
146  for (const ray & r : old_skeleton->rays)
147  rays.emplace(r);
148  for (const edge & e : old_skeleton->edges)
149  edges.emplace(e);
150  /*constraints = old_skeleton->constraints;
151  rays = old_skeleton->rays;
152  edges = old_skeleton->edges;*/
153  dim = old_skeleton->dim;
154  }
155  return (old_skeleton != nullptr);
156 }
157 
159 {
160 }
161 
163 {
164  // cout << "processing constraint " << constraint << endl;
165  // innocent until proven guilty
166  bool consistent = true;
167  // sort the rays into the ones above, below, or on the constraint
168  set<ray> rays_above, rays_below, rays_on;
169  for (auto riter = rays.begin(); riter != rays.end(); ++riter)
170  {
171  DOTPROD_TYPE dp = (*riter) * constraint; // overloaded * as dot product :-)
172  if (dp > 0)
173  {
174  rays_above.insert(*riter);
175  // cout << *riter << " is above constraint\n";
176  }
177  else if (dp < 0)
178  {
179  rays_below.insert(*riter);
180  // cout << *riter << " is below constraint\n";
181  }
182  else
183  {
184  ray old_ray = *riter;
185  rays_on.insert(old_ray);
186  // cout << *riter << " is on constraint\n";
187  }
188  }
189  // cout << rays_above.size() << " rays above; " << rays_below.size() << " rays below; " << rays_on.size() << " rays on\n";
190  // check for constitency
191  if (rays_above.size() == 0)
192  {
193  consistent = false;
194  //cout << "long test inconsistent\n";
195  }
196  // proceed only if constraint is consistent, and *not* redundant;
197  // redundancy can be checked by making sure
198  // that at least one ray is below the constraint
199  if (consistent and rays_below.size() != 0)
200  {
201  set<edge> edges_above, edges_on;
202  for (auto eiter = edges.begin(); eiter != edges.end(); ++eiter)
203  {
204  edge e = *eiter;
205  ray u = e.get_first_ray();
206  ray v = e.get_second_ray();
207  //cout << "edge " << u << ',' << v << endl;
208  // identify the edges that lie above and on this constraint
209  if ((u*constraint >= 0) and (v*constraint >= 0))
210  {
211  // cout << "old edge preserved: " << u << ',' << v << "\n";
212  edges_above.insert(e);
213  }
214  }
215  for (auto eiter = edges.begin(); eiter != edges.end(); ++eiter)
216  {
217  edge e = *eiter;
218  ray u = e.get_first_ray();
219  ray v = e.get_second_ray();
220  DOTPROD_TYPE a = u*constraint;
221  DOTPROD_TYPE b = v*constraint;
222  // identify edges that pass through the constraint
223  // (one ray above, one ray below)
224  if (a > 0 and b < 0)
225  {
226  ray w = a*v - b*u;
227  w.simplify_ray();
228  rays_on.insert(w);
229  edges_on.insert(edge(u,w));
230  // cout << "new ray (u,v) is " << w << " with constraints " << endl;
231  }
232  else if (b > 0 and a < 0)
233  {
234  ray w = b*u - a*v;
235  w.simplify_ray();
236  rays_on.insert(w);
237  edges_on.insert(edge(v,w));
238  // cout << "new ray (v,u) is " << w << " with constraints " << endl;
239  }
240  }
241  // clear the old rays, add the new ones (above and on the constraint)
242  rays.clear();
243  for (auto riter = rays_above.begin(); riter != rays_above.end(); ++riter)
244  {
245  rays.insert(*riter);
246  }
247  // cout << "inserted rays above; rays on is\n";
248  // for (auto riter = rays_on.begin(); riter != rays_on.end(); ++riter) { cout << '\t' << *riter << endl; }
249  for (auto riter = rays_on.begin(); riter != rays_on.end(); ++riter)
250  {
251  // cout << "inserting " << *riter << endl;
252  //cout << "return value: " << *(get<0>(rays.insert(*riter)));
253  rays.insert(*riter);
254  //for (auto siter = rays.begin(); siter != rays.end(); ++siter) { cout << '\t' << *siter << endl; }
255  }
256  //cout << rays.size() << " rays\n";
257  // for (auto riter = rays.begin(); riter != rays.end(); ++riter) { cout << '\t' << *riter << endl; }
258  // add the good constraint
259  constraints.push_back(constraint);
260  // determine new edges
261  set<edge> edges_new = adjacencies_by_graphs(rays_on);
262  // combine new edges with old ones that are known to be valid
263  edges = union_of_edge_sets(union_of_edge_sets(edges_above, edges_on), edges_new);
264  //cout << edges.size() << " edges\n";
265  //for (auto eiter = edges.begin(); eiter != edges.end(); ++eiter) { cout << *eiter << ' '; } cout << '\n';
266  }
267  return consistent;
268 }
269 
270 bool skeleton::solve(vector<constraint> &new_constraints)
271 {
272  // innocent until proven guilty
273  bool consistent = true;
274  //cout << "adding " << new_constraints.size() << "constraints\n";
275  //for (const constraint & c : new_constraints)
276  // cout << '\t' << c << endl;
277  // process each constraint sequentially
278  for (
279  auto nciter = new_constraints.begin();
280  consistent and nciter != new_constraints.end();
281  ++nciter
282  )
283  {
284  // perform short test of consistency first
285  consistent = is_consistent(*nciter) and solve(*nciter);
286  //if (!consistent)
287  //{
288  // cout << "inconsistent\n";
289  // cout << "failed ray: " << *nciter;
290  // cout << "skeleton: \n" << *this;
291  //}
292  }
293  //cout << rays.size() << " corner vectors\n";
294  return consistent;
295 }
296 
298  //vector<bool> &a, vector<bool> &b
299  bool * a, bool * b, unsigned m
300 )
301 {
302  int result = 0;
303  /*for (auto aiter = a.begin(); aiter != a.end(); ++aiter)
304  {
305  //cout << "checking " << *aiter << " in other: " << (b.find(*aiter) != b.end()) << endl;
306  if (b.find(*aiter) != b.end())
307  ++result;
308  } */
309  for (unsigned i = 0; i < m; ++i)
310  if (a[i] and b[i]) ++result;
311  return result;
312 }
313 
314 //vector<bool> intersections_of_active_constraints(
315  //vector<bool> &a, vector<bool> &b
317  bool * a, bool * b, bool * result, unsigned m
318 )
319 {
320  // highly unoptimized, but off the top of my head i don't know how to do better
321  //vector<bool> result(a.size());
322  /*for (auto aiter = a.begin(); aiter != a.end(); ++aiter)
323  if (b.find(*aiter) != b.end())
324  result.insert(*aiter);*/
325  for (unsigned i = 0; i < m; ++i)
326  result[i] = (a[i] and b[i]);
327  //return result;
328 }
329 
331  //vector<bool> & a, vector<bool> & b
332  bool * a, bool * b, unsigned m
333 )
334 {
335  // highly unoptimized, but off the top of my head i don't know how to do better
336  bool result = true;
337  for (unsigned i = 0; result and i < m; ++i)
338  if (a[i]) result = b[i];
339  return result;
340 }
341 
342 set<edge> union_of_edge_sets(const set<edge> & a, const set<edge> & b)
343 {
344  // optimized with a hint for the position (riter) of the new element
345  set<edge> result;
346  for (const edge & e : a) result.insert(e);
347  for (const edge & e : b) result.insert(e);
348  return result;
349 }
350 
351 set<edge> skeleton::adjacencies_by_graphs(set<ray> new_rays)
352 {
353  static unsigned long invocations;
354  set<edge> new_edges;
355  set<ray> tested_rays;
356  bool * Zu = new bool [constraints.size()] { false };
357  bool * Zv = new bool [constraints.size()] { false };
358  bool * w_active = new bool [constraints.size()] { false };
359  bool * Zuv = new bool [constraints.size()] { false };
360  // loop through each new ray, examining active constraints shared with other rays
361  for (auto riter = new_rays.begin(); riter != new_rays.end(); ++riter)
362  {
363  ray u = *riter;
364  tested_rays.insert(u);
366  // D's rays have at least dim - 2 active constraints in common with u
367  // (see Proposition 3 in Zolotych's paper)
368  set<ray> D;
369  for (auto siter = new_rays.begin(); siter != new_rays.end(); ++siter)
370  if (*riter != *siter)
371  {
372  ray v = *siter;
374  //cout << "checking constraints of " << u << " against " << v << " for " << dim << endl;
375  //if (number_of_common_constraints(*Zu, *Zv) >= dim - 2)
376  if (number_of_common_constraints(Zu, Zv, constraints.size()) >= dim - 2)
377  {
378  //cout << "accept " << u << ',' << v << " from active constraints\n";
379  D.insert(v);
380  } else {
381  //cout << "reject " << u << ',' << v << " from active constraints\n";
382  }
383  }
384  // check u with each v in D, making sure their active constraints
385  // are not a subset of the active constraints of any w in D
386  // (see Proposition 4 (graph test) in Zolotych's paper)
387  unsigned ijk = 0;
388  for (auto diter = D.begin(); diter != D.end(); ++diter)
389  {
390  ray v = *diter;
391  if (tested_rays.find(v) == tested_rays.end()) // avoid doubling edges
392  {
394  // WARNING: I have commented out the following line, because it seems
395  // unnecessary: v is in D iff the size of the intersection is at least
396  // dim - 2. If there are unexpected bugs, this commenting should be
397  // reconsidered.
398  // if (intersections_of_active_constraints(Zu, Zv).size() >= dim - 2)
399  {
400  bool can_be_added = true;
401  intersections_of_active_constraints(Zu, Zv, Zuv, constraints.size());
402  for (
403  auto dditer = D.begin();
404  dditer != D.end() and can_be_added;
405  ++dditer
406  )
407  {
408  ray w = *dditer;
409  if (!(w == v)) {
410  which_constraints_active_at(w, w_active);
411  if (is_first_subset_of_second(Zuv, w_active, constraints.size()))
412  {
413  //cout << "rejecting " << u << ',' << v << " because of " << w << endl;
414  can_be_added = false;
415  }
416  }
417  }
418  if (can_be_added)
419  {
420  edge new_edge(u, v);
421  //cout << "edge " << new_edge << " passes all criteria\n";
422  new_edges.insert(new_edge);
423  }
424  }
425  }
426  }
427  }
428  delete [] Zu;
429  delete [] Zv;
430  delete [] w_active;
431  delete [] Zuv;
432  return new_edges;
433 }
434 
435 ostream & operator << (ostream & ostr, const skeleton &skel)
436 {
437  // header, start constraints
438  ostr << "Skeleton defined by constraints" << endl;
439  for (
440  vector<constraint>::const_iterator citer=skel.constraints.begin();
441  citer != skel.constraints.end();
442  ++citer
443  )
444  ostr << '\t' << *citer << endl;
445  // rays
446  ostr << "has " << skel.rays.size() << " rays" << endl;
447  for (auto riter=skel.rays.begin(); riter != skel.rays.end(); ++riter)
448  ostr << '\t' << *riter << endl;
449  //edges
450  ostr << "connected in " << skel.edges.size() << " edges" << endl;
451  for (auto eiter=skel.edges.begin(); eiter != skel.edges.end(); ++eiter)
452  ostr << '\t' << *eiter << endl;
453  // footer
454  ostr << "End of skeleton" << endl;
455  return ostr;
456 }
457 
459 {
460  rays.clear();
461  edges.clear();
462  constraints.clear();
463  dim = other.dim;
464  for (
465  auto siter = other.rays.begin();
466  siter != other.rays.end();
467  ++siter
468  )
469  rays.insert(*siter);
470  for (
471  auto eiter = other.edges.begin();
472  eiter != other.edges.end();
473  ++eiter
474  )
475  edges.insert(*eiter);
476  for (
477  vector<constraint>::const_iterator citer = other.constraints.begin();
478  citer != other.constraints.end();
479  ++citer
480  )
481  constraints.push_back(*citer);
482  return *this;
483 }
484 
485 #endif
virtual ~skeleton()
Currently does nothing the compiler wouldn&#39;t do.
Definition: skeleton.cpp:158
set< ray > rays
Definition: lp_solver.hpp:602
virtual bool copy(const LP_Solver *)
performs a deep copy, similar to a copy constructor
Definition: skeleton.cpp:140
an edge connecting the two rays and
Definition: skeleton.hpp:58
void common_initialization(NVAR_TYPE)
Initialization common to all constructors.
Definition: skeleton.cpp:87
skeleton & operator=(const skeleton &)
Assignment operator; empties current set & copies from other.
Definition: skeleton.cpp:458
virtual bool solve(vector< constraint > &)
Adds the indicated constraints (plural!) and re-computes the skeleton.
Definition: skeleton.cpp:270
ray get_first_ray() const
Returns the first ray listed in this edge.
Definition: skeleton.hpp:84
set< edge > adjacencies_by_graphs(set< ray >)
Re-computes the edges in the skeleton using Zolotych&#39;s GraphAdj algorithm and returns the result...
Definition: skeleton.cpp:351
friend bool operator<(const edge &, const edge &)
Compares two edges lexicographically.
Definition: skeleton.cpp:58
void which_constraints_active_at(const ray &u, bool *result) const
returns the set of constraints in the skeleton active at u
Definition: skeleton.hpp:258
void swap(ray &)
Swap two rays of equal dimension by swapping their data, avoiding memory reallocation.
Definition: lp_solver.cpp:350
a constraint
Definition: lp_solver.hpp:53
skeleton of a polyhedral cone, with methods allowing definition and refinement
Definition: skeleton.hpp:178
exact or approximate polyhedral cone solution, with methods allowing definition and refinement ...
Definition: lp_solver.hpp:504
set< edge > union_of_edge_sets(const set< edge > &a, const set< edge > &b)
Definition: skeleton.cpp:342
friend ostream & operator<<(ostream &, const skeleton &)
prints out the constraints, then the rays, then the edges.
Definition: skeleton.cpp:435
edge & operator=(const edge &)
Assignment operator.
Definition: skeleton.cpp:77
ray get_second_ray() const
Returns the second ray listed in this edge.
Definition: skeleton.hpp:87
friend ostream & operator<<(ostream &, const edge &)
Output has the form where is the first ray in this edge, etc.
Definition: skeleton.cpp:52
skeleton(NVAR_TYPE)
Constructs a basic skeleton in the given number of dimensions, initialized to the axes...
Definition: skeleton.cpp:119
vector< bool > intersections_of_active_constraints(bool *, bool *, unsigned)
bool is_consistent(const constraint &c) const
tests for consistency of a potentially new constraint.
Definition: skeleton.hpp:271
edge(const ray &, const ray &)
Creates a new edge that joins the two rays.
Definition: skeleton.cpp:39
void simplify_ray()
Simplifies the ray by dividing its components by the least common denominator.
Definition: lp_solver.cpp:186
bool is_first_subset_of_second(bool *a, bool *b, unsigned m)
Definition: skeleton.cpp:330
a ray defined by nonnegative coordinates
Definition: lp_solver.hpp:190
int number_of_common_constraints(bool *a, bool *b, unsigned m)
Definition: skeleton.cpp:297