1 /* Copyright (c) 1997-2021
2 Ewgenij Gawrilow, Michael Joswig, and the polymake team
3 Technische Universität Berlin, Germany
4 https://polymake.org
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version: http://www.gnu.org/licenses/gpl.txt.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15 --------------------------------------------------------------------------------
16 */
17
18 #include "polymake/client.h"
19 #include "polymake/Integer.h"
20 #include "polymake/Rational.h"
21 #include "polymake/Matrix.h"
22 #include "polymake/IncidenceMatrix.h"
23 #include "polymake/Vector.h"
24 #include "polymake/Array.h"
25 #include "polymake/Set.h"
26 #include "polymake/vector"
27 #include "polymake/PowerSet.h"
28 #include "polymake/common/lattice_tools.h"
29
30 // FIXME: use std::vector instead of naked heap allocations when all containers are movable
31 // Try to figure out what all this fiddling with naked pointers should be good for,
32 // and whether there is a more sane alternative to it.
33
34 namespace polymake { namespace polytope {
35 namespace {
36
37 Matrix<Rational> vertices;
38
39 class Face {
40 public:
41 Vector<Rational> projFace;
42 Set<Int> vertices;
43
Face(Int coord,Set<Int> & vert,Face * lower,Face * upper)44 Face(Int coord, Set<Int>& vert, Face* lower, Face* upper)
45 {
46 vertices = vert;
47 projFace = lower->projFace;
48 if (projFace[coord] != 0)
49 projFace -= (projFace[coord]/upper->projFace[coord]) * (upper->projFace);
50 projFace = common::primitive(projFace);
51 }
Face(Set<Int> & vert,Vector<Rational> & proj)52 Face(Set<Int>& vert, Vector<Rational>& proj)
53 {
54 vertices = vert;
55 projFace = proj;
56 }
57 };
58
createChildren(std::vector<Face> * lowerFaces,std::vector<Face> * upperFaces,Int faceDim,Int coord,std::vector<Face> * lowerChildren,std::vector<Face> * upperChildren,Int verbose)59 void createChildren(std::vector<Face>* lowerFaces, std::vector<Face>* upperFaces, Int faceDim, Int coord,
60 std::vector<Face>* lowerChildren, std::vector<Face>* upperChildren, Int verbose)
61 {
62 if (verbose)
63 cout << "creating faces of dim " << faceDim << " from " << lowerFaces->size()<<"+"
64 <<upperFaces->size() << " faces of dim " << faceDim+1 << " using coord " << coord << " ... ";
65 if (lowerFaces->size() < 1 || upperFaces->size() < 1)
66 throw std::runtime_error("lattice_points_via_projection: too few faces!");
67
68 // search through all pairs of faces of lower and upper hull to find pairs
69 // which intersect in a lower dimensional face
70 for (std::vector<Face>::iterator lowerFaceIt=lowerFaces->begin();
71 lowerFaceIt!=lowerFaces->end(); ++lowerFaceIt) {
72 for (std::vector<Face>::iterator upperFaceIt=upperFaces->begin();
73 upperFaceIt!=upperFaces->end(); ++upperFaceIt) {
74 Set<Int> child = (lowerFaceIt->vertices)*(upperFaceIt->vertices);
75 if (child.size() >= faceDim+1 && pm::rank(vertices.minor(child,range(0,coord-1))) >= faceDim+1) {
76 Face childFace(coord, child, &(*lowerFaceIt), &(*upperFaceIt));
77
78 // we want to have each face only once
79 // if we have this vector (projFace) already, add the vertices defining this face to the existing one
80 std::vector<Face> * childList = (childFace.projFace[coord-1] < 0) ? upperChildren : lowerChildren;
81 bool add = true;
82 for (std::vector<Face>::iterator childIt = childList->begin();
83 childIt != childList->end(); ++childIt) {
84 if (childFace.projFace == childIt->projFace) {
85 childIt->vertices += child;
86 add = false;
87 break;
88 }
89 }
90 if (add)
91 childList->push_back(childFace);
92 }
93 }
94 }
95 if (verbose)
96 cout << "done creating " << lowerChildren->size() <<"+" << upperChildren->size() << " new faces."<< endl;
97 }
98
99 // try to find a row R in affine hull that has a nonzero value at the current coordinate
100 // if found reduce all other rows at this coordinate using R and return R
tryAffineHull(Matrix<Rational> ** affineHull,Int coord,Int verbose)101 Vector<Rational>* tryAffineHull(Matrix<Rational>** affineHull, Int coord, Int verbose)
102 {
103 if (verbose)
104 cout << "trying to find affine hull row for coord "<< coord << " ... ";
105 for (Int row = 0; row < (*affineHull)->rows(); ++row) {
106 if ((((*affineHull)->row(row))[coord]) != 0) {
107 Matrix<Rational> * newAffineHull = new Matrix<Rational>((*affineHull)->minor(~scalar2set(row),All));
108 Vector<Rational> * ahrow = new Vector<Rational>((*affineHull)->row(row));
109 for (auto r = entire(rows(*newAffineHull)); !r.at_end(); ++r) {
110 if (!is_zero((*r)[coord])) {
111 (*r) -= ((*r)[coord] / (*ahrow)[coord]) * (*ahrow);
112 }
113 }
114 delete (*affineHull);
115 (*affineHull) = newAffineHull;
116 if (verbose)
117 cout << "using row " << row << ", done updating matrix." << endl;
118 return ahrow;
119 }
120 }
121 if (verbose)
122 cout << "none found" << endl;
123 return nullptr;
124 }
125
126 // reduce all faces using the affine hull row and resort them according to the next coordinate
affineProjection(std::vector<Face> * Faces,Vector<Rational> * affineHullRow,Int coord,std::vector<Face> * lowerChildren,std::vector<Face> * upperChildren,Int verbose)127 void affineProjection(std::vector<Face>* Faces, Vector<Rational>* affineHullRow, Int coord,
128 std::vector<Face>* lowerChildren, std::vector<Face>* upperChildren, Int verbose)
129 {
130 for (Face f : *Faces) {
131 if (f.projFace[coord] != 0) {
132 f.projFace -= (f.projFace[coord] / (*affineHullRow)[coord] ) * (*affineHullRow);
133 }
134 if (f.projFace[coord-1] >= 0)
135 lowerChildren->push_back(f);
136 else
137 upperChildren->push_back(f);
138 }
139 }
140
141 // find upper and lower bound in the next coordinate for all projected points
142 // then create matrix of all lifted points
liftPoints(Matrix<Integer> * projPoints,std::vector<Face> * lowerFaces,std::vector<Face> * upperFaces,Int coord,Int verbose)143 Matrix<Integer>* liftPoints(Matrix<Integer>* projPoints,
144 std::vector<Face>* lowerFaces, std::vector<Face>* upperFaces,
145 Int coord, Int verbose)
146 {
147 Int npoints = projPoints->rows();
148 if (verbose)
149 cout << "lifting " << npoints << " points using coord " << coord << " ... ";
150
151 // first compute where all faces intersect the lines above all projected lattice points
152 Matrix<Rational> lowerBoundMatrix(lowerFaces->size(),npoints);
153 Matrix<Rational> upperBoundMatrix(upperFaces->size(),npoints);
154
155 Int nlower = 0;
156 for (const auto& lowerF : *lowerFaces) {
157 if (lowerF.projFace[coord] > 0) {
158 lowerBoundMatrix[nlower] = -(*projPoints)*lowerF.projFace / lowerF.projFace[coord];
159 ++nlower;
160 }
161 }
162 lowerBoundMatrix = lowerBoundMatrix.minor(sequence(0,nlower),All);
163 Int nupper = 0;
164 for (const auto& upperF : *upperFaces) {
165 upperBoundMatrix[nupper] = -(*projPoints)*upperF.projFace / upperF.projFace[coord];
166 ++nupper;
167 }
168
169 if (verbose >= 2)
170 cout << "[nlower=" << nlower << ",nupper=" << nupper << "] ";
171 // find the bounds for each lattice point
172 Vector<Integer> lowerBounds(lowerBoundMatrix.cols(), std::numeric_limits<Integer>::min());
173 Vector<Integer>::iterator lowerBound = lowerBounds.begin();
174
175 for (auto LBcol = entire(cols(lowerBoundMatrix)); !LBcol.at_end(); ++LBcol, ++lowerBound) {
176 for (auto LBcolentry = entire(*LBcol); !LBcolentry.at_end(); ++LBcolentry) {
177 if (*lowerBound < *LBcolentry)
178 *lowerBound = ceil(*LBcolentry);
179 }
180 }
181
182 if (verbose >= 2)
183 cout << "[apply] ";
184 Vector<Integer> upperBounds(upperBoundMatrix.cols(), std::numeric_limits<Integer>::max());
185 Vector<Integer>::iterator upperBound = upperBounds.begin();
186
187 for (auto UBcol=entire(cols(upperBoundMatrix)); !UBcol.at_end(); ++UBcol, ++upperBound) {
188 for (auto UBcolentry = entire(*UBcol); !UBcolentry.at_end(); ++UBcolentry) {
189 if (*upperBound > *UBcolentry)
190 *upperBound = floor(*UBcolentry);
191 }
192 }
193
194 if (verbose >= 2)
195 cout << "[matrix] ";
196 // generate matrix containing all lifted lattice points, using the projected lattice point with all valid values in the (coord) coordinate
197 const Int count = Int(accumulate(upperBounds - lowerBounds, operations::add())) + npoints;
198 Matrix<Integer>* points = new Matrix<Integer>(count, projPoints->cols());
199 auto point = rows(*points).begin();
200 for (Int i = 0; i < npoints; ++i) {
201 for (Integer j = lowerBounds[i]; j <= upperBounds[i]; ++j, ++point) {
202 *point = projPoints->row(i);
203 (*point)[coord] = j;
204 }
205 }
206 if (verbose)
207 cout << "created " << points->rows() << " points." << endl;
208 return points;
209 }
210
211 // calculate the next coordinate using an affine hull row
liftPointsAffine(Matrix<Integer> * projPoints,Vector<Rational> * affineHullRow,Int coord,Int verbose)212 Matrix<Integer>* liftPointsAffine(Matrix<Integer>* projPoints, Vector<Rational>* affineHullRow, Int coord, Int verbose)
213 {
214 if (verbose)
215 cout << "calculating coord "<< coord << " for "<< projPoints->rows() <<" points using affine hull row ... " ;
216 Set<Int> noninteger;
217 for (Int i = 0; i < projPoints->rows(); ++i) {
218 Rational entry = (*projPoints)[i] * (*affineHullRow) / (*affineHullRow)[coord];
219 if (entry != 0) {
220 if (entry.is_integral())
221 (*projPoints)[i][coord] = -numerator(entry);
222 else
223 noninteger += i;
224 }
225 }
226 if (noninteger.size() > 0) {
227 Matrix<Integer>* liftedPoints = new Matrix<Integer>(projPoints->minor(~noninteger,All));
228 delete projPoints;
229 if (verbose)
230 cout << "done, removed " << noninteger.size() << " non-integer points." << endl;
231 return liftedPoints;
232 } else {
233 if (verbose)
234 cout << "done." << endl;
235 return projPoints;
236 }
237 }
238
239 // main recursion calling the projection and lifting functions
points(std::vector<Face> * lowerFaces,std::vector<Face> * upperFaces,Matrix<Rational> * affineHull,Int faceDim,Int coord,Int totalDim,Int verbose)240 Matrix<Integer>* points(std::vector<Face>* lowerFaces, std::vector<Face>* upperFaces,
241 Matrix<Rational>* affineHull, Int faceDim, Int coord, Int totalDim, Int verbose)
242 {
243 Matrix<Integer>* projPoints = nullptr;
244 Vector<Rational>* affineHullRow = nullptr;
245 if (coord > 1) {
246 // create lists of upper and lower hull in one dimension lower
247 std::vector<Face> * lowerChildren = new std::vector<Face>();
248 std::vector<Face> * upperChildren = new std::vector<Face>();
249
250 affineHullRow = tryAffineHull(&affineHull,coord,verbose);
251
252 if (affineHullRow) {
253 if(verbose)
254 cout << "reducing coord "<< coord << " using affine hull ... ";
255 affineProjection(lowerFaces,affineHullRow,coord,lowerChildren,upperChildren,verbose);
256 affineProjection(upperFaces,affineHullRow,coord,lowerChildren,upperChildren,verbose);
257 if(verbose)
258 cout << "done." << endl;
259 } else {
260 faceDim--;
261 createChildren(lowerFaces,upperFaces,faceDim,coord,lowerChildren,upperChildren, verbose);
262 }
263
264 // main recursion
265 projPoints = points(lowerChildren,upperChildren,affineHull,faceDim,coord-1,totalDim,verbose);
266 delete lowerChildren;
267 delete upperChildren;
268 } else {
269 if (verbose)
270 cout << "*** projecting done ***" << endl << endl << "*** lifting points now ***" << endl;
271 // create one lattice point for the zero dimensional projection
272 projPoints = new Matrix<Integer>(1,totalDim+1);
273 (*projPoints)(0,0)=1;
274 if (faceDim == -1)
275 affineHullRow = new Vector<Rational>(affineHull->row(0));
276 delete affineHull;
277 }
278
279 // lift points up one dimension
280 Matrix<Integer> * liftedPoints;
281 if (affineHullRow) {
282 liftedPoints = liftPointsAffine(projPoints,affineHullRow,coord,verbose);
283 delete affineHullRow;
284 } else {
285 liftedPoints = liftPoints(projPoints, lowerFaces, upperFaces, coord, verbose);
286 delete projPoints;
287 }
288
289 return liftedPoints;
290 }
291
292 } // end anonymous namespace
293
integer_points_projection(BigObject p,Int verbose)294 Matrix<Integer> integer_points_projection(BigObject p, Int verbose)
295 {
296 const Int ambient = p.call_method("AMBIENT_DIM");
297 const Int dim = p.call_method("DIM");
298
299 if (dim == -1)
300 return Matrix<Integer>();
301
302 if (ambient == 0) {
303 Matrix<Integer> LP = unit_matrix<Integer>(1);
304 return LP;
305 }
306
307 const Matrix<Rational> facets = p.give("FACETS");
308 p.give("VERTICES") >> vertices;
309 const Matrix<Rational> AH = p.give("AFFINE_HULL");
310 const IncidenceMatrix<> VIF = p.give("VERTICES_IN_FACETS");
311 Matrix<Rational>* affineHull = new Matrix<Rational>(AH);
312
313 // create first faces = facets
314 std::vector<Face> * lowerFaces = new std::vector<Face>();
315 std::vector<Face> * upperFaces = new std::vector<Face>();
316 for (Int i = 0; i < facets.rows(); ++i) {
317 Vector<Rational> facet(facets[i]);
318 Set<Int> vif(VIF[i]);
319 if(facet[ambient] >= 0)
320 lowerFaces->push_back(Face(vif,facet));
321 else
322 upperFaces->push_back(Face(vif,facet));
323 }
324
325 if (verbose)
326 cout << "*** projecting faces ***" << endl;
327 // start recursion (repeated projection of the faces and lifting of the lattice points of the projected polytope)
328 Matrix<Integer> * latticePoints_ptr = points(lowerFaces,upperFaces,affineHull,dim-1,ambient,ambient,verbose);
329 delete lowerFaces;
330 delete upperFaces;
331 if (verbose)
332 cout << "*** done lifting ***" << endl;
333
334 Matrix<Integer> latticePoints = (*latticePoints_ptr);
335 delete latticePoints_ptr;
336
337 return latticePoints;
338 }
339
340 Function4perl(&integer_points_projection, "integer_points_projection(Polytope; $=0)");
341
342 } }
343
344 // Local Variables:
345 // mode:C++
346 // c-basic-offset:3
347 // indent-tabs-mode:nil
348 // End:
349