mirror of
https://github.com/Z3Prover/z3
synced 2025-04-12 04:03:39 +00:00
174 lines
5.5 KiB
C++
174 lines
5.5 KiB
C++
/*++
|
|
Copyright (c) 2020 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
smt_relevant.h
|
|
|
|
Abstract:
|
|
|
|
Relevancy propagation
|
|
|
|
Author:
|
|
|
|
Nikolaj Bjorner (nbjorner) 2021-12-27
|
|
|
|
|
|
Clauses are split into two parts:
|
|
|
|
- Roots
|
|
- Defs
|
|
|
|
The goal is to establish a labeling of literals as "relevant" such that
|
|
- the set of relevant literals satisfies Roots
|
|
- there is a set of blocked literals that can be used to satisfy the clauses in Defs
|
|
independent of their real assignment.
|
|
|
|
The idea is that the Defs clauses are obtained from Tseitin transformation so they can be
|
|
grouped by the blocked literal that was used to introduce them.
|
|
For example, when clausifying (and a b) we have the clauses
|
|
(=> (and a b) a)
|
|
(=> (and a b) b)
|
|
(or (not a) (not b) (and a b))
|
|
then the literal for "(and a b)" is blocked.
|
|
And recursively for clauses introduced for a, b if they use a Boolean connectives
|
|
at top level.
|
|
|
|
The state transitions are:
|
|
|
|
- A literal lit is assigned:
|
|
lit appears positively in a Root clause R and no other literal in R are relevant.
|
|
->
|
|
lit is set relevant
|
|
|
|
lit is justified at search level
|
|
->
|
|
lit is set relevant
|
|
|
|
- An equality n1 = n2 is assigned:
|
|
n1 is relevant
|
|
->
|
|
n2 is marked as relevant
|
|
|
|
- A lit is set relevant:
|
|
->
|
|
all clauses D in Defs where lit appears negatively are added to Roots
|
|
|
|
- When a clause R is added to Roots:
|
|
R contains a positive literal lit that is relevant
|
|
->
|
|
skip adding R to Roots
|
|
|
|
- When a clause R is added to Roots:
|
|
R contains a positive literal lit, no positive literal in R are relevant
|
|
->
|
|
lit is set relevant
|
|
|
|
- When a clause D is added to Defs:
|
|
D contains a negative literal that is relevant
|
|
->
|
|
Add D to Roots
|
|
|
|
- When an expression is set relevant:
|
|
All non-relevant children above Boolean connectives are set relevant
|
|
If nodes are treated as Boolean connectives because they are clausified
|
|
to (=> cond (= n then)) and (=> (not cond) (= n else))
|
|
|
|
Replay:
|
|
- literals that are replayed in clauses that are marked relevant are
|
|
marked relevant again.
|
|
|
|
- expressions corresponding to auxiliary clauses are added as auxiliary clauses.
|
|
|
|
- TBD: Are root clauses added under a scope discarded?
|
|
The SAT solver re-initializes clauses on its own, should we just use this mechanism?
|
|
|
|
Can a literal that is not in a root be set relevant?
|
|
- yes, if we propagate over expressions
|
|
|
|
Do we need full watch lists instead of 2-watch lists?
|
|
- probably, but unclear. The dual SAT solver only uses 2-watch lists, but uses a large clause for tracking
|
|
roots.
|
|
|
|
|
|
State machine for literals: relevant(lit), assigned(lit)
|
|
|
|
relevant(lit) transitions false -> true
|
|
if assigned(lit): add to propagation queue
|
|
if not assigned(lit): no-op (or mark enodes as relevant)
|
|
|
|
assigned(lit) transitions false -> true
|
|
if relevant(lit): add to propagation queue
|
|
if not relevant(lit): set relevant if member of root, add to propagation queue
|
|
|
|
|
|
--*/
|
|
#pragma once
|
|
#include "sat/sat_solver.h"
|
|
#include "sat/smt/sat_th.h"
|
|
|
|
|
|
namespace euf {
|
|
|
|
class solver;
|
|
|
|
class relevancy {
|
|
euf::solver& ctx;
|
|
|
|
enum class update { relevant_var, add_queue, add_clause, set_root, set_qhead };
|
|
|
|
bool m_enabled = false;
|
|
svector<std::pair<update, unsigned>> m_trail;
|
|
unsigned_vector m_lim;
|
|
unsigned m_num_scopes = 0;
|
|
bool_vector m_relevant_var_ids; // identifiers of relevant Boolean variables
|
|
sat::clause_allocator m_alloc;
|
|
sat::clause_vector m_clauses; // clauses
|
|
bool_vector m_roots; // indicate if clause is a root
|
|
vector<unsigned_vector> m_occurs; // where do literals occur
|
|
unsigned m_qhead = 0; // queue head for relevancy
|
|
svector<std::pair<sat::literal, euf::enode*>> m_queue; // propagation queue for relevancy
|
|
euf::enode_vector m_stack, m_todo;
|
|
|
|
void push_core() { m_lim.push_back(m_trail.size()); }
|
|
void flush() { for (; m_num_scopes > 0; --m_num_scopes) push_core(); }
|
|
|
|
unsigned_vector& occurs(sat::literal lit) { m_occurs.reserve(lit.index() + 1); return m_occurs[lit.index()]; }
|
|
|
|
void propagate_relevant(sat::literal lit);
|
|
|
|
void add_to_propagation_queue(sat::literal lit);
|
|
|
|
void set_relevant(sat::literal lit);
|
|
|
|
void set_asserted(sat::literal lit);
|
|
|
|
void relevant_eh(sat::bool_var v);
|
|
|
|
void propagate_relevant(euf::enode* n);
|
|
|
|
public:
|
|
relevancy(euf::solver& ctx): ctx(ctx) {}
|
|
|
|
void push() { if (m_enabled) ++m_num_scopes; }
|
|
void pop(unsigned n);
|
|
|
|
void add_root(unsigned n, sat::literal const* lits);
|
|
void add_def(unsigned n, sat::literal const* lits);
|
|
void asserted(sat::literal lit);
|
|
void propagate();
|
|
bool can_propagate() const { return m_qhead < m_queue.size(); }
|
|
|
|
void mark_relevant(euf::enode* n);
|
|
void mark_relevant(sat::literal lit);
|
|
void merge(euf::enode* n1, euf::enode* n2);
|
|
|
|
bool is_relevant(sat::bool_var v) const { return !m_enabled || m_relevant_var_ids.get(v, false); }
|
|
bool is_relevant(sat::literal lit) const { return is_relevant(lit.var()); }
|
|
bool is_relevant(euf::enode* n) const { return !m_enabled || n->is_relevant(); }
|
|
|
|
bool enabled() const { return m_enabled; }
|
|
void set_enabled(bool e);
|
|
};
|
|
}
|