diff --git a/constraint_solver/constraint_solver.h b/constraint_solver/constraint_solver.h index 4ea121eab1..0340246dbf 100644 --- a/constraint_solver/constraint_solver.h +++ b/constraint_solver/constraint_solver.h @@ -812,7 +812,7 @@ class Solver { IntVar* MakeIsLessVar(IntExpr* const left, IntExpr* const right); // b == (left < right) Constraint* MakeIsLessCt(IntExpr* const left, IntExpr* const right, - IntVar* const b); + IntVar* const b); // left < right Constraint* MakeLess(IntVar* const left, IntVar* const right); // expr < value @@ -1077,7 +1077,7 @@ class Solver { // // This is very useful to implement propagators that may only modify // the start min or end min. -IntervalVar* MakeIntervalRelaxedMax(IntervalVar* const interval_var); + IntervalVar* MakeIntervalRelaxedMax(IntervalVar* const interval_var); // ----- scheduling constraints ----- @@ -1198,6 +1198,23 @@ IntervalVar* MakeIntervalRelaxedMax(IntervalVar* const interval_var); // Create a objective with a given sense (true = maximization). OptimizeVar* MakeOptimize(bool maximize, IntVar* const v, int64 step); + // Create a minimization weighted objective. The actual objective is + // scalar_prod(vars, weights). + OptimizeVar* MakeWeightedMinimize(const vector& vars, + const vector& weights, + int64 step); + + // Create a maximization weigthed objective. + OptimizeVar* MakeWeightedMaximize(const vector& vars, + const vector& weights, + int64 step); + + // Create a weighted objective with a given sense (true = maximization). + OptimizeVar* MakeWeightedOptimize(bool maximize, + const vector& vars, + const vector& weights, + int64 step); + // ----- Meta-heuristics ----- // Search monitors which try to get the search out of local optima. @@ -1380,28 +1397,39 @@ IntervalVar* MakeIntervalRelaxedMax(IntervalVar* const interval_var); #endif + // TODO(user): DEPRECATE API of MakeSearchLog(.., IntVar* var,..). // ----- Search Log ----- + // The SearchMonitors below will display a periodic search log + // on LOG(INFO) every branch_count branches explored. - // Create a search monitor that will display a periodic search log - // on LOG(INFO). - SearchMonitor* MakeSearchLog(int period); + SearchMonitor* MakeSearchLog(int branch_count); - // Create a search monitor that will display a periodic search log - // on LOG(INFO). At each solution, this monitor will also display - // the objective value. - SearchMonitor* MakeSearchLog(int period, IntVar* const objective); + // At each solution, this monitor also display the objective value. + SearchMonitor* MakeSearchLog(int branch_count, IntVar* const objective); - // Create a search monitor that will call the display callback at each - // solution. - SearchMonitor* MakeSearchLog(int period, + // At each solution, this monitor will also display result of @p + // display_callback. + SearchMonitor* MakeSearchLog(int branch_count, ResultCallback* display_callback); - // Create a search monitor that will call the display callback and display - // the objective value at each solution. - SearchMonitor* MakeSearchLog(int period, + // At each solution, this monitor will display the objective value and the + // result of @p display_callback. + SearchMonitor* MakeSearchLog(int branch_count, IntVar* objective, ResultCallback* display_callback); + // OptimizeVar Search Logs + // At each solution, this monitor will also display the objective->Print(). + + SearchMonitor* MakeSearchLog(int branch_count, OptimizeVar* const objective); + + // Create a search monitor that will also print the result of the + // display callback. + SearchMonitor* MakeSearchLog(int branch_count, + OptimizeVar* const objective, + ResultCallback* display_callback); + + // ----- Search Trace ------ // Create a search monitor that will trace precisely the behavior of the @@ -1588,10 +1616,10 @@ IntervalVar* MakeIntervalRelaxedMax(IntervalVar* const interval_var); // Returns a decision builder for which the left-most leaf corresponds // to assignment, the rest of the tree being explored using 'db'. DecisionBuilder* MakeDecisionBuilderFromAssignment( - Assignment* const assignment, - DecisionBuilder* const db, - const IntVar* const* vars, - int size); + Assignment* const assignment, + DecisionBuilder* const db, + const IntVar* const* vars, + int size); // SolveOnce will collapse a search tree described by a 'db' decision // builder, and a set of monitors and wrap it into a single point. @@ -2587,6 +2615,9 @@ class SolutionCollector : public SearchMonitor { DISALLOW_COPY_AND_ASSIGN(SolutionCollector); }; +// TODO(user): Refactor this into an Objective class: +// - print methods for AtNode and AtSolution. +// - support for weighted objective and lexicographical objective. // ---------- Objective Management ---------- @@ -2601,15 +2632,15 @@ class OptimizeVar : public SearchMonitor { // Returns the best value found during search. int64 best() const { return best_; } - // Returns the variable passed in the ctor. + // Returns the variable that is optimized. IntVar* Var() const { return var_; } - // Internal methods virtual void EnterSearch(); virtual void RestartSearch(); virtual void RefuteDecision(Decision* d); virtual bool AtSolution(); virtual bool AcceptSolution(); + virtual string Print() const; virtual string DebugString() const; void ApplyBound(); @@ -2680,8 +2711,8 @@ class IntervalVar : public PropagationBaseObject { static const int64 kMaxValidValue; IntervalVar(Solver* const solver, const string& name) : PropagationBaseObject(solver), - start_expr_(NULL), duration_expr_(NULL), end_expr_(NULL), - performed_expr_(NULL) { + start_expr_(NULL), duration_expr_(NULL), end_expr_(NULL), + performed_expr_(NULL) { set_name(name); } virtual ~IntervalVar() {} diff --git a/constraint_solver/constraint_solveri.h b/constraint_solver/constraint_solveri.h index c46c7c8618..c24a17cade 100644 --- a/constraint_solver/constraint_solveri.h +++ b/constraint_solver/constraint_solveri.h @@ -755,7 +755,9 @@ class SymmetryBreaker : public DecisionVisitor { class SearchLog : public SearchMonitor { public: - SearchLog(Solver* const s, IntVar* const obj, + SearchLog(Solver* const s, + OptimizeVar* const obj, + IntVar* const var, ResultCallback* display_callback, int period); virtual ~SearchLog(); @@ -778,7 +780,8 @@ class SearchLog : public SearchMonitor { const int period_; scoped_ptr timer_; - IntVar* const obj_; + IntVar* const var_; + OptimizeVar* const obj_; scoped_ptr > display_callback_; int nsol_; int64 tick_; diff --git a/constraint_solver/search.cc b/constraint_solver/search.cc index 3f5e4c023c..dea48ced56 100644 --- a/constraint_solver/search.cc +++ b/constraint_solver/search.cc @@ -32,12 +32,15 @@ namespace operations_research { // ---------- Search Log --------- -SearchLog::SearchLog(Solver* const s, IntVar* const obj, +SearchLog::SearchLog(Solver* const s, + OptimizeVar* const obj, + IntVar* const var, ResultCallback* display_callback, int period) : SearchMonitor(s), period_(period), timer_(new WallTimer), + var_(var), obj_(obj), display_callback_(display_callback), nsol_(0), @@ -48,6 +51,7 @@ SearchLog::SearchLog(Solver* const s, IntVar* const obj, max_depth_(0), sliding_min_depth_(0), sliding_max_depth_(0) { + CHECK(obj == NULL || var == NULL) << "Either var or obj need to be NULL."; if (display_callback_ != NULL) { display_callback_->CheckIsRepeatable(); } @@ -81,9 +85,18 @@ bool SearchLog::AtSolution() { Maintain(); const int depth = solver()->SearchDepth(); string obj_str = ""; + int64 current = 0; + bool objective_updated = false; if (obj_ != NULL) { - const int64 current = obj_->Value(); - obj_str = StringPrintf("objective value = %" GG_LL_FORMAT "d, ", current); + current = obj_->Var()->Value(); + obj_str = obj_->Print(); + objective_updated = true; + } else if (var_!= NULL) { + current = var_->Value(); + StringAppendF(&obj_str, "%" GG_LL_FORMAT "d", current); + objective_updated = true; + } + if (objective_updated) { if (current >= objective_min_) { StringAppendF(&obj_str, "objective minimum = %" GG_LL_FORMAT "d, ", @@ -233,21 +246,30 @@ string SearchLog::MemoryUsage() { } SearchMonitor* Solver::MakeSearchLog(int period) { - return RevAlloc(new SearchLog(this, NULL, NULL, period)); + return RevAlloc(new SearchLog(this, NULL, NULL, NULL, period)); } -SearchMonitor* Solver::MakeSearchLog(int period, IntVar* const obj) { - return RevAlloc(new SearchLog(this, obj, NULL, period)); +SearchMonitor* Solver::MakeSearchLog(int period, IntVar* const var) { + return RevAlloc(new SearchLog(this, NULL, var, NULL, period)); } SearchMonitor* Solver::MakeSearchLog(int period, ResultCallback* display_callback) { - return RevAlloc(new SearchLog(this, NULL, display_callback, period)); + return RevAlloc(new SearchLog(this, NULL, NULL, display_callback, period)); } -SearchMonitor* Solver::MakeSearchLog(int period, IntVar* const obj, +SearchMonitor* Solver::MakeSearchLog(int period, IntVar* const var, ResultCallback* display_callback) { - return RevAlloc(new SearchLog(this, obj, display_callback, period)); + return RevAlloc(new SearchLog(this, NULL, var, display_callback, period)); +} + +SearchMonitor* Solver::MakeSearchLog(int period, OptimizeVar* const obj) { + return RevAlloc(new SearchLog(this, obj, NULL, NULL, period)); +} + +SearchMonitor* Solver::MakeSearchLog(int period, OptimizeVar* const obj, + ResultCallback* display_callback) { + return RevAlloc(new SearchLog(this, obj, NULL, display_callback, period)); } // ---------- Search Trace ---------- @@ -2148,6 +2170,10 @@ bool OptimizeVar::AtSolution() { return true; } +string OptimizeVar::Print() const { + return StringPrintf("objective value = %" GG_LL_FORMAT "d, ", var_->Value()); +} + string OptimizeVar::DebugString() const { string out; if (maximize_) { @@ -2174,6 +2200,79 @@ OptimizeVar* Solver::MakeOptimize(bool maximize, IntVar* const v, int64 step) { return RevAlloc(new OptimizeVar(this, maximize, v, step)); } +class WeightedOptimizeVar: public OptimizeVar { + public: + WeightedOptimizeVar(Solver* solver, + bool maximize, + const vector& sub_objectives, + const vector& weights, + int64 step) + : OptimizeVar(solver, + maximize, + solver->MakeScalProd(sub_objectives, weights)->Var(), + step), + size_(weights.size()) { + CHECK_EQ(sub_objectives.size(), weights.size()); + sub_objectives_.reset(new IntVar*[size_]); + memcpy(sub_objectives_.get(), + sub_objectives.data(), size_ * sizeof(*sub_objectives.data())); + weights_.reset(new int64[size_]); + memcpy(weights_.get(), + weights.data(), size_ * sizeof(*weights.data())); + } + + virtual ~WeightedOptimizeVar() {} + virtual string Print() const; + + private: + const int64 size_; + scoped_array sub_objectives_; + scoped_array weights_; + + DISALLOW_COPY_AND_ASSIGN(WeightedOptimizeVar); +}; + +string WeightedOptimizeVar::Print() const { + string result(OptimizeVar::Print()); + StringAppendF(&result, "\nWeighted Objective:\n"); + for (int i = 0; i < size_; ++i) { + StringAppendF(&result, "Variable %s,\tvalue %lld,\tweight %lld\n", + sub_objectives_[i]->name().c_str(), + sub_objectives_[i]->Value(), + weights_[i]); + } + return result; +} + +OptimizeVar* Solver::MakeWeightedOptimize(bool maximize, + const vector& sub_objectives, + const vector& weights, + int64 step) { + return RevAlloc(new WeightedOptimizeVar(this, + maximize, + sub_objectives, weights, + step)); +} + +OptimizeVar* Solver::MakeWeightedMinimize(const vector& sub_objectives, + const vector& weights, + int64 step) { + return RevAlloc(new WeightedOptimizeVar(this, + false, + sub_objectives, weights, + step)); +} + +OptimizeVar* Solver::MakeWeightedMaximize(const vector& sub_objectives, + const vector& weights, + int64 step) { + return RevAlloc(new WeightedOptimizeVar(this, + true, + sub_objectives, weights, + step)); +} + + // ---------- Metaheuristics --------- class Metaheuristic : public SearchMonitor {