Trouble shooting - Infeasible Model

Note

You can download this example as a Jupyter notebook or start it in interactive mode.

Trouble shooting - Infeasible Model#

From time to time, you encounter models that are infeasible. This means that there is no solution that satisfies all the constraints. Some solvers allow you to get the set of constraints that are infeasible. This is useful for debugging your model.

In the following, we show how linopy can help you with the infeasibility diagnostics by finding the set of constraints that are infeasible. So far the functionality is limited to the gurobi solver. Hopefully, we will be able to extend this to other solvers in the future.

We start by creating a simple model that is infeasible.

[1]:
import linopy
import pandas as pd

m = linopy.Model()

time = pd.RangeIndex(10, name='time')
x = m.add_variables(lower=0, coords=[time], name='x')
y = m.add_variables(lower=0, coords=[time], name='y')

m.add_constraints(x <= 5)
m.add_constraints(y <= 5)
m.add_constraints(x + y >= 12)
[1]:
Constraint `con2` (time: 10):
-----------------------------
[0]: +1 x[0] + 1 y[0] ≥ 12.0
[1]: +1 x[1] + 1 y[1] ≥ 12.0
[2]: +1 x[2] + 1 y[2] ≥ 12.0
[3]: +1 x[3] + 1 y[3] ≥ 12.0
[4]: +1 x[4] + 1 y[4] ≥ 12.0
[5]: +1 x[5] + 1 y[5] ≥ 12.0
[6]: +1 x[6] + 1 y[6] ≥ 12.0
[7]: +1 x[7] + 1 y[7] ≥ 12.0
[8]: +1 x[8] + 1 y[8] ≥ 12.0
[9]: +1 x[9] + 1 y[9] ≥ 12.0

If we now try to solve the model, we get an message that the model is infeasible.

[2]:
m.solve(solver_name='gurobi')
Restricted license - for non-production use only - expires 2025-11-24
Read LP format model from file /tmp/linopy-problem-sfp48p5a.lp
Reading time = 0.00 seconds
obj: 30 rows, 20 columns, 40 nonzeros
Gurobi Optimizer version 11.0.0 build v11.0.0rc2 (linux64 - "Ubuntu 22.04.3 LTS")

CPU model: Intel(R) Xeon(R) Platinum 8175M CPU @ 2.50GHz, instruction set [SSE2|AVX|AVX2|AVX512]
Thread count: 1 physical cores, 2 logical processors, using up to 2 threads

Optimize a model with 30 rows, 20 columns and 40 nonzeros
Model fingerprint: 0x779e65f0
Coefficient statistics:
  Matrix range     [1e+00, 1e+00]
  Objective range  [0e+00, 0e+00]
  Bounds range     [0e+00, 0e+00]
  RHS range        [5e+00, 1e+01]
Presolve removed 20 rows and 20 columns
Presolve time: 0.01s

Solved in 0 iterations and 0.01 seconds (0.00 work units)
Infeasible model
Optimization failed:
Status: warning
Termination condition: infeasible
Solution: 0 primals, 0 duals
Objective: nan
Solver model: available
Solver message: 3

[2]:
('warning', 'infeasible')

Now, we can use the model in the background to find the set of infeasible constraints. The following code will return a list constraint labels that are infeasible.

[3]:
labels = m.compute_infeasibilities()
labels

Gurobi Optimizer version 11.0.0 build v11.0.0rc2 (linux64 - "Ubuntu 22.04.3 LTS")

CPU model: Intel(R) Xeon(R) Platinum 8175M CPU @ 2.50GHz, instruction set [SSE2|AVX|AVX2|AVX512]
Thread count: 1 physical cores, 2 logical processors, using up to 2 threads


IIS computed: 3 constraints and 0 bounds
IIS runtime: 0.00 seconds (0.00 work units)
[3]:
[0, 10, 20]

Using the print_labels function, we can print the constraints that we found to be infeasible.

[4]:
m.constraints.print_labels(labels)
con0[0]: +1 x[0] ≤ 5
con1[0]: +1 y[0] ≤ 5
con2[0]: +1 x[0] + 1 y[0] ≥ 12

The two function calls above can be combined into a single call:

[5]:
m.print_infeasibilities()
Gurobi Optimizer version 11.0.0 build v11.0.0rc2 (linux64 - "Ubuntu 22.04.3 LTS")

CPU model: Intel(R) Xeon(R) Platinum 8175M CPU @ 2.50GHz, instruction set [SSE2|AVX|AVX2|AVX512]
Thread count: 1 physical cores, 2 logical processors, using up to 2 threads


IIS computed: 3 constraints and 0 bounds
IIS runtime: 0.00 seconds (0.00 work units)
con0[0]: +1 x[0] ≤ 5
con1[0]: +1 y[0] ≤ 5
con2[0]: +1 x[0] + 1 y[0] ≥ 12