1 /*
2 Copyright (C) 2007, Lou Hafer, International Business Machines Corporation
3 and others. All Rights Reserved.
4
5 This code is licensed under the terms of the Eclipse Public License (EPL).
6
7 $Id$
8 */
9 /*
10 This file is part of cbc-generic.
11 */
12
13 #if defined(_MSC_VER)
14 // Turn off compiler warning about long names
15 #pragma warning(disable : 4786)
16 #endif
17
18 #include <string>
19 #include <cassert>
20
21 #include "CoinFinite.hpp"
22 #include "CoinParam.hpp"
23
24 #include "OsiSolverInterface.hpp"
25
26 #include "CbcModel.hpp"
27 #include "CbcGenCtlBlk.hpp"
28
29 #include "CbcGenParam.hpp"
30 #include "CbcGenCbcParam.hpp"
31 #include "CbcGenOsiParam.hpp"
32
33 /*! \file CbcOsiParamUtils
34 \brief Implementation functions for CbcOsiParam parameters.
35 */
36
37 namespace {
38
39 char svnid[] = "$Id$";
40
41 }
42
43 namespace CbcOsiParamUtils {
44
45 /*
46 Function to set up OSI parameters. Does not include solver-specific
47 parameters.
48
49 ALGORITHM is commented out in CoinSolve.
50 */
51
addCbcOsiParams(int & numberParameters,CoinParamVec & parameters,OsiSolverInterface * osi)52 void addCbcOsiParams(int &numberParameters, CoinParamVec ¶meters,
53 OsiSolverInterface *osi)
54 {
55 CbcOsiParam *param;
56 OsiHintParam key;
57 bool sense;
58 OsiHintStrength strength;
59 int ival;
60 double dval;
61
62 param = new CbcOsiParam(CbcOsiParam::KEEPNAMES,
63 "keepN!ames", "Whether to keep row and column names on import.",
64 "off", 1);
65 param->appendKwd("on");
66 param->setPushFunc(pushCbcOsiKwd);
67 param->setObj(osi);
68 param->setLongHelp(
69 "Row and column names are human-friendly, but maintaining names takes up space and time. Specifying -keepnames off >before< importing a problem will discard any name information.");
70 parameters.push_back(param);
71
72 (void)osi->getIntParam(OsiMaxNumIteration, ival);
73 param = new CbcOsiParam(CbcOsiParam::MAXITERATION,
74 "maxIt!erations", "Iteration limit for OSI solver.",
75 0, COIN_INT_MAX, ival);
76 param->setPushFunc(pushCbcOsiInt);
77 param->setObj(osi);
78 param->setLongHelp(
79 "Limits the number of iterations the OSI solver can perform when solving a problem.");
80 parameters.push_back(param);
81
82 (void)osi->getIntParam(OsiMaxNumIterationHotStart, ival);
83 param = new CbcOsiParam(CbcOsiParam::MAXHOTITS,
84 "hot!StartMaxIts", "Iteration limit for OSI solver hot start.",
85 0, COIN_INT_MAX, ival);
86 param->setPushFunc(pushCbcOsiInt);
87 param->setObj(osi);
88 param->setLongHelp(
89 "Limits the number of iterations the OSI solver can perform when solving a problem from a hot start. In the context of cbc, this limits the number of iterations expended on each LP during strong branching.");
90 parameters.push_back(param);
91
92 /*
93 Simplified to on/off for OsiSolverInterface, where it goes in as a hint.
94 */
95 (void)osi->getHintParam(OsiDoPresolveInInitial, sense, strength);
96 if (sense == true) {
97 ival = 1;
98 } else {
99 ival = 0;
100 }
101 param = new CbcOsiParam(CbcOsiParam::PRESOLVE,
102 "presolve", "Whether to presolve problem", "off", ival);
103 param->appendKwd("on");
104 param->setPushFunc(pushCbcOsiHint);
105 param->setObj(osi);
106 param->setLongHelp(
107 "Presolve analyzes the model to find such things as redundant constraints, constraints which fix some variables, constraints which can be transformed into bounds, etc. For the initial solve of any problem this is worth doing unless you know that it will have no effect.");
108 parameters.push_back(param);
109
110 param = new CbcOsiParam(CbcOsiParam::PRIMALTOLERANCE,
111 "primalT!olerance",
112 "For an optimal solution no primal infeasibility may exceed this value",
113 1.0e-20, 1.0e12);
114 param->setPushFunc(pushCbcOsiDbl);
115 param->setObj(osi);
116 param->setLongHelp(
117 "Normally the default tolerance is fine, but you may want to increase it a bit if a primal run seems to be having a hard time");
118 parameters.push_back(param);
119
120 /*
121 Simplified for OsiSolverInterface, which just takes a hint.
122 */
123 (void)osi->getHintParam(OsiDoScale, sense, strength);
124 if (sense == true) {
125 ival = 1;
126 } else {
127 ival = 0;
128 }
129 param = new CbcOsiParam(CbcOsiParam::SCALING,
130 "scal!ing", "Whether to scale problem", "off", ival);
131 param->appendKwd("on");
132 param->setPushFunc(pushCbcOsiHint);
133 param->setObj(osi);
134 param->setLongHelp(
135 "Scaling can help in solving problems which might otherwise fail because of lack of accuracy. It can also reduce the number of iterations. It is not applied if the range of elements is small. When unscaled it is possible that there may be small primal and/or infeasibilities.");
136 parameters.push_back(param);
137
138 ival = osi->messageHandler()->logLevel();
139 param = new CbcOsiParam(CbcOsiParam::SOLVERLOGLEVEL,
140 "slog!Level", "Level of detail in Solver output", -1, 63, ival);
141 param->setPushFunc(pushCbcOsiLogLevel);
142 param->setObj(osi);
143 param->setLongHelp(
144 "If 0 then there should be no output in normal circumstances. 1 is probably the best value for most uses, while 2 and 3 give more information.");
145 parameters.push_back(param);
146
147 numberParameters = parameters.size();
148 assert(numberParameters <= parameters.capacity());
149 }
150
loadOsiParamObj(const CoinParamVec paramVec,int first,int last,OsiSolverInterface * obj)151 void loadOsiParamObj(const CoinParamVec paramVec, int first, int last,
152 OsiSolverInterface *obj)
153
154 {
155 int i;
156 /*
157 Load the OsiSolverInterface object into the parameters
158 */
159 for (i = first; i <= last; i++) {
160 CbcOsiParam *osiParam = dynamic_cast< CbcOsiParam * >(paramVec[i]);
161 assert(osiParam != 0);
162 osiParam->setObj(obj);
163 }
164
165 return;
166 }
167
168 /*
169 Function to set default values for solver appropriate for cbc-generic.
170 */
171
setOsiSolverInterfaceDefaults(OsiSolverInterface * osi)172 void setOsiSolverInterfaceDefaults(OsiSolverInterface *osi)
173
174 {
175 bool result;
176
177 /*
178 OsiNameDiscipline isn't supported by all solvers, so check to see that it
179 worked. If not, fall back to zero.
180 */
181 osi->setIntParam(OsiMaxNumIteration, 1000000);
182 osi->setIntParam(OsiMaxNumIterationHotStart, 1000);
183 result = osi->setIntParam(OsiNameDiscipline, 1);
184 if (!result) {
185 result = osi->setIntParam(OsiNameDiscipline, 0);
186 }
187
188 /*
189 Primal and dual feasibility tolerances (OsiPrimalTolerance and
190 OsiDualTolerance, respectively) are left to the discretion of the solver.
191 */
192 osi->setDblParam(OsiDualObjectiveLimit, 1.0e100);
193 osi->setDblParam(OsiPrimalObjectiveLimit, 1.0e100);
194 osi->setDblParam(OsiObjOffset, 0.0);
195
196 osi->setHintParam(OsiDoPresolveInInitial, true, OsiHintDo);
197 osi->setHintParam(OsiDoDualInInitial, true, OsiHintIgnore);
198 osi->setHintParam(OsiDoPresolveInResolve, false, OsiHintTry);
199 osi->setHintParam(OsiDoDualInInitial, true, OsiHintTry);
200 osi->setHintParam(OsiDoScale, true, OsiHintDo);
201 osi->setHintParam(OsiDoCrash, true, OsiHintIgnore);
202 osi->setHintParam(OsiDoReducePrint, true, OsiHintDo);
203 osi->setHintParam(OsiDoInBranchAndCut, true, OsiHintTry);
204
205 return;
206 }
207
208 /*
209 Function to push an integer parameter.
210 */
211
pushCbcOsiInt(CoinParam * param)212 int pushCbcOsiInt(CoinParam *param)
213
214 {
215 assert(param != 0);
216
217 CbcOsiParam *osiParam = dynamic_cast< CbcOsiParam * >(param);
218 assert(osiParam != 0);
219
220 OsiSolverInterface *osi = osiParam->obj();
221 assert(osi != 0);
222 int val = osiParam->intVal();
223 CbcOsiParam::CbcOsiParamCode code = osiParam->paramCode();
224
225 assert(osi != 0);
226 /*
227 Setup to return nonfatal/fatal error (1/-1) by default, so that all we need
228 to do is correct to 0 (no error) if we're successful.
229 */
230 int retval;
231 if (CoinParamUtils::isInteractive()) {
232 retval = 1;
233 } else {
234 retval = -1;
235 }
236 /*
237 Translate the parameter code from CbcOsiParamCode into the correct key for
238 CbcIntParam.
239 */
240 OsiIntParam key;
241 switch (code) {
242 case CbcOsiParam::MAXITERATION: {
243 key = OsiMaxNumIteration;
244 break;
245 }
246 case CbcOsiParam::MAXHOTITS: {
247 key = OsiMaxNumIterationHotStart;
248 break;
249 }
250 default: {
251 std::cerr << "pushCbcOsiIntParam: no equivalent OsiIntParam for "
252 << "parameter code `" << code << "'." << std::endl;
253 retval = -1;
254 break;
255 }
256 }
257
258 bool setOK = osi->setIntParam(key, val);
259 if (setOK == false) {
260 retval = -1;
261 }
262
263 return (retval);
264 }
265 /*
266 Function to push a double parameter.
267 */
268
pushCbcOsiDbl(CoinParam * param)269 int pushCbcOsiDbl(CoinParam *param)
270
271 {
272 assert(param != 0);
273
274 CbcOsiParam *osiParam = dynamic_cast< CbcOsiParam * >(param);
275 assert(osiParam != 0);
276
277 OsiSolverInterface *osi = osiParam->obj();
278 assert(osi != 0);
279 double val = osiParam->dblVal();
280 CbcOsiParam::CbcOsiParamCode code = osiParam->paramCode();
281
282 assert(osi != 0);
283 /*
284 Setup to return nonfatal/fatal error (1/-1) by default, so that all we need
285 to do is correct to 0 (no error) if we're successful.
286 */
287 int retval;
288 if (CoinParamUtils::isInteractive()) {
289 retval = 1;
290 } else {
291 retval = -1;
292 }
293 /*
294 Translate the parameter code from CbcOsiParamCode into the correct key for
295 CbcDblParam.
296 */
297 OsiDblParam key;
298 switch (code) {
299 case CbcOsiParam::PRIMALTOLERANCE: {
300 key = OsiPrimalTolerance;
301 break;
302 }
303 case CbcOsiParam::DUALTOLERANCE: {
304 key = OsiDualTolerance;
305 ;
306 break;
307 }
308 case CbcOsiParam::DUALBOUND: {
309 key = OsiDualObjectiveLimit;
310 break;
311 }
312 default: {
313 std::cerr << "pushCbcOsiDblParam: no equivalent OsiDblParam for "
314 << "parameter code `" << code << "'." << std::endl;
315 retval = -1;
316 break;
317 }
318 }
319
320 bool setOK = osi->setDblParam(key, val);
321 if (setOK == false) {
322 retval = -1;
323 }
324
325 return (retval);
326 }
327
328 /*
329 Function to push a keyword-valued parameter. This can translate into integer
330 as well as string-valued parameters.
331 */
332
pushCbcOsiKwd(CoinParam * param)333 int pushCbcOsiKwd(CoinParam *param)
334
335 {
336 assert(param != 0);
337 CbcOsiParam *osiParam = dynamic_cast< CbcOsiParam * >(param);
338 assert(osiParam != 0);
339 OsiSolverInterface *osi = osiParam->obj();
340 assert(osi != 0);
341
342 std::string str = osiParam->kwdVal();
343 CbcOsiParam::CbcOsiParamCode code = osiParam->paramCode();
344
345 int retval = 0;
346 /*
347 Figure out what we're doing and set the relevant field.
348 */
349 OsiIntParam key;
350
351 switch (code) {
352 case CbcOsiParam::KEEPNAMES: {
353 if (str == "on" || str == "off") {
354 int discipline;
355 if (str == "on") {
356 discipline = 1;
357 } else {
358 discipline = 0;
359 }
360 bool recog = osi->setIntParam(OsiNameDiscipline, discipline);
361 if (recog == false) {
362 std::cerr
363 << "pushCbcOsiKwdParam(KEEPNAMES): underlying solver does not "
364 << "recognise name discipline " << discipline << "."
365 << std::endl;
366 retval = +1;
367 }
368 } else {
369 std::cerr
370 << "pushCbcOsiKwdParam(KEEPNAMES): unrecognised keyword `"
371 << str << "'." << std::endl;
372 retval = -1;
373 }
374 break;
375 }
376 default: {
377 std::cerr
378 << "pushCbcGenKwdParam: unrecognised parameter code `"
379 << code << "'." << std::endl;
380 retval = -1;
381 break;
382 }
383 }
384
385 return (retval);
386 }
387
388 /*
389 Function to set the solver's output level. To cover all the bases, adjust
390 the message handler and set the hint. Nothing can go fatally wrong here,
391 but we'll return non-fatal error if the solver rejects the hint. The
392 implementor of an OSI has wide latitude with hints, and may elect to set a
393 log level as part of handling the hint. Do that first and then explicitly
394 set the message handler log level to be sure the new value isn't
395 overridden.
396 */
397
pushCbcOsiLogLevel(CoinParam * param)398 int pushCbcOsiLogLevel(CoinParam *param)
399
400 {
401 assert(param != 0);
402 CbcOsiParam *osiParam = dynamic_cast< CbcOsiParam * >(param);
403 assert(osiParam != 0);
404 OsiSolverInterface *osi = osiParam->obj();
405 assert(osi != 0);
406
407 int lvl = param->intVal();
408 /*
409 Setup to return nonfatal/fatal error (1/-1) by default, so that all we need
410 to do is correct to 0 (no error) if we're successful.
411 */
412 int retval;
413 if (CoinParamUtils::isInteractive()) {
414 retval = 1;
415 } else {
416 retval = -1;
417 }
418 /*
419 Now try to do the right thing with a hint. Harder to say -- assume that log
420 level 1 is `normal'.
421 */
422 OsiHintStrength strength;
423 bool sense;
424 if (lvl < 1) {
425 strength = OsiHintDo;
426 sense = true;
427 } else if (lvl == 1) {
428 strength = OsiHintIgnore;
429 sense = true;
430 } else if (lvl == 2) {
431 strength = OsiHintTry;
432 sense = false;
433 } else {
434 strength = OsiHintDo;
435 sense = false;
436 }
437
438 bool setOK = osi->setHintParam(OsiDoReducePrint, sense, strength);
439
440 /*
441 Recover the message handler and set the log level directly.
442 */
443 CoinMessageHandler *hndl = osi->messageHandler();
444 assert(hndl != 0);
445 hndl->setLogLevel(lvl);
446
447 if (setOK) {
448 return (0);
449 } else {
450 return (retval);
451 }
452 }
453
454 /*
455 Function for parameters that are enabled/disabled with a hint.
456 */
pushCbcOsiHint(CoinParam * param)457 int pushCbcOsiHint(CoinParam *param)
458
459 {
460 assert(param != 0);
461 CbcOsiParam *osiParam = dynamic_cast< CbcOsiParam * >(param);
462 assert(osiParam != 0);
463 OsiSolverInterface *osi = osiParam->obj();
464 assert(osi != 0);
465 /*
466 Setup to return nonfatal/fatal error (1/-1) by default, so that all we need
467 to do is correct to 0 (no error) if we're successful.
468 */
469 int retval;
470 if (CoinParamUtils::isInteractive()) {
471 retval = 1;
472 } else {
473 retval = -1;
474 }
475 /*
476 Set the sense for the hint.
477 */
478 std::string kwd = param->kwdVal();
479 bool sense;
480 if (kwd == "off") {
481 sense = false;
482 } else {
483 sense = true;
484 }
485 /*
486 Grab the parameter code and translate to an OSI parameter key.
487 */
488 CbcOsiParam::CbcOsiParamCode code = osiParam->paramCode();
489 OsiHintParam key;
490 switch (code) {
491 case CbcOsiParam::PRESOLVE: {
492 key = OsiDoPresolveInInitial;
493 break;
494 }
495 case CbcOsiParam::SCALING: {
496 key = OsiDoScale;
497 break;
498 }
499 default: {
500 std::cerr << "pushCbcOsiHint: no equivalent OsiHintParam for "
501 << "parameter code `" << code << "'." << std::endl;
502 retval = -1;
503 break;
504 }
505 }
506
507 bool setOK = osi->setHintParam(key, sense, OsiHintDo);
508
509 if (setOK) {
510 return (0);
511 } else {
512 return (retval);
513 }
514 }
515
516 } // end namespace CbcOsiParamUtils
517
518 /* vi: softtabstop=2 shiftwidth=2 expandtab tabstop=2
519 */
520