Local Optima

数理最適化を趣味的に楽しむ。

Python + PuLP でたくさん輸送問題

第2回で輸送問題をPuLPで解きました。

local-optima.hateblo.jp

あれでもいいのですが、あのコードでは一回解いたきりで終わってしまいます。

ただの趣味ならいいけれど、最適化を外部から呼び出せるものとして実装する必要が(普通は)あるでしょう。そして、ランダムでインスタンス(問題例)が作れるタイプの問題であれば、ガンガンインスタンスを作ってガンガン解くようなテスターを作っておくと色々と便利です。定式化や解法の違いによる計算時間の比較や問題サイズに対する計算量の増加も見やすくなります。

 

私は、カプセル化のようなことをして、モデルオブジェクトと変数オブジェクトとその他必要なデータとか出力用の関数とかを詰め込んだラッパ (wrapperです、念のため) のクラスをよく作ります。このとき,同じファイルのメイン関数で1個だけインスタンス(問題例)を解くような簡易テスト付き.pyファイルに仕立ててあげるとデバッグも楽になります。ランダムなインスタンスを作って解くテスターは別のpyファイルとかJupyter Notebookに書き、ラッパをimportして使うとすっきりします。

プログラミングの腕も開発経験もない私のやり方なので、他のやり方もあると思います。ひとつの参考程度になさってください。

輸送問題のラッパ

実行すると簡単な問題例を解いて結果を表示します。

"""
transportation.py
Local Optima 2020.
"""
import itertools as itrt
import pulp

EPS = 1e-4

def _main():
    capacity = dict(enumerate([6,10]))
    demand = dict(enumerate([4, 6, 3, 3]))
    transcost = {(0, 0): 3,
                (0, 1): 7,
                (0, 2): 11,
                (0, 3): 8,
                (1, 0): 6,
                (1, 1): 7,
                (1, 2): 8,
                (1, 3): 9}
    
    tp = Transportation(capacity, demand, transcost)
    tp.solve()
    tp.print_solution()

class Transportation():
    def __init__(self, capacity, demand, transcost):
        """ Transportation Problem Interface
        Parameters-----
        capacity: dict of factory capacity
        demand: dict of demand qty from customers
        transcost: dict of transportation cost from i to j whose keys are tuple (i,j)
        """
        self.capacity = capacity
        self.demand = demand
        self.transcost = transcost

    def solve(self):
        """ Solve transportation problem """
        model, x = self._formulate()
        model.solve()
        print("Transportation Problem Solved --", pulp.LpStatus[model.status])

    def _formulate(self):
        model = pulp.LpProblem("Transportation", pulp.LpMinimize)

        # 変数オブジェクト
        x = {(i,j): pulp.LpVariable("x{}-{}".format(i,j), lowBound=0, upBound=self.demand[j]) for i,j in self.transcost}

        # 目的関数
        model += pulp.lpSum([self.transcost[i,j] * x[i,j] for i,j in self.transcost]), "Objective"

        # 制約条件
        # 工場の出荷上限
        for i, Ci in self.capacity.items():
            model += pulp.lpSum([x[i,j] for j in self.demand.keys()]) <= Ci, "Capacity{}".format(i)

        # 顧客の需要
        for j, dj in self.demand.items():
            model += pulp.lpSum([x[i,j] for i in self.capacity.keys()]) == dj, "demand{}".format(j)

        self.model = model
        self.x = x
        return model, x

    def print_solution(self):
        """ Print Solution if possible """
        if self.model.status != 1:
            print("Not Ready to print.")
            return

        print("z =", pulp.value(self.model.objective))
        for var in self.model.variables():
            if var.varValue > EPS:
                print(var, var.varValue)

if __name__ == "__main__":
    _main()

テスター

問題例をたくさん生成して解きまくるテスターは,たとえば以下のようになります。

さきほどのtransportation.pyをfrom transportation import *して使うのがミソです。

import math
import random
import itertools as itrt

from transportation import *

def distance(pos1, pos2):
    return sum([(x1-x2)**2 for x1,x2 in zip(pos1, pos2)])**0.5

def random_transportation(factory, store, mapsize=10):
    """ Returns Transportation problem wrapper
    Parameters-----
    factory: number of factories
    store: number of stores
    mapsize (optional): 
    """
    capacity = dict(enumerate([random.randint(3,15) for _ in range(factory)]))
    demand = dict(enumerate([random.randint(1,10) for _ in range(store)]))
    posi = {n:[random.uniform(0,mapsize), random.uniform(0,10)] for n in range(factory)}
    posj = {n:[random.uniform(0,mapsize), random.uniform(0,10)] for n in range(store)}

    dist = {}
    for i,j in itrt.product(posi, posj):
        dist[i,j] = math.ceil(distance(posi[i], posj[j]))
    return Transportation(capacity, demand, dist)

for _ in range(30):
    tp = random_transportation(4,6, mapsize=20)
    tp.solve()

実行結果です。

Transportation Problem Solved -- Optimal
Transportation Problem Solved -- Infeasible
Transportation Problem Solved -- Optimal
Transportation Problem Solved -- Optimal
Transportation Problem Solved -- Optimal
(以下省略)

capacitydemandをランダムに作っているので,工場の総供給能力が需要の総合計に負けてしまう場合は実行不可能(Infeasible)になります。

補遺

輸送問題は,実は面白い性質をもった線形計画問題です。

各工場の供給能力と,各店舗の需要量を整数値にしておくと,なんと連続変数なのに最適解が整数になるのです。気になる人は上のテスターをいじくって試してみてください。

これはたまたまではないことが知られています。

輸送問題を  \text{min} \{ cx | Ax \ge b \} の形に書いたときの行列  A のことを制約行列 といいますが,これが完全単模 (totally-unimodular) であるとき,連続LPを単体法で解いたときの最適解が整数になるそうです。

Python + PuLPでナップサック問題

3回目は整数計画問題ということでナップサック問題を扱います。Knapsack ProblemちぢめてKPというのがツウです。ナップ「ザ」ックと言わないのもツウです。

ナップサック問題

コソドロのあなたは、お店で盗難を計画しています(なんと物騒な)。

闇市場での売値がv_i,重量  w_i (kg)の品物を何個かずつ持って、C(kg)までなら持って逃げられそうです。何を何個ずつ盗むのがよいでしょうか。(制限に収まる限りは何個でも持ってよいのが整数KPです。)

(ちなみに、品物がどれも1点モノで、持ち出すとしても1個ずつの場合が0-1KPです。0-1KPのほうがお好きな方にはすみませんが、今回は整数計画の例題として整数KPを雑に定式化して雑に解きます。)

ナップサック問題の定式化

品物の集合を大文字のI,品物iを盗む個数をx_i (i \in I)とすると、KPは以下の最大化問題になります。


\begin{aligned}
\text{maximize}\quad & \sum _ {i \in I} v _ i x _ i \\
\text{subject to}\quad & \sum _ {i \in I} w_ i x _ i \le C \\
                          &  x _ i \in \mathbb{N} _ {0}
\end{aligned}

 \mathbb{N} _ {0} は0を含む自然数(つまり非負整数)です。

これだけですからPuLPでの定式化も簡単です。

import pulp
from math import floor

# data
value = [1,3,2,1,3,4]
weight = [2,3,4,1,4,3]
itemsize = 6

capacity = 10

# モデルオブジェクト
model = pulp.LpProblem("IntKP", pulp.LpMaximize)

# 変数オブジェクト
x = [
     pulp.LpVariable("item{}".format(i), lowBound=0, upBound=floor(capacity/weight[i]), cat=pulp.LpInteger)
     for i in range(itemsize)
    ]

LpVariableの引数で cat=pulp.LpInteger とすると整数変数に指定できます。

cat=pulp.LpBinaryで0-1変数を指定できます。そうすると0-1KPになります。書き換えて試してみてください。

ここでも,輸送問題のときと同様,自明な上界  \lfloor C/w_i \rfloor があるので一応指定してあげます。(i番目の品物だけを詰めることを考えると,キャパシティを超えない上限個数がある)

# 目的関数
model += pulp.lpSum([value[i] * x[i] for i in range(itemsize)]), "Objective"

# 制約条件
# ナップサックの重量制限
weightsum = pulp.lpSum([weight[i] * x[i] for i in range(itemsize)])
model += weightsum <= capacity, "Capacity"

# 求解
model.solve()

print("Optimal Value =", pulp.value(model.objective))
for i, xi in enumerate(x):
    print("Item{} (V={} W={})".format(i, value[i], weight[i]), pulp.value(xi))
print("Knapsack holds {} kg".format(pulp.value(weightsum)))
Optimal Value = 13.0
Item0 (V=1 W=2) 0.0
Item1 (V=3 W=3) 0.0
Item2 (V=2 W=4) 0.0
Item3 (V=1 W=1) 1.0
Item4 (V=3 W=4) 0.0
Item5 (V=4 W=3) 3.0
Knapsack holds 10.0 kg

まとめ

LpVariableの引数で cat=pulp.LpInteger とすると整数変数。

cat=pulp.LpBinaryで0-1変数。

今日はこれだけの話でした。

補遺

Google OR-tools には KPソルバーが入っています。整数計画問題としなくても解くことができます。

https://developers.google.com/optimization/introduction/overview?hl=ja

Python + PuLPで輸送問題

PuLPで最適化シリーズ第2回は、輸送問題を解いてみます。工場から店舗への供給を最適化する、ロジスティクスの基本問題です。

モデリングの復習でもありますが,リストや辞書を使ったPythonらしい書き方でモデリングをしていきます。

輸送問題の定義

m個の工場0,\cdots,i,\cdots,m-1から,n個の店舗0,\cdots,j,\cdots,n-1に物資を輸送します。工場iの供給上限C_i、店舗jの需要量d_jを満たしつつ、輸送にかかる総費用が最小となるような、工場iから店舗jへの出荷量 x _ {ij}を求めよ。工場iから店舗jへの単位輸送費用は c _ {ij}である。

輸送問題では簡単のため,総輸送費用を

 \displaystyle{\sum _ {0 \le i \lt m}\sum _ {0 \le j \lt n} c _ {ij} x _ {ij}}

とします。

Pythonのリストを使ってこのあとの処理をする都合上、添字をゼロスタートで考えてます。)

このとき輸送問題は次の最小化の線形計画問題になります。


\begin{aligned}
\text{minimize}\quad & \sum _ {0 \le i \lt m}\sum _ {0 \le j \lt n} c _ {ij} x _ {ij} \\
\text{subject to}\quad & \sum _ {0 \le j \lt n} x _ {ij} \le C_i & (0 \le i \lt n) \\
                          & \sum _ {0 \le i \lt m} x _ {ij} = d_j & (0 \le j \lt m) \\
                          & \forall x _ {ij } \ge 0
\end{aligned}

目的関数は総費用の最小化。

制約条件の1つ目は工場の出荷量合計が供給上限を超えない制約,2つ目は店舗の入荷量合計が需要と一致する制約です。

例題

Pythonのリストで例題のデータを作ります。 m=2, n=4の例を見てみましょう。

その前に,Python + PuLPで最適化するときの段取りも思い出してくださいね。

  1. インポートする (from pulp import *)
  2. モデルオブジェクトを宣言する
  3. 変数オブジェクトを宣言する
  4. 目的関数をセットする
  5. 制約条件をセットする
  6. 最適化する
  7. 結果を見る
from pulp import *

# 工場のリスト(中身は供給上限)
capacity = [6, 10]

# 店舗のリスト(中身は需要量)
demand = [4, 6, 3, 3]

# 単位費用の行列 c_ij
cost = {(0, 0): 3,
        (0, 1): 7,
        (0, 2): 11,
        (0, 3): 8,
        (1, 0): 6,
        (1, 1): 7,
        (1, 2): 8,
        (1, 3): 9  }

工場と店舗のリストはこういう風にリストで与えるといいです。

単位費用の行列(c _ {ij} )は,二重リストにすることもできますが,私のオススメはタプルをキーとする辞書(dict)にする方法です。添字のたくさんつくものは辞書にせよ。もし今後 c _ {ij} ^kみたいなものがモデルに現れても,cost[i,j,k]で参照できるようなdictにするとよいです。

なぜか。Zen of Pythonにも書いてあるとおり,"Flat is better than nested." だからです。でも,どうしても多重リストが好きな人は多重リストにしてください。

次は,モデルオブジェクトと変数オブジェクトの宣言です。変数も2添字の行列の形になりますので,for文の中で回すことになります。

model = LpProblem("Transportation", LpMinimize)

# 変数オブジェクト
x = {}
for i,j in cost:
    x[i,j] = LpVariable("x{}-{}".format(i,j), lowBound=0, upBound=demand[j])

工場iから店舗jへの出荷量 x _ {ij}は,+\inftyにデカくなることはなく,高々,店舗jの需要量までなのは間違いないので,upBound=demand[j]を入れてあげますが,敢えて入れなくても問題ありません。

リスト内包表記に強い人は,変数のところを

x = {(i,j): LpVariable("x{}-{}".format(i,j), lowBound=0, upBound=demand[j]) for i,j in cost}

と1行にしてもかまいません。

目的関数と制約条件の設定です。 和の形になっているものは,ベタ書きしないでも,LpSumという便利な関数があります。 lpSum([リスト内包表記])の形で使ってあげます。

# 目的関数
model += lpSum([cost[i,j] * x[i,j] for i,j in cost]), "Objective"

# 制約条件
# 工場の出荷上限
for i, Ci in enumerate(capacity):
    model += lpSum([x[i,j] for j in range(len(demand))]) <= Ci, "Capacity{}".format(i)

# 顧客の需要
for j, dj in enumerate(demand):
    model += lpSum([x[i,j] for i in range(len(capacity))]) == dj, "demand{}".format(j)

あとは最適化して結果を見てみましょう。

# 求解
model.solve()

# 結果の確認
print("Optimal Value =", value(model.objective))
for var in x.values():
    # 誤差以上の値を持っている変数だけprint
    if var.varValue > 1e-4:
        print(var, var.varValue)

ソルバーの挙動として,浮動小数点に誤差が乗って解となる場合があります。そのため,値1e-4=10^{-4}以上の変数だけ表示してあげています。

Optimal Value = 103.0
x0_0 4.0
x0_3 2.0
x1_1 6.0
x1_2 3.0
x1_3 1.0

こんな結果でした。店舗3には2つの工場から出荷することになっていますね。

データの生成

距離行列は,適当に作るとウソくさいデータセットになるので,

  • 10x10のマップにランダムに工場と店舗を配置し
  • 2地点間のユークリッド距離を切り上げ

た距離行列にしてみました。以下のコードで作れます。

import math
import random
import itertools as itrt
from pprint import pprint

def distance(pos1, pos2):
    return sum([(x1-x2)**2 for x1,x2 in zip(pos1, pos2)])**0.5

posi = {n:[random.uniform(0,10), random.uniform(0,10)] for n in range(2)}
posj = {n:[random.uniform(0,10), random.uniform(0,10)] for n in range(4)}

dist = {}
for i,j in itrt.product(posi, posj):
    dist[i,j] = math.ceil(distance(posi[i], posj[j]))
pprint(dist)

itertoolsは私はas itrtってインポートしてるんですが,みなさんどうしてますか?よかったらコメント欄で教えて下さい。

高校数学に行列と線形変換の復活を望む

線形計画をするからには線形代数は切っても切れませんので、まあ、ただのヒイキみたいなもんですね。

しかし、最近の学習指導要領だとベクトルさえ選択項目になっていますから、ベクトル知らないで大学生になっちゃうケースが有り得ちゃう。流石にコレはまずいと思います。統計の学修にも支障が出るのではないでしょうか。

統計を中高カリキュラムに織り込むよりも、ベクトルや行列の出てくる線形空間の感覚を時間を使って取り入れておいたほうが、大学に入ってから、統計や最適化、AIの基礎理論を学ぶのにはスムーズに行くのではないかと思います。

Python + PuLPで線形計画

しばらく,線形計画でできる話をしていきます。

線形計画問題とは

線形「計画」とはいいますが Linear Programming の訳語です。リニアが線形(一次式)で,プログラミングはこういう文脈では「計算」くらいの意味合いです。どんな計算かといえば,

  • 何本かの一次不等式で表される領域のなかで
  • 一次式の目的関数の値を最大化 (or 最小化) するような変数の組を特定する

計算であり,要するに最大最小問題の一種です。ツウの人たちはLPっていいますね。

大学入試でも,2変数の線形計画は出題されることがありますね!

mathtrain.jp

大学入試なんかだと,「最大値を求めよ」と出題され,そのときの変数の値はオマケ的だけど必ず答えにつけようねとお作法みたいに思われていますが,実用的には,「最適のときの変数の値がコレです!!」というのをちゃんと求めるのが超重要です。

ちゃんと書きますと,n個の変数  \displaystyle{ x_i (i = 1, \cdots, n)} に対して,m本の制約条件(一次不等式)で表される領域D:

 \displaystyle{\sum_{i = 1}^{n} a _ {ij} x _ i  \le b_j \quad(j=1, \cdots, m)}

の中で,目的関数

 \displaystyle{z = \sum_{i = 1}^{n} c_i x_i}

を最大(または最小)とするのが線形計画問題です。

以上をまとめて,


\begin{aligned}
\text{maximize} \quad &z = \sum_{i = 1}^{n} c_i x_i  \\ 
\text{subject to} \quad &\sum _ {i = 1}^{n} a _ {ij} x _ i  \le b _ j \quad (j=1, \cdots, m)
\end{aligned}

のように書きます。

最大値と最小値の両方を求めなくていいの?と思う方もいるでしょう。が,実際に問題を組んでみると,最大値が +\inftyだったり,ただの0だったりすることも多いので,意味のあるほうだけ求めるのが通常です。もちろん,必要があれば両方求めてOKです。

ちなみに,制約条件に等式制約を使うのもOKです。(スラック変数というものを持ち出すと不等式制約をすべて等式制約に変換できます。逆に,等式制約 A=Bも不等式制約A \le B \land A \ge Bに変換できますが細かい話は割愛。どっちも使えます。)(鉄道方面から来た方,曲線に付けるカントとスラックのスラックと一緒です)

線形計画問題の例題を解く

今回は2変数の簡単な例題を,PuLPで解いてみましょう。

PuLPについて

PuLPは,Pythonで線形計画(と整数計画)に使えるモジュールです。セットアップについては他に詳しいサイトがたくさんありますので今回は割愛。

個人的には,Anacondaを使わずにVS Code内で完結するようなセットアップが好みです。とりあえずはpipでjupyterとpulpを入れてやればいいと思います。

例題


\begin{aligned}
\text{maximize} \quad &z = 4x + 5y  \\ 
\text{subject to} \quad  & x + y    \le 3 \\
                           & x + 2y \le 4 \\
                           & x \ge 0, \quad y \ge 0
\end{aligned}

さきほどの「高校数学の美しい物語」さんのページに載っている例題と一緒です。2変数なので図形的にも解け,最適解は (2,1) z=13です。

PuLPの使い方

PuLPを使うときのおおまかな流れは

  1. インポートする (from pulp import *)
  2. モデルオブジェクトを宣言する
  3. 変数オブジェクトを宣言する
  4. 目的関数をセットする
  5. 制約条件をセットする
  6. 最適化する
  7. 結果を見る

です。

手順1,2,3は,はじめは おまじない だと思っても結構です。

from pulp import *

# モデルオブジェクトの宣言
model = LpProblem("sample", sense=LpMaximize)

# 変数オブジェクトの宣言
x = LpVariable("x", lowBound=0)
y = LpVariable("y", lowBound=0)

モデルオブジェクトで,sense=LpMaximizeは最大化問題であることを指定しています。デフォルトだと最小化問題になりますが,最大化問題のつもりで最小化問題を解いたりするポカがあるのでいつも書くようにしておくといいです。

変数オブジェクトで,lowBound=0は変数の下界を0にします。(デフォルト-\inftyです。)変数の上界がある場合はupBound=で設定できますがやはりデフォルトは+\inftyです。

手順4と5で目的関数と制約条件をセットします。同じ書き方をしますが順番が大事です。

# 最初に+=したのが目的関数と解釈される
model += 4*x + 5*y, 'objective'

# 2番目以降は制約条件と解釈される
model += x +   y <= 3, 'c1'
model += x + 2*y <= 4, 'c2'

objective, c1, c2 は式につけた名前です。

ここで一度modelの中身をprintしてみたりなんかすると,中身が見えます。

sample:
MAXIMIZE
4*x + 5*y + 0
SUBJECT TO
c1: x + y <= 3

c2: x + 2 y <= 4

VARIABLES
x Continuous
y Continuous

この問題で合っていれば,手順6 最適化してあげます。

model.solve()
print("Status:", model.status, LpStatus[model.status])

.solve()したあと,status属性を確認してあげて,最適に解けたかどうか確かめます。model.status == 1なら最適解が見つかったという意味です。LpStatusは辞書なのでキーにstatusを入れてあげると意味の取れる文字列で見せてくれます。

Status: 1 Optimal

が表示されたのでOKでした。最適値があるはずなのに Infeasible (実行不可能) と言われたり Unbounded (無限解) と言われたら,たぶん定式化(目的関数と制約条件の設定のこと)でミスっています。

手順7 最大値とそのときの各変数の値を確認してあげます。

print("z =", value(model.objective))
for var in model.variables():
    print(var, var.varValue)

value()関数を噛ませたり,varValue属性を読んだりするのがややこしく感じるかもしれません。変数オブジェクトはいつものfloatオブジェクトとはモノが異なるのでナマで参照しても値のようには振る舞いません。

z = 13.0
x 2.0
y 1.0

と,図形的に解いたのと同じ答えが求められました。

コードの全文をまとめると以下の通り。

from pulp import *

# モデルオブジェクトの宣言
model = LpProblem("sample", sense=LpMaximize)

# 変数オブジェクトの宣言
x = LpVariable("x", lowBound=0)
y = LpVariable("y", lowBound=0)

# 最初に+=したのが目的関数と解釈される
model += 4*x + 5*y, 'objective'

# 2番目以降は制約条件と解釈される
model += x +   y <= 3, 'c1'
model += x + 2*y <= 4, 'c2'

# 求解とその前後処理
print(model)
model.solve()
print("Status:", model.status, LpStatus[model.status])

# 結果の確認
print("z =", value(model.objective))
for var in model.variables():
    print(var, var.varValue)

線形計画とは結局なんなのか

線形計画問題 (LP) がもてはやされるのはなぜか。

  • n変数のLPでも高速に解けるアルゴリズムが存在する。
  • 工学的な応用範囲が広い。

もちろん,「残念ながら,世の中は非線形でできている」のはみんな知っているのですが,線形で表せる工学的(工業的)な営みや,近似して線形だと思えればそれでいいだろうというテーマがたくさんあります。組合せ的な問題も,なかにはLPになったりするものもあるのです。

LPと同じ形で、変数が整数値しか取れない場合のものを整数計画問題といい、これもPuLP (に同梱のCBCソルバー) で解くことができます。 ナップサック問題のような組合せ最適化問題が整数計画に帰着されます。k-means、k-centerのようなクラスタリングや、みんな大好きTSPも整数計画になります。(ところが整数計画シリーズは、「整数計画問題にできても、現実的な時間で最適解を求められない」ことがあります。)

Python + PuLP での最適化は、並木誠先生の本があり、理論の説明から入っていけますがミスプリが多いというAmazonのレビューもあります…。

Pythonによる数理最適化入門 (実践Pythonライブラリー)

Pythonによる数理最適化入門 (実践Pythonライブラリー)

  • 作者:並木 誠
  • 出版社/メーカー: 朝倉書店
  • 発売日: 2018/04/09
  • メディア: 単行本
 

正誤表とサンプルコードは出版社のHPでゲットできます。

補遺

フリーで使えるソルバーということでPuLP(に同梱のCBCソルバー)を当分使っていきますが,私は実はGurobi使いです(いまのところ)。pythonAPIの出来が良く、問題の記述がPuLPより簡単で柔軟で分かりやすいだけでなく,求解の(速度的な)性能も滅茶苦茶に強いです。アカデミアにいる人はアカデミックライセンスがもらえるので公式サイトを訪問してください。

python API の使い勝手でいうとSCIPもGurobiとかなり似てて使いやすそうです。が,ライセンスがハッキリしません(アカデミアは無料,商用は連絡してね(有料?),なんだけど個人の趣味用途は??)。

CBCはその名を Coin Branch and Cut Solver というだけあって,本当は分枝カット法に基づく高度な解法を実装できるんじゃないかと思うんですが,残念ながらPuLPはそういうところまで踏み込めないみたいです。C++が使える人はゴリゴリにCBCを使い倒してみてもいいんじゃないでしょうか。pythonで比較的深いところまでCBCを叩けるcbcpyなるモジュールもあるらしいので,このブログでもそのうちやってみたいことの一つです。

Googleのor-toolsも無料にしては優秀なんじゃないかと思っていますが,Python APIC++のコードから自動生成したもののようでエラく書きにくいので,今回はパス。が,便利そうなツールなのでこれもそのうちやってみたいですね。(ただの混合整数計画を組んでしまうと結局回るのはCBCソルバーだそうです。)こちらもC++が読み書きできる方にはよいかもしれません。

Local Optima はじめました

最適化とかに関わることを書いてみたくなったので,ブログ立ち上げました。

Pythonを使っていきます。大学生・大学院生や最適化を実務に取り入れたい皆さんの役に立てば幸いです。

オペレーションズ・リサーチ(など)を勉強していました。実用を考えたときに日本語ベースのいい読み物がそんなに多くないなーと思っていました。

Local Optimaは,「局所最適解」の意味です。なんかカッコイイからですw

最適化ガチ勢の皆さんには知識でも腕前でも勝てませんので,おかしなところのご指摘、よりよいプラクティスのご指導があればコメントでいただけますと幸いです。

次回,次々回ではPuLP線形計画問題を解いていこうと思います。