domainchecking.cpp
Go to the documentation of this file.
1/*
2 * GAMS - General Algebraic Modeling System C++ API
3 *
4 * Copyright (c) 2017 GAMS Software GmbH <support@gams.com>
5 * Copyright (c) 2017 GAMS Development Corp. <support@gams.com>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice shall be included in all
15 * copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25#include "gams.h"
26#include <map>
27#include <vector>
28#include <iostream>
29#include <algorithm>
30
31using namespace gams;
32using namespace std;
33
34std::string getDataText()
35{
36 return "Sets \n"
37 "i canning plants / seattle, san-diego /; \n"
38 " \n"
39 "Alias (i,ii); \n";
40}
41
58int main(int argc, char* argv[])
59{
60 cout << "---------- Domain Checking --------------" << endl;
61
62 // define some data by using C++ data structures
63 vector<string> plants { "Seattle", "San-Diego" };
64 vector<string> markets { "New-York", "Chicago", "Topeka" };
65 map<string, double> capacity { { "Seattle", 350.0 }, { "San-Diego", 600.0 } };
66 map<string, double> demand { { "New-York", 325.0 }, { "Chicago", 300.0 }, { "Topeka", 275.0 } };
67 map<tuple<string, string>, double> distance {
68 { make_tuple<string,string> ("Seattle", "New-York"), 2.5 },
69 { make_tuple<string,string> ("Seattle", "Chicago"), 1.7 },
70 { make_tuple<string,string> ("Seattle", "Topeka"), 1.8 },
71 { make_tuple<string,string> ("San-Diego", "New-York"), 2.5 },
72 { make_tuple<string,string> ("San-Diego", "Chicago"), 1.8 },
73 { make_tuple<string,string> ("San-Diego", "Topeka"), 1.4 }
74 };
75
76
77 try {
78 GAMSWorkspaceInfo wsInfo;
79 if (argc > 1)
80 wsInfo.setSystemDirectory(argv[1]);
81 GAMSWorkspace ws(wsInfo);
82 // prepare a GAMSDatabase with data from the C++ data structures
83 GAMSDatabase db = ws.addDatabase();
84
85 // add two sets to the GAMSDatabase
86 GAMSSet i = db.addSet("i", "");
87 for (auto plant: plants)
88 i.addRecord(plant);
89 GAMSSet j = db.addSet("j", "");
90 for (auto market: markets)
91 j.addRecord(market);
92
93 // add a parameter with domain information
94 GAMSParameter a = db.addParameter("a", "capacity at plant", i);
95 for (string plant: plants)
96 a.addRecord(plant).setValue(capacity[plant]);
97
98 // if we see a domain violation something went wrong
99 if (!a.checkDomains()) {
100 cout << "*** Unexpected domain violation in a" << endl;
101 return 1;
102 }
103
104 // add a parameter with relaxed domain information
105 GAMSParameter b = db.addParameter("b", "demand at market j in cases", "j");
106 for (string market: markets)
107 b.addRecord(market).setValue(demand[market]);
108
109 // if we see a domain violation something went wrong
110 if (!b.checkDomains()) {
111 cout << "*** Unexpected domain violation in b" << endl;
112 return 1;
113 }
114
115 // add a 2-dim parameter with domain information
116 GAMSParameter d = db.addParameter("d", "distance in thousands of miles", i, j);
117 for (auto t : distance) {
118 auto tuple = t.first;
119 auto t1 = get<0>(tuple);
120 auto t2 = get<1>(tuple);
121 d.addRecord(t1, t2).setValue(distance[tuple]);
122 }
123
124 // if we see a domain violation something went wrong
125 if (!d.checkDomains()) {
126 cout << "*** Unexpected domain violation in d" << endl;
127 return 1;
128 }
129
130 // if we see a domain violation in the database something went wrong
131 if (!db.checkDomains()) {
132 cout << "*** Unexpected domain violation in db" << endl;
133 return 1;
134 }
135
136 // create some "wrong" entries
137 d.addRecord("Seattle", "aa").setValue(1);
138 d.addRecord("bb", "Seattle").setValue(1);
139 a.addRecord("aa").setValue(1);
140 a.addRecord("bb").setValue(1);
141 b.addRecord("aa").setValue(1);
142 b.addRecord("bb").setValue(1);
143
144 // now the GAMSdatabase as well as the symbols a and d should have domain violations
145 if (db.checkDomains()) {
146 cout << "*** Domain violation for db not recognized" << endl;
147 return 1;
148 }
149 if (a.checkDomains()) {
150 cout << "*** Domain violation for a not recognized" << endl;
151 return 1;
152 }
153 if (d.checkDomains()) {
154 cout << "*** Domain violation for d not recognized" << endl;
155 return 1;
156 }
157
158 // b in contrast was defined with realxed domain info only, therefore we should never see a domain violation
159 if (!b.checkDomains()) {
160 cout << "*** Unexpected domain violation in b" << endl;
161 return 1;
162 }
163
164 // for a we should see 2 domain violations ("aa" and "bb")
165 int dvCnt = 0;
166 cout << "Domain Violations of a:" << endl;
168 cout << " > ";
169 for (bool vi: sdv.violInd())
170 cout << (vi ? "true " : "false ");
171 cout << "<> ";
172 for (string k: sdv.violRec().keys())
173 cout << k << " ";
174 cout << "<<" << endl;
175 dvCnt++;
176 }
177 if (dvCnt != 2) {
178 cout << "*** Number of domain violations for a should be 2 but saw " << dvCnt << endl;
179 return 1;
180 }
181
182 // for d we should see 3 domain violations ("Seattle", *"aa"*; *"bb"*, *"Seattle"*)
183 dvCnt = 0;
184 cout << "Domain Violations of d:" << endl;
186 cout << " > ";
187 for (bool vi: sdv.violInd()) {
188 cout << (vi ? "true " : "false ");
189 if (vi)
190 dvCnt++;
191 }
192 cout << "<> ";
193 for (string k: sdv.violRec().keys())
194 cout << k << " ";
195 cout << "<<" << endl;
196 }
197 if (dvCnt != 3) {
198 cout << "*** Number of domain violations for a should be 3 but saw " << dvCnt << endl;
199 return 1;
200 }
201
202 // for db we should see 5 domain violations (all the ones from a and d)
203 dvCnt = 0;
204 cout << "Domain Violations of db:" << endl;
206 cout << " > " + DDV.violSym().name() + ": " << endl;
207 for (GAMSSymbolDomainViolation sdv: DDV.violRecs()) {
208 cout << " ";
209 for (bool vi: sdv.violInd()) {
210 cout << vi << " ";
211 if (vi)
212 dvCnt++;
213 }
214 cout << "<> ";
215 for (string k: sdv.violRec().keys())
216 cout << k << " ";
217 cout << "<<" << endl;
218 }
219 }
220 if (dvCnt != 5) {
221 cout << "*** Number of domain violations for db should be 5 but saw " << dvCnt << endl;
222 return 1;
223 }
224
225 // now we limit the amount of violated records reported to a total of 3
226 dvCnt = 0;
227 cout << "Domain Violations of db:" << endl;
229 cout << " > " + DDV.violSym().name() + ": " << endl;
230 for (GAMSSymbolDomainViolation sdv: DDV.violRecs()) {
231 cout << " ";
232 for (bool vi: sdv.violInd())
233 cout << vi << " ";
234 cout << "<> ";
235 for (string k: sdv.violRec().keys())
236 cout << k << " ";
237 cout << "<<" << endl;
238 dvCnt++;
239 }
240 }
241 if (dvCnt != 3) {
242 cout << "*** Number of domain violations for db should be 3 but saw " << dvCnt << endl;
243 return 1;
244 }
245 // now we limit the amount of violated records reported to 1 per symbol
246 dvCnt = 0;
247 cout << "Domain Violations of db:" << endl;
248 for (GAMSDatabaseDomainViolation DDV: db.getDatabaseDVs(0, 1)) {
249 cout << " > " + DDV.violSym().name() + ": " << endl;
250 for (GAMSSymbolDomainViolation sdv: DDV.violRecs()) {
251 cout << " ";
252 for (bool vi: sdv.violInd())
253 cout << vi << " ";
254 cout << "<> ";
255 for (string k: sdv.violRec().keys())
256 cout << k << " ";
257 cout << "<<" << endl;
258 dvCnt++;
259 }
260 }
261 if (dvCnt != 2) {
262 cout << "*** Number of domain violations for db should be 2 but saw " << dvCnt << endl;
263 return 1;
264 }
265
266 // by default we should get an exception when exporting a GAMSDatabase with domain violations
267 bool sawException = false;
268 try {
269 db.doExport("test.gdx");
270 } catch (...) {
271 sawException = true;
273 db.doExport("test.gdx");
274 }
275 if (!sawException) {
276 cout << "*** It should not be possible to export a GAMSDatabase containing domain violations by default" << endl;
277 return 1;
278 }
279
280 // read a parameter with domain info from gdx
281 GAMSDatabase db2 = ws.addDatabaseFromGDX("test.gdx");
282 GAMSParameter d2 = db2.getParameter("d");
283
284 // the domain of the parameter should be GAMSSet i and GAMSSet j
285 for (GAMSDomain item: d2.domains()) {
286 if (!item.isRelaxed()) {
287 GAMSSet domSet = item.getSet();
288 if (domSet.name() == "i") {
289 for (GAMSSetRecord uel: domSet)
290 if (find(plants.begin(), plants.end(), uel.key(0)) == plants.end()) {
291 cout << "*** Unexpected uel " + uel.key(0) + " found in domain i" << endl;
292 return 1;
293 }
294 } else if (domSet.name() == "j") {
295 for (GAMSSetRecord uel: domSet)
296 if (find(markets.begin(), markets.end(), uel.key(0)) == markets.end()) {
297 cout << "*** Unexpected uel " + uel.key(0) + " found in domain j" << endl;
298 return 1;
299 }
300 } else {
301 cout << "*** Expected GAMSSet i and j but found " + domSet.name() << endl;
302 return 1;
303 }
304 }
305 else
306 {
307 cout << "*** Expected GAMSSet as domain but found relaxed domain " + item.name() << endl;
308 return 1;
309 }
310 }
311
312 /* *************************************************************** *
313 * This next section is acutally not about domain checking, but we *
314 * make sure that certain things are working as expected. *
315 * *************************************************************** */
316
317 // Try reading an Alias as Set
318 GAMSJob jAlias = ws.addJobFromString(getDataText());
319 jAlias.run();
320 GAMSSet ii = jAlias.outDB().getSet("ii");
321 cout << "Elements of aliased Set:" << endl;
322 for (GAMSSetRecord item: ii)
323 cout << " > " + item.key(0) << endl;
324
325 GAMSDatabase testDB = ws.addDatabase();
326 GAMSSet testSet = testDB.addSet("test", 1);
327
328 // Try adding empty UEL
329 testSet.addRecord("");
330 cout << "Elements of test Set after adding empty UEL:" << endl;
331 cout << " > " << testSet.numberRecords() << endl;
332
333 // GAMS strips pending blanks while leading blanks are relevant
334 testSet.addRecord(" a ").setText("a");
335 cout << "Record ' a ' should be the same as ' a':" << endl;
336 cout << " > " << testSet.findRecord(" a").text() << endl;
337
338 // GAMS cannot handle UELs with more than 63 characters
339 // This should be OK ...
340 testSet.addRecord("123456789012345678901234567890123456789012345678901234567890123 ").setText("OK");
341 // ... but not this
342 sawException = false;
343 try {
344 testSet.addRecord("1234567890123456789012345678901234567890123456789012345678901234").setText("not OK");
345 } catch (...) {
346 sawException = true;
347 }
348 if (!sawException) {
349 cout << "*** It should not be possible to add a record with more than 63 characters" << endl;
350 return 1;
351 }
352
353 // GAMS cannot handle explanatory texts with more than 255 characters
354 testDB.addSet("textOK", "123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345");
355 sawException = false;
356 try {
357 testDB.addSet("textNotOK", "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456");
358 } catch (...) {
359 sawException = true;
360 }
361 if (!sawException) {
362 cout << "*** It should not be possible to add an explanatory text with more than 255 characters" << endl;
363 return 1;
364 }
365
366 testSet.addRecord("OK").setText("123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345");
367 sawException = false;
368 try {
369 testSet.addRecord("notOK").setText("1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456");
370 } catch (...) {
371 sawException = true;
372 }
373 if (!sawException) {
374 cout << "*** It should not be possible to add an explanatory text with more than 255 characters" << endl;
375 return 1;
376 }
377
378 // GAMS can handle UELs containing single and double quotes but not at the same time
379 testSet.addRecord("quote'");
380 testSet.addRecord("quote\"");
381 sawException = false;
382 try {
383 testSet.addRecord("quote'\"");
384 } catch (...) {
385 sawException = true;
386 }
387 if (!sawException) {
388 cout << "*** It should not be possible to add a record single AND double quote" << endl;
389 return 1;
390 }
391
392 testDB.doExport("test.gdx");
393 } catch (GAMSException &ex) {
394 cout << "GAMSException occured: " << ex.what() << endl;
395 } catch (exception &ex) {
396 cout << ex.what() << endl;
397 }
398
399 return 0;
400}
void setValue(const double val)
GAMSParameter getParameter(const std::string &name)
GAMSSet getSet(const std::string &name)
GAMSSet addSet(const std::string &name, const int dimension, const std::string &explanatoryText="")
std::vector< GAMSDatabaseDomainViolation > getDatabaseDVs(int maxViol=0, int maxViolPerSym=0)
GAMSParameter addParameter(const std::string &name, const int dimension, const std::string &explanatoryText="")
void setSystemDirectory(std::string systemDir)
std::string & name() const
std::vector< GAMSSymbolDomainViolation > getSymbolDVs(int maxViol=0)
GAMSDatabase outDB()
GAMSSetRecord findRecord(const std::vector< std::string > &keys)
void setText(const std::string &text)
int numberRecords() const
void setSuppressAutoDomainChecking(bool value)
void doExport(const std::string &filePath="")
std::vector< GAMSDomain > domains()
GAMSSetRecord addRecord(const std::vector< std::string > &keys)
std::string text()
GAMSParameterRecord addRecord(const std::vector< std::string > &keys)