mirror of
https://github.com/Z3Prover/z3
synced 2025-04-27 02:45:51 +00:00
Merge branch 'opt' of https://github.com/NikolajBjorner/z3 into opt
This commit is contained in:
commit
db4bbecb27
174 changed files with 30440 additions and 922 deletions
|
@ -62,4 +62,4 @@ namespace System.Diagnostics.Contracts
|
|||
public static void EndContractBlock() { }
|
||||
public static T ValueAtReturn<T>(out T v) { T[] t = new T[1]; v = t[0]; return v; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,4 +3,4 @@
|
|||
<startup>
|
||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
||||
</startup>
|
||||
</configuration>
|
||||
</configuration>
|
||||
|
|
|
@ -75,4 +75,4 @@
|
|||
<Target Name="AfterBuild">
|
||||
</Target>
|
||||
-->
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -344,4 +344,4 @@
|
|||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
</Project>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Code.Contract" version="1.0.0" targetFramework="net35" />
|
||||
</packages>
|
||||
</packages>
|
||||
|
|
|
@ -164,6 +164,6 @@ setup(
|
|||
package_data={
|
||||
'z3': [os.path.join('lib', '*'), os.path.join('include', '*.h'), os.path.join('include', 'c++', '*.h')]
|
||||
},
|
||||
scripts=[os.path.join('bin', EXECUTABLE_FILE)],
|
||||
data_files=[('bin',[os.path.join('bin',EXECUTABLE_FILE)])],
|
||||
cmdclass={'build': build, 'develop': develop, 'sdist': sdist, 'bdist_egg': bdist_egg},
|
||||
)
|
||||
|
|
|
@ -1300,8 +1300,10 @@ class BoolSortRef(SortRef):
|
|||
if isinstance(val, bool):
|
||||
return BoolVal(val, self.ctx)
|
||||
if __debug__:
|
||||
_z3_assert(is_expr(val), "True, False or Z3 Boolean expression expected. Received %s" % val)
|
||||
_z3_assert(self.eq(val.sort()), "Value cannot be converted into a Z3 Boolean value")
|
||||
if not is_expr(val):
|
||||
_z3_assert(is_expr(val), "True, False or Z3 Boolean expression expected. Received %s" % val)
|
||||
if not self.eq(val.sort()):
|
||||
_z3_assert(self.eq(val.sort()), "Value cannot be converted into a Z3 Boolean value")
|
||||
return val
|
||||
|
||||
def subsort(self, other):
|
||||
|
|
|
@ -305,6 +305,7 @@ public:
|
|||
MATCH_UNARY(is_uminus);
|
||||
MATCH_UNARY(is_to_real);
|
||||
MATCH_UNARY(is_to_int);
|
||||
MATCH_UNARY(is_is_int);
|
||||
MATCH_BINARY(is_sub);
|
||||
MATCH_BINARY(is_add);
|
||||
MATCH_BINARY(is_mul);
|
||||
|
@ -377,6 +378,9 @@ public:
|
|||
app * mk_real(int i) {
|
||||
return mk_numeral(rational(i), false);
|
||||
}
|
||||
app * mk_real(rational const& r) {
|
||||
return mk_numeral(r, false);
|
||||
}
|
||||
app * mk_le(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LE, arg1, arg2); }
|
||||
app * mk_ge(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_GE, arg1, arg2); }
|
||||
app * mk_lt(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LT, arg1, arg2); }
|
||||
|
|
|
@ -44,8 +44,11 @@ void ast_pp_util::display_decls(std::ostream& out) {
|
|||
}
|
||||
n = coll.get_num_decls();
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
ast_smt2_pp(out, coll.get_func_decls()[i], env);
|
||||
out << "\n";
|
||||
func_decl* f = coll.get_func_decls()[i];
|
||||
if (f->get_family_id() == null_family_id) {
|
||||
ast_smt2_pp(out, f, env);
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ Notes:
|
|||
#include"ast_pp.h"
|
||||
#include"lbool.h"
|
||||
#include"uint_set.h"
|
||||
#include"gparams.h"
|
||||
|
||||
const unsigned g_primes[7] = { 2, 3, 5, 7, 11, 13, 17};
|
||||
|
||||
|
@ -54,6 +55,8 @@ struct pb2bv_rewriter::imp {
|
|||
vector<rational> m_coeffs;
|
||||
bool m_keep_cardinality_constraints;
|
||||
bool m_keep_pb_constraints;
|
||||
bool m_pb_num_system;
|
||||
bool m_pb_totalizer;
|
||||
unsigned m_min_arity;
|
||||
|
||||
template<lbool is_le>
|
||||
|
@ -114,23 +117,24 @@ struct pb2bv_rewriter::imp {
|
|||
return expr_ref((is_le == l_false)?m.mk_true():m.mk_false(), m);
|
||||
}
|
||||
|
||||
#if 0
|
||||
expr_ref result(m);
|
||||
switch (is_le) {
|
||||
case l_true: if (mk_le_tot(sz, args, k, result)) return result; else break;
|
||||
case l_false: if (mk_ge_tot(sz, args, k, result)) return result; else break;
|
||||
case l_undef: break;
|
||||
if (m_pb_totalizer) {
|
||||
expr_ref result(m);
|
||||
switch (is_le) {
|
||||
case l_true: if (mk_le_tot(sz, args, k, result)) return result; else break;
|
||||
case l_false: if (mk_ge_tot(sz, args, k, result)) return result; else break;
|
||||
case l_undef: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_pb_num_system) {
|
||||
expr_ref result(m);
|
||||
switch (is_le) {
|
||||
case l_true: if (mk_le(sz, args, k, result)) return result; else break;
|
||||
case l_false: if (mk_ge(sz, args, k, result)) return result; else break;
|
||||
case l_undef: if (mk_eq(sz, args, k, result)) return result; else break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if 0
|
||||
expr_ref result(m);
|
||||
switch (is_le) {
|
||||
case l_true: if (mk_le(sz, args, k, result)) return result; else break;
|
||||
case l_false: if (mk_ge(sz, args, k, result)) return result; else break;
|
||||
case l_undef: if (mk_eq(sz, args, k, result)) return result; else break;
|
||||
}
|
||||
#endif
|
||||
// fall back to divide and conquer encoding.
|
||||
SASSERT(k.is_pos());
|
||||
expr_ref zero(m), bound(m);
|
||||
|
@ -397,22 +401,31 @@ struct pb2bv_rewriter::imp {
|
|||
for (unsigned j = 0; j + lim - 1 < out.size(); j += n) {
|
||||
expr_ref tmp(m);
|
||||
tmp = out[j + lim - 1];
|
||||
if (j + n < out.size()) {
|
||||
tmp = m.mk_and(tmp, m.mk_not(out[j + n]));
|
||||
if (j + n - 1 < out.size()) {
|
||||
tmp = m.mk_and(tmp, m.mk_not(out[j + n - 1]));
|
||||
}
|
||||
ors.push_back(tmp);
|
||||
}
|
||||
return ::mk_or(ors);
|
||||
}
|
||||
|
||||
// x0 + 5x1 + 3x2 >= k
|
||||
// x0 x1 x1 -> s0 s1 s2
|
||||
// s2 x1 x2 -> s3 s4 s5
|
||||
// k = 7: s5 or (s4 & not s2 & s0)
|
||||
// k = 6: s4
|
||||
// k = 5: s4 or (s3 & not s2 & s1)
|
||||
// k = 4: s4 or (s3 & not s2 & s0)
|
||||
// k = 3: s3
|
||||
//
|
||||
bool mk_ge(unsigned sz, expr * const* args, rational bound, expr_ref& result) {
|
||||
if (!create_basis()) return false;
|
||||
if (!bound.is_unsigned()) return false;
|
||||
vector<rational> coeffs(m_coeffs);
|
||||
result = m.mk_true();
|
||||
expr_ref_vector carry(m), new_carry(m);
|
||||
for (unsigned i = 0; i < m_base.size(); ++i) {
|
||||
rational b_i = m_base[i];
|
||||
m_base.push_back(bound + rational::one());
|
||||
for (rational b_i : m_base) {
|
||||
unsigned B = b_i.get_unsigned();
|
||||
unsigned d_i = (bound % b_i).get_unsigned();
|
||||
bound = div(bound, b_i);
|
||||
|
@ -425,16 +438,16 @@ struct pb2bv_rewriter::imp {
|
|||
coeffs[j] = div(coeffs[j], b_i);
|
||||
}
|
||||
TRACE("pb", tout << "Carry: " << carry << "\n";
|
||||
for (unsigned j = 0; j < coeffs.size(); ++j) tout << coeffs[j] << " ";
|
||||
for (auto c : coeffs) tout << c << " ";
|
||||
tout << "\n";
|
||||
);
|
||||
ptr_vector<expr> out;
|
||||
m_sort.sorting(carry.size(), carry.c_ptr(), out);
|
||||
|
||||
|
||||
expr_ref gt = mod_ge(out, B, d_i + 1);
|
||||
expr_ref ge = mod_ge(out, B, d_i);
|
||||
result = mk_or(gt, mk_and(ge, result));
|
||||
TRACE("pb", tout << result << "\n";);
|
||||
TRACE("pb", tout << "b: " << b_i << " d: " << d_i << " gt: " << gt << " ge: " << ge << " " << result << "\n";);
|
||||
|
||||
new_carry.reset();
|
||||
for (unsigned j = B - 1; j < out.size(); j += B) {
|
||||
|
@ -443,11 +456,11 @@ struct pb2bv_rewriter::imp {
|
|||
carry.reset();
|
||||
carry.append(new_carry);
|
||||
}
|
||||
TRACE("pb", tout << result << "\n";);
|
||||
TRACE("pb", tout << "bound: " << bound << " Carry: " << carry << " result: " << result << "\n";);
|
||||
return true;
|
||||
}
|
||||
|
||||
expr_ref mk_and(expr_ref& a, expr_ref& b) {
|
||||
expr_ref mk_and(expr_ref& a, expr_ref& b) {
|
||||
if (m.is_true(a)) return b;
|
||||
if (m.is_true(b)) return a;
|
||||
if (m.is_false(a)) return a;
|
||||
|
@ -491,6 +504,7 @@ struct pb2bv_rewriter::imp {
|
|||
for (unsigned i = 0; i < sz; ++i) {
|
||||
m_coeffs.push_back(pb.get_coeff(f, i));
|
||||
}
|
||||
CTRACE("pb", k.is_neg(), tout << expr_ref(m.mk_app(f, sz, args), m) << "\n";);
|
||||
SASSERT(!k.is_neg());
|
||||
switch (kind) {
|
||||
case OP_PB_GE:
|
||||
|
@ -567,6 +581,8 @@ struct pb2bv_rewriter::imp {
|
|||
m_args(m),
|
||||
m_keep_cardinality_constraints(false),
|
||||
m_keep_pb_constraints(false),
|
||||
m_pb_num_system(false),
|
||||
m_pb_totalizer(false),
|
||||
m_min_arity(2)
|
||||
{}
|
||||
|
||||
|
@ -761,6 +777,14 @@ struct pb2bv_rewriter::imp {
|
|||
void keep_pb_constraints(bool f) {
|
||||
m_keep_pb_constraints = f;
|
||||
}
|
||||
|
||||
void pb_num_system(bool f) {
|
||||
m_pb_num_system = f;
|
||||
}
|
||||
|
||||
void pb_totalizer(bool f) {
|
||||
m_pb_totalizer = f;
|
||||
}
|
||||
};
|
||||
|
||||
struct card2bv_rewriter_cfg : public default_rewriter_cfg {
|
||||
|
@ -774,6 +798,8 @@ struct pb2bv_rewriter::imp {
|
|||
card2bv_rewriter_cfg(imp& i, ast_manager & m):m_r(i, m) {}
|
||||
void keep_cardinality_constraints(bool f) { m_r.keep_cardinality_constraints(f); }
|
||||
void keep_pb_constraints(bool f) { m_r.keep_pb_constraints(f); }
|
||||
void pb_num_system(bool f) { m_r.pb_num_system(f); }
|
||||
void pb_totalizer(bool f) { m_r.pb_totalizer(f); }
|
||||
};
|
||||
|
||||
class card_pb_rewriter : public rewriter_tpl<card2bv_rewriter_cfg> {
|
||||
|
@ -784,27 +810,62 @@ struct pb2bv_rewriter::imp {
|
|||
m_cfg(i, m) {}
|
||||
void keep_cardinality_constraints(bool f) { m_cfg.keep_cardinality_constraints(f); }
|
||||
void keep_pb_constraints(bool f) { m_cfg.keep_pb_constraints(f); }
|
||||
void pb_num_system(bool f) { m_cfg.pb_num_system(f); }
|
||||
void pb_totalizer(bool f) { m_cfg.pb_totalizer(f); }
|
||||
};
|
||||
|
||||
card_pb_rewriter m_rw;
|
||||
|
||||
bool keep_cardinality() const {
|
||||
params_ref const& p = m_params;
|
||||
return
|
||||
p.get_bool("keep_cardinality_constraints", false) ||
|
||||
p.get_bool("sat.cardinality.solver", false) ||
|
||||
p.get_bool("cardinality.solver", false) ||
|
||||
gparams::get_module("sat").get_bool("cardinality.solver", false) ||
|
||||
keep_pb();
|
||||
}
|
||||
|
||||
bool keep_pb() const {
|
||||
params_ref const& p = m_params;
|
||||
return
|
||||
p.get_bool("keep_pb_constraints", false) ||
|
||||
p.get_bool("sat.pb.solver", false) ||
|
||||
p.get_bool("pb.solver", false) ||
|
||||
gparams::get_module("sat").get_bool("pb.solver", false);
|
||||
}
|
||||
|
||||
bool pb_num_system() const {
|
||||
return m_params.get_bool("pb_num_system", false);
|
||||
}
|
||||
|
||||
bool pb_totalizer() const {
|
||||
return m_params.get_bool("pb_totalizer", false);
|
||||
}
|
||||
|
||||
imp(ast_manager& m, params_ref const& p):
|
||||
m(m), m_params(p), m_lemmas(m),
|
||||
m_fresh(m),
|
||||
m_num_translated(0),
|
||||
m_rw(*this, m) {
|
||||
m_rw.keep_cardinality_constraints(p.get_bool("keep_cardinality_constraints", false));
|
||||
m_rw.keep_pb_constraints(p.get_bool("keep_pb_constraints", false));
|
||||
m_rw.keep_cardinality_constraints(keep_cardinality());
|
||||
m_rw.keep_pb_constraints(keep_pb());
|
||||
m_rw.pb_num_system(pb_num_system());
|
||||
m_rw.pb_totalizer(pb_totalizer());
|
||||
}
|
||||
|
||||
void updt_params(params_ref const & p) {
|
||||
m_params.append(p);
|
||||
m_rw.keep_cardinality_constraints(m_params.get_bool("keep_cardinality_constraints", false));
|
||||
m_rw.keep_pb_constraints(m_params.get_bool("keep_pb_constraints", false));
|
||||
m_rw.keep_cardinality_constraints(keep_cardinality());
|
||||
m_rw.keep_pb_constraints(keep_pb());
|
||||
m_rw.pb_num_system(pb_num_system());
|
||||
m_rw.pb_totalizer(pb_totalizer());
|
||||
}
|
||||
void collect_param_descrs(param_descrs& r) const {
|
||||
r.insert("keep_cardinality_constraints", CPK_BOOL, "(default: true) retain cardinality constraints (don't bit-blast them) and use built-in cardinality solver");
|
||||
r.insert("keep_pb_constraints", CPK_BOOL, "(default: true) retain pb constraints (don't bit-blast them) and use built-in pb solver");
|
||||
r.insert("pb_num_system", CPK_BOOL, "(default: false) use pb number system encoding");
|
||||
r.insert("pb_totalizer", CPK_BOOL, "(default: false) use pb totalizer encoding");
|
||||
}
|
||||
|
||||
unsigned get_num_steps() const { return m_rw.get_num_steps(); }
|
||||
|
|
|
@ -232,6 +232,7 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons
|
|||
}
|
||||
|
||||
bool is_eq = f->get_decl_kind() == OP_PB_EQ;
|
||||
br_status st = BR_DONE;
|
||||
|
||||
pb_ast_rewriter_util pbu(m);
|
||||
pb_rewriter_util<pb_ast_rewriter_util> util(pbu);
|
||||
|
@ -249,11 +250,14 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons
|
|||
default: {
|
||||
bool all_unit = true;
|
||||
unsigned sz = vec.size();
|
||||
rational slack(0);
|
||||
m_args.reset();
|
||||
m_coeffs.reset();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
m_args.push_back(vec[i].first);
|
||||
m_coeffs.push_back(vec[i].second);
|
||||
SASSERT(vec[i].second.is_pos());
|
||||
slack += vec[i].second;
|
||||
all_unit &= m_coeffs.back().is_one();
|
||||
}
|
||||
if (is_eq) {
|
||||
|
@ -271,7 +275,42 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons
|
|||
result = mk_and(m, sz, m_args.c_ptr());
|
||||
}
|
||||
else {
|
||||
result = m_util.mk_ge(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k);
|
||||
expr_ref_vector conj(m), disj(m);
|
||||
for (unsigned i = 0; i < m_args.size(); ++i) {
|
||||
rational& c = m_coeffs[i];
|
||||
if (slack < c + k) {
|
||||
conj.push_back(m_args[i]);
|
||||
slack -= c;
|
||||
k -= c;
|
||||
}
|
||||
else if (c >= k) {
|
||||
slack -= c;
|
||||
disj.push_back(m_args[i]);
|
||||
}
|
||||
else {
|
||||
continue;
|
||||
}
|
||||
m_args[i] = m_args.back();
|
||||
m_coeffs[i] = m_coeffs.back();
|
||||
--i;
|
||||
m_args.pop_back();
|
||||
m_coeffs.pop_back();
|
||||
}
|
||||
sz = m_coeffs.size();
|
||||
if (slack < k) {
|
||||
conj.push_back(m.mk_false());
|
||||
}
|
||||
else if (k.is_pos() && sz > 0) {
|
||||
conj.push_back(m_util.mk_ge(sz, m_coeffs.c_ptr(), m_args.c_ptr(), k));
|
||||
}
|
||||
result = mk_and(conj);
|
||||
if (!disj.empty()) {
|
||||
disj.push_back(result);
|
||||
result = mk_or(disj);
|
||||
}
|
||||
if (!disj.empty() || conj.size() > 1) {
|
||||
st = BR_REWRITE3;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -286,7 +325,7 @@ br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * cons
|
|||
TRACE("pb_validate",
|
||||
validate_rewrite(f, num_args, args, result););
|
||||
|
||||
return BR_DONE;
|
||||
return st;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -440,24 +440,16 @@ br_status seq_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * con
|
|||
* (seq.unit (_ BitVector 8)) ==> String constant
|
||||
*/
|
||||
br_status seq_rewriter::mk_seq_unit(expr* e, expr_ref& result) {
|
||||
sort * s = m().get_sort(e);
|
||||
bv_util bvu(m());
|
||||
|
||||
if (is_sort_of(s, bvu.get_family_id(), BV_SORT)) {
|
||||
// specifically we want (_ BitVector 8)
|
||||
rational n_val;
|
||||
unsigned int n_size;
|
||||
if (bvu.is_numeral(e, n_val, n_size)) {
|
||||
if (n_size == 8) {
|
||||
// convert to string constant
|
||||
char ch = (char)n_val.get_int32();
|
||||
TRACE("seq", tout << "rewrite seq.unit of 8-bit value " << n_val.to_string() << " to string constant \"" << ch << "\"" << std::endl;);
|
||||
char s_tmp[2] = {ch, '\0'};
|
||||
symbol s(s_tmp);
|
||||
result = m_util.str.mk_string(s);
|
||||
return BR_DONE;
|
||||
}
|
||||
}
|
||||
rational n_val;
|
||||
unsigned int n_size;
|
||||
// specifically we want (_ BitVector 8)
|
||||
if (bvu.is_bv(e) && bvu.is_numeral(e, n_val, n_size) && n_size == 8) {
|
||||
// convert to string constant
|
||||
zstring str(n_val.get_unsigned());
|
||||
TRACE("seq", tout << "rewrite seq.unit of 8-bit value " << n_val.to_string() << " to string constant \"" << str<< "\"" << std::endl;);
|
||||
result = m_util.str.mk_string(str);
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
|
@ -1431,37 +1423,10 @@ br_status seq_rewriter::mk_re_star(expr* a, expr_ref& result) {
|
|||
}
|
||||
|
||||
/*
|
||||
* (re.range c_1 c_n) = (re.union (str.to.re c1) (str.to.re c2) ... (str.to.re cn))
|
||||
* (re.range c_1 c_n)
|
||||
*/
|
||||
br_status seq_rewriter::mk_re_range(expr* lo, expr* hi, expr_ref& result) {
|
||||
return BR_FAILED;
|
||||
TRACE("seq", tout << "rewrite re.range [" << mk_pp(lo, m()) << " " << mk_pp(hi, m()) << "]\n";);
|
||||
zstring str_lo, str_hi;
|
||||
if (m_util.str.is_string(lo, str_lo) && m_util.str.is_string(hi, str_hi)) {
|
||||
if (str_lo.length() == 1 && str_hi.length() == 1) {
|
||||
unsigned int c1 = str_lo[0];
|
||||
unsigned int c2 = str_hi[0];
|
||||
if (c1 > c2) {
|
||||
// exchange c1 and c2
|
||||
unsigned int tmp = c1;
|
||||
c2 = c1;
|
||||
c1 = tmp;
|
||||
}
|
||||
zstring s(c1);
|
||||
expr_ref acc(m_util.re.mk_to_re(m_util.str.mk_string(s)), m());
|
||||
for (unsigned int ch = c1 + 1; ch <= c2; ++ch) {
|
||||
zstring s_ch(ch);
|
||||
expr_ref acc2(m_util.re.mk_to_re(m_util.str.mk_string(s_ch)), m());
|
||||
acc = m_util.re.mk_union(acc, acc2);
|
||||
}
|
||||
result = acc;
|
||||
return BR_REWRITE2;
|
||||
} else {
|
||||
m().raise_exception("string constants in re.range must have length 1");
|
||||
}
|
||||
}
|
||||
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1506,6 +1471,7 @@ br_status seq_rewriter::mk_re_opt(expr* a, expr_ref& result) {
|
|||
}
|
||||
|
||||
br_status seq_rewriter::mk_eq_core(expr * l, expr * r, expr_ref & result) {
|
||||
TRACE("seq", tout << mk_pp(l, m()) << " = " << mk_pp(r, m()) << "\n";);
|
||||
expr_ref_vector lhs(m()), rhs(m()), res(m());
|
||||
bool changed = false;
|
||||
if (!reduce_eq(l, r, lhs, rhs, changed)) {
|
||||
|
|
|
@ -876,6 +876,36 @@ bool seq_decl_plugin::is_value(app* e) const {
|
|||
}
|
||||
}
|
||||
|
||||
bool seq_decl_plugin::are_equal(app* a, app* b) const {
|
||||
if (a == b) return true;
|
||||
// handle concatenations
|
||||
return false;
|
||||
}
|
||||
|
||||
bool seq_decl_plugin::are_distinct(app* a, app* b) const {
|
||||
if (a == b) {
|
||||
return false;
|
||||
}
|
||||
if (is_app_of(a, m_family_id, OP_STRING_CONST) &&
|
||||
is_app_of(b, m_family_id, OP_STRING_CONST)) {
|
||||
return true;
|
||||
}
|
||||
if (is_app_of(a, m_family_id, OP_SEQ_UNIT) &&
|
||||
is_app_of(b, m_family_id, OP_SEQ_UNIT)) {
|
||||
return true;
|
||||
}
|
||||
if (is_app_of(a, m_family_id, OP_SEQ_EMPTY) &&
|
||||
is_app_of(b, m_family_id, OP_SEQ_UNIT)) {
|
||||
return true;
|
||||
}
|
||||
if (is_app_of(b, m_family_id, OP_SEQ_EMPTY) &&
|
||||
is_app_of(a, m_family_id, OP_SEQ_UNIT)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
expr* seq_decl_plugin::get_some_value(sort* s) {
|
||||
seq_util util(*m_manager);
|
||||
if (util.is_seq(s)) {
|
||||
|
|
|
@ -182,7 +182,11 @@ public:
|
|||
|
||||
virtual bool is_value(app * e) const;
|
||||
|
||||
virtual bool is_unique_value(app * e) const { return is_value(e); }
|
||||
virtual bool is_unique_value(app * e) const { return false; }
|
||||
|
||||
virtual bool are_equal(app* a, app* b) const;
|
||||
|
||||
virtual bool are_distinct(app* a, app* b) const;
|
||||
|
||||
virtual expr * get_some_value(sort * s);
|
||||
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
foci2.h
|
||||
|
||||
Abstract:
|
||||
|
||||
An interface class for foci2.
|
||||
|
||||
Author:
|
||||
|
||||
Ken McMillan (kenmcmil)
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef FOCI2_H
|
||||
#define FOCI2_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#define FOCI2_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define FOCI2_EXPORT __attribute__ ((visibility ("default")))
|
||||
#endif
|
||||
|
||||
class foci2 {
|
||||
public:
|
||||
virtual ~foci2(){}
|
||||
|
||||
typedef int ast;
|
||||
typedef int symb;
|
||||
|
||||
/** Built-in operators */
|
||||
enum ops {
|
||||
And = 0, Or, Not, Iff, Ite, Equal, Plus, Times, Floor, Leq, Div, Bool, Int, Array, Tsym, Fsym, Forall, Exists, Distinct, LastOp
|
||||
};
|
||||
|
||||
virtual symb mk_func(const std::string &s) = 0;
|
||||
virtual symb mk_pred(const std::string &s) = 0;
|
||||
virtual ast mk_op(ops op, const std::vector<ast> args) = 0;
|
||||
virtual ast mk_op(ops op, ast) = 0;
|
||||
virtual ast mk_op(ops op, ast, ast) = 0;
|
||||
virtual ast mk_op(ops op, ast, ast, ast) = 0;
|
||||
virtual ast mk_int(const std::string &) = 0;
|
||||
virtual ast mk_rat(const std::string &) = 0;
|
||||
virtual ast mk_true() = 0;
|
||||
virtual ast mk_false() = 0;
|
||||
virtual ast mk_app(symb,const std::vector<ast> args) = 0;
|
||||
|
||||
virtual bool get_func(ast, symb &) = 0;
|
||||
virtual bool get_pred(ast, symb &) = 0;
|
||||
virtual bool get_op(ast, ops &) = 0;
|
||||
virtual bool get_true(ast id) = 0;
|
||||
virtual bool get_false(ast id) = 0;
|
||||
virtual bool get_int(ast id, std::string &res) = 0;
|
||||
virtual bool get_rat(ast id, std::string &res) = 0;
|
||||
virtual const std::string &get_symb(symb) = 0;
|
||||
|
||||
virtual int get_num_args(ast) = 0;
|
||||
virtual ast get_arg(ast, int) = 0;
|
||||
|
||||
virtual void show_ast(ast) = 0;
|
||||
|
||||
virtual bool interpolate(const std::vector<ast> &frames, std::vector<ast> &itps, std::vector<int> parents) = 0;
|
||||
|
||||
FOCI2_EXPORT static foci2 *create(const std::string &);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,25 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
foci2.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Fake foci2, to be replaced
|
||||
|
||||
Author:
|
||||
|
||||
Ken McMillan (kenmcmil)
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include "foci2.h"
|
||||
|
||||
FOCI2_EXPORT foci2 *foci2::create(const std::string &){
|
||||
return 0;
|
||||
}
|
|
@ -1,75 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
foci2.h
|
||||
|
||||
Abstract:
|
||||
|
||||
An interface class for foci2.
|
||||
|
||||
Author:
|
||||
|
||||
Ken McMillan (kenmcmil)
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef FOCI2_H
|
||||
#define FOCI2_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#ifdef WIN32
|
||||
#define FOCI2_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define FOCI2_EXPORT __attribute__ ((visibility ("default")))
|
||||
#endif
|
||||
|
||||
class foci2 {
|
||||
public:
|
||||
virtual ~foci2(){}
|
||||
|
||||
typedef int ast;
|
||||
typedef int symb;
|
||||
|
||||
/** Built-in operators */
|
||||
enum ops {
|
||||
And = 0, Or, Not, Iff, Ite, Equal, Plus, Times, Floor, Leq, Div, Bool, Int, Array, Tsym, Fsym, Forall, Exists, Distinct, LastOp
|
||||
};
|
||||
|
||||
virtual symb mk_func(const std::string &s) = 0;
|
||||
virtual symb mk_pred(const std::string &s) = 0;
|
||||
virtual ast mk_op(ops op, const std::vector<ast> args) = 0;
|
||||
virtual ast mk_op(ops op, ast) = 0;
|
||||
virtual ast mk_op(ops op, ast, ast) = 0;
|
||||
virtual ast mk_op(ops op, ast, ast, ast) = 0;
|
||||
virtual ast mk_int(const std::string &) = 0;
|
||||
virtual ast mk_rat(const std::string &) = 0;
|
||||
virtual ast mk_true() = 0;
|
||||
virtual ast mk_false() = 0;
|
||||
virtual ast mk_app(symb,const std::vector<ast> args) = 0;
|
||||
|
||||
virtual bool get_func(ast, symb &) = 0;
|
||||
virtual bool get_pred(ast, symb &) = 0;
|
||||
virtual bool get_op(ast, ops &) = 0;
|
||||
virtual bool get_true(ast id) = 0;
|
||||
virtual bool get_false(ast id) = 0;
|
||||
virtual bool get_int(ast id, std::string &res) = 0;
|
||||
virtual bool get_rat(ast id, std::string &res) = 0;
|
||||
virtual const std::string &get_symb(symb) = 0;
|
||||
|
||||
virtual int get_num_args(ast) = 0;
|
||||
virtual ast get_arg(ast, int) = 0;
|
||||
|
||||
virtual void show_ast(ast) = 0;
|
||||
|
||||
virtual bool interpolate(const std::vector<ast> &frames, std::vector<ast> &itps, std::vector<int> parents) = 0;
|
||||
|
||||
FOCI2_EXPORT static foci2 *create(const std::string &);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,356 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
iz3foci.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Implements a secondary solver using foci2.
|
||||
|
||||
Author:
|
||||
|
||||
Ken McMillan (kenmcmil)
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
|
||||
#include "iz3hash.h"
|
||||
#include "foci2.h"
|
||||
#include "iz3foci.h"
|
||||
|
||||
using namespace stl_ext;
|
||||
|
||||
class iz3foci_impl : public iz3secondary {
|
||||
|
||||
int frames;
|
||||
int *parents;
|
||||
foci2 *foci;
|
||||
foci2::symb select_op;
|
||||
foci2::symb store_op;
|
||||
foci2::symb mod_op;
|
||||
|
||||
public:
|
||||
iz3foci_impl(iz3mgr *mgr, int _frames, int *_parents) : iz3secondary(*mgr) {
|
||||
frames = _frames;
|
||||
parents = _parents;
|
||||
foci = 0;
|
||||
}
|
||||
|
||||
typedef hash_map<ast,foci2::ast> AstToNode;
|
||||
AstToNode ast_to_node; // maps Z3 ast's to foci expressions
|
||||
|
||||
typedef hash_map<foci2::ast,ast> NodeToAst;
|
||||
NodeToAst node_to_ast; // maps Z3 ast's to foci expressions
|
||||
|
||||
// We only use this for FuncDeclToSymbol, which has no range destructor
|
||||
struct symb_hash {
|
||||
size_t operator()(const symb &s) const {
|
||||
return (size_t) s;
|
||||
}
|
||||
};
|
||||
|
||||
typedef hash_map<symb,foci2::symb> FuncDeclToSymbol;
|
||||
FuncDeclToSymbol func_decl_to_symbol; // maps Z3 func decls to symbols
|
||||
|
||||
typedef hash_map<foci2::symb,symb> SymbolToFuncDecl;
|
||||
SymbolToFuncDecl symbol_to_func_decl; // maps symbols to Z3 func decls
|
||||
|
||||
int from_symb(symb func){
|
||||
std::string name = string_of_symbol(func);
|
||||
bool is_bool = is_bool_type(get_range_type(func));
|
||||
foci2::symb f;
|
||||
if(is_bool)
|
||||
f = foci->mk_pred(name);
|
||||
else
|
||||
f = foci->mk_func(name);
|
||||
symbol_to_func_decl[f] = func;
|
||||
func_decl_to_symbol[func] = f;
|
||||
return f;
|
||||
}
|
||||
|
||||
// create a symbol corresponding to a DeBruijn index of a particular type
|
||||
// the type has to be encoded into the name because the same index can
|
||||
// occur with different types
|
||||
foci2::symb make_deBruijn_symbol(int index, type ty){
|
||||
std::ostringstream s;
|
||||
// s << "#" << index << "#" << type;
|
||||
return foci->mk_func(s.str());
|
||||
}
|
||||
|
||||
int from_Z3_ast(ast t){
|
||||
std::pair<ast,foci2::ast> foo(t,0);
|
||||
std::pair<AstToNode::iterator, bool> bar = ast_to_node.insert(foo);
|
||||
int &res = bar.first->second;
|
||||
if(!bar.second) return res;
|
||||
int nargs = num_args(t);
|
||||
std::vector<foci2::ast> args(nargs);
|
||||
for(int i = 0; i < nargs; i++)
|
||||
args[i] = from_Z3_ast(arg(t,i));
|
||||
|
||||
switch(op(t)){
|
||||
case True:
|
||||
res = foci->mk_true(); break;
|
||||
case False:
|
||||
res = foci->mk_false(); break;
|
||||
case And:
|
||||
res = foci->mk_op(foci2::And,args); break;
|
||||
case Or:
|
||||
res = foci->mk_op(foci2::Or,args); break;
|
||||
case Not:
|
||||
res = foci->mk_op(foci2::Not,args[0]); break;
|
||||
case Iff:
|
||||
res = foci->mk_op(foci2::Iff,args); break;
|
||||
case OP_OEQ: // bit of a mystery, this one...
|
||||
if(args[0] == args[1]) res = foci->mk_true();
|
||||
else res = foci->mk_op(foci2::Iff,args);
|
||||
break;
|
||||
case Ite:
|
||||
if(is_bool_type(get_type(t)))
|
||||
res = foci->mk_op(foci2::And,foci->mk_op(foci2::Or,foci->mk_op(foci2::Not,args[0]),args[1]),foci->mk_op(foci2::Or,args[0],args[2]));
|
||||
else
|
||||
res = foci->mk_op(foci2::Ite,args);
|
||||
break;
|
||||
case Equal:
|
||||
res = foci->mk_op(foci2::Equal,args); break;
|
||||
case Implies:
|
||||
args[0] = foci->mk_op(foci2::Not,args[0]); res = foci->mk_op(foci2::Or,args); break;
|
||||
case Xor:
|
||||
res = foci->mk_op(foci2::Not,foci->mk_op(foci2::Iff,args)); break;
|
||||
case Leq:
|
||||
res = foci->mk_op(foci2::Leq,args); break;
|
||||
case Geq:
|
||||
std::swap(args[0],args[1]); res = foci->mk_op(foci2::Leq,args); break;
|
||||
case Gt:
|
||||
res = foci->mk_op(foci2::Not,foci->mk_op(foci2::Leq,args)); break;
|
||||
case Lt:
|
||||
std::swap(args[0],args[1]); res = foci->mk_op(foci2::Not,foci->mk_op(foci2::Leq,args)); break;
|
||||
case Plus:
|
||||
res = foci->mk_op(foci2::Plus,args); break;
|
||||
case Sub:
|
||||
args[1] = foci->mk_op(foci2::Times,foci->mk_int("-1"),args[1]); res = foci->mk_op(foci2::Plus,args); break;
|
||||
case Uminus:
|
||||
res = foci->mk_op(foci2::Times,foci->mk_int("-1"),args[0]); break;
|
||||
case Times:
|
||||
res = foci->mk_op(foci2::Times,args); break;
|
||||
case Idiv:
|
||||
res = foci->mk_op(foci2::Div,args); break;
|
||||
case Mod:
|
||||
res = foci->mk_app(mod_op,args); break;
|
||||
case Select:
|
||||
res = foci->mk_app(select_op,args); break;
|
||||
case Store:
|
||||
res = foci->mk_app(store_op,args); break;
|
||||
case Distinct:
|
||||
res = foci->mk_op(foci2::Distinct,args); break;
|
||||
case Uninterpreted: {
|
||||
symb func = sym(t);
|
||||
FuncDeclToSymbol::iterator it = func_decl_to_symbol.find(func);
|
||||
foci2::symb f = (it == func_decl_to_symbol.end()) ? from_symb(func) : it->second;
|
||||
if(foci->get_symb(f).substr(0,3) == "lbl" && args.size()==1) // HACK to handle Z3 labels
|
||||
res = args[0];
|
||||
else if(foci->get_symb(f).substr(0,3) == "lbl" && args.size()==0) // HACK to handle Z3 labels
|
||||
res = foci->mk_true();
|
||||
else res = foci->mk_app(f,args);
|
||||
break;
|
||||
}
|
||||
case Numeral: {
|
||||
std::string s = string_of_numeral(t);
|
||||
res = foci->mk_int(s);
|
||||
break;
|
||||
}
|
||||
case Forall:
|
||||
case Exists: {
|
||||
bool is_forall = op(t) == Forall;
|
||||
foci2::ops qop = is_forall ? foci2::Forall : foci2::Exists;
|
||||
int bvs = get_quantifier_num_bound(t);
|
||||
std::vector<int> foci_bvs(bvs);
|
||||
for(int i = 0; i < bvs; i++){
|
||||
std::string name = get_quantifier_bound_name(t,i);
|
||||
//Z3_string name = Z3_get_symbol_string(ctx,sym);
|
||||
// type ty = get_quantifier_bound_type(t,i);
|
||||
foci2::symb f = foci->mk_func(name);
|
||||
foci2::ast v = foci->mk_app(f,std::vector<foci2::ast>());
|
||||
foci_bvs[i] = v;
|
||||
}
|
||||
foci2::ast body = from_Z3_ast(get_quantifier_body(t));
|
||||
foci_bvs.push_back(body);
|
||||
res = foci->mk_op(qop,foci_bvs);
|
||||
node_to_ast[res] = t; // desperate
|
||||
break;
|
||||
}
|
||||
case Variable: { // a deBruijn index
|
||||
int index = get_variable_index_value(t);
|
||||
type ty = get_type(t);
|
||||
foci2::symb symbol = make_deBruijn_symbol(index,ty);
|
||||
res = foci->mk_app(symbol,std::vector<foci2::ast>());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
std::cerr << "iZ3: unsupported Z3 operator in expression\n ";
|
||||
print_expr(std::cerr,t);
|
||||
std::cerr << "\n";
|
||||
SASSERT(0 && "iZ3: unsupported Z3 operator");
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// convert an expr to Z3 ast
|
||||
ast to_Z3_ast(foci2::ast i){
|
||||
std::pair<foci2::ast,ast> foo(i,ast());
|
||||
std::pair<NodeToAst::iterator,bool> bar = node_to_ast.insert(foo);
|
||||
if(!bar.second) return bar.first->second;
|
||||
ast &res = bar.first->second;
|
||||
|
||||
if(i < 0){
|
||||
res = mk_not(to_Z3_ast(-i));
|
||||
return res;
|
||||
}
|
||||
|
||||
// get the arguments
|
||||
unsigned n = foci->get_num_args(i);
|
||||
std::vector<ast> args(n);
|
||||
for(unsigned j = 0; j < n; j++)
|
||||
args[j] = to_Z3_ast(foci->get_arg(i,j));
|
||||
|
||||
// handle operators
|
||||
foci2::ops o;
|
||||
foci2::symb f;
|
||||
std::string nval;
|
||||
if(foci->get_true(i))
|
||||
res = mk_true();
|
||||
else if(foci->get_false(i))
|
||||
res = mk_false();
|
||||
else if(foci->get_op(i,o)){
|
||||
switch(o){
|
||||
case foci2::And:
|
||||
res = make(And,args); break;
|
||||
case foci2::Or:
|
||||
res = make(Or,args); break;
|
||||
case foci2::Not:
|
||||
res = mk_not(args[0]); break;
|
||||
case foci2::Iff:
|
||||
res = make(Iff,args[0],args[1]); break;
|
||||
case foci2::Ite:
|
||||
res = make(Ite,args[0],args[1],args[2]); break;
|
||||
case foci2::Equal:
|
||||
res = make(Equal,args[0],args[1]); break;
|
||||
case foci2::Plus:
|
||||
res = make(Plus,args); break;
|
||||
case foci2::Times:
|
||||
res = make(Times,args); break;
|
||||
case foci2::Div:
|
||||
res = make(Idiv,args[0],args[1]); break;
|
||||
case foci2::Leq:
|
||||
res = make(Leq,args[0],args[1]); break;
|
||||
case foci2::Distinct:
|
||||
res = make(Distinct,args);
|
||||
break;
|
||||
case foci2::Tsym:
|
||||
res = mk_true();
|
||||
break;
|
||||
case foci2::Fsym:
|
||||
res = mk_false();
|
||||
break;
|
||||
case foci2::Forall:
|
||||
case foci2::Exists:
|
||||
{
|
||||
int nargs = n;
|
||||
std::vector<ast> bounds(nargs-1);
|
||||
for(int i = 0; i < nargs-1; i++)
|
||||
bounds[i] = args[i];
|
||||
opr oz = o == foci2::Forall ? Forall : Exists;
|
||||
res = make_quant(oz,bounds,args[nargs-1]);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
SASSERT(false && "unknown built-in op");
|
||||
}
|
||||
}
|
||||
else if(foci->get_int(i,nval)){
|
||||
res = make_int(nval);
|
||||
}
|
||||
else if(foci->get_func(i,f)){
|
||||
if(f == select_op){
|
||||
SASSERT(n == 2);
|
||||
res = make(Select,args[0],args[1]);
|
||||
}
|
||||
else if(f == store_op){
|
||||
SASSERT(n == 3);
|
||||
res = make(Store,args[0],args[1],args[2]);
|
||||
}
|
||||
else if(f == mod_op){
|
||||
SASSERT(n == 2);
|
||||
res = make(Mod,args[0],args[1]);
|
||||
}
|
||||
else {
|
||||
std::pair<int,symb> foo(f,(symb)0);
|
||||
std::pair<SymbolToFuncDecl::iterator,bool> bar = symbol_to_func_decl.insert(foo);
|
||||
symb &func_decl = bar.first->second;
|
||||
if(bar.second){
|
||||
std::cout << "unknown function symbol:\n";
|
||||
foci->show_ast(i);
|
||||
SASSERT(0);
|
||||
}
|
||||
res = make(func_decl,args);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "iZ3: unknown FOCI expression kind\n";
|
||||
SASSERT(0 && "iZ3: unknown FOCI expression kind");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int interpolate(const std::vector<ast> &cnsts, std::vector<ast> &itps){
|
||||
SASSERT((int)cnsts.size() == frames);
|
||||
std::string lia("lia");
|
||||
#ifdef _FOCI2
|
||||
foci = foci2::create(lia);
|
||||
#else
|
||||
foci = 0;
|
||||
#endif
|
||||
if(!foci){
|
||||
std::cerr << "iZ3: cannot find foci lia solver.\n";
|
||||
SASSERT(0);
|
||||
}
|
||||
select_op = foci->mk_func("select");
|
||||
store_op = foci->mk_func("store");
|
||||
mod_op = foci->mk_func("mod");
|
||||
std::vector<foci2::ast> foci_cnsts(frames), foci_itps(frames-1), foci_parents;
|
||||
if(parents)
|
||||
foci_parents.resize(frames);
|
||||
for(int i = 0; i < frames; i++){
|
||||
foci_cnsts[i] = from_Z3_ast(cnsts[i]);
|
||||
if(parents)
|
||||
foci_parents[i] = parents[i];
|
||||
}
|
||||
int res = foci->interpolate(foci_cnsts, foci_itps, foci_parents);
|
||||
if(res == 0){
|
||||
SASSERT((int)foci_itps.size() == frames-1);
|
||||
itps.resize(frames-1);
|
||||
for(int i = 0; i < frames-1; i++){
|
||||
// foci->show_ast(foci_itps[i]);
|
||||
itps[i] = to_Z3_ast(foci_itps[i]);
|
||||
}
|
||||
}
|
||||
ast_to_node.clear();
|
||||
node_to_ast.clear();
|
||||
func_decl_to_symbol.clear();
|
||||
symbol_to_func_decl.clear();
|
||||
delete foci;
|
||||
return res;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
iz3secondary *iz3foci::create(iz3mgr *mgr, int num, int *parents){
|
||||
return new iz3foci_impl(mgr,num,parents);
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
iz3foci.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Implements a secondary solver using foci2.
|
||||
|
||||
Author:
|
||||
|
||||
Ken McMillan (kenmcmil)
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef IZ3FOCI_H
|
||||
#define IZ3FOCI_H
|
||||
|
||||
#include "iz3secondary.h"
|
||||
|
||||
/** Secondary prover based on Cadence FOCI. */
|
||||
|
||||
class iz3foci {
|
||||
public:
|
||||
static iz3secondary *create(iz3mgr *mgr, int num, int *parents);
|
||||
};
|
||||
|
||||
#endif
|
|
@ -35,7 +35,6 @@
|
|||
|
||||
#include "iz3profiling.h"
|
||||
#include "iz3translate.h"
|
||||
#include "iz3foci.h"
|
||||
#include "iz3proof.h"
|
||||
#include "iz3hash.h"
|
||||
#include "iz3interp.h"
|
||||
|
@ -167,22 +166,6 @@ struct frame_reducer {
|
|||
#endif
|
||||
|
||||
|
||||
#if 0
|
||||
static lbool test_secondary(context ctx,
|
||||
int num,
|
||||
ast *cnsts,
|
||||
ast *interps,
|
||||
int *parents = 0
|
||||
){
|
||||
iz3secondary *sp = iz3foci::create(ctx,num,parents);
|
||||
std::vector<ast> frames(num), interpolants(num-1);
|
||||
std::copy(cnsts,cnsts+num,frames.begin());
|
||||
int res = sp->interpolate(frames,interpolants);
|
||||
if(res == 0)
|
||||
std::copy(interpolants.begin(),interpolants.end(),interps);
|
||||
return res ? L_TRUE : L_FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
template<class T>
|
||||
struct killme {
|
||||
|
@ -213,11 +196,7 @@ public:
|
|||
const std::vector<int> &parents,
|
||||
std::vector<ast> &interps
|
||||
){
|
||||
int num = cnsts.size();
|
||||
iz3secondary *sp = iz3foci::create(this,num,(int *)(parents.empty()?0:&parents[0]));
|
||||
int res = sp->interpolate(cnsts, interps);
|
||||
if(res != 0)
|
||||
throw iz3_exception("secondary failed");
|
||||
throw iz3_exception("secondary interpolating prover not supported");
|
||||
}
|
||||
|
||||
void proof_to_interpolant(z3pf proof,
|
||||
|
@ -248,10 +227,9 @@ public:
|
|||
if(is_linear(parents_vec))
|
||||
parents_vec.clear();
|
||||
|
||||
// create a secondary prover
|
||||
iz3secondary *sp = iz3foci::create(this,num,parents_vec.empty()?0:&parents_vec[0]);
|
||||
sp_killer.set(sp); // kill this on exit
|
||||
|
||||
// secondary prover no longer supported
|
||||
iz3secondary *sp = NULL;
|
||||
|
||||
#define BINARY_INTERPOLATION
|
||||
#ifndef BINARY_INTERPOLATION
|
||||
// create a translator
|
||||
|
|
|
@ -53,12 +53,8 @@ class iz3translation : public iz3base {
|
|||
: iz3base(mgr,_cnsts,_parents,_theory) {}
|
||||
};
|
||||
|
||||
//#define IZ3_TRANSLATE_DIRECT2
|
||||
#ifdef _FOCI2
|
||||
#define IZ3_TRANSLATE_DIRECT
|
||||
#else
|
||||
// To use a secondary prover, define IZ3_TRANSLATE_DIRECT instead of this
|
||||
#define IZ3_TRANSLATE_FULL
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -332,7 +332,7 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
// get the lits of a Z3 clause as foci terms
|
||||
// get the lits of a Z3 clause as secondary prover terms
|
||||
void get_Z3_lits(ast t, std::vector<ast> &lits){
|
||||
opr dk = op(t);
|
||||
if(dk == False)
|
||||
|
@ -666,9 +666,9 @@ public:
|
|||
#endif
|
||||
|
||||
// interpolate using secondary prover
|
||||
profiling::timer_start("foci");
|
||||
profiling::timer_start("secondary prover");
|
||||
int sat = secondary->interpolate(preds,itps);
|
||||
profiling::timer_stop("foci");
|
||||
profiling::timer_stop("secondary prover");
|
||||
|
||||
std::cout << "lemma done" << std::endl;
|
||||
|
||||
|
@ -1495,7 +1495,7 @@ public:
|
|||
return find_nll(new_proofs);
|
||||
}
|
||||
|
||||
// translate a Z3 proof term into a foci proof term
|
||||
// translate a Z3 proof term into a secondary prover proof term
|
||||
|
||||
Iproof::node translate_main(ast proof, non_local_lits *nll, bool expect_clause = true){
|
||||
non_local_lits *old_nll = nll;
|
||||
|
|
|
@ -26,6 +26,7 @@ Notes:
|
|||
#include "theory_diff_logic.h"
|
||||
#include "theory_dense_diff_logic.h"
|
||||
#include "theory_pb.h"
|
||||
#include "theory_lra.h"
|
||||
#include "ast_pp.h"
|
||||
#include "ast_smt_pp.h"
|
||||
#include "pp_params.hpp"
|
||||
|
@ -143,6 +144,9 @@ namespace opt {
|
|||
else if (typeid(smt::theory_dense_si&) == typeid(*arith_theory)) {
|
||||
return dynamic_cast<smt::theory_dense_si&>(*arith_theory);
|
||||
}
|
||||
else if (typeid(smt::theory_lra&) == typeid(*arith_theory)) {
|
||||
return dynamic_cast<smt::theory_lra&>(*arith_theory);
|
||||
}
|
||||
else {
|
||||
UNREACHABLE();
|
||||
return dynamic_cast<smt::theory_mi_arith&>(*arith_theory);
|
||||
|
@ -401,6 +405,14 @@ namespace opt {
|
|||
return th.mk_ge(m_fm, v, val);
|
||||
}
|
||||
|
||||
|
||||
if (typeid(smt::theory_lra) == typeid(opt)) {
|
||||
smt::theory_lra& th = dynamic_cast<smt::theory_lra&>(opt);
|
||||
SASSERT(val.is_finite());
|
||||
return th.mk_ge(m_fm, v, val.get_numeral());
|
||||
}
|
||||
|
||||
// difference logic?
|
||||
if (typeid(smt::theory_dense_si) == typeid(opt) &&
|
||||
val.get_infinitesimal().is_zero()) {
|
||||
smt::theory_dense_si& th = dynamic_cast<smt::theory_dense_si&>(opt);
|
||||
|
|
|
@ -46,6 +46,8 @@ namespace sat {
|
|||
m_lit(lit),
|
||||
m_k(k),
|
||||
m_size(wlits.size()),
|
||||
m_slack(0),
|
||||
m_num_watch(0),
|
||||
m_max_sum(0) {
|
||||
for (unsigned i = 0; i < wlits.size(); ++i) {
|
||||
m_wlits[i] = wlits[i];
|
||||
|
@ -237,7 +239,6 @@ namespace sat {
|
|||
p.negate();
|
||||
}
|
||||
|
||||
TRACE("sat", display(tout << "init watch: ", p, true););
|
||||
SASSERT(p.lit() == null_literal || value(p.lit()) == l_true);
|
||||
unsigned sz = p.size(), bound = p.k();
|
||||
|
||||
|
@ -249,7 +250,7 @@ namespace sat {
|
|||
p.swap(i, j);
|
||||
}
|
||||
if (slack < bound) {
|
||||
slack += p[i].first;
|
||||
slack += p[j].first;
|
||||
++num_watch;
|
||||
}
|
||||
++j;
|
||||
|
@ -266,7 +267,7 @@ namespace sat {
|
|||
if (slack < bound) {
|
||||
literal lit = p[j].second;
|
||||
SASSERT(value(p[j].second) == l_false);
|
||||
for (unsigned i = j + 1; j < sz; ++i) {
|
||||
for (unsigned i = j + 1; i < sz; ++i) {
|
||||
if (lvl(lit) < lvl(p[i].second)) {
|
||||
lit = p[i].second;
|
||||
}
|
||||
|
@ -280,6 +281,7 @@ namespace sat {
|
|||
p.set_slack(slack);
|
||||
p.set_num_watch(num_watch);
|
||||
}
|
||||
TRACE("sat", display(tout << "init watch: ", p, true););
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -296,7 +298,7 @@ namespace sat {
|
|||
Lw = Lw u {l_s}
|
||||
Lu = Lu \ {l_s}
|
||||
}
|
||||
if (Sw < bound) conflict
|
||||
if (Sw < k) conflict
|
||||
while (Sw < k + a_max) {
|
||||
assign (l_max)
|
||||
a_max = max { ai | li in Lw, li = undef }
|
||||
|
@ -308,7 +310,19 @@ namespace sat {
|
|||
|
||||
|
||||
*/
|
||||
void card_extension::add_index(pb& p, unsigned index, literal lit) {
|
||||
if (value(lit) == l_undef) {
|
||||
m_pb_undef.push_back(index);
|
||||
if (p[index].first > m_a_max) {
|
||||
m_a_max = p[index].first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lbool card_extension::add_assign(pb& p, literal alit) {
|
||||
|
||||
TRACE("sat", display(tout << "assign: " << alit << "\n", p, true););
|
||||
SASSERT(!s().inconsistent());
|
||||
unsigned sz = p.size();
|
||||
unsigned bound = p.k();
|
||||
unsigned num_watch = p.num_watch();
|
||||
|
@ -316,63 +330,43 @@ namespace sat {
|
|||
SASSERT(value(alit) == l_false);
|
||||
SASSERT(p.lit() == null_literal || value(p.lit()) == l_true);
|
||||
SASSERT(num_watch <= sz);
|
||||
SASSERT(num_watch > 0);
|
||||
unsigned index = 0;
|
||||
unsigned a_max = 0;
|
||||
unsigned max_index = 0;
|
||||
m_a_max = 0;
|
||||
m_pb_undef.reset();
|
||||
for (; index < num_watch; ++index) {
|
||||
literal lit = p[index].second;
|
||||
if (lit == alit) {
|
||||
break;
|
||||
}
|
||||
if (value(lit) == l_undef) {
|
||||
m_pb_undef.push_back(index);
|
||||
if (p[index].first > a_max) {
|
||||
a_max = p[index].first;
|
||||
max_index = index;
|
||||
}
|
||||
}
|
||||
add_index(p, index, lit);
|
||||
}
|
||||
|
||||
for (unsigned j = index + 1; a_max == 0 && j < num_watch; ++j) {
|
||||
literal lit = p[j].second;
|
||||
if (value(lit) == l_undef) {
|
||||
m_pb_undef.push_back(j);
|
||||
a_max = p[j].first;
|
||||
max_index = j;
|
||||
}
|
||||
}
|
||||
for (unsigned j = num_watch; a_max == 0 && j < sz; ++j) {
|
||||
literal lit = p[j].second;
|
||||
if (value(lit) == l_undef) {
|
||||
p.swap(j, num_watch);
|
||||
m_pb_undef.push_back(num_watch);
|
||||
a_max = p[num_watch].first;
|
||||
max_index = num_watch;
|
||||
}
|
||||
SASSERT(index < num_watch);
|
||||
|
||||
unsigned index1 = index + 1;
|
||||
for (; m_a_max == 0 && index1 < num_watch; ++index1) {
|
||||
add_index(p, index1, p[index1].second);
|
||||
}
|
||||
|
||||
unsigned val = p[index].first;
|
||||
SASSERT(num_watch > 0);
|
||||
SASSERT(index < num_watch);
|
||||
SASSERT(value(p[index].second) == l_false);
|
||||
SASSERT(val <= slack);
|
||||
slack -= val;
|
||||
// find literals to swap with:
|
||||
for (unsigned j = num_watch; j < sz && slack < bound + a_max; ++j) {
|
||||
if (value(p[j].second) != l_false) {
|
||||
for (unsigned j = num_watch; j < sz && slack < bound + m_a_max; ++j) {
|
||||
literal lit = p[j].second;
|
||||
if (value(lit) != l_false) {
|
||||
slack += p[j].first;
|
||||
watch_literal(p, p[j]);
|
||||
p.swap(num_watch, j);
|
||||
if (value(p[num_watch].second) == l_undef && a_max < p[num_watch].first) {
|
||||
m_pb_undef.push_back(num_watch);
|
||||
a_max = p[num_watch].first;
|
||||
max_index = num_watch;
|
||||
}
|
||||
add_index(p, num_watch, lit);
|
||||
++num_watch;
|
||||
}
|
||||
}
|
||||
|
||||
SASSERT(!s().inconsistent());
|
||||
DEBUG_CODE(for (auto idx : m_pb_undef) { SASSERT(value(p[idx].second) == l_undef); });
|
||||
|
||||
if (slack < bound) {
|
||||
// maintain watching the literal
|
||||
slack += val;
|
||||
|
@ -385,34 +379,38 @@ namespace sat {
|
|||
}
|
||||
|
||||
// swap out the watched literal.
|
||||
p.set_slack(slack);
|
||||
--num_watch;
|
||||
SASSERT(num_watch > 0);
|
||||
p.set_slack(slack);
|
||||
p.set_num_watch(num_watch);
|
||||
p.swap(num_watch, index);
|
||||
if (num_watch == max_index) {
|
||||
max_index = index;
|
||||
}
|
||||
|
||||
SASSERT(max_index < sz);
|
||||
while (slack < bound + a_max && !s().inconsistent()) {
|
||||
// variable at max-index must be assigned to true.
|
||||
assign(p, p[max_index].second);
|
||||
|
||||
a_max = 0;
|
||||
// find the next a_max among m_pb_undef
|
||||
while (!m_pb_undef.empty() && l_undef != value(p[m_pb_undef.back()].second)) {
|
||||
if (slack < bound + m_a_max) {
|
||||
TRACE("sat", display(tout, p, false); for(auto j : m_pb_undef) tout << j << "\n";);
|
||||
literal_vector to_assign;
|
||||
while (!m_pb_undef.empty()) {
|
||||
index1 = m_pb_undef.back();
|
||||
literal lit = p[index1].second;
|
||||
TRACE("sat", tout << index1 << " " << lit << "\n";);
|
||||
if (slack >= bound + p[index1].first) {
|
||||
break;
|
||||
}
|
||||
m_pb_undef.pop_back();
|
||||
to_assign.push_back(lit);
|
||||
}
|
||||
if (m_pb_undef.empty()) {
|
||||
break;
|
||||
|
||||
for (literal lit : to_assign) {
|
||||
if (s().inconsistent()) break;
|
||||
if (value(lit) == l_undef) {
|
||||
assign(p, lit);
|
||||
}
|
||||
}
|
||||
max_index = m_pb_undef.back();
|
||||
a_max = p[max_index].first;
|
||||
m_pb_undef.pop_back();
|
||||
}
|
||||
|
||||
return s().inconsistent() ? l_false : l_true;
|
||||
|
||||
TRACE("sat", display(tout << "assign: " << alit << "\n", p, true););
|
||||
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
void card_extension::watch_literal(pb& p, wliteral l) {
|
||||
|
@ -467,6 +465,7 @@ namespace sat {
|
|||
set_conflict(p, lit);
|
||||
break;
|
||||
default:
|
||||
SASSERT(validate_unit_propagation(p, lit));
|
||||
m_stats.m_num_pb_propagations++;
|
||||
m_num_propagations_since_pop++;
|
||||
if (s().m_config.m_drat) {
|
||||
|
@ -483,7 +482,8 @@ namespace sat {
|
|||
}
|
||||
|
||||
void card_extension::display(std::ostream& out, pb const& p, bool values) const {
|
||||
out << p.lit() << "[" << p.size() << "]";
|
||||
if (p.lit() != null_literal) out << p.lit();
|
||||
out << "[watch: " << p.num_watch() << ", slack: " << p.slack() << "]";
|
||||
if (p.lit() != null_literal && values) {
|
||||
out << "@(" << value(p.lit());
|
||||
if (value(p.lit()) != l_undef) {
|
||||
|
@ -514,7 +514,7 @@ namespace sat {
|
|||
}
|
||||
|
||||
void card_extension::asserted_pb(literal l, ptr_vector<pb>* pbs, pb* p0) {
|
||||
TRACE("sat", tout << l << " " << !is_tag_empty(pbs) << " " << (p0 != 0) << "\n";);
|
||||
TRACE("sat", tout << "literal: " << l << " has pb: " << !is_tag_empty(pbs) << " p0 != 0: " << (p0 != 0) << "\n";);
|
||||
if (!is_tag_empty(pbs)) {
|
||||
ptr_vector<pb>::iterator begin = pbs->begin();
|
||||
ptr_vector<pb>::iterator it = begin, it2 = it, end = pbs->end();
|
||||
|
@ -524,20 +524,28 @@ namespace sat {
|
|||
continue;
|
||||
}
|
||||
switch (add_assign(p, ~l)) {
|
||||
case l_false: // conflict
|
||||
for (; it != end; ++it, ++it2) {
|
||||
*it2 = *it;
|
||||
}
|
||||
SASSERT(s().inconsistent());
|
||||
pbs->set_end(it2);
|
||||
return;
|
||||
case l_true: // unit propagation, keep watching the literal
|
||||
if (it2 != it) {
|
||||
*it2 = *it;
|
||||
}
|
||||
++it2;
|
||||
break;
|
||||
case l_false: // conflict.
|
||||
SASSERT(s().inconsistent());
|
||||
for (; it != end; ++it, ++it2) {
|
||||
*it2 = *it;
|
||||
}
|
||||
pbs->set_end(it2);
|
||||
return;
|
||||
case l_undef: // watch literal was swapped
|
||||
if (s().inconsistent()) {
|
||||
++it;
|
||||
for (; it != end; ++it, ++it2) {
|
||||
*it2 = *it;
|
||||
}
|
||||
pbs->set_end(it2);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -844,6 +852,7 @@ namespace sat {
|
|||
literal consequent = s().m_not_l;
|
||||
justification js = s().m_conflict;
|
||||
TRACE("sat", tout << consequent << " " << js << "\n";);
|
||||
TRACE("sat", s().display(tout););
|
||||
m_conflict_lvl = s().get_max_lvl(consequent, js);
|
||||
if (consequent != null_literal) {
|
||||
consequent.neg();
|
||||
|
@ -942,7 +951,7 @@ namespace sat {
|
|||
m_bound += offset;
|
||||
inc_coeff(consequent, offset);
|
||||
get_pb_antecedents(consequent, p, m_lemma);
|
||||
TRACE("sat", tout << m_lemma << "\n";);
|
||||
TRACE("sat", display(tout, p, true); tout << m_lemma << "\n";);
|
||||
for (unsigned i = 0; i < m_lemma.size(); ++i) {
|
||||
process_antecedent(~m_lemma[i], offset);
|
||||
}
|
||||
|
@ -989,6 +998,7 @@ namespace sat {
|
|||
SASSERT(lvl(v) == m_conflict_lvl);
|
||||
s().reset_mark(v);
|
||||
--idx;
|
||||
TRACE("sat", tout << "Unmark: v" << v << "\n";);
|
||||
--m_num_marks;
|
||||
js = s().m_justification[v];
|
||||
offset = get_abs_coeff(v);
|
||||
|
@ -1015,7 +1025,6 @@ namespace sat {
|
|||
|
||||
m_lemma.reset();
|
||||
|
||||
#if 1
|
||||
m_lemma.push_back(null_literal);
|
||||
for (unsigned i = 0; 0 <= slack && i < m_active_vars.size(); ++i) {
|
||||
bool_var v = m_active_vars[i];
|
||||
|
@ -1038,6 +1047,7 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
if (jus.size() > 1) {
|
||||
std::cout << jus.size() << "\n";
|
||||
for (unsigned i = 0; i < jus.size(); ++i) {
|
||||
|
@ -1047,6 +1057,7 @@ namespace sat {
|
|||
active2pb(m_A);
|
||||
display(std::cout, m_A);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (slack >= 0) {
|
||||
|
@ -1057,7 +1068,7 @@ namespace sat {
|
|||
if (m_lemma[0] == null_literal) {
|
||||
m_lemma[0] = m_lemma.back();
|
||||
m_lemma.pop_back();
|
||||
unsigned level = lvl(m_lemma[0]);
|
||||
unsigned level = m_lemma.empty() ? 0 : lvl(m_lemma[0]);
|
||||
for (unsigned i = 1; i < m_lemma.size(); ++i) {
|
||||
if (lvl(m_lemma[i]) > level) {
|
||||
level = lvl(m_lemma[i]);
|
||||
|
@ -1066,37 +1077,6 @@ namespace sat {
|
|||
}
|
||||
IF_VERBOSE(2, verbose_stream() << "(sat.card set level to " << level << " < " << m_conflict_lvl << ")\n";);
|
||||
}
|
||||
#else
|
||||
++idx;
|
||||
while (0 <= slack) {
|
||||
literal lit = lits[idx];
|
||||
bool_var v = lit.var();
|
||||
if (m_active_var_set.contains(v)) {
|
||||
int coeff = get_coeff(v);
|
||||
if (coeff < 0 && !lit.sign()) {
|
||||
slack += coeff;
|
||||
m_lemma.push_back(~lit);
|
||||
}
|
||||
else if (coeff > 0 && lit.sign()) {
|
||||
slack -= coeff;
|
||||
m_lemma.push_back(~lit);
|
||||
}
|
||||
}
|
||||
if (idx == 0 && slack >= 0) {
|
||||
IF_VERBOSE(2, verbose_stream() << "(sat.card non-asserting)\n";);
|
||||
goto bail_out;
|
||||
}
|
||||
SASSERT(idx > 0 || slack < 0);
|
||||
--idx;
|
||||
}
|
||||
if (m_lemma.size() >= 2 && lvl(m_lemma[1]) == m_conflict_lvl) {
|
||||
// TRACE("sat", tout << "Bail out on no progress " << lit << "\n";);
|
||||
IF_VERBOSE(2, verbose_stream() << "(sat.card bail non-asserting resolvent)\n";);
|
||||
goto bail_out;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
SASSERT(slack < 0);
|
||||
|
||||
|
@ -1108,7 +1088,7 @@ namespace sat {
|
|||
svector<drat::premise> ps; // TBD fill in
|
||||
s().m_drat.add(m_lemma, ps);
|
||||
}
|
||||
|
||||
|
||||
s().m_lemma.reset();
|
||||
s().m_lemma.append(m_lemma);
|
||||
for (unsigned i = 1; i < m_lemma.size(); ++i) {
|
||||
|
@ -1153,6 +1133,7 @@ namespace sat {
|
|||
|
||||
if (level > 0 && !s().is_marked(v) && level == m_conflict_lvl) {
|
||||
s().mark(v);
|
||||
TRACE("sat", tout << "Mark: v" << v << "\n";);
|
||||
++m_num_marks;
|
||||
}
|
||||
inc_coeff(l, offset);
|
||||
|
@ -1347,20 +1328,30 @@ namespace sat {
|
|||
void card_extension::get_pb_antecedents(literal l, pb const& p, literal_vector& r) {
|
||||
if (p.lit() != null_literal) r.push_back(p.lit());
|
||||
SASSERT(p.lit() == null_literal || value(p.lit()) == l_true);
|
||||
unsigned k = p.k();
|
||||
unsigned max_sum = p.max_sum();
|
||||
for (unsigned i = p.size(); i > 0 && max_sum >= k; ) {
|
||||
--i;
|
||||
literal lit = p[i].second;
|
||||
if (lit == l) {
|
||||
max_sum -= p[i].first;
|
||||
}
|
||||
else if (value(lit) == l_false) {
|
||||
r.push_back(~p[i].second);
|
||||
max_sum -= p[i].first;
|
||||
TRACE("sat", display(tout, p, true););
|
||||
|
||||
// unsigned coeff = get_coeff(p, l);
|
||||
unsigned coeff = 0;
|
||||
for (unsigned j = 0; j < p.num_watch(); ++j) {
|
||||
if (p[j].second == l) {
|
||||
coeff = p[j].first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
SASSERT(max_sum < k);
|
||||
unsigned slack = p.slack() - coeff;
|
||||
unsigned i = p.num_watch();
|
||||
|
||||
// skip entries that are not required for unit propagation.
|
||||
// slack - coeff + w_head < k
|
||||
unsigned h = 0;
|
||||
for (; i < p.size() && p[i].first + h + slack < p.k(); ++i) {
|
||||
h += p[i].first;
|
||||
}
|
||||
for (; i < p.size(); ++i) {
|
||||
literal lit = p[i].second;
|
||||
SASSERT(l_false == value(lit));
|
||||
r.push_back(~lit);
|
||||
}
|
||||
}
|
||||
|
||||
void card_extension::get_card_antecedents(literal l, card const& c, literal_vector& r) {
|
||||
|
@ -1720,7 +1711,9 @@ namespace sat {
|
|||
std::ostream& card_extension::display_justification(std::ostream& out, ext_justification_idx idx) const {
|
||||
if (is_card_index(idx)) {
|
||||
card& c = index2card(idx);
|
||||
out << "bound " << c.lit() << ": ";
|
||||
out << "bound ";
|
||||
if (c.lit() != null_literal) out << c.lit();
|
||||
out << ": ";
|
||||
for (unsigned i = 0; i < c.size(); ++i) {
|
||||
out << c[i] << " ";
|
||||
}
|
||||
|
@ -1728,14 +1721,18 @@ namespace sat {
|
|||
}
|
||||
else if (is_xor_index(idx)) {
|
||||
xor& x = index2xor(idx);
|
||||
out << "xor " << x.lit() << ": ";
|
||||
out << "xor ";
|
||||
if (x.lit() != null_literal) out << x.lit();
|
||||
out << ": ";
|
||||
for (unsigned i = 0; i < x.size(); ++i) {
|
||||
out << x[i] << " ";
|
||||
}
|
||||
}
|
||||
else if (is_pb_index(idx)) {
|
||||
pb& p = index2pb(idx);
|
||||
out << "pb " << p.lit() << ": ";
|
||||
out << "pb ";
|
||||
if (p.lit() != null_literal) out << p.lit();
|
||||
out << ": ";
|
||||
for (unsigned i = 0; i < p.size(); ++i) {
|
||||
out << p[i].first << "*" << p[i].second << " ";
|
||||
}
|
||||
|
@ -1776,6 +1773,21 @@ namespace sat {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
bool card_extension::validate_unit_propagation(pb const& p, literal alit) {
|
||||
if (p.lit() != null_literal && value(p.lit()) != l_true) return false;
|
||||
|
||||
unsigned sum = 0;
|
||||
TRACE("sat", display(tout << "validate: " << alit << "\n", p, true););
|
||||
for (unsigned i = 0; i < p.size(); ++i) {
|
||||
literal lit = p[i].second;
|
||||
lbool val = value(lit);
|
||||
if (val != l_false && lit != alit) {
|
||||
sum += p[i].first;
|
||||
}
|
||||
}
|
||||
return sum < p.k();
|
||||
}
|
||||
|
||||
bool card_extension::validate_lemma() {
|
||||
int val = -m_bound;
|
||||
normalize_active_coeffs();
|
||||
|
|
|
@ -232,10 +232,12 @@ namespace sat {
|
|||
|
||||
|
||||
// pb functionality
|
||||
unsigned m_a_max;
|
||||
void copy_pb(card_extension& result);
|
||||
void asserted_pb(literal l, ptr_vector<pb>* pbs, pb* p);
|
||||
void init_watch(pb& p, bool is_true);
|
||||
lbool add_assign(pb& p, literal alit);
|
||||
void add_index(pb& p, unsigned index, literal lit);
|
||||
void watch_literal(pb& p, wliteral lit);
|
||||
void clear_watch(pb& p);
|
||||
void set_conflict(pb& p, literal lit);
|
||||
|
@ -280,6 +282,7 @@ namespace sat {
|
|||
bool validate_assign(literal_vector const& lits, literal lit);
|
||||
bool validate_lemma();
|
||||
bool validate_unit_propagation(card const& c);
|
||||
bool validate_unit_propagation(pb const& p, literal lit);
|
||||
bool validate_conflict(literal_vector const& lits, ineq& p);
|
||||
|
||||
ineq m_A, m_B, m_C;
|
||||
|
|
|
@ -1938,23 +1938,25 @@ namespace sat {
|
|||
void solver::learn_lemma_and_backjump() {
|
||||
TRACE("sat_lemma", tout << "new lemma size: " << m_lemma.size() << "\n" << m_lemma << "\n";);
|
||||
|
||||
if (m_config.m_minimize_lemmas) {
|
||||
minimize_lemma();
|
||||
reset_lemma_var_marks();
|
||||
if (m_config.m_dyn_sub_res)
|
||||
dyn_sub_res();
|
||||
TRACE("sat_lemma", tout << "new lemma (after minimization) size: " << m_lemma.size() << "\n" << m_lemma << "\n";);
|
||||
}
|
||||
else
|
||||
reset_lemma_var_marks();
|
||||
|
||||
literal_vector::iterator it = m_lemma.begin();
|
||||
literal_vector::iterator end = m_lemma.end();
|
||||
unsigned new_scope_lvl = 0;
|
||||
++it;
|
||||
for(; it != end; ++it) {
|
||||
bool_var var = (*it).var();
|
||||
new_scope_lvl = std::max(new_scope_lvl, lvl(var));
|
||||
if (!m_lemma.empty()) {
|
||||
if (m_config.m_minimize_lemmas) {
|
||||
minimize_lemma();
|
||||
reset_lemma_var_marks();
|
||||
if (m_config.m_dyn_sub_res)
|
||||
dyn_sub_res();
|
||||
TRACE("sat_lemma", tout << "new lemma (after minimization) size: " << m_lemma.size() << "\n" << m_lemma << "\n";);
|
||||
}
|
||||
else
|
||||
reset_lemma_var_marks();
|
||||
|
||||
literal_vector::iterator it = m_lemma.begin();
|
||||
literal_vector::iterator end = m_lemma.end();
|
||||
++it;
|
||||
for(; it != end; ++it) {
|
||||
bool_var var = (*it).var();
|
||||
new_scope_lvl = std::max(new_scope_lvl, lvl(var));
|
||||
}
|
||||
}
|
||||
|
||||
unsigned glue = num_diff_levels(m_lemma.size(), m_lemma.c_ptr());
|
||||
|
@ -2390,6 +2392,7 @@ namespace sat {
|
|||
assigned at level 0.
|
||||
*/
|
||||
void solver::minimize_lemma() {
|
||||
SASSERT(!m_lemma.empty());
|
||||
SASSERT(m_unmark.empty());
|
||||
//m_unmark.reset();
|
||||
updt_lemma_lvl_set();
|
||||
|
|
110
src/shell/lp_frontend.cpp
Normal file
110
src/shell/lp_frontend.cpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*++
|
||||
Copyright (c) 2016 Microsoft Corporation
|
||||
|
||||
Author:
|
||||
|
||||
Lev Nachmanson 2016-10-27
|
||||
|
||||
--*/
|
||||
|
||||
#include "lp_params.hpp"
|
||||
#include "util/lp/lp_settings.h"
|
||||
#include "util/lp/mps_reader.h"
|
||||
#include "timeout.h"
|
||||
#include "cancel_eh.h"
|
||||
#include "scoped_timer.h"
|
||||
#include "rlimit.h"
|
||||
#include "gparams.h"
|
||||
#include <signal.h>
|
||||
|
||||
static lean::lp_solver<double, double>* g_solver = 0;
|
||||
|
||||
static void display_statistics() {
|
||||
if (g_solver && g_solver->settings().print_statistics) {
|
||||
// TBD display relevant information about statistics
|
||||
}
|
||||
}
|
||||
|
||||
static void STD_CALL on_ctrl_c(int) {
|
||||
signal (SIGINT, SIG_DFL);
|
||||
#pragma omp critical (g_display_stats)
|
||||
{
|
||||
display_statistics();
|
||||
}
|
||||
raise(SIGINT);
|
||||
}
|
||||
|
||||
static void on_timeout() {
|
||||
#pragma omp critical (g_display_stats)
|
||||
{
|
||||
display_statistics();
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
struct front_end_resource_limit : public lean::lp_resource_limit {
|
||||
reslimit& m_reslim;
|
||||
|
||||
front_end_resource_limit(reslimit& lim):
|
||||
m_reslim(lim)
|
||||
{}
|
||||
|
||||
virtual bool get_cancel_flag() { return !m_reslim.inc(); }
|
||||
};
|
||||
|
||||
void run_solver(lp_params & params, char const * mps_file_name) {
|
||||
|
||||
reslimit rlim;
|
||||
unsigned timeout = gparams::get().get_uint("timeout", 0);
|
||||
unsigned rlimit = gparams::get().get_uint("rlimit", 0);
|
||||
front_end_resource_limit lp_limit(rlim);
|
||||
|
||||
scoped_rlimit _rlimit(rlim, rlimit);
|
||||
cancel_eh<reslimit> eh(rlim);
|
||||
scoped_timer timer(timeout, &eh);
|
||||
|
||||
std::string fn(mps_file_name);
|
||||
lean::mps_reader<double, double> reader(fn);
|
||||
reader.set_message_stream(&std::cout); // can be redirected
|
||||
reader.read();
|
||||
if (!reader.is_ok()) {
|
||||
std::cerr << "cannot process " << mps_file_name << std::endl;
|
||||
return;
|
||||
}
|
||||
lean::lp_solver<double, double> * solver = reader.create_solver(false); // false - to create the primal solver
|
||||
solver->settings().set_resource_limit(lp_limit);
|
||||
g_solver = solver;
|
||||
if (params.min()) {
|
||||
solver->flip_costs();
|
||||
}
|
||||
solver->settings().set_message_ostream(&std::cout);
|
||||
solver->settings().report_frequency = params.rep_freq();
|
||||
solver->settings().print_statistics = params.print_stats();
|
||||
solver->find_maximal_solution();
|
||||
|
||||
*(solver->settings().get_message_ostream()) << "status is " << lp_status_to_string(solver->get_status()) << std::endl;
|
||||
if (solver->get_status() == lean::OPTIMAL) {
|
||||
if (params.min()) {
|
||||
solver->flip_costs();
|
||||
}
|
||||
solver->print_model(std::cout);
|
||||
}
|
||||
|
||||
// #pragma omp critical (g_display_stats)
|
||||
{
|
||||
display_statistics();
|
||||
register_on_timeout_proc(0);
|
||||
g_solver = 0;
|
||||
}
|
||||
delete solver;
|
||||
}
|
||||
|
||||
unsigned read_mps_file(char const * mps_file_name) {
|
||||
signal(SIGINT, on_ctrl_c);
|
||||
register_on_timeout_proc(on_timeout);
|
||||
lp_params p;
|
||||
param_descrs r;
|
||||
p.collect_param_descrs(r);
|
||||
run_solver(p, mps_file_name);
|
||||
return 0;
|
||||
}
|
7
src/shell/lp_frontend.h
Normal file
7
src/shell/lp_frontend.h
Normal file
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
unsigned read_mps_file(char const * mps_file_name);
|
|
@ -35,8 +35,9 @@ Revision History:
|
|||
#include"error_codes.h"
|
||||
#include"gparams.h"
|
||||
#include"env_params.h"
|
||||
#include "lp_frontend.h"
|
||||
|
||||
typedef enum { IN_UNSPECIFIED, IN_SMTLIB, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_Z3_LOG } input_kind;
|
||||
typedef enum { IN_UNSPECIFIED, IN_SMTLIB, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_Z3_LOG, IN_MPS } input_kind;
|
||||
|
||||
std::string g_aux_input_file;
|
||||
char const * g_input_file = 0;
|
||||
|
@ -342,6 +343,10 @@ int STD_CALL main(int argc, char ** argv) {
|
|||
else if (strcmp(ext, "smt") == 0) {
|
||||
g_input_kind = IN_SMTLIB;
|
||||
}
|
||||
else if (strcmp(ext, "mps") == 0 || strcmp(ext, "sif") == 0 ||
|
||||
strcmp(ext, "MPS") == 0 || strcmp(ext, "SIF") == 0) {
|
||||
g_input_kind = IN_MPS;
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (g_input_kind) {
|
||||
|
@ -367,6 +372,9 @@ int STD_CALL main(int argc, char ** argv) {
|
|||
case IN_Z3_LOG:
|
||||
replay_z3_log(g_input_file);
|
||||
break;
|
||||
case IN_MPS:
|
||||
return_value = read_mps_file(g_input_file);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
|
|
@ -44,4 +44,4 @@ public:
|
|||
|
||||
void install_smt2_extra_cmds(cmd_context & ctx) {
|
||||
ctx.insert(alloc(include_cmd));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ Revision History:
|
|||
#include"smt_setup.h"
|
||||
#include"static_features.h"
|
||||
#include"theory_arith.h"
|
||||
#include"theory_lra.h"
|
||||
#include"theory_dense_diff_logic.h"
|
||||
#include"theory_diff_logic.h"
|
||||
#include"theory_utvpi.h"
|
||||
|
@ -442,7 +443,7 @@ namespace smt {
|
|||
m_params.m_arith_propagate_eqs = false;
|
||||
m_params.m_eliminate_term_ite = true;
|
||||
m_params.m_nnf_cnf = false;
|
||||
setup_mi_arith();
|
||||
setup_r_arith();
|
||||
}
|
||||
|
||||
void setup::setup_QF_LRA(static_features const & st) {
|
||||
|
@ -467,6 +468,10 @@ namespace smt {
|
|||
m_params.m_restart_adaptive = false;
|
||||
}
|
||||
m_params.m_arith_small_lemma_size = 32;
|
||||
setup_r_arith();
|
||||
}
|
||||
|
||||
void setup::setup_QF_LIRA(static_features const& st) {
|
||||
setup_mi_arith();
|
||||
}
|
||||
|
||||
|
@ -475,7 +480,7 @@ namespace smt {
|
|||
m_params.m_relevancy_lvl = 0;
|
||||
m_params.m_arith_expand_eqs = true;
|
||||
m_params.m_arith_reflect = false;
|
||||
m_params.m_arith_propagate_eqs = false;
|
||||
m_params.m_arith_propagate_eqs = false;
|
||||
m_params.m_nnf_cnf = false;
|
||||
setup_i_arith();
|
||||
}
|
||||
|
@ -539,7 +544,7 @@ namespace smt {
|
|||
m_params.m_relevancy_lvl = 0;
|
||||
m_params.m_arith_reflect = false;
|
||||
m_params.m_nnf_cnf = false;
|
||||
setup_mi_arith();
|
||||
setup_r_arith();
|
||||
}
|
||||
|
||||
void setup::setup_QF_BV() {
|
||||
|
@ -718,6 +723,12 @@ namespace smt {
|
|||
m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params));
|
||||
}
|
||||
|
||||
void setup::setup_r_arith() {
|
||||
// to disable theory lra
|
||||
// m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params));
|
||||
m_context.register_plugin(alloc(smt::theory_lra, m_manager, m_params));
|
||||
}
|
||||
|
||||
void setup::setup_mi_arith() {
|
||||
if (m_params.m_arith_mode == AS_OPTINF) {
|
||||
m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params));
|
||||
|
@ -929,7 +940,9 @@ namespace smt {
|
|||
}
|
||||
|
||||
if (st.num_theories() == 1 && is_arith(st)) {
|
||||
if (st.m_has_real)
|
||||
if ((st.m_has_int && st.m_has_real) || (st.m_num_non_linear != 0))
|
||||
setup_QF_LIRA(st);
|
||||
else if (st.m_has_real)
|
||||
setup_QF_LRA(st);
|
||||
else
|
||||
setup_QF_LIA(st);
|
||||
|
|
|
@ -65,6 +65,7 @@ namespace smt {
|
|||
void setup_QF_LRA();
|
||||
void setup_QF_LRA(static_features const & st);
|
||||
void setup_QF_LIA();
|
||||
void setup_QF_LIRA(static_features const& st);
|
||||
void setup_QF_LIA(static_features const & st);
|
||||
void setup_QF_UFLIA();
|
||||
void setup_QF_UFLIA(static_features & st);
|
||||
|
@ -99,6 +100,7 @@ namespace smt {
|
|||
void setup_card();
|
||||
void setup_i_arith();
|
||||
void setup_mi_arith();
|
||||
void setup_r_arith();
|
||||
void setup_fpa();
|
||||
void setup_str();
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ namespace smt {
|
|||
}
|
||||
}
|
||||
|
||||
void theory::display_app(std::ostream & out, app * n) const {
|
||||
std::ostream& theory::display_app(std::ostream & out, app * n) const {
|
||||
func_decl * d = n->get_decl();
|
||||
if (n->get_num_args() == 0) {
|
||||
out << d->get_name();
|
||||
|
@ -73,9 +73,10 @@ namespace smt {
|
|||
else {
|
||||
out << "#" << n->get_id();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
void theory::display_flat_app(std::ostream & out, app * n) const {
|
||||
std::ostream& theory::display_flat_app(std::ostream & out, app * n) const {
|
||||
func_decl * d = n->get_decl();
|
||||
if (n->get_num_args() == 0) {
|
||||
out << d->get_name();
|
||||
|
@ -106,6 +107,7 @@ namespace smt {
|
|||
else {
|
||||
out << "#" << n->get_id();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
bool theory::is_relevant_and_shared(enode * n) const {
|
||||
|
|
|
@ -337,14 +337,14 @@ namespace smt {
|
|||
|
||||
virtual void collect_statistics(::statistics & st) const {
|
||||
}
|
||||
|
||||
void display_app(std::ostream & out, app * n) const;
|
||||
|
||||
void display_flat_app(std::ostream & out, app * n) const;
|
||||
|
||||
void display_var_def(std::ostream & out, theory_var v) const { return display_app(out, get_enode(v)->get_owner()); }
|
||||
std::ostream& display_app(std::ostream & out, app * n) const;
|
||||
|
||||
void display_var_flat_def(std::ostream & out, theory_var v) const { return display_flat_app(out, get_enode(v)->get_owner()); }
|
||||
std::ostream& display_flat_app(std::ostream & out, app * n) const;
|
||||
|
||||
std::ostream& display_var_def(std::ostream & out, theory_var v) const { return display_app(out, get_enode(v)->get_owner()); }
|
||||
|
||||
std::ostream& display_var_flat_def(std::ostream & out, theory_var v) const { return display_flat_app(out, get_enode(v)->get_owner()); }
|
||||
|
||||
/**
|
||||
\brief Assume eqs between variable that are equal with respect to the given table.
|
||||
|
|
|
@ -21,6 +21,7 @@ Notes:
|
|||
#include"smt_kernel.h"
|
||||
#include"smt_params.h"
|
||||
#include"smt_params_helper.hpp"
|
||||
#include"lp_params.hpp"
|
||||
#include"rewriter_types.h"
|
||||
#include"filter_model_converter.h"
|
||||
#include"ast_util.h"
|
||||
|
@ -64,6 +65,10 @@ public:
|
|||
return m_params;
|
||||
}
|
||||
|
||||
params_ref & params() {
|
||||
return m_params_ref;
|
||||
}
|
||||
|
||||
void updt_params_core(params_ref const & p) {
|
||||
m_candidate_models = p.get_bool("candidate_models", false);
|
||||
m_fail_if_inconclusive = p.get_bool("fail_if_inconclusive", true);
|
||||
|
@ -73,6 +78,7 @@ public:
|
|||
TRACE("smt_tactic", tout << "updt_params: " << p << "\n";);
|
||||
updt_params_core(p);
|
||||
fparams().updt_params(p);
|
||||
m_params_ref.copy(p);
|
||||
m_logic = p.get_sym(symbol("logic"), m_logic);
|
||||
if (m_logic != symbol::null && m_ctx) {
|
||||
m_ctx->set_logic(m_logic);
|
||||
|
@ -84,6 +90,7 @@ public:
|
|||
r.insert("candidate_models", CPK_BOOL, "(default: false) create candidate models even when quantifier or theory reasoning is incomplete.");
|
||||
r.insert("fail_if_inconclusive", CPK_BOOL, "(default: true) fail if found unsat (sat) for under (over) approximated goal.");
|
||||
smt_params_helper::collect_param_descrs(r);
|
||||
lp_params::collect_param_descrs(r);
|
||||
}
|
||||
|
||||
|
||||
|
@ -112,10 +119,12 @@ public:
|
|||
struct scoped_init_ctx {
|
||||
smt_tactic & m_owner;
|
||||
smt_params m_params; // smt-setup overwrites parameters depending on the current assertions.
|
||||
params_ref m_params_ref;
|
||||
|
||||
scoped_init_ctx(smt_tactic & o, ast_manager & m):m_owner(o) {
|
||||
m_params = o.fparams();
|
||||
smt::kernel * new_ctx = alloc(smt::kernel, m, m_params);
|
||||
m_params_ref = o.params();
|
||||
smt::kernel * new_ctx = alloc(smt::kernel, m, m_params, m_params_ref);
|
||||
TRACE("smt_tactic", tout << "logic: " << o.m_logic << "\n";);
|
||||
new_ctx->set_logic(o.m_logic);
|
||||
if (o.m_callback) {
|
||||
|
|
2615
src/smt/theory_lra.cpp
Normal file
2615
src/smt/theory_lra.cpp
Normal file
File diff suppressed because it is too large
Load diff
97
src/smt/theory_lra.h
Normal file
97
src/smt/theory_lra.h
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*++
|
||||
Copyright (c) 2016 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
theory_lra.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Lev Nachmanson (levnach) 2016-25-3
|
||||
Nikolaj Bjorner (nbjorner)
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
--*/
|
||||
#pragma once
|
||||
|
||||
#include "theory_opt.h"
|
||||
|
||||
namespace smt {
|
||||
class theory_lra : public theory, public theory_opt {
|
||||
class imp;
|
||||
imp* m_imp;
|
||||
|
||||
public:
|
||||
theory_lra(ast_manager& m, theory_arith_params& ap);
|
||||
virtual ~theory_lra();
|
||||
virtual theory* mk_fresh(context* new_ctx);
|
||||
virtual char const* get_name() const { return "lra"; }
|
||||
|
||||
virtual void init(context * ctx);
|
||||
|
||||
virtual bool internalize_atom(app * atom, bool gate_ctx);
|
||||
|
||||
virtual bool internalize_term(app * term);
|
||||
|
||||
virtual void internalize_eq_eh(app * atom, bool_var v);
|
||||
|
||||
virtual void assign_eh(bool_var v, bool is_true);
|
||||
|
||||
virtual void new_eq_eh(theory_var v1, theory_var v2);
|
||||
|
||||
virtual bool use_diseqs() const;
|
||||
|
||||
virtual void new_diseq_eh(theory_var v1, theory_var v2);
|
||||
|
||||
virtual void push_scope_eh();
|
||||
|
||||
virtual void pop_scope_eh(unsigned num_scopes);
|
||||
|
||||
virtual void restart_eh();
|
||||
|
||||
virtual void relevant_eh(app* e);
|
||||
|
||||
virtual void init_search_eh();
|
||||
|
||||
virtual final_check_status final_check_eh();
|
||||
|
||||
virtual bool is_shared(theory_var v) const;
|
||||
|
||||
virtual bool can_propagate();
|
||||
|
||||
virtual void propagate();
|
||||
|
||||
virtual justification * why_is_diseq(theory_var v1, theory_var v2);
|
||||
|
||||
// virtual void flush_eh();
|
||||
|
||||
virtual void reset_eh();
|
||||
|
||||
virtual void init_model(model_generator & m);
|
||||
|
||||
virtual model_value_proc * mk_value(enode * n, model_generator & mg);
|
||||
|
||||
virtual bool get_value(enode* n, expr_ref& r);
|
||||
|
||||
virtual bool validate_eq_in_model(theory_var v1, theory_var v2, bool is_true) const;
|
||||
|
||||
virtual void display(std::ostream & out) const;
|
||||
|
||||
virtual void collect_statistics(::statistics & st) const;
|
||||
|
||||
// optimization
|
||||
virtual inf_eps value(theory_var);
|
||||
virtual inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared);
|
||||
virtual theory_var add_objective(app* term);
|
||||
virtual expr_ref mk_ge(filter_model_converter& fm, theory_var v, inf_rational const& val);
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
|
@ -2176,6 +2176,8 @@ bool theory_seq::simplify_and_solve_eqs() {
|
|||
return m_new_propagation || ctx.inconsistent();
|
||||
}
|
||||
|
||||
void theory_seq::internalize_eq_eh(app * atom, bool_var v) {}
|
||||
|
||||
|
||||
bool theory_seq::internalize_term(app* term) {
|
||||
context & ctx = get_context();
|
||||
|
|
|
@ -343,7 +343,7 @@ namespace smt {
|
|||
virtual final_check_status final_check_eh();
|
||||
virtual bool internalize_atom(app* atom, bool) { return internalize_term(atom); }
|
||||
virtual bool internalize_term(app*);
|
||||
virtual void internalize_eq_eh(app * atom, bool_var v) {}
|
||||
virtual void internalize_eq_eh(app * atom, bool_var v);
|
||||
virtual void new_eq_eh(theory_var, theory_var);
|
||||
virtual void new_diseq_eh(theory_var, theory_var);
|
||||
virtual void assign_eh(bool_var v, bool is_true);
|
||||
|
|
|
@ -60,6 +60,7 @@ namespace smt {
|
|||
totalCacheAccessCount(0),
|
||||
cacheHitCount(0),
|
||||
cacheMissCount(0),
|
||||
m_fresh_id(0),
|
||||
m_find(*this),
|
||||
m_trail_stack(*this)
|
||||
{
|
||||
|
@ -441,7 +442,12 @@ namespace smt {
|
|||
}
|
||||
|
||||
app * theory_str::mk_fresh_const(char const* name, sort* s) {
|
||||
return u.mk_skolem(symbol(name), 0, 0, s);
|
||||
string_buffer<64> buffer;
|
||||
buffer << name;
|
||||
buffer << "!tmp";
|
||||
buffer << m_fresh_id;
|
||||
m_fresh_id++;
|
||||
return u.mk_skolem(symbol(buffer.c_str()), 0, 0, s);
|
||||
}
|
||||
|
||||
|
||||
|
@ -5552,8 +5558,8 @@ namespace smt {
|
|||
for (; arg1_grdItor != groundedMap[arg1DeAlias].end(); arg1_grdItor++) {
|
||||
std::vector<expr*> ndVec;
|
||||
ndVec.insert(ndVec.end(), arg0_grdItor->first.begin(), arg0_grdItor->first.end());
|
||||
int arg0VecSize = arg0_grdItor->first.size();
|
||||
int arg1VecSize = arg1_grdItor->first.size();
|
||||
size_t arg0VecSize = arg0_grdItor->first.size();
|
||||
size_t arg1VecSize = arg1_grdItor->first.size();
|
||||
if (arg0VecSize > 0 && arg1VecSize > 0 && u.str.is_string(arg0_grdItor->first[arg0VecSize - 1]) && u.str.is_string(arg1_grdItor->first[0])) {
|
||||
ndVec.pop_back();
|
||||
ndVec.push_back(mk_concat(arg0_grdItor->first[arg0VecSize - 1], arg1_grdItor->first[0]));
|
||||
|
@ -5645,8 +5651,8 @@ namespace smt {
|
|||
}
|
||||
|
||||
bool theory_str::is_partial_in_grounded_concat(const std::vector<expr*> & strVec, const std::vector<expr*> & subStrVec) {
|
||||
int strCnt = strVec.size();
|
||||
int subStrCnt = subStrVec.size();
|
||||
size_t strCnt = strVec.size();
|
||||
size_t subStrCnt = subStrVec.size();
|
||||
|
||||
if (strCnt == 0 || subStrCnt == 0) {
|
||||
return false;
|
||||
|
@ -5717,7 +5723,7 @@ namespace smt {
|
|||
}
|
||||
|
||||
// tail nodes
|
||||
int tailIdx = i + subStrCnt - 1;
|
||||
size_t tailIdx = i + subStrCnt - 1;
|
||||
zstring subStrTailVal;
|
||||
if (u.str.is_string(subStrVec[subStrCnt - 1], subStrTailVal)) {
|
||||
zstring strTailVal;
|
||||
|
@ -5908,8 +5914,14 @@ namespace smt {
|
|||
app * n2_curr = to_app(n2);
|
||||
|
||||
// case 0: n1_curr is const string, n2_curr is const string
|
||||
if (u.str.is_string(n1_curr) && u.str.is_string(n2_curr)) {
|
||||
if (n1_curr != n2_curr) {
|
||||
zstring n1_curr_str, n2_curr_str;
|
||||
if (u.str.is_string(n1_curr, n1_curr_str) && u.str.is_string(n2_curr, n2_curr_str)) {
|
||||
TRACE("str", tout << "checking string constants: n1=" << n1_curr_str << ", n2=" << n2_curr_str << std::endl;);
|
||||
if (n1_curr_str == n2_curr_str) {
|
||||
// TODO(mtrberzi) potential correction: if n1_curr != n2_curr,
|
||||
// assert that these two terms are in fact equal, because they ought to be
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -6864,7 +6876,7 @@ namespace smt {
|
|||
// easiest case. we will search within these bounds
|
||||
} else if (upper_bound_exists && !lower_bound_exists) {
|
||||
// search between 0 and the upper bound
|
||||
v_lower_bound == rational::zero();
|
||||
v_lower_bound = rational::zero();
|
||||
} else if (lower_bound_exists && !upper_bound_exists) {
|
||||
// check some finite portion of the search space
|
||||
v_upper_bound = v_lower_bound + rational(10);
|
||||
|
@ -8251,7 +8263,7 @@ namespace smt {
|
|||
}
|
||||
}
|
||||
if (lId == -1)
|
||||
lId = mLMap.size();
|
||||
lId = static_cast<int>(mLMap.size());
|
||||
for (std::set<expr*>::iterator itor2 = nSet.begin(); itor2 != nSet.end(); itor2++) {
|
||||
bool itorHasEqcValue = false;
|
||||
get_eqc_value(*itor2, itorHasEqcValue);
|
||||
|
@ -8290,7 +8302,7 @@ namespace smt {
|
|||
}
|
||||
}
|
||||
if (rId == -1)
|
||||
rId = mRMap.size();
|
||||
rId = static_cast<int>(mRMap.size());
|
||||
for (itor2 = nSet.begin(); itor2 != nSet.end(); itor2++) {
|
||||
bool rHasEqcValue = false;
|
||||
get_eqc_value(*itor2, rHasEqcValue);
|
||||
|
@ -9182,7 +9194,7 @@ namespace smt {
|
|||
// ----------------------------------------------------------------------------------------
|
||||
int len = atoi(lenStr.encode().c_str());
|
||||
bool coverAll = false;
|
||||
svector<int_vector> options;
|
||||
vector<int_vector, true, long long> options;
|
||||
int_vector base;
|
||||
|
||||
TRACE("str", tout
|
||||
|
@ -9227,16 +9239,16 @@ namespace smt {
|
|||
);
|
||||
|
||||
// ----------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
ptr_vector<expr> orList;
|
||||
ptr_vector<expr> andList;
|
||||
|
||||
for (long long i = l; i < h; i++) {
|
||||
orList.push_back(m.mk_eq(val_indicator, mk_string(longlong_to_string(i).c_str()) ));
|
||||
if (m_params.m_AggressiveValueTesting) {
|
||||
literal l = mk_eq(val_indicator, mk_string(longlong_to_string(i).c_str()), false);
|
||||
ctx.mark_as_relevant(l);
|
||||
ctx.force_phase(l);
|
||||
literal lit = mk_eq(val_indicator, mk_string(longlong_to_string(i).c_str()), false);
|
||||
ctx.mark_as_relevant(lit);
|
||||
ctx.force_phase(lit);
|
||||
}
|
||||
|
||||
zstring aStr = gen_val_string(len, options[i - l]);
|
||||
|
@ -10059,7 +10071,7 @@ namespace smt {
|
|||
}
|
||||
TRACE("str", tout << "last bounds are [" << lastBounds.lowerBound << " | " << lastBounds.midPoint << " | " << lastBounds.upperBound << "]!" << lastBounds.windowSize << std::endl;);
|
||||
binary_search_info newBounds;
|
||||
expr * newTester;
|
||||
expr * newTester = 0;
|
||||
if (lastTesterConstant == "more") {
|
||||
// special case: if the midpoint, upper bound, and window size are all equal,
|
||||
// we double the window size and adjust the bounds
|
||||
|
|
|
@ -350,6 +350,8 @@ protected:
|
|||
unsigned long cacheHitCount;
|
||||
unsigned long cacheMissCount;
|
||||
|
||||
unsigned m_fresh_id;
|
||||
|
||||
// cache mapping each string S to Length(S)
|
||||
obj_map<expr, app*> length_ast_map;
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ Notes:
|
|||
#include "solver_na2as.h"
|
||||
#include "tactic.h"
|
||||
#include "pb2bv_rewriter.h"
|
||||
#include "th_rewriter.h"
|
||||
#include "filter_model_converter.h"
|
||||
#include "ast_pp.h"
|
||||
#include "model_smt2_pp.h"
|
||||
|
@ -29,6 +30,7 @@ class pb2bv_solver : public solver_na2as {
|
|||
params_ref m_params;
|
||||
mutable expr_ref_vector m_assertions;
|
||||
mutable ref<solver> m_solver;
|
||||
mutable th_rewriter m_th_rewriter;
|
||||
mutable pb2bv_rewriter m_rewriter;
|
||||
|
||||
public:
|
||||
|
@ -39,6 +41,7 @@ public:
|
|||
m_params(p),
|
||||
m_assertions(m),
|
||||
m_solver(s),
|
||||
m_th_rewriter(m, p),
|
||||
m_rewriter(m, p)
|
||||
{
|
||||
}
|
||||
|
@ -121,10 +124,11 @@ public:
|
|||
private:
|
||||
void flush_assertions() const {
|
||||
proof_ref proof(m);
|
||||
expr_ref fml(m);
|
||||
expr_ref fml1(m), fml(m);
|
||||
expr_ref_vector fmls(m);
|
||||
for (unsigned i = 0; i < m_assertions.size(); ++i) {
|
||||
m_rewriter(m_assertions[i].get(), fml, proof);
|
||||
m_th_rewriter(m_assertions[i].get(), fml1, proof);
|
||||
m_rewriter(fml1, fml, proof);
|
||||
m_solver->assert_expr(fml);
|
||||
}
|
||||
m_rewriter.flush_side_constraints(fmls);
|
||||
|
|
|
@ -22,7 +22,6 @@ Notes:
|
|||
#include"solve_eqs_tactic.h"
|
||||
#include"elim_uncnstr_tactic.h"
|
||||
#include"smt_tactic.h"
|
||||
// include"mip_tactic.h"
|
||||
#include"recover_01_tactic.h"
|
||||
#include"ctx_simplify_tactic.h"
|
||||
#include"probe_arith.h"
|
||||
|
@ -72,5 +71,18 @@ tactic * mk_qflra_tactic(ast_manager & m, params_ref const & p) {
|
|||
// using_params(mk_smt_tactic(), pivot_p)),
|
||||
// p);
|
||||
|
||||
#if 0
|
||||
|
||||
params_ref simplex_0, simplex_1, simplex_2;
|
||||
simplex_0.set_uint("lp.simplex_strategy", 0);
|
||||
simplex_1.set_uint("lp.simplex_strategy", 1);
|
||||
simplex_2.set_uint("lp.simplex_strategy", 2);
|
||||
|
||||
return par(using_params(mk_smt_tactic(), simplex_0),
|
||||
using_params(mk_smt_tactic(), simplex_1),
|
||||
using_params(mk_smt_tactic(), simplex_2));
|
||||
#else
|
||||
return using_params(using_params(mk_smt_tactic(), pivot_p), p);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
144
src/test/argument_parser.h
Normal file
144
src/test/argument_parser.h
Normal file
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#include <iostream>
|
||||
|
||||
namespace lean {
|
||||
class argument_parser {
|
||||
std::unordered_map<std::string, std::string> m_options;
|
||||
std::unordered_map<std::string, std::string> m_options_with_after_string;
|
||||
std::set<std::string> m_used_options;
|
||||
std::unordered_map<std::string, std::string> m_used_options_with_after_string;
|
||||
std::vector<std::string> m_free_args;
|
||||
std::vector<std::string> m_args;
|
||||
|
||||
public:
|
||||
std::string m_error_message;
|
||||
argument_parser(unsigned argn, char * const* args) {
|
||||
for (unsigned i = 0; i < argn; i++) {
|
||||
m_args.push_back(std::string(args[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void add_option(std::string s) {
|
||||
add_option_with_help_string(s, "");
|
||||
}
|
||||
|
||||
void add_option_with_help_string(std::string s, std::string help_string) {
|
||||
m_options[s]=help_string;
|
||||
}
|
||||
|
||||
void add_option_with_after_string(std::string s) {
|
||||
add_option_with_after_string_with_help(s, "");
|
||||
}
|
||||
|
||||
void add_option_with_after_string_with_help(std::string s, std::string help_string) {
|
||||
m_options_with_after_string[s]=help_string;
|
||||
}
|
||||
|
||||
|
||||
bool parse() {
|
||||
bool status_is_ok = true;
|
||||
for (unsigned i = 0; i < m_args.size(); i++) {
|
||||
std::string ar = m_args[i];
|
||||
if (m_options.find(ar) != m_options.end() )
|
||||
m_used_options.insert(ar);
|
||||
else if (m_options_with_after_string.find(ar) != m_options_with_after_string.end()) {
|
||||
if (i == m_args.size() - 1) {
|
||||
m_error_message = "Argument is missing after "+ar;
|
||||
return false;
|
||||
}
|
||||
i++;
|
||||
m_used_options_with_after_string[ar] = m_args[i];
|
||||
} else {
|
||||
if (starts_with(ar, "-") || starts_with(ar, "//"))
|
||||
status_is_ok = false;
|
||||
|
||||
m_free_args.push_back(ar);
|
||||
}
|
||||
}
|
||||
return status_is_ok;
|
||||
}
|
||||
|
||||
bool contains(std::unordered_map<std::string, std::string> & m, std::string s) {
|
||||
return m.find(s) != m.end();
|
||||
}
|
||||
|
||||
bool contains(std::set<std::string> & m, std::string s) {
|
||||
return m.find(s) != m.end();
|
||||
}
|
||||
|
||||
bool option_is_used(std::string option) {
|
||||
return contains(m_used_options, option) || contains(m_used_options_with_after_string, option);
|
||||
}
|
||||
|
||||
std::string get_option_value(std::string option) {
|
||||
auto t = m_used_options_with_after_string.find(option);
|
||||
if (t != m_used_options_with_after_string.end()){
|
||||
return t->second;
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
bool starts_with(std::string s, char const * prefix) {
|
||||
return starts_with(s, std::string(prefix));
|
||||
}
|
||||
|
||||
bool starts_with(std::string s, std::string prefix) {
|
||||
return s.substr(0, prefix.size()) == prefix;
|
||||
}
|
||||
|
||||
std::string usage_string() {
|
||||
std::string ret = "";
|
||||
std::vector<std::string> unknown_options;
|
||||
for (auto t : m_free_args) {
|
||||
if (starts_with(t, "-") || starts_with(t, "\\")) {
|
||||
unknown_options.push_back(t);
|
||||
}
|
||||
}
|
||||
if (unknown_options.size()) {
|
||||
ret = "Unknown options:";
|
||||
}
|
||||
for (auto unknownOption : unknown_options) {
|
||||
ret += unknownOption;
|
||||
ret += ",";
|
||||
}
|
||||
ret += "\n";
|
||||
ret += "Usage:\n";
|
||||
for (auto allowed_option : m_options)
|
||||
ret += allowed_option.first + " " + (allowed_option.second.size() == 0 ? std::string("") : std::string("/") + allowed_option.second) + std::string("\n");
|
||||
for (auto s : m_options_with_after_string) {
|
||||
ret += s.first + " " + (s.second.size() == 0? " \"option value\"":("\""+ s.second+"\"")) + "\n";
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void print() {
|
||||
if (m_used_options.size() == 0 && m_used_options_with_after_string.size() == 0 && m_free_args.size() == 0) {
|
||||
std::cout << "no options are given" << std::endl;
|
||||
return;
|
||||
}
|
||||
std::cout << "options are: " << std::endl;
|
||||
for (std::string s : m_used_options) {
|
||||
std::cout << s << std::endl;
|
||||
}
|
||||
for (auto & t : m_used_options_with_after_string) {
|
||||
std::cout << t.first << " " << t.second << std::endl;
|
||||
}
|
||||
if (m_free_args.size() > 0) {
|
||||
std::cout << "free arguments are: " << std::endl;
|
||||
for (auto & t : m_free_args) {
|
||||
std::cout << t << " " << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
3236
src/test/lp.cpp
Normal file
3236
src/test/lp.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
@ -240,6 +240,7 @@ int main(int argc, char ** argv) {
|
|||
TST(pdr);
|
||||
TST_ARGV(ddnf);
|
||||
TST(model_evaluator);
|
||||
TST_ARGV(lp);
|
||||
TST(get_consequences);
|
||||
TST(pb2bv);
|
||||
TST_ARGV(sat_lookahead);
|
||||
|
|
396
src/test/smt_reader.h
Normal file
396
src/test/smt_reader.h
Normal file
|
@ -0,0 +1,396 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
// reads an MPS file reperesenting a Mixed Integer Program
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include "util/lp/lp_primal_simplex.h"
|
||||
#include "util/lp/lp_dual_simplex.h"
|
||||
#include "util/lp/lar_solver.h"
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <algorithm>
|
||||
#include "util/lp/mps_reader.h"
|
||||
#include "util/lp/ul_pair.h"
|
||||
#include "util/lp/lar_constraints.h"
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
namespace lean {
|
||||
|
||||
template<typename T>
|
||||
T from_string(const std::string& str) {
|
||||
std::istringstream ss(str);
|
||||
T ret;
|
||||
ss >> ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
class smt_reader {
|
||||
public:
|
||||
struct lisp_elem {
|
||||
std::string m_head;
|
||||
std::vector<lisp_elem> m_elems;
|
||||
void print() {
|
||||
if (m_elems.size()) {
|
||||
std::cout << '(';
|
||||
std::cout << m_head << ' ';
|
||||
for (auto & el : m_elems)
|
||||
el.print();
|
||||
|
||||
std::cout << ')';
|
||||
} else {
|
||||
std::cout << " " << m_head;
|
||||
}
|
||||
}
|
||||
unsigned size() const { return static_cast<unsigned>(m_elems.size()); }
|
||||
bool is_simple() const { return size() == 0; }
|
||||
};
|
||||
struct formula_constraint {
|
||||
lconstraint_kind m_kind;
|
||||
std::vector<std::pair<mpq, std::string>> m_coeffs;
|
||||
mpq m_right_side;
|
||||
void add_pair(mpq c, std::string name) {
|
||||
m_coeffs.push_back(make_pair(c, name));
|
||||
}
|
||||
formula_constraint() : m_right_side(numeric_traits<mpq>::zero()) {}
|
||||
};
|
||||
|
||||
lisp_elem m_formula_lisp_elem;
|
||||
|
||||
std::unordered_map<std::string, unsigned> m_name_to_var_index;
|
||||
std::vector<formula_constraint> m_constraints;
|
||||
bool m_is_OK;
|
||||
unsigned m_line_number;
|
||||
std::string m_file_name;
|
||||
std::ifstream m_file_stream;
|
||||
std::string m_line;
|
||||
smt_reader(std::string file_name):
|
||||
m_is_OK(true),
|
||||
m_line_number(0),
|
||||
m_file_name(file_name),
|
||||
m_file_stream(file_name) {
|
||||
}
|
||||
|
||||
void set_error() {
|
||||
std::cout << "setting error" << std::endl;
|
||||
m_is_OK = false;
|
||||
}
|
||||
|
||||
bool is_ok() {
|
||||
return m_is_OK;
|
||||
}
|
||||
|
||||
bool prefix(const char * pr) {
|
||||
return m_line.find(pr) == 0;
|
||||
}
|
||||
|
||||
int first_separator() {
|
||||
unsigned blank_pos = static_cast<unsigned>(m_line.find(' '));
|
||||
unsigned br_pos = static_cast<unsigned>(m_line.find('('));
|
||||
unsigned reverse_br_pos = static_cast<unsigned>(m_line.find(')'));
|
||||
return std::min(blank_pos, std::min(br_pos, reverse_br_pos));
|
||||
}
|
||||
|
||||
void fill_lisp_elem(lisp_elem & lm) {
|
||||
if (m_line[0] == '(')
|
||||
fill_nested_elem(lm);
|
||||
else
|
||||
fill_simple_elem(lm);
|
||||
}
|
||||
|
||||
void fill_simple_elem(lisp_elem & lm) {
|
||||
int separator = first_separator();
|
||||
lean_assert(-1 != separator && separator != 0);
|
||||
lm.m_head = m_line.substr(0, separator);
|
||||
m_line = m_line.substr(separator);
|
||||
}
|
||||
|
||||
void fill_nested_elem(lisp_elem & lm) {
|
||||
lean_assert(m_line[0] == '(');
|
||||
m_line = m_line.substr(1);
|
||||
int separator = first_separator();
|
||||
lm.m_head = m_line.substr(0, separator);
|
||||
m_line = m_line.substr(lm.m_head.size());
|
||||
eat_blanks();
|
||||
while (m_line.size()) {
|
||||
if (m_line[0] == '(') {
|
||||
lisp_elem el;
|
||||
fill_nested_elem(el);
|
||||
lm.m_elems.push_back(el);
|
||||
} else {
|
||||
if (m_line[0] == ')') {
|
||||
m_line = m_line.substr(1);
|
||||
break;
|
||||
}
|
||||
lisp_elem el;
|
||||
fill_simple_elem(el);
|
||||
lm.m_elems.push_back(el);
|
||||
}
|
||||
eat_blanks();
|
||||
}
|
||||
}
|
||||
|
||||
void eat_blanks() {
|
||||
while (m_line.size()) {
|
||||
if (m_line[0] == ' ')
|
||||
m_line = m_line.substr(1);
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void fill_formula_elem() {
|
||||
fill_lisp_elem(m_formula_lisp_elem);
|
||||
}
|
||||
|
||||
void parse_line() {
|
||||
if (m_line.find(":formula") == 0) {
|
||||
int first_br = static_cast<int>(m_line.find('('));
|
||||
if (first_br == -1) {
|
||||
std::cout << "empty formula" << std::endl;
|
||||
return;
|
||||
}
|
||||
m_line = m_line.substr(first_br);
|
||||
fill_formula_elem();
|
||||
}
|
||||
}
|
||||
|
||||
void set_constraint_kind(formula_constraint & c, lisp_elem & el) {
|
||||
if (el.m_head == "=") {
|
||||
c.m_kind = EQ;
|
||||
} else if (el.m_head == ">=") {
|
||||
c.m_kind = GE;
|
||||
} else if (el.m_head == "<=") {
|
||||
c.m_kind = LE;
|
||||
} else if (el.m_head == ">") {
|
||||
c.m_kind = GT;
|
||||
} else if (el.m_head == "<") {
|
||||
c.m_kind = LT;
|
||||
} else {
|
||||
std::cout << "kind " << el.m_head << " is not supported " << std::endl;
|
||||
set_error();
|
||||
}
|
||||
}
|
||||
|
||||
void adjust_rigth_side(formula_constraint & /* c*/, lisp_elem & /*el*/) {
|
||||
// lean_assert(el.m_head == "0"); // do nothing for the time being
|
||||
}
|
||||
|
||||
void set_constraint_coeffs(formula_constraint & c, lisp_elem & el) {
|
||||
lean_assert(el.m_elems.size() == 2);
|
||||
set_constraint_coeffs_on_coeff_element(c, el.m_elems[0]);
|
||||
adjust_rigth_side(c, el.m_elems[1]);
|
||||
}
|
||||
|
||||
|
||||
bool is_integer(std::string & s) {
|
||||
if (s.size() == 0) return false;
|
||||
return atoi(s.c_str()) != 0 || isdigit(s.c_str()[0]);
|
||||
}
|
||||
|
||||
void add_complex_sum_elem(formula_constraint & c, lisp_elem & el) {
|
||||
if (el.m_head == "*") {
|
||||
add_mult_elem(c, el.m_elems);
|
||||
} else if (el.m_head == "~") {
|
||||
lisp_elem & minel = el.m_elems[0];
|
||||
lean_assert(minel.is_simple());
|
||||
c.m_right_side += mpq(str_to_int(minel.m_head));
|
||||
} else {
|
||||
std::cout << "unexpected input " << el.m_head << std::endl;
|
||||
set_error();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_name(lisp_elem & name) {
|
||||
lean_assert(name.is_simple());
|
||||
lean_assert(!is_integer(name.m_head));
|
||||
return name.m_head;
|
||||
}
|
||||
|
||||
|
||||
void add_mult_elem(formula_constraint & c, std::vector<lisp_elem> & els) {
|
||||
lean_assert(els.size() == 2);
|
||||
mpq coeff = get_coeff(els[0]);
|
||||
std::string col_name = get_name(els[1]);
|
||||
c.add_pair(coeff, col_name);
|
||||
}
|
||||
|
||||
mpq get_coeff(lisp_elem & le) {
|
||||
if (le.is_simple()) {
|
||||
return mpq(str_to_int(le.m_head));
|
||||
} else {
|
||||
lean_assert(le.m_head == "~");
|
||||
lean_assert(le.size() == 1);
|
||||
lisp_elem & el = le.m_elems[0];
|
||||
lean_assert(el.is_simple());
|
||||
return -mpq(str_to_int(el.m_head));
|
||||
}
|
||||
}
|
||||
|
||||
int str_to_int(std::string & s) {
|
||||
lean_assert(is_integer(s));
|
||||
return atoi(s.c_str());
|
||||
}
|
||||
|
||||
void add_sum_elem(formula_constraint & c, lisp_elem & el) {
|
||||
if (el.size()) {
|
||||
add_complex_sum_elem(c, el);
|
||||
} else {
|
||||
lean_assert(is_integer(el.m_head));
|
||||
int v = atoi(el.m_head.c_str());
|
||||
mpq vr(v);
|
||||
c.m_right_side -= vr;
|
||||
}
|
||||
}
|
||||
|
||||
void add_sum(formula_constraint & c, std::vector<lisp_elem> & sum_els) {
|
||||
for (auto & el : sum_els)
|
||||
add_sum_elem(c, el);
|
||||
}
|
||||
|
||||
void set_constraint_coeffs_on_coeff_element(formula_constraint & c, lisp_elem & el) {
|
||||
if (el.m_head == "*") {
|
||||
add_mult_elem(c, el.m_elems);
|
||||
} else if (el.m_head == "+") {
|
||||
add_sum(c, el.m_elems);
|
||||
} else {
|
||||
lean_assert(false); // unexpected input
|
||||
}
|
||||
}
|
||||
|
||||
void create_constraint(lisp_elem & el) {
|
||||
formula_constraint c;
|
||||
set_constraint_kind(c, el);
|
||||
set_constraint_coeffs(c, el);
|
||||
m_constraints.push_back(c);
|
||||
}
|
||||
|
||||
void fill_constraints() {
|
||||
if (m_formula_lisp_elem.m_head != "and") {
|
||||
std::cout << "unexpected top element " << m_formula_lisp_elem.m_head << std::endl;
|
||||
set_error();
|
||||
return;
|
||||
}
|
||||
for (auto & el : m_formula_lisp_elem.m_elems)
|
||||
create_constraint(el);
|
||||
}
|
||||
|
||||
void read() {
|
||||
if (!m_file_stream.is_open()){
|
||||
std::cout << "cannot open file " << m_file_name << std::endl;
|
||||
set_error();
|
||||
return;
|
||||
}
|
||||
while (m_is_OK && getline(m_file_stream, m_line)) {
|
||||
parse_line();
|
||||
m_line_number++;
|
||||
}
|
||||
|
||||
m_file_stream.close();
|
||||
fill_constraints();
|
||||
}
|
||||
|
||||
/*
|
||||
void fill_lar_solver_on_row(row * row, lar_solver *solver) {
|
||||
if (row->m_name != m_cost_row_name) {
|
||||
lar_constraint c(get_lar_relation_from_row(row->m_type), row->m_right_side);
|
||||
for (auto s : row->m_row_columns) {
|
||||
var_index i = solver->add_var(s.first);
|
||||
c.add_variable_to_constraint(i, s.second);
|
||||
}
|
||||
solver->add_constraint(&c);
|
||||
} else {
|
||||
// ignore the cost row
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fill_lar_solver_on_rows(lar_solver * solver) {
|
||||
for (auto row_it : m_rows) {
|
||||
fill_lar_solver_on_row(row_it.second, solver);
|
||||
}
|
||||
}
|
||||
|
||||
void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) {
|
||||
lar_constraint c(GE, b->m_low);
|
||||
var_index i = solver->add_var(col->m_name);
|
||||
c.add_variable_to_constraint(i, numeric_traits<T>::one());
|
||||
solver->add_constraint(&c);
|
||||
}
|
||||
|
||||
void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) {
|
||||
lar_constraint c(LE, b->m_upper);
|
||||
var_index i = solver->add_var(col->m_name);
|
||||
c.add_variable_to_constraint(i, numeric_traits<T>::one());
|
||||
solver->add_constraint(&c);
|
||||
}
|
||||
|
||||
void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) {
|
||||
lar_constraint c(EQ, b->m_fixed_value);
|
||||
var_index i = solver->add_var(col->m_name);
|
||||
c.add_variable_to_constraint(i, numeric_traits<T>::one());
|
||||
solver->add_constraint(&c);
|
||||
}
|
||||
|
||||
void fill_lar_solver_on_columns(lar_solver * solver) {
|
||||
for (auto s : m_columns) {
|
||||
mps_reader::column * col = s.second;
|
||||
solver->add_var(col->m_name);
|
||||
auto b = col->m_bound;
|
||||
if (b == nullptr) return;
|
||||
|
||||
if (b->m_free) continue;
|
||||
|
||||
if (b->m_low_is_set) {
|
||||
create_low_constraint_for_var(col, b, solver);
|
||||
}
|
||||
if (b->m_upper_is_set) {
|
||||
create_upper_constraint_for_var(col, b, solver);
|
||||
}
|
||||
if (b->m_value_is_fixed) {
|
||||
create_equality_contraint_for_var(col, b, solver);
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
unsigned register_name(std::string s) {
|
||||
auto it = m_name_to_var_index.find(s);
|
||||
if (it!= m_name_to_var_index.end())
|
||||
return it->second;
|
||||
|
||||
unsigned ret = static_cast<unsigned>(m_name_to_var_index.size());
|
||||
m_name_to_var_index[s] = ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void add_constraint_to_solver(lar_solver * solver, formula_constraint & fc) {
|
||||
vector<std::pair<mpq, var_index>> ls;
|
||||
for (auto & it : fc.m_coeffs) {
|
||||
ls.push_back(std::make_pair(it.first, solver->add_var(register_name(it.second))));
|
||||
}
|
||||
solver->add_constraint(ls, fc.m_kind, fc.m_right_side);
|
||||
}
|
||||
|
||||
void fill_lar_solver(lar_solver * solver) {
|
||||
for (formula_constraint & fc : m_constraints)
|
||||
add_constraint_to_solver(solver, fc);
|
||||
}
|
||||
|
||||
|
||||
lar_solver * create_lar_solver() {
|
||||
lar_solver * ls = new lar_solver();
|
||||
fill_lar_solver(ls);
|
||||
return ls;
|
||||
}
|
||||
};
|
||||
}
|
73
src/test/test_file_reader.h
Normal file
73
src/test/test_file_reader.h
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
// reads a text file
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include "util/lp/lp_utils.h"
|
||||
#include "util/lp/lp_solver.h"
|
||||
|
||||
namespace lean {
|
||||
|
||||
template <typename T>
|
||||
struct test_result {
|
||||
lp_status m_status;
|
||||
T m_cost;
|
||||
std::unordered_map<std::string, T> column_values;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class test_file_reader {
|
||||
struct raw_blob {
|
||||
std::vector<std::string> m_unparsed_strings;
|
||||
std::vector<raw_blob> m_blobs;
|
||||
};
|
||||
|
||||
struct test_file_blob {
|
||||
std::string m_name;
|
||||
std::string m_content;
|
||||
std::unordered_map<std::string, std::string> m_table;
|
||||
std::unordered_map<std::string, test_file_blob> m_blobs;
|
||||
|
||||
test_result<T> * get_test_result() {
|
||||
test_result<T> * tr = new test_result<T>();
|
||||
throw "not impl";
|
||||
return tr;
|
||||
}
|
||||
};
|
||||
std::ifstream m_file_stream;
|
||||
public:
|
||||
// constructor
|
||||
test_file_reader(std::string file_name) : m_file_stream(file_name) {
|
||||
if (!m_file_stream.is_open()) {
|
||||
std::cout << "cannot open file " << "\'" << file_name << "\'" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
raw_blob scan_to_row_blob() {
|
||||
}
|
||||
|
||||
test_file_blob scan_row_blob_to_test_file_blob(raw_blob /* rblob */) {
|
||||
}
|
||||
|
||||
test_result<T> * get_test_result() {
|
||||
if (!m_file_stream.is_open()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
raw_blob rblob = scan_to_row_blob();
|
||||
|
||||
test_file_blob tblob = scan_row_blob_to_test_file_blob(rblob);
|
||||
|
||||
return tblob.get_test_result();
|
||||
}
|
||||
};
|
||||
}
|
|
@ -83,8 +83,8 @@ unsigned string_hash(const char * str, unsigned length, unsigned init_value) {
|
|||
Z3_fallthrough;
|
||||
case 1 :
|
||||
a+=str[0];
|
||||
Z3_fallthrough;
|
||||
/* case 0: nothing left to add */
|
||||
break;
|
||||
}
|
||||
mix(a,b,c);
|
||||
/*-------------------------------------------- report the result */
|
||||
|
|
70
src/util/lp/binary_heap_priority_queue.h
Normal file
70
src/util/lp/binary_heap_priority_queue.h
Normal file
|
@ -0,0 +1,70 @@
|
|||
|
||||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/vector.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/lp/lp_utils.h"
|
||||
namespace lean {
|
||||
// the elements with the smallest priority are dequeued first
|
||||
template <typename T>
|
||||
class binary_heap_priority_queue {
|
||||
vector<T> m_priorities;
|
||||
|
||||
// indexing for A starts from 1
|
||||
vector<unsigned> m_heap; // keeps the elements of the queue
|
||||
vector<int> m_heap_inverse; // o = m_heap[m_heap_inverse[o]]
|
||||
unsigned m_heap_size;
|
||||
// is is the child place in heap
|
||||
void swap_with_parent(unsigned i);
|
||||
void put_at(unsigned i, unsigned h);
|
||||
void decrease_priority(unsigned o, T newPriority);
|
||||
public:
|
||||
#ifdef LEAN_DEBUG
|
||||
bool is_consistent() const;
|
||||
#endif
|
||||
public:
|
||||
void remove(unsigned o);
|
||||
unsigned size() const { return m_heap_size; }
|
||||
binary_heap_priority_queue(): m_heap(1), m_heap_size(0) {} // the empty constructror
|
||||
// n is the initial queue capacity.
|
||||
// The capacity will be enlarged two times automatically if needed
|
||||
binary_heap_priority_queue(unsigned n);
|
||||
|
||||
void clear() {
|
||||
for (unsigned i = 0; i < m_heap_size; i++) {
|
||||
unsigned o = m_heap[i+1];
|
||||
m_heap_inverse[o] = -1;
|
||||
}
|
||||
m_heap_size = 0;
|
||||
}
|
||||
|
||||
void resize(unsigned n);
|
||||
void put_to_heap(unsigned i, unsigned o);
|
||||
|
||||
void enqueue_new(unsigned o, const T& priority);
|
||||
|
||||
// This method can work with an element that is already in the queue.
|
||||
// In this case the priority will be changed and the queue adjusted.
|
||||
void enqueue(unsigned o, const T & priority);
|
||||
void change_priority_for_existing(unsigned o, const T & priority);
|
||||
T get_priority(unsigned o) const { return m_priorities[o]; }
|
||||
bool is_empty() const { return m_heap_size == 0; }
|
||||
|
||||
/// return the first element of the queue and removes it from the queue
|
||||
unsigned dequeue_and_get_priority(T & priority);
|
||||
void fix_heap_under(unsigned i);
|
||||
void put_the_last_at_the_top_and_fix_the_heap();
|
||||
/// return the first element of the queue and removes it from the queue
|
||||
unsigned dequeue();
|
||||
unsigned peek() const {
|
||||
lean_assert(m_heap_size > 0);
|
||||
return m_heap[1];
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
void print(std::ostream & out);
|
||||
#endif
|
||||
};
|
||||
}
|
194
src/util/lp/binary_heap_priority_queue.hpp
Normal file
194
src/util/lp/binary_heap_priority_queue.hpp
Normal file
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/vector.h"
|
||||
#include "util/lp/binary_heap_priority_queue.h"
|
||||
namespace lean {
|
||||
// is is the child place in heap
|
||||
template <typename T> void binary_heap_priority_queue<T>::swap_with_parent(unsigned i) {
|
||||
unsigned parent = m_heap[i >> 1];
|
||||
put_at(i >> 1, m_heap[i]);
|
||||
put_at(i, parent);
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::put_at(unsigned i, unsigned h) {
|
||||
m_heap[i] = h;
|
||||
m_heap_inverse[h] = i;
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::decrease_priority(unsigned o, T newPriority) {
|
||||
m_priorities[o] = newPriority;
|
||||
int i = m_heap_inverse[o];
|
||||
while (i > 1) {
|
||||
if (m_priorities[m_heap[i]] < m_priorities[m_heap[i >> 1]])
|
||||
swap_with_parent(i);
|
||||
else
|
||||
break;
|
||||
i >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T> bool binary_heap_priority_queue<T>::is_consistent() const {
|
||||
for (int i = 0; i < m_heap_inverse.size(); i++) {
|
||||
int i_index = m_heap_inverse[i];
|
||||
lean_assert(i_index <= static_cast<int>(m_heap_size));
|
||||
lean_assert(i_index == -1 || m_heap[i_index] == i);
|
||||
}
|
||||
for (unsigned i = 1; i < m_heap_size; i++) {
|
||||
unsigned ch = i << 1;
|
||||
for (int k = 0; k < 2; k++) {
|
||||
if (ch > m_heap_size) break;
|
||||
if (!(m_priorities[m_heap[i]] <= m_priorities[m_heap[ch]])){
|
||||
return false;
|
||||
}
|
||||
ch++;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
template <typename T> void binary_heap_priority_queue<T>::remove(unsigned o) {
|
||||
T priority_of_o = m_priorities[o];
|
||||
int o_in_heap = m_heap_inverse[o];
|
||||
if (o_in_heap == -1) {
|
||||
return; // nothing to do
|
||||
}
|
||||
lean_assert(static_cast<unsigned>(o_in_heap) <= m_heap_size);
|
||||
if (static_cast<unsigned>(o_in_heap) < m_heap_size) {
|
||||
put_at(o_in_heap, m_heap[m_heap_size--]);
|
||||
if (m_priorities[m_heap[o_in_heap]] > priority_of_o) {
|
||||
fix_heap_under(o_in_heap);
|
||||
} else { // we need to propogate the m_heap[o_in_heap] up
|
||||
unsigned i = o_in_heap;
|
||||
while (i > 1) {
|
||||
unsigned ip = i >> 1;
|
||||
if (m_priorities[m_heap[i]] < m_priorities[m_heap[ip]])
|
||||
swap_with_parent(i);
|
||||
else
|
||||
break;
|
||||
i = ip;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lean_assert(static_cast<unsigned>(o_in_heap) == m_heap_size);
|
||||
m_heap_size--;
|
||||
}
|
||||
m_heap_inverse[o] = -1;
|
||||
// lean_assert(is_consistent());
|
||||
}
|
||||
// n is the initial queue capacity.
|
||||
// The capacity will be enlarged two times automatically if needed
|
||||
template <typename T> binary_heap_priority_queue<T>::binary_heap_priority_queue(unsigned n) :
|
||||
m_priorities(n),
|
||||
m_heap(n + 1), // because the indexing for A starts from 1
|
||||
m_heap_inverse(n, -1),
|
||||
m_heap_size(0)
|
||||
{ }
|
||||
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::resize(unsigned n) {
|
||||
m_priorities.resize(n);
|
||||
m_heap.resize(n + 1);
|
||||
m_heap_inverse.resize(n, -1);
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::put_to_heap(unsigned i, unsigned o) {
|
||||
m_heap[i] = o;
|
||||
m_heap_inverse[o] = i;
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::enqueue_new(unsigned o, const T& priority) {
|
||||
m_heap_size++;
|
||||
int i = m_heap_size;
|
||||
lean_assert(o < m_priorities.size());
|
||||
m_priorities[o] = priority;
|
||||
put_at(i, o);
|
||||
while (i > 1 && m_priorities[m_heap[i >> 1]] > priority) {
|
||||
swap_with_parent(i);
|
||||
i >>= 1;
|
||||
}
|
||||
}
|
||||
// This method can work with an element that is already in the queue.
|
||||
// In this case the priority will be changed and the queue adjusted.
|
||||
template <typename T> void binary_heap_priority_queue<T>::enqueue(unsigned o, const T & priority) {
|
||||
if (o >= m_priorities.size()) {
|
||||
resize(o << 1); // make the size twice larger
|
||||
}
|
||||
if (m_heap_inverse[o] == -1)
|
||||
enqueue_new(o, priority);
|
||||
else
|
||||
change_priority_for_existing(o, priority);
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::change_priority_for_existing(unsigned o, const T & priority) {
|
||||
if (m_priorities[o] > priority) {
|
||||
decrease_priority(o, priority);
|
||||
} else {
|
||||
m_priorities[o] = priority;
|
||||
fix_heap_under(m_heap_inverse[o]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// return the first element of the queue and removes it from the queue
|
||||
template <typename T> unsigned binary_heap_priority_queue<T>::dequeue_and_get_priority(T & priority) {
|
||||
lean_assert(m_heap_size != 0);
|
||||
int ret = m_heap[1];
|
||||
priority = m_priorities[ret];
|
||||
put_the_last_at_the_top_and_fix_the_heap();
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::fix_heap_under(unsigned i) {
|
||||
while (true) {
|
||||
unsigned smallest = i;
|
||||
unsigned l = i << 1;
|
||||
if (l <= m_heap_size && m_priorities[m_heap[l]] < m_priorities[m_heap[i]])
|
||||
smallest = l;
|
||||
unsigned r = l + 1;
|
||||
if (r <= m_heap_size && m_priorities[m_heap[r]] < m_priorities[m_heap[smallest]])
|
||||
smallest = r;
|
||||
if (smallest != i)
|
||||
swap_with_parent(smallest);
|
||||
else
|
||||
break;
|
||||
i = smallest;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::put_the_last_at_the_top_and_fix_the_heap() {
|
||||
if (m_heap_size > 1) {
|
||||
put_at(1, m_heap[m_heap_size--]);
|
||||
fix_heap_under(1);
|
||||
} else {
|
||||
m_heap_size--;
|
||||
}
|
||||
}
|
||||
/// return the first element of the queue and removes it from the queue
|
||||
template <typename T> unsigned binary_heap_priority_queue<T>::dequeue() {
|
||||
lean_assert(m_heap_size > 0);
|
||||
int ret = m_heap[1];
|
||||
put_the_last_at_the_top_and_fix_the_heap();
|
||||
m_heap_inverse[ret] = -1;
|
||||
return ret;
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T> void binary_heap_priority_queue<T>::print(std::ostream & out) {
|
||||
vector<int> index;
|
||||
vector<T> prs;
|
||||
while (size()) {
|
||||
T prior;
|
||||
int j = dequeue_and_get_priority(prior);
|
||||
index.push_back(j);
|
||||
prs.push_back(prior);
|
||||
out << "(" << j << ", " << prior << ")";
|
||||
}
|
||||
out << std::endl;
|
||||
// restore the queue
|
||||
for (int i = 0; i < index.size(); i++)
|
||||
enqueue(index[i], prs[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
26
src/util/lp/binary_heap_priority_queue_instances.cpp
Normal file
26
src/util/lp/binary_heap_priority_queue_instances.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/numeric_pair.h"
|
||||
#include "util/lp/binary_heap_priority_queue.hpp"
|
||||
namespace lean {
|
||||
template binary_heap_priority_queue<int>::binary_heap_priority_queue(unsigned int);
|
||||
template unsigned binary_heap_priority_queue<int>::dequeue();
|
||||
template void binary_heap_priority_queue<int>::enqueue(unsigned int, int const&);
|
||||
template void binary_heap_priority_queue<double>::enqueue(unsigned int, double const&);
|
||||
template void binary_heap_priority_queue<mpq>::enqueue(unsigned int, mpq const&);
|
||||
template void binary_heap_priority_queue<int>::remove(unsigned int);
|
||||
template unsigned binary_heap_priority_queue<numeric_pair<mpq> >::dequeue();
|
||||
template unsigned binary_heap_priority_queue<double>::dequeue();
|
||||
template unsigned binary_heap_priority_queue<mpq>::dequeue();
|
||||
template void binary_heap_priority_queue<numeric_pair<mpq> >::enqueue(unsigned int, numeric_pair<mpq> const&);
|
||||
template void binary_heap_priority_queue<numeric_pair<mpq> >::resize(unsigned int);
|
||||
template void lean::binary_heap_priority_queue<double>::resize(unsigned int);
|
||||
template binary_heap_priority_queue<unsigned int>::binary_heap_priority_queue(unsigned int);
|
||||
template void binary_heap_priority_queue<unsigned>::resize(unsigned int);
|
||||
template unsigned binary_heap_priority_queue<unsigned int>::dequeue();
|
||||
template void binary_heap_priority_queue<unsigned int>::enqueue(unsigned int, unsigned int const&);
|
||||
template void binary_heap_priority_queue<unsigned int>::remove(unsigned int);
|
||||
template void lean::binary_heap_priority_queue<mpq>::resize(unsigned int);
|
||||
}
|
50
src/util/lp/binary_heap_upair_queue.h
Normal file
50
src/util/lp/binary_heap_upair_queue.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include "util/vector.h"
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include "util/lp/binary_heap_priority_queue.h"
|
||||
|
||||
|
||||
typedef std::pair<unsigned, unsigned> upair;
|
||||
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
class binary_heap_upair_queue {
|
||||
binary_heap_priority_queue<T> m_q;
|
||||
std::unordered_map<upair, unsigned> m_pairs_to_index;
|
||||
svector<upair> m_pairs; // inverse to index
|
||||
svector<unsigned> m_available_spots;
|
||||
public:
|
||||
binary_heap_upair_queue(unsigned size);
|
||||
|
||||
unsigned dequeue_available_spot();
|
||||
bool is_empty() const { return m_q.is_empty(); }
|
||||
|
||||
unsigned size() const {return m_q.size(); }
|
||||
|
||||
bool contains(unsigned i, unsigned j) const { return m_pairs_to_index.find(std::make_pair(i, j)) != m_pairs_to_index.end();
|
||||
}
|
||||
|
||||
void remove(unsigned i, unsigned j);
|
||||
bool ij_index_is_new(unsigned ij_index) const;
|
||||
void enqueue(unsigned i, unsigned j, const T & priority);
|
||||
void dequeue(unsigned & i, unsigned &j);
|
||||
T get_priority(unsigned i, unsigned j) const;
|
||||
#ifdef LEAN_DEBUG
|
||||
bool pair_to_index_is_a_bijection() const;
|
||||
bool available_spots_are_correct() const;
|
||||
bool is_correct() const {
|
||||
return m_q.is_consistent() && pair_to_index_is_a_bijection() && available_spots_are_correct();
|
||||
}
|
||||
#endif
|
||||
void resize(unsigned size) { m_q.resize(size); }
|
||||
};
|
||||
}
|
110
src/util/lp/binary_heap_upair_queue.hpp
Normal file
110
src/util/lp/binary_heap_upair_queue.hpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include <set>
|
||||
#include "util/lp/lp_utils.h"
|
||||
#include "util/lp/binary_heap_upair_queue.h"
|
||||
namespace lean {
|
||||
template <typename T> binary_heap_upair_queue<T>::binary_heap_upair_queue(unsigned size) : m_q(size), m_pairs(size) {
|
||||
for (unsigned i = 0; i < size; i++)
|
||||
m_available_spots.push_back(i);
|
||||
}
|
||||
|
||||
template <typename T> unsigned
|
||||
binary_heap_upair_queue<T>::dequeue_available_spot() {
|
||||
lean_assert(m_available_spots.empty() == false);
|
||||
unsigned ret = m_available_spots.back();
|
||||
m_available_spots.pop_back();
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_upair_queue<T>::remove(unsigned i, unsigned j) {
|
||||
upair p(i, j);
|
||||
auto it = m_pairs_to_index.find(p);
|
||||
if (it == m_pairs_to_index.end())
|
||||
return; // nothing to do
|
||||
m_q.remove(it->second);
|
||||
m_available_spots.push_back(it->second);
|
||||
m_pairs_to_index.erase(it);
|
||||
}
|
||||
|
||||
|
||||
template <typename T> bool binary_heap_upair_queue<T>::ij_index_is_new(unsigned ij_index) const {
|
||||
for (auto it : m_pairs_to_index) {
|
||||
if (it.second == ij_index)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_upair_queue<T>::enqueue(unsigned i, unsigned j, const T & priority) {
|
||||
upair p(i, j);
|
||||
auto it = m_pairs_to_index.find(p);
|
||||
unsigned ij_index;
|
||||
if (it == m_pairs_to_index.end()) {
|
||||
// it is a new pair, let us find a spot for it
|
||||
if (m_available_spots.empty()) {
|
||||
// we ran out of empty spots
|
||||
unsigned size_was = static_cast<unsigned>(m_pairs.size());
|
||||
unsigned new_size = size_was << 1;
|
||||
for (unsigned i = size_was; i < new_size; i++)
|
||||
m_available_spots.push_back(i);
|
||||
m_pairs.resize(new_size);
|
||||
}
|
||||
ij_index = dequeue_available_spot();
|
||||
// lean_assert(ij_index<m_pairs.size() && ij_index_is_new(ij_index));
|
||||
m_pairs[ij_index] = p;
|
||||
m_pairs_to_index[p] = ij_index;
|
||||
} else {
|
||||
ij_index = it->second;
|
||||
}
|
||||
m_q.enqueue(ij_index, priority);
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_upair_queue<T>::dequeue(unsigned & i, unsigned &j) {
|
||||
lean_assert(!m_q.is_empty());
|
||||
unsigned ij_index = m_q.dequeue();
|
||||
upair & p = m_pairs[ij_index];
|
||||
i = p.first;
|
||||
j = p.second;
|
||||
m_available_spots.push_back(ij_index);
|
||||
m_pairs_to_index.erase(p);
|
||||
}
|
||||
|
||||
|
||||
template <typename T> T binary_heap_upair_queue<T>::get_priority(unsigned i, unsigned j) const {
|
||||
auto it = m_pairs_to_index.find(std::make_pair(i, j));
|
||||
if (it == m_pairs_to_index.end())
|
||||
return T(0xFFFFFF); // big number
|
||||
return m_q.get_priority(it->second);
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T> bool binary_heap_upair_queue<T>::pair_to_index_is_a_bijection() const {
|
||||
std::set<int> tmp;
|
||||
for (auto p : m_pairs_to_index) {
|
||||
unsigned j = p.second;
|
||||
unsigned size = tmp.size();
|
||||
tmp.insert(j);
|
||||
if (tmp.size() == size)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> bool binary_heap_upair_queue<T>::available_spots_are_correct() const {
|
||||
std::set<int> tmp;
|
||||
for (auto p : m_available_spots){
|
||||
tmp.insert(p);
|
||||
}
|
||||
if (tmp.size() != m_available_spots.size())
|
||||
return false;
|
||||
for (auto it : m_pairs_to_index)
|
||||
if (tmp.find(it.second) != tmp.end())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
17
src/util/lp/binary_heap_upair_queue_instances.cpp
Normal file
17
src/util/lp/binary_heap_upair_queue_instances.cpp
Normal file
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/binary_heap_upair_queue.hpp"
|
||||
namespace lean {
|
||||
template binary_heap_upair_queue<int>::binary_heap_upair_queue(unsigned int);
|
||||
template binary_heap_upair_queue<unsigned int>::binary_heap_upair_queue(unsigned int);
|
||||
template unsigned binary_heap_upair_queue<int>::dequeue_available_spot();
|
||||
template unsigned binary_heap_upair_queue<unsigned int>::dequeue_available_spot();
|
||||
template void binary_heap_upair_queue<int>::enqueue(unsigned int, unsigned int, int const&);
|
||||
template void binary_heap_upair_queue<int>::remove(unsigned int, unsigned int);
|
||||
template void binary_heap_upair_queue<unsigned int>::remove(unsigned int, unsigned int);
|
||||
template void binary_heap_upair_queue<int>::dequeue(unsigned int&, unsigned int&);
|
||||
template void binary_heap_upair_queue<unsigned int>::enqueue(unsigned int, unsigned int, unsigned int const&);
|
||||
template void binary_heap_upair_queue<unsigned int>::dequeue(unsigned int&, unsigned int&);
|
||||
}
|
335
src/util/lp/bound_analyzer_on_row.h
Normal file
335
src/util/lp/bound_analyzer_on_row.h
Normal file
|
@ -0,0 +1,335 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/vector.h"
|
||||
#include "util/lp/linear_combination_iterator.h"
|
||||
#include "implied_bound.h"
|
||||
#include "test_bound_analyzer.h"
|
||||
#include <functional>
|
||||
#include "util/lp/bound_propagator.h"
|
||||
// We have an equality : sum by j of row[j]*x[j] = rs
|
||||
// We try to pin a var by pushing the total by using the variable bounds
|
||||
// In a loop we drive the partial sum down, denoting the variables of this process by _u.
|
||||
// In the same loop trying to pin variables by pushing the partial sum up, denoting the variable related to it by _l
|
||||
namespace lean {
|
||||
|
||||
class bound_analyzer_on_row {
|
||||
|
||||
linear_combination_iterator<mpq> & m_it;
|
||||
bound_propagator & m_bp;
|
||||
unsigned m_row_or_term_index;
|
||||
int m_column_of_u; // index of an unlimited from above monoid
|
||||
// -1 means that such a value is not found, -2 means that at least two of such monoids were found
|
||||
int m_column_of_l; // index of an unlimited from below monoid
|
||||
impq m_rs;
|
||||
|
||||
public :
|
||||
// constructor
|
||||
bound_analyzer_on_row(
|
||||
linear_combination_iterator<mpq> &it,
|
||||
const numeric_pair<mpq>& rs,
|
||||
unsigned row_or_term_index,
|
||||
bound_propagator & bp
|
||||
)
|
||||
:
|
||||
m_it(it),
|
||||
m_bp(bp),
|
||||
m_row_or_term_index(row_or_term_index),
|
||||
m_column_of_u(-1),
|
||||
m_column_of_l(-1),
|
||||
m_rs(rs)
|
||||
{}
|
||||
|
||||
|
||||
unsigned j;
|
||||
void analyze() {
|
||||
|
||||
mpq a; unsigned j;
|
||||
while (((m_column_of_l != -2) || (m_column_of_u != -2)) && m_it.next(a, j))
|
||||
analyze_bound_on_var_on_coeff(j, a);
|
||||
|
||||
if (m_column_of_u >= 0)
|
||||
limit_monoid_u_from_below();
|
||||
else if (m_column_of_u == -1)
|
||||
limit_all_monoids_from_below();
|
||||
|
||||
if (m_column_of_l >= 0)
|
||||
limit_monoid_l_from_above();
|
||||
else if (m_column_of_l == -1)
|
||||
limit_all_monoids_from_above();
|
||||
}
|
||||
|
||||
bool bound_is_available(unsigned j, bool low_bound) {
|
||||
return (low_bound && low_bound_is_available(j)) ||
|
||||
(!low_bound && upper_bound_is_available(j));
|
||||
}
|
||||
|
||||
bool upper_bound_is_available(unsigned j) const {
|
||||
switch (m_bp.get_column_type(j))
|
||||
{
|
||||
case column_type::fixed:
|
||||
case column_type::boxed:
|
||||
case column_type::upper_bound:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool low_bound_is_available(unsigned j) const {
|
||||
switch (m_bp.get_column_type(j))
|
||||
{
|
||||
case column_type::fixed:
|
||||
case column_type::boxed:
|
||||
case column_type::low_bound:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const impq & ub(unsigned j) const {
|
||||
lean_assert(upper_bound_is_available(j));
|
||||
return m_bp.get_upper_bound(j);
|
||||
}
|
||||
const impq & lb(unsigned j) const {
|
||||
lean_assert(low_bound_is_available(j));
|
||||
return m_bp.get_low_bound(j);
|
||||
}
|
||||
|
||||
|
||||
const mpq & monoid_max_no_mult(bool a_is_pos, unsigned j, bool & strict) const {
|
||||
if (a_is_pos) {
|
||||
strict = !is_zero(ub(j).y);
|
||||
return ub(j).x;
|
||||
}
|
||||
strict = !is_zero(lb(j).y);
|
||||
return lb(j).x;
|
||||
}
|
||||
mpq monoid_max(const mpq & a, unsigned j) const {
|
||||
if (is_pos(a)) {
|
||||
return a * ub(j).x;
|
||||
}
|
||||
return a * lb(j).x;
|
||||
}
|
||||
mpq monoid_max(const mpq & a, unsigned j, bool & strict) const {
|
||||
if (is_pos(a)) {
|
||||
strict = !is_zero(ub(j).y);
|
||||
return a * ub(j).x;
|
||||
}
|
||||
strict = !is_zero(lb(j).y);
|
||||
return a * lb(j).x;
|
||||
}
|
||||
const mpq & monoid_min_no_mult(bool a_is_pos, unsigned j, bool & strict) const {
|
||||
if (!a_is_pos) {
|
||||
strict = !is_zero(ub(j).y);
|
||||
return ub(j).x;
|
||||
}
|
||||
strict = !is_zero(lb(j).y);
|
||||
return lb(j).x;
|
||||
}
|
||||
|
||||
mpq monoid_min(const mpq & a, unsigned j, bool& strict) const {
|
||||
if (is_neg(a)) {
|
||||
strict = !is_zero(ub(j).y);
|
||||
return a * ub(j).x;
|
||||
}
|
||||
|
||||
strict = !is_zero(lb(j).y);
|
||||
return a * lb(j).x;
|
||||
}
|
||||
|
||||
mpq monoid_min(const mpq & a, unsigned j) const {
|
||||
if (is_neg(a)) {
|
||||
return a * ub(j).x;
|
||||
}
|
||||
|
||||
return a * lb(j).x;
|
||||
}
|
||||
|
||||
|
||||
void limit_all_monoids_from_above() {
|
||||
int strict = 0;
|
||||
mpq total;
|
||||
lean_assert(is_zero(total));
|
||||
m_it.reset();
|
||||
mpq a; unsigned j;
|
||||
while (m_it.next(a, j)) {
|
||||
bool str;
|
||||
total -= monoid_min(a, j, str);
|
||||
if (str)
|
||||
strict++;
|
||||
}
|
||||
|
||||
m_it.reset();
|
||||
while (m_it.next(a, j)) {
|
||||
bool str;
|
||||
bool a_is_pos = is_pos(a);
|
||||
mpq bound = total / a + monoid_min_no_mult(a_is_pos, j, str);
|
||||
if (a_is_pos) {
|
||||
limit_j(j, bound, true, false, strict - static_cast<int>(str) > 0);
|
||||
}
|
||||
else {
|
||||
limit_j(j, bound, false, true, strict - static_cast<int>(str) > 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void limit_all_monoids_from_below() {
|
||||
int strict = 0;
|
||||
mpq total;
|
||||
lean_assert(is_zero(total));
|
||||
m_it.reset();
|
||||
mpq a; unsigned j;
|
||||
while (m_it.next(a, j)) {
|
||||
bool str;
|
||||
total -= monoid_max(a, j, str);
|
||||
if (str)
|
||||
strict++;
|
||||
}
|
||||
m_it.reset();
|
||||
while (m_it.next(a, j)) {
|
||||
bool str;
|
||||
bool a_is_pos = is_pos(a);
|
||||
mpq bound = total / a + monoid_max_no_mult(a_is_pos, j, str);
|
||||
bool astrict = strict - static_cast<int>(str) > 0;
|
||||
if (a_is_pos) {
|
||||
limit_j(j, bound, true, true, astrict);
|
||||
}
|
||||
else {
|
||||
limit_j(j, bound, false, false, astrict);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void limit_monoid_u_from_below() {
|
||||
// we are going to limit from below the monoid m_column_of_u,
|
||||
// every other monoid is impossible to limit from below
|
||||
mpq u_coeff, a;
|
||||
unsigned j;
|
||||
mpq bound = -m_rs.x;
|
||||
m_it.reset();
|
||||
bool strict = false;
|
||||
while (m_it.next(a, j)) {
|
||||
if (j == static_cast<unsigned>(m_column_of_u)) {
|
||||
u_coeff = a;
|
||||
continue;
|
||||
}
|
||||
bool str;
|
||||
bound -= monoid_max(a, j, str);
|
||||
if (str)
|
||||
strict = true;
|
||||
}
|
||||
|
||||
bound /= u_coeff;
|
||||
|
||||
if (numeric_traits<impq>::is_pos(u_coeff)) {
|
||||
limit_j(m_column_of_u, bound, true, true, strict);
|
||||
} else {
|
||||
limit_j(m_column_of_u, bound, false, false, strict);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void limit_monoid_l_from_above() {
|
||||
// we are going to limit from above the monoid m_column_of_l,
|
||||
// every other monoid is impossible to limit from above
|
||||
mpq l_coeff, a;
|
||||
unsigned j;
|
||||
mpq bound = -m_rs.x;
|
||||
bool strict = false;
|
||||
m_it.reset();
|
||||
while (m_it.next(a, j)) {
|
||||
if (j == static_cast<unsigned>(m_column_of_l)) {
|
||||
l_coeff = a;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool str;
|
||||
bound -= monoid_min(a, j, str);
|
||||
if (str)
|
||||
strict = true;
|
||||
}
|
||||
bound /= l_coeff;
|
||||
if (is_pos(l_coeff)) {
|
||||
limit_j(m_column_of_l, bound, true, false, strict);
|
||||
} else {
|
||||
limit_j(m_column_of_l, bound, false, true, strict);
|
||||
}
|
||||
}
|
||||
|
||||
// // it is the coefficent before the bounded column
|
||||
// void provide_evidence(bool coeff_is_pos) {
|
||||
// /*
|
||||
// auto & be = m_ibounds.back();
|
||||
// bool low_bound = be.m_low_bound;
|
||||
// if (!coeff_is_pos)
|
||||
// low_bound = !low_bound;
|
||||
// auto it = m_it.clone();
|
||||
// mpq a; unsigned j;
|
||||
// while (it->next(a, j)) {
|
||||
// if (be.m_j == j) continue;
|
||||
// lean_assert(bound_is_available(j, is_neg(a) ? low_bound : !low_bound));
|
||||
// be.m_vector_of_bound_signatures.emplace_back(a, j, numeric_traits<impq>::
|
||||
// is_neg(a)? low_bound: !low_bound);
|
||||
// }
|
||||
// delete it;
|
||||
// */
|
||||
// }
|
||||
|
||||
void limit_j(unsigned j, const mpq& u, bool coeff_before_j_is_pos, bool is_low_bound, bool strict){
|
||||
m_bp.try_add_bound(u, j, is_low_bound, coeff_before_j_is_pos, m_row_or_term_index, strict);
|
||||
}
|
||||
|
||||
|
||||
void advance_u(unsigned j) {
|
||||
if (m_column_of_u == -1)
|
||||
m_column_of_u = j;
|
||||
else
|
||||
m_column_of_u = -2;
|
||||
}
|
||||
|
||||
void advance_l(unsigned j) {
|
||||
if (m_column_of_l == -1)
|
||||
m_column_of_l = j;
|
||||
else
|
||||
m_column_of_l = -2;
|
||||
}
|
||||
|
||||
void analyze_bound_on_var_on_coeff(int j, const mpq &a) {
|
||||
switch (m_bp.get_column_type(j)) {
|
||||
case column_type::low_bound:
|
||||
if (numeric_traits<mpq>::is_pos(a))
|
||||
advance_u(j);
|
||||
else
|
||||
advance_l(j);
|
||||
break;
|
||||
case column_type::upper_bound:
|
||||
if(numeric_traits<mpq>::is_neg(a))
|
||||
advance_u(j);
|
||||
else
|
||||
advance_l(j);
|
||||
break;
|
||||
case column_type::free_column:
|
||||
advance_u(j);
|
||||
advance_l(j);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void analyze_row(linear_combination_iterator<mpq> &it,
|
||||
const numeric_pair<mpq>& rs,
|
||||
unsigned row_or_term_index,
|
||||
bound_propagator & bp
|
||||
) {
|
||||
bound_analyzer_on_row a(it, rs, row_or_term_index, bp);
|
||||
a.analyze();
|
||||
}
|
||||
|
||||
};
|
||||
}
|
47
src/util/lp/bound_propagator.cpp
Normal file
47
src/util/lp/bound_propagator.cpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lar_solver.h"
|
||||
namespace lean {
|
||||
bound_propagator::bound_propagator(lar_solver & ls):
|
||||
m_lar_solver(ls) {}
|
||||
column_type bound_propagator::get_column_type(unsigned j) const {
|
||||
return m_lar_solver.m_mpq_lar_core_solver.m_column_types()[j];
|
||||
}
|
||||
const impq & bound_propagator::get_low_bound(unsigned j) const {
|
||||
return m_lar_solver.m_mpq_lar_core_solver.m_r_low_bounds()[j];
|
||||
}
|
||||
const impq & bound_propagator::get_upper_bound(unsigned j) const {
|
||||
return m_lar_solver.m_mpq_lar_core_solver.m_r_upper_bounds()[j];
|
||||
}
|
||||
void bound_propagator::try_add_bound(const mpq & v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) {
|
||||
j = m_lar_solver.adjust_column_index_to_term_index(j);
|
||||
lconstraint_kind kind = is_low? GE : LE;
|
||||
if (strict)
|
||||
kind = static_cast<lconstraint_kind>(kind / 2);
|
||||
|
||||
if (!bound_is_interesting(j, kind, v))
|
||||
return;
|
||||
unsigned k; // index to ibounds
|
||||
if (is_low) {
|
||||
if (try_get_val(m_improved_low_bounds, j, k)) {
|
||||
auto & found_bound = m_ibounds[k];
|
||||
if (v > found_bound.m_bound || (v == found_bound.m_bound && found_bound.m_strict == false && strict))
|
||||
found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
|
||||
} else {
|
||||
m_improved_low_bounds[j] = m_ibounds.size();
|
||||
m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
|
||||
}
|
||||
} else { // the upper bound case
|
||||
if (try_get_val(m_improved_upper_bounds, j, k)) {
|
||||
auto & found_bound = m_ibounds[k];
|
||||
if (v < found_bound.m_bound || (v == found_bound.m_bound && found_bound.m_strict == false && strict))
|
||||
found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict);
|
||||
} else {
|
||||
m_improved_upper_bounds[j] = m_ibounds.size();
|
||||
m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
27
src/util/lp/bound_propagator.h
Normal file
27
src/util/lp/bound_propagator.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/lp/lp_settings.h"
|
||||
namespace lean {
|
||||
class lar_solver;
|
||||
class bound_propagator {
|
||||
std::unordered_map<unsigned, unsigned> m_improved_low_bounds; // these maps map a column index to the corresponding index in ibounds
|
||||
std::unordered_map<unsigned, unsigned> m_improved_upper_bounds;
|
||||
lar_solver & m_lar_solver;
|
||||
public:
|
||||
vector<implied_bound> m_ibounds;
|
||||
public:
|
||||
bound_propagator(lar_solver & ls);
|
||||
column_type get_column_type(unsigned) const;
|
||||
const impq & get_low_bound(unsigned) const;
|
||||
const impq & get_upper_bound(unsigned) const;
|
||||
void try_add_bound(const mpq & v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict);
|
||||
virtual bool bound_is_interesting(unsigned vi,
|
||||
lean::lconstraint_kind kind,
|
||||
const rational & bval) {return true;}
|
||||
unsigned number_of_found_bounds() const { return m_ibounds.size(); }
|
||||
virtual void consume(mpq const& v, unsigned j) { std::cout << "doh\n"; }
|
||||
};
|
||||
}
|
20
src/util/lp/breakpoint.h
Normal file
20
src/util/lp/breakpoint.h
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace lean {
|
||||
enum breakpoint_type {
|
||||
low_break, upper_break, fixed_break
|
||||
};
|
||||
template <typename X>
|
||||
struct breakpoint {
|
||||
unsigned m_j; // the basic column
|
||||
breakpoint_type m_type;
|
||||
X m_delta;
|
||||
breakpoint(){}
|
||||
breakpoint(unsigned j, X delta, breakpoint_type type):m_j(j), m_type(type), m_delta(delta) {}
|
||||
};
|
||||
}
|
250
src/util/lp/column_info.h
Normal file
250
src/util/lp/column_info.h
Normal file
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "util/vector.h"
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include "util/lp/lp_settings.h"
|
||||
namespace lean {
|
||||
inline bool is_valid(unsigned j) { return static_cast<int>(j) >= 0;}
|
||||
|
||||
template <typename T>
|
||||
class column_info {
|
||||
std::string m_name;
|
||||
bool m_low_bound_is_set;
|
||||
bool m_low_bound_is_strict;
|
||||
bool m_upper_bound_is_set;
|
||||
bool m_upper_bound_is_strict;
|
||||
T m_low_bound;
|
||||
T m_upper_bound;
|
||||
T m_fixed_value;
|
||||
bool m_is_fixed;
|
||||
T m_cost;
|
||||
unsigned m_column_index;
|
||||
public:
|
||||
bool operator==(const column_info & c) const {
|
||||
return m_name == c.m_name &&
|
||||
m_low_bound_is_set == c.m_low_bound_is_set &&
|
||||
m_low_bound_is_strict == c.m_low_bound_is_strict &&
|
||||
m_upper_bound_is_set == c.m_upper_bound_is_set&&
|
||||
m_upper_bound_is_strict == c.m_upper_bound_is_strict&&
|
||||
(!m_low_bound_is_set || m_low_bound == c.m_low_bound) &&
|
||||
(!m_upper_bound_is_set || m_upper_bound == c.m_upper_bound) &&
|
||||
m_cost == c.m_cost &&
|
||||
m_is_fixed == c.m_is_fixed &&
|
||||
(!m_is_fixed || m_fixed_value == c.m_fixed_value) &&
|
||||
m_column_index == c.m_column_index;
|
||||
}
|
||||
bool operator!=(const column_info & c) const { return !((*this) == c); }
|
||||
void set_column_index(unsigned j) {
|
||||
m_column_index = j;
|
||||
}
|
||||
// the default constructor
|
||||
column_info():
|
||||
m_low_bound_is_set(false),
|
||||
m_low_bound_is_strict(false),
|
||||
m_upper_bound_is_set (false),
|
||||
m_upper_bound_is_strict (false),
|
||||
m_is_fixed(false),
|
||||
m_cost(numeric_traits<T>::zero()),
|
||||
m_column_index(static_cast<unsigned>(-1))
|
||||
{}
|
||||
|
||||
column_info(unsigned column_index) :
|
||||
m_low_bound_is_set(false),
|
||||
m_low_bound_is_strict(false),
|
||||
m_upper_bound_is_set (false),
|
||||
m_upper_bound_is_strict (false),
|
||||
m_is_fixed(false),
|
||||
m_cost(numeric_traits<T>::zero()),
|
||||
m_column_index(column_index) {
|
||||
}
|
||||
|
||||
column_info(const column_info & ci) {
|
||||
m_name = ci.m_name;
|
||||
m_low_bound_is_set = ci.m_low_bound_is_set;
|
||||
m_low_bound_is_strict = ci.m_low_bound_is_strict;
|
||||
m_upper_bound_is_set = ci.m_upper_bound_is_set;
|
||||
m_upper_bound_is_strict = ci.m_upper_bound_is_strict;
|
||||
m_low_bound = ci.m_low_bound;
|
||||
m_upper_bound = ci.m_upper_bound;
|
||||
m_cost = ci.m_cost;
|
||||
m_fixed_value = ci.m_fixed_value;
|
||||
m_is_fixed = ci.m_is_fixed;
|
||||
m_column_index = ci.m_column_index;
|
||||
}
|
||||
|
||||
unsigned get_column_index() const {
|
||||
return m_column_index;
|
||||
}
|
||||
|
||||
column_type get_column_type() const {
|
||||
return m_is_fixed? column_type::fixed : (m_low_bound_is_set? (m_upper_bound_is_set? column_type::boxed : column_type::low_bound) : (m_upper_bound_is_set? column_type::upper_bound: column_type::free_column));
|
||||
}
|
||||
|
||||
column_type get_column_type_no_flipping() const {
|
||||
if (m_is_fixed) {
|
||||
return column_type::fixed;
|
||||
}
|
||||
|
||||
if (m_low_bound_is_set) {
|
||||
return m_upper_bound_is_set? column_type::boxed: column_type::low_bound;
|
||||
}
|
||||
// we are flipping the bounds!
|
||||
return m_upper_bound_is_set? column_type::upper_bound
|
||||
: column_type::free_column;
|
||||
}
|
||||
|
||||
T get_low_bound() const {
|
||||
lean_assert(m_low_bound_is_set);
|
||||
return m_low_bound;
|
||||
}
|
||||
T get_upper_bound() const {
|
||||
lean_assert(m_upper_bound_is_set);
|
||||
return m_upper_bound;
|
||||
}
|
||||
|
||||
bool low_bound_is_set() const {
|
||||
return m_low_bound_is_set;
|
||||
}
|
||||
|
||||
bool upper_bound_is_set() const {
|
||||
return m_upper_bound_is_set;
|
||||
}
|
||||
|
||||
T get_shift() {
|
||||
if (is_fixed()) {
|
||||
return m_fixed_value;
|
||||
}
|
||||
if (is_flipped()){
|
||||
return m_upper_bound;
|
||||
}
|
||||
return m_low_bound_is_set? m_low_bound : numeric_traits<T>::zero();
|
||||
}
|
||||
|
||||
bool is_flipped() {
|
||||
return m_upper_bound_is_set && !m_low_bound_is_set;
|
||||
}
|
||||
|
||||
bool adjusted_low_bound_is_set() {
|
||||
return !is_flipped()? low_bound_is_set(): upper_bound_is_set();
|
||||
}
|
||||
|
||||
bool adjusted_upper_bound_is_set() {
|
||||
return !is_flipped()? upper_bound_is_set(): low_bound_is_set();
|
||||
}
|
||||
|
||||
T get_adjusted_upper_bound() {
|
||||
return get_upper_bound() - get_low_bound();
|
||||
}
|
||||
|
||||
bool is_fixed() const {
|
||||
return m_is_fixed;
|
||||
}
|
||||
|
||||
bool is_free() {
|
||||
return !m_low_bound_is_set && !m_upper_bound_is_set;
|
||||
}
|
||||
|
||||
void set_fixed_value(T v) {
|
||||
m_is_fixed = true;
|
||||
m_fixed_value = v;
|
||||
}
|
||||
|
||||
T get_fixed_value() const {
|
||||
lean_assert(m_is_fixed);
|
||||
return m_fixed_value;
|
||||
}
|
||||
|
||||
T get_cost() const {
|
||||
return m_cost;
|
||||
}
|
||||
|
||||
void set_cost(T const & cost) {
|
||||
m_cost = cost;
|
||||
}
|
||||
|
||||
void set_name(std::string const & s) {
|
||||
m_name = s;
|
||||
}
|
||||
|
||||
std::string get_name() const {
|
||||
return m_name;
|
||||
}
|
||||
|
||||
void set_low_bound(T const & l) {
|
||||
m_low_bound = l;
|
||||
m_low_bound_is_set = true;
|
||||
}
|
||||
|
||||
void set_upper_bound(T const & l) {
|
||||
m_upper_bound = l;
|
||||
m_upper_bound_is_set = true;
|
||||
}
|
||||
|
||||
void unset_low_bound() {
|
||||
m_low_bound_is_set = false;
|
||||
}
|
||||
|
||||
void unset_upper_bound() {
|
||||
m_upper_bound_is_set = false;
|
||||
}
|
||||
|
||||
void unset_fixed() {
|
||||
m_is_fixed = false;
|
||||
}
|
||||
|
||||
bool low_bound_holds(T v) {
|
||||
return !low_bound_is_set() || v >= m_low_bound -T(0.0000001);
|
||||
}
|
||||
|
||||
bool upper_bound_holds(T v) {
|
||||
return !upper_bound_is_set() || v <= m_upper_bound + T(0.000001);
|
||||
}
|
||||
|
||||
bool bounds_hold(T v) {
|
||||
return low_bound_holds(v) && upper_bound_holds(v);
|
||||
}
|
||||
|
||||
bool adjusted_bounds_hold(T v) {
|
||||
return adjusted_low_bound_holds(v) && adjusted_upper_bound_holds(v);
|
||||
}
|
||||
|
||||
bool adjusted_low_bound_holds(T v) {
|
||||
return !adjusted_low_bound_is_set() || v >= -T(0.0000001);
|
||||
}
|
||||
|
||||
bool adjusted_upper_bound_holds(T v) {
|
||||
return !adjusted_upper_bound_is_set() || v <= get_adjusted_upper_bound() + T(0.000001);
|
||||
}
|
||||
bool is_infeasible() {
|
||||
if ((!upper_bound_is_set()) || (!low_bound_is_set()))
|
||||
return false;
|
||||
// ok, both bounds are set
|
||||
bool at_least_one_is_strict = upper_bound_is_strict() || low_bound_is_strict();
|
||||
if (!at_least_one_is_strict)
|
||||
return get_upper_bound() < get_low_bound();
|
||||
// at least on bound is strict
|
||||
return get_upper_bound() <= get_low_bound(); // the equality is impossible
|
||||
}
|
||||
bool low_bound_is_strict() const {
|
||||
return m_low_bound_is_strict;
|
||||
}
|
||||
|
||||
void set_low_bound_strict(bool val) {
|
||||
m_low_bound_is_strict = val;
|
||||
}
|
||||
|
||||
bool upper_bound_is_strict() const {
|
||||
return m_upper_bound_is_strict;
|
||||
}
|
||||
|
||||
void set_upper_bound_strict(bool val) {
|
||||
m_upper_bound_is_strict = val;
|
||||
}
|
||||
};
|
||||
}
|
82
src/util/lp/column_namer.h
Normal file
82
src/util/lp/column_namer.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
#pragma once
|
||||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include <string>
|
||||
#include "util/lp/linear_combination_iterator.h"
|
||||
namespace lean {
|
||||
class column_namer {
|
||||
public:
|
||||
virtual std::string get_column_name(unsigned j) const = 0;
|
||||
template <typename T>
|
||||
void print_linear_iterator(linear_combination_iterator<T>* it, std::ostream & out) const {
|
||||
vector<std::pair<T, unsigned>> coeff;
|
||||
T a;
|
||||
unsigned i;
|
||||
while (it->next(a, i)) {
|
||||
coeff.emplace_back(a, i);
|
||||
}
|
||||
print_linear_combination_of_column_indices(coeff, out);
|
||||
}
|
||||
template <typename T>
|
||||
void print_linear_iterator_indices_only(linear_combination_iterator<T>* it, std::ostream & out) const {
|
||||
vector<std::pair<T, unsigned>> coeff;
|
||||
T a;
|
||||
unsigned i;
|
||||
while (it->next(a, i)) {
|
||||
coeff.emplace_back(a, i);
|
||||
}
|
||||
print_linear_combination_of_column_indices_only(coeff, out);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void print_linear_combination_of_column_indices_only(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const {
|
||||
bool first = true;
|
||||
for (const auto & it : coeffs) {
|
||||
auto val = it.first;
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
if (numeric_traits<T>::is_pos(val)) {
|
||||
out << " + ";
|
||||
} else {
|
||||
out << " - ";
|
||||
val = -val;
|
||||
}
|
||||
}
|
||||
if (val == -numeric_traits<T>::one())
|
||||
out << " - ";
|
||||
else if (val != numeric_traits<T>::one())
|
||||
out << T_to_string(val);
|
||||
|
||||
out << "_" << it.second;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void print_linear_combination_of_column_indices(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const {
|
||||
bool first = true;
|
||||
for (const auto & it : coeffs) {
|
||||
auto val = it.first;
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
if (numeric_traits<T>::is_pos(val)) {
|
||||
out << " + ";
|
||||
} else {
|
||||
out << " - ";
|
||||
val = -val;
|
||||
}
|
||||
}
|
||||
if (val == -numeric_traits<T>::one())
|
||||
out << " - ";
|
||||
else if (val != numeric_traits<T>::one())
|
||||
out << val;
|
||||
|
||||
out << get_column_name(it.second);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
43
src/util/lp/conversion_helper.h
Normal file
43
src/util/lp/conversion_helper.h
Normal file
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
namespace lean {
|
||||
template <typename V>
|
||||
struct conversion_helper {
|
||||
static V get_low_bound(const column_info<mpq> & ci) {
|
||||
return V(ci.get_low_bound(), ci.low_bound_is_strict()? 1 : 0);
|
||||
}
|
||||
|
||||
static V get_upper_bound(const column_info<mpq> & ci) {
|
||||
return V(ci.get_upper_bound(), ci.upper_bound_is_strict()? -1 : 0);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct conversion_helper <double> {
|
||||
static double get_upper_bound(const column_info<mpq> & ci) {
|
||||
if (!ci.upper_bound_is_strict())
|
||||
return ci.get_upper_bound().get_double();
|
||||
double eps = 0.00001;
|
||||
if (!ci.low_bound_is_set())
|
||||
return ci.get_upper_bound().get_double() - eps;
|
||||
eps = std::min((ci.get_upper_bound() - ci.get_low_bound()).get_double() / 1000, eps);
|
||||
return ci.get_upper_bound().get_double() - eps;
|
||||
}
|
||||
|
||||
static double get_low_bound(const column_info<mpq> & ci) {
|
||||
if (!ci.low_bound_is_strict())
|
||||
return ci.get_low_bound().get_double();
|
||||
double eps = 0.00001;
|
||||
if (!ci.upper_bound_is_set())
|
||||
return ci.get_low_bound().get_double() + eps;
|
||||
eps = std::min((ci.get_upper_bound() - ci.get_low_bound()).get_double() / 1000, eps);
|
||||
return ci.get_low_bound().get_double() + eps;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
117
src/util/lp/core_solver_pretty_printer.h
Normal file
117
src/util/lp/core_solver_pretty_printer.h
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include "util/vector.h"
|
||||
#include <ostream>
|
||||
#include "util/lp/lp_settings.h"
|
||||
#include "util/lp/indexed_vector.h"
|
||||
namespace lean {
|
||||
template <typename T, typename X> class lp_core_solver_base; // forward definition
|
||||
|
||||
template <typename T, typename X>
|
||||
class core_solver_pretty_printer {
|
||||
std::ostream & m_out;
|
||||
typedef std::string string;
|
||||
lp_core_solver_base<T, X> & m_core_solver;
|
||||
vector<unsigned> m_column_widths;
|
||||
vector<vector<string>> m_A;
|
||||
vector<vector<string>> m_signs;
|
||||
vector<string> m_costs;
|
||||
vector<string> m_cost_signs;
|
||||
vector<string> m_lows; // low bounds
|
||||
vector<string> m_upps; // upper bounds
|
||||
vector<string> m_lows_signs;
|
||||
vector<string> m_upps_signs;
|
||||
unsigned m_rs_width;
|
||||
vector<X> m_rs;
|
||||
unsigned m_title_width;
|
||||
std::string m_cost_title;
|
||||
std::string m_basis_heading_title;
|
||||
std::string m_x_title;
|
||||
std::string m_low_bounds_title;
|
||||
std::string m_upp_bounds_title;
|
||||
std::string m_exact_norm_title;
|
||||
std::string m_approx_norm_title;
|
||||
|
||||
|
||||
unsigned ncols() { return m_core_solver.m_A.column_count(); }
|
||||
unsigned nrows() { return m_core_solver.m_A.row_count(); }
|
||||
unsigned m_artificial_start;
|
||||
indexed_vector<T> m_w_buff;
|
||||
indexed_vector<T> m_ed_buff;
|
||||
vector<T> m_exact_column_norms;
|
||||
|
||||
public:
|
||||
core_solver_pretty_printer(lp_core_solver_base<T, X > & core_solver, std::ostream & out);
|
||||
|
||||
void init_costs();
|
||||
|
||||
~core_solver_pretty_printer();
|
||||
void init_rs_width();
|
||||
|
||||
T current_column_norm();
|
||||
|
||||
void init_m_A_and_signs();
|
||||
|
||||
void init_column_widths();
|
||||
|
||||
void adjust_width_with_low_bound(unsigned column, unsigned & w);
|
||||
void adjust_width_with_upper_bound(unsigned column, unsigned & w);
|
||||
|
||||
void adjust_width_with_bounds(unsigned column, unsigned & w);
|
||||
|
||||
void adjust_width_with_basis_heading(unsigned column, unsigned & w) {
|
||||
w = std::max(w, (unsigned)T_to_string(m_core_solver.m_basis_heading[column]).size());
|
||||
}
|
||||
|
||||
unsigned get_column_width(unsigned column);
|
||||
|
||||
unsigned regular_cell_width(unsigned row, unsigned column, std::string name) {
|
||||
return regular_cell_string(row, column, name).size();
|
||||
}
|
||||
|
||||
std::string regular_cell_string(unsigned row, unsigned column, std::string name);
|
||||
|
||||
|
||||
void set_coeff(vector<string>& row, vector<string> & row_signs, unsigned col, const T & t, string name);
|
||||
|
||||
void print_x();
|
||||
|
||||
std::string get_low_bound_string(unsigned j);
|
||||
|
||||
std::string get_upp_bound_string(unsigned j);
|
||||
|
||||
|
||||
void print_lows();
|
||||
|
||||
void print_upps();
|
||||
|
||||
string get_exact_column_norm_string(unsigned col) {
|
||||
return T_to_string(m_exact_column_norms[col]);
|
||||
}
|
||||
|
||||
void print_exact_norms();
|
||||
|
||||
void print_approx_norms();
|
||||
|
||||
void print();
|
||||
|
||||
void print_basis_heading();
|
||||
|
||||
void print_bottom_line() {
|
||||
m_out << "----------------------" << std::endl;
|
||||
}
|
||||
|
||||
void print_cost();
|
||||
|
||||
void print_given_rows(vector<string> & row, vector<string> & signs, X rst);
|
||||
|
||||
void print_row(unsigned i);
|
||||
|
||||
};
|
||||
}
|
383
src/util/lp/core_solver_pretty_printer.hpp
Normal file
383
src/util/lp/core_solver_pretty_printer.hpp
Normal file
|
@ -0,0 +1,383 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include "util/lp/lp_utils.h"
|
||||
#include "util/lp/lp_core_solver_base.h"
|
||||
#include "util/lp/core_solver_pretty_printer.h"
|
||||
#include "util/lp/numeric_pair.h"
|
||||
namespace lean {
|
||||
|
||||
|
||||
template <typename T, typename X>
|
||||
core_solver_pretty_printer<T, X>::core_solver_pretty_printer(lp_core_solver_base<T, X > & core_solver, std::ostream & out):
|
||||
m_out(out),
|
||||
m_core_solver(core_solver),
|
||||
m_A(core_solver.m_A.row_count(), vector<string>(core_solver.m_A.column_count(), "")),
|
||||
m_signs(core_solver.m_A.row_count(), vector<string>(core_solver.m_A.column_count(), " ")),
|
||||
m_costs(ncols(), ""),
|
||||
m_cost_signs(ncols(), " "),
|
||||
m_rs(ncols(), zero_of_type<X>()),
|
||||
m_w_buff(core_solver.m_w),
|
||||
m_ed_buff(core_solver.m_ed) {
|
||||
m_low_bounds_title = "low";
|
||||
m_upp_bounds_title = "upp";
|
||||
m_exact_norm_title = "exact cn";
|
||||
m_approx_norm_title = "approx cn";
|
||||
m_artificial_start = std::numeric_limits<unsigned>::max();
|
||||
|
||||
m_column_widths.resize(core_solver.m_A.column_count(), 0),
|
||||
init_m_A_and_signs();
|
||||
init_costs();
|
||||
init_column_widths();
|
||||
init_rs_width();
|
||||
m_cost_title = "costs";
|
||||
m_basis_heading_title = "heading";
|
||||
m_x_title = "x*";
|
||||
m_title_width = static_cast<unsigned>(std::max(std::max(m_cost_title.size(), std::max(m_basis_heading_title.size(), m_x_title.size())), m_approx_norm_title.size()));
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_costs() {
|
||||
if (!m_core_solver.use_tableau()) {
|
||||
vector<T> local_y(m_core_solver.m_m());
|
||||
m_core_solver.solve_yB(local_y);
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
if (m_core_solver.m_basis_heading[i] < 0) {
|
||||
T t = m_core_solver.m_costs[i] - m_core_solver.m_A.dot_product_with_column(local_y, i);
|
||||
set_coeff(m_costs, m_cost_signs, i, t, m_core_solver.column_name(i));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
if (m_core_solver.m_basis_heading[i] < 0) {
|
||||
set_coeff(m_costs, m_cost_signs, i, m_core_solver.m_d[i], m_core_solver.column_name(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> core_solver_pretty_printer<T, X>::~core_solver_pretty_printer() {
|
||||
m_core_solver.m_w = m_w_buff;
|
||||
m_core_solver.m_ed = m_ed_buff;
|
||||
}
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_rs_width() {
|
||||
m_rs_width = static_cast<unsigned>(T_to_string(m_core_solver.get_cost()).size());
|
||||
for (unsigned i = 0; i < nrows(); i++) {
|
||||
unsigned wt = static_cast<unsigned>(T_to_string(m_rs[i]).size());
|
||||
if (wt > m_rs_width) {
|
||||
m_rs_width = wt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> T core_solver_pretty_printer<T, X>::current_column_norm() {
|
||||
T ret = zero_of_type<T>();
|
||||
for (auto i : m_core_solver.m_ed.m_index)
|
||||
ret += m_core_solver.m_ed[i] * m_core_solver.m_ed[i];
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_m_A_and_signs() {
|
||||
if (numeric_traits<T>::precise() && m_core_solver.m_settings.use_tableau()) {
|
||||
for (unsigned column = 0; column < ncols(); column++) {
|
||||
vector<T> t(nrows(), zero_of_type<T>());
|
||||
for (const auto & c : m_core_solver.m_A.m_columns[column]){
|
||||
t[c.m_i] = m_core_solver.m_A.get_val(c);
|
||||
}
|
||||
|
||||
string name = m_core_solver.column_name(column);
|
||||
for (unsigned row = 0; row < nrows(); row ++) {
|
||||
set_coeff(
|
||||
m_A[row],
|
||||
m_signs[row],
|
||||
column,
|
||||
t[row],
|
||||
name);
|
||||
m_rs[row] += t[row] * m_core_solver.m_x[column];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (unsigned column = 0; column < ncols(); column++) {
|
||||
m_core_solver.solve_Bd(column); // puts the result into m_core_solver.m_ed
|
||||
string name = m_core_solver.column_name(column);
|
||||
for (unsigned row = 0; row < nrows(); row ++) {
|
||||
set_coeff(
|
||||
m_A[row],
|
||||
m_signs[row],
|
||||
column,
|
||||
m_core_solver.m_ed[row],
|
||||
name);
|
||||
m_rs[row] += m_core_solver.m_ed[row] * m_core_solver.m_x[column];
|
||||
}
|
||||
if (!m_core_solver.use_tableau())
|
||||
m_exact_column_norms.push_back(current_column_norm() + T(1)); // a conversion missing 1 -> T
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_column_widths() {
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
m_column_widths[i] = get_column_width(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_low_bound(unsigned column, unsigned & w) {
|
||||
if (!m_core_solver.low_bounds_are_set()) return;
|
||||
w = std::max(w, (unsigned)T_to_string(m_core_solver.low_bound_value(column)).size());
|
||||
}
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_upper_bound(unsigned column, unsigned & w) {
|
||||
w = std::max(w, (unsigned)T_to_string(m_core_solver.upper_bound_value(column)).size());
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_bounds(unsigned column, unsigned & w) {
|
||||
switch (m_core_solver.get_column_type(column)) {
|
||||
case column_type::fixed:
|
||||
case column_type::boxed:
|
||||
adjust_width_with_low_bound(column, w);
|
||||
adjust_width_with_upper_bound(column, w);
|
||||
break;
|
||||
case column_type::low_bound:
|
||||
adjust_width_with_low_bound(column, w);
|
||||
break;
|
||||
case column_type::upper_bound:
|
||||
adjust_width_with_upper_bound(column, w);
|
||||
break;
|
||||
case column_type::free_column:
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> unsigned core_solver_pretty_printer<T, X>:: get_column_width(unsigned column) {
|
||||
unsigned w = static_cast<unsigned>(std::max((size_t)m_costs[column].size(), T_to_string(m_core_solver.m_x[column]).size()));
|
||||
adjust_width_with_bounds(column, w);
|
||||
adjust_width_with_basis_heading(column, w);
|
||||
for (unsigned i = 0; i < nrows(); i++) {
|
||||
unsigned cellw = static_cast<unsigned>(m_A[i][column].size());
|
||||
if (cellw > w) {
|
||||
w = cellw;
|
||||
}
|
||||
}
|
||||
if (!m_core_solver.use_tableau()) {
|
||||
w = std::max(w, (unsigned)T_to_string(m_exact_column_norms[column]).size());
|
||||
if (m_core_solver.m_column_norms.size() > 0)
|
||||
w = std::max(w, (unsigned)T_to_string(m_core_solver.m_column_norms[column]).size());
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::regular_cell_string(unsigned row, unsigned /* column */, std::string name) {
|
||||
T t = fabs(m_core_solver.m_ed[row]);
|
||||
if ( t == 1) return name;
|
||||
return T_to_string(t) + name;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::set_coeff(vector<string>& row, vector<string> & row_signs, unsigned col, const T & t, string name) {
|
||||
if (numeric_traits<T>::is_zero(t)) {
|
||||
return;
|
||||
}
|
||||
if (col > 0) {
|
||||
if (t > 0) {
|
||||
row_signs[col] = "+";
|
||||
row[col] = t != 1? T_to_string(t) + name : name;
|
||||
} else {
|
||||
row_signs[col] = "-";
|
||||
row[col] = t != -1? T_to_string(-t) + name: name;
|
||||
}
|
||||
} else { // col == 0
|
||||
if (t == -1) {
|
||||
row[col] = "-" + name;
|
||||
} else if (t == 1) {
|
||||
row[col] = name;
|
||||
} else {
|
||||
row[col] = T_to_string(t) + name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_x() {
|
||||
if (ncols() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int blanks = m_title_width + 1 - static_cast<int>(m_x_title.size());
|
||||
m_out << m_x_title;
|
||||
print_blanks(blanks, m_out);
|
||||
|
||||
auto bh = m_core_solver.m_x;
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = T_to_string(bh[i]);
|
||||
int blanks = m_column_widths[i] - static_cast<int>(s.size());
|
||||
print_blanks(blanks, m_out);
|
||||
m_out << s << " "; // the column interval
|
||||
}
|
||||
m_out << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::get_low_bound_string(unsigned j) {
|
||||
switch (m_core_solver.get_column_type(j)){
|
||||
case column_type::boxed:
|
||||
case column_type::low_bound:
|
||||
case column_type::fixed:
|
||||
if (m_core_solver.low_bounds_are_set())
|
||||
return T_to_string(m_core_solver.low_bound_value(j));
|
||||
else
|
||||
return std::string("0");
|
||||
break;
|
||||
default:
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::get_upp_bound_string(unsigned j) {
|
||||
switch (m_core_solver.get_column_type(j)){
|
||||
case column_type::boxed:
|
||||
case column_type::upper_bound:
|
||||
case column_type::fixed:
|
||||
return T_to_string(m_core_solver.upper_bound_value(j));
|
||||
break;
|
||||
default:
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_lows() {
|
||||
if (ncols() == 0) {
|
||||
return;
|
||||
}
|
||||
int blanks = m_title_width + 1 - static_cast<unsigned>(m_low_bounds_title.size());
|
||||
m_out << m_low_bounds_title;
|
||||
print_blanks(blanks, m_out);
|
||||
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = get_low_bound_string(i);
|
||||
int blanks = m_column_widths[i] - static_cast<unsigned>(s.size());
|
||||
print_blanks(blanks, m_out);
|
||||
m_out << s << " "; // the column interval
|
||||
}
|
||||
m_out << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_upps() {
|
||||
if (ncols() == 0) {
|
||||
return;
|
||||
}
|
||||
int blanks = m_title_width + 1 - static_cast<unsigned>(m_upp_bounds_title.size());
|
||||
m_out << m_upp_bounds_title;
|
||||
print_blanks(blanks, m_out);
|
||||
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = get_upp_bound_string(i);
|
||||
int blanks = m_column_widths[i] - static_cast<unsigned>(s.size());
|
||||
print_blanks(blanks, m_out);
|
||||
m_out << s << " "; // the column interval
|
||||
}
|
||||
m_out << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_exact_norms() {
|
||||
if (m_core_solver.use_tableau()) return;
|
||||
int blanks = m_title_width + 1 - static_cast<int>(m_exact_norm_title.size());
|
||||
m_out << m_exact_norm_title;
|
||||
print_blanks(blanks, m_out);
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = get_exact_column_norm_string(i);
|
||||
int blanks = m_column_widths[i] - static_cast<int>(s.size());
|
||||
print_blanks(blanks, m_out);
|
||||
m_out << s << " ";
|
||||
}
|
||||
m_out << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_approx_norms() {
|
||||
if (m_core_solver.use_tableau()) return;
|
||||
int blanks = m_title_width + 1 - static_cast<int>(m_approx_norm_title.size());
|
||||
m_out << m_approx_norm_title;
|
||||
print_blanks(blanks, m_out);
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = T_to_string(m_core_solver.m_column_norms[i]);
|
||||
int blanks = m_column_widths[i] - static_cast<int>(s.size());
|
||||
print_blanks(blanks, m_out);
|
||||
m_out << s << " ";
|
||||
}
|
||||
m_out << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print() {
|
||||
for (unsigned i = 0; i < nrows(); i++) {
|
||||
print_row(i);
|
||||
}
|
||||
print_bottom_line();
|
||||
print_cost();
|
||||
print_x();
|
||||
print_basis_heading();
|
||||
print_lows();
|
||||
print_upps();
|
||||
print_exact_norms();
|
||||
if (m_core_solver.m_column_norms.size() > 0)
|
||||
print_approx_norms();
|
||||
m_out << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_basis_heading() {
|
||||
int blanks = m_title_width + 1 - static_cast<int>(m_basis_heading_title.size());
|
||||
m_out << m_basis_heading_title;
|
||||
print_blanks(blanks, m_out);
|
||||
|
||||
if (ncols() == 0) {
|
||||
return;
|
||||
}
|
||||
auto bh = m_core_solver.m_basis_heading;
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = T_to_string(bh[i]);
|
||||
int blanks = m_column_widths[i] - static_cast<unsigned>(s.size());
|
||||
print_blanks(blanks, m_out);
|
||||
m_out << s << " "; // the column interval
|
||||
}
|
||||
m_out << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_cost() {
|
||||
int blanks = m_title_width + 1 - static_cast<int>(m_cost_title.size());
|
||||
m_out << m_cost_title;
|
||||
print_blanks(blanks, m_out);
|
||||
print_given_rows(m_costs, m_cost_signs, m_core_solver.get_cost());
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_given_rows(vector<string> & row, vector<string> & signs, X rst) {
|
||||
for (unsigned col = 0; col < row.size(); col++) {
|
||||
unsigned width = m_column_widths[col];
|
||||
string s = row[col];
|
||||
int number_of_blanks = width - static_cast<unsigned>(s.size());
|
||||
lean_assert(number_of_blanks >= 0);
|
||||
print_blanks(number_of_blanks, m_out);
|
||||
m_out << s << ' ';
|
||||
if (col < row.size() - 1) {
|
||||
m_out << signs[col + 1] << ' ';
|
||||
}
|
||||
}
|
||||
m_out << '=';
|
||||
|
||||
string rs = T_to_string(rst);
|
||||
int nb = m_rs_width - static_cast<int>(rs.size());
|
||||
lean_assert(nb >= 0);
|
||||
print_blanks(nb + 1, m_out);
|
||||
m_out << rs << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_row(unsigned i){
|
||||
print_blanks(m_title_width + 1, m_out);
|
||||
auto row = m_A[i];
|
||||
auto sign_row = m_signs[i];
|
||||
auto rs = m_rs[i];
|
||||
print_given_rows(row, sign_row, rs);
|
||||
}
|
||||
}
|
15
src/util/lp/core_solver_pretty_printer_instances.cpp
Normal file
15
src/util/lp/core_solver_pretty_printer_instances.cpp
Normal file
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/numeric_pair.h"
|
||||
#include "util/lp/core_solver_pretty_printer.hpp"
|
||||
template lean::core_solver_pretty_printer<double, double>::core_solver_pretty_printer(lean::lp_core_solver_base<double, double> &, std::ostream & out);
|
||||
template void lean::core_solver_pretty_printer<double, double>::print();
|
||||
template lean::core_solver_pretty_printer<double, double>::~core_solver_pretty_printer();
|
||||
template lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::core_solver_pretty_printer(lean::lp_core_solver_base<lean::mpq, lean::mpq> &, std::ostream & out);
|
||||
template void lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::print();
|
||||
template lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::~core_solver_pretty_printer();
|
||||
template lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::core_solver_pretty_printer(lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> > &, std::ostream & out);
|
||||
template lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::~core_solver_pretty_printer();
|
||||
template void lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::print();
|
92
src/util/lp/dense_matrix.h
Normal file
92
src/util/lp/dense_matrix.h
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#ifdef LEAN_DEBUG
|
||||
#include "util/vector.h"
|
||||
#include "util/lp/matrix.h"
|
||||
namespace lean {
|
||||
// used for debugging purposes only
|
||||
template <typename T, typename X>
|
||||
class dense_matrix: public matrix<T, X> {
|
||||
public:
|
||||
struct ref {
|
||||
unsigned m_i;
|
||||
dense_matrix & m_s;
|
||||
ref(unsigned i, dense_matrix & s) :m_i(i * s.m_n), m_s(s){}
|
||||
T & operator[] (unsigned j) {
|
||||
return m_s.m_values[m_i + j];
|
||||
}
|
||||
const T & operator[] (unsigned j) const {
|
||||
return m_s.m_v[m_i + j];
|
||||
}
|
||||
};
|
||||
ref operator[] (unsigned i) {
|
||||
return ref(i, *this);
|
||||
}
|
||||
unsigned m_m; // number of rows
|
||||
unsigned m_n; // number of const
|
||||
vector<T> m_values;
|
||||
dense_matrix(unsigned m, unsigned n);
|
||||
|
||||
dense_matrix operator*=(matrix<T, X> const & a) {
|
||||
lean_assert(column_count() == a.row_count());
|
||||
dense_matrix c(row_count(), a.column_count());
|
||||
for (unsigned i = 0; i < row_count(); i++) {
|
||||
for (unsigned j = 0; j < a.column_count(); j++) {
|
||||
T v = numeric_traits<T>::zero();
|
||||
for (unsigned k = 0; k < a.column_count(); k++) {
|
||||
v += get_elem(i, k) * a(k, j);
|
||||
}
|
||||
c.set_elem(i, j, v);
|
||||
}
|
||||
}
|
||||
*this = c;
|
||||
return *this;
|
||||
}
|
||||
|
||||
dense_matrix & operator=(matrix<T, X> const & other);
|
||||
|
||||
dense_matrix & operator=(dense_matrix const & other);
|
||||
|
||||
dense_matrix(matrix<T, X> const * other);
|
||||
void apply_from_right(T * w);
|
||||
|
||||
void apply_from_right(vector <T> & w);
|
||||
|
||||
T * apply_from_left_with_different_dims(vector<T> & w);
|
||||
void apply_from_left(vector<T> & w , lp_settings & ) { apply_from_left(w); }
|
||||
|
||||
void apply_from_left(vector<T> & w);
|
||||
|
||||
void apply_from_left(X * w, lp_settings & );
|
||||
|
||||
void apply_from_left_to_X(vector<X> & w, lp_settings & );
|
||||
|
||||
virtual void set_number_of_rows(unsigned /*m*/) {}
|
||||
virtual void set_number_of_columns(unsigned /*n*/) { }
|
||||
|
||||
T get_elem(unsigned i, unsigned j) const { return m_values[i * m_n + j]; }
|
||||
|
||||
unsigned row_count() const { return m_m; }
|
||||
unsigned column_count() const { return m_n; }
|
||||
|
||||
void set_elem(unsigned i, unsigned j, const T& val) { m_values[i * m_n + j] = val; }
|
||||
|
||||
// This method pivots row i to row i0 by muliplying row i by
|
||||
// alpha and adding it to row i0.
|
||||
void pivot_row_to_row(unsigned i, const T& alpha, unsigned i0,
|
||||
const double & pivot_epsilon);
|
||||
|
||||
void swap_columns(unsigned a, unsigned b);
|
||||
|
||||
void swap_rows(unsigned a, unsigned b);
|
||||
|
||||
void multiply_row_by_constant(unsigned row, T & t);
|
||||
|
||||
};
|
||||
template <typename T, typename X>
|
||||
dense_matrix<T, X> operator* (matrix<T, X> & a, matrix<T, X> & b);
|
||||
}
|
||||
#endif
|
186
src/util/lp/dense_matrix.hpp
Normal file
186
src/util/lp/dense_matrix.hpp
Normal file
|
@ -0,0 +1,186 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lp_settings.h"
|
||||
#ifdef LEAN_DEBUG
|
||||
#include "util/vector.h"
|
||||
#include "util/lp/numeric_pair.h"
|
||||
#include "util/lp/dense_matrix.h"
|
||||
namespace lean {
|
||||
template <typename T> void print_vector(const vector<T> & t, std::ostream & out);
|
||||
template <typename T, typename X> dense_matrix<T, X>::dense_matrix(unsigned m, unsigned n) : m_m(m), m_n(n), m_values(m * n, numeric_traits<T>::zero()) {
|
||||
}
|
||||
|
||||
template <typename T, typename X> dense_matrix<T, X>&
|
||||
dense_matrix<T, X>::operator=(matrix<T, X> const & other){
|
||||
if ( this == & other)
|
||||
return *this;
|
||||
m_values = new T[m_m * m_n];
|
||||
for (unsigned i = 0; i < m_m; i ++)
|
||||
for (unsigned j = 0; j < m_n; j++)
|
||||
m_values[i * m_n + j] = other.get_elem(i, j);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, typename X> dense_matrix<T, X>&
|
||||
dense_matrix<T, X>::operator=(dense_matrix const & other){
|
||||
if ( this == & other)
|
||||
return *this;
|
||||
m_m = other.m_m;
|
||||
m_n = other.m_n;
|
||||
m_values.resize(m_m * m_n);
|
||||
for (unsigned i = 0; i < m_m; i ++)
|
||||
for (unsigned j = 0; j < m_n; j++)
|
||||
m_values[i * m_n + j] = other.get_elem(i, j);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, typename X> dense_matrix<T, X>::dense_matrix(matrix<T, X> const * other) :
|
||||
m_m(other->row_count()),
|
||||
m_n(other->column_count()) {
|
||||
m_values.resize(m_m*m_n);
|
||||
for (unsigned i = 0; i < m_m; i++)
|
||||
for (unsigned j = 0; j < m_n; j++)
|
||||
m_values[i * m_n + j] = other->get_elem(i, j);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>::apply_from_right(T * w) {
|
||||
T * t = new T[m_m];
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
T v = numeric_traits<T>::zero();
|
||||
for (int j = 0; j < m_m; j++) {
|
||||
v += w[j]* get_elem(j, i);
|
||||
}
|
||||
t[i] = v;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_m; i++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
delete [] t;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>::apply_from_right(vector <T> & w) {
|
||||
vector<T> t(m_m, numeric_traits<T>::zero());
|
||||
for (unsigned i = 0; i < m_m; i ++) {
|
||||
auto & v = t[i];
|
||||
for (unsigned j = 0; j < m_m; j++)
|
||||
v += w[j]* get_elem(j, i);
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_m; i++)
|
||||
w[i] = t[i];
|
||||
}
|
||||
|
||||
template <typename T, typename X> T* dense_matrix<T, X>::
|
||||
apply_from_left_with_different_dims(vector<T> & w) {
|
||||
T * t = new T[m_m];
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
T v = numeric_traits<T>::zero();
|
||||
for (int j = 0; j < m_n; j++) {
|
||||
v += w[j]* get_elem(i, j);
|
||||
}
|
||||
t[i] = v;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>::apply_from_left(vector<T> & w) {
|
||||
T * t = new T[m_m];
|
||||
for (unsigned i = 0; i < m_m; i ++) {
|
||||
T v = numeric_traits<T>::zero();
|
||||
for (unsigned j = 0; j < m_m; j++) {
|
||||
v += w[j]* get_elem(i, j);
|
||||
}
|
||||
t[i] = v;
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_m; i ++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
delete [] t;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>::apply_from_left(X * w, lp_settings & ) {
|
||||
T * t = new T[m_m];
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
T v = numeric_traits<T>::zero();
|
||||
for (int j = 0; j < m_m; j++) {
|
||||
v += w[j]* get_elem(i, j);
|
||||
}
|
||||
t[i] = v;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
delete [] t;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>::apply_from_left_to_X(vector<X> & w, lp_settings & ) {
|
||||
vector<X> t(m_m);
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
X v = zero_of_type<X>();
|
||||
for (int j = 0; j < m_m; j++) {
|
||||
v += w[j]* get_elem(i, j);
|
||||
}
|
||||
t[i] = v;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
}
|
||||
|
||||
// This method pivots row i to row i0 by muliplying row i by
|
||||
// alpha and adding it to row i0.
|
||||
template <typename T, typename X> void dense_matrix<T, X>::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0,
|
||||
const double & pivot_epsilon) {
|
||||
for (unsigned j = 0; j < m_n; j++) {
|
||||
m_values[i0 * m_n + j] += m_values[i * m_n + j] * alpha;
|
||||
if (fabs(m_values[i0 + m_n + j]) < pivot_epsilon) {
|
||||
m_values[i0 + m_n + j] = numeric_traits<T>::zero();;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>::swap_columns(unsigned a, unsigned b) {
|
||||
for (unsigned i = 0; i < m_m; i++) {
|
||||
T t = get_elem(i, a);
|
||||
set_elem(i, a, get_elem(i, b));
|
||||
set_elem(i, b, t);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>::swap_rows(unsigned a, unsigned b) {
|
||||
for (unsigned i = 0; i < m_n; i++) {
|
||||
T t = get_elem(a, i);
|
||||
set_elem(a, i, get_elem(b, i));
|
||||
set_elem(b, i, t);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>::multiply_row_by_constant(unsigned row, T & t) {
|
||||
for (unsigned i = 0; i < m_n; i++) {
|
||||
set_elem(row, i, t * get_elem(row, i));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
dense_matrix<T, X> operator* (matrix<T, X> & a, matrix<T, X> & b){
|
||||
lean_assert(a.column_count() == b.row_count());
|
||||
dense_matrix<T, X> ret(a.row_count(), b.column_count());
|
||||
for (unsigned i = 0; i < ret.m_m; i++)
|
||||
for (unsigned j = 0; j< ret.m_n; j++) {
|
||||
T v = numeric_traits<T>::zero();
|
||||
for (unsigned k = 0; k < a.column_count(); k ++){
|
||||
v += (a.get_elem(i, k) * b.get_elem(k, j));
|
||||
}
|
||||
ret.set_elem(i, j, v);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
24
src/util/lp/dense_matrix_instances.cpp
Normal file
24
src/util/lp/dense_matrix_instances.cpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lp_settings.h"
|
||||
#include "util/lp/dense_matrix.hpp"
|
||||
#ifdef LEAN_DEBUG
|
||||
#include "util/vector.h"
|
||||
template lean::dense_matrix<double, double> lean::operator*<double, double>(lean::matrix<double, double>&, lean::matrix<double, double>&);
|
||||
template void lean::dense_matrix<double, double>::apply_from_left(vector<double> &);
|
||||
template lean::dense_matrix<double, double>::dense_matrix(lean::matrix<double, double> const*);
|
||||
template lean::dense_matrix<double, double>::dense_matrix(unsigned int, unsigned int);
|
||||
template lean::dense_matrix<double, double>& lean::dense_matrix<double, double>::operator=(lean::dense_matrix<double, double> const&);
|
||||
template lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::dense_matrix(lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> > const*);
|
||||
template void lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left(vector<lean::mpq>&);
|
||||
template lean::dense_matrix<lean::mpq, lean::mpq> lean::operator*<lean::mpq, lean::mpq>(lean::matrix<lean::mpq, lean::mpq>&, lean::matrix<lean::mpq, lean::mpq>&);
|
||||
template lean::dense_matrix<lean::mpq, lean::mpq> & lean::dense_matrix<lean::mpq, lean::mpq>::operator=(lean::dense_matrix<lean::mpq, lean::mpq> const&);
|
||||
template lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::dense_matrix(unsigned int, unsigned int);
|
||||
template lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >& lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::operator=(lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> > const&);
|
||||
template lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> > lean::operator*<lean::mpq, lean::numeric_pair<lean::mpq> >(lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
|
||||
template void lean::dense_matrix<lean::mpq, lean::numeric_pair< lean::mpq> >::apply_from_right( vector< lean::mpq> &);
|
||||
template void lean::dense_matrix<double,double>::apply_from_right(class vector<double> &);
|
||||
template void lean::dense_matrix<lean::mpq, lean::mpq>::apply_from_left(vector<lean::mpq>&);
|
||||
#endif
|
83
src/util/lp/eta_matrix.h
Normal file
83
src/util/lp/eta_matrix.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "util/vector.h"
|
||||
#include "util/lp/tail_matrix.h"
|
||||
#include "util/lp/permutation_matrix.h"
|
||||
namespace lean {
|
||||
|
||||
// This is the sum of a unit matrix and a one-column matrix
|
||||
template <typename T, typename X>
|
||||
class eta_matrix
|
||||
: public tail_matrix<T, X> {
|
||||
#ifdef LEAN_DEBUG
|
||||
unsigned m_length;
|
||||
#endif
|
||||
unsigned m_column_index;
|
||||
public:
|
||||
sparse_vector<T> m_column_vector;
|
||||
T m_diagonal_element;
|
||||
#ifdef LEAN_DEBUG
|
||||
eta_matrix(unsigned column_index, unsigned length):
|
||||
#else
|
||||
eta_matrix(unsigned column_index):
|
||||
#endif
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
m_length(length),
|
||||
#endif
|
||||
m_column_index(column_index) {}
|
||||
|
||||
bool is_dense() const { return false; }
|
||||
|
||||
void print(std::ostream & out) {
|
||||
print_matrix(*this, out);
|
||||
}
|
||||
|
||||
bool is_unit() {
|
||||
return m_column_vector.size() == 0 && m_diagonal_element == 1;
|
||||
}
|
||||
|
||||
bool set_diagonal_element(T const & diagonal_element) {
|
||||
m_diagonal_element = diagonal_element;
|
||||
return !lp_settings::is_eps_small_general(diagonal_element, 1e-12);
|
||||
}
|
||||
|
||||
const T & get_diagonal_element() const {
|
||||
return m_diagonal_element;
|
||||
}
|
||||
|
||||
void apply_from_left(vector<X> & w, lp_settings & );
|
||||
|
||||
template <typename L>
|
||||
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings);
|
||||
|
||||
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
|
||||
apply_from_left_local(w, settings);
|
||||
}
|
||||
|
||||
|
||||
void push_back(unsigned row_index, T val ) {
|
||||
lean_assert(row_index != m_column_index);
|
||||
m_column_vector.push_back(row_index, val);
|
||||
}
|
||||
|
||||
void apply_from_right(vector<T> & w);
|
||||
void apply_from_right(indexed_vector<T> & w);
|
||||
|
||||
T get_elem(unsigned i, unsigned j) const;
|
||||
#ifdef LEAN_DEBUG
|
||||
unsigned row_count() const { return m_length; }
|
||||
unsigned column_count() const { return m_length; }
|
||||
void set_number_of_rows(unsigned m) { m_length = m; }
|
||||
void set_number_of_columns(unsigned n) { m_length = n; }
|
||||
#endif
|
||||
void divide_by_diagonal_element() {
|
||||
m_column_vector.divide(m_diagonal_element);
|
||||
}
|
||||
void conjugate_by_permutation(permutation_matrix<T, X> & p);
|
||||
};
|
||||
}
|
136
src/util/lp/eta_matrix.hpp
Normal file
136
src/util/lp/eta_matrix.hpp
Normal file
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "util/vector.h"
|
||||
#include "util/lp/eta_matrix.h"
|
||||
namespace lean {
|
||||
|
||||
// This is the sum of a unit matrix and a one-column matrix
|
||||
template <typename T, typename X>
|
||||
void eta_matrix<T, X>::apply_from_left(vector<X> & w, lp_settings & ) {
|
||||
auto & w_at_column_index = w[m_column_index];
|
||||
for (auto & it : m_column_vector.m_data) {
|
||||
w[it.first] += w_at_column_index * it.second;
|
||||
}
|
||||
w_at_column_index /= m_diagonal_element;
|
||||
}
|
||||
template <typename T, typename X>
|
||||
template <typename L>
|
||||
void eta_matrix<T, X>::
|
||||
apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) {
|
||||
const L w_at_column_index = w[m_column_index];
|
||||
if (is_zero(w_at_column_index)) return;
|
||||
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_column_index] /= m_diagonal_element)) {
|
||||
w[m_column_index] = zero_of_type<L>();
|
||||
w.erase_from_index(m_column_index);
|
||||
}
|
||||
|
||||
for (auto & it : m_column_vector.m_data) {
|
||||
unsigned i = it.first;
|
||||
if (is_zero(w[i])) {
|
||||
L v = w[i] = w_at_column_index * it.second;
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
|
||||
w[i] = zero_of_type<L>();
|
||||
continue;
|
||||
}
|
||||
w.m_index.push_back(i);
|
||||
} else {
|
||||
L v = w[i] += w_at_column_index * it.second;
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
|
||||
w[i] = zero_of_type<L>();
|
||||
w.erase_from_index(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void eta_matrix<T, X>::apply_from_right(vector<T> & w) {
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T, X> deb(*this);
|
||||
// auto clone_w = clone_vector<T>(w, get_number_of_rows());
|
||||
// deb.apply_from_right(clone_w);
|
||||
#endif
|
||||
T t = w[m_column_index] / m_diagonal_element;
|
||||
for (auto & it : m_column_vector.m_data) {
|
||||
t += w[it.first] * it.second;
|
||||
}
|
||||
w[m_column_index] = t;
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(clone_w, w, get_number_of_rows()));
|
||||
// delete clone_w;
|
||||
#endif
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void eta_matrix<T, X>::apply_from_right(indexed_vector<T> & w) {
|
||||
if (w.m_index.size() == 0)
|
||||
return;
|
||||
#ifdef LEAN_DEBUG
|
||||
// vector<T> wcopy(w.m_data);
|
||||
// apply_from_right(wcopy);
|
||||
#endif
|
||||
T & t = w[m_column_index];
|
||||
t /= m_diagonal_element;
|
||||
bool was_in_index = (!numeric_traits<T>::is_zero(t));
|
||||
|
||||
for (auto & it : m_column_vector.m_data) {
|
||||
t += w[it.first] * it.second;
|
||||
}
|
||||
|
||||
if (numeric_traits<T>::precise() ) {
|
||||
if (!numeric_traits<T>::is_zero(t)) {
|
||||
if (!was_in_index)
|
||||
w.m_index.push_back(m_column_index);
|
||||
} else {
|
||||
if (was_in_index)
|
||||
w.erase_from_index(m_column_index);
|
||||
}
|
||||
} else {
|
||||
if (!lp_settings::is_eps_small_general(t, 1e-14)) {
|
||||
if (!was_in_index)
|
||||
w.m_index.push_back(m_column_index);
|
||||
} else {
|
||||
if (was_in_index)
|
||||
w.erase_from_index(m_column_index);
|
||||
t = zero_of_type<T>();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(w.is_OK());
|
||||
// lean_assert(vectors_are_equal<T>(wcopy, w.m_data));
|
||||
#endif
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X>
|
||||
T eta_matrix<T, X>::get_elem(unsigned i, unsigned j) const {
|
||||
if (j == m_column_index){
|
||||
if (i == j) {
|
||||
return 1 / m_diagonal_element;
|
||||
}
|
||||
return m_column_vector[i];
|
||||
}
|
||||
|
||||
return i == j ? numeric_traits<T>::one() : numeric_traits<T>::zero();
|
||||
}
|
||||
#endif
|
||||
template <typename T, typename X>
|
||||
void eta_matrix<T, X>::conjugate_by_permutation(permutation_matrix<T, X> & p) {
|
||||
// this = p * this * p(-1)
|
||||
#ifdef LEAN_DEBUG
|
||||
// auto rev = p.get_reverse();
|
||||
// auto deb = ((*this) * rev);
|
||||
// deb = p * deb;
|
||||
#endif
|
||||
m_column_index = p.get_rev(m_column_index);
|
||||
for (auto & pair : m_column_vector.m_data) {
|
||||
pair.first = p.get_rev(pair.first);
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(deb == *this);
|
||||
#endif
|
||||
}
|
||||
}
|
28
src/util/lp/eta_matrix_instances.cpp
Normal file
28
src/util/lp/eta_matrix_instances.cpp
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include <memory>
|
||||
#include "util/vector.h"
|
||||
#include "util/lp/numeric_pair.h"
|
||||
#include "util/lp/eta_matrix.hpp"
|
||||
#ifdef LEAN_DEBUG
|
||||
template double lean::eta_matrix<double, double>::get_elem(unsigned int, unsigned int) const;
|
||||
template lean::mpq lean::eta_matrix<lean::mpq, lean::mpq>::get_elem(unsigned int, unsigned int) const;
|
||||
template lean::mpq lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::get_elem(unsigned int, unsigned int) const;
|
||||
#endif
|
||||
template void lean::eta_matrix<double, double>::apply_from_left(vector<double>&, lean::lp_settings&);
|
||||
template void lean::eta_matrix<double, double>::apply_from_right(vector<double>&);
|
||||
template void lean::eta_matrix<double, double>::conjugate_by_permutation(lean::permutation_matrix<double, double>&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_left(vector<lean::mpq>&, lean::lp_settings&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_right(vector<lean::mpq>&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::mpq>::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::mpq>&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left(vector<lean::numeric_pair<lean::mpq> >&, lean::lp_settings&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_right(vector<lean::mpq>&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
|
||||
template void lean::eta_matrix<double, double>::apply_from_left_local<double>(lean::indexed_vector<double>&, lean::lp_settings&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_left_local<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left_local<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_right(lean::indexed_vector<lean::mpq>&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_right(lean::indexed_vector<lean::mpq>&);
|
||||
template void lean::eta_matrix<double, double>::apply_from_right(lean::indexed_vector<double>&);
|
39
src/util/lp/hash_helper.h
Normal file
39
src/util/lp/hash_helper.h
Normal file
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include "util/numerics/mpq.h"
|
||||
#ifdef __CLANG__
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wmismatched-tags"
|
||||
#endif
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<lean::mpq> {
|
||||
inline size_t operator()(const lean::mpq & v) const {
|
||||
return v.hash();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void hash_combine(std::size_t & seed, const T & v) {
|
||||
seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<typename S, typename T> struct hash<pair<S, T>> {
|
||||
inline size_t operator()(const pair<S, T> & v) const {
|
||||
size_t seed = 0;
|
||||
hash_combine(seed, v.first);
|
||||
hash_combine(seed, v.second);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
}
|
||||
#ifdef __CLANG__
|
||||
#pragma clang diagnostic pop
|
||||
#endif
|
42
src/util/lp/implied_bound.h
Normal file
42
src/util/lp/implied_bound.h
Normal file
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/lp/lp_settings.h"
|
||||
#include "util/lp/lar_constraints.h"
|
||||
namespace lean {
|
||||
struct implied_bound {
|
||||
mpq m_bound;
|
||||
unsigned m_j; // the column for which the bound has been found
|
||||
bool m_is_low_bound;
|
||||
bool m_coeff_before_j_is_pos;
|
||||
unsigned m_row_or_term_index;
|
||||
bool m_strict;
|
||||
|
||||
lconstraint_kind kind() const {
|
||||
lconstraint_kind k = m_is_low_bound? GE : LE;
|
||||
if (m_strict)
|
||||
k = static_cast<lconstraint_kind>(k / 2);
|
||||
return k;
|
||||
}
|
||||
bool operator==(const implied_bound & o) const {
|
||||
return m_j == o.m_j && m_is_low_bound == o.m_is_low_bound && m_bound == o.m_bound &&
|
||||
m_coeff_before_j_is_pos == o.m_coeff_before_j_is_pos &&
|
||||
m_row_or_term_index == o.m_row_or_term_index && m_strict == o.m_strict;
|
||||
}
|
||||
implied_bound(){}
|
||||
implied_bound(const mpq & a,
|
||||
unsigned j,
|
||||
bool low_bound,
|
||||
bool coeff_before_j_is_pos,
|
||||
unsigned row_or_term_index,
|
||||
bool strict):
|
||||
m_bound(a),
|
||||
m_j(j),
|
||||
m_is_low_bound(low_bound),
|
||||
m_coeff_before_j_is_pos(coeff_before_j_is_pos),
|
||||
m_row_or_term_index(row_or_term_index),
|
||||
m_strict(strict) {}
|
||||
};
|
||||
}
|
55
src/util/lp/indexed_value.h
Normal file
55
src/util/lp/indexed_value.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
class indexed_value {
|
||||
public:
|
||||
T m_value;
|
||||
// the idea is that m_index for a row element gives its column, and for a column element its row
|
||||
unsigned m_index;
|
||||
// m_other point is the offset of the corresponding element in its vector : for a row element it point to the column element offset,
|
||||
// for a column element it points to the row element offset
|
||||
unsigned m_other;
|
||||
indexed_value() {}
|
||||
indexed_value(T v, unsigned i) : m_value(v), m_index(i) {}
|
||||
indexed_value(T v, unsigned i, unsigned other) :
|
||||
m_value(v), m_index(i), m_other(other) {
|
||||
}
|
||||
|
||||
indexed_value(const indexed_value & iv) {
|
||||
m_value = iv.m_value;
|
||||
m_index = iv.m_index;
|
||||
m_other = iv.m_other;
|
||||
}
|
||||
|
||||
indexed_value & operator=(const indexed_value & right_side) {
|
||||
m_value = right_side.m_value;
|
||||
m_index = right_side.m_index;
|
||||
m_other = right_side.m_other;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const T & value() const {
|
||||
return m_value;
|
||||
}
|
||||
|
||||
void set_value(T val) {
|
||||
m_value = val;
|
||||
}
|
||||
};
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename X>
|
||||
bool check_vector_for_small_values(indexed_vector<X> & w, lp_settings & settings) {
|
||||
for (unsigned i : w.m_index) {
|
||||
const X & v = w[i];
|
||||
if ((!is_zero(v)) && settings.abs_val_is_smaller_than_drop_tolerance(v))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
169
src/util/lp/indexed_vector.h
Normal file
169
src/util/lp/indexed_vector.h
Normal file
|
@ -0,0 +1,169 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "util/vector.h"
|
||||
#include "util/debug.h"
|
||||
#include <string>
|
||||
#include <iomanip>
|
||||
#include "util/lp/lp_utils.h"
|
||||
#include "util/lp/lp_settings.h"
|
||||
#include <unordered_set>
|
||||
namespace lean {
|
||||
|
||||
template <typename T> void print_vector(const vector<T> & t, std::ostream & out);
|
||||
template <typename T> void print_vector(const buffer<T> & t, std::ostream & out);
|
||||
template <typename T> void print_sparse_vector(const vector<T> & t, std::ostream & out);
|
||||
|
||||
void print_vector(const vector<mpq> & t, std::ostream & out);
|
||||
template <typename T>
|
||||
class indexed_vector {
|
||||
public:
|
||||
// m_index points to non-zero elements of m_data
|
||||
vector<T> m_data;
|
||||
vector<unsigned> m_index;
|
||||
indexed_vector(unsigned data_size) {
|
||||
m_data.resize(data_size, numeric_traits<T>::zero());
|
||||
}
|
||||
|
||||
indexed_vector& operator=(const indexed_vector<T>& y) {
|
||||
for (unsigned i: m_index)
|
||||
m_data[i] = zero_of_type<T>();
|
||||
|
||||
m_index = y.m_index;
|
||||
|
||||
m_data.resize(y.data_size());
|
||||
for (unsigned i : m_index)
|
||||
m_data[i] = y[i];
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const indexed_vector<T>& y) const {
|
||||
std::unordered_set<unsigned> y_index;
|
||||
for (unsigned i : y.m_index)
|
||||
y_index.insert(i);
|
||||
|
||||
std::unordered_set<unsigned> this_index;
|
||||
for (unsigned i : m_index)
|
||||
this_index.insert(i);
|
||||
|
||||
for (unsigned i : y.m_index) {
|
||||
if (this_index.find(i) == this_index.end())
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned i : m_index) {
|
||||
if (y_index.find(i) == y_index.end())
|
||||
return false;
|
||||
}
|
||||
|
||||
return vectors_are_equal(m_data, m_data);
|
||||
|
||||
}
|
||||
|
||||
indexed_vector() {}
|
||||
|
||||
void resize(unsigned data_size);
|
||||
unsigned data_size() const {
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
unsigned size() {
|
||||
return m_index.size();
|
||||
}
|
||||
|
||||
void set_value(const T& value, unsigned index);
|
||||
void set_value_as_in_dictionary(unsigned index) {
|
||||
lean_assert(index < m_data.size());
|
||||
T & loc = m_data[index];
|
||||
if (is_zero(loc)) {
|
||||
m_index.push_back(index);
|
||||
loc = one_of_type<T>(); // use as a characteristic function
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void clear();
|
||||
void clear_all();
|
||||
const T& operator[] (unsigned i) const {
|
||||
return m_data[i];
|
||||
}
|
||||
|
||||
T& operator[] (unsigned i) {
|
||||
return m_data[i];
|
||||
}
|
||||
|
||||
void clean_up() {
|
||||
#if 0==1
|
||||
for (unsigned k = 0; k < m_index.size(); k++) {
|
||||
unsigned i = m_index[k];
|
||||
T & v = m_data[i];
|
||||
if (lp_settings::is_eps_small_general(v, 1e-14)) {
|
||||
v = zero_of_type<T>();
|
||||
m_index.erase(m_index.begin() + k--);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
vector<unsigned> index_copy;
|
||||
for (unsigned i : m_index) {
|
||||
T & v = m_data[i];
|
||||
if (!lp_settings::is_eps_small_general(v, 1e-14)) {
|
||||
index_copy.push_back(i);
|
||||
} else if (!numeric_traits<T>::is_zero(v)) {
|
||||
v = zero_of_type<T>();
|
||||
}
|
||||
}
|
||||
m_index = index_copy;
|
||||
}
|
||||
|
||||
|
||||
void erase_from_index(unsigned j);
|
||||
|
||||
void add_value_at_index_with_drop_tolerance(unsigned j, const T& val_to_add) {
|
||||
T & v = m_data[j];
|
||||
bool was_zero = is_zero(v);
|
||||
v += val_to_add;
|
||||
if (lp_settings::is_eps_small_general(v, 1e-14)) {
|
||||
v = zero_of_type<T>();
|
||||
if (!was_zero) {
|
||||
erase_from_index(j);
|
||||
}
|
||||
} else {
|
||||
if (was_zero)
|
||||
m_index.push_back(j);
|
||||
}
|
||||
}
|
||||
|
||||
void add_value_at_index(unsigned j, const T& val_to_add) {
|
||||
T & v = m_data[j];
|
||||
bool was_zero = is_zero(v);
|
||||
v += val_to_add;
|
||||
if (is_zero(v)) {
|
||||
if (!was_zero)
|
||||
erase_from_index(j);
|
||||
} else {
|
||||
if (was_zero)
|
||||
m_index.push_back(j);
|
||||
}
|
||||
}
|
||||
|
||||
void restore_index_and_clean_from_data() {
|
||||
m_index.resize(0);
|
||||
for (unsigned i = 0; i < m_data.size(); i++) {
|
||||
T & v = m_data[i];
|
||||
if (lp_settings::is_eps_small_general(v, 1e-14)) {
|
||||
v = zero_of_type<T>();
|
||||
} else {
|
||||
m_index.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
bool is_OK() const;
|
||||
void print(std::ostream & out);
|
||||
#endif
|
||||
};
|
||||
}
|
110
src/util/lp/indexed_vector.hpp
Normal file
110
src/util/lp/indexed_vector.hpp
Normal file
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/vector.h"
|
||||
#include "util/lp/indexed_vector.h"
|
||||
#include "util/lp/lp_settings.h"
|
||||
namespace lean {
|
||||
|
||||
template <typename T>
|
||||
void print_vector(const vector<T> & t, std::ostream & out) {
|
||||
for (unsigned i = 0; i < t.size(); i++)
|
||||
out << t[i] << " ";
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
void print_vector(const buffer<T> & t, std::ostream & out) {
|
||||
for (unsigned i = 0; i < t.size(); i++)
|
||||
out << t[i] << " ";
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void print_sparse_vector(const vector<T> & t, std::ostream & out) {
|
||||
for (unsigned i = 0; i < t.size(); i++) {
|
||||
if (is_zero(t[i]))continue;
|
||||
out << "[" << i << "] = " << t[i] << ", ";
|
||||
}
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
void print_vector(const vector<mpq> & t, std::ostream & out) {
|
||||
for (unsigned i = 0; i < t.size(); i++)
|
||||
out << t[i].get_double() << std::setprecision(3) << " ";
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void indexed_vector<T>::resize(unsigned data_size) {
|
||||
clear();
|
||||
m_data.resize(data_size, numeric_traits<T>::zero());
|
||||
lean_assert(is_OK());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void indexed_vector<T>::set_value(const T& value, unsigned index) {
|
||||
m_data[index] = value;
|
||||
lean_assert(std::find(m_index.begin(), m_index.end(), index) == m_index.end());
|
||||
m_index.push_back(index);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void indexed_vector<T>::clear() {
|
||||
for (unsigned i : m_index)
|
||||
m_data[i] = numeric_traits<T>::zero();
|
||||
m_index.resize(0);
|
||||
}
|
||||
template <typename T>
|
||||
void indexed_vector<T>::clear_all() {
|
||||
unsigned i = m_data.size();
|
||||
while (i--) m_data[i] = numeric_traits<T>::zero();
|
||||
m_index.resize(0);
|
||||
}
|
||||
template <typename T>
|
||||
void indexed_vector<T>::erase_from_index(unsigned j) {
|
||||
auto it = std::find(m_index.begin(), m_index.end(), j);
|
||||
if (it != m_index.end())
|
||||
m_index.erase(it);
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T>
|
||||
bool indexed_vector<T>::is_OK() const {
|
||||
return true;
|
||||
const double drop_eps = 1e-14;
|
||||
for (unsigned i = 0; i < m_data.size(); i++) {
|
||||
if (!is_zero(m_data[i]) && lp_settings::is_eps_small_general(m_data[i], drop_eps)) {
|
||||
return false;
|
||||
}
|
||||
if (lp_settings::is_eps_small_general(m_data[i], drop_eps) != (std::find(m_index.begin(), m_index.end(), i) == m_index.end())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::unordered_set<unsigned> s;
|
||||
for (unsigned i : m_index) {
|
||||
//no duplicates!!!
|
||||
if (s.find(i) != s.end())
|
||||
return false;
|
||||
s.insert(i);
|
||||
if (i >= m_data.size())
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
template <typename T>
|
||||
void indexed_vector<T>::print(std::ostream & out) {
|
||||
out << "m_index " << std::endl;
|
||||
for (unsigned i = 0; i < m_index.size(); i++) {
|
||||
out << m_index[i] << " ";
|
||||
}
|
||||
out << std::endl;
|
||||
print_vector(m_data, out);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
36
src/util/lp/indexed_vector_instances.cpp
Normal file
36
src/util/lp/indexed_vector_instances.cpp
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/vector.h"
|
||||
#include "util/lp/indexed_vector.hpp"
|
||||
namespace lean {
|
||||
template void indexed_vector<double>::clear();
|
||||
template void indexed_vector<double>::clear_all();
|
||||
template void indexed_vector<double>::erase_from_index(unsigned int);
|
||||
template void indexed_vector<double>::set_value(const double&, unsigned int);
|
||||
template void indexed_vector<mpq>::clear();
|
||||
template void indexed_vector<unsigned>::clear();
|
||||
template void indexed_vector<mpq>::clear_all();
|
||||
template void indexed_vector<mpq>::erase_from_index(unsigned int);
|
||||
template void indexed_vector<mpq>::resize(unsigned int);
|
||||
template void indexed_vector<unsigned>::resize(unsigned int);
|
||||
template void indexed_vector<mpq>::set_value(const mpq&, unsigned int);
|
||||
template void indexed_vector<unsigned>::set_value(const unsigned&, unsigned int);
|
||||
#ifdef LEAN_DEBUG
|
||||
template bool indexed_vector<double>::is_OK() const;
|
||||
template bool indexed_vector<mpq>::is_OK() const;
|
||||
template bool indexed_vector<lean::numeric_pair<mpq> >::is_OK() const;
|
||||
template void lean::indexed_vector< lean::mpq>::print(std::basic_ostream<char,struct std::char_traits<char> > &);
|
||||
template void lean::indexed_vector<double>::print(std::basic_ostream<char,struct std::char_traits<char> > &);
|
||||
template void lean::indexed_vector<lean::numeric_pair<lean::mpq> >::print(std::ostream&);
|
||||
#endif
|
||||
}
|
||||
template void lean::print_vector<double>(vector<double> const&, std::ostream&);
|
||||
template void lean::print_vector<unsigned int>(vector<unsigned int> const&, std::ostream&);
|
||||
template void lean::print_vector<std::string>(vector<std::string> const&, std::ostream&);
|
||||
template void lean::print_vector<lean::numeric_pair<lean::mpq> >(vector<lean::numeric_pair<lean::mpq>> const&, std::ostream&);
|
||||
template void lean::indexed_vector<double>::resize(unsigned int);
|
||||
template void lean::print_vector< lean::mpq>(vector< lean::mpq> const &, std::basic_ostream<char, std::char_traits<char> > &);
|
||||
template void lean::print_vector<std::pair<lean::mpq, unsigned int> >(vector<std::pair<lean::mpq, unsigned int>> const&, std::ostream&);
|
||||
template void lean::indexed_vector<lean::numeric_pair<lean::mpq> >::erase_from_index(unsigned int);
|
576
src/util/lp/init_lar_solver.h
Normal file
576
src/util/lp/init_lar_solver.h
Normal file
|
@ -0,0 +1,576 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
// here we are inside lean::lar_solver class
|
||||
|
||||
bool strategy_is_undecided() const {
|
||||
return m_settings.simplex_strategy() == simplex_strategy_enum::undecided;
|
||||
}
|
||||
|
||||
var_index add_var(unsigned ext_j) {
|
||||
var_index i;
|
||||
lean_assert (ext_j < m_terms_start_index);
|
||||
|
||||
if (ext_j >= m_terms_start_index)
|
||||
throw 0; // todo : what is the right way to exit?
|
||||
|
||||
if (try_get_val(m_ext_vars_to_columns, ext_j, i)) {
|
||||
return i;
|
||||
}
|
||||
lean_assert(m_vars_to_ul_pairs.size() == A_r().column_count());
|
||||
i = A_r().column_count();
|
||||
m_vars_to_ul_pairs.push_back (ul_pair(static_cast<unsigned>(-1)));
|
||||
add_non_basic_var_to_core_fields(ext_j);
|
||||
lean_assert(sizes_are_correct());
|
||||
return i;
|
||||
}
|
||||
|
||||
void register_new_ext_var_index(unsigned ext_v) {
|
||||
lean_assert(!contains(m_ext_vars_to_columns, ext_v));
|
||||
unsigned j = static_cast<unsigned>(m_ext_vars_to_columns.size());
|
||||
m_ext_vars_to_columns[ext_v] = j;
|
||||
lean_assert(m_columns_to_ext_vars_or_term_indices.size() == j);
|
||||
m_columns_to_ext_vars_or_term_indices.push_back(ext_v);
|
||||
}
|
||||
|
||||
void add_non_basic_var_to_core_fields(unsigned ext_j) {
|
||||
register_new_ext_var_index(ext_j);
|
||||
m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column);
|
||||
m_columns_with_changed_bound.increase_size_by_one();
|
||||
add_new_var_to_core_fields_for_mpq(false);
|
||||
if (use_lu())
|
||||
add_new_var_to_core_fields_for_doubles(false);
|
||||
}
|
||||
|
||||
void add_new_var_to_core_fields_for_doubles(bool register_in_basis) {
|
||||
unsigned j = A_d().column_count();
|
||||
A_d().add_column();
|
||||
lean_assert(m_mpq_lar_core_solver.m_d_x.size() == j);
|
||||
// lean_assert(m_mpq_lar_core_solver.m_d_low_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later
|
||||
m_mpq_lar_core_solver.m_d_x.resize(j + 1 );
|
||||
m_mpq_lar_core_solver.m_d_low_bounds.resize(j + 1);
|
||||
m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1);
|
||||
lean_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method
|
||||
if (register_in_basis) {
|
||||
A_d().add_row();
|
||||
m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size());
|
||||
m_mpq_lar_core_solver.m_d_basis.push_back(j);
|
||||
}else {
|
||||
m_mpq_lar_core_solver.m_d_heading.push_back(- static_cast<int>(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1);
|
||||
m_mpq_lar_core_solver.m_d_nbasis.push_back(j);
|
||||
}
|
||||
}
|
||||
|
||||
void add_new_var_to_core_fields_for_mpq(bool register_in_basis) {
|
||||
unsigned j = A_r().column_count();
|
||||
A_r().add_column();
|
||||
lean_assert(m_mpq_lar_core_solver.m_r_x.size() == j);
|
||||
// lean_assert(m_mpq_lar_core_solver.m_r_low_bounds.size() == j && m_mpq_lar_core_solver.m_r_upper_bounds.size() == j); // restore later
|
||||
m_mpq_lar_core_solver.m_r_x.resize(j + 1);
|
||||
m_mpq_lar_core_solver.m_r_low_bounds.increase_size_by_one();
|
||||
m_mpq_lar_core_solver.m_r_upper_bounds.increase_size_by_one();
|
||||
m_mpq_lar_core_solver.m_r_solver.m_inf_set.increase_size_by_one();
|
||||
m_mpq_lar_core_solver.m_r_solver.m_costs.resize(j + 1);
|
||||
m_mpq_lar_core_solver.m_r_solver.m_d.resize(j + 1);
|
||||
lean_assert(m_mpq_lar_core_solver.m_r_heading.size() == j); // as A().column_count() on the entry to the method
|
||||
if (register_in_basis) {
|
||||
A_r().add_row();
|
||||
m_mpq_lar_core_solver.m_r_heading.push_back(m_mpq_lar_core_solver.m_r_basis.size());
|
||||
m_mpq_lar_core_solver.m_r_basis.push_back(j);
|
||||
if (m_settings.bound_propagation())
|
||||
m_rows_with_changed_bounds.insert(A_r().row_count() - 1);
|
||||
} else {
|
||||
m_mpq_lar_core_solver.m_r_heading.push_back(- static_cast<int>(m_mpq_lar_core_solver.m_r_nbasis.size()) - 1);
|
||||
m_mpq_lar_core_solver.m_r_nbasis.push_back(j);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var_index add_term_undecided(const vector<std::pair<mpq, var_index>> & coeffs,
|
||||
const mpq &m_v) {
|
||||
m_terms.push_back(new lar_term(coeffs, m_v));
|
||||
m_orig_terms.push_back(new lar_term(coeffs, m_v));
|
||||
return m_terms_start_index + m_terms.size() - 1;
|
||||
}
|
||||
|
||||
// terms
|
||||
var_index add_term(const vector<std::pair<mpq, var_index>> & coeffs,
|
||||
const mpq &m_v) {
|
||||
if (strategy_is_undecided())
|
||||
return add_term_undecided(coeffs, m_v);
|
||||
|
||||
m_terms.push_back(new lar_term(coeffs, m_v));
|
||||
m_orig_terms.push_back(new lar_term(coeffs, m_v));
|
||||
unsigned adjusted_term_index = m_terms.size() - 1;
|
||||
var_index ret = m_terms_start_index + adjusted_term_index;
|
||||
if (use_tableau() && !coeffs.empty()) {
|
||||
add_row_for_term(m_orig_terms.back(), ret);
|
||||
if (m_settings.bound_propagation())
|
||||
m_rows_with_changed_bounds.insert(A_r().row_count() - 1);
|
||||
}
|
||||
lean_assert(m_ext_vars_to_columns.size() == A_r().column_count());
|
||||
return ret;
|
||||
}
|
||||
|
||||
void add_row_for_term(const lar_term * term, unsigned term_ext_index) {
|
||||
lean_assert(sizes_are_correct());
|
||||
add_row_from_term_no_constraint(term, term_ext_index);
|
||||
lean_assert(sizes_are_correct());
|
||||
}
|
||||
|
||||
void add_row_from_term_no_constraint(const lar_term * term, unsigned term_ext_index) {
|
||||
register_new_ext_var_index(term_ext_index);
|
||||
// j will be a new variable
|
||||
unsigned j = A_r().column_count();
|
||||
ul_pair ul(j);
|
||||
m_vars_to_ul_pairs.push_back(ul);
|
||||
add_basic_var_to_core_fields();
|
||||
if (use_tableau()) {
|
||||
auto it = iterator_on_term_with_basis_var(*term, j);
|
||||
A_r().fill_last_row_with_pivoting(it,
|
||||
m_mpq_lar_core_solver.m_r_solver.m_basis_heading);
|
||||
m_mpq_lar_core_solver.m_r_solver.m_b.resize(A_r().column_count(), zero_of_type<mpq>());
|
||||
} else {
|
||||
fill_last_row_of_A_r(A_r(), term);
|
||||
}
|
||||
m_mpq_lar_core_solver.m_r_x[j] = get_basic_var_value_from_row_directly(A_r().row_count() - 1);
|
||||
if (use_lu())
|
||||
fill_last_row_of_A_d(A_d(), term);
|
||||
}
|
||||
|
||||
void add_basic_var_to_core_fields() {
|
||||
bool use_lu = m_mpq_lar_core_solver.need_to_presolve_with_double_solver();
|
||||
lean_assert(!use_lu || A_r().column_count() == A_d().column_count());
|
||||
m_mpq_lar_core_solver.m_column_types.push_back(column_type::free_column);
|
||||
m_columns_with_changed_bound.increase_size_by_one();
|
||||
m_rows_with_changed_bounds.increase_size_by_one();
|
||||
add_new_var_to_core_fields_for_mpq(true);
|
||||
if (use_lu)
|
||||
add_new_var_to_core_fields_for_doubles(true);
|
||||
}
|
||||
|
||||
constraint_index add_var_bound(var_index j, lconstraint_kind kind, const mpq & right_side) {
|
||||
constraint_index ci = m_constraints.size();
|
||||
if (!is_term(j)) { // j is a var
|
||||
auto vc = new lar_var_constraint(j, kind, right_side);
|
||||
m_constraints.push_back(vc);
|
||||
update_column_type_and_bound(j, kind, right_side, ci);
|
||||
} else {
|
||||
add_var_bound_on_constraint_for_term(j, kind, right_side, ci);
|
||||
}
|
||||
lean_assert(sizes_are_correct());
|
||||
return ci;
|
||||
}
|
||||
|
||||
void update_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_index) {
|
||||
switch(m_mpq_lar_core_solver.m_column_types[j]) {
|
||||
case column_type::free_column:
|
||||
update_free_column_type_and_bound(j, kind, right_side, constr_index);
|
||||
break;
|
||||
case column_type::boxed:
|
||||
update_boxed_column_type_and_bound(j, kind, right_side, constr_index);
|
||||
break;
|
||||
case column_type::low_bound:
|
||||
update_low_bound_column_type_and_bound(j, kind, right_side, constr_index);
|
||||
break;
|
||||
case column_type::upper_bound:
|
||||
update_upper_bound_column_type_and_bound(j, kind, right_side, constr_index);
|
||||
break;
|
||||
case column_type::fixed:
|
||||
update_fixed_column_type_and_bound(j, kind, right_side, constr_index);
|
||||
break;
|
||||
default:
|
||||
lean_assert(false); // cannot be here
|
||||
}
|
||||
}
|
||||
|
||||
void add_var_bound_on_constraint_for_term(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) {
|
||||
lean_assert(is_term(j));
|
||||
unsigned adjusted_term_index = adjust_term_index(j);
|
||||
unsigned term_j;
|
||||
if (try_get_val(m_ext_vars_to_columns, j, term_j)) {
|
||||
mpq rs = right_side - m_orig_terms[adjusted_term_index]->m_v;
|
||||
m_constraints.push_back(new lar_term_constraint(m_orig_terms[adjusted_term_index], kind, right_side));
|
||||
update_column_type_and_bound(term_j, kind, rs, ci);
|
||||
}
|
||||
else {
|
||||
add_constraint_from_term_and_create_new_column_row(j, m_orig_terms[adjusted_term_index], kind, right_side);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void add_constraint_from_term_and_create_new_column_row(unsigned term_j, const lar_term* term,
|
||||
lconstraint_kind kind, const mpq & right_side) {
|
||||
|
||||
add_row_from_term_no_constraint(term, term_j);
|
||||
unsigned j = A_r().column_count() - 1;
|
||||
update_column_type_and_bound(j, kind, right_side - term->m_v, m_constraints.size());
|
||||
m_constraints.push_back(new lar_term_constraint(term, kind, right_side));
|
||||
lean_assert(A_r().column_count() == m_mpq_lar_core_solver.m_r_solver.m_costs.size());
|
||||
}
|
||||
|
||||
void decide_on_strategy_and_adjust_initial_state() {
|
||||
lean_assert(strategy_is_undecided());
|
||||
if (m_vars_to_ul_pairs.size() > m_settings.column_number_threshold_for_using_lu_in_lar_solver) {
|
||||
m_settings.simplex_strategy() = simplex_strategy_enum::lu;
|
||||
} else {
|
||||
m_settings.simplex_strategy() = simplex_strategy_enum::tableau_rows; // todo: when to switch to tableau_costs?
|
||||
}
|
||||
adjust_initial_state();
|
||||
}
|
||||
|
||||
void adjust_initial_state() {
|
||||
switch (m_settings.simplex_strategy()) {
|
||||
case simplex_strategy_enum::lu:
|
||||
adjust_initial_state_for_lu();
|
||||
break;
|
||||
case simplex_strategy_enum::tableau_rows:
|
||||
adjust_initial_state_for_tableau_rows();
|
||||
break;
|
||||
case simplex_strategy_enum::tableau_costs:
|
||||
lean_assert(false); // not implemented
|
||||
case simplex_strategy_enum::undecided:
|
||||
adjust_initial_state_for_tableau_rows();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void adjust_initial_state_for_lu() {
|
||||
copy_from_mpq_matrix(A_d());
|
||||
unsigned n = A_d().column_count();
|
||||
m_mpq_lar_core_solver.m_d_x.resize(n);
|
||||
m_mpq_lar_core_solver.m_d_low_bounds.resize(n);
|
||||
m_mpq_lar_core_solver.m_d_upper_bounds.resize(n);
|
||||
m_mpq_lar_core_solver.m_d_heading = m_mpq_lar_core_solver.m_r_heading;
|
||||
m_mpq_lar_core_solver.m_d_basis = m_mpq_lar_core_solver.m_r_basis;
|
||||
|
||||
/*
|
||||
unsigned j = A_d().column_count();
|
||||
A_d().add_column();
|
||||
lean_assert(m_mpq_lar_core_solver.m_d_x.size() == j);
|
||||
// lean_assert(m_mpq_lar_core_solver.m_d_low_bounds.size() == j && m_mpq_lar_core_solver.m_d_upper_bounds.size() == j); // restore later
|
||||
m_mpq_lar_core_solver.m_d_x.resize(j + 1 );
|
||||
m_mpq_lar_core_solver.m_d_low_bounds.resize(j + 1);
|
||||
m_mpq_lar_core_solver.m_d_upper_bounds.resize(j + 1);
|
||||
lean_assert(m_mpq_lar_core_solver.m_d_heading.size() == j); // as A().column_count() on the entry to the method
|
||||
if (register_in_basis) {
|
||||
A_d().add_row();
|
||||
m_mpq_lar_core_solver.m_d_heading.push_back(m_mpq_lar_core_solver.m_d_basis.size());
|
||||
m_mpq_lar_core_solver.m_d_basis.push_back(j);
|
||||
}else {
|
||||
m_mpq_lar_core_solver.m_d_heading.push_back(- static_cast<int>(m_mpq_lar_core_solver.m_d_nbasis.size()) - 1);
|
||||
m_mpq_lar_core_solver.m_d_nbasis.push_back(j);
|
||||
}*/
|
||||
}
|
||||
|
||||
void adjust_initial_state_for_tableau_rows() {
|
||||
for (unsigned j = 0; j < m_terms.size(); j++) {
|
||||
if (contains(m_ext_vars_to_columns, j + m_terms_start_index))
|
||||
continue;
|
||||
add_row_from_term_no_constraint(m_terms[j], j + m_terms_start_index);
|
||||
}
|
||||
}
|
||||
|
||||
// this fills the last row of A_d and sets the basis column: -1 in the last column of the row
|
||||
void fill_last_row_of_A_d(static_matrix<double, double> & A, const lar_term* ls) {
|
||||
lean_assert(A.row_count() > 0);
|
||||
lean_assert(A.column_count() > 0);
|
||||
unsigned last_row = A.row_count() - 1;
|
||||
lean_assert(A.m_rows[last_row].empty());
|
||||
|
||||
for (auto & t : ls->m_coeffs) {
|
||||
lean_assert(!is_zero(t.second));
|
||||
var_index j = t.first;
|
||||
A.set(last_row, j, - t.second.get_double());
|
||||
}
|
||||
|
||||
unsigned basis_j = A.column_count() - 1;
|
||||
A.set(last_row, basis_j, - 1 );
|
||||
}
|
||||
|
||||
void update_free_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index constr_ind) {
|
||||
mpq y_of_bound(0);
|
||||
switch (kind) {
|
||||
case LT:
|
||||
y_of_bound = -1;
|
||||
case LE:
|
||||
m_mpq_lar_core_solver.m_column_types[j] = column_type::upper_bound;
|
||||
lean_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound);
|
||||
lean_assert(m_mpq_lar_core_solver.m_r_upper_bounds.size() > j);
|
||||
{
|
||||
auto up = numeric_pair<mpq>(right_side, y_of_bound);
|
||||
m_mpq_lar_core_solver.m_r_upper_bounds[j] = up;
|
||||
}
|
||||
set_upper_bound_witness(j, constr_ind);
|
||||
break;
|
||||
case GT:
|
||||
y_of_bound = 1;
|
||||
case GE:
|
||||
m_mpq_lar_core_solver.m_column_types[j] = column_type::low_bound;
|
||||
lean_assert(m_mpq_lar_core_solver.m_r_upper_bounds.size() > j);
|
||||
{
|
||||
auto low = numeric_pair<mpq>(right_side, y_of_bound);
|
||||
m_mpq_lar_core_solver.m_r_low_bounds[j] = low;
|
||||
}
|
||||
set_low_bound_witness(j, constr_ind);
|
||||
break;
|
||||
case EQ:
|
||||
m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed;
|
||||
m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = numeric_pair<mpq>(right_side, zero_of_type<mpq>());
|
||||
set_upper_bound_witness(j, constr_ind);
|
||||
set_low_bound_witness(j, constr_ind);
|
||||
break;
|
||||
|
||||
default:
|
||||
lean_unreachable();
|
||||
|
||||
}
|
||||
m_columns_with_changed_bound.insert(j);
|
||||
}
|
||||
|
||||
void update_upper_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) {
|
||||
lean_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::upper_bound);
|
||||
mpq y_of_bound(0);
|
||||
switch (kind) {
|
||||
case LT:
|
||||
y_of_bound = -1;
|
||||
case LE:
|
||||
{
|
||||
auto up = numeric_pair<mpq>(right_side, y_of_bound);
|
||||
if (up < m_mpq_lar_core_solver.m_r_upper_bounds()[j]) {
|
||||
m_mpq_lar_core_solver.m_r_upper_bounds[j] = up;
|
||||
set_upper_bound_witness(j, ci);
|
||||
m_columns_with_changed_bound.insert(j);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GT:
|
||||
y_of_bound = 1;
|
||||
case GE:
|
||||
m_mpq_lar_core_solver.m_column_types[j] = column_type::boxed;
|
||||
{
|
||||
auto low = numeric_pair<mpq>(right_side, y_of_bound);
|
||||
m_mpq_lar_core_solver.m_r_low_bounds[j] = low;
|
||||
set_low_bound_witness(j, ci);
|
||||
m_columns_with_changed_bound.insert(j);
|
||||
if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
m_infeasible_column_index = j;
|
||||
} else {
|
||||
m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j]? column_type::boxed : column_type::fixed;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EQ:
|
||||
{
|
||||
auto v = numeric_pair<mpq>(right_side, zero_of_type<mpq>());
|
||||
if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
set_low_bound_witness(j, ci);
|
||||
m_infeasible_column_index = j;
|
||||
} else {
|
||||
m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v;
|
||||
m_columns_with_changed_bound.insert(j);
|
||||
set_low_bound_witness(j, ci);
|
||||
set_upper_bound_witness(j, ci);
|
||||
m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed;
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
lean_unreachable();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void update_boxed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) {
|
||||
lean_assert(m_status == INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::boxed && m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j]));
|
||||
mpq y_of_bound(0);
|
||||
switch (kind) {
|
||||
case LT:
|
||||
y_of_bound = -1;
|
||||
case LE:
|
||||
{
|
||||
auto up = numeric_pair<mpq>(right_side, y_of_bound);
|
||||
if (up < m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
|
||||
m_mpq_lar_core_solver.m_r_upper_bounds[j] = up;
|
||||
set_upper_bound_witness(j, ci);
|
||||
m_columns_with_changed_bound.insert(j);
|
||||
}
|
||||
|
||||
if (up < m_mpq_lar_core_solver.m_r_low_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
lean_assert(false);
|
||||
m_infeasible_column_index = j;
|
||||
} else {
|
||||
if (m_mpq_lar_core_solver.m_r_low_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j])
|
||||
m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GT:
|
||||
y_of_bound = 1;
|
||||
case GE:
|
||||
{
|
||||
auto low = numeric_pair<mpq>(right_side, y_of_bound);
|
||||
if (low > m_mpq_lar_core_solver.m_r_low_bounds[j]) {
|
||||
m_mpq_lar_core_solver.m_r_low_bounds[j] = low;
|
||||
m_columns_with_changed_bound.insert(j);
|
||||
set_low_bound_witness(j, ci);
|
||||
}
|
||||
if (low > m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
m_infeasible_column_index = j;
|
||||
} else if ( low == m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
|
||||
m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EQ:
|
||||
{
|
||||
auto v = numeric_pair<mpq>(right_side, zero_of_type<mpq>());
|
||||
if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
m_infeasible_column_index = j;
|
||||
set_upper_bound_witness(j, ci);
|
||||
} else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
m_infeasible_column_index = j;
|
||||
set_low_bound_witness(j, ci);
|
||||
} else {
|
||||
m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v;
|
||||
set_low_bound_witness(j, ci);
|
||||
set_upper_bound_witness(j, ci);
|
||||
m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed;
|
||||
m_columns_with_changed_bound.insert(j);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
lean_unreachable();
|
||||
|
||||
}
|
||||
}
|
||||
void update_low_bound_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) {
|
||||
lean_assert(m_mpq_lar_core_solver.m_column_types()[j] == column_type::low_bound);
|
||||
mpq y_of_bound(0);
|
||||
switch (kind) {
|
||||
case LT:
|
||||
y_of_bound = -1;
|
||||
case LE:
|
||||
{
|
||||
auto up = numeric_pair<mpq>(right_side, y_of_bound);
|
||||
m_mpq_lar_core_solver.m_r_upper_bounds[j] = up;
|
||||
set_upper_bound_witness(j, ci);
|
||||
m_columns_with_changed_bound.insert(j);
|
||||
|
||||
if (up < m_mpq_lar_core_solver.m_r_low_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
m_infeasible_column_index = j;
|
||||
} else {
|
||||
m_mpq_lar_core_solver.m_column_types[j] = m_mpq_lar_core_solver.m_r_low_bounds()[j] < m_mpq_lar_core_solver.m_r_upper_bounds()[j]? column_type::boxed : column_type::fixed;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GT:
|
||||
y_of_bound = 1;
|
||||
case GE:
|
||||
{
|
||||
auto low = numeric_pair<mpq>(right_side, y_of_bound);
|
||||
if (low > m_mpq_lar_core_solver.m_r_low_bounds[j]) {
|
||||
m_mpq_lar_core_solver.m_r_low_bounds[j] = low;
|
||||
m_columns_with_changed_bound.insert(j);
|
||||
set_low_bound_witness(j, ci);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EQ:
|
||||
{
|
||||
auto v = numeric_pair<mpq>(right_side, zero_of_type<mpq>());
|
||||
if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
m_infeasible_column_index = j;
|
||||
set_upper_bound_witness(j, ci);
|
||||
} else {
|
||||
m_mpq_lar_core_solver.m_r_low_bounds[j] = m_mpq_lar_core_solver.m_r_upper_bounds[j] = v;
|
||||
set_low_bound_witness(j, ci);
|
||||
set_upper_bound_witness(j, ci);
|
||||
m_mpq_lar_core_solver.m_column_types[j] = column_type::fixed;
|
||||
}
|
||||
m_columns_with_changed_bound.insert(j);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
lean_unreachable();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void update_fixed_column_type_and_bound(var_index j, lconstraint_kind kind, const mpq & right_side, constraint_index ci) {
|
||||
lean_assert(m_status == INFEASIBLE || (m_mpq_lar_core_solver.m_column_types()[j] == column_type::fixed && m_mpq_lar_core_solver.m_r_low_bounds()[j] == m_mpq_lar_core_solver.m_r_upper_bounds()[j]));
|
||||
lean_assert(m_status == INFEASIBLE || (m_mpq_lar_core_solver.m_r_low_bounds()[j].y.is_zero() && m_mpq_lar_core_solver.m_r_upper_bounds()[j].y.is_zero()));
|
||||
auto v = numeric_pair<mpq>(right_side, mpq(0));
|
||||
|
||||
mpq y_of_bound(0);
|
||||
switch (kind) {
|
||||
case LT:
|
||||
if (v <= m_mpq_lar_core_solver.m_r_low_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
m_infeasible_column_index = j;
|
||||
set_upper_bound_witness(j, ci);
|
||||
}
|
||||
break;
|
||||
case LE:
|
||||
{
|
||||
if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
m_infeasible_column_index = j;
|
||||
set_upper_bound_witness(j, ci);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GT:
|
||||
{
|
||||
if (v >= m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
m_infeasible_column_index =j;
|
||||
set_low_bound_witness(j, ci);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GE:
|
||||
{
|
||||
if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
m_infeasible_column_index = j;
|
||||
set_low_bound_witness(j, ci);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case EQ:
|
||||
{
|
||||
if (v < m_mpq_lar_core_solver.m_r_low_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
m_infeasible_column_index = j;
|
||||
set_upper_bound_witness(j, ci);
|
||||
} else if (v > m_mpq_lar_core_solver.m_r_upper_bounds[j]) {
|
||||
m_status = INFEASIBLE;
|
||||
m_infeasible_column_index = j;
|
||||
set_low_bound_witness(j, ci);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
lean_unreachable();
|
||||
|
||||
}
|
||||
}
|
||||
|
66
src/util/lp/int_set.h
Normal file
66
src/util/lp/int_set.h
Normal file
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/vector.h"
|
||||
#include "util/lp/indexed_vector.h"
|
||||
#include <ostream>
|
||||
namespace lean {
|
||||
// serves at a set of non-negative integers smaller than the set size
|
||||
class int_set {
|
||||
vector<int> m_data;
|
||||
public:
|
||||
vector<int> m_index;
|
||||
int_set(unsigned size): m_data(size, -1) {}
|
||||
int_set() {}
|
||||
bool contains(unsigned j) const {
|
||||
if (j >= m_data.size())
|
||||
return false;
|
||||
return m_data[j] >= 0;
|
||||
}
|
||||
void insert(unsigned j) {
|
||||
lean_assert(j < m_data.size());
|
||||
if (contains(j)) return;
|
||||
m_data[j] = m_index.size();
|
||||
m_index.push_back(j);
|
||||
}
|
||||
void erase(unsigned j) {
|
||||
if (!contains(j)) return;
|
||||
unsigned pos_j = m_data[j];
|
||||
unsigned last_pos = m_index.size() - 1;
|
||||
int last_j = m_index[last_pos];
|
||||
if (last_pos != pos_j) {
|
||||
// move last to j spot
|
||||
m_data[last_j] = pos_j;
|
||||
m_index[pos_j] = last_j;
|
||||
}
|
||||
m_index.pop_back();
|
||||
m_data[j] = -1;
|
||||
}
|
||||
|
||||
void resize(unsigned size) {
|
||||
m_data.resize(size, -1);
|
||||
}
|
||||
|
||||
void increase_size_by_one() {
|
||||
resize(m_data.size() + 1);
|
||||
}
|
||||
|
||||
unsigned data_size() const { return m_data.size(); }
|
||||
unsigned size() const { return m_index.size();}
|
||||
bool is_empty() const { return size() == 0; }
|
||||
void clear() {
|
||||
for (unsigned j : m_index)
|
||||
m_data[j] = -1;
|
||||
m_index.resize(0);
|
||||
}
|
||||
void print(std::ostream & out ) const {
|
||||
for (unsigned j : m_index) {
|
||||
out << j << " ";
|
||||
}
|
||||
out << std::endl;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
50
src/util/lp/iterator_on_column.h
Normal file
50
src/util/lp/iterator_on_column.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/lp/linear_combination_iterator.h"
|
||||
#include "util/lp/static_matrix.h"
|
||||
#include "util/lp/lar_term.h"
|
||||
namespace lean {
|
||||
template <typename T, typename X>
|
||||
struct iterator_on_column:linear_combination_iterator<T> {
|
||||
const vector<column_cell>& m_column; // the offset in term coeffs
|
||||
const static_matrix<T, X> & m_A;
|
||||
int m_i; // the initial offset in the column
|
||||
unsigned size() const { return m_column.size(); }
|
||||
iterator_on_column(const vector<column_cell>& column, const static_matrix<T,X> & A) // the offset in term coeffs
|
||||
:
|
||||
m_column(column),
|
||||
m_A(A),
|
||||
m_i(-1) {}
|
||||
|
||||
bool next(mpq & a, unsigned & i) {
|
||||
if (++m_i >= static_cast<int>(m_column.size()))
|
||||
return false;
|
||||
|
||||
const column_cell& c = m_column[m_i];
|
||||
a = m_A.get_val(c);
|
||||
i = c.m_i;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool next(unsigned & i) {
|
||||
if (++m_i >= static_cast<int>(m_column.size()))
|
||||
return false;
|
||||
|
||||
const column_cell& c = m_column[m_i];
|
||||
i = c.m_i;
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
m_i = -1;
|
||||
}
|
||||
|
||||
linear_combination_iterator<mpq> * clone() {
|
||||
iterator_on_column * r = new iterator_on_column(m_column, m_A);
|
||||
return r;
|
||||
}
|
||||
};
|
||||
}
|
38
src/util/lp/iterator_on_indexed_vector.h
Normal file
38
src/util/lp/iterator_on_indexed_vector.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/lp/linear_combination_iterator.h"
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
struct iterator_on_indexed_vector:linear_combination_iterator<T> {
|
||||
const indexed_vector<T> & m_v;
|
||||
unsigned m_offset;
|
||||
iterator_on_indexed_vector(const indexed_vector<T> & v) :
|
||||
m_v(v),
|
||||
m_offset(0)
|
||||
{}
|
||||
unsigned size() const { return m_v.m_index.size(); }
|
||||
bool next(T & a, unsigned & i) {
|
||||
if (m_offset >= m_v.m_index.size())
|
||||
return false;
|
||||
i = m_v.m_index[m_offset++];
|
||||
a = m_v.m_data[i];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool next(unsigned & i) {
|
||||
if (m_offset >= m_v.m_index.size())
|
||||
return false;
|
||||
i = m_v.m_index[m_offset++];
|
||||
return true;
|
||||
}
|
||||
void reset() {
|
||||
m_offset = 0;
|
||||
}
|
||||
linear_combination_iterator<T>* clone() {
|
||||
return new iterator_on_indexed_vector(m_v);
|
||||
}
|
||||
};
|
||||
}
|
44
src/util/lp/iterator_on_pivot_row.h
Normal file
44
src/util/lp/iterator_on_pivot_row.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/lp/iterator_on_indexed_vector.h"
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
struct iterator_on_pivot_row:linear_combination_iterator<T> {
|
||||
bool m_basis_returned;
|
||||
const indexed_vector<T> & m_v;
|
||||
unsigned m_basis_j;
|
||||
iterator_on_indexed_vector<T> m_it;
|
||||
unsigned size() const { return m_it.size(); }
|
||||
iterator_on_pivot_row(const indexed_vector<T> & v, unsigned basis_j) :
|
||||
m_basis_returned(false),
|
||||
m_v(v), m_basis_j(basis_j), m_it(v) {}
|
||||
bool next(T & a, unsigned & i) {
|
||||
if (m_basis_returned == false) {
|
||||
m_basis_returned = true;
|
||||
a = one_of_type<T>();
|
||||
i = m_basis_j;
|
||||
return true;
|
||||
}
|
||||
return m_it.next(a, i);
|
||||
}
|
||||
bool next(unsigned & i) {
|
||||
if (m_basis_returned == false) {
|
||||
m_basis_returned = true;
|
||||
i = m_basis_j;
|
||||
return true;
|
||||
}
|
||||
return m_it.next(i);
|
||||
}
|
||||
void reset() {
|
||||
m_basis_returned = false;
|
||||
m_it.reset();
|
||||
}
|
||||
linear_combination_iterator<T> * clone() {
|
||||
iterator_on_pivot_row * r = new iterator_on_pivot_row(m_v, m_basis_j);
|
||||
return r;
|
||||
}
|
||||
};
|
||||
}
|
37
src/util/lp/iterator_on_row.h
Normal file
37
src/util/lp/iterator_on_row.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/lp/linear_combination_iterator.h"
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
struct iterator_on_row:linear_combination_iterator<T> {
|
||||
const vector<row_cell<T>> & m_row;
|
||||
unsigned m_i; // offset
|
||||
iterator_on_row(const vector<row_cell<T>> & row) : m_row(row), m_i(0)
|
||||
{}
|
||||
unsigned size() const { return m_row.size(); }
|
||||
bool next(T & a, unsigned & i) {
|
||||
if (m_i == m_row.size())
|
||||
return false;
|
||||
auto &c = m_row[m_i++];
|
||||
i = c.m_j;
|
||||
a = c.get_val();
|
||||
return true;
|
||||
}
|
||||
bool next(unsigned & i) {
|
||||
if (m_i == m_row.size())
|
||||
return false;
|
||||
auto &c = m_row[m_i++];
|
||||
i = c.m_j;
|
||||
return true;
|
||||
}
|
||||
void reset() {
|
||||
m_i = 0;
|
||||
}
|
||||
linear_combination_iterator<T>* clone() {
|
||||
return new iterator_on_row(m_row);
|
||||
}
|
||||
};
|
||||
}
|
57
src/util/lp/iterator_on_term_with_basis_var.h
Normal file
57
src/util/lp/iterator_on_term_with_basis_var.h
Normal file
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/lp/linear_combination_iterator.h"
|
||||
#include "util/lp/numeric_pair.h"
|
||||
#include "util/lp/lar_term.h"
|
||||
namespace lean {
|
||||
struct iterator_on_term_with_basis_var:linear_combination_iterator<mpq> {
|
||||
const lar_term & m_term;
|
||||
std::unordered_map<unsigned, mpq>::const_iterator m_i; // the offset in term coeffs
|
||||
bool m_term_j_returned;
|
||||
unsigned m_term_j;
|
||||
unsigned size() const {return static_cast<unsigned>(m_term.m_coeffs.size() + 1);}
|
||||
iterator_on_term_with_basis_var(const lar_term & t, unsigned term_j) :
|
||||
m_term(t),
|
||||
m_i(t.m_coeffs.begin()),
|
||||
m_term_j_returned(false),
|
||||
m_term_j(term_j) {}
|
||||
|
||||
bool next(mpq & a, unsigned & i) {
|
||||
if (m_term_j_returned == false) {
|
||||
m_term_j_returned = true;
|
||||
a = - one_of_type<mpq>();
|
||||
i = m_term_j;
|
||||
return true;
|
||||
}
|
||||
if (m_i == m_term.m_coeffs.end())
|
||||
return false;
|
||||
i = m_i->first;
|
||||
a = m_i->second;
|
||||
m_i++;
|
||||
return true;
|
||||
}
|
||||
bool next(unsigned & i) {
|
||||
if (m_term_j_returned == false) {
|
||||
m_term_j_returned = true;
|
||||
i = m_term_j;
|
||||
return true;
|
||||
}
|
||||
if (m_i == m_term.m_coeffs.end())
|
||||
return false;
|
||||
i = m_i->first;
|
||||
m_i++;
|
||||
return true;
|
||||
}
|
||||
void reset() {
|
||||
m_term_j_returned = false;
|
||||
m_i = m_term.m_coeffs.begin();
|
||||
}
|
||||
linear_combination_iterator<mpq> * clone() {
|
||||
iterator_on_term_with_basis_var * r = new iterator_on_term_with_basis_var(m_term, m_term_j);
|
||||
return r;
|
||||
}
|
||||
};
|
||||
}
|
86
src/util/lp/lar_constraints.h
Normal file
86
src/util/lp/lar_constraints.h
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "util/vector.h"
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include "util/lp/lp_utils.h"
|
||||
#include "util/lp/ul_pair.h"
|
||||
#include "util/lp/lar_term.h"
|
||||
namespace lean {
|
||||
inline lconstraint_kind flip_kind(lconstraint_kind t) {
|
||||
return static_cast<lconstraint_kind>( - static_cast<int>(t));
|
||||
}
|
||||
|
||||
inline std::string lconstraint_kind_string(lconstraint_kind t) {
|
||||
switch (t) {
|
||||
case LE: return std::string("<=");
|
||||
case LT: return std::string("<");
|
||||
case GE: return std::string(">=");
|
||||
case GT: return std::string(">");
|
||||
case EQ: return std::string("=");
|
||||
}
|
||||
lean_unreachable();
|
||||
return std::string(); // it is unreachable
|
||||
}
|
||||
|
||||
class lar_base_constraint {
|
||||
public:
|
||||
lconstraint_kind m_kind;
|
||||
mpq m_right_side;
|
||||
virtual vector<std::pair<mpq, var_index>> get_left_side_coefficients() const = 0;
|
||||
lar_base_constraint() {}
|
||||
lar_base_constraint(lconstraint_kind kind, const mpq& right_side) :m_kind(kind), m_right_side(right_side) {}
|
||||
|
||||
virtual unsigned size() const = 0;
|
||||
virtual ~lar_base_constraint(){}
|
||||
virtual mpq get_free_coeff_of_left_side() const { return zero_of_type<mpq>();}
|
||||
};
|
||||
|
||||
struct lar_var_constraint: public lar_base_constraint {
|
||||
unsigned m_j;
|
||||
vector<std::pair<mpq, var_index>> get_left_side_coefficients() const {
|
||||
vector<std::pair<mpq, var_index>> ret;
|
||||
ret.push_back(std::make_pair(one_of_type<mpq>(), m_j));
|
||||
return ret;
|
||||
}
|
||||
unsigned size() const { return 1;}
|
||||
lar_var_constraint(unsigned j, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_j(j) { }
|
||||
};
|
||||
|
||||
|
||||
struct lar_term_constraint: public lar_base_constraint {
|
||||
const lar_term * m_term;
|
||||
vector<std::pair<mpq, var_index>> get_left_side_coefficients() const {
|
||||
return m_term->coeffs_as_vector();
|
||||
}
|
||||
unsigned size() const { return m_term->size();}
|
||||
lar_term_constraint(const lar_term *t, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_term(t) { }
|
||||
virtual mpq get_free_coeff_of_left_side() const { return m_term->m_v;}
|
||||
|
||||
};
|
||||
|
||||
|
||||
class lar_constraint : public lar_base_constraint {
|
||||
public:
|
||||
vector<std::pair<mpq, var_index>> m_coeffs;
|
||||
lar_constraint() {}
|
||||
lar_constraint(const vector<std::pair<mpq, var_index>> & left_side, lconstraint_kind kind, const mpq & right_side)
|
||||
: lar_base_constraint(kind, right_side), m_coeffs(left_side) {}
|
||||
|
||||
lar_constraint(const lar_base_constraint & c) {
|
||||
lean_assert(false); // should not be called : todo!
|
||||
}
|
||||
|
||||
unsigned size() const {
|
||||
return static_cast<unsigned>(m_coeffs.size());
|
||||
}
|
||||
|
||||
vector<std::pair<mpq, var_index>> get_left_side_coefficients() const { return m_coeffs; }
|
||||
};
|
||||
}
|
801
src/util/lp/lar_core_solver.h
Normal file
801
src/util/lp/lar_core_solver.h
Normal file
|
@ -0,0 +1,801 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/vector.h"
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "util/lp/lp_core_solver_base.h"
|
||||
#include <algorithm>
|
||||
#include "util/lp/indexed_vector.h"
|
||||
#include "util/lp/binary_heap_priority_queue.h"
|
||||
#include "util/lp/breakpoint.h"
|
||||
#include "util/lp/stacked_unordered_set.h"
|
||||
#include "util/lp/lp_primal_core_solver.h"
|
||||
#include "util/lp/stacked_vector.h"
|
||||
#include "util/lp/lar_solution_signature.h"
|
||||
#include "util/lp/iterator_on_column.h"
|
||||
#include "util/lp/iterator_on_indexed_vector.h"
|
||||
#include "util/lp/stacked_value.h"
|
||||
namespace lean {
|
||||
|
||||
class lar_core_solver {
|
||||
// m_sign_of_entering is set to 1 if the entering variable needs
|
||||
// to grow and is set to -1 otherwise
|
||||
int m_sign_of_entering_delta;
|
||||
vector<std::pair<mpq, unsigned>> m_infeasible_linear_combination;
|
||||
int m_infeasible_sum_sign; // todo: get rid of this field
|
||||
vector<numeric_pair<mpq>> m_right_sides_dummy;
|
||||
vector<mpq> m_costs_dummy;
|
||||
vector<double> m_d_right_sides_dummy;
|
||||
vector<double> m_d_costs_dummy;
|
||||
public:
|
||||
stacked_value<simplex_strategy_enum> m_stacked_simplex_strategy;
|
||||
stacked_vector<column_type> m_column_types;
|
||||
// r - solver fields, for rational numbers
|
||||
vector<numeric_pair<mpq>> m_r_x; // the solution
|
||||
stacked_vector<numeric_pair<mpq>> m_r_low_bounds;
|
||||
stacked_vector<numeric_pair<mpq>> m_r_upper_bounds;
|
||||
static_matrix<mpq, numeric_pair<mpq>> m_r_A;
|
||||
stacked_vector<unsigned> m_r_pushed_basis;
|
||||
vector<unsigned> m_r_basis;
|
||||
vector<unsigned> m_r_nbasis;
|
||||
vector<int> m_r_heading;
|
||||
stacked_vector<unsigned> m_r_columns_nz;
|
||||
stacked_vector<unsigned> m_r_rows_nz;
|
||||
|
||||
// d - solver fields, for doubles
|
||||
vector<double> m_d_x; // the solution in doubles
|
||||
vector<double> m_d_low_bounds;
|
||||
vector<double> m_d_upper_bounds;
|
||||
static_matrix<double, double> m_d_A;
|
||||
stacked_vector<unsigned> m_d_pushed_basis;
|
||||
vector<unsigned> m_d_basis;
|
||||
vector<unsigned> m_d_nbasis;
|
||||
vector<int> m_d_heading;
|
||||
|
||||
|
||||
lp_primal_core_solver<mpq, numeric_pair<mpq>> m_r_solver; // solver in rational numbers
|
||||
|
||||
lp_primal_core_solver<double, double> m_d_solver; // solver in doubles
|
||||
|
||||
lar_core_solver(
|
||||
lp_settings & settings,
|
||||
const column_namer & column_names
|
||||
);
|
||||
|
||||
lp_settings & settings() { return m_r_solver.m_settings;}
|
||||
const lp_settings & settings() const { return m_r_solver.m_settings;}
|
||||
|
||||
int get_infeasible_sum_sign() const { return m_infeasible_sum_sign; }
|
||||
|
||||
const vector<std::pair<mpq, unsigned>> & get_infeasibility_info(int & inf_sign) const {
|
||||
inf_sign = m_infeasible_sum_sign;
|
||||
return m_infeasible_linear_combination;
|
||||
}
|
||||
|
||||
void fill_not_improvable_zero_sum_from_inf_row();
|
||||
|
||||
column_type get_column_type(unsigned j) { return m_column_types[j];}
|
||||
|
||||
void init_costs(bool first_time);
|
||||
|
||||
void init_cost_for_column(unsigned j);
|
||||
|
||||
// returns m_sign_of_alpha_r
|
||||
int column_is_out_of_bounds(unsigned j);
|
||||
|
||||
void calculate_pivot_row(unsigned i);
|
||||
|
||||
void print_pivot_row(std::ostream & out, unsigned row_index) const { // remove later debug !!!!
|
||||
for (unsigned j : m_r_solver.m_pivot_row.m_index) {
|
||||
if (numeric_traits<mpq>::is_pos(m_r_solver.m_pivot_row.m_data[j]))
|
||||
out << "+";
|
||||
out << m_r_solver.m_pivot_row.m_data[j] << m_r_solver.column_name(j) << " ";
|
||||
}
|
||||
|
||||
out << " +" << m_r_solver.column_name(m_r_solver.m_basis[row_index]) << std::endl;
|
||||
|
||||
for (unsigned j : m_r_solver.m_pivot_row.m_index) {
|
||||
m_r_solver.print_column_bound_info(j, out);
|
||||
}
|
||||
m_r_solver.print_column_bound_info(m_r_solver.m_basis[row_index], out);
|
||||
|
||||
}
|
||||
|
||||
|
||||
void advance_on_sorted_breakpoints(unsigned entering);
|
||||
|
||||
void change_slope_on_breakpoint(unsigned entering, breakpoint<numeric_pair<mpq>> * b, mpq & slope_at_entering);
|
||||
|
||||
bool row_is_infeasible(unsigned row);
|
||||
|
||||
bool row_is_evidence(unsigned row);
|
||||
|
||||
bool find_evidence_row();
|
||||
|
||||
void prefix_r();
|
||||
|
||||
void prefix_d();
|
||||
|
||||
unsigned m_m() const {
|
||||
return m_r_A.row_count();
|
||||
}
|
||||
|
||||
unsigned m_n() const {
|
||||
return m_r_A.column_count();
|
||||
}
|
||||
|
||||
bool is_tiny() const { return this->m_m() < 10 && this->m_n() < 20; }
|
||||
|
||||
bool is_empty() const { return this->m_m() == 0 && this->m_n() == 0; }
|
||||
|
||||
template <typename L>
|
||||
int get_sign(const L & v) {
|
||||
return v > zero_of_type<L>() ? 1 : (v < zero_of_type<L>() ? -1 : 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void fill_evidence(unsigned row);
|
||||
|
||||
|
||||
|
||||
void solve();
|
||||
|
||||
bool low_bounds_are_set() const { return true; }
|
||||
|
||||
const indexed_vector<mpq> & get_pivot_row() const {
|
||||
return m_r_solver.m_pivot_row;
|
||||
}
|
||||
|
||||
void fill_not_improvable_zero_sum();
|
||||
|
||||
void pop_basis(unsigned k) {
|
||||
if (!settings().use_tableau()) {
|
||||
m_r_pushed_basis.pop(k);
|
||||
m_r_basis = m_r_pushed_basis();
|
||||
m_r_solver.init_basis_heading_and_non_basic_columns_vector();
|
||||
m_d_pushed_basis.pop(k);
|
||||
m_d_basis = m_d_pushed_basis();
|
||||
m_d_solver.init_basis_heading_and_non_basic_columns_vector();
|
||||
} else {
|
||||
m_d_basis = m_r_basis;
|
||||
m_d_nbasis = m_r_nbasis;
|
||||
m_d_heading = m_r_heading;
|
||||
}
|
||||
}
|
||||
|
||||
void push() {
|
||||
lean_assert(m_r_solver.basis_heading_is_correct());
|
||||
lean_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct());
|
||||
lean_assert(m_column_types.size() == m_r_A.column_count());
|
||||
m_stacked_simplex_strategy = settings().simplex_strategy();
|
||||
m_stacked_simplex_strategy.push();
|
||||
m_column_types.push();
|
||||
// rational
|
||||
if (!settings().use_tableau())
|
||||
m_r_A.push();
|
||||
m_r_low_bounds.push();
|
||||
m_r_upper_bounds.push();
|
||||
if (!settings().use_tableau()) {
|
||||
push_vector(m_r_pushed_basis, m_r_basis);
|
||||
push_vector(m_r_columns_nz, m_r_solver.m_columns_nz);
|
||||
push_vector(m_r_rows_nz, m_r_solver.m_rows_nz);
|
||||
}
|
||||
|
||||
m_d_A.push();
|
||||
if (!settings().use_tableau())
|
||||
push_vector(m_d_pushed_basis, m_d_basis);
|
||||
}
|
||||
|
||||
template <typename K>
|
||||
void push_vector(stacked_vector<K> & pushed_vector, const vector<K> & vector) {
|
||||
lean_assert(pushed_vector.size() <= vector.size());
|
||||
for (unsigned i = 0; i < vector.size();i++) {
|
||||
if (i == pushed_vector.size()) {
|
||||
pushed_vector.push_back(vector[i]);
|
||||
} else {
|
||||
pushed_vector[i] = vector[i];
|
||||
}
|
||||
}
|
||||
pushed_vector.push();
|
||||
}
|
||||
|
||||
void pop_markowitz_counts(unsigned k) {
|
||||
m_r_columns_nz.pop(k);
|
||||
m_r_rows_nz.pop(k);
|
||||
m_r_solver.m_columns_nz.resize(m_r_columns_nz.size());
|
||||
m_r_solver.m_rows_nz.resize(m_r_rows_nz.size());
|
||||
for (unsigned i = 0; i < m_r_columns_nz.size(); i++)
|
||||
m_r_solver.m_columns_nz[i] = m_r_columns_nz[i];
|
||||
for (unsigned i = 0; i < m_r_rows_nz.size(); i++)
|
||||
m_r_solver.m_rows_nz[i] = m_r_rows_nz[i];
|
||||
}
|
||||
|
||||
|
||||
void pop(unsigned k) {
|
||||
// rationals
|
||||
if (!settings().use_tableau())
|
||||
m_r_A.pop(k);
|
||||
m_r_low_bounds.pop(k);
|
||||
m_r_upper_bounds.pop(k);
|
||||
m_column_types.pop(k);
|
||||
|
||||
if (m_r_solver.m_factorization != nullptr) {
|
||||
delete m_r_solver.m_factorization;
|
||||
m_r_solver.m_factorization = nullptr;
|
||||
}
|
||||
m_r_x.resize(m_r_A.column_count());
|
||||
m_r_solver.m_costs.resize(m_r_A.column_count());
|
||||
m_r_solver.m_d.resize(m_r_A.column_count());
|
||||
if(!settings().use_tableau())
|
||||
pop_markowitz_counts(k);
|
||||
m_d_A.pop(k);
|
||||
if (m_d_solver.m_factorization != nullptr) {
|
||||
delete m_d_solver.m_factorization;
|
||||
m_d_solver.m_factorization = nullptr;
|
||||
}
|
||||
|
||||
m_d_x.resize(m_d_A.column_count());
|
||||
pop_basis(k);
|
||||
m_stacked_simplex_strategy.pop(k);
|
||||
settings().simplex_strategy() = m_stacked_simplex_strategy;
|
||||
lean_assert(m_r_solver.basis_heading_is_correct());
|
||||
lean_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct());
|
||||
}
|
||||
|
||||
bool need_to_presolve_with_double_solver() const {
|
||||
return settings().simplex_strategy() == simplex_strategy_enum::lu;
|
||||
}
|
||||
|
||||
template <typename L>
|
||||
bool is_zero_vector(const vector<L> & b) {
|
||||
for (const L & m: b)
|
||||
if (!is_zero(m)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool update_xj_and_get_delta(unsigned j, non_basic_column_value_position pos_type, numeric_pair<mpq> & delta) {
|
||||
auto & x = m_r_x[j];
|
||||
switch (pos_type) {
|
||||
case at_low_bound:
|
||||
if (x == m_r_solver.m_low_bounds[j])
|
||||
return false;
|
||||
delta = m_r_solver.m_low_bounds[j] - x;
|
||||
m_r_solver.m_x[j] = m_r_solver.m_low_bounds[j];
|
||||
break;
|
||||
case at_fixed:
|
||||
case at_upper_bound:
|
||||
if (x == m_r_solver.m_upper_bounds[j])
|
||||
return false;
|
||||
delta = m_r_solver.m_upper_bounds[j] - x;
|
||||
x = m_r_solver.m_upper_bounds[j];
|
||||
break;
|
||||
case free_of_bounds: {
|
||||
return false;
|
||||
}
|
||||
case not_at_bound:
|
||||
switch (m_column_types[j]) {
|
||||
case column_type::free_column:
|
||||
return false;
|
||||
case column_type::upper_bound:
|
||||
delta = m_r_solver.m_upper_bounds[j] - x;
|
||||
x = m_r_solver.m_upper_bounds[j];
|
||||
break;
|
||||
case column_type::low_bound:
|
||||
delta = m_r_solver.m_low_bounds[j] - x;
|
||||
x = m_r_solver.m_low_bounds[j];
|
||||
break;
|
||||
case column_type::boxed:
|
||||
if (x > m_r_solver.m_upper_bounds[j]) {
|
||||
delta = m_r_solver.m_upper_bounds[j] - x;
|
||||
x += m_r_solver.m_upper_bounds[j];
|
||||
} else {
|
||||
delta = m_r_solver.m_low_bounds[j] - x;
|
||||
x = m_r_solver.m_low_bounds[j];
|
||||
}
|
||||
break;
|
||||
case column_type::fixed:
|
||||
delta = m_r_solver.m_low_bounds[j] - x;
|
||||
x = m_r_solver.m_low_bounds[j];
|
||||
break;
|
||||
|
||||
default:
|
||||
lean_assert(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
m_r_solver.remove_column_from_inf_set(j);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void prepare_solver_x_with_signature_tableau(const lar_solution_signature & signature) {
|
||||
lean_assert(m_r_solver.inf_set_is_correct());
|
||||
for (auto &t : signature) {
|
||||
unsigned j = t.first;
|
||||
if (m_r_heading[j] >= 0)
|
||||
continue;
|
||||
auto pos_type = t.second;
|
||||
numeric_pair<mpq> delta;
|
||||
if (!update_xj_and_get_delta(j, pos_type, delta))
|
||||
continue;
|
||||
for (const auto & cc : m_r_solver.m_A.m_columns[j]){
|
||||
unsigned i = cc.m_i;
|
||||
unsigned jb = m_r_solver.m_basis[i];
|
||||
m_r_solver.m_x[jb] -= delta * m_r_solver.m_A.get_val(cc);
|
||||
m_r_solver.update_column_in_inf_set(jb);
|
||||
}
|
||||
lean_assert(m_r_solver.A_mult_x_is_off() == false);
|
||||
}
|
||||
lean_assert(m_r_solver.inf_set_is_correct());
|
||||
}
|
||||
|
||||
|
||||
template <typename L, typename K>
|
||||
void prepare_solver_x_with_signature(const lar_solution_signature & signature, lp_primal_core_solver<L,K> & s) {
|
||||
for (auto &t : signature) {
|
||||
unsigned j = t.first;
|
||||
lean_assert(m_r_heading[j] < 0);
|
||||
auto pos_type = t.second;
|
||||
switch (pos_type) {
|
||||
case at_low_bound:
|
||||
s.m_x[j] = s.m_low_bounds[j];
|
||||
break;
|
||||
case at_fixed:
|
||||
case at_upper_bound:
|
||||
s.m_x[j] = s.m_upper_bounds[j];
|
||||
break;
|
||||
case free_of_bounds: {
|
||||
s.m_x[j] = zero_of_type<K>();
|
||||
continue;
|
||||
}
|
||||
case not_at_bound:
|
||||
switch (m_column_types[j]) {
|
||||
case column_type::free_column:
|
||||
lean_assert(false); // unreachable
|
||||
case column_type::upper_bound:
|
||||
s.m_x[j] = s.m_upper_bounds[j];
|
||||
break;
|
||||
case column_type::low_bound:
|
||||
s.m_x[j] = s.m_low_bounds[j];
|
||||
break;
|
||||
case column_type::boxed:
|
||||
if (settings().random_next() % 2) {
|
||||
s.m_x[j] = s.m_low_bounds[j];
|
||||
} else {
|
||||
s.m_x[j] = s.m_upper_bounds[j];
|
||||
}
|
||||
break;
|
||||
case column_type::fixed:
|
||||
s.m_x[j] = s.m_low_bounds[j];
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
lean_assert(is_zero_vector(s.m_b));
|
||||
s.solve_Ax_eq_b();
|
||||
}
|
||||
|
||||
template <typename L, typename K>
|
||||
void catch_up_in_lu_in_reverse(const vector<unsigned> & trace_of_basis_change, lp_primal_core_solver<L,K> & cs) {
|
||||
// recover the previous working basis
|
||||
for (unsigned i = trace_of_basis_change.size(); i > 0; i-= 2) {
|
||||
unsigned entering = trace_of_basis_change[i-1];
|
||||
unsigned leaving = trace_of_basis_change[i-2];
|
||||
cs.change_basis_unconditionally(entering, leaving);
|
||||
}
|
||||
cs.init_lu();
|
||||
}
|
||||
|
||||
//basis_heading is the basis heading of the solver owning trace_of_basis_change
|
||||
// here we compact the trace as we go to avoid unnecessary column changes
|
||||
template <typename L, typename K>
|
||||
void catch_up_in_lu(const vector<unsigned> & trace_of_basis_change, const vector<int> & basis_heading, lp_primal_core_solver<L,K> & cs) {
|
||||
if (cs.m_factorization == nullptr || cs.m_factorization->m_refactor_counter + trace_of_basis_change.size()/2 >= 200) {
|
||||
for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) {
|
||||
unsigned entering = trace_of_basis_change[i];
|
||||
unsigned leaving = trace_of_basis_change[i+1];
|
||||
cs.change_basis_unconditionally(entering, leaving);
|
||||
}
|
||||
if (cs.m_factorization != nullptr)
|
||||
delete cs.m_factorization;
|
||||
cs.m_factorization = nullptr;
|
||||
} else {
|
||||
indexed_vector<L> w(cs.m_A.row_count());
|
||||
// the queues of delayed indices
|
||||
std::queue<unsigned> entr_q, leav_q;
|
||||
auto * l = cs.m_factorization;
|
||||
lean_assert(l->get_status() == LU_status::OK);
|
||||
for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) {
|
||||
unsigned entering = trace_of_basis_change[i];
|
||||
unsigned leaving = trace_of_basis_change[i+1];
|
||||
bool good_e = basis_heading[entering] >= 0 && cs.m_basis_heading[entering] < 0;
|
||||
bool good_l = basis_heading[leaving] < 0 && cs.m_basis_heading[leaving] >= 0;
|
||||
if (!good_e && !good_l) continue;
|
||||
if (good_e && !good_l) {
|
||||
while (!leav_q.empty() && cs.m_basis_heading[leav_q.front()] < 0)
|
||||
leav_q.pop();
|
||||
if (!leav_q.empty()) {
|
||||
leaving = leav_q.front();
|
||||
leav_q.pop();
|
||||
} else {
|
||||
entr_q.push(entering);
|
||||
continue;
|
||||
}
|
||||
} else if (!good_e && good_l) {
|
||||
while (!entr_q.empty() && cs.m_basis_heading[entr_q.front()] >= 0)
|
||||
entr_q.pop();
|
||||
if (!entr_q.empty()) {
|
||||
entering = entr_q.front();
|
||||
entr_q.pop();
|
||||
} else {
|
||||
leav_q.push(leaving);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
lean_assert(cs.m_basis_heading[entering] < 0);
|
||||
lean_assert(cs.m_basis_heading[leaving] >= 0);
|
||||
if (l->get_status() == LU_status::OK) {
|
||||
l->prepare_entering(entering, w); // to init vector w
|
||||
l->replace_column(zero_of_type<L>(), w, cs.m_basis_heading[leaving]);
|
||||
}
|
||||
cs.change_basis_unconditionally(entering, leaving);
|
||||
}
|
||||
if (l->get_status() != LU_status::OK) {
|
||||
delete l;
|
||||
cs.m_factorization = nullptr;
|
||||
}
|
||||
}
|
||||
if (cs.m_factorization == nullptr) {
|
||||
if (numeric_traits<L>::precise())
|
||||
init_factorization(cs.m_factorization, cs.m_A, cs.m_basis, settings());
|
||||
}
|
||||
}
|
||||
|
||||
bool no_r_lu() const {
|
||||
return m_r_solver.m_factorization == nullptr || m_r_solver.m_factorization->get_status() == LU_status::Degenerated;
|
||||
}
|
||||
|
||||
void solve_on_signature_tableau(const lar_solution_signature & signature, const vector<unsigned> & changes_of_basis) {
|
||||
r_basis_is_OK();
|
||||
lean_assert(settings().use_tableau());
|
||||
bool r = catch_up_in_lu_tableau(changes_of_basis, m_d_solver.m_basis_heading);
|
||||
|
||||
if (!r) { // it is the case where m_d_solver gives a degenerated basis
|
||||
prepare_solver_x_with_signature_tableau(signature); // still are going to use the signature partially
|
||||
m_r_solver.find_feasible_solution();
|
||||
m_d_basis = m_r_basis;
|
||||
m_d_heading = m_r_heading;
|
||||
m_d_nbasis = m_r_nbasis;
|
||||
delete m_d_solver.m_factorization;
|
||||
m_d_solver.m_factorization = nullptr;
|
||||
} else {
|
||||
prepare_solver_x_with_signature_tableau(signature);
|
||||
m_r_solver.start_tracing_basis_changes();
|
||||
m_r_solver.find_feasible_solution();
|
||||
if (settings().get_cancel_flag())
|
||||
return;
|
||||
m_r_solver.stop_tracing_basis_changes();
|
||||
// and now catch up in the double solver
|
||||
lean_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2);
|
||||
catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver);
|
||||
}
|
||||
lean_assert(r_basis_is_OK());
|
||||
}
|
||||
|
||||
bool adjust_x_of_column(unsigned j) {
|
||||
/*
|
||||
if (m_r_solver.m_basis_heading[j] >= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_r_solver.column_is_feasible(j)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
m_r_solver.snap_column_to_bound_tableau(j);
|
||||
lean_assert(m_r_solver.column_is_feasible(j));
|
||||
m_r_solver.m_inf_set.erase(j);
|
||||
*/
|
||||
lean_assert(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool catch_up_in_lu_tableau(const vector<unsigned> & trace_of_basis_change, const vector<int> & basis_heading) {
|
||||
lean_assert(r_basis_is_OK());
|
||||
// the queues of delayed indices
|
||||
std::queue<unsigned> entr_q, leav_q;
|
||||
for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) {
|
||||
unsigned entering = trace_of_basis_change[i];
|
||||
unsigned leaving = trace_of_basis_change[i+1];
|
||||
bool good_e = basis_heading[entering] >= 0 && m_r_solver.m_basis_heading[entering] < 0;
|
||||
bool good_l = basis_heading[leaving] < 0 && m_r_solver.m_basis_heading[leaving] >= 0;
|
||||
if (!good_e && !good_l) continue;
|
||||
if (good_e && !good_l) {
|
||||
while (!leav_q.empty() && m_r_solver.m_basis_heading[leav_q.front()] < 0)
|
||||
leav_q.pop();
|
||||
if (!leav_q.empty()) {
|
||||
leaving = leav_q.front();
|
||||
leav_q.pop();
|
||||
} else {
|
||||
entr_q.push(entering);
|
||||
continue;
|
||||
}
|
||||
} else if (!good_e && good_l) {
|
||||
while (!entr_q.empty() && m_r_solver.m_basis_heading[entr_q.front()] >= 0)
|
||||
entr_q.pop();
|
||||
if (!entr_q.empty()) {
|
||||
entering = entr_q.front();
|
||||
entr_q.pop();
|
||||
} else {
|
||||
leav_q.push(leaving);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
lean_assert(m_r_solver.m_basis_heading[entering] < 0);
|
||||
lean_assert(m_r_solver.m_basis_heading[leaving] >= 0);
|
||||
m_r_solver.change_basis_unconditionally(entering, leaving);
|
||||
if(!m_r_solver.pivot_column_tableau(entering, m_r_solver.m_basis_heading[entering])) {
|
||||
// unroll the last step
|
||||
m_r_solver.change_basis_unconditionally(leaving, entering);
|
||||
#ifdef LEAN_DEBUG
|
||||
bool t =
|
||||
#endif
|
||||
m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]);
|
||||
#ifdef LEAN_DEBUG
|
||||
lean_assert(t);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
lean_assert(r_basis_is_OK());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool r_basis_is_OK() const {
|
||||
#ifdef LEAN_DEBUG
|
||||
if (!m_r_solver.m_settings.use_tableau())
|
||||
return true;
|
||||
for (unsigned j : m_r_solver.m_basis) {
|
||||
lean_assert(m_r_solver.m_A.m_columns[j].size() == 1);
|
||||
lean_assert(m_r_solver.m_A.get_val(m_r_solver.m_A.m_columns[j][0]) == one_of_type<mpq>());
|
||||
}
|
||||
for (unsigned j =0; j < m_r_solver.m_basis_heading.size(); j++) {
|
||||
if (m_r_solver.m_basis_heading[j] >= 0) continue;
|
||||
if (m_r_solver.m_column_types[j] == column_type::fixed) continue;
|
||||
lean_assert(static_cast<unsigned>(- m_r_solver.m_basis_heading[j] - 1) < m_r_solver.m_column_types.size());
|
||||
lean_assert( m_r_solver.m_basis_heading[j] <= -1);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
void solve_on_signature(const lar_solution_signature & signature, const vector<unsigned> & changes_of_basis) {
|
||||
lean_assert(!settings().use_tableau());
|
||||
if (m_r_solver.m_factorization == nullptr) {
|
||||
for (unsigned j = 0; j < changes_of_basis.size(); j+=2) {
|
||||
unsigned entering = changes_of_basis[j];
|
||||
unsigned leaving = changes_of_basis[j + 1];
|
||||
m_r_solver.change_basis_unconditionally(entering, leaving);
|
||||
}
|
||||
init_factorization(m_r_solver.m_factorization, m_r_A, m_r_basis, settings());
|
||||
} else {
|
||||
catch_up_in_lu(changes_of_basis, m_d_solver.m_basis_heading, m_r_solver);
|
||||
}
|
||||
|
||||
if (no_r_lu()) { // it is the case where m_d_solver gives a degenerated basis, we need to roll back
|
||||
// std::cout << "no_r_lu" << std::endl;
|
||||
catch_up_in_lu_in_reverse(changes_of_basis, m_r_solver);
|
||||
m_r_solver.find_feasible_solution();
|
||||
m_d_basis = m_r_basis;
|
||||
m_d_heading = m_r_heading;
|
||||
m_d_nbasis = m_r_nbasis;
|
||||
delete m_d_solver.m_factorization;
|
||||
m_d_solver.m_factorization = nullptr;
|
||||
} else {
|
||||
prepare_solver_x_with_signature(signature, m_r_solver);
|
||||
m_r_solver.start_tracing_basis_changes();
|
||||
m_r_solver.find_feasible_solution();
|
||||
if (settings().get_cancel_flag())
|
||||
return;
|
||||
m_r_solver.stop_tracing_basis_changes();
|
||||
// and now catch up in the double solver
|
||||
lean_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2);
|
||||
catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver);
|
||||
}
|
||||
}
|
||||
|
||||
void create_double_matrix(static_matrix<double, double> & A) {
|
||||
for (unsigned i = 0; i < m_r_A.row_count(); i++) {
|
||||
auto & row = m_r_A.m_rows[i];
|
||||
for (row_cell<mpq> & c : row) {
|
||||
A.set(i, c.m_j, c.get_val().get_double());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fill_basis_d(
|
||||
vector<unsigned>& basis_d,
|
||||
vector<int>& heading_d,
|
||||
vector<unsigned>& nbasis_d){
|
||||
basis_d = m_r_basis;
|
||||
heading_d = m_r_heading;
|
||||
nbasis_d = m_r_nbasis;
|
||||
}
|
||||
|
||||
template <typename L, typename K>
|
||||
void extract_signature_from_lp_core_solver(const lp_primal_core_solver<L, K> & solver, lar_solution_signature & signature) {
|
||||
signature.clear();
|
||||
lean_assert(signature.size() == 0);
|
||||
for (unsigned j = 0; j < solver.m_basis_heading.size(); j++) {
|
||||
if (solver.m_basis_heading[j] < 0) {
|
||||
signature[j] = solver.get_non_basic_column_value_position(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void get_bounds_for_double_solver() {
|
||||
unsigned n = m_n();
|
||||
m_d_low_bounds.resize(n);
|
||||
m_d_upper_bounds.resize(n);
|
||||
double delta = find_delta_for_strict_boxed_bounds().get_double();
|
||||
if (delta > 0.000001)
|
||||
delta = 0.000001;
|
||||
for (unsigned j = 0; j < n; j++) {
|
||||
if (low_bound_is_set(j)) {
|
||||
const auto & lb = m_r_solver.m_low_bounds[j];
|
||||
m_d_low_bounds[j] = lb.x.get_double() + delta * lb.y.get_double();
|
||||
}
|
||||
if (upper_bound_is_set(j)) {
|
||||
const auto & ub = m_r_solver.m_upper_bounds[j];
|
||||
m_d_upper_bounds[j] = ub.x.get_double() + delta * ub.y.get_double();
|
||||
lean_assert(!low_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_low_bounds[j]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void scale_problem_for_doubles(
|
||||
static_matrix<double, double>& A,
|
||||
vector<double> & low_bounds,
|
||||
vector<double> & upper_bounds) {
|
||||
vector<double> column_scale_vector;
|
||||
vector<double> right_side_vector(A.column_count());
|
||||
settings().reps_in_scaler = 5;
|
||||
scaler<double, double > scaler(right_side_vector,
|
||||
A,
|
||||
settings().scaling_minimum,
|
||||
settings().scaling_maximum,
|
||||
column_scale_vector,
|
||||
settings());
|
||||
if (! scaler.scale()) {
|
||||
// the scale did not succeed, unscaling
|
||||
A.clear();
|
||||
create_double_matrix(A);
|
||||
} else {
|
||||
for (unsigned j = 0; j < A.column_count(); j++) {
|
||||
if (m_r_solver.column_has_upper_bound(j)) {
|
||||
upper_bounds[j] /= column_scale_vector[j];
|
||||
}
|
||||
if (m_r_solver.column_has_low_bound(j)) {
|
||||
low_bounds[j] /= column_scale_vector[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// returns the trace of basis changes
|
||||
vector<unsigned> find_solution_signature_with_doubles(lar_solution_signature & signature) {
|
||||
if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) {
|
||||
vector<unsigned> ret;
|
||||
return ret;
|
||||
}
|
||||
get_bounds_for_double_solver();
|
||||
|
||||
extract_signature_from_lp_core_solver(m_r_solver, signature);
|
||||
prepare_solver_x_with_signature(signature, m_d_solver);
|
||||
m_d_solver.start_tracing_basis_changes();
|
||||
m_d_solver.find_feasible_solution();
|
||||
if (settings().get_cancel_flag())
|
||||
return vector<unsigned>();
|
||||
|
||||
m_d_solver.stop_tracing_basis_changes();
|
||||
extract_signature_from_lp_core_solver(m_d_solver, signature);
|
||||
return m_d_solver.m_trace_of_basis_change_vector;
|
||||
}
|
||||
|
||||
|
||||
bool low_bound_is_set(unsigned j) const {
|
||||
switch (m_column_types[j]) {
|
||||
case column_type::free_column:
|
||||
case column_type::upper_bound:
|
||||
return false;
|
||||
case column_type::low_bound:
|
||||
case column_type::boxed:
|
||||
case column_type::fixed:
|
||||
return true;
|
||||
default:
|
||||
lean_assert(false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool upper_bound_is_set(unsigned j) const {
|
||||
switch (m_column_types[j]) {
|
||||
case column_type::free_column:
|
||||
case column_type::low_bound:
|
||||
return false;
|
||||
case column_type::upper_bound:
|
||||
case column_type::boxed:
|
||||
case column_type::fixed:
|
||||
return true;
|
||||
default:
|
||||
lean_assert(false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void update_delta(mpq& delta, numeric_pair<mpq> const& l, numeric_pair<mpq> const& u) const {
|
||||
lean_assert(l <= u);
|
||||
if (l.x < u.x && l.y > u.y) {
|
||||
mpq delta1 = (u.x - l.x) / (l.y - u.y);
|
||||
if (delta1 < delta) {
|
||||
delta = delta1;
|
||||
}
|
||||
}
|
||||
lean_assert(l.x + delta * l.y <= u.x + delta * u.y);
|
||||
}
|
||||
|
||||
|
||||
mpq find_delta_for_strict_boxed_bounds() const{
|
||||
mpq delta = numeric_traits<mpq>::one();
|
||||
for (unsigned j = 0; j < m_r_A.column_count(); j++ ) {
|
||||
if (m_column_types()[j] != column_type::boxed)
|
||||
continue;
|
||||
update_delta(delta, m_r_low_bounds[j], m_r_upper_bounds[j]);
|
||||
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
|
||||
|
||||
mpq find_delta_for_strict_bounds(const mpq & initial_delta) const{
|
||||
mpq delta = initial_delta;
|
||||
for (unsigned j = 0; j < m_r_A.column_count(); j++ ) {
|
||||
if (low_bound_is_set(j))
|
||||
update_delta(delta, m_r_low_bounds[j], m_r_x[j]);
|
||||
if (upper_bound_is_set(j))
|
||||
update_delta(delta, m_r_x[j], m_r_upper_bounds[j]);
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
|
||||
void init_column_row_nz_for_r_solver() {
|
||||
m_r_solver.init_column_row_non_zeroes();
|
||||
}
|
||||
|
||||
linear_combination_iterator<mpq> * get_column_iterator(unsigned j) {
|
||||
if (settings().use_tableau()) {
|
||||
return new iterator_on_column<mpq, numeric_pair<mpq>>(m_r_solver.m_A.m_columns[j], m_r_solver.m_A);
|
||||
} else {
|
||||
m_r_solver.solve_Bd(j);
|
||||
return new iterator_on_indexed_vector<mpq>(m_r_solver.m_ed);
|
||||
}
|
||||
}
|
||||
|
||||
};
|
||||
}
|
293
src/util/lp/lar_core_solver.hpp
Normal file
293
src/util/lp/lar_core_solver.hpp
Normal file
|
@ -0,0 +1,293 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include <string>
|
||||
#include "util/vector.h"
|
||||
#include "util/lp/lar_core_solver.h"
|
||||
#include "util/lp/lar_solution_signature.h"
|
||||
namespace lean {
|
||||
lar_core_solver::lar_core_solver(
|
||||
lp_settings & settings,
|
||||
const column_namer & column_names
|
||||
):
|
||||
m_infeasible_sum_sign(0),
|
||||
m_r_solver(m_r_A,
|
||||
m_right_sides_dummy,
|
||||
m_r_x,
|
||||
m_r_basis,
|
||||
m_r_nbasis,
|
||||
m_r_heading,
|
||||
m_costs_dummy,
|
||||
m_column_types(),
|
||||
m_r_low_bounds(),
|
||||
m_r_upper_bounds(),
|
||||
settings,
|
||||
column_names),
|
||||
m_d_solver(m_d_A,
|
||||
m_d_right_sides_dummy,
|
||||
m_d_x,
|
||||
m_d_basis,
|
||||
m_d_nbasis,
|
||||
m_d_heading,
|
||||
m_d_costs_dummy,
|
||||
m_column_types(),
|
||||
m_d_low_bounds,
|
||||
m_d_upper_bounds,
|
||||
settings,
|
||||
column_names){}
|
||||
|
||||
void lar_core_solver::init_costs(bool first_time) {
|
||||
lean_assert(false); // should not be called
|
||||
// lean_assert(this->m_x.size() >= this->m_n());
|
||||
// lean_assert(this->m_column_types.size() >= this->m_n());
|
||||
// if (first_time)
|
||||
// this->m_costs.resize(this->m_n());
|
||||
// X inf = this->m_infeasibility;
|
||||
// this->m_infeasibility = zero_of_type<X>();
|
||||
// for (unsigned j = this->m_n(); j--;)
|
||||
// init_cost_for_column(j);
|
||||
// if (!(first_time || inf >= this->m_infeasibility)) {
|
||||
// LP_OUT(this->m_settings, "iter = " << this->total_iterations() << std::endl);
|
||||
// LP_OUT(this->m_settings, "inf was " << T_to_string(inf) << " and now " << T_to_string(this->m_infeasibility) << std::endl);
|
||||
// lean_assert(false);
|
||||
// }
|
||||
// if (inf == this->m_infeasibility)
|
||||
// this->m_iters_with_no_cost_growing++;
|
||||
}
|
||||
|
||||
|
||||
void lar_core_solver::init_cost_for_column(unsigned j) {
|
||||
/*
|
||||
// If j is a breakpoint column, then we set the cost zero.
|
||||
// When anylyzing an entering column candidate we update the cost of the breakpoints columns to get the left or the right derivative if the infeasibility function
|
||||
const numeric_pair<mpq> & x = this->m_x[j];
|
||||
// set zero cost for each non-basis column
|
||||
if (this->m_basis_heading[j] < 0) {
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
return;
|
||||
}
|
||||
// j is a basis column
|
||||
switch (this->m_column_types[j]) {
|
||||
case fixed:
|
||||
case column_type::boxed:
|
||||
if (x > this->m_upper_bounds[j]) {
|
||||
this->m_costs[j] = 1;
|
||||
this->m_infeasibility += x - this->m_upper_bounds[j];
|
||||
} else if (x < this->m_low_bounds[j]) {
|
||||
this->m_infeasibility += this->m_low_bounds[j] - x;
|
||||
this->m_costs[j] = -1;
|
||||
} else {
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
break;
|
||||
case low_bound:
|
||||
if (x < this->m_low_bounds[j]) {
|
||||
this->m_costs[j] = -1;
|
||||
this->m_infeasibility += this->m_low_bounds[j] - x;
|
||||
} else {
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
break;
|
||||
case upper_bound:
|
||||
if (x > this->m_upper_bounds[j]) {
|
||||
this->m_costs[j] = 1;
|
||||
this->m_infeasibility += x - this->m_upper_bounds[j];
|
||||
} else {
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
break;
|
||||
case free_column:
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}*/
|
||||
}
|
||||
|
||||
|
||||
// returns m_sign_of_alpha_r
|
||||
int lar_core_solver::column_is_out_of_bounds(unsigned j) {
|
||||
/*
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
case column_type::boxed:
|
||||
if (this->x_below_low_bound(j)) {
|
||||
return -1;
|
||||
}
|
||||
if (this->x_above_upper_bound(j)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
case low_bound:
|
||||
if (this->x_below_low_bound(j)) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
case upper_bound:
|
||||
if (this->x_above_upper_bound(j)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
break;
|
||||
}*/
|
||||
lean_assert(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void lar_core_solver::calculate_pivot_row(unsigned i) {
|
||||
lean_assert(!m_r_solver.use_tableau());
|
||||
lean_assert(m_r_solver.m_pivot_row.is_OK());
|
||||
m_r_solver.m_pivot_row_of_B_1.clear();
|
||||
m_r_solver.m_pivot_row_of_B_1.resize(m_r_solver.m_m());
|
||||
m_r_solver.m_pivot_row.clear();
|
||||
m_r_solver.m_pivot_row.resize(m_r_solver.m_n());
|
||||
if (m_r_solver.m_settings.use_tableau()) {
|
||||
unsigned basis_j = m_r_solver.m_basis[i];
|
||||
for (auto & c : m_r_solver.m_A.m_rows[i]) {
|
||||
if (c.m_j != basis_j)
|
||||
m_r_solver.m_pivot_row.set_value(c.get_val(), c.m_j);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
m_r_solver.calculate_pivot_row_of_B_1(i);
|
||||
m_r_solver.calculate_pivot_row_when_pivot_row_of_B1_is_ready(i);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void lar_core_solver::prefix_r() {
|
||||
if (!m_r_solver.m_settings.use_tableau()) {
|
||||
m_r_solver.m_copy_of_xB.resize(m_r_solver.m_n());
|
||||
m_r_solver.m_ed.resize(m_r_solver.m_m());
|
||||
m_r_solver.m_pivot_row.resize(m_r_solver.m_n());
|
||||
m_r_solver.m_pivot_row_of_B_1.resize(m_r_solver.m_m());
|
||||
m_r_solver.m_w.resize(m_r_solver.m_m());
|
||||
m_r_solver.m_y.resize(m_r_solver.m_m());
|
||||
m_r_solver.m_rows_nz.resize(m_r_solver.m_m(), 0);
|
||||
m_r_solver.m_columns_nz.resize(m_r_solver.m_n(), 0);
|
||||
init_column_row_nz_for_r_solver();
|
||||
}
|
||||
|
||||
m_r_solver.m_b.resize(m_r_solver.m_m());
|
||||
if (m_r_solver.m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) {
|
||||
if(m_r_solver.m_settings.use_breakpoints_in_feasibility_search)
|
||||
m_r_solver.m_breakpoint_indices_queue.resize(m_r_solver.m_n());
|
||||
m_r_solver.m_costs.resize(m_r_solver.m_n());
|
||||
m_r_solver.m_d.resize(m_r_solver.m_n());
|
||||
m_r_solver.m_using_infeas_costs = true;
|
||||
}
|
||||
}
|
||||
|
||||
void lar_core_solver::prefix_d() {
|
||||
m_d_solver.m_b.resize(m_d_solver.m_m());
|
||||
m_d_solver.m_breakpoint_indices_queue.resize(m_d_solver.m_n());
|
||||
m_d_solver.m_copy_of_xB.resize(m_d_solver.m_n());
|
||||
m_d_solver.m_costs.resize(m_d_solver.m_n());
|
||||
m_d_solver.m_d.resize(m_d_solver.m_n());
|
||||
m_d_solver.m_ed.resize(m_d_solver.m_m());
|
||||
m_d_solver.m_pivot_row.resize(m_d_solver.m_n());
|
||||
m_d_solver.m_pivot_row_of_B_1.resize(m_d_solver.m_m());
|
||||
m_d_solver.m_w.resize(m_d_solver.m_m());
|
||||
m_d_solver.m_y.resize(m_d_solver.m_m());
|
||||
m_d_solver.m_steepest_edge_coefficients.resize(m_d_solver.m_n());
|
||||
m_d_solver.m_column_norms.clear();
|
||||
m_d_solver.m_column_norms.resize(m_d_solver.m_n(), 2);
|
||||
m_d_solver.m_inf_set.clear();
|
||||
m_d_solver.m_inf_set.resize(m_d_solver.m_n());
|
||||
}
|
||||
|
||||
void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() {
|
||||
lean_assert(m_r_solver.A_mult_x_is_off() == false);
|
||||
unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau];
|
||||
m_infeasible_sum_sign = m_r_solver.inf_sign_of_column(bj);
|
||||
m_infeasible_linear_combination.clear();
|
||||
for (auto & rc : m_r_solver.m_A.m_rows[m_r_solver.m_inf_row_index_for_tableau]) {
|
||||
m_infeasible_linear_combination.push_back(std::make_pair( rc.get_val(), rc.m_j));
|
||||
}
|
||||
}
|
||||
|
||||
void lar_core_solver::fill_not_improvable_zero_sum() {
|
||||
if (m_r_solver.m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) {
|
||||
fill_not_improvable_zero_sum_from_inf_row();
|
||||
return;
|
||||
}
|
||||
// reusing the existing mechanism for row_feasibility_loop
|
||||
m_infeasible_sum_sign = m_r_solver.m_settings.use_breakpoints_in_feasibility_search? -1 : 1;
|
||||
m_infeasible_linear_combination.clear();
|
||||
for (auto j : m_r_solver.m_basis) {
|
||||
const mpq & cost_j = m_r_solver.m_costs[j];
|
||||
if (!numeric_traits<mpq>::is_zero(cost_j)) {
|
||||
m_infeasible_linear_combination.push_back(std::make_pair(cost_j, j));
|
||||
}
|
||||
}
|
||||
// m_costs are expressed by m_d ( additional costs), substructing the latter gives 0
|
||||
for (unsigned j = 0; j < m_r_solver.m_n(); j++) {
|
||||
if (m_r_solver.m_basis_heading[j] >= 0) continue;
|
||||
const mpq & d_j = m_r_solver.m_d[j];
|
||||
if (!numeric_traits<mpq>::is_zero(d_j)) {
|
||||
m_infeasible_linear_combination.push_back(std::make_pair(-d_j, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void lar_core_solver::solve() {
|
||||
lean_assert(m_r_solver.non_basic_columns_are_set_correctly());
|
||||
lean_assert(m_r_solver.inf_set_is_correct());
|
||||
if (m_r_solver.current_x_is_feasible() && m_r_solver.m_look_for_feasible_solution_only) {
|
||||
m_r_solver.set_status(OPTIMAL);
|
||||
return;
|
||||
}
|
||||
++settings().st().m_need_to_solve_inf;
|
||||
lean_assert(!m_r_solver.A_mult_x_is_off());
|
||||
lean_assert((!settings().use_tableau()) || r_basis_is_OK());
|
||||
if (need_to_presolve_with_double_solver()) {
|
||||
prefix_d();
|
||||
lar_solution_signature solution_signature;
|
||||
vector<unsigned> changes_of_basis = find_solution_signature_with_doubles(solution_signature);
|
||||
if (m_d_solver.get_status() == TIME_EXHAUSTED) {
|
||||
m_r_solver.set_status(TIME_EXHAUSTED);
|
||||
return;
|
||||
}
|
||||
if (settings().use_tableau())
|
||||
solve_on_signature_tableau(solution_signature, changes_of_basis);
|
||||
else
|
||||
solve_on_signature(solution_signature, changes_of_basis);
|
||||
lean_assert(!settings().use_tableau() || r_basis_is_OK());
|
||||
} else {
|
||||
if (!settings().use_tableau()) {
|
||||
bool snapped = m_r_solver.snap_non_basic_x_to_bound();
|
||||
lean_assert(m_r_solver.non_basic_columns_are_set_correctly());
|
||||
if (snapped)
|
||||
m_r_solver.solve_Ax_eq_b();
|
||||
}
|
||||
if (m_r_solver.m_look_for_feasible_solution_only)
|
||||
m_r_solver.find_feasible_solution();
|
||||
else
|
||||
m_r_solver.solve();
|
||||
lean_assert(!settings().use_tableau() || r_basis_is_OK());
|
||||
}
|
||||
if (m_r_solver.get_status() == INFEASIBLE) {
|
||||
fill_not_improvable_zero_sum();
|
||||
} else if (m_r_solver.get_status() != UNBOUNDED) {
|
||||
m_r_solver.set_status(OPTIMAL);
|
||||
}
|
||||
lean_assert(r_basis_is_OK());
|
||||
lean_assert(m_r_solver.non_basic_columns_are_set_correctly());
|
||||
lean_assert(m_r_solver.inf_set_is_correct());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
10
src/util/lp/lar_core_solver_instances.cpp
Normal file
10
src/util/lp/lar_core_solver_instances.cpp
Normal file
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "util/vector.h"
|
||||
#include <functional>
|
||||
#include "util/lp/lar_core_solver.hpp"
|
13
src/util/lp/lar_solution_signature.h
Normal file
13
src/util/lp/lar_solution_signature.h
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "util/vector.h"
|
||||
#include "util/debug.h"
|
||||
#include "util/lp/lp_settings.h"
|
||||
#include <unordered_map>
|
||||
namespace lean {
|
||||
typedef std::unordered_map<unsigned, non_basic_column_value_position> lar_solution_signature;
|
||||
}
|
1549
src/util/lp/lar_solver.h
Normal file
1549
src/util/lp/lar_solver.h
Normal file
File diff suppressed because it is too large
Load diff
64
src/util/lp/lar_term.h
Normal file
64
src/util/lp/lar_term.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/lp/indexed_vector.h"
|
||||
namespace lean {
|
||||
struct lar_term {
|
||||
// the term evaluates to sum of m_coeffs + m_v
|
||||
std::unordered_map<unsigned, mpq> m_coeffs;
|
||||
mpq m_v;
|
||||
lar_term() {}
|
||||
void add_to_map(unsigned j, const mpq& c) {
|
||||
auto it = m_coeffs.find(j);
|
||||
if (it == m_coeffs.end()) {
|
||||
m_coeffs.emplace(j, c);
|
||||
} else {
|
||||
it->second += c;
|
||||
if (is_zero(it->second))
|
||||
m_coeffs.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned size() const { return static_cast<unsigned>(m_coeffs.size()); }
|
||||
|
||||
const std::unordered_map<unsigned, mpq> & coeffs() const {
|
||||
return m_coeffs;
|
||||
}
|
||||
|
||||
lar_term(const vector<std::pair<mpq, unsigned>>& coeffs,
|
||||
const mpq & v) : m_v(v) {
|
||||
for (const auto & p : coeffs) {
|
||||
add_to_map(p.second, p.first);
|
||||
}
|
||||
}
|
||||
bool operator==(const lar_term & a) const { return false; } // take care not to create identical terms
|
||||
bool operator!=(const lar_term & a) const { return ! (*this == a);}
|
||||
// some terms get used in add constraint
|
||||
// it is the same as the offset in the m_constraints
|
||||
|
||||
vector<std::pair<mpq, unsigned>> coeffs_as_vector() const {
|
||||
vector<std::pair<mpq, unsigned>> ret;
|
||||
for (const auto & p : m_coeffs) {
|
||||
ret.push_back(std::make_pair(p.second, p.first));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
// j is the basic variable to substitute
|
||||
void subst(unsigned j, indexed_vector<mpq> & li) {
|
||||
auto it = m_coeffs.find(j);
|
||||
if (it == m_coeffs.end()) return;
|
||||
const mpq & b = it->second;
|
||||
for (unsigned it_j :li.m_index) {
|
||||
add_to_map(it_j, - b * li.m_data[it_j]);
|
||||
}
|
||||
m_coeffs.erase(it);
|
||||
}
|
||||
|
||||
bool contains(unsigned j) const {
|
||||
return m_coeffs.find(j) != m_coeffs.end();
|
||||
}
|
||||
};
|
||||
}
|
50
src/util/lp/linear_combination_iterator.h
Normal file
50
src/util/lp/linear_combination_iterator.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
struct linear_combination_iterator {
|
||||
virtual bool next(T & a, unsigned & i) = 0;
|
||||
virtual bool next(unsigned & i) = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual linear_combination_iterator * clone() = 0;
|
||||
virtual ~linear_combination_iterator(){}
|
||||
virtual unsigned size() const = 0;
|
||||
};
|
||||
template <typename T>
|
||||
struct linear_combination_iterator_on_vector : linear_combination_iterator<T> {
|
||||
vector<std::pair<T, unsigned>> & m_vector;
|
||||
int m_offset;
|
||||
bool next(T & a, unsigned & i) {
|
||||
if(m_offset >= m_vector.size())
|
||||
return false;
|
||||
auto & p = m_vector[m_offset];
|
||||
a = p.first;
|
||||
i = p.second;
|
||||
m_offset++;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool next(unsigned & i) {
|
||||
if(m_offset >= m_vector.size())
|
||||
return false;
|
||||
auto & p = m_vector[m_offset];
|
||||
i = p.second;
|
||||
m_offset++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void reset() {m_offset = 0;}
|
||||
linear_combination_iterator<T> * clone() {
|
||||
return new linear_combination_iterator_on_vector(m_vector);
|
||||
}
|
||||
linear_combination_iterator_on_vector(vector<std::pair<T, unsigned>> & vec):
|
||||
m_vector(vec),
|
||||
m_offset(0)
|
||||
{}
|
||||
unsigned size() const { return m_vector.size(); }
|
||||
};
|
||||
|
||||
}
|
690
src/util/lp/lp_core_solver_base.h
Normal file
690
src/util/lp/lp_core_solver_base.h
Normal file
|
@ -0,0 +1,690 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include <set>
|
||||
#include "util/vector.h"
|
||||
#include <string>
|
||||
#include "util/lp/lp_utils.h"
|
||||
#include "util/lp/core_solver_pretty_printer.h"
|
||||
#include "util/lp/numeric_pair.h"
|
||||
#include "util/lp/static_matrix.h"
|
||||
#include "util/lp/lu.h"
|
||||
#include "util/lp/permutation_matrix.h"
|
||||
#include "util/lp/column_namer.h"
|
||||
namespace lean {
|
||||
|
||||
template <typename T, typename X> // X represents the type of the x variable and the bounds
|
||||
class lp_core_solver_base {
|
||||
unsigned m_total_iterations;
|
||||
unsigned m_iters_with_no_cost_growing;
|
||||
unsigned inc_total_iterations() { ++m_settings.st().m_total_iterations; return m_total_iterations++; }
|
||||
private:
|
||||
lp_status m_status;
|
||||
public:
|
||||
bool current_x_is_feasible() const { return m_inf_set.size() == 0; }
|
||||
bool current_x_is_infeasible() const { return m_inf_set.size() != 0; }
|
||||
int_set m_inf_set;
|
||||
bool m_using_infeas_costs;
|
||||
|
||||
|
||||
vector<unsigned> m_columns_nz; // m_columns_nz[i] keeps an approximate value of non zeroes the i-th column
|
||||
vector<unsigned> m_rows_nz; // m_rows_nz[i] keeps an approximate value of non zeroes in the i-th row
|
||||
indexed_vector<T> m_pivot_row_of_B_1; // the pivot row of the reverse of B
|
||||
indexed_vector<T> m_pivot_row; // this is the real pivot row of the simplex tableu
|
||||
static_matrix<T, X> & m_A; // the matrix A
|
||||
vector<X> & m_b; // the right side
|
||||
vector<unsigned> & m_basis;
|
||||
vector<unsigned>& m_nbasis;
|
||||
vector<int>& m_basis_heading;
|
||||
vector<X> & m_x; // a feasible solution, the fist time set in the constructor
|
||||
vector<T> & m_costs;
|
||||
lp_settings & m_settings;
|
||||
vector<T> m_y; // the buffer for yB = cb
|
||||
// a device that is able to solve Bx=c, xB=d, and change the basis
|
||||
lu<T, X> * m_factorization;
|
||||
const column_namer & m_column_names;
|
||||
indexed_vector<T> m_w; // the vector featuring in 24.3 of the Chvatal book
|
||||
vector<T> m_d; // the vector of reduced costs
|
||||
indexed_vector<T> m_ed; // the solution of B*m_ed = a
|
||||
const vector<column_type> & m_column_types;
|
||||
const vector<X> & m_low_bounds;
|
||||
const vector<X> & m_upper_bounds;
|
||||
vector<T> m_column_norms; // the approximate squares of column norms that help choosing a profitable column
|
||||
vector<X> m_copy_of_xB;
|
||||
unsigned m_basis_sort_counter;
|
||||
vector<T> m_steepest_edge_coefficients;
|
||||
vector<unsigned> m_trace_of_basis_change_vector; // the even positions are entering, the odd positions are leaving
|
||||
bool m_tracing_basis_changes;
|
||||
int_set* m_pivoted_rows;
|
||||
bool m_look_for_feasible_solution_only;
|
||||
void start_tracing_basis_changes() {
|
||||
m_trace_of_basis_change_vector.resize(0);
|
||||
m_tracing_basis_changes = true;
|
||||
}
|
||||
|
||||
void stop_tracing_basis_changes() {
|
||||
m_tracing_basis_changes = false;
|
||||
}
|
||||
|
||||
void trace_basis_change(unsigned entering, unsigned leaving) {
|
||||
unsigned size = m_trace_of_basis_change_vector.size();
|
||||
if (size >= 2 && m_trace_of_basis_change_vector[size-2] == leaving
|
||||
&& m_trace_of_basis_change_vector[size -1] == entering) {
|
||||
m_trace_of_basis_change_vector.pop_back();
|
||||
m_trace_of_basis_change_vector.pop_back();
|
||||
} else {
|
||||
m_trace_of_basis_change_vector.push_back(entering);
|
||||
m_trace_of_basis_change_vector.push_back(leaving);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned m_m() const { return m_A.row_count(); } // it is the length of basis. The matrix m_A has m_m rows and the dimension of the matrix A is m_m
|
||||
unsigned m_n() const { return m_A.column_count(); } // the number of columns in the matrix m_A
|
||||
|
||||
lp_core_solver_base(static_matrix<T, X> & A,
|
||||
vector<X> & b, // the right side vector
|
||||
vector<unsigned> & basis,
|
||||
vector<unsigned> & nbasis,
|
||||
vector<int> & heading,
|
||||
vector<X> & x,
|
||||
vector<T> & costs,
|
||||
lp_settings & settings,
|
||||
const column_namer& column_names,
|
||||
const vector<column_type> & column_types,
|
||||
const vector<X> & low_bound_values,
|
||||
const vector<X> & upper_bound_values);
|
||||
|
||||
void allocate_basis_heading();
|
||||
void init();
|
||||
|
||||
virtual ~lp_core_solver_base() {
|
||||
if (m_factorization != nullptr)
|
||||
delete m_factorization;
|
||||
}
|
||||
|
||||
vector<unsigned> & non_basis() {
|
||||
return m_nbasis;
|
||||
}
|
||||
|
||||
const vector<unsigned> & non_basis() const { return m_nbasis; }
|
||||
|
||||
|
||||
|
||||
void set_status(lp_status status) {
|
||||
m_status = status;
|
||||
}
|
||||
lp_status get_status() const{
|
||||
return m_status;
|
||||
}
|
||||
|
||||
void fill_cb(T * y);
|
||||
|
||||
void fill_cb(vector<T> & y);
|
||||
|
||||
void solve_yB(vector<T> & y);
|
||||
|
||||
void solve_Bd(unsigned entering);
|
||||
|
||||
void solve_Bd(unsigned entering, indexed_vector<T> & column);
|
||||
|
||||
void pretty_print(std::ostream & out);
|
||||
|
||||
void save_state(T * w_buffer, T * d_buffer);
|
||||
|
||||
void restore_state(T * w_buffer, T * d_buffer);
|
||||
|
||||
X get_cost() {
|
||||
return dot_product(m_costs, m_x);
|
||||
}
|
||||
|
||||
void copy_m_w(T * buffer);
|
||||
|
||||
void restore_m_w(T * buffer);
|
||||
|
||||
// needed for debugging
|
||||
void copy_m_ed(T * buffer);
|
||||
|
||||
void restore_m_ed(T * buffer);
|
||||
|
||||
bool A_mult_x_is_off() const;
|
||||
|
||||
bool A_mult_x_is_off_on_index(const vector<unsigned> & index) const;
|
||||
// from page 182 of Istvan Maros's book
|
||||
void calculate_pivot_row_of_B_1(unsigned pivot_row);
|
||||
|
||||
void calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row);
|
||||
|
||||
void update_x(unsigned entering, const X & delta);
|
||||
|
||||
const T & get_var_value(unsigned j) const {
|
||||
return m_x[j];
|
||||
}
|
||||
|
||||
void print_statistics(char const* str, X cost, std::ostream & message_stream);
|
||||
|
||||
bool print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream & message_stream);
|
||||
|
||||
bool print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const* str, std::ostream & message_stream);
|
||||
|
||||
bool print_statistics_with_cost_and_check_that_the_time_is_over(X cost, std::ostream & message_stream);
|
||||
|
||||
unsigned total_iterations() const { return m_total_iterations; }
|
||||
|
||||
void set_total_iterations(unsigned s) { m_total_iterations = s; }
|
||||
|
||||
void set_non_basic_x_to_correct_bounds();
|
||||
|
||||
bool at_bound(const X &x, const X & bound) const {
|
||||
return !below_bound(x, bound) && !above_bound(x, bound);
|
||||
}
|
||||
|
||||
|
||||
bool need_to_pivot_to_basis_tableau() const {
|
||||
lean_assert(m_A.is_correct());
|
||||
unsigned m = m_A.row_count();
|
||||
for (unsigned i = 0; i < m; i++) {
|
||||
unsigned bj = m_basis[i];
|
||||
lean_assert(m_A.m_columns[bj].size() > 0);
|
||||
if (m_A.m_columns[bj].size() > 1 || m_A.get_val(m_A.m_columns[bj][0]) != one_of_type<mpq>()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool reduced_costs_are_correct_tableau() const {
|
||||
if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows)
|
||||
return true;
|
||||
lean_assert(m_A.is_correct());
|
||||
if (m_using_infeas_costs) {
|
||||
if (infeasibility_costs_are_correct() == false) {
|
||||
std::cout << "infeasibility_costs_are_correct() does not hold" << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned n = m_A.column_count();
|
||||
for (unsigned j = 0; j < n; j++) {
|
||||
if (m_basis_heading[j] >= 0) {
|
||||
if (!is_zero(m_d[j])) {
|
||||
|
||||
std::cout << "case a\n";
|
||||
print_column_info(j, std::cout);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
auto d = m_costs[j];
|
||||
for (auto & cc : this->m_A.m_columns[j]) {
|
||||
d -= this->m_costs[this->m_basis[cc.m_i]] * this->m_A.get_val(cc);
|
||||
}
|
||||
if (m_d[j] != d) {
|
||||
std::cout << "case b\n";
|
||||
print_column_info(j, std::cout);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool below_bound(const X & x, const X & bound) const {
|
||||
if (precise()) return x < bound;
|
||||
return below_bound_numeric<X>(x, bound, m_settings.primal_feasibility_tolerance);
|
||||
}
|
||||
|
||||
bool above_bound(const X & x, const X & bound) const {
|
||||
if (precise()) return x > bound;
|
||||
return above_bound_numeric<X>(x, bound, m_settings.primal_feasibility_tolerance);
|
||||
}
|
||||
|
||||
bool x_below_low_bound(unsigned p) const {
|
||||
return below_bound(m_x[p], m_low_bounds[p]);
|
||||
}
|
||||
|
||||
bool infeasibility_costs_are_correct() const;
|
||||
bool infeasibility_cost_is_correct_for_column(unsigned j) const;
|
||||
|
||||
bool x_above_low_bound(unsigned p) const {
|
||||
return above_bound(m_x[p], m_low_bounds[p]);
|
||||
}
|
||||
|
||||
bool x_below_upper_bound(unsigned p) const {
|
||||
return below_bound(m_x[p], m_upper_bounds[p]);
|
||||
}
|
||||
|
||||
|
||||
bool x_above_upper_bound(unsigned p) const {
|
||||
return above_bound(m_x[p], m_upper_bounds[p]);
|
||||
}
|
||||
bool x_is_at_low_bound(unsigned j) const {
|
||||
return at_bound(m_x[j], m_low_bounds[j]);
|
||||
}
|
||||
bool x_is_at_upper_bound(unsigned j) const {
|
||||
return at_bound(m_x[j], m_upper_bounds[j]);
|
||||
}
|
||||
|
||||
bool x_is_at_bound(unsigned j) const {
|
||||
return x_is_at_low_bound(j) || x_is_at_upper_bound(j);
|
||||
}
|
||||
bool column_is_feasible(unsigned j) const;
|
||||
|
||||
bool calc_current_x_is_feasible_include_non_basis() const;
|
||||
|
||||
bool inf_set_is_correct() const;
|
||||
|
||||
bool column_is_dual_feasible(unsigned j) const;
|
||||
|
||||
bool d_is_not_negative(unsigned j) const;
|
||||
|
||||
bool d_is_not_positive(unsigned j) const;
|
||||
|
||||
|
||||
bool time_is_over();
|
||||
|
||||
void rs_minus_Anx(vector<X> & rs);
|
||||
|
||||
bool find_x_by_solving();
|
||||
|
||||
bool update_basis_and_x(int entering, int leaving, X const & tt);
|
||||
|
||||
bool basis_has_no_doubles() const;
|
||||
|
||||
bool non_basis_has_no_doubles() const;
|
||||
|
||||
bool basis_is_correctly_represented_in_heading() const ;
|
||||
bool non_basis_is_correctly_represented_in_heading() const ;
|
||||
|
||||
bool basis_heading_is_correct() const;
|
||||
|
||||
void restore_x_and_refactor(int entering, int leaving, X const & t);
|
||||
|
||||
void restore_x(unsigned entering, X const & t);
|
||||
|
||||
void fill_reduced_costs_from_m_y_by_rows();
|
||||
|
||||
void copy_rs_to_xB(vector<X> & rs);
|
||||
virtual bool low_bounds_are_set() const { return false; }
|
||||
X low_bound_value(unsigned j) const { return m_low_bounds[j]; }
|
||||
X upper_bound_value(unsigned j) const { return m_upper_bounds[j]; }
|
||||
|
||||
column_type get_column_type(unsigned j) const {return m_column_types[j]; }
|
||||
|
||||
bool pivot_row_element_is_too_small_for_ratio_test(unsigned j) {
|
||||
return m_settings.abs_val_is_smaller_than_pivot_tolerance(m_pivot_row[j]);
|
||||
}
|
||||
|
||||
X bound_span(unsigned j) const {
|
||||
return m_upper_bounds[j] - m_low_bounds[j];
|
||||
}
|
||||
|
||||
std::string column_name(unsigned column) const;
|
||||
|
||||
void copy_right_side(vector<X> & rs);
|
||||
|
||||
void add_delta_to_xB(vector<X> & del);
|
||||
|
||||
void find_error_in_BxB(vector<X>& rs);
|
||||
|
||||
// recalculates the projection of x to B, such that Ax = b, whereab is the right side
|
||||
void solve_Ax_eq_b();
|
||||
|
||||
bool snap_non_basic_x_to_bound() {
|
||||
bool ret = false;
|
||||
for (unsigned j : non_basis())
|
||||
ret = snap_column_to_bound(j) || ret;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool snap_column_to_bound(unsigned j) {
|
||||
switch (m_column_types[j]) {
|
||||
case column_type::fixed:
|
||||
if (x_is_at_bound(j))
|
||||
break;
|
||||
m_x[j] = m_low_bounds[j];
|
||||
return true;
|
||||
case column_type::boxed:
|
||||
if (x_is_at_bound(j))
|
||||
break; // we should preserve x if possible
|
||||
// snap randomly
|
||||
if (m_settings.random_next() % 2 == 1)
|
||||
m_x[j] = m_low_bounds[j];
|
||||
else
|
||||
m_x[j] = m_upper_bounds[j];
|
||||
return true;
|
||||
case column_type::low_bound:
|
||||
if (x_is_at_low_bound(j))
|
||||
break;
|
||||
m_x[j] = m_low_bounds[j];
|
||||
return true;
|
||||
case column_type::upper_bound:
|
||||
if (x_is_at_upper_bound(j))
|
||||
break;
|
||||
m_x[j] = m_upper_bounds[j];
|
||||
return true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool make_column_feasible(unsigned j, numeric_pair<mpq> & delta) {
|
||||
lean_assert(m_basis_heading[j] < 0);
|
||||
auto & x = m_x[j];
|
||||
switch (m_column_types[j]) {
|
||||
case column_type::fixed:
|
||||
lean_assert(m_low_bounds[j] == m_upper_bounds[j]);
|
||||
if (x != m_low_bounds[j]) {
|
||||
delta = m_low_bounds[j] - x;
|
||||
x = m_low_bounds[j];
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case column_type::boxed:
|
||||
if (x < m_low_bounds[j]) {
|
||||
delta = m_low_bounds[j] - x;
|
||||
x = m_low_bounds[j];
|
||||
return true;
|
||||
}
|
||||
if (x > m_upper_bounds[j]) {
|
||||
delta = m_upper_bounds[j] - x;
|
||||
x = m_upper_bounds[j];
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case column_type::low_bound:
|
||||
if (x < m_low_bounds[j]) {
|
||||
delta = m_low_bounds[j] - x;
|
||||
x = m_low_bounds[j];
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case column_type::upper_bound:
|
||||
if (x > m_upper_bounds[j]) {
|
||||
delta = m_upper_bounds[j] - x;
|
||||
x = m_upper_bounds[j];
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case column_type::free_column:
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
void snap_non_basic_x_to_bound_and_free_to_zeroes();
|
||||
void snap_xN_to_bounds_and_fill_xB();
|
||||
|
||||
void snap_xN_to_bounds_and_free_columns_to_zeroes();
|
||||
|
||||
void init_reduced_costs_for_one_iteration();
|
||||
|
||||
non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const;
|
||||
|
||||
void init_lu();
|
||||
int pivots_in_column_and_row_are_different(int entering, int leaving) const;
|
||||
void pivot_fixed_vars_from_basis();
|
||||
bool pivot_for_tableau_on_basis();
|
||||
bool pivot_row_for_tableau_on_basis(unsigned row);
|
||||
void init_basic_part_of_basis_heading() {
|
||||
unsigned m = m_basis.size();
|
||||
for (unsigned i = 0; i < m; i++) {
|
||||
unsigned column = m_basis[i];
|
||||
m_basis_heading[column] = i;
|
||||
}
|
||||
}
|
||||
|
||||
void init_non_basic_part_of_basis_heading() {
|
||||
this->m_nbasis.clear();
|
||||
for (int j = m_basis_heading.size(); j--;){
|
||||
if (m_basis_heading[j] < 0) {
|
||||
m_nbasis.push_back(j);
|
||||
// the index of column j in m_nbasis is (- basis_heading[j] - 1)
|
||||
m_basis_heading[j] = - static_cast<int>(m_nbasis.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void init_basis_heading_and_non_basic_columns_vector() {
|
||||
m_basis_heading.resize(0);
|
||||
m_basis_heading.resize(m_n(), -1);
|
||||
init_basic_part_of_basis_heading();
|
||||
init_non_basic_part_of_basis_heading();
|
||||
}
|
||||
|
||||
void change_basis_unconditionally(unsigned entering, unsigned leaving) {
|
||||
lean_assert(m_basis_heading[entering] < 0);
|
||||
int place_in_non_basis = -1 - m_basis_heading[entering];
|
||||
if (static_cast<unsigned>(place_in_non_basis) >= m_nbasis.size()) {
|
||||
// entering variable in not in m_nbasis, we need to put it back;
|
||||
m_basis_heading[entering] = place_in_non_basis = m_nbasis.size();
|
||||
m_nbasis.push_back(entering);
|
||||
}
|
||||
|
||||
int place_in_basis = m_basis_heading[leaving];
|
||||
m_basis_heading[entering] = place_in_basis;
|
||||
m_basis[place_in_basis] = entering;
|
||||
m_basis_heading[leaving] = -place_in_non_basis - 1;
|
||||
m_nbasis[place_in_non_basis] = leaving;
|
||||
if (m_tracing_basis_changes)
|
||||
trace_basis_change(entering, leaving);
|
||||
|
||||
}
|
||||
|
||||
void change_basis(unsigned entering, unsigned leaving) {
|
||||
lean_assert(m_basis_heading[entering] < 0);
|
||||
|
||||
int place_in_basis = m_basis_heading[leaving];
|
||||
int place_in_non_basis = - m_basis_heading[entering] - 1;
|
||||
m_basis_heading[entering] = place_in_basis;
|
||||
m_basis[place_in_basis] = entering;
|
||||
|
||||
m_basis_heading[leaving] = -place_in_non_basis - 1;
|
||||
m_nbasis[place_in_non_basis] = leaving;
|
||||
|
||||
if (m_tracing_basis_changes)
|
||||
trace_basis_change(entering, leaving);
|
||||
}
|
||||
|
||||
void restore_basis_change(unsigned entering, unsigned leaving) {
|
||||
if (m_basis_heading[entering] < 0) {
|
||||
return; // the basis has not been changed
|
||||
}
|
||||
change_basis_unconditionally(leaving, entering);
|
||||
}
|
||||
|
||||
bool non_basic_column_is_set_correctly(unsigned j) const {
|
||||
if (j >= this->m_n())
|
||||
return false;
|
||||
switch (this->m_column_types[j]) {
|
||||
case column_type::fixed:
|
||||
case column_type::boxed:
|
||||
if (!this->x_is_at_bound(j))
|
||||
return false;
|
||||
break;
|
||||
case column_type::low_bound:
|
||||
if (!this->x_is_at_low_bound(j))
|
||||
return false;
|
||||
break;
|
||||
case column_type::upper_bound:
|
||||
if (!this->x_is_at_upper_bound(j))
|
||||
return false;
|
||||
break;
|
||||
case column_type::free_column:
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool non_basic_columns_are_set_correctly() const {
|
||||
for (unsigned j : this->m_nbasis)
|
||||
if (!column_is_feasible(j)) {
|
||||
print_column_info(j, std::cout);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void print_column_bound_info(unsigned j, std::ostream & out) const {
|
||||
out << column_name(j) << " type = " << column_type_to_string(m_column_types[j]) << std::endl;
|
||||
switch (m_column_types[j]) {
|
||||
case column_type::fixed:
|
||||
case column_type::boxed:
|
||||
out << "(" << m_low_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl;
|
||||
break;
|
||||
case column_type::low_bound:
|
||||
out << m_low_bounds[j] << std::endl;
|
||||
break;
|
||||
case column_type::upper_bound:
|
||||
out << m_upper_bounds[j] << std::endl;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void print_column_info(unsigned j, std::ostream & out) const {
|
||||
out << "column_index = " << j << ", name = "<< column_name(j) << " type = " << column_type_to_string(m_column_types[j]) << std::endl;
|
||||
switch (m_column_types[j]) {
|
||||
case column_type::fixed:
|
||||
case column_type::boxed:
|
||||
out << "(" << m_low_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl;
|
||||
break;
|
||||
case column_type::low_bound:
|
||||
out << m_low_bounds[j] << std::endl;
|
||||
break;
|
||||
case column_type::upper_bound:
|
||||
out << m_upper_bounds[j] << std::endl;
|
||||
break;
|
||||
case column_type::free_column:
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
}
|
||||
std::cout << "basis heading = " << m_basis_heading[j] << std::endl;
|
||||
std::cout << "x = " << m_x[j] << std::endl;
|
||||
/*
|
||||
std::cout << "cost = " << m_costs[j] << std::endl;
|
||||
std:: cout << "m_d = " << m_d[j] << std::endl;*/
|
||||
}
|
||||
|
||||
bool column_is_free(unsigned j) { return this->m_column_type[j] == free; }
|
||||
|
||||
bool column_has_upper_bound(unsigned j) {
|
||||
switch(m_column_types[j]) {
|
||||
case column_type::free_column:
|
||||
case column_type::low_bound:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool bounds_for_boxed_are_set_correctly() const {
|
||||
for (unsigned j = 0; j < m_column_types.size(); j++) {
|
||||
if (m_column_types[j] != column_type::boxed) continue;
|
||||
if (m_low_bounds[j] > m_upper_bounds[j])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool column_has_low_bound(unsigned j) {
|
||||
switch(m_column_types[j]) {
|
||||
case column_type::free_column:
|
||||
case column_type::upper_bound:
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// only check for basic columns
|
||||
bool calc_current_x_is_feasible() const {
|
||||
unsigned i = this->m_m();
|
||||
while (i--) {
|
||||
if (!column_is_feasible(m_basis[i]))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int find_pivot_index_in_row(unsigned i, const vector<column_cell> & col) const {
|
||||
for (const auto & c: col) {
|
||||
if (c.m_i == i)
|
||||
return c.m_offset;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void transpose_rows_tableau(unsigned i, unsigned ii);
|
||||
|
||||
void pivot_to_reduced_costs_tableau(unsigned i, unsigned j);
|
||||
|
||||
bool pivot_column_tableau(unsigned j, unsigned row_index);
|
||||
bool divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col);
|
||||
|
||||
bool precise() const { return numeric_traits<T>::precise(); }
|
||||
|
||||
simplex_strategy_enum simplex_strategy() const { return
|
||||
m_settings.simplex_strategy();
|
||||
}
|
||||
|
||||
bool use_tableau() const { return m_settings.use_tableau(); }
|
||||
|
||||
template <typename K>
|
||||
static void swap(vector<K> &v, unsigned i, unsigned j) {
|
||||
auto t = v[i];
|
||||
v[i] = v[j];
|
||||
v[j] = t;
|
||||
}
|
||||
|
||||
// called when transposing row i and ii
|
||||
void transpose_basis(unsigned i, unsigned ii) {
|
||||
swap(m_basis, i, ii);
|
||||
swap(m_basis_heading, m_basis[i], m_basis[ii]);
|
||||
}
|
||||
|
||||
bool column_is_in_inf_set(unsigned j) const {
|
||||
return m_inf_set.contains(j);
|
||||
}
|
||||
|
||||
void update_column_in_inf_set(unsigned j) {
|
||||
if (column_is_feasible(j)) {
|
||||
m_inf_set.erase(j);
|
||||
} else {
|
||||
m_inf_set.insert(j);
|
||||
}
|
||||
}
|
||||
void insert_column_into_inf_set(unsigned j) {
|
||||
m_inf_set.insert(j);
|
||||
lean_assert(!column_is_feasible(j));
|
||||
}
|
||||
void remove_column_from_inf_set(unsigned j) {
|
||||
m_inf_set.erase(j);
|
||||
lean_assert(column_is_feasible(j));
|
||||
}
|
||||
bool costs_on_nbasis_are_zeros() const {
|
||||
lean_assert(this->basis_heading_is_correct());
|
||||
for (unsigned j = 0; j < this->m_n(); j++) {
|
||||
if (this->m_basis_heading[j] < 0)
|
||||
lean_assert(is_zero(this->m_costs[j]));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
unsigned & iters_with_no_cost_growing() {
|
||||
return m_iters_with_no_cost_growing;
|
||||
}
|
||||
|
||||
const unsigned & iters_with_no_cost_growing() const {
|
||||
return m_iters_with_no_cost_growing;
|
||||
}
|
||||
};
|
||||
}
|
1019
src/util/lp/lp_core_solver_base.hpp
Normal file
1019
src/util/lp/lp_core_solver_base.hpp
Normal file
File diff suppressed because it is too large
Load diff
131
src/util/lp/lp_core_solver_base_instances.cpp
Normal file
131
src/util/lp/lp_core_solver_base_instances.cpp
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "util/vector.h"
|
||||
#include <functional>
|
||||
#include "util/lp/lp_core_solver_base.hpp"
|
||||
template bool lean::lp_core_solver_base<double, double>::A_mult_x_is_off() const;
|
||||
template bool lean::lp_core_solver_base<double, double>::A_mult_x_is_off_on_index(const vector<unsigned> &) const;
|
||||
template bool lean::lp_core_solver_base<double, double>::basis_heading_is_correct() const;
|
||||
template void lean::lp_core_solver_base<double, double>::calculate_pivot_row_of_B_1(unsigned int);
|
||||
template void lean::lp_core_solver_base<double, double>::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned);
|
||||
template bool lean::lp_core_solver_base<double, double>::column_is_dual_feasible(unsigned int) const;
|
||||
template void lean::lp_core_solver_base<double, double>::fill_reduced_costs_from_m_y_by_rows();
|
||||
template bool lean::lp_core_solver_base<double, double>::find_x_by_solving();
|
||||
template lean::non_basic_column_value_position lean::lp_core_solver_base<double, double>::get_non_basic_column_value_position(unsigned int) const;
|
||||
template lean::non_basic_column_value_position lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::get_non_basic_column_value_position(unsigned int) const;
|
||||
template lean::non_basic_column_value_position lean::lp_core_solver_base<lean::mpq, lean::mpq>::get_non_basic_column_value_position(unsigned int) const;
|
||||
template void lean::lp_core_solver_base<double, double>::init_reduced_costs_for_one_iteration();
|
||||
template lean::lp_core_solver_base<double, double>::lp_core_solver_base(
|
||||
lean::static_matrix<double, double>&, vector<double>&,
|
||||
vector<unsigned int >&,
|
||||
vector<unsigned> &, vector<int> &,
|
||||
vector<double >&,
|
||||
vector<double >&,
|
||||
lean::lp_settings&, const column_namer&, const vector<lean::column_type >&,
|
||||
const vector<double >&,
|
||||
const vector<double >&);
|
||||
|
||||
template bool lean::lp_core_solver_base<double, double>::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &);
|
||||
template void lean::lp_core_solver_base<double, double>::restore_x(unsigned int, double const&);
|
||||
template void lean::lp_core_solver_base<double, double>::set_non_basic_x_to_correct_bounds();
|
||||
template void lean::lp_core_solver_base<double, double>::snap_xN_to_bounds_and_free_columns_to_zeroes();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::snap_xN_to_bounds_and_free_columns_to_zeroes();
|
||||
template void lean::lp_core_solver_base<double, double>::solve_Ax_eq_b();
|
||||
template void lean::lp_core_solver_base<double, double>::solve_Bd(unsigned int);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq>>::solve_Bd(unsigned int, indexed_vector<lean::mpq>&);
|
||||
template void lean::lp_core_solver_base<double, double>::solve_yB(vector<double >&);
|
||||
template bool lean::lp_core_solver_base<double, double>::update_basis_and_x(int, int, double const&);
|
||||
template void lean::lp_core_solver_base<double, double>::update_x(unsigned int, const double&);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::A_mult_x_is_off() const;
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::A_mult_x_is_off_on_index(const vector<unsigned> &) const;
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::basis_heading_is_correct() const ;
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::calculate_pivot_row_of_B_1(unsigned int);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::column_is_dual_feasible(unsigned int) const;
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::fill_reduced_costs_from_m_y_by_rows();
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::find_x_by_solving();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::init_reduced_costs_for_one_iteration();
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::restore_x(unsigned int, lean::mpq const&);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::set_non_basic_x_to_correct_bounds();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_Ax_eq_b();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_Bd(unsigned int);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_yB(vector<lean::mpq>&);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::update_basis_and_x(int, int, lean::mpq const&);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::update_x(unsigned int, const lean::mpq&);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::calculate_pivot_row_of_B_1(unsigned int);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init_basis_heading_and_non_basic_columns_vector();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init_reduced_costs_for_one_iteration();
|
||||
template lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::lp_core_solver_base(lean::static_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, vector<lean::numeric_pair<lean::mpq> >&, vector<unsigned int >&, vector<unsigned> &, vector<int> &, vector<lean::numeric_pair<lean::mpq> >&, vector<lean::mpq>&, lean::lp_settings&, const column_namer&, const vector<lean::column_type >&,
|
||||
const vector<lean::numeric_pair<lean::mpq> >&,
|
||||
const vector<lean::numeric_pair<lean::mpq> >&);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_statistics_with_cost_and_check_that_the_time_is_over(lean::numeric_pair<lean::mpq>, std::ostream&);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::snap_xN_to_bounds_and_fill_xB();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_Bd(unsigned int);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::update_basis_and_x(int, int, lean::numeric_pair<lean::mpq> const&);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::update_x(unsigned int, const lean::numeric_pair<lean::mpq>&);
|
||||
template lean::lp_core_solver_base<lean::mpq, lean::mpq>::lp_core_solver_base(
|
||||
lean::static_matrix<lean::mpq, lean::mpq>&,
|
||||
vector<lean::mpq>&,
|
||||
vector<unsigned int >&,
|
||||
vector<unsigned> &, vector<int> &,
|
||||
vector<lean::mpq>&,
|
||||
vector<lean::mpq>&,
|
||||
lean::lp_settings&,
|
||||
const column_namer&,
|
||||
const vector<lean::column_type >&,
|
||||
const vector<lean::mpq>&,
|
||||
const vector<lean::mpq>&);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream &);
|
||||
template std::string lean::lp_core_solver_base<double, double>::column_name(unsigned int) const;
|
||||
template void lean::lp_core_solver_base<double, double>::pretty_print(std::ostream & out);
|
||||
template void lean::lp_core_solver_base<double, double>::restore_state(double*, double*);
|
||||
template void lean::lp_core_solver_base<double, double>::save_state(double*, double*);
|
||||
template std::string lean::lp_core_solver_base<lean::mpq, lean::mpq>::column_name(unsigned int) const;
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::pretty_print(std::ostream & out);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::restore_state(lean::mpq*, lean::mpq*);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::save_state(lean::mpq*, lean::mpq*);
|
||||
template std::string lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::column_name(unsigned int) const;
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::pretty_print(std::ostream & out);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::restore_state(lean::mpq*, lean::mpq*);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::save_state(lean::mpq*, lean::mpq*);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_yB(vector<lean::mpq>&);
|
||||
template void lean::lp_core_solver_base<double, double>::init_lu();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::init_lu();
|
||||
template int lean::lp_core_solver_base<double, double>::pivots_in_column_and_row_are_different(int, int) const;
|
||||
template int lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::pivots_in_column_and_row_are_different(int, int) const;
|
||||
template int lean::lp_core_solver_base<lean::mpq, lean::mpq>::pivots_in_column_and_row_are_different(int, int) const;
|
||||
template bool lean::lp_core_solver_base<double, double>::calc_current_x_is_feasible_include_non_basis(void)const;
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::calc_current_x_is_feasible_include_non_basis(void)const;
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::calc_current_x_is_feasible_include_non_basis() const;
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::pivot_fixed_vars_from_basis();
|
||||
template bool lean::lp_core_solver_base<double, double>::column_is_feasible(unsigned int) const;
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::column_is_feasible(unsigned int) const;
|
||||
// template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_linear_combination_of_column_indices(vector<std::pair<lean::mpq, unsigned int>, std::allocator<std::pair<lean::mpq, unsigned int> > > const&, std::ostream&) const;
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::column_is_feasible(unsigned int) const;
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::snap_non_basic_x_to_bound();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init_lu();
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::A_mult_x_is_off_on_index(vector<unsigned int> const&) const;
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::find_x_by_solving();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::restore_x(unsigned int, lean::numeric_pair<lean::mpq> const&);
|
||||
template bool lean::lp_core_solver_base<double, double>::pivot_for_tableau_on_basis();
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::pivot_for_tableau_on_basis();
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq>>::pivot_for_tableau_on_basis();
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq>>::pivot_column_tableau(unsigned int, unsigned int);
|
||||
template bool lean::lp_core_solver_base<double, double>::pivot_column_tableau(unsigned int, unsigned int);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::pivot_column_tableau(unsigned int, unsigned int);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::transpose_rows_tableau(unsigned int, unsigned int);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::inf_set_is_correct() const;
|
||||
template bool lean::lp_core_solver_base<double, double>::inf_set_is_correct() const;
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::inf_set_is_correct() const;
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::infeasibility_costs_are_correct() const;
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq >::infeasibility_costs_are_correct() const;
|
||||
template bool lean::lp_core_solver_base<double, double >::infeasibility_costs_are_correct() const;
|
197
src/util/lp/lp_dual_core_solver.h
Normal file
197
src/util/lp/lp_dual_core_solver.h
Normal file
|
@ -0,0 +1,197 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/lp/static_matrix.h"
|
||||
#include "util/lp/lp_core_solver_base.h"
|
||||
#include <string>
|
||||
#include <limits>
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
#include "util/vector.h"
|
||||
|
||||
namespace lean {
|
||||
template <typename T, typename X>
|
||||
class lp_dual_core_solver:public lp_core_solver_base<T, X> {
|
||||
public:
|
||||
vector<bool> & m_can_enter_basis;
|
||||
int m_r; // the row of the leaving column
|
||||
int m_p; // leaving column; that is m_p = m_basis[m_r]
|
||||
T m_delta; // the offset of the leaving basis variable
|
||||
int m_sign_of_alpha_r; // see page 27
|
||||
T m_theta_D;
|
||||
T m_theta_P;
|
||||
int m_q;
|
||||
// todo : replace by a vector later
|
||||
std::set<unsigned> m_breakpoint_set; // it is F in "Progress in the dual simplex method ..."
|
||||
std::set<unsigned> m_flipped_boxed;
|
||||
std::set<unsigned> m_tight_set; // it is the set of all breakpoints that become tight when m_q becomes tight
|
||||
vector<T> m_a_wave;
|
||||
vector<T> m_betas; // m_betas[i] is approximately a square of the norm of the i-th row of the reverse of B
|
||||
T m_harris_tolerance;
|
||||
std::set<unsigned> m_forbidden_rows;
|
||||
|
||||
lp_dual_core_solver(static_matrix<T, X> & A,
|
||||
vector<bool> & can_enter_basis,
|
||||
vector<X> & b, // the right side vector
|
||||
vector<X> & x, // the number of elements in x needs to be at least as large as the number of columns in A
|
||||
vector<unsigned> & basis,
|
||||
vector<unsigned> & nbasis,
|
||||
vector<int> & heading,
|
||||
vector<T> & costs,
|
||||
vector<column_type> & column_type_array,
|
||||
vector<X> & low_bound_values,
|
||||
vector<X> & upper_bound_values,
|
||||
lp_settings & settings,
|
||||
const column_namer & column_names):
|
||||
lp_core_solver_base<T, X>(A,
|
||||
b,
|
||||
basis,
|
||||
nbasis,
|
||||
heading,
|
||||
x,
|
||||
costs,
|
||||
settings,
|
||||
column_names,
|
||||
column_type_array,
|
||||
low_bound_values,
|
||||
upper_bound_values),
|
||||
m_can_enter_basis(can_enter_basis),
|
||||
m_a_wave(this->m_m()),
|
||||
m_betas(this->m_m()) {
|
||||
m_harris_tolerance = numeric_traits<T>::precise()? numeric_traits<T>::zero() : T(this->m_settings.harris_feasibility_tolerance);
|
||||
this->solve_yB(this->m_y);
|
||||
this->init_basic_part_of_basis_heading();
|
||||
fill_non_basis_with_only_able_to_enter_columns();
|
||||
}
|
||||
|
||||
void init_a_wave_by_zeros();
|
||||
|
||||
void fill_non_basis_with_only_able_to_enter_columns() {
|
||||
auto & nb = this->m_nbasis;
|
||||
nb.reset();
|
||||
unsigned j = this->m_n();
|
||||
while (j--) {
|
||||
if (this->m_basis_heading[j] >= 0 || !m_can_enter_basis[j]) continue;
|
||||
nb.push_back(j);
|
||||
this->m_basis_heading[j] = - static_cast<int>(nb.size());
|
||||
}
|
||||
}
|
||||
|
||||
void restore_non_basis();
|
||||
|
||||
bool update_basis(int entering, int leaving);
|
||||
|
||||
void recalculate_xB_and_d();
|
||||
|
||||
void recalculate_d();
|
||||
|
||||
void init_betas();
|
||||
|
||||
void adjust_xb_for_changed_xn_and_init_betas();
|
||||
|
||||
void start_with_initial_basis_and_make_it_dual_feasible();
|
||||
|
||||
bool done();
|
||||
|
||||
T get_edge_steepness_for_low_bound(unsigned p);
|
||||
|
||||
T get_edge_steepness_for_upper_bound(unsigned p);
|
||||
|
||||
T pricing_for_row(unsigned i);
|
||||
|
||||
void pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows);
|
||||
|
||||
bool advance_on_known_p();
|
||||
|
||||
int define_sign_of_alpha_r();
|
||||
|
||||
bool can_be_breakpoint(unsigned j);
|
||||
|
||||
void fill_breakpoint_set();
|
||||
|
||||
void DSE_FTran();
|
||||
T get_delta();
|
||||
|
||||
void restore_d();
|
||||
|
||||
bool d_is_correct();
|
||||
|
||||
void xb_minus_delta_p_pivot_column();
|
||||
|
||||
void update_betas();
|
||||
|
||||
void apply_flips();
|
||||
|
||||
void snap_xN_column_to_bounds(unsigned j);
|
||||
|
||||
void snap_xN_to_bounds();
|
||||
|
||||
void init_beta_precisely(unsigned i);
|
||||
|
||||
void init_betas_precisely();
|
||||
|
||||
// step 7 of the algorithm from Progress
|
||||
bool basis_change_and_update();
|
||||
|
||||
void revert_to_previous_basis();
|
||||
|
||||
non_basic_column_value_position m_entering_boundary_position;
|
||||
bool update_basis_and_x_local(int entering, int leaving, X const & tt);
|
||||
void recover_leaving();
|
||||
|
||||
bool problem_is_dual_feasible() const;
|
||||
|
||||
bool snap_runaway_nonbasic_column(unsigned);
|
||||
|
||||
bool snap_runaway_nonbasic_columns();
|
||||
|
||||
unsigned get_number_of_rows_to_try_for_leaving();
|
||||
|
||||
void update_a_wave(const T & del, unsigned j) {
|
||||
this->m_A.add_column_to_vector(del, j, & m_a_wave[0]);
|
||||
}
|
||||
|
||||
bool delta_keeps_the_sign(int initial_delta_sign, const T & delta);
|
||||
|
||||
void set_status_to_tentative_dual_unbounded_or_dual_unbounded();
|
||||
|
||||
// it is positive if going from low bound to upper bound and negative if going from upper bound to low bound
|
||||
T signed_span_of_boxed(unsigned j) {
|
||||
return this->x_is_at_low_bound(j)? this->bound_span(j): - this->bound_span(j);
|
||||
}
|
||||
|
||||
void add_tight_breakpoints_and_q_to_flipped_set();
|
||||
|
||||
T delta_lost_on_flips_of_tight_breakpoints();
|
||||
|
||||
bool tight_breakpoinst_are_all_boxed();
|
||||
|
||||
T calculate_harris_delta_on_breakpoint_set();
|
||||
|
||||
void fill_tight_set_on_harris_delta(const T & harris_delta );
|
||||
|
||||
void find_q_on_tight_set();
|
||||
|
||||
void find_q_and_tight_set();
|
||||
|
||||
void erase_tight_breakpoints_and_q_from_breakpoint_set();
|
||||
|
||||
bool ratio_test();
|
||||
|
||||
void process_flipped();
|
||||
void update_d_and_xB();
|
||||
|
||||
void calculate_beta_r_precisely();
|
||||
// see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms"
|
||||
|
||||
void update_xb_after_bound_flips();
|
||||
|
||||
void one_iteration();
|
||||
|
||||
void solve();
|
||||
|
||||
bool low_bounds_are_set() const { return true; }
|
||||
};
|
||||
}
|
743
src/util/lp/lp_dual_core_solver.hpp
Normal file
743
src/util/lp/lp_dual_core_solver.hpp
Normal file
|
@ -0,0 +1,743 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include "util/vector.h"
|
||||
#include "util/lp/lp_dual_core_solver.h"
|
||||
|
||||
namespace lean {
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_a_wave_by_zeros() {
|
||||
unsigned j = this->m_m();
|
||||
while (j--) {
|
||||
m_a_wave[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::restore_non_basis() {
|
||||
auto & nb = this->m_nbasis;
|
||||
nb.reset();
|
||||
unsigned j = this->m_n();
|
||||
while (j--) {
|
||||
if (this->m_basis_heading[j] >= 0 ) continue;
|
||||
if (m_can_enter_basis[j]) {
|
||||
lean_assert(std::find(nb.begin(), nb.end(), j) == nb.end());
|
||||
nb.push_back(j);
|
||||
this->m_basis_heading[j] = - static_cast<int>(nb.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>::update_basis(int entering, int leaving) {
|
||||
// the second argument is the element of the entering column from the pivot row - its value should be equal to the low diagonal element of the bump after all pivoting is done
|
||||
if (this->m_refactor_counter++ < 200) {
|
||||
this->m_factorization->replace_column(this->m_ed[this->m_factorization->basis_heading(leaving)], this->m_w);
|
||||
if (this->m_factorization->get_status() == LU_status::OK) {
|
||||
this->m_factorization->change_basis(entering, leaving);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// need to refactor
|
||||
this->m_factorization->change_basis(entering, leaving);
|
||||
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings);
|
||||
this->m_refactor_counter = 0;
|
||||
if (this->m_factorization->get_status() != LU_status::OK) {
|
||||
LP_OUT(this->m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << this->total_iterations() << std::endl);
|
||||
this->m_iters_with_no_cost_growing++;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::recalculate_xB_and_d() {
|
||||
this->solve_Ax_eq_b();
|
||||
recalculate_d();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::recalculate_d() {
|
||||
this->solve_yB(this->m_y);
|
||||
this->fill_reduced_costs_from_m_y_by_rows();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_betas() {
|
||||
// todo : look at page 194 of Progress in the dual simplex algorithm for solving large scale LP problems : techniques for a fast and stable implementation
|
||||
// the current implementation is not good enough: todo
|
||||
unsigned i = this->m_m();
|
||||
while (i--) {
|
||||
m_betas[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::adjust_xb_for_changed_xn_and_init_betas() {
|
||||
this->solve_Ax_eq_b();
|
||||
init_betas();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::start_with_initial_basis_and_make_it_dual_feasible() {
|
||||
this->set_non_basic_x_to_correct_bounds(); // It is not an efficient version, see 3.29,
|
||||
// however this version does not require that m_x is the solution of Ax = 0 beforehand
|
||||
adjust_xb_for_changed_xn_and_init_betas();
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>::done() {
|
||||
if (this->get_status() == OPTIMAL) {
|
||||
return true;
|
||||
}
|
||||
if (this->total_iterations() > this->m_settings.max_total_number_of_iterations) { // debug !!!!
|
||||
this->set_status(ITERATIONS_EXHAUSTED);
|
||||
return true;
|
||||
}
|
||||
return false; // todo, need to be more cases
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_low_bound(unsigned p) {
|
||||
lean_assert(this->m_basis_heading[p] >= 0 && static_cast<unsigned>(this->m_basis_heading[p]) < this->m_m());
|
||||
T del = this->m_x[p] - this->m_low_bounds[p];
|
||||
del *= del;
|
||||
return del / this->m_betas[this->m_basis_heading[p]];
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_upper_bound(unsigned p) {
|
||||
lean_assert(this->m_basis_heading[p] >= 0 && static_cast<unsigned>(this->m_basis_heading[p]) < this->m_m());
|
||||
T del = this->m_x[p] - this->m_upper_bounds[p];
|
||||
del *= del;
|
||||
return del / this->m_betas[this->m_basis_heading[p]];
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_dual_core_solver<T, X>::pricing_for_row(unsigned i) {
|
||||
unsigned p = this->m_basis[i];
|
||||
switch (this->m_column_types[p]) {
|
||||
case column_type::fixed:
|
||||
case column_type::boxed:
|
||||
if (this->x_below_low_bound(p)) {
|
||||
T del = get_edge_steepness_for_low_bound(p);
|
||||
return del;
|
||||
}
|
||||
if (this->x_above_upper_bound(p)) {
|
||||
T del = get_edge_steepness_for_upper_bound(p);
|
||||
return del;
|
||||
}
|
||||
return numeric_traits<T>::zero();
|
||||
case column_type::low_bound:
|
||||
if (this->x_below_low_bound(p)) {
|
||||
T del = get_edge_steepness_for_low_bound(p);
|
||||
return del;
|
||||
}
|
||||
return numeric_traits<T>::zero();
|
||||
break;
|
||||
case column_type::upper_bound:
|
||||
if (this->x_above_upper_bound(p)) {
|
||||
T del = get_edge_steepness_for_upper_bound(p);
|
||||
return del;
|
||||
}
|
||||
return numeric_traits<T>::zero();
|
||||
break;
|
||||
case column_type::free_column:
|
||||
lean_assert(numeric_traits<T>::is_zero(this->m_d[p]));
|
||||
return numeric_traits<T>::zero();
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
lean_unreachable();
|
||||
return numeric_traits<T>::zero();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows) {
|
||||
m_r = -1;
|
||||
T steepest_edge_max = numeric_traits<T>::zero();
|
||||
unsigned initial_offset_in_rows = offset_in_rows;
|
||||
unsigned i = offset_in_rows;
|
||||
unsigned rows_left = number_of_rows_to_try;
|
||||
do {
|
||||
if (m_forbidden_rows.find(i) != m_forbidden_rows.end()) {
|
||||
if (++i == this->m_m()) {
|
||||
i = 0;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
T se = pricing_for_row(i);
|
||||
if (se > steepest_edge_max) {
|
||||
steepest_edge_max = se;
|
||||
m_r = i;
|
||||
if (rows_left > 0) {
|
||||
rows_left--;
|
||||
}
|
||||
}
|
||||
if (++i == this->m_m()) {
|
||||
i = 0;
|
||||
}
|
||||
} while (i != initial_offset_in_rows && rows_left);
|
||||
if (m_r == -1) {
|
||||
if (this->get_status() != UNSTABLE) {
|
||||
this->set_status(OPTIMAL);
|
||||
}
|
||||
} else {
|
||||
m_p = this->m_basis[m_r];
|
||||
m_delta = get_delta();
|
||||
if (advance_on_known_p()){
|
||||
m_forbidden_rows.clear();
|
||||
return;
|
||||
}
|
||||
// failure in advance_on_known_p
|
||||
if (this->get_status() == FLOATING_POINT_ERROR) {
|
||||
return;
|
||||
}
|
||||
this->set_status(UNSTABLE);
|
||||
m_forbidden_rows.insert(m_r);
|
||||
}
|
||||
}
|
||||
|
||||
// this calculation is needed for the steepest edge update,
|
||||
// it hijackes m_pivot_row_of_B_1 for this purpose since we will need it anymore to the end of the cycle
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::DSE_FTran() { // todo, see algorithm 7 from page 35
|
||||
this->m_factorization->solve_By_for_T_indexed_only(this->m_pivot_row_of_B_1, this->m_settings);
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>::advance_on_known_p() {
|
||||
if (done()) {
|
||||
return true;
|
||||
}
|
||||
this->calculate_pivot_row_of_B_1(m_r);
|
||||
this->calculate_pivot_row_when_pivot_row_of_B1_is_ready(m_r);
|
||||
if (!ratio_test()) {
|
||||
return true;
|
||||
}
|
||||
calculate_beta_r_precisely();
|
||||
this->solve_Bd(m_q); // FTRAN
|
||||
int pivot_compare_result = this->pivots_in_column_and_row_are_different(m_q, m_p);
|
||||
if (!pivot_compare_result){;}
|
||||
else if (pivot_compare_result == 2) { // the sign is changed, cannot continue
|
||||
lean_unreachable(); // not implemented yet
|
||||
} else {
|
||||
lean_assert(pivot_compare_result == 1);
|
||||
this->init_lu();
|
||||
}
|
||||
DSE_FTran();
|
||||
return basis_change_and_update();
|
||||
}
|
||||
|
||||
template <typename T, typename X> int lp_dual_core_solver<T, X>::define_sign_of_alpha_r() {
|
||||
switch (this->m_column_types[m_p]) {
|
||||
case column_type::boxed:
|
||||
case column_type::fixed:
|
||||
if (this->x_below_low_bound(m_p)) {
|
||||
return -1;
|
||||
}
|
||||
if (this->x_above_upper_bound(m_p)) {
|
||||
return 1;
|
||||
}
|
||||
lean_unreachable();
|
||||
case column_type::low_bound:
|
||||
if (this->x_below_low_bound(m_p)) {
|
||||
return -1;
|
||||
}
|
||||
lean_unreachable();
|
||||
case column_type::upper_bound:
|
||||
if (this->x_above_upper_bound(m_p)) {
|
||||
return 1;
|
||||
}
|
||||
lean_unreachable();
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
lean_unreachable();
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>::can_be_breakpoint(unsigned j) {
|
||||
if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false;
|
||||
switch (this->m_column_types[j]) {
|
||||
case column_type::low_bound:
|
||||
lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_low_bounds[j]));
|
||||
return m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
|
||||
case column_type::upper_bound:
|
||||
lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bounds[j]));
|
||||
return m_sign_of_alpha_r * this->m_pivot_row[j] < 0;
|
||||
case column_type::boxed:
|
||||
{
|
||||
bool low_bound = this->x_is_at_low_bound(j);
|
||||
bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
|
||||
return low_bound == grawing;
|
||||
}
|
||||
case column_type::fixed: // is always dual feasible so we ingore it
|
||||
return false;
|
||||
case column_type::free_column:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_breakpoint_set() {
|
||||
m_breakpoint_set.clear();
|
||||
for (unsigned j : this->non_basis()) {
|
||||
if (can_be_breakpoint(j)) {
|
||||
m_breakpoint_set.insert(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// template <typename T, typename X> void lp_dual_core_solver<T, X>::FTran() {
|
||||
// this->solve_Bd(m_q);
|
||||
// }
|
||||
|
||||
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_delta() {
|
||||
switch (this->m_column_types[m_p]) {
|
||||
case column_type::boxed:
|
||||
if (this->x_below_low_bound(m_p)) {
|
||||
return this->m_x[m_p] - this->m_low_bounds[m_p];
|
||||
}
|
||||
if (this->x_above_upper_bound(m_p)) {
|
||||
return this->m_x[m_p] - this->m_upper_bounds[m_p];
|
||||
}
|
||||
lean_unreachable();
|
||||
case column_type::low_bound:
|
||||
if (this->x_below_low_bound(m_p)) {
|
||||
return this->m_x[m_p] - this->m_low_bounds[m_p];
|
||||
}
|
||||
lean_unreachable();
|
||||
case column_type::upper_bound:
|
||||
if (this->x_above_upper_bound(m_p)) {
|
||||
return get_edge_steepness_for_upper_bound(m_p);
|
||||
}
|
||||
lean_unreachable();
|
||||
case column_type::fixed:
|
||||
return this->m_x[m_p] - this->m_upper_bounds[m_p];
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
lean_unreachable();
|
||||
return zero_of_type<T>();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::restore_d() {
|
||||
this->m_d[m_p] = numeric_traits<T>::zero();
|
||||
for (auto j : this->non_basis()) {
|
||||
this->m_d[j] += m_theta_D * this->m_pivot_row[j];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>::d_is_correct() {
|
||||
this->solve_yB(this->m_y);
|
||||
for (auto j : this->non_basis()) {
|
||||
T d = this->m_costs[j] - this->m_A.dot_product_with_column(this->m_y, j);
|
||||
if (numeric_traits<T>::get_double(abs(d - this->m_d[j])) >= 0.001) {
|
||||
LP_OUT(this->m_settings, "total_iterations = " << this->total_iterations() << std::endl
|
||||
<< "d[" << j << "] = " << this->m_d[j] << " but should be " << d << std::endl);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::xb_minus_delta_p_pivot_column() {
|
||||
unsigned i = this->m_m();
|
||||
while (i--) {
|
||||
this->m_x[this->m_basis[i]] -= m_theta_P * this->m_ed[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::update_betas() { // page 194 of Progress ... todo - once in a while betas have to be reinitialized
|
||||
T one_over_arq = numeric_traits<T>::one() / this->m_pivot_row[m_q];
|
||||
T beta_r = this->m_betas[m_r] = std::max(T(0.0001), (m_betas[m_r] * one_over_arq) * one_over_arq);
|
||||
T k = -2 * one_over_arq;
|
||||
unsigned i = this->m_m();
|
||||
while (i--) {
|
||||
if (static_cast<int>(i) == m_r) continue;
|
||||
T a = this->m_ed[i];
|
||||
m_betas[i] += a * (a * beta_r + k * this->m_pivot_row_of_B_1[i]);
|
||||
if (m_betas[i] < T(0.0001))
|
||||
m_betas[i] = T(0.0001);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::apply_flips() {
|
||||
for (unsigned j : m_flipped_boxed) {
|
||||
lean_assert(this->x_is_at_bound(j));
|
||||
if (this->x_is_at_low_bound(j)) {
|
||||
this->m_x[j] = this->m_upper_bounds[j];
|
||||
} else {
|
||||
this->m_x[j] = this->m_low_bounds[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_column_to_bounds(unsigned j) {
|
||||
switch (this->m_column_type[j]) {
|
||||
case column_type::fixed:
|
||||
this->m_x[j] = this->m_low_bounds[j];
|
||||
break;
|
||||
case column_type::boxed:
|
||||
if (this->x_is_at_low_bound(j)) {
|
||||
this->m_x[j] = this->m_low_bounds[j];
|
||||
} else {
|
||||
this->m_x[j] = this->m_upper_bounds[j];
|
||||
}
|
||||
break;
|
||||
case column_type::low_bound:
|
||||
this->m_x[j] = this->m_low_bounds[j];
|
||||
break;
|
||||
case column_type::upper_bound:
|
||||
this->m_x[j] = this->m_upper_bounds[j];
|
||||
break;
|
||||
case column_type::free_column:
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_to_bounds() {
|
||||
for (auto j : this->non_basis()) {
|
||||
snap_xN_column_to_bounds(j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_beta_precisely(unsigned i) {
|
||||
vector<T> vec(this->m_m(), numeric_traits<T>::zero());
|
||||
vec[i] = numeric_traits<T>::one();
|
||||
this->m_factorization->solve_yB_with_error_check(vec, this->m_basis);
|
||||
T beta = numeric_traits<T>::zero();
|
||||
for (T & v : vec) {
|
||||
beta += v * v;
|
||||
}
|
||||
this->m_betas[i] =beta;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_betas_precisely() {
|
||||
unsigned i = this->m_m();
|
||||
while (i--) {
|
||||
init_beta_precisely(i);
|
||||
}
|
||||
}
|
||||
|
||||
// step 7 of the algorithm from Progress
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>::basis_change_and_update() {
|
||||
update_betas();
|
||||
update_d_and_xB();
|
||||
// m_theta_P = m_delta / this->m_ed[m_r];
|
||||
m_theta_P = m_delta / this->m_pivot_row[m_q];
|
||||
// xb_minus_delta_p_pivot_column();
|
||||
apply_flips();
|
||||
if (!this->update_basis_and_x(m_q, m_p, m_theta_P)) {
|
||||
init_betas_precisely();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (snap_runaway_nonbasic_column(m_p)) {
|
||||
if (!this->find_x_by_solving()) {
|
||||
revert_to_previous_basis();
|
||||
this->iters_with_no_cost_growing()++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!problem_is_dual_feasible()) {
|
||||
// todo : shift the costs!!!!
|
||||
revert_to_previous_basis();
|
||||
this->iters_with_no_cost_growing()++;
|
||||
return false;
|
||||
}
|
||||
|
||||
lean_assert(d_is_correct());
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::recover_leaving() {
|
||||
switch (m_entering_boundary_position) {
|
||||
case at_low_bound:
|
||||
case at_fixed:
|
||||
this->m_x[m_q] = this->m_low_bounds[m_q];
|
||||
break;
|
||||
case at_upper_bound:
|
||||
this->m_x[m_q] = this->m_upper_bounds[m_q];
|
||||
break;
|
||||
case free_of_bounds:
|
||||
this->m_x[m_q] = zero_of_type<X>();
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::revert_to_previous_basis() {
|
||||
LP_OUT(this->m_settings, "revert to previous basis on ( " << m_p << ", " << m_q << ")" << std::endl);
|
||||
this->change_basis_unconditionally(m_p, m_q);
|
||||
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings);
|
||||
if (this->m_factorization->get_status() != LU_status::OK) {
|
||||
this->set_status(FLOATING_POINT_ERROR); // complete failure
|
||||
return;
|
||||
}
|
||||
recover_leaving();
|
||||
if (!this->find_x_by_solving()) {
|
||||
this->set_status(FLOATING_POINT_ERROR);
|
||||
return;
|
||||
}
|
||||
recalculate_xB_and_d();
|
||||
init_betas_precisely();
|
||||
}
|
||||
|
||||
// returns true if the column has been snapped
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>::snap_runaway_nonbasic_column(unsigned j) {
|
||||
switch (this->m_column_types[j]) {
|
||||
case column_type::fixed:
|
||||
case column_type::low_bound:
|
||||
if (!this->x_is_at_low_bound(j)) {
|
||||
this->m_x[j] = this->m_low_bounds[j];
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case column_type::boxed:
|
||||
{
|
||||
bool closer_to_low_bound = abs(this->m_low_bounds[j] - this->m_x[j]) < abs(this->m_upper_bounds[j] - this->m_x[j]);
|
||||
if (closer_to_low_bound) {
|
||||
if (!this->x_is_at_low_bound(j)) {
|
||||
this->m_x[j] = this->m_low_bounds[j];
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (!this->x_is_at_upper_bound(j)) {
|
||||
this->m_x[j] = this->m_low_bounds[j];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case column_type::upper_bound:
|
||||
if (!this->x_is_at_upper_bound(j)) {
|
||||
this->m_x[j] = this->m_upper_bounds[j];
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>::problem_is_dual_feasible() const {
|
||||
for (unsigned j : this->non_basis()){
|
||||
if (!this->column_is_dual_feasible(j)) {
|
||||
// std::cout << "column " << j << " is not dual feasible" << std::endl;
|
||||
// std::cout << "m_d[" << j << "] = " << this->m_d[j] << std::endl;
|
||||
// std::cout << "x[" << j << "] = " << this->m_x[j] << std::endl;
|
||||
// std::cout << "type = " << column_type_to_string(this->m_column_type[j]) << std::endl;
|
||||
// std::cout << "bounds = " << this->m_low_bounds[j] << "," << this->m_upper_bounds[j] << std::endl;
|
||||
// std::cout << "total_iterations = " << this->total_iterations() << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> unsigned lp_dual_core_solver<T, X>::get_number_of_rows_to_try_for_leaving() {
|
||||
unsigned s = this->m_m();
|
||||
if (this->m_m() > 300) {
|
||||
s = (unsigned)((s / 100.0) * this->m_settings.percent_of_entering_to_check);
|
||||
}
|
||||
return this->m_settings.random_next() % s + 1;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>::delta_keeps_the_sign(int initial_delta_sign, const T & delta) {
|
||||
if (numeric_traits<T>::precise())
|
||||
return ((delta > numeric_traits<T>::zero()) && (initial_delta_sign == 1)) ||
|
||||
((delta < numeric_traits<T>::zero()) && (initial_delta_sign == -1));
|
||||
|
||||
double del = numeric_traits<T>::get_double(delta);
|
||||
return ( (del > this->m_settings.zero_tolerance) && (initial_delta_sign == 1)) ||
|
||||
((del < - this->m_settings.zero_tolerance) && (initial_delta_sign == -1));
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::set_status_to_tentative_dual_unbounded_or_dual_unbounded() {
|
||||
if (this->get_status() == TENTATIVE_DUAL_UNBOUNDED) {
|
||||
this->set_status(DUAL_UNBOUNDED);
|
||||
} else {
|
||||
this->set_status(TENTATIVE_DUAL_UNBOUNDED);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::add_tight_breakpoints_and_q_to_flipped_set() {
|
||||
m_flipped_boxed.insert(m_q);
|
||||
for (auto j : m_tight_set) {
|
||||
m_flipped_boxed.insert(j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_dual_core_solver<T, X>::delta_lost_on_flips_of_tight_breakpoints() {
|
||||
T ret = abs(this->bound_span(m_q) * this->m_pivot_row[m_q]);
|
||||
for (auto j : m_tight_set) {
|
||||
ret += abs(this->bound_span(j) * this->m_pivot_row[j]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>::tight_breakpoinst_are_all_boxed() {
|
||||
if (this->m_column_types[m_q] != column_type::boxed) return false;
|
||||
for (auto j : m_tight_set) {
|
||||
if (this->m_column_types[j] != column_type::boxed) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_dual_core_solver<T, X>::calculate_harris_delta_on_breakpoint_set() {
|
||||
bool first_time = true;
|
||||
T ret = zero_of_type<T>();
|
||||
lean_assert(m_breakpoint_set.size() > 0);
|
||||
for (auto j : m_breakpoint_set) {
|
||||
T t;
|
||||
if (this->x_is_at_low_bound(j)) {
|
||||
t = abs((std::max(this->m_d[j], numeric_traits<T>::zero()) + m_harris_tolerance) / this->m_pivot_row[j]);
|
||||
} else {
|
||||
t = abs((std::min(this->m_d[j], numeric_traits<T>::zero()) - m_harris_tolerance) / this->m_pivot_row[j]);
|
||||
}
|
||||
if (first_time) {
|
||||
ret = t;
|
||||
first_time = false;
|
||||
} else if (t < ret) {
|
||||
ret = t;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_tight_set_on_harris_delta(const T & harris_delta ){
|
||||
m_tight_set.clear();
|
||||
for (auto j : m_breakpoint_set) {
|
||||
if (this->x_is_at_low_bound(j)) {
|
||||
if (abs(std::max(this->m_d[j], numeric_traits<T>::zero()) / this->m_pivot_row[j]) <= harris_delta){
|
||||
m_tight_set.insert(j);
|
||||
}
|
||||
} else {
|
||||
if (abs(std::min(this->m_d[j], numeric_traits<T>::zero() ) / this->m_pivot_row[j]) <= harris_delta){
|
||||
m_tight_set.insert(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_on_tight_set() {
|
||||
m_q = -1;
|
||||
T max_pivot;
|
||||
for (auto j : m_tight_set) {
|
||||
T r = abs(this->m_pivot_row[j]);
|
||||
if (m_q != -1) {
|
||||
if (r > max_pivot) {
|
||||
max_pivot = r;
|
||||
m_q = j;
|
||||
}
|
||||
} else {
|
||||
max_pivot = r;
|
||||
m_q = j;
|
||||
}
|
||||
}
|
||||
m_tight_set.erase(m_q);
|
||||
lean_assert(m_q != -1);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_and_tight_set() {
|
||||
T harris_del = calculate_harris_delta_on_breakpoint_set();
|
||||
fill_tight_set_on_harris_delta(harris_del);
|
||||
find_q_on_tight_set();
|
||||
m_entering_boundary_position = this->get_non_basic_column_value_position(m_q);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::erase_tight_breakpoints_and_q_from_breakpoint_set() {
|
||||
m_breakpoint_set.erase(m_q);
|
||||
for (auto j : m_tight_set) {
|
||||
m_breakpoint_set.erase(j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>::ratio_test() {
|
||||
m_sign_of_alpha_r = define_sign_of_alpha_r();
|
||||
fill_breakpoint_set();
|
||||
m_flipped_boxed.clear();
|
||||
int initial_delta_sign = m_delta >= numeric_traits<T>::zero()? 1: -1;
|
||||
do {
|
||||
if (m_breakpoint_set.size() == 0) {
|
||||
set_status_to_tentative_dual_unbounded_or_dual_unbounded();
|
||||
return false;
|
||||
}
|
||||
this->set_status(FEASIBLE);
|
||||
find_q_and_tight_set();
|
||||
if (!tight_breakpoinst_are_all_boxed()) break;
|
||||
T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign;
|
||||
if (!delta_keeps_the_sign(initial_delta_sign, del)) break;
|
||||
if (m_tight_set.size() + 1 == m_breakpoint_set.size()) {
|
||||
break; // deciding not to flip since we might get stuck without finding m_q, the column entering the basis
|
||||
}
|
||||
// we can flip m_q together with the tight set and look for another breakpoint candidate for m_q and another tight set
|
||||
add_tight_breakpoints_and_q_to_flipped_set();
|
||||
m_delta = del;
|
||||
erase_tight_breakpoints_and_q_from_breakpoint_set();
|
||||
} while (true);
|
||||
m_theta_D = this->m_d[m_q] / this->m_pivot_row[m_q];
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::process_flipped() {
|
||||
init_a_wave_by_zeros();
|
||||
for (auto j : m_flipped_boxed) {
|
||||
update_a_wave(signed_span_of_boxed(j), j);
|
||||
}
|
||||
}
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::update_d_and_xB() {
|
||||
for (auto j : this->non_basis()) {
|
||||
this->m_d[j] -= m_theta_D * this->m_pivot_row[j];
|
||||
}
|
||||
this->m_d[m_p] = - m_theta_D;
|
||||
if (m_flipped_boxed.size() > 0) {
|
||||
process_flipped();
|
||||
update_xb_after_bound_flips();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::calculate_beta_r_precisely() {
|
||||
T t = numeric_traits<T>::zero();
|
||||
unsigned i = this->m_m();
|
||||
while (i--) {
|
||||
T b = this->m_pivot_row_of_B_1[i];
|
||||
t += b * b;
|
||||
}
|
||||
m_betas[m_r] = t;
|
||||
}
|
||||
// see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms"
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::update_xb_after_bound_flips() {
|
||||
this->m_factorization->solve_By(m_a_wave);
|
||||
unsigned i = this->m_m();
|
||||
while (i--) {
|
||||
this->m_x[this->m_basis[i]] -= m_a_wave[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::one_iteration() {
|
||||
unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving();
|
||||
unsigned offset_in_rows = this->m_settings.random_next() % this->m_m();
|
||||
if (this->get_status() == TENTATIVE_DUAL_UNBOUNDED) {
|
||||
number_of_rows_to_try = this->m_m();
|
||||
} else {
|
||||
this->set_status(FEASIBLE);
|
||||
}
|
||||
pricing_loop(number_of_rows_to_try, offset_in_rows);
|
||||
lean_assert(problem_is_dual_feasible());
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::solve() { // see the page 35
|
||||
lean_assert(d_is_correct());
|
||||
lean_assert(problem_is_dual_feasible());
|
||||
lean_assert(this->basis_heading_is_correct());
|
||||
this->set_total_iterations(0);
|
||||
this->iters_with_no_cost_growing() = 0;
|
||||
do {
|
||||
if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over("", *this->m_settings.get_message_ostream())){
|
||||
return;
|
||||
}
|
||||
one_iteration();
|
||||
} while (this->get_status() != FLOATING_POINT_ERROR && this->get_status() != DUAL_UNBOUNDED && this->get_status() != OPTIMAL &&
|
||||
this->iters_with_no_cost_growing() <= this->m_settings.max_number_of_iterations_with_no_improvements
|
||||
&& this->total_iterations() <= this->m_settings.max_total_number_of_iterations);
|
||||
}
|
||||
}
|
29
src/util/lp/lp_dual_core_solver_instances.cpp
Normal file
29
src/util/lp/lp_dual_core_solver_instances.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "util/vector.h"
|
||||
#include <functional>
|
||||
#include "util/lp/lp_dual_core_solver.hpp"
|
||||
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::start_with_initial_basis_and_make_it_dual_feasible();
|
||||
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::solve();
|
||||
template lean::lp_dual_core_solver<double, double>::lp_dual_core_solver(lean::static_matrix<double, double>&, vector<bool>&,
|
||||
vector<double>&,
|
||||
vector<double>&,
|
||||
vector<unsigned int>&,
|
||||
vector<unsigned> &,
|
||||
vector<int> &,
|
||||
vector<double>&,
|
||||
vector<lean::column_type>&,
|
||||
vector<double>&,
|
||||
vector<double>&,
|
||||
lean::lp_settings&, const lean::column_namer&);
|
||||
template void lean::lp_dual_core_solver<double, double>::start_with_initial_basis_and_make_it_dual_feasible();
|
||||
template void lean::lp_dual_core_solver<double, double>::solve();
|
||||
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::restore_non_basis();
|
||||
template void lean::lp_dual_core_solver<double, double>::restore_non_basis();
|
||||
template void lean::lp_dual_core_solver<double, double>::revert_to_previous_basis();
|
||||
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::revert_to_previous_basis();
|
80
src/util/lp/lp_dual_simplex.h
Normal file
80
src/util/lp/lp_dual_simplex.h
Normal file
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include "util/vector.h"
|
||||
#include "util/lp/lp_utils.h"
|
||||
#include "util/lp/lp_solver.h"
|
||||
#include "util/lp/lp_dual_core_solver.h"
|
||||
namespace lean {
|
||||
|
||||
template <typename T, typename X>
|
||||
class lp_dual_simplex: public lp_solver<T, X> {
|
||||
lp_dual_core_solver<T, X> * m_core_solver;
|
||||
vector<T> m_b_copy;
|
||||
vector<T> m_low_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver
|
||||
vector<column_type> m_column_types_of_core_solver;
|
||||
vector<column_type> m_column_types_of_logicals;
|
||||
vector<bool> m_can_enter_basis;
|
||||
public:
|
||||
~lp_dual_simplex() {
|
||||
if (m_core_solver != nullptr) {
|
||||
delete m_core_solver;
|
||||
}
|
||||
}
|
||||
|
||||
lp_dual_simplex() : m_core_solver(nullptr) {}
|
||||
|
||||
|
||||
void decide_on_status_after_stage1();
|
||||
|
||||
void fix_logical_for_stage2(unsigned j);
|
||||
|
||||
void fix_structural_for_stage2(unsigned j);
|
||||
|
||||
void unmark_boxed_and_fixed_columns_and_fix_structural_costs();
|
||||
|
||||
void restore_right_sides();
|
||||
|
||||
void solve_for_stage2();
|
||||
|
||||
void fill_x_with_zeros();
|
||||
|
||||
void stage1();
|
||||
|
||||
void stage2();
|
||||
|
||||
void fill_first_stage_solver_fields();
|
||||
|
||||
column_type get_column_type(unsigned j);
|
||||
|
||||
void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j);
|
||||
|
||||
void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j);
|
||||
|
||||
void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver();
|
||||
|
||||
void set_type_for_logical(unsigned j, column_type col_type) {
|
||||
this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type;
|
||||
}
|
||||
|
||||
void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row,
|
||||
unsigned & slack_var,
|
||||
unsigned & artificial);
|
||||
|
||||
void augment_matrix_A_and_fill_x_and_allocate_some_fields();
|
||||
|
||||
|
||||
|
||||
void copy_m_b_aside_and_set_it_to_zeros();
|
||||
|
||||
void find_maximal_solution();
|
||||
|
||||
virtual T get_column_value(unsigned column) const {
|
||||
return this->get_column_value_with_core_solver(column, m_core_solver);
|
||||
}
|
||||
|
||||
T get_current_cost() const;
|
||||
};
|
||||
}
|
362
src/util/lp/lp_dual_simplex.hpp
Normal file
362
src/util/lp/lp_dual_simplex.hpp
Normal file
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
Copyright (c) 2017 Microsoft Corporation
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lp_dual_simplex.h"
|
||||
namespace lean{
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::decide_on_status_after_stage1() {
|
||||
switch (m_core_solver->get_status()) {
|
||||
case OPTIMAL:
|
||||
if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
|
||||
this->m_status = FEASIBLE;
|
||||
} else {
|
||||
this->m_status = UNBOUNDED;
|
||||
}
|
||||
break;
|
||||
case DUAL_UNBOUNDED:
|
||||
lean_unreachable();
|
||||
case ITERATIONS_EXHAUSTED:
|
||||
this->m_status = ITERATIONS_EXHAUSTED;
|
||||
break;
|
||||
case TIME_EXHAUSTED:
|
||||
this->m_status = TIME_EXHAUSTED;
|
||||
break;
|
||||
case FLOATING_POINT_ERROR:
|
||||
this->m_status = FLOATING_POINT_ERROR;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::fix_logical_for_stage2(unsigned j) {
|
||||
lean_assert(j >= this->number_of_core_structurals());
|
||||
switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) {
|
||||
case column_type::low_bound:
|
||||
m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
m_column_types_of_core_solver[j] = column_type::low_bound;
|
||||
m_can_enter_basis[j] = true;
|
||||
break;
|
||||
case column_type::fixed:
|
||||
this->m_upper_bounds[j] = m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
m_column_types_of_core_solver[j] = column_type::fixed;
|
||||
m_can_enter_basis[j] = false;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::fix_structural_for_stage2(unsigned j) {
|
||||
column_info<T> * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]];
|
||||
switch (ci->get_column_type()) {
|
||||
case column_type::low_bound:
|
||||
m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
m_column_types_of_core_solver[j] = column_type::low_bound;
|
||||
m_can_enter_basis[j] = true;
|
||||
break;
|
||||
case column_type::fixed:
|
||||
case column_type::upper_bound:
|
||||
lean_unreachable();
|
||||
case column_type::boxed:
|
||||
this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j];
|
||||
m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
m_column_types_of_core_solver[j] = column_type::boxed;
|
||||
m_can_enter_basis[j] = true;
|
||||
break;
|
||||
case column_type::free_column:
|
||||
m_can_enter_basis[j] = true;
|
||||
m_column_types_of_core_solver[j] = column_type::free_column;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
// T cost_was = this->m_costs[j];
|
||||
this->set_scaled_cost(j);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::unmark_boxed_and_fixed_columns_and_fix_structural_costs() {
|
||||
unsigned j = this->m_A->column_count();
|
||||
while (j-- > this->number_of_core_structurals()) {
|
||||
fix_logical_for_stage2(j);
|
||||
}
|
||||
j = this->number_of_core_structurals();
|
||||
while (j--) {
|
||||
fix_structural_for_stage2(j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::restore_right_sides() {
|
||||
unsigned i = this->m_A->row_count();
|
||||
while (i--) {
|
||||
this->m_b[i] = m_b_copy[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::solve_for_stage2() {
|
||||
m_core_solver->restore_non_basis();
|
||||
m_core_solver->solve_yB(m_core_solver->m_y);
|
||||
m_core_solver->fill_reduced_costs_from_m_y_by_rows();
|
||||
m_core_solver->start_with_initial_basis_and_make_it_dual_feasible();
|
||||
m_core_solver->set_status(FEASIBLE);
|
||||
m_core_solver->solve();
|
||||
switch (m_core_solver->get_status()) {
|
||||
case OPTIMAL:
|
||||
this->m_status = OPTIMAL;
|
||||
break;
|
||||
case DUAL_UNBOUNDED:
|
||||
this->m_status = INFEASIBLE;
|
||||
break;
|
||||
case TIME_EXHAUSTED:
|
||||
this->m_status = TIME_EXHAUSTED;
|
||||
break;
|
||||
case FLOATING_POINT_ERROR:
|
||||
this->m_status = FLOATING_POINT_ERROR;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
this->m_second_stage_iterations = m_core_solver->total_iterations();
|
||||
this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_x_with_zeros() {
|
||||
unsigned j = this->m_A->column_count();
|
||||
while (j--) {
|
||||
this->m_x[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::stage1() {
|
||||
lean_assert(m_core_solver == nullptr);
|
||||
this->m_x.resize(this->m_A->column_count(), numeric_traits<T>::zero());
|
||||
if (this->m_settings.get_message_ostream() != nullptr)
|
||||
this->print_statistics_on_A(*this->m_settings.get_message_ostream());
|
||||
m_core_solver = new lp_dual_core_solver<T, X>(
|
||||
*this->m_A,
|
||||
m_can_enter_basis,
|
||||
this->m_b, // the right side vector
|
||||
this->m_x,
|
||||
this->m_basis,
|
||||
this->m_nbasis,
|
||||
this->m_heading,
|
||||
this->m_costs,
|
||||
this->m_column_types_of_core_solver,
|
||||
this->m_low_bounds,
|
||||
this->m_upper_bounds,
|
||||
this->m_settings,
|
||||
*this);
|
||||
m_core_solver->fill_reduced_costs_from_m_y_by_rows();
|
||||
m_core_solver->start_with_initial_basis_and_make_it_dual_feasible();
|
||||
if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
|
||||
// skipping stage 1
|
||||
m_core_solver->set_status(OPTIMAL);
|
||||
m_core_solver->set_total_iterations(0);
|
||||
} else {
|
||||
m_core_solver->solve();
|
||||
}
|
||||
decide_on_status_after_stage1();
|
||||
this->m_first_stage_iterations = m_core_solver->total_iterations();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::stage2() {
|
||||
unmark_boxed_and_fixed_columns_and_fix_structural_costs();
|
||||
restore_right_sides();
|
||||
solve_for_stage2();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_solver_fields() {
|
||||
unsigned slack_var = this->number_of_core_structurals();
|
||||
unsigned artificial = this->number_of_core_structurals() + this->m_slacks;
|
||||
|
||||
for (unsigned row = 0; row < this->row_count(); row++) {
|
||||
fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial);
|
||||
}
|
||||
fill_costs_and_bounds_and_column_types_for_the_first_stage_solver();
|
||||
}
|
||||
|
||||
template <typename T, typename X> column_type lp_dual_simplex<T, X>::get_column_type(unsigned j) {
|
||||
lean_assert(j < this->m_A->column_count());
|
||||
if (j >= this->number_of_core_structurals()) {
|
||||
return m_column_types_of_logicals[j - this->number_of_core_structurals()];
|
||||
}
|
||||
return this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]->get_column_type();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) {
|
||||
// see 4.7 in the dissertation of Achim Koberstein
|
||||
lean_assert(this->m_core_solver_columns_to_external_columns.find(j) !=
|
||||
this->m_core_solver_columns_to_external_columns.end());
|
||||
|
||||
T free_bound = T(1e4); // see 4.8
|
||||
unsigned jj = this->m_core_solver_columns_to_external_columns[j];
|
||||
lean_assert(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end());
|
||||
column_info<T> * ci = this->m_map_from_var_index_to_column_info[jj];
|
||||
switch (ci->get_column_type()) {
|
||||
case column_type::upper_bound: {
|
||||
std::stringstream s;
|
||||
s << "unexpected bound type " << j << " "
|
||||
<< column_type_to_string(get_column_type(j));
|
||||
throw_exception(s.str());
|
||||
break;
|
||||
}
|
||||
case column_type::low_bound: {
|
||||
m_can_enter_basis[j] = true;
|
||||
this->set_scaled_cost(j);
|
||||
this->m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
this->m_upper_bounds[j] =numeric_traits<T>::one();
|
||||
break;
|
||||
}
|
||||
case column_type::free_column: {
|
||||
m_can_enter_basis[j] = true;
|
||||
this->set_scaled_cost(j);
|
||||
this->m_upper_bounds[j] = free_bound;
|
||||
this->m_low_bounds[j] = -free_bound;
|
||||
break;
|
||||
}
|
||||
case column_type::boxed:
|
||||
m_can_enter_basis[j] = false;
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
this->m_upper_bounds[j] = this->m_low_bounds[j] = numeric_traits<T>::zero(); // is it needed?
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
m_column_types_of_core_solver[j] = column_type::boxed;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) {
|
||||
this->m_costs[j] = 0;
|
||||
lean_assert(get_column_type(j) != column_type::upper_bound);
|
||||
if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::low_bound))) {
|
||||
m_column_types_of_core_solver[j] = column_type::boxed;
|
||||
this->m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
this->m_upper_bounds[j] = numeric_traits<T>::one();
|
||||
} else {
|
||||
m_column_types_of_core_solver[j] = column_type::fixed;
|
||||
this->m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
this->m_upper_bounds[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() {
|
||||
unsigned j = this->m_A->column_count();
|
||||
while (j-- > this->number_of_core_structurals()) { // go over logicals here
|
||||
fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j);
|
||||
}
|
||||
j = this->number_of_core_structurals();
|
||||
while (j--) {
|
||||
fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row,
|
||||
unsigned & slack_var,
|
||||
unsigned & artificial) {
|
||||
lean_assert(row < this->row_count());
|
||||
auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]];
|
||||
// we need to bring the program to the form Ax = b
|
||||
T rs = this->m_b[row];
|
||||
switch (constraint.m_relation) {
|
||||
case Equal: // no slack variable here
|
||||
set_type_for_logical(artificial, column_type::fixed);
|
||||
this->m_basis[row] = artificial;
|
||||
this->m_costs[artificial] = numeric_traits<T>::zero();
|
||||
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
|
||||
artificial++;
|
||||
break;
|
||||
|
||||
case Greater_or_equal:
|
||||
set_type_for_logical(slack_var, column_type::low_bound);
|
||||
(*this->m_A)(row, slack_var) = - numeric_traits<T>::one();
|
||||
if (rs > 0) {
|
||||
// adding one artificial
|
||||
set_type_for_logical(artificial, column_type::fixed);
|
||||
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
|
||||
this->m_basis[row] = artificial;
|
||||
this->m_costs[artificial] = numeric_traits<T>::zero();
|
||||
artificial++;
|
||||
} else {
|
||||
// we can put a slack_var into the basis, and avoid adding an artificial variable
|
||||
this->m_basis[row] = slack_var;
|
||||
this->m_costs[slack_var] = numeric_traits<T>::zero();
|
||||
}
|
||||
slack_var++;
|
||||
break;
|
||||
case Less_or_equal:
|
||||
// introduce a non-negative slack variable
|
||||
set_type_for_logical(slack_var, column_type::low_bound);
|
||||
(*this->m_A)(row, slack_var) = numeric_traits<T>::one();
|
||||
if (rs < 0) {
|
||||
// adding one artificial
|
||||
set_type_for_logical(artificial, column_type::fixed);
|
||||
(*this->m_A)(row, artificial) = - numeric_traits<T>::one();
|
||||
this->m_basis[row] = artificial;
|
||||
this->m_costs[artificial] = numeric_traits<T>::zero();
|
||||
artificial++;
|
||||
} else {
|
||||
// we can put slack_var into the basis, and avoid adding an artificial variable
|
||||
this->m_basis[row] = slack_var;
|
||||
this->m_costs[slack_var] = numeric_traits<T>::zero();
|
||||
}
|
||||
slack_var++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::augment_matrix_A_and_fill_x_and_allocate_some_fields() {
|
||||
this->count_slacks_and_artificials();
|
||||
this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials);
|
||||
unsigned n = this->m_A->column_count();
|
||||
this->m_column_types_of_core_solver.resize(n);
|
||||
m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials);
|
||||
this->m_costs.resize(n);
|
||||
this->m_upper_bounds.resize(n);
|
||||
this->m_low_bounds.resize(n);
|
||||
m_can_enter_basis.resize(n);
|
||||
this->m_basis.resize(this->m_A->row_count());
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::copy_m_b_aside_and_set_it_to_zeros() {
|
||||
for (unsigned i = 0; i < this->m_b.size(); i++) {
|
||||
m_b_copy.push_back(this->m_b[i]);
|
||||
this->m_b[i] = numeric_traits<T>::zero(); // preparing for the first stage
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>::find_maximal_solution(){
|
||||
if (this->problem_is_empty()) {
|
||||
this->m_status = lp_status::EMPTY;
|
||||
return;
|
||||
}
|
||||
|
||||
this->flip_costs(); // do it for now, todo ( remove the flipping)
|
||||
|
||||
this->cleanup();
|
||||
if (this->m_status == INFEASIBLE) {
|
||||
return;
|
||||
}
|
||||
this->fill_matrix_A_and_init_right_side();
|
||||
this->fill_m_b();
|
||||
this->scale();
|
||||
augment_matrix_A_and_fill_x_and_allocate_some_fields();
|
||||
fill_first_stage_solver_fields();
|
||||
copy_m_b_aside_and_set_it_to_zeros();
|
||||
stage1();
|
||||
if (this->m_status == FEASIBLE) {
|
||||
stage2();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> T lp_dual_simplex<T, X>::get_current_cost() const {
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto it : this->m_map_from_var_index_to_column_info) {
|
||||
ret += this->get_column_cost_value(it.first, it.second);
|
||||
}
|
||||
return -ret; // we flip costs for now
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue