# PerfForesightConsumerType

```python
# Initial imports and notebook setup, click arrow to show
from HARK.ConsumptionSaving.ConsIndShockModel import PerfForesightConsumerType
from HARK.utilities import plotFuncs
from time import clock
import matplotlib.pyplot as plt
import numpy as np
mystr = lambda number : "{:.4f}".format(number)
```

The module $\texttt{HARK.ConsumptionSaving.ConsIndShockModel}$ concerns consumption-saving models with idiosyncratic shocks to (non-capital) income.  All of the models assume CRRA utility with geometric discounting, no bequest motive, and income shocks that are either fully transitory or fully permanent.

$\texttt{ConsIndShockModel}$ currently includes three models:
1. A very basic "perfect foresight" model with no uncertainty (shocks are zero).
2. A model with risk over transitory and permanent income shocks.
3. The model described in (2), with an interest rate for debt that differs from the interest rate for savings.

This notebook provides documentation for the first of these three models.
$\newcommand{\CRRA}{\rho}$
$\newcommand{\DiePrb}{\mathsf{D}}$
$\newcommand{\PermGroFac}{\Gamma}$
$\newcommand{\Rfree}{\mathsf{R}}$
$\newcommand{\DiscFac}{\beta}$

## Statement of the model

The $\texttt{PerfForesightConsumerType}$ class solves the problem of a consumer with Constant Relative Risk Aversion utility
${\CRRA}$
\begin{equation}
U(C) = \frac{C^{1-\CRRA}}{1-\rho},
\end{equation}
who has perfect foresight about everything except whether he will die between the end of period $t$ and the beginning of period $t+1$.  Permanent labor income $P_t$ grows from period $t$ to period $t+1$ by factor $\PermGroFac_{t+1}$.  The consumer faces no artificial borrowing constraint: He is able to borrow against his entire future stream of income.

At the beginning of period $t$, the consumer has market resources $M_t$ (which includes both market wealth and currrent income) and must choose how much to consume $C_t$ and how much to retain in a riskless asset $A_t$, which will earn return factor $\Rfree$. The agent's flow of future utility $U(C_{t+n})$ from consumption is geometrically discounted by factor $\DiscFac$ per period.  The consumer only experiences future value if he survives, which occurs with probability $1-\DiePrb_{t+1}$.

For parallelism with the treatment of more complicated problems, we write the problem rather elaborately in Bellman form as:

\begin{eqnarray*}
V_t(M_t,P_t) &=& \max_{C_t}~U(C_t) ~+ \DiscFac (1 - \DiePrb_{t+1}) V_{t+1}(M_{t+1},P_{t+1}), \\
& s.t. & \\
A_t &=& M_t - C_t, \\
M_{t+1} &=& \Rfree A_t + Y_{t+1}, \\
Y_{t+1} &=& P_{t+1}, \\
P_{t+1} &=& \PermGroFac_{t+1} P_t.
\end{eqnarray*}

The parameters of the consumer's problem are the coefficient of relative risk aversion $\CRRA$, the intertemporal discount factor $\DiscFac$, an interest factor $\Rfree$, and age-varying sequences of the permanent income growth factor $\PermGroFac_t$ and survival probability $(1 - \DiePrb_t)$.  [These lecture notes](http://econ.jhu.edu/people/ccarroll/public/lecturenotes/consumption/PerfForesightCRRA) show that under these assumptions the problem can be transformed into an equivalent problem stated in terms of *normalized* variables (represented in lower case); all real variables are divided by permanent income $P_t$ and value is divided by $P_t^{1-\CRRA}$.  The Bellman form of the normalized model (see the lecture notes for details) is:

\begin{eqnarray*}
v_t(m_t) &=& \max_{c_t}~U(c_t) ~+ \DiscFac (1 - \DiePrb_{t+1}) \PermGroFac_{t+1}^{1-\CRRA} v_{t+1}(m_{t+1}), \\
& s.t. & \\
a_t &=& m_t - c_t, \\
m_{t+1} &=& a_t (\Rfree/\PermGroFac_{t+1} )+ 1.
\end{eqnarray*}

## Solution method for PerfForesightConsumerType

Because of the assumptions of CRRA utility, no risk other than mortality, and no artificial borrowing constraint, the problem has a closed form solution in which consumption is a linear function of resources, and the utility-inverse of the value function is also linear (that is, $u^{-1}(v)$ is linear in $m$).  Details of the mathematical solution of this model can be found in the lecture notes [PerfForesightCRRA](http://econ.jhu.edu/people/ccarroll/public/lecturenotes/consumption/PerfForesightCRRA).

The one period problem for this model is solved by the function $\texttt{solveConsPerfForesight}$, which creates an instance of the class $\texttt{ConsPerfForesightSolver}$. To construct an instance of the class $\texttt{PerfForesightConsumerType}$, several parameters must be passed to this constructor.

## Example parameter values

| Parameter | Description | Code | Example value | Time-varying? |
| :---: | --- | --- | --- | --- |
| $\DiscFac$ |Intertemporal discount factor  | $\texttt{DiscFac}$ | $0.96$ |  |
| $\CRRA $ |Coefficient of relative risk aversion | $\texttt{CRRA}$ | $2.0$ | |
| $\Rfree$ | Risk free interest factor | $\texttt{Rfree}$ | $1.03$ | |
| $1 - \DiePrb_{t+1}$ |Survival probability | $\texttt{LivPrb}$ | $[0.98]$ | $\surd$ |
|$\PermGroFac_{t+1}$|Permanent income growth factor|$\texttt{PermGroFac}$| $[1.01]$ | $\surd$ |
|$T$| Number of periods in this type's "cycle" |$\texttt{T_cycle}$| $1$ | |
|(none)| Number of times the "cycle" occurs |$\texttt{cycles}$| $0$ | |

Note that the survival probability and income growth factor have time subscripts; likewise, the example values for these parameters are *lists* rather than simply single floats.  This is because those parameters are in principle *time-varying*: their values can depend on which period of the problem the agent is in (for example, mortality probability depends on age).  All time-varying parameters *must* be specified as lists, even when the model is being solved for an infinite horizon case where in practice the parameter takes the same value in every period.

The last two parameters in the table specify the "nature of time" for this type: the number of (non-terminal) periods in this type's "cycle", and the number of times that the "cycle" occurs.  *Every* subclass of $\texttt{AgentType}$ uses these two code parameters to define the nature of time.  Here, $\texttt{T_cycle}$ has the value $1$, indicating that there is exactly one period in the cycle, while $\texttt{cycles}$ is $0$, indicating that the cycle is repeated in *infinite* number of times-- it is an infinite horizon model, with the same "kind" of period repeated over and over.

In contrast, we could instead specify a life-cycle model by setting $\texttt{T_cycle}$ to $1$, and specifying age-varying sequences of income growth and survival probability.  In all cases, the number of elements in each time-varying parameter should exactly equal $\texttt{T_cycle}$.

The parameter $\texttt{AgentCount}$ specifies how many consumers there are of this *type*-- how many individuals have these exact parameter values and are *ex ante* homogeneous.  This information is not relevant for solving the model, but is needed in order to simulate a population of agents, introducing *ex post* heterogeneity through idiosyncratic shocks.  Of course, simulating a perfect foresight model is quite boring, as there are *no* idiosyncratic shocks other than death!

The cell below defines a dictionary that can be passed to the constructor method for $\texttt{PerfForesightConsumerType}$, with the values from the table here.

```python
PerfForesightDict = {
    # Parameters actually used in the solution method
    "CRRA" : 2.0,                          # Coefficient of relative risk aversion
    "Rfree" : 1.03,                        # Interest factor on assets
    "DiscFac" : 0.96,                      # Default intertemporal discount factor
    "LivPrb" : [0.98],                     # Survival probability
    "PermGroFac" :[1.01],                  # Permanent income growth factor

    # Parameters that characterize the nature of time
    "T_cycle" : 1,                         # Number of periods in the cycle for this agent type
    "cycles" : 0                           # Number of times the cycle occurs (0 --> infinitely repeated)
}
```

## Inspecting the solution

With the dictionary we have just defined, we can create an instance of $\texttt{PerfForesightConsumerType}$ by passing the dictionary to the class (as if the class were a function).  This instance can then be solved by invoking its $\texttt{solve}$ method.

```python
PFexample = PerfForesightConsumerType(**PerfForesightDict)
PFexample.cycles = 0
PFexample.solve()
```

The $\texttt{solve}$ method fills in the instance's attribute $\texttt{solution}$ as a time-varying list of solutions to each period of the consumer's problem.  In this case, $\texttt{solution}$ will be a list with exactly one instance of the class $\texttt{ConsumerSolution}$, representing the solution to the infinite horizon model we specified.

```python
print(PFexample.solution)
```

    [<HARK.ConsumptionSaving.ConsIndShockModel.ConsumerSolution object at 0x1c196a6f98>]

Each element of $\texttt{solution}$ has a few attributes. To see all of them, we can use the $\texttt{vars}$ built in function: the consumption functions are instantiated in the attribute $\texttt{cFunc}$ of each element of $\texttt{ConsumerType.solution}$.  This method creates a (time varying) attribute $\texttt{cFunc}$ that contains a list of consumption functions by age.

```python
print(vars(PFexample.solution[0]))
```

    {'cFunc': <HARK.interpolation.LinearInterp object at 0x1c196a4550>, 'vFunc': <HARK.ConsumptionSaving.ConsIndShockModel.ValueFunc object at 0x1c196a6fd0>, 'vPfunc': <HARK.ConsumptionSaving.ConsIndShockModel.MargValueFunc object at 0x1c196a6d68>, 'vPPfunc': <HARK.utilities.NullFunc object at 0x1c196a6da0>, 'mNrmMin': -50.49994992551661, 'hNrm': 50.49994992551661, 'MPCmin': 0.04428139169919579, 'MPCmax': 0.04428139169919579}

The two most important attributes of a single period solution are the (normalized) consumption function $\texttt{cFunc}$ and the (normalized) value function $\texttt{vFunc}$; the marginal value function $\texttt{vPfunc}$ is also constructed.  Let's plot those functions near the lower bound of the permissible state space (the attribute $\texttt{mNrmMin}$ tells us the lower bound of $m_t$ where the consumption function is defined).

```python
print('Linear perfect foresight consumption function:')
mMin = PFexample.solution[0].mNrmMin
plotFuncs(PFexample.solution[0].cFunc,mMin,mMin+10.)
```

```python
print('Perfect foresight value function:')
plotFuncs(PFexample.solution[0].vFunc,mMin+0.1,mMin+10.1)
```

## Solution Method

### Recursive Formula for $\kappa_{t}$

The paper [BufferStockTheory](https://www.econ2.jhu.edu/people/ccarroll/papers/BufferStockTheory/) has a few other results that are used in the solution code.  One is [the recursive formula for the MPC](https://www.econ2.jhu.edu/people/ccarroll/papers/BufferStockTheory/#MPCnvrs). Starting with the last period, in which $\kappa_{T}=1$, the inverse MPC's (and therefore the MPC's themselves) can be constructed using the recursive formula:

\begin{align}
\kappa_{t}^{-1} & = & 1 + \kappa_{t+1}^{-1}(\Rfree \beta)^{1/\rho}/G
\end{align}

### Consumption Function

For the perfect foresight problem, there is a well-known [analytical solution]( http://econ.jhu.edu/people/ccarroll/public/lecturenotes/consumption/PerfForesightCRRA/#cFuncAnalytical) for the consumption function:  Calling $o_{t}$ 'overall wealth' (including market wealth plus human wealth $h_{t}$) and designating the marginal propensity to consume in period $t$ by $\kappa_{t}$:

\begin{align}
\mathrm{c}_{t} & = o_{t}\kappa_{t}
\end{align}

and in our normalized model $o_{t} = m_{t}-1+h_{t}$ (the '-1' term subtracts off the normalized current income of 1 from market resources $m$ which were market wealth plus current income).

### Value Function

A convenient feature of the perfect foresight problem is that the value function has a simple [analytical form](http://econ.jhu.edu/people/ccarroll/public/lecturenotes/consumption/PerfForesightCRRA/#vFuncAnalytical):

\begin{align}
\mathrm{v}_{t} & = \mathrm{u}(\mathrm{c}_{t}(m))\kappa_{t}^{-1}\\
 &= \mathrm{u}(o_{t} \kappa_{t}) \kappa_{t}^{-1} \\
 &= \mathrm{u}(o_{t})\kappa_{t}^{1-\rho} \kappa_{t}^{-1} \\
 &= \mathrm{u}(o_{t})\kappa_{t}^{-\rho}
\end{align}

This means that the utility-inverse of the value function, ${\scriptsize \Lambda} \equiv \mathrm{u}^{-1}(\mathrm{v})$, is linear:

\begin{align}
\scriptsize \Lambda_{t} & = o_{t} \kappa_{t}^{-\rho/(1-\rho)}
\end{align}

When uncertainty or liquidity constraints are added to the problem, the ${\scriptsize \Lambda}$ function is no longer linear. But even in these cases, the utility-inverse of the value function is much better behaved (e.g., closer to linear; bounded over any feasible finite range of $m$) than the uninverted function (which, for example, approaches $-\infty$ as $m$ approaches its lower bound).

Our procedure will therefore generically be to construct the inverse value function, and to obtain the value function from it by uninverting.  That is, we construct an interpolating approximation of $\scriptsize \Lambda_{t}$ and compute value on-the-fly from

\begin{align}
\mathrm{v}_{t}(m) & = \mathrm{u}({\scriptsize \Lambda_{t}}(m))
\end{align}

In this case, the interpolation is exact, not an approximation: We need only two points to construct a line, so we choose the minimum possible value of normalized market resources, $\texttt{mNrmMin}$, where $o_{t}=0$ so that $c_{t}=0$, and that minimum plus 1, where the inverted value function will have the value $\kappa_{t}^{-\rho/(1-\rho)}$.  From these we construct $vFuncNvrs$ as a linear interpolating function (which automatically extrapolates to the whole number line).

## Checking Solution Conditions

The code performs tests for whether the supplied parameter values meet various conditions that determine the properties of the solution.  Some conditions (like the Finite Human Wealth Condition) are required for the model to have a sensible solution, and if these conditions are violated the code generates a warning message.  Other conditions govern characteristics of the model like whether consumption is falling (whether the consumer is 'absolutely impatient').  All conditions can manually be performed using the syntax below.  The function returns "False" if none of the key conditions has been violated.

```python
PFexample.checkConditions(verbose=True,public_call=True)
```

    The value of the absolute impatience factor for the supplied parameter values satisfies the Absolute Impatience Condition. Therefore, the absolute amount of consumption is expected to fall over time.

    The value of the growth impatience factor for the supplied parameter values satisfies the Growth Impatience Condition.  Therefore, the ratio of individual wealth to permanent income will fall indefinitely.

    The return impatience factor value for the supplied parameter values satisfies the Return Impatience Condition. Therefore, the limiting consumption function is not c(m)=0

    The Finite Human wealth factor value for the supplied parameter values satisfies the Finite Human Wealth Condition. Therefore, the limiting consumption function is not c(m)=Infinity
    and human wealth normalized by permanent income is 51.50000
    and the PDV of future consumption growth is 22.58285

    False

An element of $\texttt{solution}$ also includes the (normalized) marginal value function $\texttt{vPfunc}$, and the lower and upper bounds of the marginal propensity to consume (MPC) $\texttt{MPCmin}$ and $\texttt{MPCmax}$.  Note that with a linear consumption function, the MPC is constant, so its lower and upper bound are identical.

## Simulating the model

Suppose we wanted to simulate many consumers who share the parameter values that we passed to $\texttt{PerfForesightConsumerType}$-- an *ex ante* homogeneous *type* of consumers.  To do this, our instance would have to know *how many* agents there are of this type, as well as their initial levels of assets $a_t$ and permanent income $P_t$.

### Setting Parameters

Let's fill in this information by passing another dictionary to $\texttt{PFexample}$ with simulation parameters.  The table below lists the parameters that an instance of $\texttt{PerfForesightConsumerType}$ needs in order to successfully simulate its model using the $\texttt{simulate}$ method.

| Description | Code | Example value |
| :---: | --- | --- |
| Number of consumers of this type | $\texttt{AgentCount}$ | $10000$ |
| Number of periods to simulate | $\texttt{T_sim}$ | $120$ |
| Mean of initial log (normalized) assets | $\texttt{aNrmInitMean}$ | $-6.0$ |
| Stdev of initial log  (normalized) assets | $\texttt{aNrmInitStd}$ | $1.0$ |
| Mean of initial log permanent income | $\texttt{pLvlInitMean}$ | $0.0$ |
| Stdev of initial log permanent income | $\texttt{pLvlInitStd}$ | $0.0$ |
| Aggregrate productivity growth factor | $\texttt{PermGroFacAgg}$ | $1.0$ |
| Age after which consumers are automatically killed | $\texttt{T_age}$ | $None$ |

We have specified the model so that initial assets and permanent income are both distributed lognormally, with mean and standard deviation of the underlying normal distributions provided by the user.

The parameter $\texttt{PermGroFacAgg}$ exists for compatibility with more advanced models that employ aggregate productivity shocks; it can simply be set to 1.

In infinite horizon models, it might be useful to prevent agents from living extraordinarily long lives through a fortuitous sequence of mortality shocks.  We have thus provided the option of setting $\texttt{T_age}$ to specify the maximum number of periods that a consumer can live before they are automatically killed (and replaced with a new consumer with initial state drawn from the specified distributions).  This can be turned off by setting it to $\texttt{None}$.

The cell below puts these parameters into a dictionary, then gives them to $\texttt{PFexample}$.  Note that all of these parameters *could* have been passed as part of the original dictionary; we omitted them above for simplicity.

```python
# Create parameter values necessary for simulation
SimulationParams = {
    "AgentCount" : 10000,                  # Number of agents of this type
    "T_sim" : 120,                         # Number of periods to simulate
    "aNrmInitMean" : -6.0,                 # Mean of log initial assets
    "aNrmInitStd"  : 1.0,                  # Standard deviation of log initial assets
    "pLvlInitMean" : 0.0,                  # Mean of log initial permanent income
    "pLvlInitStd"  : 0.0,                  # Standard deviation of log initial permanent income
    "PermGroFacAgg" : 1.0,                 # Aggregate permanent income growth factor
    "T_age" : None,                        # Age after which simulated agents are automatically killed
}

PFexample(**SimulationParams) # This implicitly uses the assignParameters method of AgentType
```

To generate simulated data, we need to specify which variables we want to track the "history" of for this instance.  To do so, we set the $\texttt{track_vars}$ attribute of our $\texttt{PerfForesightConsumerType}$ instance to be a list of strings with the simulation variables we want to track.

In this model, valid elments of $\texttt{track_vars}$ include $\texttt{mNrmNow}$, $\texttt{cNrmNow}$, $\texttt{aNrmNow}$, and $\texttt{pLvlNow}$.  Because this model has no idiosyncratic shocks, our simulated data will be quite boring.

### Generating simulated data

Before simulating, the $\texttt{initializeSim}$ method must be invoked.  This resets our instance back to its initial state, drawing a set of initial $\texttt{aNrmNow}$ and $\texttt{pLvlNow}$ values from the specified distributions and storing them in the attributes $\texttt{aNrmNow_init}$ and $\texttt{pLvlNow_init}$.  It also resets this instance's internal random number generator, so that the same initial states will be set every time $\texttt{initializeSim}$ is called.  In models with non-trivial shocks, this also ensures that the same sequence of shocks will be generated on every simulation run.

Finally, the $\texttt{simulate}$ method can be called.

```python
# Create PFexample object
PFexample.track_vars = ['mNrmNow']
PFexample.initializeSim()
PFexample.simulate()
```

Each simulation variable $\texttt{X}$ named in $\texttt{track_vars}$ will have the *history* of that variable for each agent stored in the attribute $\texttt{X_hist}$ as an array of shape $(\texttt{T_sim},\texttt{AgentCount})$.  To see that the simulation worked as intended, we can plot the mean of $m_t$ in each simulated period:

```python
# Plot market resources over time
plt.plot(np.mean(PFexample.mNrmNow_hist,axis=1))
plt.xlabel('Time')
plt.ylabel('Mean normalized market resources')
plt.show()
```

A perfect foresight consumer can borrow against the PDV of his future income-- his human wealth-- and thus as time goes on, our simulated impatient agents approach the (very negative) steady state level of $m_t$ while being steadily replaced with consumers with roughly $m_t=1$.

The slight wiggles in the plotted curve are due to consumers randomly dying and being replaced; their replacement will have an initial state drawn from the distributions specified by the user.  To see the current distribution of ages, we can look at the attribute $\texttt{t_age}$.

```python
# Plot the CDF
N = PFexample.AgentCount
F = np.linspace(0.,1.,N)
plt.plot(np.sort(PFexample.t_age),F)
plt.xlabel('Current age of consumers')
plt.ylabel('Cumulative distribution')
plt.show()
```

The distribution is (discretely) exponential, with a point mass at 120 with consumers who have survived since the beginning of the simulation.

One might wonder why HARK requires users to call $\texttt{initializeSim}$ before calling $\texttt{simulate}$: Why doesn't $\texttt{simulate}$ just call $\texttt{initializeSim}$ as its first step?  We have broken up these two steps so that users can simulate some number of periods, change something in the environment, and then resume the simulation.

When called with no argument, $\texttt{simulate}$ will simulate the model for $\texttt{T_sim}$ periods.  The user can optionally pass an integer specifying the number of periods to simulate (which should not exceed $\texttt{T_sim}$).

In the cell below, we simulate our perfect foresight consumers for 80 periods, then seize a bunch of their assets (dragging their wealth even more negative), then simulate for the reamining 40 periods.

```python
# The final resulting distribution is reasonably coherent
PFexample.initializeSim()
PFexample.simulate(80)
PFexample.aNrmNow += -5. # Adjust all simulated consumers' assets downward by 5
PFexample.simulate(40)

plt.plot(np.mean(PFexample.mNrmNow_hist,axis=1))
plt.xlabel('Time')
plt.ylabel('Mean normalized market resources')
plt.show()
```


```python
from qmt.physics_constants import parse_unit,to_float
from qmt.geometry import part_3d, build_3d_geometry
from qmt.materials import Materials, build_materials, make_materials_library
import pickle, logging
import matplotlib.pyplot as plt
import numpy as np
from scipy.interpolate import griddata
import deepdish
from qms.physics import PoissonFem
import fenics as fn
```

Load some 2D shapes from simple_wire.FCStd, and then build some a 3D structure out of them

```python
back_gate = part_3d.ExtrudePart("back_gate", "Sketch", z0=-0.2, thickness=0.1)
vacuum = part_3d.ExtrudePart("vacuum", "Sketch003", z0=-0.5, thickness=1.0)
wire = part_3d.ExtrudePart("wire", "Sketch002", z0=0.0, thickness=0.1)
shell = part_3d.ExtrudePart("shell", "Sketch002", z0=0.1, thickness=0.05)
build_order = [wire, shell, back_gate, vacuum]
file_path = './simple_wire.FCStd'
geo_data = build_3d_geometry(input_parts=build_order, input_file=file_path,
                             xsec_dict={'central':{'axis':(1.,0.,0.),'distance':0.}})
geo_data.write_fcstd('built_geo.fcstd')
```

At this point you can try opening built_geo.fcstd with FreeCAD and taking a look at the built shape. Feel free to skip this step if you're unfamiliar with the FreeCAD GUI.
We can check that our cross sections and parts are as expected:

```python
geo_data.xsecs
```

```python
geo_data.parts
```

Now we can do meshing. A mesh has already been pre built so you can just load it.

```python
from qms.meshing import MeshPart, MeshData
# from qms.tasks import build_3d_mesh
# mesh_data = build_3d_mesh(
#     geo_data,
#     {
#         "back_gate": MeshPart(mesh_max_size=0.1),
#         "vacuum": MeshPart(mesh_max_size=0.05),
#         "wire": MeshPart(mesh_max_size=0.01),
#         "shell": MeshPart(mesh_max_size=0.01),
#     },
#     "comsol"
# )

# mesh_data.save("mesh_data.h5")

mesh_data = MeshData.load("mesh_data.h5")
```

Determine the reference level and Al work function to get a 0.1 meV band offset between InSb and Al:

```python
mat_lib = Materials()
Al_WF = mat_lib['Al']['workFunction']
InSb_EA = mat_lib['InSb']['electronAffinity']
InSb_BG = mat_lib['InSb']['directBandGap']
InSb_VBO = mat_lib['InSb']['valenceBandOffset']
Al_WF_level = 0.0-(Al_WF)
InSb_CB_level = 0.0-InSb_EA+InSb_VBO
WF_shift = 200.*parse_unit('meV')-(Al_WF_level-InSb_CB_level)
new_Al_WF = (Al_WF-WF_shift)
ref_level = -new_Al_WF

mat_lib = make_materials_library({"Al":{"workFunction": new_Al_WF}})
mat_data = build_materials(geo_data,
                           {"back_gate": "Al", "vacuum": "air", "wire": "InSb", "shell": "Al"},
                           mat_lib)
```

Now we can run the electrostatic simulations. Again you can just load the results.

```python
# from qms.tasks.thomas_fermi import ThomasFermiPart
# from qms.tasks import run_3d_thomas_fermi
# import sympy.physics.units as spu
# logging.basicConfig(level=logging.INFO)
# tf_data = run_3d_thomas_fermi(geo_data,
#                               mesh_data,
#                               mat_data,
#                               {
#                                   "back_gate": ThomasFermiPart("metal_gate", boundary_condition={"voltage": 0.0 * spu.V}),
#                                   "vacuum": ThomasFermiPart("dielectric"),
#                                   "wire": ThomasFermiPart("semiconductor"),
#                                   "shell": ThomasFermiPart("metal_gate", boundary_condition={"voltage": 0.0 * spu.V}),
#                               },
#                               reference_level=ref_level,
#                               order=1,
#                              )
# tf_data.save("tf_data.h5")

from qms.physics import PoissonFem
tf_data = PoissonFem.load("tf_data.h5")
```

Great! Let's take a look at the potential profile to make sure it looks reasonable:

```python
femcoords = tf_data.coordinates
femdata = tf_data.potential
xgrid, ygrid, zgrid = np.mgrid[0:1, -0.2:0.2:0.005, -0.5:0.5:0.0125]
plot_potential = griddata(femcoords, femdata, (xgrid, ygrid, zgrid), method='linear')
plt.pcolor(ygrid[0],zgrid[0],plot_potential[0])
plt.colorbar()
plt.show()
```

This looks fine. Let's now look at a line cut:

```python
xgrid, ygrid, zgrid = np.mgrid[0:1, 0:1, -0.2:0.2:0.002]
potential_cut = griddata(femcoords, femdata, (xgrid, ygrid, zgrid), method='linear')
plt.plot(zgrid[0,0],potential_cut[0,0])
plt.show()
```

This all looks fine. In the region between 0.0 and 0.1, we have accumulation. Let's make sure this holds up when taking into account the conduction band offset:

```python
xgrid, ygrid, zgrid = np.mgrid[0:1, 0:1, 0:0.1:0.0005]
potential_cut = griddata(femcoords, femdata, (xgrid, ygrid, zgrid), method='linear')
plt.plot(zgrid[0,0],potential_cut[0,0])
plt.show()
```

Putting in the proper band offsets, we get:

```python
zvec = zgrid[0,0]
potential_cut = potential_cut[0,0]
offset_CB = to_float((InSb_CB_level-ref_level) /parse_unit('meV'))/1e3
offset_VB = offset_CB-InSb_BG/parse_unit('meV')/1e3
plt.plot(zvec,offset_CB-potential_cut)
plt.plot(zvec,offset_VB-potential_cut)
plt.plot(zvec,np.zeros(zvec.shape))
plt.show()
```


```python
import numpy as np
import sympy as sp
import matplotlib.pyplot as plt
%matplotlib inline

import pi_sequences as p3
import boundary_layer_func1 as p1
import sequence_limits as p2
import diffeq_midpoint as p4

import math
```

## Classwork 3
Michael Seaman, Chinmai Raman, Austin Ayers, Taylor Patti

Organized by Andrew Malfavon

## Excercise A.2: Computing $\pi$ via sequences
Michael Seaman

The following sequences all converge to pi, although at different rates.

In order:
$$a_n = 4\sum_{k=1}^{n}\frac{(-1)^{k+1}}{2k-1}$$

$$b_n = (6\sum_{k=1}^{n}k^{-2})^{1/2} $$

$$c_n = (90\sum_{k=1}^{n}k^{-4})^{1/4} $$

$$d_n = \frac{6}{\sqrt{3}}\sum_{k=0}^{n}\frac{(-1)^{k}}{3^k(2k+1)}$$

$$e_n = 16\sum_{k=0}^{n}\frac{(-1)^{k}}{5^{2k+1}(2k+1)}  - 4\sum_{k=0}^{n}\frac{(-1)^{k}}{239^{2k+1}(2k+1)}$$

```python
n = 30
plt.plot([x for x in range(n)],p3.pi_sequence(n, p3.fa),'g.')
plt.show()
```

```python
plt.plot([x for x in range(n)],p3.pi_sequence(n, p3.fb) ** .5 ,'b.')
plt.show()
```

```python
plt.plot([x for x in range(n)],p3.pi_sequence(n, p3.fc) ** .25 ,'y.')
plt.show()
```

```python
plt.plot([x for x in range(n)],p3.pi_sequence(n, p3.fd),'r.')
plt.show()
```

```python
plt.plot([x for x in range(n)],p3.pi_sequence(n, p3.fd),'c.')
plt.show()
```

```python
n = 10
plt.plot([x + 20 for x in range(n)],p3.pi_sequence(n + 20, p3.fa)[-n:],'g.')
plt.plot([x + 20 for x in range(n)],(p3.pi_sequence(n + 20, p3.fb) ** .5)[-n:] ,'b.')
plt.plot([x + 20 for x in range(n)],(p3.pi_sequence(n + 20, p3.fc) ** .25)[-n:] ,'y.')
plt.plot([x + 20 for x in range(n)],p3.pi_sequence(n + 20, p3.fd)[-n:],'r.')
plt.plot([x + 20 for x in range(n)],p3.pi_sequence(n + 20, p3.fd)[-n:],'c.')

plt.plot((20, 30), (math.pi, math.pi), 'b')

plt.show()

```

# Chinmai Raman

## Classwork 3

### 5.49 Experience Overflow in a Function
Calculates an exponential function and returns the numerator, denominator and the fraction as a 3-tuple

```python
x = np.linspace(0,1,10000)
y1 = p1.v(x, 1, np.exp)[2]
y2 = p1.v(x, 0.1, np.exp)[2]
y3 = p1.v(x, 0.01, np.exp)[2]

fig = plt.figure(1)
plt.plot(x, y1, 'b')
plt.plot(x, y2, 'r')
plt.plot(x, y3, 'g')
plt.xlabel('x')
plt.ylabel('v(x)')
plt.legend(['(1 - exp(x / mu)) / (1 - exp(1 / mu))'])
plt.axis([x[0], x[-1], min(y3), max(y3)])
plt.title('Math Function')
plt.show(fig)
```

# Austin Ayers

## Classwork 3

### A.1 Determine the limit of a sequence

Computes and returns the following sequence for N = 100

$$a_n = \frac{7+1/(n+1)}{3-1/(n+1)^2}, \qquad n=0,1,2,\ldots,N$$

```python
p2.part_a()
```

    4.0
    2.53846153846
    2.43243243243
    2.39726027397
    2.38016528926
    2.37016574586
    2.36363636364
    2.3590504451
    2.35565819861
    2.35304990758
    2.35098335855
    2.34930643127
    2.34791889007
    2.34675205855
    2.34575733545
    2.34489937543
    2.34415186773
    2.34349482852
    2.34291281052
    2.34239368698
    2.34192780643
    2.34150739272
    2.34112611129
    2.34077875038
    2.34046098306
    2.34016918739
    2.33990030857
    2.33965175226
    2.33942130105
    2.33920704846
    2.33900734635
    2.33882076264
    2.33864604703
    2.33848210307
    2.33832796527
    2.33818278006
    2.33804579007
    2.33791632097
    2.33779377038
    2.33767759855
    2.33756732039
    2.33746249879
    2.33736273877
    2.33726768255
    2.3371770053
    2.3370904114
    2.33700763123
    2.33692841841
    2.3368525473
    2.3367798109

    The series converges to: 7/3 or 2.3333..., and a_N was: 2.33671001895 and the difference was: 0.00337668561968

```python
p2.part_b()
```

    The series converges to: 7/3 or 2.3333..., and a_N was: 2.33671001895 and the difference was: 0.00337668561968
    The limit exists (to this algorithm's knowledge)
    The limit is roughly: 2.3367798109

```python
p2.part_c()
```

    None
    The limit is roughly: 1.0

```python
p2.part_d()
```

```python
p2.part_e()
```

```python
p2.part_f()
```

    the computations for x = pi go wrong for large N because sin(pi) = 0 and 2 ** (-n) approaches 0 as well, so the numerator and denominator both go to 0 and that usually leads to problems.

# diffeq_midpoint

## Taylor Patti

Uses the midpoint integration rule along with numpy vectors to produce a continuous vector which gives integral data for an array of prespecified points.

Here we use it to integrate sin from 0 to pi.

```python
function_call = p4.vector_midpoint(p4.np.sin, 0, p4.np.pi, 10000)
print function_call[1][-1]
```

    1.99999990953

Observe the close adherance to the actual value of this cannonical value.

We can also call it at a different value of x. Let's look at the value of this integral from 0 to pi over 2. Again, the result will have strikingly close adherance to the analytical value of this integral.

```python
print function_call[1][5000]
```

    1.00031411403

```python

```


# Other (Quantum) Projects

## Quantum Single Layer Perceptron (qSLP)

A. Macaluso, L.Clissa, S. Lodi, and C. Sartori. “**A Variational Algorithm for Quantum Neural Networks**”. *International Conference on Computational Science. Springer. 2020*

Github: [https://github.com/amacaluso/Quantum-Single-Layer-Perceptron](github.com/amacaluso/Quantum-Single-Layer-Perceptron)

- Potentially Enabling of Universal Approximation Property

- Quantum circuit as black box (like classical NN)

- Classical optimisation process

# (Theoretical) Advantages and limitations

+ Polylogarithimc number of parameters (+)

+ Exponential scaling in the hidden layer enables the universal approximation  (+)

+ Single execution of the activation function (+)

+ Activation function is required to be non-linear (-)

+ Generalisation of the quantum SLP (-)

# Future Works

+ Design a proper quantum activation function

+ Generalise the circuit for the quantum SLP

+ Experiments on (better) real device

+ Recent developments: *GOTO, Takahiro et al."Universal Approximation Property of Quantum Feature Map". arXiv preprint arXiv:2009.00298*

# Beyond Linearity: Quantum Splines

A. Macaluso, L.Clissa, S. Lodi, and C. Sartori. “**Quantum splines for non-linear approximations**”. *17th ACM International Conference on Computing Frontiers. 2020*

Github: [https://github.com/amacaluso/Quantum-Splines-for-Non-Linear-Approximations](**github.com/amacaluso/Quantum-Splines-for-Non-Linear-Approximations**)

- Non-linear approximation via quantum splines (Qsplines)

- Experiments on some popular activation functions for Neural Networks

- Theoretical comparison of HHL efficiency with classical alternatives

$$\newcommand{\ket}[1]{\left|{#1}\right\rangle}$$

## Quantum Splines Implementation

+ First Step – Coefficient estimation via HHL

\begin{align}
S_k \ket{\beta_k} = \ket{\tilde{y_k}} \xrightarrow{HHL} \ket{\beta_k} \simeq S_k^{-1} \ket{\tilde{y_k}}.
\end{align}

+ Second Step – Function evaluation:

\begin{align}
    \ket{\beta_k} \ket{x_{i,k}} \ket{0}  \xrightarrow{swap-test} \ket{e_1} \ket{e_2} \ket{f_{i,k}}.
\end{align}

+ Third Step – Measurement

\begin{align}
    \ket{f_{i,k}} = \sqrt{p_0}\ket{0} + \sqrt{p_1}\ket{1}
\end{align}

# Advantages and Future works

- Non-linearity achieved using a quantum algorithm

- Computational Efficiency

- Enabling the use of spline also for high-dimensional data

-  Leverage the efficiency of the HHL

- Improve HHL implementation and optimise full quantum approach

- Embed the quantum spline in Quantum Neural Networks

## Antonio Macaluso

### PhD candidate in Computer Science and Engineering, University of Bologna, Italy

#### Education:

+ Bsc and Msc in Statistics
+ Master in Data Science
+ PhD in Computer Science and Engineering (loading ...)

#### Work Experience:
+ Data Scientist at Cineca, the largest supercomputing centre in Italy (3yr)
+ Lecturer of several courses about Machine Learning and Data Science

#### Research Experience:

+ (Classical) Deep Learning
+ Quantum Computing and Quantum Machine Learning

 My PhD thesis is focused on the development of novel Quantum Machine Learning algorithms!

# Additional Information

Github: [github.com/amacaluso/Quantum-Ensemble-for-Classification](https://github.com/amacaluso/Quantum-Ensemble-for-Classification)

Presentation: [nbviewer.jupyter.org/github/amacaluso/Quantum-Ensemble-for-Classification/tree/master/presentation/](https://nbviewer.jupyter.org/github/amacaluso/Quantum-Ensemble-for-Classification/tree/master/presentation/)

GitHub: [https://github.com/amacaluso](https://github.com/amacaluso)

Email: [antonio.macaluso2@unibo.it](antonio.macaluso2@unibo.it)

Website: [www.unibo.it/sitoweb/antonio.macaluso2/en](https://www.unibo.it/sitoweb/antonio.macaluso2/en)

LinkedIn: [www.linkedin.com/in/antonio-macaluso](https://www.linkedin.com/in/antonio-macaluso)

I recently became a qiskit advocate!


## Ezyme-Subtrate Kinetics Rule Creator

This notebook uses the Boolean_rules_creator tool available to generate
rules for a boolean network describing Michaelis-Menten enzyme-substrate kinetics. For more information on the tool and installation instructions, view the GitHub page at https://github.com/LoLab-VU/Boolean_rules_creator .

   >More details on the algorithm and a description of the model are available in the preprint at
   >https://www.biorxiv.org/content/10.1101/2020.12.15.422874v1.full (Prugger et. al. 2020).

### Importing libraries

```python
from rule_creator import creating_rules
import json
import sympy
from sympy.logic.boolalg import Xor
from sympy import symbols
```

### Loading the input file

For the ES model, there is only one possibly steady state outcome, so the Boolean ruleset can be generation without optimization. The list  In this example, only the rule_creator function is used to recreate the forward, backwards, and expert-guided rulesets.

The input file for this model is 'ES_steady_states.json', which contains the possible initial boolean states for the four species: Enzyme (En), Substrate (Su), Enzyme-Substrate complex (ES), and Product (Pr). For this script, these states are always referenced in this order.

```python
fn = 'ES_steady_states.json'
En, Su, ES, Pr = symbols('En Su ES Pr')
symbols_list = ['En', 'Su', 'ES', 'Pr']
```

The input file contains pairs of intial and steady states followed by the frequency
with which the intial state leads to that final state.

```python
with open(fn) as fs:
    data = json.load(fs)
    fs.close()
print(data.items())
```

    dict_items([('[0, 0, 0, 0]', [[[0, 0, 0, 0], 100]]), ('[0, 0, 0, 1]', [[[0, 0, 0, 1], 100]]), ('[0, 0, 1, 0]', [[[1, 0, 0, 1], 100]]), ('[0, 0, 1, 1]', [[[1, 0, 0, 1], 100]]), ('[0, 1, 0, 0]', [[[0, 1, 0, 0], 100]]), ('[0, 1, 0, 1]', [[[0, 1, 0, 1], 100]]), ('[0, 1, 1, 0]', [[[1, 0, 0, 1], 100]]), ('[0, 1, 1, 1]', [[[1, 0, 0, 1], 100]]), ('[1, 0, 0, 0]', [[[1, 0, 0, 0], 100]]), ('[1, 0, 0, 1]', [[[1, 0, 0, 1], 100]]), ('[1, 0, 1, 0]', [[[1, 0, 0, 1], 100]]), ('[1, 0, 1, 1]', [[[1, 0, 0, 1], 100]]), ('[1, 1, 0, 0]', [[[1, 0, 0, 1], 100]]), ('[1, 1, 0, 1]', [[[1, 0, 0, 1], 100]]), ('[1, 1, 1, 0]', [[[1, 0, 0, 1], 100]]), ('[1, 1, 1, 1]', [[[1, 0, 0, 1], 100]])])

### Generating network rules

The function creating_rules will take these input states and generate a list of rules which contain
the possible transitions to describe the boolean network. For this model, the first three states, as seen below, can only progress to their respective steady states due to the lack of reactive species. For all other intial states the only possible steady state is [1,0,0,1].

```python
#Call the creating_rules function to generate str_rules and simple_rulelist
#Inputs are the filename, list of symbols, and backwardspaths (0 = forward paths, 1 = backward paths)
str_rules, simple_rulelist, fs_cpp_name = creating_rules(fn,symbols_list,backwardpaths=0)
```

    generating rules took  0.008001089096069336 seconds

Looking at the resulting rule list with all backwords transitions included:

```python
print('The string rules describe the full ruleset in formal logical terms, showing what states can activate that species.')
print('\nString rules:')
print(str_rules)

print('The simple rulelist show the string rules in their boolean form; this conveys the same information as above.')
print('\nSimple rulelist')
print(simple_rulelist)
```

    The string rules describe the full ruleset in formal logical terms, showing what states can activate that species.

    String rules:
    1: En* = Xor((( not En and  not Su and ES and  not Pr) or ( not En and  not Su and ES and Pr) or ( not En and Su and ES and  not Pr) or ( not En and Su and ES and Pr)), En)
    1: Su* = Xor((( not En and Su and ES and Pr) or (En and Su and ES and  not Pr) or ( not En and Su and ES and  not Pr) or (En and Su and  not ES and Pr) or (En and Su and ES and Pr)), Su)
    1: ES* = Xor(((En and Su and ES and  not Pr) or (En and Su and ES and Pr) or (En and  not Su and ES and Pr)), ES)
    1: Pr* = Xor((( not En and  not Su and ES and  not Pr) or (En and Su and  not ES and  not Pr) or (En and Su and ES and  not Pr) or ( not En and Su and ES and  not Pr) or (En and  not Su and ES and  not Pr)), Pr)

    The simple rulelist show the string rules in their boolean form; this conveys the same information as above.

    Simple rulelist
    [[(0, 0, 1, 0), (0, 0, 1, 1), (0, 1, 1, 0), (0, 1, 1, 1)], [(0, 1, 1, 1), (1, 1, 1, 0), (0, 1, 1, 0), (1, 1, 0, 1), (1, 1, 1, 1)], [(1, 1, 1, 0), (1, 1, 1, 1), (1, 0, 1, 1)], [(0, 0, 1, 0), (1, 1, 0, 0), (1, 1, 1, 0), (0, 1, 1, 0), (1, 0, 1, 0)]]

The resulting rulelist generated using forward rules for each species is displayed in the table below based on their Hamming distance, where d is the number of steps needed to reach the steady state [1,0,0,1].

| Species 	| list for d=1  	| list for d=2         	        | list for d=3         	    | list for d=4 	|
|:---------:|:--------------: 	|:-----------------------------:|:-------------------------:|:-------------:|
| En      	|     	    |  [0,0,1,1]  | [0,1,1,1], [0,0,1,0] 	    | [0,1,1,0]    	|
| Su      	|  [1,1,0,1]     |  [1,1,1,1],                     | [1,1,1,0], [0,1,1,1],  	    | [0,1,1,0]    	|
| ES      	| [1,0,1,1]    	    |  [1,1,1,1]            	    | [1,1,1,0]            	    |              	|
| Pr      	|          	    |  [1,0,1,0], [1,1,0,0],  	    | [0,0,1,0], [1,1,1,0]  	    | [0,1,1,0]   	|

This can also be done with all backward paths included:

| Species 	| list for d=1  	| list for d=2         	        | list for d=3         	    | list for d=4 	|
|:---------:|:--------------: 	|:-----------------------------:|:-------------------------:|:-------------:|
| En      	| [1,0,1,1]    	    |  [0,0,1,1], [1,0,1,0], [1,1,1,1]   | [0,1,1,1], [1,1,1,0], [0,0,1,0] 	    | [0,1,1,0]    	|
| Su      	| [1,0,1,1], [1,1,0,1]     |  [1,1,1,1], [1,0,1,0], [0,0,1,1]                    | [1,1,1,0], [0,1,1,1], [0,0,1,0] 	    | [0,1,1,0]    	|
| ES      	| [1,0,1,1], [1,1,0,1]    	    |  [1,1,0,0], [1,1,1,1]            	    | [1,1,1,0]            	    |              	|
| Pr      	|  [1,0,1,1], [1,1,0,1]           	    |  [1,0,1,0], [1,1,0,0], [0,0,1,1], [1,1,1,1]	    | [0,0,1,0], [1,1,1,0], [0,1,1,1]  	    | [0,1,1,0]    	|

With the input variable backwardspath set as 1, we generate the network with all possible backwards paths available
for transitions. The simple rulelist output describes the resulting network. The example below (from Figure 3) shows the network formed with the initial state [1,1,0,0].

### Generating simplified rules from manuscript

In order to simplify the forward only rules to match those in the paper, the Sympy library is used. For this, the string rules must first be converted to formal logic notation.

```python
str_rules_list = str_rules.split('\n')
str_rules_list = str_rules_list[0:4]
count = 0

for n in str_rules_list:
    str_rules_list[count] = n.strip('1: '+symbols_list[count]+'* = ')
    count +=1

sympy_input = []
for k in range(len(str_rules_list)):
    sympy_input.append(str_rules_list[k].replace('and', '&').replace(' or',' |').replace('not ', '~'))

print(sympy_input)
```

    ['Xor((( ~En &  ~Su & ES &  ~Pr) | ( ~En &  ~Su & ES & Pr) | ( ~En & Su & ES &  ~Pr) | ( ~En & Su & ES & Pr)), En)', 'Xor((( ~En & Su & ES & Pr) | (En & Su & ES &  ~Pr) | ( ~En & Su & ES &  ~Pr) | (En & Su &  ~ES & Pr) | (En & Su & ES & Pr)), Su)', 'Xor(((En & Su & ES &  ~Pr) | (En & Su & ES & Pr) | (En &  ~Su & ES & Pr)), ES)', 'Xor((( ~En &  ~Su & ES &  ~Pr) | (En & Su &  ~ES &  ~Pr) | (En & Su & ES &  ~Pr) | ( ~En & Su & ES &  ~Pr) | (En &  ~Su & ES &  ~Pr)), Pr)']

Using Sympy to simplify the Xor statement for each species yields the rules for the ES-B network as seen in the paper. Note that '|' represents 'or', '&' represents 'and', and '~' represents 'not'.

```python
print('Rules after simplifcation with Sympy')
#exp_EN = sympy.simplify(sympy.sympify(sympy_input[0][1]))

print('EN* = ' + str(sympy.to_dnf(sympy_input[0], simplify=True)))
print('S* = ' + str(sympy.to_dnf(sympy_input[1], simplify=True)))
print('ES* = ' + str(sympy.to_dnf(sympy_input[2], simplify=True)))
print('P* = ' + str(sympy.to_dnf(sympy_input[3], simplify=True)))

```

    Rules after simplifcation with Sympy
    EN* = ES | En
    S* = (Su & ~ES & ~En) | (Su & ~ES & ~Pr)
    ES* = (ES & ~En) | (ES & ~Pr & ~Su)
    P* = ES | Pr | (En & Su)

### Incorporating expert knowledge

Now we are demonstrating how expert knowledge can be incorporated so that we get the correct kinetics. Here we are adding two transitions we know to be necessary and recreating the rule list using these with additions with the forward paths only.

```python
addlist = [((1, 1, 1, 0), 0), ((1, 1, 0, 0), 2)]

str_rules_expert_fw, simple_rulelist_expert_fw, fs_cpp_name_expert_fw = creating_rules(fn,symbols_list,backwardpaths=0,addlist=addlist)
```

    generating rules took  0.010995149612426758 seconds

Formatting the string rules for simplification by Sympy.

```python
str_rules_list = str_rules_expert_fw.split('\n')
str_rules_list = str_rules_list[0:4]
count = 0

for n in str_rules_list:
    str_rules_list[count] = n.strip('1: '+symbols_list[count]+'* = ')
    count +=1

sympy_input = []
for k in range(len(str_rules_list)):
    sympy_input.append(str_rules_list[k].replace('and', '&').replace(' or',' |').replace('not ', '~'))

print(sympy_input)
```

    ['Xor((( ~En &  ~Su & ES &  ~Pr) | ( ~En & Su & ES & Pr) | (En & Su & ES &  ~Pr) | ( ~En & Su & ES &  ~Pr) | ( ~En &  ~Su & ES & Pr)), En)', 'Xor((( ~En & Su & ES & Pr) | (En & Su & ES &  ~Pr) | ( ~En & Su & ES &  ~Pr) | (En & Su &  ~ES & Pr) | (En & Su & ES & Pr)), Su)', 'Xor(((En & Su &  ~ES &  ~Pr) | (En & Su & ES &  ~Pr) | (En & Su & ES & Pr) | (En &  ~Su & ES & Pr)), ES)', 'Xor((( ~En &  ~Su & ES &  ~Pr) | (En & Su &  ~ES &  ~Pr) | (En & Su & ES &  ~Pr) | ( ~En & Su & ES &  ~Pr) | (En &  ~Su & ES &  ~Pr)), Pr)']

```python
print('Rules after simplifcation with Sympy')
#exp_EN = sympy.simplify(sympy.sympify(sympy_input[0][1]))

print('EN* = ' + str(sympy.to_dnf(sympy_input[0], simplify=True)))
print('S* = ' + str(sympy.to_dnf(sympy_input[1], simplify=True)))
print('ES* = ' + str(sympy.to_dnf(sympy_input[2], simplify=True)))
print('P* = ' + str(sympy.to_dnf(sympy_input[3], simplify=True)))

```

    Rules after simplifcation with Sympy
    EN* = (ES & Pr) | (ES & ~En) | (ES & ~Su) | (En & ~ES)
    S* = (Su & ~ES & ~En) | (Su & ~ES & ~Pr)
    ES* = (ES & ~En) | (ES & ~Pr & ~Su) | (En & Su & ~ES & ~Pr)
    P* = ES | Pr | (En & Su)

We can now see the transitions based on Hamming distance for these expert guided forward path rules.

| species 	| list for d=1  	| list for d=2         	        | list for d=3         	    | list for d=4 	|
|:---------:|:--------------: 	|:-----------------------------:|:-------------------------:|:-------------:|
| En      	|              	    |  [0,0,1,1]            	    | [0,0,1,0], [0,1,1,1] 	    | [0,1,1,0]    	|
| Su      	| [1,1,0,1]    	    |  [1,1,1,1]                    | [1,1,1,0], [0,1,1,1] 	    | [0,1,1,0]    	|
| ES      	| [1,0,1,1]    	    |  [1,1,1,1], [1,1,0,0]            	    | [1,1,1,0]            	    |              	|
| Pr      	|              	    |  [1,0,1,0], [1,1,0,0] 	    | [1,1,1,0], [0,0,1,0] 	    | [0,1,1,0]    	|

### Expert Backwards Rules

Finally, to demonstrate the creation of the ES-E expert guided network from the paper, we remove transitions known to be false from the rulelist by including them in the blacklist variable.

```python
blacklist = [((1, 0, 1, 1), 0), ((1, 0, 1, 0), 0), ((1, 1, 1, 1), 0),
             ((1, 0, 1, 1), 1), ((1, 0, 1, 0), 1), ((0, 0, 1, 0), 1), ((0, 0, 1, 1), 1),
             ((1, 1, 0, 1), 2),
             ((1, 0, 1, 1), 3), ((1, 1, 0, 1), 3), ((0, 1, 1, 1), 3), ((1, 1, 1, 1), 3), ((0, 0, 1, 1), 3)]

str_rules_expert_bw, simple_rulelist_expert_bw, fs_cpp_name_expert_bw = creating_rules(fn,symbols_list,backwardpaths=1,blacklist=blacklist)
```

    generating rules took  0.012009859085083008 seconds

Formatting the string rules for simplification by Sympy.

```python
str_rules_list = str_rules_expert_bw.split('\n')
str_rules_list = str_rules_list[0:4]
count = 0

for n in str_rules_list:
    str_rules_list[count] = n.strip('1: '+symbols_list[count]+'* = ')
    count +=1

sympy_input = []
for k in range(len(str_rules_list)):
    sympy_input.append(str_rules_list[k].replace('and', '&').replace(' or',' |').replace('not ', '~'))

print(sympy_input)
```

    ['Xor((( ~En &  ~Su & ES &  ~Pr) | ( ~En & Su & ES & Pr) | (En & Su & ES &  ~Pr) | ( ~En & Su & ES &  ~Pr) | ( ~En &  ~Su & ES & Pr)), En)', 'Xor((( ~En & Su & ES & Pr) | (En & Su & ES &  ~Pr) | ( ~En & Su & ES &  ~Pr) | (En & Su &  ~ES & Pr) | (En & Su & ES & Pr)), Su)', 'Xor(((En & Su &  ~ES &  ~Pr) | (En & Su & ES &  ~Pr) | (En & Su & ES & Pr) | (En &  ~Su & ES & Pr)), ES)', 'Xor((( ~En &  ~Su & ES &  ~Pr) | (En & Su &  ~ES &  ~Pr) | (En & Su & ES &  ~Pr) | ( ~En & Su & ES &  ~Pr) | (En &  ~Su & ES &  ~Pr)), Pr)']

```python
print('Rules after simplifcation with Sympy')
#exp_EN = sympy.simplify(sympy.sympify(sympy_input[0][1]))

print('EN* = ' + str(sympy.to_dnf(sympy_input[0], simplify=True)))
print('S* = ' + str(sympy.to_dnf(sympy_input[1], simplify=True)))
print('ES* = ' + str(sympy.to_dnf(sympy_input[2], simplify=True)))
print('P* = ' + str(sympy.to_dnf(sympy_input[3], simplify=True)))

```

    Rules after simplifcation with Sympy
    EN* = (ES & Pr) | (ES & ~En) | (ES & ~Su) | (En & ~ES)
    S* = (Su & ~ES & ~En) | (Su & ~ES & ~Pr)
    ES* = (ES & ~En) | (ES & ~Pr & ~Su) | (En & Su & ~ES & ~Pr)
    P* = ES | Pr | (En & Su)

You may notice that this rule list is the same as that generated from the Expert Forwards rulelist.

| species 	| list for d=1  	| list for d=2         	        | list for d=3         	    | list for d=4 	|
|:---------:|:--------------: 	|:-----------------------------:|:-------------------------:|:-------------:|
| En      	|              	    |  [0,0,1,1]            	    | [0,1,1,1], [1,1,1,0], [0,0,1,0] 	    | [0,1,1,0]    	|
| Su      	| [1,1,0,1]    	    |  [1,1,1,1]                    | [1,1,1,0], [0,1,1,1] 	    | [0,1,1,0]    	|
| ES      	| [1,0,1,1]    	    |  [1,1,1,1], [1,1,0,0]            	    | [1,1,1,0]            	    |              	|
| Pr      	|              	    |  [1,0,1,0], [1,1,0,0] 	    | [1,1,1,0], [0,0,1,0] 	    | [0,1,1,0]    	|

Although the correct kinetics can be modeled by both adding transitions to the foward paths or removing transitions from the backward paths, it is recommended to use the backward paths approach as the forward paths approach can result in losing attractor states or creating new false attractors. With the backward paths approach, we only need to ensure that transitions necessary for states to reach the attractor are not removed.


# LSTM结构和隐藏状态

因为包含循环, 所以循环网络 (RNN) 可以在处理新输入的同时存储信息。长短记忆网络（LSTM）是一种特殊的循环网络，对 LSTM 来说，序列中的每个数据（例如，给定句子中的单词）都有一个相应的 *隐藏状态* $h_t$。该隐藏状态原则上可以包含序列当前结点之前的任一节点的信息；包括一些权重，短期和长期记忆成分的表示。

因此，**LSTM的隐藏状态将根据输入语句中的每个新单词的变化而变化，可以使用隐藏状态来预测语句中下一个最可能的单词**，或者帮助确定语言模型中单词的类型，还有很多其他应用！

### 练习库

请注意，大多数练习的 notebook 都可以根据 [Github练习库](https://github.com/udacity/CVND_Exercises)中的说明在本地计算机上运行。

## Pytorch中的LSTM

要创建和训练 LSTM，必须要知道怎样构造 LSTM 的输入和隐藏状态。在 Pytorch中，可以把 LSTM 定义为：`lstm = nn.LSTM(input_size=input_dim, hidden_size=hidden_dim, num_layers=n_layers)`。

PyTorch中的LSTM期望的输入都是3D张量，其维度定义如下
>* `input_dim` = 输入中期望的序列的数量 (值为20表示有20个输入序列)
>* `hidden_dim` = 隐藏状态的特征数；即各LSTM单元在每个时间步上的输出的数量。
>* `n_layers ` = 循环层数；通常为1到3之间的值； 值为1表示每个LSTM单元有一个隐藏状态。 默认值为1。

###  隐藏状态

定义了 LSTM 的输入和隐藏状态的特征数，我们就可以在每个时间步中用它来获取输出和隐藏状态。 `out, hidden = lstm(input.view(1, 1, -1), (h0, c0))`

LSTM的输入是 **`(input, (h0, c0))`**.
>* `input` = 包含输入序列特征的张量；张量形状是（seq_len，batch，input_size）
>* `h0` = 包含一个批次中每个元素的初始隐藏状态的张量
>* `c0` = 包含一个批次中每个元素的初始记忆单元的张量

`h0` 和 `c0` 的默认值是 0。其张量形状是： (n_layers, batch, hidden_dim).

在本 notebook 示例中，这些概念将变得更加清晰。本 notebook 和 后续的 notebook 都是[此PyTorch LSTM教程](https://pytorch.org/tutorials/beginner/nlp/sequence_models_tutorial.html#lstm-s-in-pytorch)的修改版。

让我们举一个简单例子，例如我们想用 LSTM 处理一个句子 "Giraffes in a field"，将其输入 序列模型。我们的输入应是一个元素是单词的，"1x4"的行向量：

\begin{align}\begin{bmatrix}
   \text{Giraffes  }
   \text{in  }
   \text{a  }
   \text{field}
   \end{bmatrix}\end{align}

在本例中，输入语句有4个单词，我们要设定每个时间步要生成多少个输出，例如设定每个LSTM单元生成 **3个隐藏状态值**。将LSTM中的循环层数保持为默认值1。

隐藏状态和记忆单元的张量形状是（n_layers，batch，hidden_dim），在本例中，该张量形状是（1，1，3），即其输入是一批/单词序列（这一句话），循环层数为一层，生成3个隐藏状态值。

### 示例代码

接下来，让我们看一个LSTM的示例，该示例旨在查看输入是有4个值的序列（用数值类型，因为容易创建和跟踪），输出是3个值。这就是上面语句处理网络，建议你修改输入/隐藏状态的大小，以查看对LSTM结构有何影响！

```python
import torch
import torch.nn as nn
import matplotlib.pyplot as plt

%matplotlib inline

torch.manual_seed(2) # so that random variables will be consistent and repeatable for testing
```

### 定义一个简单的LSTM

**关于隐藏状态和输出的数量的说明**

除非你定义自己的LSTM，并通过在网络末端添加线性层来改变输出数量 例如，fc = nn.Linear(hidden_dim, output_dim)，否则隐藏状态数`hidden_dim`和输出的数量是相同的。

```python
from torch.autograd import Variable

# define an LSTM with an input dim of 4 and hidden dim of 3
# this expects to see 4 values as input and generates 3 values as output
input_dim = 4
hidden_dim = 3
lstm = nn.LSTM(input_size=input_dim, hidden_size=hidden_dim)

# make 5 input sequences of 4 random values each
inputs_list = [torch.randn(1, input_dim) for _ in range(5)]
print('inputs: \n', inputs_list)
print('\n')

# initialize the hidden state
# (1 layer, 1 batch_size, 3 outputs)
# first tensor is the hidden state, h0
# second tensor initializes the cell memory, c0
h0 = torch.randn(1, 1, hidden_dim)
c0 = torch.randn(1, 1, hidden_dim)

h0 = Variable(h0)
c0 = Variable(c0)
# step through the sequence one element at a time.
for i in inputs_list:
    # wrap in Variable
    i = Variable(i)

    # after each step, hidden contains the hidden state
    out, hidden = lstm(i.view(1, 1, -1), (h0, c0))
    print('out: \n', out)
    print('hidden: \n', hidden)

```

你可以看到输出张量和隐藏张量的长度始终为3，因为在定义LSTM时，我们已经用`hidden_dim`指定了。

### 一次全部

for循环对于处理大的数据序列不是很有效，因此我们也可以 **一次处理所有这些输入。**

1. 将所有输入序列连接到一个大小为 batch_size 的大张量中
2. 定义隐藏状态的张量形状
3. 获取输出和*最近*的隐藏状态（即根据序列中的最新的单词创建）

由于初始隐藏状态不同，输出看起来可能略有不同。

```python
# turn inputs into a tensor with 5 rows of data
# add the extra 2nd dimension (1) for batch_size
inputs = torch.cat(inputs_list).view(len(inputs_list), 1, -1)

# print out our inputs and their shape
# you should see (number of sequences, batch size, input_dim)
print('inputs size: \n', inputs.size())
print('\n')

print('inputs: \n', inputs)
print('\n')

# initialize the hidden state
h0 = torch.randn(1, 1, hidden_dim)
c0 = torch.randn(1, 1, hidden_dim)

# wrap everything in Variable
inputs = Variable(inputs)
h0 = Variable(h0)
c0 = Variable(c0)
# get the outputs and hidden state
out, hidden = lstm(inputs, (h0, c0))

print('out: \n', out)
print('hidden: \n', hidden)
```

### 接下来：隐藏状态和门

该 notebook 向你展示了PyTorch中LSTM的输入和输出的结构。在接下来的notebook 练习中，你将了解有关LSTM如何利用隐藏状态表示长期和短期记忆的更多信息。

#### 词性
在本课后面的 notebook 中，你将看到如何定义一个模型来标记词性（名词，动词，限定词），包括定义一个LSTM和一个线性层，以定义所需的输出大小，
最后训练模型，以创建将一个将每个输入单词与词性相关联的分类得分分布。

```python

```


# 微積分

## 積分

### 不定積分
微分すると $f(x)$ になるような関数を $f(x)$ の **不定積分**（または**原始関数**）と呼び、以下のような記号で表す

$$
\int{f(x)dx}
$$

$f(x)$ の不定積分の1つを $F(x)$ とすると、定数の微分が 0 であることから、任意定数 $C$ について $F(x)+C$ も $f(x)$ の不定積分になる

そのため、一般に次のように表される

$$
\int{f(x)dx} = F(x) + C
$$

このような $C$ は **積分定数** と呼ばれる

微分の性質から、不定積分について以下が成り立つ

$$
\begin{align}
\int(f(x) + g(x))dx &= \int f(x)dx + \int g(x)dx \\
\int(kf(x))dx &= k\int f(x)dx
\end{align}
$$

ここで、不定積分は積分定数分の不確実性があるため、上式 $\int(f(x) + g(x))dx &= \int f(x)dx + \int g(x)dx$ は「$f(x)$ の不定積分の1つを $F(x)$、$g(x)$ の不定積分の1つを $G(x)$ とすると、$(f(x) + g(x))$ の不定積分は $F(x)+G(x)+(定数)$ で表される」という意味になるため、要注意である

これは $\int(kf(x))dx &= k\int f(x)dx$ についても同様である

具体的な例として、多項式関数の不定積分を考えてみると、例えば次の式が成り立つ

$$
\int(x^3 + 2x)dx = \frac{1}{4}x^4 + x^2 + C \quad ただしCは積分定数
$$

実際に $\frac{1}{4}x^4 + x^2$ を微分すると $x^3 + 2x$ になる

また、微分の公式より、次の公式が成り立つ

$$
\begin{align}
\int x^pdx &= \frac{1}{p+1}x^{p+1} + C \\
\int\frac{1}{x}dx &= \ln|x| + C \\
\int e^xdx &= e^x + C
\end{align}
$$

#### 演習1
次の不定積分を求めよ

1. $$
\int(x^3 + 2x^2 + 1)dx
$$
2. $$
\int(x^2 + \frac{1}{x})dx
$$
3. $$
\int(\frac{1}{\sqrt x} + e^x)dx
$$

#### 演習2
次の式が成り立つことを確かめよ

1. $$
\int(x^2 + 3x + 1)e^xdx = (x^2 + x)e^x + C
$$
2. $$
\int\frac{1}{1-x^2}dx = \frac{1}{2}(\ln|1+x| - \ln|1-x|) + C
$$

#### 解答1
1. $$
\begin{align}
\int(x^3 + 2x^2 + 1)dx &= \int x^3dx + 2\int x^2dx + \int1dx \\
&= \frac{1}{4}x^4 + \frac{2}{3}x^3 + x + C
\end{align}
$$
2. $$
\begin{align}
\int(x^2 + \frac{1}{x})dx &= \int x^2dx + \int\frac{1}{x}dx \\
&= \frac{1}{3}x^3 + \ln|x| + C
\end{align}
$$
3. $$
\begin{align}
\int(\frac{1}{\sqrt x} + e^x)dx &= \int x^{-\frac{1}{2}}dx + \int e^xdx \\
&= 2x^\frac{1}{2} + e^x + C \\
&= 2\sqrt{x} + e^x + C
\end{align}
$$

#### 解答2
それぞれの右辺を微分して確かめる

1. $$
\begin{align}
\frac{d}{dx}\{(x^2 + x)e^x + C\} &= (x^2 + x)'\cdot e^x + (x^2 + x)\cdot(e^x)' \\
&= (2x + 1)e^x + (x^2 + x)e^x \\
&= (x^2 + 3x + 1)e^x
\end{align}
$$
2. $$
\begin{align}
\frac{d}{dx}\left\{\frac{1}{2}(\ln|1+x| - \ln|1-x|) + C\right\} &= \frac{1}{2}\{(\ln|1+x|)' - (\ln|1-x|)'\} \\
&= \frac{1}{2}\left(\frac{1}{1+x} - \left(-\frac{1}{1-x}\right)\right) \\
&= \frac{1}{1-x^2}
\end{align}
$$

```julia
using SymPy

@vars x

f(x) = x^3 + 2x^2 + 1

# 不定積分
integrate(f(x), x)
```

$\begin{equation*}\frac{x^{4}}{4} + \frac{2 x^{3}}{3} + x\end{equation*}$

```julia
f(x) = x^2 + 1/x
integrate(f(x), x)
```

$\begin{equation*}\frac{x^{3}}{3} + \log{\left(x \right)}\end{equation*}$

```julia
f(x) = 1/sqrt(x) + exp(x)
integrate(f(x), x)
```

$\begin{equation*}2 \sqrt{x} + e^{x}\end{equation*}$

### 定積分
関数 $f(x)$ の不定積分の1つを $F(x)$ とすると、与えられた定数 $a$, $b$ の区間における $f(x)$ の **定積分** は次の式で定義される

$$
\begin{align}
\int_a^bf(x)dx &= F(b) - F(a) \\
&= \left[F(x)\right]_a^b
\end{align}
$$

特に区間 $[a, b]$ において $f(x)\geq0$ ならば、定積分 $\int_a^bf(x)dx = F(b) - F(a)$ は、$a\leq x\leq b$, $0\leq y\leq f(x)$ 部分における面積になることが知られている

不定積分の性質から、次のことが分かる

$$
\begin{align}
\int_a^b(f(x) + g(x))dx &= \int_a^bf(x)dx + \int_a^bg(x)dx \\
\int_a^bkf(x)dx &= k\int_a^bf(x)dx
\end{align}
$$

#### 演習
次の計算を行え

1. $$
\int_0^2(x^3 + x) dx
$$
2. $$
\int_1^4(x + \sqrt x)dx
$$

#### 解答
1. $$
\begin{align}
\int_0^2(x^3 + x)dx &= \left[\frac{1}{4}x^4 + \frac{1}{2}x^2\right]_0^2 \\
&= \frac{1}{4}\times2^4 + \frac{1}{2}\times2^2 \\
&= 6
\end{align}
$$
2. $$
\begin{align}
\int_1^4(x + \sqrt x)dx &= \left[\frac{1}{2}x^2 + \frac{2}{3}x^\frac{3}{2}\right]_1^4 \\
&= (\frac{1}{2}\times4^2 + \frac{2}{3}\times4^\frac{3}{2}) - (\frac{1}{2}\times1^2 + \frac{2}{3}\times1^\frac{3}{2}) \\
&= \frac{73}{6}
\end{align}
$$

```julia
f(x) = x^3 + x
integrate(f(x), (x, 0, 2))
```

$\begin{equation*}6\end{equation*}$

```julia
f(x) = x + sqrt(x)
integrate(f(x), (x, 1, 4))
```

$\begin{equation*}\frac{73}{6}\end{equation*}$

## 偏微分と勾配

2変数関数 $z=f(x,y)$ を考える

これは $(x,y)$ の値の組に対して、$z$ の値を1つ対応付ける写像である

$z=f(x,y)$ を満たすような $(x,y,z)$ の集合は、3次元空間上の曲面と考えることが出来る

次のような具体例を考える

$$
f(x,y) = x^2 + xy + y^2 + x
$$

このとき $f(x,y)$ を $x$ の関数とみなして（$y$ を定数とみなして）微分すると、

$$
2x + y + 1
$$

となる

これを $f(x,y)$ の $x$ についての **偏導関数** と呼び、$\frac\partial{\partial x}f(x,y)$ や $f_x(x,y)$ などと記述する

偏導関数を求めることを **偏微分** する、という

偏導関数は、注目している変数を固定したときの導関数を意味している

上記の例でいうと、例えば $y=0$ に固定した関数 $f(x,0)=x^2+x$ を考えてみると、これを $x$ で微分した導関数 $2x+1$ は、偏導関数で $y=0$ としたもの $\frac\partial{\partial x}f(x,0) = 2x + 1$ に一致する

全ての $y$ についてこのような関係が成り立つため、$y=y_0$ における導関数は偏導関数 $\frac\partial{\partial x}f(x,y)$ で $y=y_0$ とおいたものであり、

$$
\frac{\partial y}{\partial x}(x,y_0) = 2x + y_0 + 1
$$

となる

$xyz$ 空間では $z=f(x,y)$ は曲面を表し、これを平面 $y=y_0$ で切り取った曲線が $z=f(x,y_0)$ となる

その切り口の曲線の導関数が、偏導関数で $y=y_0$ とおいたものであり $z=\frac\partial{\partial x}f(x,y_0)$ となる

2変数関数 $z=f(x,y)$ についても極値を考えることが出来る

もし2変数関数 $z=f(x,y)$ が $(x,y)=(x_0,y_0)$ という点で極値をとるとすると、$z=f(x,y)$ の平面 $x=x_0$ による切り口が $y=y_0$ で極値をとり、$y=y_0$ による切り口が $x=x_0$ で極値をとらなければならない

つまり必要条件は次のようになる

$$
\frac{\partial f}{\partial x}(x_0, y_0) = 0,\ \frac{\partial f}{\partial y}(x_0, y_0) = 0
$$

ここで、

$$
\nabla f = \begin{pmatrix}\frac{\partial f}{\partial x} \\ \frac{\partial f}{\partial y}\end{pmatrix}
$$

とおくと、この $\nabla f$ (ナブラf) は $\mathbb R^2$ から $\mathbb R^2$ への写像だと考えることが出来る

つまりベクトル $\begin{pmatrix}x \\ y\end{pmatrix} \in \mathbb R^2$ を1つ考えると、それに対応してベクトル $\nabla f(x,y)$ が1つ決まる

よって $f(x,y)$ の極値を求めるためには、

$$
\nabla f(x,y) = 0
$$

となる $x$, $y$ を求めれば良いことになる

具体例をもとに計算すると、

$$
\nabla f(x,y) = \begin{pmatrix}2x+y+1 \\ x+2y\end{pmatrix} = 0
$$

を解けばよいため、

$$
x=-\frac{2}{3},\ y=\frac{1}{3}
$$

となる

```julia
using Plots

@vars x  y

f(x, y) = x^2 + x*y + y^2 + x

# 2変数関数の3Dグラフをプロット
## ref: https://github.com/JuliaPy/SymPy.jl/blob/master/src/plot_recipes.jl
surface(-10:10, -10:10, f(x, y))
```

```julia
# (x,y)->z の増減表をプロット
plot(VectorField(f))
```

以上の話を一般の$n$次変数関数について拡張する

$n$次変数関数は次式で表される

$$
y=f(x_1, x_2, \cdots, x_n)
$$

または、変数をカンマ区切りの集合で表さず、ベクトル $\vec{x}=\begin{pmatrix}x_1 \\ x_2 \\ \vdots \\ x_n\end{pmatrix}$ で表し、

$$
y=f(\vec{x})
$$

と書くこともある

ここで「$f$ が偏微分可能である」とは、全ての $x_i$ について $f$ が $x_i$ で偏微分可能であることを言う

特に全ての $x_i$ について無限回偏微分可能である場合、$f$ は **滑らか** であると言う

### 滑らかな$n$次変数関数の性質
滑らかな$n$次変数関数 $f$ の **勾配** $\nabla f$ は次で定義される

$$
\nabla f = \begin{pmatrix}\frac{\partial f}{\partial x_1} \\ \frac{\partial f}{\partial x_2} \\ vdots \\ \frac{\partial f}{\partial x_n} \end{pmatrix}
$$

これは$n$次元ベクトル $\vec{x}\in\mathbb{R}^n$ が1つ決まると、それに$n$次元ベクトルを1つ対応付けることが出来るため、$\mathbb{R}^2$ から $\mathbb{R}^2$ への写像になっている

関数 $f$ が $\vec{x}$ で極値をとるためには、次の条件が必要である

$$
\nabla f(x) = \boldsymbol0
$$

これは必要条件に過ぎず、この条件を満たしても極値をとらないことがあるため要注意である

極値をとるための必要十分条件について考えるには、2階偏微分まで考える必要がある

まず、その前に関数を複数回偏微分することを考える

$f(\vec{x})$ を $x_i$ で偏微分した後に $x_j$ で偏微分したものを $\frac{\partial^2f}{\partial x_j\partial x_i}(\vec x)$ または $f_{x_ix_j}(\vec x)$ と書くことにする

$f(\vec x)$ が滑らかなときは $f_{x_ix_j}(\vec x) = f_{x_jx_i}(\vec x)$ であることが知られているため、偏微分する順序を気にする必要はない

このとき $f$ の **ヘッセ行列** は次で定義される

$$
\nabla^2f = \begin{pmatrix}
\frac{\partial^2f}{\partial x_1 \partial x_1} & \frac{\partial^2f}{\partial x_1 \partial x_2} & \ldots & \frac{\partial^2f}{\partial x_1 \partial x_n} \\
\frac{\partial^2f}{\partial x_2 \partial x_1} & \frac{\partial^2f}{\partial x_2 \partial x_2} & \ldots & \frac{\partial^2f}{\partial x_2 \partial x_n} \\
\vdots & \vdots & \ddots & \vdots \\
\frac{\partial^2f}{\partial x_n \partial x_1} & \frac{\partial^2f}{\partial x_n \partial x_2} & \ldots & \frac{\partial^2f}{\partial x_n \partial x_n}
\end{pmatrix}
$$

関数 $f$ のヘッセ行列は $\nabla^2f$ や $\boldsymbol H_f$ と記述する

$\frac{\partial^2}{\partial x_i\partial x_j}(\vec x) = \frac{\partial^2}{\partial x_j\partial x_i}(\vec x)$ であるため、ヘッセ行列は対称行列である

多変数関数 $f$ が $\nabla f(\vec x) = \boldsymbol$ となる点で極値をとるかどうかはヘッセ行列を調べれば分かる

- 極大となる条件: ヘッセ行列が負定値
- 極小となる条件: ヘッセ行列が正定値

計算例として二次形式の勾配について考えてみる

ここで **二次形式** とは$n$次対称対称行列 $\boldsymbol A$ に対して、

$$
f(\vec x) = \vec x^T \boldsymbol A \vec x
$$

で定義されるものである

この勾配を考えてみる

$\boldsymbol A = (a_{ij})$ とすると、

$$
f(\vec x) = \sum_{i=1}^n\sum_{j=1}^n a_{ij}x_ix_j
$$

となる

1つの添字 $k$ に注目して $x_k$ がこの右辺でいつ現れるかを考えると、$i=k$ のときと $j=k$ のときがあり、特に $i=j=k$ のときは $a_{kk}x_k^2$ という項がある

$f$ を $x_k$ で偏微分すると、$x_k$ が現れる項だけを考えればよく、他は 0 になる

$x_k$ が現れる項を、$i=j=k$ の場合、$i=k かつ j\neq k$ の場合、$i\neq k かつ j=k$ の場合、の3通りに分けて計算すると以下のようになる

$$
\begin{align}
\frac{\partial f}{\partial x_k}(\vec x) &= \frac{\partial}{\partial x_k}\left[a_{kk}x_k^2 + \sum_{j\neq k}a_{kj}x_kx_j + \sum_{i\neq k}a_{ik}x_ix_k\right] \\
&= 2a_{kk}x_k + \sum_{j\neq k}a_{kj}x_j + \sum_{i\neq k}a_{ik}x_i \\
&= 2a_{kk}x_k + 2\sum_{j\neq k}a_{kj}x_j \\
&= 2\sum_{j=1}^n a_{kj}x_j
\end{align}
$$

ただし、ここで $\sum_{i\neq k}$ という記号は「$i$を 1 から $n$ まで増やすが、$i=k$ の場合のみスキップして和をとる」という意味である

また、$\boldsymbol A$ が対称行列であることから $a_{ik} = a_{kj}$ である性質を利用して計算している

以上より、勾配は次の式で表される

$$
\nabla f(\vec x) = \begin{pmatrix}2\sum_{j=1}^na_{1j}x_j \\ 2\sum_{j=1}^na_{2j}x_j \\ vdots \\ 2\sum_{j=1}^na_{nj}x_j\end{pmatrix} = 2\boldsymbol A \vec x
$$

この二次形式の勾配は、二次関数 $y=ax^2$ の導関数が $y'=2ax$ であることと形式が似ているため、比較的覚えやすい

次にこの $f$ のヘッセ行列を求める

ヘッセ行列の $(k,l)$ 成分を $h_{kl}$ とすると、$h_{kl}$ は $\nabla f$ の第$k$成分を $x_l$ で偏微分したものになるため、

$$
h_{kl} = \frac{\partial}{\partial x_l}\left(2\sum_{j=1}^n a_{kj}x_j\right) = 2a_{kl}
$$

となる

従ってヘッセ行列は、

$$
\nabla^2f(\vec x) = 2\boldsymbol A
$$

である

以上より、$f$ が極値をとる条件は以下の通りである

- $\vec x$ で極小となる条件: $\boldsymbol A\vec x = \boldsymbol0 かつ \boldsymbol A が正定値$
- $\vec x$ で極大となる条件: $\boldsymbol A\vec x = \boldsymbol0 かつ \boldsymbol A が負定値$

#### 演習
次の2変数関数の極値と、極値をとるときの $x$, $y$ を求めよ

$$
f(x,y) = x^3 + 2xy + y^2 - x
$$

#### 解答
まず $x$ と $y$ で偏微分する

$$
\begin{align}
\frac{\partial}{\partial x}f(x,y) &= 3x^2 + 2y - 1 \\
\frac{\partial}{\partial y}f(x,y) &= 2xy + 2y \\
\end{align}
$$

これらを 0 とおいた方程式、つまり、

$$
\left\{
3x^2 + 2y - 1 = 0 \\
2xy + 2y = 0
\right.
$$

を解くと、

$$
(x,y) = \left(-\frac{1}{3}, \frac{1}{3}\right),\ \left(1, -1\right)
$$

を得る

これらが極値をとる $(x,y)$ の候補であるが、実際に極値になるかを調べるために2階偏導関数を計算する

$$
\begin{align}
\frac{\partial^2}{\partial x \partial x}f(x,y) &= 6x \\
\frac{\partial^2}{\partial x \partial y}f(x,y) &= 2 \\
\frac{\partial^2}{\partial y \partial y}f(x,y) &= 2
\end{align}
$$

以上より、

$$
\nabla^2f(x,y) = \begin{pmatrix}6x & 2 \\ 2 & 2\end{pmatrix}
$$

となる

ここで、$(x,y) = \left(-\frac{1}{3}, \frac{1}{3}\right)$ の時は $\nabla^2f\left(-\frac{1}{3}, \frac{1}{3}\right) = \begin{pmatrix}-2 & 2 \\ 2 & 2\end{pmatrix}$ より、正定値にも負定値にもならないため、この点では極値をとらない

一方、$(x,y) = (1,-1)$ の時は $\nabla^2f(1,-1) = \begin{pmatrix}6 & 2 \\ 2 & 2\end{pmatrix}$ で正定値となるため、

$$
(x,y) = (1,-1) の時、極小値 -1 をとる
$$

```julia

```


# Min første Jypyter Notebook
## Tester litt ulike ting

```python
from matplotlib.pylab import *

a = float(input("Skriv inn koeffisienten foran x^2-leddet: "))
b = float(input("Skriv inn koeffisienten foran x-leddet: "))
c = float(input("Skriv inn konstantleddet: "))

funksjon = "f(x) = " + str(a) + "x^2"

if b < 0:
    funksjon = funksjon + " – " + str(abs(b)) + "x"
elif b > 0:
    funksjon = funksjon + " + " + str(b) + "x"

if c < 0:
    funksjon = funksjon + " – " + str(abs(c))
elif c > 0:
    funksjon = funksjon + " + " + str(c)

print(funksjon)

x = linspace(-5, 5, 1001)
print(str(x[1]))
y = a*x**2 + b*x + c

plot(x, y)
xlabel("x")
ylabel("y")
grid()
xlim(-5,3)
axhline(y=0, color="k")
axvline(x=0, color="k")
show()
```

### Hvordan blir resultatet?

Resultatet er funksjonen $f(x)=x^2+2x-2$
\begin{equation}
    f(x)=x^2 + 2x - 2
\end{equation}

```python

```


# Linear Shell solution

## Init symbols for *sympy*

```python
from sympy import *
from geom_util import *
from sympy.vector import CoordSys3D
import matplotlib.pyplot as plt
import sys
sys.path.append("../")

%matplotlib inline

%reload_ext autoreload
%autoreload 2
%aimport geom_util
```

```python
# Any tweaks that normally go in .matplotlibrc, etc., should explicitly go here
%config InlineBackend.figure_format='retina'
plt.rcParams['figure.figsize'] = (12, 12)

plt.rc('text', usetex=True)

plt.rc('font', family='serif')

init_printing()
```

```python
N = CoordSys3D('N')
alpha1, alpha2, alpha3 = symbols("alpha_1 alpha_2 alpha_3", real = True, positive=True)
A,K,rho = symbols("A K rho")
```

### Tymoshenko theory

$u_1 \left( \alpha_1, \alpha_2, \alpha_3 \right)=u\left( \alpha_1 \right)+\alpha_3\gamma \left( \alpha_1 \right) $

$u_2 \left( \alpha_1, \alpha_2, \alpha_3 \right)=0 $

$u_3 \left( \alpha_1, \alpha_2, \alpha_3 \right)=w\left( \alpha_1 \right) $

$ \left(
\begin{array}{c}
u_1 \\
\frac { \partial u_1 } { \partial \alpha_1} \\
\frac { \partial u_1 } { \partial \alpha_2} \\
\frac { \partial u_1 } { \partial \alpha_3} \\
u_2 \\
\frac { \partial u_2 } { \partial \alpha_1} \\
\frac { \partial u_2 } { \partial \alpha_2} \\
\frac { \partial u_2 } { \partial \alpha_3} \\
u_3 \\
\frac { \partial u_3 } { \partial \alpha_1} \\
\frac { \partial u_3 } { \partial \alpha_2} \\
\frac { \partial u_3 } { \partial \alpha_3} \\
\end{array}
\right) = T \cdot
\left(
\begin{array}{c}
u \\
\frac { \partial u } { \partial \alpha_1} \\
\gamma \\
\frac { \partial \gamma } { \partial \alpha_1} \\
w \\
\frac { \partial w } { \partial \alpha_1} \\
\end{array}
\right) $

```python
T=zeros(12,6)
T[0,0]=1
T[0,2]=alpha3
T[1,1]=1
T[1,3]=alpha3
T[3,2]=1

T[8,4]=1
T[9,5]=1
T
```

```python
B=Matrix([[0, 1/(A*(K*alpha3 + 1)), 0, 0, 0, 0, 0, 0, K/(K*alpha3 + 1), 0, 0, 0], [0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1/(A*(K*alpha3 + 1)), 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0], [-K/(K*alpha3 + 1), 0, 0, 0, 0, 0, 0, 0, 0, 1/(A*(K*alpha3 + 1)), 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1]])
B
```

```python
E=zeros(6,9)
E[0,0]=1
E[1,4]=1
E[2,8]=1
E[3,1]=1
E[3,3]=1
E[4,2]=1
E[4,6]=1
E[5,5]=1
E[5,7]=1
E
```

```python
simplify(E*B*T)
```

```python
mu = Symbol('mu')
la = Symbol('lambda')
C_tensor = getIsotropicStiffnessTensor(mu, la)
C = convertStiffnessTensorToMatrix(C_tensor)
C
```

```python
S=T.T*B.T*E.T*C*E*B*T*A*(1+alpha3*K)**2
S=simplify(S)
S
```

```python
h=Symbol('h')
S_in = integrate(S*(1-alpha3*K+(alpha3**2)*K),(alpha3, -h/2, h/2))
S_in
```

```python
E,nu=symbols('E nu')
lambda_elastic=E*nu/((1+nu)*(1-2*nu))
mu_elastic=E/(2*(1+nu))
S_ins=simplify(S_in.subs(A,1).subs(la,lambda_elastic).subs(mu,mu_elastic))
S_ins
```

```python

a11=E/(1-nu**2)
a44=5*E/(12*(1+nu))

AM=Matrix([[a11,0],[0,a44]])
strainT=Matrix([[1,alpha3,0],[0,0,1]])
AT=strainT.T*AM*strainT
integrate(AT,(alpha3, -h/2, h/2))
```

```python
M=Matrix([[rho, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, rho, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, rho, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]])
M=T.T*M*T*A*(1+alpha3*K)
M
```

```python
M_in = integrate(M,(alpha3, -h/2, h/2))
M_in
```

## Cartesian coordinates

```python
import fem.geometry as g
import fem.model as m
import fem.material as mat
import fem.shell.shellsolver as s
import fem.shell.mesh1D as me
import plot

stiffness_matrix_func = lambdify([A, K, mu, la, h], S_in, "numpy")
mass_matrix_func = lambdify([A, K, rho, h], M_in, "numpy")

def stiffness_matrix(material, geometry, x1, x2, x3):
    A,K = geometry.get_A_and_K(x1,x2,x3)
    return stiffness_matrix_func(A, K, material.mu(), material.lam(), thickness)

def mass_matrix(material, geometry, x1, x2, x3):
    A,K = geometry.get_A_and_K(x1,x2,x3)
    return mass_matrix_func(A, K, material.rho, thickness)

def generate_layers(thickness, layers_count, material):
    layer_top = thickness / 2
    layer_thickness = thickness / layers_count
    layers = set()
    for i in range(layers_count):
        layer = m.Layer(layer_top - layer_thickness, layer_top, material, i)
        layers.add(layer)
        layer_top -= layer_thickness
    return layers

def solve(geometry, thickness, linear, N_width, N_height):
    layers_count = 1
    layers = generate_layers(thickness, layers_count, mat.IsotropicMaterial.steel())
    model = m.Model(geometry, layers, m.Model.FIXED_BOTTOM_LEFT_RIGHT_POINTS)
    mesh = me.Mesh1D.generate(width, layers, N_width, m.Model.FIXED_BOTTOM_LEFT_RIGHT_POINTS)
    lam, vec = s.solve(model, mesh, stiffness_matrix, mass_matrix)

    return lam, vec, mesh, geometry

width = 2
curvature = 0.8
thickness = 0.05

corrugation_amplitude = 0.05
corrugation_frequency = 20

# geometry = g.CorrugatedCylindricalPlate(width, curvature, corrugation_amplitude, corrugation_frequency)
geometry = g.CylindricalPlate(width, curvature)
# geometry = g.Plate(width)

N_width = 100
N_height = 4

lam, vec, mesh, geometry = solve(geometry, thickness, False, N_width, N_height)
results = s.convert_to_results(lam, vec, mesh, geometry)

results_index = 0

plot.plot_init_and_deformed_geometry_in_cartesian(results[results_index], 0, width, -thickness / 2, thickness / 2, 0, geometry.to_cartesian_coordinates)
to_print = 20
if (len(results) < to_print):
    to_print = len(results)

for i in range(to_print):
    print(results[i].rad_per_sec_to_Hz(results[i].freq))

```