From fcd64e6b9739decd2f2769a69a4fafa1d8857c45 Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Mon, 6 Nov 2023 15:20:03 +0100 Subject: [PATCH] polish on model_builder code --- .../EnforcedLinearConstraint.java | 4 +- .../ortools/modelbuilder/ModelBuilder.java | 40 +- ortools/linear_solver/csharp/ModelSolver.cs | 14 +- ortools/linear_solver/java/BUILD.bazel | 4 +- ortools/linear_solver/python/model_builder.py | 510 ++++++++++-------- .../python/model_builder_helper.cc | 53 +- .../python/model_builder_test.py | 6 +- ortools/linear_solver/samples/BinPackingMb.cs | 2 + ortools/linear_solver/samples/CloneModelMb.cs | 2 + .../wrappers/model_builder_helper.cc | 32 +- .../wrappers/model_builder_helper.h | 3 +- 11 files changed, 377 insertions(+), 293 deletions(-) diff --git a/ortools/java/com/google/ortools/modelbuilder/EnforcedLinearConstraint.java b/ortools/java/com/google/ortools/modelbuilder/EnforcedLinearConstraint.java index 3abc471e7f..6c382ee5fb 100644 --- a/ortools/java/com/google/ortools/modelbuilder/EnforcedLinearConstraint.java +++ b/ortools/java/com/google/ortools/modelbuilder/EnforcedLinearConstraint.java @@ -69,7 +69,7 @@ public class EnforcedLinearConstraint { helper.setEnforcedConstraintName(index, name); } - /** Adds var * coeff to the constraint.*/ + /** Adds var * coeff to the constraint. */ public void addTerm(Variable v, double coeff) { helper.safeAddEnforcedConstraintTerm(index, v.getIndex(), coeff); } @@ -102,7 +102,7 @@ public class EnforcedLinearConstraint { /** Sets the indicator value of the constraint. */ public void setIndicatorValue(boolean b) { helper.setEnforcedIndicatorValue(index, b); - } + } /** Inline setter */ public EnforcedLinearConstraint withName(String name) { diff --git a/ortools/java/com/google/ortools/modelbuilder/ModelBuilder.java b/ortools/java/com/google/ortools/modelbuilder/ModelBuilder.java index 671f971936..159cdc5ff1 100644 --- a/ortools/java/com/google/ortools/modelbuilder/ModelBuilder.java +++ b/ortools/java/com/google/ortools/modelbuilder/ModelBuilder.java @@ -163,8 +163,9 @@ public final class ModelBuilder { // Enforced Linear constraints. - /** Adds {@code lb <= expr <= ub}. */ - public EnforcedLinearConstraint addEnforcedLinearConstraint(LinearArgument expr, double lb, double ub, Variable iVar, boolean iValue) { + /** Adds {@code ivar == iValue => b <= expr <= ub}. */ + public EnforcedLinearConstraint addEnforcedLinearConstraint( + LinearArgument expr, double lb, double ub, Variable iVar, boolean iValue) { EnforcedLinearConstraint lin = new EnforcedLinearConstraint(helper); lin.setIndicatorVariable(iVar); lin.setIndicatorValue(iValue); @@ -186,39 +187,45 @@ public final class ModelBuilder { return lin; } - /** Adds {@code expr == value}. */ - public EnforcedLinearConstraint addEnforcedEquality(LinearArgument expr, double value, Variable iVar, boolean iValue) { + /** Adds {@code ivar == iValue => expr == value}. */ + public EnforcedLinearConstraint addEnforcedEquality( + LinearArgument expr, double value, Variable iVar, boolean iValue) { return addEnforcedLinearConstraint(expr, value, value, iVar, iValue); } - /** Adds {@code left == right}. */ - public EnforcedLinearConstraint addEnforcedEquality(LinearArgument left, LinearArgument right, Variable iVar, boolean iValue) { + /** Adds {@code ivar == iValue => left == right}. */ + public EnforcedLinearConstraint addEnforcedEquality( + LinearArgument left, LinearArgument right, Variable iVar, boolean iValue) { LinearExprBuilder difference = LinearExpr.newBuilder(); difference.addTerm(left, 1); difference.addTerm(right, -1); return addEnforcedLinearConstraint(difference, 0.0, 0.0, iVar, iValue); } - /** Adds {@code expr <= value}. */ - public EnforcedLinearConstraint addEnforcedLessOrEqual(LinearArgument expr, double value, Variable iVar, boolean iValue) { + /** Adds {@code ivar == iValue => expr <= value}. */ + public EnforcedLinearConstraint addEnforcedLessOrEqual( + LinearArgument expr, double value, Variable iVar, boolean iValue) { return addEnforcedLinearConstraint(expr, Double.NEGATIVE_INFINITY, value, iVar, iValue); } - /** Adds {@code left <= right}. */ - public EnforcedLinearConstraint addEnforcedLessOrEqual(LinearArgument left, LinearArgument right, Variable iVar, boolean iValue) { + /** Adds {@code ivar == iValue => left <= right}. */ + public EnforcedLinearConstraint addEnforcedLessOrEqual( + LinearArgument left, LinearArgument right, Variable iVar, boolean iValue) { LinearExprBuilder difference = LinearExpr.newBuilder(); difference.addTerm(left, 1); difference.addTerm(right, -1); return addEnforcedLinearConstraint(difference, Double.NEGATIVE_INFINITY, 0.0, iVar, iValue); } - /** Adds {@code expr >= value}. */ - public EnforcedLinearConstraint addEnforcedGreaterOrEqual(LinearArgument expr, double value, Variable iVar, boolean iValue) { + /** Adds {@code ivar == iValue => expr >= value}. */ + public EnforcedLinearConstraint addEnforcedGreaterOrEqual( + LinearArgument expr, double value, Variable iVar, boolean iValue) { return addEnforcedLinearConstraint(expr, value, Double.POSITIVE_INFINITY, iVar, iValue); } - /** Adds {@code left >= right}. */ - public EnforcedLinearConstraint addEnforcedGreaterOrEqual(LinearArgument left, LinearArgument right, Variable iVar, boolean iValue) { + /** Adds {@code ivar == iValue => left >= right}. */ + public EnforcedLinearConstraint addEnforcedGreaterOrEqual( + LinearArgument left, LinearArgument right, Variable iVar, boolean iValue) { LinearExprBuilder difference = LinearExpr.newBuilder(); difference.addTerm(left, 1); difference.addTerm(right, -1); @@ -272,7 +279,10 @@ public final class ModelBuilder { helper.clearHints(); } - /** Adds var == value as a hint to the model. Note that variables must not appear more than once in the list of hints. */ + /** + * Adds var == value as a hint to the model. Note that variables must not appear more than once in + * the list of hints. + */ void addHint(Variable v, double value) { helper.addHint(v.getIndex(), value); } diff --git a/ortools/linear_solver/csharp/ModelSolver.cs b/ortools/linear_solver/csharp/ModelSolver.cs index 4adf539927..fef5647921 100644 --- a/ortools/linear_solver/csharp/ModelSolver.cs +++ b/ortools/linear_solver/csharp/ModelSolver.cs @@ -133,8 +133,7 @@ public class Solver get { if (!helper_.HasSolution()) { - throw new SolverException("Solver.ObjectiveValue", - "Solve() was not called or no solution was found"); + throw new SolverException("Solver.ObjectiveValue", "Solve() was not called or no solution was found"); } return helper_.ObjectiveValue(); } @@ -150,7 +149,7 @@ public class Solver if (!helper_.HasSolution()) { throw new SolverException("Solver.BestObjectiveBound", - "Solve() was not called or no solution was found"); + "Solve() was not called or no solution was found"); } return helper_.BestObjectiveBound(); } @@ -176,8 +175,7 @@ public class Solver { if (!helper_.HasSolution()) { - throw new SolverException("Solver.ReducedCost())", - "Solve() was not called or no solution was found"); + throw new SolverException("Solver.ReducedCost())", "Solve() was not called or no solution was found"); } return helper_.ReducedCost(var.Index); } @@ -190,8 +188,7 @@ public class Solver { if (!helper_.HasSolution()) { - throw new SolverException("Solver.DualValue())", - "Solve() was not called or no solution was found"); + throw new SolverException("Solver.DualValue())", "Solve() was not called or no solution was found"); } return helper_.DualValue(ct.Index); } @@ -204,8 +201,7 @@ public class Solver { if (!helper_.HasSolution()) { - throw new SolverException("Solver.Activity())", - "Solve() was not called or no solution was found"); + throw new SolverException("Solver.Activity())", "Solve() was not called or no solution was found"); } return helper_.Activity(ct.Index); } diff --git a/ortools/linear_solver/java/BUILD.bazel b/ortools/linear_solver/java/BUILD.bazel index b8fffb1ce5..7a470584a2 100644 --- a/ortools/linear_solver/java/BUILD.bazel +++ b/ortools/linear_solver/java/BUILD.bazel @@ -13,9 +13,9 @@ # Description: java wrapping of the C++ code at ../ -load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") -load("@rules_jvm_external//:defs.bzl", "artifact") load("@contrib_rules_jvm//java:defs.bzl", "java_junit5_test") +load("@rules_jvm_external//:defs.bzl", "artifact") +load("//bazel:swig_java.bzl", "ortools_java_wrap_cc") ortools_java_wrap_cc( name = "modelbuilder", diff --git a/ortools/linear_solver/python/model_builder.py b/ortools/linear_solver/python/model_builder.py index 61744597af..f265f5fda7 100644 --- a/ortools/linear_solver/python/model_builder.py +++ b/ortools/linear_solver/python/model_builder.py @@ -10,6 +10,7 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. + """Methods for building and solving model_builder models. The following two sections describe the main @@ -45,6 +46,7 @@ from ortools.linear_solver import linear_solver_pb2 from ortools.linear_solver.python import model_builder_helper as mbh from ortools.linear_solver.python import model_builder_numbers as mbn + # Custom types. NumberT = Union[int, float, numbers.Real, np.number] IntegerT = Union[int, numbers.Integral, np.integer] @@ -93,10 +95,8 @@ class LinearExpr(metaclass=abc.ABCMeta): @classmethod def sum( # pytype: disable=annotation-type-mismatch # numpy-scalars - cls, - expressions: Sequence[LinearExprT], - *, - constant: NumberT = 0.0) -> LinearExprT: + cls, expressions: Sequence[LinearExprT], *, constant: NumberT = 0.0 + ) -> LinearExprT: """Creates `sum(expressions) + constant`. It can perform simple simplifications and returns different objects, @@ -115,9 +115,9 @@ class LinearExpr(metaclass=abc.ABCMeta): if len(expressions) == 1 and mbn.is_zero(checked_constant): return expressions[0] - return LinearExpr.weighted_sum(expressions, - np.ones(len(expressions)), - constant=checked_constant) + return LinearExpr.weighted_sum( + expressions, np.ones(len(expressions)), constant=checked_constant + ) @classmethod def weighted_sum( # pytype: disable=annotation-type-mismatch # numpy-scalars @@ -143,13 +143,14 @@ class LinearExpr(metaclass=abc.ABCMeta): if len(expressions) != len(coefficients): raise ValueError( "LinearExpr.weighted_sum: expressions and coefficients have" - " different lengths") + " different lengths" + ) checked_constant: np.double = mbn.assert_is_a_number(constant) if not expressions: return checked_constant - return _sum_as_flat_linear_expression(to_process=list( - zip(expressions, coefficients)), - offset=checked_constant) + return _sum_as_flat_linear_expression( + to_process=list(zip(expressions, coefficients)), offset=checked_constant + ) @classmethod def term( # pytype: disable=annotation-type-mismatch # numpy-scalars @@ -179,14 +180,12 @@ class LinearExpr(metaclass=abc.ABCMeta): if mbn.is_one(checked_coefficient) and mbn.is_zero(checked_constant): return expression if mbn.is_a_number(expression): - return np.double( - expression) * checked_coefficient + checked_constant + return np.double(expression) * checked_coefficient + checked_constant if isinstance(expression, LinearExpr): - return _as_flat_linear_expression(expression * - checked_coefficient + - checked_constant) - raise TypeError( - f"Unknown expression {expression!r} of type {type(expression)}") + return _as_flat_linear_expression( + expression * checked_coefficient + checked_constant + ) + raise TypeError(f"Unknown expression {expression!r} of type {type(expression)}") def __hash__(self): return object.__hash__(self) @@ -216,17 +215,20 @@ class LinearExpr(metaclass=abc.ABCMeta): return _Product(self, -1) def __bool__(self): - raise NotImplementedError( - f"Cannot use a LinearExpr {self} as a Boolean value") + raise NotImplementedError(f"Cannot use a LinearExpr {self} as a Boolean value") def __eq__(self, arg: LinearExprT) -> "BoundedLinearExpression": return BoundedLinearExpression(self - arg, 0, 0) def __ge__(self, arg: LinearExprT) -> "BoundedLinearExpression": - return BoundedLinearExpression(self - arg, 0, math.inf) # pytype: disable=wrong-arg-types # numpy-scalars + return BoundedLinearExpression( + self - arg, 0, math.inf + ) # pytype: disable=wrong-arg-types # numpy-scalars def __le__(self, arg: LinearExprT) -> "BoundedLinearExpression": - return BoundedLinearExpression(self - arg, -math.inf, 0) # pytype: disable=wrong-arg-types # numpy-scalars + return BoundedLinearExpression( + self - arg, -math.inf, 0 + ) # pytype: disable=wrong-arg-types # numpy-scalars class Variable(LinearExpr): @@ -353,7 +355,9 @@ class Variable(LinearExpr): return False if isinstance(arg, Variable): return VarEqVar(self, arg) - return BoundedLinearExpression(self - arg, 0.0, 0.0) # pytype: disable=wrong-arg-types # numpy-scalars + return BoundedLinearExpression( + self - arg, 0.0, 0.0 + ) # pytype: disable=wrong-arg-types # numpy-scalars def __hash__(self): return hash((self.__helper, self.__index)) @@ -370,8 +374,9 @@ class _BoundedLinearExpr(metaclass=abc.ABCMeta): """ @abc.abstractmethod - def _add_linear_constraint(self, helper: mbh.ModelBuilderHelper, - name: str) -> "LinearConstraint": + def _add_linear_constraint( + self, helper: mbh.ModelBuilderHelper, name: str + ) -> "LinearConstraint": """Creates a new linear constraint in the helper. Args: @@ -382,6 +387,27 @@ class _BoundedLinearExpr(metaclass=abc.ABCMeta): LinearConstraint: A reference to the linear constraint in the helper. """ + @abc.abstractmethod + def _add_enforced_linear_constraint( + self, + helper: mbh.ModelBuilderHelper, + var: Variable, + value: bool, + name: str, + ) -> "EnforcedLinearConstraint": + """Creates a new enforced linear constraint in the helper. + + Args: + helper (mbh.ModelBuilderHelper): The helper to create the constraint. + var (Variable): The indicator variable of the constraint. + value (bool): The indicator value of the constraint. + name (str): The name of the linear constraint. + + Returns: + Enforced LinearConstraint: A reference to the linear constraint in the + helper. + """ + def _add_linear_constraint_to_helper( bounded_expr: Union[bool, _BoundedLinearExpr], @@ -425,8 +451,8 @@ def _add_linear_constraint_to_helper( def _add_enforced_linear_constraint_to_helper( bounded_expr: Union[bool, _BoundedLinearExpr], - indicator_variable: Variable, - indicator_value: bool, + var: Variable, + value: bool, helper: mbh.ModelBuilderHelper, name: Optional[str], ): @@ -437,21 +463,22 @@ def _add_enforced_linear_constraint_to_helper( Args: bounded_expr: The bounded expression used to create the constraint. - indicator_variable: the variable used in the indicator - indicator_value: the value used in the indicator + var: the variable used in the indicator + value: the value used in the indicator helper: The helper to create the constraint. name: The name of the constraint to be created. Returns: - EnforcedLinearConstraint: a constraint in the helper corresponding to the input. + EnforcedLinearConstraint: a constraint in the helper corresponding to the + input. Raises: TypeError: If constraint is an invalid type. """ if isinstance(bounded_expr, bool): c = EnforcedLinearConstraint(helper) - c.indicator_variable = indicator_variable - c.indicator_value = indicator_value + c.indicator_variable = var + c.indicator_value = value if name is not None: helper.set_enforced_constraint_name(c.index, name) if bounded_expr: @@ -465,8 +492,7 @@ def _add_enforced_linear_constraint_to_helper( return c if isinstance(bounded_expr, _BoundedLinearExpr): # pylint: disable=protected-access - return bounded_expr._add_enforced_linear_constraint( - helper, indicator_variable, indicator_value, name) + return bounded_expr._add_enforced_linear_constraint(helper, var, value, name) raise TypeError("invalid type={}".format(type(bounded_expr))) @@ -488,8 +514,9 @@ class VarEqVar(_BoundedLinearExpr): def __bool__(self) -> bool: return hash(self.left) == hash(self.right) - def _add_linear_constraint(self, helper: mbh.ModelBuilderHelper, - name: str) -> "LinearConstraint": + def _add_linear_constraint( + self, helper: mbh.ModelBuilderHelper, name: str + ) -> "LinearConstraint": c = LinearConstraint(helper) helper.set_constraint_lower_bound(c.index, 0.0) helper.set_constraint_upper_bound(c.index, 0.0) @@ -501,12 +528,16 @@ class VarEqVar(_BoundedLinearExpr): return c def _add_enforced_linear_constraint( - self, helper: mbh.ModelBuilderHelper, - indicator_variable: 'Variable', indicator_value: bool, - name: str) -> "EnforcedLinearConstraint": + self, + helper: mbh.ModelBuilderHelper, + var: "Variable", + value: bool, + name: str, + ) -> "EnforcedLinearConstraint": + """Adds an enforced linear constraint to the model.""" c = EnforcedLinearConstraint(helper) - c.indicator_variable = indicator_variable - c.indicator_value = indicator_value + c.indicator_variable = var + c.indicator_value = value helper.set_enforced_constraint_lower_bound(c.index, 0.0) helper.set_enforced_constraint_upper_bound(c.index, 0.0) # pylint: disable=protected-access @@ -536,8 +567,9 @@ class BoundedLinearExpression(_BoundedLinearExpr): if self.__lb == self.__ub: return str(self.__expr) + " == " + str(self.__lb) else: - return (str(self.__lb) + " <= " + str(self.__expr) + " <= " + - str(self.__ub)) + return ( + str(self.__lb) + " <= " + str(self.__expr) + " <= " + str(self.__ub) + ) elif self.__lb > -math.inf: return str(self.__expr) + " >= " + str(self.__lb) elif self.__ub < math.inf: @@ -562,40 +594,47 @@ class BoundedLinearExpression(_BoundedLinearExpr): def __bool__(self) -> bool: raise NotImplementedError( - f"Cannot use a BoundedLinearExpression {self} as a Boolean value") + f"Cannot use a BoundedLinearExpression {self} as a Boolean value" + ) - def _add_linear_constraint(self, helper: mbh.ModelBuilderHelper, - name: Optional[str]) -> "LinearConstraint": + def _add_linear_constraint( + self, helper: mbh.ModelBuilderHelper, name: Optional[str] + ) -> "LinearConstraint": c = LinearConstraint(helper) flat_expr = _as_flat_linear_expression(self.__expr) # pylint: disable=protected-access - helper.add_terms_to_constraint(c.index, flat_expr._variable_indices, - flat_expr._coefficients) - helper.set_constraint_lower_bound(c.index, - self.__lb - flat_expr._offset) - helper.set_constraint_upper_bound(c.index, - self.__ub - flat_expr._offset) + helper.add_terms_to_constraint( + c.index, flat_expr._variable_indices, flat_expr._coefficients + ) + helper.set_constraint_lower_bound(c.index, self.__lb - flat_expr._offset) + helper.set_constraint_upper_bound(c.index, self.__ub - flat_expr._offset) # pylint: enable=protected-access if name is not None: helper.set_constraint_name(c.index, name) return c def _add_enforced_linear_constraint( - self, helper: mbh.ModelBuilderHelper, - indicator_variable: 'Variable', indicator_value: bool, - name: Optional[str]) -> "EnforcedLinearConstraint": + self, + helper: mbh.ModelBuilderHelper, + var: "Variable", + value: bool, + name: Optional[str], + ) -> "EnforcedLinearConstraint": + """Adds an enforced linear constraint to the model.""" c = EnforcedLinearConstraint(helper) - c.indicator_variable = indicator_variable - c.indicator_value = indicator_value + c.indicator_variable = var + c.indicator_value = value flat_expr = _as_flat_linear_expression(self.__expr) # pylint: disable=protected-access - helper.add_terms_to_enforced_constraint(c.index, - flat_expr._variable_indices, - flat_expr._coefficients) + helper.add_terms_to_enforced_constraint( + c.index, flat_expr._variable_indices, flat_expr._coefficients + ) helper.set_enforced_constraint_lower_bound( - c.index, self.__lb - flat_expr._offset) + c.index, self.__lb - flat_expr._offset + ) helper.set_enforced_constraint_upper_bound( - c.index, self.__ub - flat_expr._offset) + c.index, self.__ub - flat_expr._offset + ) # pylint: enable=protected-access if name is not None: helper.set_enforced_constraint_name(c.index, name) @@ -612,9 +651,9 @@ class LinearConstraint: linear_constraint = model.add(x + 2 * y == 5) """ - def __init__(self, - helper: mbh.ModelBuilderHelper, - index: Optional[IntegerT] = None): + def __init__( + self, helper: mbh.ModelBuilderHelper, index: Optional[IntegerT] = None + ): if index is None: self.__index = helper.add_linear_constraint() else: @@ -682,8 +721,7 @@ class LinearConstraint: raise ValueError( f"Constraint {self.index} is always false and cannot be modified" ) - self.__helper.set_constraint_coefficient(self.__index, var.index, - coeff) + self.__helper.set_constraint_coefficient(self.__index, var.index, coeff) def add_term(self, var: Variable, coeff: NumberT) -> None: """Adds var * coeff to the constraint.""" @@ -691,8 +729,7 @@ class LinearConstraint: raise ValueError( f"Constraint {self.index} is always false and cannot be modified" ) - self.__helper.safe_add_term_to_constraint(self.__index, var.index, - coeff) + self.__helper.safe_add_term_to_constraint(self.__index, var.index, coeff) def clear_terms(self) -> None: """Clear all terms of the constraint.""" @@ -710,15 +747,16 @@ class EnforcedLinearConstraint: enforced_linear_constraint = model.add_enforced(x + 2 * y == 5, z, False) """ - def __init__(self, - helper: mbh.ModelBuilderHelper, - index: Optional[IntegerT] = None): + def __init__( + self, helper: mbh.ModelBuilderHelper, index: Optional[IntegerT] = None + ): if index is None: self.__index = helper.add_enforced_linear_constraint() else: if not helper.is_enforced_linear_constraint(index): raise ValueError( - f"the given index {index} does not refer to an enforced linear constraint" + f"the given index {index} does not refer to an enforced linear" + " constraint" ) self.__index = index @@ -751,15 +789,17 @@ class EnforcedLinearConstraint: self.__helper.set_enforced_constraint_upper_bound(self.__index, bound) @property - def indicator_variable(self) -> 'Variable': - enforcement_var_index = self.__helper.enforced_constraint_indicator_variable_index( - self.__index) + def indicator_variable(self) -> "Variable": + enforcement_var_index = ( + self.__helper.enforced_constraint_indicator_variable_index(self.__index) + ) return Variable(self.__helper, enforcement_var_index, None, None, None) @indicator_variable.setter - def indicator_variable(self, var: 'Variable') -> None: + def indicator_variable(self, var: "Variable") -> None: self.__helper.set_enforced_constraint_indicator_variable_index( - self.__index, var.index) + self.__index, var.index + ) @property def indicator_value(self) -> bool: @@ -767,8 +807,7 @@ class EnforcedLinearConstraint: @indicator_value.setter def indicator_value(self, value: bool) -> None: - self.__helper.set_enforced_constraint_indicator_value( - self.__index, value) + self.__helper.set_enforced_constraint_indicator_value(self.__index, value) @property def name(self) -> str: @@ -798,7 +837,8 @@ class EnforcedLinearConstraint: f" var_indices={self.helper.enforced_constraint_var_indices(self.index)}," f" coefficients={self.helper.enforced_constraint_coefficients(self.index)}," f" indicator_variable={self.indicator_variable}" - f" indicator_value={self.indicator_value})") + f" indicator_value={self.indicator_value})" + ) def set_coefficient(self, var: Variable, coeff: NumberT) -> None: """Sets the coefficient of the variable in the constraint.""" @@ -807,7 +847,8 @@ class EnforcedLinearConstraint: f"Constraint {self.index} is always false and cannot be modified" ) self.__helper.set_enforced_constraint_coefficient( - self.__index, var.index, coeff) + self.__index, var.index, coeff + ) def add_term(self, var: Variable, coeff: NumberT) -> None: """Adds var * coeff to the constraint.""" @@ -816,7 +857,8 @@ class EnforcedLinearConstraint: f"Constraint {self.index} is always false and cannot be modified" ) self.__helper.safe_add_term_to_enforced_constraint( - self.__index, var.index, coeff) + self.__index, var.index, coeff + ) def clear_terms(self) -> None: """Clear all terms of the constraint.""" @@ -842,8 +884,7 @@ class ModelBuilder: return clone @typing.overload - def _get_linear_constraints(self, - constraints: Optional[pd.Index]) -> pd.Index: + def _get_linear_constraints(self, constraints: Optional[pd.Index]) -> pd.Index: ... @typing.overload @@ -851,8 +892,8 @@ class ModelBuilder: ... def _get_linear_constraints( - self, - constraints: Optional[_IndexOrSeries] = None) -> _IndexOrSeries: + self, constraints: Optional[_IndexOrSeries] = None + ) -> _IndexOrSeries: if constraints is None: return self.get_linear_constraints() return constraints @@ -866,8 +907,8 @@ class ModelBuilder: ... def _get_variables( - self, - variables: Optional[_IndexOrSeries] = None) -> _IndexOrSeries: + self, variables: Optional[_IndexOrSeries] = None + ) -> _IndexOrSeries: if variables is None: return self.get_variables() return variables @@ -875,15 +916,13 @@ class ModelBuilder: def get_linear_constraints(self) -> pd.Index: """Gets all linear constraints in the model.""" return pd.Index( - [ - self.linear_constraint_from_index(i) - for i in range(self.num_constraints) - ], + [self.linear_constraint_from_index(i) for i in range(self.num_constraints)], name="linear_constraint", ) def get_linear_constraint_expressions( - self, constraints: Optional[_IndexOrSeries] = None) -> pd.Series: + self, constraints: Optional[_IndexOrSeries] = None + ) -> pd.Series: """Gets the expressions of all linear constraints in the set. If `constraints` is a `pd.Index`, then the output will be indexed by the @@ -903,16 +942,20 @@ class ModelBuilder: # pylint: disable=g-long-lambda func=lambda c: _as_flat_linear_expression( # pylint: disable=g-complex-comprehension - sum(coeff * Variable(self.__helper, var_id, None, None, None) + sum( + coeff * Variable(self.__helper, var_id, None, None, None) for var_id, coeff in zip( c.helper.constraint_var_indices(c.index), c.helper.constraint_coefficients(c.index), - ))), + ) + ) + ), values=self._get_linear_constraints(constraints), ) def get_linear_constraint_lower_bounds( - self, constraints: Optional[_IndexOrSeries] = None) -> pd.Series: + self, constraints: Optional[_IndexOrSeries] = None + ) -> pd.Series: """Gets the lower bounds of all linear constraints in the set. If `constraints` is a `pd.Index`, then the output will be indexed by the @@ -934,7 +977,8 @@ class ModelBuilder: ) def get_linear_constraint_upper_bounds( - self, constraints: Optional[_IndexOrSeries] = None) -> pd.Series: + self, constraints: Optional[_IndexOrSeries] = None + ) -> pd.Series: """Gets the upper bounds of all linear constraints in the set. If `constraints` is a `pd.Index`, then the output will be indexed by the @@ -961,9 +1005,9 @@ class ModelBuilder: name="variable", ) - def get_variable_lower_bounds(self, - variables: Optional[_IndexOrSeries] = None - ) -> pd.Series: + def get_variable_lower_bounds( + self, variables: Optional[_IndexOrSeries] = None + ) -> pd.Series: """Gets the lower bounds of all variables in the set. If `variables` is a `pd.Index`, then the output will be indexed by the @@ -984,9 +1028,9 @@ class ModelBuilder: values=self._get_variables(variables), ) - def get_variable_upper_bounds(self, - variables: Optional[_IndexOrSeries] = None - ) -> pd.Series: + def get_variable_upper_bounds( + self, variables: Optional[_IndexOrSeries] = None + ) -> pd.Series: """Gets the upper bounds of all variables in the set. Args: @@ -1004,8 +1048,9 @@ class ModelBuilder: # Integer variable. - def new_var(self, lb: NumberT, ub: NumberT, is_integer: bool, - name: Optional[str]) -> Variable: + def new_var( + self, lb: NumberT, ub: NumberT, is_integer: bool, name: Optional[str] + ) -> Variable: """Create an integer variable with domain [lb, ub]. Args: @@ -1020,10 +1065,9 @@ class ModelBuilder: return Variable(self.__helper, lb, ub, is_integer, name) - def new_int_var(self, - lb: NumberT, - ub: NumberT, - name: Optional[str] = None) -> Variable: + def new_int_var( + self, lb: NumberT, ub: NumberT, name: Optional[str] = None + ) -> Variable: """Create an integer variable with domain [lb, ub]. Args: @@ -1037,10 +1081,9 @@ class ModelBuilder: return self.new_var(lb, ub, True, name) - def new_num_var(self, - lb: NumberT, - ub: NumberT, - name: Optional[str] = None) -> Variable: + def new_num_var( + self, lb: NumberT, ub: NumberT, name: Optional[str] = None + ) -> Variable: """Create an integer variable with domain [lb, ub]. Args: @@ -1056,7 +1099,9 @@ class ModelBuilder: def new_bool_var(self, name: Optional[str] = None) -> Variable: """Creates a 0-1 variable with the given name.""" - return self.new_var(0, 1, True, name) # pytype: disable=wrong-arg-types # numpy-scalars + return self.new_var( + 0, 1, True, name + ) # pytype: disable=wrong-arg-types # numpy-scalars def new_constant(self, value: NumberT) -> Variable: """Declares a constant variable.""" @@ -1099,28 +1144,34 @@ class ModelBuilder: raise TypeError("Non-index object is used as index") if not name.isidentifier(): raise ValueError("name={} is not a valid identifier".format(name)) - if (mbn.is_a_number(lower_bounds) and mbn.is_a_number(upper_bounds) - and lower_bounds > upper_bounds): + if ( + mbn.is_a_number(lower_bounds) + and mbn.is_a_number(upper_bounds) + and lower_bounds > upper_bounds + ): raise ValueError( - "lower_bound={} is greater than upper_bound={} for variable set={}" - .format(lower_bounds, upper_bounds, name)) - if (isinstance(is_integral, bool) and is_integral - and mbn.is_a_number(lower_bounds) - and mbn.is_a_number(upper_bounds) - and math.isfinite(lower_bounds) and math.isfinite(upper_bounds) - and math.ceil(lower_bounds) > math.floor(upper_bounds)): - raise ValueError("ceil(lower_bound={})={}".format( - lower_bounds, math.ceil(lower_bounds)) + - " is greater than floor(" + - "upper_bound={})={}".format( - upper_bounds, math.floor(upper_bounds)) + - " for variable set={}".format(name)) - lower_bounds = _convert_to_series_and_validate_index( - lower_bounds, index) - upper_bounds = _convert_to_series_and_validate_index( - upper_bounds, index) - is_integrals = _convert_to_series_and_validate_index( - is_integral, index) + "lower_bound={} is greater than upper_bound={} for variable set={}".format( + lower_bounds, upper_bounds, name + ) + ) + if ( + isinstance(is_integral, bool) + and is_integral + and mbn.is_a_number(lower_bounds) + and mbn.is_a_number(upper_bounds) + and math.isfinite(lower_bounds) + and math.isfinite(upper_bounds) + and math.ceil(lower_bounds) > math.floor(upper_bounds) + ): + raise ValueError( + "ceil(lower_bound={})={}".format(lower_bounds, math.ceil(lower_bounds)) + + " is greater than floor(" + + "upper_bound={})={}".format(upper_bounds, math.floor(upper_bounds)) + + " for variable set={}".format(name) + ) + lower_bounds = _convert_to_series_and_validate_index(lower_bounds, index) + upper_bounds = _convert_to_series_and_validate_index(upper_bounds, index) + is_integrals = _convert_to_series_and_validate_index(is_integral, index) return pd.Series( index=index, data=[ @@ -1131,7 +1182,8 @@ class ModelBuilder: lb=lower_bounds[i], ub=upper_bounds[i], is_integral=is_integrals[i], - ) for i in index + ) + for i in index ], ) @@ -1164,8 +1216,7 @@ class ModelBuilder: ValueError: if the index of `lower_bound`, `upper_bound`, or `is_integer` does not match the input index. """ - return self.new_var_series(name, index, lower_bounds, upper_bounds, - False) + return self.new_var_series(name, index, lower_bounds, upper_bounds, False) def new_int_var_series( self, @@ -1196,8 +1247,7 @@ class ModelBuilder: ValueError: if the index of `lower_bound`, `upper_bound`, or `is_integer` does not match the input index. """ - return self.new_var_series(name, index, lower_bounds, upper_bounds, - True) + return self.new_var_series(name, index, lower_bounds, upper_bounds, True) def new_bool_var_series( self, @@ -1240,34 +1290,30 @@ class ModelBuilder: if name: self.__helper.set_constraint_name(ct.index, name) if mbn.is_a_number(linear_expr): - self.__helper.set_constraint_lower_bound(ct.index, - lb - linear_expr) - self.__helper.set_constraint_upper_bound(ct.index, - ub - linear_expr) + self.__helper.set_constraint_lower_bound(ct.index, lb - linear_expr) + self.__helper.set_constraint_upper_bound(ct.index, ub - linear_expr) elif isinstance(linear_expr, Variable): self.__helper.set_constraint_lower_bound(ct.index, lb) self.__helper.set_constraint_upper_bound(ct.index, ub) - self.__helper.add_term_to_constraint(ct.index, linear_expr.index, - 1.0) + self.__helper.add_term_to_constraint(ct.index, linear_expr.index, 1.0) elif isinstance(linear_expr, LinearExpr): flat_expr = _as_flat_linear_expression(linear_expr) # pylint: disable=protected-access - self.__helper.set_constraint_lower_bound(ct.index, - lb - flat_expr._offset) - self.__helper.set_constraint_upper_bound(ct.index, - ub - flat_expr._offset) - self.__helper.add_terms_to_constraint(ct.index, - flat_expr._variable_indices, - flat_expr._coefficients) + self.__helper.set_constraint_lower_bound(ct.index, lb - flat_expr._offset) + self.__helper.set_constraint_upper_bound(ct.index, ub - flat_expr._offset) + self.__helper.add_terms_to_constraint( + ct.index, flat_expr._variable_indices, flat_expr._coefficients + ) else: raise TypeError( f"Not supported: ModelBuilder.add_linear_constraint({linear_expr})" - f" with type {type(linear_expr)}") + f" with type {type(linear_expr)}" + ) return ct - def add(self, - ct: Union[ConstraintT, pd.Series], - name: Optional[str] = None) -> Union[LinearConstraint, pd.Series]: + def add( + self, ct: Union[ConstraintT, pd.Series], name: Optional[str] = None + ) -> Union[LinearConstraint, pd.Series]: """Adds a `BoundedLinearExpression` to the model. Args: @@ -1295,16 +1341,16 @@ class ModelBuilder: return pd.Series( index=ct.index, data=[ - _add_linear_constraint_to_helper(expr, self.__helper, - f"{name}[{i}]") + _add_linear_constraint_to_helper( + expr, self.__helper, f"{name}[{i}]" + ) for (i, expr) in zip(ct.index, ct) ], ) else: raise TypeError("Not supported: ModelBuilder.add(" + str(ct) + ")") - def linear_constraint_from_index(self, - index: IntegerT) -> LinearConstraint: + def linear_constraint_from_index(self, index: IntegerT) -> LinearConstraint: """Rebuilds a linear constraint object from the model and its index.""" return LinearConstraint(self.__helper, index) @@ -1313,7 +1359,7 @@ class ModelBuilder: def add_enforced_linear_constraint( # pytype: disable=annotation-type-mismatch # numpy-scalars self, linear_expr: LinearExprT, - ivar: 'Variable', + ivar: "Variable", ivalue: bool, lb: NumberT = -math.inf, ub: NumberT = math.inf, @@ -1326,44 +1372,41 @@ class ModelBuilder: if name: self.__helper.set_constraint_name(ct.index, name) if mbn.is_a_number(linear_expr): - self.__helper.set_constraint_lower_bound(ct.index, - lb - linear_expr) - self.__helper.set_constraint_upper_bound(ct.index, - ub - linear_expr) + self.__helper.set_constraint_lower_bound(ct.index, lb - linear_expr) + self.__helper.set_constraint_upper_bound(ct.index, ub - linear_expr) elif isinstance(linear_expr, Variable): self.__helper.set_constraint_lower_bound(ct.index, lb) self.__helper.set_constraint_upper_bound(ct.index, ub) - self.__helper.add_term_to_constraint(ct.index, linear_expr.index, - 1.0) + self.__helper.add_term_to_constraint(ct.index, linear_expr.index, 1.0) elif isinstance(linear_expr, LinearExpr): flat_expr = _as_flat_linear_expression(linear_expr) # pylint: disable=protected-access - self.__helper.set_constraint_lower_bound(ct.index, - lb - flat_expr._offset) - self.__helper.set_constraint_upper_bound(ct.index, - ub - flat_expr._offset) - self.__helper.add_terms_to_constraint(ct.index, - flat_expr._variable_indices, - flat_expr._coefficients) + self.__helper.set_constraint_lower_bound(ct.index, lb - flat_expr._offset) + self.__helper.set_constraint_upper_bound(ct.index, ub - flat_expr._offset) + self.__helper.add_terms_to_constraint( + ct.index, flat_expr._variable_indices, flat_expr._coefficients + ) else: raise TypeError( - f"Not supported: ModelBuilder.add_enforced_linear_constraint({linear_expr})" - f" with type {type(linear_expr)}") + "Not supported:" + f" ModelBuilder.add_enforced_linear_constraint({linear_expr}) with" + f" type {type(linear_expr)}" + ) return ct def add_enforced( self, ct: Union[ConstraintT, pd.Series], - ivar: Union['Variable', pd.Series], - ivalue: Union[bool, pd.Series], - name: Optional[str] = None + var: Union["Variable", pd.Series], + value: Union[bool, pd.Series], + name: Optional[str] = None, ) -> Union[EnforcedLinearConstraint, pd.Series]: """Adds a `ivar == ivalue => BoundedLinearExpression` to the model. Args: ct: A [`BoundedLinearExpression`](#boundedlinearexpression). - ivar: The indicator variable - ivalue: the indicator value + var: The indicator variable + value: the indicator value name: An optional name. Returns: @@ -1372,38 +1415,49 @@ class ModelBuilder: Note that a special treatment is done when the argument does not contain any variable, and thus evaluates to True or False. - model.add_enforced(True, ivar, ivalue) will create a constraint 0 <= empty sum <= 0 + model.add_enforced(True, ivar, ivalue) will create a constraint 0 <= empty + sum <= 0 - model.add_enforced(False, ivar, ivalue) will create a constraint inf <= empty sum <= -inf + model.add_enforced(False, var, value) will create a constraint inf <= + empty sum <= -inf you can check the if a constraint is always false (lb=inf, ub=-inf) by calling EnforcedLinearConstraint.is_always_false() """ if isinstance(ct, _BoundedLinearExpr): - return ct._add_enforced_linear_constraint(self.__helper, ivar, - ivalue, name) - elif isinstance(ct, bool): + return ct._add_enforced_linear_constraint(self.__helper, var, value, name) + elif ( + isinstance(ct, bool) + and isinstance(var, Variable) + and isinstance(value, bool) + ): + typed_var: Variable = var + typed_value: bool = value return _add_enforced_linear_constraint_to_helper( - ct, self.__helper, ivar, ivalue, name) + ct, self.__helper, typed_var, typed_value, name + ) elif isinstance(ct, pd.Series): - ivar_series = _convert_to_var_series_and_validate_index( - ivar, ct.index) - ivalue_series = _convert_to_series_and_validate_index( - ivalue, ct.index) + ivar_series = _convert_to_var_series_and_validate_index(var, ct.index) + ivalue_series = _convert_to_series_and_validate_index(value, ct.index) return pd.Series( index=ct.index, data=[ _add_enforced_linear_constraint_to_helper( - expr, self.__helper, ivar_series[i], ivalue_series[i], - f"{name}[{i}]") for (i, expr) in zip(ct.index, ct) + expr, + self.__helper, + ivar_series[i], + ivalue_series[i], + f"{name}[{i}]", + ) + for (i, expr) in zip(ct.index, ct) ], ) else: - raise TypeError("Not supported: ModelBuilder.add_enforced(" + - str(ct) + ")") + raise TypeError("Not supported: ModelBuilder.add_enforced(" + str(ct) + ")") def enforced_linear_constraint_from_index( - self, index: IntegerT) -> LinearConstraint: + self, index: IntegerT + ) -> EnforcedLinearConstraint: """Rebuilds an enforced linear constraint object from the model and its index.""" return EnforcedLinearConstraint(self.__helper, index) @@ -1428,8 +1482,9 @@ class ModelBuilder: flat_expr = _as_flat_linear_expression(linear_expr) # pylint: disable=protected-access self.helper.set_objective_offset(flat_expr._offset) - self.helper.set_objective_coefficients(flat_expr._variable_indices, - flat_expr._coefficients) + self.helper.set_objective_coefficients( + flat_expr._variable_indices, flat_expr._coefficients + ) else: raise TypeError( f"Not supported: ModelBuilder.minimize/maximize({linear_expr})" @@ -1447,11 +1502,13 @@ class ModelBuilder: def objective_expression(self) -> "_LinearExpression": """Returns the expression to optimize.""" return _as_flat_linear_expression( - sum(variable * - self.__helper.var_objective_coefficient(variable.index) - for variable in self.get_variables() if self.__helper. - var_objective_coefficient(variable.index) != 0.0) + - self.__helper.objective_offset()) + sum( + variable * self.__helper.var_objective_coefficient(variable.index) + for variable in self.get_variables() + if self.__helper.var_objective_coefficient(variable.index) != 0.0 + ) + + self.__helper.objective_offset() + ) # Hints. def clear_hints(self): @@ -1461,6 +1518,10 @@ class ModelBuilder: def add_hint(self, var: Variable, value: NumberT): """Add var == value as a hint to the model. + args: + var: The variable of the hint + value: The value of the hint + Note that variables must not appear more than once in the list of hints. """ self.__helper.add_hint(var.index, value) @@ -1531,8 +1592,7 @@ class ModelSolver: """ def __init__(self, solver_name: str): - self.__solve_helper: mbh.ModelSolverHelper = mbh.ModelSolverHelper( - solver_name) + self.__solve_helper: mbh.ModelSolverHelper = mbh.ModelSolverHelper(solver_name) self.log_callback: Optional[Callable[[str], None]] = None def solver_is_supported(self) -> bool: @@ -1581,8 +1641,7 @@ class ModelSolver: flat_expr._offset, ) else: - raise TypeError( - f"Unknown expression {expr!r} of type {type(expr)}") + raise TypeError(f"Unknown expression {expr!r} of type {type(expr)}") def values(self, variables: _IndexOrSeries) -> pd.Series: """Returns the values of the input variables. @@ -1644,7 +1703,7 @@ class ModelSolver: Args: constraints (Union[pd.Index, pd.Series]): The set of constraints from - which to get the dual values. + which to get the dual values. Returns: pd.Series: The dual_values of all constraints in the set. @@ -1767,8 +1826,9 @@ class _LinearExpression(LinearExpr): return "".join(result) -def _sum_as_flat_linear_expression(to_process: List[Tuple[LinearExprT, float]], - offset: float = 0.0) -> _LinearExpression: +def _sum_as_flat_linear_expression( + to_process: List[Tuple[LinearExprT, float]], offset: float = 0.0 +) -> _LinearExpression: """Creates a _LinearExpression as the sum of terms.""" indices = [] coeffs = [] @@ -1795,16 +1855,17 @@ def _sum_as_flat_linear_expression(to_process: List[Tuple[LinearExprT, float]], if helper is None: helper = expr._helper else: - raise TypeError("Unrecognized linear expression: " + str(expr) + - f" {type(expr)}") + raise TypeError( + "Unrecognized linear expression: " + str(expr) + f" {type(expr)}" + ) if helper is not None: all_indices: npt.NDArray[np.int32] = np.concatenate(indices, axis=0) all_coeffs: npt.NDArray[np.double] = np.concatenate(coeffs, axis=0) sorted_indices, sorted_coefficients = helper.sort_and_regroup_terms( - all_indices, all_coeffs) - return _LinearExpression(sorted_indices, sorted_coefficients, offset, - helper) + all_indices, all_coeffs + ) + return _LinearExpression(sorted_indices, sorted_coefficients, offset, helper) else: assert not indices assert not coeffs @@ -1820,8 +1881,7 @@ def _as_flat_linear_expression(base_expr: LinearExprT) -> _LinearExpression: """Converts floats, ints and Linear objects to a LinearExpression.""" if isinstance(base_expr, _LinearExpression): return base_expr - return _sum_as_flat_linear_expression(to_process=[(base_expr, 1.0)], - offset=0.0) + return _sum_as_flat_linear_expression(to_process=[(base_expr, 1.0)], offset=0.0) @dataclasses.dataclass(repr=False, eq=False, frozen=True) @@ -1883,9 +1943,9 @@ def _attribute_series( ) -def _convert_to_series_and_validate_index(value_or_series: Union[bool, NumberT, - pd.Series], - index: pd.Index) -> pd.Series: +def _convert_to_series_and_validate_index( + value_or_series: Union[bool, NumberT, pd.Series], index: pd.Index +) -> pd.Series: """Returns a pd.Series of the given index with the corresponding values. Args: @@ -1911,9 +1971,9 @@ def _convert_to_series_and_validate_index(value_or_series: Union[bool, NumberT, return result -def _convert_to_var_series_and_validate_index(var_or_series: Union['Variable', - pd.Series], - index: pd.Index) -> pd.Series: +def _convert_to_var_series_and_validate_index( + var_or_series: Union["Variable", pd.Series], index: pd.Index +) -> pd.Series: """Returns a pd.Series of the given index with the corresponding values. Args: diff --git a/ortools/linear_solver/python/model_builder_helper.cc b/ortools/linear_solver/python/model_builder_helper.cc index bee377de44..eb58b900a3 100644 --- a/ortools/linear_solver/python/model_builder_helper.cc +++ b/ortools/linear_solver/python/model_builder_helper.cc @@ -335,16 +335,19 @@ PYBIND11_MODULE(model_builder_helper, m) { arg("ct_index")) .def("constraint_coefficients", &ModelBuilderHelper::ConstraintCoefficients, arg("ct_index")) - .def("add_enforced_linear_constraint", &ModelBuilderHelper::AddEnforcedLinearConstraint) - .def("is_enforced_linear_constraint", &ModelBuilderHelper::IsEnforcedConstraint) + .def("add_enforced_linear_constraint", + &ModelBuilderHelper::AddEnforcedLinearConstraint) + .def("is_enforced_linear_constraint", + &ModelBuilderHelper::IsEnforcedConstraint) .def("set_enforced_constraint_lower_bound", - &ModelBuilderHelper::SetEnforcedConstraintLowerBound, arg("ct_index"), - arg("lb")) + &ModelBuilderHelper::SetEnforcedConstraintLowerBound, + arg("ct_index"), arg("lb")) .def("set_enforced_constraint_upper_bound", - &ModelBuilderHelper::SetEnforcedConstraintUpperBound, arg("ct_index"), - arg("ub")) - .def("add_term_to_enforced_constraint", &ModelBuilderHelper::AddEnforcedConstraintTerm, - arg("ct_index"), arg("var_index"), arg("coeff")) + &ModelBuilderHelper::SetEnforcedConstraintUpperBound, + arg("ct_index"), arg("ub")) + .def("add_term_to_enforced_constraint", + &ModelBuilderHelper::AddEnforcedConstraintTerm, arg("ct_index"), + arg("var_index"), arg("coeff")) .def("add_terms_to_enforced_constraint", [](ModelBuilderHelper* helper, int ct_index, const std::vector& indices, @@ -357,25 +360,28 @@ PYBIND11_MODULE(model_builder_helper, m) { .def("safe_add_term_to_enforced_constraint", &ModelBuilderHelper::SafeAddEnforcedConstraintTerm, arg("ct_index"), arg("var_index"), arg("coeff")) - .def("set_enforced_constraint_name", &ModelBuilderHelper::SetEnforcedConstraintName, - arg("ct_index"), arg("name")) + .def("set_enforced_constraint_name", + &ModelBuilderHelper::SetEnforcedConstraintName, arg("ct_index"), + arg("name")) .def("set_enforced_constraint_coefficient", - &ModelBuilderHelper::SetEnforcedConstraintCoefficient, arg("ct_index"), - arg("var_index"), arg("coeff")) - .def("enforced_constraint_lower_bound", &ModelBuilderHelper::EnforcedConstraintLowerBound, - arg("ct_index")) - .def("enforced_constraint_upper_bound", &ModelBuilderHelper::EnforcedConstraintUpperBound, - arg("ct_index")) - .def("enforced_constraint_name", &ModelBuilderHelper::EnforcedConstraintName, - arg("ct_index")) - .def("enforced_constraint_var_indices", &ModelBuilderHelper::EnforcedConstraintVarIndices, - arg("ct_index")) + &ModelBuilderHelper::SetEnforcedConstraintCoefficient, + arg("ct_index"), arg("var_index"), arg("coeff")) + .def("enforced_constraint_lower_bound", + &ModelBuilderHelper::EnforcedConstraintLowerBound, arg("ct_index")) + .def("enforced_constraint_upper_bound", + &ModelBuilderHelper::EnforcedConstraintUpperBound, arg("ct_index")) + .def("enforced_constraint_name", + &ModelBuilderHelper::EnforcedConstraintName, arg("ct_index")) + .def("enforced_constraint_var_indices", + &ModelBuilderHelper::EnforcedConstraintVarIndices, arg("ct_index")) .def("enforced_constraint_coefficients", &ModelBuilderHelper::EnforcedConstraintCoefficients, arg("ct_index")) .def("set_enforced_constraint_indicator_variable_index", - &ModelBuilderHelper::SetEnforcedIndicatorVariableIndex, arg("ct_index"), arg("var_index")) + &ModelBuilderHelper::SetEnforcedIndicatorVariableIndex, + arg("ct_index"), arg("var_index")) .def("set_enforced_constraint_indicator_value", - &ModelBuilderHelper::SetEnforcedIndicatorValue, arg("ct_index"), arg("positive")) + &ModelBuilderHelper::SetEnforcedIndicatorValue, arg("ct_index"), + arg("positive")) .def("enforced_constraint_indicator_variable_index", &ModelBuilderHelper::EnforcedIndicatorVariableIndex, arg("ct_index")) .def("enforced_constraint_indicator_value", @@ -391,7 +397,8 @@ PYBIND11_MODULE(model_builder_helper, m) { arg("offset")) .def("objective_offset", &ModelBuilderHelper::ObjectiveOffset) .def("clear_hints", &ModelBuilderHelper::ClearHints) - .def("add_hint", &ModelBuilderHelper::AddHint, arg("var_index"), arg("var_value")) + .def("add_hint", &ModelBuilderHelper::AddHint, arg("var_index"), + arg("var_value")) .def("sort_and_regroup_terms", [](ModelBuilderHelper* helper, py::array_t indices, py::array_t coefficients) { diff --git a/ortools/linear_solver/python/model_builder_test.py b/ortools/linear_solver/python/model_builder_test.py index 761ac93486..a8c500acfc 100644 --- a/ortools/linear_solver/python/model_builder_test.py +++ b/ortools/linear_solver/python/model_builder_test.py @@ -2104,9 +2104,9 @@ class ModelBuilderExamplesTest(absltest.TestCase): def test_add_enforced(self): model = mb.ModelBuilder() - x = model.new_int_var(0, 10, 'x') - y = model.new_int_var(0, 10, 'y') - z = model.new_bool_var('z') + x = model.new_int_var(0, 10, "x") + y = model.new_int_var(0, 10, "y") + z = model.new_bool_var("z") ct = model.add_enforced(x + 2 * y >= 10, z, False) self.assertEqual(ct.lower_bound, 10.0) self.assertEqual(z.index, ct.indicator_variable.index) diff --git a/ortools/linear_solver/samples/BinPackingMb.cs b/ortools/linear_solver/samples/BinPackingMb.cs index 3c9c159ffe..37beac0df6 100644 --- a/ortools/linear_solver/samples/BinPackingMb.cs +++ b/ortools/linear_solver/samples/BinPackingMb.cs @@ -87,7 +87,9 @@ public class BinPackingMb // Create the solver with the SCIP backend and check it is supported. Solver solver = new Solver("SCIP"); if (!solver.SolverIsSupported()) + { return; + } // [END solver] // [START solve] diff --git a/ortools/linear_solver/samples/CloneModelMb.cs b/ortools/linear_solver/samples/CloneModelMb.cs index 33cb558593..70872d01da 100644 --- a/ortools/linear_solver/samples/CloneModelMb.cs +++ b/ortools/linear_solver/samples/CloneModelMb.cs @@ -69,7 +69,9 @@ public class SimpleMipProgramMb // Create the solver with the CP-SAT backend and checks it is supported. Solver solver = new Solver("sat"); if (!solver.SolverIsSupported()) + { return; + } // [END solver] // [START solve] diff --git a/ortools/linear_solver/wrappers/model_builder_helper.cc b/ortools/linear_solver/wrappers/model_builder_helper.cc index 0b401293b9..9e384753fc 100644 --- a/ortools/linear_solver/wrappers/model_builder_helper.cc +++ b/ortools/linear_solver/wrappers/model_builder_helper.cc @@ -21,6 +21,7 @@ #include #include +#include "absl/log/check.h" #include "absl/strings/match.h" #include "ortools/base/helpers.h" #include "ortools/base/options.h" @@ -262,7 +263,8 @@ void ModelBuilderHelper::SetEnforcedConstraintLowerBound(int ct_index, ct_proto->set_lower_bound(lb); } -void ModelBuilderHelper::SetEnforcedConstraintUpperBound(int ct_index, double ub) { +void ModelBuilderHelper::SetEnforcedConstraintUpperBound(int ct_index, + double ub) { DCHECK(IsEnforcedConstraint(ct_index)); MPGeneralConstraintProto* gen = model_.mutable_general_constraint(ct_index); MPConstraintProto* ct_proto = @@ -279,7 +281,7 @@ void ModelBuilderHelper::ClearEnforcedConstraintTerms(int ct_index) { } void ModelBuilderHelper::AddEnforcedConstraintTerm(int ct_index, int var_index, - double coeff) { + double coeff) { DCHECK(IsEnforcedConstraint(ct_index)); if (coeff == 0.0) return; MPGeneralConstraintProto* gen = model_.mutable_general_constraint(ct_index); @@ -289,8 +291,9 @@ void ModelBuilderHelper::AddEnforcedConstraintTerm(int ct_index, int var_index, ct_proto->add_coefficient(coeff); } -void ModelBuilderHelper::SafeAddEnforcedConstraintTerm(int ct_index, int var_index, - double coeff) { +void ModelBuilderHelper::SafeAddEnforcedConstraintTerm(int ct_index, + int var_index, + double coeff) { DCHECK(IsEnforcedConstraint(ct_index)); if (coeff == 0.0) return; MPGeneralConstraintProto* gen = model_.mutable_general_constraint(ct_index); @@ -309,12 +312,13 @@ void ModelBuilderHelper::SafeAddEnforcedConstraintTerm(int ct_index, int var_ind } void ModelBuilderHelper::SetEnforcedConstraintName(int ct_index, - const std::string& name) { + const std::string& name) { model_.mutable_general_constraint(ct_index)->set_name(name); } -void ModelBuilderHelper::SetEnforcedConstraintCoefficient(int ct_index, int var_index, - double coeff) { +void ModelBuilderHelper::SetEnforcedConstraintCoefficient(int ct_index, + int var_index, + double coeff) { DCHECK(IsEnforcedConstraint(ct_index)); MPGeneralConstraintProto* gen = model_.mutable_general_constraint(ct_index); MPConstraintProto* ct_proto = @@ -331,13 +335,15 @@ void ModelBuilderHelper::SetEnforcedConstraintCoefficient(int ct_index, int var_ ct_proto->add_coefficient(coeff); } -void ModelBuilderHelper::SetEnforcedIndicatorVariableIndex(int ct_index, int var_index) { +void ModelBuilderHelper::SetEnforcedIndicatorVariableIndex(int ct_index, + int var_index) { DCHECK(IsEnforcedConstraint(ct_index)); MPGeneralConstraintProto* gen = model_.mutable_general_constraint(ct_index); gen->mutable_indicator_constraint()->set_var_index(var_index); } -void ModelBuilderHelper::SetEnforcedIndicatorValue(int ct_index, bool positive) { +void ModelBuilderHelper::SetEnforcedIndicatorValue(int ct_index, + bool positive) { DCHECK(IsEnforcedConstraint(ct_index)); MPGeneralConstraintProto* gen = model_.mutable_general_constraint(ct_index); gen->mutable_indicator_constraint()->set_var_value(positive); @@ -387,7 +393,9 @@ int ModelBuilderHelper::EnforcedIndicatorVariableIndex(int ct_index) const { bool ModelBuilderHelper::EnforcedIndicatorValue(int ct_index) const { DCHECK(IsEnforcedConstraint(ct_index)); - return model_.general_constraint(ct_index).indicator_constraint().var_value() != 0; + return model_.general_constraint(ct_index) + .indicator_constraint() + .var_value() != 0; } int ModelBuilderHelper::num_variables() const { return model_.variable_size(); } @@ -421,9 +429,7 @@ void ModelBuilderHelper::SetObjectiveOffset(double offset) { model_.set_objective_offset(offset); } -void ModelBuilderHelper::ClearHints() { - model_.clear_solution_hint(); -} +void ModelBuilderHelper::ClearHints() { model_.clear_solution_hint(); } void ModelBuilderHelper::AddHint(int var_index, double var_value) { model_.mutable_solution_hint()->add_var_index(var_index); diff --git a/ortools/linear_solver/wrappers/model_builder_helper.h b/ortools/linear_solver/wrappers/model_builder_helper.h index fde0edec30..7c1d713547 100644 --- a/ortools/linear_solver/wrappers/model_builder_helper.h +++ b/ortools/linear_solver/wrappers/model_builder_helper.h @@ -102,7 +102,8 @@ class ModelBuilderHelper { void AddEnforcedConstraintTerm(int ct_index, int var_index, double coeff); void ClearEnforcedConstraintTerms(int ct_index); void SafeAddEnforcedConstraintTerm(int ct_index, int var_index, double coeff); - void SetEnforcedConstraintCoefficient(int ct_index, int var_index, double coeff); + void SetEnforcedConstraintCoefficient(int ct_index, int var_index, + double coeff); void SetEnforcedConstraintLowerBound(int ct_index, double lb); void SetEnforcedConstraintName(int ct_index, const std::string& name); void SetEnforcedConstraintUpperBound(int ct_index, double ub);