1 /*===========================================================================*
2  * This file is part of the Discrete Conic Optimization (DisCO) Solver.      *
3  *                                                                           *
4  * DisCO is distributed under the Eclipse Public License as part of the      *
5  * COIN-OR repository (http://www.coin-or.org).                              *
6  *                                                                           *
7  * Authors:                                                                  *
8  *          Aykut Bulut, Lehigh University                                   *
9  *          Yan Xu, Lehigh University                                        *
10  *          Ted Ralphs, Lehigh University                                    *
11  *                                                                           *
12  * Copyright (C) 2001-2018, Lehigh University, Aykut Bulut, Yan Xu, and      *
13  *                          Ted Ralphs.                                      *
14  * All Rights Reserved.                                                      *
15  *===========================================================================*/
16 
17 
18 #include "DcoCbfIO.hpp"
19 #include "Dco.hpp"
20 #include <CoinPackedMatrix.hpp>
21 #include <CoinFinite.hpp>
22 
23 #include <iostream>
24 #include <fstream>
25 #include <iomanip>
26 #include <cstdlib>
27 #include <cstring>
28 #include <ctime>
29 #include <vector>
30 #include <exception>
31 
32 
DcoCbfIO()33 DcoCbfIO::DcoCbfIO() {
34   col_domains_ = NULL;
35   col_domain_size_ = NULL;
36   integers_ = NULL;
37   row_domains_ = NULL;
38   row_domain_size_ = NULL;
39   obj_coef_ = NULL;
40   row_coord_ = NULL;
41   col_coord_ = NULL;
42   coef_ = NULL;
43   fixed_term_ = NULL;
44 }
45 
DcoCbfIO(int sense,int num_cols,int num_col_domains,CONES const * col_domains,int const * col_domain_size,int num_int,int const * integers,int num_rows,int num_row_domains,CONES const * row_domains,int const * row_domain_size,double const * obj_coef,int num_nz,int const * row_coord,int const * col_coord,double const * coef,double const * fixed_term)46 DcoCbfIO::DcoCbfIO(int sense,
47                    int num_cols, int num_col_domains,
48                    CONES const * col_domains, int const * col_domain_size,
49                    int num_int, int const * integers,
50                    int num_rows, int num_row_domains,
51                    CONES const * row_domains, int const * row_domain_size,
52                    double const * obj_coef,
53                    int num_nz, int const * row_coord,
54                    int const * col_coord, double const * coef,
55                    double const * fixed_term) {
56   version_ = 1;
57   if (sense!=-1 && sense!=1) {
58     std::cerr << "Sense should be either -1 (max) or 1 (min)." << std::endl;
59     throw std::exception();
60   }
61   sense_ = sense;
62   num_cols_ = num_cols;
63   num_col_domains_ = num_col_domains;
64   col_domains_ = new CONES[num_col_domains];
65   std::copy(col_domains, col_domains+num_col_domains, col_domains_);
66   col_domain_size_ = new int[num_col_domains];
67   std::copy(col_domain_size, col_domain_size+num_col_domains, col_domain_size_);
68   num_int_ = num_int;
69   integers_ = new int[num_int];
70   std::copy(integers, integers+num_int, integers_);
71   num_rows_ = num_rows;
72   num_row_domains_ = num_row_domains;
73   row_domains_ = new CONES[num_row_domains];
74   std::copy(row_domains, row_domains+num_row_domains, row_domains_);
75   row_domain_size_ = new int[num_row_domains];
76   std::copy(row_domain_size, row_domain_size+num_row_domains, row_domain_size_);
77   obj_coef_ = new double[num_cols];
78   std::copy(obj_coef, obj_coef+num_cols, obj_coef_);
79   num_nz_ = num_nz;
80   row_coord_ = new int[num_nz];
81   std::copy(row_coord, row_coord+num_nz, row_coord_);
82   col_coord_ = new int[num_nz];
83   std::copy(col_coord, col_coord+num_nz, col_coord_);
84   coef_ = new double[num_nz];
85   std::copy(coef, coef+num_nz, coef_);
86   fixed_term_ = new double[num_rows];
87   std::copy(fixed_term, fixed_term+num_rows, fixed_term_);
88 }
89 
readCbf(char const * prob_file_path)90 void DcoCbfIO::readCbf(char const * prob_file_path) {
91   // open file
92   std::ifstream prob_file;
93   prob_file.open(prob_file_path);
94   std::string line;
95   while (std::getline(prob_file, line)) {
96     if (!line.compare("VER")) {
97       // read VER block
98       prob_file >> version_;
99       if (version_ != 1) {
100         std::cerr << "Only version 1 is supported." << std::endl;
101         throw std::exception();
102       }
103     }
104     else if (!line.compare("OBJSENSE")) {
105       // read objective sense
106       std::string sense_str;
107       prob_file >> sense_str;
108       sense_ = !sense_str.compare("MAX") ? -1 : 1;
109     }
110     else if (!line.compare("VAR")) {
111       // read var
112       prob_file >> num_cols_ >> num_col_domains_;
113       col_domains_ = new CONES[num_col_domains_];
114       col_domain_size_ = new int[num_col_domains_];
115       for (int i=0; i<num_col_domains_; ++i) {
116         std::string dom;
117         prob_file >> dom >> col_domain_size_[i];
118         if (!dom.compare("F")) {
119           col_domains_[i] = FREE_RANGE;
120         }
121         else if (!dom.compare("L+")) {
122           col_domains_[i] = POSITIVE_ORT;
123         }
124         else if (!dom.compare("L-")) {
125           col_domains_[i] = NEGATIVE_ORT;
126         }
127         else if (!dom.compare("L=")) {
128           col_domains_[i] = FIXPOINT_ZERO;
129         }
130         else if (!dom.compare("Q")) {
131           col_domains_[i] = QUAD_CONE;
132         }
133         else if (!dom.compare("QR")) {
134           col_domains_[i] = RQUAD_CONE;
135         }
136         else {
137           std::cerr << "Unknown domain!" << std::endl;
138           throw std::exception();
139         }
140       }
141     }
142     else if (!line.compare("INT")) {
143       // read integrality info
144       prob_file >> num_int_;
145       integers_ = new int[num_int_];
146       for (int i=0; i<num_int_; ++i) {
147         prob_file >> integers_[i];
148       }
149     }
150     else if (!line.compare("CON")) {
151       // read constraint info
152       prob_file >> num_rows_ >> num_row_domains_;
153       row_domains_ = new CONES[num_row_domains_];
154       row_domain_size_ = new int[num_row_domains_];
155       for (int i=0; i<num_row_domains_; ++i) {
156         std::string dom;
157         prob_file >> dom >> row_domain_size_[i];
158         if (!dom.compare("F")) {
159           row_domains_[i] = FREE_RANGE;
160         }
161         else if (!dom.compare("L+")) {
162           row_domains_[i] = POSITIVE_ORT;
163         }
164         else if (!dom.compare("L-")) {
165           row_domains_[i] = NEGATIVE_ORT;
166         }
167         else if (!dom.compare("L=")) {
168           row_domains_[i] = FIXPOINT_ZERO;
169         }
170         else if (!dom.compare("Q")) {
171           row_domains_[i] = QUAD_CONE;
172         }
173         else if (!dom.compare("QR")) {
174           row_domains_[i] = RQUAD_CONE;
175         }
176       }
177     }
178     else if (!line.compare("OBJACOORD")) {
179       // read objective coef
180       obj_coef_ = new double[num_cols_]();
181       int num_coef;
182       prob_file >> num_coef;
183       for (int i=0; i<num_coef; ++i) {
184         int index;
185         double value;
186         prob_file >> index >> value;
187         obj_coef_[index] = value;
188       }
189     }
190     else if (!line.compare("ACOORD")) {
191       // read constraint coefficient
192       prob_file >> num_nz_;
193       row_coord_ = new int[num_nz_];
194       col_coord_ = new int[num_nz_];
195       coef_ = new double[num_nz_];
196       for (int i=0; i<num_nz_; ++i) {
197         prob_file >> row_coord_[i]
198                   >> col_coord_[i]
199                   >> coef_[i];
200       }
201     }
202     else if (!line.compare("BCOORD")) {
203       // read constant term
204       int nonzero_rhs;
205       fixed_term_ = new double[num_rows_]();
206       prob_file >> nonzero_rhs;
207       for (int i=0; i<nonzero_rhs; ++i) {
208         int index;
209         double value;
210         prob_file >> index >> value;
211         fixed_term_[index] = value;
212       }
213     }
214   }
215   prob_file.close();
216 }
217 
writeCbf(std::stringstream & problem_stream) const218 void DcoCbfIO::writeCbf(std::stringstream & problem_stream) const {
219   problem_stream << "# Problem written by COIN-OR DisCO." << std::endl;
220   problem_stream << std::endl;
221   // version
222   problem_stream << "VER" << std::endl;
223   problem_stream << version_ << std::endl;
224   problem_stream << std::endl;
225   // objective sense
226   problem_stream << "OBJSENSE" << std::endl;
227   if (sense_==-1) {
228     problem_stream << "MAX" << std::endl;
229   }
230   else {
231     problem_stream << "MIN" << std::endl;
232   }
233   problem_stream << std::endl;
234   // var
235   problem_stream << "VAR" << std::endl;
236   problem_stream << num_cols_ << " " << num_col_domains_ << std::endl;
237   for (int i=0; i<num_col_domains_; ++i) {
238     std::string domain;
239     if (col_domains_[i]==FREE_RANGE) {
240       domain = "F";
241     }
242     else if (col_domains_[i]==POSITIVE_ORT) {
243       domain = "L+";
244     }
245     else if (col_domains_[i]==NEGATIVE_ORT) {
246       domain = "L-";
247     }
248     else if (col_domains_[i]==FIXPOINT_ZERO) {
249       domain = "L=";
250     }
251     else if (col_domains_[i]==QUAD_CONE) {
252       domain = "Q";
253     }
254     else if (col_domains_[i]==RQUAD_CONE) {
255       domain = "QR";
256     }
257     problem_stream << domain << " " << col_domain_size_[i];
258   }
259   problem_stream << std::endl;
260   problem_stream << std::endl;
261   // int
262   problem_stream << "INT" << std::endl;
263   problem_stream << num_int_ << std::endl;
264   for (int i=0; i<num_int_; ++i) {
265     problem_stream << integers_[i] << std::endl;
266   }
267   problem_stream << std::endl;
268   // con
269   problem_stream << "CON" << std::endl;
270   problem_stream << num_rows_ << " " << num_row_domains_ << std::endl;
271   for (int i=0; i<num_row_domains_; ++i) {
272     std::string domain;
273     if (row_domains_[i]==FREE_RANGE) {
274       domain = "F";
275     }
276     else if (row_domains_[i]==POSITIVE_ORT) {
277       domain = "L+";
278     }
279     else if (row_domains_[i]==NEGATIVE_ORT) {
280       domain = "L-";
281     }
282     else if (row_domains_[i]==FIXPOINT_ZERO) {
283       domain = "L=";
284     }
285     else if (row_domains_[i]==QUAD_CONE) {
286       domain = "Q";
287     }
288     else if (row_domains_[i]==RQUAD_CONE) {
289       domain = "QR";
290     }
291     problem_stream << domain << " " << row_domain_size_[i] << std::endl;
292   }
293   problem_stream << std::endl;
294   // objacoord
295   int obj_num_nz = 0;
296   for (int i=0; i<num_cols_; ++i) {
297     if (obj_coef_[i]!=0.0) {
298       obj_num_nz++;
299     }
300   }
301   // find number of nonzero
302   problem_stream << "OBJACOORD" << std::endl;
303   problem_stream << obj_num_nz << std::endl;
304   for (int i=0; i<num_cols_; ++i) {
305     if (obj_coef_[i]!=0.0) {
306       problem_stream << i << " " << obj_coef_[i] << std::endl;
307     }
308   }
309   problem_stream << std::endl;
310   // acoord
311   problem_stream << "ACOORD" << std::endl;
312   problem_stream << num_nz_ << std::endl;
313   for (int i=0; i<num_nz_; ++i) {
314     problem_stream << row_coord_[i] << " "
315                    << col_coord_[i] << " "
316                    << coef_[i]
317                    << std::endl;
318   }
319   problem_stream << std::endl;
320   // bcoord
321   int ft_num_nz = 0;
322   for (int i=0; i<num_rows_; ++i) {
323     if (fixed_term_[i]!=0.0) {
324       ft_num_nz++;
325     }
326   }
327   problem_stream << "BCOORD" << std::endl;
328   problem_stream << ft_num_nz << std::endl;
329   for (int i=0; i<num_rows_; ++i) {
330     if (fixed_term_[i]!=0.0) {
331       problem_stream << i << " " << fixed_term_[i] << std::endl;
332     }
333   }
334 }
335 
check_row_domains() const336 int DcoCbfIO::check_row_domains() const {
337   for (int i=0; i<num_row_domains_; ++i) {
338     if (row_domains_[i]==QUAD_CONE or
339         row_domains_[i]==RQUAD_CONE) {
340       return 1;
341     }
342   }
343   return 0;
344 }
345 
~DcoCbfIO()346 DcoCbfIO::~DcoCbfIO() {
347   if (col_domains_) {
348     delete[] col_domains_;
349   }
350   if (col_domain_size_) {
351     delete[] col_domain_size_;
352   }
353   if (row_domains_) {
354     delete[] row_domains_;
355   }
356   if (row_domain_size_) {
357     delete[] row_domain_size_;
358   }
359   if (obj_coef_) {
360     delete[] obj_coef_;
361   }
362   if (row_coord_) {
363     delete[] row_coord_;
364   }
365   if (col_coord_) {
366     delete[] col_coord_;
367   }
368   if (coef_) {
369     delete[] coef_;
370   }
371   if (fixed_term_) {
372     delete[] fixed_term_;
373   }
374 }
375 
376 
377 // get problem in following form, i.e.,
378 //
379 // rowLB <= Ax <= rowUB
380 // colLB <= x  <= colUB
381 // x^i in L for i=1, ..., k
382 //
383 // this function is called after problem is read from file.
getProblem(double * & colLB,double * & colUB,double * & rowLB,double * & rowUB,CoinPackedMatrix * & matrix,int & numCones,int * & coneStart,int * & coneMembers,int * & coneType) const384 void DcoCbfIO::getProblem(double *& colLB, double *& colUB,
385                 double *& rowLB, double *& rowUB,
386                 CoinPackedMatrix *& matrix,
387                 int & numCones, int *& coneStart,
388                 int *& coneMembers, int *& coneType) const {
389   // regular columns
390   int numCols = num_cols_;
391   // add columns from lifting, reduce row domains to column domains (Lorentz cones)
392   // convert ax + alpha in L
393   // to -y + ax + alpha = 0 and y in L
394   for (int i=0; i<num_row_domains_; ++i) {
395     if (row_domains_[i]==QUAD_CONE or row_domains_[i]==RQUAD_CONE) {
396       numCols += row_domain_size_[i];
397     }
398   }
399   colLB = new double[numCols];
400   colUB = new double[numCols];
401   std::vector<int*> cMembers;
402   std::vector<int> cType;
403   std::vector<int> cStart;
404   cStart.push_back(0);
405   // set upper and lower bounds to inf and -inf
406   // no column bounds initially
407   std::fill_n(colLB, numCols, -getInfinity());
408   std::fill_n(colUB, numCols, getInfinity());
409   // iterate over col domains and set column bounds and get cone info
410   int col_index = 0;
411   for (int i=0; i<num_col_domains_; ++i) {
412     if (col_domains_[i] == FREE_RANGE) {
413       // free domain, do nothing
414     }
415     else if (col_domains_[i] == POSITIVE_ORT) {
416       // set lower bound of domain size many elements to 0
417       std::fill_n(colLB+col_index, col_domain_size_[i], 0.0);
418     }
419     else if (col_domains_[i] == NEGATIVE_ORT) {
420       // set upper bound of domain size many elements to 0
421       std::fill_n(colUB+col_index, col_domain_size_[i], 0.0);
422     }
423     else if (col_domains_[i] == FIXPOINT_ZERO) {
424       // set lower bound of domain size many elements to 0
425       std::fill_n(colLB+col_index, col_domain_size_[i], 0.0);
426       // set upper bound of domain size many elements to 0
427       std::fill_n(colUB+col_index, col_domain_size_[i], 0.0);
428     }
429     else if (col_domains_[i] == QUAD_CONE) {
430       // variables are in a lorentz cone. Add a conic constraint.
431       int * members = new int[col_domain_size_[i]];
432       for (int j=0; j<col_domain_size_[i]; ++j) members[j] = j+col_index;
433       cMembers.push_back(members);
434       cType.push_back(1);
435       cStart.push_back(cStart.back()+col_domain_size_[i]);
436       // leading variable is nonnegative.
437       colLB[members[0]] = 0.0;
438       members = 0;
439     }
440     else if (col_domains_[i] == RQUAD_CONE) {
441       // variables are in a lorentz cone. Add a conic constraint.
442       int * members = new int[col_domain_size_[i]];
443       for (int j=0; j<col_domain_size_[i]; ++j) members[j] = j+col_index;
444       cMembers.push_back(members);
445       cType.push_back(2);
446       cStart.push_back(cStart.back()+col_domain_size_[i]);
447       // leading variable is nonnegative.
448       colLB[members[0]] = 0.0;
449       colLB[members[1]] = 0.0;
450       members = 0;
451     }
452     col_index += col_domain_size_[i];
453   }
454 
455   double * rhs = new double[num_rows_];
456   for(int i=0; i<num_rows_; ++i) {
457     rhs[i] = -fixed_term_[i];
458   }
459 
460   // iterate over row domains and set column bounds and get cone info
461   rowLB = new double[num_rows_];
462   rowUB = new double[num_rows_];
463   // no row bounds initially
464   std::fill_n(rowLB, num_rows_, -getInfinity());
465   std::fill_n(rowUB, num_rows_, getInfinity());
466   // iterate over row domains and set row bounds and get cone info
467   for (int i=0, row_index=0; i<num_row_domains_; ++i) {
468     if (row_domains_[i]==FREE_RANGE) {
469       // free domain, do nothing
470     }
471     else if (row_domains_[i]==POSITIVE_ORT) {
472       // set lower bound of domain size many elements
473       std::copy(rhs+row_index, rhs+row_index+row_domain_size_[i],
474                 rowLB+row_index);
475     }
476     else if (row_domains_[i]==NEGATIVE_ORT) {
477       // set upper bound of domain size many elements
478       std::copy(rhs+row_index, rhs+row_index+row_domain_size_[i],
479                 rowUB+row_index);
480     }
481     else if (row_domains_[i]==FIXPOINT_ZERO) {
482       // set lower bound of domain size many elements
483       std::copy(rhs+row_index, rhs+row_index+row_domain_size_[i],
484                 rowLB+row_index);
485       // set upper bound of domain size many elements
486       std::copy(rhs+row_index, rhs+row_index+row_domain_size_[i],
487                 rowUB+row_index);
488     }
489     else if (row_domains_[i]==QUAD_CONE) {
490       // variables are in a lorentz cone. Add a conic constraint.
491       int * members = new int[row_domain_size_[i]];
492       for (int j=0; j<row_domain_size_[i]; ++j) members[j] = j+col_index;
493       cMembers.push_back(members);
494       cType.push_back(1);
495       cStart.push_back(cStart.back()+row_domain_size_[i]);
496       // leading row is nonnegative.
497       colLB[members[0]] = 0.0;
498       members = 0;
499       col_index += row_domain_size_[i];
500       // set rowlb and rowub to rhs
501       std::copy(rhs+row_index, rhs+row_index+row_domain_size_[i], rowLB+row_index);
502       std::copy(rhs+row_index, rhs+row_index+row_domain_size_[i], rowUB+row_index);
503     }
504     else if (row_domains_[i]==RQUAD_CONE) {
505       // variables are in a lorentz cone. Add a conic constraint.
506       int * members = new int[row_domain_size_[i]];
507       for (int j=0; j<row_domain_size_[i]; ++j) members[j] = j+col_index;
508       cMembers.push_back(members);
509       cType.push_back(2);
510       cStart.push_back(cStart.back()+row_domain_size_[i]);
511       // leading variable is nonnegative.
512       colLB[members[0]] = 0.0;
513       colLB[members[1]] = 0.0;
514       members = 0;
515       col_index += row_domain_size_[i];
516       // set rowlb and rowub to rhs
517       std::copy(rhs+row_index, rhs+row_index+row_domain_size_[i], rowLB+row_index);
518       std::copy(rhs+row_index, rhs+row_index+row_domain_size_[i], rowUB+row_index);
519     }
520     row_index += row_domain_size_[i];
521   }
522 
523   // compute cone info from cMembers and cType
524   numCones = static_cast<int>(cMembers.size());
525   coneStart = new int[numCones+1];
526   coneType = new int[numCones];
527   std::copy(cType.begin(), cType.end(), coneType);
528   std::copy(cStart.begin(), cStart.end(), coneStart);
529   int total_in_cone = 0;
530   for (int i=0; i<numCones; ++i) {
531     total_in_cone += coneStart[i+1] - coneStart[i];
532   }
533   coneMembers = new int[total_in_cone];
534   for (int i=0; i<numCones; ++i) {
535     int size = coneStart[i+1] - coneStart[i];
536     std::copy(cMembers[i], cMembers[i]+size, coneMembers+coneStart[i]);
537   }
538 
539   // get rows in CoinPackedMatrix format
540 
541   matrix = new CoinPackedMatrix(0, row_coord_, col_coord_, coef_, num_nz_);
542   {
543     // convert Ax + b in L
544     // to -y + Ax + b = 0 and y in L
545     int numcols = 0;
546 
547 
548     // rows is always row_index, row_index+1, ...,
549     // row_index+row_domain_size[i]-1
550     std::vector<int> rows;
551 
552     for (int i=0, row_index=0; i<num_row_domains_; ++i) {
553       if (row_domains_[i]==QUAD_CONE or row_domains_[i]==RQUAD_CONE) {
554         for (int j=0; j<row_domain_size_[i]; ++j)
555           rows.push_back(row_index+j);
556         numcols += row_domain_size_[i];
557       }
558       row_index += row_domain_size_[i];
559     }
560 
561     if (numcols) {
562       // colstarts is always col_index, col_index+1, ...,
563       // col_index+row_domain_size[i]-1
564       std::vector<int> colstarts;
565       for (int j=0; j<numcols; ++j)
566         colstarts.push_back(j);
567       colstarts.push_back(numcols);
568 
569       double * elements = new double[numcols];
570       std::fill_n(elements, numcols, -1.0);
571       // colstarts, rows
572       int * colstarts_arr = new int[numcols+1];
573       std::copy(colstarts.begin(), colstarts.end(), colstarts_arr);
574       int * rows_arr = new int[numcols];
575       std::copy(rows.begin(), rows.end(), rows_arr);
576       matrix->appendCols(numcols, colstarts_arr, rows_arr, elements);
577       delete[] rows_arr;
578       delete[] colstarts_arr;
579       delete[] elements;
580       colstarts.clear();
581     }
582     rows.clear();
583   }
584 }
585 
586 
getInfinity() const587 double DcoCbfIO::getInfinity() const {
588   return COIN_DBL_MAX;
589 }
590