Skip to content

Commit

Permalink
more model_builder python APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
lperron committed Jul 20, 2023
1 parent fd4a173 commit a273b95
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 0 deletions.
22 changes: 22 additions & 0 deletions ortools/linear_solver/python/model_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -1455,6 +1455,28 @@ def reduced_cost(self, var: Variable) -> np.double:
return pd.NA
return self.__solve_helper.reduced_cost(var.index)

def dual_values(self, constraints: _IndexOrSeries) -> pd.Series:
"""Returns the dual values of the input constraints.
If `constraints` is a `pd.Index`, then the output will be indexed by the
constraints. If `constraints` is a `pd.Series` indexed by the underlying
dimensions, then the output will be indexed by the same underlying
dimensions.
Args:
constraints (Union[pd.Index, pd.Series]): The set of constraints from
which to get the dual values.
Returns:
pd.Series: The dual_values of all constraints in the set.
"""
if not self.__solve_helper.has_solution():
return _attribute_series(func=lambda v: pd.NA, values=constraints)
return _attribute_series(
func=lambda v: self.__solve_helper.dual_value(v.index),
values=constraints,
)

def dual_value(self, ct: LinearConstraint) -> np.double:
"""Returns the dual value of a linear constraint after solve."""
if not self.__solve_helper.has_solution():
Expand Down
3 changes: 3 additions & 0 deletions ortools/linear_solver/python/model_builder_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,9 @@ PYBIND11_MODULE(model_builder_helper, m) {
})
.def("set_constraint_name", &ModelBuilderHelper::SetConstraintName,
arg("ct_index"), arg("name"))
.def("set_constraint_coefficient",
&ModelBuilderHelper::SetConstraintCoefficient, arg("ct_index"),
arg("var_index"), arg("coeff"))
.def("num_variables", &ModelBuilderHelper::num_variables)
.def("var_lower_bound", &ModelBuilderHelper::VarLowerBound,
arg("var_index"))
Expand Down
24 changes: 24 additions & 0 deletions ortools/linear_solver/python/model_builder_helper_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,30 @@ def test_build_model(self):
self.assertEqual((10,), var_array.shape)
self.assertEqual(model.var_name(var_array[3]), "var_3")

def test_set_coefficient(self):
var_lb = np.array([-1.0, -2.0])
var_ub = np.array([np.inf, np.inf])
obj = np.array([10.0, 20.0])
con_lb = np.array([-5.0, -6.0])
con_ub = np.array([5.0, 6.0])
constraint_matrix = sparse.csr_matrix(np.array([[1.0, 3.0], [2.0, 4.0]]))

model = model_builder_helper.ModelBuilderHelper()
model.fill_model_from_sparse_data(
var_lb, var_ub, obj, con_lb, con_ub, constraint_matrix
)
# Here, we add new variables to test that we are able to set coefficients
# for variables that are not yet in the constraint.
var_index1 = model.add_var()
var_index2 = model.add_var()
model.set_constraint_coefficient(0, var_index2, 5.0)
model.set_constraint_coefficient(0, var_index1, 6.0)
self.assertEqual([1.0, 3.0, 5.0, 6.0], model.constraint_coefficients(0))
# Here, we test that we are able to set coefficients for variables whose
# index in the constraint is different from its index in the model.
model.set_constraint_coefficient(0, var_index2, 7.0)
self.assertEqual([1.0, 3.0, 7.0, 6.0], model.constraint_coefficients(0))


if __name__ == "__main__":
absltest.main()
9 changes: 9 additions & 0 deletions ortools/linear_solver/python/model_builder_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -1845,19 +1845,28 @@ def test_simple_problem(self):
self.assertLen(model.get_linear_constraints(), 3)
solver = mb.ModelSolver("glop")
test_red_cost = solver.reduced_costs(model.get_variables())
test_dual_values = solver.dual_values(model.get_variables())
self.assertLen(test_red_cost, 3)
self.assertLen(test_dual_values, 3)
for reduced_cost in test_red_cost:
self.assertTrue(pd.isna(reduced_cost))
for dual_value in test_dual_values:
self.assertTrue(pd.isna(dual_value))
run = solver.solve(model)
self.assertEqual(run, mb.SolveStatus.OPTIMAL)
i = solver.values(model.get_variables())
self.assertSequenceAlmostEqual(i, [2, 0, 1])
red_cost = solver.reduced_costs(model.get_variables())
dual_val = solver.dual_values(model.get_linear_constraints())
self.assertSequenceAlmostEqual(red_cost, [0, -3, 0])
self.assertSequenceAlmostEqual(dual_val, [1, 0, 1])
self.assertAlmostEqual(2, solver.value(x[0]))
self.assertAlmostEqual(0, solver.reduced_cost((x[0])))
self.assertAlmostEqual(-3, solver.reduced_cost((x[1])))
self.assertAlmostEqual(0, solver.reduced_cost((x[2])))
self.assertAlmostEqual(1, solver.dual_value((x[0])))
self.assertAlmostEqual(0, solver.dual_value((x[1])))
self.assertAlmostEqual(1, solver.dual_value((x[2])))


if __name__ == "__main__":
Expand Down
15 changes: 15 additions & 0 deletions ortools/linear_solver/wrappers/model_builder_helper.cc
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,21 @@ void ModelBuilderHelper::SetConstraintName(int ct_index,
model_.mutable_constraint(ct_index)->set_name(name);
}

void ModelBuilderHelper::SetConstraintCoefficient(int ct_index, int var_index,
double coeff) {
MPConstraintProto* ct_proto = model_.mutable_constraint(ct_index);
for (int i = 0; i < ct_proto->var_index_size(); ++i) {
if (ct_proto->var_index(i) == var_index) {
ct_proto->set_coefficient(i, coeff);
return;
}
}
// If we reach this point, the variable does not exist in the constraint yet,
// so we add it to the constraint as a new term.
ct_proto->add_var_index(var_index);
ct_proto->add_coefficient(coeff);
}

int ModelBuilderHelper::num_variables() const { return model_.variable_size(); }

double ModelBuilderHelper::VarLowerBound(int var_index) const {
Expand Down
1 change: 1 addition & 0 deletions ortools/linear_solver/wrappers/model_builder_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class ModelBuilderHelper {
void SetConstraintUpperBound(int ct_index, double ub);
void AddConstraintTerm(int ct_index, int var_index, double coeff);
void SetConstraintName(int ct_index, const std::string& name);
void SetConstraintCoefficient(int ct_index, int var_index, double coeff);

int num_variables() const;
double VarLowerBound(int var_index) const;
Expand Down

0 comments on commit a273b95

Please sign in to comment.