3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-27 02:45:51 +00:00
This commit is contained in:
Nikolaj Bjorner 2017-05-23 10:52:27 -07:00
commit db4bbecb27
174 changed files with 30440 additions and 922 deletions

View file

@ -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; }
}
}
}

View file

@ -3,4 +3,4 @@
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>
</configuration>

View file

@ -75,4 +75,4 @@
<Target Name="AfterBuild">
</Target>
-->
</Project>
</Project>

View file

@ -344,4 +344,4 @@
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
</Project>

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Code.Contract" version="1.0.0" targetFramework="net35" />
</packages>
</packages>

View file

@ -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},
)

View file

@ -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):

View file

@ -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); }

View file

@ -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";
}
}
}

View file

@ -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(); }

View file

@ -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;
}

View file

@ -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)) {

View file

@ -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)) {

View file

@ -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);

View file

@ -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

View file

@ -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;
}

View file

@ -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

View file

@ -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);
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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();

View file

@ -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;

View file

@ -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
View 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
View 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);

View file

@ -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();
}

View file

@ -44,4 +44,4 @@ public:
void install_smt2_extra_cmds(cmd_context & ctx) {
ctx.insert(alloc(include_cmd));
}
}

View file

@ -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);

View file

@ -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();

View file

@ -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 {

View file

@ -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.

View file

@ -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

File diff suppressed because it is too large Load diff

97
src/smt/theory_lra.h Normal file
View 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);
};
}

View file

@ -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();

View file

@ -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);

View file

@ -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

View file

@ -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;

View file

@ -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);

View file

@ -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
View 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

File diff suppressed because it is too large Load diff

View file

@ -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
View 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;
}
};
}

View 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();
}
};
}

View file

@ -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 */

View 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
};
}

View 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
}

View 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);
}

View 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); }
};
}

View 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
}

View 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&);
}

View 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();
}
};
}

View 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));
}
}
}
}

View 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
View 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
View 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;
}
};
}

View 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);
}
}
};
}

View 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;
}
};
}

View 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);
};
}

View 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);
}
}

View 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();

View 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

View 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

View 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
View 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
View 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
}
}

View 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
View 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

View 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) {}
};
}

View 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
}

View 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
};
}

View 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
}

View 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);

View 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
View 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;
}
};
}

View 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;
}
};
}

View 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);
}
};
}

View 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;
}
};
}

View 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);
}
};
}

View 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;
}
};
}

View 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; }
};
}

View 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);
}
}
};
}

View 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());
}
}

View 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"

View 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

File diff suppressed because it is too large Load diff

64
src/util/lp/lar_term.h Normal file
View 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();
}
};
}

View 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(); }
};
}

View 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;
}
};
}

File diff suppressed because it is too large Load diff

View 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;

View 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; }
};
}

View 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);
}
}

View 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();

View 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;
};
}

View 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