# Proyecto Número Dos - Ecuación de Onda
El problema que fue escogido para resolver fue el uso de la libreria DifferentialEquations para resolver la ecuación de onda independiente del tiempo en un sistema de coordenadas polares con una simetria rotacional, el cual estará bajo tres ambientes diferentes. Estos ambientes son los siguientes:
+ Onda libre.
+ Onda amortiguada debido a un medio viscoso.
+ Onda amortiguada debido a un medio viscoso y con una pared circular la cual puede amortiguar la onda o aumentar su intensidad.

La ecuación de onda en tres dimensiones es descrita por la siguiene ecuación diferencial parcial:
\begin{equation}
\nabla^2 \psi = \frac{1}{\nu^2} \frac{\partial^2 \psi}{\partial t^2}.
\end{equation}
Si tomamos en cuenta el sistema de coordenadas polares, se tiene que el operador laplaciano esta definido de la siguiente manera:
\begin{equation}
\nabla^2 = \frac{1}{r}\frac{\partial }{\partial r}\left(r\frac{\partial}{\partial r}\right) + \frac{1}{r^2}\frac{\partial^2}{\partial \phi^2}.
\end{equation}
Al suponer que existe una simetria rotacional, la ecuación de onda se transforma en lo siguiente:
\begin{equation}
\frac{1}{r}\frac{\partial}{\partial r}\left(r\frac{\partial \psi}{\partial r}\right)  = \frac{1}{\nu^2} \frac{\partial^2 \psi}{\partial t^2}.
\end{equation}
Realizando una separación de variables, suponiendo que
\begin{equation}
\psi(r,t)= R(r)T(t)
\end{equation}
se obtienen las siguientes dos ecuaciones diferenciales
\begin{equation}
\frac{1}{r}\frac{d}{d r}\left(r\frac{d R}{d r}\right)+k^2R =0 \qquad
\frac{d^2 T}{dt^2}+k^2T=0.
\end{equation}
Para este caso, solo analizaremos la parte espacial, por lo que al realizar esas series de derivadas, la ecuación diferencial para la función radial es la siguiente:
\begin{equation}
\frac{d^2R}{dr^2} + \frac{1}{r}\frac{dR}{dr}+k^2R=0
\end{equation}

El objetivo de este proyecto será obtener la visualización de la función de onda dependiente de r y en un mapa de color. Es por ellos que el esquema de trabajo es el siguiente:
+ Definir parámetros de entrada como las dimensiones del área por analizar, constantes de amortiguación, velocidad de la onda, regiones especiales.
+ Definir la ecuación diferencial para el caso a analizar.
+ Solución de la ecuación diferencial.
+ Creación del mapa de valores.
+ Creación de las gráficas.

```julia
using DifferentialEquations
using Plots
```

## Parte 1 - Modelo determinista
En esta parte del proyecto se tomara la ecuación diferencial para una onda libre
\begin{equation}
\frac{d^2R}{dr^2} + \frac{1}{r}\frac{dR}{dr}+k^2R=0
\end{equation}
Con lo cual los parámetros necesarios serán
+ Dimensiones del área por analizar
+ Velocidad de la onda

Como se quiere realizar una gráfica de color, se necesitara crear una matriz con los valores de la solución de la ecuación diferencial.
El algoritmo se este proceso se encuentra en la función llamada $\textit{fill_map}$, al existir una simetria rotacional en el sistema, se aprovechara de esto, analizando solo un cuadrante de la región por analizar, y por medio de reflexiones en los ejes se obtendran los valores de los otros cuadrantes. A cada celda se correspondera una posición con respecto al centro, para la asignación del valor de la función de onda se tomara en cuenta la siguiente condición:
\begin{equation}
r_{\text{celda}}-r_{ij} <= 0.001
\end{equation}
por lo que cuando $r_{\text{celda}}>r_{ij}$ entonces la posicion de la matriz ij tendra el valor $R(r_{ij})$

```julia
"""
Función principal para la creación del mapa de valores
Parámetros de entrada:
x    -> valores radiales
y    -> valores de R(r), solución de la ED
map  -> matriz inicial con valores en 0
size -> Dimensiones del área por analizar
"""
function fill_map(x,y,map,size)
    # Movimiento en la horizontal de la matriz
    for i in 1:size
        # Valores para aprovechar la simetría en la horizontal
        pos_i=pos_values(i,size)
        # Movimiento en la vertical de la matriz
        for j in 1:size
            # Valores para aprovechar la simetría en la vertical
            pos_j=pos_values(j,size)
            # Asignación de los valores
            fill_pos(pos_i,pos_j,x,y,map,i,j,size)
        end
    end
end
```

    fill_map

```julia
"""
Función para la asignación de los valores
Parámetros de entrada:
pos_i -> posiciones de la matriz con simetría horizontal
pos_j -> posiciones de la matriz con simetría vertical
x     -> valores radiales
y     -> valores de R(r), solución de la ED
map   -> matriz inicial con valores en 0
size  -> Dimensiones del área por analizar
i,j   -> posicion radial (x(i)) y soluciones de ED (y(j))
"""
function fill_pos(pos_i,pos_j,x,y,map,i,j,size)
    # Cambios en las posiciones de simetría horizontal
    for ii in pos_i
        # Cambios en las posiciones de simetría vertical
        for jj in pos_j
            # Calculo de la r de la celda por asignar valor de R(r)
            r_ij=(x[i]^2+x[j]^2)^(1/2)
            # Llenado de valores en el mapa
            map[ii,jj]=fill_values(x,y,r_ij,size)
        end
    end
end
```

    fill_pos

```julia
"""
Función para el calculo de las posiciones con simetrias horizontales y verticales
Parámetros de entrada:
pos  -> posicion del cuadrante para las simetrias
size -> Dimensiones del área por analizar
"""
function pos_values(pos,size)
    return [size-pos+1,size+pos]
end
```

    pos_values

```julia
"""
Funcion que realiza la decision de que valor tendra esa celda
Parámetros de entrada:
x    -> valores radiales
y    -> valores de R(r), solución de la ED
r_ij -> posicion radial correspondiente a la celda
size -> Dimensiones del área por analizar
"""
function fill_values(x,y,r_ij,size)
    # Variable boleana que controla el ciclo
    istrue=true
    # Posición de la matriz de la r y R(r)
    n=1
    # Variable que regresara el valor correspondiente a la posicion r_ij
    value=0
    while istrue && n<size-1
        # Condición para la decisión
        if r_ij-x[n]<=0.001
            # Guardado del valor
            value=y[n]
            # Cambio para detener el ciclo
            istrue=false
        else
            # Cambio en la posición de r y R(r)
            n+=1
        end
    end
    return value
end
```

    fill_values

```julia
"""
Función que regresa el tamaño de la matriz introducida
Creada ya que las matrices que da la libreria DifferentialEquations
no tiene definida size()
Parámetros de entrada:
x -> matriz que se calculara su tamaño
"""
function obtain_size(x)
    size=0
    for i in x
        size+=1
    end
    return size
end
```

    obtain_size

```julia
"""
Función de arroja los valores de r,R(r) y el tamaño de estas matrices
Parámetros de entrada:
model -> objeto creado por ODEProblem()
"""
function solve_PDE(model)
    # Solución de la ecuación diferencial
    y = solve(model, Vern9(), abs_tol=1e-16, rel_tol=1e-16)
    # Extracción de los valores de r
    x_sol=convert(Array,y.t)
    # Extracción de los valores de R(r)
    y_sol=convert(Array,y)[1,:]
    # Calculo de la dimension de las matrices r y R(r)
    size=obtain_size(y_sol)
    return x_sol,y_sol,size
end
```

    solve_PDE

```julia
"""
Funcion que realiza las graficas de la función radial y el mapa de colores en uno solo, en
la parte izquiera la función radial y en la parte derecha el mapa de colores
Parámetros de entrada:
x_sol -> valores de r de la solucion de la ED
y_sol -> valores de R(r) de la solucion de la ED
size  -> tamaño de las matrices de r y R(r)
"""
function plot_graphics(x_sol,y_sol,size)
    # Creacion de la matriz para el mapa
    map=zeros(2*size,2*size)
    # Calculo de los valores de r para cada celda
    r=2^(1/2)*x_sol
    # Llenado de los valores de R(r) a cada celda del mapa
    fill_map(r,y_sol,map,size)
    # Creación de la gráfica del mapa de colores
    color_map=heatmap(1:2*size,1:2*size,map,color=cgrad(:inferno))
    # Creación de la gráfica de la función de onda
    radial_function=plot(x_sol,y_sol)
    # Agrupación de las gráfica de la función de onda y el mapa de colores
    plot(radial_function,color_map)
end
```

    plot_graphics

```julia
"""
Función que realiza todo el proceso para la obtención de las gráficas
a partir de los parámetros iniciales
Parámetros de entrada:
df      -> Ecuación diferencial
u0      -> Condiciones iniciales
minimum -> valor inicial del área por analizar
maximum -> valor final del área por analizar
"""
function solving_PDE(df,u0,minimum,maximum)
    # Creación del modelo para la ED
    model = ODEProblem(df,u0,(minimum,maximum))
    # Solucion de la ED
    x_sol,y_sol,size=solve_PDE(model)
    # Gráficas de la ED
    plot_graphics(x_sol,y_sol,size)
end
```

    solving_PDE

```julia
"""
Función de la ecuación de onda libre
"""
function df(du,u,p,y)
    # Velocidad de la onda
    k=1
    # Función de la velocidad de la onda
    du[1]=u[2]
    # Ecuación diferencial de la onda libre
    du[2]=-u[2]/y-k^2*u[1]
end
```

    df

```julia
# Posición y velocidad inicial de la onda
u0=[0.01,1]
# Área por analizar
min_value,max_value=0.1,100
# Solución y gráficas
solving_PDE(df,u0,min_value,max_value)
```

## Parte 2 - Modelo estocástico
Para la segunda parte del proyecto se propuso agregar un medio viscoso que afecte a la ecuación diferencial. Una de las maneras en las cuales de modela este tipo de fenomeno es por medio de suponer que este afecta proporcionalemente a la velocidad, por lo tanto la ecuación diferencial obtendra un nuevo parámetro el cual modificara a la ecuación diferencial de la siguiente manera:

\begin{equation}
\frac{d^2R}{dr^2} + \frac{1}{r}\frac{dR}{dr}+k^2R-p\frac{dR}{dt}=0
\end{equation}

Este parámetro sera asignado como una variable aleatoria, la cual obtendra su valor al inicio del calculo.

El esquema quedara invariante, lo que cambiara sera la función $\textit{solving_PDE}$ para que contemple este nuevo parámetro.

```julia
"""
Función que realiza todo el proceso para la obtención de las gráficas
a partir de los parámetros iniciales
Parámetros de entrada:
df      -> Ecuación diferencial
u0      -> Condiciones iniciales
minimum -> valor inicial del área por analizar
maximum -> valor final del área por analizar
p       -> parámetros de la ecuación diferencial
  p[1]  -> velocidad de la onda
  p[2]  -> constante de viscosidad del medio
"""
function solving_PDE(df,u0,minimum,maximum,p)
    # Creación del modelo para la ED
    model = ODEProblem(df,u0,(minimum,maximum),p)
    # Solucion de la ED
    x_sol,y_sol,size=solve_PDE(model)
    # Solución y gráficas
    plot_graphics(x_sol,y_sol,size)
end
```

    solving_PDE

```julia
"""
Función de onda que contempla la viscodidad del medio
"""
function df_stochastic(du,u,p,y)
    du[1]=u[2]
    du[2]=-u[2]/y-p[1]^2*u[1]-p[2]*u[2]
end
```

    df_stochastic

```julia
# Condiciones iniciales del sistema (posición y velocidad)
u0=[0.01,1]
# Valores mínimos y máximos del área
min_value,max_value=0.1,100
# Constante k de la onda, Viscosidad del medio como variable aleatoria
p=[1,rand()*rand()]
# Solución y gráficas de la ED
solving_PDE(df_stochastic,u0,min_value,max_value,p)
```

## Parte 3 - Refinación adicional
Para la tercera parte del proyecto se mantuvo la condición de la viscodad del medio como variable aleatoria y se añadio una condicion en la variable r, en la que se definen zonas en forma de anillo en las cuales la ecuación diferencial tiene tiene un valor constante. Por lo cual, la ecuación diferencial que se modelará sera la siguiente
\begin{equation}
\frac{d^2R}{dr^2}=\left\lbrace \begin{matrix}
-\frac{1}{r}\frac{dR}{dr}-k^2R-p\frac{dR}{dt} & \text{para} & r_1\geq r\geq r_2 \\
c & \text{para} & r_1<r<r_2
\end{matrix}\right.
\end{equation}

Como anteriormente se definio la función $\textit{solving_PDE}$ para que contemple la introducción de parámetros a la ecuación diferencial no es necesario volverla a definir.

```julia
"""
Función que realiza todo el proceso para la obtención de las gráficas
a partir de los parámetros iniciales
Parámetros:
  p[1]  -> velocidad de la onda
  p[2]  -> constante de viscosidad del medio
  p[3]  -> inicio del anillo
  p[4]  -> fin del anillo
  p[5]  -> valor de la ED dentro del anillo
"""
function df_plus(du,u,p,y)
    du[1]=u[2]
    if p[3]<y<p[4]
        du[2]=p[5]
    else
        du[2]=-u[2]/y-p[1]^2*u[1]-p[2]*u[2]
    end
end
```

    df_plus

```julia
u0=[0.01,1]
min_value,max_value=0.1,100
# Constante K de la onda
p=[0.1,
    # Constante de la viscosidad del medio como variable aleatoria
    rand()*rand(),
    # Inicio del anilo
    25,
    # Fin del anillo
    26,
    # Valor de la ED dentro del anillo
    0.5]
solving_PDE(df_plus,u0,min_value,max_value,p)
```

```julia

```


# Numerical Solution of Ramsey/Cass-Koopmans model

## by [Mateo Velásquez-Giraldo](https://github.com/Mv77)

This notebook implements a class representing Ramsey's growth model. Current utilities include:
- Numerically finding the consumption rule through 'time elimination', as originally implemented by Alexander Tabarrok and updated by Christopher D. Carroll in this [Wolfram Mathematica notebook](www.econ2.jhu.edu/people/ccarroll/public/LectureNotes/Growth/RamseyNumericSolve.zip)
- Drawing the phase diagram of the model.
- Simulating optimal capital dynamics from a given starting point.

A formal treatment of the exact version of the model implemented in this notebook can be found in [Christopher Carroll's graduate macroeconomics lecture notes](http://www.econ2.jhu.edu/people/ccarroll/public/LectureNotes/Growth/RamseyCassKoopmans/).

```python
# Setup
import numpy as np
import matplotlib.pyplot as plt

from scipy.integrate import odeint
from scipy import interpolate

from numpy import linalg as LA

# This is a jupytext paired notebook
# which can be executed from a terminal command line via "ipython [notebookname].py"
# But a terminal does not permit inline figures, so we need to test jupyter vs terminal
# Google "how can I check if code is executed in the ipython notebook"
def in_ipynb():
    try:
        if str(type(get_ipython())) == "<class 'ipykernel.zmqshell.ZMQInteractiveShell'>":
            return True
        else:
            return False
    except NameError:
        return False

# Determine whether to make the figures inline (for spyder or jupyter)
# vs whatever is the automatic setting that will apply if run from the terminal
if in_ipynb():
    # %matplotlib inline generates a syntax error when run from the shell
    # so do this instead
    get_ipython().run_line_magic('matplotlib', 'inline')
else:
    get_ipython().run_line_magic('matplotlib', 'auto')

# Import the plot-figure library matplotlib

import matplotlib
import matplotlib.pyplot as plt
matplotlib.rcParams['text.usetex'] = True
```

```python
# Implement the RCKmod class

class RCKmod:
    """
    A class representing Ramsey/Cass-Koopmans growth models.
    """

    def __init__(self,rho,alpha,theta,xi,delta,phi):
        """
        Inputs:
        - rho:   relative risk aversion coefficient for CRRA utility.
        - alpha: capital's share of production in Cobb-Douglas output function.
        - theta: time preference/discount rate.
        - xi:    population growth rate.
        - delta: capital depreciation rate.
        - phi:   labor productivity growth rate.
        """
        # Assign parameter values
        self.rho = rho
        self.alpha = alpha
        self.theta = theta
        self.xi = xi
        self.delta = delta
        self.phi = phi

        # Create empty consumption function
        self.cFunc = None

        # Maximum capital
        self.kmax = (1/(self.phi + self.xi + self.delta))**(1/(1-self.alpha))

        # Steady state capital
        self.kss = (alpha/(theta + xi + delta + rho*phi))**(1/(1-alpha))
        # Steady state consumption
        self.css = self.kss**alpha - (xi + delta + phi)*self.kss

        # Solve the model to create its consumption function
        self.solve()

    def output(self,k):
        """
        Cobb-Douglas normalized production function
        """
        return(k**self.alpha)

    def dcdt(self,c,k):
        """
        Consumption differential equation
        """
        dc = c/self.rho*(self.alpha*k**(self.alpha - 1) - self.theta -\
                         (self.xi + self.delta) -self.rho*self.phi)
        return(dc)

    def dkdt(self,c,k):
        """
        Capital differential equation
        """
        dk = self.output(k) - c - (self.phi + self.xi + self.delta)*k
        return(dk)

    def dcdk(self,c,k):
        """
        Differential equation for the time elimination method.
        This corresponds to dc/dk = (dc/dt)/(dk/dt)
        """
        return(self.dcdt(c,k)/self.dkdt(c,k))

    def solve(self, eps = 10**(-8), npoints = 400):
        """
        Solves for the model's consumption rule through the time elimination
        method.

        Parameters:
        - eps:     disturbance used to prevent dc/dk from becoming 0/0 at
                   the steady state value of capital.
        - npoints: number of points used on each side of the steady
                   state capital for solving the dc/dk equation.
        """
        # K ranges, avoiding kss through a small disturbance
        k_below = np.linspace(self.kss-eps,0.0001,npoints)
        k_above = np.linspace(self.kss+eps,self.kmax,npoints)
        k = np.concatenate((k_below,k_above)).flatten()

        # Solve for c on each side of the steady state capital,
        # Using the slope of the saddle path to approximate initial
        # conditions
        c_below = odeint(self.dcdk,
                         self.css - eps*self.slope_ss(), k_below)
        c_above = odeint(self.dcdk,
                         self.css + eps*self.slope_ss(), k_above)

        c = np.concatenate((c_below,c_above)).flatten()

        # Create consumption function as an interpolation of the
        # numerical solutions.
        self.cFunc = interpolate.interp1d(k,c)

    def dkdt_opt(self,k,t):
        """
        Differential equation for k assuming optimal c decisions.
        """
        return(self.dkdt(self.cFunc(k),k))

    def k_dynamics(self,k0,t):
        """
        Simulates optimal capital dynamics from a given starting point.
        Parameters:
        - t : vector of time points at which to solve for capital
        - k0: value of capital at t[0]
        """
        k = odeint(self.dkdt_opt, k0, t)
        return(k)

    def k0locus(self,k):
        """
        Returns the consumption value that leaves a given ammount of
        effective capital unchanged.
        """
        return(self.output(k) - (self.phi + self.xi + self.delta)*k)

    def phase_diagram(self, npoints = 200, arrows = False, n_arrows = 5, labels = True, legend = True):
        """
        Plots the model's phase diagram.
        - npoints:  number of ticks in the k axis.
        - arrows:   boolean to indicate whether or not to draw arrow
                    grid.
        - n_arrows: controls the number of arrows in the grid
        """

        k = np.linspace(0.01,self.kmax,npoints)

        # Create plot
        plt.figure()

        # Plot k0 locus
        plt.plot(k,self.k0locus(k),label = '$\\dot{k}=0$ locus')
        # Plot c0 locus
        plt.axvline(x = self.kss,linestyle = '--',
                    label = '$\\dot{c}=0$ locus')
        # Plot saddle path
        plt.plot(k,self.cFunc(k), label = 'Saddle path')
        # Plot steady state
        plt.plot(self.kss,self.css,'*r', label = 'Steady state')

        # Add arrows ilustrating behavior in different parts of
        # the diagram.
        # Taken from:
        # http://systems-sciences.uni-graz.at/etextbook/sw2/phpl_python.html
        if arrows:
            x = np.linspace(k[0],k[-1],n_arrows)
            y = np.linspace(self.cFunc(k[0]),self.cFunc(k[-1]),n_arrows)

            X, Y = np.meshgrid(x,y)
            dc = self.dcdt(Y,X)
            dk = self.dkdt(Y,X)

            M = (np.hypot(dk, dc))
            M[ M == 0] = 1.
            dk /= M
            dc /= M
            plt.quiver(X, Y, dk, dc, M, pivot='mid', alpha = 0.3)

        # Labels
        if labels:
            plt.title('Phase diagram and consumption rule\n(normalized by efficiency units)')
            plt.xlabel('k')
            plt.ylabel('c')
        if legend:
            plt.legend()

    def J_matrix(self,c,k):
        """
        Returns the matrix of first derivatives of the solution's dynamic system
        evaluated at the point (c,k).
        This matrix is used for linear approximations of the system around point
        (c,k).
        """

        J = np.array([[1/self.rho*(self.alpha*k**(self.alpha - 1)-\
                                   self.theta-self.xi-self.delta-self.phi),\
                       c/self.rho*\
                       self.alpha*(self.alpha - 1)*k**(self.alpha - 2)],
                      [-1,
                       self.alpha*k**(self.alpha-1) -\
                       (self.phi + self.xi +self.delta)]])

        return(J)

    def slope_ss(self):
        """
        Finds the slope of the saddle path at the steady state.
        """
        J = self.J_matrix(self.css,self.kss)

        # Find eigenvalues and eigenvectors
        w, v = LA.eig(J)
        # Find position of smallest eigenvalue
        min_eig = np.argsort(w)[0]

        # The slope of the saddle path is that
        # generated by the eigenvector of the
        # negative eigenvalue.
        slope = v[0,min_eig]/v[1,min_eig]

        return(slope)
```

## _Example_

This is a quick example of how the class is used.

An instance of the model is first created by assigning the required parameter values.

The model needs to be solved in order to find the consumption rule or 'saddle path'.

```python
# Create and solve model
RCKmodExample = RCKmod(rho = 2,alpha = 0.3,theta = 0.02,xi = 0.01,
                       delta = 0.08,phi = 0.03)
RCKmodExample.solve()

# Test the consumption rule
print('Consumption at k = %1.2f is c = %1.2f'\
      % (RCKmodExample.kss/2, RCKmodExample.cFunc(RCKmodExample.kss/2)))
```

    Consumption at k = 1.13 is c = 0.75

The model's phase diagram can then be generated.

```python
RCKmodExample.phase_diagram(arrows= True, n_arrows = 12)
```

The class can also be used to simulate the dynamics of capital given a starting point.

```python
# Create grid of time points
t = np.linspace(0,100,100)

# Find capital dynamics at the desired time points and with
# a given starting capital
k0 = 4
k = RCKmodExample.k_dynamics(k0,t)

# Plot
plt.figure()
plt.plot(t,k)
plt.axhline(y = RCKmodExample.kss,linestyle = '--',color = 'k',
            label = '$\\bar{k}$')
plt.title('Capital')
plt.xlabel('Time')
plt.legend()
plt.show()
```

With capital, the consumption rule can be used to find the dynamics of consumption.

```python
# Find consumption
c = RCKmodExample.cFunc(k)

# Plot
plt.figure()
plt.plot(t,c)
plt.axhline(y = RCKmodExample.css,linestyle = '--',color = 'k',
            label = '$\\bar{c}$')
plt.title('Consumption')
plt.xlabel('Time')
plt.legend()
plt.show()
```

# Appendix 1: finding the slope of the saddle path at the steady state

From the solution of the model, we know that the system of differential equations that describes the dynamics of $c$ and $k$ is

\begin{align}
\begin{bmatrix}
\dot{c_t}\\
\dot{k_t}
\end{bmatrix}
=
\begin{bmatrix}
f(c_t,k_t)\\
g(c_t,k_t)
\end{bmatrix}
=
\begin{bmatrix}
\frac{c_t}{\rho}(\alpha k_t^{\alpha - 1} - \theta - \xi - \delta) - \phi\\
k_t^\alpha - c_t - (\phi + \xi + \delta)*k_t
\end{bmatrix}
\end{align}

We seek to approximate this system around the steady state $(\bar{c},\bar{k})$ through

\begin{align}
\begin{bmatrix}
\dot{c_t}\\
\dot{k_t}
\end{bmatrix}
\approx
\begin{bmatrix}
f(\bar{c},\bar{k}) + f_c(\bar{c},\bar{k})(c_t - \bar{c}) + f_k(\bar{c},\bar{k})(k_t - \bar{k})\\
g(\bar{c},\bar{k}) + g_c(\bar{c},\bar{k})(c_t - \bar{c}) + g_k(\bar{c},\bar{k})(k_t - \bar{k})
\end{bmatrix}
\end{align}

For this we find the system's matrix of first derivatives

\begin{align}
J(c,k) =
\begin{bmatrix}
f_c(c,k) & f_k(c,k)\\
g_c(c,k) & g_k(c,k)
\end{bmatrix} =
\begin{bmatrix}
\frac{1}{\rho}(\alpha k^{\alpha - 1} - \theta - \xi - \delta) - \phi & \frac{c}{\rho}\alpha (\alpha-1) k^{\alpha - 2}\\
-1 & \alpha k^{\alpha - 1} - (\phi + \xi + \delta)
\end{bmatrix}
\end{align}

Given the saddle-path stability of the system, $J(c_{ss},k_{ss})$ will have a positive and a negative eigenvalue. The slope of the saddle path at the steady state capital is given by the eigenvector associated with the negative eigenvalue.

To understand why, rewrite the ODE system as

\begin{align}
\begin{bmatrix}
\dot{c_t}\\
\dot{k_t}
\end{bmatrix}
\approx
\begin{bmatrix}
f(\bar{c},\bar{k})\\
g(\bar{c},\bar{k})
\end{bmatrix}+ J(\bar{c},\bar{k})\times
\begin{bmatrix}
c_t - \bar{c}\\
k_t - \bar{k}
\end{bmatrix}.
\end{align}

Since $(\bar{c},\bar{k})$ are the steady state values, we know $f(\bar{c},\bar{k}) = g(\bar{c},\bar{k}) = 0$. Now define $\hat{c} = c - \bar{c}$ and $\hat{k} = k - \bar{k}$, and notice $\dot{c} = \dot{\hat{c}}$ and $\dot{k} = \dot{\hat{k}}$. With these facts and writting $J(\bar{c},\bar{k}) = J$ to reduce clutter, we have

\begin{align}
\begin{bmatrix}
\dot{\hat{c}_t}\\
\dot{\hat{k}_t}
\end{bmatrix}
\approx
J\times
\begin{bmatrix}
\hat{c}_t\\
\hat{k}_t
\end{bmatrix},
\end{align}

which is a first-order homogeneous ODE system (see ['Matrix differential equation' in Wikipedia](https://en.wikipedia.org/wiki/Matrix_differential_equation)). As discussed in the Wikipedia entry, these systems have solutions of the form

\begin{align}
\begin{bmatrix}
\hat{c}_t\\
\hat{k}_t
\end{bmatrix}
=
c_1 e^{\lambda_1 t}\mathbf{u_1} + c_2 e^{\lambda_2 t}\mathbf{u_2}
\end{align}

where $c_1$ and $c_2$ are constants, $\lambda_1$ and $\lambda_2$ are the eigenvalues of $J$, and $\mathbf{u_1}$ and $\mathbf{u_2}$ are their associated eigenvectors.

For this model, it can be shown that (for all reasonable parameter values) $J$ has a positive and a negative eigenvalue. Thus, assume $\lambda_1 < 0$ and $\lambda_2 > 0$.

We are interested in solutions that approximate to the steady state ($\hat{c}_t = \hat{k}_t =0$) as $t \rightarrow \infty$. For these solutions, we must set $u_2 = 0$: else, given $\lambda_2 > 0$, $e^{\lambda_2 t}\rightarrow \infty$ as $t \rightarrow \infty$ and the system diverges. Therefore, we are left with a solution of the type

\begin{align}
\begin{bmatrix}
\hat{c}_t\\
\hat{k}_t
\end{bmatrix}
=
c_1 e^{\lambda_1 t}\mathbf{u_1} =
\begin{bmatrix}
c_1 e^{\lambda_1 t} u_{1,1}\\
c_1 e^{\lambda_1 t} u_{1,2}
\end{bmatrix}.
\end{align}

Note that, given $\lambda_1<0$, as $t \rightarrow \infty$, $e^{\lambda_1 t}\rightarrow 0$ and $[\hat{c}_t,\hat{k}_t] = [0,0]$ which is precisely what we require.

From the previous solution, we know that in our linear approximation of the dynamic system around $[\hat{c}_t, \hat{k}_t] = [0,0]$, the ratio $\hat{c}_t/\hat{k}_t$ will be the constant $u_{1,1}/u_{1,2}$. Therefore, we can conclude that the slope of the tangent to the saddle path (in k-c coordinates) at the steady state capital $\bar{k}$ will be exactly $u_{1,1}/u_{1,2}$ where $\mathbf{u_1}$ is the eigenvector associated with the negative eigenvalue of the Jacobian matrix J.

# Appendix 2: Figures for Christopher D. Carroll's lecture notes

```python
# Figure RamseySSPlot

labels = ['$\phi$ low','$\phi$ high']
colors = ['red','blue']
g = [0.01,0.1]
npoints = 100

plt.figure()
for i in range(len(g)):

    # Create model
    RCKmodExample = RCKmod(rho = 2,alpha = 0.3,theta = 0.02,xi = 0.01,
                           delta = 0.08,phi = g[i])

    # K range
    k = np.linspace(0,RCKmodExample.kmax,npoints)

    # Plot k0 locus
    plt.plot(k,RCKmodExample.k0locus(k),color = colors[i])

    k_dummy = (RCKmodExample.kmax + RCKmodExample.kss) / 2

    plt.annotate('$\\dot{k}=0$, ' + labels[i],
                 xy=(k_dummy, RCKmodExample.k0locus(k_dummy)),
                 xytext=(k_dummy, RCKmodExample.k0locus(k_dummy)*1.3),
                 arrowprops = dict(color = 'black', width = 0.05,
                                   headwidth = 5, headlength = 7))
    # Plot c0 locus
    plt.axvline(x = RCKmodExample.kss,linestyle = '--',color = colors[i])

    plt.annotate('$\\dot{c} = 0$, ' + labels[i],
                 xy=(RCKmodExample.kss, RCKmodExample.css/3),
                 xytext=(RCKmodExample.kss + 2, RCKmodExample.css/3),
                 arrowprops = dict(color = 'black', width = 0.05,
                                   headwidth = 5, headlength = 7))

# Labels
plt.title('$\\dot{c}/c = 0$ and $\\dot{k} = 0$ Loci')
plt.xlabel('k')
plt.ylabel('c')
fig = plt.gcf() # Get the figure in order to save it
fig.savefig('./RamseyCassKoopmans-Figures/RamseySSPlot.png')
fig.savefig('./RamseyCassKoopmans-Figures/RamseySSPlot.pdf')

plt.show()
```

```python
# Figure RamseySaddlePlot
npoints = 100

# Create and solve model
RCKmodExample = RCKmod(rho = 2,alpha = 0.3,theta = 0.02,xi = 0.01,
                       delta = 0.08,phi = 0.03)
RCKmodExample.solve()
pd = RCKmodExample.phase_diagram(arrows= True, n_arrows = 12, labels = False)

# Set initial k for off-path trajectories
k0 = RCKmodExample.kss * 0.35

# Find values of c: one too high, one too low
c_high = RCKmodExample.cFunc(k0) * 1.25
c_low = RCKmodExample.cFunc(k0) * 0.75
init_cs = [c_low, c_high]

# Trajectories with high consumption values become problematic much faster
# than those with low consumption, thus, different time intervals are used
t_final = [20,3.4602]

for i in range(len(init_cs)):

    # Create dynamic system for c and k
    dck_dt = lambda ck, t : [RCKmodExample.dcdt(ck[0],ck[1]),
                             RCKmodExample.dkdt(ck[0],ck[1])]

    # Create time vector
    t = np.linspace(0,t_final[i],npoints)

    # Solve for c and k
    ck = odeint(dck_dt, [init_cs[i], k0], t)

    # Plot the trajectory in k-c space
    plt.plot(ck[:,1], ck[:,0], 'k')

plt.title('Transition to the Steady State')
plt.xlabel('k')
plt.ylabel('c')
fig = plt.gcf() # Get the figure in order to save it
fig.savefig('./RamseyCassKoopmans-Figures/RamseySaddlePlot.png')
fig.savefig('./RamseyCassKoopmans-Figures/RamseySaddlePlot.pdf')
plt.show()
```


# Hybrid Monte Carlo

## Affine Short Rate Models

In this notebook we analyse yield curve modelling based on affine term structure models. We start with a classical CIR model. Then we analyse initial yield curve calibration via deterministic shift extension. Finally, we also analyse the impact of square root processes on volatility smile.

```python
import sys
sys.path.append('../')  # make python find our modules
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import QuantLib as ql
```

## CIR Model Properties

As a first step we wet up a CIR model and analyse modelled yield curves and volatilities.

```python
from hybmc.models.AffineShortRateModel import CoxIngersollRossModel, quadraticExponential, cirMoments

r0         = 0.02
chi_       = 0.07
theta_     = 0.05
sigma_     = 0.07
cirModel = CoxIngersollRossModel(r0,chi_,theta_,sigma_,quadraticExponential(1.5))
```

We have a look at the *intitial* yield curve implied by the model.

```python
dT = 1.0/365.0
f = lambda t, T, rt : np.log(cirModel.zeroBondPrice(t,T,rt) / cirModel.zeroBondPrice(t,T+dT,rt)) / dT
```

```python
T = np.linspace(0.0,20.0,21)

X0 = cirModel.initialValues()
f_ = np.array([ f(0.0,T_,cirModel.r0) for T_ in T ])
curve = pd.DataFrame([ T, f_ ]).T
curve.columns = ['T', 'f(0,T)']
fig = px.line(curve, x='T', y='f(0,T)')
fig.show()
```

Next we check *future* model-implied curves.

```python
T0 = 5.0
shortRates = np.linspace(0.02, 0.04, 5)
for r in reversed(shortRates):
    f_ = np.array([ f(T0,T0+T_,r) for T_ in T ])
    fig.add_trace(go.Scatter(x=T0+T, y=f_, mode='lines', name='r=%6.4f'%r))
fig.show()
```

The humped shape looks much better compared to Hull-White model.

We are also interested in the volatility of rates.

Zero bonds are given by $P(t,T,r) = \exp(-B_{CIR}(t,T) r(t) + A_{CIR})(t,T)$. Future zero rates from $T_0$ to $T_1$ are defined also

\begin{align}
 F(t;T_0,T_0) &= \frac{1}{T_1-T_0} \log\left( \frac{P\left(t,T_0,r(t)\right)}{P\left(t,T_1,r(t)\right)} \right) \\
              &= \frac{-\left[ B_{CIR}(t,T_0) - B_{CIR}(t,T_1)\right] r(t) + A_{CIR}(t,T_0) - A_{CIR}(t,T_1) }{T_1-T_0}.
\end{align}

In particular, we get for the variance of $F(T_0,T_0,T_1)$

$$
 Var\left[ F(T_0,T_0,T_1) \right] = \left[ \frac{B_{CIR}(T_0,T_1)}{T_1-T_0} \right]^2 \cdot  Var\left[ r(T_0) \right].
$$

This yields the proxy ATM swap rate volatility
$$
  \sigma(T_0,T_1) = \underbrace{\frac{B_{CIR}(T_0,T_1)}{T_1-T_0}}_{\lambda(T_0,T_1)}
                    \underbrace{\sqrt{ \frac{Var\left[ r(T_0) \right]}{T_0} } }_{\sigma_{CIR}}
$$

```python
lambda_ = lambda T0,T1 : cirModel.ricattiAB(T0,T1,0.0,1.0)[1] / (T1 - T0)
expiryTimes = np.linspace(1.0, 10.0,10)
swapTerms = np.linspace(1.0, 10.0,10)
scalings = pd.DataFrame([ [T0, dT, lambda_(T0,T0+dT)] for T0 in expiryTimes for dT in swapTerms ],columns=['T0', 'dT', 'scaling'])
#fig = go.Figure(data=[go.Surface(x=scalings.T0,y=scalings.dT,z=scalings.scaling)])
fig = px.scatter_3d(scalings, x='T0', y='dT', z='scaling')
fig.show()
```

```python
sigma_CIR = lambda T0 : np.sqrt(cirMoments(cirModel.r0,T0,cirModel.chi(0.0),cirModel.theta(0.0),cirModel.sigma(0.0))[1] / T0)
vols = pd.DataFrame([ [T0, sigma_CIR(T0)] for T0 in expiryTimes], columns=['T0','sigma_CIR'])
fig = px.line(vols,x='T0',y='sigma_CIR')
fig.show()
```

## Yield Curve Fit

```python
import QuantLib as ql
today = ql.Settings.instance().evaluationDate
curveData = pd.DataFrame([[ 0.0,   5.0,   10.0,   20.0   ],
                          [ 0.020, 0.028,  0.033,  0.035 ]]).T
curveData.columns = ['T', 'f']
curveData['Date'] = [ today + int(t*365) for t in curveData['T'] ]
yts = ql.ForwardCurve(curveData['Date'],curveData['f'],ql.Actual365Fixed())
```

```python
fMarket = lambda T : yts.forwardRate(T,T,ql.Continuous).rate()
curve['fM(0,T)'] = [ fMarket(T) for T in curve['T'] ]
fig = go.Figure()
fig.add_trace(go.Scatter(x=curve['T'], y=curve['f(0,T)'], mode='lines', name='f(0,T)'))
fig.add_trace(go.Scatter(x=curve['T'], y=curve['fM(0,T)'], mode='lines', name='fM(0,T)'))
fig.show()
```

```python
zeroRateCir = lambda T : -np.log(cirModel.zeroBondPrice(0.0,T,cirModel.r0))/T
zeroRateYts = lambda T : -np.log(yts.discount(T)) / T
zeros = pd.DataFrame(np.linspace(0.1,20,200),columns=['T'])
zeros['CIR'] = [ zeroRateCir(T) for T in zeros['T'] ]
zeros['Yts'] = [ zeroRateYts(T) for T in zeros['T'] ]
fig = go.Figure()
fig.add_trace(go.Scatter(x=zeros['T'], y=zeros['CIR'], mode='lines', name='CIR'))
fig.add_trace(go.Scatter(x=zeros['T'], y=zeros['Yts'], mode='lines', name='Yts'))
fig.show()
```

```python
from hybmc.models.ShiftedRatesModel import ShiftedRatesModel
shiModel = ShiftedRatesModel(yts,cirModel)
```

```python
from hybmc.simulations.McSimulation import McSimulation

times = np.linspace(0.0,20.0,21)
nPaths = 2**13
seed = 3141
simCir = McSimulation(cirModel,times,nPaths,seed,showProgress=True)
simShi = McSimulation(shiModel,times,nPaths,seed,showProgress=True)
#
dT = 0.0
zcbCir = np.mean(np.array([
        [ cirModel.zeroBond(times[t],times[t]+dT,simCir.X[p,t,:],None) / cirModel.numeraire(times[t],simCir.X[p,t,:]) for t in range(len(times)) ]
        for p in range(nPaths) ]), axis=0)
zcbShi = np.mean(np.array([
        [ shiModel.zeroBond(times[t],times[t]+dT,simShi.X[p,t,:],None) / shiModel.numeraire(times[t],simShi.X[p,t,:]) for t in range(len(times)) ]
        for p in range(nPaths) ]), axis=0)
#
mcZeroCir = [ -np.log(df)/T for df,T in zip(zcbCir,times) ]
mcZeroShi = [ -np.log(df)/T for df,T in zip(zcbShi,times) ]
fig.add_trace(go.Scatter(x=times[1:], y=mcZeroCir[1:], mode='markers', name='CIR'))
fig.add_trace(go.Scatter(x=times[1:], y=mcZeroShi[1:], mode='markers', name='Yts'))
fig.show()
```

```python

```


# Прямая задача кинематики SCARA

```python
from matplotlib import pyplot as plt
from matplotlib import animation
import numpy as np
from IPython.display import HTML
%matplotlib notebook
```

```python
from kinematics import Vector, Quaternion, Transform
import graphics
```

## Решение прямой задачи кинематики для манипулятора SCARA

Манипулятор кинематической схемы SCARA обладает четыремя степенями подвижности.

Можно выделить высоту колонны, длины первого и второго звеньев.

Обобщенные координаты будут в радианах и метрах:

Обобщенная координата | Обозначение | Размерность
---|---|---
Вращение вокруг колонны | $q_0$ | радиан
Вращение в локте | $q_1$ | радиан
Вращение инструмента | $q_2$ | радиан
Перемещение инструмента | $q_3$ | метр

```python
def scara_chain(q, l, math_source=np):
    base = Transform.identity()
    column = base + Transform(
        Vector(0, 0, l[0]),
        Quaternion.from_angle_axis(q[0], Vector(0, 0, 1), math_source)
    )
    elbow = column + Transform(
        Vector(l[1], 0, 0),
        Quaternion.from_angle_axis(q[1], Vector(0, 0, 1), math_source)
    )
    tool = elbow + Transform(
        Vector(l[2], 0, 0),
        Quaternion.from_angle_axis(q[2], Vector(0, 0, 1), math_source)
    )
    flange = tool + Transform(
        Vector(0, 0, -q[3]),
        Quaternion.identity()
    )
    return [
        base,
        column,
        elbow,
        tool,
        flange
    ]
```

Зададим закон изменения обобщенных координат:

```python
def scara_q(t, total):
    omega = t / total * np.pi * 2
    return [
        np.pi / 4 * np.sin(omega),
        np.pi / 2,
        omega,
        3 + 3 * np.cos(omega)
    ]
```

Укажем длины звеньев:

```python
scara_l = [8, 4, 3]
```

```python
scara_fig = plt.figure()
ax = scara_fig.add_subplot(projection="3d")
ax.set_xlim(-6, 6); ax.set_ylim(-6, 6); ax.set_zlim(0, 12)
lines, = ax.plot([], [], [], color="#000000")
graphics.axis(ax, Transform.identity(), 2)
r, g, b = graphics.axis(ax, Transform.identity(), 1)

total = 100

def animate(frame):
    chain = scara_chain(scara_q(frame, total), scara_l)
    (x, y, z) = graphics.chain_to_points(chain)
    lines.set_data_3d(x, y, z)
    global r, g, b
    r.remove(); g.remove(); b.remove()
    r, g, b = graphics.axis(ax, chain[-1], 0.5)

animate(0)
fps = 25
scara_ani = animation.FuncAnimation(
    scara_fig,
    animate,
    frames=total,
    interval=1000.0/fps
)
```

```python
HTML(scara_ani.to_jshtml())
```

## Отображение траектории

```python
t_end = 10
step = 0.01
t = np.arange(0, t_end, step)
chain = scara_chain(scara_q(t, t_end), scara_l)
x, y, z = graphics.chain_to_points(chain)

fig = plt.figure()
ax = fig.add_subplot(projection="3d")
ax.set_xlim(-6, 6); ax.set_ylim(-6, 6); ax.set_zlim(0, 12)
ax.plot(x[2], y[2], z[2], color="#c0c0c0")
ax.plot(x[3], y[3], z[3], color="#c0c0c0")
ax.plot(x[4], y[4], z[4], color="#000000")
graphics.axis(ax, Transform.identity(), 2)
fig.show()
```

Определим диапазон изменения $Z$ координаты фланца:

```python
print(
    "Z координата менялась в диапазоне от",
    np.min(z[-1]),
    "до",
    np.max(z[-1])
)
```

## Измерение скорости движения

Численно продиффиринцируем для всех точек траектории:

```python
t_end = 10
step = 0.01
t = np.arange(0, t_end, step)
chain = scara_chain(scara_q(t, t_end), scara_l)
end = chain[-1]
velocity_x = np.diff(end.translation.x) / step
velocity_y = np.diff(end.translation.y) / step
velocity_z = np.diff(end.translation.z) / step
velocity_total = (velocity_x ** 2 + velocity_y ** 2 + velocity_z ** 2) ** 0.5

fig = plt.figure()
ax = fig.add_subplot()

ax.plot(t[:-1], velocity_x, color="#ff0000", label="$V_x$")
ax.plot(t[:-1], velocity_y, color="#00ff00", label="$V_y$")
ax.plot(t[:-1], velocity_z, color="#0000ff", label="$V_z$")
ax.plot(t[:-1], velocity_total, color="#000000", label="$V$")
fig.legend()
fig.show()
```

Оценим максимальную скорость на траектории:

```python
index_max = np.argmax(velocity_total)
print(
    "Максимальная скорость была",
    velocity_total[index_max],
    "в момент",
    index_max * step,
    "сек"
)
```

## Аналитическое решение прямой задачи кинематики

```python
import sympy as sp
```

```python
l_0, l_1, l_2 = sp.symbols("l_0, l_1, l_2")
q_0, q_1, q_2, q_3 = sp.symbols("q_0, q_1, q_2, q_3")
```

```python
flange = scara_chain([q_0, q_1, q_2, q_3], [l_0, l_1, l_2], sp)[-1]
sp.simplify(flange.translation.x)
```

```python
sp.simplify(flange.translation.y)
```

```python
sp.simplify(flange.translation.z)
```

## Самостоятельные задания

Для своего закона изменения обобщенных координат

- Оцените диапазон $X$, $Y$ и $Z$ координат для данной траектории;
- Оцените максимальную скорость по осям;
- Оцените абсолютную максимальную скорость.


# numpy と 線形代数

線形代数を学ぶには numpy と sympy の両方が必要である。

numpy でないと機械学習なのど高速な計算ができないし、sympy でないと数式処理ができない。

* 行列計算
* 行列方程式を解く
* 逆行列と行列式を計算する

# 行列計算

次の例を考える

$$
A = \begin{pmatrix}
5 & 6 & 2\\
4 & 7 & 19\\
0 & 3 & 12
\end{pmatrix}
$$

$$
B = \begin{pmatrix}
14 & -2 & 12\\
4 & 4 & 5\\
5 & 5 & 1
\end{pmatrix}
$$

numpy を使うためには import する必要がある。

```python
import numpy as np
A = np.matrix([[5, 6, 2],
               [4, 7, 19],
               [0, 3, 12]])
B = np.matrix([[14, -2, 12],
               [4, 4, 5],
               [5, 5, 1]])
print(A)
print(B)
```

    [[ 5  6  2]
     [ 4  7 19]
     [ 0  3 12]]
    [[14 -2 12]
     [ 4  4  5]
     [ 5  5  1]]

同じことを sympy でやってみると次のようになる。

```python
# 同じことを sympy でやってみると
from sympy import *
A_sympy = Matrix([[5, 6, 2],
               [4, 7, 19],
               [0, 3, 12]])
B_sympy = Matrix([[14, -2, 12],
               [4, 4, 5],
               [5, 5, 1]])
display(A_sympy)
display(B_sympy)
```

$\displaystyle \left[\begin{matrix}5 & 6 & 2\\4 & 7 & 19\\0 & 3 & 12\end{matrix}\right]$

$\displaystyle \left[\begin{matrix}14 & -2 & 12\\4 & 4 & 5\\5 & 5 & 1\end{matrix}\right]$

次の計算をする

* $5A$
* $A ^ 3$
* $A + B$;
* $A - B$;
* $AB$

```python
print(A)
print(5 * A)
print(A**2)
print(A**3)
print(A+B)
print(A-B)
print(A*B)

```

    [[ 5  6  2]
     [ 4  7 19]
     [ 0  3 12]]
    [[25 30 10]
     [20 35 95]
     [ 0 15 60]]
    [[ 49  78 148]
     [ 48 130 369]
     [ 12  57 201]]
    [[ 557 1284 3356]
     [ 760 2305 6994]
     [ 288 1074 3519]]
    [[19  4 14]
     [ 8 11 24]
     [ 5  8 13]]
    [[ -9   8 -10]
     [  0   3  14]
     [ -5  -2  11]]
    [[104  24  92]
     [179 115 102]
     [ 72  72  27]]

# いまここ

---
練習問題 $\quad$  Compute $A ^ 2 - 2 A + 3$ with:

$$A =
\begin{pmatrix}
1 & -1\\
2 & 1
\end{pmatrix}
$$

---

## Solving Matrix equations

We can use Numpy to (efficiently) solve large systems of equations of the form:

$$Ax=b$$

Let us illustrate that with:

$$
A = \begin{pmatrix}
5 & 6 & 2\\
4 & 7 & 19\\
0 & 3 & 12
\end{pmatrix}
$$

$$
b = \begin{pmatrix}
-1\\
2\\
1
\end{pmatrix}
$$

```python
A = np.matrix([[5, 6, 2],
               [4, 7, 19],
               [0, 3, 12]])
b = np.matrix([[-1], [2], [1]])
```

We use the `linalg.solve` command:

```python
x = np.linalg.solve(A, b)
x
```

    matrix([[ 0.45736434],
            [-0.62790698],
            [ 0.24031008]])

We can verify our result:

```python
A * x
```

    matrix([[-1.],
            [ 2.],
            [ 1.]])

---
練習問題 $\quad$  行列方程式 $Bx=b$ を解く。

---
# 逆行列と行列式を求める

逆行列は次のようにして求められる。

```python
# 逆行列は inv を使って求める
Ainv = np.linalg.inv(A)
Ainv
```

    matrix([[-0.20930233,  0.51162791, -0.7751938 ],
            [ 0.37209302, -0.46511628,  0.6744186 ],
            [-0.09302326,  0.11627907, -0.08527132]])

$A^{-1}A=\mathbb{1}$ となることを確認する。

```python
A * Ainv
```

    matrix([[  1.00000000e+00,   2.77555756e-17,   3.05311332e-16],
            [ -2.08166817e-16,   1.00000000e+00,  -2.08166817e-16],
            [  5.55111512e-17,  -5.55111512e-17,   1.00000000e+00]])

若干見にくいが、[[1,0,0],[0,1,0],[0,0,1]] であることがわかる。

行列式は次のように求める。

```python
# 行列式
np.linalg.det(A)
```

    -129.00000000000009

---

練習問題 $\quad$ 行列 $B$ の逆行列と行列式を求める。