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