ortools.math_opt.python.linear_constraints

Linear constraint in a model.

  1# Copyright 2010-2025 Google LLC
  2# Licensed under the Apache License, Version 2.0 (the "License");
  3# you may not use this file except in compliance with the License.
  4# You may obtain a copy of the License at
  5#
  6#     http://www.apache.org/licenses/LICENSE-2.0
  7#
  8# Unless required by applicable law or agreed to in writing, software
  9# distributed under the License is distributed on an "AS IS" BASIS,
 10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 11# See the License for the specific language governing permissions and
 12# limitations under the License.
 13
 14"""Linear constraint in a model."""
 15
 16from typing import Any, Iterator, NamedTuple
 17
 18from ortools.math_opt.elemental.python import enums
 19from ortools.math_opt.python import from_model
 20from ortools.math_opt.python import variables
 21from ortools.math_opt.python.elemental import elemental
 22
 23
 24class LinearConstraint(from_model.FromModel):
 25    """A linear constraint for an optimization model.
 26
 27    A LinearConstraint adds the following restriction on feasible solutions to an
 28    optimization model:
 29      lb <= sum_{i in I} a_i * x_i <= ub
 30    where x_i are the decision variables of the problem. lb == ub is allowed, this
 31    models the equality constraint:
 32      sum_{i in I} a_i * x_i == b
 33    Setting lb > ub will result in an InvalidArgument error at solve time (the
 34    values are allowed to cross temporarily between solves).
 35
 36    A LinearConstraint can be configured as follows:
 37      * lower_bound: a float property, lb above. Should not be NaN nor +inf.
 38      * upper_bound: a float property, ub above. Should not be NaN nor -inf.
 39      * set_coefficient() and get_coefficient(): get and set the a_i * x_i
 40        terms. The variable must be from the same model as this constraint, and
 41        the a_i must be finite and not NaN. The coefficient for any variable not
 42        set is 0.0, and setting a coefficient to 0.0 removes it from I above.
 43
 44    The name is optional, read only, and used only for debugging. Non-empty names
 45    should be distinct.
 46
 47    Do not create a LinearConstraint directly, use Model.add_linear_constraint()
 48    instead. Two LinearConstraint objects can represent the same constraint (for
 49    the same model). They will have the same underlying LinearConstraint.elemental
 50    for storing the data. The LinearConstraint class is simply a reference to an
 51    Elemental.
 52    """
 53
 54    __slots__ = "_elemental", "_id"
 55
 56    def __init__(self, elem: elemental.Elemental, cid: int) -> None:
 57        """Internal only, prefer Model functions (add_linear_constraint() and get_linear_constraint())."""
 58        if not isinstance(cid, int):
 59            raise TypeError(f"cid type should be int, was:{type(cid).__name__!r}")
 60        self._elemental: elemental.Elemental = elem
 61        self._id: int = cid
 62
 63    @property
 64    def lower_bound(self) -> float:
 65        return self._elemental.get_attr(
 66            enums.DoubleAttr1.LINEAR_CONSTRAINT_LOWER_BOUND, (self._id,)
 67        )
 68
 69    @lower_bound.setter
 70    def lower_bound(self, value: float) -> None:
 71        self._elemental.set_attr(
 72            enums.DoubleAttr1.LINEAR_CONSTRAINT_LOWER_BOUND, (self._id,), value
 73        )
 74
 75    @property
 76    def upper_bound(self) -> float:
 77        return self._elemental.get_attr(
 78            enums.DoubleAttr1.LINEAR_CONSTRAINT_UPPER_BOUND, (self._id,)
 79        )
 80
 81    @upper_bound.setter
 82    def upper_bound(self, value: float) -> None:
 83        self._elemental.set_attr(
 84            enums.DoubleAttr1.LINEAR_CONSTRAINT_UPPER_BOUND, (self._id,), value
 85        )
 86
 87    @property
 88    def name(self) -> str:
 89        return self._elemental.get_element_name(
 90            enums.ElementType.LINEAR_CONSTRAINT, self._id
 91        )
 92
 93    @property
 94    def id(self) -> int:
 95        return self._id
 96
 97    @property
 98    def elemental(self) -> elemental.Elemental:
 99        """Internal use only."""
100        return self._elemental
101
102    def set_coefficient(self, var: variables.Variable, coefficient: float) -> None:
103        from_model.model_is_same(var, self)
104        self._elemental.set_attr(
105            enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT,
106            (self._id, var.id),
107            coefficient,
108        )
109
110    def get_coefficient(self, var: variables.Variable) -> float:
111        from_model.model_is_same(var, self)
112        return self._elemental.get_attr(
113            enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT, (self._id, var.id)
114        )
115
116    def terms(self) -> Iterator[variables.LinearTerm]:
117        """Yields the variable/coefficient pairs with nonzero coefficient for this linear constraint."""
118        keys = self._elemental.slice_attr(
119            enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT, 0, self._id
120        )
121        coefs = self._elemental.get_attrs(
122            enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT, keys
123        )
124        for i in range(len(keys)):
125            yield variables.LinearTerm(
126                variable=variables.Variable(self._elemental, int(keys[i, 1])),
127                coefficient=float(coefs[i]),
128            )
129
130    def as_bounded_linear_expression(self) -> variables.BoundedLinearExpression:
131        """Returns the bounded expression from lower_bound, upper_bound and terms."""
132        return variables.BoundedLinearExpression(
133            self.lower_bound, variables.LinearSum(self.terms()), self.upper_bound
134        )
135
136    def __str__(self):
137        """Returns the name, or a string containing the id if the name is empty."""
138        return self.name if self.name else f"linear_constraint_{self.id}"
139
140    def __repr__(self):
141        return f"<LinearConstraint id: {self.id}, name: {self.name!r}>"
142
143    def __eq__(self, other: Any) -> bool:
144        if isinstance(other, LinearConstraint):
145            return self._id == other._id and self._elemental is other._elemental
146        return False
147
148    def __hash__(self) -> int:
149        return hash(self._id)
150
151
152class LinearConstraintMatrixEntry(NamedTuple):
153    linear_constraint: LinearConstraint
154    variable: variables.Variable
155    coefficient: float
class LinearConstraint(ortools.math_opt.python.from_model.FromModel):
 25class LinearConstraint(from_model.FromModel):
 26    """A linear constraint for an optimization model.
 27
 28    A LinearConstraint adds the following restriction on feasible solutions to an
 29    optimization model:
 30      lb <= sum_{i in I} a_i * x_i <= ub
 31    where x_i are the decision variables of the problem. lb == ub is allowed, this
 32    models the equality constraint:
 33      sum_{i in I} a_i * x_i == b
 34    Setting lb > ub will result in an InvalidArgument error at solve time (the
 35    values are allowed to cross temporarily between solves).
 36
 37    A LinearConstraint can be configured as follows:
 38      * lower_bound: a float property, lb above. Should not be NaN nor +inf.
 39      * upper_bound: a float property, ub above. Should not be NaN nor -inf.
 40      * set_coefficient() and get_coefficient(): get and set the a_i * x_i
 41        terms. The variable must be from the same model as this constraint, and
 42        the a_i must be finite and not NaN. The coefficient for any variable not
 43        set is 0.0, and setting a coefficient to 0.0 removes it from I above.
 44
 45    The name is optional, read only, and used only for debugging. Non-empty names
 46    should be distinct.
 47
 48    Do not create a LinearConstraint directly, use Model.add_linear_constraint()
 49    instead. Two LinearConstraint objects can represent the same constraint (for
 50    the same model). They will have the same underlying LinearConstraint.elemental
 51    for storing the data. The LinearConstraint class is simply a reference to an
 52    Elemental.
 53    """
 54
 55    __slots__ = "_elemental", "_id"
 56
 57    def __init__(self, elem: elemental.Elemental, cid: int) -> None:
 58        """Internal only, prefer Model functions (add_linear_constraint() and get_linear_constraint())."""
 59        if not isinstance(cid, int):
 60            raise TypeError(f"cid type should be int, was:{type(cid).__name__!r}")
 61        self._elemental: elemental.Elemental = elem
 62        self._id: int = cid
 63
 64    @property
 65    def lower_bound(self) -> float:
 66        return self._elemental.get_attr(
 67            enums.DoubleAttr1.LINEAR_CONSTRAINT_LOWER_BOUND, (self._id,)
 68        )
 69
 70    @lower_bound.setter
 71    def lower_bound(self, value: float) -> None:
 72        self._elemental.set_attr(
 73            enums.DoubleAttr1.LINEAR_CONSTRAINT_LOWER_BOUND, (self._id,), value
 74        )
 75
 76    @property
 77    def upper_bound(self) -> float:
 78        return self._elemental.get_attr(
 79            enums.DoubleAttr1.LINEAR_CONSTRAINT_UPPER_BOUND, (self._id,)
 80        )
 81
 82    @upper_bound.setter
 83    def upper_bound(self, value: float) -> None:
 84        self._elemental.set_attr(
 85            enums.DoubleAttr1.LINEAR_CONSTRAINT_UPPER_BOUND, (self._id,), value
 86        )
 87
 88    @property
 89    def name(self) -> str:
 90        return self._elemental.get_element_name(
 91            enums.ElementType.LINEAR_CONSTRAINT, self._id
 92        )
 93
 94    @property
 95    def id(self) -> int:
 96        return self._id
 97
 98    @property
 99    def elemental(self) -> elemental.Elemental:
100        """Internal use only."""
101        return self._elemental
102
103    def set_coefficient(self, var: variables.Variable, coefficient: float) -> None:
104        from_model.model_is_same(var, self)
105        self._elemental.set_attr(
106            enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT,
107            (self._id, var.id),
108            coefficient,
109        )
110
111    def get_coefficient(self, var: variables.Variable) -> float:
112        from_model.model_is_same(var, self)
113        return self._elemental.get_attr(
114            enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT, (self._id, var.id)
115        )
116
117    def terms(self) -> Iterator[variables.LinearTerm]:
118        """Yields the variable/coefficient pairs with nonzero coefficient for this linear constraint."""
119        keys = self._elemental.slice_attr(
120            enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT, 0, self._id
121        )
122        coefs = self._elemental.get_attrs(
123            enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT, keys
124        )
125        for i in range(len(keys)):
126            yield variables.LinearTerm(
127                variable=variables.Variable(self._elemental, int(keys[i, 1])),
128                coefficient=float(coefs[i]),
129            )
130
131    def as_bounded_linear_expression(self) -> variables.BoundedLinearExpression:
132        """Returns the bounded expression from lower_bound, upper_bound and terms."""
133        return variables.BoundedLinearExpression(
134            self.lower_bound, variables.LinearSum(self.terms()), self.upper_bound
135        )
136
137    def __str__(self):
138        """Returns the name, or a string containing the id if the name is empty."""
139        return self.name if self.name else f"linear_constraint_{self.id}"
140
141    def __repr__(self):
142        return f"<LinearConstraint id: {self.id}, name: {self.name!r}>"
143
144    def __eq__(self, other: Any) -> bool:
145        if isinstance(other, LinearConstraint):
146            return self._id == other._id and self._elemental is other._elemental
147        return False
148
149    def __hash__(self) -> int:
150        return hash(self._id)

A linear constraint for an optimization model.

A LinearConstraint adds the following restriction on feasible solutions to an optimization model: lb <= sum_{i in I} a_i * x_i <= ub where x_i are the decision variables of the problem. lb == ub is allowed, this models the equality constraint: sum_{i in I} a_i * x_i == b Setting lb > ub will result in an InvalidArgument error at solve time (the values are allowed to cross temporarily between solves).

A LinearConstraint can be configured as follows:
  • lower_bound: a float property, lb above. Should not be NaN nor +inf.
  • upper_bound: a float property, ub above. Should not be NaN nor -inf.
  • set_coefficient() and get_coefficient(): get and set the a_i * x_i terms. The variable must be from the same model as this constraint, and the a_i must be finite and not NaN. The coefficient for any variable not set is 0.0, and setting a coefficient to 0.0 removes it from I above.

The name is optional, read only, and used only for debugging. Non-empty names should be distinct.

Do not create a LinearConstraint directly, use Model.add_linear_constraint() instead. Two LinearConstraint objects can represent the same constraint (for the same model). They will have the same underlying LinearConstraint.elemental for storing the data. The LinearConstraint class is simply a reference to an Elemental.

LinearConstraint( elem: ortools.math_opt.python.elemental.elemental.Elemental, cid: int)
57    def __init__(self, elem: elemental.Elemental, cid: int) -> None:
58        """Internal only, prefer Model functions (add_linear_constraint() and get_linear_constraint())."""
59        if not isinstance(cid, int):
60            raise TypeError(f"cid type should be int, was:{type(cid).__name__!r}")
61        self._elemental: elemental.Elemental = elem
62        self._id: int = cid

Internal only, prefer Model functions (add_linear_constraint() and get_linear_constraint()).

lower_bound: float
64    @property
65    def lower_bound(self) -> float:
66        return self._elemental.get_attr(
67            enums.DoubleAttr1.LINEAR_CONSTRAINT_LOWER_BOUND, (self._id,)
68        )
upper_bound: float
76    @property
77    def upper_bound(self) -> float:
78        return self._elemental.get_attr(
79            enums.DoubleAttr1.LINEAR_CONSTRAINT_UPPER_BOUND, (self._id,)
80        )
name: str
88    @property
89    def name(self) -> str:
90        return self._elemental.get_element_name(
91            enums.ElementType.LINEAR_CONSTRAINT, self._id
92        )
id: int
94    @property
95    def id(self) -> int:
96        return self._id
 98    @property
 99    def elemental(self) -> elemental.Elemental:
100        """Internal use only."""
101        return self._elemental

Internal use only.

def set_coefficient( self, var: ortools.math_opt.python.variables.Variable, coefficient: float) -> None:
103    def set_coefficient(self, var: variables.Variable, coefficient: float) -> None:
104        from_model.model_is_same(var, self)
105        self._elemental.set_attr(
106            enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT,
107            (self._id, var.id),
108            coefficient,
109        )
def get_coefficient(self, var: ortools.math_opt.python.variables.Variable) -> float:
111    def get_coefficient(self, var: variables.Variable) -> float:
112        from_model.model_is_same(var, self)
113        return self._elemental.get_attr(
114            enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT, (self._id, var.id)
115        )
def terms(self) -> Iterator[ortools.math_opt.python.variables.LinearTerm]:
117    def terms(self) -> Iterator[variables.LinearTerm]:
118        """Yields the variable/coefficient pairs with nonzero coefficient for this linear constraint."""
119        keys = self._elemental.slice_attr(
120            enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT, 0, self._id
121        )
122        coefs = self._elemental.get_attrs(
123            enums.DoubleAttr2.LINEAR_CONSTRAINT_COEFFICIENT, keys
124        )
125        for i in range(len(keys)):
126            yield variables.LinearTerm(
127                variable=variables.Variable(self._elemental, int(keys[i, 1])),
128                coefficient=float(coefs[i]),
129            )

Yields the variable/coefficient pairs with nonzero coefficient for this linear constraint.

def as_bounded_linear_expression( self) -> ortools.math_opt.python.bounded_expressions.BoundedExpression[ForwardRef('LinearBase')]:
131    def as_bounded_linear_expression(self) -> variables.BoundedLinearExpression:
132        """Returns the bounded expression from lower_bound, upper_bound and terms."""
133        return variables.BoundedLinearExpression(
134            self.lower_bound, variables.LinearSum(self.terms()), self.upper_bound
135        )

Returns the bounded expression from lower_bound, upper_bound and terms.

class LinearConstraintMatrixEntry(typing.NamedTuple):
153class LinearConstraintMatrixEntry(NamedTuple):
154    linear_constraint: LinearConstraint
155    variable: variables.Variable
156    coefficient: float

LinearConstraintMatrixEntry(linear_constraint, variable, coefficient)

LinearConstraintMatrixEntry( linear_constraint: LinearConstraint, variable: ortools.math_opt.python.variables.Variable, coefficient: float)

Create new instance of LinearConstraintMatrixEntry(linear_constraint, variable, coefficient)

linear_constraint: LinearConstraint

Alias for field number 0

Alias for field number 1

coefficient: float

Alias for field number 2