# Probabilistic Programming 3: Hidden Markov modeling
## Variational inference

In this notebook, we are looking at inference in a dynamical system.

### Preliminaries

- Goal
  - Learn to apply ForneyLab to a dynamical system.
- Materials
  - Mandatory
    - These lecture notes.
  - Optional
    - [Cheatsheets: how does Julia differ from Matlab / Python](https://docs.julialang.org/en/v1/manual/noteworthy-differences/index.html).
    - [Probabilistic Programming notebook](https://github.com/bertdv/BMLIP/tree/master/lessons/notebooks/probprog/Probabilistic-Programming.ipynb)
    - [Getting started with ForneyLab](https://biaslab.github.io/forneylab/docs/getting-started/).

```julia
# Package manager
using Pkg
Pkg.activate("workspace")
Pkg.instantiate()

include("../scripts/pp-3.jl")

using Logging; disable_logging(LogLevel(0))
using LinearAlgebra
using ForneyLab
using Plots
pyplot();
```

## Data generation

In this problem setting, data is generated in the form of states over time. For example, proteins that fold into one of several functional configurations or machine translation, where one observes writing in one language and aims to infer writing in another language (both being finite sets of symbols). We don't have perfect measurement tools and have to assume measurement noise.

We will generate a synthetic data set as follows: we pick an initial state and evolve the state using a fixed state transition matrix. Then we observe the state using a fixed observation probability, referred to as an _emission_ matrix.

```julia
# Number of states
K = 3

# Length of time-series
T = 100

# Transition matrix of latent variables
transition = [0.3  0.5  0.2;
              0.6  0.2  0.7;
              0.1  0.3  0.1]

# Emission matrix for observed variables
emission = [0.8  0.2  0.1;
            0.1  0.6  0.1;
            0.1  0.2  0.8]

# Preallocate data arrays
states = Array{Int64,1}(undef, T)
observations = Array{Int64,1}(undef, T)

# Initial state
state_0 = 1

# Keep previous state in memory
state_tmin = state_0

# Generate data for entire time-series
for t = 1:T

    # Transition from previous state
    states[t] = sample(ProbabilityDistribution(Categorical, p=transition[:, state_tmin])).nzind[1]

    # Emission of current state
    observations[t] = sample(ProbabilityDistribution(Categorical, p=emission[:, states[t]])).nzind[1]

    # Update previous state
    state_tmin = states[t]

end

# Visualization.
plot(1:T, states, color="red", label="states", ylim=(0.5, 3.5), grid=false)
scatter!(1:T, observations, color="blue", label="observations")
xlabel!("time (t)")
```

## Model Specification

The Hidden Markov model is a state-space model of the form:

$$\begin{align}
x_t \sim&\ \text{Cat}(A x_{t-1}) \\
y_t \sim&\ \text{Cat}(B x_t)
\end{align}$$

where $A$ is the state transition matrix and $B$ is the emission matrix. The states $x$ are indexed by time $t$, ranging from $0$ to $T$. The observations $y$ are indexed from $1$ to $T$.

### Joint estimation of states, transition and emission matrices

It is possible to know the transition and emission matrices; in protein folding we know exactly _how_ the protein changes state, i.e. through the signaling of another molecule, and we know the concentration of signal molecules. However, in other applications we do not necessarily know the transition matrix. We can estimate the transition matrix by letting the coefficients be latent variables and posing a prior.

We start by initializing the graph and defining the initial state to be Categorically distributed. We then define two vectors of variables, with $x$ being the linear transition based on a previous state and $y$ being the linear transition from a state to an observation. ForneyLab has a special "Transition" node that applies a linear transformation to a variable. For this to work, the states and observations have to be in a "one-hot encoding" form: $x_i = 3 \iff x_i = [0. 0. 1.]$ for $K=3$.

```julia
# Map states and observations to one-hot encodings
states_1h = one_hot.(states, K)
observations_1h = one_hot.(observations, K)
```

    100-element Array{Array{Float64,1},1}:
     [0.0, 1.0, 0.0]
     [0.0, 0.0, 1.0]
     [0.0, 0.0, 1.0]
     [1.0, 0.0, 0.0]
     [0.0, 1.0, 0.0]
     [0.0, 0.0, 1.0]
     [0.0, 0.0, 1.0]
     [1.0, 0.0, 0.0]
     [0.0, 1.0, 0.0]
     [0.0, 0.0, 1.0]
     [1.0, 0.0, 0.0]
     [1.0, 0.0, 0.0]
     [1.0, 0.0, 0.0]
     ⋮
     [1.0, 0.0, 0.0]
     [1.0, 0.0, 0.0]
     [1.0, 0.0, 0.0]
     [0.0, 0.0, 1.0]
     [0.0, 1.0, 0.0]
     [0.0, 0.0, 1.0]
     [1.0, 0.0, 0.0]
     [0.0, 0.0, 1.0]
     [0.0, 1.0, 0.0]
     [1.0, 0.0, 0.0]
     [1.0, 0.0, 0.0]
     [1.0, 0.0, 0.0]

We'll use an informative prior for the emission matrix, because we think that sensor noise is relatively low, but we'll use a less informative prior for the transition matrix, because we are not sure about process noise.

```julia
# Initialize a graph
g = FactorGraph()

# Preallocate variable arrays (one-hot encodings)
x = Vector{Variable}(undef, T)
y = Vector{Variable}(undef, T)

# Prior for initial state
@RV x_0 ~ Categorical(ones(K)/K)

# Prior for transition matrix
@RV A ~ Dirichlet(ones(K, K))

# Prior for emission matrix
@RV B ~ Dirichlet([10.0  1.0  1.0;
                    1.0 10.0  1.0;
                    1.0  1.0 10.0])

# Keep previous state in memory
x_tmin = x_0

# Iterate over time
for t = 1:T

    # State transition
    @RV x[t] ~ Transition(x_tmin, A)

    # Observation emission
    @RV y[t] ~ Transition(x[t], B)

    # Update previous state
    x_tmin = x[t]

    placeholder(y[t], :y, index=t, dims=(3,))
end;
```

Now, we construct the inference algorithm and execute it.

```julia
# Define the recognition factorization
q = RecognitionFactorization(A, B, [x_0; x], ids=[:A, :B, :X])

# Generate VMP algorithm
algo = variationalAlgorithm(q)

# Construct variational free energy evaluation code
algo_F = freeEnergyAlgorithm(q);

# Load algorithms
eval(Meta.parse(algo))
eval(Meta.parse(algo_F))

# Initialize recognition distribution marginals
marginals = Dict{Symbol, ProbabilityDistribution}()
marginals[:A] = vague(Dirichlet, (3,3))
marginals[:B] = vague(Dirichlet, (3,3))
for t = 0:T
    marginals[:x_*t] = vague(ForneyLab.Categorical, 3)
end

# Initialize data
data = Dict(:y => observations_1h)

# Number of iterations to optimize the approximation
num_iterations = 10

# Preallocate Free Energy array
F = Vector{Float64}(undef, num_iterations)

for i = 1:num_iterations

    # Update recognition factors
    stepX!(data, marginals)
    stepA!(data, marginals)
    stepB!(data, marginals)

    # Track Free Energy
    F[i] = freeEnergy(data, marginals)
end;
```

Check for convergence.

```julia
# Plot free energy
plot(1:num_iterations, F, color="black", label="", xlabel="iterations", ylabel="Free Energy")
xlims!(0, num_iterations)
```

Visualize state estimates.

```julia
# Extract state estimates
states_estimated = argmax.([mean(marginals[:x_*t]) for t = 1:T])

# Visualization
plot(1:T, states, color="red", label="states", ylim=(0.5, 3.5), grid=false, xlabel="time (t)")
plot!(1:T, states_estimated, color="green", label="inferred")
scatter!(1:T, observations, color="blue", label="observations")
```

Visualize transition matrix estimation.

```julia
# Extract state estimates
transition_estimated = mean(marginals[:A])

# Visualization
l = @layout [a b]
h1 = bar(0:9, reshape(transition, (9,)), label="true", ylims=[0., 1.])
h2 = bar(reshape(transition_estimated, (9,)), label="estimated", ylims=[0., 1.])
plot(h1, h2, layout=l, size=(1000,500))
```

Visualize emission matrix estimation.

```julia
# Extract emission estimates
emission_estimated = mean(marginals[:B])

# Visualization
l = @layout [a b]
h1 = bar(0:9, reshape(emission, (9,)), label="true", ylims=[0., 1.])
h2 = bar(reshape(emission_estimated, (9,)), label="estimated", ylims=[0., 1.])
plot(h1, h2, layout=l, size=(1000,500))
```

### Assignment: change the number of states

In this assignment you need to adjust the model to handle a different number of hidden states. Specifically, observations range over 3 states while there are 2 hidden states. The cell below generates and visualizes the new data. Take the model above and adjust it to fit to the data below.

```julia
# Number of hidden states
K = 2

# Number of observation states
L = 3

# Length of time-series
T = 100

# Transition matrix of latent variables
transition = [0.8  0.3;
              0.2  0.7]

# Emission matrix for observed variables
emission = [0.7  0.1;
            0.1  0.6;
            0.2  0.3]

# Preallocate data arrays
states = Array{Int64,1}(undef, T)
observations = Array{Int64,1}(undef, T)

# Initial state
state_0 = 1

# Keep previous state in memory
state_tmin = state_0

# Generate data for entire time-series
for t = 1:T

    # Transition from previous state
    states[t] = sample(ProbabilityDistribution(Categorical, p=transition[:, state_tmin])).nzind[1]

    # Emission of current state
    observations[t] = sample(ProbabilityDistribution(Categorical, p=emission[:, states[t]])).nzind[1]

    # Update previous state
    state_tmin = states[t]

end

# Map states and observations to one-hot encodings
states_1h = one_hot.(states, K)
observations_1h = one_hot.(observations, L)

# Visualization.
p1 = plot(1:T, states, color="red", label="states", ylim=(0.5, K+.5), xlabel="time (t)")
p2 = scatter(1:T, observations, color="blue", label="observations", ylim=(0.5, L+.5), xlabel="time (t)")
plot(p1, p2, size=(900,400))
```

```julia
### Your code here
```


# Exam <a class="tocSkip">

# Subject 1

### 10p | 1. Care este avantajul utilizarii fct softmax pe ultimul strat al RN fata de utilizarea functiei sigmoid (logistic) ? In ce situatie acest avantaj se pierde ?

$$\begin{align}softmax(L_n) = \frac{e^{L_n}}{||e^L||} \quad vs \quad  & sigmoid(z) = \frac{1}{1 + e^{-z}} \\
& z = \sum_{i}{}{w_i x_i + b} \end{align} $$

```
`` Softmax is kind of Multi Class Sigmoid, but if you see the function of Softmax, the sum of all softmax units are supposed to be 1. In sigmoid it’s not really necessary.

As told earlier, in the case of softmax, increasing the output value of one class makes the the others go down (sigma=1). So, sigmoids can probably be preferred over softmax when your outputs are independent of one another. To put it more simple, if there are multiple classes and each input can belong to exactly one class, then it absolutely makes sense to use softmax, in the other cases, sigmoid seems better.

```
source: https://www.quora.com/Why-is-it-better-to-use-Softmax-function-than-sigmoid-function

```
Sigmoid - It squashes all values between 0 and 1 and when you do so repeatedly, neuron outputs and their gradients can vanish entirely
```
source: https://codelabs.developers.google.com/codelabs/cloud-tensorflow-mnist/#7

**Softmax** takes a 2-step approach to the activation:
+ **max** - takes the largest element, that will approach 1
+ **soft** - the other elements will be divided by large numbers, so will be normalized (usually, euclidean norm) to 0 => the "soft" part maintains relative order

source: https://codelabs.developers.google.com/codelabs/cloud-tensorflow-mnist/#3

### 10p | 2. Regularizarea L1 si L2 (weight decay) au un efect diferit asupra ponderilor (weight-urilor) din retea. Explicati ce efect are L1 si ce efect are L2.

```
 L1 penalties are great for recovering sparse data, as they are computationally tractable but still capable of recovering the exact sparse solution.

 L2 penalization is preferable for data that is not at all sparse.
```
source: https://www.quora.com/What-is-the-difference-between-L1-and-L2-regularization

### 10p | 3. Algoritmul Adagrad reprezinta o imbunatatire al algoritmului SGD. Explicati in ce consta acest avantaj si explicati si dezavantajul pe care Adagrad il are (obs: este rezolvat acest dezavantaj in RMSProp)

```
One of the main disadvantages of Adagrad is that it can become
blocked on local minimum.
+ This happens since the L2 norm can only increase

```

source: http://profs.info.uaic.ro/~rbenchea/rn/curs6.pdf - pg 25/40

### 10p | 4. Dati 2 dezavantaje pe care functia sigmoid (logistic) o are fata de functia tangenta hiperbolica (tanh).

```
1. The output is always positive
2. They can output 0 when they saturate

```
source: http://profs.info.uaic.ro/~rbenchea/rn/curs7.pdf - pg 4/46
```
smaller derivatives (since > 0) -> weaker gradients

```
source: https://stats.stackexchange.com/questions/101560/tanh-activation-function-vs-sigmoid-activation-function

### 10p | 5. In algoritmul de Q-learning folosind RN este de preferat ca arhitectura retelei sa aiba la input starea agentului, iar la output cate o iesire pentru fiecare actiune. Argumentati de ce este preferata aceasta arhitectura in defavoarea celei care primeste ca input starea si actiujnea si returneaza valoarea Q(s,a).

```
smth
```

### 10p | 6. Explicati de ce la o retea restrictionata Boltzmann se ajunge in echilibru termic (distributie stationara) intr-un singur pas in faza pozitiva a antrenamentului.

```
smth
```

### 15p | 7. Date urmatoarele date de intrare: $$v^1 = (1, -1, 1, -1, 1) \ , \ v^2 = (1, 1, 1, -1, -1)$$ exemplificati care sunt ponderile intermediare si care sunt cele finale obtinute in urma antrenarii unei retele Hopfield folosind invatarea Hebbiana.

```
smth
```

### 25p | 8. Data reteaua din imagine, stabiliti care ar trebui sa fie ponderile (weight-urile) noi dupa rularea algoritmului de backprop intr-o singura iteratie, folosind inputul (2,6) ce are clasa (target) 0 si reteaua din imaginea alaturata.
+ toate functiile de activare sunt sigmoid
+ functia de eroare - cross-entropia
+ neuronii nu au bias
+ rata de invatare este: $\lambda = 0.5$

Formule utile:
+ $w_{ij}^{l} = w_{ij}^l - \eta * \frac{ \partial{c_l} }{ \partial{w_{ij}} }$
+ $\delta_i^L = y_i^L - t_i^L$, $\delta_i^L = y_i^l (1-y_i^l) * \sum_k{\delta_k^{l+1} } * w_{ik}^{l+1}$
+ $\sigma(z) = \frac{1}{1+e^{-z}}$
+ $\frac{\delta C}{\delta w_{ij}^l} = \delta_i^l * y^{l-1}$

<!--$$y = x^2 \hbox{ when $x > 2$}$$ -->

Example MathJax graph
<!--
$$ \require{enclose}
\def\uline#1#2{\enclose{updiagonalstrike}{\phantom{\Rule{#1em}{#2em}{0em}}}}
\def\dline#1#2{\enclose{downdiagonalstrike}{\phantom{\Rule{#1em}{#2em}{0em}}}}
%
\def\place#1#2#3{\smash{\rlap{\hskip{#1em}\raise{#2em}{#3}}}}
%
\hskip 1em
%
\place{0}{12}{\bullet}
\place{2}{0}{\bullet}
\place{4}{4}{\bullet}
\place{6}{0}{\bullet}
\place{8}{8}{\bullet}
\place{10}{0}{\bullet}
\place{12}{4}{\bullet}
\place{14}{0}{\bullet}
\place{16}{12}{\bullet}
%
\place{.3}{4.4}{\dline{3.6}{7.6}}
\place{2.3}{.5}{\uline{1.6}{3.6}}
\place{4.3}{.4}{\dline{1.6}{3.6}}
\place{4.3}{4.4}{\uline{3.6}{3.7}}
\place{8.3}{4.4}{\dline{3.6}{3.6}}
\place{10.3}{.5}{\uline{1.6}{3.6}}
\place{12.3}{.4}{\dline{1.6}{3.6}}
\place{12.2}{4.4}{\uline{3.7}{7.6}}
%
\place{-1}{12.5}{\frac01}
\place{1}{-.5}{\frac13}
\place{2.75}{4}{\frac12}
\place{6.5}{-.5}{\frac23}
\place{7.75}{9.5}{\frac11}
\place{9}{-.5}{\frac32}
\place{12.75}{4}{\frac21}
\place{14.5}{-.5}{\frac31}
\place{16.5}{12.5}{\frac10}
%
\hskip18em\Rule{0em}{14em}{1.5em} $$
-->

**Source**: [meta.math.stackexchange.com/questions/23055/how-can-i-draw-a-tree-like-this](http://meta.math.stackexchange.com/questions/23055/how-can-i-draw-a-tree-like-this/23066#23066)
+ Apparently, MathJax (used by Jupyter) doesn't support many LaTeX features (eg: [reddit source](https://www.reddit.com/r/IPython/comments/4lxy1l/creating_a_latex_data_table_in_jupyter/) )

```
smth
```

---

# Subject 2

1. (10p) Why is the sigmoid activation preffered over BTU (binary threshold unit) ?
2. (10p) What problem does 0-initialization generate in an MLP ? What if it was a constant ?
3. (10p) What is minibatch. Why would you use minibatch. List 1 advantage over fullbatch.
4. (10p) Explain what dropout is. Show how would you apply 0.2 dropout over 2nd layer.
5. (10p) Why choose Adam optimizer over Adagrad optimizer ?
6. (10p) Describe the vanishing gradient problem. How does relu help ?

7. (15p) Given an input image `I` and a filter `F`, apply convolutionally `F` over `I`, given the formula `F(I) = I(x-i, x-j)`.

$$
I = \begin{bmatrix}
    0 & 3 & 1 & 1 & 4 \\
    2 & 1 & 0 & 0 & 0 \\
    0 & 2 & 5 & 2 & 3 \\
    1 & 1 & 3 & 0 & 2 \\
    0 & 0 & 1 & 1 & 0
\end{bmatrix},
F = \begin{bmatrix}
    0 & 1 & 2 \\
    2 & 2 & 0 \\
    0 & 2 & 1 \\
\end{bmatrix}
$$

+ 9 convolutions to be calculated given the above I, F.

8. (25p) backprop problem

---


### 0. [準備]必要なライブラリをインポートする

```python
from sympy import *     # SymPy の基本機能
from sympy.physics.quantum import * # qapplyを含む量子計算のための基本機能
from sympy.physics.quantum.qubit import *  # 量子ビット、量子ビットの測定関連の機能
from sympy.physics.quantum.gate import *   # 基本的な量子ゲート
from sympy.physics.quantum.gate import IdentityGate as _I  # 恒等変換を _I と省略
init_printing() # Jupyter Notebook で SymPy の Tex形式出力を整形します。
%matplotlib inline
# 量子回路図を描画するための準備
get_ipython().run_line_magic('matplotlib', 'inline')
import matplotlib.pyplot as plt
from sympy.physics.quantum.circuitplot import *
```

###  １. 計算に必要な量子ビット（量子レジスタ）を準備して、その値を初期化する

計算に必要な量子ビットは、sympy.physics.quantum.qubit.Qubitクラス（実態はケット・ベクトル）を必要なビット数を
初期化して作成します。

* 例１：変数 $a$ に、２量子ビット $ \vert 00 \rangle $ を準備します。

```python
a = Qubit('00')
a # print(a)
```

* 例２：変数 $ a $ の状態ベクトルをつかって、変数 $ b $ を同じ状態ベクトルとして準備します。

```python
b = Qubit(a)
b # print(b)
```

* 例３：8量子ビットをすべて $0$ として準備します。

```python
q_8 = Qubit('0'*8)
q_8 # pprint(q_8)
```

* 例４：（後述で説明する計算方法を使いますが）計算を行って、変数 $c$ の状態ベクトルを準備します。
  　　　２量子ビットの重ね合わせ

```python
c = qapply(H(1)*H(0)*a)
c # print(c)
```

Qubitクラスには次のような関数やプロパティが定義されています。

```python
q = Qubit('000')

# サイズ（量子ビット数）を返す
print("nqubits=%d"% q.nqubits)
print("len=%d" % len(q))
print("dimension=%d" % q.dimension)
print(q.qubit_values)

q.flip(1)  # 引数で指定された位置(*)の右から2番目の量子ビットを反転します.
```

### 2. 量子計算をユニタリ行列（ゲート演算子）で記述する

作用させるユニタリ行列を標準で準備されているユニタリ行列のテンソル積で表します。

まず最初に、標準で定義されているユニタリ行列を見て見ましょう。

#### １量子ビット操作
引数に渡す数字は、作用させる量子ビットの位置を表します。

* パウリ演算子

```python
op = X(0)
represent(op, nqubits=1)
```

```python
op = Y(0)
represent(op, nqubits=1)
```

```python
op = Z(0)
represent(op, nqubits=1)
```

* アダマール（Hadamard）変換

```python
op = H(0)
represent(op, nqubits=1)
```

* $ S, S^{\dagger}, T, T^{\dagger} $ 変換

```python
op = S(0)
represent(op, nqubits=1)
```

```python
op = Dagger(S(0))
represent(op, nqubits=1)
```

```python
op = S(0)**(-1)   # S^{\dagger} と同じ
represent(op, nqubits=1)
```

```python
from sympy.physics.quantum.matrixcache import matrix_cache
matrix_cache.cache_matrix('Sdg', Matrix([[1, 0], [0, -I]]))
class Sdg(OneQubitGate): # S^{\dagger} としては、この演算子を使います。
    gate_name = u'Sdg'
    gate_name_latex = u'S^{\dagger}'
    def get_target_matrix(self, format='sympy'):
        return matrix_cache.get_matrix('Sdg', format)
represent(Sdg(0),nqubits=1)
```

```python
op = T(0)
represent(op, nqubits=1)
```

```python
op = Dagger(T(0))
represent(op, nqubits=1)
```

```python
op = T(0)**(-1)   # T^{\dagger} と同じ
represent(op, nqubits=1)
```

```python
from sympy.physics.quantum.matrixcache import matrix_cache
matrix_cache.cache_matrix('Tdg',Matrix([[1, 0], [0, exp(-I*pi/4)]]))
class Tdg(OneQubitGate): # T^{\dagger} としては、この演算子を使います。
    gate_name = u'Tdg'
    gate_name_latex = u'T^{\dagger}'
    def get_target_matrix(self, format='sympy'):
        return matrix_cache.get_matrix('Tdg', format)
represent(Tdg(0),nqubits=1)
```

#### ２量子ビット操作
引数に渡す数字は２つ、作用させる量子ビットの位置を表します。省略すると 0 のみとなるため、そのあとの計算がうまくできません。
必ず、引数を指定するようにしましょう。

* CNOT操作
次の $ 4 \times 4 $ の行列です。

$ \left( \begin{array}{cccc} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 \end{array} \right) $

```python
op=CNOT(1,0)
print("controls=%s,targets=%s,gate=%s" % (op.controls, op.targets, op.gate))  # CNOTのプロパティ
represent(op,nqubits=2) # 行列形式で直接表現する方法
```

* SWAP操作

指定された量子ビットを入れ替える操作です。

```python
op = SWAP(0,1)
represent(op,nqubits=2) # 行列形式で直接表現する方法
```

* CPHASE操作

制御Zゲート、Control-Z、CZ と呼ばれ、次の行列です。

$ \left( \begin{array}{cccc} 1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & -1 \end{array} \right) $

```python
op = CPHASE(0,1)
represent(op,nqubits=2)
```

次に、テンソル積をいくつか試してみます。

```python
Op1 = X(1)*_I(0)
represent(Op1, nqubits=2)
```

```python
# 標準の１量子ビット操作のユニタリ演算は、その掛け算の順番よりも、指定した量子ビット（引数）がポイントとなります。
Op1_ = _I(0)*X(1)
represent(Op1_, nqubits=2)
```

```python
# 順番が大切になるのは、同じ位置の量子ビットに作用する場合で、積の交換はできません。
Op2 = _I(1)*X(0)
represent(Op2, nqubits=2)
```

* Hadamard 変換（8量子ビット）

```python
def hadamard(s,n):
    h = H(s)
    for i in range(s+1,n+s):
       h = H(i)*h
    return h

h_8 = hadamard(0,8)
print(h_8)
print("\n")
pprint(h_8)    # = pretty_print(h_8)
# h8.get_target_matrix() は実装がなく、エラーになります
h_8
```

```python
h_3 = hadamard(0,3)
represent(h_3,nqubits=3)
```

#### 量子回路図

ユニタリ行列のテンソル積は、量子回路として、量子回路図で表現できます。
SymPy には、量子回路図を描画するための仕組みが備わっています。量子回路図を描いてみましょう。

```python
# そのために、まずは、必要なライブラリを読み込んで準備します。
%matplotlib inline
# 量子回路図を描画するための準備
get_ipython().run_line_magic('matplotlib', 'inline')
import matplotlib.pyplot as plt
from sympy.physics.quantum.circuitplot import *
```

CNOTゲートを描いてみましょう。

```python
CircuitPlot(CNOT(1,0), 2, labels=labeller(2,'\phi')[::-1])  # labellerでつくられる添え字の上下が逆になります
```

†（ダガー）付きのゲート演算子など、ゲートの自作もできます。

```python
CircuitPlot(Sdg(0)*Tdg(0),nqubits=1)
```

ある回路を描いてみましょう。
テンソル積は、右から順番に量子ビットに作用します。一方、ゲート回路図は左から順番になります。操作の順番に注意してください。

```python
ops = CNOT(1,0)*S(1)
CircuitPlot(ops, 2, labels=labeller(2)[::-1])  # 操作の順番に注意してください。
```

```python
ops = S(1)*H(0)*CNOT(1,0)*S(1)*H(1)
CircuitPlot(ops, 2, labels=labeller(2)[::-1])
```

（参考）Z基底での測定を描くときは、Mz を使って表現します。

```python
ops = Mz(0)*Mz(1)*S(1)*H(0)*CNOT(1,0)*S(1)*H(1)
CircuitPlot(ops, 2, labels=labeller(2)[::-1])
```

### 3. ユニタリ行列を量子ビットに作用する
ここまでのユニタリ行列の準備では、実際の行列計算は行われておりません。
SymPy の特徴である「変数を変数のまま」扱ってきました。変数は単なる記号でしかありませんでした。

実際に変数を計算するのは、 qapply() という関数で行います。計算したい記述を qapply 関数の引数に指定して計算します。

直前で回路図を描いた計算をしてみましょう。

```python
ops = S(1)*H(0)*CNOT(1,0)*S(1)*H(1)
qapply(ops*Qubit('00'))
```

次の例は、４量子ビットの重ね合わせ状態を作ります。（計算して作成します）

```python
h_4 = hadamard(0,4)
hadamard_4=qapply(h_4*Qubit('0000'))
hadamard_4
```

### 4. 測定する

ここまでの量子計算（qapply）では、量子状態のまま計算されます。
シミュレーション上の理論的な計算では、qapplyまで行えば、目的が達成することが多いです。

SymPy には、観測のためのメソッドも備わっています。
measure_all()では、引数で渡された量子ビットのすべての状態の測定確率を計算で求めて出力します。

そのほかにも幾つか観測のためのメソッドが提供されます。例えば、measure_partial()は、第一引数で指定された量子ビットに対して、第二引数で指定された部分的な振幅の測定を実行します。

```python
measure_all(hadamard_4)
```

```python
for i in range(8):
    pprint(measure_all_oneshot(hadamard_4))
```

    ❘0100⟩
    ❘1011⟩
    ❘0111⟩
    ❘1010⟩
    ❘0101⟩
    ❘1101⟩
    ❘1001⟩
    ❘0011⟩

## （例題１）Toffoli（CCX）, CCCX, CCCCX ...

```python
def CCX(c1,c2,t):  return CGateS((c1,c2),X(t))
def Toffoli(c1,c2,t):  return CGateS((c1,c2),X(t))
CircuitPlot(CCX(1,2,0),3,labels=labeller(3)[::-1])
```

```python
represent(CCX(1,2,0),nqubits=3)
```

```python
def CCCX(c1,c2,c3,t): return CGateS((c1,c2,c3),X(t))
CircuitPlot(CCCX(1,2,3,0),4,labels=labeller(4)[::-1])
```

```python
def CCCCX(c1,c2,c3,c4,t): return CGateS((c1,c2,c3,c4),X(t))
CircuitPlot(CCCCX(1,2,3,4,0),5,labels=labeller(5)[::-1])
```

## （例題２）重ね合わせ状態の中の、ある状態のみマーキングする

重ね合わせ状態の中の、ある状態 $ s_t $ のみに、−１掛ける操作（ここでは、マーキングを呼びます）を考えます。

たとえば、２量子ビットの重ね合わせ状態 $\displaystyle \frac{1}{2} \left(\ |\ 00\ \rangle\ +\ |\ 01\ \rangle\ +\ |\ 10\ \rangle\ +\ |\ 11\ \rangle\ \right) $ を考えるとき、
状態 $\;|\ 10\ \rangle\;$ をマーキングするとします。

マーキング後の状態は、$\displaystyle  \frac{1}{2} \left(\ |\ 00\ \rangle\ +\ |\ 01\ \rangle\ -\ |\ 10\ \rangle\ +\ |\ 11\ \rangle\ \right) $ となることを目指します。

### ３量子ビットのマーキング操作を試してみましょう

```python
h_3 = hadamard(0,3)
target_state_3 = qapply(h_3*Qubit('000'))          #３量子ビットの重ね合わせ状態を準備します。
def CCZ(c1,c2,t): return (H(t)*CCX(c1,c2,t)*H(t)) # ＣＣＺ演算子を定義します。
```

```python
mark_7 = CCZ(1,2,0)
qapply(mark_7*target_state_3)
```

```python
mark_6 = X(0)*CCZ(1,2,0)*X(0)
qapply(mark_6*target_state_3)
```

```python
mark_5 = X(1)*CCZ(1,2,0)*X(1)
qapply(mark_5*target_state_3)
```

```python
mark_4 = X(1)*X(0)*CCZ(1,2,0)*X(1)*X(0)
qapply(mark_4*target_state_3)
```

```python
mark_3 = X(2)*CCZ(1,2,0)*X(2)
qapply(mark_3*target_state_3)
```

```python
mark_2 = X(2)*X(0)*CCZ(1,2,0)*X(2)*X(0)
qapply(mark_2*target_state_3)
```

```python
mark_1 = X(2)*X(1)*CCZ(1,2,0)*X(2)*X(1)
qapply(mark_1*target_state_3)
```

```python
mark_0 = X(2)*X(1)*X(0)*CCZ(1,2,0)*X(2)*X(1)*X(0)
qapply(mark_0*h_3*Qubit('000'))
```

## （例題３）重ね合わせ状態の中に、マーキングした状態があるかを見る

著名な操作「Grover のアルゴリズム」を試してみましょう。

（ヒント１）平均値周りの反転操作：$\displaystyle D_{n} = H_{n} \cdot \Big( 2\ |\ 0\ \rangle\langle\ 0\ |_{n}\ -\ I_{n} \Big) \cdot H_{n} $ を使います。

（ヒント２）試行回数は、$\displaystyle \mathcal{O}(\sqrt{n}) $

```python
# d_3 = h_3 * X(0)*X(1)*X(2) * H(0)*CCX(1,2,0)*H(0) * X(0)*X(1)*X(2) * h_3    # グローバル位相（絶対位相）の差に注意
# d_3 = h_3 * X(0)*X(1)*X(2) * CGateS((1,2), Z(0)) * X(0)*X(1)*X(2) * h_3
def DOp(n): return (Qubit('0'*n)*QubitBra('0'*n)*2-_I(0))     # ゲート操作で計算するには、上記コメントのような演算になります。
d_3 = h_3 * DOp(3) * h_3    # 平均値周りの反転操作
represent(d_3,nqubits=3)
```

### ３量子ビットで状態｜７＞を探す

```python
ret1=qapply(d_3*mark_7*target_state_3)
measure_all(ret1)
```

```python
ret2=qapply(d_3*mark_7*ret1)  # シミュレーター計算ですから、上で一度観測した ret1 を使っても観測の影響なく次の計算に利用可能。
measure_all(ret2)
```

```python
over_1=qapply(d_3*mark_7*ret2)  # 試行回数が多いとどうなるでしょう。
measure_all(over_1)
```

```python
over_2=qapply(d_3*mark_7*over_1)  # 試行回数が多いとどうなるでしょう。
measure_all(over_2)
```

通しで計算すると、回路が長くなってきています。少し時間かかります。

```python
from sympy.physics.quantum.gate import gate_simp
search_7_in_3qubit = gate_simp(d_3*mark_7*d_3*mark_7)
ret3=qapply(search_7_in_3qubit*target_state_3)
ret3
```

```python
print(measure_all(ret3))
for i in range(10):
    pprint(measure_all_oneshot(ret3))
```

    [(|000>, 1/128), (|001>, 1/128), (|010>, 1/128), (|011>, 1/128), (|100>, 1/128), (|101>, 1/128), (|110>, 1/128), (|111>, 121/128)]
    ❘111⟩
    ❘111⟩
    ❘111⟩
    ❘111⟩
    ❘111⟩
    ❘111⟩
    ❘111⟩
    ❘111⟩
    ❘111⟩
    ❘111⟩

```python
d_3_gate = h_3 * X(0)*X(1)*X(2) * H(0)*CCX(1,2,0)*H(0) * X(0)*X(1)*X(2) * h_3
CircuitPlot(gate_simp(d_3_gate*mark_7*d_3_gate*mark_7*h_3),nqubits=3,labels=labeller(3)[::-1])
```

## （Homework）Toffoli 再び

### 課題１）　Toffoli ゲートを、基本的な量子ゲートだけで表してください。

```python
def ToffoliGate(q0,q1,q2):
    return T(q0)*S(q1)*CNOT(q0,q1)*Tdg(q1)*CNOT(q0,q1)\
            *H(q2)*Tdg(q1)*T(q2)*CNOT(q0,q2)*Tdg(q2)*CNOT(q1,q2)\
            *T(q2)*CNOT(q0,q2)*Tdg(q2)*CNOT(q1,q2)*H(q2)
ToffoliGate(2,1,0)
CircuitPlot(ToffoliGate(2,1,0), nqubits=3)
qapply(ToffoliGate(2,1,0)*Qubit('110'))
```

```python
qapply(ToffoliGate(2,1,0)*Qubit('010'))
```

### 課題２）　CCCX ゲートを、 Toffoli ゲートと CNOT ゲートで表してください。

```python
def CCCX_with_ancilla(q0,q1,q2,t,a0,a1,a2):
    return CNOT(q0,a0)*Toffoli(q1,a0,a1)*Toffoli(q2,a1,a2)*CNOT(a2,t)*Toffoli(q2,a1,a2)*Toffoli(q1,a0,a1)*CNOT(q0,a0)
CircuitPlot(CCCX_with_ancilla(6,5,4,3,2,1,0),nqubits=7,labels=(labeller(3,'a')+['t']+labeller(3,)))
```

```python
qapply(CCCX_with_ancilla(6,5,4,3,2,1,0)*Qubit('1111000'))
```

```python
qapply(CCCX_with_ancilla(6,5,4,3,2,1,0)*Qubit('1110000'))
```

### 付録Ａ：ブラ・ベクトル
複素共役を表すための dagger（ $ \dagger $ ） をQubitオプジェクトに作用させると、ブラ・ベクトルになります。
また、内積も計算できます。
シンボルのままで内積にするには、単にブラ・ベクトル QubitBra とケット・ベクトル Qubit を掛ける（*）だけです。
実際の値を計算するには、この内積オブジェクトのdoit()メソッドで計算します。

```python
from sympy.physics.quantum.dagger import Dagger
Dagger(q)
```

```python
type(Dagger(q))
```

    sympy.physics.quantum.qubit.QubitBra

```python
ip = Dagger(q)*q
ip
```

```python
ip.doit()
```

### 付録Ｂ：量子フーリエ変換

```python
from sympy.physics.quantum.qft import QFT
fourier_3 = QFT(0,3).decompose()
fourier_3
```

```python
figure_qft3 = CircuitPlot(fourier_3, nqubits=3)
qapply(fourier_3 * Qubit('000'))
```

## 付録Ｃ

## OpenQL 発のPythonライブラリ QuantPy を紹介します

QuantPy は、フロントエンドを SymPy を使っています。
「量子ビットの準備」「量子操作（ゲート演算子）の記述」は SymPy で行います。
「量子ビットにゲート演算子を作用」する qapply() を置き換えて動作します。

```python
from quantpy.sympy.qapply import qapply
# sympy.physics.quantum.qapply を上書きします。sympy.physics.quantum のインポートの後に読み込んでください。
```

```python
from quantpy.sympy.executor.classical_simulation_executor import ClassicalSimulationExecutor
print(qapply(H(1)*H(0)*Qubit('00'), executor=ClassicalSimulationExecutor()))
```

    (0.4999999999999999+0j)|00> + (0.4999999999999999+0j)|10> + (0.4999999999999999+0j)|01> + (0.4999999999999999+0j)|11>

```python
from quantpy.sympy.expr_extension import *
sympy_expr_add_operators()
circuit = X(0)>>X(1)>>X(2)
circuit
```

```python
def ToffoliGate2(q0,q1,q2):
    return H(q2)>>CNOT(q1,q2)>>Tdg(q2)>>CNOT(q0,q2)>>T(q2)>>\
            CNOT(q1,q2)>>Tdg(q2)>>CNOT(q0,q2)>>T(q2)>>Tdg(q1)>>H(q2)>>\
            CNOT(q0,q1)>>Tdg(q1)>>CNOT(q0,q1)>>S(q1)>>T(q0)
ToffoliGate2(2,1,0)
CircuitPlot(ToffoliGate2(2,1,0), nqubits=3)
qapply(ToffoliGate2(2,1,0)*Qubit('110'))
```

```python
qapply(ToffoliGate2(2,1,0)*Qubit('100'))
```

```python

```


```python
'''How detection efficiency impacts the fidelity of the resultant state
after several photon catalysis steps'''
import cmath, random, numpy
import functools
import  matplotlib.pyplot as plt
import sys
import os
import math
from qutip import*

from sympy import*
#from sympsi import*
from scipy import optimize

from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
import time
import math
from qutip import *
from qutip.ipynbtools import plot_animation
import numpy as np
import matplotlib.pyplot as plt
import qutip
%matplotlib inline
import matplotlib.pylab as plt
import matplotlib as mpl
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from IPython.display import display, Math, Latex
import cmath
from mpl_toolkits.axes_grid1 import AxesGrid
from scipy.special import factorial
```

```python
"""Define the operators for MZI - we will deal in Heisenberg """
T = Symbol('T')
xvec = np.arange(-30.,30.)*5./40
X,Y = np.meshgrid(xvec, xvec)  ##Some plotting params
N_dim = 35##Dimenstion of the Hilbert spac

a1 = destroy(N_dim) ##This is for single-photon field
a2 = destroy(N_dim) ##for coherent field
a3 = destroy(N_dim) ##for vacuum field
def n_choose_k(n,k):
    return factorial(n)/(factorial(n-k)*factorial(k))
def D(state,alpha):
    Rho_new=displace(N_dim,alpha)*state*displace(N_dim,alpha).dag()
    return Rho_new
'''Define a rotation in phase space, or phase shifter operation'''
def Phase(theta):
    b=-1j*theta*a1.dag()*a1;
    return b.expm()
'''Squeezing operation, inputs a density matrix and outputs the squeezed density matrix for squeezing parameter r'''
def Sq(state,r):
    Rho_new=squeeze(N_dim,r)*state*squeeze(N_dim,r).dag();
    return Rho_new
'''The function below creates a beamsplitter operation that acts on two modes.
The value for k determines what number Fock state could be filtered out of the first state
based on a single photon input for the second BS port, followed by single photon detection.'''
def BS_operator_filtering(a1, a2, k):
    theta_k = np.arctan(1/np.sqrt(k))
    T = np.sin(theta_k)*np.sin(theta_k)
    R = np.cos(theta_k)*np.cos(theta_k)
    print('I am filtering', k, 'and:', theta_k*180/math.pi)
    print('BS T is : ', T, 'and : ', R)
    b = theta_k*(tensor(a1,a2.dag()) - tensor(a1.dag(),a2))
    return b.expm()
def SSV_plus(r,alpha):
    state = ket2dm((displace(N_dim,alpha)+displace(N_dim,-alpha))*squeeze(N_dim,r)*fock(N_dim,0))
    norm_state = state/state.tr()
    return norm_state
def SSV_minus(r,alpha):
    state = ket2dm((displace(N_dim,alpha)-displace(N_dim,-alpha))*squeeze(N_dim,r)*fock(N_dim,0))
    norm_state = state/state.tr()
    return norm_state
def cat_plus(alpha):
    cat = (1/(np.sqrt(2)*np.sqrt(1+np.e**(-alpha*alpha.conj()))))*(coherent(N_dim,-alpha)+(coherent(N_dim,alpha)))
    return cat
def cat_minus(alpha):
    cat = (1/(np.sqrt(2)*np.sqrt(1-np.e**(-alpha*alpha.conj()))))*(-coherent(N_dim,-alpha)+(coherent(N_dim,alpha)))
    return cat
def pnr_resolution_detector(eta, click, n_truc):
    pi_n = 0;
    l = np.arange(click,n_truc)
    for i in l:
        pi_n +=  n_choose_k(i,click)*math.pow((1-eta),(i-click))*math.pow(eta,click)*fock(N_dim,i)*fock(N_dim,i).dag()
        #print("The final Povm element is: ", pi_0)
    return Qobj(pi_n)
def Fock_Filter_povm(in1,in2,refl,num_det,eta,n_truc):
    Projector = tensor(pnr_resolution_detector(eta, num_det, n_truc),qeye(N_dim));
    Initial_state=tensor(in1,in2);
    theta_k=np.arccos(np.sqrt(refl));

    BS1= ((theta_k)*(tensor(a1,a2.dag()) - tensor(a1.dag(),a2))).expm()

    Rho=BS1*Initial_state*BS1.dag();

    Rho_filtered = ((Rho*Projector).ptrace(1))/((Rho*Projector).tr())
    '''The operation .ptrace(m) takes the partial trace over every mode EXCEPT m, where the numbering
    startes at 0.  So .ptrace(1) means you keep mode 1, which is actually the 2nd mode'''
    print('BS has reflectivity',refl,' and I am detecting the |',num,'> state, where my detector has efficiency', eta)
    return Rho_filtered
def Fock_Filter_prob(in_state,in_fock,refl,num_det,eta,n_truc):
    Projector = tensor(pnr_resolution_detector(eta, num_det, n_truc),qeye(N_dim));
    Initial_state=tensor(in_state,ket2dm(fock(N_dim,in_fock)));
    theta_k=np.arccos(np.sqrt(refl));

    BS1= ((theta_k)*(tensor(a1,a2.dag()) - tensor(a1.dag(),a2))).expm()

    Rho=BS1*Initial_state*BS1.dag();
    P=(Rho*Projector).tr()
    print('The probability of a sucessful detection is:',P)
    Rho_filtered = ((Rho*Projector).ptrace(1))/((Rho*Projector).tr())
    #Rho_filtered=Rho*Projector
    '''The operation .ptrace(m) takes the partial trace over every mode EXCEPT m, where the numbering
    startes at 0.  So .ptrace(1) means you keep mode 1, which is actually the 2nd mode'''
    print('BS has reflectivity',refl,' and I am detecting the |',num,'> state, where my detector has efficiency', eta)
    return Rho_filtered

def fid(state1,state2):
    F=np.absolute((state1.sqrtm()*state2*state1.sqrtm()).sqrtm().tr())
    return F
```

```python
#Variable definitions:
#delta = initial coherent state amplitude
# refl(1-3)= beamsplitter r^2 values at each step
# n(1-3) = number of photons detected at each detector
# beta = amplitude of final dispalcement to displace the state back
#alpha = amplitude of SSV state to be compared with.  Note, here
#this can be related to cat state amplitude by amp_cat=alpha/(Cosh[sq]-Sinh[sq])
# sq = the 'r' value, or squeezing parameter. This is the negative of the mathematica results.

'''Three-step check: P -> 0.00179605, 0.9836, r[3.] -> 0.520135, r[2.] -> 0.490869,
 r[1.] -> 0.637566, \[Alpha] -> 1.349,
 sq -> -0.476595, \[Beta] -> 2.03846, delta -> 3.53709, n1 -> 5,
 n2 -> 2, n3 -> 1}'''

refl1=0.637566**2; refl2=0.490869**2; refl3=0.520135**2;
delta=3.53709; beta=2.03846; sq=0.476595; alpha=1.349; n1=5;n2=2;n3=1;

#refl1=1/2; refl2=1/2; refl3=2/3;
#delta=np.sqrt(14); beta=np.sqrt(19/16); sq=0.476595; alpha=1.4; n1=5;n2=3;n3=1;
'''Two-step, check: {

 {P -> 0.0152557, 0.999653, r[2.] -> 0.852986,
  r[1.] -> 0.603152, \[Alpha] -> 0.901576,
  sq -> -0.224664, \[Beta] -> 0.5, delta -> 1.19655, n1 -> 1,
  n2 -> 2}
}'''
refl1=0.603152**2; refl2=0.852986**2; delta=1.19655; beta=0.5; sq=0.224664; alpha=0.901576; n1=1;n2=2

'''Four-step check: r[4.] = 0.42134; r[3.] = 0.69684; r[2.] = 0.55398;
r[1.] = .576813; \[Delta] = 4.6621868; n[4.] = 1; n[3.] = 2;
n[2.] = 4; n[1.] = 6;
sq= -0.51, alpha= 1.59, beta= 2.08625'''

refl1= .576813**2; refl2=0.55398**2; refl3=0.69684**2;refl4=0.42134**2;
delta=4.6621868; beta=2.08625; sq=0.476595; alpha=1.59; n1=6;n2=4;n3=2;n4=1;
```

```python
#TEST
#Make a loop to take max values after iterating and put into a single array
End=np.zeros(10);
for i in range(10):
    temp=np.zeros(31);
    for j in range(31):
        x=j/10+i;
        temp[j]=x;
    End[i]=max(temp);
```

```python
End
```

    array([ 3.,  4.,  5.,  6.,  7.,  8.,  9., 10., 11., 12.])

```python
'''Four-step check: r[4.] = 0.42134; r[3.] = 0.69684; r[2.] = 0.55398;
r[1.] = .576813; \[Delta] = 4.6621868; n[4.] = 1; n[3.] = 2;
n[2.] = 4; n[1.] = 6;
sq= -0.51, alpha= 1.59, beta= 2.08625'''
def mixture(purity):
    state=purity*ket2dm(fock(N_dim,1))+(1-purity)*ket2dm(fock(N_dim,0))
    return state

eta=1;
refl1= .576813**2; refl2=0.55398**2; refl3=0.69684**2;refl4=0.42134**2;
delta=4.6621868; beta=2.08625; sq=0.476595; alpha=1.59; n1=6;n2=4;n3=2;n4=1;

ssv=SSV_plus(sq,alpha)

four_step=np.zeros(21)
for k in range(21):
    first=Fock_Filter_povm(ket2dm(coherent(N_dim,delta)),ket2dm(fock(N_dim,1)),refl1,n1,eta-0.01*k,N_dim)
    second=Fock_Filter_povm(first,ket2dm(fock(N_dim,1)),refl2,n2,eta-0.01*k,N_dim);
    third=Fock_Filter_povm(second,ket2dm(fock(N_dim,1)),refl3,n3,eta-0.01*k,N_dim);
    fourth=Fock_Filter_povm(third,ket2dm(fock(N_dim,1)),refl4,n4,eta-0.01*k,N_dim);
    F=np.zeros(60)
    for i in range(60):
        final_state=D(fourth,beta*(-1)-0.3+0.01*i)
        F[i]=fid(final_state,ssv)
    four_step[k]=max(F)

plt.plot(range(21),four_step,'-*')
plt.grid()
plt.xlabel('detector efficiency index')
plt.ylabel('fidelity')
#set_xlim([40, 60])
plt.title('Fidelity as mixing with vacuum increases')
plt.show()

```

```python
'''Three-step check: P -> 0.00179605, 0.9836, r[3.] -> 0.520135, r[2.] -> 0.490869,
 r[1.] -> 0.637566, \[Alpha] -> 1.349,
 sq -> -0.476595, \[Beta] -> 2.03846, delta -> 3.53709, n1 -> 5,
 n2 -> 2, n3 -> 1}'''

refl1=0.637566**2; refl2=0.490869**2; refl3=0.520135**2;
delta=3.53709; beta=2.03846; sq=0.476595; alpha=1.349; n1=5;n2=2;n3=1;

ssv=SSV_minus(sq,alpha)

three_step=np.zeros(21)
for k in range(21):
    first=Fock_Filter_povm(ket2dm(coherent(N_dim,delta)),ket2dm(fock(N_dim,1)),refl1,n1,eta-0.01*k,N_dim)
    second=Fock_Filter_povm(first,ket2dm(fock(N_dim,1)),refl2,n2,eta-0.01*k,N_dim);
    third=Fock_Filter_povm(second,ket2dm(fock(N_dim,1)),refl3,n3,eta-0.01*k,N_dim);
    F=np.zeros(60)
    for i in range(60):
        final_state=D(third,beta*(-1)-0.3+0.01*i)
        F[i]=fid(final_state,ssv)
    three_step[k]=max(F)

plt.plot(range(21),three_step,'-*')
plt.grid()
plt.xlabel('detector efficiency index')
plt.ylabel('fidelity')
#set_xlim([40, 60])
plt.title('Fidelity as mixing with vacuum increases')
plt.show()

```

```python
'''Two-step, check: {

 {P -> 0.0152557, 0.999653, r[2.] -> 0.852986,
  r[1.] -> 0.603152, \[Alpha] -> 0.901576,
  sq -> -0.224664, \[Beta] -> 0.5, delta -> 1.19655, n1 -> 1,
  n2 -> 2}
}'''
refl1=0.603152**2; refl2=0.852986**2; delta=1.19655; beta=0.5; sq=0.224664; alpha=0.901576; n1=1;n2=2

ssv=SSV_plus(sq,alpha)

two_step=np.zeros(21)
for k in range(21):
    first=Fock_Filter_povm(ket2dm(coherent(N_dim,delta)),ket2dm(fock(N_dim,1)),refl1,n1,eta-0.01*k,N_dim)
    second=Fock_Filter_povm(first,ket2dm(fock(N_dim,1)),refl2,n2,eta-0.01*k,N_dim);
    F=np.zeros(60)
    for i in range(60):
        final_state=D(second,beta*(-1)-0.3+0.01*i)
        F[i]=fid(final_state,ssv)
    two_step[k]=max(F)

plt.plot(range(21),two_step,'-*')
plt.grid()
plt.xlabel('detector efficiency index')
plt.ylabel('fidelity')
#set_xlim([40, 60])
plt.title('Fidelity as mixing with vacuum increases')
plt.show()

```

```python
from pylab import *
rc('axes', linewidth=2)
fontsize=14
array=np.arange(1,.79,-.01)
#array=[1,0.95,0.9,0.85,0.8]
plt.plot(array,two_step,'g-o',array,three_step,'-rv',array,four_step,'b-D')
plt.grid()
plt.legend((r'$N=2$', r'$N=3$',r'$N=4$'),
           loc='lower right', shadow=True, fontsize=14)
plt.xlabel(r'$\eta$',fontsize=16)
plt.ylabel('Fidelity',fontsize=16)
#set_xlim([40, 60])
#plt.title('Fidelity of filtered state with displaced single photon', fontsize=16)
plt.tick_params(axis='both', labelsize=13)

plt.show()
```

```python
two_step
```

    array([0.99982773, 0.99069582, 0.98195552, 0.97358903, 0.96557464,
           0.95789583, 0.95053798, 0.94348408, 0.93671924, 0.93023164,
           0.92400345, 0.91802701, 0.91228881, 0.90677755, 0.90148548,
           0.89640018, 0.89151312, 0.88681491, 0.88229976, 0.87795554,
           0.87377933])

```python
three_step
```

    array([0.99141659, 0.98355323, 0.97497211, 0.96580375, 0.95605709,
           0.94586302, 0.93532703, 0.92449565, 0.91346164, 0.90235847,
           0.89120544, 0.88007526, 0.86906515, 0.85822864, 0.84758378,
           0.83718175, 0.82707627, 0.81729924, 0.80784724, 0.79874196,
           0.78999414])

```python
four_step
```

    array([0.98867933, 0.97525847, 0.96076169, 0.94543371, 0.92940169,
           0.91287004, 0.89610661, 0.87921216, 0.86235925, 0.84571169,
           0.82940623, 0.81349495, 0.79810169, 0.78329702, 0.76913131,
           0.7556173 , 0.74277801, 0.73062461, 0.71918904, 0.70843641,
           0.69835126])

```python
F=np.zeros(40)
for i in range(40):
    final_state=D(fourth,beta*(-1)-0.2+0.01*i)
    F[i]=fid(final_state,ssv)
plt.plot(range(40),F,'-*')
plt.grid()
plt.xlabel('diplacement')
plt.ylabel('fidelity')
#set_xlim([40, 60])
plt.title('Fidelity of filtered state with displaced single photon')
plt.show()
```

```python

```


# Train a simple autoencoder for MNIST with 3 Hidden Layers

The autoencoder trained here has three regularizers added to the reconstruction error, that enforce sparsity within a layer, participation of all neurons in the network and binary output behavior of binomial units.

I.e., the loss function consists of a term, that evaluates the error between the input and output
**plus** a term, that evaluates the sparsity in the hidden layers
**plus** a term, that makes sure, that all neurons are active every once in a while
**plus** a term, that punishes output activations that deviate from 0 or 1.

## Participation

To ensure, that a neuron participates on the computation, we need to consider its activity over time, which makes training harder, as we need to backprop over many trials to be correct, because the activity trace denpends on all previous trials.

Instead of simulating many trials before starting backprop, we maintain the activity trace outside of the tensorflow simulation. I.e., it will be an external input to the network.
Note, that the term we add then to the loss is still convex:

\begin{equation}
(\gamma \cdot x_{trace} + (1-\gamma) \cdot x - (1-sparsity))^2
\end{equation}

where x_trace is the activity trace which is fed to the network externally.

We initialize x_trace to be sparsity. I.e., at the beginning, no error is measured through the above loss until we get a divergence of x_trace from sparsity.

Note, the above equation is not stable. For a sophisticated and more stable version, pls. refer to the enclosed PDF.

## Sparsity

Assume we have a level of sparsity $s$ that should be inferred. Given binary units and $p=1-s$, the number of units active at a timestep in a layer with $N$ neurons is $k = p \cdot N$. We call $p$ participation.

The loss can therefore be computed in hidden layer $h$ as

\begin{equation}
(\frac{1}{N} \sum_{i} h_i - p)^2
\end{equation}

## Binary Activation

As we are using sigmoid units (but want to infer properties about binary units), we want to additionally enforce a binary behavior of the units here. I.e., a neural activation $h_i$ should be either 0 or 1.

\begin{equation}
(h_i - 0)^2 \cdot (h_i - 1)^2
\end{equation}

Note, this loss is not convex.

```python
import tensorflow as tf
from tensorflow.examples.tutorials.mnist import input_data
import matplotlib.pyplot as plt
import matplotlib.animation as animation

import math

import numpy as np

from IPython.display import HTML

%matplotlib inline

# Download and read mnist data
mnist = input_data.read_data_sets('mnist_data', one_hot=True)
```

    Extracting mnist_data/train-images-idx3-ubyte.gz
    Extracting mnist_data/train-labels-idx1-ubyte.gz
    Extracting mnist_data/t10k-images-idx3-ubyte.gz
    Extracting mnist_data/t10k-labels-idx1-ubyte.gz

```python
tf.reset_default_graph()

# Exponential smoothing factor.
gamma = 0.99

sparsity = 0.75
# Influence of sparsity constraint to loss
alph_s = 0.5
# Influence of participation constraint to loss
alph_p = 100.0
# Influence of the binary output constraint to the loss
alph_b = 0.5

size_input = 784
size_h1 = 200
size_h2 = 30

# Initial learning rate
lr = 100

def sshift(c, k=100.):
    """How to shift the sigmoid along the x-axis, such that sigmoid(0) = c.

    Args:
        c: y-value of sigmoid(0).
        k: Stretching factor of sigmoid.

    Returns:
        Returns x_s such that sigmoid(x-x_s) = c for x = 0. Note, that the original sigmoid has the property that
        sigmoid(x) = 0.5 for x = 0.
    """
    return 1./k * tf.log((1.-c)/c)

def sigmoid(x, x_s, k=100.):
    """Compute a sigmoid fuction.

    Args:
        x: Input to sigmoid
        c: y-value of sigmoid(0).
        k: Stretching factor of sigmoid.

    Returns:
        Returns 1/(1+exp(-k*(x-x_s)))
    """
    return tf.div(1.,tf.add(1.,tf.exp(-k * (x-x_s))))

# Create a five layer fully-connected network:
# size_input -> size_h1 -> size_h2 -> size_h1 -> size_input
inputs = tf.placeholder(tf.float32, shape=[None, size_input])
learning_rate = tf.placeholder(tf.float32, shape=[])

activity_trace_h1 = tf.placeholder(tf.float32, shape=[size_h1])
activity_trace_h2 = tf.placeholder(tf.float32, shape=[size_h2])
activity_trace_h3 = tf.placeholder(tf.float32, shape=[size_h1])

h1 = tf.contrib.layers.fully_connected(inputs, size_h1, activation_fn=tf.nn.sigmoid)

h2 = tf.contrib.layers.fully_connected(h1, size_h2, activation_fn=tf.nn.sigmoid)

h3 = tf.contrib.layers.fully_connected(h2, size_h1, activation_fn=tf.nn.sigmoid)

outputs = tf.contrib.layers.fully_connected(h3, size_input, activation_fn=tf.nn.sigmoid)

# Reconstruction Error
# As the other loss terms (e.g. sparsity) are normalized to layer sizes, we should do this here too
# Note, that the function L2 loss doesn't use the sqrt op.
rec_err = tf.nn.l2_loss(outputs-inputs) / float(size_input)

# Compute sparsity within layer
# (We only consider hidden layers, as the other layer shall be fully governed by the input)
loss_s = 0
# Compute binary loss.
loss_b = 0
for layer in [h1,h2,h3]:
    n_l = float(int(layer.shape[1]))
    participation = (1.-sparsity)
    loss_s += tf.reduce_mean(tf.reduce_sum(layer, 1)/n_l - participation)**2

    loss_b += tf.reduce_mean(tf.pow(layer,2)*tf.pow(layer-1,2))

# Add participation loss for all neurons
loss_p = 0
activity_traces = [activity_trace_h1, activity_trace_h2, activity_trace_h3]
participation = (1.-sparsity)
x_s = sshift(participation) # sigmoid shift
for ii, layer in enumerate([h1,h2,h3]):
    n_l = float(int(layer.shape[1]))
    a_trace = activity_traces[ii]

    ## Simple loss (unstable minima)
    #loss_p += tf.reduce_mean(tf.reduce_sum((gamma*a_trace + (1-gamma)*layer - participation)**2, 1)/n_l)

    ## Loss with minimas relatively stable around 0 or 1 (depending on trace error)
    # Root of the simple loss formulation.
    root = - 1/(1-gamma) * (gamma*a_trace - participation)
    sigval = sigmoid(participation - a_trace, x_s)
    loss_p += tf.reduce_mean(tf.reduce_sum( \
        (gamma*a_trace + (1-gamma)*layer - participation + (1-gamma) * (root - sigval))**2, 1)/n_l)

loss = rec_err + alph_s * loss_s + alph_p * loss_p + alph_b * loss_b

sess = tf.InteractiveSession()
sess.run(tf.global_variables_initializer())

tf.set_random_seed(42)

train_step = tf.train.GradientDescentOptimizer(learning_rate=learning_rate).minimize(loss)

print('Starting training ...')

# Choose a test sample, which will show the training progress during eval iterations.
test_batch = mnist.test.next_batch(1)

loss_values = []

# Monitor the hidden activations, to visualize the sparsity.
hidden_activations = []

# Monitor the activation traces to visualize their evolution.
activation_traces_train = []

batch_size = 32

# Initial activation traces.
#at_h1 = np.expand_dims(np.ones(size_h1) * sparsity, axis=0)
at_h1 = np.ones(size_h1) * (1-sparsity)
at_h2 = np.ones(size_h2) * (1-sparsity)
at_h3 = np.ones(size_h1) * (1-sparsity)

for i in range(10000):
    batch = mnist.train.next_batch(batch_size)

    # Eval current training stage
    if i % 1000 == 0:
        lr /= 2
        train_loss, train_err, train_sp, train_a = sess.run([loss, rec_err, loss_s, loss_p], feed_dict={
            inputs: batch[0], learning_rate: lr, \
            activity_trace_h1: at_h1, activity_trace_h2: at_h2, activity_trace_h3: at_h3})

        loss_values.append((i, train_loss, train_err, train_sp, train_a))
        print("step %d, training loss %g" \
              % (i, train_loss))

        # Evaluation on test sample
        h1_activity, h2_activity, h3_activity = sess.run([h1,h2,h3], feed_dict={
            inputs: test_batch[0], learning_rate: lr, \
            activity_trace_h1: at_h1, activity_trace_h2: at_h2, activity_trace_h3: at_h3})

        hidden_activations.append((i, h1_activity, h2_activity, h3_activity))

        activation_traces_train.append((i, at_h1, at_h2, at_h3))

        outs = outputs.eval(feed_dict={
                    inputs: test_batch[0], learning_rate: lr, \
            activity_trace_h1: at_h1, activity_trace_h2: at_h2, activity_trace_h3: at_h3})
        fig = plt.figure()
        plt.suptitle('Epoch %d' % (i))
        ax = fig.add_subplot(1,2,1)
        ax.imshow(test_batch[0].reshape((28,28)), vmin=-1.0, vmax=1.0)
        ax.set_title('Input')
        ax = fig.add_subplot(1,2,2)
        ax.imshow(outs.reshape((28,28)), vmin=-1.0, vmax=1.0)
        ax.set_title('Output')
        plt.show()

    # Run backprop
    train_step.run(feed_dict={inputs: batch[0], learning_rate: lr, \
            activity_trace_h1: at_h1, activity_trace_h2: at_h2, activity_trace_h3: at_h3})

    # Update activity traces
    activity_h1, activity_h2, activity_h3 = sess.run([h1, h2, h3], feed_dict={inputs: batch[0], learning_rate: lr, \
        activity_trace_h1: at_h1, activity_trace_h2: at_h2, activity_trace_h3: at_h3})
    # We update the traces by the mean activity achieved in the current batch
    activity_h1 = np.mean(activity_h1, axis=0)
    activity_h2 = np.mean(activity_h2, axis=0)
    activity_h3 = np.mean(activity_h3, axis=0)

    at_h1 = gamma * at_h1 + (1-gamma) * activity_h1
    at_h2 = gamma * at_h2 + (1-gamma) * activity_h2
    at_h3 = gamma * at_h3 + (1-gamma) * activity_h3

xval, lval, erval, sval, aval = zip(*loss_values)
plt.plot(xval, lval, label="loss")
plt.plot(xval, erval, label="reconstr. error")
plt.plot(xval, sval, label="sparsity")
plt.plot(xval, aval, label="activity")
plt.legend()
plt.title("Train Loss Development")
plt.xlabel("Iteration")
plt.ylabel("Train Loss")
plt.show()
```

# Evolution of activity histograms to visualize sparsity

Here we plot histograms of the hidden layer activation over time. I.e., at each evaluation epoch, we take the activation of all hidden layers and plot a histogram of the contained values (bins of size 0.1). Ideally, (1-sparsity)% of the activations have the value one and sparsity% of the values have the value zero. However, it is hard to define a regularizer for sigmoid units, that enforces this behavior.

```python
xval, h1val, h2val, h3val = zip(*hidden_activations)

number_of_frames = len(xval)

def update_hist(num):
    plt.cla()
    ax1.cla()
    ax2.cla()
    ax3.cla()

    ax1.set_ylim(0.0, 10.0)
    ax2.set_ylim(0.0, 10.0)
    ax3.set_ylim(0.0, 10.0)

    fig.suptitle('Epoch: %d' % xval[num], fontsize=16)

    ax1.hist(np.squeeze(h1val[num]), bins=np.linspace(0,1,11), range=(0.,1.), normed=True)
    ax1.set_title('Activation Histogram of h1')

    ax2.hist(np.squeeze(h2val[num]), bins=np.linspace(0,1,11), range=(0.,1.), normed=True)
    ax2.set_title('Activation Histogram of h2')

    ax3.hist(np.squeeze(h3val[num]), bins=np.linspace(0,1,11), range=(0.,1.), normed=True)
    ax3.set_title('Activation Histogram of h3')

fig = plt.figure(figsize = (10,5))

ax1 = fig.add_subplot(131)
ax2 = fig.add_subplot(132)
ax3 = fig.add_subplot(133)

update_hist(0)

anim = animation.FuncAnimation(fig, update_hist, number_of_frames, interval=1000 )
#plt.show()
HTML(anim.to_html5_video())
```

# Evolution of activity trace histograms to visualize participation

Here we plot histograms of the hidden layer activity traces over time. I.e., at each evaluation epoch, we take a snapshot of the current activity trace for all hidden neurons. **Note**, unlike the hidden activations, the activity traces here are not monitored over a single test sample (as we would need to simulate the test sample every iteration). Instead, the activity trace is the same as used for training, thus it is computed from mean activations of training batches.

```python
xval, h1val, h2val, h3val = zip(*activation_traces_train)

number_of_frames = len(xval)

def update_hist(num):
    plt.cla()
    ax1.cla()
    ax2.cla()
    ax3.cla()

    ax1.set_ylim(0.0, 10.0)
    ax2.set_ylim(0.0, 10.0)
    ax3.set_ylim(0.0, 10.0)

    fig.suptitle('Epoch: %d' % xval[num], fontsize=16)

    ax1.hist(np.squeeze(h1val[num]), bins=np.linspace(0,1,11), range=(0.,1.), normed=True)
    ax1.set_title('Trace Histogram of h1')

    ax2.hist(np.squeeze(h2val[num]), bins=np.linspace(0,1,11), range=(0.,1.), normed=True)
    ax2.set_title('Trace Histogram of h2')

    ax3.hist(np.squeeze(h3val[num]), bins=np.linspace(0,1,11), range=(0.,1.), normed=True)
    ax3.set_title('Trace Histogram of h3')

fig = plt.figure(figsize = (10,5))

ax1 = fig.add_subplot(131)
ax2 = fig.add_subplot(132)
ax3 = fig.add_subplot(133)

update_hist(0)

anim = animation.FuncAnimation(fig, update_hist, number_of_frames, interval=1000 )
#plt.show()
HTML(anim.to_html5_video())
```

# Test samples

Random test samples from the test set to get an subjective impression of the autoencoder quality.

```python
test_batch = mnist.test.next_batch(50)

outs = outputs.eval(feed_dict={
                    inputs: test_batch[0], learning_rate: lr})

for i in range(test_batch[0].shape[0]):
    fig = plt.figure()
    ax = fig.add_subplot(1,2,1)
    ax.imshow(test_batch[0][i,:].reshape((28,28)), vmin=-1.0, vmax=1.0)
    ax.set_title('Input')
    ax = fig.add_subplot(1,2,2)
    ax.imshow(outs[i,:].reshape((28,28)), vmin=-1.0, vmax=1.0)
    ax.set_title('Output')
    plt.show()
```

The evolution of the different loss components compared to the overall loss. Note, that is the same figure as above, just plotted with a better resolution. "loss" is the actual loss term that is optimized on consists of the other three components. "sparsity" measures how well the sparsity constraint within a layer is satisfied. "activity" evaluates the participation of all neurons (all neurons are involved in the network computation).

```python
fig = plt.figure(figsize=(15,15))
xval, lval, erval, sval, aval = zip(*loss_values)
#plt.plot(xval, lval, label="loss")
plt.plot(xval, erval, label="reconstr. error")
plt.plot(xval, sval, label="sparsity")
plt.plot(xval, aval, label="activity")
plt.legend()
plt.title("Train Loss Development")
plt.xlabel("Iteration")
plt.ylabel("Train Loss")
plt.show()
```

### Get hidden layer representation (h2 activations) from trained model for all MNIST samples

```python
train_images = mnist.train.images
test_images = mnist.test.images

hidden_activation_train = sess.run(h2, feed_dict={
            inputs: train_images, learning_rate: lr, \
            activity_trace_h1: at_h1, activity_trace_h2: at_h2, activity_trace_h3: at_h3})
hidden_activation_test = sess.run(h2, feed_dict={
            inputs: test_images, learning_rate: lr, \
            activity_trace_h1: at_h1, activity_trace_h2: at_h2, activity_trace_h3: at_h3})
```

### Train perceptron on h2 activs and plot test accuracy

```python
from sklearn.linear_model import Perceptron
classifier = Perceptron(max_iter=1000, tol=1e-3)

classifier.fit(hidden_activation_train, np.argmax(mnist.train.labels, axis=1))
print("Test accuracy: %g" % classifier.score(hidden_activation_test, np.argmax(mnist.test.labels, axis=1)))
```

    Test accuracy: 0.8016

### Train SVM on h2 activs and plot test accuracy

```python
from sklearn.svm import SVC
classifier = SVC()

classifier.fit(hidden_activation_train, np.argmax(mnist.train.labels, axis=1))
print("Test accuracy: %g" % classifier.score(hidden_activation_test, np.argmax(mnist.test.labels, axis=1)))
```

    Test accuracy: 0.9308

### Train Logistic Regression on h2 activs and plot test accuracy

```python
from sklearn.linear_model import LogisticRegression
classifier = LogisticRegression()

classifier.fit(hidden_activation_train, np.argmax(mnist.train.labels, axis=1))
print("Test accuracy: %g" % classifier.score(hidden_activation_test, np.argmax(mnist.test.labels, axis=1)))
```

    Test accuracy: 0.8779

### Train NaiveBayes on h2 activs and plot test accuracy

```python
from sklearn.naive_bayes import GaussianNB
classifier = GaussianNB()

classifier.fit(hidden_activation_train, np.argmax(mnist.train.labels, axis=1))
print("Test accuracy: %g" % classifier.score(hidden_activation_test, np.argmax(mnist.test.labels, axis=1)))
```

    Test accuracy: 0.809

### For comparison: How well Perceptron performs on the actual image data

For a perfect classifier, it should definately be easier to classify the actual image data, as the compressed image may include degenerated images (as seen in the above reconstructions). However, the degenerations are not that bad compared to the difficulties that should arise due to the much bigger feature space to learn for perceptron when operating on the original image space.

```python
from sklearn.linear_model import Perceptron
classifier = Perceptron(max_iter=1000, tol=1e-3)

classifier.fit(mnist.train.images, np.argmax(mnist.train.labels, axis=1))
print("Test accuracy: %g" % classifier.score(mnist.test.images, np.argmax(mnist.test.labels, axis=1)))
```

    Test accuracy: 0.8799


# Advection-Diffusion-Reaction Model

This notebook demonstrates how to
- Define the equation you want to solve
- Setup up and initial-boundary value problem
- Choose a particular set of numerics
- Post-process the solution

$
\newcommand{\grad}{\nabla}
$

```python
%matplotlib notebook
from proteus.iproteus import *
Profiling.logLevel=7
Profiling.verbose=False
```

# Defining an equation
The equation we want to solve is
\begin{equation*}
m_t + \nabla \cdot \left(\mathbf{f} - \mathbf{a} \grad u \right) + r = 0
\end{equation*}
where $u$ is the unknown solution and the coefficients have the specific  forms
\begin{align}
m(u) &= M u \\
f(u) &= \mathbf{B} u \\
a(u) &= \mathbf{A} \\
r(u) &= C u \\
\end{align}

# Proteus template equation
\begin{equation}
\mathbf{u} = \left[ u^0,u^1,\ldots,u^{nc-1} \right]
\end{equation}
\begin{equation*}
m^i_t + \nabla \cdot \left(\mathbf{f}^i - \sum_k^{nc-1} \mathbf{a}^{i,k} \nabla \phi^k(\mathbf{u}) \right) + r^i + H^i(\nabla u^i)= 0 \qquad \mbox{for } i=0,\ldots,nc-1
\end{equation*}

- $m$ is "mass"
- $\mathbf{f}$ is advection
- $\mathbf{a}$ is diffution
- $\phi$ is potential
- $H$ is Hamiltonian
- $r$ is reaction

Example: non-conservative advection
\begin{equation}
u_t + \mathbf{v} \cdot \nabla u = 0
\end{equation}

What is $m$?
What is $f$?
What is $H$?

```python
class LADR(TransportCoefficients.TC_base):
    """
    The coefficients of the linear advection-diffusion equation
    """
    def __init__(self,M,A,B,C):
        TransportCoefficients.TC_base.__init__(self,
                                               nc=1, #number of components
                                               variableNames=['u'],
                                               mass      = {0:{0:'linear'}},
                                               advection = {0:{0:'linear'}},
                                               diffusion = {0:{0:{0:'constant'}}},
                                               potential = {0:{0:'u'}},
                                               reaction  = {0:{0:'linear'}})

        self.M=M;
        self.A=A;
        self.B=B;
        self.C=C;
    def evaluate(self,t,c):
        c[('m',0)][:]         = self.M*c[('u',0)]
        c[('dm',0,0)][:]      = self.M
        c[('f',0)][...,0]     = self.B[0]*c[('u',0)]
        c[('f',0)][...,1]     = self.B[1]*c[('u',0)]
        c[('df',0,0)][...,0]  = self.B[0]
        c[('df',0,0)][...,1]  = self.B[1]
        c[('a',0,0)][...,0] = self.A[0][0]
        c[('a',0,0)][...,1] = self.A[0][1]
        c[('a',0,0)][...,2] = self.A[1][0]
        c[('a',0,0)][...,3] = self.A[1][1]
        c[('r',0)][:]         = self.C*c[('u',0)]
        c[('dr',0,0)][:]      = self.C

```

# Physics

```python
from proteus import default_p as p
#physics
p.name = "ladr_2d"
p.nd = 2; #Two dimensions
p.L=(1.0,1.0);
p.T=1.0

p.coefficients=LADR(M=1.0,
                    A=[[1.001,0.0],
                       [0.0,1.001]],
                    B=[0.0,0.0],
                    C=0.0)

def getDBC(x,flag):
    if x[0] == 0.0 or x[1] == 0.0:
        return lambda x,t: 1.0
    elif x[0] == p.L[0] or x[1] == p.L[1]:
        return lambda x,t: 0.0
    else:
        return None

p.dirichletConditions = {0:getDBC}
p.advectiveFluxBoundaryConditions = {}
p.diffusiveFluxBoundaryConditions = {0:{}}
p.periodicDirichletConditions = None
class IC:
    def __init__(self):
        pass
    def uOfXT(self,x,t):
        if x[0] <= 0.0 or x[1] <= 0.0:
            return 1.0
        else:
            return 0.0

p.initialConditions  = {0:IC()}
```

# Numerics

```python
from proteus import default_n as n
import proteus as pr
n.timeIntegration = pr.TimeIntegration.BackwardEuler_cfl
n.stepController = pr.StepControl.Min_dt_cfl_controller
n.runCFL=1.0
n.femSpaces = {0:pr.FemTools.C0_AffineLinearOnSimplexWithNodalBasis}
n.elementQuadrature = pr.Quadrature.SimplexGaussQuadrature(p.nd,3)
n.elementBoundaryQuadrature = pr.Quadrature.SimplexGaussQuadrature(p.nd-1,3)
n.subgridError = pr.SubgridError.AdvectionDiffusionReaction_ASGS(p.coefficients,
                                                                 p.nd,lag=False)
n.shockCapturing = pr.ShockCapturing.ResGradQuad_SC(p.coefficients,
                                                    p.nd,
                                                    shockCapturingFactor=0.99,
                                                    lag=True)
n.numericalFluxType = pr.NumericalFlux.Advection_DiagonalUpwind_Diffusion_SIPG_exterior
n.nnx=41; n.nny=41
n.tnList=[float(i)/40.0 for i in range(11)]
n.matrix = pr.LinearAlgebraTools.SparseMatrix
n.multilevelLinearSolver = pr.LinearSolvers.KSP_petsc4py
n.l_atol_res = 1.0e-8
n.parallelPartitioningType = pr.MeshTools.MeshParallelPartitioningTypes.element
n.nLayersOfOverlapForParallel = 1
n.periodicDirichletConditions = None
```

# Operator Splitting

```python
from proteus import default_s,default_so
so = default_so
so.name = p.name
so.sList=[default_s]
so.tnList = n.tnList
so.systemStepControllerType = SplitOperator.Sequential_FixedStep_Simple
so.systemStepExact= False
so.dt_system_fixed= 0.01
```

# Initialize Numerical Solution Object

```python
ns = NumericalSolution.NS_base(so,[p],[n],so.sList,opts)
```

# Calculate Solution

```python
failed = ns.calculateSolution('ladr_run1')
assert(not failed)
```

```python
from matplotlib import pyplot as plt
x = ns.modelList[0].levelModelList[-1].mesh.nodeArray[:,0]
y = ns.modelList[0].levelModelList[-1].mesh.nodeArray[:,1]
triangles = ns.modelList[0].levelModelList[-1].mesh.elementNodesArray
u = ns.modelList[0].levelModelList[-1].u[0].dof
plt.set_cmap('plasma')
plt.tricontourf(x,y,triangles,u)
plt.colorbar()
plt.show()
```

    <IPython.core.display.Javascript object>

[proteus.log](proteus.log)

```python

```


# Inferential Statistics Ib - Frequentism

## Learning objectives

Welcome to the second Frequentist inference mini-project! Over the course of working on this mini-project and the previous frequentist mini-project, you'll learn the fundamental concepts associated with frequentist inference. The following list includes the topics you will become familiar with as you work through these two mini-projects:
* the _z_-statistic
* the _t_-statistic
* the difference and relationship between the two
* the Central Limit Theorem, its assumptions and consequences
* how to estimate the population mean and standard deviation from a sample
* the concept of a sampling distribution of a test statistic, particularly for the mean
* how to combine these concepts to calculate confidence intervals and p-values
* how those confidence intervals and p-values allow you to perform hypothesis (or A/B) tests

## Prerequisites

* what a random variable is
* what a probability density function (pdf) is
* what the cumulative density function is
* a high-level sense of what the Normal distribution

If these concepts are new to you, please take a few moments to Google these topics in order to get a sense of what they are and how you might use them.

These two notebooks were designed to bridge the gap between having a basic understanding of probability and random variables and being able to apply these concepts in Python. This second frequentist inference mini-project focuses on a real-world application of this type of inference to give you further practice using these concepts.

In the previous notebook, we used only data from a known normal distribution. You'll now tackle real data, rather than simulated data, and answer some relevant real-world business problems using the data.

## Hospital medical charges

Imagine that a hospital has hired you as their data analyst. An administrator is working on the hospital's business operations plan and needs you to help them answer some business questions. This mini-project, as well as the bootstrap and Bayesian inference mini-projects also found in this unit are designed to illustrate how each of the inferential statistics methods have their uses for different use cases. In this assignment notebook, you're going to use frequentist statistical inference on a data sample to answer the questions:
* has the hospital's revenue stream fallen below a key threshold?
* are patients with insurance really charged different amounts than those without?
Answering that last question with a frequentist approach makes some assumptions, or requires some knowledge, about the two groups. In the next mini-project, you'll use bootstrapping to test that assumption. And in the final mini-project of the unit, you're going to create a model for simulating _individual_ charges (not a sampling distribution) that the hospital can use to model a range of scenarios.

We are going to use some data on medical charges obtained from [Kaggle](https://www.kaggle.com/easonlai/sample-insurance-claim-prediction-dataset). For the purposes of this exercise, assume the observations are the result of random sampling from our one hospital. Recall in the previous assignment, we introduced the Central Limit Theorem (CLT), and how it tells us that the distributions of sample statistics approach a normal distribution as $n$ increases. The amazing thing about this is that it applies to the sampling distributions of statistics that have been calculated from even highly non-normal distributions of data. Remember, also, that hypothesis testing is very much based on making inferences about such sample statistics. You're going to rely heavily on the CLT to apply frequentist (parametric) tests to answer the questions in this notebook.

```python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import t
from numpy.random import seed
medical = pd.read_csv('data/insurance2.csv')
```

```python
medical.shape
```

    (1338, 8)

```python
medical.head()
```

      age
      sex
      bmi
      children
      smoker
      region
      charges
      insuranceclaim

      0
      19
      0
      27.900
      0
      1
      3
      16884.92400
      1

      1
      18
      1
      33.770
      1
      0
      2
      1725.55230
      1

      2
      28
      1
      33.000
      3
      0
      2
      4449.46200
      0

      3
      33
      1
      22.705
      0
      0
      1
      21984.47061
      0

      4
      32
      1
      28.880
      0
      0
      1
      3866.85520
      1

__Q:__ Plot the histogram of charges and calculate the mean and standard deviation. Comment on the appropriateness of these statistics for the data.

__A:__

```python
medical.hist(column="charges")
```

```python
medical.charges.mean()
```

    13270.422265141257

```python
medical.charges.std()
```

    12110.011236693994

__Q:__ The administrator is concerned that the actual average charge has fallen below 12000, threatening the hospital's operational model. On the assumption that these data represent a random sample of charges, how would you justify that these data allow you to answer that question? And what would be the most appropriate frequentist test, of the ones discussed so far, to apply?

```python

```

__A:__

__Q:__ Given the nature of the administrator's concern, what is the appropriate confidence interval in this case? A one-sided or two-sided interval? Calculate the critical value and the relevant 95% confidence interval for the mean and comment on whether the administrator should be concerned?

__A:__

```python
n= len(medical.charges)
df= n-1
critical_val = t(df).ppf(0.95)
print(medical.charges.mean()- critical_val/np.sqrt(n)*medical.charges.std())
```

    12725.48718381623

The admin should be concerned about it.

```python

```

The administrator then wants to know whether people with insurance really are charged a different amount to those without.

__Q:__ State the null and alternative hypothesis here. Use the _t_-test for the difference between means where the pooled standard deviation of the two groups is given by
\begin{equation}
s_p = \sqrt{\frac{(n_0 - 1)s^2_0 + (n_1 - 1)s^2_1}{n_0 + n_1 - 2}}
\end{equation}

and the *t* test statistic is then given by

\begin{equation}
t = \frac{\bar{x}_0 - \bar{x}_1}{s_p \sqrt{1/n_0 + 1/n_1}}.
\end{equation}

What assumption about the variances of the two groups are we making here?

__A:__ The assumption is quite clear and simple.Both the groups have equal value pf standard deviation.

__Q:__ Perform this hypothesis test both manually, using the above formulae, and then using the appropriate function from [scipy.stats](https://docs.scipy.org/doc/scipy/reference/stats.html#statistical-tests) (hint, you're looking for a function to perform a _t_-test on two independent samples). For the manual approach, calculate the value of the test statistic and then its probability (the p-value). Verify you get the same results from both.

__A:__

```python
charges_for_insured = medical.charges.loc[medical.insuranceclaim == 1]

n0= len(charges_for_insured)
s0= charges_for_insured.std()
x0bar= charges_for_insured.mean()
```

```python
charges_for_uninsured = medical.charges.loc[medical.insuranceclaim == 0 ]

n1=len(charges_for_uninsured)
s1 = charges_for_uninsured.std()
x1bar = charges_for_uninsured.mean()
```

```python
sp= np.sqrt(((n0-1)*s0**2+(n1-1)*s1**2)/(n0+n1-2))
t_s = (x0bar-x1bar)/sp/np.sqrt(1/n0 + 1/n1)
t_s
```

    11.89329903087671

```python
p_val = (1-t(n0+n1-1).cdf(t_s))*2
p_val
```

    0.0

```python
from scipy.stats import ttest_ind
ttest_ind(charges_for_insured, charges_for_uninsured)
```

    Ttest_indResult(statistic=11.893299030876712, pvalue=4.461230231620717e-31)

Congratulations! Hopefully you got the exact same numerical results. This shows that you correctly calculated the numbers by hand. Secondly, you used the correct function and saw that it's much easier to use. All you need to do pass your data to it.

__Q:__ In the above calculations, we assumed the sample variances were equal. We may well suspect they are not (we'll explore this in another assignment). The calculation becomes a little more complicated to do by hand in this case, but we now know of a helpful function. Check the documentation for the function to tell it not to assume equal variances and perform the test again.

__A:__

```python
ttest_ind(charges_for_insured, charges_for_uninsured, equal_var=False)
```

    Ttest_indResult(statistic=13.298031957975649, pvalue=1.1105103216309125e-37)

__Q:__ Conceptual question: look through the documentation for statistical test functions in scipy.stats. You'll see the above _t_-test for a sample, but can you see an equivalent one for performing a *z*-test from a sample? Comment on your answer.
__A:__  For a small sample there doesn't exist any z-test but for large samples, both of the tests give equal results.
## Learning outcomes

Having completed this project notebook, you now have good hands-on experience:
* using the central limit theorem to help you apply frequentist techniques to answer questions that pertain to very non-normally distributed data from the real world
* performing inference using such data to answer business questions
* forming a hypothesis and framing the null and alternative hypotheses
* testing this using a _t_-test


Matrices
--------

Input matrix:

* $X \in R^{N \times p}$ (input matrix of $N$ observations and $p$ features)

Decomposition matrices:

* $F \in R^{N \times r}$
* $G \in R^{r \times p}$
* $W \in R^{p \times r}$

NMF
---

Definition:

\begin{equation}
X \approx F G
\end{equation}

C-NMF
-----

Definition:

\begin{equation}
X \approx F G = X W G
\end{equation}

where the columns of $F = X W$ are convex combinations of the features of $X$.

```
import pymf
import sys
import msaf
from msaf import utils as U
from msaf import jams2
import msaf.algorithms.cnmf3.segmenter as S
import msaf.algorithms.foote.segmenter as Foote

fig_dir = "/Users/uri/Dropbox/NYU/Dissertation/manuscript/4/figures/"
```

```
def cnmf(X, r, niter, fig=False, aspect="auto"):
    nmf_mdl = pymf.CNMF(X, num_bases=r)
    nmf_mdl.factorize(niter=niter)
    F = np.asarray(nmf_mdl.W)
    G = np.asarray(nmf_mdl.H)
    W = np.asarray(nmf_mdl.G)

    if fig:
        plt.figure(figsize=(10,4))
        plt.gcf().suptitle("C-NMF")
        plt.subplot(1,3,1); plt.imshow(F, aspect=aspect, interpolation="nearest"); plt.title("F");
        plt.subplot(1,3,2); plt.imshow(G, aspect=aspect, interpolation="nearest"); plt.title("G");
        plt.subplot(1,3,3); plt.imshow(F * G, aspect=aspect, interpolation="nearest"); plt.title("X = F * G");
        plt.show()
        plt.figure(figsize=(10,5))
        plt.subplot(1,4,1); plt.imshow(X, aspect=aspect, interpolation="nearest"); plt.title("X");
        plt.subplot(1,4,2); plt.imshow(W, aspect=aspect, interpolation="nearest"); plt.title("W");
        plt.subplot(1,4,3); plt.imshow(np.asmatrix(X) * W, aspect=aspect, interpolation="nearest"); plt.title("F = X * W");
        plt.show()

    return F, G, W

def nmf(X, r, niter, fig=False, aspect="auto"):
    nmf_mdl = pymf.NMF(X, num_bases=r)
    nmf_mdl.factorize(niter=niter)
    F = np.asarray(nmf_mdl.W)
    G = np.asarray(nmf_mdl.H)

    if fig:
        plt.figure(figsize=(10,4))
        plt.gcf().suptitle("NMF")
        plt.subplot(1,3,1); plt.imshow(F, aspect=aspect, interpolation="nearest"); plt.title("F");
        plt.subplot(1,3,2); plt.imshow(G, aspect=aspect, interpolation="nearest"); plt.title("G");
        plt.subplot(1,3,3); plt.imshow(F * G, aspect=aspect, interpolation="nearest"); plt.title("X = F * G");

    return F, G

def read_ssm(file_path):
    m = 13
    dist = "correlation"
    chroma, mfcc, tonnetz, beats, dur, anal = msaf.io.get_features(file_path, annot_beats=False)
    F = U.lognormalize_chroma(chroma)
    F = S.median_filter(F, M=m)
    X = Foote.compute_ssm(F, metric=dist)
    return X

def read_chroma(file_path, m=13):
    dist = "correlation"
    chroma, mfcc, tonnetz, beats, dur, anal = msaf.io.get_features(file_path, annot_beats=False)
    F = U.lognormalize_chroma(chroma)
#     F = F ** 2
    F = S.median_filter(F, M=m)
    return F
```

```
from scipy.cluster.vq import whiten, vq, kmeans

def get_kmeans_bounds(X, k, niter):
    codebook, dist = kmeans(X, k, iter=niter)
    codes, disto = vq(X, codebook)
    # Find points where there's a cluster difference (boundary)
    b = np.where(np.diff(codes) != 0)[0] + 1
    return np.asarray(b)

```

```
N = 10
p = 6
r = 4
niter = 100
fig = True
aspect = 'auto'

np.random.seed(1)
X = np.random.random((N,p))
X = np.ones((N,p))
X[:4,:] = np.arange(p)
X[4:7,:] = np.arange(0,p*2,2)
X[7:,:] = np.arange(p)
# X[7:,:] = np.arange(0,p*3,3)
X = X.T
# X = read_ssm("/Users/uri/datasets/Segments/audio/Isophonics_01_-_Help!.mp3")
# X = read_ssm("/Users/uri/datasets/Segments/audio/Isophonics_01_-_Come_Together.mp3")
X = read_ssm("/Users/uri/datasets/Segments/audio/Isophonics_05_-_And_I_Love_Her.mp3")
# X = read_ssm("/Users/uri/datasets/Segments/audio/Isophonics_06_-_Tell_Me_Why.mp3")

# Factorize
F, G, W = cnmf(X, r, niter, fig=fig, aspect=aspect)
F, G = nmf(X, r, niter, fig=fig, aspect=aspect)

# Find boundaries using k-means
bounds = get_kmeans_bounds(X, r, niter)
print bounds
```

```
# Experiment against consistency

X = read_ssm("/Users/uri/datasets/Segments/audio/Isophonics_01_-_Help!.mp3")
rank = 2
niter = 70
N = 100

def distNMF( x1, x2, y1, y2 ):
    min1 = distMat(x1, y1)
    min2 = distMat(x1, y2)
    if min1 < min2:
        m1 = min1
        m2 = distMat(x2, y2)
    else:
        m1 = min2
        m2 = distMat(x2, y1)
    return m1 + m2

def distMat( X, Y ):
    #return distance.euclidean(X,Y)
    return np.linalg.norm( X - Y )

CNMF = []
NMF = []

for i in range(N):
    cnmf = pymf.CNMF(X, num_bases=rank)
    cnmf.factorize(niter=niter)
    Wcnmf = np.asmatrix(cnmf.W)
    Hcnmf = np.asmatrix(cnmf.H)

    nmf = pymf.NMF(X, num_bases=rank)
    nmf.factorize(niter=niter)
    Wnmf = np.asmatrix(nmf.W)
    Hnmf = np.asmatrix(nmf.H)

    for r in range(rank):
        CNMF.append(Wcnmf[:,r]*Hcnmf[r,:])
        NMF.append(Wnmf[:,r]*Hnmf[r,:])

# Build distance matrix
RC = np.zeros((N,N))
RN = np.zeros((N,N))
M = N*N/2 - N/2
RCC = np.zeros((M))
RNN = np.zeros((M))
k = 0
for i in range(N):
    for j in range(i):
        RC[i,j] = distNMF( CNMF[2*i], CNMF[2*i+1], CNMF[2*j], CNMF[2*j+1] )
        RN[i,j] = distNMF( NMF[2*i], NMF[2*i+1], NMF[2*j], NMF[2*j+1] )
        RCC[k] = RC[i,j]
        RNN[k] = RN[i,j]
        k += 1

# fig = 10
# fig = plt.figure(fig)
# plt.clf()
# ax = fig.add_subplot(2, 1, 1)
# ax.imshow( RC, interpolation='nearest' )
# ax.set_title("C-NMF")
# ax = fig.add_subplot(2, 1, 2)
# ax.imshow( RN, interpolation='nearest' )
# ax.set_title("NMF")

histRNN = np.bincount(RNN.astype(int)) + 1
histRCC = np.ones(histRNN.shape)
h = np.bincount(RCC.astype(int)) + 1
histRCC[:h.shape[0]] = h

#print "histRNN", histRNN
#print "histRCC", histRCC

fig = 11
fig = plt.figure(fig, figsize=(5, 3))
plt.clf()
ax = fig.add_subplot(1, 1, 1)
#ax.plot( RCC )
ax.plot( histRCC, label='C-NMF' )
#ax.plot( np.sort(RCC)[::-1], label='C-NMF' )
#ax.plot( RNN )
ax.plot( histRNN, label='NMF' )
#ax.plot( np.sort(RNN)[::-1], label='NMF' )
ax.set_title("Consistency of C-NMF vs NMF")
ax.set_xlabel("Euclidean distance")
ax.set_ylabel("Count")
ax.set_yscale('log')
ax.legend()
plt.savefig('consistency.pdf', bbox_inches='tight')
plt.show()
```

```
from matplotlib import gridspec

X = read_ssm("/Users/uri/datasets/Segments/audio/Isophonics_01_-_Help!.mp3")
# X = read_ssm("/Users/uri/datasets/Segments/audio/Isophonics_01_-_I_Saw_Her_Standing_There.mp3")
# X = read_ssm("/Users/uri/datasets/Segments/audio/Isophonics_CD1_-_11_-_Black_Bird.mp3")
# X = read_ssm("/Users/uri/datasets/Segments/audio/Isophonics_05_-_Dig_It.mp3")
# X = read_ssm("/Users/uri/datasets/Segments/audio/Isophonics_05_-_And_I_Love_Her.mp3")
# X = read_chroma("/Users/uri/datasets/Segments/audio/Isophonics_01_-_Help!.mp3", m=9).T
X = read_chroma("/Users/uri/datasets/Segments/audio/Isophonics_05_-_And_I_Love_Her.mp3", m=9).T
# X = read_chroma("/Users/uri/datasets/Segments/audio/Isophonics_CD1_-_07_-_While_My_Guitar_Gently_Weeps.mp3", m=9).T

r = 4
niter = 300
fig = False
cmap = 'hot'

cF, cG, cW = cnmf(X, r, niter, fig=fig)
nF, nG = nmf(X, r, niter, fig=fig)

# plt.figure(figsize=(6,6))
# for rr in xrange(r):
#     cD = cF[:,rr] * cG[rr, :]
#     nD = nF[:,rr] * nG[rr, :]
#     print rr
#     plt.subplot(2,2,rr*2+1); plt.imshow(cD, interpolation="nearest")
#     plt.title("C-NMF")
#     plt.subplot(2,2,rr*2+2); plt.imshow(nD, interpolation="nearest")
#     plt.title("NMF")
# plt.show()

plt.figure(figsize=(14,4))
ax1 = plt.subplot2grid((5,14), (1,0), colspan=3, rowspan=3)
ax2 = plt.subplot2grid((5,14), (0,3), colspan=1, rowspan=5)
ax3 = plt.subplot2grid((5,14), (2,4), colspan=3, rowspan=1)
ax4 = plt.subplot2grid((5,14), (0,7), colspan=1, rowspan=5)
ax5 = plt.subplot2grid((5,14), (2,8), colspan=3, rowspan=1)
ax6 = plt.subplot2grid((5,14), (1,11), colspan=3, rowspan=3)
ax1.imshow(X, interpolation="nearest", aspect="auto", cmap=cmap)
ax1.set_title("X")
ax2.imshow(cW, interpolation="nearest", aspect="auto", cmap=cmap)
ax2.set_title("W")
ax2.set_xticks(xrange(r))
ax2.set_xticklabels(np.arange(r)+1)
ax2.text(-3.5, cW.shape[0]/2., r'$\times$', fontsize=15)
ax2.text(r - .25, cW.shape[0]/2., r'$\times$', fontsize=15)
ax3.imshow(cG, interpolation="nearest", aspect="auto", cmap=cmap)
ax3.set_yticks(xrange(r))
ax3.set_yticklabels(np.arange(r)+1)
ax3.set_title("G")
ax4.text(-3.5, cF.shape[0]/2. - 0.5, r'$=$', fontsize=15)
ax4.imshow(cF, interpolation="nearest", aspect="auto", cmap=cmap)
ax4.text(r - .25, cF.shape[0]/2. - 0.5, r'$\times$', fontsize=15)
ax4.set_title("F")
ax4.set_xticks(xrange(r))
ax4.set_xticklabels(np.arange(r)+1)
ax5.imshow(cG, interpolation="nearest", aspect="auto", cmap=cmap)
ax5.set_title("G")
ax5.set_yticks(xrange(r))
ax5.set_yticklabels(np.arange(r)+1)
ax6.imshow(cF * cG, interpolation="nearest", aspect="auto", cmap=cmap)
ax6.text(-25, nF.shape[0]/2., r'$=$', fontsize=15)
ax6.set_title("X'")
plt.tight_layout()
plt.savefig('cnmf-example.pdf', bbox_inches='tight')
plt.show()

plt.figure(figsize=(7, 4))
ax1 = plt.subplot2grid((5,7), (0,0), colspan=1, rowspan=5)
ax2 = plt.subplot2grid((5,7), (2,1), colspan=3, rowspan=1)
ax3 = plt.subplot2grid((5,7), (1,4), colspan=3, rowspan=3)
ax1.imshow(nF, interpolation="nearest", aspect="auto", cmap=cmap)
ax1.set_title("F")
ax1.set_xticks(xrange(r))
ax1.set_xticklabels(np.arange(r)+1)
ax1.text(r - 0.5, nF.shape[0]/2., r'$\times$', fontsize=15)
ax2.imshow(nG, interpolation="nearest", aspect="auto", cmap=cmap)
ax2.set_title("G")
ax2.set_yticks(xrange(r))
ax2.set_yticklabels(np.arange(r)+1)
ax3.imshow(nF * nG, interpolation="nearest", aspect="auto", cmap=cmap)
ax3.text(-20, nF.shape[0]/2., r'$=$', fontsize=15)
ax3.set_title("X'")
plt.tight_layout()
plt.savefig('nmf-example.pdf', bbox_inches='tight')
plt.show()
```

```
# Example of extraction of boundaries using C-NMF
# in_path = "/Users/uri/datasets/Segments/audio/Isophonics_05_-_And_I_Love_Her.mp3"
# in_path = "/Users/uri/datasets/Segments/audio/Isophonics_01_-_Come_Together.mp3"
# in_path = "/Users/uri/datasets/Segments/audio/Isophonics_03_-_Lucy_In_The_Sky_With_Diamonds.mp3"
# in_path = "/Users/uri/datasets/Segments/audio/Isophonics_CD1_-_10_-_I'm_So_Tired.mp3"
# in_path = "/Users/uri/datasets/Segments/audio/Isophonics_06_-_She's_Leaving_Home.mp3"
in_path = "/Users/uri/datasets/Segments/audio/Isophonics_08_-_Strawberry_Fields_Forever.mp3"

jam_file = in_path.replace(msaf.Dataset.audio_dir, msaf.Dataset.references_dir)
jam_file = jam_file.replace("mp3", "jams")
chroma, mfcc, tonnetz, beats, dur, anal = msaf.io.get_features(in_path, annot_beats=False)
h = 14
R = 10
X = read_chroma(in_path, m=h).T

ann_bounds = msaf.io.read_ref_bound_frames(in_path, beats)
ann_inter, ann_labels = jams2.converters.load_jams_range(jam_file,
                "sections", annotator=0, context=msaf.prefix_dict["Isophonics"])

print "Unique segments:", len(np.unique(ann_labels))
print "Annotated bounds: %s" % ann_bounds
print "Annotated labels: %s" % ann_labels
# logging.info("Estimated bounds: %s" % bound_idxs)

r = 4
niter = 300
fig = False
cmap = 'hot'

F, G, W = cnmf(X, r, niter, fig=fig)

G = G.T
orG = np.copy(G)
idx = np.argmax(G, axis=1)
max_idx = np.arange(G.shape[0])
max_idx = (max_idx, idx.flatten())
G[:, :] = 0
G[max_idx] = idx + 1

# TODO: Order matters?
oG = np.copy(G)
G = np.sum(G, axis=1)
mG = np.copy(G)
G = S.median_filter(G[:, np.newaxis], R)

bound_idxs = np.where(np.diff(G.flatten()) != 0)[0] + 1

# plt.imshow(X, interpolation="nearest", aspect="auto", cmap="hot"); plt.show()

plt.figure(figsize=(10, 5))
plt.subplot(2, 2, 1)
plt.imshow(orG.T, interpolation="nearest", aspect="auto", cmap="hot")
plt.title("(a) \n $G$")
plt.yticks(np.arange(r))
plt.xlabel("Time Frames")
plt.gca().set_yticklabels(np.arange(r)+1)
plt.subplot(2, 2, 2)
plt.imshow(oG.T, interpolation="nearest", aspect="auto", cmap="hot")
plt.title("(b) \n $\mathcal{G}$")
plt.yticks(np.arange(r))
plt.xlabel("Time Frames")
plt.gca().set_yticklabels(np.arange(r)+1)
plt.subplot(2, 2, 3)
plt.imshow(mG[:,np.newaxis].T, interpolation="nearest", aspect="auto", cmap="hot")
plt.title("(c) \n $\mathbf{g}$")
plt.yticks([])
plt.xlabel("Time Frames")
plt.subplot(2, 2, 4)
plt.imshow(G.T, interpolation="nearest", aspect="auto", cmap="hot")
plt.title("(d) \n $\mathbf{g}'$")
plt.yticks([])
plt.xlabel("Time Frames")
for ann_bound in ann_bounds[2:-2]:
    plt.axvline(ann_bound, ymin=0, ymax=1, linewidth=2.0, color="g")
plt.tight_layout()
plt.savefig(fig_dir + "cnmf-boundaries-example.pdf")
plt.show()
```

```
# Labels
print bound_idxs
print ann_labels

def most_frequent(x):
    """Returns the most frequent value in x."""
    return np.argmax(np.bincount(x))

def fill_frames(labels, bounds, N):
    """Fills up the frames with label ids."""
    label_frames = np.zeros(N)
    all_bounds = [0] + bounds.tolist()
    for i, (start, end) in enumerate(zip(all_bounds[:-1], all_bounds[1:])):
        label_frames[start:end] = labels[i]
    label_frames[all_bounds[-1]:] = labels[-1]
    return label_frames

ann_labels = MSAF.read_annot_int_labels(in_path)
print ann_labels

F, G, W = cnmf(X, r+1, niter, fig=fig)
G = G.T
orG = np.copy(G)
idx = np.argmax(G, axis=1)
max_idx = np.arange(G.shape[0])
max_idx = (max_idx, idx.flatten())
G[:, :] = 0
G[max_idx] = idx + 1

# TODO: Order matters?
oG = np.copy(G)
G = np.sum(G, axis=1)
mG = np.copy(G)
G = S.median_filter(G[:, np.newaxis], R-2)

N = len(G.flatten())
label_frames = G.flatten()
label_frames = np.asarray(label_frames, dtype=int)

est_labels = [label_frames[0]]
bound_inters = zip(bound_idxs[:-1], bound_idxs[1:])
for bound_inter in bound_inters:
    if bound_inter[1] - bound_inter[0] <= 0:
        est_labels.append(np.max(label_frames) + 1)
    else:
        est_labels.append(most_frequent(
            label_frames[bound_inter[0]: bound_inter[1]]))
    #print bound_inter, labels[-1]
est_labels.append(label_frames[-1])

label_frames = fill_frames(est_labels, bound_idxs, N)
ann_label_frames = fill_frames(ann_labels, ann_bounds[1:-1], N)

# PLOT
plt.figure(figsize=(10, 5))
plt.subplot(2, 1, 1)
pre_L = np.vstack((old_label_frames, G.T))
plt.imshow(pre_L, interpolation="nearest", aspect="auto", cmap="hot")
for bound_idx in bound_idxs:
    plt.axvline(bound_idx, ymin=0, ymax=1, linewidth=2.0, color="b")
plt.yticks([0,1])
plt.gca().set_yticklabels(["$\mathbf{g}'$", "$\mathbf{g}'_{r'}$"])
plt.xlabel("Time Frames")

plt.subplot(2, 1, 2)
L = np.vstack((label_frames[:, np.newaxis].T, ann_label_frames[:, np.newaxis].T))
plt.imshow(L, interpolation="nearest", aspect="auto", cmap="hot")
plt.yticks([0,1])
plt.gca().set_yticklabels(["Estimated", "Annotated"])
plt.xlabel("Time Frames")

plt.tight_layout()
plt.savefig("cnmf-labels-example.pdf")
plt.show()

print est_labels
print ann_labels

```

```
track_path = "/Users/uri/datasets/Segments/audio/Isophonics_05_-_And_I_Love_Her.mp3"
Xm = read_chroma(track_path, m=9).T
X = read_chroma(track_path, m=1).T

pitches = ["A", "A#", "B", "C", "C#", "D", "D#", "E", "F" ,"F#", "G", "G#"]
plt.figure(figsize=(9,6))
plt.subplot(2, 1, 1)
plt.imshow(X, interpolation="nearest", aspect="auto", cmap="hot")
plt.yticks(np.arange(12))
plt.gca().set_yticklabels(pitches)
plt.xlabel("Time Frames")
plt.subplot(2, 1, 2)
plt.imshow(Xm, interpolation="nearest", aspect="auto", cmap="hot")
plt.yticks(np.arange(12))
plt.gca().set_yticklabels(pitches)
plt.xlabel("Time Frames")
plt.tight_layout()
plt.savefig("chromagram-example.pdf")
plt.show()
```