Open
Description
Summary
As far as I can tell, if you return Block.Skip
from a block rule, you just get an empty Block, but the key is still valid. This seems unexpected relative to the behavior of something like Constraint.Skip.
This is kind of sad from the perspective of maximizing laziness: Since Skipping is all about creating indexing Sets on the fly that you probably could have actually thought about somewhere else, this is unfortunate in that you can't rely on iterating through an IndexedBlock's keys to know what is actually real.
Steps to reproduce the issue
from pyomo.environ import *
m = ConcreteModel()
m.I = Set(initialize=range(6))
m.x = Var(m.I)
@m.Constraint(m.I)
def cons(m, i):
if i % 2 == 0:
return Constraint.Skip
return m.x[i] - m.x[i - 1] >= i
@m.Block(m.I)
def blk(b, i):
if i % 2 == 1:
return Block.Skip
b.y = Var()
b.cons = Constraint(expr=b.y <= i)
return b
m.pprint()
print(1 in m.cons) # expect and get True
print(2 in m.cons) # expect and get False
print(1 in m.blk) # expect False, but get True
print(2 in m.blk) # expect and get True
# (beating a dead horse...)
m.blk[1].pprint() # empty BlockData
m.cons[2] # KeyError
output:
1 Set Declarations
I : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 6 : {0, 1, 2, 3, 4, 5}
1 Var Declarations
x : Size=6, Index=I
Key : Lower : Value : Upper : Fixed : Stale : Domain
0 : None : None : None : False : True : Reals
1 : None : None : None : False : True : Reals
2 : None : None : None : False : True : Reals
3 : None : None : None : False : True : Reals
4 : None : None : None : False : True : Reals
5 : None : None : None : False : True : Reals
1 Constraint Declarations
cons : Size=3, Index=I, Active=True
Key : Lower : Body : Upper : Active
1 : 1.0 : x[1] - x[0] : +Inf : True
3 : 3.0 : x[3] - x[2] : +Inf : True
5 : 5.0 : x[5] - x[4] : +Inf : True
1 Block Declarations
blk : Size=6, Index=I, Active=True
blk[0] : Active=True
1 Var Declarations
y : Size=1, Index=None
Key : Lower : Value : Upper : Fixed : Stale : Domain
None : None : None : None : False : True : Reals
1 Constraint Declarations
cons : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : -Inf : blk[0].y : 0.0 : True
2 Declarations: y cons
blk[1] : Active=True
0 Declarations:
blk[2] : Active=True
1 Var Declarations
y : Size=1, Index=None
Key : Lower : Value : Upper : Fixed : Stale : Domain
None : None : None : None : False : True : Reals
1 Constraint Declarations
cons : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : -Inf : blk[2].y : 2.0 : True
2 Declarations: y cons
blk[3] : Active=True
0 Declarations:
blk[4] : Active=True
1 Var Declarations
y : Size=1, Index=None
Key : Lower : Value : Upper : Fixed : Stale : Domain
None : None : None : None : False : True : Reals
1 Constraint Declarations
cons : Size=1, Index=None, Active=True
Key : Lower : Body : Upper : Active
None : -Inf : blk[4].y : 4.0 : True
2 Declarations: y cons
blk[5] : Active=True
0 Declarations:
4 Declarations: I x cons blk
True
False
True
True
{Member of blk} : Size=6, Index=I, Active=True
blk[1] : Active=True
0 Declarations:
Traceback (most recent call last):
File "/home/esjohn/src/distribution/distribution/models/cont_time_alt_paths_gdp/block_skip.py", line 28, in <module>
m.cons[2] # KeyError
~~~~~~^^^
File "/home/esjohn/src/pyomo/pyomo/core/base/indexed_component.py", line 660, in __getitem__
return self._getitem_when_not_present(index)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/esjohn/src/pyomo/pyomo/core/base/constraint.py", line 830, in _getitem_when_not_present
raise KeyError(idx)
KeyError: 2
Information on your system
Pyomo version: main
Python version: 3.11
Operating system: linux
How Pyomo was installed (PyPI, conda, source): source
Solver (if applicable):