C++ で CPLEX を利用する際の便利な記述まとめ

#はじめに
大学の研究で CPLEX を利用したシミュレーションを行ったのですが，そのとき日本語での情報が少なく苦しんだので，調べたことをいくつか共有しておきます．大規模な混合整数計画問題を解くときに，計算時間を考えて C++ を利用したので，同じような状況の人にとっては参考になる点もあるかもしれないです．

#コンソール出力の消去
CPLEX はデフォルトの状態だとコンソールに計算過程の出力も行うのですが，これによって計算速度が大幅に落ちます．また，結果の出力が見にくくなるという問題点もあります．実質的にこの出力を消す方法を書くために記事を書きました．下記の記述でコンソール出力を消すことが可能です．

```cpp
int main(){
	（前略，最適化問題の記述）
	/*
	IloCplex cplex(model);
	*/
	cplex.setOut(env.getNullStream()); // 出力消去，環境作成と求解の間に記述する
	/*
	cplex.solve();
	*/
	（後略）
}
```

#多次元配列での設計変数の定義
デフォルトの機能として１次元より大きな配列は用意されていませんが，下記の記述で利用することが可能です．

```cpp
//２次元配列
typedef IloArray<IloNumVarArray> IloNumVarMatrix;

int main(){
	IloNumVarMatrix A(env, I); //２次元設計変数列
	for (int i = 0; i < I; i++) {
		A[i] = IloNumVarArray(env, J);
		for (int j = 0; j < J; j++) {
			A[i][j] = IloNumVar(env, 0, IloInfinity, ILOFLOAT);
		}
	}
	（後略）
}

//３次元配列
typedef IloArray<IloNumVarArray> IloNumVarMatrix;
typedef IloArray<IloNumVarMatrix> IloNumVar3Matrix;

int main(){
	IloNumVar3Matrix A(env, I); //３次元設計変数列
	for (int i = 0; i < I; i++) {
		A[i] = IloNumVarMatrix(env, J);
		for (int j = 0; j < J; j++) {
			A[i][j] = IloNumVarArray(env, K);
			for (int k = 0; k < K; k++) {
				A[i][j][k] = IloNumVar(env, 0, IloInfinity, ILOFLOAT);
			}
		}
	}
	（後略）
}
```

#多次元配列での指定列での総和
先述の通り，デフォルトの機能として行方向以外の総和取得方法は用意されていませんが，下記の書き方で取得することができます．
今回は例として２次元配列の１列目の和をとります．

```cpp
int main(){
	/*
	IloNumVarMatrix A(env, I); //２次元設計変数列
	for (int i = 0; i < I; i++) {
		A[i] = IloNumVarArray(env, J);
		for (int j = 0; j < J; j++) {
			A[i][j] = IloNumVar(env, 0, IloInfinity, ILOFLOAT);
		}
	}
	*/
	（中略）
	IloExpr  expr; // 和の取得先
	for (int i = 0; i < I; i++) {
		expr += A[i][0];
	}
	（後略）
}

```

#多次元配列で定義した設計変数の計算結果取得
CPLEX は２次元以上の配列に対応していません．１次元配列についてはデフォルトで用意された機能で以下のように取得できます．

```cpp
int main(){
	IloNumVarArray vars(env); //１次元設計変数列

	（中略，最適化問題の記述）

	IloCplex cplex(model);
	cplex.setOut(env.getNullStream());
	cplex.solve();

	IloNumArray vals(env); //設計変数の取得先変数列
	cplex.getValues(vals, vars); //設計変数列の取得
	（後略）
}
```
しかし，この方法は１次元配列しか対応しておらず，また，IloNumArray は機能が少ないです．そこで，統一的な記法で設計変数の最適化結果を C++ の STL に取得することを目指します．

```cpp
typedef IloArray<IloNumVarArray> IloNumVarMatrix;
typedef IloArray<IloNumVarMatrix> IloNumVar3Matrix;

template <class T>
void getValues(vector<T>& val, IloNumVarArray& var, IloCplex& cplex) {
	for (int i = 0; i < var.getSize(); i++) {
		val.push_back(cplex.getValue(var[i]));
	}
}
template <class T>
void getValues(vector<vector<T>>& val, IloNumVarMatrix& var, IloCplex& cplex) {
	for (int i = 0; i < var.getSize(); i++) {
		val.push_back(vector<T>());
		for (int j = 0; j < var[i].getSize(); j++) {
			val[i].push_back(cplex.getValue(var[i][j]));
		}
	}
}
template <class T>
void getValues(vector<vector<vector<T>>>& val, IloNumVar3Matrix& var, IloCplex& cplex) {
	for (int i = 0; i < var.getSize(); i++) {
		val.push_back(vector<vector<T>>());
		for (int j = 0; j < var[i].getSize(); j++) {
			val[i].push_back(vector<T>());
			for (int k = 0; k < var[i][j].getSize(); k++) {
				val[i][j].push_back(cplex.getValue(var[i][j][k]));
			}
		}
	}
}

int main(){
	IloNumVarArray x(env); //設計変数列．２次元の時は IloNumVarMatrix，３次元の時は IloNumVar3Matrix

	（中略，最適化問題の記述）

	IloCplex cplex(model);
	cplex.setOut(env.getNullStream());
	cplex.solve();

	vector<int> _x; //設計変数の取得先変数列，今回は int とする．２次元の時は vector<vector<int>>，３次元の時は vector<vector<vector<int>>>
	getValues(_x, x, cplex);; //設計変数列の取得
	（後略）
}
```

#場合分け
変数の値によった場合分けを行いたい場合は，下記のように記述すれば実行可能です．解空間が歪むためか，計算速度はかなり落ちます．

```cpp
int main(){
	（前略）
	for (int i = 0; i < I; i++) {
		for (int j = 0; j < J; j++) {
			model.add(IloIfThen(env, A[i][j] < 1, A[i][j] == 0));
		}
	}
	（後略）
}
```

#おわりに
シミュレーションを行う際に，個人的に有用だと感じた機能を共有しました．

#参考
今回紹介した内容だけでなく，私が CPLEX を利用する上で参考にしたサイトもまとめておきます．
http://www-eio.upc.es/lceio/manuals/cplex-11/html/refcppcplex/html/optim.cplex.cpp/group.html
https://www.ibm.com/support/knowledgecenter/SSSA5P_12.10.0/ilog.odms.cplex.help/CPLEX/UsrMan/topics/APIs/Cpp/eg_diet_solve.html
https://www.ibm.com/support/pages/sample-create-and-use-multi-dimensional-ilonumvararray
https://www.ibm.com/support/knowledgecenter/SSSA5P_12.7.1/ilog.odms.ide.help/refcppopl/html/classes/IloIfThen.html
https://qiita.com/takewaka/items/cfbae5046e01ebf98b4e
http://cplusplus2017.blog.fc2.com/blog-entry-5.html
http://web.tuat.ac.jp/~miya/ipmemo.html
https://www.gerad.ca/aide/lib/exe/fetch.php?media=en:cplex10_en.pdf