Loading...
Searching...
No Matches
domain_checking.py
Go to the documentation of this file.
20
21import sys
22from gams import GamsWorkspace, GamsSet
23
24if __name__ == "__main__":
25 sys_dir = sys.argv[1] if len(sys.argv) > 1 else None
26 ws = GamsWorkspace(system_directory=sys_dir)
27
28 # define some data by using Python data structures
29 plants = ["Seattle", "San-Diego"]
30 markets = ["New-York", "Chicago", "Topeka"]
31 capacity = {"Seattle": 350.0, "San-Diego": 600.0}
32 demand = {"New-York": 325.0, "Chicago": 300.0, "Topeka": 275.0}
33 distance = {
34 ("Seattle", "New-York"): 2.5,
35 ("Seattle", "Chicago"): 1.7,
36 ("Seattle", "Topeka"): 1.8,
37 ("San-Diego", "New-York"): 2.5,
38 ("San-Diego", "Chicago"): 1.8,
39 ("San-Diego", "Topeka"): 1.4,
40 }
41
42 # prepare a GamsDatabase with data from the Python data structures
43 db = ws.add_database()
44
45 # add two sets to the GamsDatabase
46 i = db.add_set("i", 1, "canning plants")
47 for p in plants:
48 i.add_record(p)
49
50 j = GamsSet(db, "j", 1, "markets")
51 for m in markets:
52 j.add_record(m)
53
54 # add a parameter with domain information
55 a = db.add_parameter_dc("a", [i], "capacity at plant")
56 for p in plants:
57 a.add_record(p).value = capacity[p]
58
59 # if we see a domain violation something went wrong
60 if not a.check_domains():
61 raise Exception("Unexpected domain violation in a")
62
63 # add a parameter with relaxed domain information
64 b = db.add_parameter_dc("b", ["j"], "demand at market j in cases")
65 for m in markets:
66 b.add_record(m).value = demand[m]
67
68 # if we see a domain violation something went wrong
69 if not b.check_domains():
70 raise Exception("Unexpected domain violation in b")
71
72 # add a 2-dim parameter with domain information
73 d = db.add_parameter_dc("d", [i, j], "distance in thousands of miles")
74 for k, v in iter(distance.items()):
75 d.add_record((k[0], k[1])).value = v
76
77 # if we see a domain violation something went wrong
78 if not d.check_domains():
79 raise Exception("Unexpected domain violation in d")
80
81 # if we see a domain violation in the database something went wrong
82 if not db.check_domains():
83 raise Exception("Unexpected domain violation in db")
84
85 # create some "wrong" entries
86 d.add_record(("Seattle", "aa")).value = 1
87 d.add_record(("bb", "Seattle")).value = 1
88 a.add_record("aa").value = 1
89 a.add_record("bb").value = 1
90 b.add_record("bb").value = 1
91 b.add_record("aa").value = 1
92
93 # now the GamsDatabase as well as the symbols a and d should have domain violations
94 if db.check_domains():
95 raise Exception("Domain violation for db not recognized")
96
97 if a.check_domains():
98 raise Exception("Domain violation for a not recognized")
99
100 if d.check_domains():
101 raise Exception("Domain violation for d not recognized")
102
103 # b in contrast was defined with relaxed domain info only, therefore we should never see a domain violation
104 if not b.check_domains():
105 raise Exception("Unexpected domain violation in b")
106
107 # for symbol a we should see 2 domain violations ("aa" and "bb")
108 dv_count = 0
109 print("Domain Violations of a:")
110 for sdv in a.get_symbol_dvs():
111 print(" > ", end="")
112 for vi in sdv.violation_idx:
113 print(vi, end=" ")
114 dv_count += 1
115 print(f"<> {sdv.symbol_record.keys}<<")
116
117 if dv_count != 2:
118 raise Exception(
119 f"Number of domain violations for a should be 2 but saw {dv_count}"
120 )
121
122 # for d we should see 3 domain violations ("Seattle", "aa", "bb")
123 dv_count = 0
124 print("Domain Violations of d:")
125 for sdv in d.get_symbol_dvs():
126 print(" > ", end="")
127 for vi in sdv.violation_idx:
128 print(vi, end=" ")
129 if vi:
130 dv_count += 1
131 print(f"<> {sdv.symbol_record.keys}<<")
132
133 if dv_count != 3:
134 raise Exception(
135 f"Number of domain violations for a should be 3 but saw {dv_count}"
136 )
137
138 # for db we should see 5 domain violations (all the ones from a and d)
139 dv_count = 0
140 print("Domain Violations of db:")
141 for ddv in db.get_database_dvs():
142 print(f" > {ddv.symbol.name}:")
143 for sdv in ddv.symbol_dvs:
144 print(" ", end="")
145 for vi in sdv.violation_idx:
146 print(vi, end=" ")
147 if vi:
148 dv_count += 1
149 print(f"<> {sdv.symbol_record.keys}<<")
150
151 if dv_count != 5:
152 raise Exception(
153 f"Number of domain violations for db should be 5 but saw {dv_count}"
154 )
155
156 # now we limit the amount of violated records reported to a total of 3
157 dv_count = 0
158 print("Domain Violations of db:")
159 for ddv in db.get_database_dvs(3):
160 print(f" > {ddv.symbol.name}:")
161 for sdv in ddv.symbol_dvs:
162 print(" ", end="")
163 for vi in sdv.violation_idx:
164 print(vi, end=" ")
165 if vi:
166 dv_count += 1
167 print(f"<> {sdv.symbol_record.keys}<<")
168
169 if dv_count != 3:
170 raise Exception(
171 f"Number of domain violations for db should be 3 but saw {dv_count}"
172 )
173
174 # now we limit the amount of violated records reported to 1 per symbol
175 dv_count = 0
176 print("Domain Violations of db:")
177 for ddv in db.get_database_dvs(0, 1):
178 print(f" > {ddv.symbol.name}:")
179 for sdv in ddv.symbol_dvs:
180 print(" ", end="")
181 for vi in sdv.violation_idx:
182 print(vi, end=" ")
183 if vi:
184 dv_count += 1
185 print(f"<> {sdv.symbol_record.keys}<<")
186
187 if dv_count != 2:
188 raise Exception(
189 f"Number of domain violations for db should be 2 but saw {dv_count}"
190 )
191
192 # by default we should get an exception when exporting a GamsDatabase with domain violations
193 try:
194 db.export("test.gdx")
195 except:
196 db.suppress_auto_domain_checking = True
197 db.export("test.gdx")
198 else:
199 raise Exception(
200 "It should not be possible to export a GamsDatabase containing domain violations by default"
201 )
202
203 # read a parameter with domain info from GDX
204 db2 = ws.add_database_from_gdx("test.gdx")
205 d2 = db2["d"]
206
207 # the domain of the parameter should be GamsSet i and GamsSet j
208 for s in d2.domains:
209 if isinstance(s, GamsSet):
210 if s.name == "i":
211 for rec in s:
212 if not rec.key(0) in plants:
213 raise Exception(
214 f"Unexpected label {rec.key(0)} found in domain i"
215 )
216 elif s.name == "j":
217 for rec in s:
218 if not rec.key(0) in markets:
219 raise Exception(
220 f"Unexpected label {rec.key(0)} found in domain j"
221 )
222 else:
223 raise Exception(f"Expected GamsSet i and j but found {s.name}")
224 else:
225 raise Exception(f"Expected GamsSet as domain but found relaxed domain {s}")
226
227 # This next section is actually not about domain checking, but we
228 # make sure that certain things are working as expected.
229
230 # try reading an alias as set
231 j_alias = ws.add_job_from_string(
232 "Set i 'canning plants' / seattle, san-diego /;\nAlias (i,ii);"
233 )
234 j_alias.run()
235 ii = j_alias.out_db["ii"]
236 print("Elements of aliased set:")
237 for rec in ii:
238 print(f" > {rec.key(0)}")
239
240 test_db = ws.add_database()
241 test_set = test_db.add_set("test", 1)
242
243 # try adding empty UEL
244 test_set.add_record((""))
245 print("Element count of test set after adding empty UEL:")
246 print(f" > {test_set.number_records}")
247
248 # GAMS strips pending blanks while leading blanks are relevant
249 test_set.add_record(" a ").text = "a"
250 print("Record ' a ' should be the same as ' a':")
251 print(f" > {test_set.find_record(' a').text}")
252
253 # GAMS cannot handle UELs with more than 63 characters
254 # This should be OK ...
255 test_set.add_record(
256 "123456789012345678901234567890123456789012345678901234567890123 "
257 ).text = "OK"
258
259 # ... but not this
260 try:
261 test_set.add_record(
262 "1234567890123456789012345678901234567890123456789012345678901234"
263 ).text = "not OK"
264 except:
265 pass
266 else:
267 raise Exception(
268 "It should not be possible to add a record with more than 63 characters"
269 )
270
271 # GAMS cannot handle explanatory text with more than 255 characters
272 test_db.add_set(
273 "textOK",
274 1,
275 "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345",
276 )
277 try:
278 test_db.add_set(
279 "textNotOK",
280 1,
281 "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456",
282 )
283 except:
284 pass
285 else:
286 raise Exception(
287 "It should not be possible to add an explanatory text with more than 255 characters"
288 )
289
290 test_set.add_record(
291 "OK"
292 ).text = "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345"
293 try:
294 test_set.add_record(
295 "notOK"
296 ).text = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456"
297 except:
298 pass
299 else:
300 raise Exception(
301 "It should not be possible to add an set element text with more than 255 characters"
302 )
303
304 # GAMS can handle UELs containing single and double quotes but not at the same time
305 test_set.add_record("quote'")
306 test_set.add_record('quote"')
307 try:
308 test_set.add_record("quote'\"")
309 except:
310 pass
311 else:
312 raise Exception(
313 "It should not be possible to add a record containing single and double quotes simultaneously"
314 )
315 test_db.export("test.gdx")