GAMS与C++的交互(版本要求:GAMS25.1最多支持VS2017,GAMS38可以支持VS2019)
若有侵权或者不恰当的使用,请及时联系作者
若起到了作用请一键三联
参考源代码(官方代码):
Tutorial (gams.com)
我的理解是,想要在C++里面运行GAMS程序,只需要把GAMS里面的数据和模型分别输入到函数中进行运行即可
-
C++输出到.gdx
- 第一种方案:按照不同的分类格式一步一步地进行输出,这是官方的例程,使用的是string
#include "gams.h" #include <iostream> #include <fstream> #include <vector> #include <map> #include <cstdlib> using namespace gams; using namespace std; int main(int argc, char* argv[]) { cout << "---------- Transport GDX --------------" << endl; GAMSWorkspaceInfo wsInfo; if (argc > 1) wsInfo.setSystemDirectory(argv[1]); GAMSWorkspace ws(wsInfo); // define some data by using C++ data structures vector<string> plants = { "Seattle", "San-Diego" }; vector<string> markets = { "New-York", "Chicago", "Topeka" }; map<string, double> capacity = { { "Seattle", 350.0 }, { "San-Diego", 600.0 } }; map<string, double> demand = { { "New-York", 325.0 }, { "Chicago", 300.0 }, { "Topeka", 275.0 } }; map<tuple<string, string>, double> distance = { { make_tuple("Seattle", "New-York"), 2.5 }, { make_tuple("Seattle", "Chicago"), 1.7 }, { make_tuple("Seattle", "Topeka"), 1.8 }, { make_tuple("San-Diego", "New-York"), 2.5 }, { make_tuple("San-Diego", "Chicago"), 1.8 }, { make_tuple("San-Diego", "Topeka"), 1.4 } }; // create new GAMSDatabase instance GAMSDatabase db = ws.addDatabase(); //C++----->gdx // 这是将string格式的打上标签(添加成集合,集合名为canning plants) // add 1-dimensional set 'i' with explanatory text 'canning plants' to the GAMSDatabase GAMSSet i = db.addSet("i", 1, "canning plants"); for (string p : plants) i.addRecord(p); // add 1-dimensional set 'j' with explanatory text 'markets' to the GAMSDatabase GAMSSet j = db.addSet("j", 1, "markets"); for (string m : markets) j.addRecord(m); //添加一维parameter的方式(并且可以加上使用的是什么集合) // add parameter 'a' with domain 'i' GAMSParameter a = db.addParameter("a", "capacity of plant i in cases", i); for (string p : plants) a.addRecord(p).setValue(capacity[p]); //添加二维parameter的方式,之前也说过table是二维parameter的一种特殊形式 // add parameter 'd' with domains 'i' and 'j' GAMSParameter d = db.addParameter("d", "distance in thousands of miles", i, j); for (auto t : distance) d.addRecord(get<0>(t.first), get<1>(t.first)).setValue(t.second); //添加scaler // add scalar 'f' GAMSParameter f = db.addParameter("f", "freight in dollars per case per thousand miles"); f.addRecord().setValue(90); //输出gdx文件,路径我喜欢绝对路径(这里的规则就是普通的C++的规则) // export the GAMSDatabase to a GDX file with name 'data.gdx' located in the 'workingDirectory' of the GAMSWorkspace db.doExport("C:\\Users\\henry\\Desktop\\data.gdx"); cout << "Content of GDX file 'data.gdx':"; // add a new GAMSDatabase and initialize it from the GDX file just created GAMSDatabase db2 = ws.addDatabaseFromGDX("C:\\Users\\henry\\Desktop\\data.gdx"); //gdx------->c++ //读取我们先定义一个同格式的进行读取 // read data from symbols into C++ data structures vector<string> iNew; for (GAMSSetRecord rec : db2.getSet("i")) iNew.push_back(rec.key(0)); vector<string> jNew; for (GAMSSetRecord rec : db2.getSet("j")) jNew.push_back(rec.key(0)); map<string, double> aNew; for (GAMSParameterRecord rec : db2.getParameter("a")) aNew[rec.key(0)] = rec.value(); map<tuple<string, string>, double> dNew; for (GAMSParameterRecord rec : db2.getParameter("d")) dNew[make_tuple(rec.key(0), rec.key(1))] = rec.value(); double fNew = db2.getParameter("f").firstRecord().value(); cout << "i:" << endl; for (string s : iNew) cout << " " << s << endl; cout << "j:" << endl; for (string s : jNew) cout << " " << s << endl; cout << "a:" << endl; for (auto rec : aNew) cout << " " << rec.first << " : " << rec.second << endl; cout << "d:" << endl; for (auto rec : dNew) cout << " " << get<0>(rec.first) << ", " << get<1>(rec.first) << " : " << rec.second << endl; cout << "f:" << endl; cout << " " << fNew; return 0; }
- 尝试了一下使用数组设置set,发现并不可行,查了一下函数定义,发现成员为string,gams里面这个问题同样存在
GAMSSetRecord addRecord(); GAMSSetRecord addRecord(const std::string& key1); GAMSSetRecord addRecord(const std::string& key1, const std::string& key2); GAMSSetRecord addRecord(const std::string& key1, const std::string& key2, const std::string& key3);
解决方案,直接强制类型转换直接使用to_string()将int转化成string,如果嫌麻烦的话,可以对函数进行重载,就在头文件里复制一下加一行就可以了
//写的部分 //我来试试用数组的形式行不行,随便定义一组数据 double load[24] = { 0.0196,0.0186,0.0189,0.0202,0.0170,0.0767,0.0773,0.0889,0.2141,0.1255,0.0822,0.0558,0.340,0.0311,0.0266,0.0757,0.1181,0.1306,0.0639,0.0767,0.0735,0.0414,0.0286,0.0552 }; // 先来定义一个集合set GAMSSet hour = db.addSet("hour", 1, "time scalar"); for (int o = 1; o < 25; o++) hour.addRecord(to_string(o)); GAMSParameter load_gams = db.addParameter("load", "load of system", hour); for (int o = 1; o < 25; o++) load_gams.addRecord(to_string(o)).setValue(load[o-1]); //读的部分 vector<string> read_hour; for (GAMSSetRecord rec : db2.getSet("hour")) read_hour.push_back(rec.key(0)); map<string, double> load_new; for (GAMSParameterRecord rec : db2.getParameter("load")) load_new[rec.key(0)] = rec.value();
- 第二种方案,使用string一口气进行传输:使用GAMSJob这个类
-
写入.gdx文件
// 输入数据的格式 string getDataText() { return "Sets \n" " i canning plants / seattle, san-diego / \n" " j markets / new-york, chicago, topeka / ; \n" "Parameters \n" " \n" " a(i) capacity of plant i in cases \n" " / seattle 350 \n" " san-diego 600 / \n" " \n" " b(j) demand at market j in cases \n" " / new-york 325 \n" " chicago 300 \n" " topeka 275 / ; \n" " \n" "Table d(i,j) distance in thousands of miles \n" " new-york chicago topeka \n" " seattle 2.5 1.7 1.8 \n" " san-diego 2.5 1.8 1.4 ; \n" " \n" "Scalar f freight in dollars per case per thousand miles /90/;\n"; } //如果需要用C++写入GAMS的东西,直接将GAMS里面的内容用string按GAMS的格式写出来输入就可以 GAMSJob t3 = ws.addJobFromString(getDataText()); t3.run(); //TODO: change doExport to export? t3.outDB().doExport("C:\\Users\\henry\\Desktop\\tdata.gdx");
-
读取我还没搞明白,后续可能会更新
(但是我觉得用不上,因为读取完全可以通过gams读取gdx文件在gams里面计算,没必要使用C++调库求解)