scipy.optimize.minimizeにおけるconstraintsオプションの与え方と意味について

制約付き最小化問題をscipy.optimize.minimizeで解く
---

[scipy](http://www.scipy.org/)には[minimize](http://docs.scipy.org/doc/scipy-0.16.1/reference/generated/scipy.optimize.minimize.html)という、与えた目的関数値を賢く最小化してくれる関数が入っています。
主に線形計画法なんかで使われたりすることが多いです。

最小化にも

* 取りうる値が連続 OR 離散
* 関数が線形 OR 非線形
* 変数の次元が1つ OR 複数
* 制約条件のあり OR なし

などのパターンがあり、それらに応じ最適なアルゴリズムを選択する必要があります。
これらのアルゴリズムは後述するように`method`オプションに文字列で渡すことで指定できます。

今回は**制約付き非線形最適化問題を扱う場合の話**です。
制約条件付き非線形最小化問題を扱うアルゴリズムは[COBYLA](http://docs.scipy.org/doc/scipy/reference/optimize.minimize-cobyla.html)と[SLSQP](http://docs.scipy.org/doc/scipy/reference/optimize.minimize-slsqp.html)の2つです。
呼び出しは以下のようにします。

```py:minimize関数の呼び出し例
from scipy.optimize import minimize

result = minimize(func, x0=x, constraints=cons, method="SLSQP")
```

ここで`func`は目的関数への参照、`x0`は解探索を開始する出発点の座標です。
また`constraints`が制約条件を与えるオプションになっていて、内容的には以下の様なディクショナリを渡します。
typeは制約条件が等号(eq)か不等号(ineq)かを表すものです。

```py:制約条件の例(これは適当です)
cons = (
    {'type': 'ineq', 'fun': cons(x)},
    {'type': 'eq', lambda: x: x ** 2 - 2 * x + 1}
)
```


制約条件constraintsを与える際の疑問点
---

しかしこの`constraints`オプションの使い方がイマイチわかりませんでした。
具体的に言うと

* `eq`/`ineq`は**何と等しい/等しくないことを表しているのか??**
* `ineq`の**不等号はどっちに向いてるのか??**

という疑問がありました。

結論
---

この点は**[公式リファレンス](http://docs.scipy.org/doc/scipy-0.16.1/reference/generated/scipy.optimize.minimize.html)にちゃんと書かれていました。**

> Equality constraint means that the constraint function result is to be **zero** whereas inequality means that it is to be **non-negative**. Note that COBYLA only supports inequality constraints.

> (訳) 等号制約(`eq`を指定した場合)は与えた制約条件式の値が0に等しくなることを意味し、一方で不等号制約(`ineq`を指定した場合)は式の値が非負となることを意味します。なおCOBYLAは不等号制約のみサポートしていることに注意してください。

つまり

```math
x \leq -1
```

のような不等号制約条件の場合、一度左辺へ式を寄せてしまって

```math
g(x) = x + 1
```

としたうえで

```math
g(x) \leq 0
```

であると考えます。
この$g(x)$を`'type': 'ineq'`で渡すことで**「$g(x)$は非負だよ」という条件になり、当初の目的を達成します。**

以上から

* `eq`/`ineq`はそれぞれ**0と等しい**及び**0以上(非負)**であることを表す
* `ineq`の不等号は**常に「$(与えた式) \geq 0$」**となる

あまり日本語への翻訳情報がなかったのでメモ書きしておきました。

プログラムを組んでみる
---

それでは実際にプログラムを組んでみます。
今回は説明の、あえてscipyを使うまでもないような二次関数の最小化をやってみます。

```math
\underset{x}{minimize} \: f(x) = x^2\\
s.t. x \leq -1
```

グラフに表すとこんな感じで、$(x,y) = (-1, 1)$が解となるはずです。

![グラフ](https://s3.amazonaws.com/grapher/exports/3krkltl6vi.png)

まずは問題における目的関数$f(x)$です。

```py
def func(x):
    return x**2
```

これは簡単ですね。

次に制約条件ですが、こちらも関数にしてあげます。
簡単な式であればlabmdaで与えることもできますが、今回はあえて前者の方式にしますので

```py
def cons(x):
    return -(x + 1)
```

のようにして、最終的に以下の通りとなります。

```py:constraints_sample.py
from scipy.optimize import minimize

# 目的関数
def func(x):
    return x ** 2

# 制約条件式
def cons(x):
    return -(x + 1)

# 制約条件式が非負となるようにする
cons = (
    {'type': 'ineq', 'fun': cons}
)
x = -10 # 初期値は適当

result = minimize(func, x0=x, constraints=cons, method="SLSQP")
print(result)
```

結果で`(x, fun) = (-1,1)`が得られれば成功です。

```shell-session
> python constraints_sample.py
    njev: 2
  status: 0
     fun: array([ 1.])
       x: array([-1.])
     nit: 2
 success: True
 message: 'Optimization terminated successfully.'
     jac: array([-1.99999999,  0.        ])
    nfev: 6
```

もちろん次元が増えた場合も、`numpy.array`などで扱うことができます。

参考
---

* [scipy.optimize.minimize - SciPy v0.16.1 Reference Guide](http://docs.scipy.org/doc/scipy-0.16.1/reference/generated/scipy.optimize.minimize.html)
* [多変数スカラー関数の制約付き局所的最適化 org-技術](http://org-technology.com/posts/scipy-constrained-minimization-of-multivariate-scalar-functions.html)