1 | // copyright (C) 2002, International Business Machines |
---|
2 | // Corporation and others. All Rights Reserved. |
---|
3 | #if defined(_MSC_VER) |
---|
4 | // Turn off compiler warning about long names |
---|
5 | # pragma warning(disable:4786) |
---|
6 | #endif |
---|
7 | |
---|
8 | #include <cassert> |
---|
9 | #include <typeinfo> |
---|
10 | #include <cstdio> |
---|
11 | #include <cmath> |
---|
12 | #include <cfloat> |
---|
13 | #include <string> |
---|
14 | #include <iostream> |
---|
15 | |
---|
16 | #define CBCVERSION "0.91" |
---|
17 | |
---|
18 | #include "CoinMpsIO.hpp" |
---|
19 | #include "CoinPackedMatrix.hpp" |
---|
20 | #include "CoinPackedVector.hpp" |
---|
21 | #include "CoinWarmStartBasis.hpp" |
---|
22 | #include "CoinTime.hpp" |
---|
23 | |
---|
24 | #include "OsiSolverInterface.hpp" |
---|
25 | #include "OsiCuts.hpp" |
---|
26 | #include "OsiRowCut.hpp" |
---|
27 | #include "OsiColCut.hpp" |
---|
28 | |
---|
29 | #include "CglCutGenerator.hpp" |
---|
30 | #include "CglGomory.hpp" |
---|
31 | #include "CglProbing.hpp" |
---|
32 | #include "CglKnapsackCover.hpp" |
---|
33 | #include "CglOddHole.hpp" |
---|
34 | #include "CglClique.hpp" |
---|
35 | #include "CglFlowCover.hpp" |
---|
36 | #include "CglMixedIntegerRounding.hpp" |
---|
37 | #include "CglTwomir.hpp" |
---|
38 | |
---|
39 | #include "CbcModel.hpp" |
---|
40 | #include "CbcCutGenerator.hpp" |
---|
41 | #include "CbcHeuristic.hpp" |
---|
42 | #include "CbcCompareActual.hpp" |
---|
43 | #include "CbcParam.hpp" |
---|
44 | |
---|
45 | #ifdef COIN_USE_CLP |
---|
46 | #include "OsiClpSolverInterface.hpp" |
---|
47 | #endif |
---|
48 | #ifdef COIN_USE_DYLP |
---|
49 | #include "OsiDylpSolverInterface.hpp" |
---|
50 | #endif |
---|
51 | |
---|
52 | |
---|
53 | |
---|
54 | |
---|
55 | /* Before first solution do depth first, |
---|
56 | then it is computed to hit first solution less 2% |
---|
57 | */ |
---|
58 | class CbcCompareUser : public CbcCompareBase { |
---|
59 | public: |
---|
60 | // Weight for each infeasibility |
---|
61 | double weight_; |
---|
62 | // Number of solutions |
---|
63 | int numberSolutions_; |
---|
64 | // Default Constructor |
---|
65 | CbcCompareUser () : weight_(-1.0), numberSolutions_(0) {test_=this;}; |
---|
66 | |
---|
67 | // Copy constructor |
---|
68 | CbcCompareUser ( const CbcCompareUser &rhs) |
---|
69 | : CbcCompareBase(rhs) |
---|
70 | { |
---|
71 | weight_=rhs.weight_; |
---|
72 | numberSolutions_=rhs.numberSolutions_; |
---|
73 | }; |
---|
74 | |
---|
75 | // Assignment operator |
---|
76 | CbcCompareUser & operator=( const CbcCompareUser& rhs) |
---|
77 | { |
---|
78 | if (this!=&rhs) { |
---|
79 | CbcCompareBase::operator=(rhs); |
---|
80 | weight_=rhs.weight_; |
---|
81 | numberSolutions_=rhs.numberSolutions_; |
---|
82 | } |
---|
83 | return *this; |
---|
84 | }; |
---|
85 | |
---|
86 | /// Clone |
---|
87 | virtual CbcCompareBase * clone() const |
---|
88 | { |
---|
89 | return new CbcCompareUser (*this); |
---|
90 | }; |
---|
91 | |
---|
92 | ~CbcCompareUser() {}; |
---|
93 | |
---|
94 | /* |
---|
95 | Return true if y better than x |
---|
96 | Node y is better than node x if y has fewer unsatisfied (greater depth on tie) or |
---|
97 | after solution weighted value of y is less than weighted value of x |
---|
98 | */ |
---|
99 | virtual bool test (CbcNode * x, CbcNode * y) { |
---|
100 | if (weight_<0.0) { |
---|
101 | // before solution |
---|
102 | /* printf("x %d %d %g, y %d %d %g\n", |
---|
103 | x->numberUnsatisfied(),x->depth(),x->objectiveValue(), |
---|
104 | y->numberUnsatisfied(),y->depth(),y->objectiveValue()); */ |
---|
105 | if (x->numberUnsatisfied() > y->numberUnsatisfied()) |
---|
106 | return true; |
---|
107 | else if (x->numberUnsatisfied() < y->numberUnsatisfied()) |
---|
108 | return false; |
---|
109 | else |
---|
110 | return x->depth() < y->depth(); |
---|
111 | } else { |
---|
112 | // after solution |
---|
113 | return x->objectiveValue()+ weight_*x->numberUnsatisfied() > |
---|
114 | y->objectiveValue() + weight_*y->numberUnsatisfied(); |
---|
115 | } |
---|
116 | } |
---|
117 | // This allows method to change behavior as it is called |
---|
118 | // after each solution |
---|
119 | virtual void newSolution(CbcModel * model, |
---|
120 | double objectiveAtContinuous, |
---|
121 | int numberInfeasibilitiesAtContinuous) |
---|
122 | { |
---|
123 | if (model->getSolutionCount()==model->getNumberHeuristicSolutions()) |
---|
124 | return; // solution was got by rounding |
---|
125 | // set to get close to this solution |
---|
126 | double costPerInteger = |
---|
127 | (model->getObjValue()-objectiveAtContinuous)/ |
---|
128 | ((double) numberInfeasibilitiesAtContinuous); |
---|
129 | weight_ = 0.98*costPerInteger; |
---|
130 | numberSolutions_++; |
---|
131 | if (numberSolutions_>5) |
---|
132 | weight_ =0.0; // this searches on objective |
---|
133 | } |
---|
134 | // This allows method to change behavior |
---|
135 | virtual bool every1000Nodes(CbcModel * model, int numberNodes) |
---|
136 | { |
---|
137 | if (numberNodes>10000) |
---|
138 | weight_ =0.0; // this searches on objective |
---|
139 | return false; |
---|
140 | } |
---|
141 | }; |
---|
142 | |
---|
143 | |
---|
144 | #define MAXPARAMETERS 100 |
---|
145 | |
---|
146 | namespace { |
---|
147 | |
---|
148 | void establishParams (int &numberParameters, CbcParam *const parameters) |
---|
149 | /* |
---|
150 | Subroutine to establish the cbc parameter array. See the description of |
---|
151 | class CbcParam for details. Pulled from main() for clarity. |
---|
152 | */ |
---|
153 | { numberParameters = 0 ; |
---|
154 | |
---|
155 | parameters[numberParameters++]= |
---|
156 | CbcParam("?","For help",GENERALQUERY); |
---|
157 | parameters[numberParameters++]= |
---|
158 | CbcParam("dualT!olerance", |
---|
159 | "For an optimal solution no dual infeasibility may " |
---|
160 | "exceed this value", |
---|
161 | 1.0e-20,1.0e12,DUALTOLERANCE); |
---|
162 | parameters[numberParameters++]= |
---|
163 | CbcParam("primalT!olerance", |
---|
164 | "For an optimal solution no primal infeasibility may " |
---|
165 | " exceed this value", |
---|
166 | 1.0e-20,1.0e12,PRIMALTOLERANCE); |
---|
167 | parameters[numberParameters++]= |
---|
168 | CbcParam("inf!easibilityWeight","Each integer infeasibility is expected \ |
---|
169 | to cost this much", |
---|
170 | 0.0,1.0e20,INFEASIBILITYWEIGHT); |
---|
171 | parameters[numberParameters++]= |
---|
172 | CbcParam("integerT!olerance","For an optimal solution \ |
---|
173 | no integer variable may be this away from an integer value", |
---|
174 | 1.0e-20,0.5,INTEGERTOLERANCE); |
---|
175 | parameters[numberParameters++]= |
---|
176 | CbcParam("inc!rement","A valid solution must be at least this \ |
---|
177 | much better than last integer solution", |
---|
178 | -1.0e20,1.0e20,INCREMENT); |
---|
179 | parameters[numberParameters++]= |
---|
180 | CbcParam("allow!ableGap","Stop when gap between best possible and \ |
---|
181 | best less than this", |
---|
182 | 0.0,1.0e20,ALLOWABLEGAP); |
---|
183 | parameters[numberParameters++]= |
---|
184 | CbcParam("ratio!Gap","Stop when gap between best possible and \ |
---|
185 | best less than this fraction of larger of two", |
---|
186 | 0.0,1.0e20,GAPRATIO); |
---|
187 | parameters[numberParameters++]= |
---|
188 | CbcParam("fix!OnDj","Try heuristic based on fixing variables with \ |
---|
189 | reduced costs greater than this", |
---|
190 | -1.0e20,1.0e20,DJFIX); |
---|
191 | parameters[numberParameters++]= |
---|
192 | CbcParam("tighten!Factor","Tighten bounds using this times largest \ |
---|
193 | activity at continuous solution", |
---|
194 | 1.0,1.0e20,TIGHTENFACTOR); |
---|
195 | parameters[numberParameters++]= |
---|
196 | CbcParam("log!Level","Level of detail in BAB output", |
---|
197 | 0,63,LOGLEVEL); |
---|
198 | parameters[numberParameters++]= |
---|
199 | CbcParam("slog!Level","Level of detail in Solver output", |
---|
200 | 0,63,SOLVERLOGLEVEL); |
---|
201 | parameters[numberParameters++]= |
---|
202 | CbcParam("maxN!odes","Maximum number of nodes to do", |
---|
203 | 1,999999,MAXNODES); |
---|
204 | parameters[numberParameters++]= |
---|
205 | CbcParam("strong!Branching","Number of variables to look at in strong branching", |
---|
206 | 0,999999,STRONGBRANCHING); |
---|
207 | parameters[numberParameters++]= |
---|
208 | CbcParam("direction","Minimize or Maximize", |
---|
209 | "min!imize",DIRECTION); |
---|
210 | parameters[numberParameters-1].append("max!imize"); |
---|
211 | parameters[numberParameters++]= |
---|
212 | CbcParam("error!sAllowed","Whether to allow import errors", |
---|
213 | "off",ERRORSALLOWED); |
---|
214 | parameters[numberParameters-1].append("on"); |
---|
215 | parameters[numberParameters++]= |
---|
216 | CbcParam("gomory!Cuts","Whether to use Gomory cuts", |
---|
217 | "off",GOMORYCUTS); |
---|
218 | parameters[numberParameters-1].append("on"); |
---|
219 | parameters[numberParameters-1].append("root"); |
---|
220 | parameters[numberParameters++]= |
---|
221 | CbcParam("probing!Cuts","Whether to use Probing cuts", |
---|
222 | "off",PROBINGCUTS); |
---|
223 | parameters[numberParameters-1].append("on"); |
---|
224 | parameters[numberParameters-1].append("root"); |
---|
225 | parameters[numberParameters++]= |
---|
226 | CbcParam("knapsack!Cuts","Whether to use Knapsack cuts", |
---|
227 | "off",KNAPSACKCUTS); |
---|
228 | parameters[numberParameters-1].append("on"); |
---|
229 | parameters[numberParameters-1].append("root"); |
---|
230 | parameters[numberParameters++]= |
---|
231 | CbcParam("oddhole!Cuts","Whether to use Oddhole cuts", |
---|
232 | "off",ODDHOLECUTS); |
---|
233 | parameters[numberParameters-1].append("on"); |
---|
234 | parameters[numberParameters-1].append("root"); |
---|
235 | parameters[numberParameters++]= |
---|
236 | CbcParam("clique!Cuts","Whether to use Clique cuts", |
---|
237 | "off",CLIQUECUTS); |
---|
238 | parameters[numberParameters-1].append("on"); |
---|
239 | parameters[numberParameters-1].append("root"); |
---|
240 | parameters[numberParameters++]= |
---|
241 | CbcParam("mixed!IntegerRoundingCuts","Whether to use Mixed Integer Rounding cuts", |
---|
242 | "off",MIXEDCUTS); |
---|
243 | parameters[numberParameters-1].append("on"); |
---|
244 | parameters[numberParameters-1].append("root"); |
---|
245 | parameters[numberParameters++]= |
---|
246 | CbcParam("flow!CoverCuts","Whether to use Flow Cover cuts", |
---|
247 | "off",FLOWCUTS); |
---|
248 | parameters[numberParameters-1].append("on"); |
---|
249 | parameters[numberParameters-1].append("root"); |
---|
250 | parameters[numberParameters++]= |
---|
251 | CbcParam("two!MirCuts","Whether to use Two phase Mixed Integer Rounding cuts", |
---|
252 | "off",TWOMIRCUTS); |
---|
253 | parameters[numberParameters-1].append("on"); |
---|
254 | parameters[numberParameters-1].append("root"); |
---|
255 | parameters[numberParameters++]= |
---|
256 | CbcParam("round!ingHeuristic","Whether to use Rounding heuristic", |
---|
257 | "off",ROUNDING); |
---|
258 | parameters[numberParameters-1].append("on"); |
---|
259 | parameters[numberParameters++]= |
---|
260 | CbcParam("cost!Strategy","How to use costs", |
---|
261 | "off",COSTSTRATEGY); |
---|
262 | parameters[numberParameters-1].append("pri!orities"); |
---|
263 | parameters[numberParameters-1].append("pseudo!costs(not implemented yet)"); |
---|
264 | parameters[numberParameters++]= |
---|
265 | CbcParam("keepN!ames","Whether to keep names from import", |
---|
266 | "on",KEEPNAMES); |
---|
267 | parameters[numberParameters-1].append("off"); |
---|
268 | parameters[numberParameters++]= |
---|
269 | CbcParam("scaling","Whether to do scaling", |
---|
270 | "on",SCALING); |
---|
271 | parameters[numberParameters-1].append("off"); |
---|
272 | parameters[numberParameters++]= |
---|
273 | CbcParam("directory","Set Default import directory", |
---|
274 | DIRECTORY); |
---|
275 | parameters[numberParameters++]= |
---|
276 | CbcParam("solver!","Set the solver used by cbc", |
---|
277 | SOLVER) ; |
---|
278 | parameters[numberParameters++]= |
---|
279 | CbcParam("import","Import model from mps file", |
---|
280 | IMPORT); |
---|
281 | parameters[numberParameters++]= |
---|
282 | CbcParam("export","Export model as mps file", |
---|
283 | EXPORT); |
---|
284 | parameters[numberParameters++]= |
---|
285 | CbcParam("save!Model","Save model to binary file", |
---|
286 | SAVE); |
---|
287 | parameters[numberParameters++]= |
---|
288 | CbcParam("restore!Model","Restore model from binary file", |
---|
289 | RESTORE); |
---|
290 | parameters[numberParameters++]= |
---|
291 | CbcParam("presolve","Whether to use integer presolve - be careful", |
---|
292 | "off",PRESOLVE); |
---|
293 | parameters[numberParameters-1].append("on"); |
---|
294 | parameters[numberParameters++]= |
---|
295 | CbcParam("initialS!olve","Solve to continuous", |
---|
296 | SOLVECONTINUOUS); |
---|
297 | parameters[numberParameters++]= |
---|
298 | CbcParam("branch!AndBound","Do Branch and Bound", |
---|
299 | BAB); |
---|
300 | parameters[numberParameters++]= |
---|
301 | CbcParam("sol!ution","Prints solution to file", |
---|
302 | SOLUTION); |
---|
303 | parameters[numberParameters++]= |
---|
304 | CbcParam("max!imize","Set optimization direction to maximize", |
---|
305 | MAXIMIZE); |
---|
306 | parameters[numberParameters++]= |
---|
307 | CbcParam("min!imize","Set optimization direction to minimize", |
---|
308 | MINIMIZE); |
---|
309 | parameters[numberParameters++] = |
---|
310 | CbcParam("time!Limit","Set a time limit for solving this problem", |
---|
311 | 1.0,(double)(60*60*24*365*10),TIMELIMIT) ; |
---|
312 | parameters[numberParameters++]= |
---|
313 | CbcParam("exit","Stops cbc execution", |
---|
314 | EXIT); |
---|
315 | parameters[numberParameters++]= |
---|
316 | CbcParam("stop","Stops cbc execution", |
---|
317 | EXIT); |
---|
318 | parameters[numberParameters++]= |
---|
319 | CbcParam("quit","Stops cbc execution", |
---|
320 | EXIT); |
---|
321 | parameters[numberParameters++]= |
---|
322 | CbcParam("-","From stdin", |
---|
323 | STDIN); |
---|
324 | parameters[numberParameters++]= |
---|
325 | CbcParam("stdin","From stdin", |
---|
326 | STDIN); |
---|
327 | parameters[numberParameters++]= |
---|
328 | CbcParam("unitTest","Do unit test", |
---|
329 | UNITTEST); |
---|
330 | parameters[numberParameters++]= |
---|
331 | CbcParam("miplib","Do some of miplib test set", |
---|
332 | MIPLIB); |
---|
333 | parameters[numberParameters++]= |
---|
334 | CbcParam("ver!sion","Print out version", |
---|
335 | VERSION); |
---|
336 | |
---|
337 | assert(numberParameters<MAXPARAMETERS); |
---|
338 | |
---|
339 | return ; } |
---|
340 | |
---|
341 | #ifdef COIN_USE_READLINE |
---|
342 | #include <readline/readline.h> |
---|
343 | #include <readline/history.h> |
---|
344 | #endif |
---|
345 | |
---|
346 | // Returns next valid field |
---|
347 | |
---|
348 | int read_mode=1; |
---|
349 | char line[1000]; |
---|
350 | char * where=NULL; |
---|
351 | |
---|
352 | std::string |
---|
353 | nextField() |
---|
354 | { |
---|
355 | std::string field; |
---|
356 | if (!where) { |
---|
357 | // need new line |
---|
358 | #ifdef COIN_USE_READLINE |
---|
359 | // Get a line from the user. |
---|
360 | where = readline ("Cbc:"); |
---|
361 | |
---|
362 | // If the line has any text in it, save it on the history. |
---|
363 | if (where) { |
---|
364 | if ( *where) |
---|
365 | add_history (where); |
---|
366 | strcpy(line,where); |
---|
367 | } |
---|
368 | #else |
---|
369 | fprintf(stdout,"Cbc:"); |
---|
370 | fflush(stdout); |
---|
371 | where = fgets(line,1000,stdin); |
---|
372 | #endif |
---|
373 | if (!where) |
---|
374 | return field; // EOF |
---|
375 | where = line; |
---|
376 | // clean image |
---|
377 | char * lastNonBlank = line-1; |
---|
378 | while ( *where != '\0' ) { |
---|
379 | if ( *where != '\t' && *where < ' ' ) { |
---|
380 | break; |
---|
381 | } else if ( *where != '\t' && *where != ' ') { |
---|
382 | lastNonBlank = where; |
---|
383 | } |
---|
384 | where++; |
---|
385 | } |
---|
386 | where=line; |
---|
387 | *(lastNonBlank+1)='\0'; |
---|
388 | } |
---|
389 | // munch white space |
---|
390 | while(*where==' '||*where=='\t') |
---|
391 | where++; |
---|
392 | char * saveWhere = where; |
---|
393 | while (*where!=' '&&*where!='\t'&&*where!='\0') |
---|
394 | where++; |
---|
395 | if (where!=saveWhere) { |
---|
396 | char save = *where; |
---|
397 | *where='\0'; |
---|
398 | //convert to string |
---|
399 | field=saveWhere; |
---|
400 | *where=save; |
---|
401 | } else { |
---|
402 | where=NULL; |
---|
403 | field="EOL"; |
---|
404 | } |
---|
405 | return field; |
---|
406 | } |
---|
407 | |
---|
408 | std::string |
---|
409 | getCommand(int argc, const char *argv[]) |
---|
410 | { |
---|
411 | std::string field="EOL"; |
---|
412 | while (field=="EOL") { |
---|
413 | if (read_mode>0) { |
---|
414 | if (read_mode<argc) { |
---|
415 | field = argv[read_mode++]; |
---|
416 | if (field=="-") { |
---|
417 | std::cout<<"Switching to line mode"<<std::endl; |
---|
418 | read_mode=-1; |
---|
419 | field=nextField(); |
---|
420 | } else if (field[0]!='-') { |
---|
421 | if (read_mode!=2) { |
---|
422 | std::cout<<"skipping non-command "<<field<<std::endl; |
---|
423 | field="EOL"; // skip |
---|
424 | } else { |
---|
425 | // special dispensation - taken as -import name |
---|
426 | read_mode--; |
---|
427 | field="import"; |
---|
428 | } |
---|
429 | } else { |
---|
430 | if (field!="--") { |
---|
431 | // take off - |
---|
432 | field = field.substr(1); |
---|
433 | } else { |
---|
434 | // special dispensation - taken as -import -- |
---|
435 | read_mode--; |
---|
436 | field="import"; |
---|
437 | } |
---|
438 | } |
---|
439 | } else { |
---|
440 | field=""; |
---|
441 | } |
---|
442 | } else { |
---|
443 | field=nextField(); |
---|
444 | } |
---|
445 | } |
---|
446 | //std::cout<<field<<std::endl; |
---|
447 | return field; |
---|
448 | } |
---|
449 | std::string |
---|
450 | getString(int argc, const char *argv[]) |
---|
451 | { |
---|
452 | std::string field="EOL"; |
---|
453 | if (read_mode>0) { |
---|
454 | if (read_mode<argc) { |
---|
455 | if (argv[read_mode][0]!='-') { |
---|
456 | field = argv[read_mode++]; |
---|
457 | } else if (!strcmp(argv[read_mode],"--")) { |
---|
458 | field = argv[read_mode++]; |
---|
459 | // -- means import from stdin |
---|
460 | field = "-"; |
---|
461 | } |
---|
462 | } |
---|
463 | } else { |
---|
464 | field=nextField(); |
---|
465 | } |
---|
466 | //std::cout<<field<<std::endl; |
---|
467 | return field; |
---|
468 | } |
---|
469 | |
---|
470 | // valid = 0 okay, 1 bad, 2 not there |
---|
471 | int |
---|
472 | getIntField(int argc, const char *argv[],int * valid) |
---|
473 | { |
---|
474 | std::string field="EOL"; |
---|
475 | if (read_mode>0) { |
---|
476 | if (read_mode<argc) { |
---|
477 | // may be negative value so do not check for - |
---|
478 | field = argv[read_mode++]; |
---|
479 | } |
---|
480 | } else { |
---|
481 | field=nextField(); |
---|
482 | } |
---|
483 | int value=0; |
---|
484 | //std::cout<<field<<std::endl; |
---|
485 | if (field!="EOL") { |
---|
486 | // how do I check valid |
---|
487 | value = atoi(field.c_str()); |
---|
488 | *valid=0; |
---|
489 | } else { |
---|
490 | *valid=2; |
---|
491 | } |
---|
492 | return value; |
---|
493 | } |
---|
494 | |
---|
495 | |
---|
496 | // valid = 0 okay, 1 bad, 2 not there |
---|
497 | double |
---|
498 | getDoubleField(int argc, const char *argv[],int * valid) |
---|
499 | { |
---|
500 | std::string field="EOL"; |
---|
501 | if (read_mode>0) { |
---|
502 | if (read_mode<argc) { |
---|
503 | // may be negative value so do not check for - |
---|
504 | field = argv[read_mode++]; |
---|
505 | } |
---|
506 | } else { |
---|
507 | field=nextField(); |
---|
508 | } |
---|
509 | double value=0.0; |
---|
510 | //std::cout<<field<<std::endl; |
---|
511 | if (field!="EOL") { |
---|
512 | // how do I check valid |
---|
513 | value = atof(field.c_str()); |
---|
514 | *valid=0; |
---|
515 | } else { |
---|
516 | *valid=2; |
---|
517 | } |
---|
518 | return value; |
---|
519 | } |
---|
520 | |
---|
521 | /// For run timing |
---|
522 | |
---|
523 | double totalTime=0.0; |
---|
524 | |
---|
525 | } /* end unnamed namespace */ |
---|
526 | |
---|
527 | |
---|
528 | int main (int argc, const char *argv[]) |
---|
529 | { |
---|
530 | // next {} is just to make sure all memory should be freed - for debug |
---|
531 | { |
---|
532 | std::ios::sync_with_stdio() ; |
---|
533 | /* |
---|
534 | Create a vector of solver prototypes and establish a default solver. After |
---|
535 | this the code is solver independent. |
---|
536 | |
---|
537 | Creating multiple solvers is moderately expensive. If you don't want to |
---|
538 | make use of this feature, best to define just one. The businesss with |
---|
539 | CBC_DEFAULT_SOLVER will select the first available solver as the default, |
---|
540 | unless overridden at compile time. |
---|
541 | |
---|
542 | NOTE that processing of string parameters is case-independent, but maps are |
---|
543 | case-sensitive. The solver name given here must contain only lower case |
---|
544 | letters. |
---|
545 | */ |
---|
546 | typedef std::map<std::string,OsiSolverInterface*> solverMap_t ; |
---|
547 | typedef solverMap_t::const_iterator solverMapIter_t ; |
---|
548 | |
---|
549 | solverMap_t solvers ; |
---|
550 | |
---|
551 | # ifdef COIN_USE_CLP |
---|
552 | # ifndef CBC_DEFAULT_SOLVER |
---|
553 | # define CBC_DEFAULT_SOLVER "clp" |
---|
554 | # endif |
---|
555 | solvers["clp"] = new OsiClpSolverInterface ; |
---|
556 | # else |
---|
557 | solvers["clp"] = 0 ; |
---|
558 | # endif |
---|
559 | # ifdef COIN_USE_DYLP |
---|
560 | # ifndef CBC_DEFAULT_SOLVER |
---|
561 | # define CBC_DEFAULT_SOLVER "dylp" |
---|
562 | # endif |
---|
563 | solvers["dylp"] = new OsiDylpSolverInterface ; |
---|
564 | # else |
---|
565 | solvers["dylp"] = 0 ; |
---|
566 | # endif |
---|
567 | /* |
---|
568 | If we don't have a default solver, we're deeply confused. |
---|
569 | */ |
---|
570 | OsiSolverInterface *dflt_solver = solvers[CBC_DEFAULT_SOLVER] ; |
---|
571 | if (dflt_solver) |
---|
572 | { std::cout << "Default solver is " << CBC_DEFAULT_SOLVER << std::endl ; } |
---|
573 | else |
---|
574 | { std::cerr << "No solvers! Aborting." << std::endl ; |
---|
575 | return (1) ; } |
---|
576 | /* |
---|
577 | For calculating run time. |
---|
578 | */ |
---|
579 | double time1 = CoinCpuTime() ; |
---|
580 | double time2; |
---|
581 | /* |
---|
582 | Establish the command line interface: parameters, with associated info |
---|
583 | messages, ranges, defaults. See CbcParam for details. Scan the vector of |
---|
584 | solvers and add the names to the parameter object. |
---|
585 | */ |
---|
586 | CbcParam parameters[MAXPARAMETERS]; |
---|
587 | int numberParameters ; |
---|
588 | establishParams(numberParameters,parameters) ; |
---|
589 | |
---|
590 | { int iSolver = 0 ; |
---|
591 | for ( ; iSolver < numberParameters ; iSolver++) |
---|
592 | { int match = parameters[iSolver].matches("solver") ; |
---|
593 | if (match==1) break ; } |
---|
594 | for (solverMapIter_t solverIter = solvers.begin() ; |
---|
595 | solverIter != solvers.end() ; |
---|
596 | solverIter++) |
---|
597 | { if (solverIter->second) |
---|
598 | parameters[iSolver].append(solverIter->first) ; } |
---|
599 | int iKwd = parameters[iSolver].parameterOption(CBC_DEFAULT_SOLVER) ; |
---|
600 | parameters[iSolver].setCurrentOption(iKwd) ; } |
---|
601 | /* |
---|
602 | The rest of the default setup: establish a model, instantiate cut generators |
---|
603 | and heuristics, set various default values. |
---|
604 | */ |
---|
605 | dflt_solver->messageHandler()->setLogLevel(0) ; |
---|
606 | CbcModel *model = new CbcModel(*dflt_solver) ; |
---|
607 | model->messageHandler()->setLogLevel(1); |
---|
608 | bool goodModel=false; |
---|
609 | |
---|
610 | // Set up likely cut generators and defaults |
---|
611 | |
---|
612 | CglGomory gomoryGen; |
---|
613 | // try larger limit |
---|
614 | gomoryGen.setLimit(3000); |
---|
615 | // set default action (0=off,1=on,2=root) |
---|
616 | int gomoryAction=1; |
---|
617 | |
---|
618 | CglProbing probingGen; |
---|
619 | probingGen.setUsingObjective(true); |
---|
620 | probingGen.setMaxPass(3); |
---|
621 | probingGen.setMaxProbe(100); |
---|
622 | probingGen.setMaxLook(50); |
---|
623 | probingGen.setRowCuts(3); |
---|
624 | // set default action (0=off,1=on,2=root) |
---|
625 | int probingAction=1; |
---|
626 | |
---|
627 | CglKnapsackCover knapsackGen; |
---|
628 | // set default action (0=off,1=on,2=root) |
---|
629 | int knapsackAction=1; |
---|
630 | |
---|
631 | CglOddHole oddholeGen; |
---|
632 | oddholeGen.setMinimumViolation(0.005); |
---|
633 | oddholeGen.setMinimumViolationPer(0.0002); |
---|
634 | oddholeGen.setMaximumEntries(100); |
---|
635 | // set default action (0=off,1=on,2=root) |
---|
636 | int oddholeAction=0; |
---|
637 | |
---|
638 | CglClique cliqueGen; |
---|
639 | cliqueGen.setStarCliqueReport(false); |
---|
640 | cliqueGen.setRowCliqueReport(false); |
---|
641 | // set default action (0=off,1=on,2=root) |
---|
642 | int cliqueAction=1; |
---|
643 | |
---|
644 | CglMixedIntegerRounding mixedGen; |
---|
645 | // set default action (0=off,1=on,2=root) |
---|
646 | int mixedAction=1; |
---|
647 | |
---|
648 | CglFlowCover flowGen; |
---|
649 | // set default action (0=off,1=on,2=root) |
---|
650 | int flowAction=1; |
---|
651 | |
---|
652 | CglTwomir twomirGen; |
---|
653 | // set default action (0=off,1=on,2=root) |
---|
654 | int twomirAction=0; |
---|
655 | |
---|
656 | bool useRounding=true; |
---|
657 | |
---|
658 | int allowImportErrors=0; |
---|
659 | int keepImportNames=1; // not implemented |
---|
660 | int doScaling=1; |
---|
661 | int preSolve=0; |
---|
662 | double djFix=1.0e100; |
---|
663 | double gapRatio=1.0e100; |
---|
664 | double tightenFactor=0.0; |
---|
665 | |
---|
666 | std::string directory ="./"; |
---|
667 | std::string field; |
---|
668 | /* |
---|
669 | The main command parsing loop. |
---|
670 | */ |
---|
671 | // total number of commands read |
---|
672 | int numberGoodCommands=0; |
---|
673 | |
---|
674 | while (1) { |
---|
675 | // next command |
---|
676 | field=getCommand(argc,argv); |
---|
677 | |
---|
678 | // exit if null or similar |
---|
679 | if (!field.length()) { |
---|
680 | if (numberGoodCommands==1&&goodModel) { |
---|
681 | // we just had file name |
---|
682 | model->initialSolve(); |
---|
683 | model->solver()->messageHandler()->setLogLevel(0); |
---|
684 | CbcRounding heuristic1(*model); |
---|
685 | if (useRounding) |
---|
686 | model->addHeuristic(&heuristic1) ; |
---|
687 | // add cut generators if wanted |
---|
688 | if (probingAction==1) |
---|
689 | model->addCutGenerator(&probingGen,-1,"Probing"); |
---|
690 | else if (probingAction==2) |
---|
691 | model->addCutGenerator(&probingGen,-99,"Probing"); |
---|
692 | if (gomoryAction==1) |
---|
693 | model->addCutGenerator(&gomoryGen,-1,"Gomory"); |
---|
694 | else if (gomoryAction==2) |
---|
695 | model->addCutGenerator(&gomoryGen,-99,"Gomory"); |
---|
696 | if (knapsackAction==1) |
---|
697 | model->addCutGenerator(&knapsackGen,-1,"Knapsack"); |
---|
698 | else if (knapsackAction==2) |
---|
699 | model->addCutGenerator(&knapsackGen,-99,"Knapsack"); |
---|
700 | if (oddholeAction==1) |
---|
701 | model->addCutGenerator(&oddholeGen,-1,"OddHole"); |
---|
702 | else if (oddholeAction==2) |
---|
703 | model->addCutGenerator(&oddholeGen,-99,"OddHole"); |
---|
704 | if (cliqueAction==1) |
---|
705 | model->addCutGenerator(&cliqueGen,-1,"Clique"); |
---|
706 | else if (cliqueAction==2) |
---|
707 | model->addCutGenerator(&cliqueGen,-99,"Clique"); |
---|
708 | if (mixedAction==1) |
---|
709 | model->addCutGenerator(&mixedGen,-1,"MixedintegerRounding"); |
---|
710 | else if (mixedAction==2) |
---|
711 | model->addCutGenerator(&mixedGen,-99,"MixedintegerRounding"); |
---|
712 | if (flowAction==1) |
---|
713 | model->addCutGenerator(&flowGen,-1,"FlowCover"); |
---|
714 | else if (flowAction==2) |
---|
715 | model->addCutGenerator(&flowGen,-99,"FlowCover"); |
---|
716 | if (twomirAction==1) |
---|
717 | model->addCutGenerator(&twomirGen,-1,"TwoMirCuts"); |
---|
718 | else if (twomirAction==2) |
---|
719 | model->addCutGenerator(&twomirGen,-99,"TwoMirCuts"); |
---|
720 | // Say we want timings |
---|
721 | int numberGenerators = model->numberCutGenerators(); |
---|
722 | int iGenerator; |
---|
723 | for (iGenerator=0;iGenerator<numberGenerators;iGenerator++) { |
---|
724 | CbcCutGenerator * generator = model->cutGenerator(iGenerator); |
---|
725 | generator->setTiming(true); |
---|
726 | } |
---|
727 | model->branchAndBound(); |
---|
728 | time2 = CoinCpuTime(); |
---|
729 | totalTime += time2-time1; |
---|
730 | std::cout<<"Result "<<model->getObjValue()<< |
---|
731 | " iterations "<<model->getIterationCount()<< |
---|
732 | " nodes "<<model->getNodeCount()<< |
---|
733 | " took "<<time2-time1<<" seconds - total "<<totalTime<<std::endl; |
---|
734 | } else if (!numberGoodCommands) { |
---|
735 | // let's give the sucker a hint |
---|
736 | std::cout |
---|
737 | <<"Cbc takes input from arguments ( - switches to stdin)" |
---|
738 | <<std::endl |
---|
739 | <<"Enter ? for list of commands or (-)unitTest or -miplib" |
---|
740 | <<" for tests"<<std::endl; |
---|
741 | } |
---|
742 | break; |
---|
743 | } |
---|
744 | |
---|
745 | // see if ? at end |
---|
746 | int numberQuery=0; |
---|
747 | if (field!="?") { |
---|
748 | int length = field.length(); |
---|
749 | int i; |
---|
750 | for (i=length-1;i>0;i--) { |
---|
751 | if (field[i]=='?') |
---|
752 | numberQuery++; |
---|
753 | else |
---|
754 | break; |
---|
755 | } |
---|
756 | field=field.substr(0,length-numberQuery); |
---|
757 | } |
---|
758 | // find out if valid command |
---|
759 | int iParam; |
---|
760 | int numberMatches=0; |
---|
761 | for ( iParam=0; iParam<numberParameters; iParam++ ) { |
---|
762 | int match = parameters[iParam].matches(field); |
---|
763 | if (match==1) { |
---|
764 | numberMatches = 1; |
---|
765 | break; |
---|
766 | } else { |
---|
767 | numberMatches += match>>1; |
---|
768 | } |
---|
769 | } |
---|
770 | if (iParam<numberParameters&&!numberQuery) { |
---|
771 | // found |
---|
772 | CbcParam found = parameters[iParam]; |
---|
773 | CbcParameterType type = found.type(); |
---|
774 | int valid; |
---|
775 | numberGoodCommands++; |
---|
776 | if (type==GENERALQUERY) { |
---|
777 | std::cout<<"In argument list keywords have leading - " |
---|
778 | ", -stdin or just - switches to stdin"<<std::endl; |
---|
779 | std::cout<<"One command per line (and no -)"<<std::endl; |
---|
780 | std::cout<<"abcd? gives list of possibilities, if only one + explanation"<<std::endl; |
---|
781 | std::cout<<"abcd?? adds explanation, if only one fuller help(LATER)"<<std::endl; |
---|
782 | std::cout<<"abcd without value (where expected) gives current value"<<std::endl; |
---|
783 | std::cout<<"abcd value or abcd = value sets value"<<std::endl; |
---|
784 | std::cout<<"Commands are:"<<std::endl; |
---|
785 | for ( iParam=0; iParam<numberParameters; iParam+=4 ) { |
---|
786 | int i; |
---|
787 | for (i=iParam;i<min(numberParameters,iParam+4);i++) |
---|
788 | std::cout<<parameters[i].matchName()<<" "; |
---|
789 | std::cout<<std::endl; |
---|
790 | } |
---|
791 | } else if (type<81) { |
---|
792 | // get next field as double |
---|
793 | double value = getDoubleField(argc,argv,&valid); |
---|
794 | if (!valid) { |
---|
795 | parameters[iParam].setDoubleParameter(*model,value); |
---|
796 | } else if (valid==1) { |
---|
797 | abort(); |
---|
798 | } else { |
---|
799 | std::cout<<parameters[iParam].name()<<" has value "<< |
---|
800 | parameters[iParam].doubleParameter(*model)<<std::endl; |
---|
801 | } |
---|
802 | } else if (type<101) { |
---|
803 | // get next field as double for local use |
---|
804 | double value = getDoubleField(argc,argv,&valid); |
---|
805 | if (!valid) { |
---|
806 | if (!parameters[iParam].checkDoubleParameter(value)) { |
---|
807 | switch(type) { |
---|
808 | case DJFIX: |
---|
809 | djFix=value; |
---|
810 | preSolve=5; |
---|
811 | break; |
---|
812 | case GAPRATIO: |
---|
813 | gapRatio=value; |
---|
814 | break; |
---|
815 | case TIGHTENFACTOR: |
---|
816 | tightenFactor=value; |
---|
817 | break; |
---|
818 | default: |
---|
819 | abort(); |
---|
820 | } |
---|
821 | } |
---|
822 | } else if (valid==1) { |
---|
823 | abort(); |
---|
824 | } else { |
---|
825 | switch(type) { |
---|
826 | case DJFIX: |
---|
827 | value = djFix ; |
---|
828 | break; |
---|
829 | case GAPRATIO: |
---|
830 | value = gapRatio ; |
---|
831 | break; |
---|
832 | case TIGHTENFACTOR: |
---|
833 | value = tightenFactor ; |
---|
834 | break; |
---|
835 | default: |
---|
836 | abort(); |
---|
837 | } |
---|
838 | std::cout << parameters[iParam].name() << " has value " << |
---|
839 | value << std::endl ; |
---|
840 | } |
---|
841 | } else if (type<201) { |
---|
842 | // get next field as int |
---|
843 | int value = getIntField(argc,argv,&valid); |
---|
844 | if (!valid) { |
---|
845 | parameters[iParam].setIntParameter(*model,value); |
---|
846 | } else if (valid==1) { |
---|
847 | abort(); |
---|
848 | } else { |
---|
849 | std::cout<<parameters[iParam].name()<<" has value "<< |
---|
850 | parameters[iParam].intParameter(*model)<<std::endl; |
---|
851 | } |
---|
852 | } else if (type<301) { |
---|
853 | // one of several strings |
---|
854 | std::string value = getString(argc,argv); |
---|
855 | int action = parameters[iParam].parameterOption(value); |
---|
856 | if (action<0) { |
---|
857 | if (value!="EOL") { |
---|
858 | // no match |
---|
859 | parameters[iParam].printOptions(); |
---|
860 | } else { |
---|
861 | // print current value |
---|
862 | std::cout<<parameters[iParam].name()<<" has value "<< |
---|
863 | parameters[iParam].currentOption()<<std::endl; |
---|
864 | } |
---|
865 | } else { |
---|
866 | parameters[iParam].setCurrentOption(action); |
---|
867 | // for now hard wired |
---|
868 | switch (type) { |
---|
869 | case DIRECTION: |
---|
870 | if (action==0) |
---|
871 | model->solver()->setObjSense(1); |
---|
872 | else |
---|
873 | model->solver()->setObjSense(-1); |
---|
874 | break; |
---|
875 | case ERRORSALLOWED: |
---|
876 | allowImportErrors = action; |
---|
877 | break; |
---|
878 | case KEEPNAMES: |
---|
879 | keepImportNames = 1-action; |
---|
880 | break; |
---|
881 | case SCALING: |
---|
882 | doScaling = 1-action; |
---|
883 | break; |
---|
884 | case GOMORYCUTS: |
---|
885 | gomoryAction = action; |
---|
886 | break; |
---|
887 | case PROBINGCUTS: |
---|
888 | probingAction = action; |
---|
889 | break; |
---|
890 | case KNAPSACKCUTS: |
---|
891 | knapsackAction = action; |
---|
892 | break; |
---|
893 | case ODDHOLECUTS: |
---|
894 | oddholeAction = action; |
---|
895 | break; |
---|
896 | case CLIQUECUTS: |
---|
897 | cliqueAction = action; |
---|
898 | break; |
---|
899 | case FLOWCUTS: |
---|
900 | flowAction = action; |
---|
901 | break; |
---|
902 | case MIXEDCUTS: |
---|
903 | mixedAction = action; |
---|
904 | break; |
---|
905 | case TWOMIRCUTS: |
---|
906 | twomirAction = action; |
---|
907 | break; |
---|
908 | case ROUNDING: |
---|
909 | useRounding = action; |
---|
910 | break; |
---|
911 | case COSTSTRATEGY: |
---|
912 | if (action!=1) { |
---|
913 | printf("Pseudo costs not implemented yet\n"); |
---|
914 | } else { |
---|
915 | int numberColumns = model->getNumCols(); |
---|
916 | int * sort = new int[numberColumns]; |
---|
917 | double * dsort = new double[numberColumns]; |
---|
918 | int * priority = new int [numberColumns]; |
---|
919 | const double * objective = model->getObjCoefficients(); |
---|
920 | int iColumn; |
---|
921 | int n=0; |
---|
922 | for (iColumn=0;iColumn<numberColumns;iColumn++) { |
---|
923 | if (model->isInteger(iColumn)) { |
---|
924 | sort[n]=n; |
---|
925 | dsort[n++]=-objective[iColumn]; |
---|
926 | } |
---|
927 | } |
---|
928 | CoinSort_2(dsort,dsort+n,sort); |
---|
929 | int level=0; |
---|
930 | double last = -1.0e100; |
---|
931 | for (int i=0;i<n;i++) { |
---|
932 | int iPut=sort[i]; |
---|
933 | if (dsort[i]!=last) { |
---|
934 | level++; |
---|
935 | last=dsort[i]; |
---|
936 | } |
---|
937 | priority[iPut]=level; |
---|
938 | } |
---|
939 | model->passInPriorities( priority,false); |
---|
940 | delete [] priority; |
---|
941 | delete [] sort; |
---|
942 | delete [] dsort; |
---|
943 | } |
---|
944 | break; |
---|
945 | case PRESOLVE: |
---|
946 | preSolve = action*5; |
---|
947 | break; |
---|
948 | case SOLVER: |
---|
949 | { for (int i = 0 ; i < (int) value.length() ; i++) |
---|
950 | value[i] = tolower(value[i]) ; |
---|
951 | OsiSolverInterface *newSolver = solvers[value]->clone() ; |
---|
952 | model->assignSolver(newSolver) ; |
---|
953 | std::cout << "Solver set to " << value << "." << std::endl ; |
---|
954 | break ; } |
---|
955 | default: |
---|
956 | { std::cerr << "Unrecognized action. Aborting." << std::endl ; |
---|
957 | abort(); } |
---|
958 | } |
---|
959 | } |
---|
960 | } else { |
---|
961 | // action |
---|
962 | if (type==EXIT) |
---|
963 | break; // stop all |
---|
964 | switch (type) { |
---|
965 | case SOLVECONTINUOUS: |
---|
966 | if (goodModel) { |
---|
967 | model->initialSolve(); |
---|
968 | time2 = CoinCpuTime(); |
---|
969 | totalTime += time2-time1; |
---|
970 | std::cout<<"Result "<<model->solver()->getObjValue()<< |
---|
971 | " iterations "<<model->solver()->getIterationCount()<< |
---|
972 | " took "<<time2-time1<<" seconds - total "<<totalTime<<std::endl; |
---|
973 | time1=time2; |
---|
974 | } else { |
---|
975 | std::cout<<"** Current model not valid"<<std::endl; |
---|
976 | } |
---|
977 | break; |
---|
978 | /* |
---|
979 | Run branch-and-cut. First set a few options -- node comparison, scaling. If |
---|
980 | the solver is Clp, consider running some presolve code (not yet converted |
---|
981 | this to generic OSI) with branch-and-cut. If presolve is disabled, or the |
---|
982 | solver is not Clp, simply run branch-and-cut. Print elapsed time at the end. |
---|
983 | */ |
---|
984 | case BAB: // branchAndBound |
---|
985 | { if (goodModel) |
---|
986 | { CbcCompareUser compare; // Definition of node choice |
---|
987 | model->setNodeComparison(compare); |
---|
988 | OsiSolverInterface * solver = model->solver(); |
---|
989 | if (!doScaling) |
---|
990 | solver->setHintParam(OsiDoScale,false,OsiHintTry); |
---|
991 | #ifdef COIN_USE_CLP |
---|
992 | OsiClpSolverInterface * si = |
---|
993 | dynamic_cast<OsiClpSolverInterface *>(solver) ; |
---|
994 | if (preSolve&&si != NULL) { |
---|
995 | // get clp itself |
---|
996 | ClpSimplex * modelC = si->getModelPtr(); |
---|
997 | if (si->messageHandler()->logLevel()) |
---|
998 | si->messageHandler()->setLogLevel(1); |
---|
999 | if (modelC->tightenPrimalBounds()!=0) { |
---|
1000 | std::cout<<"Problem is infeasible!"<<std::endl; |
---|
1001 | break; |
---|
1002 | } |
---|
1003 | model->initialSolve(); |
---|
1004 | // bounds based on continuous |
---|
1005 | if (tightenFactor) { |
---|
1006 | if (modelC->tightenPrimalBounds(tightenFactor)!=0) { |
---|
1007 | std::cout<<"Problem is infeasible!"<<std::endl; |
---|
1008 | break; |
---|
1009 | } |
---|
1010 | } |
---|
1011 | if (gapRatio<1.0e100) { |
---|
1012 | double value = si->getObjValue(); |
---|
1013 | double value2 = gapRatio*(1.0e-5+fabs(value)); |
---|
1014 | model->setAllowableGap(value2); |
---|
1015 | model->setAllowableFractionGap(gapRatio); |
---|
1016 | std::cout<<"Continuous "<<value |
---|
1017 | <<", so allowable gap set to "<<value2<<std::endl; |
---|
1018 | } |
---|
1019 | if (djFix<1.0e20) { |
---|
1020 | // do some fixing |
---|
1021 | int numberColumns = modelC->numberColumns(); |
---|
1022 | int i; |
---|
1023 | const char * type = modelC->integerInformation(); |
---|
1024 | double * lower = modelC->columnLower(); |
---|
1025 | double * upper = modelC->columnUpper(); |
---|
1026 | double * solution = modelC->primalColumnSolution(); |
---|
1027 | double * dj = modelC->dualColumnSolution(); |
---|
1028 | for (i=0;i<numberColumns;i++) { |
---|
1029 | if (type[i]) { |
---|
1030 | double value = solution[i]; |
---|
1031 | if (value<lower[i]+1.0e-5&&dj[i]>djFix) { |
---|
1032 | solution[i]=lower[i]; |
---|
1033 | upper[i]=lower[i]; |
---|
1034 | } else if (value>upper[i]-1.0e-5&&dj[i]<-djFix) { |
---|
1035 | solution[i]=upper[i]; |
---|
1036 | lower[i]=upper[i]; |
---|
1037 | } |
---|
1038 | } |
---|
1039 | } |
---|
1040 | } |
---|
1041 | { |
---|
1042 | // integer presolve |
---|
1043 | CbcModel * model2 = model->integerPresolve(); |
---|
1044 | if (model2) { |
---|
1045 | // Do complete search |
---|
1046 | |
---|
1047 | CbcRounding heuristic1(*model2); |
---|
1048 | if (useRounding) |
---|
1049 | model2->addHeuristic(&heuristic1); |
---|
1050 | model2->branchAndBound(); |
---|
1051 | // get back solution |
---|
1052 | model->originalModel(model2,false); |
---|
1053 | } else { |
---|
1054 | // infeasible |
---|
1055 | exit(1); |
---|
1056 | } |
---|
1057 | } |
---|
1058 | } else |
---|
1059 | #endif |
---|
1060 | { if (model->solver()->messageHandler()->logLevel()) |
---|
1061 | model->solver()->messageHandler()->setLogLevel(1) ; |
---|
1062 | model->initialSolve() ; |
---|
1063 | if (gapRatio < 1.0e100) |
---|
1064 | { double value = model->solver()->getObjValue() ; |
---|
1065 | double value2 = gapRatio*(1.0e-5+fabs(value)) ; |
---|
1066 | model->setAllowableGap(value2) ; |
---|
1067 | std::cout << "Continuous " << value |
---|
1068 | << ", so allowable gap set to " |
---|
1069 | << value2 << std::endl ; } |
---|
1070 | CbcRounding heuristic1(*model) ; |
---|
1071 | if (useRounding) |
---|
1072 | model->addHeuristic(&heuristic1) ; |
---|
1073 | // add cut generators if wanted |
---|
1074 | if (probingAction==1) |
---|
1075 | model->addCutGenerator(&probingGen,-1,"Probing"); |
---|
1076 | else if (probingAction==2) |
---|
1077 | model->addCutGenerator(&probingGen,-99,"Probing"); |
---|
1078 | if (gomoryAction==1) |
---|
1079 | model->addCutGenerator(&gomoryGen,-1,"Gomory"); |
---|
1080 | else if (gomoryAction==2) |
---|
1081 | model->addCutGenerator(&gomoryGen,-99,"Gomory"); |
---|
1082 | if (knapsackAction==1) |
---|
1083 | model->addCutGenerator(&knapsackGen,-1,"Knapsack"); |
---|
1084 | else if (knapsackAction==2) |
---|
1085 | model->addCutGenerator(&knapsackGen,-99,"Knapsack"); |
---|
1086 | if (oddholeAction==1) |
---|
1087 | model->addCutGenerator(&oddholeGen,-1,"OddHole"); |
---|
1088 | else if (oddholeAction==2) |
---|
1089 | model->addCutGenerator(&oddholeGen,-99,"OddHole"); |
---|
1090 | if (cliqueAction==1) |
---|
1091 | model->addCutGenerator(&cliqueGen,-1,"Clique"); |
---|
1092 | else if (cliqueAction==2) |
---|
1093 | model->addCutGenerator(&cliqueGen,-99,"Clique"); |
---|
1094 | if (mixedAction==1) |
---|
1095 | model->addCutGenerator(&mixedGen,-1,"MixedintegerRounding"); |
---|
1096 | else if (mixedAction==2) |
---|
1097 | model->addCutGenerator(&mixedGen,-99,"MixedintegerRounding"); |
---|
1098 | if (flowAction==1) |
---|
1099 | model->addCutGenerator(&flowGen,-1,"FlowCover"); |
---|
1100 | else if (flowAction==2) |
---|
1101 | model->addCutGenerator(&flowGen,-99,"FlowCover"); |
---|
1102 | if (twomirAction==1) |
---|
1103 | model->addCutGenerator(&twomirGen,-1,"TwoMirCuts"); |
---|
1104 | else if (twomirAction==2) |
---|
1105 | model->addCutGenerator(&twomirGen,-99,"TwoMirCuts"); |
---|
1106 | model->branchAndBound() ; } |
---|
1107 | if (model->bestSolution()) |
---|
1108 | { std::cout << "Optimal solution " |
---|
1109 | << model->solver()->getObjValue() << std::endl ; } |
---|
1110 | else |
---|
1111 | { std::cout << "No integer solution found." << std::endl ; } |
---|
1112 | |
---|
1113 | time2 = CoinCpuTime() ; |
---|
1114 | totalTime += time2-time1 ; |
---|
1115 | std::cout << "Result " << model->solver()->getObjValue() |
---|
1116 | << " took " << time2-time1 << " seconds - total " |
---|
1117 | << totalTime << std::endl ; |
---|
1118 | time1 = time2 ; } |
---|
1119 | else |
---|
1120 | { std::cout << "** Current model not valid" << std::endl ; } |
---|
1121 | break ; } |
---|
1122 | case IMPORT: |
---|
1123 | { |
---|
1124 | // get next field |
---|
1125 | field = getString(argc,argv); |
---|
1126 | std::string fileName; |
---|
1127 | bool canOpen=false; |
---|
1128 | if (field=="-") { |
---|
1129 | // stdin |
---|
1130 | canOpen=true; |
---|
1131 | fileName = "-"; |
---|
1132 | } else { |
---|
1133 | if (field[0]=='/'||field[0]=='~') |
---|
1134 | fileName = field; |
---|
1135 | else |
---|
1136 | fileName = directory+field; |
---|
1137 | FILE *fp=fopen(fileName.c_str(),"r"); |
---|
1138 | if (fp) { |
---|
1139 | // can open - lets go for it |
---|
1140 | fclose(fp); |
---|
1141 | canOpen=true; |
---|
1142 | } else { |
---|
1143 | std::cout<<"Unable to open file "<<fileName<<std::endl; |
---|
1144 | } |
---|
1145 | } |
---|
1146 | if (canOpen) { |
---|
1147 | model->gutsOfDestructor(); |
---|
1148 | int status =model->solver()->readMps(fileName.c_str(),""); |
---|
1149 | if (!status||(status>0&&allowImportErrors)) { |
---|
1150 | // I don't think there is any need for this but .. |
---|
1151 | //OsiWarmStartBasis allSlack; |
---|
1152 | goodModel=true; |
---|
1153 | //model->setBasis(allSlack); |
---|
1154 | time2 = CoinCpuTime(); |
---|
1155 | totalTime += time2-time1; |
---|
1156 | time1=time2; |
---|
1157 | } else { |
---|
1158 | // errors |
---|
1159 | std::cout<<"There were "<<status<< |
---|
1160 | " errors on input"<<std::endl; |
---|
1161 | } |
---|
1162 | } |
---|
1163 | } |
---|
1164 | break; |
---|
1165 | case EXPORT: |
---|
1166 | { |
---|
1167 | // get next field |
---|
1168 | field = getString(argc,argv); |
---|
1169 | std::string fileName; |
---|
1170 | bool canOpen=false; |
---|
1171 | if (field[0]=='/'||field[0]=='~') |
---|
1172 | fileName = field; |
---|
1173 | else |
---|
1174 | fileName = directory+field; |
---|
1175 | FILE *fp=fopen(fileName.c_str(),"w"); |
---|
1176 | if (fp) { |
---|
1177 | // can open - lets go for it |
---|
1178 | fclose(fp); |
---|
1179 | canOpen=true; |
---|
1180 | } else { |
---|
1181 | std::cout<<"Unable to open file "<<fileName<<std::endl; |
---|
1182 | } |
---|
1183 | if (canOpen) { |
---|
1184 | model->solver()->writeMps(fileName.c_str(),""); |
---|
1185 | time2 = CoinCpuTime(); |
---|
1186 | totalTime += time2-time1; |
---|
1187 | time1=time2; |
---|
1188 | } |
---|
1189 | } |
---|
1190 | break; |
---|
1191 | case MAXIMIZE: |
---|
1192 | model->solver()->setObjSense(-1); |
---|
1193 | break; |
---|
1194 | case MINIMIZE: |
---|
1195 | model->solver()->setObjSense(1); |
---|
1196 | break; |
---|
1197 | case DIRECTORY: |
---|
1198 | { directory = getString(argc,argv); |
---|
1199 | if (directory[directory.length()-1] != '/') |
---|
1200 | directory += '/' ; |
---|
1201 | break ; } |
---|
1202 | case STDIN: |
---|
1203 | read_mode=-1; |
---|
1204 | break; |
---|
1205 | case VERSION: |
---|
1206 | std::cout<<"Coin LP version "<<CBCVERSION |
---|
1207 | <<", build "<<__DATE__<<std::endl; |
---|
1208 | break; |
---|
1209 | case UNITTEST: |
---|
1210 | { |
---|
1211 | // okay so there is not a real unit test |
---|
1212 | |
---|
1213 | int status =model->solver()->readMps("../Mps/Sample/p0033.mps", |
---|
1214 | ""); |
---|
1215 | assert(!status); |
---|
1216 | model->branchAndBound(); |
---|
1217 | model->solver()->resolve(); |
---|
1218 | std::cout<<"Optimal solution "<<model->solver()->getObjValue()<<std::endl; |
---|
1219 | assert(fabs(model->solver()->getObjValue()-3089.0)<1.0e-5); |
---|
1220 | fprintf(stderr,"Test was okay\n"); |
---|
1221 | status =model->solver()->readMps("../Mps/Sample/p0033.mps", |
---|
1222 | ""); |
---|
1223 | assert(!status); |
---|
1224 | model->setCutoff(1.0e20); |
---|
1225 | model->setBestObjectiveValue(1.0e20); |
---|
1226 | model->setMaximumSolutions(1); |
---|
1227 | model->setSolutionCount(0); |
---|
1228 | // Switch off strong branching to give better chance of NOT finding best |
---|
1229 | model->setNumberStrong(0); |
---|
1230 | // Definition of node choice |
---|
1231 | CbcCompareDefault compare(100.0); |
---|
1232 | model->setNodeComparison(compare); |
---|
1233 | model->solver()->resolve(); |
---|
1234 | model->branchAndBound(); |
---|
1235 | model->solver()->resolve(); |
---|
1236 | std::cout<<"partial solution "<<model->solver()->getObjValue()<<std::endl; |
---|
1237 | if (model->solver()->getObjValue()<3090.0) { |
---|
1238 | std::cout<<"Got optimal solution by mistake!"<<std::endl; |
---|
1239 | } |
---|
1240 | } |
---|
1241 | break; |
---|
1242 | case MIPLIB: |
---|
1243 | { |
---|
1244 | int mainTest (int argc, const char *argv[]); |
---|
1245 | // create fields for test |
---|
1246 | const char * fields[3]; |
---|
1247 | int nFields=1; |
---|
1248 | fields[0]="fake main for miplib"; |
---|
1249 | if (directory!="./") { |
---|
1250 | fields[1]=("-miplibDir="+directory).c_str(); |
---|
1251 | nFields=2; |
---|
1252 | } |
---|
1253 | mainTest(nFields,fields); |
---|
1254 | } |
---|
1255 | break; |
---|
1256 | case SOLUTION: |
---|
1257 | if (goodModel) { |
---|
1258 | // get next field |
---|
1259 | field = getString(argc,argv); |
---|
1260 | std::string fileName; |
---|
1261 | FILE *fp=NULL; |
---|
1262 | if (field=="-"||field=="EOL") { |
---|
1263 | // stdout |
---|
1264 | fp=stdout; |
---|
1265 | } else { |
---|
1266 | if (field[0]=='/'||field[0]=='~') |
---|
1267 | fileName = field; |
---|
1268 | else |
---|
1269 | fileName = directory+field; |
---|
1270 | fp=fopen(fileName.c_str(),"w"); |
---|
1271 | } |
---|
1272 | if (fp) { |
---|
1273 | // make fancy later on |
---|
1274 | int iRow; |
---|
1275 | int numberRows=model->solver()->getNumRows(); |
---|
1276 | const double * dualRowSolution = model->getRowPrice(); |
---|
1277 | const double * primalRowSolution = model->getRowActivity(); |
---|
1278 | for (iRow=0;iRow<numberRows;iRow++) { |
---|
1279 | fprintf(fp,"%7d ",iRow); |
---|
1280 | fprintf(fp,"%15.8g %15.8g\n",primalRowSolution[iRow], |
---|
1281 | dualRowSolution[iRow]); |
---|
1282 | } |
---|
1283 | int iColumn; |
---|
1284 | int numberColumns=model->solver()->getNumCols(); |
---|
1285 | const double * dualColumnSolution = |
---|
1286 | model->getReducedCost(); |
---|
1287 | const double * primalColumnSolution = |
---|
1288 | model->getColSolution(); |
---|
1289 | for (iColumn=0;iColumn<numberColumns;iColumn++) { |
---|
1290 | fprintf(fp,"%7d ",iColumn); |
---|
1291 | fprintf(fp,"%15.8g %15.8g\n", |
---|
1292 | primalColumnSolution[iColumn], |
---|
1293 | dualColumnSolution[iColumn]); |
---|
1294 | } |
---|
1295 | if (fp!=stdout) |
---|
1296 | fclose(fp); |
---|
1297 | } else { |
---|
1298 | std::cout<<"Unable to open file "<<fileName<<std::endl; |
---|
1299 | } |
---|
1300 | } else { |
---|
1301 | std::cout<<"** Current model not valid"<<std::endl; |
---|
1302 | |
---|
1303 | } |
---|
1304 | break; |
---|
1305 | default: |
---|
1306 | abort(); |
---|
1307 | } |
---|
1308 | } |
---|
1309 | } else if (!numberMatches) { |
---|
1310 | std::cout<<"No match for "<<field<<" - ? for list of commands" |
---|
1311 | <<std::endl; |
---|
1312 | } else if (numberMatches==1) { |
---|
1313 | if (!numberQuery) { |
---|
1314 | std::cout<<"Short match for "<<field<<" possible completion:" |
---|
1315 | <<std::endl; |
---|
1316 | for ( iParam=0; iParam<numberParameters; iParam++ ) { |
---|
1317 | int match = parameters[iParam].matches(field); |
---|
1318 | if (match) |
---|
1319 | std::cout<<parameters[iParam].matchName()<<std::endl; |
---|
1320 | } |
---|
1321 | } else if (numberQuery) { |
---|
1322 | std::cout<<"Short match for "<<field<<" completion:" |
---|
1323 | <<std::endl; |
---|
1324 | for ( iParam=0; iParam<numberParameters; iParam++ ) { |
---|
1325 | int match = parameters[iParam].matches(field); |
---|
1326 | if (match) { |
---|
1327 | std::cout<<parameters[iParam].matchName()<<" : "; |
---|
1328 | std::cout<<parameters[iParam].shortHelp()<<std::endl; |
---|
1329 | } |
---|
1330 | } |
---|
1331 | } |
---|
1332 | } else { |
---|
1333 | if (!numberQuery) |
---|
1334 | std::cout<<"Multiple matches for "<<field<<" - possible completions:" |
---|
1335 | <<std::endl; |
---|
1336 | else |
---|
1337 | std::cout<<"Completions of "<<field<<":"<<std::endl; |
---|
1338 | for ( iParam=0; iParam<numberParameters; iParam++ ) { |
---|
1339 | int match = parameters[iParam].matches(field); |
---|
1340 | if (match) { |
---|
1341 | std::cout<<parameters[iParam].matchName(); |
---|
1342 | if (numberQuery>=2) |
---|
1343 | std::cout<<" : "<<parameters[iParam].shortHelp(); |
---|
1344 | std::cout<<std::endl; |
---|
1345 | } |
---|
1346 | } |
---|
1347 | } |
---|
1348 | } |
---|
1349 | /* |
---|
1350 | Final cleanup. Delete the model and the vector of available solvers. |
---|
1351 | */ |
---|
1352 | delete model; |
---|
1353 | |
---|
1354 | for (solverMapIter_t solverIter = solvers.begin() ; |
---|
1355 | solverIter != solvers.end() ; |
---|
1356 | solverIter++) |
---|
1357 | { if (solverIter->second) delete solverIter->second ; } |
---|
1358 | } |
---|
1359 | return 0; |
---|
1360 | } |
---|