###### Content under Creative Commons Attribution license CC-BY 4.0, code under MIT license (c)2014 L.A. Barba, C.D. Cooper, G.F. Forsyth.

# Riding the wave

## Convection problems

Welcome to *Riding the wave: Convection problems*, the third module of  ["Practical Numerical Methods with Python"](http://openedx.seas.gwu.edu/courses/GW/MAE6286/2014_fall/about).

In the [first module](https://github.com/numerical-mooc/numerical-mooc/tree/master/lessons/01_phugoid), we learned about numerical integration methods for the solution of ordinary differential equations (ODEs). The [second module](https://github.com/numerical-mooc/numerical-mooc/tree/master/lessons/02_spacetime) introduced the finite difference method for numerical solution of partial differential equations (PDEs), where we need to discretize both *space* and *time*.

This module explores the convection equation in more depth, applied to a traffic-flow problem.  We already introduced convection in [Lesson 1 of Module 2](http://nbviewer.ipython.org/github/numerical-mooc/numerical-mooc/blob/master/lessons/02_spacetime/02_01_1DConvection.ipynb). This hyperbolic equation is very interesting because the solution can develop *shocks*, or regions with very high gradient, which are difficult to resolve well with numerical methods.

We will start by introducing the concept of a conservation law, closely related to the convection equation.  Then we'll explore different numerical schemes and how they perform when shocks are present.

## Conservation laws

You know from (non relativistic) physics that mass is _conserved_. This is one example of a conserved quantity, but there are others (like momentum and energy) and they all obey a _conservation law_. Let's start with the more intuitive case of conservation of mass.

### Conservation of mass

In any closed system, we know that the mass $M$ in the system does not change, which we can write: $\frac{D\,M}{Dt} =0$. When we change the point of view from a closed system to what engineers call a _control volume_, mass can move in and out of the volume and conservation of mass is now expressed by:

Let's imagine the control volume as a tiny cylinder of cross-section dA and length dx, like in the sketch below.

#### Figure 1. Tiny control volume in the shape of a cylinder.

If we represent the mass density by $\rho$, then mass is equal to $\rho\times$ volume. For simplicity, let's assume that mass flows in or out of the control volume only in one direction, say, the $x$-direction. Express the 1D velocity component by $u$, and conservation of mass for the control volume is translated to a mathematical expression as follows:

\begin{equation}
\frac{\partial}{\partial t}\int_{\text{cv}}\rho \, dV + \int_{\text{cs}}\rho \, u\, dA =0
\end{equation}

where "cv" stands for control volume and "cs" stands for control surface. The first term represents the rate of change of mass in the control volume, and the second term is the rate of flow of mass, with velocity $u$, accross the control surface.

Since the control volume is very small, we can take, to leading order, $\rho$ as a uniform quantity inside it, and the first term in equation (1) can be simplified to the time derivative of density multiplied by the volume of the tiny cylinder, $dAdx$:

$$\frac{\partial}{\partial t}\int_{\text{cv}}\rho \, dV \rightarrow \frac{\partial \rho}{\partial t} dAdx$$

Now, for the second term in equation (1), we have to do a little more work. The quantity inside the integral is now $\rho u$ and, to leading order, we have to take into consideration that this quantity can change in the distance $dx$. Take $\rho u$ to be the value in the center of the cylinder. Then the flow of mass on each side is illustrated in the figure below, where we use a Taylor expansion of the quantity $\rho u$ around the center of the control volume (to first order).

#### Figure 2. Flux terms on the control surfaces.

Subtracting the negative flux on the left to the positive flux on the right, we arrive at the total flux of mass accross the control surfaces, the second term in equation (1):

$$ \int_{\text{cs}}\rho \, u\, dA \rightarrow \frac{\partial}{\partial x}(\rho u) dAdx$$

We can now put together the equation of conservation of mass for the tiny cylindrical control volume, which after diving by $dAdx$ is:

\begin{equation}
\frac{\partial \rho}{\partial t} + \frac{\partial}{\partial x}(\rho u)=0
\end{equation}

This is the 1D mass conservation equation in differential form. If we take $u$ to be a constant and take it out of the spatial derivative this equation looks the same as the first PDE we studied: the linear convection equation in [Lesson 1 of Module 2](http://nbviewer.ipython.org/github/numerical-mooc/numerical-mooc/blob/master/lessons/02_spacetime/02_01_1DConvection.ipynb).
But in the form shown above, it is a typical _conservation law_. The term under the spatial derivative is called the _flux_, for reasons that should be clear from our discussion above: it represents amounts of the conserved quantity flowing across the boundary of the control volume.

##### Dig deeper

You can follow the derivation of the full three-dimensional equation of conservation of mass for a flow on this screencast by Prof. Barba (duration 12:47).

```python
from IPython.display import YouTubeVideo
YouTubeVideo('35unQgSaT88')
```

### General conservation laws

All conservation laws express the same idea: the variation of a conserved quantity inside a control volume is due to the total flux of that quantity crossing the boundary surface (plus possibly the effect of any sources inside the volume, but let's ignore those for now).

The _flux_ is a fundamental concept in conservation laws: it represents the amount of the quantity that crosses a surface per unit time. Our discussion above was limited to flow in one dimension, but in general the flux has any direction and is a vector quantity. Think about this: if the direction of flow is parallel to the surface, then no quantity comes in or out. We really only care about the component of flux perpendicular to the surface. Mathematically, for a vector flux $\vec{F}$, the amount of the conserved quantity crossing a small surface element is:

$$\vec{F}\cdot d\vec{A}$$

where $d\vec{A}$ points in the direction of the outward normal to the surface. A general conservation law for a quantity $e$ is thus (still ignoring possible sources):

\begin{equation}
\frac{\partial}{\partial t}\int_{\text{cv}}e \, dV + \oint_{\text{cs}}\vec{F}\cdot d\vec{A} =0
\end{equation}

To obtain a differential form of this conservation equation, we can apply the theorem of Gauss to the second integral, which brings the gradient of $\vec{F}$ into play. One way to recognize a conservation law in differential form is that the _fluxes appear only under the gradient operator_.

Recall the non-linear convection equation from [Lesson 1 of Module 2](http://nbviewer.ipython.org/github/numerical-mooc/numerical-mooc/blob/master/lessons/02_spacetime/02_01_1DConvection.ipynb). It was:

\begin{equation}\frac{\partial u}{\partial t} + u \frac{\partial u}{\partial x} = 0\end{equation}

If we look closely at the spatial derivative, we can rewrite this equation as

\begin{equation}\frac{\partial u}{\partial t} + \frac{\partial}{\partial x} \left(\frac{u^2}{2} \right) = 0, \end{equation}

which is the *conservation form* of the non-linear convection equation, with flux $F=\frac{u^2}{2}$.

## Traffic flow model

We've all experienced it: as rush hour approaches certain roads in or out of city centers start getting full of cars, and the speed of travel can reduce to a crawl. Sometimes, the cars stop altogether. If you're a driver, you know that the more cars on the road, the slower your trip will flow.

Traffic flow models seek to describe these everyday experiences with mathematics, to help engineers design better road systems.

Let's review the [Lighthill-Whitham-Richards](http://en.wikipedia.org/wiki/Macroscopic_traffic_flow_model) traffic model that was offered as an exercise at the end of Module 2. This model considers cars with a continuous *traffic density* (average number of cars per unit length of road) rather than keeping track of them individually. If $\rho(x)=0$, there are no cars at that point $x$ of the road.  If $\rho(x) = \rho_{\rm max}$, traffic is literally bumper to bumper.

If the number of cars on a bounded stretch of road changes, it means that cars are entering or leaving the road somehow. _Traffic density obeys a conservation law_ (!) where the flux is the number of cars leaving the road per unit time. It is given by $F=\rho u$—as with mass conservation, flux equals density times velocity. But don't forget your experience on the road: the speed of travel depends on the car density. Here, $u$ refers not to the speed of each individual car, but to the _traffic speed_ at a given point of the road.

You know from experience that with more cars on the road, the speed of travel decreases. It is also true that if you are traveling at fast speed, you are advised to leave a larger gap with cars ahead. These two considerations lead us to propose a monotonically decreasing $u=u(\rho)$ function. As a first approximation, we may consider the linear function:

\begin{equation}u(\rho) = u_{\rm max} \left(1-\frac{\rho}{\rho_{\rm max}}\right)\end{equation}

#### Figure 3. Traffic speed vs. traffic density.

The linear model of the behavior of drivers satisfies these experimental observations:
1.  All drivers will approach a maximum velocity $u_{max}$ when the road is empty.
2.  If the road is completely jampacked ($\rho \rightarrow \rho_{max}$), velocity goes to zero.

That seems like a reasonable approximation of reality!

Applying a conservation law to the vehicle traffic, the traffic density will obey the following transport equation:

\begin{equation} \frac{\partial \rho}{\partial t} + \frac{\partial F}{\partial x} = 0 \end{equation}

where $F$ is the *traffic flux*, which in the linear traffic-speed model is given by:

\begin{equation} F = \rho u_{\rm max} \left(1-\frac{\rho}{\rho_{\rm max}}\right).
\end{equation}

We can now use our numerical kung-fu to solve some interesting traffic situations, and check if our simple model gives realistic results!

### Green light!

Let's say that we are examining a road of length $4$ where the speed limit is $u_{\rm max}=1$, fitting $10$ cars per unit length $(\rho_{\rm max}=10)$. Now, imagine we have an intersection with a red light at $x=2$.  At the stoplight, traffic is bumper-to-bumper, and the traffic density decreases linearly to zero as we approach the beginning of our road. Ahead of the stoplight, the road is clear.

Mathematically, we can represent this situation with the following initial condition:

\begin{equation}\rho(x,0) = \left\{ \begin{array}{cc}
\rho_{\rm max}\frac{x}{2} & 0 \leq x < 2  \\
0 & 2 \leq x \leq 4 \\ \end{array} \right.\end{equation}

Let's see what a plot of that looks like.

```python
%matplotlib inline
import numpy
from matplotlib import pyplot
from matplotlib import rcParams
rcParams['font.family'] = 'serif'
rcParams['font.size'] = 16
```

```python
def rho_green_light(nx, rho_light):
    """Computes "green light" initial condition with shock, and linear distribution behind

    Parameters
    ----------
    nx        : int
        Number of grid points in x
    rho_light : float
        Density of cars at stoplight

    Returns
    -------
    rho: array of floats
        Array with initial values of density
    """
    rho = numpy.arange(nx)*2./nx*rho_light  # Before stoplight
    rho[int((nx-1)/2):] = 0

    return rho
```

```python
#Basic initial condition parameters
#defining grid size, time steps
nx = 81
nt = 30
dx = 4.0/(nx-1)

x = numpy.linspace(0,4,nx)

rho_max = 10.
u_max = 1.
rho_light = 10.

rho = rho_green_light(nx, rho_light)
```

```python
pyplot.plot(x, rho, color='#003366', ls='-', lw=3)
pyplot.ylabel('Traffic density')
pyplot.xlabel('Distance')
pyplot.ylim(-0.5,11.);
```

**How does the traffic behave once the light turns green?** Cars should slowly start moving forward: the density profile should move to the right. Let's see if the numerical solution agrees with that!

Before we start, let's define a function to calculate the traffic flux. We'll use it in each time step of our numerical solution.

```python
def computeF(u_max, rho_max, rho):
    """Computes flux F=V*rho

    Parameters
    ----------
    u_max  : float
        Maximum allowed velocity
    rho    : array of floats
        Array with density of cars at every point x
    rho_max: float
        Maximum allowed car density

    Returns
    -------
    F : array
        Array with flux at every point x
    """
    return u_max*rho*(1-rho/rho_max)
```

### Forward-time/backward-space

Start by using a forward-time, backward-space scheme, like you used in Module 2. The discretized form of our traffic model is:

\begin{equation}
\frac{\rho^{n+1}_i- \rho^n_{i}}{\Delta t}+ \frac{F^n_{i}-F^n_{i-1}}{\Delta x}=0
\end{equation}

Like before, we'll step in time via a for-loop, and we'll operate on all spatial points simultaneously via array operations. In each time step, we also need to call the function that computes the flux. Here is a function that implements in code the forward-time/backward-space difference scheme:

```python
def ftbs(rho, nt, dt, dx, rho_max, u_max):
    """ Computes the solution with forward in time, backward in space

    Parameters
    ----------
    rho    : array of floats
            Density at current time-step
    nt     : int
            Number of time steps
    dt     : float
            Time-step size
    dx     : float
            Mesh spacing
    rho_max: float
            Maximum allowed car density
    u_max  : float
            Speed limit

    Returns
    -------
    rho_n : array of floats
            Density after nt time steps at every point x
    """

    #initialize our results array with dimensions nt by nx
    rho_n = numpy.zeros((nt,len(rho)))
    #copy the initial u array into each row of our new array
    rho_n[0,:] = rho.copy()

    for t in range(1,nt):
        F = computeF(u_max, rho_max, rho)
        rho_n[t,1:] = rho[1:] - dt/dx*(F[1:]-F[:-1])
        rho_n[t,0] = rho[0]
        rho = rho_n[t].copy()

    return rho_n
```

We're all good to go!

**Note:** The code above saves the complete traffic density at each time-step—we'll use that in a second to create animations with our results.

Running the numerical solution is easy now: we just need to call the function for evolving the initial condition with the forward-time/backward-space scheme.

```python
sigma = 1.
dt = sigma*dx

rho_n = ftbs(rho, nt, dt, dx, rho_max, u_max)
```

Let's see how that looks.

Below is another way to use the `matplotlib` animation routines.  Instead of computing and animating at the same time, we can pass the results of our calculation, stored in the array `rho_n`, as individual frames.

It doesn't make much of a difference for a computationally light example like this one, but if you want to animate more complicated problems, it's nice to separate the computation from the visualization.  You don't want to re-run your entire simulation just so you can update the line style!

```python
from matplotlib import animation
from IPython.display import HTML
```

```python
fig = pyplot.figure();
ax = pyplot.axes(xlim=(0,4),ylim=(-.5,11.5),xlabel=('Distance'),ylabel=('Traffic density'));
line, = ax.plot([],[],color='#003366', lw=2);

def animate(data):
    x = numpy.linspace(0,4,nx)
    y = data
    line.set_data(x,y)
    return line,

anim = animation.FuncAnimation(fig, animate, frames=rho_n, interval=50)
```

```python
HTML(anim.to_html5_video())
```

**Yikes! The solution is blowing up.** This didn't happen in your traffic-flow exercise (coding assignment) for Module 2! (Thankfully.) What is going on? Is there a bug in the code?

No need to panic. Let's take a closer look at the equation we are solving:

\begin{equation}\frac{\partial \rho}{\partial t} + \frac{\partial F}{\partial x} = 0\end{equation}

Using the chain rule of calculus, rewrite is as follows:

\begin{equation} \frac{\partial \rho}{\partial t} + \frac{\partial F}{\partial \rho} \frac{\partial \rho}{\partial x} = 0\end{equation}

This form of the equation looks like the nonlinear convection equation from [Lesson 1 of Module 2](http://nbviewer.ipython.org/github/numerical-mooc/numerical-mooc/blob/master/lessons/02_spacetime/02_01_1DConvection.ipynb), right? This is a wave equation where the wave speed is $u_{\rm wave} = \frac{\partial F}{\partial\rho}$. That term is:

\begin{equation}u_{\rm wave} = \frac{\partial F}{\partial \rho} = u_{\rm max} \left( 1-2\frac{\rho}{\rho_{\rm max}} \right).\end{equation}

See how the wave speed changes sign at $\rho = \rho_{\rm max}/2$? That means that for the initial conditions given for the green-light problem, the part of the wave under $\rho = \rho_{\rm max}/2$ will want to move right, whereas the part of the wave over this mark, will move left!

There is no real problem with that in terms of the model, but a scheme that is backward in space is *unstable* for negative values of the wave speed.

## Upwind schemes

Maybe you noticed that the backward-space discretization is spatially biased: we include the points $i$ and $i-1$ in the formula. Look again at the stencil and you'll see what we mean.

#### Figure 4. Stencil of forward-time/backward-space.

In fact, the spatial bias was meant to be in the direction of propagation of the wave—this was true when we solved the convection equation (with positive wave speed $c$), but now we have some problems. Discretization schemes that are biased in the direction that information propagates are called _upwind schemes_.

Remember when we discussed the characteristic lines for the linear convection equation in [lesson 1 of the previous module](http://nbviewer.ipython.org/github/numerical-mooc/numerical-mooc/blob/wave_dev/lessons/02_spacetime/02_01_1DConvection.ipynb)? Compare the sketch of the characteristic lines with the stencil above. The point is that there is an inherent directionality in the physics, and we want the numerical scheme to have the same directionality. This is one example of _choosing an appropriate scheme_ for the physical problem.

If we wanted to solve the convection equation with negative wave speed, $c<0$, we would need a spatial bias "slanting left," which we would obtain by using the points $i$ and $i+1$ in the formula.

But if we have waves traveling in both directions, we are in a bit of a bind. One way to avoid this problem with our traffic flow model is to simply use an initial condition that doesn't produce negative speed. This should work. But later we will learn about other numerical schemes that are able to handle waves in both directions.

Just for a sanity check, let's try the forward-time/backward-space scheme with the initial conditions

\begin{equation}\rho(x,0) = \left\{ \begin{array}{cc}
2.5 x & 0 \leq x < 2  \\
0 & 2 \leq x \leq 4 \\ \end{array} \right.\end{equation}

If all values of $\rho \leq \rho_{\rm max}/2$, then $\frac{\partial F}{\partial \rho}$ is positive everywhere. For these conditions, our forward-time/backward-space scheme shouldn't have any trouble, as all wave speeds are positive.

```python
rho_light = 5.
nt = 40
rho = rho_green_light(nx, rho_light)
rho_n = ftbs(rho, nt, dt, dx, rho_max, u_max)

anim = animation.FuncAnimation(fig, animate, frames=rho_n, interval=50)
HTML(anim.to_html5_video())
```

Phew!  It works! Try this out yourself with different initial conditions.

Also, you can easily create a new function `ftfs` to do a forward-time/forward-space scheme, which is stable for negative wave speeds. Unfortunately, forward in space is unstable for positive wave speeds.  If you don't want it blowing up, make sure the wave speed is negative everywhere: $u_{\rm wave} = \frac{\partial F}{\partial \rho} < 0 \ \forall \ x$.

Look at that solution again, and you'll get some nice insights of the real physical problem. See how on the trailing edge, a shock is developing? In the context of the traffic flow problem, a shock is a sign of a traffic jam: a region where traffic is heavy and slow next to a region that is free of cars. In the initial condition, the cars in the rear end of the triangle see a mostly empty road (traffic density is low!).  They see an empty road and speed up, accordingly.  The cars in the peak of the triangle are moving pretty slowly because traffic density is higher there.  Eventually the cars that started in the rear will catch up with the rest and form a traffic jam.

## Beware the CFL!

[Lesson 2 of Module 2](http://nbviewer.ipython.org/github/numerical-mooc/numerical-mooc/blob/wave_dev/lessons/02_spacetime/02_02_CFLCondition.ipynb) discusses the CFL condition for the linear convection equation. To refresh your memory, for a constant wave speed $u_{\rm wave} = c$:

\begin{equation} \sigma = c\frac{\Delta t}{\Delta x} < 1.\end{equation}

What happens for non-linear equations? The wave speed is space- and time-dependent, $u_{\rm wave} = u_{\rm wave}(x,t)$, and the CFL condition needs to apply for every point in space, at every instant of time. We just need $\sigma>1$ in one spot, for the whole solution to blow up!

Let's generalize the CFL condition to

\begin{equation} \sigma = \max\left[ \left| u_{\rm wave} \right| \frac{\Delta t}{\Delta x} \right] < 1.\end{equation}

which in our case is

\begin{equation} \sigma = \max\left[ u_{\rm max} \left| 1-\frac{2 \rho}{\rho_{\rm max}} \right|  \frac{\Delta t}{\Delta x} \right] < 1.\end{equation}

Here, the closer $\rho$ is to zero, the more likely it is to be unstable.

### Green light and CFL

We know that the green-light problem with density at the stop light $\rho = \rho_{\rm light} = 4$ is stable using a forward-time/backward -space scheme. Earlier, we used $u_{\rm max} = 1$, and $\Delta t/\Delta x=1$, which gives a CFL $= 1$, when $\rho = 0$.

What if we change the conditions slightly, say $u_{\rm max} = 1.1$?

```python
rho_light = 4.
u_max = 1.1
nt = 40
rho = rho_green_light(nx, rho_light)
rho_n = ftbs(rho, nt, dt, dx, rho_max, u_max)

anim = animation.FuncAnimation(fig, animate, frames=rho_n, interval=50)
HTML(anim.to_html5_video())
```

That failed miserably! Only by changing $u_{\rm max}$ to $1.1$, even an algorithm that we know is stable for this problem, fails. Since we kept $\Delta t/\Delta x=1$, the CFL number for $\rho=0$ is $1.1$. See where the instability begins? Beware the CFL!

## References

* Neville D. Fowkes and John J. Mahony, *"An Introduction to Mathematical Modelling,"* Wiley & Sons, 1994. Chapter 14: Traffic Flow.

* M. J. Lighthill and G. B. Whitham (1955), On kinematic waves. II. Theory of traffic flow and long crowded roads, _Proc. Roy. Soc. A_, Vol. 229, pp. 317–345. [PDF from amath.colorado.edu](https://amath.colorado.edu/sites/default/files/2013/09/1710796241/PRSA_Lighthill_1955.pdf), checked Oct. 14, 2014. [Original source](http://rspa.royalsocietypublishing.org/content/229/1178/317.short) on the Royal Society site.

---

###### The cell below loads the style of the notebook.

```python
from IPython.core.display import HTML
css_file = '../../styles/numericalmoocstyle.css'
HTML(open(css_file, "r").read())
```

<link href='http://fonts.googleapis.com/css?family=Alegreya+Sans:100,300,400,500,700,800,900,100italic,300italic,400italic,500italic,700italic,800italic,900italic' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Arvo:400,700,400italic' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=PT+Mono' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Shadows+Into+Light' rel='stylesheet' type='text/css'>
<link href='http://fonts.googleapis.com/css?family=Nixie+One' rel='stylesheet' type='text/css'>
<link href='https://fonts.googleapis.com/css?family=Source+Code+Pro' rel='stylesheet' type='text/css'>
<style>

@font-face {
    font-family: "Computer Modern";
    src: url('http://mirrors.ctan.org/fonts/cm-unicode/fonts/otf/cmunss.otf');
}

#notebook_panel { /* main background */
    background: rgb(245,245,245);
}

div.cell { /* set cell width */
    width: 750px;
}

div #notebook { /* centre the content */
    background: #fff; /* white background for content */
    width: 1000px;
    margin: auto;
    padding-left: 0em;
}

#notebook li { /* More space between bullet points */
    margin-top:0.8em;
}

/* draw border around running cells */
div.cell.border-box-sizing.code_cell.running {
    border: 1px solid #111;
}

/* Put a solid color box around each cell and its output, visually linking them*/
div.cell.code_cell {
    background-color: rgb(256,256,256);
    border-radius: 0px;
    padding: 0.5em;
    margin-left:1em;
    margin-top: 1em;
}

div.text_cell_render{
    font-family: 'Alegreya Sans' sans-serif;
    line-height: 140%;
    font-size: 125%;
    font-weight: 400;
    width:600px;
    margin-left:auto;
    margin-right:auto;
}

/* Formatting for header cells */
.text_cell_render h1 {
    font-family: 'Nixie One', serif;
    font-style:regular;
    font-weight: 400;
    font-size: 45pt;
    line-height: 100%;
    color: rgb(0,51,102);
    margin-bottom: 0.5em;
    margin-top: 0.5em;
    display: block;
}

.text_cell_render h2 {
    font-family: 'Nixie One', serif;
    font-weight: 400;
    font-size: 30pt;
    line-height: 100%;
    color: rgb(0,51,102);
    margin-bottom: 0.1em;
    margin-top: 0.3em;
    display: block;
}

.text_cell_render h3 {
    font-family: 'Nixie One', serif;
    margin-top:16px;
    font-size: 22pt;
    font-weight: 600;
    margin-bottom: 3px;
    font-style: regular;
    color: rgb(102,102,0);
}

.text_cell_render h4 {    /*Use this for captions*/
    font-family: 'Nixie One', serif;
    font-size: 14pt;
    text-align: center;
    margin-top: 0em;
    margin-bottom: 2em;
    font-style: regular;
}

.text_cell_render h5 {  /*Use this for small titles*/
    font-family: 'Nixie One', sans-serif;
    font-weight: 400;
    font-size: 16pt;
    color: rgb(163,0,0);
    font-style: italic;
    margin-bottom: .1em;
    margin-top: 0.8em;
    display: block;
}

.text_cell_render h6 { /*use this for copyright note*/
    font-family: 'PT Mono', sans-serif;
    font-weight: 300;
    font-size: 9pt;
    line-height: 100%;
    color: grey;
    margin-bottom: 1px;
    margin-top: 1px;
}

.CodeMirror{
    font-family: "Source Code Pro";
    font-size: 90%;
}

.alert-box {
    padding:10px 10px 10px 36px;
    margin:5px;
}

.success {
    color:#666600;
    background:rgb(240,242,229);
}
</style>


# Batch Normalization
One way to make deep networks easier to train is to use more sophisticated optimization procedures such as SGD+momentum, RMSProp, or Adam. Another strategy is to change the architecture of the network to make it easier to train.
One idea along these lines is batch normalization which was proposed by [1] in 2015.

The idea is relatively straightforward. Machine learning methods tend to work better when their input data consists of uncorrelated features with zero mean and unit variance. When training a neural network, we can preprocess the data before feeding it to the network to explicitly decorrelate its features; this will ensure that the first layer of the network sees data that follows a nice distribution. However, even if we preprocess the input data, the activations at deeper layers of the network will likely no longer be decorrelated and will no longer have zero mean or unit variance since they are output from earlier layers in the network. Even worse, during the training process the distribution of features at each layer of the network will shift as the weights of each layer are updated.

The authors of [1] hypothesize that the shifting distribution of features inside deep neural networks may make training deep networks more difficult. To overcome this problem, [1] proposes to insert batch normalization layers into the network. At training time, a batch normalization layer uses a minibatch of data to estimate the mean and standard deviation of each feature. These estimated means and standard deviations are then used to center and normalize the features of the minibatch. A running average of these means and standard deviations is kept during training, and at test time these running averages are used to center and normalize features.

It is possible that this normalization strategy could reduce the representational power of the network, since it may sometimes be optimal for certain layers to have features that are not zero-mean or unit variance. To this end, the batch normalization layer includes learnable shift and scale parameters for each feature dimension.

[1] [Sergey Ioffe and Christian Szegedy, "Batch Normalization: Accelerating Deep Network Training by Reducing
Internal Covariate Shift", ICML 2015.](https://arxiv.org/abs/1502.03167)

```python
# As usual, a bit of setup
import time
import numpy as np
import matplotlib.pyplot as plt
from cs231n.classifiers.fc_net import *
from cs231n.data_utils import get_CIFAR10_data
from cs231n.gradient_check import eval_numerical_gradient, eval_numerical_gradient_array
from cs231n.solver import Solver

%matplotlib inline
plt.rcParams['figure.figsize'] = (10.0, 8.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

# for auto-reloading external modules
# see http://stackoverflow.com/questions/1907993/autoreload-of-modules-in-ipython
%load_ext autoreload
%autoreload 2

def rel_error(x, y):
    """ returns relative error """
    return np.max(np.abs(x - y) / (np.maximum(1e-8, np.abs(x) + np.abs(y))))

def print_mean_std(x,axis=0):
    print('  means: ', x.mean(axis=axis))
    print('  stds:  ', x.std(axis=axis))
    print()
```

```python
# Load the (preprocessed) CIFAR10 data.
data = get_CIFAR10_data()
for k, v in data.items():
  print('%s: ' % k, v.shape)
```

    X_train:  (49000, 3, 32, 32)
    y_train:  (49000,)
    X_val:  (1000, 3, 32, 32)
    y_val:  (1000,)
    X_test:  (1000, 3, 32, 32)
    y_test:  (1000,)

## Batch normalization: forward
In the file `cs231n/layers.py`, implement the batch normalization forward pass in the function `batchnorm_forward`. Once you have done so, run the following to test your implementation.

Referencing the paper linked to above in [1] may be helpful!

```python
# Check the training-time forward pass by checking means and variances
# of features both before and after batch normalization

# Simulate the forward pass for a two-layer network
np.random.seed(231)
N, D1, D2, D3 = 200, 50, 60, 3
X = np.random.randn(N, D1)
W1 = np.random.randn(D1, D2)
W2 = np.random.randn(D2, D3)
a = np.maximum(0, X.dot(W1)).dot(W2)

print('Before batch normalization:')
print_mean_std(a,axis=0)

gamma = np.ones((D3,))
beta = np.zeros((D3,))
# Means should be close to zero and stds close to one
print('After batch normalization (gamma=1, beta=0)')
a_norm, _ = batchnorm_forward(a, gamma, beta, {'mode': 'train'})
print_mean_std(a_norm,axis=0)

gamma = np.asarray([1.0, 2.0, 3.0])
beta = np.asarray([11.0, 12.0, 13.0])
# Now means should be close to beta and stds close to gamma
print('After batch normalization (gamma=', gamma, ', beta=', beta, ')')
a_norm, _ = batchnorm_forward(a, gamma, beta, {'mode': 'train'})
print_mean_std(a_norm,axis=0)
```

    Before batch normalization:
      means:  [ -2.3814598  -13.18038246   1.91780462]
      stds:   [27.18502186 34.21455511 37.68611762]

    After batch normalization (gamma=1, beta=0)
      means:  [ 1.33226763e-17 -3.94129174e-17  3.29597460e-17]
      stds:   [0.99999999 1.         1.        ]

    After batch normalization (gamma= [1. 2. 3.] , beta= [11. 12. 13.] )
      means:  [11. 12. 13.]
      stds:   [0.99999999 1.99999999 2.99999999]

```python
# Check the test-time forward pass by running the training-time
# forward pass many times to warm up the running averages, and then
# checking the means and variances of activations after a test-time
# forward pass.

np.random.seed(231)
N, D1, D2, D3 = 200, 50, 60, 3
W1 = np.random.randn(D1, D2)
W2 = np.random.randn(D2, D3)

bn_param = {'mode': 'train'}
gamma = np.ones(D3)
beta = np.zeros(D3)

for t in range(50):
  X = np.random.randn(N, D1)
  a = np.maximum(0, X.dot(W1)).dot(W2)
  batchnorm_forward(a, gamma, beta, bn_param)

bn_param['mode'] = 'test'
X = np.random.randn(N, D1)
a = np.maximum(0, X.dot(W1)).dot(W2)
a_norm, _ = batchnorm_forward(a, gamma, beta, bn_param)

# Means should be close to zero and stds close to one, but will be
# noisier than training-time forward passes.
print('After batch normalization (test-time):')
print_mean_std(a_norm,axis=0)
```

    After batch normalization (test-time):
      means:  [-0.03927354 -0.04349152 -0.10452688]
      stds:   [1.01531428 1.01238373 0.97819988]

## Batch normalization: backward
Now implement the backward pass for batch normalization in the function `batchnorm_backward`.

To derive the backward pass you should write out the computation graph for batch normalization and backprop through each of the intermediate nodes. Some intermediates may have multiple outgoing branches; make sure to sum gradients across these branches in the backward pass.

Once you have finished, run the following to numerically check your backward pass.

```python
# Gradient check batchnorm backward pass
np.random.seed(231)
N, D = 4, 5
x = 5 * np.random.randn(N, D) + 12
gamma = np.random.randn(D)
beta = np.random.randn(D)
dout = np.random.randn(N, D)

bn_param = {'mode': 'train'}
fx = lambda x: batchnorm_forward(x, gamma, beta, bn_param)[0]
fg = lambda a: batchnorm_forward(x, a, beta, bn_param)[0]
fb = lambda b: batchnorm_forward(x, gamma, b, bn_param)[0]

dx_num = eval_numerical_gradient_array(fx, x, dout)
da_num = eval_numerical_gradient_array(fg, gamma.copy(), dout)
db_num = eval_numerical_gradient_array(fb, beta.copy(), dout)

_, cache = batchnorm_forward(x, gamma, beta, bn_param)
dx, dgamma, dbeta = batchnorm_backward(dout, cache)
#You should expect to see relative errors between 1e-13 and 1e-8
print('dx error: ', rel_error(dx_num, dx))
print('dgamma error: ', rel_error(da_num, dgamma))
print('dbeta error: ', rel_error(db_num, dbeta))
```

    dx error:  1.7029261167605239e-09
    dgamma error:  7.420414216247087e-13
    dbeta error:  2.8795057655839487e-12

## Batch normalization: alternative backward
In class we talked about two different implementations for the sigmoid backward pass. One strategy is to write out a computation graph composed of simple operations and backprop through all intermediate values. Another strategy is to work out the derivatives on paper. For example, you can derive a very simple formula for the sigmoid function's backward pass by simplifying gradients on paper.

Surprisingly, it turns out that you can do a similar simplification for the batch normalization backward pass too!

In the forward pass, given a set of inputs $X=\begin{bmatrix}x_1\\x_2\\...\\x_N\end{bmatrix}$,

we first calculate the mean $\mu$ and variance $v$.
With $\mu$ and $v$ calculated, we can calculate the standard deviation $\sigma$  and normalized data $Y$.
The equations and graph illustration below describe the computation ($y_i$ is the i-th element of the vector $Y$).

\begin{align}
& \mu=\frac{1}{N}\sum_{k=1}^N x_k  &  v=\frac{1}{N}\sum_{k=1}^N (x_k-\mu)^2 \\
& \sigma=\sqrt{v+\epsilon}         &  y_i=\frac{x_i-\mu}{\sigma}
\end{align}

The meat of our problem during backpropagation is to compute $\frac{\partial L}{\partial X}$, given the upstream gradient we receive, $\frac{\partial L}{\partial Y}.$ To do this, recall the chain rule in calculus gives us $\frac{\partial L}{\partial X} = \frac{\partial L}{\partial Y} \cdot \frac{\partial Y}{\partial X}$.

The unknown/hart part is $\frac{\partial Y}{\partial X}$. We can find this by first deriving step-by-step our local gradients at
$\frac{\partial v}{\partial X}$, $\frac{\partial \mu}{\partial X}$,
$\frac{\partial \sigma}{\partial v}$,
$\frac{\partial Y}{\partial \sigma}$, and $\frac{\partial Y}{\partial \mu}$,
and then use the chain rule to compose these gradients (which appear in the form of vectors!) appropriately to compute $\frac{\partial Y}{\partial X}$.

If it's challenging to directly reason about the gradients over $X$ and $Y$ which require matrix multiplication, try reasoning about the gradients in terms of individual elements $x_i$ and $y_i$ first: in that case, you will need to come up with the derivations for $\frac{\partial L}{\partial x_i}$, by relying on the Chain Rule to first calculate the intermediate $\frac{\partial \mu}{\partial x_i}, \frac{\partial v}{\partial x_i}, \frac{\partial \sigma}{\partial x_i},$ then assemble these pieces to calculate $\frac{\partial y_i}{\partial x_i}$.

You should make sure each of the intermediary gradient derivations are all as simplified as possible, for ease of implementation.

After doing so, implement the simplified batch normalization backward pass in the function `batchnorm_backward_alt` and compare the two implementations by running the following. Your two implementations should compute nearly identical results, but the alternative implementation should be a bit faster.

```python
np.random.seed(231)
N, D = 100, 500
x = 5 * np.random.randn(N, D) + 12
gamma = np.random.randn(D)
beta = np.random.randn(D)
dout = np.random.randn(N, D)

bn_param = {'mode': 'train'}
out, cache = batchnorm_forward(x, gamma, beta, bn_param)

t1 = time.time()
dx1, dgamma1, dbeta1 = batchnorm_backward(dout, cache)
t2 = time.time()
dx2, dgamma2, dbeta2 = batchnorm_backward_alt(dout, cache)
t3 = time.time()

print('dx difference: ', rel_error(dx1, dx2))
print('dgamma difference: ', rel_error(dgamma1, dgamma2))
print('dbeta difference: ', rel_error(dbeta1, dbeta2))
print('speedup: %.2fx' % ((t2 - t1) / (t3 - t2)))
```

    dx difference:  0.0
    dgamma difference:  0.0
    dbeta difference:  0.0
    speedup: 1.00x

## Fully Connected Nets with Batch Normalization
Now that you have a working implementation for batch normalization, go back to your `FullyConnectedNet` in the file `cs231n/classifiers/fc_net.py`. Modify your implementation to add batch normalization.

Concretely, when the `normalization` flag is set to `"batchnorm"` in the constructor, you should insert a batch normalization layer before each ReLU nonlinearity. The outputs from the last layer of the network should not be normalized. Once you are done, run the following to gradient-check your implementation.

HINT: You might find it useful to define an additional helper layer similar to those in the file `cs231n/layer_utils.py`. If you decide to do so, do it in the file `cs231n/classifiers/fc_net.py`.

```python
np.random.seed(231)
N, D, H1, H2, C = 2, 15, 20, 30, 10
X = np.random.randn(N, D)
y = np.random.randint(C, size=(N,))

# You should expect losses between 1e-4~1e-10 for W,
# losses between 1e-08~1e-10 for b,
# and losses between 1e-08~1e-09 for beta and gammas.
for reg in [0, 3.14]:
  print('Running check with reg = ', reg)
  model = FullyConnectedNet([H1, H2], input_dim=D, num_classes=C,
                            reg=reg, weight_scale=5e-2, dtype=np.float64,
                            normalization='batchnorm')

  loss, grads = model.loss(X, y)
  print('Initial loss: ', loss)

  for name in sorted(grads):
    f = lambda _: model.loss(X, y)[0]
    grad_num = eval_numerical_gradient(f, model.params[name], verbose=False, h=1e-5)
    print('%s relative error: %.2e' % (name, rel_error(grad_num, grads[name])))
  if reg == 0: print()
```

    Running check with reg =  0
    Initial loss:  2.2611955101340957
    W1 relative error: 1.10e-04
    W2 relative error: 2.85e-06
    W3 relative error: 3.92e-10
    b1 relative error: 2.22e-03
    b2 relative error: 2.22e-08
    b3 relative error: 4.78e-11
    beta1 relative error: 7.33e-09
    beta2 relative error: 1.89e-09
    gamma1 relative error: 7.47e-09
    gamma2 relative error: 2.41e-09

    Running check with reg =  3.14
    Initial loss:  6.996533220108303
    W1 relative error: 1.98e-06
    W2 relative error: 2.28e-06
    W3 relative error: 1.11e-08
    b1 relative error: 5.55e-09
    b2 relative error: 2.22e-08
    b3 relative error: 2.23e-10
    beta1 relative error: 6.32e-09
    beta2 relative error: 5.69e-09
    gamma1 relative error: 5.94e-09
    gamma2 relative error: 4.14e-09

# Batchnorm for deep networks
Run the following to train a six-layer network on a subset of 1000 training examples both with and without batch normalization.

```python
np.random.seed(231)
# Try training a very deep net with batchnorm
hidden_dims = [100, 100, 100, 100, 100]

num_train = 1000
small_data = {
  'X_train': data['X_train'][:num_train],
  'y_train': data['y_train'][:num_train],
  'X_val': data['X_val'],
  'y_val': data['y_val'],
}

weight_scale = 2e-2
bn_model = FullyConnectedNet(hidden_dims, weight_scale=weight_scale, normalization='batchnorm')
model = FullyConnectedNet(hidden_dims, weight_scale=weight_scale, normalization=None)

print('Solver with batch norm:')
bn_solver = Solver(bn_model, small_data,
                num_epochs=10, batch_size=50,
                update_rule='adam',
                optim_config={
                  'learning_rate': 1e-3,
                },
                verbose=True,print_every=20)
bn_solver.train()

print('\nSolver without batch norm:')
solver = Solver(model, small_data,
                num_epochs=10, batch_size=50,
                update_rule='adam',
                optim_config={
                  'learning_rate': 1e-3,
                },
                verbose=True, print_every=20)
solver.train()
```

    Solver with batch norm:
    (Iteration 1 / 200) loss: 2.340974
    (Epoch 0 / 10) train acc: 0.107000; val_acc: 0.115000
    (Epoch 1 / 10) train acc: 0.313000; val_acc: 0.266000
    (Iteration 21 / 200) loss: 2.039365
    (Epoch 2 / 10) train acc: 0.384000; val_acc: 0.279000
    (Iteration 41 / 200) loss: 2.041103
    (Epoch 3 / 10) train acc: 0.494000; val_acc: 0.307000
    (Iteration 61 / 200) loss: 1.753903
    (Epoch 4 / 10) train acc: 0.534000; val_acc: 0.308000
    (Iteration 81 / 200) loss: 1.246585
    (Epoch 5 / 10) train acc: 0.574000; val_acc: 0.314000
    (Iteration 101 / 200) loss: 1.320590
    (Epoch 6 / 10) train acc: 0.635000; val_acc: 0.337000
    (Iteration 121 / 200) loss: 1.157329
    (Epoch 7 / 10) train acc: 0.684000; val_acc: 0.328000
    (Iteration 141 / 200) loss: 1.141054
    (Epoch 8 / 10) train acc: 0.775000; val_acc: 0.337000
    (Iteration 161 / 200) loss: 0.700218
    (Epoch 9 / 10) train acc: 0.808000; val_acc: 0.327000
    (Iteration 181 / 200) loss: 0.887479
    (Epoch 10 / 10) train acc: 0.789000; val_acc: 0.320000

    Solver without batch norm:
    (Iteration 1 / 200) loss: 2.302332
    (Epoch 0 / 10) train acc: 0.129000; val_acc: 0.131000
    (Epoch 1 / 10) train acc: 0.283000; val_acc: 0.250000
    (Iteration 21 / 200) loss: 2.041970
    (Epoch 2 / 10) train acc: 0.316000; val_acc: 0.277000
    (Iteration 41 / 200) loss: 1.900473
    (Epoch 3 / 10) train acc: 0.373000; val_acc: 0.282000
    (Iteration 61 / 200) loss: 1.713157
    (Epoch 4 / 10) train acc: 0.390000; val_acc: 0.310000
    (Iteration 81 / 200) loss: 1.662213
    (Epoch 5 / 10) train acc: 0.431000; val_acc: 0.298000
    (Iteration 101 / 200) loss: 1.703394
    (Epoch 6 / 10) train acc: 0.525000; val_acc: 0.346000
    (Iteration 121 / 200) loss: 1.558098
    (Epoch 7 / 10) train acc: 0.548000; val_acc: 0.321000
    (Iteration 141 / 200) loss: 1.434444
    (Epoch 8 / 10) train acc: 0.627000; val_acc: 0.331000
    (Iteration 161 / 200) loss: 1.054038
    (Epoch 9 / 10) train acc: 0.616000; val_acc: 0.316000
    (Iteration 181 / 200) loss: 0.977785
    (Epoch 10 / 10) train acc: 0.715000; val_acc: 0.329000

Run the following to visualize the results from two networks trained above. You should find that using batch normalization helps the network to converge much faster.

```python
def plot_training_history(title, label, baseline, bn_solvers, plot_fn, bl_marker='.', bn_marker='.', labels=None):
    """utility function for plotting training history"""
    plt.title(title)
    plt.xlabel(label)
    bn_plots = [plot_fn(bn_solver) for bn_solver in bn_solvers]
    bl_plot = plot_fn(baseline)
    num_bn = len(bn_plots)
    for i in range(num_bn):
        label='with_norm'
        if labels is not None:
            label += str(labels[i])
        plt.plot(bn_plots[i], bn_marker, label=label)
    label='baseline'
    if labels is not None:
        label += str(labels[0])
    plt.plot(bl_plot, bl_marker, label=label)
    plt.legend(loc='lower center', ncol=num_bn+1)

plt.subplot(3, 1, 1)
plot_training_history('Training loss','Iteration', solver, [bn_solver], \
                      lambda x: x.loss_history, bl_marker='o', bn_marker='o')
plt.subplot(3, 1, 2)
plot_training_history('Training accuracy','Epoch', solver, [bn_solver], \
                      lambda x: x.train_acc_history, bl_marker='-o', bn_marker='-o')
plt.subplot(3, 1, 3)
plot_training_history('Validation accuracy','Epoch', solver, [bn_solver], \
                      lambda x: x.val_acc_history, bl_marker='-o', bn_marker='-o')

plt.gcf().set_size_inches(15, 15)
plt.show()
```

# Batch normalization and initialization
We will now run a small experiment to study the interaction of batch normalization and weight initialization.

The first cell will train 8-layer networks both with and without batch normalization using different scales for weight initialization. The second layer will plot training accuracy, validation set accuracy, and training loss as a function of the weight initialization scale.

```python
np.random.seed(231)
# Try training a very deep net with batchnorm
hidden_dims = [50, 50, 50, 50, 50, 50, 50]
num_train = 1000
small_data = {
  'X_train': data['X_train'][:num_train],
  'y_train': data['y_train'][:num_train],
  'X_val': data['X_val'],
  'y_val': data['y_val'],
}

bn_solvers_ws = {}
solvers_ws = {}
weight_scales = np.logspace(-4, 0, num=20)
for i, weight_scale in enumerate(weight_scales):
  print('Running weight scale %d / %d' % (i + 1, len(weight_scales)))
  bn_model = FullyConnectedNet(hidden_dims, weight_scale=weight_scale, normalization='batchnorm')
  model = FullyConnectedNet(hidden_dims, weight_scale=weight_scale, normalization=None)

  bn_solver = Solver(bn_model, small_data,
                  num_epochs=10, batch_size=50,
                  update_rule='adam',
                  optim_config={
                    'learning_rate': 1e-3,
                  },
                  verbose=False, print_every=200)
  bn_solver.train()
  bn_solvers_ws[weight_scale] = bn_solver

  solver = Solver(model, small_data,
                  num_epochs=10, batch_size=50,
                  update_rule='adam',
                  optim_config={
                    'learning_rate': 1e-3,
                  },
                  verbose=False, print_every=200)
  solver.train()
  solvers_ws[weight_scale] = solver
```

    Running weight scale 1 / 20
    Running weight scale 2 / 20
    Running weight scale 3 / 20
    Running weight scale 4 / 20
    Running weight scale 5 / 20
    Running weight scale 6 / 20
    Running weight scale 7 / 20
    Running weight scale 8 / 20
    Running weight scale 9 / 20
    Running weight scale 10 / 20
    Running weight scale 11 / 20
    Running weight scale 12 / 20
    Running weight scale 13 / 20
    Running weight scale 14 / 20
    Running weight scale 15 / 20
    Running weight scale 16 / 20
    Running weight scale 17 / 20
    Running weight scale 18 / 20
    Running weight scale 19 / 20
    Running weight scale 20 / 20

```python
# Plot results of weight scale experiment
best_train_accs, bn_best_train_accs = [], []
best_val_accs, bn_best_val_accs = [], []
final_train_loss, bn_final_train_loss = [], []

for ws in weight_scales:
  best_train_accs.append(max(solvers_ws[ws].train_acc_history))
  bn_best_train_accs.append(max(bn_solvers_ws[ws].train_acc_history))

  best_val_accs.append(max(solvers_ws[ws].val_acc_history))
  bn_best_val_accs.append(max(bn_solvers_ws[ws].val_acc_history))

  final_train_loss.append(np.mean(solvers_ws[ws].loss_history[-100:]))
  bn_final_train_loss.append(np.mean(bn_solvers_ws[ws].loss_history[-100:]))

plt.subplot(3, 1, 1)
plt.title('Best val accuracy vs weight initialization scale')
plt.xlabel('Weight initialization scale')
plt.ylabel('Best val accuracy')
plt.semilogx(weight_scales, best_val_accs, '-o', label='baseline')
plt.semilogx(weight_scales, bn_best_val_accs, '-o', label='batchnorm')
plt.legend(ncol=2, loc='lower right')

plt.subplot(3, 1, 2)
plt.title('Best train accuracy vs weight initialization scale')
plt.xlabel('Weight initialization scale')
plt.ylabel('Best training accuracy')
plt.semilogx(weight_scales, best_train_accs, '-o', label='baseline')
plt.semilogx(weight_scales, bn_best_train_accs, '-o', label='batchnorm')
plt.legend()

plt.subplot(3, 1, 3)
plt.title('Final training loss vs weight initialization scale')
plt.xlabel('Weight initialization scale')
plt.ylabel('Final training loss')
plt.semilogx(weight_scales, final_train_loss, '-o', label='baseline')
plt.semilogx(weight_scales, bn_final_train_loss, '-o', label='batchnorm')
plt.legend()
plt.gca().set_ylim(1.0, 3.5)

plt.gcf().set_size_inches(15, 15)
plt.show()
```

## Inline Question 1:
Describe the results of this experiment. How does the scale of weight initialization affect models with/without batch normalization differently, and why?

## Answer:
When use batch normalization,even if we use a bad weight initialization scale,we can still get a not bad result,but without batch normalization,we are very sensetive to weight initialization scale.

# Batch normalization and batch size
We will now run a small experiment to study the interaction of batch normalization and batch size.

The first cell will train 6-layer networks both with and without batch normalization using different batch sizes. The second layer will plot training accuracy and validation set accuracy over time.

```python
def run_batchsize_experiments(normalization_mode):
    np.random.seed(231)
    # Try training a very deep net with batchnorm
    hidden_dims = [100, 100, 100, 100, 100]
    num_train = 1000
    small_data = {
      'X_train': data['X_train'][:num_train],
      'y_train': data['y_train'][:num_train],
      'X_val': data['X_val'],
      'y_val': data['y_val'],
    }
    n_epochs=10
    weight_scale = 2e-2
    batch_sizes = [5,10,50]
    lr = 10**(-3.5)
    solver_bsize = batch_sizes[0]

    print('No normalization: batch size = ',solver_bsize)
    model = FullyConnectedNet(hidden_dims, weight_scale=weight_scale, normalization=None)
    solver = Solver(model, small_data,
                    num_epochs=n_epochs, batch_size=solver_bsize,
                    update_rule='adam',
                    optim_config={
                      'learning_rate': lr,
                    },
                    verbose=False)
    solver.train()

    bn_solvers = []
    for i in range(len(batch_sizes)):
        b_size=batch_sizes[i]
        print('Normalization: batch size = ',b_size)
        bn_model = FullyConnectedNet(hidden_dims, weight_scale=weight_scale, normalization=normalization_mode)
        bn_solver = Solver(bn_model, small_data,
                        num_epochs=n_epochs, batch_size=b_size,
                        update_rule='adam',
                        optim_config={
                          'learning_rate': lr,
                        },
                        verbose=False)
        bn_solver.train()
        bn_solvers.append(bn_solver)

    return bn_solvers, solver, batch_sizes

batch_sizes = [5,10,50]
bn_solvers_bsize, solver_bsize, batch_sizes = run_batchsize_experiments('batchnorm')
```

    No normalization: batch size =  5
    Normalization: batch size =  5
    Normalization: batch size =  10
    Normalization: batch size =  50

```python
plt.subplot(2, 1, 1)
plot_training_history('Training accuracy (Batch Normalization)','Epoch', solver_bsize, bn_solvers_bsize, \
                      lambda x: x.train_acc_history, bl_marker='-^', bn_marker='-o', labels=batch_sizes)
plt.subplot(2, 1, 2)
plot_training_history('Validation accuracy (Batch Normalization)','Epoch', solver_bsize, bn_solvers_bsize, \
                      lambda x: x.val_acc_history, bl_marker='-^', bn_marker='-o', labels=batch_sizes)

plt.gcf().set_size_inches(15, 10)
plt.show()
```

## Inline Question 2:
Describe the results of this experiment. What does this imply about the relationship between batch normalization and batch size? Why is this relationship observed?

## Answer:
If we use batch normalization we should use a bigger batch size,if that we can get a more accurate mean and variance.

# Layer Normalization
Batch normalization has proved to be effective in making networks easier to train, but the dependency on batch size makes it less useful in complex networks which have a cap on the input batch size due to hardware limitations.

Several alternatives to batch normalization have been proposed to mitigate this problem; one such technique is Layer Normalization [2]. Instead of normalizing over the batch, we normalize over the features. In other words, when using Layer Normalization, each feature vector corresponding to a single datapoint is normalized based on the sum of all terms within that feature vector.

[2] [Ba, Jimmy Lei, Jamie Ryan Kiros, and Geoffrey E. Hinton. "Layer Normalization." stat 1050 (2016): 21.](https://arxiv.org/pdf/1607.06450.pdf)

## Inline Question 3:
Which of these data preprocessing steps is analogous to batch normalization, and which is analogous to layer normalization?

1. Scaling each image in the dataset, so that the RGB channels for each row of pixels within an image sums up to 1.
2. Scaling each image in the dataset, so that the RGB channels for all pixels within an image sums up to 1.
3. Subtracting the mean image of the dataset from each image in the dataset.
4. Setting all RGB values to either 0 or 1 depending on a given threshold.

## Answer:
batch normalization 3
layer normalization 124

# Layer Normalization: Implementation

Now you'll implement layer normalization. This step should be relatively straightforward, as conceptually the implementation is almost identical to that of batch normalization. One significant difference though is that for layer normalization, we do not keep track of the moving moments, and the testing phase is identical to the training phase, where the mean and variance are directly calculated per datapoint.

Here's what you need to do:

* In `cs231n/layers.py`, implement the forward pass for layer normalization in the function `layernorm_backward`.

Run the cell below to check your results.
* In `cs231n/layers.py`, implement the backward pass for layer normalization in the function `layernorm_backward`.

Run the second cell below to check your results.
* Modify `cs231n/classifiers/fc_net.py` to add layer normalization to the `FullyConnectedNet`. When the `normalization` flag is set to `"layernorm"` in the constructor, you should insert a layer normalization layer before each ReLU nonlinearity.

Run the third cell below to run the batch size experiment on layer normalization.

```python
# Check the training-time forward pass by checking means and variances
# of features both before and after layer normalization

# Simulate the forward pass for a two-layer network
np.random.seed(231)
N, D1, D2, D3 =4, 50, 60, 3
X = np.random.randn(N, D1)
W1 = np.random.randn(D1, D2)
W2 = np.random.randn(D2, D3)
a = np.maximum(0, X.dot(W1)).dot(W2)

print('Before layer normalization:')
print_mean_std(a,axis=1)

gamma = np.ones(D3)
beta = np.zeros(D3)
# Means should be close to zero and stds close to one
print('After layer normalization (gamma=1, beta=0)')
a_norm, _ = layernorm_forward(a, gamma, beta, {'mode': 'train'})
print_mean_std(a_norm,axis=1)

gamma = np.asarray([3.0,3.0,3.0])
beta = np.asarray([5.0,5.0,5.0])
# Now means should be close to beta and stds close to gamma
print('After layer normalization (gamma=', gamma, ', beta=', beta, ')')
a_norm, _ = layernorm_forward(a, gamma, beta, {'mode': 'train'})
print_mean_std(a_norm,axis=1)
```

    Before layer normalization:
      means:  [-59.06673243 -47.60782686 -43.31137368 -26.40991744]
      stds:   [10.07429373 28.39478981 35.28360729  4.01831507]

    After layer normalization (gamma=1, beta=0)
      means:  [ 4.81096644e-16  0.00000000e+00  0.00000000e+00 -2.96059473e-16]
      stds:   [0.99999995 0.99999999 1.         0.99999969]

    After layer normalization (gamma= [3. 3. 3.] , beta= [5. 5. 5.] )
      means:  [5. 5. 5. 5.]
      stds:   [2.99999985 2.99999998 2.99999999 2.99999907]

```python
# Gradient check batchnorm backward pass
np.random.seed(231)
N, D = 4, 5
x = 5 * np.random.randn(N, D) + 12
gamma = np.random.randn(D)
beta = np.random.randn(D)
dout = np.random.randn(N, D)

ln_param = {}
fx = lambda x: layernorm_forward(x, gamma, beta, ln_param)[0]
fg = lambda a: layernorm_forward(x, a, beta, ln_param)[0]
fb = lambda b: layernorm_forward(x, gamma, b, ln_param)[0]

dx_num = eval_numerical_gradient_array(fx, x, dout)
da_num = eval_numerical_gradient_array(fg, gamma.copy(), dout)
db_num = eval_numerical_gradient_array(fb, beta.copy(), dout)

_, cache = layernorm_forward(x, gamma, beta, ln_param)
dx, dgamma, dbeta = layernorm_backward(dout, cache)

#You should expect to see relative errors between 1e-12 and 1e-8
print('dx error: ', rel_error(dx_num, dx))
print('dgamma error: ', rel_error(da_num, dgamma))
print('dbeta error: ', rel_error(db_num, dbeta))
```

    dx error:  1.433615657860454e-09
    dgamma error:  4.519489546032799e-12
    dbeta error:  2.276445013433725e-12

# Layer Normalization and batch size

We will now run the previous batch size experiment with layer normalization instead of batch normalization. Compared to the previous experiment, you should see a markedly smaller influence of batch size on the training history!

```python
ln_solvers_bsize, solver_bsize, batch_sizes = run_batchsize_experiments('layernorm')

plt.subplot(2, 1, 1)
plot_training_history('Training accuracy (Layer Normalization)','Epoch', solver_bsize, ln_solvers_bsize, \
                      lambda x: x.train_acc_history, bl_marker='-^', bn_marker='-o', labels=batch_sizes)
plt.subplot(2, 1, 2)
plot_training_history('Validation accuracy (Layer Normalization)','Epoch', solver_bsize, ln_solvers_bsize, \
                      lambda x: x.val_acc_history, bl_marker='-^', bn_marker='-o', labels=batch_sizes)

plt.gcf().set_size_inches(15, 10)
plt.show()
```

## Inline Question 4:
When is layer normalization likely to not work well, and why?

1. Using it in a very deep network
2. Having a very small dimension of features
3. Having a high regularization term

## Answer:
2 If use a small dimension,the mean and varience of the dimension will became random ,can't show it's common ground.


```python
from tectosaur2.nb_config import setup
setup()
```

```python
import sympy as sp
import numpy as np
import matplotlib.pyplot as plt
from tectosaur2 import refine_surfaces, gauss_rule
from tectosaur2.mesh import pts_grid
from tectosaur2.elastic2d import ElasticU, ElasticT

nobs = 50
V = 2
zoomx = [-V, V]
zoomy = [-V, V]
xs = np.linspace(*zoomx, nobs)
ys = np.linspace(*zoomy, nobs)
obs_pts = pts_grid(xs, ys)
obsx = obs_pts[:, 0]
obsy = obs_pts[:, 1]

t = sp.var("t")
(fault,) = refine_surfaces([(t, t, t)], gauss_rule(12))

```

```python
shear_modulus = 1.0  # 3e10
poisson_ratio = 0.25

```

```python
Tstar = ElasticT(shear_modulus, poisson_ratio)
T = Tstar.direct(obs_pts, fault)
```

```python
slip = np.full((fault.n_pts, 2), 1.0 / np.sqrt(2))

```

```python
disp = np.sum(T * slip[None, None, :, :], axis=(2, 3))
v2d = disp.reshape((nobs, nobs, 2))[:, :, 0]
levels = np.linspace(-1, 1, 11)
cntf = plt.contourf(xs, ys, v2d, levels=levels, extend="both")
plt.contour(
    xs,
    ys,
    v2d,
    colors="k",
    linestyles="-",
    linewidths=0.5,
    levels=levels,
    extend="both",
)
plt.colorbar(cntf)
plt.show()

```

```python
Ustar = ElasticU(shear_modulus, poisson_ratio)
S = Ustar.direct(obs_pts, fault)
```

```python
src=fault
kappa = 3 - 4 * poisson_ratio
p = 20
z = obs_pts[:, None, 0] + obs_pts[:, None, 1] * 1j
w = src.pts[None, :, 0] + src.pts[None, :, 1] * 1j
z0 = z * 1.2
S3 = np.empty((z0.shape[0], 2, src.n_pts, 2))
for d_src in range(2):
    tw = (d_src == 0) + (d_src == 1) * 1j
    f1 = np.zeros((z.shape[0], w.shape[1]), dtype=np.complex128)
    f3 = np.zeros((z.shape[0], w.shape[1]), dtype=np.complex128)
    for m in range(p):
        ratio = ((z-z0) / (w-z0))
        if m == 0:
            T = (z-z0) ** m * np.log(w - z0)
        else:
            T = (-1.0 / m) * ratio ** m
        f1 += T * tw + np.conjugate(T) * tw
        f3 += -(z - w) * np.conjugate(tw * ratio ** m / (w - z0))

    V = (
        (1.0 / (4 * np.pi * shear_modulus * (1 + kappa))) * (-kappa * f1 + f3)
    ) + Ustar.disp_C1 * 0.5 * tw
    S3[:, 0, :, d_src] = np.real(V)
    S3[:, 1, :, d_src] = np.imag(V)
```

```python
S2 = np.empty((obs_pts.shape[0], src.n_pts, 2), dtype=np.complex128)
kappa = 3 - 4 * poisson_ratio
z0 = obs_pts[:, None, 0] + obs_pts[:, None, 1] * 1j
z = src.pts[None, :, 0] + src.pts[None, :, 1] * 1j
for d_src in range(2):
    P = (d_src == 0) + (d_src == 1) * 1j
    t1 = -kappa * P * (np.log(z0 - z) + np.conjugate(np.log(z0 - z)))
    t2 = np.conjugate(P) * ((z0 - z) / (np.conjugate(z0 - z)))
    S2[:, :, d_src] = (1.0 / (4 * np.pi * shear_modulus * (1 + kappa)) * (t1 + t2)) + Ustar.disp_C1 * P * 0.5

```

```python
np.max(np.abs(S3[25,:,:,:] - S[25,:,:,:]))
```

    3.576999807464176e-15

```python
S2[25, 0, 0], S2[25,0,1]

```

    ((-0.012288188880446207-0.026525626355736374j),
     (-0.026525626355736374-0.012492906104262819j))

```python
S[25,:,0,0]
```

    array([-0.01228819, -0.02652563])

```python
S2 = np.empty((obs_pts.shape[0], src.n_pts, 2), dtype=np.complex128)
kappa = 3 - 4 * poisson_ratio
z0 = obs_pts[:, None, 0] + obs_pts[:, None, 1] * 1j
z = src.pts[None, :, 0] + src.pts[None, :, 1] * 1j
for d_src in range(2):
    P = (d_src == 0) + (d_src == 1) * 1j
    t1 = -kappa * P * (np.log(z0 - z) + np.conjugate(np.log(z0 - z)))
    t2 = np.conjugate(P) * ((z0 - z) / (np.conjugate(z0 - z)))
    S2[:, :, d_src] = 1.0 / (4 * np.pi * shear_modulus * (1 + kappa)) * (t1 + t2) + disp_C1 * P * 0.5

```

```python
S2[0, 0, 0], S[0, :, 0, 0]

```

    ((-0.01218543426511473+0.026525823848649224j), array([0.06523708, 0.02652582]))

```python
stress = slip
disp = np.sum(S * stress[None, None, :, :], axis=(2, 3))
v2d = disp.reshape((nobs, nobs, 2))[:, :, 0]
levels = np.linspace(-1, 1, 11)
cntf = plt.contourf(xs, ys, v2d, levels=levels, extend="both")
plt.contour(
    xs,
    ys,
    v2d,
    colors="k",
    linestyles="-",
    linewidths=0.5,
    levels=levels,
    extend="both",
)
plt.colorbar(cntf)
plt.show()

```

```python

```


# メモ

pandoc -f mediawiki -t markdown math_calc_ordinarydiff.wiki -o output.md

の実験

https://ja.wikibooks.org/wiki/解析学基礎/常微分方程式

を pandoc してみた。 そこそこ使える。

常微分方程式とは何ぞや
----------------------

微分方程式とは、独立変数 $x$ と、 $x$ の関数 $y(x)$ 、およびその何階かの導関数を含む方程式である。一般化すれば、微分方程式は

$$f(x, y, y', \cdots, y^{(n)}) = 0$$

の形に書くことのできる方程式である。そして、この方程式に含まれる導関数のうちもっとも高階の導関数が $y^{(n)}$ であるとき、これを $n$ 階微分方程式と呼び、この方程式を満たすような関数を求める操作を、微分方程式を解く、という。

微分方程式は、大きく分けて常微分方程式と偏微分方程式に分かれる。常微分方程式とは、一変数関数とその導関数からなる方程式である。一方、偏微分方程式とは、多変数関数とその偏導関数との方程式である。ここでは、常微分方程式の解き方について記述することにし、本書では特に断りのない場合「微分方程式」は常微分方程式をさしているものとする。

### 解の種類

微分方程式は微分された関数が含まれた方程式であるから、その解を求めるためには多くの場合積分操作が必要であり、解には積分定数が含まれる。 $n$ 階微分方程式であれば $n$ 個の任意の積分定数が含まれる。このような積分定数を含む形の解を**一般解**と呼ぶ。

一般解のうち、積分定数にある値を与えた解を**特殊解**と呼ぶ。

さらに、微分方程式の解のなかには、方程式の解であるにもかかわらず、積分定数にどのような値を代入しても表すことのできない解も存在する。このような解を**特異解**と呼ぶ。

* 一般解
* 特殊解
* 特異解

### 問題の種類

微分方程式を解くとき、一般解が重要になる場面はそう多くない。現実にはある独立変数 $x$ における従属変数 $y$ の値が定まっていて、その条件を満たすような特殊解が必要になる場合がほとんどである。

たとえば時刻 $t=0$において、ある関数 $y(t)$ の値が $y_0$ と分かっている時に $y(t)$ に関する微分方程式を解くような場合である。

このような、ある初期条件 $ y(x_0) = y_0 $  を満たすような微分方程式

$$f(x, y, y', \cdots, y^{(n)})$$

の特殊解を求める問題を**初期値問題**といい、これらをみたす特殊解を求めることを初期値問題を解くという。

また、例えば位置 $x=0$ と $x=L$ で常に $y=0$ となるような波（固定端）の変位 $y(x)$ に関する微分方程式を解くという状況もある。

このような、ある境界条件

$$y(x_i) = y_i (i = 0, 1, \cdots, n)$$

を満たすような微分方程式

$$f(x, y, y', \cdots, y^{(n)})$$

の特殊解を求める問題を**境界値問題**といい、これらをみたす特殊解を求めることを境界値問題を解くという。

初等解法
--------

微分方程式を、有限回の式変形や変数変換や積分によって解く方法を初等解法と呼ぶ。はじめに、微分方程式の初等解法について解説する。なお、どのような微分方程式であっても初等解法によって解くことができるとは限らず、この手法が適用できる場合は限られてくることに注意されたい。

まずは、1 階の微分方程式について考えることにする。

### 変数分離形

一般に、 $n$ 階微分方程式が

$$y^{(n)} = f(x, y, y', \cdots, y^{(n-1)})$$

の形で書き表されるとき、これを正規形と呼ぶ。

1階微分方程式の正規形

$$y' = f(x, y)$$ において、右辺の式が

$$f(x, y) = X(x)Y(y)$$
のように $x$ のみの関数と $y$ のみの関数との積の形に変形できるとき、これを**変数分離形**の微分方程式と呼ぶ。この場合、微分方程式は

$$y' = X(x)Y(y)$$

の形になっているから、$Y(y) \neq 0$ と仮定して両辺を $Y(y)$ で割ることにより

$$\frac{y'}{Y(y)} = X(x)$$

と変形して、左辺が $y$ とその導関数のみの式、右辺は $x$ のみの式となるように分離することができる。

もし $Y(y) = 0$ を満たす $y$ の値が存在すれば、その値を $y=a$ とすると、もとの微分方程式に代入して

$$y'(x) = 0$$

を得る。一方、いま置いた $y=a$ も $y'=0$ を満たす関数である。すなわち、微分方程式の解は

$$y(x) = a$$

と簡単に求めることができる。これは微分方程式の特殊解である。

では、$Y(y) \neq 0$として変数を分離した式に戻ろう。
分離した式の両辺を $x$ で積分して、

$$\int\frac{1}{Y(y(x))}y'(x)dx = \int X(x)dx$$

左辺は置換積分の公式より、

$$\int\frac{1}{Y(y(x))}y'(x)dx = \int\frac{1}{Y(y)}dy$$

であるので、

$$\int\frac{1}{Y(y)}dy = \int X(x)dx$$

を得る。これで両辺の不定積分が計算できれば、微分方程式の解が求まることになる。これは微分方程式の一般解である。

#### 例題

微分方程式 $y' = xy$ を解く。

これは変数分離形の1階微分方程式である。 $y=0$ のとき $y'=0$ となって、これは微分方程式を満たす。

$y \neq 0$と仮定して両辺を $y$ で割ると、

$$\frac{y'}{y} = x$$

であるから、両辺を $x$ で積分して、

$$\int\frac{1}{y}dy = \int x dx + C$$

となる。両辺の不定積分を計算すれば、

$$\log |y| = \frac{1}{2}x^2 + C$$ となるから、これより

$$y = e^{\frac{1}{2}x^2 + C} = Ae^{\frac{1}{2}x^2}$$（Aは任意の定数）

とすることができる。これは微分方程式の一般解である。

先に求めた $y=0$ は、一般解で $A=0$ とした場合であるから、微分方程式の特殊解である。したがって、微分方程式の解は

$$y(x) = Ae^{\frac{1}{2}x^2}$$ である。

### 同次形

一見変数分離形でないように見える微分方程式であっても、適切な変数変換によって変数分離形へ持ち込むことのできる微分方程式が存在する。

1 階微分方程式の正規形

$$y' = f(x, y)$$ において、右辺の式が

$$f(x, y) = g\left(\frac{y}{x}\right)$$

のように$\frac{y}{x}$の関数として記述できるとき、これを**同次形**の微分方程式と呼ぶ。このとき微分方程式は

$$y' = g\left(\frac{y}{x}\right)$$ の形をしている。

$z(x) = \frac{y(x)}{x}$とおく。このとき $y = xz$ であるから、

$$y' = (xz)' = z + xz'$$

が成り立つ。これを元の微分方程式に代入すると、

$$z + xz' = g(z)$$ となる。これを$z'$について解くと、

$$z' = \frac{g(z) - z}{x}$$
となって、変数分離形の微分方程式となる。

変数分離形の方程式の解き方にしたがってこれを解くと、

$$\begin{align}
     \frac{z'}{g(z)-z} &= \frac{1}{x} \\
\int\frac{1}{g(z)-z}dz &= \int\frac{1}{x}dx + C \\
\int\frac{1}{g(z)-z}dz &= \log|x| + C
\end{align}$$
となる。これで左辺の不定積分を計算し、$z = \frac{y}{x}$を代入し直せば微分方程式の解が得られる。

#### 例題

微分方程式$y' = \frac{y}{x} + \frac{x}{y}$を解く。

これは同次形の1階微分方程式である。$z = \frac{y}{x}$とおくと、$y = xz$であるからこの微分方程式は

$$\begin{align}
z + xz' &= z + \frac{1}{z} \\
    xz' &= \frac{1}{z}
\end{align}$$
と書き直すことができる。これは変数分離形の微分方程式である。$z \neq 0$に注意して変数分離を行うと

$$zz' = \frac{1}{x}$$ であるから、両辺を $x$ で積分して式変形を行うと、

$$\begin{align}
     \int z dz &= \int\frac{1}{x} dx \\
\frac{1}{2}z^2 &= \log |x| + C \\
       \log|x| &= \frac{1}{2}z^2 + C \\
             x &= e^{\frac{1}{2}z^2+C} = Ae^{\frac{1}{2}z^2}
\end{align}$$ となる。ここで$z = \frac{y}{x}$を代入しなおすと、

$$x = Ae^{\frac{y^2}{2x^2}}$$

となる。これが求める微分方程式の一般解である。

### 同次形の応用

正規形の 1 階微分方程式

$$y' = f(x, y)$$

について、右辺が $x$ と $y$ の有理関数になっている場合、すなわち

$$f(x, y) = \frac{h(x, y)}{g(x, y)}$$

の場合を考える。このとき、$g(x,y)$ および $h(x,y)$ が特定の形をしている場合は、上手な式変形や変数変換によって同次形の解法を適用することができることが知られている。ここでは、いくつかの例題を用いてそれらの解法を紹介することにする。

#### 例題 1

微分方程式

$$y' = \frac{2x^2 + 3xy + y^2}{x^2 - 4xy + 2y^2}$$

を解く。

これは、$g(x,y)$ と $h(x,y)$ がともにすべての項で  $x, y$ について同次であるような場合である。例えばこのような場合には、右辺の分子と分母を $x^2$ で割ることで

$$y' = \frac{2 + 3(y/x) + (y/x)^2}{1 - 4(y/x) + 2(y/x)^2}$$

となって、容易に同次形の微分方程式へ持ち込むことができる。あとは同次形の解法に従って解けばよい。

#### 例題 2

微分方程式
$$y' = \frac{2x + 3y -8}{x - y + 1}$$
を解く。

これは、$g(x,y)$ と $h(x,y)$ がともに  $x, y$ の1次式になっている場合である。例えばこのような場合は、次の手順で解くことができることが知られている。

はじめに、連立方程式

$$\begin{cases}
2x + 3y - 8 = 0 \\
x - y + 1 = 0
\end{cases}$$

を解く。これを解くと、解は$(x,y) = (1, 2)$である。この解を用いて、

$$\begin{cases}
x = u + 1 \\
y = v + 2
\end{cases}$$

とおく。これをもとの微分方程式へ代入すると、

$$\begin{align}
\frac{dy}{dx} &= \frac{2(u+1) + 3(v+2) - 8}{(u+1) - (v+2) + 1} \\
\frac{dv}{du} &= \frac{2u + 3v}{u - v}
\end{align}$$ となる。ここで、

$$\frac{dy}{dx} = \frac{d}{dx}(v+2) = \frac{dv}{dx} = \frac{dv}{du}\frac{du}{dx} = \frac{dv}{du}\frac{d}{dx}(x-1) = \frac{dv}{du}$$

を用いた。

このように  $x, y$ から  $u, v$ への変数変換を施すと、例題 1で見た形の方程式となり、右辺の分母分子を  $u$で割ることによって同次形の微分方程式として扱うことができる。そして  $u, v$ の式として同次形の微分方程式を解いた後、変数を  $u, v$ から  $x, y$ に戻せば、求めるべき微分方程式の解が得られる。

#### 例題 3

微分方程式

$$y' = \frac{2x + 3y - 4}{4x + 6y - 3}$$

を解く。

これは、例題 2 のようにして連立方程式を解こうとしても、連立方程式の解が存在しないか、あるいは1つに定まらない場合である。このような場合は、右辺の分母を $z$ と置くことによって一般解を求めることができる。

この問題では、

$$z = 4x + 6y - 3$$ とおくと、分子は

$$2x + 3y - 4 = \frac{1}{2}(4x + 6y - 3) - \frac{5}{2} = \frac{1}{2}z - \frac{5}{2}$$
である。また、

$$z' = 4 + 6y'$$

であるから、この微分方程式は変数分離形へと変形することができて

$$\begin{align}
     \frac{z' - 4}{6} &= \frac{\frac{1}{2}z - \frac{5}{2}}{z} \\
    \frac{z}{7z-15}z' &= 1 \\
\int\frac{z}{7z-15}dz &= x + C
\end{align}$$

と変形できる。この左辺に $z = 4x + 6y - 3$ を代入すれば求めるべき微分方程式の一般解が求まる。

#### 例題 4

微分方程式

$$y' = \frac{2x + 3y - 4}{3}$$

を解く。

これは、例題 3と同様に連立方程式を解こうとしても解が一意に定まらず、かつ、分母が定数になっている場合である。この場合は右辺を $z$ と置けばよい。

$$z = \frac{2x + 3y - 4}{3}$$

とおくと、

$$z' = \frac{2 + 3y'}{3} = \frac{2 + 3z}{3}$$

となる。これは変数分離形の微分方程式であるから、その方法に従って解いた後で

$$z = \frac{2x + 3y - 4}{3}$$

を代入すれば求めるべき一般解が得られる。

#### 例題 5

微分方程式

$$y' = \frac{x^2y}{x^3+y}$$

を解く。

これは、同次形をさらに一般化させた微分方程式である。同次形では、正規形の微分方程式

$$y' = f(x,y)$$

の右辺 $f(x,y)$ について、$\lambda$ を定数として

$$f(\lambda x, \lambda y) = f(x, y)$$

が成り立つ。この例題は同次形ではないため、微分方程式の右辺を $f(x,y)$ と置いてもこれは成り立たない。しかし、

$$f(\lambda x, \lambda^3y) = \frac{(\lambda x)^2\lambda^3y}{(\lambda x)^3 + \lambda^3y} = \lambda^2\frac{x^2y}{x^3+y} = \lambda^2f(x,y)$$

が成り立っている。

一般に、$f(x,y)$ について $n \neq 0$ として

$$f(\lambda x, \lambda^n y) = \lambda^{n-1}f(x, y)$$

が成り立つとき、

$$z = \frac{y}{x^n}$$

とおいて変数変換を施し、式変形を行うことで変数分離形へ持ち込むことができることが知られている。

この例題では、$z = \frac{y}{x^3}$ と置くと、$y = x^3z$ であるから

$$y' = 3x^2z + x^3z'$$

これをもとの微分方程式へ代入すると、

$$\begin{align}
3x^2z + x^3z' &= \frac{x^2x^3z}{x^3+x^3z} \\
              &= \frac{x^2z}{1+z} \\
           z' &= -\frac{3z^2 + 2z}{x(1+z)}
\end{align}$$

となる。これは変数分離形の微分方程式であるから、変数分離形の解法に従って解き、最後に $z = \frac{y}{x^3}$ を代入すればよい。

1 階線型微分方程式
-----------------

1階微分方程式が線型であるとは、与えられた微分方程式が

$$y' + f(x)y = g(x)$$

と書けることである。このように書けない1階微分方程式は1階非線型微分方程式という。

### 斉次 1 階線型微分方程式

斉次 1 階線型微分方程式とは、1 階線型微分方程式であって、特に $g(x)=0$ であるものをいい、この時この微分方程式は斉次であるという。

$g(x) \neq 0$ の場合は非斉次であるという。また、斉次は「同次」とも呼ばれることがあるが、本書では前者で統一することにする。

まずは斉次1階線型微分方程式を解いてみよう。

簡単な微分積分法しか知らない我々は、これ程までに限定してやっと解けるようになるのである。

今解こうとしているのは、次の微分方程式である。

$$y' + f(x)y = 0$$

これは変数分離形の微分方程式である。まず $y \ne 0$ を仮定して、この式を同値変型する。

$${{y'} \over {y}} = -f(x)$$

両辺を積分して

$${\rm ln}|y| = \int{ - f(x)dx + C_0}$$ 両辺を*e*の肩に掛けて、

$$|y| = e^{\int{ - f(x) dx + C_0}}$$

右は常に正なので、$e^{C_0}=C$ として、

$$y = Ce^{\int{ - f(x)dx}}$$

この解法を**変数分離法**といい、得られた結果がこの斉次方程式の一般解である。

一般解はこのようにして求められたが、$y(x_0)=y_0$ となるときの特殊解 $y$ を求めなければならないときもある。斉次1階微分方程式の初期値問題について考えてみよう。

**初期値問題**

$$y' + f(x)y = 0 ; y(x_0)=y_0$$

を解く。

はじめに微分方程式を解くと、先に導いたように一般解

$$y = Ce^{\int{ - f(x)dx}}$$
を得る。この式の両辺に$(x, y) = (x_0, y_0)$を代入すれば、積分定数*C*の値が求められるため、改めてそれをこの式に代入しなおすことで特殊解が得られる。

あるいは、微分方程式を解く際に不定積分ではなく$x_0$から$x$までの定積分を求めることによって初期値問題を解くこともできる。多少厄介だが、積分記号を外せないときにも解を求めることができる。

変数分離を施した形

$$\frac{y'}{y} = -f(x)$$ より、両辺を$x_0$から$x$まで定積分する。

$$\begin{align}
&{\int_{x_0}^{x} {{y'} \over {y}}\ dx'} = {\int_{x_0}^{x}  -f(x')\ dx'} \\
 \iff & {\rm ln}y - {\rm ln}y_0= {{\int_{x_0}^{x} -f(x')\ dx'}} \\
 \iff & {{y} \over {y_0}} = e^{\int_{x_0}^{x} -f(x')\ dx'} \\
 \iff & y=y_0e^{\int_{x_0}^{x}  -f(x')\ dx' } \\
\end{align}$$
結局、一般解における積分定数*C*が$y_0$に、不定積分が定積分になっただけであった。

#### 例題 1

微分方程式

$$y' -4xy = 0$$

を解く。

上の解説の通り、両辺を $y$ で割り変数分離法によって計算する。この微分方程式の一般解は

$$y = Ce^{\int{ - ( -4x )dx }}= Ce^{2x^2}$$

である。

#### 例題 2

次の微分方程式の初期値問題を解け。

$$y' + y \sin x = 0 ; y(0) = {3 \over 2}$$

この微分方程式の一般解として、変数分離法によって

$$y = Ce^{\cos x}$$

が求められる。この式に$(x,y) = (0, 3/2)$を代入すれば、

$$C = \frac{3/2}{e^{\cos 0}} = \frac{3}{2e}$$

したがって求めるべき特殊解は

$$y = \frac{3}{2e}e^{\cos x} = \frac{3}{2}e^{\cos x -1}$$

あるいは、不定積分の代わりに定積分を行うことにより、

$$y={3 \over 2}e^{-\int_{0}^{x} \sin t\ dt}={3 \over 2}e^{\cos x-1}$$

が導かれる。

### 非斉次 1 階線型微分方程式

次に、非斉次1階線型微分方程式

$$y'+ f(x)y = g(x) \tag {1.1}$$

の解き方を考えてみよう。しかし今、我々にできる事は二つしかない。それは、斉次微分方程式を解くことと、各種式変形を行うことである。これを最大限駆使して解くしかない。具体的には、なんとかして(1.1)を斉次微分方程式

$$z'=\nu(x)$$

の形に式変形して、これを解くのである。

天下り式であるが、(1.1) にある関数 $h(x)$ をかけて

$$h(x)y' + h(x)f(x)y = h(x)g(x)\tag {1.2}$$

とする。ここで $h(x)$ が

$$\{h(x)y\}'= h(x)y' + h(x)f(x)y　\tag {1.3}$$

をみたすような関数であるとすると、$z=h(x)y,\nu(x)=h(x)g(x)$とすれば $z'=\nu(x)$ の形に変形できる。

ではそのような$h(x)$は存在するのだろうか。具体的に求めてみる。

$\{h(x)y\}'=h(x)y'+h'(x)y$ であるから、これを(1.3)に代入すると

$$\begin{align}
h(x)y' + h'(x)y &= h(x)y' + h(x)f(x)y \\
          h'(x) &= h(x)f(x)
\end{align}$$

を得る。$h(x)$ についてはこの変数分離形の微分方程式を解けばよい。定数倍は関係ないので、

$$h(x)=e^{\int f(x) dx}　　\tag{1.4}$$

としてよい。この $h(x)$ は**積分因子**と呼ばれる。

さて、(1.2)と(1.3)より、

$$\{h(x)y\}'=h(x)g(x) \tag{1.5}$$

を得る。これを変形すると、

$$\begin{align}
h(x)y &= \int h(x)g(x)dx + C \\
    y &= {1 \over {h(x)}}\left(\int h(x)g(x)dx+C\right)
\end{align}$$

あとはこれに(1.4)を代入すると、一般解

$$y = \frac{1}{e^{\int f(x) dx}}\left(\int e^{\int f(x) dx}g(x)dx + C\right)$$

を得る。

初期値問題

$$y'+f(x)y=g(x) ; \;\; y(x_0)=y_0$$

を解くには、(1.5) の両辺を積分する際に定積分とすれば、

$$\int_{x_0}^x \{h(x)y\}' dx=\int_{x_0}^x h(x)g(x) dx$$

を得る。あとはこれを $y$ について解けばよい。

以上、非斉次微分方程式の解法を述べた。手順をまとめると、

1.  積分因子 $h(x)=e^{\int f(x)dx}$ を求める。
2.  $g(x)$ に積分因子を掛け積分する。
3.  それを $h(x)$ で割って一般解とする。

となる。

非斉次1階線型微分方程式の別の解法として、**定数変化法**と呼ばれる方法を紹介する。

非斉次な微分方程式

$$y' + f(x)y = g(x)$$

を解くのが最終的な目標であるが、ひとまず、右辺を$g(x)=0$とおいて、斉次な微分方程式

$$y' + f(x)y = 0$$

を解くことにする。この形ならば、前々節で見た方法によって、一般解

$$y_h(x) = Ce^{\int{ - f(x)dx}}$$

を得ることができる。ここで、非斉次な場合は積分定数の $C$ が $x$ の関数になると考えて、仮に非斉次微分方程式の解を

$$y = C(x)\; y_h(x)$$

とおく。これを解くべき微分方程式へ代入すると、

$$
\begin{align}
\left\{C(x)y_h(x)\right\}' + f(x)C(x)y_h(x) &= g(x) \\
C'y_h + Cy_h' + f(x)Cy_h &= g(x) \\
C'y_h + C(y_h' + f(x)y_h) &= g(x)
\end{align}
$$

となるが、ここで $y_h$ が斉次微分方程式 $y'+f(x)y = 0$ の解であることから、

$$C'(x)y_h(x) = g(x)$$

が得られる。この中で未知関数は $C'(x)$ のみであるから、両辺を $y_h(x)$ で割って $x$ で積分すると、

$$\begin{align}
C(x) &= \int \frac{g(x)}{y_h(x)} dx + C \\
     &= \int \frac{g(x)}{e^{\int{ - f(x)dx}}} dx + C \\
     &= \int g(x)e^{\int f(x)dx} dx + C
\end{align}$$

したがって、求めるべき非斉次微分方程式の一般解は、

$$y = C(x)y_h(x) = e^{\int -f(x)dx} \left\{\int g(x)e^{\int f(x)dx} dx + C\right\}$$

となる。これは積分因子を用いて求めた一般解と等しい。

# いまここ

#### 例題 1

微分方程式

$$y'-2xy=x$$

を解く。

$f(x)=-2x$より、積分因子$h(x)$は、

$$h(x) = e^{\int -2xdx} = e^{-x^2}$$
である。これを与式右辺（$g(x)$）に掛けて積分すると、

$$\int h(x)g(x)dx = \int e^{-x^2}xdx = -\frac{1}{2}e^{-x^2} + C$$ (1.6)
したがって、微分方程式の一般解は

$$y=\frac{-\frac{1}{2}e^{-x^2} + C}{e^{-x^2}} = -{1\over 2}+Ce^{x^2}$$
となる。

あるいは、定数変化法によって求めることもできる。仮に斉次な微分方程式

$$y'-2xy=0$$ を解くと、この一般解は

$$y = Ce^{x^2}$$ となる。これより、仮に求めるべき微分方程式の解を

$$y = C(x)e^{x^2}$$ と置いて元の微分方程式に代入すると、

$$C'(x)e^{x^2} = x$$ が得られる。これより、

$$C(x) = \int\frac{x}{e^{x^2}}dx = \int e^{-x^2}xdx = -\frac{1}{2}e^{-x^2} + C$$
となるから、求める一般解は

$$y = e^{x^2}\left(-\frac{1}{2}e^{-x^2} + C\right) = -\frac{1}{2} + Ce^{x^2}$$
である。

```python
#### 例題 2

初期値問題$y'-2xy=x ; y(1)=2$を解く。

例題1で(1.6)を積分するときに定積分にする。

$$\begin{align}
\int_1^x \{ye^{-t^2}\}'dt &= \int_1^x e^{-t^2}tdt \\
\left[ye^{-t^2}\right]_1^x &= \left[-{1\over 2}e^{-t^2}\right]_1^x \\
ye^{-x^2}-2e^{-1} &= -{1\over 2}e^{-x^2}+{1\over 2}e^{-1} \\
ye^{-x^2} &= -{1\over 2}e^{-x^2}+{5\over 2}e^{-1}
\end{align}$$ したがって求める特殊解は

$$y(x)=-{1\over 2}+{5\over 2}e^{x^2-1}$$

あるいは、例題1で求めた一般解に$(x, y) = (1, 2)$を代入することによって*C*の値を求めてもよい。

```

```python
### ベルヌーイの微分方程式

1 階微分方程式のなかでも、特に

$$y' + f(x)y = g(x)y^n$$

の形の微分方程式をベルヌーイ（Bernoulli）の微分方程式と呼ぶ。$n = 0, 1$であれば上で見た非斉次1階微分方程式あるいは斉次1階微分方程式の形となり、これらの解法が適用できるが、それ以外の場合でも適切な式変形によって線型微分方程式へ帰着できることが知られている。

ベルヌーイの1階微分方程式

$$y' + f(x)y = g(x)y^n, (n \ne 0, 1)$$ の両辺に$(1-n)y^{-n}$をかけると、

$$(1-n)y^{-n}y' + f(x)(1-n)y^{1-n} = g(x)(1-n)$$
となるから、ここで$z = y^{1-n}$とおくと、$z' = (1-n)y^{-n}y'$なので、

$$z' + f(x)(1-n)z = g(x)(1-n)$$
となる。これは $z$ に関する1階線型微分方程式であるから、定数変化法あるいは積分因子を用いる方法によって計算することができて、一般解

$$z = e^{-(1-n)\int f(x)dx} \left\{(1-n)\int g(x)e^{(1-n)\int f(x)dx} dx + C\right\}$$
を得る。これに$z=y^{1-n}$を代入しなおすと、

$$\begin{align}
y^{1-n} &= e^{-(1-n)\int f(x)dx} \left\{(1-n)\int g(x)e^{(1-n)\int f(x)dx} dx + C\right\} \\
      y &= e^{-\int f(x)dx} \left\{(1-n)\int g(x)e^{(1-n)\int f(x)dx} dx + C\right\}^\frac{1}{1-n}
\end{align}$$ を得る。

```

```python
### リッカチの微分方程式

1階微分方程式のなかでも、特に

$$y' = f(x)y^2 + g(x)y + h(x)$$
の形に書くことのできる微分法定式をリッカチ（Riccati）の微分方程式と呼ぶ。この形の方程式は初等解法によって一般解を求めることはできない。しかし、なにか1つの特殊解$y_0$が見つかれば、それを元にして一般解を求めることができる。

リッカチの微分方程式

$$y' = f(x)y^2 + g(x)y + h(x)$$
について、ある特殊解$y_0$が与えられているとする。この時、$z = y - y_0$とおいて元の微分方程式へ代入すると、

$$\begin{align}
z' + y_0' &= f(x)(z + y_0)^2 + g(x)(z + y_0) + h(x) \\
       z' &= f(x)z^2 + \left\{2f(x)y_0 + g(x)\right\}z + \left\{f(x)y_0^2 + g(x)y_0 + h(x) - y_0'\right\}
\end{align}$$ となる。ここで$y_0$がこの微分方程式の特殊解であることから

$$y_0' = f(x)y_0^2 + g(x)y_0 + g(x)$$ が成り立っているので、

$$z' = f(x)z^2 + \left\{2f(x)y_0 + g(x)\right\}z$$
となる。これはベルヌーイの微分方程式で$n = 2$の場合であるから、[前節で見た方法で解くことができる](#ベルヌーイの微分方程式 "wikilink")。両辺に$-z^{-2}$をかけて

$$-z^{-2}z' = -f(x) - \left\{2f(x)y_0 + g(x)\right\}z^{-1}$$
さらに$u = z^{-1}$とおくと$u' = -z^{-2}z'$であるから

$$u' = -f(x) - \left\{2f(x)y_0 + g(x)\right\}u$$
となって、1階線型微分方程式に帰着する。この一般解は、前節で見た式から

$$z = e^{\int \left\{2f(x)y_0 + g(x)\right\}dx} \left\{-\int f(x)e^{\int \left\{2f(x)y_0 + g(x)\right\}dx} dx + C\right\}^{-1}$$
となり、求めるべき微分方程式の一般解も

$$\begin{align}
y = z + y_0 &= e^{\int \left\{2f(x)y_0 + g(x)\right\}dx} \left\{-\int f(x)e^{\int \left\{2f(x)y_0 + g(x)\right\}dx} dx + C\right\}^{-1} + y_0 \\
&= \frac{e^{\int \left\{2f(x)y_0 + g(x)\right\}dx}}{-\int f(x)e^{\int \left\{2f(x)y_0 + g(x)\right\}dx} dx + C} + y_0
\end{align}$$ と求まる。

```

```python
### 演習

次の方程式を解け

1.  $f'(x)+f(x)\cos x=0$
2.  $f'(x)+f(x)\sqrt x \sin x=0$
3.  $f'(x)+f(x){{2x}\over {x^2+1}}={1 \over {x^2+1}}$
4.  $f'(x)+f(x)=xe^x$
5.  $f'(x)+f(x)x^2=1$
6.  $f'(x)+f(x){x \over {x^2+1}}=1-{x^3 \over {x^4+1}}f(x)$
7.  $f'(x)+f(x)\sqrt{x^2+1}=0,f(0)=\sqrt 5$
8.  $f'(x)+f(x)\sqrt{x^2+1}e^-x=0,f(0)=1$
9.  $f'(x)+f(x)\sqrt{x^2+1}e^-x=0,f(0)=0$
10. $f'(x)-2xf(x)=x,f(0)=1$
11. $f'(x)+xf(x)=x+1,f({3 \over 2})=0$
12. $f'(x)+2f(x)={1 \over {x^2+1}},f(1)=2$
13. $f'(x)-2xf(x)=x,f(0)=1$
14. $(x^2+1)f'(x)+xf(x)=(x^2+1)^{5 \over 2}$
15. $(x^2+1)f'(x)+3xf(x)=(x^2+1)^{5 \over 2},f(1)={1 \over 3}$

### 原子核の崩壊速度

線型微分方程式のひとつの応用例として、原子核の崩壊に関するものを見てみよう。

物理学者ラザフォードは、放射性元素の原子核は不安定で、一定の割合で崩壊する事を示した。つまり、原子核の数をyという関数で表すことにすれば

:   y\'=-λy　　　　　　　　　　　　　　　　　　　(5.1)

という関係式が成り立つ。ここで比例定数λは崩壊定数と呼ばれる正数である。
この関係式は、まさに一階線形常微分方程式となっているので、これまでに述べた方法で解くことができる。
y(x~0~)=y~0~とすれば、(5.1)は

$$y=y_0e^{-\lambda(x-x_0)}$$　　　　　　　　　　　　　　　　　　　(5.2)

と解ける。適当に文字を置き換えると、[高等学校理科 物理II
原子と原子核の](高等学校理科_物理II_原子と原子核 "wikilink")1.2.3で述べた式が導かれたことになる。

```

```python
一階定数係数連立線型常微分方程式と高階定数係数線型常微分方程式
--------------------------------------------------------------

### 連立線型常微分方程式と行列の指数関数

上の節では一階の線型常微分方程式の解法を述べた。その中でも最もやさしい定数係数の方程式

$$y'=ay$$ の解は、変数分離法により簡単に求まり、

$$y=Ce^{xa}$$ であった。ただし、C=y(0)である。

次に、 $n$ 本の一階定数係数線型常微分方程式を連立させた方程式

$$\begin{cases}
y_1'=a_{11}y_1+a_{12}y_2+\cdots a_{1n}y_n \\
y_2'=a_{21}y_1+a_{22}y_2+\cdots a_{2n}y_n \\
\vdots \\
y_n'=a_{n1}y_1+a_{n2}y_2+\cdots a_{nn}y_n
\end{cases}$$ を考えよう。この方程式は、行列を用いて

$$\mathbf{y}'=A\mathbf{y} \cdots \bigstar$$ と表すことができる。ただし
$\mathbf{y}=\begin{pmatrix}
y_1 \\
y_2 \\
\vdots \\
y_n
\end{pmatrix},A=\begin{pmatrix}
a_{11}&a_{12}&\cdots&a_{1n} \\
a_{21}&a_{22}&\cdots&a_{2n} \\
\vdots&&&\vdots\\
a_{n1}&a_{n2}&\cdots&a_{nn} \\
\end{pmatrix}$である。

方程式が1本のときの例から類推すれば、この連立方程式の解は

$$e^{xA}$$
のようなものが定義できれば、それを用いて表せそうである。しかし、行列の指数関数をどうやって定義すればよいだろうか？そのために、そもそも実数上の関数としての指数関数がどのように定義されるかを考えてみると、次のようにしてTaylor展開で定義できることが思い出される。

$$e^x=\sum_{k=0}^\infty \frac{x^k}{k!}$$
行列であっても、この式に代入することは可能そうである。すなわち、次のように定義する。

**定義**　正方行列*A*に対して、$e^{xA}:=\sum_{k=0}^\infty \frac{(xA)^k}{k!}$

この級数が収束するのか、またどの程度よい収束をするのかが問題だが、結論から言えば一様絶対収束する。詳しい証明は省くが、ゆえにこの級数を項別微分することができ、

$$(e^{xA})'=Ae^{xA}$$ が成り立つ。

このことから、連立線型微分方程式は初期条件を与えると次のように解けることがわかる。

**定理**

$$\mathbf{y}=e^{xA}\mathbf{y}(0)$$

:   は、方程式$\bigstar$の初期値$\mathbf{y}(0)=\begin{pmatrix}c_1 \\ c_2 \\ \cdots \\ c_n\end{pmatrix}$における解になっている。

実際に解になっていることは代入によって確かめることができる。

### 高階定数係数線型常微分方程式

次に、 $n$ 階の定数係数線型常微分方程式

$$y^{(n)}=a_{n-1}y^{(n-1)}+\cdots+a_1y'+a_0y$$
を考える。この方程式は、実は次のようにして連立常微分方程式とみなして行列を使って表せる。

$$\frac{d}{dx}\begin{pmatrix}y \\ y' \\ \vdots \\ y^{(n-2)} \\ y^{(n-1)}\end{pmatrix}=
\begin{pmatrix}
0&1&0&\cdots&0 \\
0&0&1&\cdots&0 \\
\vdots&&&&\vdots \\
0&0&0&\cdots&1 \\
a_0&a_1&a_2&\cdots&a_{n-1}
\end{pmatrix}
\begin{pmatrix}y \\ y' \\ \vdots \\ y^{(n-2)} \\ y^{(n-1)}\end{pmatrix}$$
よって、上の節で述べた方法により初期値問題を解くことができる。

### 具体的な行列に対する計算法

では、具体的な係数行列が与えられたとき、どのようにすれば行列の指数関数が計算できるかを見てみよう。

#### 対角行列の場合

対角行列

$$D=\begin{pmatrix}
c_1&0&\cdots&0 \\
0&c_2&\cdots&0 \\
\vdots&&&\vdots \\
0&0&\cdots&c_n
\end{pmatrix}$$ に対して$e^{xD}$を計算してみよう。

すぐにわかるように、

$$D^k=\begin{pmatrix}
c_1^k&0&\cdots&0 \\
0&c_2^k&\cdots&0 \\
\vdots&&&\vdots \\
0&0&\cdots&c_n^k
\end{pmatrix}$$ である。よって、各成分ごとの計算から
$e^{xD}=\begin{pmatrix}
e^{c_1x}&0&\cdots&0 \\
0&e^{c_2x}&\cdots&0 \\
\vdots&&&\vdots \\
0&0&\cdots&e^{c_nx}
\end{pmatrix}$ である。

#### 対角化可能な行列の場合

行列*A*が$P^{-1}AP=D$と対角化可能な場合も行列の指数関数は容易に計算できる。なぜならば、

$$A^k=(PDP^{-1})^k=PD^kP^{-1}$$ なので、これを代入することで

$$e^{xA}=Pe^{xD}P^{-1}$$
となり、対角行列の指数関数は容易に計算できるからである。

#### 対角化不可能な行列の場合

係数行列が対角化不可能なときは上記のようにはいかず、一般にはJordan標準形を用いることになる。しかし、特殊な場合にはそこまでの計算をする必要はない。たとえば、固有値がすべて等しい場合には次のようにして計算することができる。

 $n$ 次正方行列*A*の $n$ 個の固有値がすべて$\lambda$のとき、この行列の固有多項式は$(t-\lambda)^n$なので、Cayley-Hamiltonの定理より

$$(A-\lambda I)^n=O$$ である。このことを用いると、

$$\begin{align}
e^{xA}
&=e^{\lambda xI+x(A-\lambda I)} \\
&=e^{\lambda x}\sum_{k=0}^\infty \frac{x^k(A-\lambda I)^k}{k!} \\
&=e^{\lambda x}\sum_{k=0}^{n-1} \frac{x^k(A-\lambda I)^k}{k!} \\
\end{align}$$ と有限回の計算で指数関数を計算することができる。

```

```python
### 具体例

二階の線型常微分方程式の具体例として、ばねにつながれた物体の運動を記述してみよう。ばねにつながれた物体の時刻 $x$ における変位を $y$ とする。このとき、ばねから物体が受ける力は（負の比例定数で）変位に比例することが知られている。このことを用いて物体の運動方程式を記述すると、

$$y''=\frac{-k}{m}y$$
となる。ただし*k*はばね定数と呼ばれる正の数、*m*は物体の質量である。

この方程式を行列を用いて書き直すと、

$$\frac{d}{dx}\begin{pmatrix}y \\ y'\end{pmatrix}=
\begin{pmatrix}0&1 \\ \frac{-k}{m}&0 \end{pmatrix}
\begin{pmatrix}y \\ y'\end{pmatrix}$$
と表せる。$A=\begin{pmatrix}0&1 \\ \frac{-k}{m}&0 \end{pmatrix}$とする。この行列は対角化できるので、指数関数が計算できて、

$$e^{xA}=
\begin{pmatrix}
\cosh(i\sqrt{\frac{k}{m}}x) & -i\sqrt{\frac{m}{k}}\sinh(i\sqrt{\frac{k}{m}}x) \\
i\sqrt{\frac{k}{m}}\sinh(i\sqrt{\frac{k}{m}}x) & \cosh(i\sqrt{\frac{k}{m}}x) \\
\end{pmatrix}=\begin{pmatrix}
\cos(\sqrt{\frac{k}{m}}x) & \sqrt{\frac{m}{k}}\sin(\sqrt{\frac{k}{m}}x) \\
-\sqrt{\frac{k}{m}}\sin(\sqrt{\frac{k}{m}}x) & \cos(\sqrt{\frac{k}{m}}x) \\
\end{pmatrix}$$ である。初期条件を
$\begin{pmatrix} y(0) \\ y'(0)\end{pmatrix}=\begin{pmatrix} y_0 \\ v_0 \end{pmatrix}$
で定めると、解は

$$\begin{pmatrix} y \\ y'\end{pmatrix}=\begin{pmatrix}
\cos(\sqrt{\frac{k}{m}}x) & \sqrt{\frac{m}{k}}\sin(\sqrt{\frac{k}{m}}x) \\
-\sqrt{\frac{k}{m}}\sin(\sqrt{\frac{k}{m}}x) & \cos(\sqrt{\frac{k}{m}}x) \\
\end{pmatrix}\begin{pmatrix} y_0 \\ v_0 \end{pmatrix}=\begin{pmatrix}y_0\cos(\sqrt{\frac{k}{m}}x)+v_0\sqrt{\frac{m}{k}}\sin(\sqrt{\frac{k}{m}}x) \\
-y_0\sqrt{\frac{k}{m}}\sin(\sqrt{\frac{k}{m}}x)+v_0\cos(\sqrt{\frac{k}{m}}x) \end{pmatrix}$$
と求められた。これがばねによって振動する物体の時刻 $x$ における変位と速度である。

常微分方程式の初期値問題の解の存在と一意性
------------------------------------------

前節まででみたように、いくつかの微分方程式については積分計算によって解を具体的に求めることができるが、一方でそのような求積法の存在しない常微分方程式も多い。だが、そのような方程式についても、ある条件を満たせば解の存在や一意性が保証されることがある。ここではそのような例を見ていこう。

もし解の存在や一意性が保証されるならば、簡単に求積できない微分方程式でも少しは調べやすくなる。一意性が保証されるということは、まぐれやあてずっぽうであっても解をひとつみつけさえすれば、解けたのと同じになるからだ。また、ここで扱う存在と一意性に関する定理は、その解を（ある関数列の極限として）具体的に構成する方法を含んでおり、その意味であてずっぽうではなく解を見つける方法を提供してくれてもいるのである。

本節では、独立変数 $x$ の関数 $y$ についての1階常微分方程式

$$y'=f(x,y) ,\ y(x_0)=y_0$$...(\*)
について考える。関数 $y$ が(\*)を満たすことは、

$$y(x)=y_0+\int_{x_0}^x f(t,y(t)) dt$$...(\*)\'
を満たすことと同値であることも注意しておく。2変数関数$f(x,y)$に対していくつかの仮定を課したときに、この方程式の解がどのように構成されるかを見ていく。

```

```python
### 冪級数による解法

本節では、*f*が次の仮定(H1)を満たすとする。

:   (H1)
    $f(x,y)$は点$(x_0,y_0)$の近傍で解析的（すなわち冪級数展開可能）であり、$f(x,y)=\sum_{j,l=0}^\infty f_{j,l} (x-x_0)^j (y-y_0)^l$と表される。

このとき、次が成り立つ。

**定理5.1.1**
$f(x,y)$が仮定(H1)を満たすとき、(\*)を満たす$x=x_0$の近傍で解析的な関数 $y$ がただひとつ存在する。

これを証明したい。ただ、冪級数の中心が一般の形だと計算が煩雑になるので、ここでは次の形の定理を証明することにする。

**定理5.1.1**\'
$f(x,y)$が原点の近傍で解析的であり、$f(x,y)=\sum_{j,l=0}^\infty f_{j,l} x^j y^l$と表されるとき、常微分方程式

$$y'=f(x,y) ,\ y(0)=0$$...(☆)
を満たす$x=0$の近傍で解析的な関数 $y$ がただひとつ存在する。

いくつかの補題に分けて証明しよう。

**補題5.1.2**
冪級数$y=\sum_{j=0}^\infty y_j x^j$であって(☆)を満たすものがあるならば、その係数$y_j$は一意に定まる。

（証明）
$y_0=0$である。$j \ge 1$のときは$y=\sum_{j=0}^\infty y_j x^j$を(☆)に代入すると、

$$(lhs)=y_1 +2y_2 x+3y_3 x^2+\cdots$$

$$\begin{align}(rhs)
&=f_{0,0}+f_{0,1}(y_1 x+y_2 x^2+\cdots)+f_{0,2}(y_1 x+y_2 x^2+\cdots)^2+f_{1,0} x+f_{1,1} x(y_1 x+y_2 x^2+\cdots)+f_{2,0} x^2+\cdots \\
&=f_{0,0}+(f_{0,1} y_1+f_{1,0}) x+ (f_{0,1}y_2+f_{0,2}y_1^2+f_{1,1}y_1+f_{2,0}) x^2+\cdots \\
\end{align}$$
であり、次数の低い方から係数を比較することで、係数$y_j$が順に決まっていくことがわかる。//

**補題5.1.3**
$f(x,y)$の冪級数展開の優級数$F(x,y)$があるとき、常微分方程式

$$y'=F(x,y) ,\ y(0)=0$$...(☆)\'
の冪級数解は、補題5.1.2で定まる(☆)の冪級数解の優級数である。

（証明）
$f(x,y)=\sum_{j,l=0}^\infty f_{j,l} x^j y^l,F(x,y)=\sum_{j,l=0}^\infty F_{j,l} x^j y^l$とし、(☆)の解を$y=\sum_{j=0}^\infty y_j x^j$、(☆)\'の解を$Y=\sum_{j=0}^\infty Y_j x^j$とする。
ただし、$y,Y$の冪級数表示は現時点では収束性については何も分かっていない、形式的冪級数である。すべての*j*,*l*について$|f_{j,l}| \le |F_{j,l}|$が成り立つならばすべての*j*について$|y_j| \le |Y_j|$であることを数学的帰納法で証明する。$y_0=Y_0=0,y_1=f_{0,0},Y_1=F_{0,0}$なので、$j=0,1$のときは成り立つ。
$j \le m$なるすべての*j*で成り立つと仮定する。補題5.1.2の証明から、$y_m$は$f_{j,l} (j+l \le m-1),y_{j} (j \le m-1)$に関する多項式の値であり、その係数は非負である。$Y_m$も同様に、同じ非負係数多項式に$F_{j,l} (j+l \le m-1),Y_{j} (j \le m-1)$を代入した値である。よって、帰納法の仮定より、$|y_m| \le |Y_m|$が成り立つ。よって、すべての自然数*j*について$|y_j| \le |Y_j|$が成り立つ。//

**補題5.1.4**
$f(x,y)$が原点の近傍$|x| \le r,|y| \le \rho$において$|f(x,y)| \le M$を満たすとき、$\sum_{j,l=0}^\infty \frac{M}{r^j \rho^l} x^j y^l$は$f(x,y)$の冪級数展開の優級数である。

（証明）
$|f_{j,l}| \le \frac{M}{r^j \rho^l}$を示せばよい。$f(x,y)$の定義域を複素変数に拡張して[w:コーシーの積分公式を用いると](w:コーシーの積分公式 "wikilink")、$|x|<r,|y|<\rho$のとき
$f(x,y)=\int_{|\zeta|=r} \frac{d\zeta}{2i\pi}\int_{|\xi|=\rho} \frac{d\xi}{2i\pi} \frac{f(\zeta,\xi)}{(\zeta-x)(\xi-y)}=-\frac{1}{4\pi^2}\int_{|\zeta|=r} d\zeta \int_{|\xi=\rho} d\xi f(\zeta,\xi)\left(\sum_{j=0}^\infty \frac{x^j}{\zeta^{j+1}}\right)\left(\sum_{l=0}^\infty \frac{y^l}{\xi^{l+1}}\right)=-\sum_{j,l=0}^\infty \frac{1}{4\pi^2} \int_{|\zeta|=r} d\zeta \int_{|\xi|=\rho} d\xi \frac{f(\zeta,\xi)}{\zeta^{j+1}\xi^{l+1}} x^j y^l$であるから、$|f_{j,l}| \le \frac{1}{4\pi^2} \int_{|\zeta|=r} d\zeta \int_{|\xi|=\rho} d\xi \frac{|f(\zeta,\xi)|}{|\zeta|^{j+1}|\xi|^{l+1}} \le \frac{M}{r^j \rho^l}$である。//

**補題5.1.5**
補題5.1.3の微分方程式で$F(x,y)=\sum_{j,l=0}^\infty \frac{M}{r^j \rho^l} x^j y^l$としたものの解は、$x=0$の近傍で解析的な関数であり、収束する冪級数で表される。

（証明）

$$y'=\sum_{j,l=0}^\infty \frac{M}{r^j \rho^l} x^j y^l=\frac{M}{\left(1-\frac{x}{r}\right)\left(1-\frac{y}{\rho}\right)}$$
は変数分離形なので解を求めることができて、

$$\left(1-\frac{y}{\rho}\right)dy=\frac{M}{\left(1-\frac{x}{r}\right)}dx$$

$$-\frac{\rho}{2}\left(1-\frac{y}{\rho}\right)^2=-Mr\log\left(1-\frac{x}{r}\right)+C$$
であり、$y(0)=0$より$C=-\frac{\rho}{2}$であることに注意して整理すると、

$$y=\rho-\sqrt{\rho\left(\rho+2Mr\log\left(1-\frac{x}{r}\right)\right)}$$
である。これは確かに$|x| < r(1-e^{-\frac{\rho}{2Mr}})$で解析的な関数である。//

（定理5.1.1'の証明）
補題5.1.3,5.1.4,5.1.5より、補題5.1.2の冪級数は収束する優級数をもち、したがって自身も収束する。よって、この冪級数の極限として、解が一意的に存在することがわかる。//

```

```python
### ピカールの逐次近似法

本節では、*f*が次の仮定(H2)を満たすとする。

:   (H2)
    $f(x,y)$は点$(x_0,y_0)$の近傍$D=\{|x-x_0| \le r,|y-y_0| \le \rho\}$でリプシッツ連続である、すなわちある*K*に対して$|f(x_1,y_1)-f(x_2,y_2)| \le K \sqrt{(x_1-x_2)^2+(y_1-y_2)^2}$が成り立つ。

このとき、解は次のようにして構成される。

**定義5.2.1**
$f(x,y)$が仮定(H2)を満たすとき、漸化式$y_{j+1}(x)=y_0+\int_{x_0}^x f(t,y_j(t)) dx,\ y_0(x)=y_0$で定まる関数列$y_j$を**ピカールの逐次近似列**という。

**定理5.2.2**
$f(x,y)$が仮定(H2)を満たすとき、$M=\max_{(x,y) \in D} |f(x,y)|,\delta=\min\left\{\frac{\rho}{M},r\right\}$とする。閉区間$[x_0-\delta,x_0+\delta]$において(\*)を満たす関数 $y$ がただひとつ存在し、それはピカールの逐次近似列$y_j$の$j \to \infty$における極限として定義される。

これをいくつかの補題に分けて証明しよう。

**補題5.2.3**
$x_0-\delta \le x \le x_0+\delta$のとき、$\left|\int_{x_0}^x f(t,y(t)) dt\right| \le M|x-x_0| \le \rho$である。

（証明）
$\left|\int_{x_0}^x f(t,y(t)) dt\right| \le M|x-x_0| \le M\delta \le \rho$//

補題5.2.3を帰納的に用いることで、任意の*j*について$y_j$の値域が$|y-y_0| \le \rho$に含まれ、したがって関数列$y_j$がwell-definedであることが従う。

次に、解の一意性を先に示しておこう。

**補題5.2.4**
$f(x,y)$が仮定(H2)を満たすとき、閉区間$[x_0-\delta,x_0+\delta]$において(\*)を満たす関数はただ一つである。

（証明）
$y(x),\tilde{y}(x)$がともに(\*)\'を満たすとすると、*f*がリプシッツ連続であることから

$$|y(x)-\tilde{y}(x)|=\left|\int_{x_0}^x f(t,y(t))-f(t,\tilde{y}(t))dt\right| \le \left|\int_{x_0}^x |f(t,y(t))-f(t,\tilde{y}(t))|dt\right| \le K\left|\int_{x_0}^x |y(t)-\tilde{y}(t)| dt\right|$$
である。一方、補題5.2.3より、

$$|y(x)-\tilde{y}(x)| \le |y(x)-y_0|+|\tilde{y}(x)-y_0|=\left|\int_{x_0}^x f(t,y(t)) dt\right|+\left|\int_{x_0}^x f(t,\tilde{y}(t)) dt\right| \le 2\rho$$
なので、

$$|y(x)-\tilde{y}(x)| \le 2\rho K|x-x_0|$$ であり、よって

$$|y(x)-\tilde{y}(x)| \le \left|\int_{x_0}^x 2\rho K|t-x_0| dt\right|=2\rho \frac{(K|x-x_0|)^2}{2}$$
である。同様に繰り返すことで、任意の自然数*l*に対して

$$|y(x)-\tilde{y}(x)| \le 2\rho \frac{(K|x-x_0|)^l}{l!} \le 2\rho \frac{(K\delta)^l}{l!}$$
であることがわかるが、$\lim_{l \to \infty} \frac{(K\delta)^l}{l!}=0$なので、$y(x)=\tilde{y}(x)$である。//

**補題5.2.6** 関数列$y_j$は一様収束する。

（証明） 補題5.2.3と*f*がリプシッツ連続であることより、

$$|y_1(x)-y_0| \le M|x-x_0|$$

$$|y_2(x)-y_1(x)| \le\left|\int_{x_0}^x |f(t,y_1(t))-f(t,y_0(t))| dt\right| \le K\left|\int_{x_0}^x |y_1(t)-y_0(t)| dt\right| \le K\int_{x_0}^x M|t-x_0| dt \le KM\frac{|x-x_0|^2}{2}$$
以下同様に繰り返して、

$$|y_j(x)-y_{j-1}(x)| \le \frac{M}{K}\frac{(K|x-x_0|)^l}{l!} \le \frac{M}{K}\frac{(K\delta)^l}{l!}$$
である。よって、$j<l$のとき

$$|y_j(x)-y_l(x)| \le \sum_{i=j+1}^l  \frac{M}{K}\frac{(K\delta)^i}{i!}$$
であるが、右辺は$j \to \infty$において0に収束するので、$y_j$は一様収束する。//

以上で定理5.2.2を示す準備は整った。

（定理5.2.2の証明）
$y_j$が一様収束することに注意して$y_{j+1}(x)=y_0+\int_{x_0}^x f(t,y_j(t)) dx$の両辺の$j \to \infty$における極限を考えると、

$$\lim_{j \to \infty}y_j(x)=y_0+\int_{x_0}^x f(t,\lim_{j \to \infty}y_j(t)) dx$$
である。これは$\lim_{j \to \infty}y_j(x)$が(\*)\'の解であることを示している。

```

```python
### コーシーの折れ線とペアノの定理

本節では、*f*が次の仮定(H3)を満たすとする。

:   (H3)
    $f(x,y)$は点$(x_0,y_0)$の近傍$D=\{|x-x_0| \le r,|y-y_0| \le \rho\}$で連続である。

このとき、解は次のようにして構成される。

**定義5.3.1**
自然数*j*に対し、$x_0-r \le x \le x_0+r$における関数$y_j$を次のように定める。まず、$y_j(x_0)=y_0$とする。次に、非負整数*i*に対して$x_i=x_0+\frac{ir}{j}$と定め、$x_i< x \le x_{i+1}$のときには

$$y_j(x)=y_0+\frac{r}{j}\sum_{d=0}^{i-1}f(x_d,y_j(x_d))+(x-x_i)f(x_i,y_j(x_i))$$...(\#)
とする。$x<0$についても同様にする。このようにして定まる関数$y_j$のグラフは連続な折れ線になり、これを**コーシーの折れ線**という。

前節までに見た「解析的」や「リプシッツ連続」と比べ、「連続」はとても弱い仮定であり、より多くの関数が満たしている。だがその分本節では解の一意性は失われ、存在しか示すことができない。すなわち、次が成り立つのみである。

**定理5.3.2**
コーシーの折れ線$y_j$は一様収束する部分列$y_{j_l}$を持ち、$\lim_{l \to \infty} y_{j_l}$は方程式(\*)の解である。

まず、次の補題を示す。

**補題5.3.3**
$M=\max_{(x,y) \in D} |f(x,y)|,\delta=\min\left\{\frac{\rho}{M},r\right\}$とする。関数列$y_j$は$x_0-\delta \le x \le x_0+\delta$において一様有界かつ同程度連続である。

（証明） 定義より

$$|y_j(x_1)-y_j(x_2)| \le M|x_1-x_2|$$
なので、同程度連続である。また、この式で$x_2=0$とすると

$$|y_j(x)| \le |y_0|+M\delta \le |y_0|+\rho$$ なので、一様有界である。//

補題5.3.3と[アスコリ＝アルツェラの定理により](解析学基礎/関数列の極限#アスコリ＝アルツェラの定理 "wikilink")、$y_j$が一様収束する部分列を持つことがわかるので、あとはこの部分列の極限が解になっていることを示せばよい。

（定理5.3.2の証明）
$y(x)=\lim_{l \to \infty} y_{j_l}$が(\*)\'の解になっていることを示したい。(\*)と(\#)を辺々引いた式

$$y(x)-y_{j,l}(x)+\sum_{d=0}^{i-1} \int_{x_d}^{x_{d+1}} (f(x,y(x))-f(x_d,y_j(x_d))) dx+\int_{t_i}^t(f(x,y(x))-f(x_i,y_j(x_i))) dx=0$$
が成り立つことを示せばよい。左辺を$A_{j_l}$とする。$A_{j_l}=0$を示したい。まず、任意の$\varepsilon>0$に対してある $n$ が存在して、$l>N$ならば$|y(x)-y_{j_l}(x)|<\varepsilon$である。次に有界閉集合*D*上の連続関数*f*は一様連続なので、任意の$\varepsilon'>0$に対して、$|x_1-x_2|+|y_1-y_2|<\delta'$ならば$|f(x_1,y_1)-f(x_2,y_2)|<\varepsilon'$となるように、$\delta'>0$をとることができる。この$\delta$に対して$\varepsilon<\frac{\delta'}{2}$を満たすように$\varepsilon>0$をとり、この$\varepsilon$に対して$|y(x)-y_{j_l}(x)|<\varepsilon$かつ$(M+1)\frac{r}{j_l}<\frac{\delta'}{2}$を満たすように*l*をとれば、$x_d< x \le x_{d+1}$のときには

$$|x-x_d|+|y(x)-y_{j_l}(x_d)| \le |x-x_d|+|y(x)-y_{j_l}(x)|+|y_{j_l}(x)-y_{j_l}(x_d)| \le \frac{r}{j_l}+\varepsilon+M\frac{r}{j_l}<\delta'$$
なので、$|f(x,y(x))-f(x_d,y_j(x_d))|<\varepsilon'$である。よって、

$$|A_{j_l}| < \varepsilon+(i+1)\int_{x_d}^{x_{d+1}} \varepsilon'=\varepsilon+\frac{(i+1)r\varepsilon'}{j_l}$$
である。$\varepsilon,\varepsilon'$は任意なので、$A_{j_l}=0$である。

```

```python
陰関数型の1階常微分方程式
-------------------------

陰関数型の1階常微分方程式

$$\displaystyle F \Bigl( k+\ell x+my, \; \; \frac{dy}{dx} \; \Bigr)= 0$$

は求積法で一般解を表示することができる。ここに、$F$
は任意の既知関数であり、$k,\; \ell,\; m$ は任意定数である。
この陰関数型1階常微分方程式の一般解は、次に示す三通りの式で与えられる。

:

    :   $x = \! \int \! \left\{ \frac{1}{\; \ell + m \psi (t)\;} \cdot \frac{\; d \phi (t) \;}{dt} \; \right\} dt+C,$

<!-- -->

:

    :   $k + \ell x + my = \phi (t),$

<!-- -->

:

    :   $F \bigl( \phi (t), \; \psi (t) \bigr) \equiv 0.$

ここに、$t$ は媒介変数であり、$\phi (t)$ と $\psi (t)$ は $t$ の関数で、
$F \bigl( \phi (t),\; \psi (t) \bigr) \equiv 0$ は
$\phi (t),\; \psi (t)$ に関する恒等式である。なお $C$ は積分定数である。
以下で、その解法を示す。

与えられた常微分方程式
$\displaystyle F \Bigl( k+\ell x+my,\; \; \frac{dy}{dx} \; \Bigr)= 0$
に対して、$t$ を媒介変数とする任意関数 $\phi (t), \; \psi (t)$
を導入し、

:

    :   $k + \ell x + my = \phi (t),$

<!-- -->

:

    :   $\displaystyle \frac{dy}{dx} = \psi (t)$

と置く。ただし、$m\ne 0$ とする。 上式 $k + \ell x + my =\phi (t)$
の両辺を$x$で微分すると、

$$\displaystyle \ell + m \frac{dy}{dx} = \frac{d \phi (t)}{dt} \cdot \frac{dt}{dx}$$

となる。 ここで、$\frac{dy}{dx} = \psi (t)$ と
$\ell + m \frac{dy}{dx} = \frac{d \phi (t)}{dt} \cdot \frac{dt}{dx}$
から $\frac{dy}{dx}$ を消去すると、

$$\displaystyle \ell + m \psi(t) = \frac{d \phi (t)}{dt} \cdot \frac{dt}{dx}$$

を得る。この式を変形すると、

$$\frac{dx}{dt} = \frac{1}{\; \ell + m \psi (t)\;} \cdot \frac{d \phi (t)}{dt}$$

となる。上式は変数分離形であるから積分すると、

$$x= \! \int \! \left\{ \frac{1}{\; \ell + m \psi (t)\;}
         \cdot \frac{\; {d} \phi (t)\; }{dt} \, \right\} dt+C$$

となり、$x$ が $t$ の関数として表示された。 これにより $y$ は
$k + \ell x + my =\phi (t)$ と上式
$x = \int \left\{ \frac{1}{\;\ell+m\psi(t)\;} \cdot \frac{d \phi (t)}{dt} \right\} dt+C$
により $t$ の関数として与えられる。なお $C$ は積分定数である。

```

```python
#### 例題 1

陰関数型の関数 $F$ が、
$F \bigl( \phi (t),\; \psi (t) \bigr) = \phi (t) - \psi (t) = 0$
のとき、

:

    :   $x = \! \int \! \left\{ \frac{1}{\; \ell + m \psi (t)\;} \cdot \frac{\; d \phi (t) \;}{dt} \, \right\} dt+C,$

<!-- -->

:

    :   $k + \ell x + my = \phi (t)$

から、一般解を求めよ。 解きかたは、$\psi (t)=\phi (t)$ の関係を
$x = \! \int \! \left\{ \frac{1}{\; \ell + m \psi (t)\;} \cdot \frac{\; d \phi (t) \;}{dt} \; \right\} dt+C$
に適用すればよい。

```

```python
#### 例題 2

陰関数型の関数 $F$ が、
$F \bigl( \phi (t), \; \psi (t) \bigr) = \phi (t) - \psi (t) = 0$
の場合は、解き始めの仮定 $k + \ell x + my = \phi (t)$ と
$\frac{dy}{dx} = \psi (t)$ から、

:

    :   $k + \ell x + my = \frac{dy}{dx}$

が成り立つ。この式は、1階線型常微分方程式であるから求積法で解ける。
その一般解と、「例題1」の一般解とが一致することを確かめよ。
（注：積分定数は異なる形をしている。）

[しようひふんほうていしき](Category:解析学 "wikilink")

```


---
title: '曾经的神：Support Vector Machine'
date: 2021-07-30
permalink: /posts/2021/07/svmyyds/
tags:
  - Machine Learning
---
# Intro
最近申请工作的时候被问到了，自己这个菜鸡🐔对国内工作不熟悉加上没有充分准备，感觉回答的不是很好😭，
今天，我们就来好好梳理一下什么是SVM，怎么用，原理和trick～

---

# Support Vector Machine (Classifier)
suggest readings:
- Alpaydin: 10.3, 13.1, 13.2
- Murphy: 14.5.2.2
- Geron: chapter 5, appendix C

## Alpaydin 阅读笔记

### [10.3] Geometry of the Linear Discriminant （线性可区分的图像）
- 从一个简单的 two classes 情况开始解析，在这种情况下，单个的区分方程已经足够：
- Discriminant between two classes:
$$
\begin{equation}\label{Discriminant_between_two_classes}
\begin{split}
g(\chi) &= g_1(\chi) - g_2(\chi)\\
        &= ((w_1)^T\chi + w_{10}) - ((w_2)^T\chi + w_{20})\\
        &= (w_1 - w_2)^T\chi + (w_{10} - w_{20})\\
        &= w^T\chi + w_0
\end{split}
\end{equation}
$$
- we choose
$$
\left\{
        \begin{matrix}
            C_1,\text{ }g(\chi) > 0\\
            C_2,\text{ }otherwise
        \end{matrix}
\right.
$$

`weight vector threshold`:
- defines a hyperplane where $w$ is the weight vector and $w_0$ is the threshold.

### Decision rule
- choose $C_1$ if $w^T\chi > -w_0$, and choose $C_2$ otherwise. The hyperplane divides the input space into two half-spaces: decision region $R_1$ for $C_1$ and $R_2$ for $C_2$. Any $\chi$ in $R_1$ is on the `positive` side of the hyperplane and any $\chi$ in $R_2$ is on the `negative` side.

### Decision bounduary equation
- Let's take 2 points on the decision surface; that is, $g(\chi_1) = g(\chi_2) = 0$, then

$$
\begin{equation}
\begin{split}
    w^T\chi_1 + w_0 &= w^T\chi_2 + w_0\\
    w^T(\chi_1 - \chi_2) &= 0
\end{split}
\end{equation}
$$

and we see that $w$ is *normal* to any vector lying on the hyperplane. Let's rewrite $\chi$ as

$$
\begin{equation}
    \chi = \chi_p + r\frac{w}{||w||}
\end{equation}
$$

- $\chi_p$: normal projection of $\chi$ onto the hyperplane
- $r$: the distance from $\chi$ to the hyperplane
    - $<0$ if $\chi$ is on the negative side
    - $>0$ if $\chi$ is on the positive side

$$
\begin{equation}
    r = \frac{g(\chi)}{||w||}
\end{equation}
$$

So, at the origin the distance $r_0$ will be

$$
\begin{equation}
    r_0 = \frac{w_0}{||w||}
\end{equation}
$$

Thus $w_0$ determines the `location` of the hyperplane with respect to the origin, and $w$ determines its `orientation`.

### Multiple Classes

When there are K>2 classes, there are K discriminant（可辨别的）funtions. When they are linear, we have

$$
\begin{equation}
g_i(\chi|w_i, w_{i0}) = w^{T}_i\chi + w_{i0}
\end{equation}
$$

We assume that the parameters, $w_i$, $w_i0$, are computed so as to have
$$
g_i(\chi)=
\left\{
        \begin{matrix}
            C_1,\text{ }g(\chi) > 0\\
            C_2,\text{ }otherwise
        \end{matrix}
\right.
$$

for all $\chi$ in the training set.

- Using such discriminant functions corresponds to *assuming* that ``all calsses are linearly separable``

- Sometimes not all the points can lies either in (+) or (-) class. The susal approach is to assign $\chi$ to the class having the highest discriminant:
    - choose $C_i$ if $g_i(\chi) = max^K_{j=1}g_j(\chi)$
        - linear classifier: recall the $\frac{|g_i(\chi)|}{||w_i||}$ is the ``distance from the input point to the hyperplane``, assuming $w_i$ have similar length, this assigns the point to the class(among all $g_j(\chi) > 0$) to whose hyperplane the point is most distant.
        - linear classifier cont: it geometrically divides the feature space into K convex decision regions $R_i$

### [13.1] Kernel Machines
> *Kernel Machines* are `maximum margin methods` that allow the model to be written as a sum of the influences of a subset of the training instances. These influences are given by application-specific similarity kernels, and we discuss "kernelized" classification, regression, outlier detection, and dimensionality reduction, as well as *how to choose and use kernels*.

---

- Each learning algorithm has a different `inductive bias`, makes different `assumptions`, and defines a different `objective function` and thus may find a different *linear model*.
- The model `support vector machine`(SVM) was generalized under the name `kernel machine`

  It is popular because:
- It never solve a more complex problem as a `first step` before the actual problem(Vapnik 1995)
- Goal: We only need to find the boundary separating those $\chi$ that have low $p(\chi)$, that is, $\chi$ where $p(x) < \theta$, for some threshold $\theta \in (0,1)$

### Linear Discriminant

- Generative vs. Discriminative
    * Generative model
        - lkj

### Support Vector Machine (SVM)

### Primal & Dual Unconstrained Optimization

```python

```