# メモ

お気楽 sympy プログラミング超入門

http://www.nct9.ne.jp/m_hiroi/light/python3.html#sympy_abc

* Hello, SymPy!!
* SymPy の基本
* 多項式
* 微積分
* 集合
* 行列
* 線形代数
* 素数と約数
* 数列と級数
* 論理代数
* 等式と不等式
* 微分方程式
* いろいろな関数
* 特殊関数
* 関数のグラフ

を読む。

微分

$f(x) = x^2$ の微分は $f'(x) = 2x$ になるが、sympy
で次のように計算できる。

```python
from sympy.abc import *
from sympy import *
f = x**2 + 2*x + 1
display(f)
g = f.diff()
display(g)
```

$\displaystyle x^{2} + 2 x + 1$

$\displaystyle 2 x + 2$

$x$ に値を入れずに、数式のままで微分ができている。

このようなシステムを数式処理システム formula manipulation system と呼ぶ。

# 基本

有理数は `Rational(p, q)`

sympy の虚数単位は I

sympy は虚数単位を含む数式を複素数として扱う。

定数
*   sympy では特殊な定数 (E, pi, oo) や数学関数を数式として扱う
*   E : 自然対数の底 (ネイピア数)
*   pi : 円周率
*   oo : 無限大

```python
from sympy import *
display(Integer(12345))
display(Integer('12345') + 3)
display(Rational(1, 2))
display(Integer(2) / Integer(6)) # Rational(1,3)
display(Rational(1, 2) + Rational(1, 3))
print()
display(1.1 + 2.2 * I)
display(sqrt(2))
display(sqrt(8))
display(pi)
display(E)
display(exp(1))
display(oo)
```

$\displaystyle 12345$

$\displaystyle 12348$

$\displaystyle \frac{1}{2}$

$\displaystyle \frac{1}{3}$

$\displaystyle \frac{5}{6}$

$\displaystyle 1.1 + 2.2 i$

$\displaystyle \sqrt{2}$

$\displaystyle 2 \sqrt{2}$

$\displaystyle \pi$

$\displaystyle e$

$\displaystyle e$

$\displaystyle \infty$

```python
# 数式の出力
from sympy import *
# init_printing() # colab ではデフォルトなので必ずしも指定しないでよい
display(sqrt(2))
display(1 / sqrt(2))
x,y = symbols('x,y')
display(x**2+y+3)
```

$\displaystyle \sqrt{2}$

$\displaystyle \frac{\sqrt{2}}{2}$

$\displaystyle x^{2} + y + 3$

# 変数の定義

シンボル (symbol) を使って数式の変数を表す。

シンボルは `Symbol(), symbols(), var()` などで生成する。

引数にはシンボルの名前 (文字列) を渡す。

`Symbol()` はシンボルをひとつ生成する。

`symbols()` と `var()` は複数のシンボルを生成する。

*   引数の文字列を空白文字やカンマ (,) で区切る。
*   コロン (:) を使うと複数のシンボルを簡単に指定できる。
*   たとえば、a:z とすると a から z までの 26 個のシンボルを生成する。
*   x:10 は x0 から x9 まで, x1:6 は x1 から x5 までのシンボルを生成する。

生成したシンボルは python の変数に格納して使用する。

シンボル名と python の変数名は同じにしたほうがわかりやすい。

var() は同じ名前の大域変数に生成したシンボルを格納する。

`from sympy.abc import *` は一度にシンボルを生成できて便利だが変数をオーバーライトするので注意する。

```python
from sympy import *
x, y, z = symbols('x y z')
display(x)
display(y)
display(z)
display(x + 2*y + 3*z)
display((x + y) ** 2)
display((x + y + z) ** 3)
display(x**3 + 2*x**2 + 3*x + 4)
display(sqrt(x))
display(sqrt(x*x + y*y))
var('a:c')
display(a)
display(b)
display(c)
var('x1:4')
display(x1)
display(x2)
display(x3)

```

$\displaystyle x$

$\displaystyle y$

$\displaystyle z$

$\displaystyle x + 2 y + 3 z$

$\displaystyle \left(x + y\right)^{2}$

$\displaystyle \left(x + y + z\right)^{3}$

$\displaystyle x^{3} + 2 x^{2} + 3 x + 4$

$\displaystyle \sqrt{x}$

$\displaystyle \sqrt{x^{2} + y^{2}}$

$\displaystyle a$

$\displaystyle b$

$\displaystyle c$

$\displaystyle x_{1}$

$\displaystyle x_{2}$

$\displaystyle x_{3}$

数式 expr の展開は関数 `expand()` で、因数分解は関数 `factor()`
で行うことができる。

数式の展開は目的別の関数も用意されている。

*   `expand_power_exp(), expand_power_base()` $\quad$ 指数を含む式の展開
*   `expand_trig()` $\quad$ 三角関数を含む式の展開
*   `expand_log()` $\quad$ 対数関数を含む式の展開

```python
from sympy.abc import *
from sympy import *
display(expand((x + y)**2))
display(expand((x + y + z)**2))
display(expand((x + y)**3))
a = expand((x + y)**4)
display(a)
display(factor(a))
display(factor(x**2 - 1))
display(factor(x**2 - 2*x + 1))
display(factor(x**3 + y**3))
```

$\displaystyle x^{2} + 2 x y + y^{2}$

$\displaystyle x^{2} + 2 x y + 2 x z + y^{2} + 2 y z + z^{2}$

$\displaystyle x^{3} + 3 x^{2} y + 3 x y^{2} + y^{3}$

$\displaystyle x^{4} + 4 x^{3} y + 6 x^{2} y^{2} + 4 x y^{3} + y^{4}$

$\displaystyle \left(x + y\right)^{4}$

$\displaystyle \left(x - 1\right) \left(x + 1\right)$

$\displaystyle \left(x - 1\right)^{2}$

$\displaystyle \left(x + y\right) \left(x^{2} - x y + y^{2}\right)$

```python
from sympy.abc import *
from sympy import *
display(x**(y+z))
display(expand(x**(y+z)))
display(expand_power_exp(x**(y+z)))
display((x*y)**z)
display(expand((x*y)**z))
display(expand_power_base((x*y)**z, force=True)) # force=True が必要

```

$\displaystyle x^{y + z}$

$\displaystyle x^{y} x^{z}$

$\displaystyle x^{y} x^{z}$

$\displaystyle \left(x y\right)^{z}$

$\displaystyle \left(x y\right)^{z}$

$\displaystyle x^{z} y^{z}$

```python
from sympy.abc import *
from sympy import *
display(expand(sin(x + y))) # expand しない
display(expand_trig(sin(x + y)))
display(expand_trig(cos(x + y)))
display(expand_trig(sin(2*x)))
display(expand_trig(cos(2*x)))

```

$\displaystyle \sin{\left(x + y \right)}$

$\displaystyle \sin{\left(x \right)} \cos{\left(y \right)} + \sin{\left(y \right)} \cos{\left(x \right)}$

$\displaystyle - \sin{\left(x \right)} \sin{\left(y \right)} + \cos{\left(x \right)} \cos{\left(y \right)}$

$\displaystyle 2 \sin{\left(x \right)} \cos{\left(x \right)}$

$\displaystyle 2 \cos^{2}{\left(x \right)} - 1$

```python
from sympy.abc import *
from sympy import *
display(expand_log(log(x*y)))
display(expand_log(log(x*y), force=True))
display(expand_log(log(x/y)))
display(expand_log(log(x/y), force=True))
display(expand_log(log(x**2)))
display(expand_log(log(x**2), force=True))

```

$\displaystyle \log{\left(x y \right)}$

$\displaystyle \log{\left(x \right)} + \log{\left(y \right)}$

$\displaystyle \log{\left(\frac{x}{y} \right)}$

$\displaystyle \log{\left(x \right)} - \log{\left(y \right)}$

$\displaystyle \log{\left(x^{2} \right)}$

$\displaystyle 2 \log{\left(x \right)}$

```python
from sympy.abc import *
from sympy import *
var('x1 y1 z1', positive=True)
display(expand_log(log(x1*y1)))
display(expand_log(log(x1/y1)))
display(expand_log(log(x1**2)))
```

$\displaystyle \log{\left(x_{1} \right)} + \log{\left(y_{1} \right)}$

$\displaystyle \log{\left(x_{1} \right)} - \log{\left(y_{1} \right)}$

$\displaystyle 2 \log{\left(x_{1} \right)}$

メモ $\quad$ 変数の定義域が指定されていないと数学的に展開 expand が無理な場合、展開しない。 変数を positive=True にするか、force=True で展開できる。

数式の簡約化 `simplify()`

数式の簡約化は関数 `simplify()` を使うと簡単

目的別の関数もある。

*   `powsimp()` $\quad$ 指数の簡約化
*   `trigsimp()` $\quad$ 三角関数を含む式の簡約化
*   `logcombine()` $\quad$ 対数関数を含む式の簡約化

```python
from sympy.abc import *
from sympy import *
display((x**2 + 2*x + 1)/(x + 1))
display(simplify((x**2 + 2*x + 1)/(x + 1)))
display(simplify((x**2 - 1)/(x + 1)))
display(x**y * x**z)
display(powsimp(x**y * x**z))
display(x**y / x**z)
display(powsimp(x**y / x**z))

```

$\displaystyle \frac{x^{2} + 2 x + 1}{x + 1}$

$\displaystyle x + 1$

$\displaystyle x - 1$

$\displaystyle x^{y} x^{z}$

$\displaystyle x^{y + z}$

$\displaystyle x^{y} x^{- z}$

$\displaystyle x^{y - z}$

```python
from sympy.abc import *
from sympy import *
display(sin(x)**2 + cos(x)**2)
display(trigsimp(sin(x)**2 + cos(x)**2))
display(sin(x)*cos(y) + cos(x)*sin(y))
display(trigsimp(sin(x)*cos(y) + cos(x)*sin(y)))
display(cos(x)*cos(y) + sin(x)*sin(y))
display(trigsimp(cos(x)*cos(y) + sin(x)*sin(y)))
display(1 + tan(x)**2)
display(trigsimp(1 + tan(x)**2))
display(cos(x+y))
display(expand_trig(cos(x+y)))
```

$\displaystyle \sin^{2}{\left(x \right)} + \cos^{2}{\left(x \right)}$

$\displaystyle 1$

$\displaystyle \sin{\left(x \right)} \cos{\left(y \right)} + \sin{\left(y \right)} \cos{\left(x \right)}$

$\displaystyle \sin{\left(x + y \right)}$

$\displaystyle \sin{\left(x \right)} \sin{\left(y \right)} + \cos{\left(x \right)} \cos{\left(y \right)}$

$\displaystyle \cos{\left(x - y \right)}$

$\displaystyle \tan^{2}{\left(x \right)} + 1$

$\displaystyle \frac{1}{\cos^{2}{\left(x \right)}}$

$\displaystyle \cos{\left(x + y \right)}$

$\displaystyle - \sin{\left(x \right)} \sin{\left(y \right)} + \cos{\left(x \right)} \cos{\left(y \right)}$

```python
from sympy.abc import *
from sympy import *
display(log(x) + log(y))
display(logcombine(log(x) + log(y)))
display(logcombine(log(x) + log(y), force=True))
display(log(x) - log(y))
display(logcombine(log(x) - log(y)))
display(logcombine(log(x) - log(y), force=True))
display(x * log(y))
display(logcombine(x * log(y)))
display(logcombine(x * log(y), force=True))

```

$\displaystyle \log{\left(x \right)} + \log{\left(y \right)}$

$\displaystyle \log{\left(x \right)} + \log{\left(y \right)}$

$\displaystyle \log{\left(x y \right)}$

$\displaystyle \log{\left(x \right)} - \log{\left(y \right)}$

$\displaystyle \log{\left(x \right)} - \log{\left(y \right)}$

$\displaystyle \log{\left(\frac{x}{y} \right)}$

$\displaystyle x \log{\left(y \right)}$

$\displaystyle x \log{\left(y \right)}$

$\displaystyle \log{\left(y^{x} \right)}$

メモ $\quad$ 数式の変数に値を代入するには `subs()` を使う。

数式を評価して数値 float にするには `evalf()`、`N()` を使う。

```python
from sympy.abc import *
from sympy import *
a = x**2
display(a)
display(a.subs(x, 10))
display(a.subs(x, 1.2345))
display(a.subs(x, y + z))
display(a.subs(x, sqrt(2)))
b = a.subs(x, y + z)
display(b)
display(b.subs({y: sqrt(2), z: sqrt(3)}))

```

$\displaystyle x^{2}$

$\displaystyle 100$

$\displaystyle 1.52399025$

$\displaystyle \left(y + z\right)^{2}$

$\displaystyle 2$

$\displaystyle \left(y + z\right)^{2}$

$\displaystyle \left(\sqrt{2} + \sqrt{3}\right)^{2}$

```python
from sympy.abc import *
from sympy import *
display(pi.evalf())
display(N(pi, 50))
display(sqrt(2).evalf(50))
a = x**2
b = a.subs(x, y + z)
display(b)
display(b.subs({y: sqrt(2), z: sqrt(3)}).evalf())
display(b.subs({y: sqrt(2), z: sqrt(3)}).evalf(20))

```

$\displaystyle 3.14159265358979$

$\displaystyle 3.1415926535897932384626433832795028841971693993751$

$\displaystyle 1.4142135623730950488016887242096980785696718753769$

$\displaystyle \left(y + z\right)^{2}$

$\displaystyle 9.89897948556636$

$\displaystyle 9.8989794855663561964$

# いまここ

# 多項式 polynomial
* 多項式の除算で商と余りを求めるには関数 `div()` を使う
* 有理式 (多項式の分数) の簡約化には関数 `cansel()` がある
* 関数 `apart()` は有理式を「部分分数」に分解する
* 部分分数とは、分母が因数分解されている有理式をいくつかの有理式に分解すること
* 次の条件を満たすとき、有理式 $g(x) / f(x)$ は簡単な形に分解できる
  * f(x) は 1 次式の積に因数分解できるような整式であること
  * g(x) は f(x) より次数が少ない整式であること

  ``` {.item}
  g(x) / f(x) = b1 / (x - a1) + b2 / (x - a2) + ... + bn / (x - an)
  ```

* 関数 `collect()` は指定した変数で式をまとめる
* そのあと、メソッド `coeff()` で指定した次数の係数を取り出すことができる

```python
# div
from sympy.abc import *
from sympy import *
numerator = x**3 + x**2 - 4*x + 1
denominator = x-2
display(numerator, denominator)
quatient,remainder = div(numerator,denominator)
display(quatient,remainder)
display(quatient * denominator)
display(expand(quatient * denominator))
display(expand(quatient * denominator + remainder))
```

$\displaystyle x^{3} + x^{2} - 4 x + 1$

$\displaystyle x - 2$

$\displaystyle x^{2} + 3 x + 2$

$\displaystyle 5$

$\displaystyle \left(x - 2\right) \left(x^{2} + 3 x + 2\right)$

$\displaystyle x^{3} + x^{2} - 4 x - 4$

$\displaystyle x^{3} + x^{2} - 4 x + 1$

```python
# div
from sympy.abc import *
from sympy import *
numerator = x**3 - x**2 - 4*x + 4
denominator = x-2
display(numerator, denominator)
quatient,remainder = div(numerator,denominator)
display(quatient,remainder)
display(quatient * denominator)
display(expand(quatient * denominator))
display(expand(quatient * denominator + remainder))
```

$\displaystyle x^{3} - x^{2} - 4 x + 4$

$\displaystyle x - 2$

$\displaystyle x^{2} + x - 2$

$\displaystyle 0$

$\displaystyle \left(x - 2\right) \left(x^{2} + x - 2\right)$

$\displaystyle x^{3} - x^{2} - 4 x + 4$

$\displaystyle x^{3} - x^{2} - 4 x + 4$

```python
# cancel
from sympy.abc import *
from sympy import *
numerator = x**3 - x**2 - 4*x + 4
denominator = x-2
display(numerator / denominator)
display(cancel(numerator / denominator))
display(simplify(numerator / denominator))
```

$\displaystyle \frac{x^{3} - x^{2} - 4 x + 4}{x - 2}$

$\displaystyle x^{2} + x - 2$

$\displaystyle x^{2} + x - 2$

```python
# cancel
from sympy.abc import *
from sympy import *
numerator = x**2 + 2*x + 1
denominator = x**2 + x
display(numerator / denominator)
display(cancel(numerator / denominator))
display(simplify(numerator / denominator))
```

$\displaystyle \frac{x^{2} + 2 x + 1}{x^{2} + x}$

$\displaystyle \frac{x + 1}{x}$

$\displaystyle \frac{x + 1}{x}$

```python
# cancel
from sympy.abc import *
from sympy import *
expr = 1/(x - 1) + 1/(x - 2)
display(expr)
display(cancel(expr))
display(factor(cancel(expr)))
display(simplify(expr))
display(expand(simplify(expr)))
```

$\displaystyle \frac{1}{x - 1} + \frac{1}{x - 2}$

$\displaystyle \frac{2 x - 3}{x^{2} - 3 x + 2}$

$\displaystyle \frac{2 x - 3}{\left(x - 2\right) \left(x - 1\right)}$

$\displaystyle \frac{2 x - 3}{\left(x - 2\right) \left(x - 1\right)}$

$\displaystyle \frac{2 x}{x^{2} - 3 x + 2} - \frac{3}{x^{2} - 3 x + 2}$

```python
# apart
from sympy.abc import *
from sympy import *
numerator = 2*x - 3
denominator = x**2 - 3*x + 2
display(numerator/denominator)
display(apart(numerator/denominator))
display(factor(numerator/denominator))
display(simplify(numerator/denominator))
```

$\displaystyle \frac{2 x - 3}{x^{2} - 3 x + 2}$

$\displaystyle \frac{1}{x - 1} + \frac{1}{x - 2}$

$\displaystyle \frac{2 x - 3}{\left(x - 2\right) \left(x - 1\right)}$

$\displaystyle \frac{2 x - 3}{x^{2} - 3 x + 2}$

```python
# apart
from sympy.abc import *
from sympy import *
numerator = 6*x**2 + x - 17
denominator = x**3 - 7*x -6
display(numerator/denominator)
display(apart(numerator/denominator))
display(factor(numerator/denominator))
display(simplify(numerator/denominator))
```

$\displaystyle \frac{6 x^{2} + x - 17}{x^{3} - 7 x - 6}$

$\displaystyle \frac{1}{x + 2} + \frac{3}{x + 1} + \frac{2}{x - 3}$

$\displaystyle \frac{6 x^{2} + x - 17}{\left(x - 3\right) \left(x + 1\right) \left(x + 2\right)}$

$\displaystyle \frac{- 6 x^{2} - x + 17}{- x^{3} + 7 x + 6}$

```python
# apart
from sympy.abc import *
from sympy import *
numerator = x + 1
denominator = x**4 + x**2
display(numerator/denominator)
display(apart(numerator/denominator))
display(factor(numerator/denominator))
display(simplify(numerator/denominator))
```

$\displaystyle \frac{x + 1}{x^{4} + x^{2}}$

$\displaystyle - \frac{x + 1}{x^{2} + 1} + \frac{1}{x} + \frac{1}{x^{2}}$

$\displaystyle \frac{x + 1}{x^{2} \left(x^{2} + 1\right)}$

$\displaystyle \frac{x + 1}{x^{4} + x^{2}}$

```python

    >>> apart((x + 1) / (x**4 + x**2))
      x + 1    1     1
    - ───── + ─── + ────
       2       x      2
      x  + 1         x

    >>> x**2 + y*x + z*x + y*z
     2
    x  + x⋅y + x⋅z + y⋅z
    >>> collect(x**2 + y*x + z*x + y*z, x)
     2
    x  + x⋅(y + z) + y⋅z
    >>> collect(x**2 + y*x + z*x + y*z, x).coeff(x, 2)
    1
    >>> collect(x**2 + y*x + z*x + y*z, x).coeff(x, 1)
    y + z
    >>> collect(x**2 + y*x + z*x + y*z, x).coeff(x, 0)
    y⋅z

方程式の求解には関数 solveset(expr, var) を使う

数式 expr が 0 になる変数 var の値を求める

キーワード引数 domain で領域を指定できる (Complexes が default)

たとえば、x^2^ + 1 = 0 の解は I, -I になるが、実数では存在しない

解が存在しない場合は空集合を表す EmptySet() を返す

sympy の集合 (Set) は python の set とは違う (あとで説明する)

sympy には特別な集合を表すクラスがあらかじめ定義されている

*   EmptySet, 空集合
*   UniversalSet, 全体集合
*   Naturals, 自然数全体の集合
*   Naturals0, 零と自然数全体の集合
*   Integers, 整数全体の集合
*   Reals, 実数全体の集合
*   Complexes, 複素数全体の集合
*   これらのクラスのオブジェクト (シングルトン) は sympy.S
    の同名の変数に格納されている

関数 roots(expr, var) は解とその個数を辞書に格納して返す

重根があるときは便利

    >>> solveset(x**2 - 1, x)
    {-1, 1}
    >>> solveset(x**2 + 1, x)
    {-ⅈ, ⅈ}

    >>> S.UniversalSet
    UniversalSet()
    >>> S.EmptySet
    ∅
    >>> S.Complexes
    ℂ
    >>> S.Reals
    ℝ
    >>> S.Integers
    ℤ
    >>> S.Naturals
    ℕ

    >>> solveset(x**2 + 1, x, domain=Reals)
    ∅
    >>> var('a b c')
    (a, b, c)
    >>> solveset(a*x**2 + b*x + c, x)
    ⎧           _____________              _____________ ⎫
    ⎪          ╱           2              ╱           2  ⎪
    ⎨   b    ╲╱  -4⋅a⋅c + b        b    ╲╱  -4⋅a⋅c + b   ⎬
    ⎪- ──── - ────────────────, - ──── + ────────────────⎪
    ⎩  2⋅a         2⋅a            2⋅a         2⋅a        ⎭

    >>> solveset(x**3, x)
    {0}
    >>> roots(x**3, x)
    {0: 3}
    >>> solveset(x**3 - 4*x**2 + 5*x - 2, x)
    {1, 2}
    >>> roots(x**3 - 4*x**2 + 5*x - 2, x)
    {1: 2, 2: 1}

連立一次方程式は関数 linsolve(\[expr1, expr2, \...\], \[var1, var2,
\...\]) を使って解く

式と変数はタプルに格納して渡してもよい

鶴亀算

1.  鶴と亀、合わせて 100 匹いる。足の合計が 272
    本のとき、鶴と亀はそれぞれ何匹ずついるか。
2.  鶴と亀とトンボが合わせて 10 匹いる。足の合計が 38 本で羽の合計が 14
    枚であるとき、鶴と亀とトンボはそれぞれ何匹ずついるか。(トンボの足は
    6 本で羽は 4 枚)
3.  鶏と犬とタコ、合わせて 24 匹が台所にいる。足の合計が 102
    本のとき、鶏、犬、タコはそれぞれ何匹ずついるか。

解答

1.  x + y = 100, 2x + 4y = 272 を解く
2.  x + y + z = 10, 2x + 4y + 6z = 38, 2x + 4z = 14 を解く
3.  x + y + z= 24, 2x + 4y + 8z = 102 を解く

solveset() や linsolve() の他にも、方程式の解を求める関数 solve() がある

#### ●微積分 {#abc03}

微分

*   微分は関数 diff(expr, var, \[n\]) で行う
*   引数 n に数値を指定すると高階微分になる
*   または、diff(expr, var, var, \...) のように変数 var
    の後に同じ変数を指定すると高階微分になる
*   多変数関数の微分 (偏微分) も diff(expr, var1, var2, \...) で OK
*   Derivative() は diff()
    と引数が同じで、評価前の状態を保持するオブジェクトを返す
*   メソッド doit() で微分を実行する

<!-- -->

    >>> var('n x y z')
    (n, x, y, z)
    >>> diff(x**2, x)
    2⋅x
    >>> diff(x**2, x, 2)
    2
    >>> diff(x**2, x, 3)
    0
    >>> diff(x**n, x)
       n
    n⋅x
    ────
     x

    >>> diff(x**n, x, 2)
       n
    n⋅x ⋅(n - 1)
    ────────────
          2
         x

    >>> diff(log(x), x)
    1
    ─
    x

    >>> diff(exp(x), x)
     x
    ℯ
    >>> diff(sin(x), x)
    cos(x)
    >>> diff(cos(x), x)
    -sin(x)
    >>> diff(tan(x), x)
       2
    tan (x) + 1

    >>> diff(x**3*y**3*z**3, x)
       2  3  3
    3⋅x ⋅y ⋅z
    >>> diff(x**3*y**3*z**3, x, x)
         3  3
    6⋅x⋅y ⋅z
    >>> diff(x**3*y**3*z**3, x, x, y, y, z, z)
    216⋅x⋅y⋅z
    >>> diff(x**3*y**3*z**3, x, 2, y, 2, z, 2)
    216⋅x⋅y⋅z

    >>> Derivative((x**3 * y**3 * z**3), x, y, z)
        3
       ∂       ⎛ 3  3  3⎞
    ───────────⎝x ⋅y ⋅z ⎠
    ∂z ∂y ∂x
    >>> Derivative((x**3 * y**3 * z**3), x, y, z).doit()
        2  2  2
    27⋅x ⋅y ⋅z
    >>> Derivative((x**3 * y**3 * z**3), x, 2, y, 2, z, 2)
          6
         ∂     ⎛ 3  3  3⎞
    ───────────⎝x ⋅y ⋅z ⎠
      2   2   2
    ∂z  ∂y  ∂x
    >>> Derivative((x**3 * y**3 * z**3), x, 2, y, 2, z, 2).doit()
    216⋅x⋅y⋅z

積分

*   積分は関数 integrate(expr, var) で行う (積分定数は出力されない)
*   定積分も integrate(expr, (var, low, high)) で OK
*   二重積分も integrate(expr, (var1, l1, h1), (var2, l2, h2), \...) で
    OK
*   Integral() は integrate()
    と引数が同じで、評価前の状態を保持するオブジェクトを返す
*   メソッド doit() で微分を実行する
*   積分を評価できないときは Integral() を返す

<!-- -->

    >>> integrate(x, x)
     2
    x
    ──
    2
    >>> integrate(x**2, x)
     3
    x
    ──
    3
    >>> integrate(x**3, x)
     4
    x
    ──
    4
    >>> integrate(x, (x, 0, 1))
    1/2
    >>> integrate(x**2, (x, 0, 1))
    1/3
    >>> integrate(x**3, (x, 0, 1))
    1/4
    >>> integrate(exp(x), x)
     x
    ℯ
    >>> integrate(exp(-x), x)
      -x
    -ℯ
    >>> integrate(exp(-x), (x, 0, oo))
    1
    >>> integrate(1/x, x)
    log(x)
    >>> integrate(log(x), x)
    x⋅log(x) - x
    >>> integrate(sin(x), x)
    -cos(x)
    >>> integrate(cos(x), x)
    sin(x)
    >>> integrate(tan(x), x)
    -log(cos(x))
    >>> integrate(4/(1 + x**2), x)
    4⋅atan(x)
    >>> integrate(4/(1 + x**2), (x, 0, 1))
    π

    >>> Integral(x*y**2, x, y)
    ⌠ ⌠
    ⎮ ⎮    2
    ⎮ ⎮ x⋅y  dx dy
    ⌡ ⌡
    >>> Integral(x*y**2, x, y).doit()
     2  3
    x ⋅y
    ─────
      6
    >>> integrate(x*y**2, (x, 0, 1), (y, 0, 1))
    1/6
    >>> integrate(x*y**2, (x, 1 - y, 1), (y, 0, 1))
    3/20

    >>> integrate(x**x, x)
    ⌠
    ⎮  x
    ⎮ x  dx
    ⌡

極限

*   極限は関数 limit(function, var, point) で計算する
*   f(x) の x -\> 0 の極限は limit(f(x), x, 0) とする
*   極限の方向は第 4 引数で指定する (\;+\; or \;-\;)

<!-- -->

    >>> limit(x, x, oo)
    ∞
    >>> limit(1/x, x, oo)
    0
    >>> limit(1/x, x, 0, '+')
    ∞
    >>> limit(1/x, x, 0, '-')
    -∞
    >>> limit(x/(x + 1), x, oo)
    1
    >>> limit(sin(x)/x, x, 0)
    1
    >>> limit(sqrt(x**2+3*x+4) - x, x, oo)
    3/2

テーラー (Taylor) 展開

*   メソッド series() は関数 f(x) をテーラー展開する
*   x0 を省略すると原点 (0) になる
*   n は最高次数を指定する
*   オーダー記号 O() はメソッド removeO() で削除する

<!-- -->

    >>> cos(x).series(x)
         2    4
        x    x     ⎛ 6⎞
    1 - ── + ── + O⎝x ⎠
        2    24

    >>> sin(x).series(x)
         3     5
        x     x      ⎛ 6⎞
    x - ── + ──── + O⎝x ⎠
        6    120

    >>> exp(x).series(x)
             2    3    4     5
            x    x    x     x      ⎛ 6⎞
    1 + x + ── + ── + ── + ──── + O⎝x ⎠
            2    6    24   120

    >>> exp(x).series(x, 0, 10)
             2    3    4     5      6      7       8        9
            x    x    x     x      x      x       x        x       ⎛ 10⎞
    1 + x + ── + ── + ── + ──── + ──── + ───── + ───── + ────── + O⎝x  ⎠
            2    6    24   120    720    5040    40320   362880

    >>> exp(x).series(x).removeO()
      5     4    3    2
     x     x    x    x
    ──── + ── + ── + ─ + x + 1
    120    24   6    2

#### ●集合 {#abc04}

python には集合を表すデータ型 set があるが、sympy ではクラス Set を使う

sympy の Set は immutable なデータ構造

Set は基本クラスで、実際に使用するのはサブクラス

ここでは集合の基本的な使い方を簡単に説明する

詳細はリファレンス
[Sets](http://docs.sympy.org/latest/modules/sets.html) を参照

基本的な演算子とメソッド

和集合, +, \|, union

積集合, &, intersect, intersection

差集合, -, complement (a - b は b.complement(a) と同じ)

対称差, \^, symmetric\_difference ((a - b) \| (b - a) と同じ)

直積集合, \*, (a \* b は ProductSet(a, b) と同じ)

*   a \*\* n は a \* a \* \... \* a (n 個の a の直積集合)

べき集合, powerset

部分集合の判定, issubset, is\_subset, issuperset, is\_superset

真部分集合の判定, is\_proper\_subset, is\_proper\_superset

積集合 (共通集合) の有無, isdisjoint, is\_disjoint

要素 x は集合 a に含まれているか, x.contains(a), x in a

EmptySet は空集合を表すクラス (シングルトンは sympy.S.EmptySet)

*   属性 is\_EmptySet は True (他のクラスのオブジェクトは None)
*   属性 is\_iterable は False
*   関数 len() の値は 0

UniversalSet は全体集合を表すクラス (シングルトンは
sympy.S.UniversalSet)

*   属性 is\_UniversalSet は True (他のクラスのオブジェクトは None)
*   属性 is\_iterable は False
*   関数 len() は適用不可

<!-- -->

    >>> from sympy import *
    >>> init_printing()
    >>> S.EmptySet
    ∅
    >>> S.EmptySet.is_EmptySet
    True
    >>> S.EmptySet.is_iterable
    False
    >>> len(S.EmptySet)
    0
    >>> S.UniversalSet
    UniversalSet()
    >>> S.UniversalSet.is_UniversalSet
    True
    >>> S.UniversalSet.is_iterable
    False
    >>> S.UniversalSet | S.EmptySet
    UniversalSet()
    >>> S.UniversalSet & S.EmptySet
    ∅

Interval は実数区間を表すクラス

``` {.item}
Interval(s, e, left_open=False, right_open=False) => [s, e]
```

返り値は Interval
のオブジェクトだが、表示には角カッコや丸カッコが用いられる

キーワード引数 xxxx\_open で xxxx 側の open / close を指定する

*   (s, e), s \< x \< e
*   \[s, e), s \<= x \< e
*   (s, e\], s \< x \<= e
*   \[s, e\], s \<= x \<= e

または次のクラスメソッドを使う

*   Ropen(s, e) =\> \[s, e)
*   Lopen(s, e) =\> (s, e\]
*   open(s, e) =\> (s, e)

端点 s, e の open / close はメソッド left\_open, right\_open
で確認できる

端点 s, e には oo, -oo を指定できる

属性 is\_iterable は False

関数 len() は適用不可

    >>> a = Interval(1, 10)
    >>> a
    [1, 10]
    >>> a.is_iterable
    False
    >>> 1 in a
    True
    >>> 0 in a
    False
    >>> 10 in a
    True
    >>> 11 in a
    False
    >>> b = Interval(5, 15)
    >>> b
    [5, 15]
    >>> a & b
    [5, 10]
    >>> a | b
    [1, 15]
    >>> a - b
    [1, 5)
    >>> b - a
    (10, 15]
    >>> a ^ b
    [1, 5) ∪ (10, 15]
    >>> 5 in a ^ b
    False
    >>> 1 in a ^ b
    True
    >>> 10 in a ^ b
    False
    >>> 15 in a ^ b
    True

FiniteSet は有限個の要素を持つ集合を表すクラス

``` {.item}
FiniteSet(item1, item2, ..., itemN) => {item1, item2., ..., itemN}
```

*   python の frozenset (immutable な set) とほぼ同じ
*   返り値は FiniteSet のオブジェクトだが、表示には { \... }
    が用いられる
*   属性 is\_iterable は True (イテレータが利用できる)
*   関数 len() は要素数を返す

<!-- -->

    >>> a = FiniteSet(1, 2, 3, 4)
    >>> a
    {1, 2, 3, 4}
    >>> a.is_iterable
    True
    >>> for x in a: print(x)
    ...
    1
    2
    3
    4
    >>> len(a)
    4
    >>> for x in range(0, 6):
    ...     print(x, x in a)
    ...
    0 False
    1 True
    2 True
    3 True
    4 True
    5 False
    >>> b = FiniteSet(3, 4, 5, 6)
    >>> b
    {3, 4, 5, 6}
    >>> a | b
    {1, 2, 3, 4, 5, 6}
    >>> a & b
    {3, 4}
    >>> a - b
    {1, 2}
    >>> b - a
    {5, 6}
    >>> a ^ b
    {1, 2, 5, 6}

集合演算を表すクラス

*   Union, 和集合
*   Intersection. 積集合
*   Complement, 差集合
*   SymmetricDifference, 対称差
*   ProductSet, 直積集合

<!-- -->

    >>> c = Interval(5, 8)
    >>> c
    [5, 8]
    >>> a | c
    {1, 2, 3, 4} ∪ [5, 8]
    >>> b | c
    {3, 4} ∪ [5, 8]
    >>> a & c
    ∅
    >>> b & c
    {5, 6}
    >>> a - c
    {1, 2, 3, 4}
    >>> c - a
    [5, 8]
    >>> b - c
    {3, 4}
    >>> c - b
    (5, 6) ∪ (6, 8]
    >>> a ^ c
    {1, 2, 3, 4} ∪ [5, 8]
    >>> b ^ c
    {3, 4} ∪ (5, 6) ∪ (6, 8]
    >>> a * b
    {1, 2, 3, 4} × {3, 4, 5, 6}
    >>> for x in a * b: print(x)
    ...
    (1, 3)
    (1, 4)
    (1, 5)
    (1, 6)
    (2, 3)
    (2, 4)
    (2, 5)
    (2, 6)
    (3, 3)
    (3, 4)
    (3, 5)
    (3, 6)
    (4, 3)
    (4, 4)
    (4, 5)
    (4, 6)
    >>> a * c
    {1, 2, 3, 4} × [5, 8]
    >>> (1, 5.5) in a * c
    True
    >>> (4, 8.1) in a * c
    False
    >>> (2.5, 6) in a * c
    False

特別な集合を表すクラス

Naturals, 自然数全体の集合

Naturals0, 零と自然数全体の集合

Integers, 整数全体の集合

Reals, 実数全体の集合

Complexes, 複素数全体の集合

Range, python の range() とよく似た集合

*   インデックスによるアクセスが可能
*   oo や -oo の指定もできる

ImageSet, 写像

*   ImageSet(func, xs) は集合 xs の要素に関数 func を適用した集合を表す
*   第 1 引数 func には sympy の Lambda() を渡す
*   Lambda() は python の lambda 式とよく似ている
*   lambda 式とは違い、Lambda() の第 1
    引数に渡す変数はシンボルであること

この他にもいろいろある

    >>> c & S.Naturals
    {5, 6, 7, 8}
    >>> c | S.Naturals
    ℕ ∪ [5, 8]
    >>> Reals - c
    (-∞, 5) ∪ (8, ∞)
    >>> Integers - c
    ℤ \ [5, 8]
    >>> 5 in Integers - c
    False
    >>> 5.5 in Integers - c
    False
    >>> 6 in Integers - c
    False
    >>> 7 in Integers - c
    False
    >>> 8 in Integers - c
    False
    >>> 9 in Integers - c
    True
    >>> Range(1, 5)
    {1, 2, 3, 4}
    >>> Range(1, 8, 2)
    {1, 3, 5, 7}
    >>> d = Range(1, oo)
    >>> d
    {1, 2, …, ∞}
    >>> d[0]
    1
    >>> d[1]
    2
    >>> d[100]
    101
    >>> d[-1]
    ∞

    >>> var('x y z')
    (x, y, z)
    >>> a = ImageSet(Lambda(x, x * x), S.Naturals0)
    >>> a
    ImageSet(Lambda(x, x**2), Naturals0)
    >>> 100 in a
    True
    >>> 200 in a
    False
    >>> 400 in a
    True
    >>> b = ImageSet(Lambda(x, x * x), Range(10))
    >>> b
    ImageSet(Lambda(x, x**2), Range(0, 10, 1))
    >>> for n in b: print(n)

------------------------------------------------------------------------

::: {.ce}
**Copyright (C) 2018 Makoto Hiroi\
All rights reserved.**
:::

::: {.small}

------------------------------------------------------------------------

\[ [Home](../index.html) \| [Light](index.html) \|
[python3](python3.html#sympy_abc) \]
:::

```

M.Hiroi's Home Page
Python3 Programming
お気楽 SymPy プログラミング超入門
[ Home | Light | Python3 ]
●Hello, SymPy!!
SymPy は Python で数式処理システムのような記号計算を行うためのライブラリ
記号計算とは、数学で数式を計算するのと同様な計算を行うこと
たとえば、f(x) = x2 の微分は f'(x) = 2x になるが、SymPy を使うと同じように計算できる
>>> import sympy as sy
>>> x = Symbol('x')
>>> f = x**2 + 2*x + 1
>>> f
x**2 + 2*x + 1
>>> g = f.diff()
>>> g
2*x + 2
SymPy には多くの機能が実装されている
本ページでは基本的な使い方を簡単に説明する
SymPy のインストール
Windows で python3 がインストールされている場合、pip を使うと簡単
シェル上でコマンド pip install sympy を実行する
mpmath-1.0.0 と sympy-1.2 がインストールされる
mpmath は任意精度の浮動小数点数を扱うライブラリ
UNIX 系 OS で python3 の場合は pip3 を使う
Ubuntu 系 OS の場合、pip3 は sudo apt install python3-pip でインストールできる
あとは pip3 install sympy を実行するだけ
Cygwin の場合、Python のカテゴリーをすべてインストールするのが簡単 (pip3 もインストールされる)
matplotlib がインストールされている場合、プロットは matplotlib を使って行われる
>>> f = x**2 - 1
>>> plotting.plot(f, (x, -2, 2))
<sympy.plotting.plot.Plot object at 0x0000020B1F37D588>

Matplotlib のインストール
Windows の場合、pip install matplotlib で必要なパッケージをすべてインストールできる
UNIX 系 OS の場合は pip3 を使ってインストールする
ただし、Python3 / Tkinter がインストールされていないと動作しない
Ubuntu 系 OS ではコマンド sudo apt install python3-tkinter で Tkinter をインストールできる
Cygwin の場合、基本的には pip3 でインストールできる (必要なものが揃っているならば)
実際にプロットするには X サーバーが必要
X11 カテゴリーをすべてインストールするのが簡単
Windows 10 の場合、スタートボタン -> Cygwin-X -> XWin Server を実行する
または Cygwin Terminal でコマンド run xwin -multiwindow を実行する
Cygwin Terminal で GUI アプリケーションを実行する場合、環境変数 DISPLAY の設定が必要
$ export DISPLAY=:0.0
bash の設定ファイル .bashrc に書き込んでおくとよい
参考 URL
SymPy, (本家)
Welcome to SymPy’s documentation!, (本家)
3.2. Sympy : Python での代数計算, (Scipy Lecture Notes)
Pythonで数学の勉強：代数基礎(数式と関数), (@tibigame さん)
SymPy - Wikipedia
●SymPy の基本
数
SymPy の数は整数 (多倍長整数)、有理数、浮動小数点数 (任意精度)、複素数
整数は Integer() で、浮動小数点数 Float() で生成できるが、Python の整数と浮動小数点数を使うこともできる
Python の整数と浮動小数点数は自動的に SymPy のそれに変換される
有理数は Rational(p, q), p: 分子, q: 分母, p, q は多倍長整数
Python3 の場合、演算子 / は整数同士の割り算
SymPy で a, b が整数 (Integer) の場合、a / b は有理数になる
SymPy の虚数単位は I になる
Python には複素数を表すデータ型 (complex) があるが SymPy にはない
SymPy は虚数単位を含む数式を複素数として扱う
基本的な演算は Python と同じ (+, -, *, /, //, %, **)
定数と数学関数
SymPy では特殊な定数 (E, pi, oo) や数学関数を数式として扱う
E : 自然対数の底 (ネイピア数)
pi : 円周率
oo : 無限大
>>> import sympy as sy
>>> Integer(12345)
12345
>>> Integer('12345')
12345
>>> type(Integer('12345'))
<class 'sympy.core.numbers.Integer'>

>>> Float(1.2345)
1.23450000000000
>>> Float('1.2345')
1.23450000000000
>>> type(Float('1.2345'))
<class 'sympy.core.numbers.Float'>

>>> Rational(1, 2)
1/2
>>> Integer(1) / Integer(3)
1/3
>>> Rational(1, 2) + Rational(1, 3)
5/6

>>> 1 + 2 * I
1 + 2*I
>>> 1.1 + 2.2 * I
1.1 + 2.2*I
>>> sqrt(2)
sqrt(2)
>>> sqrt(4)
2
>>> sqrt(8)
2*sqrt(2)
>>> pi
pi
>>> E
E
>>> oo
oo
数式の出力
init_printing() を実行すると、使用しているコンソールに適した方法で数式を表示する
SymPy は LaTex のフォーマットで数式を出力することができる
それを受け付けるコンソール (たとえば IPython QTConsole など) であれば、美しい数式が表示される
本家の SymPy Live で公開されている Online Shell を試してみるとよい
そうでなければ unicode pretty printer が使用される
init_printing() の引数に use_unicode=False を指定すると ascii pretty printer になる
>>> init_printing()
>>> sqrt(2)
√2
>>> 1 / sqrt(2)
√2
──
2
>>> pi
π

>>>init_printing(use_unicode=False)
>>>ssqrt(2)
  ___
\/ 2
>>>s1 / sqrt(2)
  ___
\/ 2
-----
  2
>>> pi
pi
変数の定義
SumPy の場合、「シンボル (symbol)」を使って数式の変数を表す
プログラミングにおいて、シンボルというデータ型は「記号」を意味するのが一般的
Lisp / Scheme の世界では、シンボルは基本的なデータ型のひとつ
シンボルは Symbol(), symbols(), var() などで生成する
引数にはシンボルの名前 (文字列) を渡す
Symbol('name') => symbol
symbols('name1 name2 ... nameN') => symbol1, symbol2, ..., symbolN
var('name1 name2 ... nameN') => symbol1, symbol2, ..., symbolN
Symbol() はシンボルをひとつ生成する
symbols() と var() は複数のシンボルを生成する
引数の文字列を空白文字やカンマ (,) で区切る
コロン (:) を使うと複数のシンボルを簡単に指定できる
たとえば、a:z とすると a から z までの 26 個のシンボルを生成する
x:10 は x0 から x9 まで, x1:6 は x1 から x5 までのシンボルを生成する
生成したシンボルは Python の変数に格納して使用する
シンボル名と Python の変数名は同じにしたほうがわかりやすい
var() は同じ名前の大域変数に生成したシンボルを格納する
>>> x, y, z = symbols('x y z')
>>> x
x
>>> y
y
>>> z
z
>>> x + 2*y + 3*z
x + 2⋅y + 3⋅z
>>> (x + y) ** 2
       2
(x + y)
>>> (x + y + z) ** 3
           3
(x + y + z)
>>> x**3 + 2*x**2 + 3*x + 4
 3      2
x  + 2⋅x  + 3⋅x + 4
>>> sqrt(x)
√x
>>> sqrt(x*x + y*y)
   _________
  ╱  2    2
╲╱  x  + y

>>> var('a:c')
(a, b, c)
>>> a
a
>>> b
b
>>> c
c
>>> var('x1:4')
(x1, x2, x3)
>>> x1
x1
>>> x2
x2
>>> x3
x3
数式の展開と因数分解
数式 expr の展開は関数 expand() で、因数分解は関数 factor() で行うことができる
数式の展開は目的別の関数も用意されている
expand_power_exp(), expand_power_base(), 指数を含む式の展開
expand_trig(), 三角関数を含む式の展開
expand_log(), 対数関数を含む式の展開
この他にもいろいろある
詳細はリファレンス Simplify を参照
>>> expand((x + y)**2)
 2            2
x  + 2⋅x⋅y + y
>>> expand((x + y + z)**2)
 2                    2            2
x  + 2⋅x⋅y + 2⋅x⋅z + y  + 2⋅y⋅z + z
>>> expand((x + y)**3)
 3      2          2    3
x  + 3⋅x ⋅y + 3⋅x⋅y  + y
>>> a = expand((x + y)**4)
>>> a
 4      3        2  2        3    4
x  + 4⋅x ⋅y + 6⋅x ⋅y  + 4⋅x⋅y  + y
>>> factor(a)
       4
(x + y)
>>> factor(x**2 - 1)
(x - 1)⋅(x + 1)
>>> factor(x**2 - 2*x + 1)
       2
(x - 1)
>>> factor(x**3 + y**3)
        ⎛ 2          2⎞
(x + y)⋅⎝x  - x⋅y + y ⎠

>>> expand_power_exp(x**(y+z))
 y  z
x ⋅x
>>> (x*y)**z
     z
(x⋅y)
>>> expand_power_base((x*y)**z)
     z
(x⋅y)
>>> expand_power_base((x*y)**z, force=True)
 z  z
x ⋅y

>>> expand_trig(sin(x + y))
sin(x)⋅cos(y) + sin(y)⋅cos(x)
>>> expand_trig(cos(x + y))
-sin(x)⋅sin(y) + cos(x)⋅cos(y)
>>> expand_trig(sin(2*x))
2⋅sin(x)⋅cos(x)
>>> expand_trig(cos(2*x))
     2
2⋅cos (x) - 1

>>> expand_log(log(x*y))
log(x⋅y)
>>> expand_log(log(x*y), force=True)
log(x) + log(y)

>>> expand_log(log(x/y))
   ⎛x⎞
log⎜─⎟
   ⎝y⎠
>>> expand_log(log(x/y), force=True)
log(x) - log(y)

>>> expand_log(log(x**2))
   ⎛ 2⎞
log⎝x ⎠
>>> expand_log(log(x**2), force=True)
2⋅log(x)

>>> var('x1 y1 z1', positive=True)
(x₁, y₁, z₁)
>>> expand_log(log(x1*y1))
log(x₁) + log(y₁)
>>> expand_log(log(x1/y1))
log(x₁) - log(y₁)
>>> expand_log(log(x1**2))
2⋅log(x₁)
数式を展開できない場合は、引数の数式をそのまま返す
たとえば、log(x) の定義域は x > 0 だが、変数 x の範囲が負数を含んでいると数式を展開できない
シンボルを生成するとき、positive=True を指定すると、その変数がとりうる値は正数に限定される
または、force=True を指定すると、強制的に式を展開することができる
数式の簡約化
数式の簡約化は関数 simplify() を使うと簡単
目的別の関数も用意されている
powsimp(), 指数の簡約化
trigsimp(), 三角関数を含む式の簡約化
logcombine(), 対数関数を含む式の簡約化
この他にもいろいろある
詳細はリファレンス Simplify を参照
>>> (x**2 + 2*x + 1)/(x + 1)
 2
x  + 2⋅x + 1
────────────
   x + 1
>>> simplify((x**2 + 2*x + 1)/(x + 1))
x + 1
>>> simplify((x**2 - 1)/(x + 1))
x - 1

>>> x**y * x**z
 y  z
x ⋅x
>>> powsimp(x**y * x**z)
 y + z
x
>>> x**y / x**z
 y  -z
x ⋅x
>>> powsimp(x**y / x**z)
 y - z
x

>>> sin(x)**2 + cos(x)**2
   2         2
sin (x) + cos (x)
>>> trigsimp(sin(x)**2 + cos(x)**2)
1
>>> sin(x)*cos(y) + cos(x)*sin(y)
sin(x)⋅cos(y) + sin(y)⋅cos(x)
>>> trigsimp(sin(x)*cos(y) + cos(x)*sin(y))
sin(x + y)
>>> cos(x)*cos(y) + sin(x)*sin(y)
sin(x)⋅sin(y) + cos(x)⋅cos(y)
>>> trigsimp(cos(x)*cos(y) + sin(x)*sin(y))
cos(x - y)
>>> 1 + tan(x)**2
   2
tan (x) + 1
>>> trigsimp(1 + tan(x)**2)
   1
───────
   2
cos (x)

>>> log(x) + log(y)
log(x) + log(y)
>>> logcombine(log(x) + log(y))
log(x) + log(y)
>>> logcombine(log(x) + log(y), force=True)
log(x⋅y)
>>> log(x) - log(y)
log(x) - log(y)
>>> logcombine(log(x) - log(y))
log(x) - log(y)
>>> logcombine(log(x) - log(y), force=True)
   ⎛x⎞
log⎜─⎟
   ⎝y⎠
>>> x * log(y)
x⋅log(y)
>>> logcombine(x * log(y))
x⋅log(y)
>>> logcombine(x * log(y), force=True)
   ⎛ x⎞
log⎝y ⎠
数式の評価
数式 expr の変数に値を代入するにはメソッド subs() を使う
expr.subs(変数, 値) => 値 or 数式
expr.subs([(変数1, 値1), (変数2, 値2), ...]) => 値 or 数式
expr.subs({変数1: 値1, 変数2: 値2, ...}) => 値 or 数式
値は数だけではなく他の変数や数式でもかまわない
数式 expr を評価して浮動小数点数を得るにはメソッド evalf() または関数 N() を使う
expr.evalf([n]) => float_number
sympy.N(expr, [n]) => float_number
引数 n を指定すると n 桁の浮動小数点数 (Float) を返す
>>> a = x**2
>>> a
 2
x
>>> a.subs(x, 10)
100
>>> a.subs(x, 1.2345)
1.52399025000000
>>> a.subs(x, y + z)
       2
(y + z)
>>> a.subs(x, sqrt(2))
2
>>> b = a.subs(x, y + z)
>>> b.subs({y: sqrt(2), z: sqrt(3)})
         2
(√2 + √3)
>>> pi.evalf()
3.14159265358979
>>> N(pi, 50)
3.1415926535897932384626433832795028841971693993751
>>> sqrt(2).evalf(50)
1.4142135623730950488016887242096980785696718753769
>>> b.subs({y: sqrt(2), z: sqrt(3)}).evalf()
9.89897948556636
>>> b.subs({y: sqrt(2), z: sqrt(3)}).evalf(20)
9.8989794855663561964
●多項式
多項式の除算で商と余りを求めるには関数 div() を使う
有理式 (多項式の分数) の簡約化には関数 cansel() がある
関数 apart() は有理式を「部分分数」に分解する
部分分数は、分母が因数分解されている有理式をいくつかの有理式に分解すること
次の条件を満たすとき、有理式 g(x) / f(x) は簡単な形に分解できる
f(x) は 1 次式の積に因数分解できるような整式であること
g(x) は f(x) より次数が少ない整式であること
g(x) / f(x) = b1 / (x - a1) + b2 / (x - a2) + ... + bn / (x - an)
関数 collect() は指定した変数で式をまとめる
そのあと、メソッド coeff() で指定した次数の係数を取り出すことができる
>>> div(x**3 + x**2 - 4*x + 1, x - 2)
⎛ 2             ⎞
⎝x  + 3⋅x + 2, 5⎠

>>> div(x**3 - x**2 - 4*x + 4, x - 2)
⎛ 2           ⎞
⎝x  + x - 2, 0⎠

>>> (x**3 - x**2 - 4*x + 4) / (x - 2)
 3    2
x  - x  - 4⋅x + 4
─────────────────
      x - 2

>>> cancel((x**3 - x**2 - 4*x + 4) / (x - 2))
 2
x  + x - 2

>>> cancel((x**2 + 2*x + 1) / (x**2 + x))
x + 1
─────
  x

>>> 1/(x - 1) + 1/(x - 2)
  1       1
───── + ─────
x - 1   x - 2

>>> cancel(1/(x - 1) + 1/(x - 2))
  2⋅x - 3
──────────────
 2
x  - 3⋅x + 2

>>> apart((2*x - 3)/(x**2 - 3*x + 2))
  1       1
───── + ─────
x - 1   x - 2

>>> apart((6*x**2 + x - 17) / (x**3 - 7*x -6))
  1       3       2
───── + ───── + ─────
x + 2   x + 1   x - 3

>>> apart((x + 1) / (x**4 + x**2))
  x + 1    1     1
- ───── + ─── + ────
   2       x      2
  x  + 1         x

>>> x**2 + y*x + z*x + y*z
 2
x  + x⋅y + x⋅z + y⋅z
>>> collect(x**2 + y*x + z*x + y*z, x)
 2
x  + x⋅(y + z) + y⋅z
>>> collect(x**2 + y*x + z*x + y*z, x).coeff(x, 2)
1
>>> collect(x**2 + y*x + z*x + y*z, x).coeff(x, 1)
y + z
>>> collect(x**2 + y*x + z*x + y*z, x).coeff(x, 0)
y⋅z
方程式の求解には関数 solveset(expr, var) を使う
数式 expr が 0 になる変数 var の値を求める
キーワード引数 domain で領域を指定できる (Complexes が default)
たとえば、x2 + 1 = 0 の解は I, -I になるが、実数では存在しない
解が存在しない場合は空集合を表す EmptySet() を返す
SymPy の集合 (Set) は Python の set とは違う (あとで説明する)
SymPy には特別な集合を表すクラスがあらかじめ定義されている
EmptySet, 空集合
UniversalSet, 全体集合
Naturals, 自然数全体の集合
Naturals0, 零と自然数全体の集合
Integers, 整数全体の集合
Reals, 実数全体の集合
Complexes, 複素数全体の集合
これらのクラスのオブジェクト (シングルトン) は sympy.S の同名の変数に格納されている
関数 roots(expr, var) は解とその個数を辞書に格納して返す
重根があるときは便利
>>> solveset(x**2 - 1, x)
{-1, 1}
>>> solveset(x**2 + 1, x)
{-ⅈ, ⅈ}

>>> S.UniversalSet
UniversalSet()
>>> S.EmptySet
∅
>>> S.Complexes
ℂ
>>> S.Reals
ℝ
>>> S.Integers
ℤ
>>> S.Naturals
ℕ

>>> solveset(x**2 + 1, x, domain=Reals)
∅
>>> var('a b c')
(a, b, c)
>>> solveset(a*x**2 + b*x + c, x)
⎧           _____________              _____________ ⎫
⎪          ╱           2              ╱           2  ⎪
⎨   b    ╲╱  -4⋅a⋅c + b        b    ╲╱  -4⋅a⋅c + b   ⎬
⎪- ──── - ────────────────, - ──── + ────────────────⎪
⎩  2⋅a         2⋅a            2⋅a         2⋅a        ⎭

>>> solveset(x**3, x)
{0}
>>> roots(x**3, x)
{0: 3}
>>> solveset(x**3 - 4*x**2 + 5*x - 2, x)
{1, 2}
>>> roots(x**3 - 4*x**2 + 5*x - 2, x)
{1: 2, 2: 1}
連立一次方程式は関数 linsolve([expr1, expr2, ...], [var1, var2, ...]) を使って解く
式と変数はタプルに格納して渡してもよい
鶴亀算
鶴と亀、合わせて 100 匹いる。足の合計が 272 本のとき、鶴と亀はそれぞれ何匹ずついるか。
鶴と亀とトンボが合わせて 10 匹いる。足の合計が 38 本で羽の合計が 14 枚であるとき、鶴と亀とトンボはそれぞれ何匹ずついるか。(トンボの足は 6 本で羽は 4 枚)
鶏と犬とタコ、合わせて 24 匹が台所にいる。足の合計が 102 本のとき、鶏、犬、タコはそれぞれ何匹ずついるか。
解答
x + y = 100, 2x + 4y = 272 を解く
>>> linsolve([x + y - 100, 2*x + 4*y - 272], [x, y])
{(64, 36)}
x + y + z = 10, 2x + 4y + 6z = 38, 2x + 4z = 14 を解く
>>> linsolve([x + y + z - 10, 2*x + 4*y + 6*z - 38, 2*x + 4*z -14], [x, y, z])
{(3, 5, 2)}
x + y + z= 24, 2x + 4y + 8z = 102 を解く
>>> linsolve([x + y + z - 24, 2*x + 4*y + 8*z - 102], [x, y, z])
{(2⋅z - 3, -3⋅z + 27, z)}
solveset() や linsolve() の他にも、方程式の解を求める関数 solve() がある
●微積分
微分
微分は関数 diff(expr, var, [n]) で行う
引数 n に数値を指定すると高階微分になる
または、diff(expr, var, var, ...) のように変数 var の後に同じ変数を指定すると高階微分になる
多変数関数の微分 (偏微分) も diff(expr, var1, var2, ...) で OK
Derivative() は diff() と引数が同じで、評価前の状態を保持するオブジェクトを返す
メソッド doit() で微分を実行する
>>> var('n x y z')
(n, x, y, z)
>>> diff(x**2, x)
2⋅x
>>> diff(x**2, x, 2)
2
>>> diff(x**2, x, 3)
0
>>> diff(x**n, x)
   n
n⋅x
────
 x

>>> diff(x**n, x, 2)
   n
n⋅x ⋅(n - 1)
────────────
      2
     x

>>> diff(log(x), x)
1
─
x

>>> diff(exp(x), x)
 x
ℯ
>>> diff(sin(x), x)
cos(x)
>>> diff(cos(x), x)
-sin(x)
>>> diff(tan(x), x)
   2
tan (x) + 1

>>> diff(x**3*y**3*z**3, x)
   2  3  3
3⋅x ⋅y ⋅z
>>> diff(x**3*y**3*z**3, x, x)
     3  3
6⋅x⋅y ⋅z
>>> diff(x**3*y**3*z**3, x, x, y, y, z, z)
216⋅x⋅y⋅z
>>> diff(x**3*y**3*z**3, x, 2, y, 2, z, 2)
216⋅x⋅y⋅z

>>> Derivative((x**3 * y**3 * z**3), x, y, z)
    3
   ∂       ⎛ 3  3  3⎞
───────────⎝x ⋅y ⋅z ⎠
∂z ∂y ∂x
>>> Derivative((x**3 * y**3 * z**3), x, y, z).doit()
    2  2  2
27⋅x ⋅y ⋅z
>>> Derivative((x**3 * y**3 * z**3), x, 2, y, 2, z, 2)
      6
     ∂     ⎛ 3  3  3⎞
───────────⎝x ⋅y ⋅z ⎠
  2   2   2
∂z  ∂y  ∂x
>>> Derivative((x**3 * y**3 * z**3), x, 2, y, 2, z, 2).doit()
216⋅x⋅y⋅z
積分
積分は関数 integrate(expr, var) で行う (積分定数は出力されない)
定積分も integrate(expr, (var, low, high)) で OK
二重積分も integrate(expr, (var1, l1, h1), (var2, l2, h2), ...) で OK
Integral() は integrate() と引数が同じで、評価前の状態を保持するオブジェクトを返す
メソッド doit() で微分を実行する
積分を評価できないときは Integral() を返す
>>> integrate(x, x)
 2
x
──
2
>>> integrate(x**2, x)
 3
x
──
3
>>> integrate(x**3, x)
 4
x
──
4
>>> integrate(x, (x, 0, 1))
1/2
>>> integrate(x**2, (x, 0, 1))
1/3
>>> integrate(x**3, (x, 0, 1))
1/4
>>> integrate(exp(x), x)
 x
ℯ
>>> integrate(exp(-x), x)
  -x
-ℯ
>>> integrate(exp(-x), (x, 0, oo))
1
>>> integrate(1/x, x)
log(x)
>>> integrate(log(x), x)
x⋅log(x) - x
>>> integrate(sin(x), x)
-cos(x)
>>> integrate(cos(x), x)
sin(x)
>>> integrate(tan(x), x)
-log(cos(x))
>>> integrate(4/(1 + x**2), x)
4⋅atan(x)
>>> integrate(4/(1 + x**2), (x, 0, 1))
π

>>> Integral(x*y**2, x, y)
⌠ ⌠
⎮ ⎮    2
⎮ ⎮ x⋅y  dx dy
⌡ ⌡
>>> Integral(x*y**2, x, y).doit()
 2  3
x ⋅y
─────
  6
>>> integrate(x*y**2, (x, 0, 1), (y, 0, 1))
1/6
>>> integrate(x*y**2, (x, 1 - y, 1), (y, 0, 1))
3/20

>>> integrate(x**x, x)
⌠
⎮  x
⎮ x  dx
⌡
極限
極限は関数 limit(function, var, point) で計算する
f(x) の x -> 0 の極限は limit(f(x), x, 0) とする
極限の方向は第 4 引数で指定する ('+' or '-')
>>> limit(x, x, oo)
∞
>>> limit(1/x, x, oo)
0
>>> limit(1/x, x, 0, '+')
∞
>>> limit(1/x, x, 0, '-')
-∞
>>> limit(x/(x + 1), x, oo)
1
>>> limit(sin(x)/x, x, 0)
1
>>> limit(sqrt(x**2+3*x+4) - x, x, oo)
3/2
テーラー (Taylor) 展開
メソッド series() は関数 f(x) をテーラー展開する
f(x).series(var, [x0, n])
x0 を省略すると原点 (0) になる
n は最高次数を指定する
オーダー記号 O() はメソッド removeO() で削除する
>>> cos(x).series(x)
     2    4
    x    x     ⎛ 6⎞
1 - ── + ── + O⎝x ⎠
    2    24

>>> sin(x).series(x)
     3     5
    x     x      ⎛ 6⎞
x - ── + ──── + O⎝x ⎠
    6    120

>>> exp(x).series(x)
         2    3    4     5
        x    x    x     x      ⎛ 6⎞
1 + x + ── + ── + ── + ──── + O⎝x ⎠
        2    6    24   120

>>> exp(x).series(x, 0, 10)
         2    3    4     5      6      7       8        9
        x    x    x     x      x      x       x        x       ⎛ 10⎞
1 + x + ── + ── + ── + ──── + ──── + ───── + ───── + ────── + O⎝x  ⎠
        2    6    24   120    720    5040    40320   362880

>>> exp(x).series(x).removeO()
  5     4    3    2
 x     x    x    x
──── + ── + ── + ─ + x + 1
120    24   6    2
●集合
Python には集合を表すデータ型 set があるが、SymPy ではクラス Set を使う
SymPy の Set は immutable なデータ構造
Set は基本クラスで、実際に使用するのはサブクラス
ここでは集合の基本的な使い方を簡単に説明する
詳細はリファレンス Sets を参照
基本的な演算子とメソッド
和集合, +, |, union
積集合, &, intersect, intersection
差集合, -, complement (a - b は b.complement(a) と同じ)
対称差, ^, symmetric_difference ((a - b) | (b - a) と同じ)
直積集合, *, (a * b は ProductSet(a, b) と同じ)
a ** n は a * a * ... * a (n 個の a の直積集合)
べき集合, powerset
部分集合の判定, issubset, is_subset, issuperset, is_superset
真部分集合の判定, is_proper_subset, is_proper_superset
積集合 (共通集合) の有無, isdisjoint, is_disjoint
要素 x は集合 a に含まれているか, x.contains(a), x in a
EmptySet は空集合を表すクラス (シングルトンは sympy.S.EmptySet)
属性 is_EmptySet は True (他のクラスのオブジェクトは None)
属性 is_iterable は False
関数 len() の値は 0
UniversalSet は全体集合を表すクラス (シングルトンは sympy.S.UniversalSet)
属性 is_UniversalSet は True (他のクラスのオブジェクトは None)
属性 is_iterable は False
関数 len() は適用不可
>>> import sympy as sy
>>> init_printing()
>>> S.EmptySet
∅
>>> S.EmptySet.is_EmptySet
True
>>> S.EmptySet.is_iterable
False
>>> len(S.EmptySet)
0
>>> S.UniversalSet
UniversalSet()
>>> S.UniversalSet.is_UniversalSet
True
>>> S.UniversalSet.is_iterable
False
>>> S.UniversalSet | S.EmptySet
UniversalSet()
>>> S.UniversalSet & S.EmptySet
∅
Interval は実数区間を表すクラス
Interval(s, e, left_open=False, right_open=False) => [s, e]
返り値は Interval のオブジェクトだが、表示には角カッコや丸カッコが用いられる
キーワード引数 xxxx_open で xxxx 側の open / close を指定する
(s, e), s < x < e
[s, e), s <= x < e
(s, e], s < x <= e
[s, e], s <= x <= e
または次のクラスメソッドを使う
Ropen(s, e) => [s, e)
Lopen(s, e) => (s, e]
open(s, e) => (s, e)
端点 s, e の open / close はメソッド left_open, right_open で確認できる
端点 s, e には oo, -oo を指定できる
属性 is_iterable は False
関数 len() は適用不可
>>> a = Interval(1, 10)
>>> a
[1, 10]
>>> a.is_iterable
False
>>> 1 in a
True
>>> 0 in a
False
>>> 10 in a
True
>>> 11 in a
False
>>> b = Interval(5, 15)
>>> b
[5, 15]
>>> a & b
[5, 10]
>>> a | b
[1, 15]
>>> a - b
[1, 5)
>>> b - a
(10, 15]
>>> a ^ b
[1, 5) ∪ (10, 15]
>>> 5 in a ^ b
False
>>> 1 in a ^ b
True
>>> 10 in a ^ b
False
>>> 15 in a ^ b
True
FiniteSet は有限個の要素を持つ集合を表すクラス
FiniteSet(item1, item2, ..., itemN) => {item1, item2., ..., itemN}
Python の frozenset (immutable な set) とほぼ同じ
返り値は FiniteSet のオブジェクトだが、表示には { ... } が用いられる
属性 is_iterable は True (イテレータが利用できる)
関数 len() は要素数を返す
>>> a = FiniteSet(1, 2, 3, 4)
>>> a
{1, 2, 3, 4}
>>> a.is_iterable
True
>>> for x in a: print(x)
...
1
2
3
4
>>> len(a)
4
>>> for x in range(0, 6):
...     print(x, x in a)
...
0 False
1 True
2 True
3 True
4 True
5 False
>>> b = FiniteSet(3, 4, 5, 6)
>>> b
{3, 4, 5, 6}
>>> a | b
{1, 2, 3, 4, 5, 6}
>>> a & b
{3, 4}
>>> a - b
{1, 2}
>>> b - a
{5, 6}
>>> a ^ b
{1, 2, 5, 6}
集合演算を表すクラス
Union, 和集合
Intersection. 積集合
Complement, 差集合
SymmetricDifference, 対称差
ProductSet, 直積集合
>>> c = Interval(5, 8)
>>> c
[5, 8]
>>> a | c
{1, 2, 3, 4} ∪ [5, 8]
>>> b | c
{3, 4} ∪ [5, 8]
>>> a & c
∅
>>> b & c
{5, 6}
>>> a - c
{1, 2, 3, 4}
>>> c - a
[5, 8]
>>> b - c
{3, 4}
>>> c - b
(5, 6) ∪ (6, 8]
>>> a ^ c
{1, 2, 3, 4} ∪ [5, 8]
>>> b ^ c
{3, 4} ∪ (5, 6) ∪ (6, 8]
>>> a * b
{1, 2, 3, 4} × {3, 4, 5, 6}
>>> for x in a * b: print(x)
...
(1, 3)
(1, 4)
(1, 5)
(1, 6)
(2, 3)
(2, 4)
(2, 5)
(2, 6)
(3, 3)
(3, 4)
(3, 5)
(3, 6)
(4, 3)
(4, 4)
(4, 5)
(4, 6)
>>> a * c
{1, 2, 3, 4} × [5, 8]
>>> (1, 5.5) in a * c
True
>>> (4, 8.1) in a * c
False
>>> (2.5, 6) in a * c
False
特別な集合を表すクラス
Naturals, 自然数全体の集合
Naturals0, 零と自然数全体の集合
Integers, 整数全体の集合
Reals, 実数全体の集合
Complexes, 複素数全体の集合
Range, Python の range() とよく似た集合
インデックスによるアクセスが可能
oo や -oo の指定もできる
ImageSet, 写像
ImageSet(func, xs) は集合 xs の要素に関数 func を適用した集合を表す
第 1 引数 func には SymPy の Lambda() を渡す
Lambda() は Python の lambda 式とよく似ている
Lambda(x, expr) は lambda x: expr のこと
Lambda((x, y, z, ...), expr) は lambda x, y, z, ...: expr のこと
lambda 式とは違い、Lambda() の第 1 引数に渡す変数はシンボルであること
この他にもいろいろある
>>> c & S.Naturals
{5, 6, 7, 8}
>>> c | S.Naturals
ℕ ∪ [5, 8]
>>> Reals - c
(-∞, 5) ∪ (8, ∞)
>>> Integers - c
ℤ \ [5, 8]
>>> 5 in Integers - c
False
>>> 5.5 in Integers - c
False
>>> 6 in Integers - c
False
>>> 7 in Integers - c
False
>>> 8 in Integers - c
False
>>> 9 in Integers - c
True
>>> Range(1, 5)
{1, 2, 3, 4}
>>> Range(1, 8, 2)
{1, 3, 5, 7}
>>> d = Range(1, oo)
>>> d
{1, 2, …, ∞}
>>> d[0]
1
>>> d[1]
2
>>> d[100]
101
>>> d[-1]
∞

>>> var('x y z')
(x, y, z)
>>> a = ImageSet(Lambda(x, x * x), S.Naturals0)
>>> a
ImageSet(Lambda(x, x**2), Naturals0)
>>> 100 in a
True
>>> 200 in a
False
>>> 400 in a
True
>>> b = ImageSet(Lambda(x, x * x), Range(10))
>>> b
ImageSet(Lambda(x, x**2), Range(0, 10, 1))
>>> for n in b: print(n)
...
0
1
4
9
16
25
36
49
64
81
Copyright (C) 2018 Makoto Hiroi
All rights reserved.
[ Home | Light | Python3 ]


```python
import numpy as np
import pandas as pd
import linearsolve as ls
import matplotlib.pyplot as plt
plt.style.use('classic')
%matplotlib inline
```

# Class 18: Optimal Monetary Policy in the New-Keynesian Model

In this notebook, we will derive and compute the optimal monetary policy response to exogenous shocks in the new-Keynesian model.

## The New-Keynesian Model

Consider the New-Keynesian model without a specification for monetary policy:

\begin{align}
y_t & = E_t y_{t+1}  - \left( r_{t} - \bar{r}\right) + g_t\\
i_{t} & = r_{t} + E_t \pi_{t+1}\\
\pi_t & = \beta E_t\pi_{t+1}  + \kappa y_t+ u_t,
\end{align}

where $\bar{y}$ and $\pi^T$ have been set to zero. We can interpret $y$ as the output gap (log difference of output from steady state). The system above has three equations, but four endogenous variables: $y_t$, $\pi_t$, $i_t$, and $r_t$ ($u_t$ and $g_t$ are exoenous to the system). Previously, we closed the model by assuming a *rule* for monetary that specified the nominal interest rate as a linear function of inflation and output. Here we will derive optimal monetary policy.

## The Optimization Problem

Suppose that the central bank has preferences over inflation and output in period $t$ is represented by the *loss function*:

\begin{align}
L\left(\pi_t, y_t\right) & = \frac{1}{2}\bigg[\pi_t^2 + \lambda y_t^2\bigg].
\end{align}

The loss function reflects the degree of disutility that the central bank inscurs when inflation and the output gap are different from zero. $\lambda$ denotes the weight that the central bank places on output fluctuations. If $\lambda =0$, then central bank cares only about inflation stabilization. A value for $\lambda>0$ means that the central bank wants to stabilize output and inflation. In the US, the Fed is required by law to promote stability in inflation *and* employment and so $\lambda>0$ would make sense for a model of the Fed's decisionmaking.

The unconstrained minimum of $L$ is obtained at $\pi_t= 0$ and $y_t = 0$. But often this is not feasible given the constraints of the model. To see why, note that the new-Keynesian Phillips curve imposes a constraint on the feasible combinations of output and inflation. Therefore, the central bank's optimization problem is to solve:

\begin{align}
\min_{\pi, y} \frac{1}{2}\bigg[\pi^2 + \lambda y^2\bigg]
\end{align}

subject to:

\begin{align}
\pi & = \beta \pi' + \kappa y + u
\end{align}

where time subscripts have been dropped. $\pi'$ means expected inflation next period.

The problem can be recast as an unconstrained optimization problem by substituting the Phillips curve into the loss function to eliminate inflation:

\begin{align}
\min_{y} \frac{1}{2}\bigg[\left(\beta \pi' + \kappa y + u\right)^2 + \lambda y^2\bigg]
\end{align}

The first-order condition with respect to $y$ is:

\begin{align}
\kappa \underbrace{\left(\beta \pi' + \kappa y + u\right)}_{\pi} + \lambda y & = 0
\end{align}

or:

\begin{align}
\pi & = \frac{\lambda}{\kappa}y
\end{align}

Since the central bank solves the same problem in each period, we have the final equation for the system given in the first section:

\begin{align}
\boxed{\pi_t  = \frac{\lambda}{\kappa}y_t}
\end{align}

This equation expresses the optimal outcome of monetary policy. It should maintain a fixed proportional relationship between output and inflation that depends only on $\lambda$ and $\kappa$. Given this equation, the optimal setting of the nominal interest rate is determined by the IS and Fisher equations.

## Exogenous Shocks and Parameters

As usual, he variables $g_t$, $u_t$, and $v_t$ represent exogenous shocks to aggregate demand and inflation. They follow AR(1) processes:

\begin{align}
g_{t+1} & = \rho_g g_{t} + \epsilon^g_{t+1}\\
u_{t+1} & = \rho_u u_{t} + \epsilon^u_{t+1}.
\end{align}

We will use the following parameterization in the simulations below:

| $\beta$ | $\bar{r}$    | $\kappa$ | $\rho_g$ | $\rho_u$ | $\lambda$ |
|---------|--------------|----------|----------|----------|-----------|
| 0.995   | $-\log\beta$ | 0.1      | 0.5      | 0.5      | 0.1       |

I have assumed a value of 0.1 for $\lambda$, the weight on ouput in the loss function. This value is in the range of estimates for the Fed's loss function.

```python
# Create a variable called 'parameters' that stores the model parameter values (except lambda) in a Pandas Series. PROVIDED
parameters = pd.Series({'beta':0.995,'r_bar':-np.log(0.995),'kappa':0.1,'rho_g':0.5,'rho_u':0.5})

# Add the value for lambda as 'lam' to 'parameters'
parameters['lam'] = 0.1

# Create variable called 'var_names' that stores the variable names in a list with state variables ordered first. PROVIDED
var_names = ['g','u','y','pi','i','r']

# Create variable called 'shock_names' that stores an exogenous shock name for each state variable. PROVIDED
shock_names = ['e_g','e_u']

# Define a function that evaluates the equilibrium conditions of the model solved for zero. PROVIDED
def equilibrium_equations(variables_forward,variables_current,parameters):

    # Parameters. PROVIDED
    p = parameters

    # Current variables. PROVIDED
    cur = variables_current

    # Forward variables. PROVIDED
    fwd = variables_forward

    # IS equation. PROVIDED
    is_equation = fwd.y - (cur.r -p.r_bar) + cur.g - cur.y

    # Fisher_equation. PROVIDED
    fisher_equation = cur.r + fwd.pi - cur.i

    # Monetary policy. Variable name: 'monetary_policy'
    monetary_policy = p.lam*cur.y  + p.kappa*cur.pi

    # Phillips curve. PROVIDED
    phillips_curve = p.beta*fwd.pi + p.kappa*cur.y + cur.u - cur.pi

    # Demand process. PROVIDED
    demand_process = p.rho_g*cur.g - fwd.g

    # Inflation process. PROVIDED
    inflation_process = p.rho_u*cur.u - fwd.u

    # Stack equilibrium conditions into a numpy array. PROVIDED
    return np.array([is_equation,fisher_equation,monetary_policy,phillips_curve,demand_process,inflation_process])

# Initialize the model into a variable named 'model'. PROVIDED
nk_model = ls.model(equations = equilibrium_equations,n_states=2,var_names=var_names,shock_names=shock_names,parameters = parameters)

# Compute the steady state numerically using .compute_ss() method of nk_model. PROVIDED
nk_model.compute_ss([0,0,0,0.01,0.01,0.01])

# Find the log-linear approximation around the non-stochastic steady state and solve using .approximate_and_solve() method of nk_model
# set argumement 'log_linear' to False because the model is already log-linear.
nk_model.approximate_and_solve(log_linear = False)
```

## Optimal Responses to Shocks

Compute a 21 period impulse responses of the model's variables to a 0.01 unit shock to the exogenous component of  demand ($g_t$) and a 0.01/4 unit shock to the exogenous component of inflation ($u_t$) in period 5.

```python
# Compute the desired impulse responses
nk_model.impulse(shocks=[0.01,0.01/4],T=21,t0=5)
```

### Demand Shocks

Plot the impulse responses of output and inflation to the demand shock. Multiply output by 100 and multiply inflation by 400 (to annualize the values).

```python
# Create figure. PROVIDED
fig = plt.figure(figsize=(12,4))

# Create left subplot. PROVIDED
ax1 = fig.add_subplot(1,2,1)
# Create right subplot. PROVIDED
ax2 = fig.add_subplot(1,2,2)

# Left subplot title. PROVIDED
ax1.set_title('Output $y_t$')
# Right subplot title. PROVIDED
ax2.set_title('Inflation $\pi_t$')

# Left subplot y-axis label. PROVIDED
ax1.set_ylabel('Percent')
# Right subplot y-axis label. PROVIDED
ax2.set_ylabel('Percent')

# Plot output on left axis
ax1.plot(nk_model.irs['e_g']['y']*100,lw=4)

# Plot inflation on right axis
ax2.plot(nk_model.irs['e_g']['pi']*400,lw=4)
```

Apparently optimal policy entails perfectly offsetting demand shocks so that output and inflation are perfectly stabilized. This is because there is no tradeoff in stabilizing output versus inflation in the face of a demand shock.

### Inflation Shocks

Plot the impulse responses of output and inflation to the demand shock. Multiply output by 100 and multiply inflation by 400.

```python
# Create figure. PROVIDED
fig = plt.figure(figsize=(12,4))

# Create left subplot. PROVIDED
ax1 = fig.add_subplot(1,2,1)
# Create right subplot. PROVIDED
ax2 = fig.add_subplot(1,2,2)

# Left subplot title. PROVIDED
ax1.set_title('Output $y_t$')
# Right subplot title. PROVIDED
ax2.set_title('Inflation $\pi_t$')

# Left subplot y-axis label. PROVIDED
ax1.set_ylabel('Percent')
# Right subplot y-axis label. PROVIDED
ax2.set_ylabel('Percent')

# Plot output on left axis
ax1.plot(nk_model.irs['e_u']['y']*100,lw=4)

# Plot inflation on right axis
ax2.plot(nk_model.irs['e_u']['pi']*400,lw=4)

```

The Phillips curve implies that there *is* a tradeoff in setabilizing output versus inflation in the face of an inflation shock. The optimal policy is to allow inflation to rise and to allow output to fall so that the full force of the shock does not fall on inflation.

## Stochastic Simulation with Optimal Monetary Policy

Now we simulate how would output and inflation evolve if monetary policy were set optimally. Specifically, construct a 201 period stochastic simulation with the seed for the random number generator set to 126. Note that the shock covariance matrix for the model is:

\begin{align}
\text{Covariance matrix} & = \left[\begin{array}{cc}\sigma_g^2 & 0\\ 0 & \sigma_u^2\end{array}\right]
\end{align}

Use the following values for $\sigma_g^2$ and $\sigma_u^2$ in the simulation:

| $\sigma_g^2$      | $\sigma_u^2$      |
|-------------------|-------------------|
| 0.0022 | 0.0012 |

```python
# Define variable 'sig_g' equal to the the standard deviation of sigma_g
sig_g = 0.002

# Define variable 'sig_u' equal to the the standard deviation of sigma_u
sig_u = 0.001

# Compute simulation
nk_model.stoch_sim(cov_mat = [[sig_g**2,0],[0,sig_u**2]],T=201,seed=126)
```

Plot the simulated values of output and inflation.

```python
# Create figure. PROVIDED
fig = plt.figure(figsize=(12,4))
# Create subplot. PROVIDED
ax = fig.add_subplot(1,1,1)
# Add plot title. PROVIDED
ax.set_title('Output and inflation under optimal monetary policy')
# y-axis label. PROVIDED
ax.set_ylabel('Percent')

# Plot output
(nk_model.simulated['y']*100).plot(lw=2,label='output')

# Plot inflation
(nk_model.simulated['pi']*400).plot(lw=2,label='inflation')

# Construct legend. PROVIDED
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
```

Compute the correlations of output and inflation under optimal monetary policy.

```python
# Correlations of output and inflation
nk_model.simulated[['y','pi']].corr()
```

      y
      pi

      y
      1.0
      -1.0

      pi
      -1.0
      1.0

By requiring that output be proportional to inflation, the optimal monetary policy implies perfect negative correlation between output and inflation. All demand shocks are neutralized.


# Multi-trait LMMs

### Set up the environment

```python
%matplotlib inline
from warnings import simplefilter
simplefilter(action='ignore', category=FutureWarning)

import sys
import scipy as sp
import numpy as np
import scipy.stats as st
import pylab as pl
import pandas as pd
import h5py
sp.random.seed(0)

import limix.util as lmx_util
import limix.plot as lmx_plt

def no_annotate(*args):
    pass
from limix.plot import manhattan
manhattan._annotate = no_annotate
```

### Download the data

First, load the yeast data, which have been imported into an hdf5 file.

To process your own data, use the limix command line binary (see [here](http://nbviewer.jupyter.org/github/limix/limix-tutorials/blob/master/preprocessing_QC/loading_files.ipynb) for an example).

```python
sys.path.append('./..')
import data as tutorial_data
file_name = tutorial_data.get_file('BYxRM')
```

### Set up the data object

The data object allows us to query the genotype and phenotype data.

```python
f = h5py.File(file_name, 'r')
pheno_group = f['phenotype']
pheno_df = pd.DataFrame(pheno_group['matrix'][:],
                        columns=np.char.decode(pheno_group['col_header']['phenotype_ID'][:]),
                        index=np.char.decode(pheno_group['row_header']['sample_ID'][:]))
```

```python
pheno_df.shape
```

    (1008, 46)

```python
pheno_df.head()
```

      Cadmium_Chloride
      Caffeine
      Calcium_Chloride
      Cisplatin
      Cobalt_Chloride
      Congo_red
      Copper
      Cycloheximide
      Diamide
      E6_Berbamine
      ...
      x6-Azauracil
      Xylose
      YNB
      YNB:ph3
      YNB:ph8
      YPD
      YPD:15C
      YPD:37C
      YPD:4C
      Zeocin

      A01_01
      -7.323520
      0.279993
      0.313118
      1.658179
      -1.604442
      5.841617
      -4.130950
      0.821226
      3.622602
      -0.378747
      ...
      1.199054
      -0.309149
      17.470822
      0.055225
      -0.184268
      24.548971
      0.712171
      0.890842
      4.118372
      8.592818

      A01_02
      -8.098236
      -0.206326
      -0.534844
      -0.918012
      0.892198
      -1.618172
      1.131947
      -0.764736
      -2.946279
      -2.475193
      ...
      0.443182
      0.697908
      18.052925
      0.283462
      1.662201
      26.808476
      -1.522498
      0.006062
      0.066515
      -4.220476

      A01_03
      7.605720
      -0.127960
      -0.311102
      -2.712088
      3.301709
      -6.680571
      -1.138056
      -3.382532
      -3.157866
      -4.178616
      ...
      1.472629
      1.474329
      18.163782
      -0.951379
      0.369565
      26.183975
      1.082289
      1.723157
      5.714088
      -6.506519

      A01_04
      -6.147649
      0.878392
      -0.563331
      -0.827358
      -1.291270
      2.753851
      -2.268121
      0.867456
      -6.832079
      -1.970630
      ...
      0.054694
      -0.739040
      15.575856
      -1.690867
      -0.840344
      20.893646
      -0.636280
      0.053258
      -3.196936
      0.294462

      A01_05
      9.379060
      -1.353169
      0.405204
      -1.137234
      4.260286
      -7.391216
      -3.222742
      1.586376
      1.048391
      -3.971730
      ...
      0.267463
      0.959138
      17.808444
      0.127621
      1.967267
      28.621507
      -3.720999
      -0.768724
      -4.002786
      7.045575

5 rows × 46 columns

```python
geno_group = f['genotype']
chromosomes = geno_group['col_header']['chrom'][:]
positions = geno_group['col_header']['pos'][:]
geno_df = pd.DataFrame(geno_group['matrix'][:], columns=positions,
                       index=np.char.decode(geno_group['row_header']['sample_ID'][:]),
                      dtype='int64')
```

```python
geno_df.shape
```

    (1008, 11623)

```python
geno_df.head()
```

      27915
      28323
      28652
      29667
      30756
      31059
      31213
      31636
      31756
      31976
      ...
      925487
      925742
      926177
      927903
      928103
      929518
      929724
      930545
      931289
      931944

      A01_01
      1
      1
      1
      1
      1
      1
      1
      1
      1
      1
      ...
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0

      A01_02
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      ...
      1
      1
      1
      1
      1
      1
      1
      1
      1
      1

      A01_03
      1
      1
      1
      1
      1
      1
      1
      1
      1
      1
      ...
      0
      0
      0
      0
      0
      0
      0
      1
      1
      1

      A01_04
      1
      1
      1
      1
      1
      1
      1
      1
      1
      1
      ...
      1
      1
      1
      1
      1
      1
      1
      1
      1
      1

      A01_05
      0
      0
      0
      0
      0
      0
      0
      0
      0
      0
      ...
      1
      1
      1
      1
      1
      1
      1
      1
      1
      1

5 rows × 11623 columns

### Visualize the correlation among traits

```python
#Remove NaNs
filtered_pheno_df = pheno_df.dropna()
phenotype_names = filtered_pheno_df.columns
# center phenotype
normalized_pheno_df = (filtered_pheno_df - filtered_pheno_df.mean()) / filtered_pheno_df.std()
```

```python
pl.figure(figsize=[20,20])
Ce= np.cov(normalized_pheno_df.values.T)
pl.imshow(Ce,aspect='auto',interpolation='none')
pl.xticks(np.arange(len(phenotype_names)),phenotype_names,rotation=90)
pl.yticks(np.arange(len(phenotype_names)),phenotype_names,rotation=0)
pl.colorbar()
```

### Select a subset of the phenotypes

```python
phenotype_names = ['YPD:37C','YPD:15C','YPD:4C']
data_subsample = pheno_df[phenotype_names]
```

```python
phenotypes = data_subsample.dropna()
sample_idx = phenotypes.index.intersection(geno_df.index)
phenotypes = phenotypes.loc[sample_idx]
phenotypes = (phenotypes - phenotypes.mean()) / phenotypes.std()
phenotypes.describe()
```

      YPD:37C
      YPD:15C
      YPD:4C

      count
      8.040000e+02
      8.040000e+02
      8.040000e+02

      mean
      -2.651279e-17
      2.209399e-18
      5.744438e-17

      std
      1.000000e+00
      1.000000e+00
      1.000000e+00

      min
      -2.651086e+00
      -2.497574e+00
      -2.020168e+00

      25%
      -7.669938e-01
      -7.652285e-01
      -7.548557e-01

      50%
      4.068578e-02
      -7.631450e-02
      -1.699190e-01

      75%
      8.097791e-01
      7.221308e-01
      6.011856e-01

      max
      2.867696e+00
      2.745373e+00
      3.382201e+00

We start by (further) examining the correlation among the phenotypes:

```python
#pairwise corrrelations of the first 3 traits
pl.figure(figsize=[15,5])
pl.subplot(1,3,1)
pl.plot(phenotypes[phenotype_names[0]].values,phenotypes[phenotype_names[1]].values,'.')
pl.xlabel(phenotype_names[0])
pl.ylabel(phenotype_names[1])

pl.subplot(1,3,2)
pl.plot(phenotypes[phenotype_names[1]].values,phenotypes[phenotype_names[2]].values,'.')
pl.xlabel(phenotype_names[1])
pl.ylabel(phenotype_names[2])

pl.subplot(1,3,3)
pl.plot(phenotypes[phenotype_names[0]].values,phenotypes[phenotype_names[2]].values,'.')
pl.xlabel(phenotype_names[0])
pl.ylabel(phenotype_names[2])
```

# Variance Decomposition

Here, we show how to estimate the genetic and residual covariance using the limix class limix.varDecomp.VarianceDecomposition (see [here](https://www.pydoc.io/pypi/limix-1.0.6/autoapi/varDecomp/varianceDecomposition/index.html)).

```python
from limix.vardec import VarianceDecomposition
```

```python
# genetic relatedness matrix
from limix.stats import linear_kinship, gower_norm
sample_relatedness = gower_norm(linear_kinship(geno_df.loc[sample_idx].values))
```

    100%|██████████| 100/100 [00:00<00:00, 421.01it/s]

```python
# variance component model
vc = VarianceDecomposition(phenotypes.values)
vc.addFixedEffect()
vc.addRandomEffect(K=sample_relatedness)
vc.addRandomEffect(is_noise=True)
vc.optimize()

# retrieve genetic and noise covariance matrix
Cg = vc.getTraitCovar(0)
Cn = vc.getTraitCovar(1)
```

### Empirical Correlation

```python
Ce = np.corrcoef(phenotypes.T)
```

### Plot the correlation among the phenotypes (empirical), the genetic covariance among phenotypes, and the noise covariance.

```python
pl.figure(figsize=[15,5])
pl.subplot(1,3,1)
pl.imshow(Ce,aspect='auto',interpolation='none',vmin=-1,vmax=1)
pl.xticks(np.arange(len(phenotype_names)),phenotypes.columns)
pl.yticks(np.arange(len(phenotype_names)),phenotypes.columns)
pl.title('empirical correlation')

pl.subplot(1,3,2)
pl.imshow(Cg,aspect='auto',interpolation='none',vmin=-1,vmax=1)
pl.xticks(np.arange(len(phenotype_names)),phenotypes.columns)
pl.yticks(np.arange(len(phenotype_names)),phenotypes.columns)
pl.title('genetic covariance')

pl.subplot(1,3,3)
pl.imshow(Cn,aspect='auto',interpolation='none',vmin=-1,vmax=1)
pl.xticks(np.arange(len(phenotype_names)),phenotypes.columns)
pl.yticks(np.arange(len(phenotype_names)),phenotypes.columns)
pl.title('noise covariance')
pl.colorbar()
```

# Univariate association testing

As shown earlier, univariate (single-trait) association testing with linear mixed models can be performed with the function ``limix.qtl.qtl_test_lmm`` (see [here](https://limix.readthedocs.io/en/stable/qtl.html#linear-mixed-models)).

```python
from limix.qtl import qtl_test_lmm
```

```python
# load snp data
snps = geno_df.loc[sample_idx].values
positions = geno_df.columns
```

Run the LMM and convert the P-values into a pandas DataFrame.

```python
lmm = qtl_test_lmm(snps=snps,
                   pheno=phenotypes.values,
                   K=sample_relatedness)
pv_lmm = lmm.getPv()

pvalues_lmm = pd.DataFrame(data=pv_lmm.T,
                           index=positions,
                           columns=phenotype_names)
```

Plot the results from (univariate) GWAS using Manhattan plots.

```python
for p_ID in phenotype_names:
    pl.figure(figsize=[15,4])
    lmx_plt.plot_manhattan(pd.DataFrame(dict(pv=pvalues_lmm[p_ID].values,pos=positions,chrom=chromosomes,alpha=0.05)))
    pl.title(p_ID)
```

# Multi-trait association testing

Multi-trait association testing with linear mixed models can be performed using the function ``limix.qtl.qtl_test_lmm_kronecker`` (see [here](https://limix.readthedocs.io/en/stable/qtl.html#limix.qtl.qtl_test_lmm_kronecker)).

Here we show how to perform the following multi-trait tests:
- any effect test (that is, a test to determine if a SNP has an effect on any of the phenotypes)
- common effect test (test to determine if a SNP has the same effect size and sign/direction for both phenotypes)
- specific effect test (test to determine if a SNP has a specific effect on the two traits; good for GxE)

### Any effect test

```python
N, P = phenotypes.values.shape
```

```python
covs = np.ones((N, 1))      #covariates
Acovs = np.eye(P)           #the design matrix for the covariates
Asnps = np.eye(P)           #the design matrix for the SNPs
K1r = sample_relatedness    #the first sample-sample covariance matrix (non-noise)
```

```python
from limix.qtl import qtl_test_lmm_kronecker
```

```python
lmm, pvalues = qtl_test_lmm_kronecker(snps=snps,
                                      phenos=phenotypes.values,
                                      covs=covs,
                                      Acovs=Acovs,
                                      Asnps=Asnps,
                                      K1r=K1r)
```

Convert the P-values into a DataFrame:

```python
pvalues = pd.DataFrame(data=pvalues.T,index=positions,columns=['multi_trait'])
```

### Plot the results from multi-trait GWAS using Manhattan plots.

```python
pl.figure(figsize=[15,4])
pl.title('Any effect test')
lmx_plt.plot_manhattan(pd.DataFrame(dict(pv=pvalues['multi_trait'].values,
                       pos=positions,chrom=chromosomes)))
```

### Common effect test

A common effect test is a 1 degree of freedom test and can be done by setting
\begin{equation}
\mathbf{A}_1^\text{(snp)} = \mathbf{1}_{1,P},\;\;\;
\mathbf{A}_0^\text{(snp)} = \mathbf{0}
\end{equation}

```python
covs = np.ones((N, 1))      #covariates
Acovs = np.eye(P)           #the design matrix for the covariates
Asnps = np.ones((1,P))      #the design matrix for the SNPs
K1r = sample_relatedness    #the first sample-sample covariance matrix (non-noise)
```

```python
lmm, pvalues_common = qtl_test_lmm_kronecker(snps=snps,
                                             phenos=phenotypes.values,
                                             #covs=covs,
                                             #Acovs=Acovs,
                                             Asnps=Asnps,
                                             K1r=K1r)
```

Convert the P-values into a DataFrame:

```python
pvalues_common = pd.DataFrame(data=pvalues_common.T,index=positions,columns=['common'])
```

### Manhattan plot

```python
pl.figure(figsize=[15,4])
pl.title('common')
lmx_plt.plot_manhattan(pd.DataFrame(dict(pv=pvalues_common['common'].values,
                       pos=positions,chrom=chromosomes)))
```

### Testing for a specific effect

For a specific effect test for trait $p$
the alternative model is set to have both a common and a specific effect
for transcript $p$ from the SNP while the null model has only a common effect.

It is a 1 degree of freedom test and,
in the particular case of $P=3$ traits and for $p=0$, it can be done by setting
\begin{equation}
\mathbf{A}_1^\text{(snp)} =
\begin{pmatrix}
  1 & 0 & 0 \\
  1 & 1 & 1
 \end{pmatrix}
\;\;\;,
\mathbf{A}_0^\text{(snp)} = \mathbf{1}_{1,3}
\end{equation}

Specific effect tests can be performed using the function ``limix.qtl.qtl_test_interaction_lmm_kronecker`` (see [here](https://limix.readthedocs.io/en/stable/qtl.html#limix.qtl.qtl_test_interaction_lmm_kronecker)).

```python
Asnps0 = np.ones((1,P))     #the null model design matrix for the SNPs
Asnps1 = np.zeros((2,P))          #the alternative model design matrix for the SNPs
Asnps1[0,:] = 1.0
Asnps1[1,1] = 1.0
print("Design(0): \n"+str(Asnps0))
print("Design(Alt): \n"+str(Asnps1))
```

    Design(0):
    [[1. 1. 1.]]
    Design(Alt):
    [[1. 1. 1.]
     [0. 1. 0.]]

```python
from limix.qtl import qtl_test_interaction_lmm_kronecker

pvalues_inter = qtl_test_interaction_lmm_kronecker(snps=snps,
                                                   phenos=phenotypes.values,
                                                   covs=covs,
                                                   Acovs=Acovs,
                                                   Asnps0=Asnps0,
                                                   Asnps1=Asnps1,
                                                   K1r=K1r)

```

Convert the P-values into a DataFrame:

```python
pvalues_inter = pd.DataFrame(data=np.concatenate(pvalues_inter).T,
                             index=positions,
                             columns=["specific","null_common","alternative_any"])
```

### Manhattan plot

```python
pl.figure(figsize=[15,4])
pl.title('specific')
lmx_plt.plot_manhattan(pd.DataFrame(dict(pv=pvalues_inter['specific'].values,
                       pos=positions,chrom=chromosomes,
                       alpha=0.1)))
```

```python

```


 Pregunta de Investigación

 El uso de funciones de pérdida robustas, como la función de Huber, permite mejorar el desempeño de una red en problemas de regresión donde la salida presenta outliers.

 INF-395 Redes Neuronales y Deep Learning

 Autores: Francisco Andrades | Lucas Díaz

Lenguaje: Python

Temas:

    - Arquitectura de Redes Neuronales *Feed-Forward*
    - Entrenamiento de Redes Neuronales.
    - Parte Básica de Redes Convolucionales.
    - Problemas Especiales.

Videos Explicativos:

    - https://youtu.be/DbpbfwR61Ws

    - https://youtu.be/WL5InIBNLos

```python
# Librerías
!pip install scipy==1.6

import pandas as pd
import numpy as np
import seaborn as sns; sns.set()
import matplotlib.pyplot as plt

import scipy
from scipy import stats

from sklearn.model_selection import train_test_split, KFold
from sklearn.metrics import mean_squared_error as mse
from sklearn.metrics import mean_absolute_error as mae
from sklearn.compose import ColumnTransformer
from sklearn.datasets import fetch_openml
from sklearn.pipeline import Pipeline
from sklearn.impute import SimpleImputer
from sklearn.preprocessing import StandardScaler, OneHotEncoder
from sklearn.linear_model import LogisticRegression

from tensorflow import keras
from tensorflow.keras import layers
from tensorflow.keras.datasets import boston_housing
from tensorflow.keras import optimizers
# LOSSES
from tensorflow.keras.losses import Huber
from tensorflow.keras.losses import mean_squared_error as mse_loss
from tensorflow.keras.losses import mean_absolute_error as mae_loss
from tensorflow.keras.losses import LogCosh

from tensorflow.keras.callbacks import EarlyStopping

#P2
from tensorflow.keras import backend as K
def root_mean_squared_error(y_true, y_pred):
        return K.sqrt(K.mean(K.square(y_pred - y_true)))
```

    Requirement already satisfied: scipy==1.6 in /Users/lucas/opt/anaconda3/lib/python3.8/site-packages (1.6.0)
    Requirement already satisfied: numpy>=1.16.5 in /Users/lucas/opt/anaconda3/lib/python3.8/site-packages (from scipy==1.6) (1.19.2)

```python
path = ''
```

 Pregunta de Investigación

\
**Hipótesis:** El uso de funciones de pérdida robustas, como la función de Huber, **permite** mejorar el desempeño de una red en problemas de regresión donde la salida presenta outliers.

**Objetivo:**  Demostrar la hipótesis.

**Propuesta:** Se demostrará la hipótesis evidenciando al menos **1** caso donde la función de Huber mejora el desempeño de una arquitectura frente a la función de pérdida *Mean Squared Error*. Posteriormente se extenderá la demostración a otras funciones de pérdida robustas.

**Metodología**
1. Se harán experimentos en 2 datasets sintéticos y 2 reales, utilizando una arquitectura feed-forward **fija para cada dataset**.
2. Se entrenará la arquitectura de forma independiente para cada función de pérdida y se evaluará utilizando como métricas MAE y MSE.
3. Se ejecutarán N iteraciones del paso 2 y se obtendrá una distribución de resultados pertinente a cada función de pérdida.
4. Mediante Hipótesis Testing, se responderá a la siguiente pregunta para cada métrica: ¿Se puede afirmar, con determinada confianza, que la media de la distribución generada por Huber es estrictamente menor a la media de la distribución generada por MSE?
5. Si la respuesta es positiva para ambas métricas en al menos **1** caso, se entenderá por demostrada la hipótesis.

**Esqueleto**
1. Recordatorio MSE, MAE, HUBER.
    - ¿Por qué escogemos MSE como la función de pérdida a comparar?
    - ¿Por qué consideramos que la hipótesis debiese ser cierta?
2. Descripción de los datasets.
    -  ¿Por qué los escogimos?
3. Evaluación de los modelos.
    - ¿Por qué utilizar MAE y MSE como métricas?
4. Experimentos.
    - Generación de las distribuciones.
    - Hipótesis Testing.
5. Extensión a otras funciones de pérdida robustas.
    - LogCosh()

****

 1. Recordatorio MSE, MAE Y HUBER

#### MSE

- Media de los errores al cuadrado (Norma L2)
- Sensible a outliers

$$MSE = \frac{\sum_{i=1}^n(y_i-y_i^p)^2} {n}$$

#### MAE

- Media del valor absoluto de los errores (Norma L1)
- Diferenciable por trazos.

$$MAE = \frac{\sum_{i=1}^n|y_i-y_i^p|} {n}$$

#### Huber

- Combina MSE con MAE.
- MAE, pero se convierte en MSE para errores pequeños.
- Robustez frente a outliers (MAE), pero diferenciable en 0 (MSE).

En 1964, Peter Huber, en su *paper Robust estimation of a location parameter*, define la función (con $k(\epsilon)$):

\begin{equation*}
    \rho(y - f(x)) =
            \begin{cases}
            \frac{1}{2}(y - f(x))^2 &      \text{ para } \quad |y - f(x)| < \delta \\
            \delta|y - f(x)| - \frac{1}{2}\delta^2 &  \text{ para } \quad |y - f(x)| \geq \delta
            \end{cases}
\end{equation*}

Por conveniencia, se puede reescribir la función de Huber con parámetros utilizados en este trabajo, así:

\begin{align*}
    t &= y - f(x)\\
    k &= \delta
\end{align*}

\begin{equation*}
    \rho(y - f(x)) =
            \begin{cases}
            \frac{1}{2}(y - f(x))^2 &      \text{ para } \quad |y - f(x)| < \delta \\
            \delta|y - f(x)| - \frac{1}{2}\delta^2 &  \text{ para } \quad |y - f(x)| \geq \delta
            \end{cases}
\end{equation*}

donde:

\begin{align*}
y&: \text{ valor real.}\\
f(x)&: \text{ predicción del modelo.}
\end{align*}

- Sintonización parámetro $\delta$ en Huber($\delta$):

Se puede interpretar $\delta$ como el valor en donde el modelo comienza a considerar "grande" la diferencia entre el valor real y la predicción.

****

 2. Descripción de los datasets

Se trabajarán con dos datasets sintéticos y dos datasets reales.

* **Dataset sintético 1**: No contiene outliers.

* **Dataset sintético 2**: Contiene outliers.

* **Dataset real Boston housing price**: Contiene outliers moderados.

* **Dataset real Challenge**: Contiene outliers extremos.

Todos los datasets se preprocesarán de la siguiente forma:

```python
from sklearn.compose import make_column_selector as selector

numeric_transformer = StandardScaler()
categorical_transformer = OneHotEncoder(handle_unknown='ignore')

preprocessor = ColumnTransformer(transformers=[
    ('num', numeric_transformer, selector(dtype_include="number")),
    ('cat', categorical_transformer, selector(dtype_include="category"))
])
```

```python
def procesar_data(X, y):
    x_train, x_test, y_train, y_test = [pd.DataFrame(elem) for elem in train_test_split(X,
                                                                                y,
                                                                                shuffle=True,
                                                                                random_state=1)]

    x_train, x_test = preprocessor.fit_transform(x_train), preprocessor.transform(x_test)
    y_train, y_test = preprocessor.fit_transform(y_train), preprocessor.transform(y_test)

    return x_train, x_test, y_train, y_test
```

## Datasets sintéticos

La construcción de datasets sintéticos busca mostrar de forma más evidente la ventaja de utilizar -en problemas de regresión- un modelo con una loss function robusta a *outliers*, frente a una que no.

En este caso, los modelos a utilizar son:

* Regresión lineal utilizando **MSE** como *loss function*.

* Regresión lineal utilizando la función de **Huber** como *loss function*.

Para la generación de los datasets, se utiliza la función ```gendata2(n_samples, n_outliers)```. En dicha función, ```n_samples``` corresponde a la cantidad de datos totales que se utilizarán, mientras ```n_outliers``` es una fracción del total de datos, correspondiente a la cantidad de outliers.

A continuación, se generarán los datasets sintéticos a utilizar:

* A la izquierda, el dataset sintético **sin outliers**, donde los modelos obtendrán un resultado similar.
* A la derecha, en cambio, el dataset sintético **con outliers**, donde el modelo con una *loss function* robusta se desempeñará mejor.

```python
def gendata(n_samples=1000, n_outliers=50):
    X = np.linspace(0, 100, n_samples)

    y = np.linspace(0, 100, n_samples) + np.random.normal(0, 5, n_samples)

    arreglo_pos = np.random.randint(0, n_samples, n_outliers)

    y[arreglo_pos] = y[arreglo_pos] + (np.random.choice((-1,1),size=n_outliers)
                                       *np.random.randint(50, 210, n_outliers))

    return X, y

# Datasets sintéticos
X, y = gendata(400, 0)
X_outliers, y_outliers = gendata(400, 10)

# Preprocesar datasets sintéticos

    #sin outliers
x_train_sint, x_test_sint, y_train_sint, y_test_sint = procesar_data(X, y)

    #con outliers
x_train_sintout, x_test_sintout, y_train_sintout, y_test_sintout = procesar_data(X_outliers, y_outliers)
```

```python
#PLOTEANDO
fig, ax = plt.subplots(1, 2, figsize=(24,12))

ax[0].scatter(np.concatenate((x_train_sint,
                              x_test_sint)),
              np.concatenate((y_train_sint,
                              y_test_sint)))
ax[0].set_title('data sin outliers')

ax[1].scatter(np.concatenate((x_train_sintout,
                              x_test_sintout)),
              np.concatenate((y_train_sintout,
                              y_test_sintout)))
ax[1].set_title('data con outliers')

plt.show()
```

Al comparar ambos gráficos, es notoria la presencia de outliers en el que está ubicado a la derecha. Dicha "exageración" en los datos que son atípicos, evidenciará de mejor manera el desempeño de ambos métodos.

A continuación, se utilizará la herramienta para diagramas de caja ```pyplot.boxplot()```, para interpretar la distribución de los datos a través de sus cuartiles, además de poder representar los valores atípicos.

```python
fig, ax = plt.subplots(2, 2, figsize=(20,14))

ax[0, 0].boxplot(np.concatenate((y_train_sint, y_test_sint)))
ax[0, 0].set_title('y values: sin outliers')
ax[0, 1].boxplot(np.concatenate((x_train_sint, x_test_sint)), vert=False)
ax[0, 1].set_title('X values')

fig.tight_layout(pad=2.0)#just to separate

ax[1, 0].boxplot(np.concatenate((y_train_sintout, y_test_sintout)))
ax[1, 0].set_title('y values: con outliers')
ax[1, 1].boxplot(np.concatenate((x_train_sintout, x_test_sintout)), vert=False)
ax[1, 1].set_title('X values')

plt.show()
```

Lo anterior, permite vislumbrar que los valores atípicos del segundo dataset están, en efecto, significativamente distantes con respecto al resto de datos. Por contraparte, con esta herramienta, el primer dataset tampoco presenta valores atípicos.

Finalmente, cabe destacar la organización de los valores de *X*, que a simple vista se muestran distribuidos uniformemente.

# Datasets reales

Los datasets reales a utilizar corresponden a:

* ```boston_housing```: importado desde ```tensorflow.keras.datasets```, dataset que tiene outliers moderados.
* **metadata_casas_train.csv**: utilizado en la segunda parte de la Tarea, dataset con valores atípicos en la columna **'precio'**, con outliers extremos.

En el caso del dataset Boston, se utilizará una una red neuronal de 3 capas densas (2 ocultas + 1 de salida).

Para el dataset del Challenge, se utilizará una red neuronal con 2 capas densas(1 oculta + 1 de salida).

Funciones de pérdida a utilizar:
* MSE loss function.
* Huber loss function.

Lo anterior, pretende buscar si es que la naturaleza de los valores atípicos genera algún cambio a la hora de resolver un problema con determinadas *loss functions*.

```python
from tensorflow.keras.datasets import boston_housing

#BOSTON
(x_train, y_boston), (x_test, y_test) = boston_housing.load_data()

X = np.concatenate((x_train, x_test))
y = np.concatenate((y_boston, y_test))

X_train_boston, X_test_boston, y_train_boston, y_test_boston = procesar_data(X, y)

#CASAS CHALLENGE
df_train = pd.read_csv(path+'metadata_casas_train.csv',index_col=0)

df_houses = pd.read_csv(path+'metadata_casas_train.csv',index_col=0)
df_houses.zipcode = df_houses.zipcode.astype('category')
y_houses = pd.DataFrame(np.array(df_houses.pop('precio')).reshape(-1,1))

X_train_challenge, X_test_challenge, y_train_challenge, y_test_challenge = procesar_data(df_houses, y_houses)

```

```python
fig, ax = plt.subplots(1, 2, figsize=(24,12))

ax[0].boxplot(np.concatenate((y_train_boston, y_test_boston)))
ax[0].set_title('y boston: outliers moderados')

ax[1].boxplot(np.concatenate((y_train_challenge, y_test_challenge)))
ax[1].set_title('y values: outliers extremos')

plt.show()

```

****

 3. Evaluación de los modelos

Para evaluar los modelos se escogieron las métricas MAE y MSE.

¿Es válido evaluar los modelos utilizando estas métricas?

Se presentará un pequeño experimento para demostrar que las métricas son, en efecto, válidas y que la función de pérdida con que se entrena el modelo **no asegura un mejor desempeño cuando coincide con alguna de las métricas**.

```python
def pequeño_experimento(n_samples=1000, n_outliers=50):
    X = np.linspace(0, 100, n_samples)
    y = np.linspace(0, 100, n_samples) + np.random.normal(0, 5, n_samples)

    np.random.seed(5)
    arreglo_pos = np.random.randint(0, n_samples, n_outliers)

    y[arreglo_pos] = y[arreglo_pos] + np.random.randint(50, 210, n_outliers)
    return X, y
```

```python
X_p, y_p = pequeño_experimento(400, 10)
x_train_sint_p, x_test_sint_p, y_train_sint_p, y_test_sint_p = procesar_data(X_p, y_p)

#PLOTEANDO
fig = plt.figure(figsize=(24,12))

plt.scatter(np.concatenate((x_train_sint_p,
                            x_test_sint_p)),
            np.concatenate((y_train_sint_p,
                            y_test_sint_p)))

plt.scatter(x_train_sint_p,
            y_train_sint_p)
plt.title('data')
plt.show()
```

```python
def linearmodels(n_features, loss):
    inputs = keras.Input(shape=(n_features, ), name='input_data')
    outputs = layers.Dense(1, name='output')(inputs)
    model = keras.Model(inputs=inputs, outputs = outputs, name='modelo')
    model.compile(loss = loss,
                  optimizer=optimizers.Adam(lr=0.01),
                  metrics=['mean_absolute_error','mean_squared_error'],
                 )
    return model
```

```python
modelo_huber = linearmodels(x_train_sint_p.shape[1],mae_loss)
modelo_mse = linearmodels(x_train_sint_p.shape[1],mse_loss)
```

```python
history = modelo_huber.fit(x_train_sint_p,y_train_sint_p,epochs=50,
                           verbose=0,)
history2 = modelo_mse.fit(x_train_sint_p,y_train_sint_p,epochs=50,
                          verbose=0,)

preds_huber = modelo_huber.predict(x_test_sint_p)
preds_mse = modelo_mse.predict(x_test_sint_p)

print('MSE con loss MAE: ', mse(preds_huber,y_test_sint_p))
print('MSE con loss MSE: ', mse(preds_mse,y_test_sint_p))

print('\nMAE con loss MAE: ', mae(preds_huber,y_test_sint_p))
print('MAE con loss MSE: ', mae(preds_mse,y_test_sint_p))

hide_toggle()
```

    MSE con loss MAE:  0.041196428311516174
    MSE con loss MSE:  0.04725872877278875

    MAE con loss MAE:  0.11049072496476306
    MAE con loss MSE:  0.13411814509041042

Toggle show/hide

****

 4. Experimentos

#### Pipeline del Experimento

```python
def one_iteration(loss, model_func, verbose, *data_args):

    x_train,y_train,x_test,y_test = data_args

    n_features = x_train.shape[1]
    model = model_func(n_features, loss)
    history = model.fit(
        x = x_train,
        y = y_train,
        batch_size=32,
        epochs=100,
        verbose=0,
    )
    test_results = model.evaluate(x_test, y_test, verbose=0)
    if not(verbose):
        return test_results[1], test_results[2]

```

#### Pipeline del Experimento

```python
def experiment(loss, model_func, plot, *data_args):
    x_train,y_train,x_test,y_test = data_args
    maes = np.array([one_iteration(loss, model_func, False, x_train, y_train, x_test, y_test) for i in range(100)])
    if plot:
        fig = plt.figure(figsize=(20, 5))
        ax1 = fig.add_subplot(121)
        ax2 = fig.add_subplot(122)
        ax1.set_title('MAE')
        ax2.set_title('MSE')
        ax1.hist(maes[:,0])
        ax2.hist(maes[:,1])
        plt.show()
    return maes
```

#### Sintetic Dataset

```python
def linearmodels(n_features, loss):
    inputs = keras.Input(shape=(n_features, ), name='input data')
    outputs = layers.Dense(1, name='output')(inputs)
    model = keras.Model(inputs=inputs, outputs = outputs, name='modelo')
    model.compile(loss = loss,
                  optimizer=optimizers.Adam(lr=0.01),
                  metrics=['mean_absolute_error','mean_squared_error'],
                 )
    return model
```

> #### Sin outliers

```python
sintetic_huber = experiment(Huber(1),
                            linearmodels,
                            True,
                            x_train_sint,
                            y_train_sint,
                            x_test_sint,
                            y_test_sint
                           )
sintetic_mse = experiment(mse_loss,
                          linearmodels,
                          True,
                          x_train_sint,
                          y_train_sint,
                          x_test_sint,
                          y_test_sint
                         )

```

```python
# H0: mean_mse <= mean_huber
# H1: mean_mse > mean_huber
print("Resultado para MAE: ", stats.ttest_ind(sintetic_mse[:, 0], sintetic_huber[:, 0], alternative='greater'))
print("Resultado para MSE: ", stats.ttest_ind(sintetic_mse[:, 1], sintetic_huber[:, 1], alternative='greater'))
```

    Resultado para MAE:  Ttest_indResult(statistic=-0.8737023794784703, pvalue=0.8083306087635856)
    Resultado para MSE:  Ttest_indResult(statistic=-1.1105159126041166, pvalue=0.8659382187911061)

> #### Con outliers

```python
sintetic_outliers_huber = experiment(Huber(1), linearmodels, True,
                                     x_train_sintout, y_train_sintout,
                                     x_test_sintout, y_test_sintout)

sintetic_outliers_mse = experiment(mse_loss, linearmodels, True,
                                   x_train_sintout, y_train_sintout,
                                   x_test_sintout, y_test_sintout)

```

```python
print("Resultados MAE: ", stats.ttest_ind(sintetic_outliers_mse[:, 0], sintetic_outliers_huber[:, 0], alternative='greater'))
print("Resultados MSE: ", stats.ttest_ind(sintetic_outliers_mse[:, 1], sintetic_outliers_huber[:, 1], alternative='greater'))
```

    Resultados MAE:  Ttest_indResult(statistic=17.488092543084342, pvalue=2.5262852393005206e-42)
    Resultados MSE:  Ttest_indResult(statistic=22.99484427063339, pvalue=4.095052397409369e-58)

#### Boston Housing Dataset

```python
def boston_model(n_features,loss):
    inputs = keras.Input(shape=(n_features, ), name='input_boston')
    x = layers.Dense(16, activation='relu')(inputs)
    x = layers.Dense(8, activation='relu')(x)
    outputs = layers.Dense(1, name='Output')(x)
    model = keras.Model(inputs=inputs, outputs=outputs, name='Modelo_boston')
    model.compile(loss=loss,optimizer='adam',metrics=['mean_absolute_error','mean_squared_error'])
    return model
```

#### Boston Housing Dataset

```python
boston_huber = experiment(Huber(0.1), boston_model, True,
                          X_train_boston, y_train_boston,
                          X_test_boston, y_test_boston)
boston_mse = experiment(mse_loss, boston_model, True,
                        X_train_boston, y_train_boston,
                        X_test_boston, y_test_boston)

```

```python
print("Resultados MAE: ", stats.ttest_ind(boston_mse[:, 0], boston_huber[:, 0], alternative='greater'))
print("Resultados MSE: ", stats.ttest_ind(boston_mse[:, 1], boston_huber[:, 1], alternative='greater'))
```

    Resultados MAE:  Ttest_indResult(statistic=3.944972662648769, pvalue=5.5377310138874965e-05)
    Resultados MSE:  Ttest_indResult(statistic=1.7419278573077739, pvalue=0.041536981277773985)

#### Challenge Dataset

```python
def challenge_model(n_features,loss):
    inputs = keras.Input(shape=(n_features, ), name='input_challenge')
    x = layers.Dense(4, activation='relu')(inputs)
    outputs = layers.Dense(1, name='Output')(x)
    model = keras.Model(inputs=inputs, outputs=outputs, name='Modelo_challenge')
    model.compile(loss=loss,optimizer='adam',metrics=['mean_absolute_error','mean_squared_error'])
    return model
```

#### Challenge Dataset

```python
challenge_huber = experiment(Huber(0.1), challenge_model, True,
                             X_train_challenge, y_train_challenge,
                             X_test_challenge, y_test_challenge)

challenge_mse = experiment(mse_loss, challenge_model, True,
                             X_train_challenge, y_train_challenge,
                             X_test_challenge, y_test_challenge)

```

```python
print("Resultados MAE: ", stats.ttest_ind(challenge_mse[:, 0], challenge_huber[:, 0], alternative='greater'))
print("Resultados MSE: ", stats.ttest_ind(challenge_mse[:, 1], challenge_huber[:, 1], alternative='greater'))
```

    Resultados MAE:  Ttest_indResult(statistic=14.16762999543144, pvalue=3.180405821688636e-32)
    Resultados MSE:  Ttest_indResult(statistic=-4.246195086577404, pvalue=0.9999832903073754)

***

5. Extensión a otras loss functions robustas

#### LogCosh Loss

\begin{equation}
L(y, y^p) = \sum_{i=1}^n log(cosh(y_{i}^p - y_i))
\end{equation}

#### Challenge Dataset

```python
challenge_logcosh = experiment(LogCosh(), challenge_model, True,
                             X_train_challenge, y_train_challenge,
                             X_test_challenge, y_test_challenge)

challenge_mse = experiment(mse_loss, challenge_model, True,
                             X_train_challenge, y_train_challenge,
                             X_test_challenge, y_test_challenge)

```

```python
print("Resultados MAE: ", stats.ttest_ind(challenge_mse[:, 0], challenge_logcosh[:, 0], alternative='greater'))
print("Resultados MSE: ", stats.ttest_ind(challenge_mse[:, 1], challenge_logcosh[:, 1], alternative='greater'))
```

    Resultados MAE:  Ttest_indResult(statistic=12.267830600650663, pvalue=2.1055720093883013e-26)
    Resultados MSE:  Ttest_indResult(statistic=0.05287432502408132, pvalue=0.4789426744847888)

# Conclusiones

* Dados los resultados experimentales, efectivamente favorece el desempeño de un modelo el ser entrenado con una función de pérdida robusta como Huber.

* Como complemento del punto anterior, también se muestra experimentalmente que otras funciones robustas -como LogCosh- mejoran el desempeño de MSE como función de pérdida.

* Como punto de mejora a esta investigación, se podría haber demostrado que utilizar una función robusta como Huber, nunca empeora el resultado que se obtiene con MSE como función de pérdida.