チュートリアルを参考に、DBから読み込んで計算
前回作ったDBからデータを引っ張ってきて、計算させてみる。 ほぼチュートリアル(Transport9.java)が参考になりそう。接続させるところだけ修正して、動かしてみた。
他に参考になりそうなところでは、いったんMySQLからcsvファイルを作り、それを読み込むような手法もあった(databases:mysql [GAMS Interfaces Wiki])。 ということで下のコードはTransport9.javaほぼそのまま。
public class TransportFromDB { /** * Modelを組み立て実行する */ public void runJob() { GAMSWorkspace ws = new GAMSWorkspace(); // ひな形となるモデルを読み込み GAMSJob tp = ws.addJobFromFile("/PATH/TO/YOUR/Model.gms"); GAMSDatabase gdb; try { // GAMSDatabaseをDBから読み込み、組み立て gdb = readDataFromDB(ws); // Optionを設定 // ソルバとしてxpressを設定している GAMSOptions opt = ws.addOptions(); opt.defines("gdxincname", gdb.getName()); opt.setAllModelTypes("xpress"); // 計算の実行 tp.run(opt, gdb); // 計算結果の出力 for (GAMSVariableRecord rec : tp.OutDB().getVariable("x")) { System.out.println("x(" + rec.getKeys()[0] + "," + rec.getKeys()[1] + "): level=" + rec.getLevel() + " marginal=" + rec.getMarginal()); } } catch (SQLException ex) { Logger.getLogger(TransportFromDB.class.getName()).log(Level.SEVERE, null, ex); } } /** * MySQLからデータを読み込む * @param ws GAMSWorkspace * @return 返却するGAMSDatabase * @throws SQLException 例外 */ private GAMSDatabase readDataFromDB(GAMSWorkspace ws) throws SQLException { // プロパティファイル(mysql.properties)からパラメータを読み取り ResourceBundle rb = ResourceBundle.getBundle("mysql"); GAMSDatabase gdb = ws.addDatabase(); // Connectionを取得 Connection conn = DriverManager.getConnection( rb.getString("DB_URL"), rb.getString("DB_USER"), rb.getString("DB_PASSWORD") ); // read GAMS sets readSet(conn, gdb, "SELECT Plant FROM Plant", "i", 1, "canning plants"); readSet(conn, gdb, "SELECT Market FROM Market", "j", 1, "markets"); // read GAMS parameters readParameter(conn, gdb, "SELECT Plant, Capacity FROM Plant", "a", 1, "capacity of plant i in cases"); readParameter(conn, gdb, "SELECT Market, Demand FROM Market", "b", 1, "demand at market j in cases"); readParameter(conn, gdb, "SELECT Plant,Market,Distance FROM Distance", "d", 2, "distance in thousands of miles"); return gdb; } /** * Set項目をDBから取得してGAMSDatabaseに設定する * @param conn DBコネクション * @param gdb GAMSDatabase * @param query SQLクエリ * @param setName Set名 * @param setDimension Setの取り得る次元 * @param setDescription Setの説明 * @throws SQLException 例外 */ private void readSet(Connection conn, GAMSDatabase gdb, String query, String setName, int setDimension, String setDescription) throws SQLException { Statement st = conn.createStatement(); ResultSet rs = st.executeQuery(query); ResultSetMetaData meta = rs.getMetaData(); if (meta.getColumnCount() != setDimension) { System.err.println("ERROR!"); conn.close(); System.exit(-1); } GAMSSet set = gdb.addSet(setName, setDimension, setDescription); String[] keys = new String[setDimension]; while (rs.next()) { for (int i=0; i<setDimension; i++) { keys[i] = rs.getString(i+1); } set.addRecord(keys); } st.close(); } /** * Parameter項目をDBから取得してGAMSDabaseに設定する * @param conn DBコネクション * @param gdb GAMSDatabase * @param query SQLクエリ * @param paramName Parameter名 * @param paramDim Parameterの取り得る次元 * @param paramDesciption Parameterの説明 * @throws SQLException 例外 */ private void readParameter(Connection conn, GAMSDatabase gdb, String query, String paramName, int paramDim, String paramDesciption) throws SQLException { Statement st = conn.createStatement(); ResultSet rs = st.executeQuery(query); ResultSetMetaData meta = rs.getMetaData(); int numberOfColumns = meta.getColumnCount(); if (numberOfColumns != (paramDim + 1)) { System.err.println("ERROR!"); conn.close(); System.exit(-1); } GAMSParameter parameter = gdb.addParameter(paramName, paramDim, paramDesciption); String[] keys = new String[paramDim]; while(rs.next()) { for(int i=0; i<paramDim; i++) { keys[i] = rs.getString(i+1); } parameter.addRecord(keys).setValue(Double.valueOf(rs.getString(numberOfColumns))); } st.close(); } }
コードの中で読んでいるmysql.propertiesは下記で、src/main/resources
に置いておく。
# DBのプロパティ DB_URL=jdbc:mysql://localhost:3306/transport DB_USER=work DB_PASSWORD=(workに対して設定したもの)
以前書いた最適化計算をGAMSで行う - GIGでは、Netbeans上から動作を確認したが、Netbeans8に上げてからか、システムの環境変数を拾ってくれない。 どこかで調べなくては。
結局コマンドライン上から動かして見たが、次に「おや?」と思ったのが、結果が以前の同じモデルの計算と異なる(ダメじゃん...)。
*.lstファイルを比較してみたけど、展開した式は同じ...。これも調べなくては。
モデルをひっぺがす
上記の場合、当然SQL分をべた書きしている箇所などは外から読ませほうがいい。 Set, Parameterなどデータの型や次元は、今データベースにいれていない。これらはモデルのメタデータというくくりで、実際のデータと分けた方が良いかもしれない。
例えば、以下のmodel.json
をModelを登録時に作成するなど(そういえば結果の格納は何にも考えていなかった...)
{ "Model" : { "name" : "transport" }, "Data" : { "i" : { "type" : "set", "dim" : "1" }, "j" : { "type" : "set", "dim" : "1" }, "a" : { "type" : "paramer", "dim" : "1" }, "b" : { "type" : "paramer", "dim" : "1" }, "d" : { "type" : "paramer", "dim" : "2" } }, "Results" : ["x.l", "x.m"] }
まだまだ考えがまとまっていない。。