source: trunk/Cbc/src/CbcGenOsiParamUtils.cpp @ 2464

Last change on this file since 2464 was 2464, checked in by unxusr, 7 months ago

.clang-format with proposal for formatting code

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.4 KB
Line 
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: CbcGenOsiParamUtils.cpp 2464 2019-01-03 19:03:23Z unxusr $
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
37namespace {
38
39char svnid[] = "$Id: CbcGenOsiParamUtils.cpp 2464 2019-01-03 19:03:23Z unxusr $";
40
41}
42
43namespace 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
52void addCbcOsiParams(int &numberParameters, CoinParamVec &parameters,
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
151void 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
172void 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
212int 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
269int 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
333int 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
398int 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*/
457int 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
Note: See TracBrowser for help on using the repository browser.