3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-08-23 11:37:54 +00:00
z3/src/math/polysat/viable.h
Jakob Rath f184545aca Debug dlist insertion
Found because of assertion failure in
test_polysat::test_fixed_point_arith_div_mul_inverse()
2022-10-05 17:24:28 +02:00

254 lines
7.3 KiB
C++

/*++
Copyright (c) 2021 Microsoft Corporation
Module Name:
maintain viable domains
It uses the interval extraction functions from forbidden intervals.
An empty viable set corresponds directly to a conflict that does not rely on
the non-viable variable.
Author:
Nikolaj Bjorner (nbjorner) 2021-03-19
Jakob Rath 2021-04-06
--*/
#pragma once
#include <limits>
#include "util/dlist.h"
#include "util/map.h"
#include "util/small_object_allocator.h"
#include "math/polysat/types.h"
#include "math/polysat/conflict.h"
#include "math/polysat/constraint.h"
#include "math/polysat/forbidden_intervals.h"
namespace polysat {
class solver;
class univariate_solver;
class univariate_solver_factory;
class viable {
friend class test_fi;
solver& s;
forbidden_intervals m_forbidden_intervals;
struct entry final : public dll_base<entry>, public fi_record {};
enum class entry_kind { unit_e, equal_e, diseq_e };
ptr_vector<entry> m_alloc;
ptr_vector<entry> m_units; // set of viable values based on unit multipliers
ptr_vector<entry> m_equal_lin; // entries that have non-unit multipliers, but are equal
ptr_vector<entry> m_diseq_lin; // entries that have distinct non-zero multipliers
svector<std::tuple<pvar, entry_kind, entry*>> m_trail; // undo stack
bool well_formed(entry* e);
entry* alloc_entry();
bool intersect(pvar v, entry* e);
bool refine_viable(pvar v, rational const& val);
bool refine_equal_lin(pvar v, rational const& val);
bool refine_disequal_lin(pvar v, rational const& val);
std::ostream& display(std::ostream& out, pvar v, entry* e) const;
void insert(entry* e, pvar v, ptr_vector<entry>& entries, entry_kind k);
public:
viable(solver& s);
~viable();
// declare and remove var
void push_var(unsigned bit_width);
void pop_var();
// undo adding/removing of entries
void pop_viable();
void push_viable();
/**
* update state of viable for pvar v
* based on affine constraints.
* Returns true if the state has been changed.
*/
bool intersect(pvar v, signed_constraint const& c);
bool intersect(pdd const & p, pdd const & q, signed_constraint const& c);
/**
* Check whether variable v has any viable values left according to m_viable.
*/
bool has_viable(pvar v);
/**
* check if value is viable according to m_viable.
*/
bool is_viable(pvar v, rational const& val);
/*
* Extract min and max viable values for v
*/
rational min_viable(pvar v);
rational max_viable(pvar v);
/**
* Find a next viable value for variable.
*/
dd::find_t find_viable(pvar v, rational & val);
/**
* Retrieve the unsat core for v.
* \pre there are no viable values for v
*/
bool resolve(pvar v, conflict& core);
/** Log all viable values for the given variable.
* (Inefficient, but useful for debugging small instances.)
*/
void log(pvar v);
/** Like log(v) but for all variables */
void log();
std::ostream& display(std::ostream& out) const;
class iterator {
entry* curr = nullptr;
bool visited = false;
unsigned idx = 0;
public:
iterator(entry* curr, bool visited) :
curr(curr), visited(visited || !curr) {}
iterator& operator++() {
if (idx < curr->side_cond.size())
++idx;
else {
idx = 0;
visited = true;
curr = curr->next();
}
return *this;
}
signed_constraint& operator*() {
return idx < curr->side_cond.size() ? curr->side_cond[idx] : curr->src;
}
bool operator==(iterator const& other) const {
return visited == other.visited && curr == other.curr;
}
bool operator!=(iterator const& other) const {
return !(*this == other);
}
};
class constraints {
viable const& v;
pvar var;
public:
constraints(viable const& v, pvar var) : v(v), var(var) {}
iterator begin() const { return iterator(v.m_units[var], false); }
iterator end() const { return iterator(v.m_units[var], true); }
};
constraints get_constraints(pvar v) const {
return constraints(*this, v);
}
class int_iterator {
entry* curr = nullptr;
bool visited = false;
public:
int_iterator(entry* curr, bool visited) :
curr(curr), visited(visited || !curr) {}
int_iterator& operator++() {
visited = true;
curr = curr->next();
return *this;
}
eval_interval const& operator*() {
return curr->interval;
}
bool operator==(int_iterator const& other) const {
return visited == other.visited && curr == other.curr;
}
bool operator!=(int_iterator const& other) const {
return !(*this == other);
}
};
class intervals {
viable const& v;
pvar var;
public:
intervals(viable const& v, pvar var): v(v), var(var) {}
int_iterator begin() const { return int_iterator(v.m_units[var], false); }
int_iterator end() const { return int_iterator(v.m_units[var], true); }
};
intervals units(pvar v) { return intervals(*this, v); }
std::ostream& display(std::ostream& out, pvar v) const;
struct var_pp {
viable const& v;
pvar var;
var_pp(viable const& v, pvar var) : v(v), var(var) {}
};
};
inline std::ostream& operator<<(std::ostream& out, viable const& v) {
return v.display(out);
}
inline std::ostream& operator<<(std::ostream& out, viable::var_pp const& v) {
return v.v.display(out, v.var);
}
class viable_fallback {
solver& s;
scoped_ptr<univariate_solver_factory> m_usolver_factory;
u_map<scoped_ptr<univariate_solver>> m_usolver; // univariate solver for each bit width
vector<signed_constraints> m_constraints;
svector<unsigned> m_constraints_trail;
public:
viable_fallback(solver& s);
// declare and remove var
void push_var(unsigned bit_width);
void pop_var();
// add/remove constraints stored in the fallback solver
void push_constraint(pvar v, signed_constraint const& c);
void pop_constraint();
// Check whether all constraints for 'v' are satisfied.
bool check_constraints(pvar v);
dd::find_t find_viable(pvar v, rational& out_val);
signed_constraints unsat_core(pvar v);
};
}