source: trunk/Cbc/examples/driverFat.cpp @ 2469

Last change on this file since 2469 was 2469, checked in by unxusr, 5 months ago

formatting

File size: 22.8 KB
Line 
1// $Id: driverFat.cpp 1898 2013-04-09 18:06:04Z stefan $
2// Copyright (C) 2007, International Business Machines
3// Corporation and others.  All Rights Reserved.
4// This code is licensed under the terms of the Eclipse Public License (EPL).
5
6#include <cassert>
7#include <iomanip>
8
9#include "CoinPragma.hpp"
10#include "CbcModel.hpp"
11#include "CbcHeuristic.hpp"
12#include "OsiClpSolverInterface.hpp"
13#include "CbcSolver.hpp"
14#include "ClpSimplex.hpp"
15#include "ClpFactorization.hpp"
16#include "ClpPresolve.hpp"
17#include "CoinSort.hpp"
18#include "CoinHelperFunctions.hpp"
19
20#include "CoinTime.hpp"
21#include "CoinSignal.hpp"
22/*
23  This shows how to trap signals.
24  This just traps ctrl-c and allows user to pause and then hit S or C
25  In this simple version Stop may not be effective until a heuristic has exited
26 */
27
28static CbcModel *currentBranchModel = NULL;
29extern "C" {
30static void signal_handler(int whichSignal)
31{
32  int gotChar = 'X';
33  while (toupper(gotChar) != 'S' && toupper(gotChar) != 'C') {
34    // See what user wants to do
35    fprintf(stderr, "Enter S to stop, C to continue:");
36    gotChar = getchar();
37  }
38  if (currentBranchModel != NULL && toupper(gotChar) == 'S') {
39    currentBranchModel->sayEventHappened(); // say why stopped
40    if (currentBranchModel->heuristicModel())
41      currentBranchModel->heuristicModel()->sayEventHappened();
42  }
43  return;
44}
45}
46static CoinSighandler_t saveSignal = signal(SIGINT, signal_handler);
47// Threshold below which use normal clp
48static int rowsThreshold = -1;
49//#############################################################################
50
51/************************************************************************
52
53This main program shows how to take advantage of the standalone cbc in your program,
54while still making major modifications.
55First it reads in an integer model from an mps file
56Then it initializes the integer model with cbc defaults
57Then it calls CbcMain1 passing all parameters apart from first but with callBack to modify stuff
58Finally it prints solution
59
60************************************************************************/
61/* Meaning of whereFrom:
62   1 after initial solve by dualsimplex etc
63   2 after preprocessing
64   3 just before branchAndBound (so user can override)
65   4 just after branchAndBound (before postprocessing)
66   5 after postprocessing
67*/
68/* Meaning of model status is as normal
69   status
70      -1 before branchAndBound
71      0 finished - check isProvenOptimal or isProvenInfeasible to see if solution found
72      (or check value of best solution)
73      1 stopped - on maxnodes, maxsols, maxtime
74      2 difficulties so run was abandoned
75      (5 event user programmed event occurred)
76
77      cbc secondary status of problem
78        -1 unset (status_ will also be -1)
79        0 search completed with solution
80        1 linear relaxation not feasible (or worse than cutoff)
81        2 stopped on gap
82        3 stopped on nodes
83        4 stopped on time
84        5 stopped on user event
85        6 stopped on solutions
86        7 linear relaxation unbounded
87
88   but initially check if status is 0 and secondary status is 1 -> infeasible
89   or you can check solver status.
90*/
91/* Return non-zero to return quickly */
92static int callBack(CbcModel *model, int whereFrom)
93{
94  int returnCode = 0;
95  switch (whereFrom) {
96  case 1:
97  case 2:
98    if (!model->status() && model->secondaryStatus())
99      returnCode = 1;
100    break;
101  case 3: {
102    // set up signal trapping
103    saveSignal = signal(SIGINT, signal_handler);
104    currentBranchModel = model;
105    /*******************************
106        This tells code to be normal in heuristics i.e. smaller problems
107        in practice you should probably make CoinMax(...,20000) or
108        some such.
109        You may wish to switch off strong branching and use priorities
110        or something - as strong branching uses large model
111      *******************************/
112    rowsThreshold = model->getNumRows();
113    // make heuristics do more nodes
114    for (int i = 0; i < model->numberHeuristics(); i++) {
115      CbcHeuristic *heuristic = model->heuristic(i);
116      heuristic->setNumberNodes(5 * heuristic->numberNodes());
117    }
118    // could try doing feasibility after cuts?
119    //model->setSpecialOptions(33554432|
120    //                 model->specialOptions());
121  } break;
122  case 4: {
123    // restore
124    signal(SIGINT, saveSignal);
125    currentBranchModel = NULL;
126  }
127  // If not good enough could skip postprocessing
128  break;
129  case 5:
130    break;
131  default:
132    abort();
133  }
134  return returnCode;
135}
136#include "CbcEventHandler.hpp"
137/** This is so user can trap events and do useful stuff. 
138
139    CbcModel model_ is available as well as anything else you care
140    to pass in
141*/
142
143class MyEventHandler3 : public CbcEventHandler {
144
145public:
146  /**@name Overrides */
147  //@{
148  virtual CbcAction event(CbcEvent whichEvent);
149  //@}
150
151  /**@name Constructors, destructor etc*/
152  //@{
153  /** Default constructor. */
154  MyEventHandler3();
155  /// Constructor with pointer to model (redundant as setEventHandler does)
156  MyEventHandler3(CbcModel *model);
157  /** Destructor */
158  virtual ~MyEventHandler3();
159  /** The copy constructor. */
160  MyEventHandler3(const MyEventHandler3 &rhs);
161  /// Assignment
162  MyEventHandler3 &operator=(const MyEventHandler3 &rhs);
163  /// Clone
164  virtual CbcEventHandler *clone() const;
165  //@}
166
167protected:
168  // data goes here
169};
170//-------------------------------------------------------------------
171// Default Constructor
172//-------------------------------------------------------------------
173MyEventHandler3::MyEventHandler3()
174  : CbcEventHandler()
175{
176}
177
178//-------------------------------------------------------------------
179// Copy constructor
180//-------------------------------------------------------------------
181MyEventHandler3::MyEventHandler3(const MyEventHandler3 &rhs)
182  : CbcEventHandler(rhs)
183{
184}
185
186// Constructor with pointer to model
187MyEventHandler3::MyEventHandler3(CbcModel *model)
188  : CbcEventHandler(model)
189{
190}
191
192//-------------------------------------------------------------------
193// Destructor
194//-------------------------------------------------------------------
195MyEventHandler3::~MyEventHandler3()
196{
197}
198
199//----------------------------------------------------------------
200// Assignment operator
201//-------------------------------------------------------------------
202MyEventHandler3 &
203MyEventHandler3::operator=(const MyEventHandler3 &rhs)
204{
205  if (this != &rhs) {
206    CbcEventHandler::operator=(rhs);
207  }
208  return *this;
209}
210//-------------------------------------------------------------------
211// Clone
212//-------------------------------------------------------------------
213CbcEventHandler *MyEventHandler3::clone() const
214{
215  return new MyEventHandler3(*this);
216}
217
218CbcEventHandler::CbcAction
219MyEventHandler3::event(CbcEvent whichEvent)
220{
221  // If in sub tree carry on
222  if (!model_->parentModel()) {
223    if (whichEvent == solution || whichEvent == heuristicSolution) {
224#ifdef STOP_EARLY
225      return stop; // say finished
226#else
227      // If preprocessing was done solution will be to processed model
228#if 0
229      int numberColumns = model_->getNumCols();
230      const double * bestSolution = model_->bestSolution();
231      assert (bestSolution);
232      printf("value of solution is %g\n",model_->getObjValue());
233      for (int i=0;i<numberColumns;i++) {
234        if (fabs(bestSolution[i])>1.0e-8)
235          printf("%d %g\n",i,bestSolution[i]);
236      }
237#endif
238      return noAction; // carry on
239#endif
240    } else {
241      return noAction; // carry on
242    }
243  } else {
244    return noAction; // carry on
245  }
246}
247//#############################################################################
248
249/**
250
251    This is to allow the user to replace initialSolve and resolve
252*/
253
254class CbcSolverShortFat : public OsiClpSolverInterface {
255
256public:
257  //---------------------------------------------------------------------------
258  /**@name Solve methods */
259  //@{
260  /// Solve initial LP relaxation
261  virtual void initialSolve();
262
263  /// Resolve an LP relaxation after problem modification
264  virtual void resolve();
265
266  //@}
267
268  /**@name Constructors and destructors */
269  //@{
270  /// Default Constructor
271  CbcSolverShortFat();
272
273  /// Clone
274  virtual OsiSolverInterface *clone(bool CopyData = true) const;
275
276  /// Copy constructor
277  CbcSolverShortFat(const CbcSolverShortFat &);
278
279  /// Assignment operator
280  CbcSolverShortFat &operator=(const CbcSolverShortFat &rhs);
281
282  /// Destructor
283  virtual ~CbcSolverShortFat();
284
285  //@}
286
287  //---------------------------------------------------------------------------
288
289private:
290  /**@name Private member data */
291  //@{
292  //@}
293};
294static bool firstSolve = true;
295void CbcSolverShortFat::initialSolve()
296{
297  ClpSimplex *model = getModelPtr();
298  if (model->numberRows() < rowsThreshold) {
299    OsiClpSolverInterface::initialSolve();
300    return;
301  }
302#if LOGLEVEL > 1
303  double time1 = CoinCpuTime();
304#endif
305  ClpPresolve pinfo;
306#define INCREASE 6
307#define PERTURB 5
308#ifdef PERTURB
309  bool externalPerturb = true;
310#endif
311  if (firstSolve) { // before preprocessing
312    model = pinfo.presolvedModel(*model, 1.0e-8, false, 5, false);
313  } else {
314    externalPerturb = false;
315    /* do initial factorization to get infeasibilities
316       maybe fix all non basic
317       initial fix checks if Osi already has basis - yes it has
318       
319     */
320    setBasis(basis_, model);
321  }
322#define LOGLEVEL 0
323#if LOGLEVEL < 3
324  model->setLogLevel(0);
325#endif
326  int numberColumns = model->numberColumns();
327  int originalNumberRows = model->numberRows();
328  // change factorization frequency from 200
329  model->setFactorizationFrequency(100 + model->numberRows() / 50);
330  int numberIterations = 0;
331#ifdef PERTURB
332  double *objective = model->objective();
333  double *saveObjective = NULL;
334  if (externalPerturb) {
335    saveObjective = CoinCopyOfArray(objective, numberColumns);
336    double multiplier = 1.0;
337    for (int i = 0; i < PERTURB; i++)
338      multiplier *= 0.1;
339    for (int i = 0; i < numberColumns; i++) {
340      double value = CoinDrand48() * multiplier;
341      // should be more sophisticated
342      if (value > 1.0e-7) {
343        if (objective[i] < 0.0)
344          value = -value;
345        objective[i] += value;
346      }
347    }
348  }
349#endif
350
351  // We will need arrays to choose rows to add
352  double *weight = new double[originalNumberRows];
353  int *sort = new int[originalNumberRows];
354  int numberSort = 0;
355  char *take = new char[originalNumberRows];
356
357  const double *rowLower = model->rowLower();
358  const double *rowUpper = model->rowUpper();
359  int iRow, iColumn;
360  // Set up initial list
361  numberSort = 0;
362  int numberNonBasicRows = 0;
363  for (iRow = 0; iRow < originalNumberRows; iRow++) {
364    weight[iRow] = 1.123e50;
365    if (model->getRowStatus(iRow) != ClpSimplex::basic) {
366      sort[numberSort++] = iRow;
367      weight[iRow] = -10.0;
368      numberNonBasicRows++;
369    } else if (rowLower[iRow] == rowUpper[iRow]) {
370      sort[numberSort++] = iRow;
371      weight[iRow] = 0.0;
372    }
373  }
374  numberSort /= 2;
375  numberSort = CoinMax(numberSort, numberNonBasicRows);
376  // Just add this number of rows each time in small problem
377  int smallNumberRows = 2 * numberColumns;
378  smallNumberRows = CoinMin(smallNumberRows, originalNumberRows / 20);
379  // and pad out with random rows
380  double ratio = (static_cast< double >(smallNumberRows - numberSort)) / (static_cast< double >(originalNumberRows));
381  bool primalInfeasible = false;
382  if (firstSolve) {
383    for (iRow = 0; iRow < originalNumberRows; iRow++) {
384      if (weight[iRow] == 1.123e50 && CoinDrand48() < ratio)
385        sort[numberSort++] = iRow;
386    }
387  }
388  /* This is optional.
389     The best thing to do is to miss out random rows and do a set which makes dual feasible.
390     If that is not possible then make sure variables have bounds.
391     
392     One way that normally works is to automatically tighten bounds.
393  */
394  if (firstSolve) {
395    // However for some we need to do anyway
396    double *columnLower = model->columnLower();
397    double *columnUpper = model->columnUpper();
398    for (iColumn = 0; iColumn < numberColumns; iColumn++) {
399      columnLower[iColumn] = CoinMax(-1.0e6, columnLower[iColumn]);
400      columnUpper[iColumn] = CoinMin(1.0e6, columnUpper[iColumn]);
401    }
402  }
403  double *fullSolution = model->primalRowSolution();
404
405  // Just do this number of passes
406  int maxPass = 50;
407  // And take out slack rows until this pass
408  int takeOutPass = INCREASE;
409  int iPass;
410
411  const CoinBigIndex *start = model->clpMatrix()->getVectorStarts();
412  const int *length = model->clpMatrix()->getVectorLengths();
413  const int *row = model->clpMatrix()->getIndices();
414  int *whichColumns = new int[numberColumns];
415  for (int iRow = 0; iRow < originalNumberRows; iRow++)
416    weight[iRow] = 0.0;
417  for (int i = 0; i < numberSort; i++) {
418    int iRow = sort[i];
419    weight[iRow] = 1.0;
420  }
421
422  int numberSmallColumns = 0;
423  for (iColumn = 0; iColumn < numberColumns; iColumn++) {
424    bool take = false;
425    ;
426    for (int j = start[iColumn]; j < start[iColumn] + length[iColumn]; j++) {
427      int iRow = row[j];
428      if (weight[iRow]) {
429        take = true;
430        break;
431      }
432    }
433    if (take)
434      whichColumns[numberSmallColumns++] = iColumn;
435  }
436#if LOGLEVEL > 1
437  printf("%d rows, %d columns in initial problem\n", numberSort, numberSmallColumns);
438#endif
439  for (iPass = 0; iPass < maxPass; iPass++) {
440#if LOGLEVEL > 2
441    printf("Start of pass %d\n", iPass);
442#endif
443    // Cleaner this way
444    std::sort(sort, sort + numberSort);
445    // Create small problem
446    ClpSimplex small(model, numberSort, sort, numberSmallColumns, whichColumns);
447    small.setFactorizationFrequency(100 + numberSort / 200);
448#ifndef PERTURB
449    small.setPerturbation(50);
450#else
451    if (!externalPerturb)
452      small.setPerturbation(50);
453#endif
454#if LOGLEVEL > 2
455    small.setLogLevel(1);
456#endif
457    // A variation is to just do N iterations
458    //if (iPass)
459    //small.setMaximumIterations(100);
460    // Solve
461    //small.factorization()->messageLevel(8);
462    small.dual();
463    numberIterations += small.numberIterations();
464    primalInfeasible = (small.status() == 1);
465    if (primalInfeasible)
466      break;
467    bool dualInfeasible = (small.status() == 2);
468    // move solution back
469    double *solution = model->primalColumnSolution();
470    const double *smallSolution = small.primalColumnSolution();
471    for (int j = 0; j < numberSmallColumns; j++) {
472      iColumn = whichColumns[j];
473      solution[iColumn] = smallSolution[j];
474      model->setColumnStatus(iColumn, small.getColumnStatus(j));
475    }
476    for (iRow = 0; iRow < numberSort; iRow++) {
477      int kRow = sort[iRow];
478      model->setRowStatus(kRow, small.getRowStatus(iRow));
479    }
480    // compute full solution
481    memset(fullSolution, 0, originalNumberRows * sizeof(double));
482    model->clpMatrix()->times(1.0, model->primalColumnSolution(), fullSolution);
483    if (iPass != maxPass - 1) {
484      // Mark row as not looked at
485      for (iRow = 0; iRow < originalNumberRows; iRow++)
486        weight[iRow] = 1.123e50;
487      // Look at rows already in small problem
488      int iSort;
489      int numberDropped = 0;
490      int numberKept = 0;
491      int numberBinding = 0;
492      int numberInfeasibilities = 0;
493      double sumInfeasibilities = 0.0;
494      for (iSort = 0; iSort < numberSort; iSort++) {
495        iRow = sort[iSort];
496        if (model->getRowStatus(iRow) == ClpSimplex::basic) {
497          // Basic - we can get rid of if early on
498          if (iPass < takeOutPass && !dualInfeasible) {
499            // may have hit max iterations so check
500            double infeasibility = CoinMax(fullSolution[iRow] - rowUpper[iRow],
501              rowLower[iRow] - fullSolution[iRow]);
502            weight[iRow] = -infeasibility;
503            if (infeasibility > 1.0e-8) {
504              numberInfeasibilities++;
505              sumInfeasibilities += infeasibility;
506            } else {
507              weight[iRow] = 1.0;
508              numberDropped++;
509            }
510          } else {
511            // keep
512            weight[iRow] = -1.0e40;
513            numberKept++;
514          }
515        } else {
516          // keep
517          weight[iRow] = -1.0e50;
518          numberKept++;
519          numberBinding++;
520        }
521      }
522      // Now rest
523      for (iRow = 0; iRow < originalNumberRows; iRow++) {
524        sort[iRow] = iRow;
525        if (weight[iRow] == 1.123e50) {
526          // not looked at yet
527          double infeasibility = CoinMax(fullSolution[iRow] - rowUpper[iRow],
528            rowLower[iRow] - fullSolution[iRow]);
529          weight[iRow] = -infeasibility;
530          if (infeasibility > 1.0e-8) {
531            numberInfeasibilities++;
532            sumInfeasibilities += infeasibility;
533          }
534        }
535      }
536      // sort
537      CoinSort_2(weight, weight + originalNumberRows, sort);
538      numberSort = CoinMin(originalNumberRows, smallNumberRows + numberKept);
539      memset(take, 0, originalNumberRows);
540      for (iRow = 0; iRow < numberSort; iRow++)
541        take[sort[iRow]] = 1;
542      numberSmallColumns = 0;
543      for (iColumn = 0; iColumn < numberColumns; iColumn++) {
544        int n = 0;
545        for (int j = start[iColumn]; j < start[iColumn] + length[iColumn]; j++) {
546          int iRow = row[j];
547          if (take[iRow])
548            n++;
549        }
550        if (n)
551          whichColumns[numberSmallColumns++] = iColumn;
552      }
553#if LOGLEVEL > 1
554      printf("%d rows binding, %d rows kept, %d rows dropped - new size %d rows, %d columns\n",
555        numberBinding, numberKept, numberDropped, numberSort, numberSmallColumns);
556      printf("%d rows are infeasible - sum is %g\n", numberInfeasibilities,
557        sumInfeasibilities);
558#endif
559      if (!numberInfeasibilities) {
560#if LOGLEVEL > 1
561        printf("Exiting as looks optimal\n");
562#endif
563        break;
564      }
565      numberInfeasibilities = 0;
566      sumInfeasibilities = 0.0;
567      for (iSort = 0; iSort < numberSort; iSort++) {
568        if (weight[iSort] > -1.0e30 && weight[iSort] < -1.0e-8) {
569          numberInfeasibilities++;
570          sumInfeasibilities += -weight[iSort];
571        }
572      }
573#if LOGLEVEL > 1
574      printf("in small model %d rows are infeasible - sum is %g\n", numberInfeasibilities,
575        sumInfeasibilities);
576#endif
577    }
578  }
579  delete[] weight;
580  delete[] sort;
581  delete[] whichColumns;
582  delete[] take;
583#ifdef PERTURB
584  if (externalPerturb) {
585    memcpy(objective, saveObjective, numberColumns * sizeof(double));
586    delete[] saveObjective;
587  }
588  model->setPerturbation(50);
589#endif
590  if (!primalInfeasible) {
591    model->primal(1);
592    numberIterations += model->numberIterations();
593  } else {
594    model->setProblemStatus(1);
595  }
596  model->setNumberIterations(numberIterations);
597  if (firstSolve) {
598    pinfo.postsolve(true);
599    model = getModelPtr();
600    model->primal(1);
601    firstSolve = false;
602  }
603  basis_ = getBasis(model);
604#if LOGLEVEL > 1
605  printf("solve took %g seconds and %d iterations\n", CoinCpuTime() - time1,
606    numberIterations);
607#endif
608}
609
610//-----------------------------------------------------------------------------
611void CbcSolverShortFat::resolve()
612{
613  ClpSimplex *model = getModelPtr();
614  if (model->numberRows() < rowsThreshold) {
615    OsiClpSolverInterface::resolve();
616  } else {
617#if LOGLEVEL > 1
618    printf("resolve\n");
619#endif
620    initialSolve();
621  }
622  return;
623}
624
625//#############################################################################
626// Constructors, destructors clone and assignment
627//#############################################################################
628
629//-------------------------------------------------------------------
630// Default Constructor
631//-------------------------------------------------------------------
632CbcSolverShortFat::CbcSolverShortFat()
633  : OsiClpSolverInterface()
634{
635}
636
637//-------------------------------------------------------------------
638// Clone
639//-------------------------------------------------------------------
640OsiSolverInterface *
641CbcSolverShortFat::clone(bool CopyData) const
642{
643  if (CopyData) {
644    return new CbcSolverShortFat(*this);
645  } else {
646    printf("warning CbcSolveUser clone with copyData false\n");
647    return new CbcSolverShortFat();
648  }
649}
650
651//-------------------------------------------------------------------
652// Copy constructor
653//-------------------------------------------------------------------
654CbcSolverShortFat::CbcSolverShortFat(
655  const CbcSolverShortFat &rhs)
656  : OsiClpSolverInterface(rhs)
657{
658}
659
660//-------------------------------------------------------------------
661// Destructor
662//-------------------------------------------------------------------
663CbcSolverShortFat::~CbcSolverShortFat()
664{
665}
666
667//-------------------------------------------------------------------
668// Assignment operator
669//-------------------------------------------------------------------
670CbcSolverShortFat &
671CbcSolverShortFat::operator=(const CbcSolverShortFat &rhs)
672{
673  if (this != &rhs) {
674    OsiClpSolverInterface::operator=(rhs);
675  }
676  return *this;
677}
678//-------------------------------------------------------------------
679
680int main(int argc, const char *argv[])
681{
682
683  CbcSolverShortFat solver1;
684  // Read in model using argv[1]
685  // and assert that it is a clean model
686  std::string mpsFileName;
687  if (argc < 2) {
688    fprintf(stderr, "Do not know where to find input file.\n");
689    exit(1);
690  }
691  if (argc >= 2)
692    mpsFileName = argv[1];
693  int numMpsReadErrors;
694  if (!strstr(mpsFileName.c_str(), ".lp"))
695    numMpsReadErrors = solver1.readMps(mpsFileName.c_str(), "");
696  else
697    numMpsReadErrors = solver1.readLp(mpsFileName.c_str(), 1.0e-12);
698  if (numMpsReadErrors != 0) {
699    printf("%d errors reading MPS file\n", numMpsReadErrors);
700    return numMpsReadErrors;
701  }
702  // Tell solver to return fast if presolve or initial solve infeasible
703  solver1.getModelPtr()->setMoreSpecialOptions(3);
704
705  // Pass to Cbc initialize defaults
706  CbcModel modelA(solver1);
707  CbcModel *model = &modelA;
708  CbcMain0(modelA);
709  // Event handler
710  MyEventHandler3 eventHandler;
711  model->passInEventHandler(&eventHandler);
712  /* Now go into code for standalone solver
713     Could copy arguments and add -quit at end to be safe
714     but this will do
715  */
716  if (argc > 2) {
717    CbcMain1(argc - 1, argv + 1, modelA, callBack);
718  } else {
719    const char *argv2[] = { "driverFat", "-solve", "-quit" };
720    CbcMain1(3, argv2, modelA, callBack);
721  }
722  // Solver was cloned so get current copy
723  OsiSolverInterface *solver = model->solver();
724  // Print solution if finished (could get from model->bestSolution() as well
725
726  if (model->bestSolution()) {
727
728    const double *solution = solver->getColSolution();
729
730    int iColumn;
731    int numberColumns = solver->getNumCols();
732    std::cout << std::setiosflags(std::ios::fixed | std::ios::showpoint) << std::setw(14);
733
734    std::cout << "--------------------------------------" << std::endl;
735    // names may not be in current solver - use original
736
737    for (iColumn = 0; iColumn < numberColumns; iColumn++) {
738      double value = solution[iColumn];
739      if (fabs(value) > 1.0e-7 && solver->isInteger(iColumn))
740        std::cout << std::setw(6) << iColumn << " " << std::setw(8) << setiosflags(std::ios::left) << solver1.getModelPtr()->columnName(iColumn)
741                  << resetiosflags(std::ios::adjustfield) << std::setw(14) << " " << value << std::endl;
742    }
743    std::cout << "--------------------------------------" << std::endl;
744
745    std::cout << std::resetiosflags(std::ios::fixed | std::ios::showpoint | std::ios::scientific);
746  } else {
747    std::cout << " No solution!" << std::endl;
748  }
749  return 0;
750}
Note: See TracBrowser for help on using the repository browser.