diff --git a/include/mp/problem-builder.h b/include/mp/problem-builder.h index 943d10410..b8c975106 100644 --- a/include/mp/problem-builder.h +++ b/include/mp/problem-builder.h @@ -31,106 +31,10 @@ #include "mp/error.h" #include "mp/problem-base.h" +#include "mp/suffix.h" namespace mp { -class Suffix { - private: - std::string name_; - int kind_; - std::vector values_; - - public: - explicit Suffix(const std::string &name, int kind = 0, std::size_t size = 0) - : name_(name), kind_(kind), values_(size) {} - - const char *name() const { return name_.c_str(); } - - int kind() const { return kind_; } - - void Swap(Suffix &other) { - name_.swap(other.name_); - values_.swap(other.values_); - } - - int value(std::size_t index) const { - assert(index <= values_.size()); - return values_[index]; - } - void set_value(std::size_t index, int value) { - assert(index <= values_.size()); - values_[index] = value; - } - - // Iterates over nonzero suffix values and sends them to the visitor. - template - void VisitValues(Visitor &visitor) const { - for (std::size_t i = 0, n = values_.size(); i < n; ++i) { - int value = values_[i]; - if (value != 0) - visitor.Visit(i, value); - } - } -}; - -class SuffixSet { - private: - int kind_; - - struct SuffixLess { - bool operator()(const Suffix &lhs, const Suffix &rhs) const { - return std::strcmp(lhs.name(), rhs.name()) < 0; - } - }; - - typedef std::set Set; - Set set_; - - public: - explicit SuffixSet(int kind = 0) : kind_(kind) { - assert(0 <= kind && kind <= suf::NUM_KINDS); - } - - // Finds a suffix with specified name. - Suffix *Find(const char *name) { - Set::iterator i = set_.find(Suffix(name)); - return const_cast(i != set_.end() ? &*i : 0); - } - const Suffix *Find(const char *name) const { - Set::const_iterator i = set_.find(Suffix(name)); - return i != set_.end() ? &*i : 0; - } - - Suffix &Add(const char *name, std::size_t num_values) { - Suffix &suffix = const_cast(*set_.insert(Suffix(name)).first); - Suffix(name, kind_, num_values).Swap(suffix); - return suffix; - } - - typedef Set::const_iterator iterator; - - iterator begin() const { return set_.begin(); } - iterator end() const { return set_.end(); } -}; - -class SuffixManager { - private: - SuffixSet suffixes_[suf::NUM_KINDS]; - - public: - SuffixManager() { - for (int kind = 0; kind < suf::NUM_KINDS; ++kind) - suffixes_[kind] = SuffixSet(kind); - } - - SuffixSet &get(int kind) { - assert(kind < suf::NUM_KINDS); - return suffixes_[kind]; - } -}; - -// TODO: test suffixes - // A minimal implementation of the ProblemBuilder concept. template class ProblemBuilder { diff --git a/include/mp/solver.h b/include/mp/solver.h index 6a5a7c504..6ffe52398 100644 --- a/include/mp/solver.h +++ b/include/mp/solver.h @@ -42,6 +42,7 @@ #include "mp/nl.h" #include "mp/option.h" #include "mp/sol.h" +#include "mp/suffix.h" namespace mp { @@ -958,11 +959,12 @@ template void SolutionWriter::HandleSolution( fmt::StringRef message, const double *values, const double *dual_values, double) { + typedef typename ProblemBuilder::SuffixPtr SuffixPtr; + SuffixData data; if (solver_.need_multiple_solutions()) { - typedef typename ProblemBuilder::SuffixPtr SuffixPtr; SuffixPtr nsol_suffix = builder_.suffixes(suf::PROBLEM).Find("nsol"); - if (nsol_suffix) - nsol_suffix->set_value(0, num_solutions_); + data.Attach(nsol_suffix, 1); + data.set_value(0, num_solutions_); } // TODO: pass to WriteSol //option_info.bsname = const_cast(solver_.long_name()); diff --git a/include/mp/suffix.h b/include/mp/suffix.h new file mode 100644 index 000000000..dc8539ebf --- /dev/null +++ b/include/mp/suffix.h @@ -0,0 +1,166 @@ +/* + AMPL suffix support + Suffixes are values associated with model components. + See http://www.ampl.com/NEW/suffixes.html + + Copyright (C) 2014 AMPL Optimization Inc + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, + provided that the above copyright notice appear in all copies and that + both that the copyright notice and this permission notice and warranty + disclaimer appear in supporting documentation. + + The author and AMPL Optimization Inc disclaim all warranties with + regard to this software, including all implied warranties of + merchantability and fitness. In no event shall the author be liable + for any special, indirect or consequential damages or any damages + whatsoever resulting from loss of use , data or profits, whether in an + action of contract, negligence or other tortious action, arising out + of or in connection with the use or performance of this software. + + Author: Victor Zverovich + */ + +#ifndef MP_SUFFIX_H_ +#define MP_SUFFIX_H_ + +namespace mp { + +template +class SuffixData; + +class Suffix { + private: + std::string name_; + int kind_; + int *values_; + std::size_t size_; + + friend class SuffixData; + + void set_data(int *values, std::size_t size) { + size_ = size; + values_ = values; + } + + public: + explicit Suffix(const std::string &name, int kind = 0) + : name_(name), kind_(kind), values_(0), size_(0) {} + + const char *name() const { return name_.c_str(); } + + int kind() const { return kind_; } + + int value(std::size_t index) const { + assert(index <= size_); + return values_[index]; + } + + // Iterates over nonzero suffix values and sends them to the visitor. + template + void VisitValues(Visitor &visitor) const { + for (std::size_t i = 0; i < size_; ++i) { + int value = values_[i]; + if (value != 0) + visitor.Visit(i, value); + } + } +}; + +// Suffix data. +// For compatibility with ASL, suffix data is stored separately. +// This class is used to control the lifetime of the data and +// automatically detach it from the suffix once it is destroyed. +template +class SuffixData { + private: + SuffixPtr suffix_; + std::vector values_; + + FMT_DISALLOW_COPY_AND_ASSIGN(SuffixData); + + public: + SuffixData() : suffix_() {} + ~SuffixData() { + if (suffix_) + suffix_->set_data(0, 0); + } + + // Attaches data to a suffix. + void Attach(SuffixPtr suffix, std::size_t num_values) { + suffix_ = suffix; + values_.resize(num_values); + suffix->set_data(&values_[0], values_.size()); + } + + int value(std::size_t index) const { + assert(index <= values_.size()); + return values_[index]; + } + + void set_value(std::size_t index, int value) { + assert(index <= values_.size()); + values_[index] = value; + } +}; + +class SuffixSet { + private: + int kind_; + + struct SuffixLess { + bool operator()(const Suffix &lhs, const Suffix &rhs) const { + return std::strcmp(lhs.name(), rhs.name()) < 0; + } + }; + + typedef std::set Set; + Set set_; + + public: + explicit SuffixSet(int kind = 0) : kind_(kind) { + assert(0 <= kind && kind <= suf::NUM_KINDS); + } + + // Finds a suffix with specified name. + Suffix *Find(const char *name) { + Set::iterator i = set_.find(Suffix(name)); + return const_cast(i != set_.end() ? &*i : 0); + } + const Suffix *Find(const char *name) const { + Set::const_iterator i = set_.find(Suffix(name)); + return i != set_.end() ? &*i : 0; + } + + Suffix &Add(const char *name) { + return const_cast(*set_.insert(Suffix(name, kind_)).first); + } + + typedef Set::const_iterator iterator; + + iterator begin() const { return set_.begin(); } + iterator end() const { return set_.end(); } +}; + +class SuffixManager { + private: + SuffixSet suffixes_[suf::NUM_KINDS]; + + public: + SuffixManager() { + for (int kind = 0; kind < suf::NUM_KINDS; ++kind) + suffixes_[kind] = SuffixSet(kind); + } + + SuffixSet &get(int kind) { + assert(kind < suf::NUM_KINDS); + return suffixes_[kind]; + } +}; + +// TODO: test suffixes + +} // namespace mp + +#endif // MP_SUFFIX_H_ diff --git a/src/asl/problem.h b/src/asl/problem.h index 9107ba8dc..5eece8f51 100644 --- a/src/asl/problem.h +++ b/src/asl/problem.h @@ -31,6 +31,9 @@ namespace mp { +template +class SuffixData; + namespace internal { class ASLBuilder; } @@ -120,6 +123,12 @@ class ASLSuffixPtr { Proxy(ASL *asl, SufDesc *p) : asl_(asl), ptr_(p) {} + friend class mp::SuffixData; + + void set_data(int *values, std::size_t) { + ptr_->u.i = values; + } + public: const char *name() const { return ptr_->sufname; } int kind() const { return ptr_->kind; } diff --git a/test/solver-test.cc b/test/solver-test.cc index 87c54089c..c1b69075d 100644 --- a/test/solver-test.cc +++ b/test/solver-test.cc @@ -1227,7 +1227,7 @@ TEST(SolutionWriterTest, CountSolutions) { int nsol = 5; for (int i = 0; i < nsol; ++i) writer.HandleFeasibleSolution("", 0, 0, 0); - problem_builder.suffixes(mp::suf::PROBLEM).Add("nsol", 1); + problem_builder.suffixes(mp::suf::PROBLEM).Add("nsol"); EXPECT_CALL(writer.sol_writer(), Write(_, MatchNSol(nsol))); writer.HandleSolution("", 0, 0, 0); } @@ -1249,7 +1249,7 @@ TEST(SolutionWriterTest, WriteFeasibleSolutions) { EXPECT_CALL(sol_writer, Write(StringRefEq(filename), _)); writer.HandleFeasibleSolution("", 0, 0, 0); } - problem_builder.suffixes(mp::suf::PROBLEM).Add("nsol", 1); + problem_builder.suffixes(mp::suf::PROBLEM).Add("nsol"); EXPECT_CALL(sol_writer, Write(_, MatchNSol(nsol))); writer.HandleSolution("", 0, 0, 0); } @@ -1348,7 +1348,7 @@ TEST_F(SolverAppTest, ParseOptionsBeforeReadingProblem) { EXPECT_EQ(0, app_.Run(Args("test", "-w", "testproblem"))); } -// Matcher that checks if the argument of type ProblemBuilderToNLAdapter +// Matcher that return true if the argument of type ProblemBuilderToNLAdapter // points to the solver's problem builder. MATCHER_P(MatchAdapterToBuilder, solver, "") { return &arg.builder() == solver->GetProblemBuilder(""); @@ -1402,7 +1402,8 @@ TEST_F(SolverAppTest, ReportInputTime) { EXPECT_THAT(output(), testing::MatchesRegex("timing=1\nInput time = .+s\n")); } -// Matcher that checks if the argument points to the solver's problem builder. +// Matcher that returns true if the argument points to the solver's problem +// builder. MATCHER_P(MatchBuilder, solver, "") { return &arg == solver->GetProblemBuilder(""); } @@ -1416,7 +1417,7 @@ TEST_F(SolverAppTest, Solve) { EXPECT_EQ(0, app_.Run(Args("test", "testproblem"))); } -// Matcher that checks if the argument is a solution writer. +// Matcher that returns true if the argument is a solution writer. MATCHER(MatchSolutionWriter, "") { return dynamic_cast*>(&arg) != 0; }