Skip to content

Commit aa2fbd4

Browse files
authored
Merge branch 'master' into knitro-lic
2 parents 775c035 + 9d9d437 commit aa2fbd4

8 files changed

Lines changed: 55 additions & 31 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ dist/
1010

1111
.idea/
1212
.vscode/
13+
.claude/
1314

1415
bench/local
1516
debug/

lib/ipopt_model.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,8 @@ double IpoptModel::get_constraint_primal(const ConstraintIndex &constraint)
181181
double IpoptModel::get_constraint_dual(const ConstraintIndex &constraint)
182182
{
183183
int index = _constraint_internal_index(constraint);
184-
return m_result.mult_g[index];
184+
auto dual = -m_result.mult_g[index];
185+
return dual;
185186
}
186187

187188
ConstraintIndex IpoptModel::add_linear_constraint(const ScalarAffineFunction &f,

src/pyoptinterface/_src/knitro.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,7 @@ def _result_status_knitro(model: "Model"):
221221
return ResultStatusCode.INFEASIBLE_POINT
222222
return ResultStatusCode.NO_SOLUTION
223223

224+
224225
# Model Attribute
225226
model_attribute_get_func_map = {
226227
ModelAttribute.ObjectiveValue: lambda model: model.get_obj_value(),

tests/conftest.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,24 +34,36 @@ def nlp_model_ctor(request):
3434
return ctor
3535

3636

37-
model_interface_dict = {}
37+
model_interface_dict_full = {}
3838

3939
if gurobi.is_library_loaded():
40-
model_interface_dict["gurobi"] = gurobi.Model
40+
model_interface_dict_full["gurobi"] = gurobi.Model
4141
if xpress.is_library_loaded():
42-
model_interface_dict["xpress"] = xpress.Model
42+
model_interface_dict_full["xpress"] = xpress.Model
4343
if copt.is_library_loaded():
44-
model_interface_dict["copt"] = copt.Model
44+
model_interface_dict_full["copt"] = copt.Model
4545
if mosek.is_library_loaded():
46-
model_interface_dict["mosek"] = mosek.Model
46+
model_interface_dict_full["mosek"] = mosek.Model
4747
if highs.is_library_loaded():
4848
model_interface_dict["highs"] = highs.Model
4949
if knitro.is_library_loaded() and knitro.has_valid_license():
5050
model_interface_dict["knitro"] = knitro.Model
5151

5252

53-
@pytest.fixture(params=model_interface_dict.keys())
53+
@pytest.fixture(params=model_interface_dict_full.keys())
5454
def model_interface(request):
5555
name = request.param
56-
model_interface_class = model_interface_dict[name]
56+
model_interface_class = model_interface_dict_full[name]
57+
return model_interface_class()
58+
59+
60+
model_interface_dict_oneshot = model_interface_dict_full.copy()
61+
if ipopt.is_library_loaded():
62+
model_interface_dict_oneshot["ipopt"] = ipopt.Model
63+
64+
65+
@pytest.fixture(params=model_interface_dict_oneshot.keys())
66+
def model_interface_oneshot(request):
67+
name = request.param
68+
model_interface_class = model_interface_dict_oneshot[name]
5769
return model_interface_class()

tests/test_in_constraint.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import pytest
55

66

7-
def test_linear_in_constraint(model_interface):
8-
model = model_interface
7+
def test_linear_in_constraint(model_interface_oneshot):
8+
model = model_interface_oneshot
99

1010
if isinstance(model, gurobi.Model):
1111
pytest.skip("Gurobi does not support range linear constraints")

tests/test_matrix_api.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
from pytest import approx
55

66

7-
def test_matrix_api(model_interface):
8-
model = model_interface
7+
def test_matrix_api(model_interface_oneshot):
8+
model = model_interface_oneshot
99

1010
N = 10
1111
x = model.add_m_variables(N, lb=0.0)
@@ -29,8 +29,8 @@ def test_matrix_api(model_interface):
2929
assert obj_value == approx(-N * ub)
3030

3131

32-
def test_quicksum_ndarray(model_interface):
33-
model = model_interface
32+
def test_quicksum_ndarray(model_interface_oneshot):
33+
model = model_interface_oneshot
3434

3535
N = 10
3636
x = model.add_m_variables((N, 2 * N), lb=1.0, ub=3.0)

tests/test_qp.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
import numpy as np
55

66

7-
def test_simple_qp(model_interface):
8-
model = model_interface
7+
def test_simple_qp(model_interface_oneshot):
8+
model = model_interface_oneshot
99

1010
N = 6
1111

@@ -19,15 +19,18 @@ def test_simple_qp(model_interface):
1919

2020
model.optimize()
2121
status = model.get_model_attribute(poi.ModelAttribute.TerminationStatus)
22-
assert status == poi.TerminationStatusCode.OPTIMAL
22+
assert (
23+
status == poi.TerminationStatusCode.OPTIMAL
24+
or status == poi.TerminationStatusCode.LOCALLY_SOLVED
25+
)
2326

2427
obj_val = model.get_model_attribute(poi.ModelAttribute.ObjectiveValue)
2528
assert obj_val == approx(N**2, rel=1e-5)
2629

2730

2831
# reported by https://github.com/metab0t/PyOptInterface/issues/59
29-
def test_shuffle_qp_objective(model_interface):
30-
model = model_interface
32+
def test_shuffle_qp_objective(model_interface_oneshot):
33+
model = model_interface_oneshot
3134

3235
N = 3
3336
weights = model.add_m_variables(N, lb=0)
@@ -68,8 +71,8 @@ def test_shuffle_qp_objective(model_interface):
6871
assert np.all(np.abs(obj_values - obj_values[0]) < 1e-8)
6972

7073

71-
def test_duplicated_quadratic_terms(model_interface):
72-
model = model_interface
74+
def test_duplicated_quadratic_terms(model_interface_oneshot):
75+
model = model_interface_oneshot
7376

7477
x = model.add_m_variables(2, lb=1.0)
7578

tests/test_simple_opt.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,8 @@ def test_simple_opt(model_interface):
9494
assert y_val == approx(8.0)
9595

9696

97-
def test_constant_objective(model_interface):
98-
model = model_interface
97+
def test_constant_objective(model_interface_oneshot):
98+
model = model_interface_oneshot
9999

100100
x = model.add_variable(lb=0.0, ub=1.0)
101101
obj = 1.0
@@ -106,13 +106,16 @@ def test_constant_objective(model_interface):
106106
model.set_objective(obj, poi.ObjectiveSense.Maximize)
107107
model.optimize()
108108
status = model.get_model_attribute(poi.ModelAttribute.TerminationStatus)
109-
assert status == poi.TerminationStatusCode.OPTIMAL
109+
assert (
110+
status == poi.TerminationStatusCode.OPTIMAL
111+
or status == poi.TerminationStatusCode.LOCALLY_SOLVED
112+
)
110113
obj_val = model.get_model_attribute(poi.ModelAttribute.ObjectiveValue)
111114
assert obj_val == approx(1.0)
112115

113116

114-
def test_constraint_primal_dual(model_interface):
115-
model = model_interface
117+
def test_constraint_primal_dual(model_interface_oneshot):
118+
model = model_interface_oneshot
116119

117120
x = model.add_variable(lb=0.0, ub=1.0)
118121
y = model.add_variable(lb=0.0, ub=1.0)
@@ -128,7 +131,10 @@ def test_constraint_primal_dual(model_interface):
128131

129132
model.optimize()
130133
status = model.get_model_attribute(poi.ModelAttribute.TerminationStatus)
131-
assert status == poi.TerminationStatusCode.OPTIMAL
134+
assert (
135+
status == poi.TerminationStatusCode.OPTIMAL
136+
or status == poi.TerminationStatusCode.LOCALLY_SOLVED
137+
)
132138

133139
primal_val = model.get_constraint_attribute(con1, poi.ConstraintAttribute.Primal)
134140
assert primal_val == approx(1.0)
@@ -137,8 +143,8 @@ def test_constraint_primal_dual(model_interface):
137143
assert dual_val == approx(0.5)
138144

139145

140-
def test_add_quadratic_expr_as_linear_throws_error(model_interface):
141-
model = model_interface
146+
def test_add_quadratic_expr_as_linear_throws_error(model_interface_oneshot):
147+
model = model_interface_oneshot
142148

143149
xs = model.add_m_variables(10)
144150
x2_sum = poi.quicksum(x * x for x in xs.flat)
@@ -147,8 +153,8 @@ def test_add_quadratic_expr_as_linear_throws_error(model_interface):
147153
model.add_linear_constraint(x2_sum <= 1.0)
148154

149155

150-
def test_exprbuilder_self_operation(model_interface):
151-
model = model_interface
156+
def test_exprbuilder_self_operation(model_interface_oneshot):
157+
model = model_interface_oneshot
152158

153159
x = model.add_m_variables(2, lb=1.0, ub=4.0)
154160

0 commit comments

Comments
 (0)