pyomo icon indicating copy to clipboard operation
pyomo copied to clipboard

LP writer dies given a Var with an empty domain

Open emma58 opened this issue 1 year ago • 3 comments

Summary

If I create a Var that has an empty domain created by intersecting Binary and $\{2, 3\}$ (the model is structurally infeasible), the LP writer croaks somewhere in figuring out the domain (I think).

Steps to reproduce the issue

from pyomo.environ import *

m = ConcreteModel()
m.thing = Set(initialize=[2, 3])
m.x = Var(domain=Binary & m.thing)

m.c = Constraint(expr=m.x >= 0.5)

m.obj = Objective(expr=m.x)

SolverFactory('gurobi').solve(m, tee=True)

Error Message

Traceback (most recent call last):
  File "/home/esjohn/src/pyomo/pyomo/core/base/set.py", line 729, in _get_discrete_interval
    step = min(abs(r.step) for r in ranges if r.step != 0)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
ValueError: min() arg is an empty sequence

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/esjohn/src/pyomo/just_checking.py", line 11, in <module>
    SolverFactory('gurobi').solve(m, tee=True)
  File "/home/esjohn/src/pyomo/pyomo/opt/base/solvers.py", line 598, in solve
    self._presolve(*args, **kwds)
  File "/home/esjohn/src/pyomo/pyomo/solvers/plugins/solvers/GUROBI.py", line 249, in _presolve
    ILMLicensedSystemCallSolver._presolve(self, *args, **kwds)
  File "/home/esjohn/src/pyomo/pyomo/opt/solver/shellcmd.py", line 223, in _presolve
    OptSolver._presolve(self, *args, **kwds)
  File "/home/esjohn/src/pyomo/pyomo/opt/base/solvers.py", line 704, in _presolve
    self._convert_problem(
  File "/home/esjohn/src/pyomo/pyomo/opt/base/solvers.py", line 756, in _convert_problem
    return convert_problem(
           ^^^^^^^^^^^^^^^^
  File "/home/esjohn/src/pyomo/pyomo/opt/base/convert.py", line 97, in convert_problem
    problem_files, symbol_map = converter.apply(*tmp, **tmpkw)
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/esjohn/src/pyomo/pyomo/solvers/plugins/converter/model.py", line 78, in apply
    (problem_filename, symbol_map_id) = instance.write(
                                        ^^^^^^^^^^^^^^^
  File "/home/esjohn/src/pyomo/pyomo/core/base/block.py", line 1940, in write
    (filename, smap) = problem_writer(self, filename, solver_capability, io_options)
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/esjohn/src/pyomo/pyomo/repn/plugins/lp_writer.py", line 208, in __call__
    info = self.write(model, FILE, **io_options)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/esjohn/src/pyomo/pyomo/repn/plugins/lp_writer.py", line 241, in write
    return _LPWriter_impl(ostream, config).write(model)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/esjohn/src/pyomo/pyomo/repn/plugins/lp_writer.py", line 522, in write
    if v.is_binary():
       ^^^^^^^^^^^^^
  File "/home/esjohn/src/pyomo/pyomo/core/base/var.py", line 190, in is_binary
    return domain.get_interval() == (0, 1, 1)
           ^^^^^^^^^^^^^^^^^^^^^
  File "/home/esjohn/src/pyomo/pyomo/core/base/set.py", line 704, in get_interval
    return self._get_discrete_interval()
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/esjohn/src/pyomo/pyomo/core/base/set.py", line 738, in _get_discrete_interval
    return (vals[0], vals[0], 0)
            ~~~~^^^
IndexError: list index out of range

Information on your system

Pyomo version: main Python version: 3.11 Operating system: How Pyomo was installed (PyPI, conda, source): Solver (if applicable):

emma58 avatar Apr 18 '24 18:04 emma58

Hi, I would like to understand the correct behavior we would want to see here to be able to provide a fix for this. I have tried replacing Binary & m.thing by Set(initialize=[]) and this runs fine with the solver 'gurobi' and yield the lp below. Would you expect this model in your example ?

* Source Pyomo model name=unknown *\

min x1: +1 x2

s.t.

c_l_x3_: +1 x2 >= 0.5

bounds -inf <= x2 <= +inf end

quantresearch1 avatar Sep 13 '24 04:09 quantresearch1

I think the correct behavior is for get_interval() to return (None, None, None) for Sets with no ranges.

That should cause the LP writer (and all the other writers) to die on error: the variable is not binary, integer, or continuous, so the model is not a (MI)LP [or (MI)QP] -- and not compatible with the writer format (so the fact that the LP writer is OK with your modified model is also probably an error). My feeling is that domain errors like this aren't quite the same thing as an "infeasible model", and should probably return a different error (I could also probably be convinced that I am wrong - so it would be good to bring this up at the Dev call or have others weigh in here).

jsiirola avatar Sep 13 '24 13:09 jsiirola

BWOM: We looked at this issue during the dev call on 1/21/2025 and @jsiirola volunteered to look at it.

blnicho avatar Jan 21 '25 20:01 blnicho