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