Skip to content

Commit 2d138e3

Browse files
authored
Merge pull request Pyomo#3288 from thisandthatuser/indsetwithin
Fixes bug with IndexedSet objects and the within argument
2 parents f5e6f32 + 643c0e5 commit 2d138e3

File tree

5 files changed

+232
-4
lines changed

5 files changed

+232
-4
lines changed

examples/pyomo/tutorials/set.dat

+3
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,6 @@ set S[5] := 2 3;
1616

1717
set T[2] := 1 3;
1818
set T[5] := 2 3;
19+
20+
set X[2] := 1;
21+
set X[5] := 2 3;

examples/pyomo/tutorials/set.out

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
23 Set Declarations
1+
24 Set Declarations
22
A : Size=1, Index=None, Ordered=Insertion
33
Key : Dimen : Domain : Size : Members
44
None : 1 : Any : 3 : {1, 2, 3}
@@ -89,5 +89,9 @@
8989
2 : 1 : Any : 5 : {1, 3, 5, 7, 9}
9090
3 : 1 : Any : 5 : {1, 4, 7, 10, 13}
9191
4 : 1 : Any : 5 : {1, 5, 9, 13, 17}
92+
X : Size=2, Index=B, Ordered=Insertion
93+
Key : Dimen : Domain : Size : Members
94+
2 : 1 : S[2] : 1 : {1,}
95+
5 : 1 : S[5] : 2 : {2, 3}
9296

93-
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
97+
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

examples/pyomo/tutorials/set.py

+7
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,13 @@ def P_init(model, i, j):
171171
#
172172
model.S = Set(model.B, within=model.A)
173173

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

175182
#
176183
# Validation of set arrays can also be performed with the _validate_ option.

pyomo/core/base/set.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1932,7 +1932,8 @@ class Set(IndexedComponent):
19321932
19331933
within : initialiser(set), optional
19341934
A set that defines the valid values that can be contained
1935-
in this set
1935+
in this set. If the latter is indexed, the former can be indexed or
1936+
non-indexed, in which case it applies to all indices.
19361937
domain : initializer(set), optional
19371938
A set that defines the valid values that can be contained
19381939
in this set
@@ -2218,7 +2219,7 @@ def _getitem_when_not_present(self, index):
22182219

22192220
domain = self._init_domain(_block, index, self)
22202221
if domain is not None:
2221-
domain.construct()
2222+
domain.parent_component().construct()
22222223
if _d is UnknownSetDimen and domain is not None and domain.dimen is not None:
22232224
_d = domain.dimen
22242225

pyomo/core/tests/unit/test_set.py

+213
Original file line numberDiff line numberDiff line change
@@ -4543,9 +4543,11 @@ def test_construction(self):
45434543
m.I = Set(initialize=[1, 2, 3])
45444544
m.J = Set(initialize=[4, 5, 6])
45454545
m.K = Set(initialize=[(1, 4), (2, 6), (3, 5)], within=m.I * m.J)
4546+
m.L = Set(initialize=[1, 3], within=m.I)
45464547
m.II = Set([1, 2, 3], initialize={1: [0], 2: [1, 2], 3: range(3)})
45474548
m.JJ = Set([1, 2, 3], initialize={1: [0], 2: [1, 2], 3: range(3)})
45484549
m.KK = Set([1, 2], initialize=[], dimen=lambda m, i: i)
4550+
m.LL = Set([2, 3], within=m.II, initialize={2: [1, 2], 3: [1]})
45494551

45504552
output = StringIO()
45514553
m.I.pprint(ostream=output)
@@ -4569,23 +4571,28 @@ def test_construction(self):
45694571
'I': [-1, 0],
45704572
'II': {1: [10, 11], 3: [30]},
45714573
'K': [-1, 4, -1, 6, 0, 5],
4574+
'L': [-1],
4575+
'LL': {3: [30]},
45724576
}
45734577
}
45744578
)
45754579

45764580
self.assertEqual(list(i.I), [-1, 0])
45774581
self.assertEqual(list(i.J), [4, 5, 6])
45784582
self.assertEqual(list(i.K), [(-1, 4), (-1, 6), (0, 5)])
4583+
self.assertEqual(list(i.L), [-1])
45794584
self.assertEqual(list(i.II[1]), [10, 11])
45804585
self.assertEqual(list(i.II[3]), [30])
45814586
self.assertEqual(list(i.JJ[1]), [0])
45824587
self.assertEqual(list(i.JJ[2]), [1, 2])
45834588
self.assertEqual(list(i.JJ[3]), [0, 1, 2])
45844589
self.assertEqual(list(i.KK[1]), [])
45854590
self.assertEqual(list(i.KK[2]), [])
4591+
self.assertEqual(list(i.LL[3]), [30])
45864592

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

45904597
# Additional tests for tuplize:
45914598
i = m.create_instance(data={None: {'K': [(1, 4), (2, 6)], 'KK': [1, 4, 2, 6]}})
@@ -6388,3 +6395,209 @@ def test_issue_1112(self):
63886395
self.assertEqual(len(vals), 1)
63896396
self.assertIsInstance(vals[0], SetProduct_OrderedSet)
63906397
self.assertIsNot(vals[0], cross)
6398+
6399+
def test_issue_3284(self):
6400+
# test creating (indexed and non-indexed) sets using the within argument
6401+
# using concrete model and initialization
6402+
problem = ConcreteModel()
6403+
# non-indexed sets not using the within argument
6404+
problem.A = Set(initialize=[1, 2, 3])
6405+
problem.B = Set(dimen=2, initialize=[(1, 2), (3, 4), (5, 6)])
6406+
# non-indexed sets using within argument
6407+
problem.subset_A = Set(within=problem.A, initialize=[2, 3])
6408+
problem.subset_B = Set(within=problem.B, dimen=2, initialize=[(1, 2), (5, 6)])
6409+
# indexed sets not using the within argument
6410+
problem.C = Set(problem.A, initialize={1: [-1, 3], 2: [4, 7], 3: [3, 8]})
6411+
problem.D = Set(
6412+
problem.B, initialize={(1, 2): [1, 5], (3, 4): [3], (5, 6): [6, 8, 9]}
6413+
)
6414+
# indexed sets using an indexed set for the within argument
6415+
problem.subset_C = Set(
6416+
problem.A, within=problem.C, initialize={1: [-1], 2: [4], 3: [3, 8]}
6417+
)
6418+
problem.subset_D = Set(
6419+
problem.B,
6420+
within=problem.D,
6421+
initialize={(1, 2): [1, 5], (3, 4): [], (5, 6): [6]},
6422+
)
6423+
# indexed sets using a non-indexed set for the within argument
6424+
problem.E = Set([0, 1], within=problem.A, initialize={0: [1, 2], 1: [3]})
6425+
problem.F = Set(
6426+
[(1, 2, 3), (4, 5, 6)],
6427+
within=problem.B,
6428+
initialize={(1, 2, 3): [(1, 2)], (4, 5, 6): [(3, 4)]},
6429+
)
6430+
# check them
6431+
self.assertEqual(list(problem.A), [1, 2, 3])
6432+
self.assertEqual(list(problem.B), [(1, 2), (3, 4), (5, 6)])
6433+
self.assertEqual(list(problem.subset_A), [2, 3])
6434+
self.assertEqual(list(problem.subset_B), [(1, 2), (5, 6)])
6435+
self.assertEqual(list(problem.C[1]), [-1, 3])
6436+
self.assertEqual(list(problem.C[2]), [4, 7])
6437+
self.assertEqual(list(problem.C[3]), [3, 8])
6438+
self.assertEqual(list(problem.D[(1, 2)]), [1, 5])
6439+
self.assertEqual(list(problem.D[(3, 4)]), [3])
6440+
self.assertEqual(list(problem.D[(5, 6)]), [6, 8, 9])
6441+
self.assertEqual(list(problem.subset_C[1]), [-1])
6442+
self.assertEqual(list(problem.subset_C[2]), [4])
6443+
self.assertEqual(list(problem.subset_C[3]), [3, 8])
6444+
self.assertEqual(list(problem.subset_D[(1, 2)]), [1, 5])
6445+
self.assertEqual(list(problem.subset_D[(3, 4)]), [])
6446+
self.assertEqual(list(problem.subset_D[(5, 6)]), [6])
6447+
self.assertEqual(list(problem.E[0]), [1, 2])
6448+
self.assertEqual(list(problem.E[1]), [3])
6449+
self.assertEqual(list(problem.F[(1, 2, 3)]), [(1, 2)])
6450+
self.assertEqual(list(problem.F[(4, 5, 6)]), [(3, 4)])
6451+
6452+
# try adding elements to test the domains (1 compatible, 1 incompatible)
6453+
# set subset_A
6454+
problem.subset_A.add(1)
6455+
error_message = (
6456+
"Cannot add value 4 to Set subset_A.\n\tThe value is not in the domain A"
6457+
)
6458+
with self.assertRaisesRegex(ValueError, error_message):
6459+
problem.subset_A.add(4)
6460+
# set subset_B
6461+
problem.subset_B.add((3, 4))
6462+
with self.assertRaisesRegex(ValueError, r".*Cannot add value \(7, 8\)"):
6463+
problem.subset_B.add((7, 8))
6464+
# set subset_C
6465+
problem.subset_C[2].add(7)
6466+
with self.assertRaisesRegex(ValueError, ".*Cannot add value 8 to Set"):
6467+
problem.subset_C[2].add(8)
6468+
# set subset_D
6469+
problem.subset_D[(5, 6)].add(9)
6470+
with self.assertRaisesRegex(ValueError, ".*Cannot add value 2 to Set"):
6471+
problem.subset_D[(3, 4)].add(2)
6472+
# set E
6473+
problem.E[1].add(2)
6474+
with self.assertRaisesRegex(ValueError, ".*Cannot add value 4 to Set"):
6475+
problem.E[1].add(4)
6476+
# set F
6477+
problem.F[(1, 2, 3)].add((3, 4))
6478+
with self.assertRaisesRegex(ValueError, r".*Cannot add value \(4, 3\)"):
6479+
problem.F[(4, 5, 6)].add((4, 3))
6480+
# check them
6481+
self.assertEqual(list(problem.A), [1, 2, 3])
6482+
self.assertEqual(list(problem.B), [(1, 2), (3, 4), (5, 6)])
6483+
self.assertEqual(list(problem.subset_A), [2, 3, 1])
6484+
self.assertEqual(list(problem.subset_B), [(1, 2), (5, 6), (3, 4)])
6485+
self.assertEqual(list(problem.C[1]), [-1, 3])
6486+
self.assertEqual(list(problem.C[2]), [4, 7])
6487+
self.assertEqual(list(problem.C[3]), [3, 8])
6488+
self.assertEqual(list(problem.D[(1, 2)]), [1, 5])
6489+
self.assertEqual(list(problem.D[(3, 4)]), [3])
6490+
self.assertEqual(list(problem.D[(5, 6)]), [6, 8, 9])
6491+
self.assertEqual(list(problem.subset_C[1]), [-1])
6492+
self.assertEqual(list(problem.subset_C[2]), [4, 7])
6493+
self.assertEqual(list(problem.subset_C[3]), [3, 8])
6494+
self.assertEqual(list(problem.subset_D[(1, 2)]), [1, 5])
6495+
self.assertEqual(list(problem.subset_D[(3, 4)]), [])
6496+
self.assertEqual(list(problem.subset_D[(5, 6)]), [6, 9])
6497+
self.assertEqual(list(problem.E[0]), [1, 2])
6498+
self.assertEqual(list(problem.E[1]), [3, 2])
6499+
self.assertEqual(list(problem.F[(1, 2, 3)]), [(1, 2), (3, 4)])
6500+
self.assertEqual(list(problem.F[(4, 5, 6)]), [(3, 4)])
6501+
6502+
# using abstract model and no initialization
6503+
model = AbstractModel()
6504+
# non-indexed sets not using the within argument
6505+
model.A = Set()
6506+
model.B = Set(dimen=2)
6507+
# non-indexed sets using within argument
6508+
model.subset_A = Set(within=model.A)
6509+
model.subset_B = Set(within=model.B, dimen=2)
6510+
# indexed sets not using the within argument
6511+
model.C = Set(model.A)
6512+
model.D = Set(model.B)
6513+
# indexed sets using an indexed set for the within argument
6514+
model.subset_C = Set(model.A, within=model.C)
6515+
model.subset_D = Set(model.B, within=model.D)
6516+
# indexed sets using a non-indexed set for the within argument
6517+
model.E_index = Set()
6518+
model.F_index = Set()
6519+
model.E = Set(model.E_index, within=model.A)
6520+
model.F = Set(model.F_index, within=model.B)
6521+
problem = model.create_instance(
6522+
data={
6523+
None: {
6524+
'A': [3, 4, 5],
6525+
'B': [(1, 2), (7, 8)],
6526+
'subset_A': [3, 4],
6527+
'subset_B': [(1, 2)],
6528+
'C': {3: [3], 4: [4, 8], 5: [5, 6]},
6529+
'D': {(1, 2): [2], (7, 8): [0, 1]},
6530+
'subset_C': {3: [3], 4: [8], 5: []},
6531+
'subset_D': {(1, 2): [], (7, 8): [0, 1]},
6532+
'E_index': [0, 1],
6533+
'F_index': [(1, 2, 3), (4, 5, 6)],
6534+
'E': {0: [3, 4], 1: [5]},
6535+
'F': {(1, 2, 3): [(1, 2)], (4, 5, 6): [(7, 8)]},
6536+
}
6537+
}
6538+
)
6539+
6540+
# check them
6541+
self.assertEqual(list(problem.A), [3, 4, 5])
6542+
self.assertEqual(list(problem.B), [(1, 2), (7, 8)])
6543+
self.assertEqual(list(problem.subset_A), [3, 4])
6544+
self.assertEqual(list(problem.subset_B), [(1, 2)])
6545+
self.assertEqual(list(problem.C[3]), [3])
6546+
self.assertEqual(list(problem.C[4]), [4, 8])
6547+
self.assertEqual(list(problem.C[5]), [5, 6])
6548+
self.assertEqual(list(problem.D[(1, 2)]), [2])
6549+
self.assertEqual(list(problem.D[(7, 8)]), [0, 1])
6550+
self.assertEqual(list(problem.subset_C[3]), [3])
6551+
self.assertEqual(list(problem.subset_C[4]), [8])
6552+
self.assertEqual(list(problem.subset_C[5]), [])
6553+
self.assertEqual(list(problem.subset_D[(1, 2)]), [])
6554+
self.assertEqual(list(problem.subset_D[(7, 8)]), [0, 1])
6555+
self.assertEqual(list(problem.E[0]), [3, 4])
6556+
self.assertEqual(list(problem.E[1]), [5])
6557+
self.assertEqual(list(problem.F[(1, 2, 3)]), [(1, 2)])
6558+
self.assertEqual(list(problem.F[(4, 5, 6)]), [(7, 8)])
6559+
6560+
# try adding elements to test the domains (1 compatible, 1 incompatible)
6561+
# set subset_A
6562+
problem.subset_A.add(5)
6563+
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
6564+
problem.subset_A.add(6)
6565+
# set subset_B
6566+
problem.subset_B.add((7, 8))
6567+
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
6568+
problem.subset_B.add((3, 4))
6569+
# set subset_C
6570+
problem.subset_C[4].add(4)
6571+
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
6572+
problem.subset_C[4].add(9)
6573+
# set subset_D
6574+
problem.subset_D[(1, 2)].add(2)
6575+
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
6576+
problem.subset_D[(1, 2)].add(3)
6577+
# set E
6578+
problem.E[1].add(4)
6579+
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
6580+
problem.E[1].add(1)
6581+
# set F
6582+
problem.F[(1, 2, 3)].add((7, 8))
6583+
with self.assertRaisesRegex(ValueError, ".*Cannot add value "):
6584+
problem.F[(4, 5, 6)].add((4, 3))
6585+
# check them
6586+
self.assertEqual(list(problem.A), [3, 4, 5])
6587+
self.assertEqual(list(problem.B), [(1, 2), (7, 8)])
6588+
self.assertEqual(list(problem.subset_A), [3, 4, 5])
6589+
self.assertEqual(list(problem.subset_B), [(1, 2), (7, 8)])
6590+
self.assertEqual(list(problem.C[3]), [3])
6591+
self.assertEqual(list(problem.C[4]), [4, 8])
6592+
self.assertEqual(list(problem.C[5]), [5, 6])
6593+
self.assertEqual(list(problem.D[(1, 2)]), [2])
6594+
self.assertEqual(list(problem.D[(7, 8)]), [0, 1])
6595+
self.assertEqual(list(problem.subset_C[3]), [3])
6596+
self.assertEqual(list(problem.subset_C[4]), [8, 4])
6597+
self.assertEqual(list(problem.subset_C[5]), [])
6598+
self.assertEqual(list(problem.subset_D[(1, 2)]), [2])
6599+
self.assertEqual(list(problem.subset_D[(7, 8)]), [0, 1])
6600+
self.assertEqual(list(problem.E[0]), [3, 4])
6601+
self.assertEqual(list(problem.E[1]), [5, 4])
6602+
self.assertEqual(list(problem.F[(1, 2, 3)]), [(1, 2), (7, 8)])
6603+
self.assertEqual(list(problem.F[(4, 5, 6)]), [(7, 8)])

0 commit comments

Comments
 (0)