mirror of
https://github.com/Z3Prover/z3
synced 2025-08-26 13:06:05 +00:00
scaffoldin
This commit is contained in:
parent
4be2fc416f
commit
3c9c7105d3
5 changed files with 293 additions and 74 deletions
BIN
levelwise.pdf
Normal file
BIN
levelwise.pdf
Normal file
Binary file not shown.
|
@ -4,6 +4,7 @@ z3_add_component(nlsat
|
||||||
nlsat_evaluator.cpp
|
nlsat_evaluator.cpp
|
||||||
nlsat_explain.cpp
|
nlsat_explain.cpp
|
||||||
nlsat_interval_set.cpp
|
nlsat_interval_set.cpp
|
||||||
|
levelwise.cpp
|
||||||
nlsat_simplify.cpp
|
nlsat_simplify.cpp
|
||||||
nlsat_solver.cpp
|
nlsat_solver.cpp
|
||||||
nlsat_types.cpp
|
nlsat_types.cpp
|
||||||
|
|
290
src/nlsat/levelwise.cpp
Normal file
290
src/nlsat/levelwise.cpp
Normal file
|
@ -0,0 +1,290 @@
|
||||||
|
#include "levelwise.h"
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
namespace nlsat {
|
||||||
|
|
||||||
|
// Context for property propagation (e.g., which case of rule 4.2 applies)
|
||||||
|
enum class property_mapping_case : unsigned {
|
||||||
|
case1 = 0,
|
||||||
|
case2 = 1,
|
||||||
|
case3 = 2,
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Properties for indexed roots, in the order specified by the levelwise paper.
|
||||||
|
* The order is important for the algorithm and must match the paper exactly.
|
||||||
|
*/
|
||||||
|
enum class polynom_property : unsigned {
|
||||||
|
ir_ord, // ir_ord(≼, s) for all ≼ for roots of level i and s ∈ Ri−1
|
||||||
|
an_del, // an_del(p) for all p of level i
|
||||||
|
non_null, // non_null(p) for all p of level i
|
||||||
|
ord_inv_reducible, // ord_inv(p) for all reducible p of level i
|
||||||
|
ord_inv_irreducible, // ord_inv(p) for all irreducible p of level i
|
||||||
|
sgn_inv_reducible, // sgn_inv(p) for all reducible p of level i
|
||||||
|
sgn_inv_irreducible, // sgn_inv(p) for all irreducible p of level i
|
||||||
|
connected, // connected(i)
|
||||||
|
an_sub, // an_sub(i)
|
||||||
|
sample, // sample(s) for all s ∈ Ri of level i
|
||||||
|
repr, // repr(I, s) for all I of level i and s ∈ ...
|
||||||
|
holds, // marker for "goal holds" (terminal)
|
||||||
|
_count // always last: number of properties
|
||||||
|
};
|
||||||
|
|
||||||
|
// Utility: property to string (for debugging, logging, etc.)
|
||||||
|
inline const char* to_string(polynom_property prop) {
|
||||||
|
switch (prop) {
|
||||||
|
case polynom_property::ir_ord: return "ir_ord";
|
||||||
|
case polynom_property::an_del: return "an_del";
|
||||||
|
case polynom_property::non_null: return "non_null";
|
||||||
|
case polynom_property::ord_inv_reducible: return "ord_inv_reducible";
|
||||||
|
case polynom_property::ord_inv_irreducible: return "ord_inv_irreducible";
|
||||||
|
case polynom_property::sgn_inv_reducible: return "sgn_inv_reducible";
|
||||||
|
case polynom_property::sgn_inv_irreducible: return "sgn_inv_irreducible";
|
||||||
|
case polynom_property::connected: return "connected";
|
||||||
|
case polynom_property::an_sub: return "an_sub";
|
||||||
|
case polynom_property::sample: return "sample";
|
||||||
|
case polynom_property::repr: return "repr";
|
||||||
|
case polynom_property::holds: return "holds";
|
||||||
|
default: return "?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A single property instance ("fact") subject.
|
||||||
|
// Note: Not all fields are used by every property; unused fields are left at defaults.
|
||||||
|
struct property_inst {
|
||||||
|
polynom_property prop;
|
||||||
|
// Optional subject parameters
|
||||||
|
poly* p = nullptr; // for properties about a polynomial p
|
||||||
|
var y = -1; // variable (e.g., indexed root variable)
|
||||||
|
unsigned i = 0; // level/index (paper's i)
|
||||||
|
unsigned s_idx = 0; // index for "s ∈ R_i" (sample/root index), if applicable
|
||||||
|
unsigned I_idx = 0; // index for interval I (for repr), if applicable
|
||||||
|
unsigned ord_id = 0; // identifier for an indexed root ordering ≼, if applicable
|
||||||
|
|
||||||
|
// Convenience builders
|
||||||
|
static property_inst of(poly* p, polynom_property prop, unsigned level) {
|
||||||
|
property_inst r{prop}; r.p = p; r.i = level; return r;
|
||||||
|
}
|
||||||
|
static property_inst at_level(polynom_property prop, unsigned level) {
|
||||||
|
property_inst r{prop}; r.i = level; return r;
|
||||||
|
}
|
||||||
|
static property_inst sample_at(unsigned s_idx, unsigned level) {
|
||||||
|
property_inst r{polynom_property::sample}; r.s_idx = s_idx; r.i = level; return r;
|
||||||
|
}
|
||||||
|
static property_inst repr_of(unsigned I_idx, unsigned s_idx, unsigned level) {
|
||||||
|
property_inst r{polynom_property::repr}; r.I_idx = I_idx; r.s_idx = s_idx; r.i = level; return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display
|
||||||
|
std::string str() const {
|
||||||
|
std::ostringstream out;
|
||||||
|
out << to_string(prop) << "(";
|
||||||
|
bool first = true;
|
||||||
|
auto add = [&](std::string const& k, uint64_t v) {
|
||||||
|
if (!first) out << ", ";
|
||||||
|
first = false;
|
||||||
|
out << k << "=" << v;
|
||||||
|
};
|
||||||
|
if (p) add("p", reinterpret_cast<uint64_t>(p));
|
||||||
|
if (y != -1) add("y", static_cast<uint64_t>(y));
|
||||||
|
add("i", i);
|
||||||
|
if (s_idx) add("s", s_idx);
|
||||||
|
if (I_idx) add("I", I_idx);
|
||||||
|
if (ord_id) add("ord", ord_id);
|
||||||
|
out << ")";
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equality (coarse; pointer identity for p is intentional)
|
||||||
|
bool operator==(property_inst const& o) const {
|
||||||
|
return prop == o.prop &&
|
||||||
|
p == o.p &&
|
||||||
|
y == o.y &&
|
||||||
|
i == o.i &&
|
||||||
|
s_idx == o.s_idx &&
|
||||||
|
I_idx == o.I_idx &&
|
||||||
|
ord_id == o.ord_id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Proof rule identifiers (align roughly with paper sections; can be refined later).
|
||||||
|
enum class proof_rule : unsigned {
|
||||||
|
// Skeleton rules; refine/extend as we implement specific inferences
|
||||||
|
axiom, // introduce a known/assumed property
|
||||||
|
derive, // generic derivation step (placeholder)
|
||||||
|
r_ir_ord, // rules for ir_ord propagation
|
||||||
|
r_an_del,
|
||||||
|
r_non_null,
|
||||||
|
r_ord_inv_red,
|
||||||
|
r_ord_inv_irred,
|
||||||
|
r_sgn_inv_red,
|
||||||
|
r_sgn_inv_irred,
|
||||||
|
r_connected,
|
||||||
|
r_an_sub,
|
||||||
|
r_sample,
|
||||||
|
r_repr,
|
||||||
|
r_close_holds // closes to 'holds'
|
||||||
|
};
|
||||||
|
|
||||||
|
inline const char* to_string(proof_rule r) {
|
||||||
|
switch (r) {
|
||||||
|
case proof_rule::axiom: return "axiom";
|
||||||
|
case proof_rule::derive: return "derive";
|
||||||
|
case proof_rule::r_ir_ord: return "r_ir_ord";
|
||||||
|
case proof_rule::r_an_del: return "r_an_del";
|
||||||
|
case proof_rule::r_non_null: return "r_non_null";
|
||||||
|
case proof_rule::r_ord_inv_red: return "r_ord_inv_red";
|
||||||
|
case proof_rule::r_ord_inv_irred:return "r_ord_inv_irred";
|
||||||
|
case proof_rule::r_sgn_inv_red: return "r_sgn_inv_red";
|
||||||
|
case proof_rule::r_sgn_inv_irred:return "r_sgn_inv_irred";
|
||||||
|
case proof_rule::r_connected: return "r_connected";
|
||||||
|
case proof_rule::r_an_sub: return "r_an_sub";
|
||||||
|
case proof_rule::r_sample: return "r_sample";
|
||||||
|
case proof_rule::r_repr: return "r_repr";
|
||||||
|
case proof_rule::r_close_holds: return "r_close_holds";
|
||||||
|
default: return "?";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// A single proof step: premises -> conclusion, with metadata.
|
||||||
|
struct proof_step {
|
||||||
|
proof_rule rule;
|
||||||
|
property_mapping_case ctx = property_mapping_case::case1; // for multi-case rules (e.g., 4.2)
|
||||||
|
std::vector<unsigned> premises; // indices into facts_
|
||||||
|
unsigned conclusion; // index into facts_
|
||||||
|
|
||||||
|
std::string str(std::vector<property_inst> const& facts) const {
|
||||||
|
std::ostringstream out;
|
||||||
|
out << to_string(rule) << "[case=" << static_cast<unsigned>(ctx) << "]: ";
|
||||||
|
out << "{";
|
||||||
|
for (unsigned k = 0; k < premises.size(); ++k) {
|
||||||
|
if (k) out << ", ";
|
||||||
|
out << facts[premises[k]].str();
|
||||||
|
}
|
||||||
|
out << "} => " << facts[conclusion].str();
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// A lightweight proof object to manage facts and steps.
|
||||||
|
class levelwise_proof {
|
||||||
|
std::vector<property_inst> m_facts;
|
||||||
|
std::vector<proof_step> m_steps;
|
||||||
|
|
||||||
|
// Linear lookup to avoid hashing complexity here.
|
||||||
|
int find_fact(property_inst const& f) const {
|
||||||
|
for (unsigned i = 0; i < m_facts.size(); ++i)
|
||||||
|
if (m_facts[i] == f) return static_cast<int>(i);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
unsigned intern_fact(property_inst const& f) {
|
||||||
|
int idx = find_fact(f);
|
||||||
|
if (idx >= 0) return static_cast<unsigned>(idx);
|
||||||
|
m_facts.push_back(f);
|
||||||
|
return static_cast<unsigned>(m_facts.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned add_axiom(property_inst const& concl) {
|
||||||
|
unsigned c = intern_fact(concl);
|
||||||
|
m_steps.push_back(proof_step{proof_rule::axiom, property_mapping_case::case1, {}, c});
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned add_step(proof_rule rule,
|
||||||
|
property_mapping_case ctx,
|
||||||
|
std::vector<unsigned> const& premises,
|
||||||
|
property_inst const& conclusion) {
|
||||||
|
unsigned c = intern_fact(conclusion);
|
||||||
|
m_steps.push_back(proof_step{rule, ctx, premises, c});
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string dump() const {
|
||||||
|
std::ostringstream out;
|
||||||
|
for (auto const& st : m_steps)
|
||||||
|
out << st.str(m_facts) << "\n";
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<property_inst> const& facts() const { return m_facts; }
|
||||||
|
std::vector<proof_step> const& steps() const { return m_steps; }
|
||||||
|
};
|
||||||
|
|
||||||
|
// Property propagation mapping: for each property, the set of properties it implies (see levelwise paper, e.g., rule 4.2)
|
||||||
|
// Overload: property_dependencies with context (for rules like 4.2 with multiple cases).
|
||||||
|
// Note: This is a scaffold. Fill per paper’s precise rules (TODO).
|
||||||
|
inline std::vector<polynom_property>
|
||||||
|
get_property_dependencies(polynom_property prop, property_mapping_case /*p_case*/, polynomial_ref& /*p*/, unsigned /*i*/) {
|
||||||
|
std::vector<polynom_property> ret;
|
||||||
|
switch (prop) {
|
||||||
|
case polynom_property::ir_ord:
|
||||||
|
// TODO: fill using Property 4.3 / Rule 4.2 cases; currently no direct property implications recorded here.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case polynom_property::an_del:
|
||||||
|
// Example: an_del(p) might imply non_null(p) or be used in combination with others; leave empty until specified.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case polynom_property::non_null:
|
||||||
|
// Usually terminal for p; no generic implications without context.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case polynom_property::ord_inv_reducible:
|
||||||
|
// Often accompanies sign invariance for reducible polynomials; no generic implication recorded here.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case polynom_property::ord_inv_irreducible:
|
||||||
|
// Likewise for irreducible.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case polynom_property::sgn_inv_reducible:
|
||||||
|
// In many presentations, sign invariance implies order invariance on reducible components.
|
||||||
|
// If the paper states so, uncomment:
|
||||||
|
// ret.push_back(polynom_property::ord_inv_reducible);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case polynom_property::sgn_inv_irreducible:
|
||||||
|
// If sign invariance implies order invariance for irreducible p, uncomment:
|
||||||
|
// ret.push_back(polynom_property::ord_inv_irreducible);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case polynom_property::connected:
|
||||||
|
// connected(i) might enable sample or representation derivations; leave empty here.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case polynom_property::an_sub:
|
||||||
|
// an_sub(i) may enable additional sampling; leave empty here.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case polynom_property::sample:
|
||||||
|
// sample(s) is often a leaf for evaluating properties at s.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case polynom_property::repr:
|
||||||
|
// repr(I, s) together with others may lead to holds; no generic implication alone.
|
||||||
|
break;
|
||||||
|
|
||||||
|
case polynom_property::holds:
|
||||||
|
// Terminal goal, no further implications.
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example driver/stub that will eventually orchestrate Levelwise projection with proof logging.
|
||||||
|
void lewelwise_project(polynomial_ref_vector & /*ps*/, var /*max_x*/) {
|
||||||
|
//std::cout << "call\n";
|
||||||
|
//exit(0);
|
||||||
|
// Scaffold example (no-ops for now):
|
||||||
|
// levelwise_proof pr;
|
||||||
|
// for (auto* p : ps) { pr.add_axiom(property_inst::of(p, polynom_property::an_del, /*level*/0)); }
|
||||||
|
// std::cout << pr.dump();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,78 +1,6 @@
|
||||||
// Context for property propagation (e.g., which case of rule 4.2 applies)
|
|
||||||
enum class property_mapping_case : unsigned {
|
|
||||||
case1 = 0,
|
|
||||||
case2 = 1,
|
|
||||||
case3 = 3,
|
|
||||||
};
|
|
||||||
|
|
||||||
/*++
|
|
||||||
Copyright (c) 2025
|
|
||||||
|
|
||||||
Module Name:
|
|
||||||
levelwise.h
|
|
||||||
|
|
||||||
Abstract:
|
|
||||||
Levelwise algorithm property and mapping definitions (see levelwise paper).
|
|
||||||
|
|
||||||
--*/
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
|
||||||
#include "nlsat_types.h"
|
#include "nlsat_types.h"
|
||||||
|
|
||||||
namespace nlsat {
|
namespace nlsat {
|
||||||
|
void lewelwise_project(polynomial_ref_vector & ps, var max_x);
|
||||||
/**
|
|
||||||
* Properties for indexed roots, in the order specified by the levelwise paper.
|
|
||||||
* The order is important for the algorithm and must match the paper exactly.
|
|
||||||
*/
|
|
||||||
enum class polynom_property : unsigned {
|
|
||||||
ir_ord, // ir_ord(≼, s) for all ≼ for roots of level i and s ∈ Ri−1
|
|
||||||
an_del, // an_del(p) for all p of level i
|
|
||||||
non_null, // non_null(p) for all p of level i
|
|
||||||
ord_inv_reducible, // ord_inv(p) for all reducible p of level i
|
|
||||||
ord_inv_irreducible, // ord_inv(p) for all irreducible p of level i
|
|
||||||
sgn_inv_reducible, // sgn_inv(p) for all reducible p of level i
|
|
||||||
sgn_inv_irreducible, // sgn_inv(p) for all irreducible p of level i
|
|
||||||
connected, // connected(i)
|
|
||||||
an_sub, // an_sub(i)
|
|
||||||
sample, // sample(s) for all s ∈ Ri of level i
|
|
||||||
repr, // repr(I, s) for all I of level i and s ∈ ...
|
|
||||||
holds,
|
|
||||||
_count // always last: number of properties
|
|
||||||
};
|
|
||||||
|
|
||||||
// Utility: property to string (for debugging, logging, etc.)
|
|
||||||
inline const char* to_string(polynom_property prop) {
|
|
||||||
switch (prop) {
|
|
||||||
case polynom_property::ir_ord: return "ir_ord";
|
|
||||||
case polynom_property::an_del: return "an_del";
|
|
||||||
case polynom_property::non_null: return "non_null";
|
|
||||||
case polynom_property::ord_inv_reducible: return "ord_inv_reducible";
|
|
||||||
case polynom_property::ord_inv_irreducible: return "ord_inv_irreducible";
|
|
||||||
case polynom_property::sgn_inv_reducible: return "sgn_inv_reducible";
|
|
||||||
case polynom_property::sgn_inv_irreducible: return "sgn_inv_irreducible";
|
|
||||||
case polynom_property::connected: return "connected";
|
|
||||||
case polynom_property::an_sub: return "an_sub";
|
|
||||||
case polynom_property::sample: return "sample";
|
|
||||||
case polynom_property::repr: return "repr";
|
|
||||||
case polynom_property::holds: return "holds";
|
|
||||||
default: throw "unknown";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Property propagation mapping: for each property, the set of properties it implies (see levelwise paper, e.g., rule 4.2)
|
|
||||||
// Example: property_dependencies[root_property::sgn_inv] = {root_property::ord_inv, ...}
|
|
||||||
// Overload: property_dependencies with context (for rules like 4.2 with multiple cases)
|
|
||||||
inline const std::vector<polynom_property> get_property_dependencies(polynom_property prop, property_mapping_case p_case) {
|
|
||||||
// Each property has a table of vectors, one per context case
|
|
||||||
static const std::vector<polynom_property> table[][10] = {
|
|
||||||
|
|
||||||
};
|
|
||||||
return table[(unsigned)prop][(unsigned)p_case];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace nlsat
|
} // namespace nlsat
|
||||||
|
|
|
@ -1246,7 +1246,7 @@ namespace nlsat {
|
||||||
|
|
||||||
polynomial_ref_vector samples(m_pm);
|
polynomial_ref_vector samples(m_pm);
|
||||||
|
|
||||||
|
lewelwise_project(ps, max_x);
|
||||||
if (x < max_x)
|
if (x < max_x)
|
||||||
cac_add_cell_lits(ps, x, samples);
|
cac_add_cell_lits(ps, x, samples);
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue