source: branches/devel/Cbc/src/CbcGeneric.cpp @ 648

Last change on this file since 648 was 608, checked in by lou, 12 years ago

Cbc-generic: Add message handler, separate libCbc and cbc-generic main log
level parameters.

  • Property svn:eol-style set to native
File size: 11.9 KB
Line 
1/*
2  Copyright (C) 2007, Lou Hafer, International Business Machines Corporation
3  and others.  All Rights Reserved.
4
5  This file is part of cbc-generic.
6*/
7
8#include "CbcConfig.h"
9#include "CoinPragma.hpp"
10
11#include <cassert>
12#include <typeinfo>
13#include <cstdio>
14#include <cmath>
15#include <cfloat>
16#include <string>
17#include <iostream>
18
19#include "CoinFileIO.hpp"
20#include "CoinMpsIO.hpp"
21#include "CoinPackedMatrix.hpp"
22#include "CoinPackedVector.hpp"
23#include "CoinWarmStartBasis.hpp"
24#include "CoinTime.hpp"
25#include "OsiSolverInterface.hpp"
26#include "OsiCuts.hpp"
27#include "OsiRowCut.hpp"
28#include "OsiColCut.hpp"
29
30#include "CglCutGenerator.hpp"
31#include "CglProbing.hpp"
32#include "CglClique.hpp"
33#include "CglFlowCover.hpp"
34#include "CglGomory.hpp"
35#include "CglKnapsackCover.hpp"
36#include "CglMixedIntegerRounding2.hpp"
37#include "CglOddHole.hpp"
38#include "CglRedSplit.hpp"
39#include "CglTwomir.hpp"
40
41#include "CglPreProcess.hpp"
42
43#include "CbcModel.hpp"
44#include "CbcEventHandler.hpp"
45#include "CbcTree.hpp"
46#include "CbcCutGenerator.hpp"
47#include "CbcHeuristic.hpp"
48#include "CbcHeuristicFPump.hpp"
49#include "CbcHeuristicGreedy.hpp"
50#include "CbcHeuristicLocal.hpp"
51#include "CbcTreeLocal.hpp"
52#include "CbcCompareActual.hpp"
53
54#include "CoinParam.hpp"
55
56#include "CbcGenCtlBlk.hpp"
57#include "CbcGenParam.hpp"
58#include "CbcGenCbcParam.hpp"
59#include "CbcGenOsiParam.hpp"
60
61namespace {
62
63  char svnid[] = "$Id$" ;
64
65}
66
67namespace CbcGenSolvers
68{
69  OsiSolverInterface *setupSolvers() ;
70  void deleteSolvers() ;
71}
72
73/*
74  Unnamed local namespace for cbc-generic support types and functions.
75*/
76
77namespace {
78
79/*
80  Utility to mark the parameter as having been set by the user. This is a
81  bit clumsy --- we need to cast to a derived parameter type to get the
82  parameter code. But it'll do 'til I think of a better way.
83*/
84
85void markAsSetByUser (CbcGenCtlBlk &ctlBlk, CoinParam *param)
86
87{ CbcGenParam *genParam = dynamic_cast<CbcGenParam *>(param) ;
88  CbcCbcParam *cbcParam = dynamic_cast<CbcCbcParam *>(param) ;
89  CbcOsiParam *osiParam = dynamic_cast<CbcOsiParam *>(param) ;
90  int code = -1 ;
91
92  if (genParam != 0)
93  { code = genParam->paramCode() ; }
94  else
95  if (cbcParam != 0)
96  { code = cbcParam->paramCode() ; }
97  else
98  if (osiParam != 0)
99  { code = osiParam->paramCode() ; }
100  else
101  { std::cerr
102      << "Unrecognised parameter class! Serious internal confusion."
103      << std::endl ; }
104
105  if (code >= 0)
106  { ctlBlk.setByUser_[code] = true ; }
107
108  return ; }
109
110
111} // end unnamed namespace
112
113int main (int argc, const char *argv[])
114{
115/*
116  This interior block contains all memory allocation; useful for debugging.
117*/
118{ double time1 = CoinCpuTime() ;
119  double time2 ;
120/*
121  Try and get all the various i/o to come out in order. Synchronise with C
122  stdio and make stderr and stdout unbuffered.
123*/
124  std::ios::sync_with_stdio() ;
125  setbuf(stderr,0) ;
126  setbuf(stdout,0) ;
127/*
128  The constructor for ctlBlk establishes the default values for cbc-generic
129  parameters. A little more work is required to create the vector of available
130  solvers and set the default.
131*/
132  CbcGenCtlBlk ctlBlk ;
133  ctlBlk.setMessages() ;
134  ctlBlk.setLogLevel(1) ;
135  OsiSolverInterface *dfltSolver = CbcGenSolvers::setupSolvers() ;
136  ctlBlk.dfltSolver_ = dfltSolver ;
137  assert (ctlBlk.dfltSolver_) ;
138/*
139  Now we can begin to initialise the parameter vector. Create a vector of the
140  proper size, then load up the parameters that are relevant to the main
141  program (specifically, values held in ctlBlk and actions evoked from the
142  main program).
143*/
144  int numParams = 0 ;
145  CoinParamVec paramVec ;
146  paramVec.reserve(CbcOsiParam::CBCOSI_LASTPARAM) ;
147  ctlBlk.paramVec_ = &paramVec ;
148  ctlBlk.genParams_.first_ = numParams ;
149  CbcGenParamUtils::addCbcGenParams(numParams,paramVec,&ctlBlk) ;
150  ctlBlk.genParams_.last_ = numParams-1 ;
151/*
152  Establish a CbcModel object with the default lp solver. Install any defaults
153  that are available from ctlBlk.
154*/
155  CbcModel *model = new CbcModel(*dfltSolver) ;
156  ctlBlk.model_ = model ;
157  model->messageHandler()->setLogLevel(1) ;
158  model->setNumberStrong(ctlBlk.chooseStrong_.numStrong_) ;
159  model->setNumberBeforeTrust(ctlBlk.chooseStrong_.numBeforeTrust_) ;
160  CbcCbcParamUtils::setCbcModelDefaults(model) ;
161  OsiSolverInterface *osi = model->solver() ;
162/*
163  Set up the remaining classes of parameters, taking defaults from the CbcModel
164  and OsiSolverInterface objects we've set up. There are parameters that
165  belong to CbcModel (CbcCbcParam) and to the underlying OsiSolverInterface
166  (CbcOsiParam).
167*/
168  ctlBlk.cbcParams_.first_ = numParams ;
169  CbcCbcParamUtils::addCbcCbcParams(numParams,paramVec,model) ;
170  ctlBlk.cbcParams_.last_ = numParams-1 ;
171  ctlBlk.osiParams_.first_ = numParams ;
172  CbcOsiParamUtils::addCbcOsiParams(numParams,paramVec,osi) ;
173  ctlBlk.osiParams_.last_ = numParams-1 ;
174/*
175  Initialise the vector that tracks parameters that have been changed by user
176  command.
177*/
178  ctlBlk.setByUser_.resize(CbcOsiParam::CBCOSI_LASTPARAM,false) ;
179/*
180  The main command parsing loop. Call getCommand to get the next parameter.
181  (The user will be prompted in interactive mode.) If we find something,
182  proceed to process it.
183
184  If we don't find anything, behaviour depends on what we've seen so far:
185
186  * An empty command/parameter and no history of previous success gets a
187    brief message. If we're in interactive mode, allow the user to try again,
188    otherwise quit.
189
190  * An empty command/parameter in interactive mode with some history of
191    successful commands is ignored. Iterate and try again.
192
193  * An empty command/parameter when we're not interactive is taken
194    as the end of commands. If we have a good model, force branchAndBound.
195    (This is one aspect of giving the expected behaviour for
196    `cbc-generic [parameters] foo.mps'.)
197*/
198  bool keepParsing = true ;
199  bool forceImport = false ;
200  std::string forceImportFile = "" ;
201  std::string prompt = "cbcGen: " ;
202  std::string pfx = "" ;
203  while (keepParsing)
204  { std::string paramName = CoinParamUtils::getCommand(argc,argv,prompt,&pfx);
205    if (paramName.length() == 0)
206    { if (ctlBlk.paramsProcessed_ == 0)
207      { if (CoinParamUtils::isInteractive())
208        { std::cout
209            << "Type `?' or `help' for usage and command keywords."
210            << " Type `quit' to quit." ; }
211        else
212        { std::cout
213            << "Type `cbc-generic -help' for usage and parameter keywords." ;
214          keepParsing = false ; }
215        std::cout << std::endl ; }
216      else
217      if (!CoinParamUtils::isInteractive())
218      { keepParsing = false ;
219        if (ctlBlk.goodModel_ == true &&
220            ctlBlk.bab_.majorStatus_ == CbcGenCtlBlk::BACNotRun)
221        { paramName = "branchAndCut" ;
222          pfx = "-" ; } } }
223    if (paramName == "")
224    { continue ; }
225/*
226  Do we have a parameter we recognise? In command line mode, if there was no
227  prefix (either `-' or `--'), the user didn't intend this as a command
228  keyword.
229*/
230    int matchNdx ;
231    if (!CoinParamUtils::isCommandLine() || pfx == "-" || pfx == "--")
232    { matchNdx = CoinParamUtils::lookupParam(paramName,paramVec) ; }
233    else
234    { matchNdx = -3 ; }
235    std::cout
236      << "Command is `" << paramName
237      << "', pfx `" << pfx
238      << "', match = " << matchNdx << std::endl ;
239/*
240  If matchNdx is positive, we have a unique parameter match and we can get on
241  with processing. If the return value is negative, and we're not
242  interactive, quit. If we're interactive, react as appropriate:
243    -1: There was a `?' in the command string. Prompt again.
244    -2: No `?', and one or more short matches. Prompt again.
245    -3: No `?', but we didn't match anything either. If we're in command line
246        mode, and there was no `-' or `--' prefix, try forcing `import' (but
247        just once, eh). This is the other piece required to get `cbc-generic
248        [parameters] foo.mps' to work as expected.) In interactive mode,
249        we'll require the user to say `import'.  Interactive mode and no
250        history of successful commands gets the help message.
251    -4: Configuration error, offer `report to maintainers' message.
252*/
253    if (matchNdx < 0)
254    { if (matchNdx == -3)
255      { if (CoinParamUtils::isCommandLine() && pfx == "")
256        { if (!forceImport)
257          { forceImportFile = paramName ;
258            paramName = "import" ;
259            matchNdx = CoinParamUtils::lookupParam(paramName,paramVec) ;
260            forceImport = true ; }
261          else
262          { std::cout << "No commands matched `" << paramName << "'."
263                << std::endl ; } }
264        else
265        { std::cout << "No commands matched `" << paramName << "'."
266              << std::endl ; 
267          if (ctlBlk.paramsProcessed_ == 0)
268          { std::cout
269              << "Type `?' or `help' for usage and command keywords."
270              << " Type `quit' to quit." << std::endl ; } } }
271      else
272      if (matchNdx == -4)
273      { std::cout
274          << "Please report this error by filing a ticket at "
275          << "https://projects.coin-or.org/Cbc/wiki."
276          << std::endl ; } }
277    if (matchNdx < 0)
278    { keepParsing = CoinParamUtils::isInteractive() ;
279      continue ; }
280    CoinParam *param = paramVec[matchNdx] ;
281    ctlBlk.paramsProcessed_++ ;
282/*
283  Depending on the type, we may need a parameter. For keyword parameters, check
284  that the keyword is recognised --- setKwdVal will quietly fail on a bad
285  keyword.
286*/
287    CoinParam::CoinParamType type = param->type() ;
288    int valid = 0 ;
289    switch (type)
290    { case CoinParam::coinParamAct:
291      { break ; }
292      case CoinParam::coinParamInt:
293      { int ival = CoinParamUtils::getIntField(argc,argv,&valid) ;
294        if (valid == 0)
295        { param->setIntVal(ival) ; }
296        break ; }
297      case CoinParam::coinParamDbl:
298      { double dval = CoinParamUtils::getDoubleField(argc,argv,&valid) ;
299        if (valid == 0)
300        { param->setDblVal(dval) ; }
301        break ; }
302      case CoinParam::coinParamStr:
303      { if (forceImport)
304        { param->setStrVal(forceImportFile) ; }
305        else
306        { const std::string tmp =
307                CoinParamUtils::getStringField(argc,argv,&valid) ;
308          if (valid == 0)
309          { param->setStrVal(tmp) ; } }
310        break ; }
311      case CoinParam::coinParamKwd:
312      { const std::string tmp =
313                CoinParamUtils::getStringField(argc,argv,&valid) ;
314        if (valid == 0)
315        { param->setKwdVal(tmp) ;
316          if (param->kwdVal() != tmp)
317          { std::cout
318              << "Unrecognised keyword `" << tmp << "' for parameter "
319              << param->name() << std::endl ;
320            param->printKwds() ;
321            std::cout << std::endl ;
322            valid = 1 ; } }
323        break ; }
324      default:
325      { assert (false) ;
326        break ; } }
327/*
328  Deal with missing or incorrect values.
329
330  If valid came back as 2, we're short a parameter. This is interpreted as a
331  request to tell the user the current value.  If valid came back as 1, we
332  had some sort of parse error. Print an error message.
333*/
334    if (valid != 0)
335    { switch (valid)
336      { case 1:
337        { std::cout
338            << "Could not parse the value given for parameter `"
339            << param->name() << "'." << std::endl ;
340          break ; }
341        case 2:
342        { std::cout
343            << "Current value of " << param->name() << " parameter is `"
344            << *param << "'." << std::endl ;
345          break ; }
346        default:
347        { std::cout
348            << "Parse status is " << valid
349            << "; this indicates internal confusion." << std::endl
350            << "Please report this error by filing a ticket at "
351            << "https://projects.coin-or.org/Cbc/wiki."
352            << std::endl ; } }
353      keepParsing = CoinParamUtils::isInteractive() ;
354      continue ; }
355/*
356  Ok, call the parameter's push function to do the heavy lifting. Push and pull
357  functions return 0 for success, 1 for non-fatal error, -1 for fatal error.
358*/
359    if (param->pushFunc() == 0)
360    { std::cout << "Parameter `" << param->name()
361                << "' is not implemented." << std::endl ; }
362    else
363    { int retval = (param->pushFunc())(param) ;
364      markAsSetByUser(ctlBlk,param) ;
365      if (retval < 0)
366      { keepParsing = false ; } } }
367/*
368  End of loop to parse and execute parameter actions. Time to do cleanup.
369  The destructor for CbcGenCtlBlk will delete anything with a non-null pointer,
370  so we need to be careful that the default solver is deleted only once.
371*/
372    ctlBlk.dfltSolver_ = 0 ;
373    CbcGenSolvers::deleteSolvers() ;
374    for (int i = 0 ; i < paramVec.size() ; i++)
375    { if (paramVec[i] != 0) delete paramVec[i] ; }
376  }
377/*
378  End of memory allocation block. There should be no allocated objects at
379  this point.
380*/
381  return (0) ;
382}   
Note: See TracBrowser for help on using the repository browser.