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