1 /* SpecialSimplex.cpp -- Check for a special simplex using CPLEX
2 
3    Copyright 2007 Matthias Koeppe
4 
5    This file is part of LattE.
6 
7    LattE is free software; you can redistribute it and/or modify it
8    under the terms of the version 2 of the GNU General Public License
9    as published by the Free Software Foundation.
10 
11    LattE is distributed in the hope that it will be useful, but
12    WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with LattE; if not, write to the Free Software Foundation,
18    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
19 */
20 
21 #include <fstream>
22 #include <iostream>
23 #include <cstdlib>
24 #include <cassert>
25 
26 #include <cplex.h>
27 
28 #include "latte_gmp.h"
29 #include "latte_random.h"
30 #include "SpecialSimplex.h"
31 #include "triangulation/TriangulationWithTOPCOM.h"
32 #include "triangulation/RegularTriangulationWith4ti2.h"
33 #include "triangulation/RegularTriangulationWithCddlib.h"
34 #include "dual.h"
35 #include "print.h"
36 
37 using namespace std;
38 
39 static double
convert_ZZ_to_double(const ZZ & zz)40 convert_ZZ_to_double(const ZZ &zz)
41 {
42   mpz_class mpz = convert_ZZ_to_mpz(zz);
43   return mpz.get_d();
44 }
45 
46 listCone *
FindSpecialSimplex(listCone * cone,int numOfVars)47 FindSpecialSimplex(listCone *cone, int numOfVars)
48 {
49   CPXENVptr env;
50   int status;
51   env = CPXopenCPLEX(&status);
52   if (status != 0) {
53     cerr << "Failed to obtain CPLEX environent." << endl;
54     abort();
55   }
56 
57   int num_rays = lengthListVector(cone->rays);
58 
59   CPXLPptr lp = CPXcreateprob(env, &status, "repr");
60   if (status != 0) abort();
61 
62   status = CPXchgprobtype(env, lp, CPXPROB_MILP);
63   if (status != 0) abort();
64 
65   /* Fill equations that express that e_n is a linear combination of
66      the rays, using variables x_i as multipliers. */
67   status = CPXnewrows(env, lp, numOfVars, /*rhs:*/ NULL, /*sense:*/ NULL,
68 		      /*rngval:*/ NULL, /*rownames:*/ NULL);
69   if (status != 0) abort();
70 
71   status = CPXnewcols(env, lp, num_rays, /*obj:*/ NULL,
72 		      /*lb:*/ NULL, /*ub:*/ NULL,
73 		      /*ctype:*/ NULL, /*colname:*/NULL);
74   if (status != 0) abort();
75 
76   listVector *ray;
77   int j;
78   for (ray = cone->rays, j = 0; ray!=NULL; ray = ray->rest, j++) {
79     int i;
80     for (i = 0; i<numOfVars; i++) {
81       status = CPXchgcoef(env, lp, i, j, convert_ZZ_to_double(ray->first[i]));
82       if (status != 0) abort();
83     }
84     /* Add constraints x_i <= y_i <= M x_i */
85     double M = 1000;
86     int beg[2];
87     int ind[4];
88     double val[4];
89     char sense[2];
90     beg[0] = 0;  ind[0] = j; val[0] = +1; ind[1] = num_rays + j; val[1] = -M; sense[0] = 'L';
91     beg[1] = 2;  ind[2] = j; val[2] = -1; ind[3] = num_rays + j; val[3] = +1; sense[1] = 'L';
92     status = CPXaddrows(env, lp, 1, 2, 4, /* rhs: */ NULL, sense, beg, ind, val,
93 			/* colname: */ NULL, /* rowname: */ NULL);
94     if (status != 0) abort();
95     int index = num_rays + j;
96     char ctype = 'B';
97     status = CPXchgctype(env, lp, 1, &index, &ctype);
98     if (status != 0) abort();
99     /* We minimize the number of used rays. */
100     status = CPXchgcoef(env, lp, -1, index, 1.0);
101     if (status != 0) abort();
102   }
103   { /* Enter -10 * e_n as a right-hand side.
104        Later we try + 10 * e_n too. */
105     int index = numOfVars - 1;
106     double value = +10.0;
107     status = CPXchgrhs(env, lp, 1, &index, &value);
108     if (status != 0) abort();
109   }
110 #if 0
111   /* Add a cardinality constraint. */
112   {
113     int beg = 0;
114     int *ind = new int[num_rays];
115     double *val = new double[num_rays];
116     double rhs = numOfVars;
117     char sense = 'E';
118     int i;
119     for (i = 0; i<num_rays; i++) {
120       ind[i] = num_rays + i;
121       val[i] = 1.0;
122     }
123     status = CPXaddrows(env, lp, 0, 1, num_rays, &rhs, &sense, &beg, ind, val,
124 			/* colname: */ NULL, /* rowname: */ NULL);
125     delete[] ind;
126     delete[] val;
127     if (status != 0) abort();
128   }
129 #endif
130   /* Add variables and constraints that ensure we can linearly
131      represent all other unit vectors too (linear span property). */
132   int k;
133   double *lb = new double[num_rays];
134   double *ub = new double[num_rays];
135   {
136     int i;
137     for (i = 0; i<num_rays; i++) {
138       lb[i] = -CPX_INFBOUND;
139       ub[i] = CPX_INFBOUND;
140     }
141   }
142   for (k = 0; k<numOfVars - 1; k++) {
143     int row_offset = CPXgetnumrows(env, lp);
144     int col_offset = CPXgetnumcols(env, lp);
145     status = CPXnewrows(env, lp, numOfVars, /*rhs:*/ NULL, /*sense:*/ NULL,
146 			/*rngval:*/ NULL, /*rownames:*/ NULL);
147     if (status != 0) abort();
148     { /* Enter 10 * e_k as a right-hand side. */
149       int index = row_offset + k;
150       double value = 10.0;
151       status = CPXchgrhs(env, lp, 1, &index, &value);
152       if (status != 0) abort();
153     }
154     /* New variables z_k */
155     status = CPXnewcols(env, lp, num_rays, NULL,
156 			lb, ub,
157 			/*ctype:*/ NULL, /*colname:*/NULL);
158     if (status != 0) abort();
159 
160     for (ray = cone->rays, j = 0; ray!=NULL; ray = ray->rest, j++) {
161       int i;
162       for (i = 0; i<numOfVars; i++) {
163 	status = CPXchgcoef(env, lp, i + row_offset, j + col_offset,
164 			    convert_ZZ_to_double(ray->first[i]));
165 	if (status != 0) abort();
166       }
167       /* Add constraints -M x_i <= z_{ki} <= M x_i */
168       double M = 1000;
169       int beg[2];
170       int ind[4];
171       double val[4];
172       char sense[2];
173       beg[0] = 0;  ind[0] = j + col_offset; val[0] = +1; ind[1] = num_rays + j; val[1] = -M; sense[0] = 'L';
174       beg[1] = 2;  ind[2] = j + col_offset; val[2] = -1; ind[3] = num_rays + j; val[3] = -M; sense[1] = 'L';
175       status = CPXaddrows(env, lp, 0, 2, 4, /* rhs: */ NULL, sense, beg, ind, val,
176 			  /* colname: */ NULL, /* rowname: */ NULL);
177       if (status != 0) abort();
178     }
179   }
180   status = CPXwriteprob(env, lp, "special-simplex.lp", "LP");
181   if (status != 0) abort();
182 
183   status = CPXsetintparam(env, CPX_PARAM_SCRIND, CPX_ON);
184   if (status != 0) abort();
185 
186   status = CPXmipopt(env, lp);
187   if (status != 0) abort();
188 
189   int stat = CPXgetstat(env, lp);
190   if (stat != CPXMIP_OPTIMAL) {
191     cerr << "No solution for + e_n (CPLEX solution status "
192 	   << stat << ")." << endl;
193 
194     int index = numOfVars - 1;
195     double value = -10.0;
196     status = CPXchgrhs(env, lp, 1, &index, &value);
197     if (status != 0) abort();
198 
199     status = CPXmipopt(env, lp);
200     if (status != 0) abort();
201 
202     int stat = CPXgetstat(env, lp);
203     if (stat != CPXMIP_OPTIMAL) {
204       cerr << "No solution for -e_n (CPLEX solution status "
205 	   << stat << ")." << endl;
206       cerr << "Did not find special simplex." << endl;
207       exit(1);
208     }
209   }
210 
211   /* Inspect which rays form the special simplex. */
212 
213   cerr << "Vertical line is generated as follows by the rays: " << endl;
214   listCone *special = createListCone();
215   special->vertex = new Vertex(*cone->vertex);
216   {
217     double *x = new double[num_rays];
218     double *y = new double[num_rays];
219     status = CPXgetmipx(env, lp, y, 0, num_rays - 1);
220     if (status != 0) abort();
221     status = CPXgetmipx(env, lp, x, num_rays, 2 * num_rays - 1);
222     if (status != 0) abort();
223     int i;
224     listVector *ray;
225     for (ray = cone->rays, i = 0; i<num_rays; ray = ray->rest, i++) {
226       if (fabs(x[i] - 1.0) < 0.1) {
227 	cerr << "+ " << y[i] << " * " << ray->first << endl;
228 	special->rays = new listVector(ray->first, special->rays);
229       }
230     }
231     delete[] x;
232   }
233 
234   if (lengthListVector(special->rays) == numOfVars) {
235     cerr << "Found simplicial special cone (good)." << endl;
236   }
237   else {
238     cerr << "Smallest special cone is non-simplicial; it has " << lengthListVector(special->rays)
239 	 << " generators." << endl;
240   }
241   return special;
242 }
243 
244 struct special_height_data {
245   listCone *special_cone;
246   vec_ZZ c;
247 };
248 
249 static bool
has_ray(listCone * cone,const vec_ZZ & ray_vector)250 has_ray(listCone *cone, const vec_ZZ &ray_vector)
251 {
252   listVector *ray;
253   for (ray = cone->rays; ray != NULL; ray = ray->rest)
254     if (ray->first == ray_vector) return true;
255   return false;
256 }
257 
258 void
special_height(mpq_t height,const vec_ZZ & ray,void * data)259 special_height(mpq_t height, const vec_ZZ &ray, void *data)
260 {
261   special_height_data *shd = (special_height_data *) data;
262 
263   /* Compute nominal height. */
264   ZZ alpha;
265   alpha = 100000;
266   ZZ h;
267   InnerProduct(h, shd->c, ray);
268   h = alpha-h;
269 
270   int max_height = 100000;
271 
272   /* Increase height of all non-special rays */
273   if (!has_ray(shd->special_cone, ray)) {
274     h += 1000 * uniform_random_number(1000, max_height);
275   }
276 
277   mpz_class hz = convert_ZZ_to_mpz(h);
278   mpq_set_z(height, hz.get_mpz_t());
279 }
280 
281 
282 static bool
facets_ok(listCone * cone,int numOfVars)283 facets_ok(listCone *cone, int numOfVars)
284 {
285   listVector *facet;
286   for (facet = cone->facets; facet!=NULL; facet=facet->rest) {
287     if (facet->first[numOfVars - 1] == 0)
288       return false;
289   }
290   return true;
291 }
292 
293 class FacetCheckingConeTransducer : public ConeTransducer {
294   BarvinokParameters *params;
295   bool have_bad_facets;
296 public:
FacetCheckingConeTransducer(BarvinokParameters * a_params)297   FacetCheckingConeTransducer(BarvinokParameters *a_params) :
298     params(a_params), have_bad_facets(false) {}
299   int ConsumeCone(listCone *cone);
300   virtual ~FacetCheckingConeTransducer();
301 };
302 
ConsumeCone(listCone * cone)303 int FacetCheckingConeTransducer::ConsumeCone(listCone *cone)
304 {
305   int numOfVars = cone->rays->first.length();
306   if (cone->facets == NULL) {
307       dualizeCone(cone, params->Number_of_Variables, params);
308       dualizeCone(cone, params->Number_of_Variables, params);
309   }
310   if (!facets_ok(cone, numOfVars)) {
311     cerr << "This cone has bad facets." << endl;
312     printCone(cone, numOfVars);
313     have_bad_facets = true;
314   }
315   return consumer->ConsumeCone(cone);
316 }
317 
~FacetCheckingConeTransducer()318 FacetCheckingConeTransducer::~FacetCheckingConeTransducer()
319 {
320   if (have_bad_facets) {
321     cerr << "There are cones with bad facets." << endl;
322   }
323 }
324 
325 
326 
327 class ExistenceCheckingConeTransducer : public ConeTransducer {
328   bool found_special;
329   listCone *special;
330 public:
ExistenceCheckingConeTransducer(listCone * a_special)331   ExistenceCheckingConeTransducer(listCone *a_special) :
332     found_special(false),
333     special(a_special) {}
334   int ConsumeCone(listCone *cone);
335   virtual ~ExistenceCheckingConeTransducer();
336 };
337 
338 static bool
cone_equal(listCone * a,listCone * b)339 cone_equal(listCone *a, listCone *b)
340 {
341   if (lengthListVector(a->rays) != lengthListVector(b->rays))
342     return false;
343   listVector *a_ray;
344   for (a_ray = a->rays; a_ray != NULL; a_ray = a_ray->rest) {
345     if (!has_ray(b, a_ray->first)) return false;
346   }
347   return true;
348 }
349 
350 static bool
is_subcone(listCone * a,listCone * b)351 is_subcone(listCone *a, listCone *b)
352 {
353   listVector *a_ray;
354   for (a_ray = a->rays; a_ray != NULL; a_ray = a_ray->rest) {
355     if (!has_ray(b, a_ray->first)) return false;
356   }
357   return true;
358 }
359 
ConsumeCone(listCone * cone)360 int ExistenceCheckingConeTransducer::ConsumeCone(listCone *cone)
361 {
362   int numOfVars = cone->rays->first.length();
363   if (/* cone_equal(cone, special) */ is_subcone(special, cone)) {
364     if (!cone_equal(special, cone)) {
365       cerr << "Warning: Special cone only appeared as a subcone." << endl;
366     }
367     cerr << "Cone found: " << endl;
368     printCone(cone, numOfVars);
369     found_special = true;
370   }
371   return consumer->ConsumeCone(cone);
372 }
373 
~ExistenceCheckingConeTransducer()374 ExistenceCheckingConeTransducer::~ExistenceCheckingConeTransducer()
375 {
376   if (!found_special) {
377     cerr << "WARNING: Special cone did not appear in the triangulation." << endl;
378   }
379   else {
380     cerr << "Special cone appears in the triangulation. Good."  << endl;
381   }
382 }
383 
384 
385 void
special_triangulation_with_subspace_avoiding_facets(listCone * cone,BarvinokParameters * Parameters,ConeConsumer & consumer)386 special_triangulation_with_subspace_avoiding_facets
387 (listCone *cone, BarvinokParameters *Parameters, ConeConsumer &consumer)
388 {
389   int numOfVars = Parameters->Number_of_Variables;
390   listCone *special_cone;
391   if (Parameters->triangulation_special_cone) {
392     cerr << "Using the provided special cone..." << endl;
393     special_cone = Parameters->triangulation_special_cone;
394   }
395   else {
396     cerr << "Looking for a special cone using CPLEX..." << endl;
397     special_cone = FindSpecialSimplex(cone, numOfVars);
398     cerr << "Found special cone: " << endl;
399     if (special_cone->facets == NULL) {
400       dualizeCone(special_cone, numOfVars, Parameters);
401       dualizeCone(special_cone, numOfVars, Parameters);
402     }
403     printListCone(special_cone, numOfVars);
404   }
405   ConeConsumer *effective_consumer = &consumer;
406   /* Install check for singularity-avoiding facet normals. */
407 #if 1
408   FacetCheckingConeTransducer checking_transducer(Parameters);
409   checking_transducer.SetConsumer(effective_consumer);
410   effective_consumer = &checking_transducer;
411 #endif
412   /* Install check for special simplex. */
413   ExistenceCheckingConeTransducer existence_transducer(special_cone);
414   existence_transducer.SetConsumer(effective_consumer);
415   effective_consumer = &existence_transducer;
416 #ifdef TRY_WITH_TOPCOM
417   listCone *sorted_cone = createListCone();
418   sorted_cone->vertex = new Vertex(*cone->vertex);
419   listVector *ray;
420   for (ray = cone->rays; ray != NULL; ray = ray->rest) {
421     if (!has_ray(special_cone, ray->first))
422       sorted_cone->rays = appendVectorToListVector(ray->first, sorted_cone->rays);
423   }
424   for (ray = cone->rays; ray != NULL; ray = ray->rest) {
425     if (has_ray(special_cone, ray->first))
426       sorted_cone->rays = appendVectorToListVector(ray->first, sorted_cone->rays);
427   }
428   cerr << "Sorted cone: " << endl;
429   printCone(sorted_cone, numOfVars);
430   triangulate_cone_with_TOPCOM(sorted_cone,
431 			       numOfVars, *effective_consumer);
432 #else
433   special_height_data shd;
434   shd.special_cone = special_cone;
435   {
436     /* Choose a generic facet normal (c,1) for the special cone */
437     shd.c.SetLength(numOfVars);
438     int i;
439     for (i = 0; i<numOfVars; i++)
440       shd.c[i] = uniform_random_number(1, 100000);
441   }
442   /*FIXME: */ Parameters->nonsimplicial_subdivision = true;
443 #if 1
444   triangulate_cone_with_cddlib(cone, Parameters,
445 			       special_height, &shd,
446 			       numOfVars, *effective_consumer);
447 #else
448   triangulate_cone_with_4ti2(cone, Parameters,
449 			     special_height, &shd,
450 			     *effective_consumer);
451 #endif
452 #endif
453 }
454