Skip to content

Commit d96ed97

Browse files
Refactor constraint handling: replace raw pointers with std::optional<std::reference_wrapper<constraint>> for improved safety and consistency in method signatures.
1 parent 680dad3 commit d96ed97

5 files changed

Lines changed: 58 additions & 51 deletions

File tree

include/linspire.hpp

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ namespace linspire
126126
* @param reason An optional constraint that serves as the reason for adding this new constraint.
127127
* @return true if the constraint was successfully added; false if it leads to inconsistency.
128128
*/
129-
[[nodiscard]] bool new_lt(const utils::lin &lhs, const utils::lin &rhs, bool strict = false, constraint *reason = nullptr) noexcept;
129+
[[nodiscard]] bool new_lt(const utils::lin &lhs, const utils::lin &rhs, bool strict = false, std::optional<std::reference_wrapper<constraint>> reason = std::nullopt) noexcept;
130130
/**
131131
* @brief Adds a new equality constraint to the solver.
132132
*
@@ -138,7 +138,7 @@ namespace linspire
138138
* @param reason An optional constraint that serves as the reason for adding this new equality constraint.
139139
* @return true if the equality constraint was successfully added; false if it leads to inconsistency.
140140
*/
141-
[[nodiscard]] bool new_eq(const utils::lin &lhs, const utils::lin &rhs, constraint *reason = nullptr) noexcept;
141+
[[nodiscard]] bool new_eq(const utils::lin &lhs, const utils::lin &rhs, std::optional<std::reference_wrapper<constraint>> reason = std::nullopt) noexcept;
142142
/**
143143
* @brief Adds a new greater-than or greater-than-or-equal-to constraint to the solver.
144144
*
@@ -152,7 +152,7 @@ namespace linspire
152152
* @param reason An optional constraint that serves as the reason for adding this new constraint.
153153
* @return true if the constraint was successfully added; false if it leads to inconsistency.
154154
*/
155-
[[nodiscard]] bool new_gt(const utils::lin &lhs, const utils::lin &rhs, bool strict = false, constraint *reason = nullptr) noexcept { return new_lt(rhs, lhs, strict, reason); }
155+
[[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

157157
/**
158158
* @brief Retracts a previously added constraint from the solver.
@@ -185,7 +185,7 @@ namespace linspire
185185
*
186186
* @return A constant reference to the vector of constraints representing the last conflict explanation.
187187
*/
188-
[[nodiscard]] const std::vector<constraint *> &get_conflict() const noexcept { return cnfl; }
188+
[[nodiscard]] const std::vector<std::reference_wrapper<constraint>> &get_conflict() const noexcept { return cnfl; }
189189

190190
/**
191191
* @brief Checks if two linear expressions can be made equal.
@@ -226,8 +226,8 @@ namespace linspire
226226
private:
227227
[[nodiscard]] bool is_basic(const utils::var v) const noexcept { return tableau.count(v); }
228228

229-
[[nodiscard]] bool set_lb(const utils::var x_i, const utils::inf_rational &v, constraint *reason = nullptr) noexcept;
230-
[[nodiscard]] bool set_ub(const utils::var x_i, const utils::inf_rational &v, constraint *reason = nullptr) noexcept;
229+
[[nodiscard]] bool set_lb(const utils::var x_i, const utils::inf_rational &v, std::optional<std::reference_wrapper<constraint>> reason = std::nullopt) noexcept;
230+
[[nodiscard]] bool set_ub(const utils::var x_i, const utils::inf_rational &v, std::optional<std::reference_wrapper<constraint>> reason = std::nullopt) noexcept;
231231

232232
void update(const utils::var x_i, const utils::inf_rational &v) noexcept;
233233

@@ -237,11 +237,11 @@ namespace linspire
237237

238238
void new_row(const utils::var x, utils::lin &&l) noexcept;
239239

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<constraint *> cnfl; // the last conflict explanation..
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..
245245
#ifdef LINSPIRE_ENABLE_LISTENERS
246246
std::unordered_map<utils::var, std::set<listener *>> listening; // for each variable, the listeners listening to it..
247247
std::set<listener *> listeners; // the collection of listeners..

include/var.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "json.hpp"
66
#include <set>
77
#include <optional>
8+
#include <functional>
89

910
namespace linspire
1011
{
@@ -25,9 +26,9 @@ namespace linspire
2526
friend json::json to_json(const var &x) noexcept;
2627

2728
private:
28-
void set_lb(const utils::inf_rational &v, constraint *reason = nullptr) noexcept;
29+
void set_lb(const utils::inf_rational &v, std::optional<std::reference_wrapper<constraint>> reason = std::nullopt) noexcept;
2930
void unset_lb(const utils::inf_rational &v, constraint &reason) noexcept;
30-
void set_ub(const utils::inf_rational &v, constraint *reason = nullptr) noexcept;
31+
void set_ub(const utils::inf_rational &v, std::optional<std::reference_wrapper<constraint>> reason = std::nullopt) noexcept;
3132
void unset_ub(const utils::inf_rational &v, constraint &reason) noexcept;
3233

3334
private:

src/linspire.cpp

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ namespace linspire
4141
utils::inf_rational solver::ub(const utils::var x) const noexcept { return vars[x].get_ub(); }
4242
utils::inf_rational solver::val(const utils::var x) const noexcept { return vars[x].val; }
4343

44-
bool solver::new_eq(const utils::lin &lhs, const utils::lin &rhs, constraint *reason) noexcept
44+
bool solver::new_eq(const utils::lin &lhs, const utils::lin &rhs, std::optional<std::reference_wrapper<constraint>> reason) noexcept
4545
{
4646
LOG_TRACE(utils::to_string(lhs) + " == " + utils::to_string(rhs));
4747
utils::lin expr = lhs - rhs;
@@ -68,18 +68,17 @@ namespace linspire
6868
assert(c != 0);
6969
const utils::inf_rational c_right = utils::inf_rational(-expr.known_term) / c; // the right-hand side of the constraint is the division of the negation of the known term by the coefficient..
7070
// we can set both the lower and upper bound of the variable to the right-hand side of the constraint..
71-
return set_lb(x, c_right, reason) && set_ub(x, c_right, reason);
71+
return reason ? set_lb(x, c_right, reason.value()) && set_ub(x, c_right, reason.value()) : set_lb(x, c_right) && set_ub(x, c_right);
7272
}
7373
default: // the expression is still a general linear expression..
7474
const utils::inf_rational c_right = utils::inf_rational(-expr.known_term);
7575
expr.known_term = utils::rational::zero;
7676
// we add the expression to the tableau, associating it with a new (slack) variable
7777
utils::var slack = new_var(std::move(expr));
78-
return set_lb(slack, c_right, reason) && set_ub(slack, c_right, reason);
78+
return reason ? set_lb(slack, c_right, reason.value()) && set_ub(slack, c_right, reason.value()) : set_lb(slack, c_right) && set_ub(slack, c_right);
7979
}
8080
}
81-
82-
bool solver::new_lt(const utils::lin &lhs, const utils::lin &rhs, bool strict, constraint *reason) noexcept
81+
bool solver::new_lt(const utils::lin &lhs, const utils::lin &rhs, bool strict, std::optional<std::reference_wrapper<constraint>> reason) noexcept
8382
{
8483
LOG_TRACE(utils::to_string(lhs) + (strict ? " < " : " <= ") + utils::to_string(rhs));
8584
utils::lin expr = lhs - rhs;
@@ -106,18 +105,25 @@ namespace linspire
106105
assert(c != 0);
107106
const utils::inf_rational c_right = utils::inf_rational(-expr.known_term, strict ? -1 : 0) / c; // the right-hand side of the constraint is the division of the negation of the known term minus an infinitesimal by the coefficient..
108107
if (is_positive(c))
109-
return set_ub(x, c_right, reason); // we are in the case `c * v < c_right`..
108+
return reason ? set_ub(x, c_right, reason.value()) : set_ub(x, c_right); // we are in the case `c * v < c_right`..
110109
else
111-
return set_lb(x, c_right, reason); // we are in the case `c * v > c_right`..
110+
return reason ? set_lb(x, c_right, reason.value()) : set_lb(x, c_right); // we are in the case `c * v > c_right`..
112111
}
113112
default: // the expression is still a general linear expression..
114113
const utils::inf_rational c_right = utils::inf_rational(-expr.known_term, strict ? -1 : 0);
115114
expr.known_term = utils::rational::zero;
116115
// we add the expression to the tableau, associating it with a new (slack) variable
117116
utils::var slack = new_var(std::move(expr));
118-
return set_ub(slack, c_right, reason); // we are in the case `expr < c_right`..
117+
return reason ? set_ub(slack, c_right, reason.value()) : set_ub(slack, c_right); // we are in the case `expr < c_right`..
119118
}
120119
}
120+
bool solver::new_gt(const utils::lin &lhs, const utils::lin &rhs, bool strict, std::optional<std::reference_wrapper<constraint>> reason) noexcept
121+
{
122+
if (reason)
123+
return new_lt(rhs, lhs, strict, *reason);
124+
else
125+
return new_lt(rhs, lhs, strict);
126+
}
121127

122128
void solver::retract(constraint &c) noexcept
123129
{
@@ -152,12 +158,12 @@ namespace linspire
152158
for (const auto &[v, c] : l.vars)
153159
if (is_positive(c)) // we use the most restrictive upper bound of v
154160
for (const auto &w : vars.at(v).ubs.begin()->second)
155-
cnfl.push_back(w);
161+
cnfl.push_back(*w);
156162
else if (is_negative(c)) // we use the most restrictive lower bound of v
157163
for (const auto &w : vars.at(v).lbs.rbegin()->second)
158-
cnfl.push_back(w);
164+
cnfl.push_back(*w);
159165
for (const auto &w : vars.at(x_i).lbs.rbegin()->second) // we use the most restrictive lower bound of x_i
160-
cnfl.push_back(w);
166+
cnfl.push_back(*w);
161167
return false;
162168
}
163169
}
@@ -173,12 +179,12 @@ namespace linspire
173179
for (const auto &[v, c] : l.vars)
174180
if (is_positive(c)) // we use the most restrictive lower bound of v
175181
for (const auto &w : vars.at(v).lbs.rbegin()->second)
176-
cnfl.push_back(w);
182+
cnfl.push_back(*w);
177183
else if (is_negative(c)) // we use the most restrictive upper bound of v
178184
for (const auto &w : vars.at(v).ubs.begin()->second)
179-
cnfl.push_back(w);
185+
cnfl.push_back(*w);
180186
for (const auto &w : vars.at(x_i).ubs.begin()->second) // we use the most restrictive upper bound of x_i
181-
cnfl.push_back(w);
187+
cnfl.push_back(*w);
182188
return false;
183189
}
184190
}
@@ -187,7 +193,7 @@ namespace linspire
187193

188194
bool solver::match(const utils::lin &l0, const utils::lin &l1) const noexcept { return lb(l0) <= ub(l1) && ub(l0) >= lb(l1); }
189195

190-
bool solver::set_lb(const utils::var x, const utils::inf_rational &v, constraint *reason) noexcept
196+
bool solver::set_lb(const utils::var x, const utils::inf_rational &v, std::optional<std::reference_wrapper<constraint>> reason) noexcept
191197
{
192198
assert(x < vars.size());
193199
assert(v > utils::rational::negative_infinite);
@@ -196,14 +202,14 @@ namespace linspire
196202
{ // inconsistent bound..
197203
cnfl.clear();
198204
if (reason)
199-
cnfl.push_back(reason);
205+
cnfl.push_back(reason.value());
200206
for (const auto &w : vars.at(x).ubs.begin()->second) // we use the most restrictive upper bound of x
201-
cnfl.push_back(w);
207+
cnfl.push_back(*w);
202208
return false;
203209
}
204210
if (reason)
205211
{ // we have a reason for this bound..
206-
if (auto it = reason->lbs.find(x); it != reason->lbs.end())
212+
if (auto it = reason->get().lbs.find(x); it != reason->get().lbs.end())
207213
{ // we already have a lower bound for this variable in the reason..
208214
if (it->second < v)
209215
{ // we update the lower bound only if the new one is more restrictive..
@@ -212,14 +218,14 @@ namespace linspire
212218
}
213219
}
214220
else
215-
reason->lbs.emplace(x, v);
221+
reason->get().lbs.emplace(x, v);
216222
}
217223
vars.at(x).set_lb(v, reason);
218224
if (val(x) < v && !is_basic(x))
219225
update(x, v);
220226
return true;
221227
}
222-
bool solver::set_ub(const utils::var x, const utils::inf_rational &v, constraint *reason) noexcept
228+
bool solver::set_ub(const utils::var x, const utils::inf_rational &v, std::optional<std::reference_wrapper<constraint>> reason) noexcept
223229
{
224230
assert(x < vars.size());
225231
assert(v < utils::rational::positive_infinite);
@@ -228,14 +234,14 @@ namespace linspire
228234
{ // inconsistent bound..
229235
cnfl.clear();
230236
if (reason)
231-
cnfl.push_back(reason);
237+
cnfl.push_back(reason.value());
232238
for (const auto &w : vars.at(x).lbs.rbegin()->second) // we use the most restrictive lower bound of x
233-
cnfl.push_back(w);
239+
cnfl.push_back(*w);
234240
return false;
235241
}
236242
if (reason)
237243
{ // we have a reason for this bound..
238-
if (auto it = reason->ubs.find(x); it != reason->ubs.end())
244+
if (auto it = reason->get().ubs.find(x); it != reason->get().ubs.end())
239245
{ // we already have an upper bound for this variable in the reason..
240246
if (it->second > v)
241247
{ // we update the upper bound only if the new one is more restrictive..
@@ -244,7 +250,7 @@ namespace linspire
244250
}
245251
}
246252
else
247-
reason->ubs.emplace(x, v);
253+
reason->get().ubs.emplace(x, v);
248254
}
249255
vars.at(x).set_ub(v, reason);
250256
if (val(x) > v && !is_basic(x))

src/var.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ 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, constraint *reason) noexcept
12+
void var::set_lb(const utils::inf_rational &v, std::optional<std::reference_wrapper<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())
18-
it->second.insert(reason); // we add the reason to the existing lower bound `v`..
18+
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}); // we create a new lower bound `v`..
20+
lbs.emplace(v, std::set<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`..
@@ -35,15 +35,15 @@ namespace linspire
3535
lbs.erase(it);
3636
}
3737

38-
void var::set_ub(const utils::inf_rational &v, constraint *reason) noexcept
38+
void var::set_ub(const utils::inf_rational &v, std::optional<std::reference_wrapper<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())
44-
it->second.insert(reason); // we add the reason to the existing upper bound `v`..
44+
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}); // we create a new upper bound `v`..
46+
ubs.emplace(v, std::set<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`..

tests/test_linspire.cpp

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@ void test2()
8686
linspire::constraint c0;
8787

8888
// x >= 0
89-
bool res0 = s.new_gt({{x, 1}}, 0, false, &c0);
89+
bool res0 = s.new_gt({{x, 1}}, 0, false, c0);
9090
assert(res0);
9191
// x >= 1 (we add this constraint with the same reason as the previous one)
92-
bool res1 = s.new_gt({{x, 1}}, 1, false, &c0);
92+
bool res1 = s.new_gt({{x, 1}}, 1, false, c0);
9393
assert(res1);
9494

9595
auto cons = s.check();
@@ -121,10 +121,10 @@ void test3()
121121
linspire::constraint c1;
122122

123123
// y >= x + 1
124-
bool res0 = s.new_gt({{y, 1}, {x, -1}}, 1, false, &c0);
124+
bool res0 = s.new_gt({{y, 1}, {x, -1}}, 1, false, c0);
125125
assert(res0);
126126
// z >= y + 1
127-
bool res1 = s.new_gt({{z, 1}, {y, -1}}, 1, false, &c1);
127+
bool res1 = s.new_gt({{z, 1}, {y, -1}}, 1, false, c1);
128128
assert(res1);
129129
bool cons = s.check();
130130
assert(cons);
@@ -161,22 +161,22 @@ void test4()
161161
linspire::constraint c2;
162162

163163
// x + y >= 1
164-
bool res0 = s.new_gt({{x, 1}, {y, 1}}, 1, false, &c0);
164+
bool res0 = s.new_gt({{x, 1}, {y, 1}}, 1, false, c0);
165165
assert(res0);
166166
// x >= 2
167-
bool res1 = s.new_gt({{x, 1}}, 2, false, &c1);
167+
bool res1 = s.new_gt({{x, 1}}, 2, false, c1);
168168
assert(res1);
169169
bool cons = s.check();
170170
assert(cons);
171171

172172
// x + y <= 0
173-
bool res2 = s.new_lt({{x, 1}, {y, 1}}, 0, false, &c2);
173+
bool res2 = s.new_lt({{x, 1}, {y, 1}}, 0, false, c2);
174174
assert(res2);
175175
cons = s.check();
176176
assert(!cons);
177177
auto expl = s.get_conflict();
178178
assert(expl.size() == 2);
179-
assert((expl[0] == &c0 && expl[1] == &c2) || (expl[0] == &c2 && expl[1] == &c0));
179+
assert((&expl[0].get() == &c0 && &expl[1].get() == &c2) || (&expl[0].get() == &c2 && &expl[1].get() == &c0));
180180

181181
s.retract(c0);
182182
cons = s.check();

0 commit comments

Comments
 (0)