# Two Asset HANK Model [](https://cepr.org/active/publications/discussion_papers/dp.php?dpno=13071)

[](https://mybinder.org/v2/gh/econ-ark/HARK/BayerLuetticke/notebooks?filepath=HARK%2FBayerLuetticke%2FTwoAsset.ipynb)

- Adapted from original slides by Christian Bayer and Ralph Luetticke (Henceforth, 'BL')
- Jupyter notebook originally by Seungcheol Lee
- Further edits by Chris Carroll, Tao Wang, Edmund Crawley

### Overview

BL propose a method for solving Heterogeneous Agent DSGE models that uses fast tools originally employed for image and video compression to speed up a variant of the solution methods proposed by Michael Reiter.

The Bayer-Luetticke method has the following broad features:
   * The model is formulated and solved in discrete time (in contrast with some other recent approaches )
   * Solution begins by calculation of the steady-state equilibrium (StE) with no aggregate shocks
   * Both the representation of the consumer's problem and the desciption of the distribution are subjected to a form of "dimensionality reduction"
      * This means finding a way to represent them efficiently using fewer points
   * "Dimensionality reduction" of the consumer's decision problem is performed before any further analysis is done
      * This involves finding a representation of the policy functions using some class of basis functions
   * Dimensionality reduction of the joint distribution is accomplished using a "copula"
      * See the companion notebook for description of the copula
   * The method approximates the business-cycle-induced _deviations_ of the individual policy functions from those that characterize the riskless StE
      * This is done using the same basis functions originally optimized to match the StE individual policy function
      * The method of capturing dynamic deviations from a reference frame is akin to video compression

### Setup

#### The Recursive Dynamic Planning Problem

BL describe their problem a generic way; here, we will illustrate the meaning of their derivations and notation using the familiar example of the Krusell-Smith model, henceforth KS.

Consider a household problem in presence of aggregate and idiosyncratic risk
   * $S_t$ is an (exogenous) aggregate state (e.g., levels of productivity and unemployment)
   * $s_{it}$ is a partly endogenous idiosyncratic state (e.g., wealth)
   * $\mu_t$ is the distribution over $s$ at date $t$ (e.g., the wealth distribution)
   * $P_{t}$ is the pricing kernel
      * It captures the info about the aggregate state that the consumer needs to know in order to behave optimally
      * e.g., KS showed that for their problem, a good _approximation_ to $P_{t}$ could be constructed using only $S_{t}$ and the aggregate capital stock $K_{t}$
   * $\Gamma$ defines the budget set
      * This delimits the set of feasible choices $x$ that the agent can make

The Bellman equation is:

\begin{equation}
        v(s_{it},S_t,\mu_t) = \max\limits_{x \in \Gamma(s_{it},P_t)} u(s_{it},x) + \beta \mathbb{E}_{t} v(s_{it+1}(x,s_{it}),S_{t+1},\mu_{t+1})
\end{equation}

which, for many types of problems, implies an Euler equation:
     \begin{equation}
        u^{\prime}\left(x(s_{it},S_t,\mu_t)\right) = \beta R(S_t,\mu_t) \mathbb{E}_{t} u^{\prime}\left(x(s_{it+1},S_{t+1},\mu_{t+1})\right)
     \end{equation}

#### Solving for the StE

The steady-state equilibrium is the one that will come about if there are no aggregate risks (and consumers know this)

The first step is to solve for the steady-state:
   * Discretize the state space
      * Representing the nodes of the discretization in a set of vectors
      * Such vectors will be represented by an overbar
         * e.g. $\bar{m}$ is the nodes of cash-on-hand $m$
   * The optimal policy $\newcommand{\policy}{c}\newcommand{\Policy}{C}\policy(s_{i};P)$ induces flow utility $u_{\policy}$ whose discretization is a vector $\bar{u}_{\bar{\policy}}$
   * Idiosyncratic dynamics are captured by a transition probability matrix $\Pi_{\bar{\policy}}$
       * $\Pi$ is like an expectations operator
       * It depends on the vectorization of the policy function $\bar{\policy}$
   * $P$ is constant because in StE aggregate prices are constant
       * e.g., in the KS problem, $P$ would contain the (constant) wage and interest rates
   * In StE, the discretized Bellman equation implies
     \begin{equation}
        \bar{v} = \bar{u}_c + \beta \Pi_{\bar{\policy}}\bar{v}
      \end{equation}
     holds for the optimal policy
     * A linear interpolator is used to represent the value function
   * For the distribution, which (by the definition of steady state) is constant:

\begin{eqnarray}
        \bar{\mu} & = & \bar{\mu} \Pi_{\bar{\policy}} \\
        d\bar{\mu} & = & d\bar{\mu} \Pi_{\bar{\policy}}
\end{eqnarray}
     where we differentiate in the second line because we will be representing the distribution as a histogram, which counts the _extra_ population obtained by moving up

We will define an approximate equilibrium in which:
   * $\bar{\policy}$ is the vector that defines a linear interpolating policy function $\policy$ at the state nodes
       * given $P$ and $v$
       * $v$ is a linear interpolation of $\bar{v}$
       * $\bar{v}$ is value at the discretized nodes
   * $\bar{v}$ and $d\bar{\mu}$ solve the approximated Bellman equation
       * subject to the steady-state constraint
   * Markets clear ($\exists$ joint requirement on $\bar{\policy}$, $\mu$, and $P$; denoted as $\Phi(\bar{\policy}, \mu, P) = 0$)

This can be solved by:
   1. Given $P$,
       1. Finding $d\bar{\mu}$ as the unit-eigenvalue of $\Pi_{\bar{\policy}}$
       2. Using standard solution techniques to solve the micro decision problem
          * Like wage and interest rate
   2. Using a root-finder to solve for $P$
      * This basically iterates the other two steps until it finds values where they are consistent

####  Introducing aggregate risk

With aggregate risk
   * Prices $P$ and the distribution $\mu$ change over time

Yet, for the household:
   * Only prices and continuation values matter
   * The distribution does not influence decisions directly

#### Redefining equilibrium (Reiter, 2002)
A sequential equilibrium with recursive individual planning   is:
   * A sequence of discretized Bellman equations, such that
     \begin{equation}
        v_t = \bar{u}_{P_t} + \beta \Pi_{\policy_t} v_{t+1}
     \end{equation}
     holds for policy $\policy_t$ which optimizes with respect to $v_{t+1}$ and $P_t$
   * and a sequence of "histograms" (discretized distributions), such that
     \begin{equation}
        d\mu_{t+1} = d\mu_t \Pi_{\policy_t}
     \end{equation}
     holds given the policy $h_{t}$, that is optimal given $P_t$, $v_{t+1}$
   * Prices, distribution, and policies lead to market clearing

```python
from __future__ import print_function

# This is a jupytext paired notebook that autogenerates a corresponding .py file
# which can be executed from a terminal command line via "ipython [name].py"
# But a terminal does not permit inline figures, so we need to test jupyter vs terminal
# Google "how can I check if code is executed in the ipython notebook"

def in_ipynb():
    try:
        if str(type(get_ipython())) == "<class 'ipykernel.zmqshell.ZMQInteractiveShell'>":
            return True
        else:
            return False
    except NameError:
        return False

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

# The tools for navigating the filesystem
import sys
import os

# Find pathname to this file:
my_file_path = os.path.dirname(os.path.abspath("TwoAsset.ipynb"))

# Relative directory for pickled code
code_dir = os.path.join(my_file_path, "../Assets/Two")

sys.path.insert(0, code_dir)
sys.path.insert(0, my_file_path)
```

```python
## Load Stationary equilibrium (StE) object EX3SS_20

import pickle
os.chdir(code_dir) # Go to the directory with pickled code

## EX3SS_20.p is the information in the stationary equilibrium (20: the number of illiquid and liquid weath grids )
EX3SS=pickle.load(open("EX3SS_20.p", "rb"))

## WangTao: Find the code that generates this
```

#### Compact notation

It will be convenient to rewrite the problem using a compact notation proposed by Schmidt-Grohe and Uribe (2004)

The equilibrium conditions can be represented as a non-linear difference equation
   * Controls: $Y_t = [v_t \ P_t \ Z_t^Y]$ and States: $X_t=[\mu_t \ S_t \ Z_t^X]$
      * where $Z_t$ are purely aggregate states/controls
   * Define
     \begin{align}
      F(d\mu_t, S_t, d\mu_{t+1}, S_{t+1}, v_t, P_t, v_{t+1}, P_{t+1}, \epsilon_{t+1})
      &= \begin{bmatrix}
           d\mu_{t+1} - d\mu_t\Pi_{\policy_t} \\
           v_t - (\bar{u}_{\policy_t} + \beta \Pi_{\policy_t}v_{t+1}) \\
           S_{t+1} - \Policy(S_t,d\mu_t,\epsilon_{t+1}) \\
           \Phi(\policy_t,d\mu_t,P_t,S_t) \\
           \epsilon_{t+1}
           \end{bmatrix}
     \end{align}
     s.t.
     \begin{equation}
     \policy_t(s_{t}) = \arg \max\limits_{x \in \Gamma(s,P_t)} u(s,x) + \beta \mathop{\mathbb{E}_{t}} v_{t+1}(s_{t+1})
     \end{equation}
   * The solution is a function-valued difference equation:
\begin{equation}
     \mathop{\mathbb{E}_{t}}F(X_t,X_{t+1},Y_t,Y_{t+1},\epsilon_{t+1}) = 0
\end{equation}
     where $\mathop{\mathbb{E}}$ is the expectation over aggregate states
   * It becomes real-valued when we replace the functions by their discretized counterparts
   * Standard techniques can solve the discretized version

#### So, is all solved?
The dimensionality of the system F is a big problem
   * With high dimensional idiosyncratic states, discretized value functions and distributions become large objects
   * For example:
      * 4 income states $\times$ 100 illiquid capital states $\times$ 100 liquid capital states $\rightarrow$ $\geq$ 40,000 values in $F$

### Bayer-Luetticke method
#### Idea:
1. Use compression techniques as in video encoding
   * Apply a discrete cosine transformation (DCT) to all value/policy functions
      * DCT is used because it is the default in the video encoding literature
      * Choice of cosine is unimportant; linear basis functions might work just as well
   * Represent fluctuations as differences from this reference frame
   * Assume all coefficients of the DCT from the StE that are close to zero do not change when there is an aggregate shock (small things stay small)

2. Assume no changes in the rank correlation structure of $\mu$
   * Calculate the Copula, $\bar{C}$ of $\mu$ in the StE
   * Perturb only the marginal distributions
      * This assumes that the rank correlations remain the same
      * See the companion notebook for more discussion of this
   * Use fixed Copula to calculate an approximate joint distribution from marginals

The approach follows the insight of KS in that it uses the fact that some moments of the distribution do not matter for aggregate dynamics

#### Details
1) Compression techniques from video encoding
   * Let $\bar{\Theta} = dct(\bar{v})$ be the coefficients obtained from the DCT of the value function in StE
   * Define an index set $\mathop{I}$ that contains the x percent largest (i.e. most important) elements from $\bar{\Theta}$
   * Let $\theta$ be a sparse vector with non-zero entries only for elements $i \in \mathop{I}$
   * Define
   \begin{equation}
    \tilde{\Theta}(\theta_t)=\left\{
      \begin{array}{@{}ll@{}}
         \bar{\Theta}(i)+\theta_t(i), & i \in \mathop{I} \\
         \bar{\Theta}(i), & \text{else}
      \end{array}\right.
   \end{equation}
   * This assumes that the basis functions with least contribution to representation of the function in levels, make no contribution at all to its changes over time

2) Decoding
   * Now we reconstruct $\tilde{v}(\theta_t)=dct^{-1}(\tilde{\Theta}(\theta_{t}))$
      * idct=$dct^{-1}$ is the inverse dct that goes from the $\theta$ vector to the corresponding values
   * This means that in the StE the reduction step adds no addtional approximation error:
       * Remember that $\tilde{v}(0)=\bar{v}$ by construction
   * But it allows us to reduce the number of derivatives that need to be calculated from the outset.
       * We only calculate derivatives for those basis functions that make an important contribution to the representation of the function

3) The histogram is recovered as follows
   * $\mu_t$ is approximated as $\bar{C}(\bar{\mu_t}^1,...,\bar{\mu_t}^n)$, where $n$ is the dimensionality of the idiosyncratic states
      * $\mu_t^{i}$ are the marginal distributions
   * The StE distribution is obtained when $\mu = \bar{C}(\bar{\mu}^1,...,\bar{\mu}^n)$
   * Typically prices are only influenced through the marginal distributions
   * The approach ensures that changes in the mass of one state (say, wealth) are distributed in a sensible way across the other dimensions
      * Where "sensible" means "like in StE"
   * The implied distributions look "similar" to the StE one (different in (Reiter, 2009))

4) The large system above is now transformed into a much smaller system:
     \begin{align}
      F(\{d\mu_t^1,...,d\mu_t^n\}, S_t, \{d\mu_{t+1}^1,...,d\mu_{t+1}^n\}, S_{t+1}, \theta_t, P_t, \theta_{t+1}, P_{t+1})
      &= \begin{bmatrix}
           d\bar{C}(\bar{\mu}_t^1,...,\bar{\mu}_t^n) - d\bar{C}(\bar{\mu}_t^1,...,\bar{\mu}_t^n)\Pi_{\policy_t} \\
           dct\left[idct\left(\tilde{\Theta}(\theta_t) - (\bar{u}_{\policy_t} + \beta \Pi_{\policy_t}idct(\tilde{\Theta}(\theta_{t+1}))\right)\right] \\
           S_{t+1} - \Policy(S_t,d\mu_t) \\
           \Phi(\policy_t,d\mu_t,P_t,S_t) \\
           \end{bmatrix}
     \end{align}

### The two-asset HANK model

We illustrate the algorithm in a two-asset HANK model described as below

#### Households
- Maximizing discounted felicity
   - Consumption $c$
      - CRRA coefficent: $\xi$
      - EOS of CES consumption bundle: $\eta$
   - Disutility from work in GHH form:
     - Frisch elasticity $\gamma$
- Two assets:
   - Liquid nominal bonds $b$, greater than lower bound $\underline b$
      - Borrowing constraint due to a wedge between borrowing and saving rate:  $R^b(b<0)=R^B(b>0)+\bar R$
   - Illiquid assets capital $k$ nonnegative
      - Trading of illiquid assets is subject to a friction governed by $v$, the fraction of agents who can trade
      - If nontrading, receive dividend $r$ and depreciates by $\tau$
- Idiosyncratic labor productivity $h$:
   - $h = 0$ for entreprener, only receive profits $\Pi$
   - $h = 1$ for labor, evolves according to an autoregressive process,
     - $\rho_h$ persistence parameter
     - $\epsilon^h$: idiosyncratic risk

#### Production
- Intermediate good producer
    - CRS production with TFP $Z$
    - Wage $W$
    - Cost of capital $r+\delta$
- Reseller
    - Rotemberg price setting: quadratic adjustment cost scalled by $\frac{\eta}{2\kappa}$
    - Constant discount factor $\beta$
    - Investment subject to Tobin's q adjustment cost $\phi$
- Aggregate risks $\Omega$ include
   - TFP $Z$, AR(1) process with persistence of $\rho^Z$ and shock $\epsilon^Z$
   - Uncertainty
   - Monetary policy
- Central bank
   - Taylor rule on nominal saving rate $R^B$: reacts to deviation of inflation from target by $\theta_R$
   - $\rho_R$: policy innertia
   - $\epsilon^R$: monetary policy shocks
- Government (fiscal rule)
   - Government spending $G$
   - Tax $T$
   - $\rho_G$: intensity of repaying government debt: $\rho_G=1$ implies roll-over

#### Taking stock

- Individual state variables: $\newcommand{\liquid}{m}\liquid$, $k$ and $h$, the joint distribution of individual states $\Theta$
- Individual control variables: $c$, $n$, $\liquid'$, $k'$
- Optimal policy for adjusters and nonadjusters are $c^*_a$, $n^*_a$ $k^*_a$ and $\liquid^*_a$ and  $c^*_n$, $n^*_n$ and $\liquid^*_n$, respectively

```python
import time
from HARK.BayerLuetticke.Assets.Two.FluctuationsTwoAsset import FluctuationsTwoAsset, SGU_solver, plot_IRF

start_time = time.perf_counter()

## Choose an aggregate shock to perturb(one of three shocks: MP, TFP, Uncertainty)

# EX3SS['par']['aggrshock']           = 'MP'
# EX3SS['par']['rhoS']    = 0.0      # Persistence of variance
# EX3SS['par']['sigmaS']  = 0.001    # STD of variance shocks

#EX3SS['par']['aggrshock']           = 'TFP'
#EX3SS['par']['rhoS']    = 0.95
#EX3SS['par']['sigmaS']  = 0.0075

EX3SS['par']['aggrshock'] = 'Uncertainty'
EX3SS['par']['rhoS'] = 0.001    # Persistence of variance
EX3SS['par']['sigmaS'] = 0.02    # STD of variance shocks

## Choose an accuracy of approximation with DCT
### Determines number of basis functions chosen -- enough to match this accuracy
### EX3SS is precomputed steady-state pulled in above
EX3SS['par']['accuracy'] = 0.99999

## Implement state reduction and DCT
### Do state reduction on steady state

EX3SR = FluctuationsTwoAsset(**EX3SS)
SR = EX3SR.StateReduc()

print('SGU_solver')
SGUresult = SGU_solver(SR['Xss'],SR['Yss'],SR['Gamma_state'],SR['indexMUdct'],SR['indexVKdct'],SR['par'],SR['mpar'],SR['grid'],SR['targets'],SR['Copula'],SR['P_H'],SR['aggrshock'])
print('plot_IRF')
plot_IRF(SR['mpar'],SR['par'],SGUresult['gx'],SGUresult['hx'],SR['joint_distr'],
         SR['Gamma_state'],SR['grid'],SR['targets'],SR['Output'])

end_time = time.perf_counter()
print('Elapsed time is ',  (end_time-start_time), ' seconds.')
```

```python

```


# Welfare Costs of Short-Run Price Volatility

```python
%run requirements.ipynb
```

## **1: Setup**

We consider the incredibly simple static maximization problem:

$$\begin{align}
    U &= \max_{C,E} C^{\alpha}E^{1-\alpha}, \\
    \text{s.t. }I&= C+pE,
\end{align}$$
with the standard simple solution
$$\begin{align}
    C &= \alpha I \\
    E &= (1-\alpha)\dfrac{I}{p}
\end{align}$$

Consider instead the case where $E$ is split into a number of hours $h\in\lbrace 1,...,H\rbrace$ with hourly prices $p_h$. Augment the utility function to accomodate bliss point preferences / price volatility aversion. As outlined in the paper this implies the form
$$\begin{align}
    U = \max_{C,E} C^{\alpha}E^{1-\alpha}+u^{hour}(E_1,...,E_H)+u^{h,scale}(E),
\end{align}$$
with $E=\sum_h E_h$ and $p = \sum_h p_hE_h/E$.

The first order conditions are now on the form:
$$\begin{align}
\lambda &= \alpha \left(\dfrac{C}{E}\right)^{\alpha-1} \tag{1}\\
p_h \lambda &= (1-\alpha)\left(\dfrac{C}{E}\right)^{\alpha}+\dfrac{\partial u^{hour}}{\partial E_h}+\dfrac{\partial u^{h,scale}}{\partial E} \tag{2}
\end{align}$$

Combination of (2) for hours $h,j$ implies
$$\begin{align}
    \dfrac{\partial u^{hour}}{\partial E_h} - \dfrac{\partial u^{hour}}{\partial E_j} = \lambda(p_h-p_j). \tag{3}
\end{align}$$

## **2: Logit example**

The logit-like example applies the functional forms (notation from paper adopted):

$$\begin{align}
    u_h &=  \dfrac{1}{\sigma}\ln\left(\tilde{u}_h\right) \\
    \tilde{u}_h&= \exp\left(\sigma\lambda(p_h-p_t)\right) \\
    \chi_h(\tilde{u}_h) &= \underline{D}+\dfrac{(1-\underline{D})(\overline{D}-\underline{D})}{1-\underline{D}+(\overline{D}-1)\tilde{u}_h}
\end{align}$$

Using the scenario with *level* bliss points the implied utility function becomes (notation in the paper):
$$\begin{align}
    u_h^{hour} = \dfrac{1}{\sigma}\left[E_h\ln\left(\dfrac{1-\underline{D}}{\overline{D}-1}\right)-(E_h-\underline{E}_h)\ln\left(E_h-\underline{E}_h\right)-\left(\overline{E}_h-E_h\right)\ln\left(\overline{E}_h-E_h\right)\right]
\end{align}$$

*Note: In the paper we define level bliss-point and share bliss-point preferences. The formulation with upper and lower bounds applied here is not consistent with the share bliss-point preferences, as this requires $\sum_h \chi_h = 1$ per construction. In the MNL/GNMNL examples outlined below, we present both types.*

### **2.1: Initialize instance of Logit-class and solve**

Initialize Logit class with default parameters. The settings include:
* "I": Exogenous income variable. Default value = $1000$.
* "alpha": Preference parameter $(\alpha)$. Default value = $0.5$.
* "sigma": Preference parameter $(\sigma)$. Default value = $1$.
* "upper": Preference parameter $(\overline{D})$. Default value = $2$.
* "lower": Preference parameter $(\underline{D})$. Default value = $0$.
* "eps": Technical parameter to increase numerical stability for unbounded functions / functions with unbounded support. Default value = $0$.
* "n": Number of hours in a year $(H)$. Default value = $500$.

**Sample function parameters (See appendix A for more)**
* "loc": If hourly prices are sampled, this is the mean of the distribution. Default = $200$.
* "variance_p": If hourly prices are sampled, this is the variance. Default = 1.
* "variance_g": If hourly preferences are sampled, this is the variance of the truncated distribution. Default = 1.
* "corr": If hourly prices and preferences are sampled, this is the correlation coefficient of the samples. Default = 0.
* "seed": If sampling is used, seed is set to ensure replicability. Default = 30.
* "u_cge": Utility function of arguments $C,E$. Default = "lambda C,E: $C^{\alpha}*E^{1-\alpha}$".

```python
TestL = WCPV.Logit() # Initialize
# TestL.__dict__
```

Draw a random sample of preference parameters $\lbrace g_h\rbrace_{h=1}^H$ and hourly prices $\lbrace p_h\rbrace_{h=1}^H$:

```python
TestL.draw_sample() # Add sample
```

Solve model with and without flexible prices:

```python
TestL.solve_npv() # solve without price volatility
TestL.solve_fp() # solve w. flexible prices
```

    The solution converged.

Utiliy function:

```python
TestL.plt_uhour()
```

### **2.2: Comparing $E_h$ with flexible and non-flexible prices**

Compare hourly consumption with/without flexible prices (plot next to samples of $p_h,g_h$):

```python
TestL.plots(['plt_compare_eh','plt_ph','plt_gh'])
```

Scatter-plot of $E_h,p_h,g_h$, with (fp) and without (npv) flexible prices:

```python
TestL.plots(['plt_eh_npv_gh','plt_eh_npv_ph','plt_eh_fp_gh', 'plt_eh_fp_ph'])
```

With flexible prices hourly demand correlates negatively with prices. However, with the current parameter-values they are still predominantly driven by habits. Note that this is in part due to the limited variation in prices (only $\sigma_p=1$), and in part due to parameter-values. To see this, let us try to update $\sigma$ and $\sigma_p$ respectively:

### **2.3: The effect of a higher $\sigma$**

Increase $\sigma$ from $1$ to $100$:

*Note: There are two update functions; "upd_par" updates parameter values (and utility functions). "upd_all" updates parameters, samples, and solutions.*

```python
TestL.upd_all({'sigma': 10})
```

    The solution converged.

Re-plot:

```python
TestL.plots(['plt_compare_eh','plt_ph','plt_eh_fp_gh', 'plt_eh_fp_ph'])
```

It is clear that price variation induces much larger responses in the hourly consumption. Note that as the bounds on hourly demand are on $0,2$ times the bliss-point share consumption, the lower bound (in levels) is the same across hours, wheres it differs on the upper bound. Thus the lower bound is visually more pronounced than the upper bound.

### **2.4: The effect of higher price volatility**

Let's reset the value of $\sigma=1$, and instead increase the variance of prices $p_h$ from $1$ to $100$:

```python
TestL.upd_all({'sigma': 1,
               'variance_p': 100})
```

    The solution converged.

```python
TestL.plots(['plt_compare_eh','plt_ph','plt_eh_fp_gh', 'plt_eh_fp_ph'])
```

### **2.5: The effect of changing the bounds**

Let's next consider the effect of changing e.g. $\underline{D}=-0.5$ instead, (and let $\sigma_p=1$ again):

```python
TestL.upd_all({'lower': -0.5,
              'variance_p': 1})
```

    The solution converged.

Note that chaning $\underline{D},\overline{D}$ alters the shape of the utility function, but leaves the bliss-point unaltered:

```python
TestL.plt_uhour()
```

```python
TestL.plots(['plt_compare_eh','plt_ph','plt_eh_fp_gh', 'plt_eh_fp_ph'])
```

Now, let's increse the variance in prices as well:

```python
TestL.upd_all({'variance_p': 100})
```

    The solution converged.

```python
TestL.plots(['plt_eh_fp_gh','plt_eh_fp_ph'])
```

## **3: Multinomial Logit Example**

The logit-like example applies the functional forms (notation from paper adopted):

$$\begin{align}
    u_h &=  -\dfrac{1}{\sigma}\ln\left(\tilde{u}_h\right) \\
    \tilde{u}_h&= \dfrac{\exp\left(\sigma\lambda(p_t-p_h)\right)}{\sum_jg_j\exp\left\lbrace \sigma\lambda\left(p_t-p_j\right)\right\rbrace} \\
    \chi_h(\tilde{u}_h) &= \tilde{u}_h.
\end{align}$$
The demand is then given by
$$\begin{align}
    E_h = g_hE_t^*\tilde{u}_h.
\end{align}$$

Using this formulation we can either have a *level* or a *share* bliss-point. In the case of the *level* bliss-point we have
$$\begin{align}
    u^{hour} = \sum_h E_h\left(\ln(g_hE_t^*)-E_h\left(\ln(E_h)-1\right)\right),
\end{align}$$
where $E_t^*$ is the level of demand in absence of any price variation. Note that as $\sum g_h\chi_h = 1$ in this formulation, the flexible prices does not allow the total consumption of $E$ to change.

With a *share* bliss-point assumption, utility is instead given by:
$$\begin{align}
    u^{hour} = \dfrac{1}{\sigma}\left(E_t\ln\left(E_t\right)+\sum_h E_h\ln\left(g_hE_h\right)\right)
\end{align}$$

In this case demand is given by

$$\begin{align}
E_h = E_t \tilde{u}_h.
\end{align}$$

```python
Settings = {'type': 'MNL',
            'BP_Ref': 'share'}
MNL = WCPV.Logit(name='mnl',**Settings)
```

```python
MNL.draw_sample()
MNL.solve_npv()
MNL.solve_fp()
```

    The solution converged.

Increase $\sigma_p^2$ and lower $\sigma$:

```python
MNL.upd_all({'sigma': 0.1,
             'variance_p': 1000})
MNL.plots(['plt_compare_eh','plt_ph','plt_eh_fp_gh', 'plt_eh_fp_ph'])
```

Note: The MNL formulation is very sensitive to outliers, as we do not have bounds on how much the consumer shifts consumption from one hour to another (the lower bound is zero). Thus changing $\sigma$ slightly up produces a huge outlier:

```python
MNL.upd_all({'sigma': 0.5})
MNL.plots(['plt_compare_eh','plt_ph','plt_eh_fp_gh', 'plt_eh_fp_ph'])
```

## **Appendix A: Drawing samples**

Samples are drawn as follows:
* Define settings:
    * $n$: Sample length,
    * $var_g$: Variance of sample $g_h$,
    * $loc$: average price level,
    * $var_p$: Variance of sample $p_h$,
    * $corr$: correlation between $(g_h,p_h)$ samples.
* Draw sample of $g_h$ from truncated normal on $(0.5,2)$ with variance $var_g$. Normalize to ensure $\sum g_h=1$. Compute variance in normalized version and denote $\sigma_g^2$.
* Generate $p_h$ from
    $$\begin{align}
    p_h = \mu + \beta g_h+\epsilon_h,
    \end{align}$$
    with parameters
    $$\begin{align}
       \beta =& corr \dfrac{\sqrt{var_p}}{\sigma_g} \\
       \mu =& loc-\beta/n \\
       \epsilon_h\sim& \mathscr{N}(0,\sigma_{\epsilon}), && \sigma_{\epsilon} = \sqrt{var_p-\beta^2\sigma_g^2}
    \end{align}$$
* *NB: An optional setting is to pass a 'seed' to the draw functions, to fix the pseudo-random sample. In the class Logit a seed is set automatically. Setting a seed can be avoided by (1) not parsing an argument 'seed' in the draw functions, and (2) using the class Logit by updating parameter 'self.seed' to None.*

## **Appendix B: Increasing price volatility with fixed weighted price levels and correlation patterns**

To evaluate how volatility affects demand and welfare, we take an existing sample of prices and increase volatility. Note: The change in volatility keeps (1) expected price $\sum p_hg_h$ constant, (2) the correlation between $g_h,p_h$ constant. Only increases volatility.

```python
test = WCPV.Logit() # Initialize
test.draw_sample() # Add sample
```

Create a matrix of samples with varying volatility (first sample is in column 1, etc.):

```python
test.perturbed_samples(np.linspace(1,1000,200))
```

Plot them against each other (sorted) to see the effect:

```python
pd.DataFrame({'Low $\sigma_p$': pd.Series(test.samples['ph'][:,10]).sort_values().reset_index(drop=True),
              'Medium $\sigma_p$': pd.Series(test.samples['ph'][:,100]).sort_values().reset_index(drop=True),
              'High $\sigma_p$': pd.Series(test.samples['ph'][:,-1]).sort_values().reset_index(drop=True)}).plot();
```

Check correlation coefficients, average prices etc. for the three samples:

```python
pd.DataFrame({'$\sigma_p^2$': [np.var(test.samples['ph'][:,10]),np.var(test.samples['ph'][:,100]),np.var(test.samples['ph'][:,-1])],
              'Average P': [sum(test.samples['ph'][:,10]*test.gh), sum(test.samples['ph'][:,100]*test.gh),sum(test.samples['ph'][:,-1]*test.gh)],
              'Correlation with $g_h$': [np.corrcoef(test.samples['ph'][:,10],test.gh)[1,0], np.corrcoef(test.samples['ph'][:,100],test.gh)[1,0], np.corrcoef(test.samples['ph'][:,-1],test.gh)[1,0]]})
```

      $\sigma_p^2$
      Average P
      Correlation with $g_h$

      0
      51.081741
      200.028376
      -0.045736

      1
      501.838374
      200.028376
      -0.045736

      2
      997.670670
      200.028376
      -0.045736


## The SEAIRQ model

In the present notebook, we consider the deterministic version of the SEAIRQ model.

In the SEAIRQ model we consider, there are the following classes:

* **$S$** Susceptible
* **$E$** Exposed
* **$A$** Asymptomatic infected (this class is between $E$ and $I_a$, $I_s$, and models the time an individual is already infectious, but it is not clear yet whether the individual will remain asymptomatic)
* **$I_a$** Asymptomatic infected
* **$I_s$** Symptomatic infected
* **$R$** Recovered
* **$Q$** Quarantined

In the SEAIR model, once an individual is exposed, it undergoes the transitions

\begin{equation}
    E \rightarrow A \rightarrow I_x \rightarrow R,
\end{equation}

where $I_x$ is either $I_a$ or $I_s$.

In the SEAIRQ model, there is a finite rate from the states $S$, $E$, $A$, $I_a$, $I_s$ to a quarantined class $Q$, in which an individual does not contribute to any infection rates anymore.

# 0. Definition of the model

```python
%%capture
## compile PyRoss for this notebook
import os
owd = os.getcwd()
os.chdir('../../')
%run setup.py install
os.chdir(owd)
```

```python
%matplotlib inline
import numpy as np
import pyross
import matplotlib.pyplot as plt
#from matplotlib import rc; rc('text', usetex=True)
```

```python
M = 1                  # the SIR model has no age structure
Ni = 10000*np.ones(M)   # so there is only one age group
N = np.sum(Ni)         # and the total population is the size of this age group

alpha = 0              # fraction of asymptomatic infectives
beta  = 0.2            # infection rate

gE    = 1.             # progression rate from E to A
gA   = 0.2            # rate to go from A to Ia
gIa   = 0.1            # recovery rate of asymptomatic infectives
gIs   = 0.1            # recovery rate of symptomatic infectives
gE    = 0.04           # recovery rate of E
fsa   = 1              # the self-isolation parameter

E0  = np.array([0])
A0  = np.array([1])
Ia0 = np.array([0])     # the SIR model has only one kind of infective
Is0 = np.array([20])     # we take these to be symptomatic
Q0  = np.array([0])
R0  = np.array([0])     # and assume there are no recovered individuals initially
S0  = N-(Ia0+Is0+R0+E0)    # The initial susceptibles are obtained from S + E + A + Ia + Is + R = N

# there is no contact structure
def contactMatrix(t):
    return np.identity(M)

# duration of simulation and number of datapoints
Tf = 500;  Nt=Tf+1;
```

# 1. SEAIR model

```python
# For the SEAIR model, we set all rates to the Q-class to zero
tE    = 0.00           # rate E  -> Q
tA    = 0.00           # rate A  -> Q
tIa   = 0.00           # rate Ia -> Q
tIs   = 0.00           # rate Is -> Q
#tIs   = 0.05           # rate Is -> Q

# instantiate model
parameters = {'alpha':alpha, 'beta':beta,
              'gE':gE,'gA':gA,
              'gIa':gIa, 'gIs':gIs, 'gE':gE, 'fsa':fsa}
model = pyross.deterministic.SEAIR(parameters, M, Ni)

# simulate model
data=model.simulate(S0, E0, A0, Ia0, Is0, contactMatrix, Tf, Nt)
```

```python
# plot the results of the simulation

S  = data['X'][:,0].flatten()
E  = data['X'][:,1].flatten()
A  = data['X'][:,2].flatten()
Ia = data['X'][:,3].flatten()
Is = data['X'][:,4].flatten()
t = data['t']

fig = plt.figure(num=None, figsize=(10, 8), dpi=80, facecolor='w', edgecolor='k')
plt.rcParams.update({'font.size': 22})

plt.fill_between(t, 0, S/N, color="#348ABD", alpha=0.3)
plt.plot(t, S/N, '-', color="#348ABD", label='$S$', lw=4)

plt.plot(t, E/N, '-', color="green", label='$E$', lw=4)
plt.fill_between(t, 0, E/N, color='green', alpha=0.3)

plt.fill_between(t, 0, A/N, color='#A60628', alpha=0.3)
plt.plot(t, A/N, '-', color='#A60628', label='$A$', lw=4)

plt.fill_between(t, 0, (Ia+Is)/N, color='orange', alpha=0.1)
plt.plot(t, (Ia+Is)/N, '-', color='orange', label='$I$', ls='--',lw=4)

R=N-S-E-A-Ia-Is; plt.fill_between(t, 0, R/N, color="dimgrey", alpha=0.3)
plt.plot(t, R/N, '-', color="dimgrey", label='$R$', lw=4)

plt.legend(fontsize=26); plt.grid()
plt.xlabel(r'time')
plt.ylabel(r'fraction of total population')
plt.autoscale(enable=True, axis='x', tight=True)
```

# 2. SEAIRQ model

We now set the rate at which symptomatic infectives get quarantined to $1/20$ days. All other classes stay as before, i.e. never get quarantined.

(Technically, this is similar to changing the self-isolation parameter "fsa". Although the parameter "fsa" only isolates symptomatics, the $Q$ class is more general and mimics contact tracing. This can be used to quarantine al the classes starting from $S$ to $Is$.)

```python
# We now consider a nonzero rate Is -> Q
tE    = 0.00           # rate E  -> Q
tA    = 0.00           # rate A  -> Q
tIa   = 0.00           # rate Ia -> Q
tIs   = 0.05           # rate Is -> Q

# instantiate model
parameters = {'alpha':alpha, 'beta':beta,
              'gE':gE,'gA':gA,
              'gIa':gIa, 'gIs':gIs, 'gE':gE, 'fsa':fsa,
              'tE':tE, 'tA':tA,'tIa':tIa,'tIs':tIs}
model = pyross.deterministic.SEAIRQ(parameters, M, Ni)

# simulate model
data=model.simulate(S0, E0, A0, Ia0, Is0, Q0, contactMatrix, Tf, Nt)
```

```python
# plot the results of the simulation

S  = data['X'][:,0].flatten()
E  = data['X'][:,1].flatten()
A  = data['X'][:,2].flatten()
Ia = data['X'][:,3].flatten()
Is = data['X'][:,4].flatten()
Q  = data['X'][:,5].flatten()
t = data['t']

fig = plt.figure(num=None, figsize=(10, 8), dpi=80, facecolor='w', edgecolor='k')
plt.rcParams.update({'font.size': 22})

plt.fill_between(t, 0, S/N, color="#348ABD", alpha=0.3)
plt.plot(t, S/N, '-', color="#348ABD", label='$S$', lw=4)

plt.plot(t, E/N, '-', color="green", label='$E$', lw=4)
plt.fill_between(t, 0, E/N, color='green', alpha=0.3)

plt.fill_between(t, 0, A/N, color='#A60628', alpha=0.3)
plt.plot(t, A/N, '-', color='#A60628', label='$A$', lw=4)

plt.fill_between(t, 0, (Ia+Is)/N, color='orange', alpha=0.1)
plt.plot(t, (Ia+Is)/N, '-', color='orange', label='$I$', ls='--',lw=4)

R=N-S-E-A-Ia-Is; plt.fill_between(t, 0, R/N, color="dimgrey", alpha=0.3)
plt.plot(t, R/N, '-', color="dimgrey", label='$R$', lw=4)

plt.legend(fontsize=26); plt.grid()
plt.xlabel(r'time')
plt.ylabel(r'fraction of total population')
plt.autoscale(enable=True, axis='x', tight=True)
```

# 3. SEAIRQ model with more nonzero rates to quarantine class

We now set the rate at which symptomatic infectives get quarantined to $1/20$ days, and the rates at which the $E$, $A$, $I_a$, $I_s$ get quarantined to $1/50$ days.

```python
# For the SEAIR model, we set all rates to the Q-state to zero
tE    = 0.02           # rate E  -> Q
tA    = 0.02           # rate A  -> Q
tIa   = 0.02           # rate Ia -> Q
tIs   = 0.05           # rate Is -> Q

# instantiate model
parameters = {'alpha':alpha, 'beta':beta,
              'gE':gE,'gA':gA,
              'gIa':gIa, 'gIs':gIs, 'gE':gE, 'fsa':fsa,
              'tE':tE,'tA':tA,'tIa':tIa,'tIs':tIs}
model = pyross.deterministic.SEAIRQ(parameters, M, Ni)

# simulate model
data=model.simulate(S0, E0, A0, Ia0, Is0, Q0, contactMatrix, Tf, Nt)
```

```python
# plot the results of the simulation

S  = data['X'][:,0].flatten()
E  = data['X'][:,1].flatten()
A  = data['X'][:,2].flatten()
Ia = data['X'][:,3].flatten()
Is = data['X'][:,4].flatten()
Q  = data['X'][:,5].flatten()
t = data['t']

fig = plt.figure(num=None, figsize=(10, 8), dpi=80, facecolor='w', edgecolor='k')
plt.rcParams.update({'font.size': 22})

plt.fill_between(t, 0, S/N, color="#348ABD", alpha=0.3)
plt.plot(t, S/N, '-', color="#348ABD", label='$S$', lw=4)

plt.plot(t, E/N, '-', color="green", label='$E$', lw=4)
plt.fill_between(t, 0, E/N, color='green', alpha=0.3)

plt.fill_between(t, 0, A/N, color='#A60628', alpha=0.3)
plt.plot(t, A/N, '-', color='#A60628', label='$A$', lw=4)

plt.fill_between(t, 0, (Ia+Is)/N, color='orange', alpha=0.1)
plt.plot(t, (Ia+Is)/N, '-', color='orange', label='$I$', ls='--',lw=4)

R=N-S-E-A-Ia-Is; plt.fill_between(t, 0, R/N, color="dimgrey", alpha=0.3)
plt.plot(t, R/N, '-', color="dimgrey", label='$R$', lw=4)

plt.legend(fontsize=26); plt.grid()
plt.xlabel(r'time')
plt.ylabel(r'fraction of total population')
plt.autoscale(enable=True, axis='x', tight=True)
```

We now observe qualitatively different behaviour as compared to 1 and 2. There is no clear peak in the number of exposed anymore, and the number of susceptibles decreases slowly. The processes has not reached a steady state yet, even after 500 days.

We now replot only the $E$, $A$, and $I = I_a + I_s$ classes, which are hardly visible in the above plot:

```python
# plot only the E, A, and I = Ia + Is classes

fig = plt.figure(num=None, figsize=(10, 8), dpi=80, facecolor='w', edgecolor='k')
plt.rcParams.update({'font.size': 22})

#plt.fill_between(t, 0, S/N, color="#348ABD", alpha=0.3)
#plt.plot(t, S/N, '-', color="#348ABD", label='$S$', lw=4)

plt.plot(t, E/N, '-', color="green", label='$E$', lw=4)
plt.fill_between(t, 0, E/N, color='green', alpha=0.3)

plt.fill_between(t, 0, A/N, color='#A60628', alpha=0.3)
plt.plot(t, A/N, '-', color='#A60628', label='$A$', lw=4)

plt.fill_between(t, 0, (Ia+Is)/N, color='orange', alpha=0.1)
plt.plot(t, (Ia+Is)/N, '-', color='orange', label='$I$', ls='--',lw=4)

#R=N-S-E-A-Ia-Is; plt.fill_between(t, 0, R/N, color="dimgrey", alpha=0.3)
#plt.plot(t, R/N, '-', color="dimgrey", label='$R$', lw=4)

plt.legend(fontsize=26); plt.grid()
plt.xlabel(r'time')
plt.ylabel(r'fraction of total population')
plt.autoscale(enable=True, axis='x', tight=True)
```

As can be seen, the peak now is at about 350 days, and after 500 days the average numbers in all classes are decreasing.


```python
%matplotlib inline
from sympy import *
init_printing(use_unicode=True)
```

```python
r, u, v, c, r_c, u_c, v_c, E, p, r_p, u_p, v_p, e, a, b, q, b_0, b_1, b_2, b_3, q_0, q_1, q_2, q_3, q_4, q_5  = symbols('r u v c r_c u_c v_c E p r_p u_p v_p e a b q b_0 b_1 b_2 b_3 q_0 q_1 q_2 q_3 q_4 q_5')
```

```python
gamma = symbols('gamma',positive=True)
```

#### $f_{2}(c,p) = \dfrac{1}{2}r_{c}c^{2}+\dfrac{1}{4}u_{c}c^{4}+\dfrac{1}{6}v_{c}c^{6}+\dfrac{1}{2}r_{p}p^{2}+\dfrac{1}{4}u_{p}p^{4}+\dfrac{1}{6}v_{p}p^{6}-\gamma cp-ec^{2}p^{2}-Ep$

```python
f2 = ((1/2)*r_c*c**2+(1/4)*u_c*c**4+(1/6)*v_c*c**6+(1/2)*r_p*p**2
      +(1/4)*u_p*p**4+(1/6)*v_p*p**6-E*p-gamma*c*p-e*c**2*p**2)
nsimplify(f2)
```

### Solve for $E(c,p)$

### $\dfrac{\partial f_{2}(c,p)}{\partial p} = 0 = $

```python
E_c = solve(f2.diff(p),E)[0]
E_c
```

### Solve for $p_{min}(c)$

### $\dfrac{\partial f_{2}(c,p)}{\partial c} = 0 = $

```python
p_min = nsimplify(solve(f2.diff(c),p)[0])
p_min
```

### Plug $p_{min}(c)$ into $E(p,c)$:

```python
E_c = nsimplify(E_c.subs(p,p_min))
E_c
```

### Series expand $E(p_{min}(c),c)$ in powers of $c$ to order 7:

# Multiply this by gamma, then solve the same problem

```python
series(E_c,c,n=7)
```

```python
Etrun = a*c+b*c**3+q*c**5
```

```python
solve(Etrun.diff(c),c)
```

```python
c_L = solve(Etrun.diff(c),c)[1]
c_U = solve(Etrun.diff(c),c)[3]
c_L,c_U
```

```python
E_L = simplify(Etrun.subs(c,c_U))
E_U = simplify(Etrun.subs(c,c_L))
```

```python
E_L,E_U
```

```python
rc = (gamma**2+a*gamma)/r_p

B = (r_p*u_c/gamma+u_p*(r_c/gamma)**3-2*e*r_c/gamma-2*e*r_p*r_c**2/gamma**3)

Q = ((2*e*r_c)**2/gamma**3+8*r_p*e**2*r_c**3/gamma**5-2*e*u_c/gamma
     -4*e*r_c*r_p*u_c/gamma**3-6*e*u_p*r_c**4/gamma**5+r_p*v_c/gamma
     +3*u_c*u_p*r_c**2/gamma**3+v_p*(r_c/gamma)**5)
```

```python
collect(expand(B.subs(r_c,rc)),a)
```

```python
collect(expand(Q.subs(r_c,rc)),a)
```

```python
b0 = (u_p*(gamma/r_p)**3-4*e*gamma/r_p+u_c*r_p/gamma)

b1 = (3*u_p*gamma**2/r_p**3-6*e/r_p)

b2 = (3*u_p*gamma/r_p**3-2*e/(gamma*r_p))

b3 = u_p/r_p**3

q0 = (v_p*(gamma/r_p)**5-6*e*u_p*gamma**3/r_p**4+3*(gamma/r_p**2)*(4*e**2+u_c*u_p)-6*e*u_c/gamma+v_c*r_p/gamma)

q1 = (5*v_p*gamma**4/r_p**5-24*e*u_p*gamma**2/r_p**4+2*(16*e**2+3*u_c*u_p)/r_p**2-4*e*u_c/gamma**2)

q2 = (10*v_p*gamma**3/r_p**5-36*e*u_p*gamma/r_p**4+(28*e**2+3*u_c*u_p)/(gamma*r_p**2))

q3 = (10*v_p*gamma**2/r_p**5-24*e*u_p/r_p**4+8*(e/(gamma*r_p))**2)

q4 = (5*v_p*gamma/r_p**5-6*e*u_p/(r_p**4*gamma))

q5 = v_p/r_p**5
```

```python
uc = solve(b0-b_0,u_c)[0]
up = solve(b3-b_3,u_p)[0]
vc = solve(q0-q_0,v_c)[0]
vp = solve(q5-q_5,v_p)[0]
```

```python
replacements = [(v_p,vp),(u_p,up),(u_c,uc)]
```

```python
vc = simplify(vc.subs([i for i in replacements]))
```

```python
expand(vc)
```

```python
uc = simplify(uc.subs([i for i in replacements]))
```

```python
expand(uc)
```

```python
up
```

```python
vp
```

### $b_0$

```python
b0
```

### $b_1$

```python
b1 = simplify(b1.subs(u_p,up))
```

```python
b1
```

### $b_2$

```python
b2 = simplify(b2.subs(u_p,up))
```

```python
b2
```

### $b_3$

```python
b3
```

### $B(a)$ i.t.o. $b_3$

```python
B_a = b_0+b1*a+b2*a**2+b_3*a**3
```

```python
B_a
```

### $q_0$

```python
q0
```

### $q_1$

```python
q1 = simplify(q1.subs([i for i in replacements]))
```

```python
q1
```

### $q_2$

```python
q2 = q2.subs([i for i in replacements])
```

```python
expand(q2)
```

### $q_3$

```python
q3 = simplify(q3.subs([i for i in replacements]))
```

```python
q3
```

### $q_4$

```python
q4 = simplify(q4.subs([i for i in replacements]))
```

```python
q4
```

### $Q(a)$ i.t.o. $b_0$, $b_3$, $q_5$

```python
Q_a = q_0+q1*a+q2*a**2+q3*a**3+q4*a**4+q_5*a**5
```

```python
collect(expand(Q_a),a)
```

### $R(a)$

```python
series(B_a**2-(20*a*Q_a/9),a,n=7)
```

```python

```


# Le-Net 1 based architecture

We start with 41X41 (I) after first convolution (9x9)we have 33X33 (L1). The next pooling layer reduces dimension with 3 to an output image of 11X11 with 4x4 pooling kernels (L2). Then we apply different types of convolution 4x4 kernels on the L2 layer resulting in 8x8 (L3) . Then followed by pooling 2X2 resulting in 4x4 output map (L4). So we have 16 connection for each element in layer L4 (which depend on the amount of different Covolutions in L3)

\begin{equation}
f(x)=\frac{1}{1+e^{-x}} \\
F_{k}= f( \sum_{i} \mathbf{W^{k}_{i} \cdot y_{i}}-b_{k})
\end{equation}

\begin{equation}
E=\sum_{k} \frac{1}{2}|t_k-F_{k}|^{2} \\
\Delta W_{ij}= - \eta \frac{dE}{d W_{ij}}
\end{equation}

\begin{equation}
\Delta W_{ij}= \sum_{k} - \eta \frac{dE}{d F_{k}} \frac{dF_{k}}{dx_{k}} \frac{dx_{k}}{dW_{ij}}=\sum_{k} \eta (t_{k}-F_{k})\frac{e^{-x_{k}}}{(1+e^{-x_{k}})^{2}} \frac{dx_{k}}{dW_{ij}} \\
= \eta (t_{k}-F_{k})\frac{e^{-x_{k}}}{(1+e^{-x_{k}})^{2}} y_{ij}
\end{equation}

\begin{equation}
\Delta b_{k}= - \eta \frac{dE}{d F_{k}} \frac{dF_{k}}{dx_{k}} \frac{dx_{k}}{b_{k}}=\eta (t_{k}-F_{k})\frac{e^{-x_{k}}}{(1+e^{-x_{k}})^{2}} \cdot-1
\end{equation}

Since $\frac{e^{-x_{k}}}{(1+e^{-x_{k}})^{2}}$ is always positive we can neglect this term in our programme

\begin{equation}
x_{k}=\sum_{ij} W^{k}[i,j] \; y^{4rb}[i,j] - b_{k}
\end{equation}

\begin{equation}
y^{4rb}[i,j]= \sum_{u,v} W^{3rb}[u,v] \; y^{3rb} [2i+u,2j+v]
\end{equation}

\begin{equation}
y^{3rb} [2i+u,2j+v]= f\left (x^{3rb}[2i+u,2j+v] \right)
\end{equation}

\begin{equation}
x^{3rb}[2i+u,2j+v]=\sum_{nm} W^{2rb}[n,m] \; y^{2rb}[n+(2i+u),m+(2j+v)] -b^{3rb}[2i+u,2j+v]
\end{equation}

\begin{equation}
\begin{split}
\Delta W^{2rb}[n,m] =\sum_{k} - \eta  \frac{dE}{dF_{k}}
\frac{dF_{k}}{dx_{k}}
\sum_{ij} \frac{dx_{k}}{dy^{4rb}[i,j]}
 \sum_{uv}\frac{dy^{4rb}[i,j]}{d y^{3rb} [2i+u,2j+v]}
\frac{d y^{3rb} [2i+u,2j+v]}{d x^{3rb}[2i+u,2j+v]}
\sum_{nm}\frac{d x^{3rb}[2i+u,2j+v]}{d W^{2rb}[n,m]}
\end{split}
\end{equation}

\begin{equation}
\begin{split}
\Delta b^{3rb}[2i+u,2j+v] =\sum_{k} - \eta  \frac{dE}{dF_{k}}
\frac{dF_{k}}{dx_{k}}
\sum_{ij} \frac{dx_{k}}{dy^{4rb}[i,j]}
 \sum_{uv}\frac{dy^{4rb}[i,j]}{d y^{3rb} [2i+u,2j+v]}
\frac{d y^{3rb} [2i+u,2j+v]}{d x^{3rb}[2i+u,2j+v]}
\frac{d x^{3rb}[2i+u,2j+v]}{d b^{3rb}[2i+u,2j+v]}
\end{split}
\end{equation}

\begin{equation}
    \frac{dx_{k}}{dy^{4rb}[i,j]} = W^{4rbk}[i,j]\\
\end{equation}

\begin{equation}
    \frac{dy^{4rb}[i,j]}{d y^{3rb} [2i+u,2j+v]} = W^{3rb}[u,v] \\
 \end{equation}

\begin{equation}
\frac{d y^{3rb} [2i+u,2j+v]}{d x^{3rb}[2i+u,2j+v]}=\frac{e^{-x^{3rb}[2i+u,2j+v]}}{(1+e^{-x^{3rb}[2i+u,2j+v]})^2}
\end{equation}
This term is first not included since it is always positive. If the training will not converge it might be possible to include this term

 \begin{equation}
    \frac{d y^{3rb} [2i+u,2j+v]}{d W^{2rb}[n,m]}= y^{2rb} [n+(2i+u),m+(2j+v)] \\
\end{equation}

\begin{equation}
\frac{d x^{3rb}[2i+u,2j+v]}{d b^{3rb}[2i+u,2j+v]}=-1
\end{equation}

```python
%matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
from numpy import linalg as lin
import scipy.signal as sig
from PIL import Image
import glob
import matplotlib.cm as cm
import itertools
```

```python
########### Load Input ############################################################################################################################
# In this script I used the brightness to determine structures, instead of one RGB color:
# this is determined by: 0.2126*R + 0.7152*G + 0.0722*B
# Source: https://en.wikipedia.org/wiki/Relative_luminance

patchSize=40 # patchsize this must be 48 since our network can only handle this value

# Open forest
Amount_data= len(glob.glob('Forest/F*'))
Patches_F=np.empty([1,patchSize,patchSize])
Patches_F_RGB=np.empty([1,patchSize,patchSize,3])
Patches_t=np.empty([3])

for k in range (0, Amount_data):
    name="Forest/F%d.png" % (k+1)
    img = Image.open(name)
    data=img.convert('RGB')
    data= np.asarray( data, dtype="int32" )
    data=0.2126*data[:,:,0]+0.7152*data[:,:,1]+0.0722*data[:,:,2]
    data2=img.convert('RGB')
    data2= np.asarray( data2, dtype="int32" )

    Yamount=data.shape[0]/patchSize # Counts how many times the windowsize fits in the picture
    Xamount=data.shape[1]/patchSize # Counts how many times the windowsize fits in the picture

    # Create patches for structure
    data_t=np.array([[data[j*patchSize:(j+1)*patchSize,i*patchSize:(i+1)*patchSize] for i in range(0,Xamount)] for j in range(0,Yamount)])
    data_t=np.reshape(data_t, [data_t.shape[0]*data_t.shape[1], patchSize, patchSize])
    Patches_F=np.append(Patches_F,data_t,axis=0)
    #Create patches for colour
    data_t=np.array([[data2[j*patchSize:(j+1)*patchSize,i*patchSize:(i+1)*patchSize,:] for i in range(0,Xamount)] for j in range(0,Yamount)])
    data_t=np.reshape(data_t, [data_t.shape[0]*data_t.shape[1], patchSize, patchSize, 3])
    Patches_F_RGB=np.append(Patches_F_RGB, data_t,axis=0)
Patches_F=np.delete(Patches_F, 0,0)
Patches_F_RGB=np.delete(Patches_F_RGB, 0,0)

# Open city
Amount_data= len(glob.glob('City/C*'))
Patches_C=np.empty([1,patchSize,patchSize])
Patches_C_RGB=np.empty([1,patchSize,patchSize,3])
Patches_t=np.empty([3])

for k in range (0, Amount_data):
    name="City/C%d.png" % (k+1)
    img = Image.open(name)
    data=img.convert('RGB')
    data = np.asarray( data, dtype="int32" )
    data=0.2126*data[:,:,0]+0.7152*data[:,:,1]+0.0722*data[:,:,2]
    data2=img.convert('RGB')
    data2= np.asarray( data2, dtype="int32" )

    Yamount=data.shape[0]/patchSize # Counts how many times the windowsize fits in the picture
    Xamount=data.shape[1]/patchSize # Counts how many times the windowsize fits in the picture

    # Create patches for structure
    data_t=np.array([[data[j*patchSize:(j+1)*patchSize,i*patchSize:(i+1)*patchSize] for i in range(0,Xamount)] for j in range(0,Yamount)])
    data_t=np.reshape(data_t, [data_t.shape[0]*data_t.shape[1], patchSize, patchSize])
    Patches_C=np.append(Patches_C,data_t,axis=0)
    #Create patches for colour
    data_t=np.array([[data2[j*patchSize:(j+1)*patchSize,i*patchSize:(i+1)*patchSize,:] for i in range(0,Xamount)] for j in range(0,Yamount)])
    data_t=np.reshape(data_t, [data_t.shape[0]*data_t.shape[1], patchSize, patchSize, 3])
    Patches_C_RGB=np.append(Patches_C_RGB, data_t,axis=0)

Patches_C=np.delete(Patches_C, 0,0)
Patches_C_RGB=np.delete(Patches_C_RGB, 0,0)

# Open water
Amount_data= len(glob.glob('Water/W*'))
Patches_W=np.empty([1,patchSize,patchSize])
Patches_W_RGB=np.empty([1,patchSize,patchSize,3])
Patches_t=np.empty([3])

for k in range (0, Amount_data):
    name="Water/W%d.png" % (k+1)
    img = Image.open(name)
    data=img.convert('RGB')
    data = np.asarray( data, dtype="int32" )
    data=0.2126*data[:,:,0]+0.7152*data[:,:,1]+0.0722*data[:,:,2]

    data2 = img.convert('RGB')
    data2 = np.asarray( data2, dtype="int32" )

    Yamount=data.shape[0]/patchSize # Counts how many times the windowsize fits in the picture
    Xamount=data.shape[1]/patchSize # Counts how many times the windowsize fits in the picture

    # Create patches for structure
    data_t=np.array([[data[j*patchSize:(j+1)*patchSize,i*patchSize:(i+1)*patchSize] for i in range(0,Xamount)] for j in range(0,Yamount)])
    data_t=np.reshape(data_t, [data_t.shape[0]*data_t.shape[1], patchSize, patchSize])
    Patches_W=np.append(Patches_W,data_t,axis=0)
    #Create patches for colour
    data_t=np.array([[data2[j*patchSize:(j+1)*patchSize,i*patchSize:(i+1)*patchSize,:] for i in range(0,Xamount)] for j in range(0,Yamount)])
    data_t=np.reshape(data_t, [data_t.shape[0]*data_t.shape[1], patchSize, patchSize, 3])
    Patches_W_RGB=np.append(Patches_W_RGB, data_t,axis=0)

Patches_W=np.delete(Patches_W, 0,0)
Patches_W_RGB=np.delete(Patches_W_RGB, 0,0)
```

```python
########### Functions ############################################################################################################################

# Define Activitation functions, pooling and convolution functions (the rules)

def Sigmoid(x):
    return (1/(1+np.exp(-x)))

def Sigmoid_dx(x):
    return np.exp(-x)/((1+np.exp(-x))**2)

def TanH(x):
    return (1-np.exp(-x))/(1+np.exp(-x))

def Pool(I,W):
    PoolImg=np.zeros((len(I)/len(W),len(I)/len(W))) # W must fit an integer times into I.
    for i in range(0,len(PoolImg)):
        for j in range(0,len(PoolImg)):
            SelAr=I[i*len(W):(i+1)*len(W),j*len(W):(j+1)*len(W)]
            PoolImg[i,j]=np.inner(SelAr.flatten(),W.flatten()) # Now this is just an inner product since we have vectors
    return PoolImg

# To automatically make Gaussian kernels
def makeGaussian(size, fwhm = 3, center=None):
    x = np.arange(0, size, 1, float)
    y = x[:,np.newaxis]

    if center is None:
        x0 = y0 = size // 2
    else:
        x0 = center[0]
        y0 = center[1]

    return np.exp(-4*np.log(2) * ((x-x0)**2 + (y-y0)**2) / fwhm**2)

# To automatically define pooling nodes
def Pool_node(N):
    s=(N,N)
    a=float(N)*float(N)
    return (1.0/a)*np.ones(s)

```

```python
#################### Define pooling layers ###########################################################################
P12=Pool_node(4)*(1.0/100.0) #factor 1000 added to lower values more
P34=Pool_node(1)*(1.0/10)

#################### Define Convolution layers #######################################################################

######### First C layer #########
C1=[]

## First Kernel

# Inspiration: http://en.wikipedia.org/wiki/Sobel_operator
# http://stackoverflow.com/questions/9567882/sobel-filter-kernel-of-large-size

Kernel=np.array([[4,3,2,1,0,-1,-2,-3,-4],
                 [5,4,3,2,0,-2,-3,-4,-5],
                 [6,5,4,3,0,-3,-4,-5,-6],
                 [7,6,5,4,0,-4,-5,-6,-7],
                 [8,7,6,5,0,-5,-6,-7,-8],
                 [7,6,5,4,0,-4,-5,-6,-7],
                 [6,5,4,3,0,-3,-4,-5,-6],
                 [5,4,3,2,0,-2,-3,-4,-5],
                 [4,3,2,1,0,-1,-2,-3,-4]])

C1.append(Kernel)

## Second Kernel
Kernel=np.matrix.transpose(Kernel)
C1.append(Kernel)

######### Initialize output weights and biases #########

# Define the number of branches in one row
N_branches= 3
ClassAmount=3 # Forest, City, Water
Size_C2=5
S_H3=((patchSize-C1[0].shape[0]+1)/P12.shape[1])-Size_C2+1
S_H4=S_H3/P34.shape[1]
C2INIT=np.random.rand(len(C1),N_branches, Size_C2, Size_C2) # second convolution weigths
WINIT=np.random.rand(ClassAmount, len(C1), N_branches, S_H3, S_H3) # end-weight from output to classifier-neurons
W2INIT=np.random.rand(3,3)

H3_bias=np.random.rand(len(C1),N_branches) # bias in activation function from C2 to H3
Output_bias=np.random.rand(ClassAmount) # bias on the three classes

#learning rates
n_bias=1*10**-2
n_W=1*10**-2
n_C2=1*10**-2
n_H3_bias=1*10**-2
#Labda=5*10**-3

```

```python
N_plts=len(C1)
for i in range(0,N_plts):
    plt.subplot(4,3,i+1)
    plt.imshow(C1[i])
```

# For the extra information regarding the code in the following cell

a random patch is chosen in the following way: the program counts how many files and patches there are in total, then it permutes the sequence so that a random patch is chosen every iteration (forest, city, water). After selecting the number the file has to be found back.

```python
N_F=Patches_F.shape[0]
N_C=Patches_C.shape[0]
N_W=Patches_W.shape[0]

N_total=N_F+N_C+N_W
Sequence = np.arange(N_total)
Sequence = np.random.permutation(Sequence)
```

```python
CV_RGB=np.zeros([10])

##### CROSS VALIDATION WITH COLOUR #####
for CROSSES in range(0,10):
        # TRAINING PHASE
    W=np.copy(WINIT)
    W2=np.copy(W2INIT)
    C2=np.copy(C2INIT)
    n_W=10
    Sample_iterations=0

    ###### Chooses patch and defines label #####
    for PP in range(0,int(np.ceil(0.9*N_total))):
        SS=Sequence[PP]
        #SS=14000
        if SS<N_F:
            Class_label=np.array([1,0,0])
            inputPatch=Patches_F[SS]
            Int_RGB=np.mean(np.mean(Patches_F_RGB[SS,:,:,:], axis=0), axis=0)/255

        elif(SS>=N_F) and (SS<(N_F+N_C)):
            Class_label=np.array([0,1,0])
            inputPatch=Patches_C[SS-N_F]
            Int_RGB=np.mean(np.mean(Patches_C_RGB[SS-N_F,:,:,:], axis=0), axis=0)/255

        else:
            Class_label=np.array([0,0,1])
            inputPatch=Patches_W[SS-N_F-N_C]
            Int_RGB=np.mean(np.mean(Patches_W_RGB[SS-N_F-N_C,:,:,:], axis=0), axis=0)/255

        ### Layer 1 ###
        H1=[]
        H2=[]
        H3=np.zeros((len(C1), N_branches, S_H3,S_H3))
        H4=np.zeros((len(C1), N_branches, S_H4,S_H4))
        x=np.zeros(ClassAmount)
        f=np.zeros(ClassAmount)

        II=1
        ITER=0
        for r in range (0, len(C1)):
            H1.append(sig.convolve(inputPatch, C1[r], 'valid'))
            H2.append(Pool(H1[r], P12))
            #From here on BP trakes place!
            for b in range(0,N_branches):
                H3[r][b]=Sigmoid(sig.convolve(H2[r], C2[r][b],'valid')-H3_bias[r][b])
                H4[r][b]=Pool(H3[r][b],P34)
        #Now we have 3x3x4x4 inputs, connected to the 3 output nodes
        y=np.append([H4.flatten()], [Int_RGB])
        for k in range(0,ClassAmount):
            W_t=np.append([W[k].flatten()], [W2[k]])
            x[k]=np.inner(y, W_t)
            f[k]=Sigmoid(x[k]-Output_bias[k])
            ###### Back-propagation #####
            # First learning the delta's
        e_k=f-Class_label
        delta_k=e_k*Sigmoid_dx(x)
        for k in range(0, ClassAmount):
            #update weights output layer
            W[k]=W[k]-n_W*delta_k[k]*H4
            W2[k]=W2[k]-n_W*delta_k[k]*Int_RGB

        Sample_iterations=Sample_iterations+1
        if Sample_iterations==5000:
            n_W=5
            print Sample_iterations
        if Sample_iterations==10000:
            n_W=1
            print Sample_iterations
        if Sample_iterations==15000:
            n_W=0.1
            print Sample_iterations

    print "Training completed"

        ####### Test phase #######
    N_correct=0
###### Chooses patch and defines label #####
    for PP in range(int(np.ceil(0.9*N_total)),N_total):
        SS=Sequence[PP]
        if SS<N_F:
            Class_label=np.array([1,0,0])
            inputPatch=Patches_F[SS]
            Int_RGB=np.mean(np.mean(Patches_F_RGB[SS,:,:,:], axis=0), axis=0)/255

        elif(SS>=N_F) and (SS<(N_F+N_C)):
            Class_label=np.array([0,1,0])
            inputPatch=Patches_C[SS-N_F]
            Int_RGB=np.mean(np.mean(Patches_C_RGB[SS-N_F,:,:,:], axis=0), axis=0)/255

        else:
            Class_label=np.array([0,0,1])
            inputPatch=Patches_W[SS-N_F-N_C]
            Int_RGB=np.mean(np.mean(Patches_W_RGB[SS-N_F-N_C,:,:,:], axis=0), axis=0)/255

    ### Layer 1 ###
        H1=[]
        H2=[]
        H3=np.zeros((len(C1), N_branches, S_H3,S_H3))
        #H4=np.zeros((len(C1), N_branches, S_H4,S_H4))
        x=np.zeros(ClassAmount)
        f=np.zeros(ClassAmount)
        for r in range (0, len(C1)):
            H1.append(sig.convolve(inputPatch, C1[r], 'valid'))
            H2.append(Pool(H1[r], P12))
            for b in range(0,N_branches):
                H3[r][b]=Sigmoid(sig.convolve(H2[r], C2[r][b],'valid')-H3_bias[r][b])
                H4[r][b]=Pool(H3[r][b],P34)
        y=np.append([H4.flatten()], [Int_RGB])
        #Now we have 3x3x4x4 inputs, connected to the 3 output nodes
        for k in range(0,ClassAmount):
            W_t=np.append([W[k].flatten()], [W2[k]])
            x[k]=np.inner(y, W_t)
            f[k]=Sigmoid(x[k]-Output_bias[k])

        if np.argmax(f)==np.argmax(Class_label):
            N_correct=N_correct+1

    Perc_corr=float(N_correct)/(N_total-int(np.ceil(0.9*N_total)))
    print Perc_corr
    CV_RGB[CROSSES]=Perc_corr
    #Sequence=np.roll(Sequence,(N_total-int(np.ceil(0.9*N_total))))
```

    Training completed
    0.545454545455

    /anaconda/lib/python2.7/site-packages/IPython/kernel/__main__.py:6: RuntimeWarning: overflow encountered in exp

```python
# Save calculated parameters
CV1_withRGB=np.mean(CV_RGB)
Std_CV1_withRGB=np.std(CV_RGB)

with open("CV1_withRGB.txt", 'w') as f:
    f.write(str(CV1_withRGB))
with open("Std_CV1_withRGB.txt", 'w') as f:
    f.write(str(Std_CV1_withRGB))
```

```python
##### CROSS VALIDATION WITHOUT COLOUR #####
CV_noRGB=np.zeros([10])
for CROSSES in range(0,10):
        # TRAINING PHASE
    W=np.copy(WINIT)
    W2=np.copy(W2INIT)
    C2=np.copy(C2INIT)
    n_W=25
    Sample_iterations=0

    ###### Chooses patch and defines label #####
    for PP in range(0,int(np.ceil(0.9*N_total))):
        SS=Sequence[PP]
        #SS=14000
        if SS<N_F:
            Class_label=np.array([1,0,0])
            inputPatch=Patches_F[SS]

        elif(SS>=N_F) and (SS<(N_F+N_C)):
            Class_label=np.array([0,1,0])
            inputPatch=Patches_C[SS-N_F]

        else:
            Class_label=np.array([0,0,1])
            inputPatch=Patches_W[SS-N_F-N_C]

        ### Layer 1 ###
        H1=[]
        H2=[]
        H3=np.zeros((len(C1), N_branches, S_H3,S_H3))
        H4=np.zeros((len(C1), N_branches, S_H4,S_H4))
        x=np.zeros(ClassAmount)
        f=np.zeros(ClassAmount)

        for r in range (0, len(C1)):
            H1.append(sig.convolve(inputPatch, C1[r], 'valid'))
            H2.append(Pool(H1[r], P12))
            #From here on BP trakes place!
            for b in range(0,N_branches):
                H3[r][b]=Sigmoid(sig.convolve(H2[r], C2[r][b],'valid')-H3_bias[r][b])
                H4[r][b]=Pool(H3[r][b],P34)
        #Now we have 3x3x4x4 inputs, connected to the 3 output nodes
        y=H4.flatten()
        for k in range(0,ClassAmount):
            W_t=W[k].flatten()
            x[k]=np.inner(y, W_t)
            f[k]=Sigmoid(x[k]-Output_bias[k])
            ###### Back-propagation #####
            # First learning the delta's
        e_k=f-Class_label
        delta_k=e_k*Sigmoid_dx(x)
        for k in range(0, ClassAmount):
            #update weights output layer
            W[k]=W[k]-n_W*delta_k[k]*H4

        Sample_iterations=Sample_iterations+1
        if Sample_iterations==5000:
            n_W=10
            print Sample_iterations
        if Sample_iterations==10000:
            n_W=2.5
            print Sample_iterations
        if Sample_iterations==15000:
            n_W=0.5
            print Sample_iterations

    print "Training completed"

        ####### Test phase #######
    N_correct=0
###### Chooses patch and defines label #####
    for PP in range(int(np.ceil(0.9*N_total)),N_total):
        SS=Sequence[PP]
        if SS<N_F:
            Class_label=np.array([1,0,0])
            inputPatch=Patches_F[SS]

        elif(SS>=N_F) and (SS<(N_F+N_C)):
            Class_label=np.array([0,1,0])
            inputPatch=Patches_C[SS-N_F]

        else:
            Class_label=np.array([0,0,1])
            inputPatch=Patches_W[SS-N_F-N_C]

    ### Layer 1 ###
        H1=[]
        H2=[]
        H3=np.zeros((len(C1), N_branches, S_H3,S_H3))
        #H4=np.zeros((len(C1), N_branches, S_H4,S_H4))
        x=np.zeros(ClassAmount)
        f=np.zeros(ClassAmount)
        for r in range (0, len(C1)):
            H1.append(sig.convolve(inputPatch, C1[r], 'valid'))
            H2.append(Pool(H1[r], P12))
            for b in range(0,N_branches):
                H3[r][b]=Sigmoid(sig.convolve(H2[r], C2[r][b],'valid')-H3_bias[r][b])
                H4[r][b]=Pool(H3[r][b],P34)
        y=H4.flatten()
        #Now we have 3x3x4x4 inputs, connected to the 3 output nodes
        for k in range(0,ClassAmount):
            W_t=W[k].flatten()
            x[k]=np.inner(y, W_t)
            f[k]=Sigmoid(x[k]-Output_bias[k])

        if np.argmax(f)==np.argmax(Class_label):
            N_correct=N_correct+1

    Perc_corr=float(N_correct)/(N_total-int(np.ceil(0.9*N_total)))
    print Perc_corr
    CV_noRGB[CROSSES]=Perc_corr
    Sequence=np.roll(Sequence,(N_total-int(np.ceil(0.9*N_total))))

# Save calculated parameters
CV1_withoutRGB=np.mean(CV_noRGB)
Std_CV1_withoutRGB=np.std(CV_noRGB)

with open("CV1_withoutRGB.txt", 'w') as f:
    f.write(str(CV1_withoutRGB))
with open("Std_CV1_withoutRGB.txt", 'w') as f:
    f.write(str(Std_CV1_withoutRGB))

```

    5000
    10000
    15000
    Training completed
    0.468691588785
    5000
    10000
    15000
    Training completed
    0.466355140187
    5000
    10000
    15000
    Training completed
    0.470560747664
    5000
    10000
    15000
    Training completed
    0.476168224299
    5000
    10000
    15000
    Training completed
    0.464953271028
    5000
    10000
    15000
    Training completed
    0.471962616822
    5000
    10000
    15000
    Training completed
    0.478504672897
    5000
    10000
    15000
    Training completed
    0.477102803738
    5000
    10000
    15000
    Training completed
    0.470093457944
    5000
    10000
    15000
    Training completed
    0.469626168224

    /anaconda/lib/python2.7/site-packages/IPython/kernel/__main__.py:6: RuntimeWarning: overflow encountered in exp

```python
##### CROSS VALIDATION WITH BACKPROPAGATION AND COLOUR #####

# TRAINING PHASE

ERROR_cv=np.zeros([10])

for CROSSES in range(0,10):
    W=np.copy(WINIT)
    W2=np.copy(W2INIT)
    C2=np.copy(C2INIT)
    n_W=10
    n_C2=1*10**-2

    Sample_iterations=0

###### Chooses patch and defines label #####
#for PP in range(0,len(Sequence)):
    for PP in range(0,int(np.ceil(0.9*N_total))):
        SS=Sequence[PP]
        if SS<N_F:
            Class_label=np.array([1,0,0])
            inputPatch=Patches_F[SS]
            Int_RGB=np.mean(np.mean(Patches_F_RGB[SS,:,:,:], axis=0), axis=0)/255

        elif(SS>=N_F) and (SS<(N_F+N_C)):
            Class_label=np.array([0,1,0])
            inputPatch=Patches_C[SS-N_F]
            Int_RGB=np.mean(np.mean(Patches_C_RGB[SS-N_F,:,:,:], axis=0), axis=0)/255

        else:
            Class_label=np.array([0,0,1])
            inputPatch=Patches_W[SS-N_F-N_C]
            Int_RGB=np.mean(np.mean(Patches_W_RGB[SS-N_F-N_C,:,:,:], axis=0), axis=0)/255

    ### Layer 1 ###
        H1=[]
        H2=[]
        H3=np.zeros((len(C1), N_branches, S_H3,S_H3))
        H4=np.zeros((len(C1), N_branches, S_H4,S_H4))
        x=np.zeros(ClassAmount)
        f=np.zeros(ClassAmount)

        for r in range (0, len(C1)):
            H1.append(sig.convolve(inputPatch, C1[r], 'valid'))
            H2.append(Pool(H1[r], P12))
        #From here on BP trakes place!
            for b in range(0,N_branches):
                H3[r][b]=Sigmoid(sig.convolve(H2[r], C2[r][b],'valid')-H3_bias[r][b])
                H4[r][b]=Pool(H3[r][b],P34)
    #Now we have 3x3x4x4 inputs, connected to the 3 output nodes
        y=np.append([H4.flatten()], [Int_RGB])
        for k in range(0,ClassAmount):
            W_t=np.append([W[k].flatten()], [W2[k]])
            x[k]=np.inner(y, W_t)
            f[k]=Sigmoid(x[k]-Output_bias[k])

            ###### Back-propagation #####
            # First learning the delta's
        delta_H4=np.zeros([ClassAmount,len(C1),N_branches,S_H4,S_H4])
        e_k=f-Class_label
        delta_k=e_k*Sigmoid_dx(x)
        for k in range(0, ClassAmount):
        #update weights output layer
            W[k]=W[k]-n_W*delta_k[k]*H4
            W2[k]=W2[k]-n_W*delta_k[k]*Int_RGB
            delta_H4[i]=delta_k[i]*W[i]
        delta_H4=np.sum(delta_H4, axis=0)
        delta_H3=(float(1)/10)*delta_H4
        C2_diff=np.zeros([len(C1),N_branches, Size_C2, Size_C2])
        for r in range(0, len(C1)):
            C2_t=np.array([[delta_H3[r][:]*H2[r][(0+u):(4+u),(0+v):(4+v)] for u in range(0,Size_C2)] for v in range (0,Size_C2)])
            C2_t=np.sum(np.sum(C2_t, axis=4),axis=3)
            C2_t=np.rollaxis(C2_t,2)
            C2_diff[r]=-n_C2*C2_t
        C2=C2+C2_diff
        Normalization_factor=np.sum(np.sum(np.abs(C2),axis=3),axis=2)/np.sum(np.sum(np.abs(C2_INIT),axis=3),axis=2)
        for r in range(0,len(C1)):
            for b in range(0, N_branches):
                C2[r][b]=C2[r][b]/Normalization_factor[r][b]

        Sample_iterations=Sample_iterations+1
        if Sample_iterations==5000:
            n_W=5
            n_C2=0.75*10**-2
            print Sample_iterations
        if Sample_iterations==10000:
            n_W=1
            n_C2=0.25*10**-2
            print Sample_iterations
        if Sample_iterations==15000:
            n_W=0.1
            n_C2=0.1*10**-2
            print Sample_iterations

    print "Training completed"
    ###### test phase!
    N_correct=0
    for PP in range(int(np.ceil(0.9*N_total)),N_total):
        SS=Sequence[PP]
        if SS<N_F:
            Class_label=np.array([1,0,0])
            inputPatch=Patches_F[SS]
            Int_RGB=np.mean(np.mean(Patches_F_RGB[SS,:,:,:], axis=0), axis=0)/255

        elif(SS>=N_F) and (SS<(N_F+N_C)):
            Class_label=np.array([0,1,0])
            inputPatch=Patches_C[SS-N_F]
            Int_RGB=np.mean(np.mean(Patches_C_RGB[SS-N_F,:,:,:], axis=0), axis=0)/255

        elif(SS>=(N_F+N_C)) and (SS<N_F+N_C+N_W):
            Class_label=np.array([0,0,1])
            inputPatch=Patches_W[SS-N_F-N_C]
            Int_RGB=np.mean(np.mean(Patches_W_RGB[SS-N_F-N_C,:,:,:], axis=0), axis=0)/255

        # else:
        #     Class_label=np.array([0,0,0,1])
        #     inputPatch=Patches_G[SS-N_F-N_C-N_W]
        #     Int_RGB=np.mean(np.mean(Patches_G_RGB[SS-N_F-N_C-N_W,:,:,:], axis=0), axis=0)/255

    ### Layer 1 ###
        H1=[]
        H2=[]
        H3=np.zeros((len(C1), N_branches, S_H3,S_H3))
        H4=np.zeros((len(C1), N_branches, S_H4,S_H4))
        x=np.zeros(ClassAmount)
        f=np.zeros(ClassAmount)
        for r in range (0, len(C1)):
            H1.append(sig.convolve(inputPatch, C1[r], 'valid'))
            H2.append(Pool(H1[r], P12))
            for b in range(0,N_branches):
                H3[r][b]=Sigmoid(sig.convolve(H2[r], C2[r][b],'valid')-H3_bias[r][b])
                H4[r][b]=Pool(H3[r][b],P34)
        y=np.append([H4.flatten()], [Int_RGB])
    #Now we have 3x3x4x4 inputs, connected to the 3 output nodes
        for k in range(0,ClassAmount):
            W_t=np.append([W[k].flatten()], [W2[k]])
            x[k]=np.inner(y, W_t)
            f[k]=Sigmoid(x[k]-Output_bias[k])
        if np.argmax(f)==np.argmax(Class_label):
            #print True
            N_correct=N_correct+1

    Perc_corr=float(N_correct)/(N_total-int(np.ceil(0.9*N_total)))
    print Perc_corr
    ERROR_cv[CROSSES]=Perc_corr
    Sequence=np.roll(Sequence,(N_total-int(np.ceil(0.9*N_total))))

```

    5000
    10000
    15000
    Training completed
    0.671962616822
    5000
    10000
    15000
    Training completed
    0.670560747664
    5000
    10000
    15000
    Training completed
    0.656074766355
    5000
    10000
    15000
    Training completed
    0.692056074766
    5000
    10000
    15000
    Training completed
    0.683177570093
    5000
    10000
    15000
    Training completed
    0.67476635514
    5000
    10000
    15000
    Training completed
    0.682710280374
    5000
    10000
    15000
    Training completed
    0.656542056075
    5000
    10000
    15000
    Training completed
    0.691588785047
    5000
    10000
    15000
    Training completed
    0.65

    /anaconda/lib/python2.7/site-packages/IPython/kernel/__main__.py:6: RuntimeWarning: overflow encountered in exp

```python
CV2_withRGB=np.mean(ERROR_cv)
Std_CV2_withRGB=np.std(ERROR_cv)

with open("CV2_withRGB.txt", 'w') as f:
    f.write(str(CV2_withRGB))
with open("Std_CV2_withRGB.txt", 'w') as f:
    f.write(str(Std_CV2_withRGB))
```

```python
# CROSS VALIDATION WTIH BACKPROPAGATION AND WITHOUT COLOUR

# TRAINING PHASE

ERROR_cv_without=np.zeros([10])

for CROSSES in range(0,10):
    W=np.copy(WINIT)
    W2=np.copy(W2INIT)
    C2=np.copy(C2INIT)
    n_W=25
    n_C2=1*10**-2

    Sample_iterations=0

###### Chooses patch and defines label #####
#for PP in range(0,len(Sequence)):
    for PP in range(0,int(np.ceil(0.9*N_total))):
        SS=Sequence[PP]
        if SS<N_F:
            Class_label=np.array([1,0,0])
            inputPatch=Patches_F[SS]

        elif(SS>=N_F) and (SS<(N_F+N_C)):
            Class_label=np.array([0,1,0])
            inputPatch=Patches_C[SS-N_F]

        else:
            Class_label=np.array([0,0,1])
            inputPatch=Patches_W[SS-N_F-N_C]

    ### Layer 1 ###
        H1=[]
        H2=[]
        H3=np.zeros((len(C1), N_branches, S_H3,S_H3))
        H4=np.zeros((len(C1), N_branches, S_H4,S_H4))
        x=np.zeros(ClassAmount)
        f=np.zeros(ClassAmount)

        for r in range (0, len(C1)):
            H1.append(sig.convolve(inputPatch, C1[r], 'valid'))
            H2.append(Pool(H1[r], P12))
        #From here on BP trakes place!
            for b in range(0,N_branches):
                H3[r][b]=Sigmoid(sig.convolve(H2[r], C2[r][b],'valid')-H3_bias[r][b])
                H4[r][b]=Pool(H3[r][b],P34)
    #Now we have 3x3x4x4 inputs, connected to the 3 output nodes
        y=H4.flatten()
        for k in range(0,ClassAmount):
            W_t=W[k].flatten()
            x[k]=np.inner(y, W_t)
            f[k]=Sigmoid(x[k]-Output_bias[k])

            ###### Back-propagation #####
            # First learning the delta's
        delta_H4=np.zeros([ClassAmount,len(C1),N_branches,S_H4,S_H4])
        e_k=f-Class_label
        delta_k=e_k*Sigmoid_dx(x)
        for k in range(0, ClassAmount):
        #update weights output layer
            W[k]=W[k]-n_W*delta_k[k]*H4
            W2[k]=W2[k]-n_W*delta_k[k]*Int_RGB
            delta_H4[i]=delta_k[i]*W[i]
        delta_H4=np.sum(delta_H4, axis=0)
        delta_H3=(float(1)/10)*delta_H4
        C2_diff=np.zeros([len(C1),N_branches, Size_C2, Size_C2])
        for r in range(0, len(C1)):
            C2_t=np.array([[delta_H3[r][:]*H2[r][(0+u):(4+u),(0+v):(4+v)] for u in range(0,Size_C2)] for v in range (0,Size_C2)])
            C2_t=np.sum(np.sum(C2_t, axis=4),axis=3)
            C2_t=np.rollaxis(C2_t,2)
            C2_diff[r]=-n_C2*C2_t
        C2=C2+C2_diff
        Normalization_factor=np.sum(np.sum(np.abs(C2),axis=3),axis=2)/np.sum(np.sum(np.abs(C2_INIT),axis=3),axis=2)
        for r in range(0,len(C1)):
            for b in range(0, N_branches):
                C2[r][b]=C2[r][b]/Normalization_factor[r][b]

        Sample_iterations=Sample_iterations+1
        if Sample_iterations==5000:
            n_W=10
            n_C2=0.75*10**-2
            print Sample_iterations
        if Sample_iterations==10000:
            n_W=2.5
            n_C2=0.25*10**-2
            print Sample_iterations
        if Sample_iterations==15000:
            n_W=0.5
            n_C2=0.1*10**-2
            print Sample_iterations

    print "Training completed"
    ###### test phase!
    N_correct=0
    for PP in range(int(np.ceil(0.9*N_total)),N_total):
        SS=Sequence[PP]
        if SS<N_F:
            Class_label=np.array([1,0,0])
            inputPatch=Patches_F[SS]

        elif(SS>=N_F) and (SS<(N_F+N_C)):
            Class_label=np.array([0,1,0])
            inputPatch=Patches_C[SS-N_F]

        elif(SS>=(N_F+N_C)) and (SS<N_F+N_C+N_W):
            Class_label=np.array([0,0,1])
            inputPatch=Patches_W[SS-N_F-N_C]

        # else:
        #     Class_label=np.array([0,0,0,1])
        #     inputPatch=Patches_G[SS-N_F-N_C-N_W]
        #     Int_RGB=np.mean(np.mean(Patches_G_RGB[SS-N_F-N_C-N_W,:,:,:], axis=0), axis=0)/255

    ### Layer 1 ###
        H1=[]
        H2=[]
        H3=np.zeros((len(C1), N_branches, S_H3,S_H3))
        H4=np.zeros((len(C1), N_branches, S_H4,S_H4))
        x=np.zeros(ClassAmount)
        f=np.zeros(ClassAmount)
        for r in range (0, len(C1)):
            H1.append(sig.convolve(inputPatch, C1[r], 'valid'))
            H2.append(Pool(H1[r], P12))
            for b in range(0,N_branches):
                H3[r][b]=Sigmoid(sig.convolve(H2[r], C2[r][b],'valid')-H3_bias[r][b])
                H4[r][b]=Pool(H3[r][b],P34)
        y=H4.flatten()
    #Now we have 3x3x4x4 inputs, connected to the 3 output nodes
        for k in range(0,ClassAmount):
            W_t=W[k].flatten()
            x[k]=np.inner(y, W_t)
            f[k]=Sigmoid(x[k]-Output_bias[k])
        if np.argmax(f)==np.argmax(Class_label):
            #print True
            N_correct=N_correct+1

    Perc_corr=float(N_correct)/(N_total-int(np.ceil(0.9*N_total)))
    print Perc_corr
    ERROR_cv_without[CROSSES]=Perc_corr
    Sequence=np.roll(Sequence,(N_total-int(np.ceil(0.9*N_total))))

```

```python
CV2_withoutRGB=np.mean(ERROR_cv_without)
Std_CV2_withoutRGB=np.std(ERROR_cv_without)

with open("ERROR_cv_without.txt", 'w') as f:
    f.write(str(CV2_withRGB))
with open("Std_CV2_withoutRGB.txt", 'w') as f:
    f.write(str(Std_CV2_withoutRGB))
```

```python

```


# ***Introduction to Radar Using Python and MATLAB***
## Andy Harrison - Copyright (C) 2019 Artech House
<br/>

# Right Circular Cone Radar Cross Section
***

Referring to Section 7.4.1.6, the geometry of the right circular cone is given in Figure 7.11. For axial incidence, the radar cross section is independent of polarization, and is written as (Equation 7.48)

$$
    \sigma = \frac{\lambda^2}{\pi}\frac{\left(\dfrac{ka\sin(\pi/n)}{n}\right)^2}{\Big( \cos(\pi/n) - \cos(3\pi/n)\Big)^2} \hspace{0.5in} \text{(m}^2\text{)},
$$

Equation 7.48 is for first-order diffraction only, which is valid when $ka \gg 1$.  Taking double diffraction into account, a more accurate expression for the axial radar cross section is (Equation 7.49)

\begin{align}
    \sigma = \frac{\lambda^2}{\pi}\left(\dfrac{ka\sin(\pi/n)}{n}\right)^2\, \Bigg| &\dfrac{1}{\big( \cos(\pi/n) - \cos(3\pi/n)\big)^2}  \nonumber \\[7pt]
    &+ \frac{\sin(\pi/n)\, \exp\Big(j(2ka - \pi/4)\Big)}{n\sqrt{\pi k a}\, \big( \cos(\pi/n) - \cos(3\pi/2n)\big)^2} \Bigg|^2 \hspace{0.15in} \text{(m}^2\text{)}.\nonumber
\end{align}

If the angle of the incident energy is normal to the generator of the cone, $\theta_i = 90^\text{o} - \alpha$, then the geometrical theory of diffraction equations are no longer valid.  Instead, an expression based on the asymptotic expansion of the physical optics equation is used.  This is written as

\begin{equation}\label{eq:rcs_cone_normal}
    \sigma = \frac{8\lambda^2 \pi}{9 \sin^2\alpha\cos\alpha} \left(\frac{a}{\lambda}\right)^3 \hspace{0.5in} \text{(m}^2\text{)}.
\end{equation}

The other special case is when the cone is viewed from the base, $\theta_i = 180^o$.  For this case, the physical optics expression for a circular disc is used; see Table 7.1, and repeated here as

\begin{equation}\label{eq:rcs_cone_base}
    \sigma = \frac{\lambda^2(ka)^4}{4\pi} \hspace{0.5in} \text{(m}^2\text{)}.
\end{equation}

For all other incident angles, the radar cross section depends on the polarization of the incident energy, and is given by the following equations

\begin{align}\label{eq:rcs_cone_arbitrary_1}
    \sigma = &\frac{\lambda^2ka}{4\pi^2\sin\theta_i}\left(\frac{\sin(\pi/n)}{n}\right)^2 \times  \Bigg| \exp\Big[-j(2ka\sin\theta_i - \frac{\pi}{4})\Big] \Big[\Big(\cos\frac{\pi}{n} - 1\Big)^{-1} \nonumber \\[7pt] &\pm \Big(\cos\frac{\pi}{n} - \cos\frac{3\pi - 2\theta_i}{n} \Big)^{-1} \Big]  + \exp\Big[j(2ka\sin\theta_i - \frac{\pi}{4})\Big] \Big[\Big(\cos\frac{\pi}{n} - 1\Big)^{-1} \nonumber \\[7pt]
    & \pm \Big(\cos\frac{\pi}{n} - \cos\frac{3\pi + 2\theta_i}{n} \Big)^{-1} \Big] \Bigg|^2, \, \, 0 < \theta_i < \alpha;
\end{align}

\begin{align}\label{eq:rcs_cone_arbitrary_2}
    \sigma = \frac{\lambda^2ka}{4\pi^2\sin\theta_i}\left(\frac{\sin(\pi/n)}{n}\right)^2 &\Bigg[\Big(\cos\frac{\pi}{n} - 1\Big)^{-1} \pm \nonumber \\[7pt]
    & \Big(\cos\frac{\pi}{n} - \cos\frac{3\pi - 2\theta_i}{n} \Big)^{-1} \Bigg]^2, \, \,   \alpha < \theta_i < \pi/2;
\end{align}

\begin{align}\label{eq:rcs_cone_arbitrary_3}
    \sigma = &\frac{\lambda^2ka}{4\pi^2\sin\theta_i}\left(\frac{\sin(\pi/n)}{n}\right)^2 \times \Bigg| \exp\Big[-j(2ka\sin\theta_i - \frac{\pi}{4})\Big] \Big[\Big(\cos\frac{\pi}{n} - 1\Big)^{-1} \nonumber \\[7pt]
    &  \pm \Big(\cos\frac{\pi}{n} - \cos\frac{3\pi - 2\theta_i}{n} \Big)^{-1} \Big]  + \exp\Big[j(2ka\sin\theta_i - \frac{\pi}{4})\Big] \Big[\Big(\cos\frac{\pi}{n} - 1\Big)^{-1} \nonumber \\[7pt]
    &  \pm \Big(\cos\frac{\pi}{n} - \cos\frac{2\theta_i-\pi}{n} \Big)^{-1} \Big] \Bigg|^2, \, \, \pi/2 < \theta_i < \pi.
\end{align}

The positive sign is used for horizontal polarization and the negative sign is used for vertical polarization.
***

Begin by getting the library path

```python
import lib_path
```

Set the operating frequency (Hz), the cone half angle (radians), and the base radius (m)

```python
from numpy import radians

frequency = 1e9

cone_half_angle = radians(15.0)

base_radius = 1.4
```

Set up the incident angles (radians) using the `linspace` routine from `scipy`

```python
from numpy import linspace

from scipy.constants import pi

incident_angle = linspace(0, pi, 1801)
```

Calculate the radar cross section (m^2) for the right circular cone

```python
from Libs.rcs.right_circular_cone import radar_cross_section

from numpy import array

rcs = array([radar_cross_section(frequency, cone_half_angle, base_radius, ia) for ia in incident_angle])
```

Display the radar cross section (dBsm) for the right circular cone

```python
from matplotlib import pyplot as plt

from numpy import log10, degrees

# Set the figure size

plt.rcParams["figure.figsize"] = (15, 10)

# Display the results

plt.plot(degrees(incident_angle), 10 * log10(rcs[:, 0]), '', label='VV')

plt.plot(degrees(incident_angle), 10 * log10(rcs[:, 1]), '--', label='HH')

# Set the plot title and labels

plt.title('RCS vs Incident Angle', size=14)

plt.ylabel('RCS (dBsm)', size=12)

plt.xlabel('Incident Angle (deg)', size=12)

# Set the tick label size

plt.tick_params(labelsize=12)

# Turn on the grid

plt.grid(linestyle=':', linewidth=0.5)

# Set the legend

plt.legend(loc='upper right', prop={'size': 10})
```

```python

```


# Doble pozo

- ¿Crees que podrías representar gráficamente con matplotlib aquí el siguiente potencial externo?

\begin{equation}
V(x)=E_{0}\left[ \left(\frac{x}{a}\right)^4 -2\left(\frac{x}{a}\right)^2 \right]-\frac{b}{a}x
\end{equation}

- ¿Qué interpretación le das a las constantes $E_{0}$, $a$ y $b$?

- Juega a modificar los parámetros del potencial, la temperatura, y la viscosidad.

```python
import openmm as mm
from openmm import app
from openmm import unit
import numpy as np
from tqdm import tqdm
import matplotlib.pyplot as plt
```

```python
# Definición del sistema.

n_particles = 1
mass = 100 * unit.amu
```

```python
# Creación del sistema.

system = mm.System()

for ii in range(n_particles):
    system.addParticle(mass)
```

```python
# Añadiendo el potencial externo al sistema

Eo = 3.0 * unit.kilocalories_per_mole
a = 0.5 * unit.nanometers
b = 0.5 * unit.kilocalories_per_mole
k = 1.0*unit.kilocalories_per_mole/unit.angstrom**2

A = Eo/(a**4)
B = -2.0*Eo/(a**2)
C = -b/a
D = k/2.0

force = mm.CustomExternalForce('A*x^4+B*x^2+C*x + D*(y^2+z^2)')
force.addGlobalParameter('A', A)
force.addGlobalParameter('B', B)
force.addGlobalParameter('C', C)
force.addGlobalParameter('D', D)

for ii in range(n_particles):
    force.addParticle(ii, [])

_ = system.addForce(force)

```

```python
# Definición del estado termodinámico y el integrador.

step_size = 0.01*unit.picoseconds
temperature = 300*unit.kelvin
friction = 1.0/unit.picosecond # Damping para la dinámica de Langevin

integrator = mm.LangevinIntegrator(temperature, friction, step_size)
```

**Por favor prueba a remplazar en la siguiente celda 'CPU' por 'CUDA' a ver si corre**

```python
# Creación de la plataforma.

platform_name = 'CPU'
platform = mm.Platform.getPlatformByName(platform_name)
```

```python
# Creación del contexto.

context = mm.Context(system, integrator, platform)
```

```python
# Condiciones iniciales

initial_positions  = np.zeros([n_particles, 3], np.float32) * unit.angstroms
initial_positions[0,0] = 5.0 * unit.angstroms

initial_velocities = np.zeros([n_particles, 3], np.float32) * unit.angstroms/unit.picoseconds

context.setPositions(initial_positions)
context.setVelocities(initial_velocities)
```

```python
# Parámetros de la simulación.

simulation_time = 10.0*unit.nanosecond
saving_time = 1.0*unit.picoseconds
```

```python
n_steps_per_period = int(saving_time/step_size) # número de pasos del periodo de guardado
n_periods = int(simulation_time/saving_time) # número de periodos guardados
```

```python
# Creación de arrays reporteros del tiempo, la posición y la velocidad.

times = np.zeros([n_periods], np.float32) * unit.picoseconds
positions = np.zeros([n_periods, n_particles, 3], np.float32) * unit.angstroms
velocities = np.zeros([n_periods, n_particles, 3], np.float32) * unit.angstroms/unit.picosecond
potential_energies   = np.zeros([n_periods], np.float32) * unit.kilocalories_per_mole
kinetic_energies     = np.zeros([n_periods], np.float32) * unit.kilocalories_per_mole
```

```python
# Almacenamiento en reporteros de las condiciones iniciales para tiempo 0

state = context.getState(getPositions=True, getVelocities=True, getEnergy=True)

times[0] = state.getTime()
positions[0] = state.getPositions()
velocities[0] = state.getVelocities()
kinetic_energies[0]=state.getKineticEnergy()
potential_energies[0]=state.getPotentialEnergy()
```

```python
# Ejecuto el bucle sobre el número de periodos que vamos a simular

for ii in tqdm(range(1, n_periods)):
    context.getIntegrator().step(n_steps_per_period)
    state = context.getState(getPositions=True, getVelocities=True, getEnergy=True)
    times[ii] = state.getTime()
    positions[ii] = state.getPositions()
    velocities[ii] = state.getVelocities()
    kinetic_energies[ii]=state.getKineticEnergy()
    potential_energies[ii]=state.getPotentialEnergy()
```

    100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 9999/9999 [02:53<00:00, 57.69it/s]

```python
#Graficando las trayectorias en X, Y y Z

plt.rcParams['figure.figsize'] = 18, 4

for ii, axis_label in zip(range(3),['X','Y','Z']):
    plt.plot(times, positions[:,0,ii])
    plt.ylabel('{} ({})'.format(axis_label, positions.unit))
    plt.xlabel('time ({})'.format(times.unit))
    plt.show()
```

```python
# Viendo la trayectoria en el plano X,Y

plt.rcParams['figure.figsize'] = 5, 5

plt.plot(positions[:,0,0], positions[:,0,1], '.')
plt.xlabel('X ({})'.format(positions.unit))
plt.ylabel('Y ({})'.format(positions.unit))
plt.xlim([-10.0, 10.0])
plt.ylim([-10.0, 10.0])
plt.show()
```

```python

```

```python

```