ortools.math_opt.python.compute_infeasible_subsystem_result

Data types for the result of calling `mathopt.compute_infeasible_subsystem.

  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"""Data types for the result of calling `mathopt.compute_infeasible_subsystem."""
 15
 16import dataclasses
 17from typing import FrozenSet, Mapping
 18
 19import immutabledict
 20
 21from ortools.math_opt import infeasible_subsystem_pb2
 22from ortools.math_opt.python import linear_constraints as linear_constraints_mod
 23from ortools.math_opt.python import model
 24from ortools.math_opt.python import result
 25from ortools.math_opt.python import variables as variables_mod
 26
 27
 28@dataclasses.dataclass(frozen=True)
 29class ModelSubsetBounds:
 30    """Presence of the upper and lower bounds in a two-sided constraint.
 31
 32    E.g. for 1 <= x <= 2, `lower` is the constraint 1 <= x and `upper` is the
 33    constraint x <= 2.
 34
 35    Attributes:
 36      lower: If the lower bound half of the two-sided constraint is selected.
 37      upper: If the upper bound half of the two-sided constraint is selected.
 38    """
 39
 40    lower: bool = False
 41    upper: bool = False
 42
 43    def empty(self) -> bool:
 44        """Is empty if both `lower` and `upper` are False."""
 45        return not (self.lower or self.upper)
 46
 47    def to_proto(self) -> infeasible_subsystem_pb2.ModelSubsetProto.Bounds:
 48        """Returns an equivalent proto message for these bounds."""
 49        return infeasible_subsystem_pb2.ModelSubsetProto.Bounds(
 50            lower=self.lower, upper=self.upper
 51        )
 52
 53
 54def parse_model_subset_bounds(
 55    bounds: infeasible_subsystem_pb2.ModelSubsetProto.Bounds,
 56) -> ModelSubsetBounds:
 57    """Returns an equivalent `ModelSubsetBounds` to the input proto."""
 58    return ModelSubsetBounds(lower=bounds.lower, upper=bounds.upper)
 59
 60
 61@dataclasses.dataclass(frozen=True)
 62class ModelSubset:
 63    """A subset of a Model's constraints (including variable bounds/integrality).
 64
 65    When returned from `solve.compute_infeasible_subsystem`, the contained
 66    `ModelSubsetBounds` will all be nonempty.
 67
 68    Attributes:
 69      variable_bounds: The upper and/or lower bound constraints on these variables
 70        are included in the subset.
 71      variable_integrality: The constraint that a variable is integer is included
 72        in the subset.
 73      linear_constraints: The upper and/or lower bounds from these linear
 74        constraints are included in the subset.
 75    """
 76
 77    variable_bounds: Mapping[variables_mod.Variable, ModelSubsetBounds] = (
 78        immutabledict.immutabledict()
 79    )
 80    variable_integrality: FrozenSet[variables_mod.Variable] = frozenset()
 81    linear_constraints: Mapping[
 82        linear_constraints_mod.LinearConstraint, ModelSubsetBounds
 83    ] = immutabledict.immutabledict()
 84
 85    def empty(self) -> bool:
 86        """Returns true if all the nested constraint collections are empty.
 87
 88        Warning: When `self.variable_bounds` or `self.linear_constraints` contain
 89        only ModelSubsetBounds which are themselves empty, this function will return
 90        False.
 91
 92        Returns:
 93          True if this is empty.
 94        """
 95        return not (
 96            self.variable_bounds or self.variable_integrality or self.linear_constraints
 97        )
 98
 99    def to_proto(self) -> infeasible_subsystem_pb2.ModelSubsetProto:
100        """Returns an equivalent proto message for this `ModelSubset`."""
101        return infeasible_subsystem_pb2.ModelSubsetProto(
102            variable_bounds={
103                var.id: bounds.to_proto()
104                for (var, bounds) in self.variable_bounds.items()
105            },
106            variable_integrality=sorted(var.id for var in self.variable_integrality),
107            linear_constraints={
108                con.id: bounds.to_proto()
109                for (con, bounds) in self.linear_constraints.items()
110            },
111        )
112
113
114def parse_model_subset(
115    model_subset: infeasible_subsystem_pb2.ModelSubsetProto, mod: model.Model
116) -> ModelSubset:
117    """Returns an equivalent `ModelSubset` to the input proto."""
118    if model_subset.quadratic_constraints:
119        raise NotImplementedError(
120            "quadratic_constraints not yet implemented for ModelSubset in Python"
121        )
122    if model_subset.second_order_cone_constraints:
123        raise NotImplementedError(
124            "second_order_cone_constraints not yet implemented for ModelSubset in"
125            " Python"
126        )
127    if model_subset.sos1_constraints:
128        raise NotImplementedError(
129            "sos1_constraints not yet implemented for ModelSubset in Python"
130        )
131    if model_subset.sos2_constraints:
132        raise NotImplementedError(
133            "sos2_constraints not yet implemented for ModelSubset in Python"
134        )
135    if model_subset.indicator_constraints:
136        raise NotImplementedError(
137            "indicator_constraints not yet implemented for ModelSubset in Python"
138        )
139    return ModelSubset(
140        variable_bounds={
141            mod.get_variable(var_id): parse_model_subset_bounds(bounds)
142            for var_id, bounds in model_subset.variable_bounds.items()
143        },
144        variable_integrality=frozenset(
145            mod.get_variable(var_id) for var_id in model_subset.variable_integrality
146        ),
147        linear_constraints={
148            mod.get_linear_constraint(con_id): parse_model_subset_bounds(bounds)
149            for con_id, bounds in model_subset.linear_constraints.items()
150        },
151    )
152
153
154@dataclasses.dataclass(frozen=True)
155class ComputeInfeasibleSubsystemResult:
156    """The result of searching for an infeasible subsystem.
157
158    This is the result of calling `mathopt.compute_infeasible_subsystem()`.
159
160    Attributes:
161      feasibility: If the problem was proven feasible, infeasible, or no
162        conclusion was reached. The fields below are ignored unless the problem
163        was proven infeasible.
164      infeasible_subsystem: Ignored unless `feasibility` is `INFEASIBLE`, a subset
165        of the model that is still infeasible.
166      is_minimal: Ignored unless `feasibility` is `INFEASIBLE`. If True, then the
167        removal of any constraint from `infeasible_subsystem` makes the sub-model
168        feasible. Note that, due to problem transformations MathOpt applies or
169        idiosyncrasies of the solvers contract, the returned infeasible subsystem
170        may not actually be minimal.
171    """
172
173    feasibility: result.FeasibilityStatus = result.FeasibilityStatus.UNDETERMINED
174    infeasible_subsystem: ModelSubset = ModelSubset()
175    is_minimal: bool = False
176
177    def to_proto(
178        self,
179    ) -> infeasible_subsystem_pb2.ComputeInfeasibleSubsystemResultProto:
180        """Returns an equivalent proto for this `ComputeInfeasibleSubsystemResult`."""
181        return infeasible_subsystem_pb2.ComputeInfeasibleSubsystemResultProto(
182            feasibility=self.feasibility.value,
183            infeasible_subsystem=self.infeasible_subsystem.to_proto(),
184            is_minimal=self.is_minimal,
185        )
186
187
188def parse_compute_infeasible_subsystem_result(
189    infeasible_system_result: infeasible_subsystem_pb2.ComputeInfeasibleSubsystemResultProto,
190    mod: model.Model,
191) -> ComputeInfeasibleSubsystemResult:
192    """Returns an equivalent `ComputeInfeasibleSubsystemResult` to the input proto."""
193    return ComputeInfeasibleSubsystemResult(
194        feasibility=result.FeasibilityStatus(infeasible_system_result.feasibility),
195        infeasible_subsystem=parse_model_subset(
196            infeasible_system_result.infeasible_subsystem, mod
197        ),
198        is_minimal=infeasible_system_result.is_minimal,
199    )
@dataclasses.dataclass(frozen=True)
class ModelSubsetBounds:
29@dataclasses.dataclass(frozen=True)
30class ModelSubsetBounds:
31    """Presence of the upper and lower bounds in a two-sided constraint.
32
33    E.g. for 1 <= x <= 2, `lower` is the constraint 1 <= x and `upper` is the
34    constraint x <= 2.
35
36    Attributes:
37      lower: If the lower bound half of the two-sided constraint is selected.
38      upper: If the upper bound half of the two-sided constraint is selected.
39    """
40
41    lower: bool = False
42    upper: bool = False
43
44    def empty(self) -> bool:
45        """Is empty if both `lower` and `upper` are False."""
46        return not (self.lower or self.upper)
47
48    def to_proto(self) -> infeasible_subsystem_pb2.ModelSubsetProto.Bounds:
49        """Returns an equivalent proto message for these bounds."""
50        return infeasible_subsystem_pb2.ModelSubsetProto.Bounds(
51            lower=self.lower, upper=self.upper
52        )

Presence of the upper and lower bounds in a two-sided constraint.

E.g. for 1 <= x <= 2, lower is the constraint 1 <= x and upper is the constraint x <= 2.

Attributes:
  • lower: If the lower bound half of the two-sided constraint is selected.
  • upper: If the upper bound half of the two-sided constraint is selected.
ModelSubsetBounds(lower: bool = False, upper: bool = False)
lower: bool = False
upper: bool = False
def empty(self) -> bool:
44    def empty(self) -> bool:
45        """Is empty if both `lower` and `upper` are False."""
46        return not (self.lower or self.upper)

Is empty if both lower and upper are False.

48    def to_proto(self) -> infeasible_subsystem_pb2.ModelSubsetProto.Bounds:
49        """Returns an equivalent proto message for these bounds."""
50        return infeasible_subsystem_pb2.ModelSubsetProto.Bounds(
51            lower=self.lower, upper=self.upper
52        )

Returns an equivalent proto message for these bounds.

def parse_model_subset_bounds( bounds: ortools.math_opt.infeasible_subsystem_pb2.ModelSubsetProto.Bounds) -> ModelSubsetBounds:
55def parse_model_subset_bounds(
56    bounds: infeasible_subsystem_pb2.ModelSubsetProto.Bounds,
57) -> ModelSubsetBounds:
58    """Returns an equivalent `ModelSubsetBounds` to the input proto."""
59    return ModelSubsetBounds(lower=bounds.lower, upper=bounds.upper)

Returns an equivalent ModelSubsetBounds to the input proto.

@dataclasses.dataclass(frozen=True)
class ModelSubset:
 62@dataclasses.dataclass(frozen=True)
 63class ModelSubset:
 64    """A subset of a Model's constraints (including variable bounds/integrality).
 65
 66    When returned from `solve.compute_infeasible_subsystem`, the contained
 67    `ModelSubsetBounds` will all be nonempty.
 68
 69    Attributes:
 70      variable_bounds: The upper and/or lower bound constraints on these variables
 71        are included in the subset.
 72      variable_integrality: The constraint that a variable is integer is included
 73        in the subset.
 74      linear_constraints: The upper and/or lower bounds from these linear
 75        constraints are included in the subset.
 76    """
 77
 78    variable_bounds: Mapping[variables_mod.Variable, ModelSubsetBounds] = (
 79        immutabledict.immutabledict()
 80    )
 81    variable_integrality: FrozenSet[variables_mod.Variable] = frozenset()
 82    linear_constraints: Mapping[
 83        linear_constraints_mod.LinearConstraint, ModelSubsetBounds
 84    ] = immutabledict.immutabledict()
 85
 86    def empty(self) -> bool:
 87        """Returns true if all the nested constraint collections are empty.
 88
 89        Warning: When `self.variable_bounds` or `self.linear_constraints` contain
 90        only ModelSubsetBounds which are themselves empty, this function will return
 91        False.
 92
 93        Returns:
 94          True if this is empty.
 95        """
 96        return not (
 97            self.variable_bounds or self.variable_integrality or self.linear_constraints
 98        )
 99
100    def to_proto(self) -> infeasible_subsystem_pb2.ModelSubsetProto:
101        """Returns an equivalent proto message for this `ModelSubset`."""
102        return infeasible_subsystem_pb2.ModelSubsetProto(
103            variable_bounds={
104                var.id: bounds.to_proto()
105                for (var, bounds) in self.variable_bounds.items()
106            },
107            variable_integrality=sorted(var.id for var in self.variable_integrality),
108            linear_constraints={
109                con.id: bounds.to_proto()
110                for (con, bounds) in self.linear_constraints.items()
111            },
112        )

A subset of a Model's constraints (including variable bounds/integrality).

When returned from solve.compute_infeasible_subsystem, the contained ModelSubsetBounds will all be nonempty.

Attributes:
  • variable_bounds: The upper and/or lower bound constraints on these variables are included in the subset.
  • variable_integrality: The constraint that a variable is integer is included in the subset.
  • linear_constraints: The upper and/or lower bounds from these linear constraints are included in the subset.
ModelSubset( variable_bounds: Mapping[ortools.math_opt.python.variables.Variable, ModelSubsetBounds] = immutabledict({}), variable_integrality: FrozenSet[ortools.math_opt.python.variables.Variable] = frozenset(), linear_constraints: Mapping[ortools.math_opt.python.linear_constraints.LinearConstraint, ModelSubsetBounds] = immutabledict({}))
variable_bounds: Mapping[ortools.math_opt.python.variables.Variable, ModelSubsetBounds] = immutabledict({})
variable_integrality: FrozenSet[ortools.math_opt.python.variables.Variable] = frozenset()
linear_constraints: Mapping[ortools.math_opt.python.linear_constraints.LinearConstraint, ModelSubsetBounds] = immutabledict({})
def empty(self) -> bool:
86    def empty(self) -> bool:
87        """Returns true if all the nested constraint collections are empty.
88
89        Warning: When `self.variable_bounds` or `self.linear_constraints` contain
90        only ModelSubsetBounds which are themselves empty, this function will return
91        False.
92
93        Returns:
94          True if this is empty.
95        """
96        return not (
97            self.variable_bounds or self.variable_integrality or self.linear_constraints
98        )

Returns true if all the nested constraint collections are empty.

Warning: When self.variable_bounds or self.linear_constraints contain only ModelSubsetBounds which are themselves empty, this function will return False.

Returns:

True if this is empty.

100    def to_proto(self) -> infeasible_subsystem_pb2.ModelSubsetProto:
101        """Returns an equivalent proto message for this `ModelSubset`."""
102        return infeasible_subsystem_pb2.ModelSubsetProto(
103            variable_bounds={
104                var.id: bounds.to_proto()
105                for (var, bounds) in self.variable_bounds.items()
106            },
107            variable_integrality=sorted(var.id for var in self.variable_integrality),
108            linear_constraints={
109                con.id: bounds.to_proto()
110                for (con, bounds) in self.linear_constraints.items()
111            },
112        )

Returns an equivalent proto message for this ModelSubset.

115def parse_model_subset(
116    model_subset: infeasible_subsystem_pb2.ModelSubsetProto, mod: model.Model
117) -> ModelSubset:
118    """Returns an equivalent `ModelSubset` to the input proto."""
119    if model_subset.quadratic_constraints:
120        raise NotImplementedError(
121            "quadratic_constraints not yet implemented for ModelSubset in Python"
122        )
123    if model_subset.second_order_cone_constraints:
124        raise NotImplementedError(
125            "second_order_cone_constraints not yet implemented for ModelSubset in"
126            " Python"
127        )
128    if model_subset.sos1_constraints:
129        raise NotImplementedError(
130            "sos1_constraints not yet implemented for ModelSubset in Python"
131        )
132    if model_subset.sos2_constraints:
133        raise NotImplementedError(
134            "sos2_constraints not yet implemented for ModelSubset in Python"
135        )
136    if model_subset.indicator_constraints:
137        raise NotImplementedError(
138            "indicator_constraints not yet implemented for ModelSubset in Python"
139        )
140    return ModelSubset(
141        variable_bounds={
142            mod.get_variable(var_id): parse_model_subset_bounds(bounds)
143            for var_id, bounds in model_subset.variable_bounds.items()
144        },
145        variable_integrality=frozenset(
146            mod.get_variable(var_id) for var_id in model_subset.variable_integrality
147        ),
148        linear_constraints={
149            mod.get_linear_constraint(con_id): parse_model_subset_bounds(bounds)
150            for con_id, bounds in model_subset.linear_constraints.items()
151        },
152    )

Returns an equivalent ModelSubset to the input proto.

@dataclasses.dataclass(frozen=True)
class ComputeInfeasibleSubsystemResult:
155@dataclasses.dataclass(frozen=True)
156class ComputeInfeasibleSubsystemResult:
157    """The result of searching for an infeasible subsystem.
158
159    This is the result of calling `mathopt.compute_infeasible_subsystem()`.
160
161    Attributes:
162      feasibility: If the problem was proven feasible, infeasible, or no
163        conclusion was reached. The fields below are ignored unless the problem
164        was proven infeasible.
165      infeasible_subsystem: Ignored unless `feasibility` is `INFEASIBLE`, a subset
166        of the model that is still infeasible.
167      is_minimal: Ignored unless `feasibility` is `INFEASIBLE`. If True, then the
168        removal of any constraint from `infeasible_subsystem` makes the sub-model
169        feasible. Note that, due to problem transformations MathOpt applies or
170        idiosyncrasies of the solvers contract, the returned infeasible subsystem
171        may not actually be minimal.
172    """
173
174    feasibility: result.FeasibilityStatus = result.FeasibilityStatus.UNDETERMINED
175    infeasible_subsystem: ModelSubset = ModelSubset()
176    is_minimal: bool = False
177
178    def to_proto(
179        self,
180    ) -> infeasible_subsystem_pb2.ComputeInfeasibleSubsystemResultProto:
181        """Returns an equivalent proto for this `ComputeInfeasibleSubsystemResult`."""
182        return infeasible_subsystem_pb2.ComputeInfeasibleSubsystemResultProto(
183            feasibility=self.feasibility.value,
184            infeasible_subsystem=self.infeasible_subsystem.to_proto(),
185            is_minimal=self.is_minimal,
186        )

The result of searching for an infeasible subsystem.

This is the result of calling mathopt.compute_infeasible_subsystem().

Attributes:
  • feasibility: If the problem was proven feasible, infeasible, or no conclusion was reached. The fields below are ignored unless the problem was proven infeasible.
  • infeasible_subsystem: Ignored unless feasibility is INFEASIBLE, a subset of the model that is still infeasible.
  • is_minimal: Ignored unless feasibility is INFEASIBLE. If True, then the removal of any constraint from infeasible_subsystem makes the sub-model feasible. Note that, due to problem transformations MathOpt applies or idiosyncrasies of the solvers contract, the returned infeasible subsystem may not actually be minimal.
ComputeInfeasibleSubsystemResult( feasibility: ortools.math_opt.python.result.FeasibilityStatus = <FeasibilityStatus.UNDETERMINED: 1>, infeasible_subsystem: ModelSubset = ModelSubset(variable_bounds=immutabledict({}), variable_integrality=frozenset(), linear_constraints=immutabledict({})), is_minimal: bool = False)
feasibility: ortools.math_opt.python.result.FeasibilityStatus = <FeasibilityStatus.UNDETERMINED: 1>
infeasible_subsystem: ModelSubset = ModelSubset(variable_bounds=immutabledict({}), variable_integrality=frozenset(), linear_constraints=immutabledict({}))
is_minimal: bool = False
178    def to_proto(
179        self,
180    ) -> infeasible_subsystem_pb2.ComputeInfeasibleSubsystemResultProto:
181        """Returns an equivalent proto for this `ComputeInfeasibleSubsystemResult`."""
182        return infeasible_subsystem_pb2.ComputeInfeasibleSubsystemResultProto(
183            feasibility=self.feasibility.value,
184            infeasible_subsystem=self.infeasible_subsystem.to_proto(),
185            is_minimal=self.is_minimal,
186        )

Returns an equivalent proto for this ComputeInfeasibleSubsystemResult.

def parse_compute_infeasible_subsystem_result( infeasible_system_result: ortools.math_opt.infeasible_subsystem_pb2.ComputeInfeasibleSubsystemResultProto, mod: ortools.math_opt.python.model.Model) -> ComputeInfeasibleSubsystemResult:
189def parse_compute_infeasible_subsystem_result(
190    infeasible_system_result: infeasible_subsystem_pb2.ComputeInfeasibleSubsystemResultProto,
191    mod: model.Model,
192) -> ComputeInfeasibleSubsystemResult:
193    """Returns an equivalent `ComputeInfeasibleSubsystemResult` to the input proto."""
194    return ComputeInfeasibleSubsystemResult(
195        feasibility=result.FeasibilityStatus(infeasible_system_result.feasibility),
196        infeasible_subsystem=parse_model_subset(
197            infeasible_system_result.infeasible_subsystem, mod
198        ),
199        is_minimal=infeasible_system_result.is_minimal,
200    )

Returns an equivalent ComputeInfeasibleSubsystemResult to the input proto.