Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add interface for defining external statistics. #23

Merged
merged 7 commits into from
May 6, 2018
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
clasp 3.3.4:
* fixed: Atoms of incremental programs not always marked as frozen.
* added internal interface for user defined statistics
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, I’d write “user-defined“ with a hyphen here as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Thanks.


clasp 3.3.3: Sunday, 5th November 2017
* fixed: possible race condition in Windows alarm handling (libpotassco)
Expand Down
14 changes: 13 additions & 1 deletion clasp/clasp_facade.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,20 @@ class ClaspFacade : public ModelHandler {
* or the problem is unconditionally unsat.
*/
bool read();
//@}

typedef void(*StatsCallback)(ExternalStatistics*, void*);

//! Adds a callback for defining external (i.e. user-defined) statistics.
/*!
* \param cb Callback method to be called once before and after each solve step.
* \param data Additional data passed back on each call to cb.
*/
void addStatisticsCallback(StatsCallback cb, void* data);

//! Gets all registered callbacks for defining external statistics.
Potassco::Span<std::pair<StatsCallback, void*> > getStatisticsCallbacks() const;

//@}

/*!
* \name Solve functions
Expand Down
5 changes: 5 additions & 0 deletions clasp/cli/clasp_output.h
Original file line number Diff line number Diff line change
Expand Up @@ -137,14 +137,17 @@ class JsonOutput : public Output, private StatsVisitor {
virtual void visitLogicProgramStats(const Asp::LpStats& stats);
virtual void visitProblemStats(const ProblemStats& stats);
virtual void visitSolverStats(const SolverStats& stats);
virtual void visitExternalStats(const StatisticObject& stats);
virtual UPtr doPrint(const OutPair& out, UPtr data);
void printChildren(const StatisticObject& s);
enum ObjType { type_object, type_array };
void pushObject(const char* k = 0, ObjType t = type_object);
char popObject();
void printKeyValue(const char* k, const char* v) ;
void printKeyValue(const char* k, uint64 v);
void printKeyValue(const char* k, uint32 v);
void printKeyValue(const char* k, double d);
void printKeyValue(const char* k, const StatisticObject& o);
void printString(const char* s, const char* sep);
void printKey(const char* k);
void printModel(const OutputTable& out, const Model& m, PrintLevel x);
Expand Down Expand Up @@ -225,6 +228,7 @@ class TextOutput : public Output, private StatsVisitor {
virtual void visitLogicProgramStats(const Asp::LpStats& stats);
virtual void visitProblemStats(const ProblemStats& stats);
virtual void visitSolverStats(const SolverStats& stats);
virtual void visitExternalStats(const StatisticObject& stats);

virtual UPtr doPrint(const OutPair& out, UPtr data);
const char* fieldSeparator() const;
Expand All @@ -239,6 +243,7 @@ class TextOutput : public Output, private StatsVisitor {
void printSolveProgress(const Event& ev);
void printValues(const OutputTable& out, const Model& m);
void printMeta(const OutputTable& out, const Model& m);
void printChildren(const StatisticObject& s, unsigned level);
private:
struct SolveProgress {
int lines;
Expand Down
96 changes: 95 additions & 1 deletion clasp/statistics.h
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class StatisticObject {
}
std::size_t hash() const;
uint64 toRep() const;
const void* self() const;
static StatisticObject fromRep(uint64);
private:
struct I {
Expand Down Expand Up @@ -158,7 +159,6 @@ class StatisticObject {
StatisticObject(const void* obj, uint32 type);

typedef PodVector<const I*>::type RegVec;
const void* self() const;
const I* tid() const;
static RegVec types_;
uint64 handle_;
Expand Down Expand Up @@ -206,6 +206,7 @@ class StatsMap {
// Own interface
const StatisticObject* find(const char* k) const;
bool add(const char* k, const StatisticObject&);
void push(const char* k, const StatisticObject&);
StatisticObject toStats() const { return StatisticObject::map(this); }
private:
typedef PodVector<std::pair<const char*, StatisticObject> >::type MapType;
Expand Down Expand Up @@ -274,6 +275,98 @@ class ClaspStatistics : public Potassco::AbstractStatistics {
Impl* impl_;
};

//! A class for registering user-defined statistics.
/*!
* Functions in this interface taking a key as parameter require
* that the key is valid, i.e. the key must be reachable from root().
*/
class ExternalStatistics {
public:
typedef Potassco::Statistics_t Type;
typedef Potassco::AbstractStatistics::Key_t Key_t;

ExternalStatistics();
~ExternalStatistics();

//! Returns the root key of this object.
/*!
* The root key always exists and is of type Potassco::Statistics_t::Map.
*/
Key_t root() const;

//! Returns the type of the statistic object with the given key.
Type type(Key_t key) const;

//! Returns true if this object contains no keys beyond root().
bool empty() const;

//! Creates a statistic object under the given name in the given map.
/*!
* \pre type(object) == Potassco::Statistics_t::Map.
* \param map The map object to which the statistic object should be added.
* \param name The name under which the statistic object should be added.
* \param type The type of the statistic object to create.
* \return The key of the added statistic object.
*
* \note If a statistic object with the given name already exists in map,
* the function either returns its key provided that the types match,
* or otherwise signals failure by throwing an std::logic_error.
*/
Key_t mapAdd(Key_t map, const char* name, Type type);

//! Returns the key of the statistic object in map with the given name.
/*!
* \pre type(object) == Potassco::Statistics_t::Map.
* \note If map does not contain an object of the given name, the function
* return 0.
*/
Key_t mapGet(Key_t map, const char* name) const;

//! Creates a statistic object at the given index in the given array.
/*!
* \pre type(object) == Potassco::Statistics_t::Array.
* \param array The array object to which the statistic object should be added.
* \param index The index at which the statistic object should be added.
* \param type The type of the statistic object to create.
* \return The key of the created statistic object.
*
* \note If a statistic object already exists at the given index, the function
* either returns its key provided that the types match, or otherwise
* signals failure by throwing an std::logic_error.
*
* \note Given an array A of size i, adding A[j] implicitly also creates all
* A[k] for i <= k < j.
*/
Key_t arrayAdd(Key_t array, std::size_t index, Type type);

//! Returns the key of the statistic object at the given index in array.
/*!
* \pre type(object) == Potassco::Statistics_t::Array.
* \note If index is not a valid index in array, the function returns 0.
*/
Key_t arrayGet(Key_t array, std::size_t index) const;

//! Sets newValue as value for the given statistic object.
/*!
* \pre type(object) == Potassco::Statistics_t::Value.
*/
void set(Key_t object, double newValue);

//! Increments the value of object by i and returns its old value.
/*!
* \pre type(object) == Potassco::Statistics_t::Value.
*/
double fetchAdd(Key_t object, double i);

//! Converts this object to a StatisticObject of type map.
StatisticObject toStats() const;
private:
ExternalStatistics(const ExternalStatistics&);
ExternalStatistics& operator=(const ExternalStatistics&);
struct Impl;
Impl* impl_;
};

struct SolverStats;
struct JumpStats;
struct ExtendedStats;
Expand All @@ -299,6 +392,7 @@ class StatsVisitor {
virtual void visitLogicProgramStats(const Asp::LpStats& stats) = 0;
virtual void visitProblemStats(const ProblemStats& stats) = 0;
virtual void visitSolverStats(const SolverStats& stats) = 0;
virtual void visitExternalStats(const StatisticObject& stats) = 0;
};

}
Expand Down
61 changes: 52 additions & 9 deletions src/clasp_facade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,10 @@ double _getResult(const SolveResult* r) { return static_cast<double>(r->operator
double _getSignal(const SolveResult* r) { return static_cast<double>(r->signal); }
double _getExhausted(const SolveResult* r) { return static_cast<double>(r->exhausted()); }
}

struct ClaspFacade::Statistics {
typedef std::pair<StatsCallback, void*> UserCallback;

Statistics(ClaspFacade& f) : self_(&f), tester_(0), level_(0), clingo_(0) {}
~Statistics() { delete clingo_; delete solvers_.multi; }
void start(uint32 level);
Expand All @@ -564,17 +567,24 @@ struct ClaspFacade::Statistics {
void addTo(StatsMap& solving, StatsMap* accu) const;
void accept(StatsVisitor& out, bool final) const;
bool incremental() const { return self_->incremental(); }
void addExternal(StatsCallback, void*);
Potassco::Span<UserCallback> getExternal() const;

Potassco::AbstractStatistics* getClingo();
typedef StatsVec<SolverStats> SolverVec;
typedef SingleOwnerPtr<Asp::LpStats> LpStatsPtr;
typedef PrgDepGraph::NonHcfStats TesterStats;
typedef StatsVec<SolverStats> SolverVec;
typedef SingleOwnerPtr<Asp::LpStats> LpStatsPtr;
typedef PrgDepGraph::NonHcfStats TesterStats;
typedef PodVector<UserCallback>::type UserStatsVec;
typedef SingleOwnerPtr<ExternalStatistics> UserStatsPtr;
ClaspFacade* self_;
LpStatsPtr lp_; // level 0 and asp
SolverStats solvers_; // level 0
SolverVec solver_; // level > 1
SolverVec accu_; // level > 1 and incremental
TesterStats* tester_; // level > 0 and nonhcfs
uint32 level_; // active stats level
LpStatsPtr lp_; // level 0 and asp
SolverStats solvers_; // level 0
SolverVec solver_; // level > 1
SolverVec accu_; // level > 1 and incremental
TesterStats* tester_; // level > 0 and nonhcfs
UserStatsPtr userStats_; // user statistics: only if requested
UserStatsVec userCbs_; // callbacks for user-defined stats
uint32 level_; // active stats level
// For clingo stats interface
class ClingoView : public ClaspStatistics {
public:
Expand Down Expand Up @@ -614,6 +624,16 @@ void ClaspFacade::Statistics::initLevel(uint32 level) {
tester_ = self_->ctx.sccGraph->nonHcfStats();
}
}
void ClaspFacade::Statistics::addExternal(StatsCallback cb, void* data) {
if (!userStats_.get()) {
userStats_ = new ExternalStatistics();
}
userCbs_.push_back(UserCallback(cb, data));
}

Potassco::Span<ClaspFacade::Statistics::UserCallback> ClaspFacade::Statistics::getExternal() const {
return Potassco::toSpan(userCbs_);
}
void ClaspFacade::Statistics::start(uint32 level) {
// cleanup previous state
solvers_.reset();
Expand All @@ -633,6 +653,9 @@ void ClaspFacade::Statistics::start(uint32 level) {
}
if (!incremental()) { solver_.release(); }
}
for (UserStatsVec::const_iterator it = userCbs_.begin(), end = userCbs_.end(); it != end; ++it) {
it->first(userStats_.get(), it->second);
}
}
void ClaspFacade::Statistics::end() {
self_->ctx.accuStats(solvers_); // compute solvers = sum(solver[1], ... , solver[n])
Expand All @@ -641,6 +664,9 @@ void ClaspFacade::Statistics::end() {
solver_[i]->accu(self_->ctx.solverStats(i), true);
solver_[i]->flush();
}
for (UserStatsVec::const_iterator it = userCbs_.begin(), end = userCbs_.end(); it != end; ++it) {
it->first(userStats_.get(), it->second);
}
if (tester_) { tester_->endStep(); }
if (clingo_) { clingo_->update(*this); }
}
Expand All @@ -658,6 +684,9 @@ void ClaspFacade::Statistics::accept(StatsVisitor& out, bool final) const {
const SolverVec& solver = final ? accu_ : solver_;
const uint32 nThreads = final ? (uint32)accu_.size() : self_->ctx.concurrency();
const uint32 nSolver = (uint32)solver.size();
if (userStats_.get() && !userStats_->empty()) {
out.visitExternalStats(userStats_->toStats());
}
if (nThreads > 1 && nSolver > 1 && out.visitThreads(StatsVisitor::Enter)) {
for (uint32 i = 0, end = std::min(nSolver, nThreads); i != end; ++i) {
out.visitThread(i, *solver[i]);
Expand Down Expand Up @@ -696,13 +725,17 @@ ClaspFacade::Statistics::ClingoView::ClingoView(const ClaspFacade& f) {
keys_.add("problem", problem_.toStats());
keys_.add("solving", solving_.toStats());
keys_.add("summary", summary_.toStats());

if (f.incremental()) {
accu_ = new Accu();
accu_->step.bind(*f.accu_.get());
}
setRoot(keys_.toStats());
}
void ClaspFacade::Statistics::ClingoView::update(const ClaspFacade::Statistics& stats) {
if (stats.userStats_.get() && !stats.userStats_->empty()) {
keys_.add("userdefined", stats.userStats_->toStats());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason why userdefined is spelled without a hyphen here? I know that comment is a bit picky, but it might be inconvenient to make such things consistent at a later point in time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For consistency, this should be user_defined. Or alternatively, external to indicate that these are not maintained by clasp.

Copy link
Member

@pluehne pluehne Mar 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay. I was just referring to the fact that “user-defined” is a compound adjective and should be written with a hyphen, but if hyphens are not allowed within keys, that’s just how it is 🙂.

Copy link
Contributor Author

@BenKaufmann BenKaufmann Mar 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I forgot to add the "Yep, you are right". I just wanted to express that our keys typically use underscores (and unfortunately sometimes camel-case) instead of hyphens.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I totally understand, thanks for the info … and sorry about the fuss and my pedantry anyway 😄!

}
if (stats.level_ > 0 && accu_.get() && keys_.add("accu", accu_->toStats())) {
accu_->step.addTo(*accu_);
accu_->add("solving", accu_->solving_.toStats());
Expand Down Expand Up @@ -914,6 +947,16 @@ bool ClaspFacade::read() {
if (!p.more()) { p.reset(); }
return true;
}

void ClaspFacade::addStatisticsCallback(StatsCallback cb, void* data) {
POTASSCO_REQUIRE(stats_.get(), "Statistics not yet available");
stats_->addExternal(cb, data);
}

Potassco::Span<std::pair<ClaspFacade::StatsCallback, void*> > ClaspFacade::getStatisticsCallbacks() const {
return stats_.get() ? stats_->getExternal() : Potassco::toSpan<std::pair<StatsCallback, void*> >();
}

void ClaspFacade::prepare(EnumMode enumMode) {
POTASSCO_REQUIRE(solve_.get() && !solving());
EnumOptions& en = config_->solve;
Expand Down
Loading