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