SPSS Modeler ノードリファレンス 2-14 CPLEXの最適化ノード

# 2-14 CPLEXの最適化ノード[レコード設定タブ]
<img width="200" alt="CPLEXの最適化ノード.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/707691a2-a903-0b46-6199-e75a78ad8463.png">

## 1.ノードの目的
Modelerからの入力をつかってCPLEXで最適化を行います。

## 2.解説動画（4分）


<iframe width="560" height="315" src="https://www.youtube.com/embed/MacmqGTXdSc" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>



## 3.クイックスタート
一時間ごとの電力需要データから最大消費電力を最小化する、蓄電池への充放電を算出します。


＊データは[5.参考情報]からダウンロードできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/a462ebce-c847-d3df-d3e8-c95d1c07283c.png)

[線グラフ]ノードでもデータを確認しておきます。日中帯は需要が多く、夜間や朝は少ないということがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/2296670d-f438-ee11-84b6-e547ef14fca6.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/e77a1f0c-7a78-e04c-dab0-4ed244c3c691.png)

[可変長ファイル]ノードに[CPLEXの最適化ノード]ノードと[テーブル]ノードを順番に接続します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/9819bc52-f7b0-2594-95b6-ee08d807591e.png)


[CPLEXの最適化ノード]ノードをダブルクリックして編集します。[OPLモデル]に以下のOPLモデルのコードを入力します。このコードはあらかじめCPLEX Optimization Studioなどで作成しておきます。

```c#:PowerPeakMinimize.mod
//①既知の情報の定義///////////////////////////////////////////////////////////////////////////
//１時間毎の電力需要量
tuple TDemands{
  int time;
  float demand;
}

{TDemands}Demands=...;

float BatteryCapacity = 500;    //蓄電池の容量
float ChargeDemandsEfficiency = 100;  //時間あたりに充放電できる量
float BatteryInit = 0;    //蓄電池充電量初期値


//②決定変数と決定式///////////////////////////////////////////////////////////////////////////
//決定変数
dvar float BatteryInput[d in Demands];  //充放電量

//決定式
dexpr float BatteryCharge[d in Demands] = BatteryInit + sum(d2 in Demands:d2.time<=d.time)BatteryInput[d2];  //バッテリー残量
dexpr float Consumption[d in Demands] = d.demand+ BatteryInput[d];      //電力消費量
dexpr float MaxConsumption = max(d in Demands) Consumption[d];  //電力消費の最大値

//③目的関数の定義///////////////////////////////////////////////////////////////////////////
minimize MaxConsumption;//電力消費を最小化する

//④制約条件の定義///////////////////////////////////////////////////////////////////////////
subject to{
  forall(d in Demands){
    Consumption[d] >= 0;    //電力消費量はマイナスにならない
    BatteryCharge[d] <= BatteryCapacity;      //バッテリー容量以上の電力は充電できない
    if(d.time==1){                    //バッテリーからの放電量は充電されている量以下である
      BatteryInput[d] >= -BatteryInit;    
    }else{
      BatteryInput[d] >= -BatteryCharge[item(Demands,d.time-1)];
    }  
    BatteryInput[d] <= ChargeDemandsEfficiency;    //バッテリーへの充電量は時間当たり上限がある
    BatteryInput[d] >= -ChargeDemandsEfficiency;    //バッテリーからの放電量は時間当たり上限がある  
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/bafc0860-f1fa-0f79-f90d-f6e4a19d7070.png)


なお、上のOPLモデルの意味は以下の記事で解説しています。

CPLEXで蓄電池をつかったピーク消費電力最小化 - Qiita 

https://qiita.com/kawada2017/items/85c0306c46f48a3b6a5d

そして、[入力データ]タブの設定をします。
[データソース]が入力CSVであることを確認します。
次に、[入力データに対応するOPLのタプルセット名]にOPLモデルでデータ・ファイルから読み込んでいたタプルを指定します。ここでは「Demands」になります。
そして、[入力マッピング]でOPLモデルのタプルの列名の「time」、「demand」とModelerからの入力データの列名の「TIME」、「DEMAND」マッピングを行います。「demand」のデータ型は[浮動小数点]であることに注意してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/2ce55bbc-8ac8-4a0b-f77e-1b43b89dca29.png)



次に、[出力]タブの設定をします。
[出力モード]は[決定変数]を選びます。
[OPLでの目的関数値の変数名]に「MaxConsumption」を入力します。これが最適化結果でスカラー値なので、すべての行に同じ値が入ります。
[出力に使用する目的関数値のフィールド名]に「最大電力消費量」を入力します。これが最適化値の列名になります。
[出力タプル]にデータ・ファイルから読み込んでいたタプルを指定します。ここでは「Demands」になります。結果的にはModelerから入力したデータになっています。これでtimeとdemandの列が出力されます（Modelerからの入力ではなくtupleからの出力なので、小文字で定義されています）

そして、[変数の出力]でOPLモデルの決定変数の「BatteryInput」を「充放電」として、決定式の「BatteryCharge」を「バッテリー残量」として、「Consumption」を「電力消費量」として出力しています。これらはすべてfloatなので[データ型]は[浮動小数点]にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/ab1674d5-a0ed-4b5f-3597-c014c44f507c.png)

なお、「BatteryInput」、「BatteryCharge」、「Consumption」はいずれも、出力タプルである「Demands」に基づく配列なのでこのように一緒に出力が可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/63f9ed59-3bae-1e45-1a87-be83f205334f.png)

[出力ノード]を実行すると最適化の結果を参照できます。
需要が少ないときに充電をして、需要が多いときに放電することによって、消費量のピークを抑えています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/970d39b7-eceb-b8d1-00d7-1507421e44ff.png)

最後に[線グラフ]ノードでもデータを確認しておきます。朝の需要が少ないときに充電をして、日中帯は需要が多いときには放電することによって、消費量のピークを抑えていることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/19b99e65-f970-b74b-7d4b-1e5edfe37680.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/973a6577-a879-049a-be54-5517cc211fc2.png)

## 4.Tips
需要の予測をModelerで行って、そのまま[CPLEXの最適化]ノードで最適化することももちろん可能です。

電力需要予測は以下の記事のストリームで行いましたので、このストリームを流用します。

SPSS Modelerで電力需要を予測する - Qiita 

https://qiita.com/kawada2017/items/d8566628618bfb87917a

このストリームに[条件抽出]ノードを接続し、「'$FutureFlag' = 1」の条件で予測結果のみに絞り込みます。
次に、今回のCPLEXのモデルで必要としている「TIME」という連番の列を追加します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/34f07c58-a400-ce34-1218-62753ebc014a.png)

先ほどの[CPLEXの最適化]ノードをコピーします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/9fa3f24f-9a09-4781-6a74-83cbe260a5de.png)

[入力データ]のタブの入力フィールドで、「demand」への入力データとして、ここでは上側95％区間の予測値「$TSUCI-DEMAND」を選んでみました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/911a5012-e27a-ae67-e251-a5ae8ba3d9bc.png)

[CPLEXの最適化]ノードの出力からはCPLEXからのみの出力となるので、「TS」などの元々あった情報も出力するためには、[レコード結合]でつなぐ必要があります。重複項目はフィルターします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/8e5b690a-3f59-26bf-d8f9-06e3a089bd6d.png)

後は、[テーブル]ノードと[線グラフ]ノードもコピーしてくれば完成です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/3ba73a22-2bdf-a76c-216a-fb6e03ad673a.png)

結果を見てみましょう。各時間にどのくらい充放電すればよいかが決まりました。そして月間の最大電力消費量は185.980に抑えられることがわかりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/137534ff-5c41-faa4-ceeb-508f32e5f810.png)

グラフでみてもピークが抑えられていることが読み取れました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/33a2b445-df99-b7c1-27c1-fd5023b84657.png)


## 5.参考情報

* テスト環境
    * Modeler 18.4
    * Windows 11 64bit

#### サンプルストリーム
電力ピーク最適化3.str

https://github.com/hkwd/spsssample/raw/master/230105%E9%9C%80%E8%A6%81%E4%BA%88%E6%B8%ACCPLEX/%E9%9B%BB%E5%8A%9B%E3%83%94%E3%83%BC%E3%82%AF%E6%9C%80%E9%81%A9%E5%8C%963.str

#### 利用データ

右クリックでリンク先を保存してください。
demand0501.csv

https://raw.githubusercontent.com/hkwd/spsssample/master/230105%E9%9C%80%E8%A6%81%E4%BA%88%E6%B8%ACCPLEX/demand0501.csv

過去電力需要.csv

https://raw.githubusercontent.com/hkwd/spsssample/master/230105%E9%9C%80%E8%A6%81%E4%BA%88%E6%B8%ACCPLEX/%E9%81%8E%E5%8E%BB%E9%9B%BB%E5%8A%9B%E9%9C%80%E8%A6%81.csv

#### 【リレー連載】わたしの推しノード –最強のラスボス「CPELXの最適化ノード」が予測の次の最終ステージで実力を解放 | IBM ソリューション ブログ 

こちらはシフト作成で「CPELXの最適化ノード」使っている記事です。

https://www.ibm.com/blogs/solutions/jp-ja/spssmodeler-push-node-19/

#### ノードのヘルプ

https://www.ibm.com/docs/ja/spss-modeler/18.4.0?topic=nodes-cplex-optimization-node

#### SPSS Modeler 逆引きストリーム集（データ加工）

https://qiita.com/416nishimaki/items/4bf813e6cba84b2a1008

#### SPSS Modeler ノードリファレンス目次

https://qiita.com/416nishimaki/private/503037195081d3323eee