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 @@ -16,3 +16,6 @@ set S[5] := 2 3;

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

set X[2] := 1;
set X[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 @@ -2218,7 +2219,7 @@ def _getitem_when_not_present(self, index):

domain = self._init_domain(_block, index, self)
if domain is not None:
domain.construct()
domain.parent_component().construct()
if _d is UnknownSetDimen and domain is not None and domain.dimen is not None:
_d = domain.dimen

Expand Down
215 changes: 215 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,211 @@ 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_message = (
"Cannot add value %s to Set %s.\n"
"\tThe value is not in the domain %s"
% (4, 'subset_A', 'A')
)
with self.assertRaisesRegex(ValueError, error_message):
problem.subset_A.add(4)
# set subset_B
problem.subset_B.add((3, 4))
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
problem.subset_B.add((7, 8))
# set subset_C
problem.subset_C[2].add(7)
with self.assertRaisesRegex(ValueError, ".*Cannot add value 8 to Set"):
problem.subset_C[2].add(8)
# set subset_D
problem.subset_D[(5, 6)].add(9)
with self.assertRaisesRegex(ValueError, ".*Cannot add value 2 to Set"):
problem.subset_D[(3, 4)].add(2)
# set E
problem.E[1].add(2)
with self.assertRaisesRegex(ValueError, ".*Cannot add value 4 to Set"):
problem.E[1].add(4)
# set F
problem.F[(1, 2, 3)].add((3, 4))
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
problem.F[(4, 5, 6)].add((4, 3))
# 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)]), [])
self.assertEqual(list(problem.subset_D[(5, 6)]), [6, 9])
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)
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
problem.subset_A.add(6)
# set subset_B
problem.subset_B.add((7, 8))
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
problem.subset_B.add((3, 4))
# set subset_C
problem.subset_C[4].add(4)
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
problem.subset_C[4].add(9)
# set subset_D
problem.subset_D[(1, 2)].add(2)
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
problem.subset_D[(1, 2)].add(3)
# set E
problem.E[1].add(4)
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
problem.E[1].add(1)
# set F
problem.F[(1, 2, 3)].add((7, 8))
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
problem.F[(4, 5, 6)].add((4, 3))
# 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