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

# 始めに
別記事
[CPLEXサンプル(warehouse)をWatson StudioのDecision Optimization上で動かす](https://qiita.com/makaishi2/items/4c4935f8ce31ac4d2efe)  と 
[CPLEXサンプル(warehouse)をWatson MLのWebサービス化する](https://qiita.com/makaishi2/items/51942a303034ba664f7c) 
でOPLで実装したCPLEXのコードをWatson Studio / Watson Machine Learning上で動かす方法を説明しました。
当記事は、Python APIによる実装で同じことをした場合の手順の説明です。

# 対象コード
対象とするコードは、以下のものをポーティングしたものです。

https://ibmdecisionoptimization.github.io/docplex-doc/mp/diet.html

すべての栄養素に関する制約を満たしつつ、コスト最小の献立にするには、どうしたらいいかという問題です。

このコードをModel Builder用に修正したものが、下記になります。

```py3
#dd-cell
food = inputs['diet_food']
nutrients = inputs['diet_nutrients']
food_nutrients = inputs['diet_food_nutrients']
food_nutrients.set_index('Food', inplace=True)
#dd-cell
from docplex.mp.model import Model

# Model
mdl = Model(name='diet')

# Create decision variables, limited to be >= Food.qmin and <= Food.qmax
qty = food[['name', 'qmin', 'qmax']].copy()
qty['var'] = qty.apply(lambda x: mdl.continuous_var(lb=x['qmin'],
                                                    ub=x['qmax'],
                                                    name=x['name']),
                       axis=1)
# make the name the index
qty.set_index('name', inplace=True)

# Limit range of nutrients, and mark them as KPIs
for n in nutrients.itertuples():
    amount = mdl.sum(qty.loc[f.name]['var'] * food_nutrients.loc[f.name][n.name]
                     for f in food.itertuples())
    mdl.add_range(n.qmin, amount, n.qmax)
    mdl.add_kpi(amount, publish_name='Total %s' % n.name)

# Minimize cost
obj = mdl.sum(qty.loc[f.name]['var'] * f.unit_cost for f in food.itertuples())
mdl.add_kpi(obj, publish_name="Minimal cost");
mdl.minimize(obj)

mdl.print_information()
#dd-markdown <h1>Solve</h1>
#dd-cell
ok = mdl.solve()
#dd-cell
mdl.print_solution()
#dd-markdown Make dataframe from solution
#dd-cell
import pandas
import numpy

solution_df = pandas.DataFrame(columns=['name', 'value'])

for index, dvar in enumerate(mdl.solution.iter_variables()):
    solution_df.loc[index,'name'] = dvar.to_string()
    solution_df.loc[index,'value'] = dvar.solution_value
#dd-cell
solution_df
#dd-cell
outputs['solution'] = solution_df
```

実装上のポイントは以下の点になります。

* 入力データは辞書型の変数``inputs``から取得可能
* 例えば``inputs['diet_food']``という参照により、``diet_food.csv``をデータフレームに取り込んだ結果が取得できる
* 出力も同じルールで``outputs['solution']``という形で辞書にデータフレームを保存すると、出力用csv``solution.csv``として取得可能

# 入力データ
OPLの場合と同様に、入力データはすべてCSVにする必要があります。
今回の場合、CSVデータとしては``diet_food.csv``、``diet_nutrients.csv``、``diet_food_nutrients.csv``の３つを利用します。
それぞれのCSVファイルは、以下のgithubからダウンロードして下さい。

https://github.com/makaishi2/cplex-samples/tree/master/diet-python

## IBM クラウドへのユーザー登録・Watson Studioの準備
これからの操作をするためには、IBM Cloudにアカウント登録を行い、Watson Studioのインスタンスを作成してWatson Studioのプロジェクトを作る必要があります。また、Watson Machine Learningとの関連づけも必要です。
一連の手順は、別記事に記載してありますので、こちらを参照して下さい。

[無料でなんでも試せる! Watson Studioセットアップガイド](https://qiita.com/makaishi2/items/b666f847a98c2e42dfbb)

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

![スクリーンショット 2020-07-20 15.41.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/b611f665-b0a5-66df-b8bd-9bebee90140f.png)

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

![スクリーンショット 2020-07-20 15.42.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/a090a90f-f637-6489-3578-b255c3a932e8.png)

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


![スクリーンショット 2020-07-29 21.43.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/cdc3fc44-de5a-ed1f-7f72-ef1006e0e409.png)


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


## CSVデータのロード

モデル構築の最初のステップは事前準備したCSVデータのロードです。
①上の画面からdrag and dropで3つのCSVデータをアップロードして下さい。
②アップロードが終わると、下の画面のように3つのCSVファイルが選択された状態になるはずです。
③この状態で、黄色の枠で示した、``Import``のリンクをクリックして下さい。

![スクリーンショット 2020-07-29 21.44.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/954bfdc5-bca9-072c-14a6-1646039e8200.png)

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

![スクリーンショット 2020-07-29 21.44.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/cfe2c575-85d3-999e-9806-31e2b85b3f87.png)

## Modelの定義

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

![スクリーンショット 2020-07-29 21.44.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/b9789bd4-d838-25d9-5751-457b234ed02e.png)


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

![スクリーンショット 2020-07-29 21.45.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/1b9987db-dad0-6df4-8abf-52277a4e4ff0.png)


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

![スクリーンショット 2020-07-20 16.01.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/f6c88603-6b0e-fa29-8056-f9a94cf762db.png)

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

![スクリーンショット 2020-07-29 21.45.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/b22d42ae-a214-6b9d-32ed-fb0e9e1470cf.png)

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

![スクリーンショット 2020-07-29 21.46.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/7f47e0db-8e47-cd01-6e37-f1b9b4781c5a.png)

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

![スクリーンショット 2020-07-29 22.32.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/4057ed54-b3df-cf89-8f55-1800306b206b.png)

コード・データ一式は、下記githubにアップしておきました。

https://github.com/makaishi2/cplex-samples/tree/master/diet-python

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

## Watson MLでのWebサービス化
ここまでの動作が確認された場合、次のステップとして、動作を確認したPythonコードをWatson MLにデプロイし、Webサービスとして呼び出すことも可能です。
その手順については、別記事[CPLEXサンプル(diet.py)をWatson MLのWebサービス化する](https://qiita.com/makaishi2/items/e877211259f149c2ff2e)で紹介します。