Loading...
Searching...
No Matches
domainchecking.cpp File Reference

This example enforces referential integrity also known in the GAMS lingo as domain checking. More...

#include "gams.h"
#include <map>
#include <vector>
#include <iostream>
#include <algorithm>

Go to the source code of this file.

Functions

std::string getDataText ()
 
int main (int argc, char *argv[])
 

Detailed Description

This example enforces referential integrity also known in the GAMS lingo as domain checking.

The GAMS C++ API does a delayed domain checking of symbols. So you can add records to a domain controlled parameter (e.g. p(i)) even though the GAMSSet i does not contain the label (yet). The user can trigger an explicit check of the referential integrity by calling the GAMSDatabase.CheckDomains (or GAMSSymbol.CheckDomains) method. The Object- oriented API provides methods to access the records that violate the referential integrity (see GAMSDatabaseDomainViolation for details). Domain checking is implicitly done when the GAMSDatabase is exported to a GDX file via the GAMSDatabase.export method or for databases provided in the GAMSJob.run method. The implicit domain check can be suppressed (and left to GAMS when importing data) via the GAMSDatabase.SuppressAutoDomainChecking property. This example demonstrates how to trigger domain checking and how to access the records that violate the referential integrity.

Definition in file domainchecking.cpp.

Function Documentation

◆ getDataText()

std::string getDataText ( )

Definition at line 34 of file domainchecking.cpp.

35{
36 return "Sets \n"
37 "i canning plants / seattle, san-diego /; \n"
38 " \n"
39 "Alias (i,ii); \n";
40}

Referenced by main().

◆ main()

int main ( int argc,
char * argv[] )

Definition at line 58 of file domainchecking.cpp.

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}
std::vector< GAMSDatabaseDomainViolation > getDatabaseDVs(int maxViol=0, int maxViolPerSym=0)
GAMSSet addSet(const std::string &name, const int dimension, const std::string &explanatoryText="", GAMSEnum::SetType setType=GAMSEnum::SetType::Multi)
GAMSParameter addParameter(const std::string &name, const int dimension, const std::string &explanatoryText="")
void doExport(const std::string &filePath="")
GAMSSet getSet(const std::string &name)
GAMSParameter getParameter(const std::string &name)
void setSuppressAutoDomainChecking(bool value)
GAMSDatabase outDB()
void setValue(const double val)
GAMSParameterRecord addRecord(const std::vector< std::string > &keys)
void setText(const std::string &text)
std::string text()
GAMSSetRecord findRecord(const std::vector< std::string > &keys)
GAMSSetRecord addRecord(const std::vector< std::string > &keys)
std::vector< GAMSSymbolDomainViolation > getSymbolDVs(int maxViol=0)
std::string & name() const
int numberRecords() const
std::vector< GAMSDomain > domains()
void setSystemDirectory(const std::string &systemDir)
std::string getDataText()