[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