BonMilpRounding.cpp
Go to the documentation of this file.
1 // Copyright (C) 2007, International Business Machines Corporation and others.
2 // All Rights Reserved.
3 // This code is published under the Eclipse Public License.
4 //
5 // Authors :
6 // Pierre Bonami CNRS
7 //
8 // Date : May, 26 2010
9 
10 #include "BonMilpRounding.hpp"
11 #include "CoinHelperFunctions.hpp"
12 #include "CbcModel.hpp"
13 #include "BonSubMipSolver.hpp"
14 
15 #include "CoinTime.hpp"
16 
17 #include <fstream>
18 
19 #include <iomanip>
20 
21 #include "CoinHelperFunctions.hpp"
22 #include "OsiClpSolverInterface.hpp"
23 
24 //#define DEBUG_BON_HEURISTIC_DIVE_MIP
25 
26 using namespace std;
27 
28 namespace Bonmin
29 {
30  MilpRounding::MilpRounding(BonminSetup * setup)
31  :
32  CbcHeuristic(),
33  setup_(setup),
34  howOften_(20),
35  mip_(NULL)
36  {
37  Initialize(setup);
38  }
39 
40  void
42  delete mip_;
43  mip_ = new SubMipSolver (*b, b->prefix());
44 
45  }
46 
48  :
49  CbcHeuristic(copy),
50  setup_(copy.setup_),
51  howOften_(copy.howOften_),
52  mip_(new SubMipSolver(*copy.mip_))
53  {
54  }
55 
56  MilpRounding &
58  {
59  if(this != &rhs) {
60  CbcHeuristic::operator=(rhs);
61  setup_ = rhs.setup_;
62  howOften_ = rhs.howOften_;
63  delete mip_;
64  if(rhs.mip_)
65  mip_ = new SubMipSolver(*rhs.mip_);
66  }
67  return *this;
68  }
69 
71  delete mip_;
72  }
73 
74  struct MatComp{
75  const int * iRow;
76  const int * jCol;
78  bool operator()(int i,int j){
79  return (jCol[i] < jCol[j]) || (jCol[i] == jCol[j] && iRow[i] < iRow[j]);
80  }
81  };
82 
83 
84  int
85  MilpRounding::solution(double &solutionValue, double *betterSolution)
86  {
87  if(model_->getCurrentPassNumber() > 1) return 0;
88  if (model_->currentDepth() > 2 && (model_->getNodeCount()%howOften_)!=0)
89  return 0;
90 
91  int returnCode = 0; // 0 means it didn't find a feasible solution
92 
93  OsiTMINLPInterface * nlp = NULL;
94  if(setup_->getAlgorithm() == B_BB)
95  nlp = dynamic_cast<OsiTMINLPInterface *>(model_->solver()->clone());
96  else
97  nlp = dynamic_cast<OsiTMINLPInterface *>(setup_->nonlinearSolver()->clone());
98 
99  TMINLP2TNLP* minlp = nlp->problem();
100 
101  // set tolerances
102  double integerTolerance = model_->getDblParam(CbcModel::CbcIntegerTolerance);
103  //double primalTolerance = 1.0e-6;
104 
105  int n;
106  int m;
107  int nnz_jac_g;
108  int nnz_h_lag;
109  Ipopt::TNLP::IndexStyleEnum index_style;
110  minlp->get_nlp_info(n, m, nnz_jac_g,
111  nnz_h_lag, index_style);
112 
113  const Bonmin::TMINLP::VariableType* variableType = minlp->var_types();
114  const double* x_sol = minlp->x_sol();
115  const double* g_l = minlp->g_l();
116  const double* g_u = minlp->g_u();
117 
118  const double * colsol = model_->solver()->getColSolution();
119 
120 
121  // Get information about the linear and nonlinear part of the instance
122  TMINLP* tminlp = nlp->model();
124  tminlp->get_constraints_linearity(m, c_lin());
125  vector<int> c_idx(m);
126  int n_lin = 0;
127  for (int i=0;i<m;i++) {
128  if (c_lin[i]==Ipopt::TNLP::LINEAR)
129  c_idx[i] = n_lin++;
130  else
131  c_idx[i] = -1;
132  }
133 
134 
135  // Get the structure of the jacobian
136  vector<int> indexRow(nnz_jac_g);
137  vector<int> indexCol(nnz_jac_g);
138  minlp->eval_jac_g(n, x_sol, false,
139  m, nnz_jac_g,
140  indexRow(), indexCol(), 0);
141 
142  // get the jacobian values
143  vector<double> jac_g(nnz_jac_g);
144  minlp->eval_jac_g(n, x_sol, false,
145  m, nnz_jac_g,
146  NULL, NULL, jac_g());
147 
148  // Sort the matrix to column ordered
149  vector<int> sortedIndex(nnz_jac_g);
150  CoinIotaN(sortedIndex(), nnz_jac_g, 0);
151  MatComp c;
152  c.iRow = indexRow();
153  c.jCol = indexCol();
154  std::sort(sortedIndex.begin(), sortedIndex.end(), c);
155 
156  vector<int> row (nnz_jac_g);
157  vector<double> value (nnz_jac_g);
158  vector<int> columnStart(n,0);
159  vector<int> columnLength(n,0);
160  int indexCorrection = (index_style == Ipopt::TNLP::C_STYLE) ? 0 : 1;
161  int iniCol = -1;
162  int nnz = 0;
163  for(int i=0; i<nnz_jac_g; i++) {
164  int thisIndexCol = indexCol[sortedIndex[i]]-indexCorrection;
165  int thisIndexRow = c_idx[indexRow[sortedIndex[i]] - indexCorrection];
166  if(thisIndexCol != iniCol) {
167  iniCol = thisIndexCol;
168  columnStart[thisIndexCol] = nnz;
169  columnLength[thisIndexCol] = 0;
170  }
171  if(thisIndexRow == -1) continue;
172  columnLength[thisIndexCol]++;
173  row[nnz] = thisIndexRow;
174  value[nnz] = jac_g[i];
175  nnz++;
176  }
177 
178  // Build the row lower and upper bounds
179  vector<double> newRowLower(n_lin);
180  vector<double> newRowUpper(n_lin);
181  for(int i = 0 ; i < m ; i++){
182  if(c_idx[i] == -1) continue;
183  newRowLower[c_idx[i]] = g_l[i];
184  newRowUpper[c_idx[i]] = g_u[i];
185  }
186 
187  // Get solution array for heuristic solution
188  vector<double> newSolution(n);
189  std::copy(x_sol, x_sol + n, newSolution.begin());
190 
191  // Define the constraint matrix for MILP
192  CoinPackedMatrix matrix(true,n_lin,n, nnz, value(), row(), columnStart(), columnLength());
193 
194  // create objective function and columns lower and upper bounds for MILP
195  // and create columns for matrix in MILP
196  //double alpha = 0;
197  double beta = 1;
198  vector<double> objective(n);
199  vector<int> idxIntegers;
200  idxIntegers.reserve(n);
201  for(int i = 0 ; i < n ; i++){
202  if(variableType[i] != Bonmin::TMINLP::CONTINUOUS){
203  idxIntegers.push_back(i);
204  objective[i] = beta*(1 - 2*colsol[i]);
205  }
206  }
207 
208 #if 0
209  // Get dual multipliers and build gradient of the lagrangean
210  const double * duals = nlp->getRowPrice() + 2 *n;
211  vector<double> grad(n, 0);
212  vector<int> indices(n, 0);
213  tminlp->eval_grad_f(n, x_sol, false, grad());
214  for(int i = 0 ; i < m ; i++){
215  if(c_lin[i] == Ipopt::TNLP::LINEAR) continue;
216  int nnz;
217  tminlp->eval_grad_gi(n, x_sol, false, i, nnz, indices(), NULL);
218  tminlp->eval_grad_gi(n, x_sol, false, i, nnz, NULL, grad());
219  for(int k = 0 ; k < nnz ; k++){
220  objective[indices[k]] += alpha *duals[i] * grad[k];
221  }
222  }
223  for(int i = 0 ; i < n ; i++){
224  if(variableType[i] != Bonmin::TMINLP::CONTINUOUS)
225  objective[i] += alpha * grad[i];
226  //if(fabs(objective[i]) < 1e-4) objective[i] = 0;
227  else objective[i] = 0;
228  }
229  std::copy(objective.begin(), objective.end(), std::ostream_iterator<double>(std::cout, " "));
230  std::cout<<std::endl;
231 #endif
232 
233  // load the problem to OSI
234  OsiSolverInterface *si = mip_->solver();
235  assert(si != NULL);
236  CoinMessageHandler * handler = model_->messageHandler()->clone();
237  si->passInMessageHandler(handler);
238  si->messageHandler()->setLogLevel(1);
239 
240  si->loadProblem(matrix, model_->solver()->getColLower(), model_->solver()->getColUpper(), objective(),
241  newRowLower(), newRowUpper());
242  si->setInteger(idxIntegers(), static_cast<int>(idxIntegers.size()));
243  si->applyCuts(noGoods);
244 
245  bool hasFractionnal = true;
246  while(hasFractionnal){
247  mip_->optimize(DBL_MAX, 0, 60);
248  hasFractionnal = false;
249 #if 0
250  bool feasible = false;
251  if(mip_->getLastSolution()) {
252  const double* solution = mip_->getLastSolution();
253  std::copy(solution, solution + n, newSolution.begin());
254  feasible = true;
255 
256  }
257 
258  if(feasible) {
259  // fix the integer variables and solve the NLP
260  // also add no good cut
261  CoinPackedVector v;
262  double lb = 1;
263  for (int iColumn=0;iColumn<n;iColumn++) {
264  if (variableType[iColumn] != Bonmin::TMINLP::CONTINUOUS) {
265  double value=newSolution[iColumn];
266  if (fabs(floor(value+0.5)-value)>integerTolerance) {
267 #ifdef DEBUG_BON_HEURISTIC_DIVE_MIP
268  cout<<"It should be infeasible because: "<<endl;
269  cout<<"variable "<<iColumn<<" is not integer"<<endl;
270 #endif
271  feasible = false;
272  break;
273  }
274  else {
275  value=floor(newSolution[iColumn]+0.5);
276  if(value > 0.5){
277  v.insert(iColumn, -1);
278  lb -= value;
279  }
280  minlp->SetVariableUpperBound(iColumn, value);
281  minlp->SetVariableLowerBound(iColumn, value);
282  }
283  }
284  }
285  }
286 #endif
287  }
288  bool feasible = false;
289  if(mip_->getLastSolution()) {
290  const double* solution = mip_->getLastSolution();
291  std::copy(solution, solution + n, newSolution.begin());
292  feasible = true;
293 
294  delete handler;
295  }
296 
297 
298  if(feasible) {
299  // fix the integer variables and solve the NLP
300  // also add no good cut
301  CoinPackedVector v;
302  double lb = 1;
303  for (int iColumn=0;iColumn<n;iColumn++) {
304  if (variableType[iColumn] != Bonmin::TMINLP::CONTINUOUS) {
305  double value=newSolution[iColumn];
306  if (fabs(floor(value+0.5)-value)>integerTolerance) {
307  feasible = false;
308  break;
309  }
310  else {
311  value=floor(newSolution[iColumn]+0.5);
312  if(value > 0.5){
313  v.insert(iColumn, -1);
314  lb -= value;
315  }
316  minlp->SetVariableUpperBound(iColumn, value);
317  minlp->SetVariableLowerBound(iColumn, value);
318  }
319  }
320  }
321  OsiRowCut c;
322  c.setRow(v);
323  c.setLb(lb);
324  c.setUb(DBL_MAX);
325  noGoods.insert(c);
326  if(feasible) {
327  nlp->initialSolve();
328  if(minlp->optimization_status() != Ipopt::SUCCESS) {
329  feasible = false;
330  }
331  std::copy(x_sol,x_sol+n, newSolution.begin());
332  }
333  }
334  if(feasible) {
335  double newSolutionValue;
336  minlp->eval_f(n, newSolution(), true, newSolutionValue);
337  if(newSolutionValue < solutionValue) {
338  std::copy(newSolution.begin(), newSolution.end(), betterSolution);
339  solutionValue = newSolutionValue;
340  returnCode = 1;
341  }
342  }
343 
344  delete nlp;
345 
346 #ifdef DEBUG_BON_HEURISTIC_DIVE_MIP
347  std::cout<<"DiveMIP returnCode = "<<returnCode<<std::endl;
348 #endif
349 
350  return returnCode;
351  }
352  void
354  roptions->SetRegisteringCategory("Primal Heuristics (undocumented)", RegisteredOptions::UndocumentedCategory);
355  roptions->AddStringOption2(
356  "MILP_rounding_heuristic",
357  "if yes runs the heuristic",
358  "no",
359  "no", "don't run it",
360  "yes", "runs the heuristic",
361  "");
362  }
363 }
Base class for all MINLPs that use a standard triplet matrix form and dense vectors.
Definition: BonTMINLP.hpp:59
static void registerOptions(Ipopt::SmartPtr< Bonmin::RegisteredOptions > roptions)
const TMINLP2TNLP * problem() const
get pointer to the TMINLP2TNLP adapter
This is class provides an Osi interface for a Mixed Integer Linear Program expressed as a TMINLP (so ...
OsiSolverInterface * clone(bool copyData=true) const
Virtual copy constructor.
void SetVariableLowerBound(Ipopt::Index var_no, Ipopt::Number x_l)
Change the lower bound on the variable.
Ipopt::SolverReturn optimization_status() const
Get Optimization status.
int howOften_
How often to do (code can change)
void SetVariableUpperBound(Ipopt::Index var_no, Ipopt::Number x_u)
Change the upper bound on the variable.
virtual void initialSolve()
Solve initial continuous relaxation.
static char * j
Definition: OSdtoa.cpp:3622
real alpha
const double * getLastSolution()
get the solution found in last local search (return NULL if no solution).
virtual bool get_constraints_linearity(Ipopt::Index m, Ipopt::TNLP::LinearityType *const_types)=0
overload this method to provide the constraint linearity.
A very simple class to provide a common interface for solving MIPs with Cplex and Cbc...
Bonmin::Algorithm getAlgorithm()
Get the algorithm used.
~MilpRounding()
Destructor.
void fint fint * k
OsiSolverInterface * solver()
const Ipopt::Number * g_l()
Get the current values for constraints lower bounds.
virtual bool eval_grad_gi(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Index i, Ipopt::Index &nele_grad_gi, Ipopt::Index *jCol, Ipopt::Number *values)
Compute the structure or values of the gradient for one constraint.
Definition: BonTMINLP.hpp:313
virtual int solution(double &solutionValue, double *betterSolution)
Performs heuristic.
virtual bool eval_grad_f(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Number *grad_f)=0
overload this method to return the vector of the gradient of the objective w.r.t. ...
bool operator()(int i, int j)
Destructor.
virtual const double * getRowPrice() const
Get pointer to array[getNumRows()] of dual prices.
const Ipopt::Number * x_sol() const
get the solution values
OsiTMINLPInterface * nonlinearSolver()
Pointer to the non-linear solver used.
virtual bool eval_f(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Number &obj_value)
Returns the value of the objective function in x.
MilpRounding(BonminSetup *setup)
Constructor with setup.
void fint * m
int nnz
ATTENTION: Filter expect the jacobian to be ordered by row.
This is an adapter class that converts a TMINLP to a TNLP to be solved by Ipopt.
SubMipSolver * mip_
A subsolver for MIP.
virtual bool get_nlp_info(Ipopt::Index &n, Ipopt::Index &m, Ipopt::Index &nnz_jac_g, Ipopt::Index &nnz_h_lag, TNLP::IndexStyleEnum &index_style)
This call is just passed onto the TMINLP object.
BonminSetup * setup_
Setup to use for local searches (will make copies).
void optimize(double cutoff, int loglevel, double maxTime)
update cutoff and optimize MIP.
void fint * n
return b
Definition: OSdtoa.cpp:1719
real c
void Initialize(BonminSetup *setup)
Initialize method.
const Ipopt::Number * g_u()
Get the current values for constraints upper bounds.
const char * prefix() const
Get prefix to use for options.
VariableType
Type of the variables.
Definition: BonTMINLP.hpp:192
MilpRounding & operator=(const MilpRounding &rhs)
Assignment operator.
const TMINLP::VariableType * var_types()
Get the variable types.
virtual bool eval_jac_g(Ipopt::Index n, const Ipopt::Number *x, bool new_x, Ipopt::Index m, Ipopt::Index nele_jac, Ipopt::Index *iRow, Ipopt::Index *jCol, Ipopt::Number *values)
Returns the jacobian of the constraints.