mirror of
https://github.com/Z3Prover/z3
synced 2025-06-20 21:03:39 +00:00
Add more logging to polysat (#5186)
* Add polysat logging support * Don't really need the usual log levels * Indent log headings * Add display method to ptr_vector * Add some logging to solver * Use __FUNCSIG__ on MSVC
This commit is contained in:
parent
7067fc16ae
commit
feb31045f5
6 changed files with 271 additions and 3 deletions
|
@ -3,6 +3,7 @@ z3_add_component(polysat
|
||||||
solver.cpp
|
solver.cpp
|
||||||
constraint.cpp
|
constraint.cpp
|
||||||
justification.cpp
|
justification.cpp
|
||||||
|
log.cpp
|
||||||
COMPONENT_DEPENDENCIES
|
COMPONENT_DEPENDENCIES
|
||||||
util
|
util
|
||||||
dd
|
dd
|
||||||
|
|
92
src/math/polysat/log.cpp
Normal file
92
src/math/polysat/log.cpp
Normal file
|
@ -0,0 +1,92 @@
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
#include "math/polysat/log.h"
|
||||||
|
|
||||||
|
#if POLYSAT_LOGGING_ENABLED
|
||||||
|
|
||||||
|
static LogLevel
|
||||||
|
get_max_log_level(std::string const& fn, std::string const& pretty_fn)
|
||||||
|
{
|
||||||
|
(void)fn;
|
||||||
|
(void)pretty_fn;
|
||||||
|
|
||||||
|
// bool const from_decision_queue = pretty_fn.find("DecisionQueue") != std::string::npos;
|
||||||
|
// if (from_decision_queue) {
|
||||||
|
// return LogLevel::Info;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (pretty_fn.find("add_") != std::string::npos) {
|
||||||
|
// return LogLevel::Info;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (fn == "analyze") {
|
||||||
|
// return LogLevel::Trace;
|
||||||
|
// }
|
||||||
|
// if (fn.find("minimize") != std::string::npos) {
|
||||||
|
// return LogLevel::Trace;
|
||||||
|
// }
|
||||||
|
// if (fn == "propagate_literal") {
|
||||||
|
// return LogLevel::Trace;
|
||||||
|
// }
|
||||||
|
|
||||||
|
return LogLevel::Verbose;
|
||||||
|
// return LogLevel::Warn;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Filter log messages
|
||||||
|
bool
|
||||||
|
polysat_should_log(LogLevel msg_level, std::string fn, std::string pretty_fn)
|
||||||
|
{
|
||||||
|
LogLevel max_log_level = get_max_log_level(fn, pretty_fn);
|
||||||
|
return msg_level <= max_log_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char const*
|
||||||
|
level_color(LogLevel msg_level)
|
||||||
|
{
|
||||||
|
switch (msg_level) {
|
||||||
|
case LogLevel::Heading1:
|
||||||
|
return "\x1B[31m"; // red
|
||||||
|
case LogLevel::Heading2:
|
||||||
|
return "\x1B[33m"; // yellow
|
||||||
|
case LogLevel::Heading3:
|
||||||
|
return "\x1B[34m"; // blue
|
||||||
|
default:
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int polysat_log_indent_level = 0;
|
||||||
|
|
||||||
|
std::pair<std::ostream&, bool>
|
||||||
|
polysat_log(LogLevel msg_level, std::string fn, std::string /* pretty_fn */)
|
||||||
|
{
|
||||||
|
std::ostream& os = std::cerr;
|
||||||
|
int const fd = fileno(stderr);
|
||||||
|
|
||||||
|
size_t width = 20;
|
||||||
|
size_t padding = width - std::min(width, fn.size());
|
||||||
|
|
||||||
|
char const* color = level_color(msg_level);
|
||||||
|
if (color && !isatty(fd)) { color = nullptr; }
|
||||||
|
|
||||||
|
if (color) { os << color; }
|
||||||
|
os << "[" << fn << "] " << std::string(padding, ' ');
|
||||||
|
os << std::string(polysat_log_indent_level, ' ');
|
||||||
|
return {os, (bool)color};
|
||||||
|
}
|
||||||
|
|
||||||
|
polysat_log_indent::polysat_log_indent(int amount)
|
||||||
|
: m_amount{amount}
|
||||||
|
{
|
||||||
|
polysat_log_indent_level += m_amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
polysat_log_indent::~polysat_log_indent()
|
||||||
|
{
|
||||||
|
polysat_log_indent_level -= m_amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // POLYSAT_LOGGING_ENABLED
|
106
src/math/polysat/log.h
Normal file
106
src/math/polysat/log.h
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
#ifndef POLYSAT_LOG_HPP
|
||||||
|
#define POLYSAT_LOG_HPP
|
||||||
|
|
||||||
|
|
||||||
|
// By default, enable logging only in debug mode
|
||||||
|
#ifndef POLYSAT_LOGGING_ENABLED
|
||||||
|
# ifndef NDEBUG
|
||||||
|
# define POLYSAT_LOGGING_ENABLED 1
|
||||||
|
# else
|
||||||
|
# define POLYSAT_LOGGING_ENABLED 0
|
||||||
|
# endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
#if POLYSAT_LOGGING_ENABLED
|
||||||
|
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class polysat_log_indent
|
||||||
|
{
|
||||||
|
int m_amount;
|
||||||
|
public:
|
||||||
|
polysat_log_indent(int amount);
|
||||||
|
~polysat_log_indent();
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Lower log level means more important
|
||||||
|
enum class LogLevel : int {
|
||||||
|
None = 0,
|
||||||
|
Heading1 = 1,
|
||||||
|
Heading2 = 2,
|
||||||
|
Heading3 = 3,
|
||||||
|
Default = 4,
|
||||||
|
Verbose = 5,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Filter log messages
|
||||||
|
bool
|
||||||
|
polysat_should_log(LogLevel msg_level, std::string fn, std::string pretty_fn);
|
||||||
|
|
||||||
|
std::pair<std::ostream&, bool>
|
||||||
|
polysat_log(LogLevel msg_level, std::string fn, std::string pretty_fn);
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define __PRETTY_FUNCTION__ __FUNCSIG__
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define LOG_(lvl, x) \
|
||||||
|
do { \
|
||||||
|
if (polysat_should_log(lvl, __func__, __PRETTY_FUNCTION__)) { \
|
||||||
|
auto pair = polysat_log(lvl, __func__, __PRETTY_FUNCTION__); \
|
||||||
|
std::ostream& os = pair.first; \
|
||||||
|
bool should_reset = pair.second; \
|
||||||
|
os << x; \
|
||||||
|
if (should_reset) { \
|
||||||
|
os << "\x1B[0m"; /* reset color */ \
|
||||||
|
} \
|
||||||
|
os << std::endl; \
|
||||||
|
} \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define LOG_CONCAT_HELPER(a,b) a ## b
|
||||||
|
#define LOG_CONCAT(a,b) LOG_CONCAT_HELPER(a,b)
|
||||||
|
|
||||||
|
#define LOG_INDENT(lvl, x) \
|
||||||
|
LOG_(lvl, x); \
|
||||||
|
polysat_log_indent LOG_CONCAT(polysat_log_indent_obj_, __LINE__) (4);
|
||||||
|
|
||||||
|
#define LOG_H1(x) LOG_INDENT(LogLevel::Heading1, x)
|
||||||
|
#define LOG_H2(x) LOG_INDENT(LogLevel::Heading2, x)
|
||||||
|
#define LOG_H3(x) LOG_INDENT(LogLevel::Heading3, x)
|
||||||
|
#define LOG(x) LOG_(LogLevel::Default , x)
|
||||||
|
#define LOG_V(x) LOG_(LogLevel::Verbose , x)
|
||||||
|
|
||||||
|
#define IF_LOGGING(x) \
|
||||||
|
do { \
|
||||||
|
x; \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
|
||||||
|
#else // POLYSAT_LOGGING_ENABLED
|
||||||
|
|
||||||
|
|
||||||
|
#define LOG_(lvl, x) \
|
||||||
|
do { \
|
||||||
|
/* do nothing */ \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#define LOG_H1(x) LOG_(0, x)
|
||||||
|
#define LOG_H2(x) LOG_(0, x)
|
||||||
|
#define LOG_H3(x) LOG_(0, x)
|
||||||
|
#define LOG(x) LOG_(0, x)
|
||||||
|
#define LOG_V(x) LOG_(0, x)
|
||||||
|
|
||||||
|
#define IF_LOGGING(x) \
|
||||||
|
do { \
|
||||||
|
/* do nothing */ \
|
||||||
|
} while (false)
|
||||||
|
|
||||||
|
#endif // POLYSAT_LOGGING_ENABLED
|
||||||
|
|
||||||
|
|
||||||
|
#endif // POLYSAT_LOG_HPP
|
|
@ -16,6 +16,7 @@ Author:
|
||||||
--*/
|
--*/
|
||||||
|
|
||||||
#include "math/polysat/solver.h"
|
#include "math/polysat/solver.h"
|
||||||
|
#include "math/polysat/log.h"
|
||||||
|
|
||||||
namespace polysat {
|
namespace polysat {
|
||||||
|
|
||||||
|
@ -82,13 +83,39 @@ namespace polysat {
|
||||||
|
|
||||||
solver::~solver() {}
|
solver::~solver() {}
|
||||||
|
|
||||||
|
#if POLYSAT_LOGGING_ENABLED
|
||||||
|
void solver::log_viable(pvar v) {
|
||||||
|
if (size(v) <= 5) {
|
||||||
|
vector<rational> xs;
|
||||||
|
for (rational x = rational::zero(); x < rational::power_of_two(size(v)); x += 1) {
|
||||||
|
if (is_viable(v, x)) {
|
||||||
|
xs.push_back(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG("Viable for pvar " << v << ": " << xs);
|
||||||
|
} else {
|
||||||
|
LOG("Viable for pvar " << v << ": <range too big>");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
lbool solver::check_sat() {
|
lbool solver::check_sat() {
|
||||||
TRACE("polysat", tout << "check\n";);
|
TRACE("polysat", tout << "check\n";);
|
||||||
while (m_lim.inc()) {
|
while (m_lim.inc()) {
|
||||||
if (is_conflict() && at_base_level()) return l_false;
|
LOG_H1("Next solving loop iteration");
|
||||||
|
LOG("Free variables: " << m_free_vars);
|
||||||
|
LOG("Assignments: " << m_search);
|
||||||
|
LOG("Conflict: " << m_conflict);
|
||||||
|
IF_LOGGING({
|
||||||
|
for (pvar v = 0; v < m_viable.size(); ++v) {
|
||||||
|
log_viable(v);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (is_conflict() && at_base_level()) { LOG_H2("UNSAT"); return l_false; }
|
||||||
else if (is_conflict()) resolve_conflict();
|
else if (is_conflict()) resolve_conflict();
|
||||||
else if (can_propagate()) propagate();
|
else if (can_propagate()) propagate();
|
||||||
else if (!can_decide()) return l_true;
|
else if (!can_decide()) { LOG_H2("SAT"); return l_true; }
|
||||||
else decide();
|
else decide();
|
||||||
}
|
}
|
||||||
return l_undef;
|
return l_undef;
|
||||||
|
@ -127,6 +154,7 @@ namespace polysat {
|
||||||
void solver::add_eq(pdd const& p, unsigned dep) {
|
void solver::add_eq(pdd const& p, unsigned dep) {
|
||||||
p_dependency_ref d(mk_dep(dep), m_dm);
|
p_dependency_ref d(mk_dep(dep), m_dm);
|
||||||
constraint* c = constraint::eq(m_level, p, d);
|
constraint* c = constraint::eq(m_level, p, d);
|
||||||
|
LOG("Adding constraint: " << *c);
|
||||||
m_constraints.push_back(c);
|
m_constraints.push_back(c);
|
||||||
add_watch(*c);
|
add_watch(*c);
|
||||||
}
|
}
|
||||||
|
@ -169,6 +197,7 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::propagate(pvar v) {
|
void solver::propagate(pvar v) {
|
||||||
|
LOG_H2("Propagate pvar " << v);
|
||||||
auto& wlist = m_watch[v];
|
auto& wlist = m_watch[v];
|
||||||
unsigned i = 0, j = 0, sz = wlist.size();
|
unsigned i = 0, j = 0, sz = wlist.size();
|
||||||
for (; i < sz && !is_conflict(); ++i)
|
for (; i < sz && !is_conflict(); ++i)
|
||||||
|
@ -179,6 +208,9 @@ namespace polysat {
|
||||||
wlist.shrink(j);
|
wlist.shrink(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true iff the constraint should be removed from the current watch list.
|
||||||
|
*/
|
||||||
bool solver::propagate(pvar v, constraint& c) {
|
bool solver::propagate(pvar v, constraint& c) {
|
||||||
switch (c.kind()) {
|
switch (c.kind()) {
|
||||||
case ckind_t::eq_t:
|
case ckind_t::eq_t:
|
||||||
|
@ -193,6 +225,7 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool solver::propagate_eq(pvar v, constraint& c) {
|
bool solver::propagate_eq(pvar v, constraint& c) {
|
||||||
|
LOG_H3("Propagate " << m_vars[v] << " in " << c);
|
||||||
SASSERT(c.kind() == ckind_t::eq_t);
|
SASSERT(c.kind() == ckind_t::eq_t);
|
||||||
SASSERT(!c.vars().empty());
|
SASSERT(!c.vars().empty());
|
||||||
auto var = m_vars[v].var();
|
auto var = m_vars[v].var();
|
||||||
|
@ -210,11 +243,14 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
LOG("Assignments: " << m_search);
|
||||||
auto p = c.p().subst_val(m_search);
|
auto p = c.p().subst_val(m_search);
|
||||||
|
LOG("Substituted: " << c.p() << " := " << p);
|
||||||
TRACE("polysat", tout << c.p() << " := " << p << "\n";);
|
TRACE("polysat", tout << c.p() << " := " << p << "\n";);
|
||||||
if (p.is_zero())
|
if (p.is_zero())
|
||||||
return false;
|
return false;
|
||||||
if (p.is_never_zero()) {
|
if (p.is_never_zero()) {
|
||||||
|
LOG("Conflict (never zero under current assignment)");
|
||||||
// we could tag constraint to allow early substitution before
|
// we could tag constraint to allow early substitution before
|
||||||
// swapping watch variable in case we can detect conflict earlier.
|
// swapping watch variable in case we can detect conflict earlier.
|
||||||
set_conflict(c);
|
set_conflict(c);
|
||||||
|
@ -334,20 +370,25 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::decide() {
|
void solver::decide() {
|
||||||
|
LOG_H2("Decide");
|
||||||
SASSERT(can_decide());
|
SASSERT(can_decide());
|
||||||
decide(m_free_vars.next_var());
|
decide(m_free_vars.next_var());
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::decide(pvar v) {
|
void solver::decide(pvar v) {
|
||||||
|
IF_LOGGING(log_viable(v));
|
||||||
rational val;
|
rational val;
|
||||||
switch (find_viable(v, val)) {
|
switch (find_viable(v, val)) {
|
||||||
case l_false:
|
case l_false:
|
||||||
|
LOG("Conflict: no value for pvar " << v);
|
||||||
set_conflict(v);
|
set_conflict(v);
|
||||||
break;
|
break;
|
||||||
case l_true:
|
case l_true:
|
||||||
|
LOG("Propagation: pvar " << v << " := " << val << " (due to unique value)");
|
||||||
assign_core(v, val, justification::propagation(m_level));
|
assign_core(v, val, justification::propagation(m_level));
|
||||||
break;
|
break;
|
||||||
case l_undef:
|
case l_undef:
|
||||||
|
LOG("Decision: pvar " << v << " := " << val);
|
||||||
push_level();
|
push_level();
|
||||||
assign_core(v, val, justification::decision(m_level));
|
assign_core(v, val, justification::decision(m_level));
|
||||||
break;
|
break;
|
||||||
|
@ -360,6 +401,7 @@ namespace polysat {
|
||||||
else
|
else
|
||||||
++m_stats.m_num_propagations;
|
++m_stats.m_num_propagations;
|
||||||
TRACE("polysat", tout << "v" << v << " := " << val << " " << j << "\n";);
|
TRACE("polysat", tout << "v" << v << " := " << val << " " << j << "\n";);
|
||||||
|
LOG("pvar " << v << " := " << val << " by " << j);
|
||||||
SASSERT(is_viable(v, val));
|
SASSERT(is_viable(v, val));
|
||||||
m_value[v] = val;
|
m_value[v] = val;
|
||||||
m_search.push_back(std::make_pair(v, val));
|
m_search.push_back(std::make_pair(v, val));
|
||||||
|
@ -400,6 +442,7 @@ namespace polysat {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
void solver::resolve_conflict() {
|
void solver::resolve_conflict() {
|
||||||
|
LOG_H2("Resolve conflict");
|
||||||
++m_stats.m_num_conflicts;
|
++m_stats.m_num_conflicts;
|
||||||
|
|
||||||
SASSERT(!m_conflict.empty());
|
SASSERT(!m_conflict.empty());
|
||||||
|
@ -413,6 +456,7 @@ namespace polysat {
|
||||||
reset_marks();
|
reset_marks();
|
||||||
for (constraint* c : m_conflict) {
|
for (constraint* c : m_conflict) {
|
||||||
SASSERT(c);
|
SASSERT(c);
|
||||||
|
LOG("Conflicting: " << *c);
|
||||||
for (auto v : c->vars())
|
for (auto v : c->vars())
|
||||||
set_mark(v);
|
set_mark(v);
|
||||||
}
|
}
|
||||||
|
@ -540,6 +584,7 @@ namespace polysat {
|
||||||
}
|
}
|
||||||
|
|
||||||
void solver::backjump(unsigned new_level) {
|
void solver::backjump(unsigned new_level) {
|
||||||
|
LOG_H3("Backjumping to level " << new_level << " from level " << m_level);
|
||||||
unsigned num_levels = m_level - new_level;
|
unsigned num_levels = m_level - new_level;
|
||||||
if (num_levels > 0)
|
if (num_levels > 0)
|
||||||
pop_levels(num_levels);
|
pop_levels(num_levels);
|
||||||
|
@ -596,6 +641,7 @@ namespace polysat {
|
||||||
void solver::add_lemma(constraint* c) {
|
void solver::add_lemma(constraint* c) {
|
||||||
if (!c)
|
if (!c)
|
||||||
return;
|
return;
|
||||||
|
LOG("Lemma: " << *c);
|
||||||
add_watch(*c);
|
add_watch(*c);
|
||||||
m_redundant.push_back(c);
|
m_redundant.push_back(c);
|
||||||
for (unsigned i = m_redundant.size() - 1; i-- > 0; ) {
|
for (unsigned i = m_redundant.size() - 1; i-- > 0; ) {
|
||||||
|
|
|
@ -87,13 +87,18 @@ namespace polysat {
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find a next viable value for varible.
|
* Find a next viable value for variable.
|
||||||
* l_false - there are no viable values.
|
* l_false - there are no viable values.
|
||||||
* l_true - there is only one viable value left.
|
* l_true - there is only one viable value left.
|
||||||
* l_undef - there are multiple viable values, return a guess
|
* l_undef - there are multiple viable values, return a guess
|
||||||
*/
|
*/
|
||||||
lbool find_viable(pvar v, rational & val);
|
lbool find_viable(pvar v, rational & val);
|
||||||
|
|
||||||
|
/** Log all viable values for the given variable.
|
||||||
|
* (Inefficient, but useful for debugging small instances.)
|
||||||
|
*/
|
||||||
|
void log_viable(pvar v);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* undo trail operations for backtracking.
|
* undo trail operations for backtracking.
|
||||||
* Each struct is a subclass of trail and implements undo().
|
* Each struct is a subclass of trail and implements undo().
|
||||||
|
|
|
@ -609,6 +609,19 @@ public:
|
||||||
ptr_vector(unsigned s):vector<T *, false>(s) {}
|
ptr_vector(unsigned s):vector<T *, false>(s) {}
|
||||||
ptr_vector(unsigned s, T * elem):vector<T *, false>(s, elem) {}
|
ptr_vector(unsigned s, T * elem):vector<T *, false>(s, elem) {}
|
||||||
ptr_vector(unsigned s, T * const * data):vector<T *, false>(s, const_cast<T**>(data)) {}
|
ptr_vector(unsigned s, T * const * data):vector<T *, false>(s, const_cast<T**>(data)) {}
|
||||||
|
std::ostream& display(std::ostream& out, char const* delim = " ") const {
|
||||||
|
bool first = true;
|
||||||
|
for (auto const* u : *this) {
|
||||||
|
if (!first)
|
||||||
|
out << delim;
|
||||||
|
first = false;
|
||||||
|
if (u)
|
||||||
|
out << *u;
|
||||||
|
else
|
||||||
|
out << "<NULL>";
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename T, typename SZ = unsigned>
|
template<typename T, typename SZ = unsigned>
|
||||||
|
@ -635,6 +648,11 @@ inline std::ostream& operator<<(std::ostream& out, svector<T> const& v) {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
inline std::ostream& operator<<(std::ostream& out, ptr_vector<T> const& v) {
|
||||||
|
return v.display(out);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
template<typename Hash, typename Vec>
|
template<typename Hash, typename Vec>
|
||||||
struct vector_hash_tpl {
|
struct vector_hash_tpl {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue