source: trunk/Cbc/src/CbcGenParamUtils.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: 48.9 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: CbcGenParamUtils.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 "CoinFileIO.hpp"
22
23#include "CoinFinite.hpp"
24#include "CoinParam.hpp"
25
26#include "CbcModel.hpp"
27
28#include "CbcGenParam.hpp"
29#include "CbcGenCtlBlk.hpp"
30
31/*! \file CbcGenParamUtils
32    \brief Implementation functions for CbcGenParam parameters.
33*/
34
35namespace {
36
37char svnid[] = "$Id: CbcGenParamUtils.cpp 2464 2019-01-03 19:03:23Z unxusr $";
38
39}
40
41namespace CbcGenSolvers {
42void setupSolverParam(CbcGenParam &solverParam);
43}
44
45namespace CbcGenParamUtils {
46
47/*
48  Function to add cbc-generic control parameters to the cbc-generic parameter
49  vector. Were needed, defaults are drawn from ctlBlk-> This function is a
50  friend of CbcGenCtlBlk.
51*/
52
53void addCbcGenParams(int &numberParameters, CoinParamVec &parameters,
54  CbcGenCtlBlk *ctlBlk)
55
56{
57  CbcGenParam *param;
58  std::string empty = "";
59
60  param = new CbcGenParam(CbcGenParam::GENERALQUERY, "?",
61    "Print a list of commands", false);
62  param->setPushFunc(doHelpParam);
63  param->setObj(ctlBlk);
64  parameters.push_back(param);
65
66  param = new CbcGenParam(CbcGenParam::FULLGENERALQUERY, "???",
67    "Print a list with *all* commands, even those hidden with `?'", false);
68  param->setPushFunc(doHelpParam);
69  param->setObj(ctlBlk);
70  parameters.push_back(param);
71
72  param = new CbcGenParam(CbcGenParam::PRINTVERSION,
73    "version", "Print version");
74  param->setPushFunc(doVersionParam);
75  param->setObj(ctlBlk);
76  parameters.push_back(param);
77
78  /*
79      Built into CoinParam parsing. No additional actions required. doNothingParam
80      simply prevents them from being reported as unimplemented.
81    */
82  param = new CbcGenParam(CbcGenParam::STDIN, "-",
83    "Switch to interactive command line mode", false);
84  param->setPushFunc(doNothingParam);
85  parameters.push_back(param);
86  param = new CbcGenParam(CbcGenParam::STDIN, "stdin",
87    "Switch to interactive command line mode", false);
88  param->setPushFunc(doNothingParam);
89  param->setObj(ctlBlk);
90  parameters.push_back(param);
91
92  param = new CbcGenParam(CbcGenParam::BAB,
93    "branch!AndCut", "Do Branch and Cut");
94  param->setPushFunc(doBaCParam);
95  param->setObj(ctlBlk);
96  param->setLongHelp(
97    "This does branch and cut. There are many parameters which can affect the performance.  First just try with default settings and look carefully at the log file.  Did cuts help?  Did they take too long?  Look at output to see which cuts were effective and then do some tuning.  You will see that the options for cuts are off, on, root and ifmove.  Off is obvious, on means that this cut generator will be tried in the branch and cut tree (you can fine tune using 'depth').  Root means just at the root node while 'ifmove' means that cuts will be used in the tree if they look as if they are doing some good and moving the objective value.  If pre-processing reduced the size of the problem or strengthened many coefficients then it is probably wise to leave it on.  Switch off heuristics which did not provide solutions.  The other major area to look at is the search.  Hopefully good solutions were obtained fairly early in the search so the important point is to select the best variable to branch on.  See whether strong branching did a good job - or did it just take a lot of iterations.  Adjust the strongBranching and trustPseudoCosts parameters.");
98  parameters.push_back(param);
99
100  param = new CbcGenParam(CbcGenParam::CPP,
101    "cpp!Generate", "Generates C++ code", -1, 50000);
102  param->setObj(ctlBlk);
103  param->setLongHelp(
104    "Once you like what the stand-alone solver does then this allows you to generate user_driver.cpp which approximates the code.  0 gives simplest driver, 1 generates saves and restores, 2 generates saves and restores even for variables at default value. 4 bit in cbc generates size dependent code rather than computed values.");
105  parameters.push_back(param);
106
107  param = new CbcGenParam(CbcGenParam::CLIQUECUTS,
108    "clique!Cuts", "Whether to use clique cuts", "off",
109    ctlBlk->clique_.action_);
110  param->appendKwd("on");
111  param->appendKwd("root");
112  param->appendKwd("ifmove");
113  param->appendKwd("forceOn");
114  param->setObj(ctlBlk);
115  param->setPushFunc(pushCbcGenCutParam);
116  param->setLongHelp(
117    "This switches on clique cuts (either at root or in entire tree). See branchAndCut for information on options.");
118  parameters.push_back(param);
119
120  param = new CbcGenParam(CbcGenParam::CUTDEPTH,
121    "cutD!epth", "Depth in tree at which to do cuts",
122    -1, 999999, ctlBlk->cutDepth_);
123  param->setObj(ctlBlk);
124  param->setPushFunc(pushCbcGenIntParam);
125  param->setLongHelp(
126    "Cut generators may be off, on only at the root, on if they look useful, and on at some interval.  If they are done every node then that is that, but it may be worth doing them every so often.  The original method was every so many nodes but it is more logical to do it whenever depth in tree is a multiple of K.  This option does that and defaults to -1 (off).");
127  parameters.push_back(param);
128
129  param = new CbcGenParam(CbcGenParam::CUTSTRATEGY,
130    "cuts!OnOff", "Switches all cuts on or off", "off", 0);
131  param->appendKwd("on");
132  param->appendKwd("root");
133  param->appendKwd("ifmove");
134  param->appendKwd("forceOn");
135  param->setObj(ctlBlk);
136  param->setLongHelp(
137    "This can be used to switch on or off all cuts (apart from Reduce and Split).  Then you can set individual ones off or on.  See branchAndCut for information on options.");
138  parameters.push_back(param);
139
140  param = new CbcGenParam(CbcGenParam::COMBINE,
141    "combine!Solutions",
142    "Whether to use combine solution heuristic", "off",
143    ctlBlk->combine_.action_);
144  param->appendKwd("on");
145  param->setObj(ctlBlk);
146  param->setLongHelp(
147    "This switches on a heuristic which does branch and cut on the problem given by just using variables which have appeared in one or more solutions. It is obviously only tried after two or more solutions.");
148  parameters.push_back(param);
149
150  param = new CbcGenParam(CbcGenParam::COSTSTRATEGY,
151    "cost!Strategy", "Whether to use costs or column order as priorities",
152    "off", 0);
153  param->appendKwd("pri!orities");
154  param->appendKwd("column!Order");
155  param->setObj(ctlBlk);
156  param->setLongHelp(
157    "This orders the variables in order of their absolute costs - with largest cost ones being branched on first.  This primitive strategy can be surprisingly effective.  The column order option is obviously not on costs but it's easy to implement.");
158  parameters.push_back(param);
159
160  param = new CbcGenParam(CbcGenParam::DEBUG,
161    "debug!In", "Read/write valid solution from/to file", "", false);
162  param->setObj(ctlBlk);
163  param->setPushFunc(doDebugParam);
164  param->setLongHelp(
165    "This will read a solution file from the given file name.  It will use the default directory given by 'directory'.  A name of '$' will use the previous value for the name.  This is initialized to '', i.e. it must be set.\n\nIf set to create it will create a file called debug.file after B&C search; if set to createAfterPre it will create the file before undoing preprocessing.\n\nThe idea is that if you suspect a bad cut generator and you did not use preprocessing you can do a good run with debug set to 'create' and then switch on the cuts you suspect and re-run with debug set to 'debug.file'  Similarly if you do use preprocessing, but use createAfterPre.  The create case has the same effect as saveSolution.");
166  parameters.push_back(param);
167
168  param = new CbcGenParam(CbcGenParam::DIRECTORY,
169    "directory", "Set Default directory for import etc.",
170    ctlBlk->dfltDirectory_);
171  param->setObj(ctlBlk);
172  param->setLongHelp(
173    "This sets the directory which import, export, saveModel, restoreModel etc. will use. It is initialized to the current directory.");
174  param->setPushFunc(pushCbcGenStrParam);
175  parameters.push_back(param);
176
177  param = new CbcGenParam(CbcGenParam::EXIT, "end", "Stops execution");
178  param->setPushFunc(doExitParam);
179  param->setObj(ctlBlk);
180  param->setLongHelp(
181    "This stops execution; end, exit, quit and stop are synonyms.");
182  parameters.push_back(param);
183
184  param = new CbcGenParam(CbcGenParam::ERRORSALLOWED,
185    "error!sAllowed", "Whether to allow import errors", "off", 0);
186  param->appendKwd("on");
187  param->setObj(ctlBlk);
188  param->setLongHelp(
189    "The default is not to use any model which had errors when reading the mps file.  Setting this to 'on' will allow all errors from which the code can recover simply by ignoring the error.  There are some errors from which the code can not recover, e.g., no ENDATA.  This has to be set before import, i.e., -errorsAllowed on -import xxxxxx.mps.");
190  parameters.push_back(param);
191
192  param = new CbcGenParam(CbcGenParam::EXIT, "exit", "Stops execution");
193  param->setPushFunc(doExitParam);
194  param->setObj(ctlBlk);
195  param->setLongHelp(
196    "This stops execution; end, exit, quit and stop are synonyms.");
197  parameters.push_back(param);
198
199  param = new CbcGenParam(CbcGenParam::EXPORT,
200    "export", "Export model as mps file",
201    std::string("default.mps"));
202  param->setObj(ctlBlk);
203  param->setLongHelp(
204    "This will write an MPS format file to the given file name.  It will use the default directory given by 'directory'.  A name of '$' will use the previous value for the name.  This is initialized to 'default.mps'.");
205  parameters.push_back(param);
206
207  param = new CbcGenParam(CbcGenParam::DJFIX, "fix!OnDj",
208    "Try heuristic that fixes variables based on reduced costs",
209    -1.0e20, 1.0e20, ctlBlk->djFix_.threshold_);
210  param->setPushFunc(pushCbcGenDblParam);
211  param->setObj(ctlBlk);
212  param->setLongHelp(
213    "If set, integer variables with reduced costs greater than the specified value will be fixed before branch and bound - use with extreme caution!");
214  parameters.push_back(param);
215
216  param = new CbcGenParam(CbcGenParam::FPUMP,
217    "feas!ibilityPump", "Whether to try Feasibility Pump", "off",
218    ctlBlk->fpump_.action_);
219  param->appendKwd("on");
220  param->setObj(ctlBlk);
221  param->setLongHelp(
222    "This switches on feasibility pump heuristic at root. This is due to Fischetti and Lodi and uses a sequence of LPs to try and get an integer feasible solution.  Some fine tuning is available by passFeasibilityPump.");
223  parameters.push_back(param);
224
225  param = new CbcGenParam(CbcGenParam::FPUMPITS,
226    "passF!easibilityPump", "How many passes in feasibility pump",
227    0, 10000, ctlBlk->fpump_.iters_);
228  param->setObj(ctlBlk);
229  param->setLongHelp(
230    "This fine tunes the Feasibility Pump heuristic by doing more or fewer passes.");
231  parameters.push_back(param);
232
233  param = new CbcGenParam(CbcGenParam::FLOWCUTS,
234    "flow!CoverCuts", "Whether to use Flow Cover cuts", "off",
235    ctlBlk->flow_.action_);
236  param->appendKwd("on");
237  param->appendKwd("root");
238  param->appendKwd("ifmove");
239  param->appendKwd("forceOn");
240  param->setObj(ctlBlk);
241  param->setPushFunc(pushCbcGenCutParam);
242  param->setLongHelp(
243    "This switches on flow cover cuts (either at root or in entire tree).  See branchAndCut for information on options.");
244  parameters.push_back(param);
245
246  param = new CbcGenParam(CbcGenParam::GOMORYCUTS, "gomory!Cuts",
247    "Whether to use Gomory cuts", "off",
248    ctlBlk->gomory_.action_);
249  param->appendKwd("on");
250  param->appendKwd("root");
251  param->appendKwd("ifmove");
252  param->appendKwd("forceOn");
253  param->setObj(ctlBlk);
254  param->setPushFunc(pushCbcGenCutParam);
255  param->setLongHelp(
256    "The original cuts - beware of imitations!  Having gone out of favor, they are now more fashionable as LP solvers are more robust and they interact well with other cuts.  They will almost always give cuts (although in this executable they are limited as to number of variables in cut).  However the cuts may be dense so it is worth experimenting. See branchAndCut for information on options.");
257  parameters.push_back(param);
258
259  param = new CbcGenParam(CbcGenParam::GREEDY,
260    "greedy!Heuristic", "Whether to use a greedy heuristic", "off",
261    ctlBlk->greedyCover_.action_);
262  param->appendKwd("on");
263  param->appendKwd("root");
264  param->setObj(ctlBlk);
265  param->setLongHelp(
266    "Switches on a pair of greedy heuristic which will try and obtain a solution.  It may just fix a percentage of variables and then try a small branch and cut run.");
267  parameters.push_back(param);
268
269  param = new CbcGenParam(CbcGenParam::HEURISTICSTRATEGY,
270    "heur!isticsOnOff", "Switches most heuristics on or off", "off", 0);
271  param->appendKwd("on");
272  param->setObj(ctlBlk);
273  param->setLongHelp(
274    "This can be used to switch on or off all heuristics.  Then you can set individual ones off or on.  CbcTreeLocal is not included as it dramatically alters search.");
275  parameters.push_back(param);
276
277  param = new CbcGenParam(CbcGenParam::KNAPSACKCUTS,
278    "knapsack!Cuts", "Whether to use Knapsack cuts", "off",
279    ctlBlk->knapsack_.action_);
280  param->appendKwd("on");
281  param->appendKwd("root");
282  param->appendKwd("ifmove");
283  param->appendKwd("forceOn");
284  param->setObj(ctlBlk);
285  param->setPushFunc(pushCbcGenCutParam);
286  param->setLongHelp(
287    "This switches on knapsack cuts (either at root or in entire tree). See branchAndCut for information on options.");
288  parameters.push_back(param);
289
290  /*
291      param = new CbcGenParam(CbcGenParam::LANDPCUTS,
292        "lift!AndProjectCuts","Whether to use lift-and-project cuts","off",
293        ctlBlk->landp_.action_) ;
294      param->appendKwd("on") ;
295      param->appendKwd("root") ;
296      param->appendKwd("ifmove") ;
297      param->appendKwd("forceOn") ;
298      param->setObj(ctlBlk) ;
299      param->setLongHelp(
300        "This switches on lift-and-project cuts (either at root or in entire tree). See branchAndCut for information on options."
301        ) ;
302      parameters.push_back(param) ;
303    */
304
305  param = new CbcGenParam(CbcGenParam::LOCALTREE,
306    "local!TreeSearch", "Whether to use local tree search", "off",
307    ctlBlk->localTree_.action_);
308  param->appendKwd("on");
309  param->setObj(ctlBlk);
310  param->setLongHelp(
311    "This switches on a local search algorithm when a solution is found.  This is from Fischetti and Lodi and is not really a heuristic although it can be used as one. When used from this program it has limited functionality.  It is not controlled by heuristicsOnOff.");
312  parameters.push_back(param);
313
314  param = new CbcGenParam(CbcGenParam::LOGLEVEL,
315    "log!Level", "Level of detail in cbc-generic output.",
316    -1, 999999, ctlBlk->logLvl_);
317  param->setObj(ctlBlk);
318  param->setPushFunc(pushCbcGenIntParam);
319  param->setLongHelp(
320    "If set to 0 then there should be no output in normal circumstances. A value of 1 is probably the best value for most uses, while 2 and 3 give more information.");
321  parameters.push_back(param);
322
323  param = new CbcGenParam(CbcGenParam::MIXEDCUTS,
324    "mixed!IntegerRoundingCuts",
325    "Whether to use Mixed Integer Rounding cuts", "off",
326    ctlBlk->mir_.action_);
327  param->appendKwd("on");
328  param->appendKwd("root");
329  param->appendKwd("ifmove");
330  param->appendKwd("forceOn");
331  param->setObj(ctlBlk);
332  param->setPushFunc(pushCbcGenCutParam);
333  param->setLongHelp(
334    "This switches on mixed integer rounding cuts (either at root or in entire tree).  See branchAndCut for information on options.");
335  parameters.push_back(param);
336
337  param = new CbcGenParam(CbcGenParam::USESOLUTION,
338    "force!Solution",
339    "Whether to use given solution as crash for BAB", "off", 0);
340  param->appendKwd("on");
341  param->setObj(ctlBlk);
342  param->setLongHelp(
343    "If on then tries to branch to solution given by AMPL or priorities file.");
344  parameters.push_back(param);
345
346  param = new CbcGenParam(CbcGenParam::HELP, "help",
347    "Print out version, non-standard options and some help");
348  param->setPushFunc(doHelpParam);
349  param->setObj(ctlBlk);
350  param->setLongHelp(
351    "This prints out some help to get a user started. If you're seeing this message, you should be past that stage.");
352  parameters.push_back(param);
353
354  param = new CbcGenParam(CbcGenParam::IMPORT,
355    "import", "Import model from mps file",
356    ctlBlk->lastMpsIn_);
357  param->setPushFunc(doImportParam);
358  param->setObj(ctlBlk);
359  param->setLongHelp(
360    "This will read an MPS format file from the given file name.  It will use the default directory given by 'directory'.  A name of '$' will use the previous value for the name.  This is initialized to '', i.e., it must be set.  If you have libgz then it can read compressed files 'xxxxxxxx.gz'.");
361  parameters.push_back(param);
362
363  param = new CbcGenParam(CbcGenParam::SOLVECONTINUOUS, "initialS!olve",
364    "Solve to continuous optimum");
365  param->setObj(ctlBlk);
366  param->setLongHelp(
367    "This just solves the problem to the continuous optimum, without adding any cuts.");
368  parameters.push_back(param);
369
370  param = new CbcGenParam(CbcGenParam::MESSAGES, "mess!ages",
371    "Controls whether standardised message prefix is printed", "off", 0);
372  param->appendKwd("on");
373  param->setObj(ctlBlk);
374  param->setLongHelp(
375    "By default, messages have a standard prefix, such as:\n   Clp0005 2261  Objective 109.024 Primal infeas 944413 (758)\nbut this program turns this off to make it look more friendly.  It can be useful to turn them back on if you want to be able to 'grep' for particular messages or if you intend to override the behavior of a particular message.");
376  parameters.push_back(param);
377
378  param = new CbcGenParam(CbcGenParam::MIPLIB,
379    "miplib", "Do some of miplib test set");
380  parameters.push_back(param);
381
382  param = new CbcGenParam(CbcGenParam::OUTDUPROWS,
383    "outDup!licates",
384    "Takes duplicate rows, etc., out of the integer model", false);
385  parameters.push_back(param);
386
387  param = new CbcGenParam(CbcGenParam::OUTPUTFORMAT,
388    "output!Format", "Which output format to use", 1, 6, 2);
389  param->setObj(ctlBlk);
390  param->setLongHelp(
391    "Normally export will be done using normal representation for numbers and two values per line.  You may want to do just one per line (for grep or suchlike) and you may wish to save with absolute accuracy using a coded version of the IEEE value. A value of 2 is normal. Otherwise, odd values give one value per line, even values two.  Values of 1 and 2 give normal format, 3 and 4 give greater precision, 5 and 6 give IEEE values.  When exporting a basis, 1 does not save values, 2 saves values, 3 saves with greater accuracy and 4 saves in IEEE format.");
392  parameters.push_back(param);
393
394  /*
395      In order for initialisation to work properly, the order of the keywords here
396      must match the order of the enum IPPControl in CbcGenCtlBlk.hpp.
397    */
398  param = new CbcGenParam(CbcGenParam::PREPROCESS,
399    "preprocess", "Whether to use integer preprocessing", "off",
400    ctlBlk->preProcess_);
401  param->appendKwd("on");
402  param->appendKwd("save");
403  param->appendKwd("equal");
404  param->appendKwd("sos");
405  param->appendKwd("trysos");
406  param->appendKwd("equalall");
407  param->appendKwd("strategy");
408  param->setObj(ctlBlk);
409  param->setPushFunc(pushCbcGenKwdParam);
410  param->setLongHelp(
411    "This tries to reduce size of the model in a similar way to presolve and it also tries to strengthen the model. This can be very useful and is worth trying.  save option saves on file presolved.mps.  equal will turn <= cliques into ==.  sos will create sos sets if all 0-1 in sets (well one extra is allowed) and no overlaps.  trysos is same but allows any number extra. equalall will turn all valid inequalities into equalities with integer slacks. strategy is as on but uses CbcStrategy.");
412  parameters.push_back(param);
413
414  param = new CbcGenParam(CbcGenParam::PRINTOPTIONS,
415    "pO!ptions", "Dubious print options", 0, COIN_INT_MAX, 0, false);
416  param->setObj(ctlBlk);
417  param->setPushFunc(pushCbcGenIntParam);
418  param->setLongHelp(
419    "If this is greater than 0 then presolve will give more information and branch and cut will give statistics");
420  parameters.push_back(param);
421
422  param = new CbcGenParam(CbcGenParam::PROBINGCUTS,
423    "probing!Cuts", "Whether to use Probing cuts", "off",
424    ctlBlk->probing_.action_);
425  param->appendKwd("on");
426  param->appendKwd("root");
427  param->appendKwd("ifmove");
428  param->appendKwd("forceOn");
429  param->appendKwd("forceOnBut");
430  param->setObj(ctlBlk);
431  param->setPushFunc(pushCbcGenCutParam);
432  param->setLongHelp(
433    "This switches on probing cuts (either at root or in entire tree). See branchAndCut for information on options.");
434  parameters.push_back(param);
435
436  param = new CbcGenParam(CbcGenParam::INTPRINT,
437    "printi!ngOptions", "Print options", "normal", 0);
438  param->appendKwd("integer");
439  param->appendKwd("special");
440  param->appendKwd("rows");
441  param->appendKwd("all");
442  param->setPushFunc(pushCbcGenKwdParam);
443  param->setObj(ctlBlk);
444  param->setLongHelp(
445    "This changes the amount and format of printing a solution:\nnormal - nonzero column variables \ninteger - nonzero integer column variables\nspecial - in format suitable for OsiRowCutDebugger\nrows - nonzero column variables and row activities\nall - all column variables and row activities.\n\nFor non-integer problems 'integer' and 'special' act like 'normal'.  Also see printMask for controlling output.");
446  parameters.push_back(param);
447
448  param = new CbcGenParam(CbcGenParam::PRINTMASK,
449    "printM!ask",
450    "Control printing of solution with a regular expression", empty);
451  param->setPushFunc(doPrintMaskParam);
452  param->setObj(ctlBlk);
453  param->setLongHelp(
454    "If set then only those names which match mask are printed in a solution. '?' matches any character and '*' matches any set of characters.  The default is '' (unset) so all variables are printed. This is only active if model has names.");
455  parameters.push_back(param);
456
457  param = new CbcGenParam(CbcGenParam::PRIORITYIN,
458    "prio!rityIn", "Import priorities etc from file", empty);
459  param->setObj(ctlBlk);
460  param->setLongHelp(
461    "This will read a file with priorities from the given file name.  It will use the default directory given by 'directory'.  A name of '$' will use the previous value for the name.  This is initialized to '', i.e. it must be set.  This can not read from compressed files. File is in csv format with allowed headings - name, number, priority, direction, up, down, solution.  Exactly one of name and number must be given.");
462  parameters.push_back(param);
463
464  param = new CbcGenParam(CbcGenParam::REDSPLITCUTS,
465    "reduce!AndSplitCuts",
466    "Whether to use Reduce-and-Split cuts", "off",
467    ctlBlk->redSplit_.action_);
468  param->appendKwd("on");
469  param->appendKwd("root");
470  param->appendKwd("ifmove");
471  param->appendKwd("forceOn");
472  param->setObj(ctlBlk);
473  param->setPushFunc(pushCbcGenCutParam);
474  param->setLongHelp(
475    "This switches on reduce and split cuts (either at root or in entire tree). See branchAndCut for information on options.");
476  parameters.push_back(param);
477
478  param = new CbcGenParam(CbcGenParam::ROUNDING,
479    "round!ingHeuristic", "Whether to use Rounding heuristic", "off",
480    ctlBlk->rounding_.action_);
481  param->appendKwd("on");
482  param->setObj(ctlBlk);
483  param->setLongHelp(
484    "This switches on a simple (but effective) rounding heuristic at each node of tree.");
485  parameters.push_back(param);
486
487  param = new CbcGenParam(CbcGenParam::EXIT, "quit", "Stops execution");
488  param->setPushFunc(doExitParam);
489  param->setObj(ctlBlk);
490  param->setLongHelp(
491    "This stops execution; end, exit, quit and stop are synonyms.");
492  parameters.push_back(param);
493
494  param = new CbcGenParam(CbcGenParam::DUMMY,
495    "sleep", "for debug", 0, 9999, 0, false);
496  param->setObj(ctlBlk);
497  param->setLongHelp(
498    "If passed to solver from ampl, then ampl will wait so that you can copy .nl file for debug.");
499  parameters.push_back(param);
500
501  param = new CbcGenParam(CbcGenParam::SOLUTION,
502    "solu!tion", "Prints solution to file",
503    std::string("stdout"));
504  param->setPushFunc(doSolutionParam);
505  param->setObj(ctlBlk);
506  param->setLongHelp(
507    "This will write a primitive solution file to the given file name.  It will use the default directory given by 'directory'.  A name of '$' will use the previous value for the name.  This is initialized to 'stdout'.  The amount of output can be varied using printi!ngOptions or printMask.");
508  parameters.push_back(param);
509
510  param = new CbcGenParam;
511  CbcGenSolvers::setupSolverParam(*param);
512  param->setObj(ctlBlk);
513  parameters.push_back(param);
514
515  param = new CbcGenParam(CbcGenParam::SOS,
516    "sos!Options", "Whether to use SOS from AMPL", "off", 0);
517  param->appendKwd("on");
518  param->setObj(ctlBlk);
519  param->setLongHelp(
520    "Normally if AMPL says there are SOS variables they should be used, but sometimes they should be turned off - this does so.");
521  parameters.push_back(param);
522
523  param = new CbcGenParam(CbcGenParam::EXIT, "stop", "Stops execution");
524  param->setPushFunc(doExitParam);
525  param->setObj(ctlBlk);
526  param->setLongHelp(
527    "This stops execution; end, exit, quit and stop are synonyms.");
528  parameters.push_back(param);
529
530  param = new CbcGenParam(CbcGenParam::STRENGTHEN,
531    "strengthen", "Create strengthened problem");
532  param->setObj(ctlBlk);
533  param->setLongHelp(
534    "This creates a new problem by applying the root node cuts. All tight constraints will be in resulting problem.");
535  parameters.push_back(param);
536
537  param = new CbcGenParam(CbcGenParam::TIGHTENFACTOR, "tighten!Factor",
538    "Tighten bounds using value times largest activity at continuous solution",
539    1.0, 1.0e20);
540  param->setObj(ctlBlk);
541  param->setLongHelp(
542    "This sleazy trick can help on some problems.");
543  parameters.push_back(param);
544
545  param = new CbcGenParam(CbcGenParam::TWOMIRCUTS,
546    "two!MirCuts",
547    "Whether to use Two phase Mixed Integer Rounding cuts", "off",
548    ctlBlk->twomir_.action_);
549  param->appendKwd("on");
550  param->appendKwd("root");
551  param->appendKwd("ifmove");
552  param->appendKwd("forceOn");
553  param->setObj(ctlBlk);
554  param->setPushFunc(pushCbcGenCutParam);
555  param->setLongHelp(
556    "This switches on two phase mixed integer rounding cuts (either at root or in entire tree). See branchAndCut for information on options.");
557  parameters.push_back(param);
558
559  param = new CbcGenParam(CbcGenParam::UNITTEST, "unitTest", "Do unit test");
560  param->setObj(ctlBlk);
561  param->setLongHelp(
562    "This exercises the unit test.");
563  parameters.push_back(param);
564
565  param = new CbcGenParam(CbcGenParam::USERCBC,
566    "userCbc", "Hand coded Cbc stuff", 0, COIN_INT_MAX, 0, false);
567  param->setObj(ctlBlk);
568  param->setLongHelp(
569    "There are times (e.g., when using AMPL interface) when you may wish to do something unusual.  Look for USERCBC in main driver and modify sample code.");
570  parameters.push_back(param);
571
572  param = new CbcGenParam(CbcGenParam::VERBOSE,
573    "verbose", "Switches on longer help on single ?",
574    0, 15, ctlBlk->verbose_, false);
575  param->setPushFunc(pushCbcGenIntParam);
576  param->setObj(ctlBlk);
577  param->setLongHelp(
578    "Set to 1 to get short help with ? list, 2 to get long help.");
579  parameters.push_back(param);
580
581  param = new CbcGenParam(CbcGenParam::SHOWUNIMP,
582    "unimp!lemented", "Report unimplemented commands.", false);
583  param->setPushFunc(doUnimplementedParam);
584  param->setObj(ctlBlk);
585  parameters.push_back(param);
586
587  numberParameters = parameters.size();
588  assert(((unsigned)numberParameters) <= parameters.capacity());
589
590  return;
591}
592
593void loadGenParamObj(const CoinParamVec paramVec, int first, int last,
594  CbcGenCtlBlk *ctlBlk)
595
596{
597  int i;
598  /*
599      Load the cbc-generic object into the parameters
600    */
601  for (i = first; i <= last; i++) {
602    CbcGenParam *genParam = dynamic_cast<CbcGenParam *>(paramVec[i]);
603    assert(genParam != 0);
604    genParam->setObj(ctlBlk);
605  }
606
607  return;
608}
609
610/* Functions to implement  cbc-generic (CbcGenParam) parameters */
611
612/*
613  Maintainer's utility, scan the parameters and report the ones that are
614  unimplemented (i.e., have no pushFunc).
615*/
616
617int doUnimplementedParam(CoinParam *param)
618
619{
620  assert(param != 0);
621
622  CbcGenParam *genParam = dynamic_cast<CbcGenParam *>(param);
623  assert(genParam != 0);
624
625  CbcGenCtlBlk *ctlBlk = genParam->obj();
626  assert(ctlBlk != 0);
627
628  assert(ctlBlk->paramVec_ != 0);
629  CoinParamVec &paramVec = *ctlBlk->paramVec_;
630
631  int unimpCnt = 0;
632  int maxAcross = 5;
633  for (unsigned i = 0; i < paramVec.size(); i++) {
634    CoinParam *param = paramVec[i];
635    if (param->pushFunc() == 0) {
636      if (unimpCnt % maxAcross == 0) {
637        std::cout << std::endl;
638      } else {
639        std::cout << " ";
640      }
641      std::cout << param->name();
642      unimpCnt++;
643    }
644  }
645  if (unimpCnt % maxAcross != 1) {
646    std::cout << std::endl;
647  }
648  std::cout << unimpCnt << " unimplemented parameters." << std::endl;
649
650  return (0);
651}
652
653/*
654  Noop function. Mainly to eliminate commands from the list returned by
655  doUnimplmentedParam.
656*/
657
658int doNothingParam(CoinParam *param)
659{
660  return (0);
661}
662
663/*
664  Function to terminate command parsing by returning -1.
665*/
666
667int doExitParam(CoinParam *param)
668
669{
670  return (-1);
671}
672
673/*
674  Function to print the current version.
675*/
676
677int doVersionParam(CoinParam *param)
678
679{
680  assert(param != 0);
681  CbcGenParam *genParam = dynamic_cast<CbcGenParam *>(param);
682  assert(genParam != 0);
683  CbcGenCtlBlk *ctlBlk = genParam->obj();
684  assert(ctlBlk != 0);
685
686  std::cout << "cbc-generic version " << ctlBlk->version_ << std::endl;
687  std::cout
688    << "cbc-generic is experimental software. If you want a stable MIP "
689    << "solver, please" << std::endl
690    << "use cbc. If you discover bugs while using cbc-generic "
691    << "please specify" << std::endl
692    << "cbc-generic in the ticket description or email subject line."
693    << std::endl;
694
695  return (0);
696}
697
698/*
699  Function to handle help (HELP), `?' (GENERALQUERY), and `???'
700  (FULLGENERALQUERY).
701*/
702
703int doHelpParam(CoinParam *param)
704
705{
706  assert(param != 0);
707  CbcGenParam *genParam = dynamic_cast<CbcGenParam *>(param);
708  assert(genParam != 0);
709  CbcGenCtlBlk *ctlBlk = genParam->obj();
710  assert(ctlBlk != 0);
711
712  CbcGenParam::CbcGenParamCode code = genParam->paramCode();
713
714  int verbose = ctlBlk->verbose_;
715  bool shortHelp = ((verbose & 0x01) ? true : false);
716  bool longHelp = ((verbose & 0x02) ? true : false);
717  bool hidden = ((verbose & 0x08) ? true : false);
718
719  CoinParamVec *paramVec = ctlBlk->paramVec_;
720  assert(paramVec != 0);
721  /*
722      Tune up the initial settings. FULLGENERALQUERY will print normally hidden
723      params, and a request for long help overrules a request for short help.
724    */
725  if (code == CbcGenParam::FULLGENERALQUERY) {
726    hidden = true;
727  }
728  if (longHelp) {
729    shortHelp = false;
730  }
731
732  CoinParamUtils::printGenericHelp();
733
734  std::cout << "\nAvailable commands are:";
735  std::string pfx("  ");
736  CoinParamUtils::printHelp(*paramVec, 0, paramVec->size() - 1, pfx,
737    shortHelp, longHelp, hidden);
738
739  return (0);
740}
741
742/*
743  Function to push a double-valued parameter.
744*/
745
746int pushCbcGenDblParam(CoinParam *param)
747
748{
749  assert(param != 0);
750  CbcGenParam *genParam = dynamic_cast<CbcGenParam *>(param);
751  assert(genParam != 0);
752  CbcGenCtlBlk *ctlBlk = genParam->obj();
753  assert(ctlBlk != 0);
754
755  double val = genParam->dblVal();
756  CbcGenParam::CbcGenParamCode code = genParam->paramCode();
757
758  int retval = 0;
759  /*
760      Figure out what we're doing and set the relevant field.
761    */
762  switch (code) {
763  case CbcGenParam::DJFIX: {
764    ctlBlk->djFix_.action_ = true;
765    ctlBlk->djFix_.threshold_ = val;
766    break;
767  }
768  default: {
769    std::cerr << "pushCbcGenDbl: no equivalent CbcGenCtlBlk field for "
770              << "parameter code `" << code << "'." << std::endl;
771    retval = -1;
772    break;
773  }
774  }
775
776  return (retval);
777}
778
779/*
780  Function to push an integer-valued parameter.
781*/
782
783int pushCbcGenIntParam(CoinParam *param)
784
785{
786  assert(param != 0);
787  CbcGenParam *genParam = dynamic_cast<CbcGenParam *>(param);
788  assert(genParam != 0);
789  CbcGenCtlBlk *ctlBlk = genParam->obj();
790  assert(ctlBlk != 0);
791
792  int val = genParam->intVal();
793  CbcGenParam::CbcGenParamCode code = genParam->paramCode();
794
795  int retval = 0;
796  /*
797      Figure out what we're doing and set the relevant field.
798    */
799  switch (code) {
800  case CbcGenParam::CUTDEPTH: {
801    ctlBlk->setCutDepth(val);
802    break;
803  }
804  case CbcGenParam::LOGLEVEL: {
805    ctlBlk->setLogLevel(val);
806    break;
807  }
808  case CbcGenParam::PRINTOPTIONS: {
809    ctlBlk->printOpt_ = val;
810    break;
811  }
812  case CbcGenParam::VERBOSE: {
813    ctlBlk->verbose_ = val;
814    break;
815  }
816  default: {
817    std::cerr << "pushCbcGenInt: no equivalent CbcGenCtlBlk field for "
818              << "parameter code `" << code << "'." << std::endl;
819    retval = -1;
820    break;
821  }
822  }
823
824  return (retval);
825}
826
827/*
828  Function to push a keyword-valued parameter. This is the catch-all function
829  for keyword parameters that don't belong to any other useful grouping.
830*/
831
832int pushCbcGenKwdParam(CoinParam *param)
833
834{
835  assert(param != 0);
836  CbcGenParam *genParam = dynamic_cast<CbcGenParam *>(param);
837  assert(genParam != 0);
838  CbcGenCtlBlk *ctlBlk = genParam->obj();
839  assert(ctlBlk != 0);
840
841  std::string str = genParam->kwdVal();
842  CbcGenParam::CbcGenParamCode code = genParam->paramCode();
843
844  int retval = 0;
845  /*
846      Figure out what we're doing and set the relevant field.
847    */
848  switch (code) {
849  case CbcGenParam::PREPROCESS: {
850    if (str == "off") {
851      ctlBlk->setIPPAction(CbcGenCtlBlk::IPPOff);
852    } else if (str == "on") {
853      ctlBlk->setIPPAction(CbcGenCtlBlk::IPPOn);
854    } else if (str == "save") {
855      ctlBlk->setIPPAction(CbcGenCtlBlk::IPPSave);
856    } else if (str == "equal") {
857      ctlBlk->setIPPAction(CbcGenCtlBlk::IPPEqual);
858    } else if (str == "sos") {
859      ctlBlk->setIPPAction(CbcGenCtlBlk::IPPSOS);
860    } else if (str == "trysos") {
861      ctlBlk->setIPPAction(CbcGenCtlBlk::IPPTrySOS);
862    } else if (str == "equalall") {
863      ctlBlk->setIPPAction(CbcGenCtlBlk::IPPEqualAll);
864    } else if (str == "strategy") {
865      ctlBlk->setIPPAction(CbcGenCtlBlk::IPPStrategy);
866    } else {
867      std::cerr
868        << "pushCbcGenKwdParam(PREPROCESS): unrecognised keyword `"
869        << str << "'." << std::endl;
870      retval = -1;
871    }
872    break;
873  }
874  case CbcGenParam::COSTSTRATEGY: {
875    if (str == "off") {
876      ctlBlk->priorityAction_ = CbcGenCtlBlk::BPOff;
877    } else if (str == "priorities") {
878      ctlBlk->priorityAction_ = CbcGenCtlBlk::BPCost;
879    }
880    if (str == "columnOrder") {
881      ctlBlk->priorityAction_ = CbcGenCtlBlk::BPOrder;
882    } else {
883      std::cerr
884        << "pushCbcGenKwdParam(COSTSTRATEGY): unrecognised keyword `"
885        << str << "'." << std::endl;
886      retval = -1;
887    }
888    break;
889  }
890  case CbcGenParam::INTPRINT: {
891    if (str == "normal") {
892      ctlBlk->printMode_ = 0;
893    } else if (str == "integer") {
894      ctlBlk->printMode_ = 1;
895    } else if (str == "special") {
896      ctlBlk->printMode_ = 2;
897    } else if (str == "rows") {
898      ctlBlk->printMode_ = 3;
899    } else if (str == "all") {
900      ctlBlk->printMode_ = 4;
901    } else {
902      std::cerr
903        << "pushCbcGenKwdParam(INTPRINT): unrecognised keyword `"
904        << str << "'." << std::endl;
905      retval = -1;
906    }
907    break;
908  }
909  default: {
910    std::cerr
911      << "pushCbcGenKwdParam: unrecognised parameter code `"
912      << code << "'." << std::endl;
913    retval = -1;
914    break;
915  }
916  }
917
918  return (retval);
919}
920
921/*
922  Function to push a string-valued parameter
923*/
924
925int pushCbcGenStrParam(CoinParam *param)
926
927{
928  assert(param != 0);
929  CbcGenParam *genParam = dynamic_cast<CbcGenParam *>(param);
930  assert(genParam != 0);
931  CbcGenCtlBlk *ctlBlk = genParam->obj();
932  assert(ctlBlk != 0);
933
934  std::string str = genParam->strVal();
935  CbcGenParam::CbcGenParamCode code = genParam->paramCode();
936
937  int retval = 0;
938  /*
939      Figure out what we're doing and set the relevant field.
940    */
941  switch (code) {
942  case CbcGenParam::DIRECTORY: {
943    char dirSep = CoinFindDirSeparator();
944    if (str[str.length() - 1] != dirSep) {
945      str += dirSep;
946    }
947    ctlBlk->dfltDirectory_ = str;
948    break;
949  }
950  default: {
951    std::cerr << "pushCbcGenStr: no equivalent CbcGenCtlBlk field for "
952              << "parameter code `" << code << "'." << std::endl;
953    retval = -1;
954    break;
955  }
956  }
957
958  return (retval);
959}
960
961/*
962  The various parameters to control cut generators can be
963  grouped, as they all use the same set of keywords.
964*/
965int pushCbcGenCutParam(CoinParam *param)
966
967{
968  assert(param != 0);
969  CbcGenParam *genParam = dynamic_cast<CbcGenParam *>(param);
970  assert(genParam != 0);
971  CbcGenCtlBlk *ctlBlk = genParam->obj();
972  assert(ctlBlk != 0);
973
974  std::string str = genParam->kwdVal();
975  CbcGenParam::CbcGenParamCode code = genParam->paramCode();
976  /*
977      Setup to return nonfatal/fatal error (1/-1) by default, so that all we need
978      to do is correct to 0 (no error) if we're successful.
979    */
980  int retval;
981  if (CoinParamUtils::isInteractive()) {
982    retval = 1;
983  } else {
984    retval = -1;
985  }
986  /*
987      First translate the keyword into the correct CGControl enum value.
988    */
989  CbcGenCtlBlk::CGControl action;
990  if (str == "off") {
991    action = CbcGenCtlBlk::CGOff;
992  } else if (str == "on") {
993    action = CbcGenCtlBlk::CGOn;
994  } else if (str == "root") {
995    action = CbcGenCtlBlk::CGRoot;
996  } else if (str == "ifmove") {
997    action = CbcGenCtlBlk::CGIfMove;
998  } else if (str == "forceOn") {
999    action = CbcGenCtlBlk::CGForceOn;
1000  } else if (str == "forceOnBut") {
1001    action = CbcGenCtlBlk::CGForceBut;
1002  } else {
1003    std::cerr
1004      << "pushCbcGenCutParam: unrecognised keyword `"
1005      << str << "'." << std::endl;
1006    return (retval);
1007  }
1008  /*
1009      Validate the parameter code and set a variable to separate cuts from
1010      heuristics.
1011    */
1012  bool isCut = false;
1013  bool isHeuristic = false;
1014  switch (code) {
1015  case CbcGenParam::CLIQUECUTS:
1016  case CbcGenParam::FLOWCUTS:
1017  case CbcGenParam::GOMORYCUTS:
1018  case CbcGenParam::KNAPSACKCUTS:
1019  case CbcGenParam::MIXEDCUTS:
1020  case CbcGenParam::PROBINGCUTS:
1021  case CbcGenParam::REDSPLITCUTS:
1022  case CbcGenParam::TWOMIRCUTS:
1023  case CbcGenParam::CUTSTRATEGY: {
1024    isCut = true;
1025    break;
1026  }
1027  case CbcGenParam::COMBINE:
1028  case CbcGenParam::FPUMP:
1029  case CbcGenParam::ROUNDING:
1030  case CbcGenParam::LOCALTREE:
1031  case CbcGenParam::HEURISTICSTRATEGY: {
1032    isHeuristic = true;
1033    break;
1034  }
1035  default: {
1036    std::cerr
1037      << "pushCbcGenCutParam: unrecognised parameter code `"
1038      << code << "'." << std::endl;
1039    return (retval);
1040  }
1041  }
1042  /*
1043      See if the action is valid for the specified type. Heuristics are on or
1044      off; cuts can be any of the other codes. Only probing can use forceOnBut.
1045    */
1046  if (isHeuristic) {
1047    if (!(action == CbcGenCtlBlk::CGOff || action == CbcGenCtlBlk::CGOn)) {
1048      std::cerr
1049        << "pushCbcGenCutParam: only on or off is valid for a heuristic."
1050        << std::endl;
1051      return (retval);
1052    }
1053  } else if (isCut) {
1054    if (action == CbcGenCtlBlk::CGForceBut && code != CbcGenParam::PROBINGCUTS) {
1055      std::cerr
1056        << "pushCbcGenCutParam: forceOnBut is valid only for probing."
1057        << std::endl;
1058      return (retval);
1059    }
1060  }
1061  /*
1062      We've done the basic checks; go ahead and set the relevant field in the
1063      control block. We shouldn't need the default case, but some compilers will
1064      complain if it's missing.
1065    */
1066  switch (code) {
1067  case CbcGenParam::CLIQUECUTS: {
1068    ctlBlk->setCliqueAction(action);
1069    break;
1070  }
1071  case CbcGenParam::FLOWCUTS: {
1072    ctlBlk->setFlowAction(action);
1073    break;
1074  }
1075  case CbcGenParam::GOMORYCUTS: {
1076    ctlBlk->setGomoryAction(action);
1077    break;
1078  }
1079  case CbcGenParam::KNAPSACKCUTS: {
1080    ctlBlk->setKnapsackAction(action);
1081    break;
1082  }
1083  case CbcGenParam::MIXEDCUTS: {
1084    ctlBlk->setMirAction(action);
1085    break;
1086  }
1087  case CbcGenParam::PROBINGCUTS: {
1088    ctlBlk->setProbingAction(action);
1089    break;
1090  }
1091  case CbcGenParam::REDSPLITCUTS: {
1092    ctlBlk->setRedSplitAction(action);
1093    break;
1094  }
1095  case CbcGenParam::TWOMIRCUTS: {
1096    ctlBlk->setTwomirAction(action);
1097    break;
1098  }
1099  case CbcGenParam::CUTSTRATEGY: {
1100    ctlBlk->setCliqueAction(action);
1101    ctlBlk->setFlowAction(action);
1102    ctlBlk->setGomoryAction(action);
1103    ctlBlk->setKnapsackAction(action);
1104    ctlBlk->setMirAction(action);
1105    ctlBlk->setProbingAction(action);
1106    ctlBlk->setRedSplitAction(action);
1107    ctlBlk->setTwomirAction(action);
1108    break;
1109  }
1110  case CbcGenParam::COMBINE: {
1111    ctlBlk->setCombineAction(action);
1112    break;
1113  }
1114  case CbcGenParam::FPUMP: {
1115    ctlBlk->setFPumpAction(action);
1116    break;
1117  }
1118  case CbcGenParam::GREEDY: {
1119    ctlBlk->setGreedyCoverAction(action);
1120    ctlBlk->setGreedyEqualityAction(action);
1121    break;
1122  }
1123  case CbcGenParam::LOCALTREE: {
1124    ctlBlk->setTreeLocalAction(action);
1125    break;
1126  }
1127  case CbcGenParam::ROUNDING: {
1128    ctlBlk->setRoundingAction(action);
1129    break;
1130  }
1131  case CbcGenParam::HEURISTICSTRATEGY: {
1132    ctlBlk->setCombineAction(action);
1133    ctlBlk->setFPumpAction(action);
1134    ctlBlk->setGreedyCoverAction(action);
1135    ctlBlk->setRoundingAction(action);
1136    ctlBlk->setTreeLocalAction(action);
1137    break;
1138  }
1139  default: {
1140    std::cerr
1141      << "pushCbcGenCutParam: internal confusion!" << std::endl;
1142    return (-1);
1143  }
1144  }
1145
1146  return (0);
1147}
1148
1149/*
1150  This routine imports a new constraint system into the solver.
1151*/
1152
1153int doImportParam(CoinParam *param)
1154
1155{
1156  assert(param != 0);
1157  CbcGenParam *genParam = dynamic_cast<CbcGenParam *>(param);
1158  assert(genParam != 0);
1159  CbcGenCtlBlk *ctlBlk = genParam->obj();
1160  assert(ctlBlk != 0);
1161  /*
1162      Setup to return nonfatal/fatal error (1/-1) by default, so that all we need
1163      to do is correct to 0 (no error) if we're successful.
1164    */
1165  int retval;
1166  if (CoinParamUtils::isInteractive()) {
1167    retval = 1;
1168  } else {
1169    retval = -1;
1170  }
1171  /*
1172      Figure out where we're going to acquire this new model. As special cases,
1173      `$' says `use the previous input source' and `-' says `use stdin'.
1174    */
1175  std::string field = genParam->strVal();
1176  std::string fileName;
1177  if (field == "$") {
1178    fileName = ctlBlk->lastMpsIn_;
1179    field = fileName;
1180  } else if (field == "-") {
1181    fileName = "stdin";
1182    field = fileName;
1183  } else {
1184    fileName = field;
1185  }
1186  /*
1187      See if we can open a file. fileCoinReadable understands a fair bit about
1188      platforms and compressed files and will try variations of the file name
1189      (see the doxygen doc'n for details). The file name returned in field wil
1190      be the one that actually worked.
1191    */
1192  bool canOpen = fileCoinReadable(fileName, ctlBlk->dfltDirectory_);
1193  if (canOpen == false) {
1194    std::cout
1195      << "Unable to open file `" << fileName
1196      << "', original name '" << genParam->strVal() << "'." << std::endl;
1197    return (retval);
1198  }
1199  /*
1200      We can find the file. Record the name. This requires a little finesse: what
1201      we want is the base file name (and extension(s), if present) but not the
1202      prefix, unless it's an absolute path.
1203    */
1204  if (!fileAbsPath(fileName)) {
1205    std::string::size_type pos = fileName.rfind(field);
1206    ctlBlk->lastMpsIn_ = fileName.substr(pos);
1207  } else {
1208    ctlBlk->lastMpsIn_ = fileName;
1209  }
1210  /*
1211      Try to read the file. Standard OSI doesn't support the Clp extensions for
1212      keepImportNames and allowImportErrors. It should at least support
1213      keepImportNames. Status will be zero for a successful read.
1214    */
1215  OsiSolverInterface *lpSolver = ctlBlk->model_->solver();
1216  int status = lpSolver->readMps(fileName.c_str(), "");
1217  if (status) {
1218    std::cout
1219      << "There were " << status << " errors on input." << std::endl;
1220    return (retval);
1221  }
1222  /*
1223      We have a model! Return success.
1224    */
1225  ctlBlk->goodModel_ = true;
1226
1227  return (0);
1228}
1229
1230/*
1231  This routine imports a debug file into the solver, or arranges for its
1232  creation. Import works in the standard way, using the file name provided
1233  with the command.
1234
1235  As special cases, if the file name is `create' or `createAfterPre', the
1236  action here sets up to cause a debug file containing the solution to be
1237  dumped after branch-and-cut is completed.  `createAfterPre' will dump the
1238  solution before undoing the presolve transforms.  `create' will dump the
1239  solution after integer presolve is backed out.
1240*/
1241
1242int doDebugParam(CoinParam *param)
1243
1244{
1245  assert(param != 0);
1246  CbcGenParam *genParam = dynamic_cast<CbcGenParam *>(param);
1247  assert(genParam != 0);
1248  CbcGenCtlBlk *ctlBlk = genParam->obj();
1249  assert(ctlBlk != 0);
1250  /*
1251      Setup to return nonfatal/fatal error (1/-1) by default, so that all we need
1252      to do is correct to 0 (no error) if we're successful.
1253    */
1254  int retval;
1255  if (CoinParamUtils::isInteractive()) {
1256    retval = 1;
1257  } else {
1258    retval = -1;
1259  }
1260  /*
1261       If the file name is `create' or `createAfterPre', we're just setting up to
1262       make a debug file the next time we do branch-and-cut.
1263    */
1264  std::string field = genParam->strVal();
1265  if (field == "create" || field == "createAfterPre") {
1266    ctlBlk->debugCreate_ = field;
1267    return (0);
1268  }
1269  /*
1270      Figure out where we're going to acquire the debug file. As special cases,
1271      `$' says `use the previous input source' and `-' says `use stdin'.
1272    */
1273  std::string fileName;
1274  if (field == "$") {
1275    fileName = ctlBlk->debugFile_;
1276    field = fileName;
1277  } else if (field == "-") {
1278    fileName = "stdin";
1279    field = fileName;
1280  } else {
1281    fileName = field;
1282  }
1283  /*
1284      See if we can open a file. fileCoinReadable understands a fair bit about
1285      platforms and compressed files and will try variations of the file name
1286      (see the doxygen doc'n for details). The file name returned in field wil be
1287      the one that actually worked. No default prefix --- a debug file is assumed
1288      to always be in the current directory.
1289    */
1290  bool canOpen = fileCoinReadable(fileName, ctlBlk->dfltDirectory_);
1291  if (canOpen == false) {
1292    std::cout
1293      << "Unable to open file `" << fileName
1294      << "', original name '" << genParam->strVal() << "'." << std::endl;
1295    return (retval);
1296  }
1297  /*
1298      We can find the file. Record the name. This requires a little finesse: what
1299      we want is the base file name (and extension(s), if present) but not the
1300      prefix, unless it's an absolute path.
1301    */
1302  if (!fileAbsPath(fileName)) {
1303    std::string::size_type pos = fileName.rfind(field);
1304    ctlBlk->lastMpsIn_ = fileName.substr(pos);
1305  } else {
1306    ctlBlk->lastMpsIn_ = fileName;
1307  }
1308  /*
1309      Load the primal variable values into the debug solution vector.
1310    */
1311  int intUnused, numCols;
1312  double dblUnused;
1313  double *primals;
1314
1315  bool readOK = readSolution(fileName,
1316    intUnused, numCols, dblUnused, 0, 0, &primals, 0);
1317
1318  if (readOK) {
1319    if (ctlBlk->debugSol_.values_) {
1320      delete[] ctlBlk->debugSol_.values_;
1321    }
1322    ctlBlk->debugSol_.numCols_ = numCols;
1323    ctlBlk->debugSol_.values_ = primals;
1324    retval = 0;
1325  } else {
1326    if (primals) {
1327      delete[] primals;
1328    }
1329  }
1330
1331  return (retval);
1332}
1333
1334/*
1335  Utility routine to save the current solution to a file. No formatting, and
1336  not intended to be portable in any way, shape, or form.
1337*/
1338
1339void saveSolution(const OsiSolverInterface *osi, std::string fileName)
1340
1341{
1342  FILE *fp = fopen(fileName.c_str(), "wb");
1343
1344  if (fp) {
1345    int numberRows = osi->getNumRows();
1346    int numberColumns = osi->getNumCols();
1347    double objectiveValue = osi->getObjValue();
1348
1349    fwrite(&numberRows, sizeof(int), 1, fp);
1350    fwrite(&numberColumns, sizeof(int), 1, fp);
1351    fwrite(&objectiveValue, sizeof(double), 1, fp);
1352
1353    const double *primalRowSolution = osi->getRowActivity();
1354    const double *dualRowSolution = osi->getRowPrice();
1355    const double *primalColumnSolution = osi->getColSolution();
1356    const double *dualColumnSolution = osi->getReducedCost();
1357
1358    fwrite(primalRowSolution, sizeof(double), numberRows, fp);
1359    fwrite(dualRowSolution, sizeof(double), numberRows, fp);
1360    fwrite(primalColumnSolution, sizeof(double), numberColumns, fp);
1361    fwrite(dualColumnSolution, sizeof(double), numberColumns, fp);
1362
1363    fclose(fp);
1364  } else {
1365    std::cout
1366      << "saveSolution: Unable to open file `"
1367      << fileName << "'." << std::endl;
1368  }
1369
1370  return;
1371}
1372
1373/*
1374  Utility routine to read in a solution dump created by saveSolution. Generally
1375  we don't need all the info in this file, so the routine accepts a bunch of
1376  reference/pointer paramaters and fills in any that are non-null. It's the
1377  client's responsibility to dispose of space allocated for solution vectors.
1378  The parameters fileName, numRows, numCols, and objVal are mandatory. The rest
1379  can be null.
1380*/
1381bool readSolution(std::string fileName,
1382  int &numRows, int &numCols, double &objVal,
1383  double **rowActivity, double **dualVars,
1384  double **primalVars, double **reducedCosts)
1385
1386{
1387  FILE *fp = fopen(fileName.c_str(), "rb");
1388  bool retval = true;
1389
1390  numRows = -1;
1391  numCols = -1;
1392  objVal = 0;
1393  *rowActivity = 0;
1394  *dualVars = 0;
1395  *primalVars = 0;
1396  *reducedCosts = 0;
1397
1398  if (fp) {
1399    fread(&numRows, sizeof(int), 1, fp);
1400    fread(&numCols, sizeof(int), 1, fp);
1401    fread(&objVal, sizeof(double), 1, fp);
1402
1403    if (rowActivity != NULL) {
1404      *rowActivity = new double[numRows];
1405      fread(*rowActivity, sizeof(double), numRows, fp);
1406    } else {
1407      fseek(fp, numRows * sizeof(double), SEEK_CUR);
1408    }
1409
1410    if (dualVars != NULL) {
1411      *dualVars = new double[numRows];
1412      fread(*dualVars, sizeof(double), numRows, fp);
1413    } else {
1414      fseek(fp, numRows * sizeof(double), SEEK_CUR);
1415    }
1416
1417    if (primalVars != NULL) {
1418      *primalVars = new double[numCols];
1419      fread(*primalVars, sizeof(double), numCols, fp);
1420    } else {
1421      fseek(fp, numCols * sizeof(double), SEEK_CUR);
1422    }
1423
1424    if (reducedCosts != NULL) {
1425      *reducedCosts = new double[numCols];
1426      fread(*reducedCosts, sizeof(double), numCols, fp);
1427    } else {
1428      fseek(fp, numCols * sizeof(double), SEEK_CUR);
1429    }
1430
1431    fclose(fp);
1432  } else {
1433    std::cout
1434      << "readSolution: Unable to open file `"
1435      << fileName << "'." << std::endl;
1436    retval = false;
1437  }
1438
1439  return (retval);
1440}
1441
1442} // end namespace CbcGenParamUtils
Note: See TracBrowser for help on using the repository browser.