Skip to content

Commit f547796

Browse files
authored
Merge pull request #36 from scipopt/issue-32-linexpr-ctors
Add new c'tors to LinExpr
2 parents 4e25acc + f9678e5 commit f547796

File tree

5 files changed

+231
-3
lines changed

5 files changed

+231
-3
lines changed

.github/workflows/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
verbose: true # optional (default = false)
4242
fail_ci_if_error: true
4343
test_release_win:
44-
runs-on: windows-2019
44+
runs-on: windows-2022
4545
steps:
4646
- uses: actions/checkout@v3
4747
- uses: turtlebrowser/get-conan@main

changelog.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1010

1111
## Added
1212

13+
- [PR36](https://github.com/scipopt/SCIPpp/pull/36)
14+
New c'tors for `LinExpr` to allow use-cases like
15+
```cpp
16+
const Var& [x1, x2] = ...
17+
const array A_VARS = ...
18+
const vector V_VARS = ...
19+
20+
LinExpr le(A_VARS); // new c'tor for std::array
21+
le += V_VARS; // new c'tor for std::vector
22+
le += {x1, x2}; // new c'tor for std::initializer_list
23+
// all of them also available with a second argument for coefficients
24+
```
1325
- [PR31](https://github.com/scipopt/SCIPpp/pull/31) Add `InitialSolution` and `Model::addSolution`.
1426
- [PR28](https://github.com/scipopt/SCIPpp/pull/28) Add `Var::getVar`.
1527

include/scippp/lin_expr.hpp

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
#pragma once
22

3+
#include <array>
34
#include <vector>
45

56
#include "scippp/var.hpp"
67

8+
// just for tests, see test_linexpr.cpp
9+
namespace LinExpression { // NOLINT(readability-identifier-naming)
10+
struct CheckInternalsUsingFriendStruct;
11+
}
712
namespace scippp {
813

914
/**
@@ -12,6 +17,7 @@ namespace scippp {
1217
*/
1318
class LinExpr {
1419
friend class Model;
20+
friend struct LinExpression::CheckInternalsUsingFriendStruct; // just for tests, see test_linexpr.cpp
1521
//! Constant term.
1622
double m_constant { 0.0 };
1723
//! Variables in the linear combination.
@@ -39,12 +45,67 @@ class LinExpr {
3945
{
4046
}
4147
/**
42-
* Creates a linear expression with zero as constant the given variable with coefficient one.
48+
* Creates a linear expression with zero as constant and the given variable with coefficient one.
4349
* @since 1.0.0
44-
* @remark This is on purpose not an explicit c'tor to allow expressions like x <= 1.
50+
* @remark This is on purpose not an explicit c'tor to allow expressions like <code>x <= 1</code>.
4551
* @param var Variable to store with coefficient one in the expression.
4652
*/
4753
LinExpr(const Var& var);
54+
/**
55+
* Creates a linear expression with zero as constant and the given variables with coefficient one.
56+
* @since 1.3.0
57+
* @remark This is on purpose not an explicit c'tor to allow expressions like <code>... + {x1, x2}</code>.
58+
* @param vars Variables to store with coefficient one in the expression.
59+
*/
60+
LinExpr(std::initializer_list<Var> vars);
61+
/**
62+
* Creates a linear expression with zero as constant and the given variables with the provided coefficients.
63+
* @since 1.3.0
64+
* @param vars Variables to store in the expression.
65+
* @param coeffs Coefficients to store in the expression.
66+
* @pre \p vars and \p coeffs have the same length.
67+
*/
68+
LinExpr(std::initializer_list<Var> vars, std::initializer_list<double> coeffs);
69+
/**
70+
* @copybrief LinExpr(std::initializer_list<Var>)
71+
* @since 1.3.0
72+
* @remark This is on purpose not an explicit c'tor to allow expressions like <code>... + vars</code>.
73+
* @tparam N Size of the array \p vars.
74+
* @param vars Variables to store with coefficient one in the expression.
75+
*/
76+
template <std::size_t N>
77+
LinExpr(const std::array<Var, N>& vars)
78+
: m_vars { vars.begin(), vars.end() }
79+
, m_coeffs(vars.size(), 1)
80+
{
81+
}
82+
/**
83+
* @copybrief LinExpr(std::initializer_list<Var>, std::initializer_list<double>)
84+
* @since 1.3.0
85+
* @tparam N Size of the arrays \p vars and \p coeffs.
86+
* @param vars Variables to store in the expression.
87+
* @param coeffs Coefficients to store in the expression.
88+
*/
89+
template <std::size_t N>
90+
LinExpr(const std::array<Var, N>& vars, std::array<double, N>& coeffs)
91+
: m_vars { vars.begin(), vars.end() }
92+
, m_coeffs { coeffs.begin(), coeffs.end() }
93+
{
94+
}
95+
/**
96+
* @copybrief LinExpr(std::initializer_list<Var>)
97+
* @since 1.3.0
98+
* @remark This is on purpose not an explicit c'tor to allow expressions like <code>... + vars</code>.
99+
* @param vars Variables to store with coefficient one in the expression.
100+
*/
101+
LinExpr(const std::vector<Var>& vars);
102+
/**
103+
* @copybrief LinExpr(std::initializer_list<Var>, std::initializer_list<double>)
104+
* @since 1.3.0
105+
* @param vars Variables to store in the expression.
106+
* @param coeffs Coefficients to store in the expression.
107+
*/
108+
LinExpr(const std::vector<Var>& vars, const std::vector<double>& coeffs);
48109

49110
/**
50111
* Returns the constant term of the expression.

source/lin_expr.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include "scippp/lin_expr.hpp"
22

3+
#include <cassert>
4+
35
namespace scippp {
46

57
LinExpr::LinExpr(const Var& var)
@@ -8,6 +10,32 @@ LinExpr::LinExpr(const Var& var)
810
{
911
}
1012

13+
LinExpr::LinExpr(std::initializer_list<Var> vars)
14+
: m_vars { vars }
15+
, m_coeffs(vars.size(), 1)
16+
{
17+
}
18+
19+
LinExpr::LinExpr(std::initializer_list<Var> vars, std::initializer_list<double> coeffs)
20+
: m_vars { vars }
21+
, m_coeffs { coeffs }
22+
{
23+
assert(vars.size() == coeffs.size()); // GCOVR_EXCL_LINE
24+
}
25+
26+
LinExpr::LinExpr(const std::vector<Var>& vars)
27+
: m_vars { vars }
28+
, m_coeffs(vars.size(), 1)
29+
{
30+
}
31+
32+
LinExpr::LinExpr(const std::vector<Var>& vars, const std::vector<double>& coeffs)
33+
: m_vars { vars }
34+
, m_coeffs { coeffs }
35+
{
36+
assert(vars.size() == coeffs.size()); // GCOVR_EXCL_LINE
37+
}
38+
1139
double LinExpr::getConstant() const
1240
{
1341
return m_constant;

test/test_linexpr.cpp

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
#include <boost/test/unit_test.hpp>
2+
3+
#include <algorithm>
4+
5+
#include "scippp/model.hpp"
6+
#include "scippp/solving_statistics.hpp"
7+
8+
using namespace scippp;
9+
using namespace std;
10+
11+
BOOST_AUTO_TEST_SUITE(LinExpression)
12+
13+
BOOST_AUTO_TEST_CASE(AddOneVar)
14+
{
15+
Model model("Simple");
16+
array coeff { 1, 1 };
17+
const auto& [x1, x2] = model.addVars<2>("x_", coeff);
18+
LinExpr l;
19+
l += x1; // allocates memory, thus slow
20+
l += x2; // allocates memory again, thus slow
21+
model.addConstr(l <= 1, "capacity");
22+
model.setObjsense(Sense::MAXIMIZE);
23+
model.solve();
24+
BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 1);
25+
}
26+
27+
BOOST_AUTO_TEST_CASE(CtorArray)
28+
{
29+
array objCoeff { 1.0, 1.0 };
30+
31+
Model m1("m1");
32+
const auto VARS1 = m1.addVars<2>("x_", objCoeff);
33+
LinExpr l1(VARS1);
34+
m1.addConstr(l1 <= 1, "capacity");
35+
m1.setObjsense(Sense::MAXIMIZE);
36+
m1.solve();
37+
BOOST_TEST(m1.getSolvingStatistic(statistics::PRIMALBOUND) == 1.0);
38+
39+
Model m2("m2");
40+
const auto VARS2 = m2.addVars<2>("x_", objCoeff);
41+
LinExpr l2(VARS2, objCoeff); // same as before, but different c'tor
42+
m2.addConstr(l2 <= 1, "capacity");
43+
m2.setObjsense(Sense::MAXIMIZE);
44+
m2.solve();
45+
BOOST_TEST(m2.getSolvingStatistic(statistics::PRIMALBOUND) == 1.0);
46+
47+
const double FAC { 2.0 };
48+
array constrCoeff { FAC, FAC };
49+
50+
Model m3("m3");
51+
const auto VARS3 = m3.addVars<2>("x_", objCoeff);
52+
LinExpr l3(VARS3, constrCoeff);
53+
m3.addConstr(l3 <= 1, "capacity");
54+
m3.setObjsense(Sense::MAXIMIZE);
55+
m3.solve();
56+
BOOST_TEST(m3.getSolvingStatistic(statistics::PRIMALBOUND) == 1.0 / FAC);
57+
}
58+
59+
BOOST_AUTO_TEST_CASE(AddArray)
60+
{
61+
Model model("Simple");
62+
array coeff { 1, 1 };
63+
const auto VARS = model.addVars<2>("x_", coeff);
64+
LinExpr l;
65+
l += VARS;
66+
model.addConstr(l <= 1, "capacity");
67+
model.setObjsense(Sense::MAXIMIZE);
68+
model.solve();
69+
BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 1);
70+
}
71+
72+
BOOST_AUTO_TEST_CASE(AddVector)
73+
{
74+
Model model("Simple");
75+
array coeff { 1, 1 };
76+
const auto A_VARS { model.addVars<2>("x_", coeff) };
77+
const vector<Var> VARS { A_VARS.begin(), A_VARS.end() };
78+
LinExpr l1;
79+
l1 += VARS;
80+
model.addConstr(l1 <= 1, "capacity1");
81+
LinExpr l2(VARS, vector { 2.0, 2.0 });
82+
model.addConstr(l2 <= 2, "capacity2"); // duplicate, but different c'tor
83+
model.setObjsense(Sense::MAXIMIZE);
84+
model.solve();
85+
BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 1);
86+
}
87+
88+
BOOST_AUTO_TEST_CASE(AddInitializerList)
89+
{
90+
Model model("Simple");
91+
array coeff { 1, 1 };
92+
const auto& [x1, x2] = model.addVars<2>("x_", coeff);
93+
LinExpr l;
94+
l += { x1, x2 };
95+
model.addConstr(l <= 1, "capacity");
96+
// duplicate, but different c'tor:
97+
model.addConstr(LinExpr({ x1, x2 }, { 2.0, 2.0 }) <= 2, "capacity2");
98+
model.setObjsense(Sense::MAXIMIZE);
99+
model.solve();
100+
BOOST_TEST(model.getSolvingStatistic(statistics::PRIMALBOUND) == 1);
101+
}
102+
103+
BOOST_AUTO_TEST_CASE(CheckInternalsUsingFriendStruct)
104+
{
105+
const double VAL { 2.0 };
106+
for (size_t num { 1 }; num < 4; ++num) {
107+
Model m("m");
108+
const auto VARS = m.addVars("x", num);
109+
110+
LinExpr lDef(VARS);
111+
BOOST_TEST(lDef.getConstant() == 0);
112+
BOOST_TEST(lDef.m_constant == 0);
113+
BOOST_TEST(lDef.m_vars.size() == num);
114+
BOOST_TEST(lDef.m_coeffs.size() == num);
115+
BOOST_TEST(all_of(lDef.m_coeffs.begin(), lDef.m_coeffs.end(), [](double d) { return d == 1; }));
116+
117+
const vector COEFF(num, VAL);
118+
LinExpr lCoeff(VARS, COEFF);
119+
BOOST_TEST(lCoeff.getConstant() == 0);
120+
BOOST_TEST(lCoeff.m_constant == 0);
121+
BOOST_TEST(lCoeff.m_vars.size() == num);
122+
BOOST_TEST(lCoeff.m_coeffs.size() == num);
123+
BOOST_TEST(all_of(lCoeff.m_coeffs.begin(), lCoeff.m_coeffs.end(), [&VAL](double d) { return d == VAL; }));
124+
}
125+
}
126+
127+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)