#include <initializer_list>
#include <iostream>
#include <utility>
#include "util/debug.h"
#include "util/params.h"
#include "math/lp/int_cube.h"
#include "math/lp/int_solver.h"
#include "math/lp/lar_solver.h"
#include "math/lp/numeric_pair.h"
namespace lp {
namespace lcube_test {
struct ineq {
vector<std::pair<mpq, unsigned>> m_coeffs;
lconstraint_kind m_kind;
mpq m_rs;
};
static void setup(lar_solver& solver, const vector<ineq>& ineqs, svector<unsigned>* term_columns = nullptr) {
unsigned term_ext = 1000;
for (const auto& in : ineqs) {
unsigned t = solver.add_term(in.m_coeffs, term_ext++);
solver.add_var_bound(t, in.m_kind, in.m_rs);
if (term_columns)
term_columns->push_back(t);
}
auto st = solver.solve();
VERIFY(st == lp_status::OPTIMAL || st == lp_status::FEASIBLE);
}
static void verify_model(const lar_solver& solver, const vector<ineq>& ineqs) {
for (const auto& in : ineqs) {
impq v;
for (const auto& p : in.m_coeffs)
v += solver.get_column_value(p.second) * p.first;
switch (in.m_kind) {
case lconstraint_kind::LE: VERIFY(v <= impq(in.m_rs)); break;
case lconstraint_kind::GE: VERIFY(v >= impq(in.m_rs)); break;
default: VERIFY(false);
}
}
}
static void verify_int_values(const lar_solver& solver, std::initializer_list<unsigned> vars) {
for (unsigned j : vars)
VERIFY(solver.get_column_value(j).is_int());
}
static void test_paper_example_undef() {
std::cout << "lcube: paper example, expecting undef\n";
lar_solver solver;
unsigned x1 = solver.add_named_var(0, true, "x1");
unsigned x2 = solver.add_named_var(1, true, "x2");
vector<ineq> ineqs;
ineqs.push_back(ineq{{{mpq(3), x1}, {mpq(-1), x2}}, lconstraint_kind::LE, mpq(0)});
ineqs.push_back(ineq{{{mpq(-2), x1}, {mpq(-1), x2}}, lconstraint_kind::LE, mpq(-2)});
ineqs.push_back(ineq{{{mpq(-2), x1}, {mpq(1), x2}}, lconstraint_kind::LE, mpq(1)});
setup(solver, ineqs);
int_solver i_s(solver);
solver.set_int_solver(&i_s);
lia_move m = int_cube(i_s).find_largest_cube();
std::cout << "lcube returned " << lia_move_to_string(m) << "\n";
VERIFY(m == lia_move::undef);
VERIFY(solver.ax_is_correct());
}
static void test_beats_unit_cube() {
std::cout << "lcube: beating the unit cube test\n";
lar_solver solver;
unsigned x1 = solver.add_named_var(0, true, "x1");
unsigned x2 = solver.add_named_var(1, true, "x2");
vector<ineq> ineqs;
ineqs.push_back(ineq{{{mpq(3), x1}, {mpq(-1), x2}}, lconstraint_kind::LE, mpq(0)});
ineqs.push_back(ineq{{{mpq(-2), x1}, {mpq(-1), x2}}, lconstraint_kind::LE, mpq(-1)});
ineqs.push_back(ineq{{{mpq(-2), x1}, {mpq(1), x2}}, lconstraint_kind::LE, mpq(1)});
svector<unsigned> tcols;
setup(solver, ineqs, &tcols);
solver.set_column_value_test(x1, impq(mpq(1, 4)));
solver.set_column_value_test(x2, impq(mpq(5, 4)));
solver.set_column_value_test(tcols[0], impq(mpq(-1, 2)));
solver.set_column_value_test(tcols[1], impq(mpq(-7, 4)));
solver.set_column_value_test(tcols[2], impq(mpq(3, 4)));
int_solver i_s(solver);
solver.set_int_solver(&i_s);
lia_move m = int_cube(i_s)();
std::cout << "unit cube returned " << lia_move_to_string(m) << "\n";
VERIFY(m == lia_move::undef);
m = int_cube(i_s).find_largest_cube();
std::cout << "lcube returned " << lia_move_to_string(m) << "\n";
VERIFY(m == lia_move::sat);
verify_int_values(solver, {x1, x2});
verify_model(solver, ineqs);
}
static void test_flip_repair() {
std::cout << "lcube: rounding repair\n";
lar_solver solver;
unsigned x = solver.add_named_var(0, true, "x");
unsigned y = solver.add_named_var(1, true, "y");
unsigned r = solver.add_named_var(2, false, "r");
solver.add_var_bound(r, lconstraint_kind::GE, mpq(0));
solver.add_var_bound(r, lconstraint_kind::LE, mpq(1, 10));
vector<ineq> ineqs;
ineqs.push_back(ineq{{{mpq(1), x}, {mpq(1), y}, {mpq(1), r}}, lconstraint_kind::GE, mpq(9, 10)});
ineqs.push_back(ineq{{{mpq(1), x}, {mpq(1), y}, {mpq(1), r}}, lconstraint_kind::LE, mpq(11, 10)});
ineqs.push_back(ineq{{{mpq(1), x}, {mpq(-1), y}, {mpq(1), r}}, lconstraint_kind::GE, mpq(-11, 10)});
ineqs.push_back(ineq{{{mpq(1), x}, {mpq(-1), y}, {mpq(1), r}}, lconstraint_kind::LE, mpq(1, 10)});
setup(solver, ineqs);
int_solver i_s(solver);
solver.set_int_solver(&i_s);
lia_move m = int_cube(i_s).find_largest_cube();
std::cout << "lcube returned " << lia_move_to_string(m)
<< ", flip successes: " << solver.settings().stats().m_lcube_flip_success << "\n";
VERIFY(m == lia_move::sat);
verify_int_values(solver, {x, y});
verify_model(solver, ineqs);
}
static void test_infinite_lattice_width() {
std::cout << "lcube: infinite lattice width\n";
lar_solver solver;
unsigned x = solver.add_named_var(0, true, "x");
unsigned y = solver.add_named_var(1, true, "y");
vector<ineq> ineqs;
ineqs.push_back(ineq{{{mpq(3), x}, {mpq(5), y}}, lconstraint_kind::GE, mpq(7)});
setup(solver, ineqs);
int_solver i_s(solver);
solver.set_int_solver(&i_s);
lia_move m = int_cube(i_s).find_largest_cube();
std::cout << "lcube returned " << lia_move_to_string(m) << "\n";
VERIFY(m == lia_move::sat);
verify_int_values(solver, {x, y});
verify_model(solver, ineqs);
}
static void test_edge_at_least_one() {
std::cout << "lcube: edge length at least 1\n";
lar_solver solver;
unsigned x = solver.add_named_var(0, true, "x");
unsigned y = solver.add_named_var(1, true, "y");
unsigned r = solver.add_named_var(2, false, "r");
solver.add_var_bound(r, lconstraint_kind::GE, mpq(0));
solver.add_var_bound(r, lconstraint_kind::LE, mpq(1, 10));
vector<ineq> ineqs;
ineqs.push_back(ineq{{{mpq(1), x}, {mpq(2), y}, {mpq(1), r}}, lconstraint_kind::GE, mpq(0)});
ineqs.push_back(ineq{{{mpq(1), x}, {mpq(2), y}, {mpq(1), r}}, lconstraint_kind::LE, mpq(8)});
ineqs.push_back(ineq{{{mpq(1), x}, {mpq(-2), y}, {mpq(1), r}}, lconstraint_kind::GE, mpq(0)});
ineqs.push_back(ineq{{{mpq(1), x}, {mpq(-2), y}, {mpq(1), r}}, lconstraint_kind::LE, mpq(8)});
setup(solver, ineqs);
int_solver i_s(solver);
solver.set_int_solver(&i_s);
lia_move m = int_cube(i_s).find_largest_cube();
std::cout << "lcube returned " << lia_move_to_string(m) << "\n";
VERIFY(m == lia_move::sat);
verify_int_values(solver, {x, y});
verify_model(solver, ineqs);
}
static void test_dispatch() {
std::cout << "lcube: dispatch through int_solver::check\n";
lar_solver solver;
params_ref p;
p.set_bool("lcube", true);
solver.settings().updt_params(p);
VERIFY(solver.settings().lcube());
solver.settings().m_int_find_cube_period = 1;
unsigned x = solver.add_named_var(0, true, "x");
unsigned y = solver.add_named_var(1, true, "y");
unsigned r = solver.add_named_var(2, false, "r");
solver.add_var_bound(r, lconstraint_kind::GE, mpq(0));
solver.add_var_bound(r, lconstraint_kind::LE, mpq(1, 10));
vector<ineq> ineqs;
ineqs.push_back(ineq{{{mpq(1), x}, {mpq(1), y}, {mpq(1), r}}, lconstraint_kind::GE, mpq(9, 10)});
ineqs.push_back(ineq{{{mpq(1), x}, {mpq(1), y}, {mpq(1), r}}, lconstraint_kind::LE, mpq(11, 10)});
ineqs.push_back(ineq{{{mpq(1), x}, {mpq(-1), y}, {mpq(1), r}}, lconstraint_kind::GE, mpq(-11, 10)});
ineqs.push_back(ineq{{{mpq(1), x}, {mpq(-1), y}, {mpq(1), r}}, lconstraint_kind::LE, mpq(1, 10)});
svector<unsigned> tcols;
setup(solver, ineqs, &tcols);
solver.set_column_value_test(x, impq(mpq(1, 2)));
solver.set_column_value_test(y, impq(mpq(1, 2)));
solver.set_column_value_test(r, impq(mpq(1, 10)));
solver.set_column_value_test(tcols[0], impq(mpq(11, 10)));
solver.set_column_value_test(tcols[1], impq(mpq(11, 10)));
solver.set_column_value_test(tcols[2], impq(mpq(1, 10)));
solver.set_column_value_test(tcols[3], impq(mpq(1, 10)));
int_solver i_s(solver);
solver.set_int_solver(&i_s);
explanation ex;
lia_move m = i_s.check(&ex);
std::cout << "check returned " << lia_move_to_string(m)
<< ", lcube calls: " << solver.settings().stats().m_lcube_calls << "\n";
VERIFY(m == lia_move::sat);
VERIFY(solver.settings().stats().m_lcube_calls >= 1);
verify_int_values(solver, {x, y});
verify_model(solver, ineqs);
}
static void run() {
test_paper_example_undef();
test_beats_unit_cube();
test_flip_repair();
test_infinite_lattice_width();
test_edge_at_least_one();
test_dispatch();
std::cout << "lcube tests passed\n";
}
} }
void tst_lcube() {
lp::lcube_test::run();
}