ブログ

【Julia入門】Juliaの基本事項⑥ 文字列・Unicode文字列

統計や機械学習に関するプログラミングではPythonRが用いられることが多いですが、近年Juliaも注目を集めています。そこで当シリーズではJuliaの基本構文からライブラリの用い方などについて取りまとめます。当記事ではJuliaにおける文字列の取り扱いについて取りまとめを行いました。

Julia入門
https://www.hello-statisticians.com/julia

Julia 1.8 Documentation
https://docs.julialang.org/en/v1/

文字列

文字列の基本的な取り扱い

Juliaで文字列を取り扱うにあたっては下記のように""を用います。

s = "Hello Julia"
println(s)
println(typeof(s))

・実行結果

Hello Julia
String

上記のtypeof関数の出力により、文字列を取り扱う際の型はString型であることが確認できます。文字列の取り扱いにあたっては下記のようにインデックスを指定することで文字を取り出すこともできます。

s = "Hello Julia"
println(s[1])
println(typeof(s[1]))

・実行結果

H
Char

上記の出力結果より、文字を取り扱う際の型がChar型であることも確認できます。また、インデックスには数字だけでなく最後の文字を表すendや、1文字目〜5文字目までを表す1:5のような表現も用いることができます。

s = "Hello Julia"
println(s[end])
println(s[1:5])

・実行結果

a
Hello

文字列の連結

Juliaでは下記のようにstringを用いることで文字列の連結を行うことができます。

s1 = "Hello"
s2 = "Julia"
s = string(s1, " ", s2)

println(s1)
println(s2)
println(s)

・実行結果

Hello
Julia
Hello Julia

文字列の連結にあたってはstringと同様に*演算子を用いることもできます。

s1 = "Hello"
s2 = "Julia"
s = s1 * " " * s2)

println(s1)
println(s2)
println(s)

・実行結果

Hello
Julia
Hello Julia

文字列の補間

print文やprintln文の中で変数に格納された値を用いることを文字列の補間(string interpolation)と言います。たとえば下記を実行することで前項と同様なHello Juliaの出力を得ることができます。

s1 = "Hello"
s2 = "Julia"
println("$s1 $s2")

・実行結果

Hello Julia

Unicode文字列

参考

Julia 1.8 Documentation
https://docs.julialang.org/en/v1/

【Julia入門】Juliaの基本事項⑤ Juliaにおける複素数の取り扱い

統計や機械学習に関するプログラミングではPythonRが用いられることが多いですが、近年Juliaも注目を集めています。そこで当シリーズではJuliaの基本構文からライブラリの用い方などについて取りまとめます。当記事ではJuliaにおける複素数の取り扱いについて取りまとめを行いました。

Julia入門
https://www.hello-statisticians.com/julia

Julia 1.8 Documentation
https://docs.julialang.org/en/v1/

複素数の基本的な取り扱い

虚数単位の表現

Juliaにおける虚数単位の表現にあたってはimを用います。imは下記のように用いることができます。

z = 1 + 2im
println(z)

・実行結果

1 + 2im

複素数の基本演算

複素数の基本的な演算は下記のように行うことができます。

z1 = 1 + 2im
z2 = 2 - 3im

println(z1+z2)
println(z1-z2)
println(z1*z2)

・実行結果

3 - 1im
-1 + 5im
8 + 1im

複素数の乗算に関しては合わせてド・モアブルの定理なども抑えておくと良いと思います。

実部・虚部の取得

Juliaでの複素数の取り扱いにあたっては下記のようにrealimagを用いることで複素数の実部と虚部の取得を行うことができます。

z = 1 + 2im
println(real(z))
println(imag(z))

・実行結果

1
2

複素共役と絶対値の取り扱い

複素共役の取得

複素共役は下記のようにconjを用いることで取得できます。

z = 1 + 2im
println(conj(z))

・実行結果

1 - 2im

絶対値の取得

複素数の絶対値はabsを用いることで下記のように取得できます。

z = 1 + 2im
println(abs(z))

・実行結果

2.23606797749979

参考

Julia 1.8 Documentation
https://docs.julialang.org/en/v1/

統計学を学ぶにあたって最低限抑えておきたい数学 〜確率・期待値〜

当記事では「統計学を学ぶにあたって最低限抑えておきたい数学」の中から「確率・期待値」に関して取り扱います。確率・期待値は統計学を学ぶにあたっての主要トピックである確率分布を取り扱うにあたって必須の内容なので、一通り理解しておくと良いと思います。
取りまとめにあたっては数学の解説に関してはなるべくシンプルに取り扱いますが、統計学への応用に関連した複雑な内容に関しては目次に「*」をつけました。「*」がついているものはやや難しいので、読み飛ばしても問題ありません。

・基本数学まとめ
https://www.hello-statisticians.com/math_basic

確率

確率の定義

全事象$U$の事象の起こる確率がどれも「同様に確からしい」場合、全事象$U$の場合の数を$n(U)$、事象$A$の場合の数を$n(A)$とおく。このとき事象$A$が起こる確率$P(A)$は下記のように定義できる。
$$
\large
\begin{align}
P(A) = \frac{n(A)}{n(U)}
\end{align}
$$

和の法則

条件付き確率

期待値

「確率」の統計学への応用

確率分布

Ch.7 「関数の展開」の演習問題の解答例 〜統計学のための数学入門30講〜

当記事は「統計学のための数学入門$30$講(朝倉書店)」の読解サポートにあたってChapter.$7$の「関数の展開」の章末問題の解答の作成を行いました。
基本的には書籍の購入者向けの解説なので、まだ入手されていない方は購入の上ご確認ください。また、解説はあくまでサイト運営者が独自に作成したものであり、書籍の公式ページではないことにご注意ください。

・書籍解答まとめ
https://www.hello-statisticians.com/answer_textbook_math#math_stat

本章のまとめ

演習問題解答

問題$7.1$

$\displaystyle f(x) = \frac{1}{x+a} = (x+a)^{-1}$の導関数は下記のように計算できる。
$$
\large
\begin{align}
f'(x) &= -(x+a)^{-2} \\
f^{”}(x) &= 2!(x+a)^{-3} \\
f^{(3)}(x) &= -3!(x+a)^{-4} \\
f^{(4)}(x) &= 4!(x+a)^{-5} \\
f^{(5)}(x) &= -5!(x+a)^{-6} \\
& \vdots
\end{align}
$$

よって$\displaystyle f(x) = \frac{1}{x+a} = (x+a)^{-1}$のテイラー展開は下記のように得られる。
$$
\large
\begin{align}
f(x) &= f(0) + \frac{f'(0)}{1!}x + \frac{f^{”}(0)}{2!}x^2 + \frac{f^{(3)}(0)}{3!}x^3 \cdots \\
&= a^{-1} + \frac{-a^{-2}}{1!}x + \frac{2!a^{-3}}{2!}x^2 + \frac{-3!a^{-4}}{3!}x^3 \cdots \\
&= \frac{1}{a} – \frac{1}{a^2}x + \frac{1}{a^3}x^2 – \frac{1}{a^4}x^3 \cdots , \quad |x|<|a|
\end{align}
$$

問題$7.2$

下記のような結果が得られる。

$x$ 真値第$2$項まで第$3$項まで第$4$項まで
$0.5$ $0.66666667$$0.5$$0.75$$0.625$
$0.3$ $0.76923077$$0.7$$0.79$$0.763$
$0.1$ $0.9090909$$0.9$$0.91$$0.909$
$0.05$ $0.95238095$$0.95$$0.9525$$0.952375$
$0.01$ $0.9900990$$0.99$$0.9901$$0.990099$
$0$ $1$$1$$1$$1$

詳しい計算やグラフの描画は下記を実行することで行うことができる。

import numpy as np
import matplotlib.pyplot as plt

x = np.array([0.5, 0.3, 0.1, 0.05, 0.01, 0.])
a = 1.
f_x = np.zeros([6,4])

f_x[:,0] = 1/(x+a)

f_x[:,1] = 1/a - x/a**2
f_x[:,2] = 1/a - x/a**2 + x**2/a**3
f_x[:,3] = 1/a - x/a**2 + x**2/a**3 - x**3/a**4

print(f_x)

label_line = ["origin", "2nd_order", "3rd_order", "4th_order"]

for i in range(f_x.shape[1]):
    plt.plot(x, f_x[:,i], label=label_line[i])

plt.legend()
plt.show()

・実行結果

[[ 0.66666667  0.5         0.75        0.625     ]
 [ 0.76923077  0.7         0.79        0.763     ]
 [ 0.90909091  0.9         0.91        0.909     ]
 [ 0.95238095  0.95        0.9525      0.952375  ]
 [ 0.99009901  0.99        0.9901      0.990099  ]]

【Julia入門】Juliaの基本事項④ 基本的な演算子・更新演算子

統計や機械学習に関するプログラミングではPythonRが用いられることが多いですが、近年Juliaも注目を集めています。そこで当シリーズではJuliaの基本構文からライブラリの用い方などについて取りまとめます。当記事ではJuliaの基本的な演算子と更新演算子について取りまとめを行いました。

Julia入門
https://www.hello-statisticians.com/julia

Julia 1.8 Documentation
https://docs.julialang.org/en/v1/

基本的な演算子

基本的な演算子まとめ

四則演算のような基本的な計算は下記のような演算子を用いることで実行できます。

演算子 概要
x + y 加算
x - y 減算
x * y 乗算
x / y 除算
x^y べき乗
x % y 除算の余り

等号・不等号が成立するかの確認や論理演算は下記のような演算子を用いることで実行できます。

演算子 概要
x == y 等価演算子
x != y 不等価演算子
x > y 大なり演算子
x >= y 大なりイコール演算子
x & y and
x | y or
!x 否定

基本的な演算子の使用例

四則演算に関する演算子は下記のように計算を行うことができます。

x = 5
y = 2

println(x+y)
println(x-y)
println(x*y)
println(x/y)
println(x^y)
println(x%y)

・実行結果

7
3
10
2.5
25
1

等号・不等号に関する演算子は下記のように使用することができます。

x = 5
y = 2

println(x == y)
println(x != y)
println(x > y)
println(x >= y)
println(!(x > y))

・実行結果

false
true
true
true
false

更新演算子

更新演算子まとめ

Juliaには下記のような更新演算子があります。

演算子 概要
+= 加算
-=減算
*= 乗算
/= 除算
^= べき乗
%= 除算の余り

更新演算子を用いたx += 1x = x+1と同様の処理を行います。

更新演算子の使用例

更新演算子を用いることで下記のような計算を行うことができます。

x = 1
println(x)

x += 1
println(x)

・実行結果

1
2

更新演算子はfor文やwhile文などのループ処理などで用いられることが多いことも合わせて抑えておくと良いと思います。

参考

Julia 1.8 Documentation
https://docs.julialang.org/en/v1/

【Julia入門】Juliaの基本事項③ Juliaの定数の定義と予め定義された定数

統計や機械学習に関するプログラミングではPythonRが用いられることが多いですが、近年Juliaも注目を集めています。そこで当シリーズではJuliaの基本構文からライブラリの用い方などについて取りまとめます。当記事ではJuliaの定数について取りまとめを行いました。

Julia入門
https://www.hello-statisticians.com/julia

Julia 1.8 Documentation
https://docs.julialang.org/en/v1/

定数の定義

Juliaにおける定数の定義は下記のようにconstを用いることで行うことができます。

const c1 = 1.0
println(c1)

・実行結果

1.0

変数では一度定めた値を変更できますが、定数の場合は基本的には更新できないことに注意が必要です。

const c2 = 1.0
c2 = 2.0

・実行結果

WARNING: redefinition of constant c2. This may fail, cause incorrect answers, or produce other errors.

予め定義された定数

円周率を表すpiは予め定義されているので、三角関数などの計算の際は用いると良いです。

println(pi/3)
println(cos(pi/3))

・実行結果

1.0471975511965976
0.5000000000000001

また、VERSIONにはJuliaのバージョンが格納されていることも抑えておくと良いと思います。

println(VERSION)

・実行結果

1.8.0

バージョンに関してはインストールされたJuliaのバージョンが出力されるので、インストールのタイミングによって結果が異なることにご注意ください。

参考

Julia 1.8 Documentation
https://docs.julialang.org/en/v1/

【Julia入門】Juliaの基本事項② Juliaのプリミティブ型・任意精度演算

統計や機械学習に関するプログラミングではPythonRが用いられることが多いですが、近年Juliaも注目を集めています。そこで当シリーズではJuliaの基本構文からライブラリの用い方などについて取りまとめます。当記事ではJuliaのプリミティブ型と任意精度演算について取りまとめを行いました。

Julia入門
https://www.hello-statisticians.com/julia

Julia 1.8 Documentation
https://docs.julialang.org/en/v1/

プリミティブ型

プリミティブ型

Juliaの標準のプリミティブ型は下記があります。

Int8 UInt8Int16UInt16Int32UInt32Int64
UInt64 Int128UInt128BoolFloat16Float32Float64

基本的な理解にあたってはIntが符号付き整数型、UIntが符号なし整数型、Floatが浮動小数点型を表し、それぞれ続く数字がビット数を表すと抑えておくと良いです。

typeof関数

Juliaの全ての値には型が存在し、下記のようにtypeof関数を用いることで型の確認を行うことができます。

println(typeof(1))
println(typeof(0.5))

・実行結果

Int64
Float64

数値は基本的に上記のように整数を表すInt型や浮動小数を表すFloat型を元に表されますが、truefalseのような値は下記で確認できるようにBool型を元に表されます。

println(typeof(true))
println(typeof(false))

・実行結果

Bool
Bool

プレフィックス・サフィックス・任意精度演算

プレフィックス

サフィックス

任意精度演算

整数は基本的にInt8型〜Int128型で表されますが、下記で確認できるように桁の大きな場合はBigInt型を用いて表されます。

x1 = 12345678901234567890123456789
x2 = 12345678901234567890123456789012345
x3 = 1234567890123456789012345678901234567890

println(typeof(x1))
println(typeof(x2))
println(typeof(x3))

・実行結果

Int128
Int128
BigInt

参考

Julia 1.8 Documentation
https://docs.julialang.org/en/v1/

【Julia入門】Juliaの基本事項① Juliaにおける変数の使い方

統計や機械学習に関するプログラミングではPythonRが用いられることが多いですが、近年Juliaも注目を集めています。そこで当シリーズではJuliaの基本構文からライブラリの用い方などについて取りまとめます。当記事ではJuliaにおける変数の使い方について取りまとめを行いました。

Julia入門
https://www.hello-statisticians.com/julia

Julia 1.8 Documentation
https://docs.julialang.org/en/v1/

基本事項

print・println

Juliaの出力にあたってよく用いるのがprintprintln文です。どちらも出力を制御しますが、printが改行を含まないのに対し、printlnが改行を含むことに注意が必要です。下記のサンプルを実行すると理解しやすいです。

print("Hello World 1.")
println("Hello World 2.")
print("Hello World 3.")

・実行結果

Hello World 1.Hello World 2.
Hello World 3.

上記で得られるようにprintが改行を含まないのに対し、printlnが改行を含むことを抑えておくと良いです。

変数の数値の代入

変数への数値の代入にあたってはPythonなど多くの言語で用いられるように=を用いることで代入を行うことができます。

x = 1
y = 2.0

println(x)
println(y)

・実行結果

1
2.0

上記ではxには整数、yには小数を代入しましたが、それぞれ出力でも区別されていることも抑えておくと良いです。

Juliaの変数と数式

Juliaにおける乗算・累乗

Juliaでは変数の前に数値をつけると暗黙に掛け算が実行されます。たとえば$x=1$のとき$2x+1$は下記のように出力できます。

x = 1
println(2x+1)

・実行結果

3

上記では$2x+1$を計算するにあたって、2*xのように演算子を用いなくとも計算が行われたことに着目しておくと良いです。

また、累乗は下記のように^を用いて実行することができます。

x = 1
println(2(x-3)^2 - 3(x-2))

・実行結果

11

乗算と累乗についてはTeXの式表記に対応していることも合わせて抑えておくと良いかもしれません。

Juliaにおける三角関数

Juliaを用いて三角関数を表す場合、piを用いることで円周率の$\pi = 3.14 \cdots$を表すことができることを抑えておくと良いです。

theta = pi/4
println(theta)

・実行結果

0.7853981633974483

また、三角関数のcossinなどもそのまま用いることができることも抑えておくと良いと思います。

theta = pi/6
println(cos(theta))
println(sin(theta))

・実行結果

0.8660254037844387
0.49999999999999994

参考

Julia 1.8 Documentation
https://docs.julialang.org/en/v1/

Ch.3 「代表的な確率分布」の章末問題の解答例 〜現代数理統計学の基礎(共立出版)〜

当記事は「現代数理統計学の基礎(共立出版)」の読解サポートにあたってChapter.3の「代表的な確率分布」の章末問題の解説について行います。
基本的には書籍の購入者向けの解説なので、まだ入手されていない方は購入の上ご確認ください。また、解説はあくまでサイト運営者が独自に作成したものであり、書籍の公式ページではないことにご注意ください。

・解答まとめ
https://www.hello-statisticians.com/answer_textbook_math_stat#basic

章末の演習問題について

問題3.1の解答例

問題3.2の解答例

問題3.3の解答例

問題3.4の解答例

問題3.5の解答例

問題3.6の解答例

問題3.7の解答例

問題3.8の解答例

問題3.9の解答例

問題3.10の解答例

問題3.11の解答例

$$
\large
\begin{align}
f(x|\alpha,\beta) = \frac{\beta \alpha^{\beta}}{x^{\beta+1}}, \quad \alpha<x, \, \alpha>0, \, \beta>0
\end{align}
$$

$\beta > 1$のとき$E[X]$は下記のように計算することができる。
$$
\large
\begin{align}
E[X] &= \int_{\alpha}^{\infty} x f(x) dx \\
&= \int_{\alpha}^{\infty} x \cdot \frac{\beta \alpha^{\beta}}{x^{\beta+1}} dx \\
&= \int_{\alpha}^{\infty} \frac{\beta \alpha^{\beta}}{x^{\beta}} dx \\
&= \left[ -\frac{\beta \alpha^{\beta}}{\beta-1} \cdot \frac{1}{x^{\beta-1}} \right]_{\alpha}^{\infty} \\
&= \frac{\beta \alpha^{\beta}}{\beta-1} \cdot \frac{1}{\alpha^{\beta-1}} \\
&= \frac{\beta \alpha}{\beta-1}
\end{align}
$$

同様に$\beta > 2$のとき$E[X^2]$は下記のように計算できる。
$$
\large
\begin{align}
E[X^2] &= \int_{\alpha}^{\infty} x^2 f(x) dx \\
&= \int_{\alpha}^{\infty} \frac{\beta \alpha^{\beta}}{x^{\beta-1}} dx \\
&= \left[ -\frac{\beta \alpha^{\beta}}{\beta-2} \cdot \frac{1}{x^{\beta-2}} \right]_{\alpha}^{\infty} \\
&= \frac{\beta \alpha^{\beta}}{\beta-2} \cdot \frac{1}{\alpha^{\beta-2}} \\
&= \frac{\beta \alpha^2}{\beta-2}
\end{align}
$$

よって分散$V[X]$は$V[X]=E[X^2]-E[X]^2$を用いて下記のように得られる。
$$
\large
\begin{align}
V[X] &= E[X^2] – E[X]^2 \\
&= \frac{\beta \alpha^2}{\beta-2} – \frac{\beta^2 \alpha^2}{(\beta-1)^2} \\
&= \frac{\beta \alpha^2}{(\beta-1)^2(\beta-2)} [ (\beta-1)^2 – \beta(\beta-2) ] \\
&= \frac{\beta \alpha^2}{(\beta-1)^2(\beta-2)} [ (\beta^2 – 2\beta + 1) – (\beta^2 – 2\beta) ] \\
&= \frac{\beta \alpha^2}{(\beta-1)^2(\beta-2)}
\end{align}
$$

問題3.12の解答例

問題3.13の解答例

問題3.14の解答例

問題3.15の解答例

問題3.16の解答例

問題3.17の解答例

問題3.18の解答例

問題3.19の解答例

問題3.20の解答例

問題3.21の解答例

問題3.22の解答例

まとめ

制約付き問題の最適化とKKT(Karush Kuhn Tucker)条件

カルシュ・キューン・タッカー(KKT; Karush Kuhn Tucker)条件は制約付き問題の最適化の際に用いられる$1$次の必要条件で、様々な問題の最適化にあたって用いられます。当記事ではKKT条件など、制約付き問題の最適化における重要なトピックについて取りまとめを行いました。
「新版 数理計画入門(朝倉書店)」の$4.7$節の「制約付き問題の最適性条件」などの内容を参考に作成を行いました。

・用語/公式解説
https://www.hello-statisticians.com/explain-terms

制約付き問題とKKT条件

問題設定

目的関数$f(\mathbf{x})$の制約付き最適化は下記のように表すことができる。
$$
\large
\begin{align}
\mathrm{Objective} : \quad & f(\mathbf{x}) \, \rightarrow \, \mathrm{Minimize} \\
\mathrm{Constraint} : \quad & c_i(\mathbf{x}) = 0 \quad (i=1, 2, \cdots ,l) \\
& c_i(\mathbf{x}) \leq 0 \quad (i=l+1, l+2, \cdots ,m)
\end{align}
$$

上記は一般的な表記である一方で抽象的なので、以下では下記の具体例を元に確認を行う。
$$
\large
\begin{align}
\mathrm{Objective} : \quad & f(\mathbf{x}) = (x_1-1)^{2} + (x_2-2)^2 \, \rightarrow \, \mathrm{Minimize} \\
\mathrm{Constraint} : \quad & c_1(\mathbf{x}) = x_1^2+x_2^2-2 \leq 0 \\
& c_2(\mathbf{x}) = -x_1+x_2 \leq 0 \\
& c_3(\mathbf{x}) = -x_2 \leq 0
\end{align}
$$

図に基づく最適解の導出と有効制約

最適解の導出にあたってまず制約条件の$c_1(\mathbf{x})$〜$c_3(\mathbf{x})$の取る領域の図示を行う。$x_1^2+x_2^2 \leq 2, x_2 \leq x_1, x_2 \geq 0$が成立する領域は下記を実行することで図示できる。

import numpy as np
import matplotlib.pyplot as plt

r = np.sqrt(2)
theta = np.linspace(0,2*np.pi,100)

x = np.arange(-2., 2.01, 0.01)
x_r1 = np.linspace(0., 1., 100)
x_r2 = np.linspace(1., np.sqrt(2), 100)

plt.plot(r*np.cos(theta), r*np.sin(theta), "g--")
plt.plot(x, 0*x, "g--")
plt.plot(x, x, "g--")

plt.fill_between(x_r1, x_r1*0, x_r1, color="green")
plt.fill_between(x_r2, x_r2*0, np.sqrt(2-x_r2**2+0.0001), color="green")
plt.show()

・実行結果

$\sqrt{2-x_1^2}$の値が計算誤差により負の値になる場合があるのでnp.sqrt(2-x_r2**2+0.0001)のように0.0001を加算を行なった。さらに下記を実行することで制約条件の領域に目的関数$f(\mathbf{x}) = (x_1-1)^{2} + (x_2-2)^2$を描画できる。

import numpy as np
import matplotlib.pyplot as plt

r = np.sqrt(2)
theta = np.linspace(0,2*np.pi,100)

x_r1 = np.linspace(0., 1., 100)
x_r2 = np.linspace(1., np.sqrt(2), 100)

x_, y_ = np.arange(-2., 2.01, 0.01), np.arange(-1., 3.01, 0.01)
x, y = np.meshgrid(x_,y_)
z = (x-1)**2 + (y-2)**2

levs = np.arange(0.5,3.5,0.5)**2

plt.plot(r*np.cos(theta), r*np.sin(theta), "k--")

plt.fill_between(x_r1, x_r1*0, x_r1, color="green")
plt.fill_between(x_r2, x_r2*0, np.sqrt(2-x_r2**2+0.0001), color="green")
plt.contour(x,y,z,levels=levs,cmap="viridis")

plt.show()

・実行結果

上図の緑の領域の中で目的関数の等高線が中心の紫に近い$(x_1,x_2)=(1,1)$が最適解である。また、最適解の$(1,1)$で等号が成立する制約条件を有効制約というが、この例では$c_1(\mathbf{x}) = x_1^2+x_2^2-2 \leq 0$と$c_2(\mathbf{x}) = -x_1+x_2 \leq 0$が対応する。

KKT条件

制約付き最適化問題の最適解では基本的に下記で表すように目的関数と有効制約の勾配ベクトルが釣り合うような状態となる。

import numpy as np
import matplotlib.pyplot as plt

r = np.sqrt(2)
theta = np.linspace(0,2*np.pi,100)

x_r1 = np.linspace(0., 1., 100)
x_r2 = np.linspace(1., np.sqrt(2), 100)

x_, y_ = np.arange(-2., 2.01, 0.01), np.arange(-1., 3.01, 0.01)
x, y = np.meshgrid(x_,y_)
z = (x-1)**2 + (y-2)**2

levs = np.arange(0.5,3.5,0.5)**2

plt.plot(r*np.cos(theta), r*np.sin(theta), "k--")

plt.fill_between(x_r1, x_r1*0, x_r1, color="green")
plt.fill_between(x_r2, x_r2*0, np.sqrt(2-x_r2**2+0.0001), color="green")
plt.contour(x,y,z,levels=levs,cmap="viridis")

plt.quiver(1, 1, -1, 1, color="green", angles='xy', scale_units='xy', scale=1)
plt.quiver(1, 1, 1, 1, color="green", angles='xy', scale_units='xy', scale=1)
plt.quiver(1, 1, 0, -2, color="purple", angles='xy', scale_units='xy', scale=1)

plt.show()

・実行結果

上図では目的関数の勾配ベクトルを紫、有効制約の勾配ベクトルを緑で表した。このような勾配ベクトルの釣り合いを元にカルシュ・キューン・タッカー(KKT; Karush Kuhn Tucker)条件が定式化される。局所最適解$x^{*}$に関してKKT条件は一般的には下記の数式で表される。
$$
\large
\begin{align}
& \nabla f(\mathbf{x}^{*}) + \sum_{i=1}^{n} \lambda_i^{*} \nabla c_i(\mathbf{x}^{*}) = \mathbf{0} \quad (1) \\
& c_i(\mathbf{x}^{*}) = 0 \qquad (i=1, \cdots, l) \\
& c_i(\mathbf{x}^{*}) \leq 0, \lambda_i^{*} \geq 0 \qquad (i=l+1, \cdots, m) \\
& c_i(\mathbf{x}^{*}) < 0, \lambda_i^{*} = 0 \qquad (i=m+1, \cdots, n)
\end{align}
$$

当項で取り扱った図におけるベクトルの釣り合いは上記の$(1)$式に$x_1=1,x_2=1$を代入することで下記のように$\displaystyle \lambda_1=\frac{1}{2}, \lambda_2=1$を得た。
$$
\large
\begin{align}
\nabla f(\mathbf{x}) + \lambda_1 \nabla c_1(\mathbf{x}) + \lambda_2 \nabla c_2(\mathbf{x}) &= \mathbf{0} \\
\nabla [(x_1-1)^2+(x_2-2)^2] + \lambda_1 \nabla (x_1^2+x_2^2-2) + \lambda_2 \nabla (-x_1+x_2) &= \mathbf{0} \\
\left(\begin{array}{c} 2(x_1-1) \\ 2(x_2-2) \end{array} \right) + \lambda_1 \left(\begin{array}{c} 2x_1 \\ 2x_2 \end{array} \right) + \lambda_2 \left(\begin{array}{c} -1 \\ 1 \end{array} \right) &= \left(\begin{array}{c} 0 \\ 0 \end{array} \right) \\
\left(\begin{array}{c} 2(x_1-1) + 2\lambda_1 – \lambda_2 \\ 2(x_2-2) + 2\lambda_1 + \lambda_2 \end{array} \right) &= \left(\begin{array}{c} 0 \\ 0 \end{array} \right) \\
\left(\begin{array}{c} 2 \cdot (1-1) + 2\lambda_1 – \lambda_2 \\ 2 \cdot (1-2) + 2\lambda_1 + \lambda_2 \end{array} \right) &= \left(\begin{array}{c} 0 \\ 0 \end{array} \right) \\
\left(\begin{array}{c} 2\lambda_1 – \lambda_2 \\ 2\lambda_1 + \lambda_2 \end{array} \right) &= \left(\begin{array}{c} 0 \\ 2 \end{array} \right) \\
\lambda_1=\frac{1}{2}, \lambda_2=1 &
\end{align}
$$

当項で取り扱ったKKT条件は制約付き最適化における$1$次の必要条件であり、十分条件ではないことに注意が必要である。十分性については次節で確認を行う。

制約付き問題の最適解の十分条件

凸計画問題とKKT条件

① 目的関数が凸関数
② 等式制約関数$c_i$が$1$次関数
③ 不等式制約関数$c_i$が凸関数

上記が成立する問題を『凸計画問題』というが、凸計画問題でKKT条件が成立する場合の$\mathbf{x}^{*}$はその問題の『大域的最適解』であることが知られている。

ヘッセ行列と$2$次の十分条件

$$
\large
\begin{align}
\mathrm{Objective} : \quad & f(\mathbf{x}) \, \rightarrow \, \mathrm{Minimize} \\
\mathrm{Constraint} : \quad & c_i(\mathbf{x}) = 0 \quad (i=1, 2, \cdots ,l) \\
& c_i(\mathbf{x}) \leq 0 \quad (i=l+1, l+2, \cdots ,m)
\end{align}
$$

上記のように定義した制約付き最適化問題に対し、ラグランジュ関数$L(\mathbf{x},\boldsymbol{\lambda})$を下記のように定義する。
$$
\large
\begin{align}
L(\mathbf{x},\boldsymbol{\lambda}) = f(\mathbf{x}) + \sum_{i=1}^{m} \lambda_{i} c_{i}(\mathbf{x})
\end{align}
$$

上記のラグランジュ関数$L$に対し、変数$\mathbf{x}$に関する勾配ベクトル$\nabla_{x} L(\mathbf{x},\boldsymbol{\lambda})$とヘッセ行列$\nabla_{x}^{2} L(\mathbf{x},\boldsymbol{\lambda})$を下記のように定義する。
$$
\large
\begin{align}
\nabla_{x} L(\mathbf{x},\boldsymbol{\lambda}) &= \nabla f(\mathbf{x}) + \sum_{i=1}^{m} \lambda_{i} \nabla c_{i}(\mathbf{x}) \\
\nabla_{x}^{2} L(\mathbf{x},\boldsymbol{\lambda}) &= \nabla^{2} f(\mathbf{x}) + \sum_{i=1}^{m} \lambda_{i} \nabla^{2} c_{i}(\mathbf{x})
\end{align}
$$

ここで全ての有効制約の勾配ベクトルと直交するベクトルの集合を$M^{*}$とおく。このとき任意のベクトル$\mathbf{y} \in M^{*}$に対して下記が成立することが最適性の$2$次の必要条件である。
$$
\large
\begin{align}
\mathbf{y}^{\mathrm{T}} \nabla_{x}^{2} L(\mathbf{x}^{*},\boldsymbol{\lambda}^{*}) \mathbf{y} \geq 0
\end{align}
$$

同様に任意のベクトル$\mathbf{y} \in M^{*}$に対して下記が成立することが最適性の$2$次の十分条件である。
$$
\large
\begin{align}
\mathbf{y}^{\mathrm{T}} \nabla_{x}^{2} L(\mathbf{x}^{*},\boldsymbol{\lambda}^{*}) \mathbf{y} > 0
\end{align}
$$

$2$次の十分条件の具体例

$$
\large
\begin{align}
\mathrm{Objective} : \quad & f(\mathbf{x}) = -2 x_1 x_2^2 \, \rightarrow \, \mathrm{Minimize} \\
\mathrm{Constraint} : \quad & c_1(\mathbf{x}) = \frac{1}{2} x_1^2 + x_2^2 – \frac{3}{2} \leq 0 \\
& c_2(\mathbf{x}) = -x_1 \leq 0 \\
& c_3(\mathbf{x}) = -x_2 \leq 0
\end{align}
$$

目的関数と制約条件は下記を実行することで図示することができる。

import numpy as np
import matplotlib.pyplot as plt

theta = np.linspace(0,2*np.pi,100)
x_r = np.linspace(0., np.sqrt(3), 100)

x_, y_ = np.arange(-3., 3.01, 0.01), np.arange(-3., 3.01, 0.01)
x, y = np.meshgrid(x_,y_)

z = -2*x*y**2

levs = np.arange(-6,6,2)

plt.plot(np.sqrt(3)*np.cos(theta), np.sqrt(1.5)*np.sin(theta), "k--")
plt.fill_between(x_r, x_r*0, np.sqrt(1.5-0.5*x_r**2), color="green")
plt.contour(x,y,z,levels=levs,cmap="viridis")

plt.colorbar()
plt.show()

・実行結果

上図より、$x_1=1, x_2=1$のとき目的関数が最小値$f(\mathbf{x}) = -2 \cdot 1 \cdot 1^2 = -2$を取ることや有効制約が$c_1(\mathbf{x})$であることが確認できる。

ここで目的関数$f(\mathbf{x})$の勾配ベクトル$\nabla f(\mathbf{x})$や有効制約$c_1(\mathbf{x})$の勾配ベクトル$\nabla c_1(\mathbf{x})$は下記のように表せる。
$$
\large
\begin{align}
\nabla f(\mathbf{x}) &= \left(\begin{array}{c} -2x_2^2 \\ -4x_1x_2 \end{array} \right) \\
\nabla c_1(\mathbf{x}) &= \left(\begin{array}{c} x_1 \\ 2x_2 \end{array} \right)
\end{align}
$$

上記に$\displaystyle \mathbf{x} = \left(\begin{array}{c} x_1^{*} \\ x_2^{*} \end{array} \right) = \left(\begin{array}{c} 1 \\ 1 \end{array} \right)$を代入すると下記が得られる。
$$
\large
\begin{align}
\nabla f(\mathbf{x}^{*}) &= \left(\begin{array}{c} -2 \\ -4 \end{array} \right) \\
\nabla c_1(\mathbf{x}^{*}) &= \left(\begin{array}{c} 1 \\ 2 \end{array} \right)
\end{align}
$$

上記を元に$\nabla f(\mathbf{x}^{*})$と$\lambda_1 \nabla c_1(\mathbf{x}^{*}) = 2 \nabla c_1(\mathbf{x}^{*})$は下記のように図示できる。

import numpy as np
import matplotlib.pyplot as plt

theta = np.linspace(0,2*np.pi,100)
x_r = np.linspace(0., np.sqrt(3), 100)

x_, y_ = np.arange(-3.5, 3.51, 0.01), np.arange(-3., 5.01, 0.01)
x, y = np.meshgrid(x_,y_)

z = -2*x*y**2

levs = np.arange(-6,6,2)

plt.plot(np.sqrt(3)*np.cos(theta), np.sqrt(1.5)*np.sin(theta), "k--")
plt.fill_between(x_r, x_r*0, np.sqrt(1.5-0.5*x_r**2), color="lightgreen")
plt.contour(x,y,z,levels=levs,cmap="viridis")

plt.quiver(1, 1, -2, -4, color="purple", angles='xy', scale_units='xy', scale=1)
plt.quiver(1, 1, 2, 4, color="green", angles='xy', scale_units='xy', scale=1)

plt.show()

・実行結果

また、ヘッセ行列$\nabla^{2} f(\mathbf{x})$と$\nabla^{2} c_1(\mathbf{x})$は下記のように得られる。
$$
\large
\begin{align}
\nabla^{2} f(\mathbf{x}) &= \left(\begin{array}{cc} 0 & -4x_2 \\ -4x_2 & -4x_1 \end{array} \right) \\
\nabla^{2} c_1(\mathbf{x}) &= \left(\begin{array}{cc} 1 & 0 \\ 0 & 2 \end{array} \right)
\end{align}
$$

上記より$\mathbf{x}^{*}$におけるラグランジュ関数のヘッセ行列$\nabla_{x}^{2} L(\mathbf{x}^{*},\boldsymbol{\lambda}^{*})$は下記のように得られる。
$$ \large \begin{align} \nabla_{x}^{2} L(\mathbf{x}^{*},\boldsymbol{\lambda}^{*}) &= \nabla^{2} f(\mathbf{x}^{*}) + \lambda_{1}^{*} \nabla^{2} c_1(\mathbf{x}^{*}) \\
&= \left(\begin{array}{cc} 0 & -4 \cdot 1 \\ -4 \cdot 1 & -4 \cdot 1 \end{array} \right) + 2 \left(\begin{array}{cc} 1 & 0 \\ 0 & 2 \end{array} \right) \\
&= \left(\begin{array}{cc} 2 & -4 \\ -4 & 0 \end{array} \right)
\end{align}
$$

ここで最適解$\mathbf{x}^{*}$における有効制約の勾配ベクトル$\displaystyle \nabla c_1(\mathbf{x}^{*}) = \left(\begin{array}{c} 1 \\ 2 \end{array} \right)$に垂直なベクトル$\mathbf{y}$は下記のように表せる。
$$
\large
\begin{align}
\mathbf{y} = \left(\begin{array}{c} 2t \\ -t \end{array} \right), \quad t \in \mathbb{R} \quad \mathrm{and} \quad t \neq 0
\end{align}
$$

上記に対し、$\mathbf{y}^{\mathrm{T}} \nabla_{x}^{2} L(\mathbf{x}^{*},\boldsymbol{\lambda}^{*}) \mathbf{y}$は下記のように表せる。
$$
\large
\begin{align}
\mathbf{y}^{\mathrm{T}} \nabla_{x}^{2} L(\mathbf{x}^{*},\boldsymbol{\lambda}^{*}) \mathbf{y} &= \left(\begin{array}{cc} 2t & -t \end{array} \right) \left(\begin{array}{cc} 2 & -4 \\ -4 & 0 \end{array} \right) \left(\begin{array}{c} 2t \\ -t \end{array} \right) \\
&= \left(\begin{array}{cc} 8t & -8t \end{array} \right) \left(\begin{array}{c} 2t \\ -t \end{array} \right) \\
&= 24t^2 > 0
\end{align}
$$

上記より$\mathbf{y}^{\mathrm{T}} \nabla_{x}^{2} L(\mathbf{x}^{*},\boldsymbol{\lambda}^{*}) \mathbf{y} > 0$が成立するので$2$次の十分条件が成立することが確認できる。