[Cbc] Writing intermediate solutions via a CbcEventHandler

John Forrest john.forrest at fastercoin.com
Sat Aug 20 14:35:01 EDT 2016


Luis,

I may put up an example as to how to do this, but I took your code and 
fixed it up so that it works on an example - not necessarily in the most 
elegant way.  In some cases, CbcMain1 does a bit more work - but I hope 
the attached works for you.

John Forrest
On 19/08/16 18:48, Luís Borges de Oliveira wrote:
> Hello,
>
> I'm trying to write intermediate solutions using a CbcEventHandler 
> similarly to what is done in Cbc/examples/driver4.cpp. However, I'd 
> like to output the solution to the initial, non-preprocessed model. 
> I've tried to replicate what it is done in CbcMain1, after the B&B 
> search finishes, in particular calling CglPreProcess::postProcess, 
> along with some more handwaving based on what Cbc's nextBestSolution 
> command does.
>
> Alas, it's not working out. Not only am I not getting a solution with 
> the same number of columns as the original model, but accessing the 
> the ClpSimplex's columnNames segfaults at some point.
>
> Here's the code I have so far. Any hints you provide would be very 
> useful at this point! Am I in the right track, or should I be doing 
> something completely different? (E.g., I've considered grabbing the 
> solution to the preprocessed model and feed it to 
> computeCompleteSolution() from CbcMipStartIO.cpp, assuming I can get 
> accurate variable names from the preprocessed model.)
>
> #include  <algorithm>
> #include  <fstream>
> #include  <sstream>
>
> #include  "CbcModel.hpp"
> #include  "OsiClpSolverInterface.hpp"
> #include  "CbcSolver.hpp"
> #include  "CbcEventHandler.hpp"
> #include  "CglPreProcess.hpp"
>
> extern  CglPreProcess*cbcPreProcessPointer;
>
> class  SolutionWriter  :public  CbcEventHandler  {
>      CbcModel*original_model_;
>      string  solution_prefix_;
>      string  solution_directory_;
>      std::set<int>seen_solutions_;     int  external_solution_id_  = 1;
>
> public:
>      virtual  CbcEventHandler::CbcAction  event(CbcEvent  whichEvent) {
>          // If in sub tree carry on          if  (!model_->parentModel()) {
>              int  solution_id  = model_->getSolutionCount();
>
>              if  ((whichEvent == solution || whichEvent == heuristicSolution)
>                  && seen_solutions_.find(solution_id) == seen_solutions_.end()) {
>
>                  seen_solutions_.insert(solution_id);
>
>                  const  double*incumbent_solution  = model_->bestSolution();
>
>                  auto  solver  =dynamic_cast<OsiClpSolverInterface*> (model_->solver());
>                  ClpSimplex*current_lp_solver  = solver->getModelPtr();
>                  ClpSimplex  lp_solver{*current_lp_solver};
>                  solver->swapModelPtr(&lp_solver);
>
>                  double*lp_solver_solution  = lp_solver.primalColumnSolution();
>                  double*lp_solver_lower  = lp_solver.columnLower();
>                  double*lp_solver_upper  = lp_solver.columnUpper();
>                  int  lp_solver_number_columns  = lp_solver.numberColumns();
>
>                  memcpy(lp_solver_solution, incumbent_solution,
>                         lp_solver_number_columns  *sizeof(double));
>                  for  (int  i  = 0; i < lp_solver_number_columns; i++) {
>                      if  (solver->isInteger(i)) {
>                          double  x  = floor(lp_solver_solution[i] + 0.5);
>                          lp_solver_lower[i] = lp_solver_upper[i] = x;
>                      }
>                  }
>                  lp_solver.allSlackBasis();
>                  lp_solver.initialSolve();
>                  lp_solver.computeObjectiveValue(false);
>
>                  cbcPreProcessPointer->postProcess(*solver);
>
>                  // write solution
>                  // XXX: some wishful thinking here: hoping we would see the                  // original number of columns at this point.                  lp_solver_number_columns = lp_solver.numberColumns();
>                  const  double*solution  = solver->getColSolution();
>                  const  double*reduced_costs  = solver->getReducedCost();
>                  assert(solution && reduced_costs);
>                  auto  column_names  = lp_solver.columnNames();
>                  string::size_type  max_name_length  = 0;
>                  for  (auto  const&name: *column_names) {
>                      max_name_length =std::max(max_name_length, name.length());
>                  }
>
>                  stringstream  filename;
>                  filename << solution_directory_
>                           << solution_prefix_
>                           << external_solution_id_++
>                           <<".txt";
>
>                  {
>                      ofstream  out(filename.str());
>
>                      if  (!out.is_open()) {
>                          std::cout <<"Couldn't open "
>                                    << filename.str() <<" for writing "  << endl;
>                          abort();
>                      }
>
>                      char  buf[100];
>                      snprintf(buf,sizeof  buf,"Incumbent - objective value %.8f\n",
>                               lp_solver.getObjValue());
>                      out << buf;
>
>                      for  (int  i  = 0; i < lp_solver_number_columns; i++) {
>                          if  (fabs(solution[i]) > 1.0e-8) {
>                              auto&name  = (*column_names)[i];
>
>                              string  padding(std::max(max_name_length, name.length())
>                                             - name.length(),' ');
>
>                              char  buf[100];
>                              snprintf(buf,sizeof  buf,"%7d %s%s %15.8g %15.8g\n",
>                                       i,
>                                       name.c_str(),
>                                       padding.c_str(),
>                                       solution[i],
>                                       reduced_costs[i]);
>                              out << buf;
>                          }
>                      }
>                  }
>
>                  std::cout <<"[SolutionWriter] Solution saved to "  << filename.str() <<std::endl;
>
>                  solver->swapModelPtr(current_lp_solver);
>              }
>          }
>
>          return  noAction;// carry on      }
>
>      SolutionWriter() =delete;
>      SolutionWriter(const  SolutionWriter&) =default;
>      SolutionWriter(const  SolutionWriter&&) =delete;
>
>      SolutionWriter(CbcModel*model,
>                     CbcModel*original,
>                     const  string&solution_prefix  ="solution",
>                     const  string&solution_directory  =".")
>          : CbcEventHandler(model),
>            original_model_(original),
>            solution_prefix_(solution_prefix),
>            solution_directory_(solution_directory) {
>          if  (solution_directory_.back() !='/') solution_directory_.append("/");
>      }
>
>      virtual  CbcEventHandler*clone()const  override  {
>          return  new  SolutionWriter(*this);
>      }
> };
>
> Thanks in advance,
> Luís
>
>
> _______________________________________________
> Cbc mailing list
> Cbc at list.coin-or.org
> http://list.coin-or.org/mailman/listinfo/cbc


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://list.coin-or.org/pipermail/cbc/attachments/20160820/a925302d/attachment-0001.html>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: postprocess.cpp
Type: text/x-c
Size: 14888 bytes
Desc: not available
URL: <http://list.coin-or.org/pipermail/cbc/attachments/20160820/a925302d/attachment-0001.bin>


More information about the Cbc mailing list