ortools.math_opt.python.compute_infeasible_subsystem_result

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

  1#!/usr/bin/env python3
  2# Copyright 2010-2025 Google LLC
  3# Licensed under the Apache License, Version 2.0 (the "License");
  4# you may not use this file except in compliance with the License.
  5# You may obtain a copy of the License at
  6#
  7#     http://www.apache.org/licenses/LICENSE-2.0
  8#
  9# Unless required by applicable law or agreed to in writing, software
 10# distributed under the License is distributed on an "AS IS" BASIS,
 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 12# See the License for the specific language governing permissions and
 13# limitations under the License.
 14
 15"""Data types for the result of calling `mathopt.compute_infeasible_subsystem."""
 16
 17import dataclasses
 18from typing import FrozenSet, Mapping
 19
 20import immutabledict
 21
 22from ortools.math_opt import infeasible_subsystem_pb2
 23from ortools.math_opt.python import linear_constraints as linear_constraints_mod
 24from ortools.math_opt.python import model
 25from ortools.math_opt.python import result
 26from ortools.math_opt.python import variables as variables_mod
 27
 28
 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        )
 53
 54
 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)
 60
 61
 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        )
113
114
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    )
153
154
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        )
187
188
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    )
@dataclasses.dataclass(frozen=True)
class ModelSubsetBounds:
30@dataclasses.dataclass(frozen=True)
31class ModelSubsetBounds:
32    """Presence of the upper and lower bounds in a two-sided constraint.
33
34    E.g. for 1 <= x <= 2, `lower` is the constraint 1 <= x and `upper` is the
35    constraint x <= 2.
36
37    Attributes:
38      lower: If the lower bound half of the two-sided constraint is selected.
39      upper: If the upper bound half of the two-sided constraint is selected.
40    """
41
42    lower: bool = False
43    upper: bool = False
44
45    def empty(self) -> bool:
46        """Is empty if both `lower` and `upper` are False."""
47        return not (self.lower or self.upper)
48
49    def to_proto(self) -> infeasible_subsystem_pb2.ModelSubsetProto.Bounds:
50        """Returns an equivalent proto message for these bounds."""
51        return infeasible_subsystem_pb2.ModelSubsetProto.Bounds(
52            lower=self.lower, upper=self.upper
53        )

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:
45    def empty(self) -> bool:
46        """Is empty if both `lower` and `upper` are False."""
47        return not (self.lower or self.upper)

Is empty if both lower and upper are False.

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

Returns an equivalent proto message for these bounds.

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

Returns an equivalent ModelSubsetBounds to the input proto.

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

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:
87    def empty(self) -> bool:
88        """Returns true if all the nested constraint collections are empty.
89
90        Warning: When `self.variable_bounds` or `self.linear_constraints` contain
91        only ModelSubsetBounds which are themselves empty, this function will return
92        False.
93
94        Returns:
95          True if this is empty.
96        """
97        return not (
98            self.variable_bounds or self.variable_integrality or self.linear_constraints
99        )

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.

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

Returns an equivalent proto message for this ModelSubset.

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

Returns an equivalent ModelSubset to the input proto.

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

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
179    def to_proto(
180        self,
181    ) -> infeasible_subsystem_pb2.ComputeInfeasibleSubsystemResultProto:
182        """Returns an equivalent proto for this `ComputeInfeasibleSubsystemResult`."""
183        return infeasible_subsystem_pb2.ComputeInfeasibleSubsystemResultProto(
184            feasibility=self.feasibility.value,
185            infeasible_subsystem=self.infeasible_subsystem.to_proto(),
186            is_minimal=self.is_minimal,
187        )

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

Returns an equivalent ComputeInfeasibleSubsystemResult to the input proto.