diff --git a/constraint_solver/constraint_solver.cc b/constraint_solver/constraint_solver.cc index bdf2ecef5c..dca3ce6b50 100644 --- a/constraint_solver/constraint_solver.cc +++ b/constraint_solver/constraint_solver.cc @@ -2130,7 +2130,7 @@ string Solver::GetName(const PropagationBaseObject* object) const { if (delegate_object != NULL) { const string& prefix = delegate_object->first; const PropagationBaseObject* delegate = delegate_object->second; - return prefix + "<" + delegate->DebugString() + ">"; + return prefix + "<" + delegate->name() + ">"; } return empty_name_; } diff --git a/constraint_solver/constraint_solver.h b/constraint_solver/constraint_solver.h index ed76c380dc..0d45b4be86 100644 --- a/constraint_solver/constraint_solver.h +++ b/constraint_solver/constraint_solver.h @@ -1271,35 +1271,73 @@ IntervalVar* MakeIntervalRelaxedMax(IntervalVar* const interval_var); // ----- Tree Monitor ----- // Creates a tree monitor that outputs a detailed overview of the - // decision phase in cpviz format. Automatically writes result to - // Files file_tree and file_visualization when the search finishes. + // decision phase in cpviz format. The XML data is written to files + // file_tree and file_visualization as the search finishes. SearchMonitor* MakeTreeMonitor(const IntVar* const* vars, int size, string const& file_tree, string const& file_visualization); // Creates a tree monitor that outputs a detailed overview of the - // decision phase in cpviz format. Automatically writes result to - // Files file_tree and file_visualization when the search finishes. + // decision phase in cpviz format. The XML data is written to files + // file_tree and file_visualization as the search finishes. SearchMonitor* MakeTreeMonitor(const vector& vars, string const& file_tree, string const& file_visualization); + // Creates a tree monitor that outputs a detailed overview of the + // decision phase in cpviz format. The XML data is written to files + // file_config, file_tree and file_visualization as the search + // finishes. + SearchMonitor* MakeTreeMonitor(const IntVar* const* vars, int size, + string const& file_config, + string const& file_tree, + string const& file_visualization); + + // Creates a tree monitor that outputs a detailed overview of the + // decision phase in cpviz format. The XML data is written to files + // file_config, file_tree and file_visualization as the search + // finishes. + SearchMonitor* MakeTreeMonitor(const vector& vars, + string const& file_config, + string const& file_tree, + string const& file_visualization); + +#if !defined(SWIG) + // Creates a tree monitor that outputs a detailed overview of the + // decision phase in cpviz format. The XML data is copied to tree_xml + // and visualization_xml as the search finishes. The tree monitor does + // not take ownership of either string. + SearchMonitor* MakeTreeMonitor(const IntVar* const* vars, int size, + string* const tree_xml, + string* const visualization_xml); // Creates a tree monitor that outputs a detailed overview of the // decision phase in cpviz format. The XML data is copied to tree_xml // and visualization_xml as the search finishes. The tree monitor does // not take ownership of either string. - SearchMonitor* MakeTreeMonitorString(const IntVar* const* vars, int size, - string* const tree_xml, - string* const visualization_xml); + SearchMonitor* MakeTreeMonitor(const vector& vars, + string* const tree_xml, + string* const visualization_xml); // Creates a tree monitor that outputs a detailed overview of the - // decision phase in cpviz format. The XML data is copied to tree_xml - // and visualization_xml as the search finishes. The tree monitor does - // not take ownership of either string. - SearchMonitor* MakeTreeMonitorString(const vector& vars, - string* const tree_xml, - string* const visualization_xml); + // decision phase in cpviz format. The XML data is copied to config_xml, + // tree_xml and visualization_xml as the search finishes. The tree monitor + // does not take ownership of these strings. + SearchMonitor* MakeTreeMonitor(const IntVar* const* vars, int size, + string* const config_xml, + string* const tree_xml, + string* const visualization_xml); + + // Creates a tree monitor that outputs a detailed overview of the + // decision phase in cpviz format. The XML data is copied to config_xml, + // tree_xml and visualization_xml as the search finishes. The tree monitor + // does not take ownership of these strings. + SearchMonitor* MakeTreeMonitor(const vector& vars, + string* const config_xml, + string* const tree_xml, + string* const visualization_xml); + +#endif // ----- Search Log ----- @@ -1990,7 +2028,7 @@ class PropagationBaseObject : public BaseObject { } // Naming - string name() const; + virtual string name() const; void set_name(const string& name); private: diff --git a/constraint_solver/constraint_solver.swig b/constraint_solver/constraint_solver.swig index 7d7877126f..746b36790a 100644 --- a/constraint_solver/constraint_solver.swig +++ b/constraint_solver/constraint_solver.swig @@ -14,7 +14,6 @@ %include base/base.swig %include exception.i - // Include the file we want to wrap a first time. #ifdef SWIGPYTHON %{ @@ -30,8 +29,6 @@ struct FailureProtect { } }; - - namespace operations_research { class CallPyDecisionBuilder : public DecisionBuilder { public: @@ -319,7 +316,6 @@ class PyDecisionBuilder(object): %rename (TemporalDisjunction) MakeTemporalDisjunction; %rename (TransitionConstraint) MakeTransitionConstraint; %rename (TreeMonitor) MakeTreeMonitor; -%rename (TreeMonitorString) MakeTreeMonitorString; %rename (TrueConstraint) MakeTrueConstraint; namespace operations_research { @@ -1065,7 +1061,7 @@ static bool PyCallbackBool(PyObject* pyfunc) { // Add display methods on Solver and remove DebugString method. %ignore Solver::DebugString; -%extend Solver { +%extend Solver { Constraint* TreeNoCycle(const vector& nexts, const vector& active, ResultCallback1* callback = NULL) { @@ -1634,7 +1630,6 @@ class LongResultCallback3 { %rename (makeTemporalDisjunction) MakeTemporalDisjunction; %rename (makeTrueConstraint) MakeTrueConstraint; %rename (makeTreeMonitor) MakeTreeMonitor; -%rename (makeTreeMonitorString) MakeTreeMonitorString; %rename (makeGuidedLocalSearch) MakeGuidedLocalSearch; %rename (value) *::Value; %rename (min) *::Min; diff --git a/constraint_solver/element.cc b/constraint_solver/element.cc index 30b237c7fc..ba42cced74 100644 --- a/constraint_solver/element.cc +++ b/constraint_solver/element.cc @@ -243,6 +243,9 @@ class IntExprElement : public BaseIntExprElement { IntExprElement(Solver* const s, const int64* const vals, int size, IntVar* const expr); virtual ~IntExprElement(); + virtual string name() const { + return StringPrintf("IntElement(values, %s)", expr_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("IntElement(values, %d, %s)", size_, expr_->DebugString().c_str()); @@ -298,6 +301,9 @@ class IncreasingIntExprElement : public BaseIntExpr { virtual void SetRange(int64 mi, int64 ma); virtual bool Bound() const { return (index_->Bound()); } // TODO(user) : improve me, the previous test is not always true + virtual string name() const { + return StringPrintf("IntElement(values, %s)", index_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("IntElement(values, %d, %s)", size_, index_->DebugString().c_str()); @@ -492,6 +498,9 @@ class IntExprFunctionElement : public BaseIntExprElement { IntVar* const expr, bool del); virtual ~IntExprFunctionElement(); + virtual string name() const { + return StringPrintf("IntFunctionElement(%s)", expr_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("IntFunctionElement(%s)", expr_->DebugString().c_str()); } @@ -921,6 +930,9 @@ class IntExprArrayElement : public BaseIntExpr { virtual void SetMax(int64 m); virtual void SetRange(int64 mi, int64 ma); virtual bool Bound() const; + virtual string name() const { + return StringPrintf("IntArrayElement(vars, %s)", var_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("IntArrayElement(vars, %d, %s)", size_, var_->DebugString().c_str()); diff --git a/constraint_solver/expressions.cc b/constraint_solver/expressions.cc index be372bdaa1..ab7c7d327c 100644 --- a/constraint_solver/expressions.cc +++ b/constraint_solver/expressions.cc @@ -2674,6 +2674,11 @@ class PlusIntExpr : public BaseIntExpr { right_->SetMax(m - left_->Min()); } virtual bool Bound() const { return (left_->Bound() && right_->Bound()); } + virtual string name() const { + return StringPrintf("(%s + %s)", + left_->name().c_str(), + right_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("(%s + %s)", left_->DebugString().c_str(), @@ -2738,6 +2743,10 @@ class PlusIntCstExpr : public BaseIntExpr { expr_->SetMax(m - value_); } virtual bool Bound() const { return (expr_->Bound()); } + virtual string name() const { + return StringPrintf("(%s + %" GG_LL_FORMAT "d)", + expr_->name().c_str(), value_); + } virtual string DebugString() const { return StringPrintf("(%s + %" GG_LL_FORMAT "d)", expr_->DebugString().c_str(), value_); @@ -2803,6 +2812,11 @@ class SubIntExpr : public BaseIntExpr { right_->SetMin(left_->Min() - m); } virtual bool Bound() const { return (left_->Bound() && right_->Bound()); } + virtual string name() const { + return StringPrintf("(%s - %s)", + left_->name().c_str(), + right_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("(%s - %s)", left_->DebugString().c_str(), @@ -2983,6 +2997,10 @@ class SubIntCstExpr : public BaseIntExpr { expr_->SetMin(value_ - m); } virtual bool Bound() const { return (expr_->Bound()); } + virtual string name() const { + return StringPrintf("(%" GG_LL_FORMAT "d - %s)", + value_, expr_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("(%" GG_LL_FORMAT "d - %s)", value_, expr_->DebugString().c_str()); @@ -3034,6 +3052,9 @@ class OppIntExpr : public BaseIntExpr { expr_->SetMin(-m); } virtual bool Bound() const { return (expr_->Bound()); } + virtual string name() const { + return StringPrintf("(-%s)", expr_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("(-%s)", expr_->DebugString().c_str()); } @@ -3079,6 +3100,10 @@ class TimesIntPosCstExpr : public BaseIntExpr { } virtual void SetMax(int64 m); virtual bool Bound() const { return (expr_->Bound()); } + virtual string name() const { + return StringPrintf("(%s * %" GG_LL_FORMAT "d)", + expr_->name().c_str(), value_); + } virtual string DebugString() const { return StringPrintf("(%s * %" GG_LL_FORMAT "d)", expr_->DebugString().c_str(), value_); @@ -3140,6 +3165,10 @@ class TimesIntNegCstExpr : public BaseIntExpr { } virtual void SetMax(int64 m); virtual bool Bound() const { return (expr_->Bound()); } + virtual string name() const { + return StringPrintf("(%s * %" GG_LL_FORMAT "d)", + expr_->name().c_str(), value_); + } virtual string DebugString() const { return StringPrintf("(%s * %" GG_LL_FORMAT "d)", expr_->DebugString().c_str(), value_); @@ -3345,6 +3374,11 @@ class TimesIntExpr : public BaseIntExpr { } virtual void SetMax(int64 m); virtual bool Bound() const; + virtual string name() const { + return StringPrintf("(%s * %s)", + left_->name().c_str(), + right_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("(%s * %s)", left_->DebugString().c_str(), @@ -3393,6 +3427,11 @@ class TimesIntPosExpr : public BaseIntExpr { } virtual void SetMax(int64 m); virtual bool Bound() const; + virtual string name() const { + return StringPrintf("(%s * %s)", + left_->name().c_str(), + right_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("(%s * %s)", left_->DebugString().c_str(), @@ -3439,6 +3478,11 @@ class TimesBooleanPosIntExpr : public BaseIntExpr { virtual void Range(int64* mi, int64* ma); virtual void SetRange(int64 mi, int64 ma); virtual bool Bound() const; + virtual string name() const { + return StringPrintf("(%s * %s)", + boolvar_->name().c_str(), + expr_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("(%s * %s)", boolvar_->DebugString().c_str(), @@ -3547,6 +3591,11 @@ class TimesBooleanIntExpr : public BaseIntExpr { virtual void Range(int64* mi, int64* ma); virtual void SetRange(int64 mi, int64 ma); virtual bool Bound() const; + virtual string name() const { + return StringPrintf("(%s * %s)", + boolvar_->name().c_str(), + expr_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("(%s * %s)", boolvar_->DebugString().c_str(), @@ -3750,6 +3799,10 @@ class DivIntPosCstExpr : public BaseIntExpr { virtual void SetMax(int64 m) { expr_->SetMax((m + 1) * value_ - 1); } + virtual string name() const { + return StringPrintf("(%s div %" GG_LL_FORMAT "d)", + expr_->name().c_str(), value_); + } virtual string DebugString() const { return StringPrintf("(%s div %" GG_LL_FORMAT "d)", expr_->DebugString().c_str(), value_); @@ -3801,6 +3854,9 @@ class IntAbs : public BaseIntExpr { virtual void WhenRange(Demon* d) { expr_->WhenRange(d); } + virtual string name() const { + return StringPrintf("IntAbs(%s)", expr_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("IntAbs(%s)", expr_->DebugString().c_str()); } @@ -3905,6 +3961,9 @@ class IntSquare : public BaseIntExpr { virtual void WhenRange(Demon* d) { expr_->WhenRange(d); } + virtual string name() const { + return StringPrintf("IntSquare(%s)", expr_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("IntSquare(%s)", expr_->DebugString().c_str()); } @@ -3945,6 +4004,9 @@ class PosIntSquare : public BaseIntExpr { virtual void WhenRange(Demon* d) { expr_->WhenRange(d); } + virtual string name() const { + return StringPrintf("PosIntSquare(%s)", expr_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("PosIntSquare(%s)", expr_->DebugString().c_str()); } @@ -3993,6 +4055,10 @@ class MinIntExpr : public BaseIntExpr { left_->SetMax(m); } } + virtual string name() const { + return StringPrintf("MinIntExpr(%s, %s)", left_->name().c_str(), + right_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("MinIntExpr(%s, %s)", left_->DebugString().c_str(), @@ -4051,6 +4117,10 @@ class MinCstIntExpr : public BaseIntExpr { virtual bool Bound() const { return (expr_->Bound() || expr_->Min() >= value_); } + virtual string name() const { + return StringPrintf("MinCstIntExpr(%s, %" GG_LL_FORMAT "d)", + expr_->name().c_str(), value_); + } virtual string DebugString() const { return StringPrintf("MinCstIntExpr(%s, %" GG_LL_FORMAT "d)", expr_->DebugString().c_str(), value_); @@ -4112,6 +4182,10 @@ class MaxIntExpr : public BaseIntExpr { left_->SetMax(m); right_->SetMax(m); } + virtual string name() const { + return StringPrintf("MaxIntExpr(%s, %s)", left_->name().c_str(), + right_->name().c_str()); + } virtual string DebugString() const { return StringPrintf("MaxIntExpr(%s, %s)", left_->DebugString().c_str(), @@ -4170,6 +4244,10 @@ class MaxCstIntExpr : public BaseIntExpr { virtual bool Bound() const { return (expr_->Bound() || expr_->Max() <= value_); } + virtual string name() const { + return StringPrintf("MaxCstIntExpr(%s, %" GG_LL_FORMAT "d)", + expr_->name().c_str(), value_); + } virtual string DebugString() const { return StringPrintf("MaxCstIntExpr(%s, %" GG_LL_FORMAT "d)", expr_->DebugString().c_str(), value_); @@ -4280,6 +4358,13 @@ class SimpleConvexPiecewiseExpr : public BaseIntExpr { } } } + virtual string name() const { + return StringPrintf( + "ConvexPiecewiseExpr(%s, ec = %" GG_LL_FORMAT "d, ed = %" + GG_LL_FORMAT "d, ld = %" GG_LL_FORMAT "d, lc = %" GG_LL_FORMAT "d)", + var_->name().c_str(), + early_cost_, early_date_, late_date_, late_cost_); + } virtual string DebugString() const { return StringPrintf( "ConvexPiecewiseExpr(%s, ec = %" GG_LL_FORMAT "d, ed = %" @@ -4349,6 +4434,11 @@ class SemiContinuousExpr : public BaseIntExpr { expr_->SetMax(y); } } + virtual string name() const { + return StringPrintf("SemiContinuous(%s, fixed_charge = %" GG_LL_FORMAT + "d, step = %" GG_LL_FORMAT "d)", + expr_->name().c_str(), fixed_charge_, step_); + } virtual string DebugString() const { return StringPrintf("SemiContinuous(%s, fixed_charge = %" GG_LL_FORMAT "d, step = %" GG_LL_FORMAT "d)", @@ -4401,6 +4491,11 @@ class SemiContinuousStepOneExpr : public BaseIntExpr { expr_->SetMax(m - fixed_charge_); } } + virtual string name() const { + return StringPrintf("SemiContinuousStepOne(%s, fixed_charge = %" + GG_LL_FORMAT "d)", + expr_->name().c_str(), fixed_charge_); + } virtual string DebugString() const { return StringPrintf("SemiContinuousStepOne(%s, fixed_charge = %" GG_LL_FORMAT "d)", @@ -4450,6 +4545,11 @@ class SemiContinuousStepZeroExpr : public BaseIntExpr { expr_->SetMax(0); } } + virtual string name() const { + return StringPrintf("SemiContinuousStepZero(%s, fixed_charge = %" + GG_LL_FORMAT "d)", + expr_->name().c_str(), fixed_charge_); + } virtual string DebugString() const { return StringPrintf("SemiContinuousStepZero(%s, fixed_charge = %" GG_LL_FORMAT "d)", diff --git a/constraint_solver/tree_monitor.cc b/constraint_solver/tree_monitor.cc index d7aaf8fa5f..b477feeb14 100644 --- a/constraint_solver/tree_monitor.cc +++ b/constraint_solver/tree_monitor.cc @@ -21,10 +21,105 @@ namespace operations_research { +const char* kConfigXml = + "\n" + "\n" + " \n" + " \n" + ""; + class XmlHelper; class TreeNode; +// String comparator that compares strings naturally, even those +// including integer numbers. +struct NaturalLess { + bool operator()(const string& s1, const string& s2) const { + int start = 0; + int length = std::min(s1.length(), s2.length()); + + // Ignore common characters at the beginning. + while (start < length && s1[start] == s2[start] && + (s1[start] < '0' || s1[start] > '9')) { + ++start; + } + + // If one string is the substring of another, then the shorter string is + // smaller. + if (start == length) { + return s1.length() < s2.length(); + } + + int number_s1 = 0; + int number_s2 = 0; + + // Extract a number if we have one. + for (int i = start; i < s1.length() && s1[i] >= '0' && s1[i] <= '9'; ++i) { + number_s1 = number_s1 * 10 + (s1[i] - '0'); + } + + for (int i = start; i < s2.length() && s2[i] >= '0' && s2[i] <= '9'; ++i) { + number_s2 = number_s2 * 10 + (s2[i] - '0'); + } + + // Do a numerical comparison only if there are two numbers. + if (number_s1 && number_s2) { + return number_s1 < number_s2; + } + + return s1.compare(s2) < 0; + } +}; + + +// TreeDecisionVisitor is used to gain access to the variables and values +// involved in a decision. +class TreeDecisionVisitor : public DecisionVisitor { + public: + TreeDecisionVisitor() {} + virtual ~TreeDecisionVisitor() {} + + virtual void VisitSetVariableValue(IntVar* const var, int64 value) { + name_ = var->name(); + value_ = value; + valid_ = true; + } + + virtual void VisitSplitVariableDomain(IntVar* const var, + int64 value, + bool start_with_lower_half) { + name_ = var->name(); + value_ = value; + valid_ = true; + } + + virtual void VisitUnknownDecision() { + valid_ = false; + } + + // Indicates whether name and value can be called. + bool valid() { return valid_; } + + // Returns the name of the current variable. + const string& name() { + CHECK(valid_); + return name_; + } + + // Returns the value of the current variable. + int64 value() { + CHECK(valid_); + return value_; + } + + private: + string name_; + int64 value_; + bool valid_; +}; + // The TreeMonitor may be attached to a search to obtain an output in CPViz // format (http://sourceforge.net/projects/cpviz/). It produces both the // Tree XML file as well as the Visualization XML. CPViz can then be used @@ -46,6 +141,14 @@ class TreeMonitor: public SearchMonitor { TreeMonitor(Solver* const solver, const IntVar* const* vars, int size, string* const tree_xml, string* const visualization_xml); + TreeMonitor(Solver* const solver, const IntVar* const* vars, int size, + string const& filename_config, string const& filename_tree, + string const& filename_visualizer); + + TreeMonitor(Solver* const solver, const IntVar* const* vars, int size, + string* const config_xml, string* const tree_xml, + string* const visualization_xml); + ~TreeMonitor(); // Callback for the beginning of the search. @@ -55,7 +158,6 @@ class TreeMonitor: public SearchMonitor { // The decision is empty if a solution has been reached. virtual void EndNextDecision(DecisionBuilder* const decision_builder, Decision* const decision); - // Callback for the end of the search. virtual void ExitSearch(); @@ -73,43 +175,31 @@ class TreeMonitor: public SearchMonitor { // after a solution is found. virtual void RefuteDecision(Decision* const decision); - private: - // Strips the additional descriptions from IntVar and returns the - // original name. - static string BaseName(string const& name); + // Strips characters that cause problems with CPViz from attributes + static string StripSpecialCharacters(string attribute); + private: // Registers vars and sets Min and Max accordingly. void Init(const IntVar* const* vars, int size); + string* const config_xml_; TreeNode* current_node_; + const string filename_config_; const string filename_tree_; const string filename_visualizer_; int id_counter_; + string last_decision_; + hash_map last_value_; string last_variable_; int64 min_; int64 max_; scoped_ptr root_node_; - hash_map last_value_; int search_level_; - IntVarMap vars_; string* const tree_xml_; + IntVarMap vars_; string* const visualization_xml_; }; -SearchMonitor* Solver::MakeTreeMonitorString(const IntVar* const* vars, - int size, string* const tree_xml, - string* const visualization_xml) { - return RevAlloc(new TreeMonitor(this, vars, size, tree_xml, - visualization_xml)); -} - -SearchMonitor* Solver::MakeTreeMonitorString(const vector& vars, - string* const tree_xml, - string* const visualization_xml) { - return RevAlloc(new TreeMonitor(this, vars.data(), vars.size(), tree_xml, - visualization_xml)); -} - SearchMonitor* Solver::MakeTreeMonitor(const IntVar* const* vars, int size, string const& file_tree, string const& file_visualization) { @@ -124,15 +214,64 @@ SearchMonitor* Solver::MakeTreeMonitor(const vector& vars, file_visualization)); } +SearchMonitor* Solver::MakeTreeMonitor(const IntVar* const* vars, int size, + string const& file_config, + string const& file_tree, + string const& file_visualization) { + return RevAlloc(new TreeMonitor(this, vars, size, file_config, file_tree, + file_visualization)); +} + +SearchMonitor* Solver::MakeTreeMonitor(const vector& vars, + string const& file_config, + string const& file_tree, + string const& file_visualization) { + return RevAlloc(new TreeMonitor(this, vars.data(), vars.size(), file_config, + file_tree, file_visualization)); +} + +#if !defined(SWIG) +SearchMonitor* Solver::MakeTreeMonitor(const IntVar* const* vars, + int size, string* const tree_xml, + string* const visualization_xml) { + return RevAlloc(new TreeMonitor(this, vars, size, tree_xml, + visualization_xml)); +} + +SearchMonitor* Solver::MakeTreeMonitor(const vector& vars, + string* const tree_xml, + string* const visualization_xml) { + return RevAlloc(new TreeMonitor(this, vars.data(), vars.size(), tree_xml, + visualization_xml)); +} + +SearchMonitor* Solver::MakeTreeMonitor(const IntVar* const* vars, + int size, string* const config_xml, + string* const tree_xml, + string* const visualization_xml) { + return RevAlloc(new TreeMonitor(this, vars, size, config_xml, tree_xml, + visualization_xml)); +} + +SearchMonitor* Solver::MakeTreeMonitor(const vector& vars, + string* const config_xml, + string* const tree_xml, + string* const visualization_xml) { + return RevAlloc(new TreeMonitor(this, vars.data(), vars.size(), config_xml, + tree_xml, visualization_xml)); +} +#endif + // Represents a node in the decision phase. Can either be the root node, a // successful attempt, a failure or a solution. class TreeNode { public: - typedef hash_map > DomainMap; + typedef std::map, NaturalLess> DomainMap; enum TreeNodeType { ROOT, TRY, FAIL, SOLUTION }; TreeNode(TreeNode* parent, int id) - : id_(id), + : cycles_(1), + id_(id), name_(""), node_type_(TRY), parent_(parent) {} @@ -184,51 +323,30 @@ class TreeNode { void set_node_type(TreeNodeType node_type) { node_type_ = node_type; } // Returns the parent node or NULL if node has no parent. - TreeNode* parent() const { return parent_; } - - // Return the first child or NULL if it does not exist - TreeNode const* FirstChild() const { - return children_.size() ? children_[0] : NULL; + TreeNode* Parent() { + return --cycles_ ? this : parent_; } - // Checks whether the provided domain matches the domain of the node. - // Disregards changes of the currently active variable. - bool DomainEquals(TreeMonitor::IntVarMap const& vars) { - for (ConstIter it(vars); !it.at_end(); ++it) { - // Do not check changes in the current variable, as we want to skip - // a possible change of the decision variable to see if other variables - // have changed. - if (it->first == name_) { - continue; - } - - int counter = 0; - scoped_ptr intvar_it( - it->second->MakeDomainIterator(false)); - - for (intvar_it->Init(); intvar_it->Ok(); intvar_it->Next()) { - if (domain_[it->first][counter++] != intvar_it->Value()) { - return false; - } - } - - if (counter != (domain_[it->first]).size()) { - return false; - } - } - return true; + // Adds a cycle instead of duplicate nodes. + void AddCycle() { + cycles_++; } // Adds a new child, initializes it and returns the corresponding pointer. - bool AddChild(int id, string name, hash_map const& last_value, + bool AddChild(int id, const string& name, + hash_map const& last_value, bool is_final_node, TreeMonitor::IntVarMap const& vars, TreeNode** child) { CHECK_NOTNULL(child); - for (int i = 0; i < children_.size(); ++i) { - // Reuse the branch if the domains match. - if (children_[i]->DomainEquals(vars)) { - *child = children_[i]; - return false; + if (!is_final_node) { + for (int i = 0; i < children_.size(); ++i) { + // Reuse existing branch if possible + if (children_[i]->name_ == name && + branch_values_[i] == FindOrDie(last_value, name_)) { + children_[i]->AddCycle(); + *child = children_[i]; + return false; + } } } @@ -236,8 +354,9 @@ class TreeNode { tree_node->set_name(name); tree_node->SetDomain(vars); children_.push_back(tree_node); - branch_values_.push_back(last_value.find(name_)->second); + branch_values_.push_back(FindOrDie(last_value, name_)); *child = tree_node; + return true; } @@ -245,26 +364,28 @@ class TreeNode { void GenerateVisualizationXML(XmlHelper* const visualization_writer) { CHECK_NOTNULL(visualization_writer); - // The root node referes to the imaginary tree node '-1'. - const int kRootTreeNodeId = -1; // There currently is only support for one visualizer. - const int kVisualizerState = 1; + const int kVisualizerState = 0; visualization_writer->StartElement("state"); visualization_writer->AddAttribute("id", id_); - visualization_writer->AddAttribute("tree_node", - id_ ? id_ : kRootTreeNodeId); + visualization_writer->AddAttribute("tree_node", id_); visualization_writer->StartElement("visualizer_state"); visualization_writer->AddAttribute("id", kVisualizerState); - const DomainMap domain = parent_ ? parent_->domain() : domain_; + int index = 0; + int name = -1; - for (ConstIter it(domain); !it.at_end(); ++it) { + for (ConstIter it(domain_); !it.at_end(); ++it) { vector current = it->second; visualization_writer->StartElement(current.size() == 1 ? "integer" : "dvar"); - visualization_writer->AddAttribute("index", it->first); + visualization_writer->AddAttribute("index", ++index); + + if (it->first == name_) { + name = index; + } if (current.size() > 1 && current.size() == (current.back() - current[0] + 1)) { @@ -291,14 +412,14 @@ class TreeNode { if (node_type_ == FAIL) { visualization_writer->StartElement("failed"); - visualization_writer->AddAttribute("index", name_); + visualization_writer->AddAttribute("index", name); visualization_writer->AddAttribute( "value", StringPrintf("%" GG_LL_FORMAT "d", parent_->branch_value(0))); visualization_writer->EndElement(); // failed } else if (node_type_ == TRY) { visualization_writer->StartElement("focus"); - visualization_writer->AddAttribute("index", name_); + visualization_writer->AddAttribute("index", name); visualization_writer->EndElement(); // focus } @@ -327,10 +448,11 @@ class TreeNode { TreeNode* child = children_[i]; tree_writer->StartElement(kElementName[child->node_type_]); tree_writer->AddAttribute("id", child->id_); - tree_writer->AddAttribute("parent", id()); - tree_writer->AddAttribute("name", name()); + tree_writer->AddAttribute("parent", id_); + tree_writer->AddAttribute("name", + TreeMonitor::StripSpecialCharacters(name_)); - if (name().empty()) { + if (name_.empty()) { tree_writer->AddAttribute("size", "0"); tree_writer->AddAttribute("value", "0"); } else { @@ -338,6 +460,7 @@ class TreeNode { const DomainMap domain = parent_ && parent_->children_.size() ? parent_->children_[0]->domain() : domain_; + tree_writer->AddAttribute( "size", StringPrintf("%zu", FindOrDie(domain, name_).size())); tree_writer->AddAttribute("value", StringPrintf("%" GG_LL_FORMAT "d", @@ -360,6 +483,7 @@ class TreeNode { private: vector branch_values_; vector children_; + int cycles_; DomainMap domain_; const int id_; string name_; @@ -371,7 +495,9 @@ TreeMonitor::TreeMonitor(Solver* const solver, const IntVar* const* vars, int size, string const& filename_tree, string const& filename_visualizer) : SearchMonitor(solver), + config_xml_(NULL), current_node_(NULL), + filename_config_(""), filename_tree_(filename_tree), filename_visualizer_(filename_visualizer), root_node_(NULL), @@ -388,7 +514,9 @@ TreeMonitor::TreeMonitor(Solver* const solver, const IntVar* const* vars, int size, string* const tree_xml, string* const visualization_xml) : SearchMonitor(solver), + config_xml_(NULL), current_node_(NULL), + filename_config_(""), filename_tree_(""), filename_visualizer_(""), root_node_(NULL), @@ -403,6 +531,49 @@ TreeMonitor::TreeMonitor(Solver* const solver, const IntVar* const* vars, Init(vars, size); } +TreeMonitor::TreeMonitor(Solver* const solver, const IntVar* const* vars, + int size, string const& filename_config, + string const& filename_tree, + string const& filename_visualizer) + : SearchMonitor(solver), + config_xml_(NULL), + current_node_(NULL), + filename_config_(filename_config), + filename_tree_(filename_tree), + filename_visualizer_(filename_visualizer), + root_node_(NULL), + search_level_(0), + tree_xml_(NULL), + visualization_xml_(NULL) { + CHECK_NOTNULL(solver); + CHECK_NOTNULL(vars); + + Init(vars, size); +} + +TreeMonitor::TreeMonitor(Solver* const solver, const IntVar* const* vars, + int size, string* const config_xml, + string* const tree_xml, + string* const visualization_xml) + : SearchMonitor(solver), + config_xml_(config_xml), + current_node_(NULL), + filename_config_(""), + filename_tree_(""), + filename_visualizer_(""), + root_node_(NULL), + search_level_(0), + tree_xml_(tree_xml), + visualization_xml_(visualization_xml) { + CHECK_NOTNULL(solver); + CHECK_NOTNULL(vars); + CHECK_NOTNULL(config_xml); + CHECK_NOTNULL(tree_xml); + CHECK_NOTNULL(visualization_xml); + + Init(vars, size); +} + TreeMonitor::~TreeMonitor() {} void TreeMonitor::Init(const IntVar* const* vars, int size) { @@ -414,7 +585,7 @@ void TreeMonitor::Init(const IntVar* const* vars, int size) { min_ = std::min(min_, vars[i]->Min()); max_ = std::max(max_, vars[i]->Max()); - string name = BaseName(vars[i]->name()); + string name = vars[i]->name(); if (name.empty()) { name = StringPrintf("%d", i); @@ -441,62 +612,32 @@ void TreeMonitor::EnterSearch() { void TreeMonitor::EndNextDecision(DecisionBuilder* const decision_builder, Decision* const decision) { - const string kDomainStartToken = "("; - const string kDomainEndToken = ") == "; - if (decision) { - string value_str; + TreeDecisionVisitor visitor; + decision->Accept(&visitor); - // Extract the required data from the DebugString, as there is no obvious - // way to extract the name and the value of the variable affected by the - // decision. - // TODO(user): Find better solution. - // Debug string is "[Name(Domain) == Value]". - string debug_string = decision->DebugString(); - - // Get the name of the variable. - size_t pos = debug_string.find(kDomainStartToken); - if (pos != string::npos) { - last_variable_ = debug_string.substr(1, pos - 1); - } - - // Get the value of the variable. - pos = debug_string.find(kDomainEndToken); - - if (pos != string::npos) { - string value_str = debug_string.substr(pos + 5, - debug_string.length() - pos - 6); - - last_value_[last_variable_] = atol(value_str.c_str()); + if (visitor.valid()) { + last_variable_ = visitor.name(); + last_value_[last_variable_] = visitor.value(); } } - if (current_node_->AddChild(id_counter_, last_variable_, last_value_, vars_, - ¤t_node_)) { - ++id_counter_; + if (!decision || decision->DebugString() != last_decision_) { + if (current_node_->AddChild(id_counter_, last_variable_, last_value_, + !decision , vars_, ¤t_node_)) { + ++id_counter_; + } + } else { + current_node_->AddCycle(); } + last_decision_ = decision ? decision->DebugString() : ""; + if (!decision) { current_node_->set_node_type(TreeNode::SOLUTION); } } -string TreeMonitor::BaseName(string const& name) { - // Some IntVar desciptors return "Var(Name(DebugString))". - // Extract the name. - int start = name.find('('); - - if (start != string::npos) { - int end = name.find('(', start + 1); - - if (end != string::npos) { - return name.substr(start+1, end-start-1); - } - } - - return name; -} - void TreeMonitor::RefuteDecision(Decision* const decision) { // Called when the solver goes up one level in the tree and undos a // change in the tree. As we have added multiple levels for both 'fail' @@ -507,23 +648,22 @@ void TreeMonitor::RefuteDecision(Decision* const decision) { // Solver calls RefuteDecision even on success if it looks for // more than one solution. // Just go back to the previous decision. - current_node_ = current_node_->parent(); - } else if (current_node_->node_type() == TreeNode::TRY - && current_node_->id() == id_counter_ -1) { + current_node_ = current_node_->Parent(); + } else if (current_node_->node_type() == TreeNode::TRY) { // Add an extra node in case of a failure, so we can see the failed // decision. current_node_->set_node_type(TreeNode::TRY); - if (current_node_->AddChild(id_counter_, current_node_->parent()->name(), - last_value_, vars_, ¤t_node_)) { + if (current_node_->AddChild(id_counter_, last_variable_, + last_value_, true, vars_, ¤t_node_)) { ++id_counter_; } current_node_->set_node_type(TreeNode::FAIL); - current_node_ = current_node_->parent(); + current_node_ = current_node_->Parent(); } - current_node_ = current_node_->parent(); + current_node_ = current_node_->Parent(); } string TreeMonitor::GenerateTreeXML() const { @@ -555,11 +695,14 @@ string TreeMonitor::GenerateVisualizationXML() const { xml_writer.AddAttribute("xsi:noNamespaceSchemaLocation", "visualization.xsd"); xml_writer.StartElement("visualizer"); - xml_writer.AddAttribute("id", 1); + xml_writer.AddAttribute("id", 0); xml_writer.AddAttribute("type", "vector"); xml_writer.AddAttribute("display", "expanded"); xml_writer.AddAttribute("min", StringPrintf("%" GG_LL_FORMAT "d", min_)); xml_writer.AddAttribute("max", StringPrintf("%" GG_LL_FORMAT "d", max_)); + xml_writer.AddAttribute("width", StringPrintf("%zd", vars_.size())); + xml_writer.AddAttribute("height", + StringPrintf("%" GG_LL_FORMAT "d", max_ - min_ + 1)); xml_writer.EndElement(); // End of element: visualizer root_node_->GenerateVisualizationXML(&xml_writer); @@ -600,10 +743,51 @@ void TreeMonitor::ExitSearch() { } else { LG << "Failed to gain write access to file: " << filename_tree_; } + + if(!filename_config_.empty()) { + std::ofstream file_config_(filename_config_.c_str()); + + if (file_config_.is_open()) { + file_config_ << kConfigXml; + file_config_.close(); + } else { + LG << "Failed to gain write access to file: " << filename_config_; + } + } } else { + CHECK_NOTNULL(tree_xml_); *tree_xml_ = GenerateTreeXML(); + + CHECK_NOTNULL(visualization_xml_); *visualization_xml_ = GenerateVisualizationXML(); + + if (config_xml_) { + *config_xml_ = kConfigXml; + } } } } + +string TreeMonitor::StripSpecialCharacters(string attribute) { + // Numbers, characters, dashes, underscored, brackets, colons, slashes, + // periods, question marks, and parentheses are allowed + const char* kAllowedCharacters = "0123456789abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ_-[]:/.?()"; + + set character_set; + + char* allowed = const_cast(kAllowedCharacters); + + while (*allowed) { + character_set.insert(*(allowed++)); + } + + for (int i = 0; i < attribute.length(); ++i) { + if (character_set.find(attribute[i]) == character_set.end()) { + attribute.replace(i,1,"_"); + } + } + + return attribute; +} } // namespace