Skip to content

Commit d59dad6

Browse files
Add add_constraint method and update constraint handling in var class
- Implemented add_constraint method to incorporate previously created constraints into the solver. - Updated set_lb, unset_lb, set_ub, and unset_ub methods to use const references for constraint parameters. - Enhanced unit tests to verify the functionality of adding, retracting, and re-adding constraints.
1 parent 2888ce2 commit d59dad6

5 files changed

Lines changed: 81 additions & 24 deletions

File tree

include/linspire.hpp

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,16 @@ namespace linspire
154154
*/
155155
[[nodiscard]] bool new_gt(const utils::lin &lhs, const utils::lin &rhs, bool strict = false, std::optional<std::reference_wrapper<constraint>> reason = std::nullopt) noexcept;
156156

157+
/**
158+
* @brief Adds a previously created constraint to the solver.
159+
*
160+
* This function incorporates a previously defined constraint into the solver. It updates
161+
* the internal state of the solver to account for the new constraint.
162+
*
163+
* @param c The constraint to be added.
164+
*/
165+
void add_constraint(const constraint &c) noexcept;
166+
157167
/**
158168
* @brief Retracts a previously added constraint from the solver.
159169
*
@@ -162,7 +172,7 @@ namespace linspire
162172
*
163173
* @param c The constraint to be retracted.
164174
*/
165-
void retract(constraint &c) noexcept;
175+
void retract(const constraint &c) noexcept;
166176

167177
/**
168178
* @brief Checks the consistency of the current set of constraints.
@@ -185,7 +195,7 @@ namespace linspire
185195
*
186196
* @return A constant reference to the vector of constraints representing the last conflict explanation.
187197
*/
188-
[[nodiscard]] const std::vector<std::reference_wrapper<constraint>> &get_conflict() const noexcept { return cnfl; }
198+
[[nodiscard]] const std::vector<std::reference_wrapper<const constraint>> &get_conflict() const noexcept { return cnfl; }
189199

190200
/**
191201
* @brief Checks if two linear expressions can be made equal.
@@ -237,11 +247,11 @@ namespace linspire
237247

238248
void new_row(const utils::var x, utils::lin &&l) noexcept;
239249

240-
std::vector<var> vars; // index is the variable id
241-
std::unordered_map<std::string, utils::var> exprs; // the expressions (string to numeric variable) for which already exist slack variables..
242-
std::map<utils::var, utils::lin> tableau; // basic variable -> expression
243-
std::vector<std::set<utils::var>> t_watches; // for each variable `v`, a set of tableau rows watching `v`..
244-
std::vector<std::reference_wrapper<constraint>> cnfl; // the last conflict explanation..
250+
std::vector<var> vars; // index is the variable id
251+
std::unordered_map<std::string, utils::var> exprs; // the expressions (string to numeric variable) for which already exist slack variables..
252+
std::map<utils::var, utils::lin> tableau; // basic variable -> expression
253+
std::vector<std::set<utils::var>> t_watches; // for each variable `v`, a set of tableau rows watching `v`..
254+
std::vector<std::reference_wrapper<const constraint>> cnfl; // the last conflict explanation..
245255
#ifdef LINSPIRE_ENABLE_LISTENERS
246256
std::unordered_map<utils::var, std::set<listener *>> listening; // for each variable, the listeners listening to it..
247257
std::set<listener *> listeners; // the collection of listeners..

include/var.hpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ namespace linspire
2626
friend json::json to_json(const var &x) noexcept;
2727

2828
private:
29-
void set_lb(const utils::inf_rational &v, std::optional<std::reference_wrapper<constraint>> reason = std::nullopt) noexcept;
30-
void unset_lb(const utils::inf_rational &v, constraint &reason) noexcept;
31-
void set_ub(const utils::inf_rational &v, std::optional<std::reference_wrapper<constraint>> reason = std::nullopt) noexcept;
32-
void unset_ub(const utils::inf_rational &v, constraint &reason) noexcept;
29+
void set_lb(const utils::inf_rational &v, std::optional<std::reference_wrapper<const constraint>> reason = std::nullopt) noexcept;
30+
void unset_lb(const utils::inf_rational &v, const constraint &reason) noexcept;
31+
void set_ub(const utils::inf_rational &v, std::optional<std::reference_wrapper<const constraint>> reason = std::nullopt) noexcept;
32+
void unset_ub(const utils::inf_rational &v, const constraint &reason) noexcept;
3333

3434
private:
35-
std::map<utils::inf_rational, std::set<constraint *>> lbs, ubs; // the lower and upper bounds with their reasons..
36-
utils::inf_rational val; // the current value of this variable..
35+
std::map<utils::inf_rational, std::set<const constraint *>> lbs, ubs; // the lower and upper bounds with their reasons..
36+
utils::inf_rational val; // the current value of this variable..
3737
};
3838

3939
[[nodiscard]] std::string to_string(const var &x) noexcept;

src/linspire.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,20 @@ namespace linspire
119119
}
120120
bool solver::new_gt(const utils::lin &lhs, const utils::lin &rhs, bool strict, std::optional<std::reference_wrapper<constraint>> reason) noexcept { return new_lt(rhs, lhs, strict, reason); }
121121

122-
void solver::retract(constraint &c) noexcept
122+
void solver::add_constraint(const constraint &c) noexcept
123+
{
124+
for (const auto &[x, lb] : c.lbs)
125+
vars[x].set_lb(lb, c);
126+
for (const auto &[x, ub] : c.ubs)
127+
vars[x].set_ub(ub, c);
128+
}
129+
130+
void solver::retract(const constraint &c) noexcept
123131
{
124132
for (const auto &[x, lb] : c.lbs)
125133
vars[x].unset_lb(lb, c);
126-
c.lbs.clear();
127134
for (const auto &[x, ub] : c.ubs)
128135
vars[x].unset_ub(ub, c);
129-
c.ubs.clear();
130136
}
131137

132138
bool solver::check() noexcept

src/var.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,24 @@ namespace linspire
99
utils::inf_rational var::get_lb() const noexcept { return lbs.empty() ? utils::rational::negative_infinite : lbs.rbegin()->first; }
1010
utils::inf_rational var::get_ub() const noexcept { return ubs.empty() ? utils::rational::positive_infinite : ubs.begin()->first; }
1111

12-
void var::set_lb(const utils::inf_rational &v, std::optional<std::reference_wrapper<constraint>> reason) noexcept
12+
void var::set_lb(const utils::inf_rational &v, std::optional<std::reference_wrapper<const constraint>> reason) noexcept
1313
{
1414
assert(v <= get_ub()); // we cannot set a lower bound greater than the current upper bound..
1515
if (reason)
1616
{ // we add a new lower bound `v` with the given reason..
1717
if (auto it = lbs.find(v); it != lbs.end())
1818
it->second.insert(&reason->get()); // we add the reason to the existing lower bound `v`..
1919
else
20-
lbs.emplace(v, std::set<constraint *>{&reason->get()}); // we create a new lower bound `v`..
20+
lbs.emplace(v, std::set<const constraint *>{&reason->get()}); // we create a new lower bound `v`..
2121
}
2222
else
2323
{ // we remove all the lower bounds that are less than `v`..
2424
auto it = lbs.upper_bound(v);
2525
lbs.erase(lbs.begin(), it);
26-
lbs.emplace(v, std::set<constraint *>());
26+
lbs.emplace(v, std::set<const constraint *>());
2727
}
2828
}
29-
void var::unset_lb(const utils::inf_rational &v, constraint &reason) noexcept
29+
void var::unset_lb(const utils::inf_rational &v, const constraint &reason) noexcept
3030
{
3131
assert(lbs.find(v) != lbs.end());
3232
auto it = lbs.find(v);
@@ -35,24 +35,24 @@ namespace linspire
3535
lbs.erase(it);
3636
}
3737

38-
void var::set_ub(const utils::inf_rational &v, std::optional<std::reference_wrapper<constraint>> reason) noexcept
38+
void var::set_ub(const utils::inf_rational &v, std::optional<std::reference_wrapper<const constraint>> reason) noexcept
3939
{
4040
assert(v >= get_lb()); // we cannot set an upper bound less than the current lower bound..
4141
if (reason)
4242
{ // we add a new upper bound `v` with the given reason..
4343
if (auto it = ubs.find(v); it != ubs.end())
4444
it->second.insert(&reason->get()); // we add the reason to the existing upper bound `v`..
4545
else
46-
ubs.emplace(v, std::set<constraint *>{&reason->get()}); // we create a new upper bound `v`..
46+
ubs.emplace(v, std::set<const constraint *>{&reason->get()}); // we create a new upper bound `v`..
4747
}
4848
else
4949
{ // we remove all the upper bounds that are greater than `v`..
5050
auto it = ubs.lower_bound(v);
5151
ubs.erase(it, ubs.end());
52-
ubs.emplace(v, std::set<constraint *>());
52+
ubs.emplace(v, std::set<const constraint *>());
5353
}
5454
}
55-
void var::unset_ub(const utils::inf_rational &v, constraint &reason) noexcept
55+
void var::unset_ub(const utils::inf_rational &v, const constraint &reason) noexcept
5656
{
5757
assert(ubs.find(v) != ubs.end());
5858
auto it = ubs.find(v);

tests/test_linspire.cpp

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,13 +183,54 @@ void test4()
183183
assert(cons);
184184
}
185185

186+
/**
187+
* @brief Unit test for adding, retracting, and re-adding a constraint.
188+
*
189+
* This test verifies:
190+
* - Adding a constraint and checking consistency.
191+
* - Retracting the constraint and ensuring bounds are reset.
192+
* - Adding the same constraint again and checking consistency.
193+
*/
194+
void test5()
195+
{
196+
linspire::solver s;
197+
auto x = s.new_var();
198+
199+
linspire::constraint c0;
200+
201+
// Add constraint: x >= 5
202+
bool res0 = s.new_gt({{x, 1}}, 5, false, c0);
203+
assert(res0);
204+
bool cons = s.check();
205+
assert(cons);
206+
assert(s.lb(x) == 5);
207+
assert(s.ub(x) == utils::rational::positive_infinite);
208+
assert(s.val(x) >= 5);
209+
210+
// Retract constraint
211+
s.retract(c0);
212+
cons = s.check();
213+
assert(cons);
214+
assert(s.lb(x) == utils::rational::negative_infinite);
215+
assert(s.ub(x) == utils::rational::positive_infinite);
216+
217+
// Add the same constraint again
218+
s.add_constraint(c0);
219+
cons = s.check();
220+
assert(cons);
221+
assert(s.lb(x) == 5);
222+
assert(s.ub(x) == utils::rational::positive_infinite);
223+
assert(s.val(x) >= 5);
224+
}
225+
186226
int main()
187227
{
188228
test0();
189229
test1();
190230
test2();
191231
test3();
192232
test4();
233+
test5();
193234

194235
return 0;
195236
}

0 commit comments

Comments
 (0)