LoginSignup
3
1

More than 3 years have passed since last update.

CPLEXサンプル(warehouse)をWatson StudioのDecision Optimization上で動かす

Last updated at Posted at 2020-07-20

はじめに

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>

};

実行結果例は、以下のとおりです。

スクリーンショット 2020-07-20 15.20.03.png

対象アプリケーションのDropSolve化

Saas版のDropSolveの便利な点は、UI機能を持っていて、CPLEXコードとデータをDrag and Dropすると、そのまま解が得られた点です。
更に、この時、入力データはExcelで作成可能でした。

下の画面にこの時のExcelを示します。
warehousessupplyCostsという2枚のシートがあることがわかります。

スクリーンショット 2020-07-20 15.27.53.png

スクリーンショット 2020-07-20 15.28.00.png

このシートは、下記の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.csvspplyCosts.csvの2つのファイルを準備することになります。

モデルビルダーの起動

次の手順でモデルビルダーを起動します。
資産管理の画面から、画面右上の「プロジェクトに追加」をクリック

スクリーンショット 2020-07-20 15.41.54.png

メニューから「意思決定最適化の実験」を選択

スクリーンショット 2020-07-20 15.42.02.png

①下の画面で「warehouse-do」などと、名称を入力
②画面右下の「Create」ボタンをクリック

スクリーンショット 2020-07-20 15.46.37.png

これで、下のようなモデルビルダーの初期画面が表示されます。

スクリーンショット 2020-07-20 15.49.05.png

CSVデータのロード

モデル構築の最初のステップは事前準備したCSVデータのロードです。上の画面からdrag and dropで2つのCSVデータをアップロードして下さい。

アップロードが終わると、下の画面のように2つのCSVファイルが選択された状態になるはずです。この状態で、黄色の枠で示した、Importのリンクをクリックして下さい。

スクリーンショット 2020-07-20 15.51.58.png

すると、下の画面のように2つのCSVデータがモデルビルダーに取り込まれた形になるはずです。これで、データの準備は完了となります。

スクリーンショット 2020-07-20 15.54.33.png

Modelの定義

データの準備が完了したら、次はモデルの定義です。上の画面で赤枠で囲んだ、「Run model」をクリックして下さい。すると、下の画面になります。
今回は、実装済みのOPLコード(DropSolveで動いていたもの)を取り込めばいいので、一番右の「Import」を選びます。

スクリーンショット 2020-07-20 15.57.56.png

下のような画面になります。今回の実装はOPLなので、「OPL」タブを選択します。

スクリーンショット 2020-07-20 16.00.32.png

Browse」ボタンをクリックして事前準備済みのOPLコードwarehouse_ml.modを指定します。
その後で、画面右下の「Import」ボタンをクリックして下さい。

スクリーンショット 2020-07-20 16.01.18.png

下のような画面に遷移したら、コードの準備も完了です。

スクリーンショット 2020-07-20 16.01.38.png

モデルの実行

これで、Watson StudioでDOモデルを実行するためのすべての準備は整いました。あとは画面右上の「Run model」ボタンをクリックすると、モデルが実行されます。
しばらく待って、下のような画面になれば、モデルの実行に成功しています。

スクリーンショット 2020-07-20 16.08.13.png

Solution Tables」のタブをクリックすると、下のような最適解が表示されます。

スクリーンショット 2020-07-20 16.08.26.png

こちらもデータ一式は、下記githubにアップしておきました。

別のデータで最適化する場合

いったんModel Builderで登録したデータを削除します。それから、新しいCSVデータを同じファイル名でクラウド上にアップロードしなおして下さい。
あとは、先ほどと同じ手順で、Model Builder上にデータ登録し、モデルを実行すれば結果が得られます。

おまけ

以上で説明したように、Watson StudioのModel Builderで動くようになった最適化モデルは、Watson Machine Learingにモデルを登録することで、Webサービスとして呼び出すことも可能です。
その手順について別記事 CPLEXサンプル(warehouse)をWatson MLのWebサービス化する で解説しましたので、あわせて参照してください。

3
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1