|
| 1 | +############### |
| 2 | +Irreducible Infeasible Subsystems (IIS) |
| 3 | +############### |
| 4 | + |
| 5 | +For the following, let us assume that a Model object is available, which is created as follows: |
| 6 | + |
| 7 | +.. code-block:: python |
| 8 | +
|
| 9 | + from pyscipopt import Model, IISfinder, SCIP_RESULT |
| 10 | + model = Model() |
| 11 | +
|
| 12 | +.. contents:: Contents |
| 13 | + |
| 14 | +What is an IIS? |
| 15 | +=============== |
| 16 | + |
| 17 | +It is a common issue for integer programming practitioners to (unexpectedly) encounter infeasible problems. |
| 18 | +Often it is desirable to better understand exactly why the problem is infeasible. |
| 19 | +Was it an error in the input data, was the underlying formulation incorrect, or was the model simply infeasible by construction? |
| 20 | + |
| 21 | +A common tool for helping diagnose the reason for infeasibility is an **Irreducible Infeasible Subsystem (IIS)**. |
| 22 | +An IIS is a subset of constraints and variable bounds from the original problem that: |
| 23 | + |
| 24 | +1. Remains infeasible when considered together |
| 25 | +2. Cannot be further reduced without the subsystem becoming feasible |
| 26 | + |
| 27 | +Practitioners can use IIS finders to narrow their focus onto a smaller, more manageable problem. |
| 28 | +Note, however, that there are potentially many different irreducible subsystems for a given infeasible problem, and that IIS finders may not provide a guarantee of an IIS of minimum size. |
| 29 | + |
| 30 | +Generating an IIS |
| 31 | +================= |
| 32 | +Let us create a simple infeasible model and then generate an IIS for it. |
| 33 | + |
| 34 | +.. code-block:: python |
| 35 | +
|
| 36 | + from pyscipopt import Model |
| 37 | +
|
| 38 | + m = Model() |
| 39 | + x1 = m.addVar("x1", vtype="B") |
| 40 | + x2 = m.addVar("x2", vtype="B") |
| 41 | + x3 = m.addVar("x3", vtype="B") |
| 42 | +
|
| 43 | + # These four constraints cannot be satisfied simultaneously |
| 44 | + m.addCons(x1 + x2 == 1, name="c1") |
| 45 | + m.addCons(x2 + x3 == 1, name="c2") |
| 46 | + m.addCons(x1 + x3 == 1, name="c3") |
| 47 | + m.addCons(x1 + x2 + x3 <= 0, name="c4") |
| 48 | +
|
| 49 | + model.optimize() |
| 50 | + iis = model.generateIIS() |
| 51 | +
|
| 52 | +When you run this code, SCIP will output a log showing the progress of finding the IIS: |
| 53 | + |
| 54 | +.. code-block:: text |
| 55 | +
|
| 56 | + presolving: |
| 57 | + presolving (1 rounds: 1 fast, 0 medium, 0 exhaustive): |
| 58 | + 2 deleted vars, 2 deleted constraints, 0 added constraints, 0 tightened bounds, 0 added holes, 0 changed sides, 0 changed coefficients |
| 59 | + 0 implications, 0 cliques, 0 implied integral variables (0 bin, 0 int, 0 cont) |
| 60 | + presolving detected infeasibility |
| 61 | + Presolving Time: 0.00 |
| 62 | +
|
| 63 | + SCIP Status : problem is solved [infeasible] |
| 64 | + Solving Time (sec) : 0.00 |
| 65 | + Solving Nodes : 0 |
| 66 | + Primal Bound : +1.00000000000000e+20 (0 solutions) |
| 67 | + Dual Bound : +1.00000000000000e+20 |
| 68 | + Gap : 0.00 % |
| 69 | + time(s)| node | cons | vars | bounds| infeasible |
| 70 | + 0.0| 0| 1| 3| 6| no |
| 71 | + 0.0| 0| 2| 3| 6| no |
| 72 | + 0.0| 0| 4| 3| 6| no |
| 73 | + 0.0| 0| 3| 3| 6| yes |
| 74 | + 0.0| 0| 2| 3| 6| yes |
| 75 | + 0.0| 0| 2| 3| 6| yes |
| 76 | +
|
| 77 | + IIS Status : irreducible infeasible subsystem (IIS) found |
| 78 | + IIS irreducible : yes |
| 79 | + Generation Time (sec) : 0.01 |
| 80 | + Generation Nodes : 0 |
| 81 | + Num. Cons. in IIS : 2 |
| 82 | + Num. Vars. in IIS : 3 |
| 83 | + Num. Bounds in IIS : 6 |
| 84 | +
|
| 85 | +.. note:: |
| 86 | + While an already optimized infeasible model is not required to use the IIS functionality, it is |
| 87 | + encouraged to call this functionality only after ``model.optimize()``. Otherwise, SCIP will naturally optimize |
| 88 | + the base problem first to ensure that it is actually infeasible. |
| 89 | + |
| 90 | + After SCIP finds that the model is infeasible, see that SCIP's IIS finders alternate between including constraints to make the problem feasible, and removing constraints to make the problem as small as possible. |
| 91 | + You see in the final statistics that the IIS is indeed irreducible, with 3 variables and 2 constraints. |
| 92 | + |
| 93 | +The IIS Object |
| 94 | +============== |
| 95 | + |
| 96 | +The ``IIS`` object returned by ``generateIIS()`` can be queried to access the following information: |
| 97 | + |
| 98 | +- **time**: The CPU time spent finding the IIS |
| 99 | +- **irreducible**: Boolean indicating if the IIS is irreducible |
| 100 | +- **nodes**: Number of nodes explored during IIS generation |
| 101 | +- **model**: A ``Model`` object containing the subscip with the IIS constraints |
| 102 | + |
| 103 | +You can interact with the subscip to examine which constraints and variables are part of the IIS: |
| 104 | + |
| 105 | +.. code-block:: python |
| 106 | +
|
| 107 | + iis = model.generateIIS() |
| 108 | + subscip = iis.getSubscip() |
| 109 | + |
| 110 | + # Get constraints in the IIS |
| 111 | + for cons in subscip.getConss(): |
| 112 | + print(f"Constraint: {cons.name}") |
| 113 | + |
| 114 | + # Get variables in the IIS |
| 115 | + for var in subscip.getVars(): |
| 116 | + print(f"Variable: {var.name}") |
| 117 | +
|
| 118 | +Creating a Custom IIS Finder |
| 119 | +============================= |
| 120 | + |
| 121 | +You may want to implement your own algorithm to find an IIS. |
| 122 | +PySCIPOpt supports this through the ``IISfinder`` class, which allows you to define custom logic |
| 123 | +for identifying infeasible subsystems. |
| 124 | + |
| 125 | +Basic Structure |
| 126 | +--------------- |
| 127 | + |
| 128 | +To create a custom IIS finder, inherit from the ``IISfinder`` class and implement the ``iisfinderexec`` method: |
| 129 | + |
| 130 | +.. code-block:: python |
| 131 | +
|
| 132 | + from pyscipopt import Model, IISfinder, SCIP_RESULT |
| 133 | +
|
| 134 | + class SimpleIISFinder(IISfinder): |
| 135 | + """ |
| 136 | + A simple IIS finder that removes constraints one by one |
| 137 | + until the problem becomes feasible. |
| 138 | + """ |
| 139 | + |
| 140 | + def iisfinderexec(self): |
| 141 | + subscip = self.iis.getSubscip() |
| 142 | + constraints = subscip.getConss() |
| 143 | + |
| 144 | + # Start with all constraints |
| 145 | + active_constraints = set(constraints) |
| 146 | + |
| 147 | + for cons in constraints: |
| 148 | + # Temporarily remove the constraint |
| 149 | + active_constraints.discard(cons) |
| 150 | + |
| 151 | + # Check if remaining constraints are still infeasible |
| 152 | + # (This would require setting up a sub-problem with only active_constraints) |
| 153 | + # For simplicity, we use the full solve approach here |
| 154 | + |
| 155 | + subscip.freeTransform() |
| 156 | + subscip.delCons(cons) |
| 157 | + subscip.optimize() |
| 158 | + |
| 159 | + if subscip.getStatus() == SCIP_STATUS.INFEASIBLE: |
| 160 | + # Still infeasible without this constraint |
| 161 | + # Keep it removed |
| 162 | + pass |
| 163 | + else: |
| 164 | + # Feasible without it, so constraint is needed in IIS |
| 165 | + active_constraints.add(cons) |
| 166 | + # In practice, you'd recreate the subscip with all active constraints |
| 167 | + |
| 168 | + iis.markIrreducible() |
| 169 | + return {"result": SCIP_RESULT.SUCCESS} |
| 170 | +
|
| 171 | +Including Your Custom IIS Finder |
| 172 | +--------------------------------- |
| 173 | + |
| 174 | +To use your custom IIS finder, include it in the model before calling ``generateIIS()``: |
| 175 | + |
| 176 | +.. code-block:: python |
| 177 | +
|
| 178 | + # Create model |
| 179 | + model = Model() |
| 180 | + |
| 181 | + # Add variables and constraints (infeasible problem) |
| 182 | + x1 = model.addVar("x1", vtype="B") |
| 183 | + x2 = model.addVar("x2", vtype="B") |
| 184 | + x3 = model.addVar("x3", vtype="B") |
| 185 | + |
| 186 | + model.addCons(x1 + x2 == 1, name="c1") |
| 187 | + model.addCons(x2 + x3 == 1, name="c2") |
| 188 | + model.addCons(x1 + x3 == 1, name="c3") |
| 189 | + |
| 190 | + # Create and include the custom IIS finder |
| 191 | + simple_iis = SimpleIISFinder() |
| 192 | + model.includeIISfinder( |
| 193 | + simple_iis, |
| 194 | + name="simpleiis", |
| 195 | + desc="Simple greedy IIS finder", |
| 196 | + priority=1000000 # Higher priority means it will be used first |
| 197 | + ) |
| 198 | + |
| 199 | + # Solve to verify infeasibility |
| 200 | + model.optimize() |
| 201 | + |
| 202 | + # Generate IIS using our custom finder |
| 203 | + iis = model.generateIIS() |
| 204 | + |
| 205 | + # Examine the result |
| 206 | + print(f"\nIIS Information:") |
| 207 | + print(f" Time: {iis.getTime():.2f} seconds") |
| 208 | + print(f" Nodes: {iis.getNNodes()}") |
| 209 | + print(f" Irreducible: {iis.isSubscipIrreducible()}") |
| 210 | + print(f" Number of constraints: {iis.getSubscip().getNConss()}") |
| 211 | +
|
| 212 | +Key Methods in IISfinder |
| 213 | +------------------------- |
| 214 | + |
| 215 | +When implementing a custom IIS finder, you have access to several important methods: |
| 216 | + |
| 217 | +- ``subscip=self.iis.getSubscip()``: Get the sub-problem (Model) containing the candidate IIS |
| 218 | +- ``subscip.getConss()``: Get all constraints in the subscip |
| 219 | +- ``subscip.delCons(cons)``: Remove a constraint from the subscip |
| 220 | +- ``subscip.addCons(cons)``: Add a constraint back to the subscip |
| 221 | +- ``subscip.optimize()``: Solve the subscip |
| 222 | +- ``subscip.getStatus()``: Check if the subscip is infeasible |
0 commit comments