# The Density Matrix & Mixed States

Throughout the majority of the Qiskit textbook, we have been representing the state of our qubits using the state-vector notation introduced in the [Representing Qubit States](../ch-states/representing-qubit-states.ipynb) section. Working with this representation is convenient when dealing with states that can always be expressed as a [linear combination](../ch-appendix/linear_algebra.ipynb#Spanning-Sets,-Linear-Dependence,-and-Bases) of basis states, each with an associated probability amplitude. However, in quantum computation and quantum communication, there are many practical scenarios in which the state of our qubits cannot be written down as linear combinations in a given basis, but instead must be expressed in terms of ensembles (statistical mixtures) of multiple states, each with an associated probability of occurrence.

For example, consider the case where Alice wants to send Bob the state $| + \rangle$. Imagine that, due to noise in their communication channel, there is a chance that the relative phase of the state could flip with some probability $p$. Therefore, Bob could end up with the "flipped" state $| - \rangle$ (with probability $p$), or with the desired state $| + \rangle$ (with probability $1 - p$). Notice that Bob’s state is either $| + \rangle$ **or** $| - \rangle$, but not a quantum superposition of the two. Thus, if we want to know what will happen to Bob’s qubit after he, for instance, applies some gates and performs a measurement, we will have to consider both of these cases separately. This might not be too difficult a task when dealing with only two states, but in situations where the number of possible states is larger, keeping track of how each of these states evolves individually can become impractical. As we will see throughout this section, this is where the density matrix representation comes in handy.

Simply put, the density matrix is an alternative way of expressing quantum states. However, unlike the state-vector representation, this formalism allows us to use the same mathematical language to describe both the more simple quantum states we've been working with so far (*pure states*), as well as the *mixed states* that consist of ensembles of pure states.

We will now formally introduce the density matrix notation by looking at the how it is used to represent both pure and mixed states.

## Contents
1. [Pure States](#pure)
    1.1 [Exercises](#ex1)
2. [Mixed States](#mixed)
    2.1 [Example](#example)
    2.2 [Exercises](#ex2)
3. [Properties of the Density Matrix](#properties)
4. [The Reduced Density Matrix](#reduced)
    4.1 [Exercises](#ex4)
5. [Mixed States in the Bloch Sphere](#bloch)

## 1. Pure States

Pure states are those for which we can precisely define their quantum state at every point in time. For example, if we initialize the single qubit $|q \rangle $ in state $ | 0 \rangle $, and apply a Hadamard gate, we know our final state will be:

$$
| q \rangle = \frac{1}{\sqrt{2}} \left( | 0 \rangle  + | 1 \rangle  \right) = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 \\ 1 \end{bmatrix} = | + \rangle
$$

We understand that if we were to perform a measurement of this state, the outcome will be probabilisitc. We will measure state $ | 0 \rangle $ with $ 50 \% $ probability, state $ | 1 \rangle $ with $ 50 \% $ probability. However, **before** performing any measurements we can say with $ 100 \% $ certainty that, if our qubit initialization process and our Hadamard gate are ideal, the resulting quantum state will always be $ | + \rangle $. We therefore say that, since there is no uncertainty on what this quantum state will be, $ |q \rangle $ is a pure state.

In general, we know that in the conventional state vector notation, an $n$-qubit pure state can be expressed as:

$$ | \psi \rangle = \begin{bmatrix} \alpha_0 \\ \alpha_1 \\ \vdots \\ \alpha_{N-1} \end{bmatrix}, $$

where $ N = 2^{n} $. An alternative way to express this pure quantum state is in the form of a matrix. This can be done by using the density operator representation, which is defined as:

$$ \rho \equiv  | \psi \rangle \langle \psi | $$

Here, the term $| \psi \rangle \langle \psi | $ represents the [outer product](../ch-gates/proving-universality.ipynb#outer) of the state $ \psi $ with itself:

$$
\begin{aligned}
& \rho = \begin{bmatrix} \alpha_0 \\ \alpha_1 \\ \vdots \\ \alpha_N \end{bmatrix} \begin{bmatrix} \alpha_0^* & \alpha_1^* & \dots & \alpha_N^* \end{bmatrix}
\\
\\
& \rho = \begin{bmatrix} |\alpha_0|^2 & \alpha_0 \alpha_1^* & \dots & \alpha_0 \alpha_N^* \\ \alpha_1 \alpha_0^* & |\alpha_1|^2 & \dots & \alpha_1 \alpha_N^* \\ \vdots & \vdots & \ddots & \vdots \\ \alpha_N \alpha_0^* & \alpha_N \alpha_1^* & \dots & |\alpha_N|^2 \end{bmatrix}
\end{aligned}
$$

Let's consider, for example, the following two-qubit, maximally-entangled pure state:

$$ | \psi_{AB} \rangle = \frac{1}{\sqrt{2}} \left ( | 0 0 \rangle + | 1 1 \rangle \right ) = \frac{1}{\sqrt{2}} \begin{bmatrix} 1 \\ 0 \\ 0 \\ 1 \end{bmatrix}
$$

The density matrix representation for this state is then given by:

$$
\begin{aligned}
& \rho_{AB}  = | \psi_{AB} \rangle \langle \psi_{AB} |
\\
\\
& \rho_{AB} = \left ( \frac{1}{\sqrt{2}} \begin{bmatrix} 1 \\ 0 \\ 0 \\ 1 \end{bmatrix} \right ) \left ( \frac{1}{\sqrt{2}} \begin{bmatrix} 1 & 0 & 0 & 1 \end{bmatrix} \right )
\\
\\
& \rho_{AB} = \frac{1}{2} \begin{bmatrix} 1 & 0 & 0 & 1 \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 0 \\ 1 & 0 & 0 & 1 \\ \end{bmatrix}
\end{aligned}
$$

In Qiskit, we can use the ``quantum_info`` module to represent quantum states either in state vector notation, or in the density matrix representation. For convenience, we will import this module as ``qi``:

```python
from qiskit import QuantumCircuit
import qiskit.quantum_info as qi
```

Let's once again consider the entangled pure state $| \psi_{AB} \rangle $. We can prepare this state by applying a Hadamard gate to the first qubit, and an CNOT between the first and second qubits:

```python
qc_AB = QuantumCircuit(2)
qc_AB.h(0)
qc_AB.cx(0,1)
qc_AB.draw()
```

To obtain the state constructed by our ``QuantumCircuit`` in state vector notation, we can make use of the ``Statevector.from_instruction()`` class method from the ``quantum_info`` module as follows:

```python
psi_AB = qi.Statevector.from_instruction(qc_AB)
psi_AB.draw('latex', prefix='|\\psi_{AB}\\rangle = ')
```

$$
|\psi_{AB}\rangle =
\begin{bmatrix}
\tfrac{1}{\sqrt{2}} & 0 & 0 & \tfrac{1}{\sqrt{2}}  \\
 \end{bmatrix}
$$

Similarly, we can use the ``DensityMatrix.from_instruction()`` class method to obtain density matrix representation for this same state:

```python
rho_AB = qi.DensityMatrix.from_instruction(qc_AB)
rho_AB.draw('latex', prefix='\\rho_{AB} = ')
```

$$
\rho_{AB} =
\begin{bmatrix}
\tfrac{1}{2} & 0 & 0 & \tfrac{1}{2}  \\
 0 & 0 & 0 & 0  \\
 0 & 0 & 0 & 0  \\
 \tfrac{1}{2} & 0 & 0 & \tfrac{1}{2}  \\
 \end{bmatrix}
$$

As expected, the result matches our calculation. We can also visualize the density matrix using a cityscape plot of the state:

```python
from qiskit.visualization import plot_state_city
plot_state_city(rho_AB.data, title='Density Matrix')
```

Now, all we have done so far is show a different way to represent quantum states, but there is no apparent advantage in doing so. To understand why the density matrix representation is beneficial, we need to learn about the concept of mixed states.

### 1.1 Exercises
Find the corresponding density matrix for the following pure states. Use Qiskit to prepare the states, and verify your results using the ``quantum_info`` module:
1.	$ \frac{1}{\sqrt{2}} \left ( |0\rangle - i |1\rangle \right ) $

2.	$ \frac{1}{2}\left ( |0 0 \rangle + |0 1 \rangle + |1 0 \rangle + |1 1 \rangle \right ) $

## 2. Mixed States

Mixed states are those that consist of statistical ensembles of different quantum states. This means that, unlike pure states, mixed states cannot be represented as linear superpositions of normalized state vectors. Lets first take a look at a simple example to explain what we mean by this.

Consider, once again, the two-qubit entangled state:

$$ | \psi_{AB} \rangle = \frac{1}{\sqrt{2}} \left ( | 0_A 0_B \rangle + | 1_A 1_B \rangle \right ) $$

Here we have explicitly used the subscripts $A$ and $B$ to label the qubits associated with registers $q_1$ and $q_0$, respectively. Now, let's assume that right after preparing our state $| \psi_{AB} \rangle $ we perform a measurement on register $q_1$, as shown below:

We can now ask the question: What will the state on register $q_0$ be right after performing the measurement on register $q_1$?

As we learned in the [Multiple Qubits and Entangled States](../ch-gates/multiple-qubits-entangled-states.ipynb#entangled) section, we know that since qubits $A$ and $B$ are entangled, measuring a ``0`` in register $q_1$ implies that the quantum state in register $q_0$ will immediately project onto state $| 0_B \rangle$:

$$ \frac{1}{\sqrt{2}}(|0_A 0_B\rangle + |1_A 1_B\rangle) \quad \xrightarrow[]{\text{measure}} \quad |0_A\rangle | 0_B\rangle \quad \text{(with probability of 50%)} $$

Similarly, measuring a ``1`` in register $q_1$ will project $q_0$ onto state $ | 1_B \rangle $:

$$ \frac{1}{\sqrt{2}}(|0_A 0_B\rangle + |1_A 1_B\rangle) \quad \xrightarrow[]{\text{measure}} \quad |1_A\rangle | 1_B\rangle \quad \text{(with probability of 50%)} $$

So how do we, in general, represent the final state in register $q_0$ (labeled as $\psi_B$ in the diagram), not for a specific measurement outcome in $q_1$, but for an arbitrary result of this measurement process?

We know that after a measurement, $\psi_B$ will be in state $| 0_B \rangle $ with probability $1/2$, or in state $| 1_B \rangle $ with probability $1/2$; however, $\psi_B$ is **not** in a linear superposition of $| 0_B \rangle $ and $| 1_B \rangle $. In other words, $\psi_B$ **cannot** be expressed as a state vector of the form $1/\sqrt{2} \left (|0_B \rangle + | 1_B \rangle \right)$. Instead, we have to use a different notation to write down that $\psi_B$ is rather an ensemble (not a quantum superposition) of the states $| 0_B \rangle $ and $| 1_B \rangle $, and whose outcome depends on what we measure on register $q_1$.

We then call $\psi_B$ a mixed state, which can be represented as an ensemble of states:

$$
\left \{| \psi_{B_0} \rangle , | \psi_{B_1} \rangle \right \} = \left \{ | 0_B \rangle , | 1_B \rangle \right \},
$$

each with an associated probability of occurrence:

$$ \left \{ p_0, p_1 \right \} = \left \{ 1/2, 1/2 \right \} $$

What this representation shows is that, upon measurement of $q_1$, $\psi_B$ will be in either state $| 0_B \rangle $ ``or`` state $| 1_B \rangle $, each with a classical probability of occurrence of $ 1/2$. Notice we avoided using the ket notation for state $\psi_B$ since we reserve kets only for state vectors that can be written as linear combinations in an orthonormal basis, which $\psi_B$ is not.

In general, a mixed state consisting of an ensemble of n pure states can be expressed in the form of a list of outcome elements:

$$
\left \{ |\psi_j \rangle \right \}_{j = 1}^n = \left \{ | \psi_1 \rangle, | \psi_2 \rangle, \dots, | \psi_n \rangle \right \},
$$

where each item has a corresponding probability of occurrence given by:

$$
\left \{ p_j \right \}_{j = 1}^n = \left \{ p_1, p_2, \dots, p_n \right \}
$$

Although this way of expressing $\psi_B$ (or any general mixed state) is perfectly valid, it turns out to be somewhat inconvenient. Since a mixed state can consist of a myriad of pure states, it can be difficult to track how the whole ensemble evolves when, for example, gates are applied to it. It is here that we turn to the density matrix representation.

A mixed state, consisting of several possible outcome pure states $|\psi_j \rangle$ , each with probability of occurrence $p_j$, is defined as a density matrix of the form:

$$ \rho \equiv \sum_{j} p_j |\psi_j \rangle \langle \psi_j | $$

It is easy to see that this general definition of the density matrix also holds for pure states, for which we will only have one $ |\psi_j \rangle $ term with $p_j = 1$.

So, going back to our example, we know that since the two possible outcomes of state $\psi_B$ are $| 0_B \rangle $ or $| 1_B \rangle $, both with classical probability of occurence of $1/2$, we can construct the following density matrix for this state:

$$
\begin{aligned}
\rho_B & = \frac{1}{2} | 0_B \rangle \langle 0_B | + \frac{1}{2} | 1_B \rangle \langle 1_B |
\\
\\
& = \frac{1}{2} \begin{bmatrix} 1 & 0 \end{bmatrix} \begin{bmatrix} 1 \\ 0 \end{bmatrix} + \frac{1}{2} \begin{bmatrix} 0 & 1 \end{bmatrix} \begin{bmatrix} 0 \\ 1 \end{bmatrix}
\\
\\
& = \frac{1}{2} \begin{bmatrix} 1 & 0 \\ 0 & 0 \end{bmatrix} + \frac{1}{2} \begin{bmatrix} 0 & 0 \\ 0 & 1 \end{bmatrix}
\\
\\
& = \frac{1}{2} \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}
\end{aligned}
$$

At this point, it is important to highlight that the $|\psi_j \rangle $ states that compose an ensemble need not be basis states (like $ | 0 \rangle $ and $ | 1 \rangle )$; these states can in fact be any arbitrary normalized pure state, as we will show in the next example.

### 2.1 Example

Let's consider the case where we initialize a qubit in the $ |0 \rangle $ state, and then apply a Hadamard gate. Now, unlike the scenario we described for [pure states](#pure), this Hadamard gate is not ideal. Due to errors in the quantum-computer hardware, only 80% of the times the state is prepared, this Hadamard gate produces the desired state:

$$ | \psi_1 \rangle = \frac{1}{\sqrt{2}} \left( | 0 \rangle  + | 1 \rangle  \right) $$

The remaining 20% of the times, the pulse applied to rotate the state is either too short or too long by $\frac{\pi}{6}$ radians about the x-axis. This means that when we use this Hadamard gate, we could end up with following two undesired outcome states:

$$ | \psi_2 \rangle = \frac{\sqrt{3}}{2}| 0 \rangle  + \frac{1}{2} | 1 \rangle, \quad \quad | \psi_3 \rangle = \frac{1}{2} | 0 \rangle  + \frac{\sqrt{3}}{2} | 1 \rangle $$

The figure below shows the Bloch representation for the three possible states our qubit could take if the short pulse happens 10% of the time, and the long pulse the remaining 10% of the time:

Since we do not know the outcome of our qubit everytime we prepare it, we can represent it as a mixed state of the form:

$$ \rho_H = \frac{4}{5} | \psi_1 \rangle \langle \psi_1 | + \frac{1}{10} | \psi_2 \rangle \langle \psi_2 | + \frac{1}{10} | \psi_3 \rangle \langle \psi_3 | $$

Here, the factors $\frac{4}{5}, \frac{1}{10}$ and $\frac{1}{10}$ correspond to the classical probabilities of obtaining the states $| \psi_1 \rangle, | \psi_2 \rangle$ and $| \psi_3 \rangle$, respectively.

By replacing each of these three possible state vectors into $\rho$, we can find the density matrix that represents this mixture:

$$
\begin{aligned}
& \rho_H = \frac{4}{5} \begin{bmatrix} \frac{1}{2} & \frac{1}{2} \\ \frac{1}{2} & \frac{1}{2} \end{bmatrix} + \frac{1}{10} \begin{bmatrix} \frac{3}{4} & \frac{\sqrt{3}}{4} \\ \frac{\sqrt{3}}{4} & \frac{1}{4} \end{bmatrix} + \frac{1}{10} \begin{bmatrix} \frac{1}{4} & \frac{\sqrt{3}}{4} \\ \frac{\sqrt{3}}{4} & \frac{3}{4} \end{bmatrix}
\\
\\
& \rho_H = \begin{bmatrix} \frac{1}{2} & \frac{\sqrt{3}}{20} + \frac{2}{5} \\ \frac{\sqrt{3}}{20} + \frac{2}{5} & \frac{1}{2} \end{bmatrix}
\end{aligned}
$$

In Qiskit, we can define the density matrix of mixed states by directly inputting the matrix values into the ``DensityMatrix`` class:

```python
import numpy as np
rho_H_matrix = np.array([[1/2,np.sqrt(3)/20 + 2/5],[np.sqrt(3)/20 + 2/5,1/2]])
rho_H = qi.DensityMatrix(rho_H_matrix)
rho_H.draw('latex', prefix='\\rho_H = ')
```

$$
\rho_H =
\begin{bmatrix}
\tfrac{1}{2} & 0.4866  \\
 0.4866 & \tfrac{1}{2}  \\
 \end{bmatrix}
$$

From this example, we can see how the density matrix representation can help us construct useful and realistic models that capture the effects of non-ideal (noisy) environmental or external sources on both ideal quantum states and quantum gates.

### 2.2 Exercises

Derive and compare the density matrices for:

1. A pure one-qubit state in an equal superposition of $| 0 \rangle$ and $| 1 \rangle $:

    $ \rho_p = | \psi_p \rangle \langle \psi_p |, \text{ with } |\psi_p \rangle = \tfrac{1}{\sqrt{2}} \left ( | 0 \rangle + | 1 \rangle \right )$

2. A mixed one-qubit state in an equal mixture of $| 0 \rangle $ and $| 1 \rangle $:

    $ \rho_{m} = \frac{1}{2} | 0 \rangle \langle 0 | + \frac{1}{2} | 1 \rangle \langle 1 | $

## 3. Properties of the Density Matrix

### Unitary Evolution

A very natural question to ask at this point is: How do mixed states evolve under [unitary operations](../ch-gates/proving-universality.ipynb#2.2-Unitary-and-Hermitian-matrices-)? It can be shown, with little effort, that if an initial arbitrary state $|\psi_j \rangle$ with probability of occurrence $p_j$ evolves into the state $\hat U|\psi_j \rangle$ after a unitary evolution, then the evolution of a density matrix, consisting of an essemble of normalized states $\{|\psi_j \rangle \}_{j = 1}^{n}$ with probabilities $\{p_j\}_{j = 1}^{n}$, is given by:

$$
\rho = \sum_{j} p_j |\psi_j \rangle \langle \psi_j | \enspace \xrightarrow[]{\enspace \hat U \enspace} \enspace \rho' = \sum_{j} p_j \hat U |\psi_j \rangle \langle \psi_j | \hat U^{\dagger} = \hat U \rho \hat U^{\dagger}
$$

Here, $\hat U^{\dagger}$ is the conjugate transpose of the operator $\hat U$. So, for example, let's consider the following mixed state:

$$
\begin{align}
\rho_{0} &= \frac{1}{3} | 1 \rangle \langle 1 | + \frac{2}{3} | + \rangle \langle + |
\\
\\
&= \frac{1}{3} \begin{bmatrix} 0 & 0 \\ 0 & 1 \end{bmatrix} + \frac{2}{3} \begin{bmatrix} \frac{1}{2} & \frac{1}{2} \\ \frac{1}{2} & \frac{1}{2} \end{bmatrix}
\\
\\
&= \begin{bmatrix} \frac{1}{3} & \frac{1}{3} \\ \frac{1}{3} & \frac{2}{3} \end{bmatrix}
\end{align}
$$

If we are interested in knowing what will be the resulting state $\rho_{0}'$ after a [Y Pauli gate](../ch-states/single-qubit-gates.ipynb#ynzgatez) is applied, we then have:

$$
\begin{align}
\rho_{0}' = Y \rho_{0} Y^{\dagger} &= \begin{bmatrix} 0 & -i \\ i & 0 \end{bmatrix} \begin{bmatrix} \frac{1}{3} & \frac{1}{3} \\ \frac{1}{3} & \frac{2}{3} \end{bmatrix} \begin{bmatrix} 0 & i \\ -i & 0 \end{bmatrix}
\\
\\
&= \begin{bmatrix} -\frac{2}{3} & \frac{1}{3} \\ \frac{1}{3} & -\frac{1}{3} \end{bmatrix}
\end{align}
$$

We can also use Qiskit to evolve mixed states through unitary operators. Let's first define our state by using the `DensityMatrix.from_label()` method:

```python
rho_0 = 1/3*qi.DensityMatrix.from_label('1') + 2/3*qi.DensityMatrix.from_label('+')
rho_0.draw('latex', prefix='\\rho_0 = ')
```

$$
\rho_0 =
\begin{bmatrix}
\tfrac{1}{3} & \tfrac{1}{3}  \\
 \tfrac{1}{3} & \tfrac{2}{3}  \\
 \end{bmatrix}
$$

We can now define our operator in a similar way, but using the matrix `Operator` class:

```python
from qiskit.visualization import array_to_latex
Y = qi.Operator.from_label('Y')
array_to_latex(Y.data, prefix='Y =')
```

$$
Y =
\begin{bmatrix}
0 & -i  \\
 i & 0  \\
 \end{bmatrix}
$$

Lastly, we can evolve our mixed state by using the `DensityMatrix.evolve()` method:

```python
rho_0p = rho_0.evolve(Y)
rho_0p.draw('latex', prefix='\\rho\'_0 = ')
```

$$
\rho'_0 =
\begin{bmatrix}
\tfrac{2}{3} & -\tfrac{1}{3}  \\
 -\tfrac{1}{3} & \tfrac{1}{3}  \\
 \end{bmatrix}
$$

As expected, this result matches our calculation of $\rho_0'$ up to a global phase of -1.

### Matrix Elements

We understand that in the case of pure states in the state vector representation, each vector element corresponds to a probability amplitude. But what do the elements of the density matrix represent?

Consider, once again, a general mixed state $\rho$ consisting of an ensemble of pure states $\{|\psi_j \rangle \}_{j = 1}^{n}$, each with probability of occurrence $\{p_j\}_{j = 1}^{n}$:

$$ \rho = \sum_{j} p_j |\psi_j \rangle \langle \psi_j | $$

We know that each individual pure state $ |\psi_j \rangle $ can be written as a linear superposition of elements forming a complete orthonormal basis $\left \{ |\phi_u \rangle \right \}_{u = 1}^{m}$:

$$ |\psi_j \rangle = \sum_{u} \alpha_{u}^{(j)} |\phi_u \rangle $$

We can then replace this expression into our definition of our general mixed state, and get a density matrix in terms of the orthonormal basis elements:

$$
\rho = \sum_{j} p_j \left ( \sum_{u} \alpha_{u}^{(j)} |\phi_u \rangle \right ) \left (  \sum_{v} \left ( \alpha_{v}^{(j)} \right )^* \langle \phi_v | \right )
$$

Since the coefficients $ p_j, \alpha_{u}^{(j)}, \left ( \alpha_{v}^{(j)} \right )^* $ are just numbers, we can reorganize the expression as:

$$
\begin{aligned}
& \rho = \sum_{u, v} \left ( \sum_{j} p_j \alpha_{u}^{(j)} \left ( \alpha_{v}^{(j)} \right )^* \right ) |\phi_u \rangle \langle \phi_v |
\\
\\
& \rho = \sum_{u,v} \rho_{uv} |\phi_u \rangle \langle \phi_v |,
\end{aligned}
$$

where $\rho_{uv}$ are the individual matrix elements in the $\left \{ |\phi_u \rangle \right \}_{u = 1}^{m}$ basis (the term inside parentheses). Written in matrix form, $\rho$ is given by:

$$
\rho = \begin{bmatrix} \rho_{11} & \rho_{12} & \dots & \rho_{1m} \\ \rho_{21} & \rho_{22} & \dots & \rho_{2m} \\ \vdots & \vdots & \ddots & \vdots \\ \rho_{m1} & \rho_{m2} & \dots & \rho_{mm} \end{bmatrix}
$$

It's worth noticing that, in $\rho_{uv}$, the diagonal terms $\rho_{kk}$ actually correspond to the probability of finding the system in a particular basis state $|\phi_k \rangle$:

$$
\rho_{kk} = \sum_{j} p_j \alpha_{k}^{(j)} \left ( \alpha_{k}^{(j)} \right )^* = \sum_{j} p_j |\alpha_{k}^{(j)} |^{2}
$$

Here, $|\alpha_{k}^{(j)} |^{2}$ is corresponds to probability of finding the basis state $|\phi_k \rangle$ within a given $ |\psi_j \rangle $ state, so summing over all $p_j$ values gives us the total probability of the whole system being in state $|\phi_k \rangle$.

On the other hand, the off-diagonal terms of the matrix are a measure of the *coherence* between the different basis states of the system. In other words, they can be used to quantify how a pure superposition state could evolve (decohere) into a mixed state.

Lastly, it is not very difficult to show from the definition of $\rho_{uv}$ that $\rho_{vu}^* = \rho_{uv}$, which basically means the density matrix is Hermitian ($\rho^\dagger = \rho$).

### Trace and Positivity Conditions

A density matrix that represents a valid pure or mixed state must satisfy two conditions:

1. Its trace must be equal to one:

    Remembering that the [trace of a matrix](https://en.wikipedia.org/wiki/Trace_(linear_algebra)) (denoted as $\text{Tr}$) is the sum of its diagonal terms, we have:

    $$ \text{Tr}(\rho) =\sum_{k} \rho_{kk} = \sum_{k} \sum_{j} p_j |\alpha_{k}^{(j)} |^{2} = \sum_{j} p_j \sum_{k} |\alpha_{k}^{(j)} |^{2} = 1
    $$

    This follows as all basis states are normalized, and all probabilities must add to 1:

    $$ \sum_{k} |\alpha_{k}^{(j)} |^{2} = 1 \quad \text{and} \quad \sum_{j} p_j = 1, $$

2. The matrix must be [positive-definite](https://en.wikipedia.org/wiki/Definite_matrix):

    For an arbitrary state $|\psi_q \rangle $ that is part of the state space:

    $$
    \langle \psi_q | \rho | \psi_q \rangle = \sum_{j} p_j \langle \psi_q |\psi_j \rangle \langle \psi_j | \psi_q \rangle = \sum_{j} p_j |\langle \psi_q |\psi_j \rangle |^{2} \geq 0
    $$

### State Purity

A very useful property of the density matrix is that when taking the trace $\text{Tr}$ of its square $\rho^{2}$, we obtain a scalar value $\gamma$ that is good measure of the purity of the state the matrix represents. For normalized states, this value is always less than or equal to 1, with the equality occurring for the case of a pure state:

$$ \gamma \equiv \text{Tr}(\rho^{2}) \leq 1 $$

$$ \gamma \equiv \text{Tr}(\rho^{2}) = 1 \; \text{if pure} $$

In Qiskit, we can easily extract the purity of a density matrix by using the `purity()` class method. For example, for the pure state $| + \rangle$, we should expect to see a purity of 1:

```python
rho_p = qi.DensityMatrix.from_label('+')
display(rho_p.draw('latex', prefix='\\rho_p = '))
gamma_p = rho_p.purity()
print("State purity: ", np.round(np.real(gamma_p),3))
```

$$
\rho_p =
\begin{bmatrix}
\tfrac{1}{2} & \tfrac{1}{2}  \\
 \tfrac{1}{2} & \tfrac{1}{2}  \\
 \end{bmatrix}
$$

    State purity:  1.0

And, for a mixed state, like $\rho_m = \frac{1}{2} | 0 \rangle \langle 0 | + \frac{1}{2} | 1 \rangle \langle 1 | $, we expect a purity of less than 1:

```python
rho_m = 1/2*(qi.DensityMatrix.from_label('0') + qi.DensityMatrix.from_label('1'))
display(rho_m.draw('latex', prefix='\\rho_m = '))
gamma_m = rho_m.purity()
print("State purity: ", np.round(np.real(gamma_m),3))
```

$$
\rho_m =
\begin{bmatrix}
\tfrac{1}{2} & 0  \\
 0 & \tfrac{1}{2}  \\
 \end{bmatrix}
$$

    State purity:  0.5

### Non-uniqueness

One of the drawbacks of representing the density matrices in terms of ensembles of basis states, is that their outcome is not unique. Consider, for example, the following two mixed states:

$$
\rho_{m1} = \frac{1}{2} | 0 \rangle \langle 0 | + \frac{1}{2} | 1 \rangle \langle 1 | \quad \text{and} \quad \rho_{m2} = \frac{1}{2} | + \rangle \langle + | + \frac{1}{2} | - \rangle \langle - |
$$

These are clearly mixtures of different pure states; however, if we express $\rho_{m2} $ in the computational basis, we get:

$$
\begin{aligned}
\rho_{m2} & = \frac{1}{2} \left ( \frac{1}{\sqrt{2}} \left( | 0 \rangle + | 1 \rangle \right ) \frac{1}{\sqrt{2}} \left( \langle 0 | + \langle 1 | \right ) \right ) + \frac{1}{2} \left ( \frac{1}{\sqrt{2}} \left( | 0 \rangle - | 1 \rangle \right ) \frac{1}{\sqrt{2}} \left( \langle 0 | - \langle 1 | \right ) \right )
\\
\\
& = \frac{1}{4} \left ( | 0 \rangle \langle 0 | + | 0 \rangle \langle 1 | + | 1 \rangle \langle 0 | + | 1 \rangle \langle 1 | \right ) + \frac{1}{4} \left ( | 0 \rangle \langle 0 | - | 0 \rangle \langle 1 | - | 1 \rangle \langle 0 | + | 1 \rangle \langle 1 | \right )
\\
\\
& = \frac{1}{2} | 0 \rangle \langle 0 | + \frac{1}{2} | 1 \rangle \langle 1 |,
\end{aligned}
$$

which is identical to $\rho_{m1}$. Understanding that the same density matrix can represent many different ensembles of quantum states is important to avoid drawing conclusions about a particular system simply based on its density matrix representation alone. For example, if we were to sample quantum states from the system described by $\rho_{m1}$, we will get different states than if we were to sample $\rho_{m2}$ ($|0 \rangle $ or $|1 \rangle $ vs. $|+ \rangle $ or $|- \rangle $). So, even though these density matrices correctly capture the outcome probabilities of $\rho_{m1}$ and $\rho_{m2}$ upon measurement, care must be taken when using this representation if subtleties of a system must be understood.

## 4. The Reduced Density Matrix

Another advantage of working with the density matrix notation is that, when dealing with composite systems, it provides a practical way to extract the state of each subsystem, even if they are entangled. This is done in the form of what is known as the reduced density matrix.

Consider a quantum system composed of subsystems $A$ and $B$, and fully described by the density matrix $\rho_{AB}$. The reduced density matrix of subsystem $A$ is then given by:

$$ \rho_{A} = \text{Tr}_B(\rho_{AB}), $$

Here, $\text{Tr}_B$ is an operation known as the partial trace, which is defined as:

$$
\text{Tr}_B \left (| \xi_u \rangle \langle \xi_v | \otimes | \chi_u \rangle \langle \chi_v | \right ) \equiv | \xi_u \rangle \langle \xi_v | \text{ Tr} \left ( | \chi_u \rangle \langle \chi_v | \right )
$$

$| \xi_u \rangle $ and $| \xi_v \rangle $ are arbitrary states in the subspace of $A$, and $| \chi_u \rangle $ and $| \chi_v \rangle $ arbitrary states in the subspace of $B$. $\text{Tr}$ is the standard trace operation, which for two arbitrary states $\text{Tr} \left ( | \chi_u \rangle \langle \chi_v | \right ) = \langle \chi_v |\chi_u \rangle $. Similarly, we can calculate the reduced density matrix of subsystem $B$ using the partial trace over $A$:

$$
\text{Tr}_A \left (| \xi_u \rangle \langle \xi_v | \otimes | \chi_u \rangle \langle \chi_v | \right ) \equiv \text{Tr} \left ( | \xi_u \rangle \langle \xi_v | \right ) | \chi_u \rangle \langle \chi_v |
$$

As an example, let's reconsider the pure entangled state:

$$ | \psi_{AB} \rangle = \frac{1}{\sqrt{2}} \left ( | 0_A 0_B \rangle + | 1_A 1_B \rangle \right ) $$

This system is then composed of single-qubit subsystem $A$ with basis vectors $ \left \{ |\xi_1 \rangle, |\xi_2 \rangle \right \} = \{ | 0_A \rangle, | 1_A \rangle \}$, and single-qubit subsystem $B$ with basis vectors $ \left \{ |\chi_1 \rangle, |\chi_2 \rangle \right \} = \{ | 0_B \rangle, | 1_B \rangle \}$. We know that this system is not separable (i.e., $| \chi_{AB} \rangle \neq |\chi_{A}\rangle \otimes |\chi_{B}\rangle$); however, by using the reduced density matrix, we can find a full description for subsystems $A$ and $B$ as follows.

The density matrix of our state $| \psi_{AB} \rangle$ can be expressed in terms of outer products of the basis vectors as:

$$
\rho_{AB} = | \psi_{AB} \rangle \langle \psi_{AB} | =  \frac{1}{2} \left [ | 0_A 0_B \rangle \langle 0_A 0_B | + | 0_A 0_B \rangle \langle 1_A 1_B | + | 1_A 1_B \rangle \langle 0_A 0_B | + | 1_A 1_B \rangle \langle 1_A 1_B | \right ]
$$

Now, to calculate the reduced density matrix for, let's say, subsystem $B$, we have:

$$
\begin{aligned}
\rho_{B} & = \text{Tr}_A(\rho_{AB})
\\
\\
& = \frac{1}{2}\left [ \text{Tr}_A(| 0_A 0_B \rangle \langle 0_A 0_B |) + \text{Tr}_A(| 0_A 0_B \rangle \langle 1_A 1_B |) + \text{Tr}_A(| 1_A 1_B \rangle \langle 0_A 0_B |) + \text{Tr}_A(| 1_A 1_B \rangle \langle 1_A 1_B |) \right ]
\\
\\
& = \frac{1}{2}\left [ \text{Tr}(| 0_A \rangle \langle 0_A |)| 0_B \rangle \langle 0_B | + \text{Tr}(| 0_A \rangle \langle 1_A |)| 0_B \rangle \langle 1_B | + \text{Tr}(| 1_A \rangle \langle 0_A |) | 1_B \rangle \langle 0_B | + \text{Tr}(| 1_A \rangle \langle 1_A |) | 1_B \rangle \langle 1_B | \right ]
\\
\\
& = \frac{1}{2}\left [ \langle 0_A | 0_A \rangle | 0_B \rangle \langle 0_B | + \langle 1_A | 0_A \rangle | 0_B \rangle \langle 1_B | + \langle 0_A | 1_A \rangle | 1_B \rangle \langle 0_B | + \langle 1_A | 1_A \rangle | 1_B \rangle \langle 1_B | \right ]
\\
\\
& = \frac{1}{2}\left [ | 0_B \rangle \langle 0_B | + | 1_B \rangle \langle 1_B | \right ]
\\
\\
&= \frac{1}{2} \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix}
\end{aligned}
$$

At first glance, this result might seem rather strange. We started with the pure entangled state $|\psi_{AB} \rangle $, and  calculated that the state of one of its parts (subsystem $B$) is the mixed state $\rho_{B}$. However, going back to the first example given in [section 2](#mixed) above, we see that the result from calculating the reduced density matrix for $B$ is equivalent to the representation we obtained for $\psi_B$, when measurements were taken over qubit $A$. We can then conclude that the reduced density matrix $\rho_{B}$ is a way to describe the statistical outcomes of subsystem $B$, when the measurement outcomes of subsystem $A$ are averaged out. This is in fact what ”tracing out” subsystem $A$ means.

It is worth mentioning that so far we have described the concept of partial trace for a bipartite (two-part) system, but this can be generalized for multi-part systems.

In Qiskit, we can easily obtain the reduced density matrix of a system by using the ``partial_trace()`` function and passing the density matrix of the composite system, and a list with the subsystems to trace over:

```python
rho_B = qi.partial_trace(rho_AB,[0])
rho_A = qi.partial_trace(rho_AB,[1])

display(rho_B.draw('latex', prefix=" \\rho_{B} = "),
        rho_A.draw('latex', prefix=" \\rho_{A} = "))
```

$$
 \rho_{B} =
\begin{bmatrix}
\tfrac{1}{2} & 0  \\
 0 & \tfrac{1}{2}  \\
 \end{bmatrix}
$$

$$
 \rho_{A} =
\begin{bmatrix}
\tfrac{1}{2} & 0  \\
 0 & \tfrac{1}{2}  \\
 \end{bmatrix}
$$

In this specific example, $\rho_A$ and $\rho_b$ are equal, but this is not always the case.

### 4.1 Exercises
Calculate the reduced density matrices for each of the following composite states.

1. The pure state: $ |\psi_{pr} \rangle = \frac{1}{2} \left ( | 00 \rangle + | 01 \rangle + | 10 \rangle + | 11 \rangle \right )$

2. The mixed state: $ \rho_{mr} = \frac{1}{4} | \Phi^+ \rangle \langle \Phi^+ | + \frac{3}{4} | \Phi^- \rangle \langle \Phi^- |$, where $| \Phi^+ \rangle$ and $| \Phi^- \rangle$ are the Bell states:

    $| \Phi^+ \rangle = \frac{1}{\sqrt{2}} \left( |00 \rangle + |11 \rangle \right ) $

    $| \Phi^- \rangle = \frac{1}{\sqrt{2}} \left( |00 \rangle - |11 \rangle \right ) $

## 5. Mixed States in the Bloch Sphere

In the [Representing Qubit States](../ch-states/representing-qubit-states.ipynb#bloch-sphere) chapter, we learned how the state vector of a qubit can be visualized using the Bloch sphere representation. By parameterizing the probability amplitudes as a function of a polar angle $\theta$, and azimuthal angle $\varphi$, any normalized single-qubit state can be expressed as:

$$
|q \rangle = \cos\left(\frac{\theta}{2} \right ) | 0 \rangle + e^{i\varphi}\sin \left(\frac{\theta}{2} \right ) | 1 \rangle.
$$

The qubit can therefore be represented as a vector extending from the origin to the surface of a sphere of unit radius, with its orientation determined by these two angles.

This geometrical interpretation of states can be generalized to also include mixed states. This is done by relying on the fact that the density matrix of a single-qubit state can be expanded in the form:

$$
\begin{align}
& \rho = \frac{1}{2} \left ( \hat I + \vec r \cdot \hat{ \vec \sigma} \right )
\\
\\
& \rho = \frac{1}{2} \hat I + \frac{1}{2} r_x \hat \sigma_x + \frac{1}{2} r_y \hat \sigma_y + \frac{1}{2} r_z \hat \sigma_z,
\end{align}
$$

where $\hat I$ is the identity matrix, and $\hat \sigma_x, \hat \sigma_y, \hat \sigma_z $ the $X, Y, Z$ [Pauli matrices](../ch-appendix/linear_algebra.ipynb), respectively:

$$
\hat I = \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix},
\quad
\hat \sigma_x = \begin{bmatrix} 0 & 1 \\ 1 & 0 \end{bmatrix},
\quad
\hat \sigma_y = \begin{bmatrix} 0 & -i \\ i & 0 \end{bmatrix},
\quad
\hat \sigma_z = \begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix}
$$

The $r_x$, $r_y$ and $r_z$ coefficients correspond to the components of what is known as the Bloch vector $\vec r$. This is in fact the vector that represents our mixed state inside the Bloch sphere. Replacing the matrices into the expression for $\rho$ and simplifying:

$$
\rho = \frac{1}{2} \begin{bmatrix} 1 + r_z & r_x - i r_y \\ r_x + i r_y & 1 - r_z \end{bmatrix}
$$

Let's reconsider the [example](#example) from section 2, where we obtained the density matrix:

$$
\rho_H = \begin{bmatrix} \frac{1}{2} & \frac{\sqrt{3}}{20} + \frac{2}{5} \\ \frac{\sqrt{3}}{20} + \frac{2}{5} & \frac{1}{2} \end{bmatrix}
$$

It is easy to see that, for this particular case:

$$ r_x = 2 \left(\frac{\sqrt{3}}{20} + \frac{2}{5} \right), \quad r_y = 0, \quad r_z = 0 $$

This means that, in the bloch sphere, $\rho_H$ is represented by a vector $\vec r$ extending from the origin in the positive x direction, with length $ r_x \approx 0.973 $.

In Qiskit, we can use the ``plot_bloch_multivector()`` function to plot the density matrix of our mixed state in the Bloch sphere:

```python
from qiskit.visualization import plot_bloch_multivector
plot_bloch_multivector(rho_H.data)
```

So, as expected, we get a vector along the positive x axis, with length slightly smaller than 1. This is a very convenient way of expressing that this state is not pure because it has been corrupted by noise. Rather than having to use three separate Bloch spheres to represent each of the three possible pure-state outcomes given in the example ($\psi_1, \psi_2, \psi_3 $), we now have a single Bloch vector representation for our noisy state.

One last scenario we might want to consider is the possibility of visualizing multi-qubit states using multiple Bloch spheres. We previously learned that, by the use of the [reduced density matrix](#reduced), we can actually find a representation for each individual part that makes up a composite state, even if the state is entangled. For instance, let's look at the following partially-entangled state:

$$
| \psi_{CD} \rangle = \frac{1}{2\sqrt{2}}\left ( \sqrt{3} | 0_C0_D \rangle + | 0_C1_D \rangle + | 1_C0_D \rangle + \sqrt{3} | 1_C1_D \rangle \right ).
$$

Here, $| 0_C0_D \rangle $ and $| 1_C1_D \rangle $ have the same probability of occurrence, but are 3 times more likely to occur than $ | 0_C1_D \rangle $ and $| 1_C0_D \rangle$. Since this is an entangled state, we know that it is not separable (i.e., $| \psi_{CD} \rangle \neq |\psi_{C}\rangle \otimes |\psi_{D}\rangle$); therefore, this state cannot be represented in terms of unit vectors in two individual Bloch spheres. However, by expressing $C$ and $D$ in terms of their reduced density matrices $\rho_C$ and $\rho_D$, we can then visualize the composite state as two Bloch vectors, one for each of these matrices.

In Qiskit, state $| \psi_{CD} \rangle $ can be generated using the following circuit:

```python
qc_CD = QuantumCircuit(2)
qc_CD.ry(np.pi/3,0)
qc_CD.h(1)
qc_CD.cx(0,1)
qc_CD.cx(1,0)
qc_CD.draw()
```

```python
psi_CD = qi.Statevector.from_instruction(qc_CD)
psi_CD.draw('latex', prefix='|\\psi_{CD}\\rangle =')
```

$$
|\psi_{CD}\rangle =
\begin{bmatrix}
0.61237 & \tfrac{1}{\sqrt{8}} & \tfrac{1}{\sqrt{8}} & 0.61237  \\
 \end{bmatrix}
$$

Alternatively, we can express this state in terms of its density matrix $\rho_{CD} = | \psi_{CD} \rangle \langle \psi_{CD} |: $

```python
rho_CD = qi.DensityMatrix.from_instruction(qc_CD)
rho_CD.draw('latex', prefix='\\rho_{CD} =')
```

$$
\rho_{CD} =
\begin{bmatrix}
\tfrac{3}{8} & 0.21651 & 0.21651 & \tfrac{3}{8}  \\
 0.21651 & \tfrac{1}{8} & \tfrac{1}{8} & 0.21651  \\
 0.21651 & \tfrac{1}{8} & \tfrac{1}{8} & 0.21651  \\
 \tfrac{3}{8} & 0.21651 & 0.21651 & \tfrac{3}{8}  \\
 \end{bmatrix}
$$

And now, we can find the density matrices for the corresponding subsystems $C$ and $D$:

```python
rho_D = qi.partial_trace(rho_CD,[0])
rho_C = qi.partial_trace(rho_CD,[1])

display(rho_D.draw('latex', prefix=" \\rho_{D} = "),
        rho_C.draw('latex', prefix=" \\rho_{C} = "))
```

$$
 \rho_{D} =
\begin{bmatrix}
\tfrac{1}{2} & 0.43301  \\
 0.43301 & \tfrac{1}{2}  \\
 \end{bmatrix}
$$

$$
 \rho_{C} =
\begin{bmatrix}
\tfrac{1}{2} & 0.43301  \\
 0.43301 & \tfrac{1}{2}  \\
 \end{bmatrix}
$$

Each of these reduced density matrices has a Bloch vector associated with them, each with components:

$$ r_x \approx 0.86602, \quad r_y = 0, \quad r_z = 0 $$

So, if now we use the ``plot_bloch_multivector()`` function in Qiskit to plot the composite state $\rho_{CD}$, we see we get two Bloch vectors that correctly represent each of these reduced density matrices $\rho_C$ and $\rho_D$:

```python
plot_bloch_multivector(rho_CD.data)
```

Understanding the Bloch vector representation for multi-qubit states explains why, when we try to plot a two-qubit maximally entangled state in the Bloch sphere, we get an "empty" plot. Since the reduced density matrices of a state like:

$$ | \psi_{AB} \rangle = \frac{1}{\sqrt{2}} \left ( | 0_A 0_B \rangle + | 1_A 1_B \rangle \right ) $$

are given by:

$$
\rho_A = \rho_B = \frac{1}{2} \begin{bmatrix} 1 & 0 \\ 0 & 1 \end{bmatrix} = \frac{1}{2} \hat I,
$$

we see that the Bloch vector components $r_x, r_y, r_z$ are all 0. Therefore, $\rho_A$ and $\rho_B$ actually have $\vec r $ vectors of zero length, represented by points at the origin of the sphere:

```python
plot_bloch_multivector(rho_AB.data)
```

Although visualizing multi-qubit entangled states using Bloch spheres can provide some intuition about the quantum system in hand, it is important to remember that what we are plotting are the Bloch vectors of the individual reduced density matrices of each subsystem, and not of the state as a whole. Since density matrices are [not unique](#non_uniqueness), different quantum states can lead to the same representation. For example, the reduced density matrices for the four Bell states: $\tfrac{|00 \rangle + |11 \rangle}{\sqrt{2}}$, $\tfrac{|00 \rangle - |11 \rangle}{\sqrt{2}}$, $\tfrac{|01 \rangle + |10 \rangle}{\sqrt{2}}$, and $\tfrac{|01 \rangle - |10 \rangle}{\sqrt{2}}$, all lead to the same multivector Bloch representations (same as shown in the plot above). Therefore, this visualization should not be used to replace, but to complement, other multi-qubit visualization methods, such as the [q-sphere](https://qiskit.org/documentation/stubs/qiskit.visualization.plot_state_qsphere.html).

## 6. References

[1] Nielsen, M. A., & Chuang, I. _Quantum Computation and Quantum Information,_ 2002.

[2] Benenti, G., Casati, G., & Strini, G. _Principles of Quantum Computation and Information-Volume II: Basic Tools and Special Topics,_ 2007.

```python
import qiskit.tools.jupyter
%qiskit_version_table
```

Version Information

Qiskit Software	Version

Qiskit	0.27.0

Terra	0.17.4

Aer	0.8.2

Ignis	0.6.0

Aqua	0.9.2

IBM Q Provider	0.14.0

System information

Python	3.7.7 (default, May  6 2020, 04:59:01)
[Clang 4.0.1 (tags/RELEASE_401/final)]

OS	Darwin

CPUs	8

Memory (Gb)	32.0

Wed Jun 16 09:54:04 2021 BST


```python
# Importing all tools that could be necessary for the tasks
import numpy as np
import scipy as sp
from scipy import linalg
from scipy import optimize
from scipy import interpolate
import sympy as sm

%matplotlib inline
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
```

# 1. Human capital accumulation

Consider a worker living in **two periods**, $t \in \{1,2\}$.

In each period she decides whether to **work ($l_t = 1$) or not ($l_t = 0$)**.

She can *not* borrow or save and thus **consumes all of her income** in each period.

If she **works** her **consumption** becomes:

$$c_t = w h_t l_t\,\,\text{if}\,\,l_t=1$$

where $w$ is **the wage rate** and $h_t$ is her **human capital**.

If she does **not work** her consumption becomes:

$$c_t = b\,\,\text{if}\,\,l_t=0$$

where $b$ is the **unemployment benefits**.

Her **utility of consumption** is:

$$ \frac{c_t^{1-\rho}}{1-\rho} $$

Her **disutility of working** is:

$$ \gamma l_t $$

From period 1 to period 2, she **accumulates human capital** according to:

$$ h_2 = h_1 + l_1 +
\begin{cases}
0 & \text{with prob. }0.5 \\
\Delta & \text{with prob. }0.5
\end{cases} \\
$$

where $\Delta$ is a **stochastic experience gain**.

In the **second period** the worker thus solves:

$$
\begin{eqnarray*}
v_{2}(h_{2}) & = &\max_{l_{2}} \frac{c_2^{1-\rho}}{1-\rho} - \gamma l_2
\\ & \text{s.t.} & \\
c_{2}& = & \begin{cases}
w h_2 &
\text{if }l_2 = 1  \\
b & \text{if }l_2 = 0
\end{cases} \\
l_{2}& \in &\{0,1\}
\end{eqnarray*}
$$

In the **first period** the worker thus solves:

$$
\begin{eqnarray*}
v_{1}(h_{1}) &=& \max_{l_{1}} \frac{c_1^{1-\rho}}{1-\rho} - \gamma l_1 + \beta\mathbb{E}_{1}\left[v_2(h_2)\right]
\\ & \text{s.t.} & \\
c_{1}& = & \begin{cases}
w h_1 &
\text{if }l_1 = 1  \\
b & \text{if }l_1 = 0
\end{cases} \\
h_2 &=& h_1 + l_1 + \begin{cases}
0 & \text{with prob. }0.5\\
\Delta & \text{with prob. }0.5
\end{cases}\\
l_{1} &\in& \{0,1\}\\
\end{eqnarray*}
$$

where $\beta$ is the **discount factor** and $\mathbb{E}_{1}\left[v_2(h_2)\right]$ is the **expected value of living in period two**.

The **parameters** of the model are:

```python
rho = 2
beta = 0.96
gamma = 0.1
w = 2
b = 1
Delta = 0.1
```

The **relevant levels of human capital** are:

```python
h_vec = np.linspace(0.1,1.5,100)
```

**Question 1:** Solve the model in period 2 and illustrate the solution (including labor supply as a function of human capital).

To solve the model for period 2, we must define the various functions making up the model. This includes consumtion, utility, bequest and lastly value for both periods.

```python
# Defining the consumption function, utility function, bequest function and maximization problem for period 2.
def c(w,b,h,l):
    if l==1:
        return w*h
    else:
        return b

def utility(w,b,h,l,rho):
    return c(w,b,h,l)**(1-rho)/(1-rho)

def bequest(l,gamma):
    return gamma*l

def v2(w,b,h2,l2,rho,gamma):
    return utility(w,b,h2,l2,rho)-bequest(l2,gamma)
```

```python
# Defining maximization problem for period 1 for later use
def v1(w,b,h1,l1,Delta,rho,beta,v2_interp):

    # a. v2 value if not working in period 1
    h2_nowork = h1 + l1 + 0
    v2_nowork = v2_interp([h2_nowork])[0]

    # b. v2 value if working in period 1
    h2_work = h1 + 1 + Delta
    v2_work = v2_interp([h2_work])[0]

    # c. Expected value of v2
    v2 = 0.5 * v2_nowork + 0.5 * v2_work

    # d. Total value
    return utility(w,b,h1,l1,rho) - bequest(l1,gamma) + beta*v2
```

We then go on to solving the maximization problem for period 2. This will let us illustrate the chosen consumption, value and labor supply based on human capital of the consumer.

```python
# We then define the function for solving the maximization problem for period 2
def solve_period_2(rho,gamma,Delta):

    # a. grids
    h2_vec = np.linspace(0.1,1.5,100)
    v2_vec = np.empty(100)
    c2_vec = np.empty(100)
    l2_vec = np.empty(100)

    # b. solve for each h2 in grid
    for i,h2 in enumerate(h2_vec):

        # i. Deciding whether to work or not to work depending on value
        if (v2(w,b,h2,1,rho,gamma) > v2(w,b,h2,0,rho,gamma)) == True:
            l2_vec[i] = 1
        else:
            l2_vec[i] = 0

        v2_vec[i] = v2(w,b,h2,l2_vec[i],rho,gamma)

        # ii. objective
        obj = lambda c2: -v2(w,b,h2_vec,0,rho,gamma)

        # iii. initial value (consume half)
        x0 = h2/2

        # iv. optimizer
        result = optimize.minimize_scalar(obj,x0,method='bounded',bounds=[0.1,h2])

        # v. save
        v2_vec[i] = -result.fun
        c2_vec[i] = result.x

    return h2_vec,v2_vec,c2_vec,l2_vec

# solve
h2_vec,v2_vec,c2_vec,l2_vec = solve_period_2(rho,gamma,Delta)

# illustration
# Plotting consumption as a function of human capital for period 2
fig = plt.figure(figsize=(10,4))
ax = fig.add_subplot(1,2,1)
ax.plot(h2_vec,c2_vec)
ax.grid()
ax.set_xlabel('$h_2$')
ax.set_ylabel('$c_2$')
ax.set_title('Consumption function in period 2')

# Plotting value as a function of human capital depending on whether consumer chooses to work or not
ax = fig.add_subplot(1,2,2)
ax.plot(h2_vec,v2(w,b,h2_vec,1,rho,gamma),color='green',label='value if working')
ax.axhline(y=v2(w,b,h2_vec,0,rho,gamma),color='blue',label='value if not working')
ax.legend(loc='lower right')
ax.grid()
ax.set_xlabel('$h_2$')
ax.set_ylabel('$v_2$')
ax.set_title('Value function in period 2')
ax.set_ylim([-5,0]);

# Plotting labor supply as a function of human capital
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(h2_vec,l2_vec)
ax.grid()
ax.set_xlabel('$h_2$')
ax.set_ylabel('$l_2$')
ax.set_title('Labor supply function in period 2');
```

We see that a consumer will only choose to work in period 2 if the human capital is above approximately 0.55

**Question 2:** Solve the model in period 1 and illustrate the solution (including labor supply as a function of human capital).

First we define a variable holding the optimal value of period 2 to include in the maximization of period 1.

```python
# Variable for including value from period 2 in period 1
v2_interp = interpolate.RegularGridInterpolator([h2_vec], v2_vec,
                                                bounds_error=False,fill_value=None)
```

Next we go on to defining the solution for the maximization problem of period 1 for use in illustrating consumption, value and labor supply as a function of human capital in period 1.

```python
# Defining the solution for maximization in period 1
def solve_period_1(rho,beta,Delta,v2_interp):

    # a. grids
    h1_vec = np.linspace(0.1,1.5,100)
    v1_vec = np.empty(100)
    c1_vec = np.empty(100)
    l1_vec = np.empty(100)

    # b. solve for each h1 in grid
    for i,h1 in enumerate(h1_vec):

        # Choosing whether to work or not
        if (v1(w,b,h1,1,Delta,rho,beta,v2_interp) > v1(w,b,h1,0,Delta,rho,beta,v2_interp)) == True:
            l1_vec[i] = 1
        else:
            l1_vec[i] = 0

        v1_vec[i] = v1(w,b,h1,l1_vec[i],Delta,rho,beta,v2_interp)

        # i. objective
        obj = lambda c1: -v1(w,b,h1,0,Delta,rho,beta,v2_interp)

        # ii. initial guess (consume half)
        x0 = h1*1/2

        # iii. optimize
        result = optimize.minimize_scalar(obj,x0,method='bounded',bounds=[0.1,h1])

        # iv. save
        v1_vec[i] = -result.fun
        c1_vec[i] = result.x

    return h1_vec,v1_vec,c1_vec,l1_vec

# solve
h1_vec,v1_vec,c1_vec,l1_vec = solve_period_1(rho,beta,Delta,v2_interp)

# illustrate
# Consumption as a function of human capital
fig = plt.figure(figsize=(10,4))
ax = fig.add_subplot(1,2,1)
ax.plot(h1_vec,c1_vec)
ax.grid()
ax.set_xlabel('$h_1$')
ax.set_ylabel('$c_1$')
ax.set_title('consumption function in period 1')

# Value as a function of human capital
ax = fig.add_subplot(1,2,2)
ax.plot(h1_vec,v1_vec,color='green',label='value if working')
ax.legend(loc='lower right')
ax.grid()
ax.set_xlabel('$h_1$')
ax.set_ylabel('$v_1$')
ax.set_title('Value function in period 1')
ax.set_ylim([-5,0]);

# Labor supply as a function of human capital
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(h1_vec,l1_vec)
ax.grid()
ax.set_xlabel('$h_1$')
ax.set_ylabel('$l_1$')
ax.set_title('Labor supply function in period 1');
```

**Question 3:** Will the worker never work if her potential wage income is lower than the unemployment benefits she can get? Explain and illustrate why or why not.

To figure out if the worker will work if the wage is lower than the unemployment benefit, we redefine the wage and run the solution to the maximization problem again.

```python
# Redefining the wage to make it lower than the unemployment benefits
w_under=0.99

# Defining the solution function with the new wage variable
def solve_period_1_w_under(rho,beta,Delta,v2_interp):

    # a. grids
    v1_vec = np.empty(100)
    l1_vec = np.empty(100)

    # b. solve for each h1 in grid
    for i,h1 in enumerate(h1_vec):

        # Choosing whether to work or not
        if (v1(w_under,b,h1,1,Delta,rho,beta,v2_interp) > v1(w_under,b,h1,0,Delta,rho,beta,v2_interp)) == True:
            l1_vec[i] = 1
        else:
            l1_vec[i] = 0

        v1_vec[i] = v1(w_under,b,h1,l1_vec[i],Delta,rho,beta,v2_interp)

    return v1_vec,l1_vec

# solve
v1_vec,l1_vec = solve_period_1_w_under(rho,beta,Delta,v2_interp)

# Plotting the labor supply as a function of human capital
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(h1_vec,l1_vec)
ax.grid()
ax.set_xlabel('$h_1$')
ax.set_ylabel('$l_1$')
ax.set_title('Labor supply function in period 1');
```

As we see, the worker will still choose to work if the human capital is high enough. However, as illustrated, the human capital does need to be much higher if the wage is just a fraction lower than the unemployment benefits for the worker to choose to work.

# 2. AS-AD model

Consider the following **AS-AD model**. The **goods market equilibrium** is given by

$$ y_{t} = -\alpha r_{t} + v_{t} $$

where $y_{t}$ is the **output gap**, $r_{t}$ is the **ex ante real interest** and $v_{t}$ is a **demand disturbance**.

The central bank's **Taylor rule** is

$$ i_{t} = \pi_{t+1}^{e} + h \pi_{t} + b y_{t}$$

where $i_{t}$ is the **nominal interest rate**, $\pi_{t}$ is the **inflation gap**, and $\pi_{t+1}^{e}$ is the **expected inflation gap**.

The **ex ante real interest rate** is given by

$$ r_{t} = i_{t} - \pi_{t+1}^{e} $$

Together, the above implies that the **AD-curve** is

$$ \pi_{t} = \frac{1}{h\alpha}\left[v_{t} - (1+b\alpha)y_{t}\right]$$

Further, assume that the **short-run supply curve (SRAS)** is given by

$$ \pi_{t} = \pi_{t}^{e} + \gamma y_{t} + s_{t}$$

where $s_t$ is a **supply disturbance**.

**Inflation expectations are adaptive** and given by

$$ \pi_{t}^{e} = \phi\pi_{t-1}^{e} + (1-\phi)\pi_{t-1}$$

Together, this implies that the **SRAS-curve** can also be written as

$$ \pi_{t} = \pi_{t-1} + \gamma y_{t} - \phi\gamma y_{t-1} + s_{t} - \phi s_{t-1} $$

The **parameters** of the model are:

```python
par = {}

par['alpha'] = 5.76
par['h'] = 0.5
par['b'] = 0.5
par['phi'] = 0
par['gamma'] = 0.075
```

**Question 1:** Use the ``sympy`` module to solve for the equilibrium values of output, $y_t$, and inflation, $\pi_t$, (where AD = SRAS) given the parameters ($\alpha$, $h$, $b$, $\alpha$, $\gamma$) and $y_{t-1}$ , $\pi_{t-1}$, $v_t$, $s_t$, and $s_{t-1}$.

We start out by defining all variables as sympy symbols for illustrating the mathmatical progression and solution for equilibrium values in the model.

```python
# Defining variables as symbols

alpha = sm.symbols('alpha')
h = sm.symbols('h')
b = sm.symbols('b')
phi = sm.symbols('phi')
gamma = sm.symbols('gamma')
it = sm.symbols('i_t')
yt = sm.symbols('y_t')
pit = sm.symbols('pi_t')
vt = sm.symbols('v_t')
st = sm.symbols('s_t')
ylag = sm.symbols('y_t-1')
pilag = sm.symbols('pi_t-1')
vlag = sm.symbols('v_t-1')
slag = sm.symbols('s_t-1')

# Enabling nice printing
sm.init_printing(use_unicode=True)
```

We then need to define the AD function and SRAS function as given above.

```python
# Stating the AD-function
AD = sm.Eq((vt-(1+b*alpha)*yt)/(h*alpha),pit)
AD
```

```python
# Stating the SRAS-function
SRAS = sm.Eq(pilag+gamma*yt-phi*gamma*ylag+st-phi*slag,pit)
SRAS
```

As both functions express the inflation gap, we can set the two equal and solve for the output gap to find the equilibrium value of the output gap.

```python
# Setting the two functions equal
ADSRAS = sm.Eq((vt-(1+b*alpha)*yt)/(h*alpha),pilag+gamma*yt-phi*gamma*ylag+st-phi*slag)
ADSRAS
```

```python
# Solving for the output gap
solveyt=sm.solve(ADSRAS,yt)
solveyt
```

Next we substitue the found function for the equilibrium value of the output gap into the AD-function to find the equilibrium value of the inflation gap.

```python
# Substituting equilibrium value of output gap into AD-function to find inflation gap
solvepit=AD.subs(yt,solveyt[0])
solvepit
```

**Question 2:** Find and illustrate the equilibrium when $y_{t-1} = \pi_{t-1} = v_t = s_t = s_{t-1} = 0$. Illustrate how the equilibrium changes when instead $v_t = 0.1$.

We start out by defining the AD-function,the SRAS function and the needed variables.

```python
# Defining the AD-function
def AD(vt,yt,b,h,alpha):
    return (vt-yt*(alpha*b+1))/(alpha*h)

# Defining the SRAS-function
def SRAS(gamma,phi,ylag,yt,slag,pilag,st):
    return st+pilag+gamma*yt-phi*slag-gamma*phi*ylag
```

```python
# defining the variables
ylag,pilag,vt,st,slag = 0,0,0,0,0
alpha = 5.76
h = 0.5
b = 0.5
phi = 0
gamma = 0.075
v_shock = 0.1
yt = np.linspace(-0.5,0.5,100)
```

Next we define the line for plotting in a graph to illustrate the effect of a shock in demand.

```python
# Defining the lines for the graph
ADline = AD(vt,yt,b,h,alpha)
ADlineShock = AD(v_shock,yt,b,h,alpha)
ASline = SRAS(gamma,phi,ylag,yt,slag,pilag,st)

# Plotting the AD lines and the AS line
fig = plt.figure()
ax = fig.add_subplot(1,1,1)
ax.plot(yt, ADline,label='Original AD')
ax.plot(yt, ADlineShock, label='AD after shock')
ax.plot(yt, ASline, label='AS')
ax.set_xlabel(r'inflation gap')
ax.set_ylabel('$output gap$')
ax.legend(loc='upper right')
ax.set_title('AD-AS')
ax.grid()
plt.show()
```

We see that a demand shock clearly shifts the accumulated demand to the right, which in turn gives a new equilibrium.

**Persistent disturbances:** Now, additionaly, assume that both the demand and the supply disturbances are AR(1) processes

$$ v_{t} = \delta v_{t-1} + x_{t} $$
$$ s_{t} = \omega s_{t-1} + c_{t} $$

where $x_{t}$ is a **demand shock**, and $c_t$ is a **supply shock**. The **autoregressive parameters** are:

```python
par['delta'] = 0.80
par['omega'] = 0.15
```

**Question 3:** Starting from $y_{-1} = \pi_{-1} = s_{-1} = 0$, how does the economy evolve for $x_0 = 0.1$, $x_t = 0, \forall t > 0$ and $c_t = 0, \forall t \geq 0$?

To see how the economy evolves as a function of demand- and supply shocks, we first must define the functions for each of the shocks.

```python
# Defining the parameters
delta = 0.8
omega = 0.15

# Defining the demand- and supplyshock functions
def vtar1(delta,vlag,xt):
    return delta*vlag+xt

def star1(omega,slag,ct):
    return omega*slag+ct
```

Next we define the equilibrium functions for the output gap and inflation gap.

```python
# Equlibrium value of output gap
def yt_eq(alpha,gamma,h,phi,b,ylag,slag,st,vt):
    return (alpha*gamma*h*phi*ylag+alpha*h*phi*slag-alpha*h*pilag-alpha*h*st+vt)/(alpha*b+alpha*gamma*h+1)

# Equilibrium value of inflation gap
def pit_eq(alpha,gamma,h,phi,b,ylag,slag,st,vt):
    return (vt-yt_eq(alpha,gamma,h,phi,b,ylag,slag,st,vt))/(alpha*h)
```

Lastly we forecast values for equilibrium values for both output gap and inflation gap, based on the necessary variables. This allows us to illustrate the forcsted values and the thereby the evolution of the economy.

```python
# Setting the forecast to 10 periods and creating empty vectors to fill
T=10
pitv =[0]
ytv =[0]
stv =[0]
vtv =[0]
ctv =np.zeros(T)
xtv =np.zeros(T)
xtv[1]=0.1

# Filling out the vectors with values dependant on the relevant variables
for t in range (1,T):
    stv.append(star1(omega,stv[t-1],ctv[t]))
    vtv.append(vtar1(delta,vtv[t-1],xtv[t]))
    ytv.append(yt_eq(alpha,gamma,h,phi,b,ytv[t-1],stv[1-t],stv[t],vtv[t]))
    pitv.append(pit_eq(alpha,gamma,h,phi,b,ytv[t-1],stv[1-t],stv[t],vtv[t]))
```

```python
# Creating a timeframe for use as x-axis
t_value = np.linspace(1,T,T)

# Plotting the equilibrium vlues of both output gap and inflation gap for the next 10 periods.
fig = plt.figure()
plt.axes()
ax = fig.add_subplot(1,1,1)
ax.plot(t_value, ytv, label='EQ-values of yt')
ax.plot(t_value, pitv, label='EQ-values of pit')
ax.set_xlabel('t_value')
ax.set_ylabel('EQ-value of inflation- and outputgap')
ax.set_title('Evolution of economy')
ax.legend(loc='lower right')
ax.grid()
```

As seen above, the equilibrium values will rise in the first couple of periods and then decline once again.

**Stochastic shocks:** Now, additionally, assume that $x_t$ and $c_t$ are stochastic and normally distributed

$$ x_{t}\sim\mathcal{N}(0,\sigma_{x}^{2}) $$
$$ c_{t}\sim\mathcal{N}(0,\sigma_{c}^{2}) $$

The **standard deviations of the shocks** are:

```python
par['sigma_x'] = 3.492
par['sigma_c'] = 0.2
```

**Question 4:** Simulate the AS-AD model for 1,000 periods. Calculate the following five statistics:

1. Variance of $y_t$, $var(y_t)$
2. Variance of $\pi_t$, $var(\pi_t)$
3. Correlation between $y_t$ and $\pi_t$, $corr(y_t,\pi_t)$
4. Auto-correlation between $y_t$ and $y_{t-1}$, $corr(y_t,y_{t-1})$
5. Auto-correlation between $\pi_t$ and $\pi_{t-1}$, $corr(\pi_t,\pi_{t-1})$

We start out by defining the new parameters and the stochastic shocks in addition to resetting the vectors for the variables for simulating the equilibrium values over 1000 periods.

```python
# a. true parameters in the data generating process
par['sigma_x'] = 3.492
par['sigma_c'] = 0.2

# b. simulate observed dataset
np.random.seed(2019); # set the seed
t_sim = 1000;

# stochastic variables nd vectors
pitv =[0]
ytv =[0]
stv =[0]
vtv =[0]
ct_stoc = np.random.normal(loc=0.0,scale=par['sigma_c'],size=t_sim)
xt_stoc = np.random.normal(loc=0.0,scale=par['sigma_x'],size=t_sim)

# creating the equilibrium values
for t in range (1,t_sim):
    stv.append(star1(omega,stv[t-1],ct_stoc[t]))
    vtv.append(vtar1(delta,vtv[t-1],xt_stoc[t]))
    ytv.append(yt_eq(alpha,gamma,h,phi,b,ytv[t-1],stv[1-t],stv[t],vtv[t]))
    pitv.append(pit_eq(alpha,gamma,h,phi,b,ytv[t-1],stv[1-t],stv[t],vtv[t]))
```

We then go on to plot the equilibrium values as a scatterplot over the 1000 periods.

```python
# Creating a timeframe for the x-axis
t_sim_value = np.linspace(1,t_sim,t_sim)

# Illustrating the equilibrium values for the output gap
fig = plt.figure(figsize=(10,4))
ax = fig.add_subplot(1,2,1)
ax.scatter(t_sim_value,ytv)
ax.grid(True)
ax.set_xlabel('t-sim value')
ax.set_ylabel('output gap')
ax.set_title('output gap simulated 1000 times')

# Illustrating the equilibrium values for the inflation gap
ax = fig.add_subplot(1,2,2)
ax.scatter(t_sim_value,pitv)
ax.grid(True)
ax.set_xlabel('t-sim value')
ax.set_ylabel('inflation gap')
ax.set_title('inflation gap simulated 1000 times');
```

We then go on to calculating the variance and correlations of the output gap and inflation gap using numpy.

```python
# Variance of output gap
var_yt = np.var(ytv)
# Variance of inflation gap
var_pit = np.var(pitv)
# Autocorrelation of inflation gap
cor_pit = np.corrcoef(pitv[1:t_sim],pitv[0:-1])
# Autocorrelation of output gap
cor_yt = np.corrcoef(ytv[1:t_sim],ytv[0:-1])
# Correlation between output gap and inflation gap
cor_ytpit = np.corrcoef(ytv,pitv)

# Printing the results
print(f'variance of yt is {var_yt}')
print(f'variance of pit is {var_pit}')
print(f'autocorrelation of pit is {cor_pit}')
print(f'autocorrelation of yt is {cor_yt}')
print(f'correlation between pit and yt is {cor_ytpit}')
```

    variance of yt is 1.8999010368515332
    variance of pit is 2.1797771543103277
    autocorrelation of pit is [[1.         0.79317795]
     [0.79317795 1.        ]]
    autocorrelation of yt is [[1.         0.78867755]
     [0.78867755 1.        ]]
    correlation between pit and yt is [[1.         0.99028573]
     [0.99028573 1.        ]]

**Question 5:** Plot how the correlation between $y_t$ and $\pi_t$ changes with $\phi$. Use a numerical optimizer or root finder to choose $\phi\in(0,1)$ such that the simulated correlation between $y_t$ and $\pi_t$ comes close to 0.31.

**Quesiton 6:** Use a numerical optimizer to choose $\sigma_x>0$, $\sigma_c>0$ and $\phi\in(0,1)$ to make the simulated statistics as close as possible to US business cycle data where:

1. $var(y_t) = 1.64$
2. $var(\pi_t) = 0.21$
3. $corr(y_t,\pi_t) = 0.31$
4. $corr(y_t,y_{t-1}) = 0.84$
5. $corr(\pi_t,\pi_{t-1}) = 0.48$

# 3. Exchange economy

Consider an **exchange economy** with

1. 3 goods, $(x_1,x_2,x_3)$
2. $N$ consumers indexed by \\( j \in \{1,2,\dots,N\} \\)
3. Preferences are Cobb-Douglas with log-normally distributed coefficients

    $$ \begin{eqnarray*}
    u^{j}(x_{1},x_{2},x_{3}) &=&
    \left(x_{1}^{\beta_{1}^{j}}x_{2}^{\beta_{2}^{j}}x_{3}^{\beta_{3}^{j}}\right)^{\gamma}\\
     &  & \,\,\,\beta_{i}^{j}=\frac{\alpha_{i}^{j}}{\alpha_{1}^{j}+\alpha_{2}^{j}+\alpha_{3}^{j}} \\
     &  & \,\,\,\boldsymbol{\alpha}^{j}=(\alpha_{1}^{j},\alpha_{2}^{j},\alpha_{3}^{j}) \\
     &  & \,\,\,\log(\boldsymbol{\alpha}^j) \sim \mathcal{N}(\mu,\Sigma) \\
    \end{eqnarray*} $$

4. Endowments are exponentially distributed,

$$
\begin{eqnarray*}
\boldsymbol{e}^{j} &=& (e_{1}^{j},e_{2}^{j},e_{3}^{j}) \\
 &  & e_i^j \sim f, f(z;\zeta) =  1/\zeta \exp(-z/\zeta)
\end{eqnarray*}
$$

Let $p_3 = 1$ be the **numeraire**. The implied **demand functions** are:

$$
\begin{eqnarray*}
x_{i}^{\star j}(p_{1},p_{2},\boldsymbol{e}^{j})&=&\beta^{j}_i\frac{I^j}{p_{i}} \\
\end{eqnarray*}
$$

where consumer $j$'s income is

$$I^j = p_1 e_1^j + p_2 e_2^j +p_3 e_3^j$$

The **parameters** and **random preferences and endowments** are given by:

```python
# a. parameters
N = 50000
mu = np.array([3,2,1])
Sigma = np.array([[0.25, 0, 0], [0, 0.25, 0], [0, 0, 0.25]])
gamma = 0.8
zeta = 1

# b. random draws
seed = 1986
np.random.seed(seed)

# preferences
alphas = np.exp(np.random.multivariate_normal(mu, Sigma, size=N))
betas = alphas/np.reshape(np.sum(alphas,axis=1),(N,1))

# endowments
e1 = np.random.exponential(zeta,size=N)
e2 = np.random.exponential(zeta,size=N)
e3 = np.random.exponential(zeta,size=N)
```

**Question 1:** Plot the histograms of the budget shares for each good across agents.

As the simulation is run above, we can plot the budget shares for each good across agents directly.

```python
plt.hist(betas, bins=50, label=['good 1','good 2', 'good 3'])
plt.xlabel('Budget share')
plt.ylabel('Consumers')
plt.legend(loc='upper right');
```

Consider the **excess demand functions:**

$$ z_i(p_1,p_2) = \sum_{j=1}^N x_{i}^{\star j}(p_{1},p_{2},\boldsymbol{e}^{j}) - e_i^j$$

**Question 2:** Plot the excess demand functions.

**Question 3:** Find the Walras-equilibrium prices, $(p_1,p_2)$, where both excess demands are (approximately) zero, e.g. by using the following tâtonnement process:

1. Guess on $p_1 > 0$, $p_2 > 0$ and choose tolerance $\epsilon > 0$ and adjustment aggressivity parameter, $\kappa > 0$.
2. Calculate $z_1(p_1,p_2)$ and $z_2(p_1,p_2)$.
3. If $|z_1| < \epsilon$ and $|z_2| < \epsilon$ then stop.
4. Else set $p_1 = p_1 + \kappa \frac{z_1}{N}$ and $p_2 = p_2 + \kappa \frac{z_2}{N}$ and return to step 2.

**Question 4:** Plot the distribution of utility in the Walras-equilibrium and calculate its mean and variance.

**Question 5:** Find the Walras-equilibrium prices if instead all endowments were distributed equally. Discuss the implied changes in the distribution of utility. Does the value of $\gamma$ play a role for your conclusions?


```python
from simba.core import StateSpace
from simba.graph import nodes_from_network
from sympy import symbols, sqrt, Matrix, I
```

```python
omega_s, chi, gamma = symbols('omega_s chi gamma', real=True)

a = Matrix([[0, 0, -I*omega_s, 0],
            [0, 0, 0, I*omega_s],
            [-I*omega_s, 0, -gamma, -chi],
            [0, I*omega_s, -chi, -gamma]])
b = -sqrt(2*gamma) * Matrix([[0, 0], [0, 0], [1, 0], [0, 1]])
c = sqrt(2*gamma) * Matrix([[0, 0, 1, 0], [0, 0, 0, 1]])
d = Matrix.eye(2)
ss = StateSpace(a, b, c, d, paired_operator_form=True)
slh = ss.to_slh()
slh
```

$$\displaystyle \left(I_{2\times2}, \left[\begin{matrix}0 & 0 & \sqrt{2} \sqrt{\gamma} & 0\end{matrix}\right] \left[\begin{matrix}a_{1}\\\overline{a_{1}}\\a_{2}\\\overline{a_{2}}\end{matrix}\right], \left[\begin{matrix}\overline{a_{1}} & a_{1} & \overline{a_{2}} & a_{2}\end{matrix}\right] \left[\begin{matrix}0 & 0 & \frac{\omega_{s}}{2} & 0\\0 & 0 & 0 & \frac{\omega_{s}}{2}\\\frac{\omega_{s}}{2} & 0 & 0 & - \frac{i \chi}{2}\\0 & \frac{\omega_{s}}{2} & \frac{i \chi}{2} & 0\end{matrix}\right] \left[\begin{matrix}a_{1}\\\overline{a_{1}}\\a_{2}\\\overline{a_{2}}\end{matrix}\right]\right)$$

```python
split_network = slh.split()
g = nodes_from_network(split_network).as_graphviz_agraph()
g.layout(prog='dot')
g.draw('quantum-expander.png')
g.draw('quantum-expander.pdf')
```

```python
split_network.gs[0]
```

$$\displaystyle \left(I_{2\times2}, \left[\begin{matrix}0 & 0\end{matrix}\right] \left[\begin{matrix}a_{1}\\\overline{a_{1}}\end{matrix}\right], \left[\begin{matrix}\overline{a_{1}} & a_{1}\end{matrix}\right] \left[\begin{matrix}0 & 0\\0 & 0\end{matrix}\right] \left[\begin{matrix}a_{1}\\\overline{a_{1}}\end{matrix}\right]\right)$$

```python
split_network.gs[1]
```

$$\displaystyle \left(I_{2\times2}, \left[\begin{matrix}\sqrt{2} \sqrt{\gamma} & 0\end{matrix}\right] \left[\begin{matrix}a_{2}\\\overline{a_{2}}\end{matrix}\right], \left[\begin{matrix}\overline{a_{2}} & a_{2}\end{matrix}\right] \left[\begin{matrix}0 & - \frac{i \chi}{2}\\\frac{i \chi}{2} & 0\end{matrix}\right] \left[\begin{matrix}a_{2}\\\overline{a_{2}}\end{matrix}\right]\right)$$

```python
split_network.interaction_hamiltonian.expr
```

$\displaystyle i \sqrt{\gamma} \sqrt{\gamma_{2}} a_{2} \overline{a'_{2}} - i \sqrt{\gamma_{2}} \overline{\sqrt{\gamma}} \overline{a_{2}} a'_{2} + \omega_{s} a_{1} \overline{a_{2}} + \omega_{s} \overline{a_{1}} a_{2}$

```python
split_network.states
```

$\displaystyle \left[\begin{matrix}a_{1}\\\overline{a_{1}}\\a_{2}\\\overline{a_{2}}\\a'_{1}\\\overline{a'_{1}}\\a'_{2}\\\overline{a'_{2}}\end{matrix}\right]$

Starting from general $\alpha, \beta, \Gamma$

```python
alpha, beta, Gamma = symbols('alpha beta Gamma', real=True)

a = Matrix([[0, 0, -I*Gamma, 0],
            [0, 0, 0, I*Gamma],
            [-I*Gamma, 0, -(alpha+beta)/2, (alpha-beta)/2],
            [0, I*Gamma, (alpha-beta)/2, -(alpha+beta)/2]])
b = -sqrt(alpha+beta) * Matrix([[0, 0], [0, 0], [1, 0], [0, 1]])
c = sqrt(alpha+beta) * Matrix([[0, 0, 1, 0], [0, 0, 0, 1]])
d = Matrix.eye(2)
ss = StateSpace(a, b, c, d, paired_operator_form=True)
slh = ss.to_slh()
slh
```

$$\displaystyle \left(I_{2\times2}, \left[\begin{matrix}0 & 0 & \sqrt{\alpha + \beta} & 0\end{matrix}\right] \left[\begin{matrix}a_{1}\\\overline{a_{1}}\\a_{2}\\\overline{a_{2}}\end{matrix}\right], \left[\begin{matrix}\overline{a_{1}} & a_{1} & \overline{a_{2}} & a_{2}\end{matrix}\right] \left[\begin{matrix}0 & 0 & \frac{\Gamma}{2} & 0\\0 & 0 & 0 & \frac{\Gamma}{2}\\\frac{\Gamma}{2} & 0 & 0 & \frac{i \left(\alpha - \beta\right)}{4}\\0 & \frac{\Gamma}{2} & \frac{i \left(- \alpha + \beta\right)}{4} & 0\end{matrix}\right] \left[\begin{matrix}a_{1}\\\overline{a_{1}}\\a_{2}\\\overline{a_{2}}\end{matrix}\right]\right)$$

```python
split_network = slh.split()
g = nodes_from_network(split_network).as_graphviz_agraph()
g.layout(prog='dot')
g.draw('quantum-expander-alpha-beta.png')
```

```python

```


# Python for Engineers Assignment 2018
Answer all questions.

すべての質問に答えなさい。

Submission deadline 07/16 (July 16).

提出締め切り07/16 (7月16日）。

Please submit your answer by email.

どうぞ、メールによってあなたの答えを提出してください。

You may use the following file formats:

あなたは以下のファイル形式を使ってもよい:
- .py
- .ipynb
- .txt

You may use multiple files.

あなたは複数のファイルを使ってもよい。

You will be awarded points for:
あなたは以下のためのポイントを授与される：
- correct solution. 正しい答え。
- concise solution. 簡潔な答え。
- optimising your code by importing relevant library functions. 外部や基準のライブラリやパッケージを利用します。
- writing functions to avoid repetition. 重複を防ぐために関数（functions)を使いなさい。
- using comments to explain what your code does. あなたのコードを説明するために、コメントを用いる。

## Part A : Data Analysis　データ分析

#### Question 1
*(2 marks)*

Import the data from the url to a Pandas data frame:

データをurlから輸入しなさい：Pandas data frameにの:

https://raw.githubusercontent.com/justmarkham/DAT8/master/data/u.user。

#### Question 2
*(2 marks)*

Remove all entries with occupation `other` or `retired` from the data set.

職業によってすべての入力を取り除きなさい データセットからの`other`または`retired` 。

#### Question 3
*(6 marks)*

Plot a bar chart showing:

棒グラフをプロットしなさい：
- the number of people per occupation for the 10 occupations that occur most frequently in the data set.

最も頻繁にデータセットに存在する10の職業のための職業あたり人々の数。
- plot an additional bar, labelled other, showing the sum of all remaining entries in the data set.

PLEASE TRANSLATE TO JAPANESE

#### Question 4
*(2 marks)*

Find the:
- mean
- standard deviaion

of the age of people with the occupation: `administrator`.

PLEASE TRANSLATE TO JAPANESE

## Part B : Movement of a Particle 粒子の動き

A particle moves along a curve.

曲線に沿って粒子が動く。

The cartesian coordinates of the position of the particle $(r_x, r_y)$ are:

粒子$（r_x、r_y）$のポジションのデカルト座標は下記である：

\begin{align}
r_x &= \exp \left( -\dfrac{t}{T_1} \right)
\\
r_y &= 2 \cos \left(\dfrac{t}{T_2}\right)
\end{align}

$t$ = time (s)

Constants:

$T_1 = 1$ s

$T_2 = 1/3$ s.

#### Question 1
*(8 marks)*

Plot the trajectory of the particle in the $xy$-plane over the time interval $t = 0$ to $t = 2\pi$ s.

時間間隔の上の$xy$-飛行機の粒子の弾道をプロットしなさい$t = 0$ to $t = 2\pi$ s.

On the graph, show the particle’s locations at $t = 0$, $\pi/2$ and $2\pi$ using:

グラフ、ショーについて 使っているドルt= 0$、$\pi／2つ$、および$2\pi$の粒子の位置 ：
- a marker
- a label

#### Question 2
*(10 marks)*

The $x$ and $y$ components of the particle's velocity $(v_x, v_y)$:

粒子の速度$（v_x、v_y）$の$x$と$y$コンポーネント：

$$v_x=r_x'$$
$$v_y=r_y'$$

The $x$ and $y$ components of the particle's acceleration $(a_x, a_y)$:

粒子の加速$$（a_x、a_y）の$x$と$y$コンポーネント：

$$a_x=v_x'=r_x''$$
$$a_y=v_y'=v_y''$$

__(a)__ Express the particles acceleration $(v_x, v_y)$ symbolically, i.e. show the equation.

粒子加速$（v_x、v_y）$を象徴的に表現しなさい。すなわち、方程式を示す。

__(b)__ Plot the *magnitude* of the acceleration of the particle against time over the time interval $t = 0$ to $t = 2\pi$ s.

PLEASE TRANSLATE TO JAPANESE

The magnitude of acceleration, $A$:
$$A = \sqrt{a_x^2 + a_y^2}$$

#### Question 3
*(10 marks)*

Find the magnitude velocity of the particle at time $ t = 1$s.

粒子 時 ドル$t= 1$s のマグニチュード速度を見つけなさい。

Estimate the time/times when $r_y=0$

時間, $r_y=$ を評価しなさい

#### Question 4
*(10 marks)*

Animate the trajectory of the particle and save your work as a .mp4 file.

粒子の弾道をアニメーション化し、.mp4ファイルとしてあなたの仕事を保存しなさい。

```python

```

```python

```

```python

```

```python

```


# Neural Ordinary Differential Equations
A discussion of an interesting new paradigm in Neural Networks, adapted from "Neural Ordinary Differential Equations" by Chen et al. 2019 (https://arxiv.org/pdf/1806.07366.pdf)

## 1 - Background

### 1.1 - Neural Network Setup
Neural networks are a set of algorithms that are loosely based on the functonality of the human brain. These algorithms are designed to learn patterns in numerical data, by propagating input data through a series of hidden layers, with each layer being composed of several artificial 'neurons' or nodes. The final result is the output layer, a numerical representation of our desired prediction.

Consider an input dataset composed of a set of $m$ features: $(\vec{x_1}, \vec{x_2}, ..., \vec{x_m})$ .

We add a bias component to this dataset, $\vec{x_0} = 1$, a vector of ones, yielding our input layer, $\vec{x} := \vec{x_0}, ... \vec{x_m}$.

We need to now consider how to pass this input to our first hidden layer. Suppose this the first layer is composed of $n$ nodes, we define a set of weights $w=\vec{w_1},\vec{w_2} ..., \vec{w_n}$ where each $\vec{w_i}$ is a vector of length $m$ corresponding to node $i$. We then define $\vec{z_i} := \vec{x}\cdot \vec{w_{i}}$.

We are passing $\vec{z_i}$ to each node in the first hidden layer, but but what occurs at the node, and what is output at the next layer? The answer is the activation function. For the purpose of this discussion, we will focus solely on the sigmoid activation function $\sigma(z) = \dfrac{1}{1+e^{-z}}$, though it should be noted that many others exist. At each node, we will compute $\sigma(z_i)$ and that's it! We have our single layer representation.

To extrapolate to a full neural network, we simply repeat this process, using the output of the activation functions of the previous layer as the new input layer, taking the dot product with a new set of weights and computing the activation of these new inputs. The final layer will be our output layer, $\vec{y}$, and will have a number of neurons corresponding to our desired predictable (if we are predicting hand-written digits as in the exemplary MNIST dataset, our $\vec{y}$ will be a vector of length 10).

Our problem is thus clear, we must compute: $W$, the weights, $L$, the number of layers and $n_{}$ the number of neurons in each layer. And we must do so efficiently, as there will often be many many weights to compute and many layers in our network.

### 1.2 - Computing parameters and hyperparameters
We will seperate the values we wish to compute into two categories: 'parameters' (the weights) and the 'hyperparameters' (the number of layers and the number of neurons in each layer). In general, determining the hyperparameters is much more of an art than a science, and requires significant experimentation and intuition. On the other hand, determining the parameters can be done by following one of several well-defined protocols.

For our purposes, we will simply define a cost-function, which will turn out to be an aggregate of the cost function used in typical logistic regression (__NOTE: if you don't know logistic regression, stop here and go learn that first__). For now, I will simply state the cost function of a neural network implementing the sigmoid activation function:

\begin{align}
J(\Theta) = - \dfrac{1}{m}\sum_{i=1}^m \sum_{k=1}^K\left[
y_{k}^{(i)} \log((h_{\Theta}(x^{(i)}))_k) + (1-y)\log(1-(h_{\Theta}(x^{(i)}))_k)
\right]
+ \dfrac{\lambda}{2m}\sum_{l=1}^{L-1}\sum_{i=1}^{s_l}\sum_{j=1}^{s_{l+1}}\left(
\Theta_{j,i}^{l}
\right)^{2}
\end{align}
where $L$ is the total number of layers in the network, $s_l$ is the number of nodes in layer $l$, $K$ is the number of output classes/units and $\lambda$ is a regularization parameter, to prevent overfitting (another hyperparameter to compute).

With this cost function, we will choose starting values for the parameters, feed these forward through the network, and calculate the loss, known as the feedforward step. We then take this loss to update the weight and bias values moving backward through the network, known as the backpropagation step.

## 2 - Neural Networks in Python

#### 2.1 - Definitions
As per [this](https://towardsdatascience.com/how-to-build-your-own-neural-network-from-scratch-in-python-68998a08e4f6) tutorial, we will define the input layer $\textbf{x}$, an arbitrary amount of hidden layers, an output layer $\hat{\textbf{y}}$, the set of weights $\textbf{W}$ and biases $\textbf{b}$ and the activation function $\sigma$.

We will now create this class in Python, for a neural network with one hidden layer, having 4 nodes:

```python
import numpy as np

class NeuralNetwork:
    def __init__(self, x, y):
        self.input      = x
        self.weights1   = np.random.rand(self.input.shape[1],4)
        self.weights2   = np.random.rand(4,1)
        self.y          = y
        self.output     = np.zeros(y.shape)
```

To this class definition, we will add a feedforward step, where we assume the bias values $\textbf{b}$ are $0$ for simplicity.

### 2.2 - Training
To train this network, we need to find appropriate values for $\textbf{W}$ and $\textbf{b}$. We do so by feeding forward through the network with an initial set of parameters, and subsequently updating these parameters using backpropagation with an appropriate cost function. Adding the feedforward to our class definition is trivial:

```python
class NeuralNetwork:

    def __init__(self, x, y):
        self.input      = x
        self.weights1   = np.random.rand(self.input.shape[1],4)
        self.weights2   = np.random.rand(4,1)
        self.y          = y
        self.output     = np.zeros(self.y.shape)

    def feedforward(self):
        self.layer1 = sigmoid(np.dot(self.input, self.weights1))
        self.output = sigmoid(np.dot(self.layer1, self.weights2))
```

Now, to add the backpropagation step, we must determine the gradient of our cost function.

### 2. - MNIST Dataset
We will now work towards building a standard neural network from scratch, in Python, using the MNIST handwritten number dataset (a toy dataset commonly used in teaching image recognition).


# Midterm review

## 1- Measurement systems
### Goal of experiments
- __1- Engineering/scientific experimentation__
- __2- Operational system__

### Units - Unit conversion - Dimensional analysis
### Errors and Uncertainties

## 2- Dynamic response of measurement systems

\begin{align*}
a_n\frac{d^ny}{dt^n} + a_{n-1}\frac{d^{n-1}y}{dt^{n-1}}x + \dots + a_1 \frac{dy}{dt} + a_0 y = F(t)
\end{align*}

###  0$^{\mathrm{th}}$ order system

\begin{align*}
y(t) = K F(t) = \frac{1}{a_0} F(t) \quad \text{$K=1/a_0$ is called the _static sensitivity_ or _steady-state gain_}
\end{align*}

###  1$^{\mathrm{st}}$ order system

\begin{align*}
a_1 \frac{dy}{dt} + a_0 y = F(t) \quad
\tau \frac{dy}{dt} + y = K F(t)
\end{align*}
with $\tau = a_1/a_0$ the time constant of the system.

###  2$^{\mathrm{nd}}$ order system

\begin{align*}
a_2 \frac{d^2y}{dt^2} + a_1 \frac{dy}{dt} + a_0 y = F(t) \quad
\frac{1}{\omega^2_n} \frac{d^2y}{dt^2} + \frac{2\zeta}{\omega_n} \frac{dy}{dt} + y = K F(t)
\end{align*}

## 3- Digital data acquisition
### Binary to decimal conversion

### ADC system
__Resolution__
\begin{align*}
\Delta V = \frac{V_{max} - V_{min}}{2^N}
\end{align*}
N : number of bits

__Quantization error__
\begin{align*}
Q = \pm \frac{1}{2} \Delta V = \pm \frac{V_{max} - V_{min}}{2^{N+1}}
\end{align*}

__Clipping__
### Discrete Sampling

### Aliasing
Nyquist Shannon sampling rate thereom states:
>_If a function, $y(t)$, contains no frequencies greater than $f_{signal}(MAX)$, it is completely determined if it is sampled with a sampling frequency $f_s$ such that:_
\begin{align*}
f_s > 2 f_{signal}(MAX)
\end{align*}
> if $f_s \ngtr 2f_{signal}(MAX)$
\begin{align*}
f_a =  \left| f - f_s \cdot \mathrm{NINT}\left( \frac{f}{f_s} \right) \right|
\end{align*}

### Fourier Series
\begin{align*}
y(t) = \frac{A_0}{2} + \sum_{n=1}^{\infty} \left( A_n \cos \left(\frac{2\pi nt}{T}\right) + B_n \sin \left(\frac{2\pi nt}{T}\right) \right)
\end{align*}
- __Fundamental harmonic/frequency__: $n=1$, noted $\omega_0=2\pi/T$ or $f_0 = 1/T$.
- $T$: signal period
- Frequencies of $\sin$ and $\cos$ are multiple of the fundamental frequency $\omega$: __$n^{th}$ harmonic__.
- $A_n$, $B_n$: Fourier coefficients
- $A_0/2$: mean of the function

### Fourier Transform, DFT, FFT
\begin{align*}
Y(k \Delta f) = \sum_{n=0}^{N-1} y(n \Delta t) e^{-i (2\pi k \Delta f)(n\Delta t)} \qquad k = 0,\,1,\,2, \dots , \, N-1
\end{align*}

\begin{array}{ll}
N & \text{number of samples taken over sampling interval $T_s$}\\
T_s & T_s = N\Delta t\quad\text{total sampling time}\\
\Delta f & \text{frequency increment/resolution}\\
 & \Delta f = 1/T_s = 1/N\Delta t= f_s/N\\
\Delta t & \text{time increment (or sampling period)}\\
 & \Delta t = T_s/N\\
f_s & \text{sampling rate}\\
 & f_s = 1/\Delta t = N/T_s\\
f_{max} & f_{max} = f_{folding} = f_s/2 = N/2 \cdot \Delta f \quad \text{max resolved frequency}
\end{array}

### Leakage
### Windowing
### Anti-aliasing filter

### Analog signal reconstruction
If an analog signal is sampled properly: reconstruct the analog signal with the _cardinal series_.

## 4- Signal Conditioning

### Basic electronics

### Filters
#### $1^{\mathrm{st}}$ order low-pass filter

\begin{align*}
\omega_{cutoff} & = \frac{1}{RC} \quad  f_{cutoff} =  \frac{\omega_{cutoff}}{2 \pi} = \frac{1}{2\pi RC} \\
G & = \frac{|V_{out}|}{|V_{in}|} =  \frac{1}{\sqrt{1+ \left( \frac{f}{f_{cutoff}}\right)^2 }} \quad
\phi = -\arctan \left( \frac{f}{f_{cutoff}} \right)
\end{align*}
#### Low-pass filter of order $n$
\begin{align*}
G = \frac{1}{\sqrt{1+ \left( \frac{f}{f_{cutoff}}\right)^{2n} }}
\end{align*}
#### $1^{\mathrm{st}}$ order high-pass filter
\begin{align*}
G = \frac{1}{\sqrt{1+ \left( \frac{f_{cutoff}}{f}\right)^2 }} \quad
\phi = \arctan \left( \frac{f_{cutoff}}{f} \right)
\end{align*}

### Op-Amps
ideal op-amps vs real effects (impedance loading, GBP, CMRR)

## 5- Design Stage uncertainty analysis

### Expected uncertainty
RSS: root of the sum of the squares

### Combining elemental uncertainties
\begin{align}
u_{sys} = \sqrt{\sum_i u_{e,i}^2} \quad \text{with $u_{e,i}$ elemental error}
\end{align}

### Overall uncertainty
\begin{align}
u_{overall} = \sqrt{\sum_j u_{sys,j}^2} \quad \text{with $u_{sys,j}$ error of each component}
\end{align}

### Uncertainty of a result
\begin{align}
R = f(x_1, x_2, \dots) \quad u_R = \sqrt{\sum_i \left( u_{x_i} \frac{\partial f}{\partial x_i} \right)^2}
\end{align}
If f is polynomial
\begin{align}
f = x_1^{a_1} \cdot x_2^{a_2} \cdot \dots \quad \frac{u_R}{R} = \sqrt{\sum_i \left( \frac{u_{x_i}}{x_i} a_i \right)^2}
\end{align}

## Sample Problems
### Quiz
> What are the major elements of a data acquisition/measurement system?

> How can you check for clipping in your acquired data? How could you correct for it?

> When acquiring a repeatable signal of fixed frequency with a data acquisition system, which test can you use to determine if you have aliasing in your data?

> In digital data acquisition, what is leakage? How will it manifest itself on a frequency spectrum?

> Propose at least one strategy to minimize leakage.

> How should you select the sampling frequency and number of samples acquired with digital data acquisition system to have the best possible frequency resolution?

> A signal consists of a sine wave of frequency $f$ = 30 Hz. Data are sampled discretely at sampling frequency $f_s$ = 49 Hz. Is there aliasing? Calculate the apparent frequency in Hz.

### Filter
A beam vibrates at 100 Hz. The vibrations are measured with a strain gage and Wheatstone bridge.  The output voltage of the sensor is $V_{out} = 2 V_s \epsilon$, with $V_s=5.00$ V the supply voltage and $\epsilon=\delta L/L$ the strain.  The strain is in the range of $\pm 5\times 10^{-4}$. In addition, you know that:

> there is noise on the input: $_{fnoise}$ = 40,000Hz with amplitude 0.100 mV .

> The output is sent to your NI USB-6008 DAQ board, which has a 12bit A/D converter with a range of $\pm$ 5V .

> All op-amps have a GBP (non-inverting) of 1.0 MHz.

Propose a scheme to condition the signal (amplify if necessarty and filter the noise out).

_Solution_: Here we have to design the signal conditioning step of the measurement system.  This has two steps:
- 1 select amplifier gain (if necessary) so that we pair the DAQ and Wheatstone bridge well.
- 2 select cutoff frequency and order of filter to effectively remove the noise from the signal.  Effective filtering means that amplitude of the noise should be less than DAQ (ADC) resolution and carrier component of signal is not significantly affected.

__step 1__: First we need to determine what is the amplitude of the carrier signal.

```python
import numpy
A_c = 2*5.00*0.0005
f_c = 100 # Hz
A_n = 0.0001
f_n = 40000 #Hz
#V_o = A*numpy.sin(2*numpy.pi*100*t)
```

So in summary we have:
- Carrier signal: $f_c$ = 100 Hz, $A_c$ = 5.00 mV
- Noise signal: $f_n$ = 40 000 Hz, $A_n$ = 0.100 mV

DAQ has resolution:
- $\Delta V = (V_M - V_m)/(2^N)$

```python
deltaV = (10)/2**12
print('\Delta V = ',deltaV,' V')
```

Here we see that the signal is about twice the resolution of the DAQ.  We have to amplify!

We select gain such that amplitude of amplified carrier is 80% of input range of DAS, or 4 V.

```python
# gain of amplifier
G=4/0.005
print('G = ',G)
```

While a gain of 800 is ok from a GBP point of view, we will run into impedance loading issues ($R$ will be either too high compare to $R_{in} = 1 M\Omega$ of real op-amp, or too small with respect to $R_{out} = 1 \Omega$ of real op-amp.  Therefore ideal op-amp rules will fail.  As a rule of thumb we do not want the gain of amplifier stage to be more than 30.

Let's use two stages.

```python
# use 2 amplifier stages
G_i = numpy.sqrt(G)
print('G_i = ', G_i)
A_cA = A_c*G
A_nA = A_n*G
print('amplitude of carrier ',A_cA,' V', 'amplitude of noise ',A_nA, ' V')
```

Step 2:  Select parameter of Low-pass filter, i.e.:
- select $f_{co}$, and
- the order $n$ of LPF.

Again with the goals:
- $A_n < \Delta V$
- $A_c$ and $\phi_c$ not changed significantly

```python
#gain of LPF for noise
G_LPF_n = deltaV/A_nA
print(G_LPF_n)
```

Therefore the gain of your low pass filter at the frequency of the noise should be about 3%.

Let's select a cutoff frequency of 10 times the carrier frequency and assess its merit.

```python
# n = 1 LPF
f_co = 10*f_c
G_LPF = 1/numpy.sqrt(1+(f_n/f_co)**2)
print(f_co, G_LPF)
G_LPF = 1/numpy.sqrt(1+(f_c/f_co)**2)
phi = - numpy.arctan(f_c/f_co)*180/numpy.pi #deg
print(G_LPF, phi)
```

We have a successful design!

The noise is now below the resolution of the DAS and the carrier frequency has not been affected significantly (0.5% decrease in amplitude, phase of $\approx-6^\circ$).

## Dynamic response
A first-order, passive low-pass (RC) filter is designed to have a cut-off frequency, $f_c = 1$ kHz. Its resistance equals 120 $\Omega$. Determine the filter:
> time constant (in s)

> capacitance (in F)

> signal magnitude ratio at $f = 3$ kHz.

```python

```


```
from sympy import *
from sympy.interactive.printing import init_printing
init_printing()
```

```
from sympa import domath
```

```
import pandas as pd
```

```
df = pd.DataFrame({'x' : range(7)})
df['x'] = df.x * 10
df['y'] = df.x * 0.05
df['r'] = pi * df['y'] / 2
df
```

      x
      y
      r

      0
        0
       0.0
             0

      1
       10
       0.5
       0.25*pi

      2
       20
       1.0
        0.5*pi

      3
       30
       1.5
       0.75*pi

      4
       40
       2.0
        1.0*pi

      5
       50
       2.5
       1.25*pi

      6
       60
       3.0
        1.5*pi

```
#for those who don't know..."pi" is irrational...
df['r'][1]
```

```
x,y,r,rn1,x1,xn1,yn1 = symbols('x y r r_-1 x_1 x_-1 y_-1')
f = x + xn1
g = x**y + x*y*3.0 + (x1 * 0.5 + xn1 * pi / (yn1 + 1.0))**2
h = sin(r)
i = sin(r) / cos(rn1 + 1)
```

```
f, g, h, i
```

```
df.x + df.x.shift(1) #this is easy...but not exactly
```

    0    NaN
    1     10
    2     30
    3     50
    4     70
    5     90
    6    110
    Name: x, dtype: float64

```
#3.0 * df.x * df.y + df.x.mul(df.y) + (df.shift(1).mul(pi).div(df.shift(1) + 1.0))# + (0.5 * df.x.shift(-1))) # what the heck am I doing???
```

```
df['f'] = domath(df,f)
df['g'] = domath(df,g)
df['h'] = domath(df,h)
df['i'] = domath(df,i)
```

```
#round the excessive decimals...
df.g, df.h, df.i = (df[c].round(4) for c in ('g','h','i')); df
```

      x
      y
      r
      f
      g
      h
      i

      0
        0
       0.0
             0
       NaN
              NaN
       0.0000
          NaN

      1
       10
       0.5
       0.25*pi
        10
         118.1623
       0.7071
       1.3087

      2
       20
       1.0
        0.5*pi
        30
        1371.9676
       1.0000
      -4.6958

      3
       30
       1.5
       0.75*pi
        50
        2942.9143
       0.7071
      -0.8403

      4
       40
       2.0
        1.0*pi
        70
        5771.1786
       0.0000
       0.0000

      5
       50
       2.5
       1.25*pi
        90
       23220.5400
      -0.7071
       1.3087

      6
       60
       3.0
        1.5*pi
       110
              NaN
      -1.0000
      -4.6958

```
assert f.subs([(x,50),(xn1,40)]) == df['f'].iloc[5]
assert round(g.subs([(x,50),(y,2.5),(xn1,40),(x1,60),(yn1,2.0)]),4) == df['g'].iloc[5]
assert round(h.subs([(r,1.25*pi)]),4) == df['h'].iloc[5]
assert round(i.subs([(r,1.25*pi),(rn1,pi)]),4) == df['i'].iloc[5]
```

```

```

```

```


```python
import sympy
import Hamilton
import Birkhoff
sympy.init_printing()
```

```python
x = sympy.IndexedBase("x")
y = sympy.IndexedBase("y")
p = sympy.IndexedBase("p")
q = sympy.IndexedBase("q")
t = sympy.IndexedBase("tau")
o = sympy.Symbol("omega", positive=True)
a = sympy.Symbol("alpha", positive=True)
b = sympy.Symbol("beta")
l = sympy.Symbol("lambda", positive=True)
```

First, create your Hamiltonian

```python
H = p[1]*p[1]/4 + o*o/4/q[1]/q[1] - 1/a/2/q[1] - 2/sympy.sqrt(q[1]*q[1]+q[2]*q[2]) + (a+2)*p[2]*p[2]/a/4; H
```

Substitute Hamiltonian to a class `Hamiltonian`

```python
ham = Hamilton.Hamiltonian(H, [p[1],p[2]], [q[1],q[2]])
```

Expand Hamiltonian around an equilibrium point

```python
equilibrium_points = [0, 0, o*o*a/(4*a+1), 0]
ham.expand_around_equilibrium(equilibrium_points, max_degree=4)
```

`rescale()` will rescale the coefficient of $p_i$ and $q_i$

```python
ham.rescale()
ham.coeff_subs([(a, (8-l*l)/(4*l*l-4))])
ham.rotate45()
```

Now we are able to calculate Birkhoff normal form.

Substitute the above `Hamiltonian` to a class `LieTransform`

```python
birkhoff = Birkhoff.LieTransform.fromHamiltonian(ham)
birkhoff.exec()
```

    Initialized
    do exec() and calculate normal form
    Lie transform completed!!
    do normalform() to print normal form of input Hamiltonian

Run `normalform()` and you will see Birkhoff normal form!!

```python
birkhoff.normalform()
```

```python

```


```python
import sympy as sp
import numpy as np
import cloudpickle

```

    ['m1', 'g'] [1, 9.81]

```python
q = q0, q1 = sp.symbols('q:2')
sympars = m1, g = sp.symbols('m_1, g') # Same order should be used inside MechanicalSystem Class
order = ['m1', 'g']
G = sp.Matrix([sp.cos(q0)*g, sp.sin(q1)*m1])
```

```python
to_save = {'order': order,
            'G' : sp.lambdify([q, *sympars], G)}

with open('gfile.txt', 'wb') as gfile:
    test = cloudpickle.dumps(to_save)
    gfile.write(test)

with open('gfile.txt', 'rb') as gfile:
    model = cloudpickle.load(gfile)
```

```python
# In main script
pars = {'m1' : 1, 'g' : 9.81} # order is arbitrary

# In systemsym
constant_names = model['order']
constant_value = [pars[name] for name in constant_names] # Corresponding numbers in same order as above

Luse = lambda q: model['G'](q, *constant_value) # in systemsym class
Luse([0,3.14/2]) # in equations of motion

```

    array([[ 9.81      ],
           [ 0.99999968]])