[Cbc] Writing intermediate solutions via a CbcEventHandler

Luís Borges de Oliveira lbo at siscog.pt
Fri Aug 19 13:48:01 EDT 2016


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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://list.coin-or.org/pipermail/cbc/attachments/20160819/fd01385e/attachment.html>


More information about the Cbc mailing list