Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes bug with IndexedSet objects and the within argument #3288

Merged
merged 11 commits into from
Jul 11, 2024
3 changes: 3 additions & 0 deletions examples/pyomo/tutorials/set.dat
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,8 @@ set M := 1 3;
set S[2] := 1 3;
set S[5] := 2 3;

set X[2] := 1;
set X[5] := 2 3;

blnicho marked this conversation as resolved.
Show resolved Hide resolved
set T[2] := 1 3;
set T[5] := 2 3;
8 changes: 6 additions & 2 deletions examples/pyomo/tutorials/set.out
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
23 Set Declarations
24 Set Declarations
A : Size=1, Index=None, Ordered=Insertion
Key : Dimen : Domain : Size : Members
None : 1 : Any : 3 : {1, 2, 3}
Expand Down Expand Up @@ -89,5 +89,9 @@
2 : 1 : Any : 5 : {1, 3, 5, 7, 9}
3 : 1 : Any : 5 : {1, 4, 7, 10, 13}
4 : 1 : Any : 5 : {1, 5, 9, 13, 17}
X : Size=2, Index=B, Ordered=Insertion
Key : Dimen : Domain : Size : Members
2 : 1 : S[2] : 1 : {1,}
5 : 1 : S[5] : 2 : {2, 3}

23 Declarations: A B C D E F G H Hsub I J K K_2 L M N O P R S T U V
24 Declarations: A B C D E F G H Hsub I J K K_2 L M N O P R S X T U V
7 changes: 7 additions & 0 deletions examples/pyomo/tutorials/set.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,13 @@ def P_init(model, i, j):
#
model.S = Set(model.B, within=model.A)

#
# Validation of a set array can also be linked to another set array. If so, the
# elements under each index must also be found under the corresponding index in
# the validation set array:
#
model.X = Set(model.B, within=model.S)


#
# Validation of set arrays can also be performed with the _validate_ option.
Expand Down
5 changes: 3 additions & 2 deletions pyomo/core/base/set.py
Original file line number Diff line number Diff line change
Expand Up @@ -1932,7 +1932,8 @@ class Set(IndexedComponent):

within : initialiser(set), optional
A set that defines the valid values that can be contained
in this set
in this set. If the latter is indexed, the former can be indexed or
non-indexed, in which case it applies to all indices.
domain : initializer(set), optional
A set that defines the valid values that can be contained
in this set
Expand Down Expand Up @@ -2217,7 +2218,7 @@ def _getitem_when_not_present(self, index):
_d = None

domain = self._init_domain(_block, index, self)
if domain is not None:
if domain is not None and hasattr(domain, "construct"):
domain.construct()
blnicho marked this conversation as resolved.
Show resolved Hide resolved
if _d is UnknownSetDimen and domain is not None and domain.dimen is not None:
_d = domain.dimen
Expand Down
258 changes: 258 additions & 0 deletions pyomo/core/tests/unit/test_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -4543,9 +4543,11 @@ def test_construction(self):
m.I = Set(initialize=[1, 2, 3])
m.J = Set(initialize=[4, 5, 6])
m.K = Set(initialize=[(1, 4), (2, 6), (3, 5)], within=m.I * m.J)
m.L = Set(initialize=[1, 3], within=m.I)
m.II = Set([1, 2, 3], initialize={1: [0], 2: [1, 2], 3: range(3)})
m.JJ = Set([1, 2, 3], initialize={1: [0], 2: [1, 2], 3: range(3)})
m.KK = Set([1, 2], initialize=[], dimen=lambda m, i: i)
m.LL = Set([2, 3], within=m.II, initialize={2: [1, 2], 3: [1]})

output = StringIO()
m.I.pprint(ostream=output)
Expand All @@ -4569,23 +4571,28 @@ def test_construction(self):
'I': [-1, 0],
'II': {1: [10, 11], 3: [30]},
'K': [-1, 4, -1, 6, 0, 5],
'L': [-1],
'LL': {3: [30]},
}
}
)

self.assertEqual(list(i.I), [-1, 0])
self.assertEqual(list(i.J), [4, 5, 6])
self.assertEqual(list(i.K), [(-1, 4), (-1, 6), (0, 5)])
self.assertEqual(list(i.L), [-1])
self.assertEqual(list(i.II[1]), [10, 11])
self.assertEqual(list(i.II[3]), [30])
self.assertEqual(list(i.JJ[1]), [0])
self.assertEqual(list(i.JJ[2]), [1, 2])
self.assertEqual(list(i.JJ[3]), [0, 1, 2])
self.assertEqual(list(i.KK[1]), [])
self.assertEqual(list(i.KK[2]), [])
self.assertEqual(list(i.LL[3]), [30])

# Implicitly-constructed set should fall back on initialize!
self.assertEqual(list(i.II[2]), [1, 2])
self.assertEqual(list(i.LL[2]), [1, 2])

# Additional tests for tuplize:
i = m.create_instance(data={None: {'K': [(1, 4), (2, 6)], 'KK': [1, 4, 2, 6]}})
Expand Down Expand Up @@ -6388,3 +6395,254 @@ def test_issue_1112(self):
self.assertEqual(len(vals), 1)
self.assertIsInstance(vals[0], SetProduct_OrderedSet)
self.assertIsNot(vals[0], cross)

def test_issue_3284(self):
# test creating (indexed and non-indexed) sets using the within argument
# using concrete model and initialization
problem = ConcreteModel()
# non-indexed sets not using the within argument
problem.A = Set(initialize=[1, 2, 3])
problem.B = Set(dimen=2, initialize=[(1, 2), (3, 4), (5, 6)])
# non-indexed sets using within argument
problem.subset_A = Set(within=problem.A, initialize=[2, 3])
problem.subset_B = Set(within=problem.B, dimen=2, initialize=[(1, 2), (5, 6)])
# indexed sets not using the within argument
problem.C = Set(problem.A, initialize={1: [-1, 3], 2: [4, 7], 3: [3, 8]})
problem.D = Set(
problem.B, initialize={(1, 2): [1, 5], (3, 4): [3], (5, 6): [6, 8, 9]}
)
# indexed sets using an indexed set for the within argument
problem.subset_C = Set(
problem.A, within=problem.C, initialize={1: [-1], 2: [4], 3: [3, 8]}
)
problem.subset_D = Set(
problem.B,
within=problem.D,
initialize={(1, 2): [1, 5], (3, 4): [], (5, 6): [6]},
)
# indexed sets using a non-indexed set for the within argument
problem.E = Set([0, 1], within=problem.A, initialize={0: [1, 2], 1: [3]})
problem.F = Set(
[(1, 2, 3), (4, 5, 6)],
within=problem.B,
initialize={(1, 2, 3): [(1, 2)], (4, 5, 6): [(3, 4)]},
)
# check them
self.assertEqual(list(problem.A), [1, 2, 3])
self.assertEqual(list(problem.B), [(1, 2), (3, 4), (5, 6)])
self.assertEqual(list(problem.subset_A), [2, 3])
self.assertEqual(list(problem.subset_B), [(1, 2), (5, 6)])
self.assertEqual(list(problem.C[1]), [-1, 3])
self.assertEqual(list(problem.C[2]), [4, 7])
self.assertEqual(list(problem.C[3]), [3, 8])
self.assertEqual(list(problem.D[(1, 2)]), [1, 5])
self.assertEqual(list(problem.D[(3, 4)]), [3])
self.assertEqual(list(problem.D[(5, 6)]), [6, 8, 9])
self.assertEqual(list(problem.subset_C[1]), [-1])
self.assertEqual(list(problem.subset_C[2]), [4])
self.assertEqual(list(problem.subset_C[3]), [3, 8])
self.assertEqual(list(problem.subset_D[(1, 2)]), [1, 5])
self.assertEqual(list(problem.subset_D[(3, 4)]), [])
self.assertEqual(list(problem.subset_D[(5, 6)]), [6])
self.assertEqual(list(problem.E[0]), [1, 2])
self.assertEqual(list(problem.E[1]), [3])
self.assertEqual(list(problem.F[(1, 2, 3)]), [(1, 2)])
self.assertEqual(list(problem.F[(4, 5, 6)]), [(3, 4)])

# try adding elements to test the domains (1 compatible, 1 incompatible)
# set subset_A
problem.subset_A.add(1)
error_raised = False
try:
problem.subset_A.add(4)
except ValueError:
error_raised = True
self.assertEqual(error_raised, True)
blnicho marked this conversation as resolved.
Show resolved Hide resolved
# set subset_B
problem.subset_B.add((3, 4))
error_raised = False
try:
problem.subset_B.add((7, 8))
except ValueError:
error_raised = True
self.assertEqual(error_raised, True)
# set subset_C
problem.subset_C[2].add(7)
error_raised = False
try:
problem.subset_C[2].add(8)
except ValueError:
error_raised = True
self.assertEqual(error_raised, True)
# set subset_D
problem.subset_D[(3, 4)].add(3)
error_raised = False
try:
problem.subset_D[(3, 4)].add(4)
except ValueError:
error_raised = True
self.assertEqual(error_raised, True)
# set E
problem.E[1].add(2)
error_raised = False
try:
problem.E[1].add(4)
except ValueError:
error_raised = True
self.assertEqual(error_raised, True)
# set F
problem.F[(1, 2, 3)].add((3, 4))
error_raised = False
try:
problem.F[(4, 5, 6)].add((4, 3))
except ValueError:
error_raised = True
self.assertEqual(error_raised, True)
# check them
self.assertEqual(list(problem.A), [1, 2, 3])
self.assertEqual(list(problem.B), [(1, 2), (3, 4), (5, 6)])
self.assertEqual(list(problem.subset_A), [2, 3, 1])
self.assertEqual(list(problem.subset_B), [(1, 2), (5, 6), (3, 4)])
self.assertEqual(list(problem.C[1]), [-1, 3])
self.assertEqual(list(problem.C[2]), [4, 7])
self.assertEqual(list(problem.C[3]), [3, 8])
self.assertEqual(list(problem.D[(1, 2)]), [1, 5])
self.assertEqual(list(problem.D[(3, 4)]), [3])
self.assertEqual(list(problem.D[(5, 6)]), [6, 8, 9])
self.assertEqual(list(problem.subset_C[1]), [-1])
self.assertEqual(list(problem.subset_C[2]), [4, 7])
self.assertEqual(list(problem.subset_C[3]), [3, 8])
self.assertEqual(list(problem.subset_D[(1, 2)]), [1, 5])
self.assertEqual(list(problem.subset_D[(3, 4)]), [3])
self.assertEqual(list(problem.subset_D[(5, 6)]), [6])
self.assertEqual(list(problem.E[0]), [1, 2])
self.assertEqual(list(problem.E[1]), [3, 2])
self.assertEqual(list(problem.F[(1, 2, 3)]), [(1, 2), (3, 4)])
self.assertEqual(list(problem.F[(4, 5, 6)]), [(3, 4)])

# using abstract model and no initialization
model = AbstractModel()
# non-indexed sets not using the within argument
model.A = Set()
model.B = Set(dimen=2)
# non-indexed sets using within argument
model.subset_A = Set(within=model.A)
model.subset_B = Set(within=model.B, dimen=2)
# indexed sets not using the within argument
model.C = Set(model.A)
model.D = Set(model.B)
# indexed sets using an indexed set for the within argument
model.subset_C = Set(model.A, within=model.C)
model.subset_D = Set(model.B, within=model.D)
# indexed sets using a non-indexed set for the within argument
model.E_index = Set()
model.F_index = Set()
model.E = Set(model.E_index, within=model.A)
model.F = Set(model.F_index, within=model.B)
problem = model.create_instance(
data={
None: {
'A': [3, 4, 5],
'B': [(1, 2), (7, 8)],
'subset_A': [3, 4],
'subset_B': [(1, 2)],
'C': {3: [3], 4: [4, 8], 5: [5, 6]},
'D': {(1, 2): [2], (7, 8): [0, 1]},
'subset_C': {3: [3], 4: [8], 5: []},
'subset_D': {(1, 2): [], (7, 8): [0, 1]},
'E_index': [0, 1],
'F_index': [(1, 2, 3), (4, 5, 6)],
'E': {0: [3, 4], 1: [5]},
'F': {(1, 2, 3): [(1, 2)], (4, 5, 6): [(7, 8)]},
}
}
)

# check them
self.assertEqual(list(problem.A), [3, 4, 5])
self.assertEqual(list(problem.B), [(1, 2), (7, 8)])
self.assertEqual(list(problem.subset_A), [3, 4])
self.assertEqual(list(problem.subset_B), [(1, 2)])
self.assertEqual(list(problem.C[3]), [3])
self.assertEqual(list(problem.C[4]), [4, 8])
self.assertEqual(list(problem.C[5]), [5, 6])
self.assertEqual(list(problem.D[(1, 2)]), [2])
self.assertEqual(list(problem.D[(7, 8)]), [0, 1])
self.assertEqual(list(problem.subset_C[3]), [3])
self.assertEqual(list(problem.subset_C[4]), [8])
self.assertEqual(list(problem.subset_C[5]), [])
self.assertEqual(list(problem.subset_D[(1, 2)]), [])
self.assertEqual(list(problem.subset_D[(7, 8)]), [0, 1])
self.assertEqual(list(problem.E[0]), [3, 4])
self.assertEqual(list(problem.E[1]), [5])
self.assertEqual(list(problem.F[(1, 2, 3)]), [(1, 2)])
self.assertEqual(list(problem.F[(4, 5, 6)]), [(7, 8)])

# try adding elements to test the domains (1 compatible, 1 incompatible)
# set subset_A
problem.subset_A.add(5)
error_raised = False
try:
problem.subset_A.add(6)
except ValueError:
error_raised = True
self.assertEqual(error_raised, True)
# set subset_B
problem.subset_B.add((7, 8))
error_raised = False
try:
problem.subset_B.add((3, 4))
except ValueError:
error_raised = True
self.assertEqual(error_raised, True)
# set subset_C
problem.subset_C[4].add(4)
error_raised = False
try:
problem.subset_C[4].add(9)
except ValueError:
error_raised = True
self.assertEqual(error_raised, True)
# set subset_D
problem.subset_D[(1, 2)].add(2)
error_raised = False
try:
problem.subset_D[(1, 2)].add(3)
except ValueError:
error_raised = True
self.assertEqual(error_raised, True)
# set E
problem.E[1].add(4)
error_raised = False
try:
problem.E[1].add(1)
except ValueError:
error_raised = True
self.assertEqual(error_raised, True)
# set F
problem.F[(1, 2, 3)].add((7, 8))
error_raised = False
try:
problem.F[(4, 5, 6)].add((4, 3))
except ValueError:
error_raised = True
self.assertEqual(error_raised, True)
# check them
self.assertEqual(list(problem.A), [3, 4, 5])
self.assertEqual(list(problem.B), [(1, 2), (7, 8)])
self.assertEqual(list(problem.subset_A), [3, 4, 5])
self.assertEqual(list(problem.subset_B), [(1, 2), (7, 8)])
self.assertEqual(list(problem.C[3]), [3])
self.assertEqual(list(problem.C[4]), [4, 8])
self.assertEqual(list(problem.C[5]), [5, 6])
self.assertEqual(list(problem.D[(1, 2)]), [2])
self.assertEqual(list(problem.D[(7, 8)]), [0, 1])
self.assertEqual(list(problem.subset_C[3]), [3])
self.assertEqual(list(problem.subset_C[4]), [8, 4])
self.assertEqual(list(problem.subset_C[5]), [])
self.assertEqual(list(problem.subset_D[(1, 2)]), [2])
self.assertEqual(list(problem.subset_D[(7, 8)]), [0, 1])
self.assertEqual(list(problem.E[0]), [3, 4])
self.assertEqual(list(problem.E[1]), [5, 4])
self.assertEqual(list(problem.F[(1, 2, 3)]), [(1, 2), (7, 8)])
self.assertEqual(list(problem.F[(4, 5, 6)]), [(7, 8)])
Loading