Skip to content

Commit 6ded37c

Browse files
committed
iis tutorial
1 parent 91c3598 commit 6ded37c

File tree

1 file changed

+222
-0
lines changed

1 file changed

+222
-0
lines changed

docs/tutorials/iis.rst

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
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

Comments
 (0)