```python
import sys
sys.path.append('..')
import os
import torch
import numpy as np
import matplotlib.pyplot as plt
from sympy import simplify_logic
import time
from sklearn.metrics import accuracy_score
import pandas as pd
from sklearn.tree import DecisionTreeClassifier, plot_tree
from sklearn.tree import _tree, export_text

from lens.utils.base import validate_network, set_seed, tree_to_formula
from lens.utils.relu_nn import get_reduced_model, prune_features
from lens.utils.psi_nn import prune_equal_fanin
from lens import logic

results_dir = 'results/xor'
if not os.path.isdir(results_dir):
    os.makedirs(results_dir)

device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
concepts = ['c1', 'c2', 'c3', 'c4']
n_rep = 10
tot_epochs = 4001
prune_epochs = 2000
```

```python
# XOR problem
x_train = torch.tensor([
    [0, 0, 0, 1],
    [0, 1, 0, 1],
    [1, 0, 0, 1],
    [1, 1, 0, 1],
], dtype=torch.float)
y_train = torch.tensor([0, 1, 1, 0], dtype=torch.long)

x_test = torch.tensor([
    [0, 0.95, 0, 1],
    [0, 0.9, 0.2, 0.7],
    [0.05, 1, 0.5, 1],
    [0.1, 0.8, 0.1, 0.8],
    [0.45, 1, 0.95, 0.9],
    [0, 0.35, 0.95, 0.9],
    [0.95, 0.9, 0.75, 0.2],
    [0.75, 0.2, 0.75, 0.15],
    [0.75, 0.15, 0.75, 0.2],
], dtype=torch.float)
y_test = torch.tensor([1, 1, 1, 1, 1, 0, 0, 1, 1], dtype=torch.long)
```

```python
def train_nn(x_train, y_train, need_pruning, seed, device, relu=False, verbose=False):
    set_seed(seed)
    x_train = x_train.to(device)
    y_train = y_train.to(device)
    layers = [
        torch.nn.Linear(x_train.size(1), 10),
        torch.nn.ReLU() if relu else torch.nn.LeakyReLU(),
        torch.nn.Linear(10, 4),
        torch.nn.ReLU() if relu else torch.nn.LeakyReLU(),
        torch.nn.Linear(4, 2),
        torch.nn.Softmax(dim=1),
    ]
    model = torch.nn.Sequential(*layers).to(device)

    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
    loss_form = torch.nn.CrossEntropyLoss()
    model.train()
    for epoch in range(tot_epochs):
        # forward pass
        optimizer.zero_grad()
        y_pred = model(x_train)
        # Compute Loss
        loss = loss_form(y_pred, y_train)

        for module in model.children():
            if isinstance(module, torch.nn.Linear):
                loss += 0.004 * torch.norm(module.weight, 1)
                loss += 0.004 * torch.norm(module.bias, 1)
                break

        # backward pass
        loss.backward()
        optimizer.step()

        if epoch > prune_epochs and need_pruning:
            prune_features(model, n_classes=1, device=device)
            need_pruning = False

        # compute accuracy
        if epoch % 500 == 0 and verbose:
            y_pred_d = torch.argmax(y_pred, dim=1)
            accuracy = y_pred_d.eq(y_train).sum().item() / y_train.size(0)
            print(f'\t Epoch {epoch}: train accuracy: {accuracy:.4f}')

    return model
```

```python
def train_psi_nn(x_train, y_train, need_pruning, seed, device, verbose=False):
    set_seed(seed)
    x_train = x_train.to(device)
    y_train = y_train.to(device).to(torch.float)
    layers = [
        torch.nn.Linear(x_train.size(1), 10),
        torch.nn.Sigmoid(),
        torch.nn.Linear(10, 4),
        torch.nn.Sigmoid(),
        torch.nn.Linear(4, 1),
        torch.nn.Sigmoid(),
    ]
    model = torch.nn.Sequential(*layers).to(device)

    optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
    loss_form = torch.nn.BCELoss()
    model.train()
    for epoch in range(tot_epochs):
        # forward pass
        optimizer.zero_grad()
        y_pred = model(x_train).squeeze()
        # Compute Loss
        loss = loss_form(y_pred, y_train)

        for module in model.children():
            if isinstance(module, torch.nn.Linear):
                loss += 0.00001 * torch.norm(module.weight, 1)

        # backward pass
        loss.backward()
        optimizer.step()

        if epoch > prune_epochs and need_pruning:
            model = prune_equal_fanin(model, 2, validate=True, device=device)
            need_pruning = False

        # compute accuracy
        if epoch % 500 == 0 and verbose:
            y_pred_d = y_pred > 0.5
            accuracy = y_pred_d.eq(y_train).sum().item() / y_train.size(0)
            print(f'\t Epoch {epoch}: train accuracy: {accuracy:.4f}')

    return model
```

```python
def c_to_y(method, need_pruning, relu, verbose=False):
    methods = []
    splits = []
    explanations = []
    explanations_inv = []
    model_accuracies = []
    explanation_accuracies = []
    explanation_accuracies_inv = []
    elapsed_times = []
    elapsed_times_inv = []
    for seed in range(n_rep):
        explanation, explanation_inv = '', ''
        explanation_accuracy, explanation_accuracy_inv = 0, 0

        print(f'Seed [{seed+1}/{n_rep}]')

        if method == 'tree':
            classifier = DecisionTreeClassifier(random_state=seed)
            classifier.fit(x_train.detach().numpy(), y_train.detach().numpy())
            y_preds = classifier.predict(x_test.detach().numpy())
            model_accuracy = accuracy_score(y_test.detach().numpy(), y_preds)

            target_class = 1
            start = time.time()
            explanation = tree_to_formula(classifier, concepts, target_class)
            elapsed_time = time.time() - start

            target_class_inv = 0
            start = time.time()
            explanation_inv = tree_to_formula(classifier, concepts, target_class_inv)
            elapsed_time = time.time() - start

        else:
            if method == 'psi':
                # positive class
                target_class = 1
                model = train_psi_nn(x_train, y_train.eq(target_class), need_pruning, seed, device, verbose)
                y_preds = model(x_test.to(device)).cpu().detach().numpy()
                model_accuracy = accuracy_score(y_test.cpu().detach().numpy(), y_preds>0.5)

            else:
                model = train_nn(x_train, y_train, need_pruning, seed, device, relu, verbose)
                y_preds = model(x_test.to(device)).cpu().detach().numpy()
                model_accuracy = accuracy_score(y_test.cpu().detach().numpy(), y_preds.argmax(axis=1))

            # positive class
            target_class = 1
            start = time.time()
            if method == 'psi':
                global_explanation = logic.generate_fol_explanations(model, device)[0]
            else:
                global_explanation, _, _ = logic.relu_nn.combine_local_explanations(model,
                                                                                x_train.to(device), y_train.to(device),
                                                                                target_class=target_class,
                                                                                method=method, device=device)
            elapsed_time = time.time() - start

            if global_explanation:
                explanation_accuracy, _ = logic.base.test_explanation(global_explanation, target_class, x_test, y_test)
                explanation = logic.base.replace_names(global_explanation, concepts)

            # negative class
            target_class_inv = 0
            if method == 'psi':
                model = train_psi_nn(x_train, y_train.eq(target_class_inv), need_pruning, seed, device, verbose)

            start = time.time()
            if method == 'psi':
                global_explanation_inv = logic.generate_fol_explanations(model, device)[0]
            else:
                global_explanation_inv, _, _ = logic.relu_nn.combine_local_explanations(model,
                                                                                x_train.to(device),
                                                                                y_train.to(device),
                                                                                target_class=target_class_inv,
                                                                                method=method, device=device)
            elapsed_time_inv = time.time() - start
            if global_explanation_inv:
                explanation_accuracy_inv, _ = logic.base.test_explanation(global_explanation_inv,
                                                                          target_class_inv, x_test, y_test)
                explanation_inv = logic.base.replace_names(global_explanation_inv, concepts)

        if verbose:
            print(f'\t Model\'s accuracy: {model_accuracy:.4f}')
            print(f'\t Class {target_class} - Global explanation: "{explanation}" - Accuracy: {explanation_accuracy:.4f}')
            print(f'\t Elapsed time {elapsed_time}')
            print(f'\t Class {target_class} - Global explanation: "{explanation_inv}" - Accuracy: {explanation_accuracy_inv:.4f}')
            print(f'\t Elapsed time {elapsed_time_inv}')

        methods.append(method)
        splits.append(seed)
        explanations.append(explanation)
        explanations_inv.append(explanation_inv)
        model_accuracies.append(model_accuracy)
        explanation_accuracies.append(explanation_accuracy)
        explanation_accuracies_inv.append(explanation_accuracy_inv)
        elapsed_times.append(elapsed_time)
        elapsed_times_inv.append(elapsed_time_inv)

    results = pd.DataFrame({
        'method': methods,
        'split': splits,
        'explanation': explanations,
        'explanation_inv': explanations_inv,
        'model_accuracy': model_accuracies,
        'explanation_accuracy': explanation_accuracies,
        'explanation_accuracy_inv': explanation_accuracies_inv,
        'elapsed_time': elapsed_times,
        'elapsed_time_inv': elapsed_times_inv,
    })
    results.to_csv(os.path.join(results_dir, f'results_{method}.csv'))

    return results
```

# General pruning

```python
method = 'pruning'
need_pruning = True
relu = False
results_pruning = c_to_y(method, need_pruning, relu)
results_pruning
```

# LIME

```python
method = 'lime'
need_pruning = False
relu = False
results_lime = c_to_y(method, need_pruning, relu)
results_lime
```

# Relu

```python
method = 'weights'
need_pruning = False
relu = True
results_weights = c_to_y(method, need_pruning, relu)
results_weights
```

# Psi network

```python
method = 'psi'
need_pruning = True
relu = False
results_psi = c_to_y(method, need_pruning, relu, verbose=True)
results_psi
```

# Decision tree

```python
method = 'decision_tree'
need_pruning = False
relu = False
results_tree = c_to_y(method, need_pruning, relu)
results_tree
```

# Summary

```python
cols = ['model_accuracy', 'explanation_accuracy', 'explanation_accuracy_inv', 'elapsed_time', 'elapsed_time_inv']
mean_cols = [f'{c}_mean' for c in cols]
sem_cols = [f'{c}_sem' for c in cols]

# pruning
df_mean = results_pruning[cols].mean()
df_sem = results_pruning[cols].sem()
df_mean.columns = mean_cols
df_sem.columns = sem_cols
summary_pruning = pd.concat([df_mean, df_sem])
summary_pruning.name = 'pruning'

# lime
df_mean = results_lime[cols].mean()
df_sem = results_lime[cols].sem()
df_mean.columns = mean_cols
df_sem.columns = sem_cols
summary_lime = pd.concat([df_mean, df_sem])
summary_lime.name = 'lime'

# weights
df_mean = results_weights[cols].mean()
df_sem = results_weights[cols].sem()
df_mean.columns = mean_cols
df_sem.columns = sem_cols
summary_weights = pd.concat([df_mean, df_sem])
summary_weights.name = 'weights'

# psi
df_mean = results_psi[cols].mean()
df_sem = results_psi[cols].sem()
df_mean.columns = mean_cols
df_sem.columns = sem_cols
summary_psi = pd.concat([df_mean, df_sem])
summary_psi.name = 'psi'

# tree
df_mean = results_tree[cols].mean()
df_sem = results_tree[cols].sem()
df_mean.columns = mean_cols
df_sem.columns = sem_cols
summary_tree = pd.concat([df_mean, df_sem])
summary_tree.name = 'tree'

summary = pd.concat([summary_pruning, summary_lime, summary_weights, summary_psi, summary_tree], axis=1).T
summary.columns = mean_cols + sem_cols
summary
```

```python
summary.to_csv(os.path.join(results_dir, 'summary.csv'))
```


#### _Speech Processing Labs 2020: Signals: Module 2_

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

import cmath
from math import floor
from matplotlib.animation import FuncAnimation
from IPython.display import HTML
plt.style.use('ggplot')

from dspMisc import *
```

# 3 Impulse Source, IIR Filter

### Learning Outcomes
* Be able to explain the difference between an FIR and an IIR filter
* Understand that IIR filters can be applied to impulse trains to produce different spectral envelopes

### Need to know
* Topic Videos: Impulse Response, Source Filter Model
* [Building the source: impulse trains](sp-m2-1-impulse-as-source.ipynb)
* [FIR filters](sp-m2-2-fir-filters.ipynb)

Equation alert: If you're viewing this on github, please note that the equation rendering is not always perfect. You should view the notebooks through a jupyter notebook server for an accurate view.

## 3.0 Filters again

The previous notebook explored Finite Impulse Response (FIR) filters which took a
 an input sequence $x[n]$ to give us some desired output sequence $y[n]$.  We saw that these FIR filters basically applied a weighted average across the input sequence and that this could be characterised in terms of convolution.
Now, we'll look at Infinite Impulse Response (IIR) filters, which allow us to model more complex feedback behaviour that's closer to what we might expect from the vocal tract.

## 3.1 Definition of an Infinite Impulse Response filter

Unlike FIR filters, IIR filters use outputs from previous time steps to calculate the next output.

$$ y[n] = \sum_{k=0}^N b[k] x[n-k] + \sum_{k=0}^M a[k] y[n-k]$$

The 'infinite' in the name comes from the fact that you can define a filter in this way so that a single impulse results in an infinitely long non-zero output sequence.

For example

$$ y[n] =  x[n] + y[n-1] $$

if $x[n] = 1$ for $n=1$ and 0 otherwise, then

* $ y[1] = 1 + 0  = 1$
* $ y[2] = 0 + 1 = 1$
* $ y[3] = 0 + 1 = 1$

i.e. $y[n] = 1$ for all $n$ to infinity!

These can be much more efficient than using an FIR filter: we can get the same effect with fewer parameters. (e.g. in some cases you can define a 4th order FIR filter that does the same job as a 19th order FIR filter). The flip side is that they're harder to analyze.

Unfortunately, there's no direct way to compute the a(k)'s and b(k)'s from the impulse response for an IIR filter, but we can use the **z Transform** to do this (we won't go into it in this class).  Instead we'll focus on how we can use an IIR filter to create a speech shaped sound.

## 3.2 Using an IIR filter to shape the spectral envelope

### Let's start with an impulse train

```python
N=400
f_s = 8000
t_s = 1/f_s

## Set the impulse train frequency in Hz
frequency = 100

## Generate N samples of an impulse train with frequency=frequency given a sampling rate of f_s
x_imp, time_steps = make_impulse_train(sample_rate=f_s, frequency=frequency, n_samples=N)

## plot and label
fig, ax = plt.subplots(figsize=(16, 4))
ax.set(ylim=(-1, 2))
ax.scatter(time_steps, x_imp)
ax.plot(time_steps, x_imp)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Amplitude")
ax.set_title("An impulse train")
```

### Now let's define a function to calculate the output of an IIR filter given feedback coefficients `a` and feedforward coefficients `b`.

```python
# iir_filter applies the IIR filter defined by feedforward coefficients, b, and feedback coefficients, a,
# where the coefficients are input as ordered lists
def iir_filter(x, a=[], b=[]):

    ## Get the size of the input
    ## Note the size of N will depend on how we deal with edge cases

    N = len(x)

    ## Initialize our output array, y, to all zeros
    y = np.zeros(N)

    ## step through each input index n
    for n in range(N):
        y[n] = 0

        ## get the contribution from the inputs
        for k in range(len(b)):
            if n-k >= 0:
                ## multiply the previous inputs with filter coefficients, b, and add them up
                ## just like an FIR filter
                y[n] = y[n] + (b[k]*x[n-k])

        ## get contribution from the existing outputs
        for l in range(len(a)):
            ## Note: We don't want to include the current output in this calculation!

            if n-l > 0:
                ## multiply the previous outputs with filter coefficients, a, and add them up
                y[n] = y[n] + (a[l]*y[n-l-1])

    return y

```

### Apply the IIR filter to our impulse train

```python
## An impulse train with amplified impulse to make things easier to see.
## It doesn't change the shape of the frequency response
x = x_imp * 10

## Note coefficients opposite sign to what Simon has in his slides.  Maybe a matlab thing?
## Or a notation thing to make transfer function look nicer?
a = [3.22666099, -4.3967485, 3.03596532, -0.88529281]
b = [1]

## You might like to try this Low pass filter from Lyon's DSP textbook
#a=[1.194, -0.436]
#b=[0.0605, 0.121, 0.0605]

y_iir = iir_filter(x, a=a, b=b)

```

### Plot the filtered output in the time domain

```python
## Plot the filtered output
fig, ax = plt.subplots(figsize=(16, 4))
ax.plot(range(N), y_iir)
print(N, np.max(y_iir))
ax.set_xlabel("Time (s)")
ax.set_ylabel("Amplitude")
ax.set_title("Impulse train transformed by an IIR filter")

## Starting to look more speech...ish
```

Now we're starting to make something that looks like a speech waveform!

### Now let's look at the DFT of the filter's output

```python
## Get the DFT outputs of the IIR filter's output
mags, phases = get_dft_mag_phase(y_iir, N)

## Plot the DFT frequencies rather than DFT output indices
dft_freqs = (f_s/N) * np.arange(N)

## Plot the magnitude spectrum
fig, ax = plt.subplots(figsize=(16, 4))
ax.plot(dft_freqs[0:int(N/2)], mags[0:int(N/2)])
ax.set_xlabel("Frequency (Hz))")
ax.set_ylabel("Magnitude")
ax.set_title("Frequency response of IIR filter output (sample rate: %d samples/sec)" % f_s)

```

### Exercise

* What happens to the filtered signal if we alter the filter coefficients:
    * Try out the filter coefficients in the cell below and describe what sort of effect they have.
    * What sort of frequencies do they boost and which do they dampen?
    * Do any look like low-pass or high-pass filters, for example?
    * Do all of them produce outputs that look like vocal pulses?

* If you play around the coefficients it's quite easy to end up with an output that doesn't 'fade out' over time.  What happens to the DFT in this case?

```python
## Try out these different sets of IIR filter coefficients
## ex 1
a = [0.8]
b = [1]

## ex 2
a = [-0.8]
b = [1,-1]

## ex 3:
a = [0.566, -0.16017]
b = [1, 1.414, 1]

## ex 4:
a = [0.566, -0.16017]
b = [1, 0.414, -0.414, -1]

## ex 5:
a = [1.272, -0.80899]
b = [1, 0.414, -0.414, -1]

## ex 6: unstable (unbounded) but still gives us a frequency response like the one previous
a = [1.554, -1.207]
b = [1, 0.414, -0.414, -1]

```

```python
## set which coeffients you want to use here.
## e.g. ex 1
a = [0.8]
b = [1]

## the impulse train
x = x_imp * 10
y_iir = iir_filter(x, a=a, b=b)

## Plot the filtered output
fig, ax = plt.subplots(figsize=(16, 4))
ax.plot(range(N), y_iir)
ax.set_xlabel("Time (s)")
ax.set_ylabel("Amplitude")
ax.set_title("Impulse train transformed by an IIR filter: a=%s, b=%s" % (repr(a), repr(b)))

#print(N, np.max(y_iir))

## Plot the DFT frequencies rather than DFT output indices
mags, phases = get_dft_mag_phase(y_iir, N)
dft_freqs = (f_s/N) * np.arange(N)

## Plot the magnitude spectrum
fig, ax = plt.subplots(figsize=(16, 4))
#ax.scatter(dft_freqs[0:int(N/2)],mags[0:int(N/2)])
ax.plot(dft_freqs[0:int(N/2)], mags[0:int(N/2)])
ax.set_xlabel("Frequency (Hz))")
ax.set_ylabel("Magnitude")
ax.set_title("Frequency response of IIR filter output: a=%s, b=%s" % (repr(a), repr(b)))

```

### Notes

```python

```

---

## 3.3 (Optional Extras) Designing a filter using poles and zeros

This section is very much optional extension. The following gives a very, very terse overview of how you can design an IIR filter frequency response using the concept of poles and filters. This requires a fair grip on solving polynomials and complex numbers, but once you've got that it's actually quite nice!

If you're interested in learning more about this (or signal processing in general), it's probably time to pick up a Digital Signal Processing textbook. Rick Lyon's [Understanding Digital Signal Processing](https://www.dsprelated.com/books/6.php) is probably the most accessible I've come across. The example filters above were taken from that book. The end of this section basically gives a excessively detailed derivation going from poles and filters to coefficients for those examples.

Previous SLP students have also recommended [The Scientist and Engineer's Guide to
Digital Signal Processing](http://www.dspguide.com/pdfbook.htm), though the free online version renders a bit weirdly!.

IIR filters have the following form:

$$ y[n] = \sum_{k=0}^N b(k) x[n-k] + \sum_{k=0}^M a(k) y[n-k]$$

Unlike an FIR filter, we can't just put an impulse in and find out what the frequency response of the filter is.  Instead, to understand the properties of an IIR filter, we use the **z-transform**.  The z-transform is the discrete version of the continuous Laplace transform.  We won't get into the details here, but if you do read more about this you'll see that you can think of the Laplace transform as a generalization of the Fourier Transform. So the intuitions you've developed about how the Fourier Transform works (e.g. finding correlations between inputs and basis functions) will help understand the Laplace Transform too.

We use the z-transform to convert filter difference equations to an equivalent form in the z-domain (z being a complex number).  We rewrite our difference equation in terms of the z-transformed input $x[n]$ and output $y[n]$ functions, which we denote as $X(z)$ and $Y(z)$ respectively.  Some algebra then allows us to define a **transfer function** $H(z)$:

$$ Y(z) = H(z)X(z)$$

So, $H(z)$ is what our filter does to our input to get our output in the z-domain. You can think of it as the filter part of the source filter model (the source being our impulse train and/or white noise).

Unsurprisingly, we can actually define $H(z)$ in terms of our the feedforward coefficients $b(k)$ and the feedback coefficients $a(k)$. What's surprising is that we can define it in a relatively simple way:

$$ H(z) = \frac{Y(z)}{X(z)} = \frac{\sum_{k=0}^N b(k)z^{-k}}{1- \sum_{k=1}^M a(k)z^{-k}}\\ $$

You can think of  $z^{-k}$ as referring to a position in the sequence $k$ steps back in time.

We can analyze $H(z)$ to figure out how the filter is going to behave. To do this we need to factorize the numerator and denominator polynomials of $H(z)$.

$$
\begin{align}
H(z) &= \frac{\sum_{k=0}^N b(k)z^{-k}}{1- \sum_{k=1}^M a(k)z^{-k}}\\
&= G\frac{(z-z_0)(z-z_1)...(z-z_N)}{(z-p_0)(z-p_1)...(z-p_M)}
\end{align}
$$

In the equation above, $G$ is an arbitrary gain constant which doesn't affect the frequency response or filter stability. The polynomial roots, $z_n$ and $p_m$ are called *zeros* and *poles* respectively.  These are the (complex) values of $z$ which cause the numerator (zeros) or the denominator (poles) to equal 0.  We can interpret them as follows:

* **Zeros:** when the numerator goes to zero, the transfer function $H(z)$ goes to zero, and we see decrease (attenuation) in the frequency magnitude response.
* **Poles:** when the denominator goes to zero, the transfer function $H(z)$ goes to infinity, leads to an increase (gain) the frequency magnitude response (like vowel formants!)

It may not look like it right now, but it's way easier to analyze IIR filters in the z-domain than in the time domain! Once we know the poles and the zeros of the transfer function, we basically know what the frequency response will look like and whether the filter is stable or not.  The main things to note are:

* If a pole falls inside the unit-circle (of the complex z-plane), the filter output will be **stable**.
    * Given an impulse input, $y[n]$ it will eventually go to zero as $n$ increases.
* If a pole falls outside the unit circle, the output will be **unstable**
    * Given an impulse input, $y[n]$ will keep increasing in magnitude as more time passes (definitely not the behaviour of the vocal tract!).
* If a pole lies exactly on the unit circle, the output will oscillate between finite values determined by the pole locations forever.

An IIR filter is only as stable as it's weakest link - if any poles sit outside the unit circle, the filter as a whole will be unstable.  Zeros don't affect filter stability.

Another property of poles (and zeros) is that, if the denominator polynomial has real-valued coefficients (i.e. the $a(k)$ values), poles (and zeros) either come in pairs of complex conjugates (i.e. if $p=a+ib$ is a pole, so is $p^*=a-ib$), or it's a value on the real axis of the z-plane.  We can use these facts to design filters using the factorized form of the transfer function, which will then allow us to derived the filter coefficients $a(k)$ and $b(k)$. We'll see some examples this below.

The frequency response of the filter is $H(z)$ evaluated at $e^{j\omega}$, where $-\pi \lt \omega \lt \pi$ represents frequencies in terms of radians per sample ($\pi$ radians/sample $\mapsto$ a frequency matching half the sampling frequency (i.e. $f_s/2$).

$$ H(\omega) = H(e^{j\omega}) = \frac{\sum_{k=0}^N b(k)e^{-jk\omega}}{1 -\sum_{k=0}^M a(k)e^{-jk\omega}} $$

These example IIR filters from Richard J Lyon's book
[Understanding Digital Signal Processing](https://www.dsprelated.com/books/6.php), which is a very nice introduction to the topic.

Working through from the factorized form to the coefficients is not hard, but it is very annoying and it's very easy to make mistakes, so I've given all the steps expanding out the factorized forms of the filter examples he gives in his book below.

### Ex 1:

$$
\begin{aligned}
H_1(z) &= \frac{G}{z-0.8}\\
       &= G\frac{1}{1-(0.8z^{-1})}
\end{aligned}
$$

* One pole on the real axis, inside the unit circle, stable peak at $\omega=0$ rad/sample
* no zeros

```python
a = [0.8]
b = [1]
```

### Ex 2:

$$
\begin{aligned}
H_1(z) &= G\frac{(z-1)}{z-(-0.8)}\\
       &= G\frac{1 - z^{-1}}{1 -  (-0.8z^{-1})}
\end{aligned}
$$

* pole on real axis at -0.8, inside the unit circle, stable peak at $\omega=-\pi$ rad/sample, i.e. $f_s/2$ Hz which is 8000/2 = 4000 Hz in our setup above.
* one zero at z=1, this causes attenuation at $\omega = 0$ rads/sample (0 Hz)

```python
a = [-0.8]
b = [1,-1]
```

### Ex 3
$$
\begin{align}
H_3(z) &= G\frac{[z-(-0.707 + j0.707)][z-(-0.707 - j0.707)]}{[z-(0.283+j0.283)][z-(0.283-j0.283)]} \\
&= G\frac{z^2 -z(-0.707 - j0.707) - z(-0.707 + j0.707) + (-0.707 + j0.707)(-0.707 - j0.707) }{z^2 - z(0.283-j0.283) -z(0.283+j0.283) + (0.283+j0.283)0.283-j0.283)} \\
&= G\frac{z^2 +z0.707 + j0.707z + z0.707 - j0.707z + 1 }{z^2 - 0.283z + j0.283z - 0.283z - j0.283z + 0.16017} \\
&= G\frac{z^2 +z[0.707  + 0.707] + 1 }{z^2 - 0.283z - 0.283z  + 0.16017} \\
&= G\frac{z^2 + 1.414z + 1 }{z^2 - 0.566z + 0.16017} \\
&= G\frac{1 + 1.414z^{-1} + 1z^{-2} }{1 - 0.566z^{-1} + 0.16017z^{-2}} \\
\end{align}
$$

* The poles are inside the unit circle, this produces a low pass filter  (pass band is 'bordered' by +/- $\omega = \pi/4$ rad/sample, 1000 Hz)
    * $(0.283+j0.283) \mapsto 0.400e^{j\pi/4}$
* zeros on the unit circle, attenuation at $3\pi/4$ rad/sample = 3000 Hz.
    * $(-0.707+j0.707) \mapsto 1e^{j3\pi/4} $

```python
a = [0.566, -0.16017]
b = [1, 1.414, 1]

```

### Ex 4:
$$
\begin{align}
H_4(z) &= G\frac{[z-1][z-(-0.707 + j0.707)][z-(-0.707 - j0.707)]}{[z-(0.283+j0.283)][z-(0.283-j0.283)]} \\
&= G\frac{[z-1] [z^2 + 1.414z + 1] }{z^2 - 0.566z + 0.16017}\\
&= G\frac{z^3 + 1.414z^2 + 1z -z^2 - 1.414z - 1}{z^2 - 0.566z + 0.16017}\\
&= G\frac{z^3 + 0.414z^2 - 0.414z - 1}{z^2 - 0.566z + 0.16017}\\
&= G\frac{1 + 0.414z^{-1} - 0.414z^{-2} - z^{-3}}{1 - 0.566z^{-1} + 0.16017z^{-2}}\\
\end{align}
$$

* We've added an extra zero at $z=0$ compared to example 3.  This means that we get a valley (attenuation) in the frequency response at $\omega = 0$ (0 Hz).

```python
a = [0.566, -0.16017]
b = [1, 0.414, -0.414, -1]
```

### Ex 5:
$$
\begin{align}
H_5(z) &= G\frac{[z-1][z-(-0.707 + j0.707)][z-(-0.707 - j0.707)]}{[z-(0.636+j0.636)][z-(0.636-j636)]} \\
&= G\frac{[z-1] [z^2 + 1.414z + 1] }{z^2 - 1.272z + 0.808992}\\
&= G\frac{1 + 0.414z^{-1} - 0.414z^{-2} - z^{-3}}{1 - 1.272z^{-1} +  0.808992z^{-2}}\\
\end{align}
$$

* poles are nearer to the unit circle (magnitude 0.80899), so the peaks associated with them (+/- $\omega = \pi/4$ rad/sec (1000 Hz with our sample rate) are sharper.

```python
a = [1.272, -0.80899]
b = [1, 0.414, -0.414, -1]
```

### Ex 6:
$$
\begin{align}
H_6(z) &= G \frac{[z-1][z-(-0.707 + j0.707)][z-(-0.707 - j0.707)]}{[z-(0.777+j0.777)][z-(0.777-777)]} \\
&= G \frac{1 + 0.414z^{-1} - 0.414z^{-2} - z^{-3}}{1 - 1.554z^{-1} +  1.207z^{-2}}\\
\end{align}
$$

* poles are now outside the unit circle (magnitude 1.207), so the filter is now unstable: as n increases, the value of $y[n]$ eventually goes to infinity.
* It still has the same shape frequency response though!

```python
a = [1.554, -1.207]
b = [1, 0.414, -0.414, -1]
```


Created: Tuesday 7 August 2018
github.com/douglask3/savanna_fire_feedback_test

Quantifying the uncertainity in climate potentials and mortality using Bayesian inference

Part 2: Bayesian inference

1,* Douglas Kelley,
1 France Gerard,
2 Rhys Whitley,
3 Elmar Veenendaal

1 Centre for Ecology and Hydrology, Maclean Building, Crowmarsh Gifford, Wallingford, Oxfordshire, United Kingdom

2 Natural Perils Pricing, Commercial & Consumer Portfolio & Pricing, Suncorp Group, Sydney, Australia

3 Wageningen Univsersity, Wageningen UR

Summary

This notebook aims to quantify the model parameters of a global tree cover model model. Tree Cover is calculated as a product  precipitation, tempurature  and radiation controls describing large scale climate potetials, and mortality from fire, seasonal drought, tempuature and people, and exlusion from land use modulate tree cover. This limitation model for tree cover (LimTREE) is optimized using a Bayesian Inference framework, which provides a probability distribution for the models paramters, and allows us to describe the magnitude and relative impact of controls and varaibles in terms of probabilities. Here, we cover model description and optimization.

Python code and calculations below

### Model description
Tree Cover ($T$) is calculated as a product five controls. Mean annual precipitation ($MAP$), mean annual tempurature ($MAT$) and incoming shortwave radiation ($SW$) represent three liberative controls on  $T$, i.e, increases in any of the variables lead to an increase in $T$. Mortality ($M$), which combines varaibles describing fire (represented by burnt area - $F$), drought (by number of dry days, $D$) and extreme seasonal tempuratures (by mean maximum temperuature of the hottest month, $MMxT$), and land use exclusion ($E$) from fractional urban ($U$), crop ($C$) and pasture ($P$) cover are suppresive controls. Each control is expressed as a linear combination of their respective variables and represented by a simple logistic curve:

\begin{equation}
    f(x) = \frac{1}{1 + e^{-k\cdot(x-x_0)}}
\end{equation}

\begin{eqnarray}
    T &=& F_{max} \cdot \prod f(x) \\[1em]
\end{eqnarray}

Where $f(x)$ is the limitation imposed by control $x = MAP, MAT, SW, M, E$ and $T_{max}$ is a maximum permitted monthly burnt area used to aid our model optimization. $x_0$ is the value of control $x$ when it imposes a limitation of 50\% on burnt area (i.e,$f(x) = 0.5$), and $k$ is the steepness of the logistic curve, equal to ¼ of the gradient at $x = x_0$. $k > 0$ for $MAP$, $MAT$ and $SW$, where $T$ increases with the control, whereas $k < 0$ for $M$ and $E$, for which $T$ decreases. Each suppresive control
is represented by a combination of variables ($x_i$) weighted by their respective influence ($v_i$). Where possible, units are consistent across variables within each control, and as such the combined variables are normalised to maintain these units:

\begin{eqnarray}
    x &=& \sum v_i \cdot x_i / \sum v_i  \\[1em]
\end{eqnarray}

\begin{eqnarray}
    v_i = 1
\end{eqnarray}

This leaves 14 free parameters that need to be optimised against observations of burnt area.

Now lets get this coded up in python

### Load libraries

```python
import os
from   io     import StringIO
import numpy  as np
import pandas as pd
import csv

import pymc3  as pm3
from   pymc3.backends import SQLite
from   scipy  import optimize
from   theano import tensor as tt

import matplotlib.pyplot as plt

# setup nice plotting
plt.style.use('ggplot')
%matplotlib inline

# paths and parameters
sample_pc     = 20
nIterations   = 20000 # 20000
nJobs         = 1    # 12
nChains       = 10  # 10
outPath       = "../data/vegFracControls.csv"
param_outpath = '../outputs/params-fullnewModel'
```

```python
def npLog0 (x):
    return [np.log(i) if i > 0 else 0.0000001 for i in x]
```

## 2.1 Fire limitation model definition

Could possibly contain this in a class object, but I'm not sure theano can instantiate the object to be used by the GPU. If I've made absolutely no sense just then, then I would leave the following as is.

```python
from pdb import set_trace as browser

def tt_log(x):
    x = tt.clip(x, 0.0000001, 1000000000)
    return tt.log(x)

def tt_sigmoid(x, k, x0):
    """
    Sigmoid function to describe limitation using tensor
    """
    return 1.0/(1.0 + tt.exp(-k*(x - x0)))

def MAP(map):#, drought, m_drought):
    """
    Definition to describe precip: while we are just returning input for now,
    we include climate potential functions for capability to be modified later.
    """
    #map = map + (1.0-drought) * (1/m_drought) * (tt.exp(-m_drought * map) - 1)
    map = tt_log(map)
    return map

def SW(sw1, sw2, d):
    """
    Definition to describe short wave
    """
    sw = (sw1 + d * sw2) /(1.0 + d)
    #sw = tt_log(sw)
    return sw

def Temp(mat, min_mat, q10):
    """
    Definition to describe temp.
    """
    mat = (1.0 + q10)**(mat - min_mat)

    #mat = mat - min_mat
    #mat = mat / (max_mat - min_mat)
    #mat = tt.clip(mat, 0.0000001, 100000)
    #mat = tt.log(mat)
    return mat

def convert_unity(x, k):
    return 1 - tt.exp(x * (-1.0/ k))

def mortality(fire, drought, maxTemp, minTemp, pop_density,
              buffalo, cattle, goat, sheep, v_drought, v_maxTemp, v_minTemp,
              v_pop, v_buffalo, v_cattle, v_goat, v_sheep,
              p_fire,
              k_pop, k_buffalo, k_cattle, k_goat, k_sheep,
              p_drought, min_maxTemp, max_minTemp, q10_maxTemp, q10_minTemp):
    """
    Definition of mortality
    """
    #maxTemp = maxTemp - min_maxTemp
    #maxTemp = maxTemp / (max_maxTemp - min_maxTemp)
    #maxTemp = tt.clip(maxTemp, 0.0, 1.0)
    #maxTemp = maxTemp **(p_maxTemp)

    maxTemp = Temp(maxTemp, min_maxTemp, q10_maxTemp)
    minTemp = Temp(-minTemp, -max_minTemp, q10_minTemp)

    drought = drought ** (p_drought)
    pop_density = convert_unity(pop_density, k_pop    )
    buffalo     = convert_unity(buffalo    , k_buffalo)
    cattle      = convert_unity(cattle     , k_cattle )
    goat        = convert_unity(goat       , k_goat   )
    sheep       = convert_unity(sheep      , k_sheep  )

    fire = fire**(p_fire)
    mort = fire  +  v_drought * drought  +  v_maxTemp * maxTemp  +  v_minTemp * minTemp + \
           v_pop * pop_density + v_buffalo * buffalo + v_cattle * cattle  + v_goat * goat + v_sheep * sheep

    return mort / (1.0 + v_drought + v_maxTemp + v_minTemp + v_pop + v_buffalo + v_cattle + v_goat + v_sheep)

def exclusion(urban_area, crop_area, pas_area, v_crop, v_pas):
    """
    Definition for the measure of fire supression
    """
    excl = urban_area  +  v_crop * crop_area + v_pas * pas_area

    return excl / (1.0 + v_crop + v_pas)

```

## 2.2 Import data

Load data and do any necessary transformation needed for the Bayesian modelling framework. We  going to load 10% of data-points. When we evalutate, that gives us 90% spare.

```python
def load_with_buffer(filename, line_select, **kwargs):
    s_buf = StringIO()
    line_select = np.sort(line_select)
    with open(filename) as file:
        count = -1
        lineN = -1
        for line in file:
            lineN += 1
            if lineN == 0 or lineN == line_select[count]:
                s_buf.write(line)
                count += 1
                if count == len(line_select): break

    s_buf.seek(0)
    df = pd.read_csv(s_buf,**kwargs)
    return df

def file_len(fname):
    with open(fname) as f:
        for i, l in enumerate(f): pass
    return i + 1

DATAPATH = os.path.expanduser(outPath)

nlines      = file_len(DATAPATH)
npoints     = round(sample_pc * nlines / 100)
line_select = np.random.choice(range(0, nlines), npoints, False)
fd          = load_with_buffer(DATAPATH, line_select)
```

Do a sanity check to make sure our data has imported correctly.

```python
print(fd.info())
print(fd.head())
print(fd.min())
print(fd.max())
```

    <class 'pandas.core.frame.DataFrame'>
    RangeIndex: 4987 entries, 0 to 4986
    Data columns (total 44 columns):
    BurntArea                   4987 non-null float64
    crop                        4987 non-null float64
    MaxWind                     4987 non-null float64
    buffalo                     4987 non-null float64
    cattle                      4987 non-null float64
    MADD_CMORPH                 4987 non-null float64
    MADD_CRU                    4987 non-null float64
    MADD_GPCC                   4987 non-null float64
    MADD_MSWEP                  4987 non-null float64
    MADM_CMORPH                 4987 non-null float64
    MADM_CRU                    4987 non-null float64
    MADM_GPCC                   4987 non-null float64
    MADM_MSWEP                  4987 non-null float64
    goat                        4987 non-null float64
    MAP_CMORPH                  4987 non-null float64
    MAP_CRU                     4987 non-null float64
    MAP_GPCC                    4987 non-null float64
    MAP_MSWEP                   4987 non-null float64
    MAT                         4987 non-null float64
    MConc_CMORPH                4987 non-null float64
    MConc_CRU                   4987 non-null float64
    MConc_GPCC                  4987 non-null float64
    MConc_MSWEP                 4987 non-null float64
    MDDM_CMORPH                 4987 non-null float64
    MDDM_CRU                    4987 non-null float64
    MDDM_GPCC                   4987 non-null float64
    MDDM_MSWEP                  4987 non-null float64
    MTWM                        4987 non-null float64
    pas                         4987 non-null float64
    PopDen                      4987 non-null float64
    SW                          4987 non-null float64
    SW.1                        4987 non-null float64
    TreeCover                   4987 non-null float64
    urban                       4987 non-null float64
    sheep                       4987 non-null float64
    MTCM                        4987 non-null float64
    BurntArea_GFED              4987 non-null float64
    BurntArea_GFED.1            4987 non-null float64
    BurntArea_meris             4987 non-null float64
    BurntArea_MODIS             4987 non-null float64
    BurntArea_MCD               4987 non-null float64
    BurntArea_GFED_four_s       4987 non-null float64
    BurntArea_GFED_four         4987 non-null float64
    BurntArea_MCD_forty_five    4987 non-null float64
    dtypes: float64(44)
    memory usage: 1.7 MB
    None
       BurntArea      crop   MaxWind  buffalo        cattle  MADD_CMORPH  \
    0   0.000006  0.000002  6.260526      0.0  4.050973e-01     0.907838
    1   0.000008  0.000014  5.843527      0.0  2.529809e-01     0.857775
    2   0.000000  0.000000  0.000000      0.0  1.160095e-27     0.894343
    3   0.000000  0.000000  4.791956      0.0  2.132288e-01     0.855612
    4   0.002773  0.150053  5.330987      0.0  5.058890e+00     0.722786

       MADD_CRU  MADD_GPCC  MADD_MSWEP  MADM_CMORPH  ...         sheep      MTCM  \
    0  1.000000   0.954631    0.866844     0.901959  ...  5.621986e-03  5.735714
    1  1.000000   0.949744    0.874905     0.729050  ...  4.868020e-03  6.807143
    2  1.000000   0.934106    0.882918     0.874533  ...  2.018402e-29  8.500000
    3  1.000000   0.924918    0.838144     0.827557  ...  0.000000e+00  8.714286
    4  0.880028   0.783342    0.668022     0.903827  ...  7.862720e-02  3.592857

       BurntArea_GFED  BurntArea_GFED.1  BurntArea_meris  BurntArea_MODIS  \
    0        0.000006          0.000051         0.000000         0.000000
    1        0.000008          0.000000         0.000000         0.000000
    2        0.000000          0.000000         0.000000         0.000000
    3        0.000000          0.000000         0.000000         0.000000
    4        0.002773          0.002569         0.003734         0.003312

       BurntArea_MCD  BurntArea_GFED_four_s  BurntArea_GFED_four  \
    0       0.000004               0.000006             0.000051
    1       0.000015               0.000008             0.000000
    2       0.000079               0.000000             0.000000
    3       0.000210               0.000000             0.000000
    4       0.005669               0.002773             0.002569

       BurntArea_MCD_forty_five
    0                  0.000004
    1                  0.000015
    2                  0.000079
    3                  0.000210
    4                  0.005669

    [5 rows x 44 columns]
    BurntArea                   0.000000e+00
    crop                        0.000000e+00
    MaxWind                     0.000000e+00
    buffalo                    -3.214751e-13
    cattle                     -1.221860e-12
    MADD_CMORPH                 2.939716e-02
    MADD_CRU                    6.970892e-02
    MADD_GPCC                   7.958412e-02
    MADD_MSWEP                  4.401794e-04
    MADM_CMORPH                 1.504516e-01
    MADM_CRU                    1.067267e-01
    MADM_GPCC                   0.000000e+00
    MADM_MSWEP                  1.027350e-01
    goat                       -9.043055e-13
    MAP_CMORPH                  7.332322e-01
    MAP_CRU                     0.000000e+00
    MAP_GPCC                    3.498418e-05
    MAP_MSWEP                   1.781753e-03
    MAT                        -4.866071e+00
    MConc_CMORPH                8.642738e-02
    MConc_CRU                   5.383221e-03
    MConc_GPCC                  5.937037e-02
    MConc_MSWEP                 4.783452e-02
    MDDM_CMORPH                 7.347213e-02
    MDDM_CRU                    2.387020e-01
    MDDM_GPCC                   1.465109e-01
    MDDM_MSWEP                  2.304148e-03
    MTWM                        8.542857e+00
    pas                         0.000000e+00
    PopDen                      0.000000e+00
    SW                          2.746841e+01
    SW.1                        3.305233e+01
    TreeCover                   0.000000e+00
    urban                       0.000000e+00
    sheep                      -1.563413e-12
    MTCM                       -2.373571e+01
    BurntArea_GFED              0.000000e+00
    BurntArea_GFED.1            0.000000e+00
    BurntArea_meris             0.000000e+00
    BurntArea_MODIS             0.000000e+00
    BurntArea_MCD               0.000000e+00
    BurntArea_GFED_four_s       0.000000e+00
    BurntArea_GFED_four         0.000000e+00
    BurntArea_MCD_forty_five    0.000000e+00
    dtype: float64
    BurntArea                      0.905067
    crop                           0.729860
    MaxWind                       13.375506
    buffalo                      269.805500
    cattle                       304.827360
    MADD_CMORPH                    0.996048
    MADD_CRU                       1.000000
    MADD_GPCC                      1.000000
    MADD_MSWEP                     1.000000
    MADM_CMORPH                    1.000000
    MADM_CRU                       1.000000
    MADM_GPCC                      1.000000
    MADM_MSWEP                     1.000000
    goat                         612.380550
    MAP_CMORPH                  7680.668500
    MAP_CRU                     7345.421400
    MAP_GPCC                    7329.232400
    MAP_MSWEP                   6606.023000
    MAT                           30.825000
    MConc_CMORPH                   1.000000
    MConc_CRU                      1.000000
    MConc_GPCC                     1.000000
    MConc_MSWEP                    1.000000
    MDDM_CMORPH                    1.000000
    MDDM_CRU                       1.000000
    MDDM_GPCC                      1.000000
    MDDM_MSWEP                     1.000000
    MTWM                          46.092857
    pas                            0.826389
    PopDen                      8241.323000
    SW                           351.884670
    SW.1                         387.525670
    TreeCover                      0.805655
    urban                         26.097485
    sheep                        546.529970
    MTCM                          24.028572
    BurntArea_GFED                 0.905067
    BurntArea_GFED.1               0.888886
    BurntArea_meris                0.967044
    BurntArea_MODIS                0.894569
    BurntArea_MCD                  0.906856
    BurntArea_GFED_four_s          0.905067
    BurntArea_GFED_four            0.888886
    BurntArea_MCD_forty_five       0.906856
    dtype: float64

## 2.3 Baysian framework

A simple explanation of Baye's law is:

\begin{equation}
    P(\beta|X) \propto P(\beta)\cdot P(X|\beta)
\end{equation}

where $X$ is our data (observations of some arbitrary system), and $\beta$ our set of unexplained parameters that describe the reponse of our _proposed understanding_ of this system as it varies with $X$.

### 2.3.1 Prior definitions
Because I have no idea what the uncertainty on the hyper parameters should look like (beyond $\beta> 0$), I've set them all as wide, familiar distrubtions, spread generously beyound what is realistic plausable. Our full prior looks like this:

\begin{eqnarray}
    P(x_0) &=& \mathcal{N}(0.5, 0.5) \\
    P(k_x) &=& \mathcal{exp}(0.1) \\
    P(T_{max}) = P(p_{popden}) = P(v_i) &=& \mathcal{exp}(1) \\
    P(\sigma) &=& \mathcal{N_{1/2}}(1) \\[1.5em]
\end{eqnarray}

So our full set of piors looks something like this:

\begin{eqnarray}
    P(\beta) &=& \prod_{x=1}^{4}\mathcal{S}(P(x_0),P(k_x), P(v_x)) \cdot P(D_{max}) \cdot P(\sigma) \\[1.5em]
\end{eqnarray}

This is loosly describes a (albeit slightly skewed) Normal distribution, so we can approximate our error ($\sigma$) with a normal distibution.

Back to the code.., `pymc3` is quite funky in that it allows me to create an empty `Model()` object and just add things to it as I need them using a `with` statement. I've called our Bayesian model `tree_error` as that is what we are trying to Quantify.

```python
nChains = nChains * nJobs
```

```python
def cutLastX(varname, mcmc_traces, ncut = 50):
    vals = mcmc_traces.get_values(varname)
    def subcut(vals, r, ncut = 50):
        cut_np = (r+1) * round(len(vals)/nChains)
        ncut = round(len(vals) * ncut / (nChains *100))
        return vals[(cut_np - ncut):cut_np]
    vals = [subcut(vals, r) for r in range(nChains)]
    return np.array(vals).flatten()
```

```python
def run_pr_droughtVar(fire_dataset, pr_dataset, drough_var):
    with pm3.Model() as tree_error:

    # first for the sigmoids
        MAP_x0         = pm3.Normal('MAP_x0'       ,  5.0, 1.0)
        MAP_k          = pm3.Exponential('MAP_k'        ,  1.0)

        MAT_x0         = pm3.Normal('MAT_x0'   ,  0.0, 1.0)
        MAT_k          = pm3.Exponential('MAT_k'    ,  1.0)

        SW_x0          = pm3.Normal('SW_x0'   ,  200.0, 100.0)
        SW_k           = pm3.Exponential('SW_k'    ,        1.5)

        mort_x0        = pm3.Normal('mort_x0', 0.5, 0.5)
        mort_k         = pm3.Exponential('mort_k' , 1.0)

        ex_x0          = pm3.Normal('ex_x0', 0.5, 0.5)
        ex_k           = pm3.Exponential('ex_k' , 1.0)

        max_T          = pm3.Uniform('max_T',     0.8)
        #pow_f          = pm3.Uniform('pow_f'         ,     0.0,    1.0)
    # now for the hyper-parameters that describe the independent fire condition covariates

        #cNpp = pm3.Uniform('cNPP', 0.0, 10)
        #m_drought   = pm3.Exponential('m_drought', 1.0)
        v_drought   = pm3.Exponential('v_drought', 1.0)
        v_maxTemp   = pm3.Exponential('v_maxTemp', 1.0)
        v_minTemp   = pm3.Exponential('v_minTemp', 1.0)
        v_pop       = pm3.Exponential('v_pop'    , 1.0)
        v_buffalo   = pm3.Exponential('v_buffalo', 1.0)
        v_cattle    = pm3.Exponential('v_cattle' , 1.0)
        v_goat      = pm3.Exponential('v_goat'   , 1.0)
        v_sheep     = pm3.Exponential('v_sheep'  , 1.0)
        v_crop      = pm3.Exponential('v_crop'   , 1.0)
        v_pas       = pm3.Exponential('v_pas'   , 1.0)

        trans_d     = pm3.Uniform('trans_d',  0.0, 1.0)
        p_fire      = pm3.Exponential('p_fire', 1000.0)
        #k_pop       = pm3.Exponential('k_pop' , 1000.0)
        #k_buffalo   = pm3.Exponential('k_buffalo', 1000.0)
        #k_cattle    = pm3.Exponential('k_cattle' , 1000.0)
        #k_goat      = pm3.Exponential('k_goat'   , 1000.0)
        #k_sheep     = pm3.Exponential('k_sheep'  , 1000.0)

        k_pop       = pm3.Lognormal('k_pop'    , 7.0, 1.0)
        k_buffalo   = pm3.Lognormal('k_buffalo', 7.0, 1.0)
        k_cattle    = pm3.Lognormal('k_cattle' , 7.0, 1.0)
        k_goat      = pm3.Lognormal('k_goat'   , 7.0, 1.0)
        k_sheep     = pm3.Lognormal('k_sheep'  , 7.0, 1.0)

        p_drought   = pm3.Exponential('p_drought', 1.0)
        min_maxTemp = pm3.Normal     ('min_maxTemp', 20.0, 10)
        max_minTemp = pm3.Normal     ('max_minTemp', 20.0, 10)
        #min_mat     = pm3.Normal     ('min_mat', 0.0, 3.0)
        #max_mat     = pm3.Normal     ('max_mat', 15.0, 5.0)
        q10_maxTemp = pm3.Exponential('q10_maxTemp', 1.0)
        q10_minTemp = pm3.Exponential('q10_minTemp', 1.0)

        # describe the standard deviation in the error term
        sigma       = pm3.HalfNormal('sigma', sd=1)

        # transform hyper-covariates
        MAP_var = 'MAP_' + pr_dataset
        drght_var = drough_var + '_' + pr_dataset
        fire_var = "BurntArea_" + fire_dataset

        f_precip = MAP(fd[MAP_var].values)#, fd[drght_var].values, m_drought)
        #f_temp   = MAT(fd["MAT"].values, min_mat, max_mat)
        f_sw     = SW (fd["SW"].values, fd["SW.1"].values, trans_d)

        f_mort   = mortality(fd[fire_var].values, fd[drght_var].values, fd["MTWM"].values, fd["MTCM"].values,
                             fd["PopDen"].values, fd["buffalo"].values, fd["cattle"].values, fd["goat"].values,
                             fd["sheep"].values,
                             v_drought, v_maxTemp, v_minTemp,
                             v_pop, v_buffalo, v_cattle, v_goat, v_sheep,
                             p_fire,
                             k_pop, k_buffalo, k_cattle, k_goat, k_sheep,
                             p_drought,
                             min_maxTemp, max_minTemp, q10_maxTemp, q10_minTemp)

        f_exc    = exclusion(fd["urban"].values, fd["crop"].values, fd["pas"].values,
                             v_crop, v_pas)

        # Tree cover is assumed to be the product of the 4 sigmoid

        prediction = max_T * np.product([tt_sigmoid(f_precip, MAP_k, MAP_x0),
                                 #tt_sigmoid(f_temp, MAT_k, MAT_x0),
                                 tt_sigmoid(f_sw, SW_k, SW_x0),
                                 tt_sigmoid(f_mort, - mort_k, mort_x0),
                                 tt_sigmoid(f_exc, - ex_k, ex_x0)])

        #prediction = tt.clip(prediction, -100, 0.8)
        # calculate the error between observed and predicted burnt area
        error = pm3.TruncatedNormal('error', mu = prediction, sd = sigma, observed=fd['TreeCover'].values,
                                    lower = 0.0, upper = 0.81)
         # set the step-method (criteria algorithm for moving around information space)
        step = pm3.Metropolis()

        # do the sampling
        mcmc_traces = pm3.sample(nIterations, step=step, chains = nChains)

    #pm3.traceplot(mcmc_traces);
    varnames = mcmc_traces.varnames

    vals = [cutLastX(i, mcmc_traces) for i in varnames]

    vals = pd.DataFrame(np.array(vals).T, columns=varnames)
    out_fname = param_outpath + '_' + fire_dataset + '_' + pr_dataset + '_' + drough_var + '.csv'
    vals.to_csv(out_fname, index=False)
    #plt.plot(vals["MAP_x0"])
    return mcmc_traces

```

```python
for fire_d in ["GFED_four_s"]:
    for pr_d in ['GPCC', 'CRU', 'CMORPH']:
        for dr_v in ['MADD', 'MADM', 'MConc', 'MDDM']:
            mcmc_traces = run_pr_droughtVar(fire_d, pr_d, dr_v)
```

    INFO (theano.gof.compilelock): Waiting for existing lock by process '32682' (I am process '602')
    INFO (theano.gof.compilelock): To manually release the lock, delete /users/global/doukel/.theano/compiledir_Linux-3.19-fc20.x86_64-x86_64-with-fedora-20-Heisenbug-x86_64-3.7.3-64/lock_dir
    Multiprocess sampling (10 chains in 4 jobs)
    CompoundStep
    >Metropolis: [sigma]
    >Metropolis: [q10_minTemp]
    >Metropolis: [q10_maxTemp]
    >Metropolis: [max_minTemp]
    >Metropolis: [min_maxTemp]
    >Metropolis: [p_drought]
    >Metropolis: [k_sheep]
    >Metropolis: [k_goat]
    >Metropolis: [k_cattle]
    >Metropolis: [k_buffalo]
    >Metropolis: [k_pop]
    >Metropolis: [p_fire]
    >Metropolis: [trans_d]
    >Metropolis: [v_pas]
    >Metropolis: [v_crop]
    >Metropolis: [v_sheep]
    >Metropolis: [v_goat]
    >Metropolis: [v_cattle]
    >Metropolis: [v_buffalo]
    >Metropolis: [v_pop]
    >Metropolis: [v_minTemp]
    >Metropolis: [v_maxTemp]
    >Metropolis: [v_drought]
    >Metropolis: [max_T]
    >Metropolis: [ex_k]
    >Metropolis: [ex_x0]
    >Metropolis: [mort_k]
    >Metropolis: [mort_x0]
    >Metropolis: [SW_k]
    >Metropolis: [SW_x0]
    >Metropolis: [MAT_k]
    >Metropolis: [MAT_x0]
    >Metropolis: [MAP_k]
    >Metropolis: [MAP_x0]
    Sampling 10 chains: 100%|██████████| 205000/205000 [26:49:55<00:00,  1.34draws/s]
    The gelman-rubin statistic is larger than 1.4 for some parameters. The sampler did not converge.
    The estimated number of effective samples is smaller than 200 for some parameters.
    Multiprocess sampling (10 chains in 4 jobs)
    CompoundStep
    >Metropolis: [sigma]
    >Metropolis: [q10_minTemp]
    >Metropolis: [q10_maxTemp]
    >Metropolis: [max_minTemp]
    >Metropolis: [min_maxTemp]
    >Metropolis: [p_drought]
    >Metropolis: [k_sheep]
    >Metropolis: [k_goat]
    >Metropolis: [k_cattle]
    >Metropolis: [k_buffalo]
    >Metropolis: [k_pop]
    >Metropolis: [p_fire]
    >Metropolis: [trans_d]
    >Metropolis: [v_pas]
    >Metropolis: [v_crop]
    >Metropolis: [v_sheep]
    >Metropolis: [v_goat]
    >Metropolis: [v_cattle]
    >Metropolis: [v_buffalo]
    >Metropolis: [v_pop]
    >Metropolis: [v_minTemp]
    >Metropolis: [v_maxTemp]
    >Metropolis: [v_drought]
    >Metropolis: [max_T]
    >Metropolis: [ex_k]
    >Metropolis: [ex_x0]
    >Metropolis: [mort_k]
    >Metropolis: [mort_x0]
    >Metropolis: [SW_k]
    >Metropolis: [SW_x0]
    >Metropolis: [MAT_k]
    >Metropolis: [MAT_x0]
    >Metropolis: [MAP_k]
    >Metropolis: [MAP_x0]
    Sampling 10 chains: 100%|██████████| 205000/205000 [27:22:14<00:00,  1.30draws/s]
    The gelman-rubin statistic is larger than 1.2 for some parameters.
    The estimated number of effective samples is smaller than 200 for some parameters.
    Multiprocess sampling (10 chains in 4 jobs)
    CompoundStep
    >Metropolis: [sigma]
    >Metropolis: [q10_minTemp]
    >Metropolis: [q10_maxTemp]
    >Metropolis: [max_minTemp]
    >Metropolis: [min_maxTemp]
    >Metropolis: [p_drought]
    >Metropolis: [k_sheep]
    >Metropolis: [k_goat]
    >Metropolis: [k_cattle]
    >Metropolis: [k_buffalo]
    >Metropolis: [k_pop]
    >Metropolis: [p_fire]
    >Metropolis: [trans_d]
    >Metropolis: [v_pas]
    >Metropolis: [v_crop]
    >Metropolis: [v_sheep]
    >Metropolis: [v_goat]
    >Metropolis: [v_cattle]
    >Metropolis: [v_buffalo]
    >Metropolis: [v_pop]
    >Metropolis: [v_minTemp]
    >Metropolis: [v_maxTemp]
    >Metropolis: [v_drought]
    >Metropolis: [max_T]
    >Metropolis: [ex_k]
    >Metropolis: [ex_x0]
    >Metropolis: [mort_k]
    >Metropolis: [mort_x0]
    >Metropolis: [SW_k]
    >Metropolis: [SW_x0]
    >Metropolis: [MAT_k]
    >Metropolis: [MAT_x0]
    >Metropolis: [MAP_k]
    >Metropolis: [MAP_x0]
    Sampling 10 chains: 100%|██████████| 205000/205000 [26:49:54<00:00,  1.34draws/s]
    The gelman-rubin statistic is larger than 1.4 for some parameters. The sampler did not converge.
    The estimated number of effective samples is smaller than 200 for some parameters.
    Multiprocess sampling (10 chains in 4 jobs)
    CompoundStep
    >Metropolis: [sigma]
    >Metropolis: [q10_minTemp]
    >Metropolis: [q10_maxTemp]
    >Metropolis: [max_minTemp]
    >Metropolis: [min_maxTemp]
    >Metropolis: [p_drought]
    >Metropolis: [k_sheep]
    >Metropolis: [k_goat]
    >Metropolis: [k_cattle]
    >Metropolis: [k_buffalo]
    >Metropolis: [k_pop]
    >Metropolis: [p_fire]
    >Metropolis: [trans_d]
    >Metropolis: [v_pas]
    >Metropolis: [v_crop]
    >Metropolis: [v_sheep]
    >Metropolis: [v_goat]
    >Metropolis: [v_cattle]
    >Metropolis: [v_buffalo]
    >Metropolis: [v_pop]
    >Metropolis: [v_minTemp]
    >Metropolis: [v_maxTemp]
    >Metropolis: [v_drought]
    >Metropolis: [max_T]
    >Metropolis: [ex_k]
    >Metropolis: [ex_x0]
    >Metropolis: [mort_k]
    >Metropolis: [mort_x0]
    >Metropolis: [SW_k]
    >Metropolis: [SW_x0]
    >Metropolis: [MAT_k]
    >Metropolis: [MAT_x0]
    >Metropolis: [MAP_k]
    >Metropolis: [MAP_x0]
    Sampling 10 chains: 100%|██████████| 205000/205000 [27:22:45<00:00,  1.31draws/s]
    The gelman-rubin statistic is larger than 1.4 for some parameters. The sampler did not converge.
    The estimated number of effective samples is smaller than 200 for some parameters.
    Multiprocess sampling (10 chains in 4 jobs)
    CompoundStep
    >Metropolis: [sigma]
    >Metropolis: [q10_minTemp]
    >Metropolis: [q10_maxTemp]
    >Metropolis: [max_minTemp]
    >Metropolis: [min_maxTemp]
    >Metropolis: [p_drought]
    >Metropolis: [k_sheep]
    >Metropolis: [k_goat]
    >Metropolis: [k_cattle]
    >Metropolis: [k_buffalo]
    >Metropolis: [k_pop]
    >Metropolis: [p_fire]
    >Metropolis: [trans_d]
    >Metropolis: [v_pas]
    >Metropolis: [v_crop]
    >Metropolis: [v_sheep]
    >Metropolis: [v_goat]
    >Metropolis: [v_cattle]
    >Metropolis: [v_buffalo]
    >Metropolis: [v_pop]
    >Metropolis: [v_minTemp]
    >Metropolis: [v_maxTemp]
    >Metropolis: [v_drought]
    >Metropolis: [max_T]
    >Metropolis: [ex_k]
    >Metropolis: [ex_x0]
    >Metropolis: [mort_k]
    >Metropolis: [mort_x0]
    >Metropolis: [SW_k]
    >Metropolis: [SW_x0]
    >Metropolis: [MAT_k]
    >Metropolis: [MAT_x0]
    >Metropolis: [MAP_k]
    >Metropolis: [MAP_x0]
    Sampling 10 chains: 100%|██████████| 205000/205000 [26:50:25<00:00,  1.40draws/s]
    The gelman-rubin statistic is larger than 1.4 for some parameters. The sampler did not converge.
    The estimated number of effective samples is smaller than 200 for some parameters.
    Multiprocess sampling (10 chains in 4 jobs)
    CompoundStep
    >Metropolis: [sigma]
    >Metropolis: [q10_minTemp]
    >Metropolis: [q10_maxTemp]
    >Metropolis: [max_minTemp]
    >Metropolis: [min_maxTemp]
    >Metropolis: [p_drought]
    >Metropolis: [k_sheep]
    >Metropolis: [k_goat]
    >Metropolis: [k_cattle]
    >Metropolis: [k_buffalo]
    >Metropolis: [k_pop]
    >Metropolis: [p_fire]
    >Metropolis: [trans_d]
    >Metropolis: [v_pas]
    >Metropolis: [v_crop]
    >Metropolis: [v_sheep]
    >Metropolis: [v_goat]
    >Metropolis: [v_cattle]
    >Metropolis: [v_buffalo]
    >Metropolis: [v_pop]
    >Metropolis: [v_minTemp]
    >Metropolis: [v_maxTemp]
    >Metropolis: [v_drought]
    >Metropolis: [max_T]
    >Metropolis: [ex_k]
    >Metropolis: [ex_x0]
    >Metropolis: [mort_k]
    >Metropolis: [mort_x0]
    >Metropolis: [SW_k]
    >Metropolis: [SW_x0]
    >Metropolis: [MAT_k]
    >Metropolis: [MAT_x0]
    >Metropolis: [MAP_k]
    >Metropolis: [MAP_x0]
    Sampling 10 chains: 100%|██████████| 205000/205000 [26:54:00<00:00,  1.33draws/s]
    The gelman-rubin statistic is larger than 1.4 for some parameters. The sampler did not converge.
    The estimated number of effective samples is smaller than 200 for some parameters.
    Multiprocess sampling (10 chains in 4 jobs)
    CompoundStep
    >Metropolis: [sigma]
    >Metropolis: [q10_minTemp]
    >Metropolis: [q10_maxTemp]
    >Metropolis: [max_minTemp]
    >Metropolis: [min_maxTemp]
    >Metropolis: [p_drought]
    >Metropolis: [k_sheep]
    >Metropolis: [k_goat]
    >Metropolis: [k_cattle]
    >Metropolis: [k_buffalo]
    >Metropolis: [k_pop]
    >Metropolis: [p_fire]
    >Metropolis: [trans_d]
    >Metropolis: [v_pas]
    >Metropolis: [v_crop]
    >Metropolis: [v_sheep]
    >Metropolis: [v_goat]
    >Metropolis: [v_cattle]
    >Metropolis: [v_buffalo]
    >Metropolis: [v_pop]
    >Metropolis: [v_minTemp]
    >Metropolis: [v_maxTemp]
    >Metropolis: [v_drought]
    >Metropolis: [max_T]
    >Metropolis: [ex_k]
    >Metropolis: [ex_x0]
    >Metropolis: [mort_k]
    >Metropolis: [mort_x0]
    >Metropolis: [SW_k]
    >Metropolis: [SW_x0]
    >Metropolis: [MAT_k]
    >Metropolis: [MAT_x0]
    >Metropolis: [MAP_k]
    >Metropolis: [MAP_x0]
    Sampling 10 chains: 100%|██████████| 205000/205000 [27:02:41<00:00,  1.35draws/s]
    The gelman-rubin statistic is larger than 1.4 for some parameters. The sampler did not converge.
    The estimated number of effective samples is smaller than 200 for some parameters.
    Multiprocess sampling (10 chains in 4 jobs)
    CompoundStep
    >Metropolis: [sigma]
    >Metropolis: [q10_minTemp]
    >Metropolis: [q10_maxTemp]
    >Metropolis: [max_minTemp]
    >Metropolis: [min_maxTemp]
    >Metropolis: [p_drought]
    >Metropolis: [k_sheep]
    >Metropolis: [k_goat]
    >Metropolis: [k_cattle]
    >Metropolis: [k_buffalo]
    >Metropolis: [k_pop]
    >Metropolis: [p_fire]
    >Metropolis: [trans_d]
    >Metropolis: [v_pas]
    >Metropolis: [v_crop]
    >Metropolis: [v_sheep]
    >Metropolis: [v_goat]
    >Metropolis: [v_cattle]
    >Metropolis: [v_buffalo]
    >Metropolis: [v_pop]
    >Metropolis: [v_minTemp]
    >Metropolis: [v_maxTemp]
    >Metropolis: [v_drought]
    >Metropolis: [max_T]
    >Metropolis: [ex_k]
    >Metropolis: [ex_x0]
    >Metropolis: [mort_k]
    >Metropolis: [mort_x0]
    >Metropolis: [SW_k]
    >Metropolis: [SW_x0]
    >Metropolis: [MAT_k]
    >Metropolis: [MAT_x0]
    >Metropolis: [MAP_k]
    >Metropolis: [MAP_x0]
    Sampling 10 chains: 100%|██████████| 205000/205000 [26:50:41<00:00,  1.36draws/s]
    The gelman-rubin statistic is larger than 1.4 for some parameters. The sampler did not converge.
    The estimated number of effective samples is smaller than 200 for some parameters.
    Multiprocess sampling (10 chains in 4 jobs)
    CompoundStep
    >Metropolis: [sigma]
    >Metropolis: [q10_minTemp]
    >Metropolis: [q10_maxTemp]
    >Metropolis: [max_minTemp]
    >Metropolis: [min_maxTemp]
    >Metropolis: [p_drought]
    >Metropolis: [k_sheep]
    >Metropolis: [k_goat]
    >Metropolis: [k_cattle]
    >Metropolis: [k_buffalo]
    >Metropolis: [k_pop]
    >Metropolis: [p_fire]
    >Metropolis: [trans_d]
    >Metropolis: [v_pas]
    >Metropolis: [v_crop]
    >Metropolis: [v_sheep]
    >Metropolis: [v_goat]
    >Metropolis: [v_cattle]
    >Metropolis: [v_buffalo]
    >Metropolis: [v_pop]
    >Metropolis: [v_minTemp]
    >Metropolis: [v_maxTemp]
    >Metropolis: [v_drought]
    >Metropolis: [max_T]
    >Metropolis: [ex_k]
    >Metropolis: [ex_x0]
    >Metropolis: [mort_k]
    >Metropolis: [mort_x0]
    >Metropolis: [SW_k]
    >Metropolis: [SW_x0]
    >Metropolis: [MAT_k]
    >Metropolis: [MAT_x0]
    >Metropolis: [MAP_k]
    >Metropolis: [MAP_x0]
    Sampling 10 chains: 100%|██████████| 205000/205000 [28:22:46<00:00,  1.24draws/s]
    The gelman-rubin statistic is larger than 1.4 for some parameters. The sampler did not converge.
    The estimated number of effective samples is smaller than 200 for some parameters.
    Multiprocess sampling (10 chains in 4 jobs)
    CompoundStep
    >Metropolis: [sigma]
    >Metropolis: [q10_minTemp]
    >Metropolis: [q10_maxTemp]
    >Metropolis: [max_minTemp]
    >Metropolis: [min_maxTemp]
    >Metropolis: [p_drought]
    >Metropolis: [k_sheep]
    >Metropolis: [k_goat]
    >Metropolis: [k_cattle]
    >Metropolis: [k_buffalo]
    >Metropolis: [k_pop]
    >Metropolis: [p_fire]
    >Metropolis: [trans_d]
    >Metropolis: [v_pas]
    >Metropolis: [v_crop]
    >Metropolis: [v_sheep]
    >Metropolis: [v_goat]
    >Metropolis: [v_cattle]
    >Metropolis: [v_buffalo]
    >Metropolis: [v_pop]
    >Metropolis: [v_minTemp]
    >Metropolis: [v_maxTemp]
    >Metropolis: [v_drought]
    >Metropolis: [max_T]
    >Metropolis: [ex_k]
    >Metropolis: [ex_x0]
    >Metropolis: [mort_k]
    >Metropolis: [mort_x0]
    >Metropolis: [SW_k]
    >Metropolis: [SW_x0]
    >Metropolis: [MAT_k]
    >Metropolis: [MAT_x0]
    >Metropolis: [MAP_k]
    >Metropolis: [MAP_x0]
    Sampling 10 chains: 100%|██████████| 205000/205000 [28:02:16<00:00,  1.25draws/s]
    The gelman-rubin statistic is larger than 1.4 for some parameters. The sampler did not converge.
    The estimated number of effective samples is smaller than 200 for some parameters.
    Multiprocess sampling (10 chains in 4 jobs)
    CompoundStep
    >Metropolis: [sigma]
    >Metropolis: [q10_minTemp]
    >Metropolis: [q10_maxTemp]
    >Metropolis: [max_minTemp]
    >Metropolis: [min_maxTemp]
    >Metropolis: [p_drought]
    >Metropolis: [k_sheep]
    >Metropolis: [k_goat]
    >Metropolis: [k_cattle]
    >Metropolis: [k_buffalo]
    >Metropolis: [k_pop]
    >Metropolis: [p_fire]
    >Metropolis: [trans_d]
    >Metropolis: [v_pas]
    >Metropolis: [v_crop]
    >Metropolis: [v_sheep]
    >Metropolis: [v_goat]
    >Metropolis: [v_cattle]
    >Metropolis: [v_buffalo]
    >Metropolis: [v_pop]
    >Metropolis: [v_minTemp]
    >Metropolis: [v_maxTemp]
    >Metropolis: [v_drought]
    >Metropolis: [max_T]
    >Metropolis: [ex_k]
    >Metropolis: [ex_x0]
    >Metropolis: [mort_k]
    >Metropolis: [mort_x0]
    >Metropolis: [SW_k]
    >Metropolis: [SW_x0]
    >Metropolis: [MAT_k]
    >Metropolis: [MAT_x0]
    >Metropolis: [MAP_k]
    >Metropolis: [MAP_x0]
    Sampling 10 chains: 100%|██████████| 205000/205000 [28:01:50<00:00,  1.25draws/s]
    The gelman-rubin statistic is larger than 1.4 for some parameters. The sampler did not converge.
    The estimated number of effective samples is smaller than 200 for some parameters.
    Multiprocess sampling (10 chains in 4 jobs)
    CompoundStep
    >Metropolis: [sigma]
    >Metropolis: [q10_minTemp]
    >Metropolis: [q10_maxTemp]
    >Metropolis: [max_minTemp]
    >Metropolis: [min_maxTemp]
    >Metropolis: [p_drought]
    >Metropolis: [k_sheep]
    >Metropolis: [k_goat]
    >Metropolis: [k_cattle]
    >Metropolis: [k_buffalo]
    >Metropolis: [k_pop]
    >Metropolis: [p_fire]
    >Metropolis: [trans_d]
    >Metropolis: [v_pas]
    >Metropolis: [v_crop]
    >Metropolis: [v_sheep]
    >Metropolis: [v_goat]
    >Metropolis: [v_cattle]
    >Metropolis: [v_buffalo]
    >Metropolis: [v_pop]
    >Metropolis: [v_minTemp]
    >Metropolis: [v_maxTemp]
    >Metropolis: [v_drought]
    >Metropolis: [max_T]
    >Metropolis: [ex_k]
    >Metropolis: [ex_x0]
    >Metropolis: [mort_k]
    >Metropolis: [mort_x0]
    >Metropolis: [SW_k]
    >Metropolis: [SW_x0]
    >Metropolis: [MAT_k]
    >Metropolis: [MAT_x0]
    >Metropolis: [MAP_k]
    >Metropolis: [MAP_x0]
    Sampling 10 chains: 100%|██████████| 205000/205000 [28:20:03<00:00,  1.35draws/s]
    The gelman-rubin statistic is larger than 1.4 for some parameters. The sampler did not converge.
    The estimated number of effective samples is smaller than 200 for some parameters.

```python
for fire_d in ["GFED_four_s"]:#, "GFED_four", "MCD_forty_five", "meris", "MODIS"]:
    for pr_d in ['MSWEP']:#, 'GPCC', 'CRU', 'CMORPH']:
        for dr_v in ['MADD']:#, 'MADM', 'MConc', 'MDDM']:
            mcmc_traces = run_pr_droughtVar(fire_d, pr_d, dr_v)
```

### 2.3.2 Likelihood definition

For the sake of simplicity (and because I don't really know any better), we define the model error as normally distributed (i.i.d.) although it most likely isn't. We could make this more complicated later by defining the error as heteroscedastic, but I wouldn't bother with that until we have some idea of the convergence. We're describing the error (observations minus model predictions) as follows:

\begin{eqnarray}
    P(X|\beta) &=& \mathcal{N}(F_{burn}, \sigma) \\[1em]
    \mathcal{N}(F_{burn}, \sigma) &=& \frac{N}{\sigma\sqrt{2\pi}}\exp\left\{\sum_{i=1}^{N}\left(\frac{y_i - F_{burn, i}}{\sigma_i}\right)^2\right\}
\end{eqnarray}

where $y_i$ is a set of observations we're attempting to optimise on. Below is the code that describes the above:

### 2.3.3 Posterior sampling

Because it is nigh impossible to determine the posterior solution analytically we will instead sample the information space to **infer** the posterior solutions for each of the model parameters. In this case we are using a Metropolis-Hasting step MCMC.

I've tried using No-U-Turn (NUTS) sampling (which is the new kid on the block), but there are issues with it's current implementation in pymc3 (see github repo issues). Can use it once problems are ironed out - but TBH it doesn't matter if we're getting a reasonable convergence.

```python
#pm3.traceplot(mcmc_traces);
```

The iterations at the start are just letting the optimization settle. So we will only sample to last 5% of iterations for futher analysis. Here, exporting to netcdf for others to do their own analysis

```python
print(param_outpath + '.csv')
```

```python
def cutLastX(varname, ncut = 50):
    vals = mcmc_traces.get_values(varname)
    def subcut(vals, r, ncut = 50):
        cut_np = (r+1) * round(len(vals)/nChains)
        ncut = round(len(vals) * ncut / (nChains *100))
        return vals[(cut_np - ncut):cut_np]
    vals = [subcut(vals, r) for r in range(nChains)]
    return np.array(vals).flatten()

varnames = mcmc_traces.varnames

vals = [cutLastX(i) for i in varnames]

vals = pd.DataFrame(np.array(vals).T, columns=varnames)
vals.to_csv(param_outpath + '.csv', index=False)

vals.head()
plt.plot(vals["mort_x0"])
```

## Plotting

Let look at the pdf of the last 5% of iterations for each parameter

```python
variables2Plot = varnames

nvar = len(variables2Plot)
nprow = 4
npcol = np.ceil(nvar / nprow)

plt.figure(figsize=(5*nprow,5 * npcol))
def plotVar(var1, pn):
    plt.subplot(npcol, nprow, pn)
    param = vals[var1]

    hist, bins = np.histogram(param, bins=50)
    hist = 100.0 * hist / np.sum(hist)
    bins = bins[1:] - np.diff(bins)/2
    plt.plot(bins, hist)
    plt.xlabel(var1)

pn = 0
for i in variables2Plot:
    pn = pn + 1
    plotVar(i, pn)
```

And what to the sigmoids look like?

```python
from pdb import set_trace as browser

def pltVsFire(x, xlab, pnt = 'o', *args, **kw):
    plt.plot(x, fd.fire, pnt, alpha = 0.03, *args, **kw)
    plt.xlabel(xlab)

def np_sigmoid(x, k, x0):
    """
    Sigmoid function to describe limitation using tensor
    """
    return 1.0/(1.0 + np.exp(-k*(x - x0)))

def returnSigmoid(x, k, x0):
    return np_sigmoid(x, k, x0)

def meanParam(x, x0, k, kmult = 1.0):
    x0 = np.mean(vals[x0])
    k  = np.mean(vals[k]) * kmult

    return returnSigmoid(x, k, x0)

def randomParam(x, x0, k, kmult = 1.0, size = 100):
    ps = np.random.choice(vals.shape[0], size = size, replace = False)
    return [returnSigmoid(x, vals[k][i] * kmult, vals[x0][i]) for i in ps]
```

```python
plt.figure(figsize=(10, 10))

####################
## Fuel           ##
####################
plt.subplot(2, 2, 1)
## scatter plot
fp = np.mean(vals['fuel_pw'])
fg = np.mean(vals['fuel_pg'])
cNPP = 1.0#np.mean(vals['cNPP'])

f_fuel = fuel_load(fd["vegCover"].values, fd["alphaMax"].values, fp, fg)
print(f_fuel.max())
pltVsFire(f_fuel, "NPP (g/m$^2$)", 'go')

## Line of best fit
#Fuel = np.arange(-6, fd.fuel.max(), 0.01)
Fuel = np.arange(0, f_fuel.max(), 0.01)
r_fuel = randomParam(Fuel, 'fuel_x0', 'fuel_k')
#NPP = np.exp(Fuel)
for r in r_fuel: plt.plot(Fuel, r, 'k', alpha=.01)

## cfg plot
#plt.xscale('log')
#plt.xlim([0.5, fd.NPP.max()])

####################
## Moisture       ##
####################
plt.subplot(2, 2, 2)
## scatter plot
Fmax = np.mean(vals['max_f'])
cM = np.mean(vals['cM'])
cMT = np.mean(vals['cMT'])
f_moisture = moisture(fd["alpha"].values, fd["emc"].values, fd["treecover"].values, cM, cMT)
pltVsFire(f_moisture , "Moisture = $\\alpha$ + M $\cdot$ EMC",'bo')

## Line of best fit
mst = np.arange(0.0, f_moisture.max(), 0.05)
r_moisture = randomParam(mst, 'moisture_x0', 'moisture_k', -1.0)
for r in r_moisture: plt.plot(mst, r, 'k', alpha=.01)

plt.ylim(0, 1)

####################
## Igntions       ##
####################
plt.subplot(2, 2, 3)
## scatter plot
cP  = np.mean(vals['cP1'])
cC  = np.mean(vals['cC' ])
cD1 = np.mean(vals['cD1'])
igniteMax = 10

f_ignite = ignition(fd["lightning_ignitions"].values, \
                    fd["cropland"].values, \
                    fd["pasture"].values, \
                    fd["population_density"].values, \
                    cC, cP, cD1)
#f_ignite = np.exp(f_ignite)
pltVsFire(f_ignite, "Ignitions events = Lightn + P $\cdot$ Pop Dens + D1 $\cdot$ Pasture")

## Line of best fit
Ignite = np.arange(0.0, igniteMax, 0.1)
#Ignite = np.exp(Ignite)
#=Ignite = np.arange(0.0, 10000, 1)
r_Ignite = randomParam(Ignite, 'igntions_x0', 'igntions_k')
#yay = returnSigmoid(Ignite, 10, 1.0)
#Ignite = np.exp(Ignite)
for r in r_Ignite: plt.plot(Ignite, r, 'k', alpha=.01)

#plt.plot(Ignite, yay, 'red')

## cfg plot
#plt.xscale('log')
plt.xlim(0, igniteMax)

####################
## Suppression    ##
####################
plt.subplot(2, 2, 4)
#scatter plot
cP2 = np.mean(vals['cP2'])
cD2 = np.mean(vals['cD2'])
maxF = np.mean(vals['max_f'])
f_suppression = maxF * supression(fd["cropland"].values, \
                           fd["cropland"].values, \
                           fd["population_density"].values, \
                           cP2,
                           cD2)

pltVsFire(f_suppression, "Suppression = Cropland + D2 $\cdot$ Pop den")

# Line of best fit
Suppress = np.arange(0, 100, 0.01)
r_suppression = randomParam(Suppress, 'suppression_x0', 'suppression_k', -1.0)
for r in r_suppression: plt.plot(Suppress, r * Fmax, 'k', alpha=.01)

plt.xlim(0, 100)
plt.ylim(0, 1)
```

```python

```

```python

```


# 8.3 Damage function within a fininite element solver
@todo - this notebook must be updated - currently not running

```python
%matplotlib inline
import sys
sys.path = ['/home/rch/git/bmcs'] + sys.path
import sympy as sp
import numpy as np
import matplotlib.pyplot as plt
```

## Define the damage functions

\begin{align}
1 - \frac{f_t}{E \varepsilon} \exp(-\frac{f_t}{G_f} (\varepsilon - \varepsilon_0) L_s )
\end{align}

```python
from ibvpy.mats.mats_damage_fn import \
    IDamageFn, LiDamageFn, JirasekDamageFn, AbaqusDamageFn,\
    PlottableFn, DamageFn, GfDamageFn
from traits.api import Float, Property
from traitsui.api import View, Item, VGroup, UItem
```

```python
f_t = 2.4
G_f = 0.090
E = 30000.0
```

```python
omega_fn = GfDamageFn(G_f=G_f, f_t=f_t, L_s=1)
```

```python
eps = np.array([0.00013033], dtype=np.float_)
omega_fn.trait_set(L_s = 10)
omega = omega_fn(eps)
sig = (1 - omega) * E * eps
eps, omega, sig, omega_fn.f_t, omega_fn.eps_0, omega_fn.L_s, omega_fn.G_f
```

```python
ax = plt.subplot(111)
omega_fn.plot(ax)
```

```python

```

## Nonlinear solver

```python
eps_0 = f_t / E
eps_ch = G_f / f_t
L_ch = E * G_f / f_t**2

print('eps_0', eps_0)
print( 'L_ch', L_ch)

L = 200.0
u_max = 0.2
eps_max = eps_ch
eps = np.linspace(0, eps_max, 4)

omega_fn_abaqus = AbaqusDamageFn(s_0=eps_0, s_u=0.03)

n_T = 50
K_max = 200

for N in [2,4,6]:
    sig_t = []
    eps_t = []
    L_el = (N - 1.0) / N * L
    L_s = 10#1.0 / N * L
    eps_s = 0.0
    eps_s_arr = np.array([eps_s], dtype=np.float_)
    u_t = np.linspace(0, u_max, n_T)
    #omega_fn.L_s = L_s
    print('elem size', L_s)
    for u in u_t:
        print('=== increment %g ' % u,)
        for K in range(K_max):
            omega = omega_fn(eps_s_arr)
            eps_s = eps_s_arr[0]
            u_s = eps_s * L_s
            u_el = u - u_s
            R = 1 / L_el * (u - eps_s_arr * L_s) - \
                (1 - omega) * eps_s_arr
            if np.fabs(R) < 1e-8:
                print('converged in %d iterations' % K)
                break
            dR = -L_s / L_el + omega_fn.diff(eps_s_arr) * \
                eps_s_arr - (1 - omega_fn(eps_s_arr))
            d_eps_s = -R / dR
            eps_s_arr += d_eps_s
            if K == K_max - 1:
                raise ValueError('No convergence')
        sig = ((1.0 - omega_fn(eps_s_arr)) * E * eps_s_arr)[0]
        sig_t.append(sig)
        eps_t.append(u)
    plt.plot(eps_t, sig_t)
plt.show();
```

```python
u_t
```

```python

```

```python

```

tour6_energy/8_3_Damage function within a finite element implementation.ipynb


# **2.3 Tensile behavior of brittle-matrix composite**

# **Model 1:** Deterministic matrix strength (ACK model)

The typical shape of the stress-strain curve of the composite involves three stages:
- elastic stage which is governed by the mixture rule
- stage of matrix fragmentation
- saturated crack pattern with a linear branch

How to interpret and characterize these three distinguished phases of composite material behavior?

The ACK model developed by Aveston, Cooper and Kelly is an analytical model that represents the composite tensile response by a trilinear law as shown in the following figure. This model is based on the following assumptions:
- The bond behavior is governed by a constant frictional bond in the debonded interface
- The constitutive law for both reinforcement and matrix is assumed to be linear-elastic with brittle failure upon reaching their strengths
- Multiple cracking occurs at a constant level of applied stress, inducing a horizontal branch in the stress-strain behavior

## Stress-strain curve

The trilinear curve of ACK model represents the composite tensile response by identifying the following characteristic points:
- [$\sigma_{1}, \varepsilon_{1}$]: The inital values of stress and strain are set to zero.
- [$\sigma_{2}, \varepsilon_{2}$]: In the first stage, the matrix is uncracked and perfect bond between matrix and fabric is assumed up to the first cracking stress $\sigma_{2}$ , which is defined as
\begin{align}
\sigma_{2} = E_\mathrm{c} \varepsilon_{2}
\end{align}
where $\varepsilon_{2}$ is the composite strain value at which the matrix cracks and $E_\mathrm{c}$ is the composite stiffness.
The strain $\varepsilon_{2}$ is given as
\begin{align}
\varepsilon_{2} = \dfrac{\sigma_\mathrm{mu}}{E_\mathrm{m}}
\end{align}
where $\sigma_\mathrm{mu}$ and $E_\mathrm{m}$ are the matrix tensile strength and stiffness, respectively.
The composite stiffness has been obtained using the
[**mixture rule**](../bmcs_course/1_1_elastic_stiffness_of_the_composite.ipynb)
and can be expressed here as:
\begin{align}
E_\mathrm{c} = E_\mathrm{f} \; V_\mathrm{f} + E_\mathrm{m} \; (1 - V_\mathrm{f})
\end{align}
where $E_\mathrm{f}$ is the fiber stiffness, and $V_\mathrm{f}$ is denoting the fiber volume fraction (reinforcement ratio).

- [$\sigma_{3}, \varepsilon_{3}$]: The second stage is characterized by the crack propagation. In this phase, the load is
assumed to be constant up to the strain value $\varepsilon_{3}$ calculated as follows:
\begin{align}
\varepsilon_{3} = \dfrac{\sigma_\mathrm{mu}}{E_\mathrm{m}} \; (1 + 0.666  \alpha_\mathrm{e})
\end{align}
where where $\alpha_\mathrm{e}$ is an homogenization coefficient given as
\begin{align}
\alpha_\mathrm{e} = \dfrac{E_\mathrm{m} \; (1 - V_\mathrm{f}) }{E_\mathrm{f} \; V_\mathrm{f}}
\end{align}

- [$\sigma_{4}, \varepsilon_{4}$]: Finally, in the third stage, when the crack pattern is stabilized the load increases linearly up to the ultimate tensile stress $\sigma_4$  with a slope equal to $E_\mathrm{r}$
The ultimate tensile stress is given as
\begin{align}
\sigma_4 = \sigma_\mathrm{fu} \; V_\mathrm{f}
\end{align}
where $\sigma_\mathrm{fu}$ is the tensile strength of the fiber.
The slope $E_\mathrm{r}$ represents the effective stiffness of the reinforcement with respect to the whole cross section and is given as
\begin{align}
E_\mathrm{r} = E_\mathrm{f} \; V_\mathrm{f}
\end{align}
The composilte strain at failure $\varepsilon_{4}$ is given as
\begin{align}
\varepsilon_{4} = \varepsilon_{3} + \dfrac{\sigma_\mathrm{4} - \sigma_\mathrm{2}}{E_\mathrm{r}}
\end{align}

## Crack spacing

### Matrix cracking is like car parking ?!

Consider a process where particles (cars) are randomly introduced in a system (along the street).
They must not overlap any previously parked car. What is the average distance between two neighbouring cars?

Probabilistic analysis of the car parking problem delivers the result that the average spacing is 1.337 larger than the car length ([Wikipedia: Random sequential adsorption](https://en.wikipedia.org/wiki/Random_sequential_adsorption))

### How long is the car in a concrete tensile specimen?

The final average crack spacing $l_\mathrm{cs}$ is given as
<!-- 1.337 \; \dfrac{(1 -  V_\mathrm{f}) \; \sigma_\mathrm{mu}}{  V_\mathrm{f} \; T} \\ -->
\begin{align}
l_\mathrm{cs} &= 1.337 \; l_\mathrm{shielded} =  1.337 \; \dfrac{A_\mathrm{m} \sigma_\mathrm{mu}}{\bar{\tau}p}
\end{align}

## Examples

### **Task 1:** Evaluate the tensile stress-strain curve
Consider a steel reinforced cross section of $100 \times 100$ mm
reinforced with a rebar ($d = 16$ mm) diameter.
Plot the stress-strain curve using the ACK model:

```python
E_m = 25000 # 28000 # concrete stiffness [MPa]
E_f = 182000 # 210000 # reinforcement stiffnes [MPa]
sig_mu = 3 # 3 # concrete tensile strength [MPa]
sig_fu = 1380 # 500 # reinforcement strength [MPa]
```

Plot the composite stress-strain curve

```python
%matplotlib widget
import matplotlib.pylab as plt
fig, ax = plt.subplots(1,1,figsize=(7,3))
fig.canvas.toolbar_position = 'top'
fig.canvas.header_visible = False
for V_f in [0.01, 0.015]:
    E_c = E_m * (1 - V_f) + E_f * V_f # composite stiffness
    alpha_e = E_m * (1 - V_f) / (E_f * V_f) # homogenization coefficient
    eps_1, sig_1 = 0, 0
    eps_2 = sig_mu / E_m
    sig_2 = eps_2 * E_c
    eps_3, sig_3 = sig_mu / E_m * (1 + 0.6666 * alpha_e), sig_2
    sig_4 = sig_fu * V_f
    E_r = E_f * V_f # effective reinforcement stiffness related to the composite cross section
    eps_4 = eps_3 + (sig_4 - sig_3) / E_r
    ax.plot([eps_1, eps_2, eps_3, eps_4], [sig_1, sig_2, sig_3, sig_4], label=r'$V_\mathrm{f}$ = %g' % V_f);
    print([eps_1, eps_2, eps_3, eps_4], [sig_1, sig_2, sig_3, sig_4])
    ax.set_xlabel(r'$\varepsilon_\mathrm{c}$ [-]'); ax.set_ylabel(r'$\sigma_\mathrm{c}$ [MPa]')
    ax.plot([0,eps_4],[0,E_r*eps_4], color='black', linewidth=1, linestyle='dashed');
    ax.legend()
```

    Canvas(toolbar=Toolbar(toolitems=[('Home', 'Reset original view', 'home', 'home'), ('Back', 'Back to previous …

    [0, 0.00012, 0.0012078032967032966, 0.007038352747252748] [0, 3.1884, 3.1884, 13.8]
    [0, 0.00012, 0.0008415395604395604, 0.007221539560439561] [0, 3.2826, 3.2826, 20.7]

```python
fig.savefig('ack.pdf')
```

**Note** that the curve does not depend on the bond $\tau p$.

### **Task 2:** Evaluate the crack spacing

What does ACK model predict for a specimen with the dimensions $100 \times 100$ mm reinforced with 1% ratio.

```python
T = 395
```

```python
tau = 3.8*2
1.337 * A_c * sig_mu / T
```

    20.308860759493673

```python
1.337 * A_c * sig_mu / (T*1.5)
```

    13.539240506329113


# Simple Fortran kernel for Jupyter

## Switching compilers
`%comipler: compiler-name .file-extention`

### default compiler is intel fortran

```fortran
use, intrinsic :: iso_fortran_env
print *, compiler_version()
print *, compiler_options()
end
```

     GCC version 9.1.0
     -mtune=generic -march=x86-64 -Wall

### gfortran

```fortran
%compiler: gfortran
use, intrinsic :: iso_fortran_env
print *, compiler_version()
print *, compiler_options()
end
```

     GCC version 9.1.0
     -mtune=generic -march=x86-64 -Wall

## Change file extention
default file extention is .f90 (free format)

for FIXED FORMAT source use .f/.for

```fortran
%compiler: gfortran .f
      PARAMETER(N = 100)
      OPEN(9, FILE = 'SINCOS.XY')
      PI = 4.0 * ATAN(1.0)
      DO 10 I = 0, N
        X = 2.0 * I * PI / N
        WRITE(9, *) X, SIN(X), COS(X)
   10 CONTINUE
      STOP 'NORMAL TERMINATION'
      END
```

    STOP NORMAL TERMINATION

## Write text file
- %writefile *fn* [-a]
- %%writefile *fn* [-a]
- -a append

```fortran
%writefile: Laurence_Sterne.txt
“O Cupid! Cupid! prince of Gods and men.”
```

    write to file:Laurence_Sterne.txt

```fortran
%%writefile: Laurence_Sterne.txt -a
Laurence Sterne. (1713–1768).  A Sentimental Journey through France and Italy.
```

    append to file:Laurence_Sterne.txt

## Compile C program
- %compiler: gcc .c

```fortran
%compiler: gcc .c
#include <stdio.h>
int main(void) {
    printf("Hello world\n");
    return 0;
}
```

    Hello world

## Compile C++ program
- %compiler: g++ .c++

```fortran
%compiler: g++ .c++
#include <iostream>

using namespace std;

int main() {
    std::cout << "Hello World!" << std::endl;
    return 0;
}
```

    Hello World!

## Separate module compilation
`%module: object-file-name`

```fortran
%module: m
%fcflags: -O2
!
! The Sieve of Eratosthenes
!
module m_eratos
    implicit none
contains
    integer function nprimes(n)
        integer, intent(in) :: n
        logical, allocatable :: tab(:)
        integer :: i
        allocate(tab(2:n), source = .true.)
        do i = 2, int(sqrt(real(n)))
            if (tab(i)) tab(i**2::i) = .false.
        end do
        nprimes = count(tab)
    end function nprimes
end module m_eratos
```

    [gfort kernel] module objects created successfully: m.o

## Compiler options
`%fcflags: arg1 arg2 ...`
`%ldflags: arg1 arg2 ...`

gfortran *fcflags* source.f90 *ldflags*

to use module, put object-file-names to *fcflags*

```fortran
%fcflags: m.o
program test
    use m_eratos
    implicit none
    integer :: i
    do i = 1, 8
       write(*, *) i, 10**i, nprimes(10**i), nint(10**i / log(10.0**i))
       write(9, *) i, 10**i, nprimes(10**i), nint(10**i / log(10.0**i))
    end do
end program test
```

               1          10           4           4
               2         100          25          22
               3        1000         168         145
               4       10000        1229        1086
               5      100000        9592        8686
               6     1000000       78498       72382
               7    10000000      664579      620421
               8   100000000     5761455     5428681

# Matplotlib Ready
`%fig: args`

```python
import matplotlib.pyplot as plt
import numpy as np
_fig = plt.figure( args )
```

> ...

> ( user inputs are executed by Python's exec command )

> ...

```python
plt.show()
```

### note
I don't know why, but `plt.figure()` doesn't work properlly with `exec/eval` commands. So it is hard coded in the kernel.

### plot number of primes $\pi(n)$
$$
\large\pi(n)\simeq{n \over {\log(n)}}
$$

```fortran
%fcflags: m.o -O2
program prime
    use m_eratos
    implicit none
    integer :: i
    open(9, file = 'primes.csv')
    write(9, '(5g0)') 'n', ',', 'primes', ',', 'n/log(n)'
    do i = 2, 501
      ! write(*, *) i, ',', nprimes(i)
       write(9, '(5g0)') i, ',', nprimes(i), ',', i / log(real(i))
    end do
    stop 'normal end'
end program prime
```

    STOP normal end

```fortran
%fig:
x, y1, y2 = np.loadtxt('primes.csv', unpack = True, delimiter=',', skiprows=1)
plt.grid(which='both')
plt.plot(x, y1)
plt.plot(x, y2)
plt.title('Prime numbers', fontsize = 25)
plt.xlabel('n', fontsize = 20)
plt.ylabel('number of primes', fontsize = 20)
```

```fortran
%fig:
x, y1, y2, y3 = np.loadtxt('fort.9', unpack = True)
plt.yscale('log')
plt.grid(which='both')
plt.plot(x, y2)
plt.plot(x, y3)
plt.title('Prime number theorem', fontsize = 25)
plt.xlabel('10^x', fontsize = 20)
plt.ylabel('number of primes', fontsize = 20)
```

### subplot
sin cos curves

```fortran
%fig:figsize=(6, 2)
x, y1, y2 = np.loadtxt('SINCOS.XY', unpack = True)
plt.subplot(1, 2, 1)
plt.grid(which='both')
plt.plot(x, y1)
plt.subplot(1, 2, 2)
plt.grid(which='both')
plt.plot(x, y2)
```

### Pandas examples
DataFrame is AxesSubplot object.

Internal variable \_fig is accessible and assignable (e.g. \_fig = rhs).
However multiple assignment is not possble.

```fortran
%fig:
import pandas as pd
ax = _fig.add_subplot(1, 1, 1)

x = np.arange(100)
df = pd.DataFrame({'x': x, 'sin(x)': np.sin(x / 5)})
df.plot(ax=ax)
```

```fortran
%fig:
from pandas import *
fig, axes = plt.subplots(nrows=2, ncols=1, figsize=(9, 6))
_fig = fig
# Multiple assignment is not allowed for _fig.

df = DataFrame(np.random.randn(1000, 4))
df.plot(ax=axes[0], legend=False)
df.plot(ax=axes[1], legend=False, kind='hist')
```

## BMP example

```fortran
%module: BMP
    module m_bmp
      use, intrinsic :: iso_fortran_env
      implicit none
      type :: t_bmp_file_header
        sequence
        integer(int16) :: bfType = transfer('BM', 0_int16) ! BitMap
        integer(int32) :: bfSize          ! file size in bytes
        integer(int16) :: bfReserved1 = 0 ! always 0
        integer(int16) :: bfReserved2 = 0 ! always 0
        integer(int32) :: bfOffBits
      end type t_bmp_file_header
      !
      type :: t_bmp_info_header
        sequence
        integer(int32) :: biSize          = Z'28' ! size of bmp_info_header ; 40bytes
        integer(int32) :: biWidth
        integer(int32) :: biHeight
        integer(int16) :: biPlanes        = 1 ! always 1
        integer(int16) :: biBitCount
        integer(int32) :: biCompression   = 0 ! 0:nocompression, 1:8bitRLE, 2:4bitRLE, 3:bitfield
        integer(int32) :: biSizeImage
        integer(int32) :: biXPelsPerMeter = 3780 ! 96dpi
        integer(int32) :: biYPelsPerMeter = 3780 ! 96dpi
        integer(int32) :: biClrUsed       = 0
        integer(int32) :: biClrImportant  = 0
      end type t_bmp_info_header
      !
      type :: t_rgb
        sequence
        integer(int8) :: ib, ig, ir
      end type t_rgb
    contains
      subroutine wr(bmp, fn)
        type(t_rgb), intent(in) :: bmp(:, :)
        character(len = *), intent(in) :: fn
        type(t_bmp_file_header) :: bmp_file_header
        type(t_bmp_info_header) :: bmp_info_header
        integer :: i, nx, ny
        nx = size(bmp, 1)
        ny = size(bmp, 2)
        bmp_file_header%bfSize      = 14 + 40 + 0 + (3 * nx + mod(ny, 4)) * ny
        bmp_file_header%bfOffBits   = 14 + 40
        bmp_info_header%biWidth     = nx
        bmp_info_header%biHeight    = ny
        bmp_info_header%biBitCount  = 24
        bmp_info_header%biSizeImage = (3 * nx + mod(ny, 4)) * ny
        open(9, file = fn // '.bmp', access = 'stream', status = 'unknown')
        write(9) bmp_file_header
        write(9) bmp_info_header
        write(9) (bmp(:, i), repeat(achar(0), mod(nx, 4)), i = 1, ny)
        close(9)
      end subroutine wr
    end module m_bmp
```

    [gfort kernel] module objects created successfully: BMP.o

```fortran
%fcflags: BMP.o
    module m_mandel
      use, intrinsic :: iso_fortran_env
      implicit none
      integer, parameter :: kd = real64
      integer, parameter :: maxiter = 255
    contains
      pure elemental integer function mandel(c)
        complex(kd), intent(in) :: c
        complex(kd) :: z
        z = c
        do mandel = maxiter, 1, -1
          if (abs(z) > 2.0_kd) exit
          z = z**2 + c
        end do
      end function mandel
    end module m_mandel

    program mandelbrot
      use m_bmp
      use m_mandel
      implicit none
      complex(kd), allocatable :: c(:, :)
      type(t_rgb), allocatable :: bmp(:, :)
      integer  :: ix, iy, nx, ny
      real(kd) :: xmin, ymin, xmax, ymax
      xmin = 0.15_kd
      xmax = 0.25_kd
      ymin = 0.50_kd
      ymax = 0.60_kd
      nx = 320
      ny = 320
      allocate( c(nx, ny) )
      forall (ix = 1:nx, iy = 1:ny) c(ix, iy) = cmplx(pos(ix, nx, xmin, xmax), pos(iy, ny, ymin, ymax))
      bmp = to_rgb( mandel(c) )
      call wr(bmp, 'mandel')
      stop 'normal end'
    contains
      pure real(kd) function pos(i, n, rmin, rmax)
        integer , intent(in) :: i, n
        real(kd), intent(in) :: rmin, rmax
        pos = (rmax - rmin) / (n - 1) * i + rmin
      end function pos
      !
      pure elemental type(t_rgb) function to_rgb(k)
        integer, intent(in) :: k
        to_rgb = t_rgb(10 * k,  k, 5 * k)
      end function to_rgb
    end program mandelbrot
```

    STOP normal end

## PIL

```fortran
%fig:
from PIL import Image
im1 = Image.open('mandel.bmp')
plt.imshow(im1)
```

# Display Images   (MIME: image/xxx)
`%image: fn1 fn2 ...`

```fortran
%image: mandel.bmp
```

## Python ; limited functionality (exec line by line)
- %py:
- **print** *text* -> eval(*text*)    ; not pyhon2 nor python3 **print**

```fortran
%py:
from sympy import *
x = Symbol('x')
bernoulli_gen = x / (exp(x) - 1)
print bernoulli_gen

bernoulli_ser = series(bernoulli_gen , x, 0, 10).removeO()
btmp = Poly(bernoulli_ser, x).coeffs()
bernoulli = [0, 0, 0, 0, 0, 0]

indx = [0, 1, 2, 4, 6, 8]
for i in range(6): bernoulli[i] = factorial(indx[i]) * btmp[5 - i]
print bernoulli
```

    x/(exp(x) - 1)
    [1, -1/2, 1/6, -1/30, 1/42, -1/30]

### Bernoulli numbers
$$
B_0=1, B_1=-{1\over2}, B_2={1\over6}, B_4=-{1\over30}, B_6={1\over42}, B_8=-{1\over30}
$$