3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-06 01:24:08 +00:00

add stand-alone simplex

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2014-01-21 08:40:28 -08:00
parent c6a9dae00a
commit 26a3d2ca31
9 changed files with 1560 additions and 20 deletions

View file

@ -15,6 +15,7 @@ def init_project_def():
add_lib('sat', ['util'])
add_lib('nlsat', ['polynomial', 'sat'])
add_lib('hilbert', ['util'], 'math/hilbert')
add_lib('simplex', ['util'], 'math/simplex')
add_lib('interval', ['util'], 'math/interval')
add_lib('realclosure', ['interval'], 'math/realclosure')
add_lib('subpaving', ['interval'], 'math/subpaving')
@ -75,7 +76,7 @@ def init_project_def():
add_lib('api', ['portfolio', 'user_plugin', 'smtparser', 'realclosure','interp','opt'],
includes2install=['z3.h', 'z3_v1.h', 'z3_macros.h'] + API_files)
add_exe('shell', ['api', 'sat', 'extra_cmds','opt'], exe_name='z3')
add_exe('test', ['api', 'fuzzing'], exe_name='test-z3', install=False)
add_exe('test', ['api', 'fuzzing', 'simplex'], exe_name='test-z3', install=False)
add_dll('api_dll', ['api', 'sat', 'extra_cmds'], 'api/dll',
reexports=['api'],
dll_name='libz3',

View file

@ -0,0 +1,26 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
simplex.h
Abstract:
Multi-precision simplex tableau.
Author:
Nikolaj Bjorner (nbjorner) 2014-01-15
Notes:
--*/
#include"simplex.h"
#include"sparse_matrix_def.h"
#include"simplex_def.h"
namespace simplex {
template class simplex<mpz_ext>;
template class simplex<mpq_ext>;
};

157
src/math/simplex/simplex.h Normal file
View file

@ -0,0 +1,157 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
simplex.h
Abstract:
Multi-precision simplex tableau.
- It uses code from theory_arith where applicable.
- It is detached from the theory class and ASTs.
- It uses non-shared mpz/mpq's avoiding global locks and operations on rationals.
- It follows the same sparse tableau layout (no LU yet).
- It does not include features for non-linear arithmetic.
- Branch/bound/cuts is external.
Author:
Nikolaj Bjorner (nbjorner) 2014-01-15
Notes:
--*/
#ifndef _SIMPLEX_H_
#define _SIMPLEX_H_
#include "sparse_matrix.h"
#include "mpq_inf.h"
#include "heap.h"
#include "lbool.h"
namespace simplex {
template<typename Ext>
class simplex {
typedef unsigned var_t;
typedef typename Ext::eps_numeral eps_numeral;
typedef typename Ext::numeral numeral;
typedef typename Ext::manager manager;
typedef typename Ext::eps_manager eps_manager;
typedef typename Ext::scoped_numeral scoped_numeral;
typedef _scoped_numeral<eps_manager> scoped_eps_numeral;
typedef typename _scoped_numeral_vector<eps_manager> scoped_eps_numeral_vector;
typedef sparse_matrix<Ext> matrix;
struct var_lt {
bool operator()(var_t v1, var_t v2) const { return v1 < v2; }
};
typedef heap<var_lt> var_heap;
enum pivot_strategy_t {
S_BLAND,
S_GREATEST_ERROR,
S_LEAST_ERROR,
S_DEFAULT
};
struct var_info {
unsigned m_base2row:29;
unsigned m_is_base:1;
unsigned m_lower_valid:1;
unsigned m_upper_valid:1;
eps_numeral m_value;
eps_numeral m_lower;
eps_numeral m_upper;
numeral m_base_coeff;
var_info():
m_base2row(0),
m_is_base(false),
m_lower_valid(false),
m_upper_valid(false)
{}
};
static const var_t null_var = UINT_MAX;
matrix M;
unsigned m_max_iterations;
volatile bool m_cancel;
var_heap m_to_patch;
mutable manager m;
mutable eps_manager em;
vector<var_info> m_vars;
svector<var_t> m_row2base;
bool m_bland;
random_gen m_random;
public:
simplex():
m_max_iterations(UINT_MAX),
m_cancel(false),
m_to_patch(1024),
m_bland(false) {}
typedef typename matrix::row row;
typedef typename matrix::row_iterator row_iterator;
typedef typename matrix::col_iterator col_iterator;
void ensure_var(var_t v);
row add_row(unsigned num_vars, var_t base, var_t const* vars, numeral const* coeffs);
void del_row(row const& r);
void set_lower(var_t var, eps_numeral const& b);
void set_upper(var_t var, eps_numeral const& b);
void unset_lower(var_t var);
void unset_upper(var_t var);
void set_value(var_t var, eps_numeral const& b);
void set_cancel(bool f) { m_cancel = f; }
void set_max_iterations(unsigned m) { m_max_iterations = m; }
lbool make_feasible();
lbool optimize(var_t var);
eps_numeral const& get_value(var_t v);
void display(std::ostream& out) const;
private:
var_t select_var_to_fix();
pivot_strategy_t pivot_strategy();
var_t select_smallest_var() { return m_to_patch.empty()?null_var:m_to_patch.erase_min(); }
var_t select_error_var(bool least);
// row get_infeasible_row() { }
void check_blands_rule(var_t v) { }
bool make_var_feasible(var_t x_i);
void update_and_pivot(var_t x_i, var_t x_j, numeral const& a_ij, eps_numeral const& new_value);
void update_value(var_t v, eps_numeral const& delta);
void update_value_core(var_t v, eps_numeral const& delta);
var_t select_pivot(var_t x_i, bool is_below, scoped_numeral& out_a_ij);
var_t select_blands_pivot(var_t x_i, bool is_below, scoped_numeral& out_a_ij);
template<bool is_below>
var_t select_pivot_core(var_t x_i, scoped_numeral& out_a_ij);
int get_num_non_free_dep_vars(var_t x_j, int best_so_far);
bool below_lower(var_t v) const;
bool above_upper(var_t v) const;
bool above_lower(var_t v) const;
bool below_upper(var_t v) const;
bool outside_bounds(var_t v) const { return below_lower(v) || above_upper(v); }
bool is_free(var_t v) const { return !m_vars[v].m_lower_valid && !m_vars[v].m_upper_valid; }
bool is_non_free(var_t v) const { return !is_free(v); }
unsigned get_num_vars() const { return m_vars.size(); }
bool is_base(var_t x) const { return m_vars[x].m_is_base; }
bool well_formed() const;
};
};
#endif

View file

@ -0,0 +1,480 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
simplex_def.h
Abstract:
Author:
Nikolaj Bjorner (nbjorner) 2014-01-15
Notes:
--*/
#ifndef _SIMPLEX_DEF_H_
#define _SIMPLEX_DEF_H_
namespace simplex {
template<typename Ext>
typename simplex<Ext>::row
simplex<Ext>::add_row(var_t base, unsigned num_vars, var_t const* vars, numeral const* coeffs) {
DEBUG_CODE(
bool found = false;
for (unsigned i = 0; !found && i < num_vars; ++i) found = vars[i] == base;
SASSERT(found);
);
row r = M.mk_row();
for (unsigned i = 0; i < num_vars; ++i) {
M.add(r, coeffs[i], vars[i]);
}
while (m_row2base.size() <= r.id()) {
m_row2base.push_back(null_var);
}
m_row2base[r.id()] = base;
m_vars[base].m_base2row = r.id();
m_vars[base].m_is_base = true;
return r;
}
template<typename Ext>
void simplex<Ext>::del_row(row const& r) {
m_is_base[m_row2base[r.id()]] = false;
M.del(r);
}
template<typename Ext>
void simplex<Ext>::set_lower(var_t var, eps_numeral const& b) {
var_info& vi = m_vars[var];
em.set(vi.m_lower, b);
vi.m_lower_valid = true;
if (em.lt(vi.m_value, b)) {
scoped_eps_numeral delta(em);
em.sub(b, vi.m_value, delta);
update_value(var, delta);
}
}
template<typename Ext>
void simplex<Ext>::set_upper(var_t var, eps_numeral const& b) {
var_info& vi = m_vars[var];
em.set(vi.m_upper, b);
vi.m_upper_valid = true;
if (em.gt(vi.m_value, b)) {
scoped_eps_numeral delta(em);
em.sub(b, vi.m_value, delta);
update_value(var, delta);
}
}
template<typename Ext>
void simplex<Ext>::unset_lower(var_t var) {
m_vars[var].m_lower_valid = false;
}
template<typename Ext>
void simplex<Ext>::unset_upper(var_t var) {
m_vars[var].m_upper_valid = false;
}
template<typename Ext>
void simplex<Ext>::set_value(var_t var, eps_numeral const& b) {
scoped_eps_numeral delta(em);
em.sub(b, m_vars[var].m_value, delta);
update_value(var, delta);
}
template<typename Ext>
typename simplex<Ext>::eps_numeral const&
simplex<Ext>::get_value(var_t v) {
return m_vars[v].m_value;
}
template<typename Ext>
void simplex<Ext>::display(std::ostream& out) const {
M.display(out);
for (unsigned i = 0; i < m_vars.size(); ++i) {
var_info const& vi = m_vars[i];
out << "v" << i << " ";
if (vi.m_is_base) out << "b:" << vi.m_base2row << " ";
em.display(out, vi.m_value);
out << " [";
if (vi.m_lower_valid) em.display(out, vi.m_lower) else out << "-oo";
out << ":";
if (vi.m_upper_valid) em.display(out, vi.m_upper) else out << "oo";
out << "]";
out << "\n";
}
}
template<typename Ext>
void simplex<Ext>::ensure_var(var_t v) {
while (v >= m_vars.size()) {
M.ensure_var(m_vars.size());
m_vars.push_back(var_info());
}
}
template<typename Ext>
lbool simplex<Ext>::make_feasible() {
unsigned num_iterations = 0;
var_t v = null_var;
while ((v = select_var_to_fix()) != null_var) {
if (m_cancel || num_iterations > m_max_iterations) {
return l_undef;
}
check_blands_rule(v);
if (!make_var_feasible(v)) {
return l_false;
}
++num_iterations;
SASSERT(well_formed());
}
return l_true;
}
/**
\brief Make x_j the new base variable for row of x_i.
x_i is assumed base variable of row r_i.
x_j is assumed to have coefficient a_ij in r_i.
a_ii*x_i + a_ij*x_j + r_i = 0
current value of x_i is v_i
new value of x_i is new_value
a_ii*(x_i + new_value - x_i) + a_ij*((x_i - new_value)*a_ii/a_ij + x_j) + r_i = 0
Let r_k be a row where x_j has coefficient x_kj != 0.
r_k <- r_k * a_ij - r_i * a_kj
*/
template<typename Ext>
void simplex<Ext>::update_and_pivot(var_t x_i, var_t x_j, numeral const& a_ij, eps_numeral const& new_value) {
SASSERT(is_base(x_i));
SASSERT(!is_base(x_j));
var_info& x_iI = m_vars[x_i];
var_info& x_jI = m_vars[x_j];
scoped_eps_numeral theta(em);
theta = x_iI.m_value;
theta -= new_value;
numeral const& a_ii = x_iI.m_base_coeff;
em.mul(theta, a_ii, theta);
em.div(theta, a_ij, theta);
update_value(x_j, theta);
SASSERT(em.eq(x_iI.m_value, new_value));
unsigned r_i = x_iI.m_base2row;
x_jI.m_base2row = r_i;
m_row2base[r_i] = x_j;
x_jI.m_is_base = true;
x_iI.m_is_base = false;
if (outside_bounds(x_j)) {
m_to_patch.insert(x_j);
}
col_iterator it = M.col_begin(x_j), end = M.col_end(x_j);
scoped_numeral a_kj(m), g(m);
for (; it != end; ++it) {
row r_k = it.get_row();
if (r_k != r_i) {
a_kj = it.get_row_entry().m_coeff;
a_kj.neg();
M.mul(r_k, a_ij);
M.add(r_k, a_kj, r_i);
var_t s = m_row2base[r_k.id()];
numeral& coeff = m_vars[s].m_base_coeff;
m.mul(coeff, a_ij, coeff);
M.gcd_normalize(r_k, g);
if (!m.is_one(g)) {
m.div(coeff, g, coeff);
}
}
}
}
template<typename Ext>
void simplex<Ext>::update_value(var_t v, eps_numeral const& delta) {
update_value_core(v, delta);
col_iterator it = M.col_begin(v), end = M.col_end(v);
// v <- v + delta
// s*s_coeff + v*v_coeff + R = 0
// ->
// (v + delta)*v_coeff + (s - delta*v_coeff/s_coeff)*v + R = 0
for (; it != end; ++it) {
row r = it.get_row();
var_t s = m_row2base[r.id()];
var_info& si = m_vars[s];
scoped_eps_numeral delta2(em);
numeral const& coeff = it.get_row_entry().m_coeff;
em.mul(delta, coeff, delta2);
em.div(delta2, si.m_base_coeff, delta2);
delta2.neg();
update_value_core(s, delta2);
// TBD m.add(si.m_base_coeff, delta2, si.m_base_coeff);
}
}
template<typename Ext>
void simplex<Ext>::update_value_core(var_t v, eps_numeral const& delta) {
eps_numeral& val = m_vars[v].m_value;
em.add(val, delta, val);
if (is_base(v) && outside_bounds(v)) {
m_to_patch.insert(v);
}
}
template<typename Ext>
bool simplex<Ext>::below_lower(var_t v) const {
var_info const& vi = m_vars[v];
return vi.m_lower_valid && em.lt(vi.m_value, vi.m_lower);
}
template<typename Ext>
bool simplex<Ext>::above_upper(var_t v) const {
var_info const& vi = m_vars[v];
return vi.m_upper_valid && em.gt(vi.m_value, vi.m_upper);
}
template<typename Ext>
bool simplex<Ext>::above_lower(var_t v) const {
var_info const& vi = m_vars[v];
return !vi.m_lower_valid || em.gt(vi.m_value, vi.m_lower);
}
template<typename Ext>
bool simplex<Ext>::below_upper(var_t v) const {
var_info const& vi = m_vars[v];
return !vi.m_upper_valid || em.lt(vi.m_value, vi.m_upper);
}
template<typename Ext>
bool simplex<Ext>::make_var_feasible(var_t x_i) {
scoped_numeral a_ij(m);
scoped_eps_numeral value(em);
bool is_below;
if (below_lower(x_i)) {
is_below = true;
value = m_vars[x_i].m_lower;
}
else if (above_upper(x_i)) {
is_below = false;
value = m_vars[x_i].m_upper;
}
else {
// x_i is already feasible
return true;
}
var_t x_j = select_pivot(x_i, is_below, a_ij);
if (x_j != null_var) {
update_and_pivot(x_i, x_j, a_ij, value);
}
return x_j != null_var;
}
/**
\brief Wrapper for select_blands_pivot_core and select_pivot_core
*/
template<typename Ext>
typename simplex<Ext>::var_t
simplex<Ext>::select_pivot(var_t x_i, bool is_below, scoped_numeral& out_a_ij) {
if (m_bland) {
return select_blands_pivot(x_i, is_below, out_a_ij);
}
if (is_below) {
return select_pivot_core<true>(x_i, out_a_ij);
}
else {
return select_pivot_core<false>(x_i, out_a_ij);
}
}
/**
\brief Select a variable x_j in the row r defining the base var x_i,
s.t. x_j can be used to patch the error in x_i. Return null_theory_var
if there is no x_j. Otherwise, return x_j and store its coefficient
in out_a_ij.
The argument is_below is true (false) if x_i is below its lower
bound (above its upper bound).
*/
template<typename Ext>
template<bool is_below>
typename simplex<Ext>::var_t
simplex<Ext>::select_pivot_core(var_t x_i, scoped_numeral & out_a_ij) {
SASSERT(is_base(x_i));
var_t max = get_num_vars();
var_t result = max;
row r = row(m_vars[x_i].m_base2row);
int n;
unsigned best_col_sz = UINT_MAX;
int best_so_far = INT_MAX;
row_iterator it = M.row_begin(r), end = M.row_end(r);
for (; it != end; ++it) {
var_t x_j = it->m_var;
numeral const & a_ij = it->m_coeff;
bool is_neg = is_below ? m.is_neg(a_ij) : m.is_pos(a_ij);
bool is_pos = !is_neg;
if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) {
int num = get_num_non_free_dep_vars(x_j, best_so_far);
unsigned col_sz = M.column_size(x_j);
if (num < best_so_far || (num == best_so_far && col_sz < best_col_sz)) {
result = x_j;
out_a_ij = a_ij;
best_so_far = num;
best_col_sz = col_sz;
n = 1;
}
else if (num == best_so_far && col_sz == best_col_sz) {
n++;
if (m_random()%n == 0) {
result = x_j;
out_a_ij = a_ij;
}
}
}
}
return result < max ? result : null_var;
}
/**
\brief Return the number of base variables that are non free and are v dependent.
The function adds 1 to the result if v is non free.
The function returns with a partial result r if r > best_so_far.
This function is used to select the pivot variable.
*/
template<typename Ext>
int simplex<Ext>::get_num_non_free_dep_vars(var_t x_j, int best_so_far) {
int result = is_non_free(x_j);
col_iterator it = M.col_begin(x_j), end = M.col_end(x_j);
for (; it != end; ++it) {
var_t s = m_row2base[it.get_row().id()];
result += is_non_free(s);
if (result > best_so_far)
return result;
}
return result;
}
/**
\brief Using Bland's rule, select a variable x_j in the row r defining the base var x_i,
s.t. x_j can be used to patch the error in x_i. Return null_theory_var
if there is no x_j. Otherwise, return x_j and store its coefficient
in out_a_ij.
*/
template<typename Ext>
typename simplex<Ext>::var_t
simplex<Ext>::select_blands_pivot(var_t x_i, bool is_below, scoped_numeral & out_a_ij) {
SASSERT(is_base(x_i));
unsigned max = get_num_vars();
var_t result = max;
row r(m_vars[x_i].m_base2row);
row_iterator it = M.row_begin(r), end = M.row_end(r);
for (; it != end; ++it) {
var_t x_j = it->m_var;
numeral const & a_ij = it->m_coeff;
bool is_neg = is_below ? m.is_neg(a_ij) : m.is_pos(a_ij);
bool is_pos = !is_neg;
if (x_i != x_j && ((is_pos && above_lower(x_j)) || (is_neg && below_upper(x_j)))) {
SASSERT(!is_base(x_j));
if (x_j < result) {
result = x_j;
out_a_ij = a_ij;
}
}
}
return result < max ? result : null_var;
}
template<typename Ext>
lbool simplex<Ext>::optimize(var_t v) {
// TBD SASSERT(is_feasible());
// pick row for v and check if primal
// bounds are slack.
// return l_false for unbounded.
// return l_undef for canceled.
// return l_true for optimal.
return l_true;
}
template<typename Ext>
typename simplex<Ext>::pivot_strategy_t
simplex<Ext>::pivot_strategy() {
if (m_bland) {
return S_BLAND;
}
return S_DEFAULT;
}
template<typename Ext>
typename simplex<Ext>::var_t
simplex<Ext>::select_var_to_fix() {
switch (pivot_strategy()) {
case S_BLAND:
return select_smallest_var();
case S_GREATEST_ERROR:
return select_error_var(false);
case S_LEAST_ERROR:
return select_error_var(true);
default:
return select_smallest_var();
}
}
template<typename Ext>
typename simplex<Ext>::var_t
simplex<Ext>::select_error_var(bool least) {
var_t best = null_var;
scoped_eps_numeral best_error(em);
scoped_eps_numeral curr_error(em);
typename var_heap::iterator it = m_to_patch.begin();
typename var_heap::iterator end = m_to_patch.end();
for (; it != end; ++it) {
var_t v = *it;
var_info const& vi = m_vars[v];
if (below_lower(v))
em.sub(vi.m_lower, vi.m_value, curr_error);
else if (above_upper(v))
em.sub(vi.m_value, vi.m_lower, curr_error);
else
continue;
SASSERT(is_pos(curr_error));
if ((best == null_var) ||
(!least && curr_error > best_error) ||
(least && curr_error < best_error)) {
best = v;
best_error = curr_error;
}
}
if (best == null_var)
m_to_patch.clear(); // all variables are satisfied
else
m_to_patch.erase(best);
return best;
}
template<typename Ext>
bool simplex<Ext>::well_formed() const {
SASSERT(M.well_formed());
for (unsigned i = 0; i < m_row2base.size(); ++i) {
var_t s = m_row2base[i];
SASSERT(i == m_vars[s].m_base2row); // unless row is deleted.
//
// TBD: extract coefficient of base variable and compare
// with m_vars[s].m_base_coeff;
//
}
return true;
}
};
#endif

View file

@ -0,0 +1,254 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
sparse_matrix.h
Abstract:
Author:
Nikolaj Bjorner (nbjorner) 2014-01-15
Notes:
--*/
#ifndef _SPARSE_MATRIX_H_
#define _SPARSE_MATRIX_H_
#include "mpq_inf.h"
namespace simplex {
template<typename Ext>
class sparse_matrix {
public:
typedef typename Ext::numeral numeral;
typedef typename Ext::scoped_numeral scoped_numeral;
typedef typename Ext::manager manager;
typedef unsigned var_t;
struct row_entry {
numeral m_coeff;
var_t m_var;
row_entry(numeral& c, var_t v): m_coeff(c), m_var(v) {}
};
private:
static const int dead_id = -1;
/**
\brief A row_entry is: m_var*m_coeff
m_col_idx points to the place in the
column where the variable occurs.
*/
struct _row_entry : public row_entry {
union {
int m_col_idx;
int m_next_free_row_entry_idx;
};
_row_entry(numeral const & c, var_t v): row_entry(c, v), m_col_idx(0) {}
_row_entry() : row_entry(numeral(), dead_id), m_col_idx(0) {}
bool is_dead() const { return m_var == dead_id; }
};
/**
\brief A column entry points to the row and the row_entry within the row
that has a non-zero coefficient on the variable associated
with the column entry.
*/
struct col_entry {
int m_row_id;
union {
int m_row_idx;
int m_next_free_col_entry_idx;
};
col_entry(int r, int i): m_row_id(r), m_row_idx(i) {}
col_entry(): m_row_id(0), m_row_idx(0) {}
bool is_dead() const { return m_row_id == dead_id; }
};
struct column;
/**
\brief A row contains a base variable and set of
row_entries. The base variable must occur in the set of
row_entries with coefficient 1.
*/
struct _row {
vector<_row_entry> m_entries;
unsigned m_size; // the real size, m_entries contains dead row_entries.
int m_first_free_idx; // first available position.
_row();
unsigned size() const { return m_size; }
unsigned num_entries() const { return m_entries.size(); }
void reset(manager& m);
_row_entry & add_row_entry(unsigned & pos_idx);
void del_row_entry(unsigned idx);
void compress(manager& m, vector<column> & cols);
void compress_if_needed(manager& m, vector<column> & cols);
void save_var_pos(svector<int> & result_map) const;
void reset_var_pos(svector<int> & result_map) const;
bool is_coeff_of(var_t v, numeral const & expected) const;
int get_idx_of(var_t v) const;
};
/**
\brief A column stores in which rows a variable occurs.
The column may have free/dead entries. The field m_first_free_idx
is a reference to the first free/dead entry.
*/
struct column {
svector<col_entry> m_entries;
unsigned m_size;
int m_first_free_idx;
column():m_size(0), m_first_free_idx(-1) {}
unsigned size() const { return m_size; }
unsigned num_entries() const { return m_entries.size(); }
void reset();
void compress(vector<_row> & rows);
void compress_if_needed(vector<_row> & rows);
void compress_singleton(vector<_row> & rows, unsigned singleton_pos);
col_entry const * get_first_col_entry() const;
col_entry & add_col_entry(int & pos_idx);
void del_col_entry(unsigned idx);
};
manager m;
vector<_row> m_rows;
svector<unsigned> m_dead_rows; // rows to recycle
vector<column> m_columns; // per var
svector<int> m_var_pos; // temporary map from variables to positions in row
bool well_formed_row(unsigned row_id) const;
bool well_formed_column(unsigned column_id) const;
void del_row_entry(_row& r, unsigned pos);
public:
sparse_matrix() {}
~sparse_matrix();
class row {
unsigned m_id;
public:
row(unsigned r):m_id(r) {}
row():m_id(UINT_MAX) {}
bool operator!=(row const& other) const {
return m_id != other.m_id;
}
unsigned id() const { return m_id; }
};
void ensure_var(var_t v);
row mk_row();
void add(row r, numeral const& n, var_t var);
void add(row r, numeral const& n, row src);
void mul(row r, numeral const& n);
void neg(row r);
void del(row r);
void gcd_normalize(row const& r, scoped_numeral& g);
class row_iterator {
friend sparse_matrix;
unsigned m_curr;
_row & m_row;
void move_to_used() {
while (m_curr < m_row.m_entries.size() &&
m_row.m_entries[m_curr].is_dead()) {
++m_curr;
}
}
row_iterator(_row & r, bool begin):
m_curr(0), m_row(r) {
if (begin) {
move_to_used();
}
else {
m_curr = m_row.m_entries.size();
}
}
public:
row_entry & operator*() const { return m_row.m_entries[m_curr]; }
row_entry * operator->() const { return &(operator*()); }
row_iterator & operator++() { ++m_curr; move_to_used(); return *this; }
row_iterator operator++(int) { row_iterator tmp = *this; ++*this; return tmp; }
bool operator==(row_iterator const & it) const { return m_curr == it.m_curr; }
bool operator!=(row_iterator const & it) const { return m_curr != it.m_curr; }
};
row_iterator row_begin(row const& r) { return row_iterator(m_rows[r.id()], true); }
row_iterator row_end(row const& r) { return row_iterator(m_rows[r.id()], false); }
unsigned column_size(var_t v) const { return m_columns[v].size(); }
class col_iterator {
friend sparse_matrix;
unsigned m_curr;
column const& m_col;
vector<_row> const& m_rows;
void move_to_used() {
while (m_curr < m_col.m_entries.size() && m_col.m_entries[m_curr].is_dead()) {
++m_curr;
}
}
col_iterator(column const& c, vector<_row> const& r, bool begin):
m_curr(0), m_col(c), m_rows(r) {
if (begin) {
move_to_used();
}
else {
m_curr = m_col.m_entries.size();
}
}
public:
row get_row() { return row(m_col.m_entries[m_curr].m_row_id); }
row_entry const& get_row_entry() {
col_entry const& c = m_col.m_entries[m_curr];
int row_id = c.m_row_id;
return m_rows[row_id].m_entries[c.m_row_idx];
}
col_iterator & operator++() { ++m_curr; move_to_used(); return *this; }
col_iterator operator++(int) { col_iterator tmp = *this; ++*this; return tmp; }
bool operator==(col_iterator const & it) const { return m_curr == it.m_curr; }
bool operator!=(col_iterator const & it) const { return m_curr != it.m_curr; }
};
col_iterator col_begin(int v) const { return col_iterator(m_columns[v], m_rows, true); }
col_iterator col_end(int v) const { return col_iterator(m_columns[v], m_rows, false); }
void display(std::ostream& out) const;
bool well_formed() const;
};
struct mpz_ext {
typedef mpz numeral;
typedef scoped_mpz scoped_numeral;
typedef unsynch_mpz_manager manager;
typedef mpq_inf eps_numeral;
typedef unsynch_mpq_inf_manager eps_manager;
};
struct mpq_ext {
typedef mpq numeral;
typedef scoped_mpq scoped_numeral;
typedef unsynch_mpq_manager manager;
typedef mpq_inf eps_numeral;
typedef unsynch_mpq_inf_manager eps_manager;
};
};
#endif

View file

@ -0,0 +1,580 @@
/*++
Copyright (c) 2014 Microsoft Corporation
Module Name:
sparse_matrix_def.h
Abstract:
Author:
Nikolaj Bjorner (nbjorner) 2014-01-15
Notes:
mainly hoisted from theory_arith.h and theory_arith_aux.h
--*/
#ifndef _SPARSE_MATRIX_DEF_H_
#define _SPARSE_MATRIX_DEF_H_
#include "sparse_matrix.h"
#include "uint_set.h"
namespace simplex {
// -----------------------------------
//
// Rows
//
// -----------------------------------
template<typename Ext>
sparse_matrix<Ext>::_row::_row():
m_size(0),
m_first_free_idx(-1) {
}
template<typename Ext>
void sparse_matrix<Ext>::_row::reset(manager& m) {
for (unsigned i = 0; i < m_entries.size(); ++i) {
m.reset(m_entries[i].m_coeff);
}
m_entries.reset();
m_size = 0;
m_first_free_idx = -1;
}
/**
\brief Add a new row_entry. The result is a reference to the new row_entry.
The position of the new row_entry in the
row is stored in pos_idx.
*/
template<typename Ext>
typename sparse_matrix<Ext>::_row_entry &
sparse_matrix<Ext>::_row::add_row_entry(unsigned & pos_idx) {
m_size++;
if (m_first_free_idx == -1) {
pos_idx = m_entries.size();
m_entries.push_back(_row_entry());
return m_entries.back();
}
else {
SASSERT(m_first_free_idx >= 0);
pos_idx = static_cast<unsigned>(m_first_free_idx);
_row_entry & result = m_entries[pos_idx];
SASSERT(result.is_dead());
m_first_free_idx = result.m_next_free_row_entry_idx;
return result;
}
}
/**
\brief Delete row_entry at position idx.
*/
template<typename Ext>
void sparse_matrix<Ext>::_row::del_row_entry(unsigned idx) {
_row_entry & t = m_entries[idx];
SASSERT(!t.is_dead());
t.m_next_free_row_entry_idx = m_first_free_idx;
t.m_var = dead_id;
m_size--;
m_first_free_idx = idx;
SASSERT(t.is_dead());
}
/**
\brief Remove holes (i.e., dead entries) from the row.
*/
template<typename Ext>
void sparse_matrix<Ext>::_row::compress(manager& m, vector<column> & cols) {
unsigned i = 0;
unsigned j = 0;
unsigned sz = m_entries.size();
for (; i < sz; i++) {
_row_entry & t1 = m_entries[i];
if (!t1.is_dead()) {
if (i != j) {
_row_entry & t2 = m_entries[j];
t2.m_coeff.swap(t1.m_coeff);
t2.m_var = t1.m_var;
t2.m_col_idx = t1.m_col_idx;
SASSERT(!t2.is_dead());
column & col = cols[t2.m_var];
col.m_entries[t2.m_col_idx].m_row_idx = j;
}
j++;
}
}
SASSERT(j == m_size);
//
// alternative: update the free-list to point to the
// tail and avoid shrinking.
// if m.does not allocate memory (for wrapper around
// double), also bypass this step.
//
for (unsigned i = m_size; i < m_entries.size(); ++i) {
m.reset(m_entries[i].m_coeff);
}
m_entries.shrink(m_size);
m_first_free_idx = -1;
}
template<typename Ext>
void sparse_matrix<Ext>::_row::compress_if_needed(manager& m, vector<column> & cols) {
if (size() *2 < num_entries()) {
compress(m, cols);
}
}
/**
\brief Fill the map var -> pos/idx
*/
template<typename Ext>
inline void sparse_matrix<Ext>::_row::save_var_pos(svector<int> & result_map) const {
typename vector<_row_entry>::const_iterator it = m_entries.begin();
typename vector<_row_entry>::const_iterator end = m_entries.end();
unsigned idx = 0;
for (; it != end; ++it, ++idx) {
if (!it->is_dead()) {
result_map[it->m_var] = idx;
}
}
}
/**
\brief Reset the map var -> pos/idx. That is for all variables v in the row, set result[v] = -1
This method can be viewed as the "inverse" of save_var_pos.
*/
template<typename Ext>
inline void sparse_matrix<Ext>::_row::reset_var_pos(svector<int> & result_map) const {
typename vector<_row_entry>::const_iterator it = m_entries.begin();
typename vector<_row_entry>::const_iterator end = m_entries.end();
for (; it != end; ++it) {
if (!it->is_dead()) {
result_map[it->m_var] = -1;
}
}
}
template<typename Ext>
int sparse_matrix<Ext>::_row::get_idx_of(var_t v) const {
typename vector<_row_entry>::const_iterator it = m_entries.begin();
typename vector<_row_entry>::const_iterator end = m_entries.end();
for (unsigned idx = 0; it != end; ++it, ++idx) {
if (!it->is_dead() && it->m_var == v)
return idx;
}
return -1;
}
// -----------------------------------
//
// Columns
//
// -----------------------------------
template<typename Ext>
void sparse_matrix<Ext>::column::reset() {
m_entries.reset();
m_size = 0;
m_first_free_idx = -1;
}
/**
\brief Remove holes (i.e., dead entries) from the column.
*/
template<typename Ext>
void sparse_matrix<Ext>::column::compress(vector<_row> & rows) {
unsigned i = 0;
unsigned j = 0;
unsigned sz = m_entries.size();
for (; i < sz; i++) {
col_entry & e1 = m_entries[i];
if (!e1.is_dead()) {
if (i != j) {
m_entries[j] = e1;
_row & r = rows[e1.m_row_id];
r.m_entries[e1.m_row_idx].m_col_idx = j;
}
j++;
}
}
SASSERT(j == m_size);
m_entries.shrink(m_size);
m_first_free_idx = -1;
}
/**
\brief Invoke compress if the column contains too many holes (i.e., dead entries).
*/
template<typename Ext>
inline void sparse_matrix<Ext>::column::compress_if_needed(vector<_row> & rows) {
if (size() * 2 < num_entries()) {
compress(rows);
}
}
/**
\brief Special version of compress, that is used when the column contain
only one entry located at position singleton_pos.
*/
template<typename Ext>
void sparse_matrix<Ext>::column::compress_singleton(vector<_row> & rows, unsigned singleton_pos) {
SASSERT(m_size == 1);
if (singleton_pos != 0) {
col_entry & s = m_entries[singleton_pos];
m_entries[0] = s;
row & r = rows[s.m_row_id];
r[s.m_row_idx].m_col_idx = 0;
}
m_first_free_idx = -1;
m_entries.shrink(1);
}
template<typename Ext>
const typename sparse_matrix<Ext>::col_entry *
sparse_matrix<Ext>::column::get_first_col_entry() const {
typename svector<col_entry>::const_iterator it = m_entries.begin();
typename svector<col_entry>::const_iterator end = m_entries.end();
for (; it != end; ++it) {
if (!it->is_dead()) {
return it;
}
}
return 0;
}
template<typename Ext>
typename sparse_matrix<Ext>::col_entry &
sparse_matrix<Ext>::column::add_col_entry(int & pos_idx) {
m_size++;
if (m_first_free_idx == -1) {
pos_idx = m_entries.size();
m_entries.push_back(col_entry());
return m_entries.back();
}
else {
pos_idx = m_first_free_idx;
col_entry & result = m_entries[pos_idx];
SASSERT(result.is_dead());
m_first_free_idx = result.m_next_free_col_entry_idx;
return result;
}
}
template<typename Ext>
void sparse_matrix<Ext>::column::del_col_entry(unsigned idx) {
col_entry & c = m_entries[idx];
SASSERT(!c.is_dead());
c.m_row_id = dead_id;
c.m_next_free_col_entry_idx = m_first_free_idx;
m_first_free_idx = idx;
m_size--;
}
// -----------------------------------
//
// Matrix
//
// -----------------------------------
template<typename Ext>
sparse_matrix<Ext>::~sparse_matrix() {
for (unsigned i = 0; i < m_rows.size(); ++i) {
_row& r = m_rows[i];
for (unsigned j = 0; j < r.m_entries.size(); ++j) {
m.reset(r.m_entries[j].m_coeff);
}
}
}
template<typename Ext>
void sparse_matrix<Ext>::ensure_var(var_t v) {
while (m_columns.size() <= v) {
m_columns.push_back(column());
m_var_pos.push_back(-1);
}
}
template<typename Ext>
typename sparse_matrix<Ext>::row
sparse_matrix<Ext>::mk_row() {
if (m_dead_rows.empty()) {
row r(m_rows.size());
m_rows.push_back(_row());
return r;
}
else {
row r(m_dead_rows.back());
m_dead_rows.pop_back();
return r;
}
}
template<typename Ext>
void sparse_matrix<Ext>::add(row dst, numeral const& n, var_t v) {
_row& r = m_rows[dst.id()];
column& c = m_columns[v];
unsigned r_idx;
int c_idx;
_row_entry & r_entry = r.add_row_entry(r_idx);
col_entry& c_entry = c.add_col_entry(c_idx);
r_entry.m_var = v;
m.set(r_entry.m_coeff, n);
r_entry.m_col_idx = c_idx;
c_entry.m_row_id = dst.id();
c_entry.m_row_idx = r_idx;
}
/**
\brief Set row1 <- row1 + row2 * n
*/
template<typename Ext>
void sparse_matrix<Ext>::add(row row1, numeral const& n, row row2) {
// m_stats.m_add_rows++;
_row & r1 = m_rows[row1.id()];
_row & r2 = m_rows[row2.id()];
r1.save_var_pos(m_var_pos);
//
// loop over variables in row2,
// add terms in row2 to row1.
//
#define ADD_ROW(_SET_COEFF_, _ADD_COEFF_) \
row_iterator it = row_begin(row2); \
row_iterator end = row_end(row2); \
for (; it != end; ++it) { \
var_t v = it->m_var; \
int pos = m_var_pos[v]; \
if (pos == -1) { \
/* variable v is not in row1 */ \
unsigned row_idx; \
_row_entry & r_entry = r1.add_row_entry(row_idx); \
r_entry.m_var = v; \
m.set(r_entry.m_coeff, it->m_coeff); \
_SET_COEFF_; \
column & c = m_columns[v]; \
int col_idx; \
col_entry & c_entry = c.add_col_entry(col_idx); \
r_entry.m_col_idx = col_idx; \
c_entry.m_row_id = row1.id(); \
c_entry.m_row_idx = row_idx; \
} \
else { \
/* variable v is in row1 */ \
_row_entry & r_entry = r1.m_entries[pos]; \
SASSERT(r_entry.m_var == v); \
_ADD_COEFF_; \
if (m.is_zero(r_entry.m_coeff)) { \
del_row_entry(r1, pos); \
} \
} \
} \
((void) 0)
if (m.is_one(n)) {
ADD_ROW({},
m.add(r_entry.m_coeff, it->m_coeff, r_entry.m_coeff));
}
else if (m.is_minus_one(n)) {
ADD_ROW(m.neg(r_entry.m_coeff),
m.sub(r_entry.m_coeff, it->m_coeff, r_entry.m_coeff));
}
else {
scoped_numeral tmp(m);
ADD_ROW(m.mul(r_entry.m_coeff, n, r_entry.m_coeff),
m.mul(it->m_coeff, n, tmp);
m.add(r_entry.m_coeff, tmp, r_entry.m_coeff));
}
r1.reset_var_pos(m_var_pos);
r1.compress_if_needed(m, m_columns);
}
template<typename Ext>
void sparse_matrix<Ext>::del_row_entry(_row& r, unsigned pos) {
_row_entry & r_entry = r.m_entries[pos];
var_t v = r_entry.m_var;
int col_idx = r_entry.m_col_idx;
r.del_row_entry(pos);
column & c = m_columns[v];
c.del_col_entry(col_idx);
c.compress_if_needed(m_rows);
}
/**
\brief Set row <- -row
*/
template<typename Ext>
void sparse_matrix<Ext>::neg(row r) {
row_iterator it = row_begin(r);
row_iterator end = row_end(r);
for (; it != end; ++it) {
m.neg(it->m_coeff);
}
}
/**
\brief Set row <- n*row
*/
template<typename Ext>
void sparse_matrix<Ext>::mul(row r, numeral const& n) {
SASSERT(!m.is_zero(n));
if (m.is_one(n)) {
// no op
}
else if (m.is_minus_one(n)) {
neg(r);
}
else {
row_iterator it = row_begin(r);
row_iterator end = row_end(r);
for (; it != end; ++it) {
m.mul(it->m_coeff, n, it->m_coeff);
}
}
}
/**
\brief Delete row.
*/
template<typename Ext>
void sparse_matrix<Ext>::del(row r) {
_row& rw = r.id();
for (unsigned i = 0; i < rw.m_entries.size(); ++i) {
_row_entry& e = rw.m_entries[i];
if (!e.is_dead()) {
del_row_entry(rw, i);
}
}
SASSERT(rw.m_size == 0);
m_dead_rows.push_back(r.id());
}
/**
\brief normalize coefficients by dividing with they coefficients.
return the gcd.
*/
template<typename Ext>
void sparse_matrix<Ext>::gcd_normalize(row const& r, scoped_numeral& g) {
g.reset();
row_iterator it = row_begin(r), end = row_end(r);
for (; it != end && !m.is_one(g); ++it) {
if (m.is_zero(g)) g = it->m_coeff;
else m.gcd(g, it->m_coeff, g);
}
if (m.is_zero(g)) {
g = numeral(1);
}
if (!m.is_one(g)) {
row_iterator it2 = row_begin(r);
for (; it2 != end; ++it2) {
m.div(it2->m_coeff, g, it2->m_coeff);
}
}
}
/**
\brief well_formed check
*/
template<typename Ext>
bool sparse_matrix<Ext>::well_formed() const {
for (unsigned i = 0; i < m_rows.size(); ++i) {
well_formed_row(i);
}
for (unsigned i = 0; i < m_columns.size(); ++i) {
well_formed_column(i);
}
return true;
}
/**
\brief well_formed row check
*/
template<typename Ext>
bool sparse_matrix<Ext>::well_formed_row(unsigned row_id) const {
uint_set vars, dead;
_row const& r = m_rows[row_id];
for (unsigned i = 0; i < r.num_entries(); ++i) {
_row_entry const& e = r.m_entries[i];
if (e.is_dead()) {
dead.insert(i);
continue;
}
SASSERT(!vars.contains(e.m_var));
SASSERT(!m.is_zero(e.m_coeff));
SASSERT(e.m_var != dead_id);
col_entry const& c = m_columns[e.m_var].m_entries[e.m_col_idx];
SASSERT(c.m_row_id == row_id);
SASSERT(c.m_row_idx == i);
vars.insert(e.m_var);
}
int idx = r.m_first_free_idx;
while (idx != -1) {
SASSERT(dead.contains(idx));
dead.remove(idx);
idx = r.m_entries[idx].m_next_free_row_entry_idx;
}
SASSERT(dead.empty());
return true;
}
/**
\brief well_formed column check
*/
template<typename Ext>
bool sparse_matrix<Ext>::well_formed_column(var_t v) const {
uint_set rows, dead;
column const& col = m_columns[v];
for (unsigned i = 0; i < col.num_entries(); ++i) {
col_entry const& c = col.m_entries[i];
if (c.is_dead()) {
dead.insert(i);
continue;
}
SASSERT(!rows.contains(c.m_row_id));
_row const& row = m_rows[c.m_row_id];
_row_entry const& r = row.m_entries[c.m_row_idx];
SASSERT(r.m_var == v);
SASSERT(r.m_col_idx == i);
rows.insert(c.m_row_id);
}
int idx = col.m_first_free_idx;
while (idx != -1) {
SASSERT(dead.contains(idx));
dead.remove(idx);
idx = col.m_entries[idx].m_next_free_col_entry_idx;
}
SASSERT(dead.empty());
return true;
}
/**
\brief display method
*/
template<typename Ext>
void sparse_matrix<Ext>::display(std::ostream& out) const {
for (unsigned i = 0; i < m_rows.size(); ++i) {
if (m_rows[i].size() == 0) continue;
row_iterator it = row_begin(row(i)), end = row_end(row(i));
for (; it != end; ++it) {
m.display(out, it->m_coeff)
out << "*v" << it->m_var << " ";
}
out << "\n";
}
}
};
#endif

24
src/test/simplex.cpp Normal file
View file

@ -0,0 +1,24 @@
#include "sparse_matrix.h"
#include "sparse_matrix_def.h"
#include "simplex.h"
#include "simplex_def.h"
typedef simplex::simplex<simplex::mpz_ext> Simplex;
void tst_simplex() {
simplex::sparse_matrix<simplex::mpz_ext> M;
Simplex S;
S.make_feasible();
unsynch_mpz_manager m;
scoped_mpz_vector coeffs(m);
svector<unsigned> vars;
for (unsigned i = 0; i < 5; ++i) {
S.ensure_var(i);
vars.push_back(i);
coeffs.push_back(mpz(i+1));
}
Simplex::row r = S.add_row(1, coeffs.size(), vars.c_ptr(), coeffs.c_ptr());
}

View file

@ -19,7 +19,7 @@ Revision History:
#include"mpq_inf.h"
template<bool SYNCH>
std::string mpq_inf_manager<SYNCH>::to_string(mpq_inf const & a) const {
std::string mpq_inf_manager<SYNCH>::to_string(mpq_inf const & a) {
if (m.is_zero(a.second))
return m.to_string(a.first);

View file

@ -26,12 +26,12 @@ typedef std::pair<mpq, mpq> mpq_inf;
template<bool SYNCH = true>
class mpq_inf_manager {
mpq_manager<SYNCH> & m;
mpq_manager<SYNCH> m;
double m_inf;
public:
typedef mpq_inf numeral;
mpq_inf_manager(mpq_manager<SYNCH> & _m, double inf = 0.0001):m(_m) {
mpq_inf_manager(double inf = 0.0001) {
set_inf(inf);
}
@ -83,6 +83,14 @@ public:
}
bool is_int(mpq_inf const & a) const { return m.is_int(a.first) && m.is_zero(a.second); }
bool is_pos(mpq_inf const & a) const {
return m.is_pos(a.first) || (m.is_zero(a.first) && m.is_pos(a.second));
}
bool is_neg(mpq_inf const & a) const {
return m.is_neg(a.first) || (m.is_zero(a.first) && m.is_neg(a.second));
}
bool is_rational(mpq_inf const & a) const { return m.is_zero(a.second); }
@ -104,15 +112,15 @@ public:
return m.is_zero(a.first) && m.is_zero(a.second);
}
bool eq(mpq_inf const & a, mpq_inf const & b) const {
bool eq(mpq_inf const & a, mpq_inf const & b) {
return m.eq(a.first, b.first) && m.eq(a.second, b.second);
}
bool eq(mpq_inf const & a, mpq const & b) const {
bool eq(mpq_inf const & a, mpq const & b) {
return m.eq(a.first, b) && m.is_zero(a.second);
}
bool eq(mpq_inf const & a, mpq const & b, inf_kind k) const {
bool eq(mpq_inf const & a, mpq const & b, inf_kind k) {
if (!m.eq(a.first, b))
return false;
switch (k) {
@ -124,15 +132,15 @@ public:
return false;
}
bool lt(mpq_inf const & a, mpq_inf const & b) const {
bool lt(mpq_inf const & a, mpq_inf const & b) {
return m.lt(a.first, b.first) || (m.lt(a.second, b.second) && m.eq(a.first, b.first));
}
bool lt(mpq_inf const & a, mpq const & b) const {
bool lt(mpq_inf const & a, mpq const & b) {
return m.lt(a.first, b) || (m.is_neg(a.second) && m.eq(a.first, b));
}
bool lt(mpq_inf const & a, mpq const & b, inf_kind k) const {
bool lt(mpq_inf const & a, mpq const & b, inf_kind k) {
if (m.lt(a.first, b))
return true;
if (m.eq(a.first, b)) {
@ -146,13 +154,13 @@ public:
return false;
}
bool gt(mpq_inf const & a, mpq_inf const & b) const { return lt(b, a); }
bool gt(mpq_inf const & a, mpq_inf const & b) { return lt(b, a); }
bool gt(mpq_inf const & a, mpq const & b) const {
bool gt(mpq_inf const & a, mpq const & b) {
return m.gt(a.first, b) || (m.is_pos(a.second) && m.eq(a.first, b));
}
bool gt(mpq_inf const & a, mpq const & b, inf_kind k) const {
bool gt(mpq_inf const & a, mpq const & b, inf_kind k) {
if (m.gt(a.first, b))
return true;
if (m.eq(a.first, b)) {
@ -166,17 +174,17 @@ public:
return false;
}
bool le(mpq_inf const & a, mpq_inf const & b) const { return !gt(a, b); }
bool le(mpq_inf const & a, mpq_inf const & b) { return !gt(a, b); }
bool le(mpq_inf const & a, mpq const & b) const { return !gt(a, b); }
bool le(mpq_inf const & a, mpq const & b) { return !gt(a, b); }
bool le(mpq_inf const & a, mpq const & b, inf_kind k) const { return !gt(a, b, k); }
bool le(mpq_inf const & a, mpq const & b, inf_kind k) { return !gt(a, b, k); }
bool ge(mpq_inf const & a, mpq_inf const & b) const { return !lt(a, b); }
bool ge(mpq_inf const & a, mpq_inf const & b) { return !lt(a, b); }
bool ge(mpq_inf const & a, mpq const & b) const { return !lt(a, b); }
bool ge(mpq_inf const & a, mpq const & b) { return !lt(a, b); }
bool ge(mpq_inf const & a, mpq const & b, inf_kind k) const { return !lt(a, b, k); }
bool ge(mpq_inf const & a, mpq const & b, inf_kind k) { return !lt(a, b, k); }
void add(mpq_inf const & a, mpq_inf const & b, mpq_inf & c) {
m.add(a.first, b.first, c.first);
@ -208,6 +216,16 @@ public:
m.mul(b, a.second, c.second);
}
void div(mpq_inf const & a, mpq const & b, mpq_inf & c) {
m.div(a.first, b, c.first);
m.div(a.second, b, c.second);
}
void div(mpq_inf const & a, mpz const & b, mpq_inf & c) {
m.div(a.first, b, c.first);
m.div(a.second, b, c.second);
}
void inc(mpq_inf & a) {
m.inc(a.first);
}
@ -246,7 +264,7 @@ public:
}
}
std::string to_string(mpq_inf const & a) const;
std::string to_string(mpq_inf const & a);
};
typedef mpq_inf_manager<true> synch_mpq_inf_manager;