はじめに
IBMのSAAS版クラウドサービスである、「IBM Decision Optimization on Cloud」は近々利用できなくなります。
現在このサービスを利用している場合、移行先はWatson Studio / Watson Machine Learningになります。
わかりやすい移行ガイドが、あまりないため、参考情報としてこの記事を記載します。
前提
IBM クラウドへのユーザー登録・Watson Studioの準備
これからの操作をするためには、IBM Cloudにアカウント登録を行い、Watson Studioのインスタンスを作成してWatson Studioのプロジェクトを作る必要があります。また、Watson Machine Learningとの関連づけも必要です。
一連の手順は、別記事に記載してありますので、こちらを参照して下さい。
無料でなんでも試せる! Watson Studioセットアップガイド
対象アプリケーション
下記リンク先に記載されている「Warehouse location」を元アプリケーションとします。
同じコードは、下記にもアップしておきました。
https://github.com/makaishi2/cplex-samples/tree/master/warehouse-org
そんなに分量もないので、コード自体もアップしておきます。
warehouse_cloud.mod
// --------------------------------------------------------------------------
// Licensed Materials - Property of IBM
//
// 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55
// Copyright IBM Corporation 1998, 2013. All Rights Reserved.
//
// Note to U.S. Government Users Restricted Rights:
// Use, duplication or disclosure restricted by GSA ADP Schedule
// Contract with IBM Corp.
// --------------------------------------------------------------------------
include "warehouse_data.mod";
range stores = 1..plan.nbStores;
dvar boolean Open[ warehouses ];
dvar boolean Supply[ stores ][ warehouses ];
// expression
dexpr float totalOpeningCost = sum( w in warehouses ) w.fixedCost * Open[w];
dexpr float totalSupplyCost = sum( w in warehouses , s in stores, k in supplyCosts : k.storeId == s && k.warehouseName == w.name )
Supply[s][w] * k.cost;
minimize
totalOpeningCost + totalSupplyCost;
subject to {
forall( s in stores )
ctEachStoreHasOneWarehouse:
sum( w in warehouses ) Supply[s][w] == 1;
forall( w in warehouses, s in stores )
ctUseOpenWarehouses:
Supply[s][w] <= Open[w];
forall( w in warehouses )
ctMaxUseOfWarehouse:
sum( s in stores) Supply[s][w] <= w.capacity;
}
{int} StoresSupplied[w in warehouses] = { s | s in stores : Supply[s][w] == 1 };
{string} OpenWarehouses = { w.name | w in warehouses : Open[w] == 1 };
tuple TSuppliedStore {
string warehouseName;
int storeId;
}
{TSuppliedStore} network;
execute DISPLAY_RESULTS{
network.clear();
writeln("* Open Warehouses=", OpenWarehouses);
for ( var w in warehouses) {
if ( Open[w] ==1) {
writeln("* stores supplied by ", w.name, ": ", StoresSupplied[w]);
for (var s in stores) {
if (Supply[s][w] == 1) {
network.addOnly(w.name, s);
}
}
}
}
}
warehouse_data.mod
tuple TWarehouse {
key string name;
int capacity;
float fixedCost;
}
tuple TSupplyCost {
key string warehouseName;
key int storeId;
float cost;
}
tuple TPlan {
int nbStores;
}
TPlan plan = ...;
{TWarehouse} warehouses = ...;
{TSupplyCost} supplyCosts = ...;
warehouse_cloud.dat
// --------------------------------------------------------------------------
// Licensed Materials - Property of IBM
//
// 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55
// Copyright IBM Corporation 1998, 2013. All Rights Reserved.
//
// Note to U.S. Government Users Restricted Rights:
// Use, duplication or disclosure restricted by GSA ADP Schedule
// Contract with IBM Corp.
// --------------------------------------------------------------------------
// 10 stores to be opened.
plan = <10>;
warehouses =
{
<Bonn , 1, 30>
, <Bordeaux, 4, 30>
, <London , 2, 30>
, <Paris , 1, 30>
, <Rome , 3, 30>
};
supplyCosts =
{
<Bonn,1,20>
, <Bonn,2,28>
, <Bonn,3,74>
, <Bonn, 4,2>
, <Bonn,5,46>
, <Bonn,6,42>
, <Bonn,7,1>
, <Bonn,8,10>
, <Bonn,9,93>
, <Bonn,10,47>
//
, <Bordeaux,1,24>
, <Bordeaux,2,27>
, <Bordeaux,3,97>
, <Bordeaux,4,55>
, <Bordeaux,5,96>
, <Bordeaux,6,22>
, <Bordeaux,7,5>
, <Bordeaux,8,73>
, <Bordeaux,9,35>
, <Bordeaux,10,65>
//
, <London,1,11>
, <London,2,82>
, <London,3,71>
, <London,4,73>
, <London,5,59>
, <London,6,29>
, <London,7,73>
, <London,8,13>
, <London,9,63>
, <London,10,55>
//
, <Paris,1,25>
, <Paris,2,83>
, <Paris,3,96>
, <Paris,4,69>
, <Paris,5,83>
, <Paris,6,67>
, <Paris,7,59>
, <Paris,8,43>
, <Paris,9,85>
, <Paris,10,71>
//
, <Rome,1,30>
, <Rome,2,74>
, <Rome,3,70>
, <Rome,4,61>
, <Rome,5,4>
, <Rome,6,59>
, <Rome,7,56>
, <Rome,8,96>
, <Rome,9,46>
, <Rome,10,95>
};
実行結果例は、以下のとおりです。
対象アプリケーションのDropSolve化
Saas版のDropSolveの便利な点は、UI機能を持っていて、CPLEXコードとデータをDrag and Dropすると、そのまま解が得られた点です。
更に、この時、入力データはExcelで作成可能でした。
下の画面にこの時のExcelを示します。
warehouses
とsupplyCosts
という2枚のシートがあることがわかります。
このシートは、下記のOPLコードでいうと、
{TWarehouse} warehouses = ...;
と
{TSupplyCost} supplyCosts = ...;
に対応している形になります。
// --------------------------------------------------------------------------
// Licensed Materials - Property of IBM
//
// 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55
// Copyright IBM Corporation 1998, 2013. All Rights Reserved.
//
// Note to U.S. Government Users Restricted Rights:
// Use, duplication or disclosure restricted by GSA ADP Schedule
// Contract with IBM Corp.
// --------------------------------------------------------------------------
tuple TWarehouse {
key string name;
int capacity;
float fixedCost;
}
{TWarehouse} warehouses = ...;
tuple TSupplyCost {
key string warehouseName;
key int storeId;
float cost;
}
{TSupplyCost} supplyCosts = ...;
tuple TPlan {
int nbStores;
}
// Excel化できないための一時対応
TPlan plan = <10>;
range stores = 1..plan.nbStores;
dvar boolean Open[ warehouses ];
dvar boolean Supply[ stores ][ warehouses ];
// expression
dexpr float totalOpeningCost = sum( w in warehouses ) w.fixedCost * Open[w];
dexpr float totalSupplyCost = sum( w in warehouses , s in stores, k in supplyCosts : k.storeId == s && k.warehouseName == w.name )
Supply[s][w] * k.cost;
minimize
totalOpeningCost + totalSupplyCost;
subject to {
forall( s in stores )
ctEachStoreHasOneWarehouse:
sum( w in warehouses ) Supply[s][w] == 1;
forall( w in warehouses, s in stores )
ctUseOpenWarehouses:
Supply[s][w] <= Open[w];
forall( w in warehouses )
ctMaxUseOfWarehouse:
sum( s in stores) Supply[s][w] <= w.capacity;
}
{int} StoresSupplied[w in warehouses] = { s | s in stores : Supply[s][w] == 1 };
{string} OpenWarehouses = { w.name | w in warehouses : Open[w] == 1 };
tuple TSuppliedStore {
string warehouseName;
int storeId;
}
{TSuppliedStore} network;
execute DISPLAY_RESULTS{
network.clear();
writeln("* Open Warehouses=", OpenWarehouses);
for ( var w in warehouses) {
if ( Open[w] ==1) {
writeln("* stores supplied by ", w.name, ": ", StoresSupplied[w]);
for (var s in stores) {
if (Supply[s][w] == 1) {
network.addOnly(w.name, s);
}
}
}
}
}
コードとExcelは、下記のgithubにアップしておきました。
DropSolveアプリケーションのWatson Studio / Watson Machine Learningへの移行
ここまでが、問題設定としての話の前置きで、ここからがこの記事の本題です。
これから、上記のDrop SolveサンプルアプリをWatson Stuido / Watson Machine Learningで動かすための手順について説明します。
データ準備
最初のステップとしてデータ準備を行う必要があります。
具体的には、入力Excelの各シートから、<シート名>.csv
という名前のCSVファイルをExportします。
すべてのシートについて同じ操作をして下さい。
上記のサンプルの場合、warehouses.csv
とspplyCosts.csv
の2つのファイルを準備することになります。
モデルビルダーの起動
次の手順でモデルビルダーを起動します。
資産管理の画面から、画面右上の「プロジェクトに追加」をクリック
メニューから「意思決定最適化の実験」を選択
①下の画面で「warehouse-do」などと、名称を入力
②画面右下の「Create」ボタンをクリック
これで、下のようなモデルビルダーの初期画面が表示されます。
CSVデータのロード
モデル構築の最初のステップは事前準備したCSVデータのロードです。上の画面からdrag and dropで2つのCSVデータをアップロードして下さい。
アップロードが終わると、下の画面のように2つのCSVファイルが選択された状態になるはずです。この状態で、黄色の枠で示した、Import
のリンクをクリックして下さい。
すると、下の画面のように2つのCSVデータがモデルビルダーに取り込まれた形になるはずです。これで、データの準備は完了となります。
Modelの定義
データの準備が完了したら、次はモデルの定義です。上の画面で赤枠で囲んだ、「Run model」をクリックして下さい。すると、下の画面になります。
今回は、実装済みのOPLコード(DropSolveで動いていたもの)を取り込めばいいので、一番右の「Import」を選びます。
下のような画面になります。今回の実装はOPLなので、「OPL」タブを選択します。
「Browse」ボタンをクリックして事前準備済みのOPLコードwarehouse_ml.mod
を指定します。
その後で、画面右下の「Import」ボタンをクリックして下さい。
下のような画面に遷移したら、コードの準備も完了です。
モデルの実行
これで、Watson StudioでDOモデルを実行するためのすべての準備は整いました。あとは画面右上の「Run model」ボタンをクリックすると、モデルが実行されます。
しばらく待って、下のような画面になれば、モデルの実行に成功しています。
「Solution Tables」のタブをクリックすると、下のような最適解が表示されます。
こちらもデータ一式は、下記githubにアップしておきました。
別のデータで最適化する場合
いったんModel Builderで登録したデータを削除します。それから、新しいCSVデータを同じファイル名でクラウド上にアップロードしなおして下さい。
あとは、先ほどと同じ手順で、Model Builder上にデータ登録し、モデルを実行すれば結果が得られます。
おまけ
以上で説明したように、Watson StudioのModel Builderで動くようになった最適化モデルは、Watson Machine Learingにモデルを登録することで、Webサービスとして呼び出すことも可能です。
その手順について別記事 CPLEXサンプル(warehouse)をWatson MLのWebサービス化する で解説しましたので、あわせて参照してください。