Skip to content

Commit 1a3d1ad

Browse files
add base line bounds tightening utility
1 parent 7044bb8 commit 1a3d1ad

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

src/math/lp/int_solver.cpp

+81-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ namespace lp {
3030
};
3131

3232
class int_solver::imp {
33-
public:
33+
public:
3434
int_solver& lia;
3535
lar_solver& lra;
3636
lar_core_solver& lrac;
@@ -196,6 +196,8 @@ namespace lp {
196196
return lia.settings().dio_eqs() && m_number_of_calls % m_dioph_eq_period == 0;
197197
}
198198

199+
// HNF
200+
199201
bool should_hnf_cut() {
200202
return (!settings().dio_eqs() || settings().dio_enable_hnf_cuts())
201203
&& settings().enable_hnf() && m_number_of_calls % settings().hnf_cut_period() == 0;
@@ -210,6 +212,83 @@ namespace lp {
210212
return r;
211213
}
212214

215+
// Tighten bounds
216+
// expose at level of lar_solver so it can be invoked by theory_lra after back-jumping
217+
// consider multi-round bound tightnening as well and deal with divergence issues.
218+
lia_move tighten_bounds() {
219+
220+
struct bound_consumer {
221+
imp& i;
222+
bound_consumer(imp& i) : i(i) {}
223+
lar_solver& lp() { return i.lra; }
224+
bool bound_is_interesting(unsigned vi, lp::lconstraint_kind kind, const rational& bval) const { return true; }
225+
bool add_eq(lpvar u, lpvar v, lp::explanation const& e, bool is_fixed) { return false; }
226+
};
227+
bound_consumer bc(*this);
228+
std_vector<implied_bound> ibounds;
229+
lp_bound_propagator<bound_consumer> bp(bc, ibounds);
230+
bp.init();
231+
232+
unsigned num_refined_bounds = 0;
233+
234+
auto set_conflict = [&](unsigned j, u_dependency * d1, u_dependency * d2) {
235+
++settings().stats().m_bounds_tightening_conflicts;
236+
for (auto e : lra.flatten(d1))
237+
m_ex->push_back(e);
238+
for (auto e : lra.flatten(d2))
239+
m_ex->push_back(e);
240+
};
241+
242+
auto refine_bound = [&](implied_bound const& ib) {
243+
unsigned j = ib.m_j;
244+
auto const& bound = ib.m_bound;
245+
if (!lra.column_is_int(j)) // for now, ignore non-integers.
246+
return l_undef;
247+
if (ib.m_is_lower_bound) {
248+
if (lra.column_has_lower_bound(j) && lra.column_lower_bound(j) >= bound)
249+
return l_undef;
250+
auto d = ib.explain_implied();
251+
if (lra.column_has_upper_bound(j) && lra.column_upper_bound(j) < ceil(bound)) {
252+
set_conflict(j, d, lra.get_column_upper_bound_witness(j));
253+
return l_false;
254+
}
255+
256+
lra.update_column_type_and_bound(j, lconstraint_kind::GE, ceil(bound), d);
257+
++num_refined_bounds;
258+
} else {
259+
if (lra.column_has_upper_bound(j) && lra.column_upper_bound(j) <= bound)
260+
return l_undef;
261+
auto d = ib.explain_implied();
262+
if (lra.column_has_lower_bound(j) && lra.column_lower_bound(j) > floor(bound)) {
263+
set_conflict(j, d, lra.get_column_lower_bound_witness(j));
264+
return l_false;
265+
}
266+
267+
lra.update_column_type_and_bound(j, lconstraint_kind::LE, floor(bound), d);
268+
++num_refined_bounds;
269+
}
270+
return l_true;
271+
};
272+
273+
for (unsigned row_index = 0; row_index < lra.row_count(); ++row_index) {
274+
auto nb = bound_analyzer_on_row<row_strip<mpq>, lp_bound_propagator<bound_consumer>>::analyze_row(
275+
lra.A_r().m_rows[row_index],
276+
null_ci,
277+
zero_of_type<numeric_pair<mpq>>(),
278+
row_index,
279+
bp);
280+
281+
settings().stats().m_bounds_tightenings += static_cast<unsigned>(ibounds.size());
282+
for (auto const& ib : ibounds)
283+
if (l_false == refine_bound(ib))
284+
return lia_move::conflict;
285+
ibounds.clear();
286+
}
287+
288+
verbose_stream() << num_refined_bounds << "\n";
289+
return num_refined_bounds > 0 ? lia_move::continue_with_check : lia_move::undef;
290+
}
291+
213292

214293
lia_move check(lp::explanation * e) {
215294
SASSERT(lra.ax_is_correct());
@@ -236,6 +315,7 @@ namespace lp {
236315
if (r == lia_move::undef) r = patch_basic_columns();
237316
if (r == lia_move::undef && should_find_cube()) r = int_cube(lia)();
238317
if (r == lia_move::undef) lra.move_non_basic_columns_to_bounds();
318+
// if (r == lia_move::undef) r = tighten_bounds();
239319
if (r == lia_move::undef && should_hnf_cut()) r = hnf_cut();
240320
if (r == lia_move::undef && should_gomory_cut()) r = gomory(lia).get_gomory_cuts(2);
241321
if (r == lia_move::undef && should_solve_dioph_eq()) r = solve_dioph_eq();

src/math/lp/lp_settings.h

+4
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,8 @@ struct statistics {
138138
unsigned m_dio_rewrite_conflicts = 0;
139139
unsigned m_dio_branching_sats = 0;
140140
unsigned m_dio_branching_conflicts = 0;
141+
unsigned m_bounds_tightening_conflicts = 0;
142+
unsigned m_bounds_tightenings = 0;
141143
::statistics m_st = {};
142144

143145
void reset() {
@@ -181,6 +183,8 @@ struct statistics {
181183
st.update("arith-dio-branching-sats", m_dio_branching_sats);
182184
st.update("arith-dio-branching-depth", m_dio_branching_depth);
183185
st.update("arith-dio-branching-conflicts", m_dio_branching_conflicts);
186+
st.update("arith-bounds-tightening-conflicts", m_bounds_tightening_conflicts);
187+
st.update("arith-bounds-tightenings", m_bounds_tightenings);
184188
st.copy(m_st);
185189
}
186190
};

0 commit comments

Comments
 (0)