# Selección óptima de portafolios I

En la clase pasada vimos que:
- La LAC describe las posibles selecciones de riesgo-rendimiento entre un activo libre de riesgo y un activo riesgoso.
- Su pendiente es igual al radio de Sharpe del activo riesgoso.
- La asignación óptima de capital para cualquier inversionista es el punto tangente de la curva de indiferencia del inversionista con la LAC (depende de las preferencias particulares - aversión al riesgo).

Para todo lo anterior, supusimos que ya teníamos el portafolio óptimo (activo riesgoso).

En el siguiente análisis:

**Objetivos:**
- ¿Cuál es el portafolio óptimo de activos riesgosos?
 - ¿Cuál es el mejor portafolio de activos riesgosos?
 - Es un portafolio eficiente en media-varianza.
- Problema: dado un conjunto de activos riesgosos, ¿cómo construimos la mejor combinación?

*Referencia:*
- Notas del curso "Portfolio Selection and Risk Management", Rice University, disponible en Coursera.
___

## 1. Maximizando el radio de Sharpe

### ¿Qué pasa si tenemos dos activos riesgosos?

Cuando tenemos dos o más activos riesgosos, tenemos disponibles diferentes LAC. ¿Qué significan sus pendientes?

 Ver en el tablero.

Pregunta:
- ¿Qué es lo que se quiere?

**Conclusión:**
- El mejor portafolio de activos no depende de las preferencias individuales, y por tanto va a ser igual para todos.
- Dicho mejor portafolio maximiza el radio de Sharpe.
- A este portafolio lo llamaremos el portafolio eficiente en media-varianza (EMV)

**Idea principal: el portafolio óptimo de activos riesgosos es independiente de las preferencias del inversionista.**
- El portafolio EMV determina el portafolio óptimo de activos riesgosos.
- Todos tendremos el mismo portafolio de activos riesgosos (EMV), y lo combinaremos con el activo libre de reisgo de acuerdo con las preferencias de cada uno de nosotros (aversión al riesgo).
- La LAC combinando el activo libre de riesgo y el portafolio EMV, se vuelve el conjunto de portafolios eficientes.

Entonces, se deben seguir los siguientes pasos:
1. Crear la frontera media-varianza.
2. Encontrar el portafolio que maximize el radio de Sharpe (portafolio EMV).
3. Construir la frontera eficiente (LAC) del punto $(0,r_f)$ al punto $(\sigma_s,E[r_s])$ del portafolio EMV.
4. Combinar de acuerdo a sus preferencias.
___

## 2. Solución analítica del portafolio EMV: caso con dos activos.

Queremos solucionar el siguiente problema:

\begin{align}
\max_{w_1,w_2} &\quad \frac{E[r_p]-r_f}{\sigma_p}\\
\text{s.a.}    &\quad E[r_p]=w_1E[r_1]+w_2E[r_2]\\
               &\quad \sigma_p=\sqrt{w_1^2\sigma_1^2+w_2^2\sigma_2^2+2w_1w_2\rho_{12}\sigma_1\sigma_2}\\
               &\quad w_1+w_2=1, \quad w_1,w_2\geq0
\end{align}

el cual es equivalente a

\begin{align}
\max_{w_1} &\quad \frac{w_1E[r_1]+(1-w_1)E[r_2]-r_f}{\sqrt{w_1^2\sigma_1^2+(1-w_1)^2\sigma_2^2+2w_1(1-w_1)\rho_{12}\sigma_1\sigma_2}}\\
\text{s.a.}    &\quad 0\leq w_1\leq1
\end{align}

**Actividad.**
El anterior es un problema de maximizar una función de una variable en un dominio cerrado. No debaría representar dificultad.

Encontrar la solución analítica a este problema.

Quien primero lo haga, y salga a explicarlo al tablero, le subo alguna tarea o quiz a 100.

Deben llegar a:

$$w_{1,EMV}=\frac{(E[r_1]-r_f)\sigma_2^2-(E[r_2]-r_f)\sigma_{12}}{(E[r_2]-r_f)\sigma_1^2+(E[r_1]-r_f)\sigma_2^2-((E[r_1]-r_f)+(E[r_2]-r_f))\sigma_{12}}.$$

Si nadie lo ha hecho en 30 min., procederé a hacerlo yo.

**Nota:**
- así como obtuvimos una expresión para el peso del portafolio de mínima varianza con dos activos, obtenemos una expresión para el peso del portafolio Eficiente en Media-Varianza.
- Estas actividades son sin duda un buen ejercicio, y se pueden replicar usando técnicas de varias variables (multiplicadores de Lagrange) cuando se tengan más de dos activos.
- Sin embargo, la complejidad del problema crece considerablemente con el número de variables, y la solución analítica deja de ser viable cuando mencionamos que un portafolio bien diversificado consta aproximadamente de 50-60 activos.
- En esos casos, este problema se soluciona con rutinas numéricas que hagan la optimización por nosotros.
- Por eso, les enseño cómo resolver este problema con optimizadores numéricos, porque son una solución viable y escalable a más variables.

## 3. Ejemplo ilustrativo.

Retomamos el ejemplo de mercados de acciones en los países integrantes del $G5$: EU, RU, Francia, Alemania y Japón.

```python
# Importamos pandas y numpy
import pandas as pd
import numpy as np
```

```python
# Resumen en base anual de rendimientos esperados y volatilidades
annual_ret_summ = pd.DataFrame(columns=['EU', 'RU', 'Francia', 'Alemania', 'Japon'], index=['Media', 'Volatilidad'])
annual_ret_summ.loc['Media'] = np.array([0.1355, 0.1589, 0.1519, 0.1435, 0.1497])
annual_ret_summ.loc['Volatilidad'] = np.array([0.1535, 0.2430, 0.2324, 0.2038, 0.2298])

annual_ret_summ.round(4)
```

      EU
      RU
      Francia
      Alemania
      Japon

      Media
      0.1355
      0.1589
      0.1519
      0.1435
      0.1497

      Volatilidad
      0.1535
      0.243
      0.2324
      0.2038
      0.2298

```python
# Matriz de correlación
corr = pd.DataFrame(data= np.array([[1.0000, 0.5003, 0.4398, 0.3681, 0.2663],
                                    [0.5003, 1.0000, 0.5420, 0.4265, 0.3581],
                                    [0.4398, 0.5420, 1.0000, 0.6032, 0.3923],
                                    [0.3681, 0.4265, 0.6032, 1.0000, 0.3663],
                                    [0.2663, 0.3581, 0.3923, 0.3663, 1.0000]]),
                    columns=annual_ret_summ.columns, index=annual_ret_summ.columns)
corr.round(4)
```

      EU
      RU
      Francia
      Alemania
      Japon

      EU
      1.0000
      0.5003
      0.4398
      0.3681
      0.2663

      RU
      0.5003
      1.0000
      0.5420
      0.4265
      0.3581

      Francia
      0.4398
      0.5420
      1.0000
      0.6032
      0.3923

      Alemania
      0.3681
      0.4265
      0.6032
      1.0000
      0.3663

      Japon
      0.2663
      0.3581
      0.3923
      0.3663
      1.0000

Supondremos, además, que la tasa libre de riesgo es $r_f=5\%$.

```python
# Tasa libre de riesgo
rf = 0.05
```

Entonces, supondremos que tenemos disponibles los activos correspondientes a los mercados de acciones de EU y Japón, y en adición el activo libre de riesgo.

#### 1. Construir la frontera de mínima varianza

```python
# Vector de w variando entre 0 y 1 con n pasos
n = 101
w = np.linspace(0, 1, n)
# Rendimientos esperados individuales
# Activo1: EU, Activo2:Japon
E1 = annual_ret_summ.loc['Media', 'EU']
E2 = annual_ret_summ.loc['Media', 'Japon']
# Volatilidades individuales
s1 = annual_ret_summ.loc['Volatilidad', 'EU']
s2 = annual_ret_summ.loc['Volatilidad', 'Japon']
# Correlacion
r12 = corr.loc['EU', 'Japon']
# Covarianza
s12 = r12 * s1 * s2
```

```python
# DataFrame de portafolios:
# 1. Índice: i
# 2. Columnas 1-2: w, 1-w
# 3. Columnas 3-4: E[r], sigma
# 4. Columna 5: Sharpe ratio
portafolios = pd.DataFrame(data={'w_1': w,
                                 'w_2': 1 - w,
                                 'Media': w * E1 + (1 - w) * E2,
                                 'Vol': ((w * s1)**2 + ((1 - w) * s2)**2 + 2 * w * (1 - w) * s12)**0.5
                                })
portafolios['RS'] = (portafolios['Media'] - rf) / portafolios['Vol']
portafolios.head()
```

      w_1
      w_2
      Media
      Vol
      RS

      0
      0.00
      1.00
      0.149700
      0.229800
      0.433856

      1
      0.01
      0.99
      0.149558
      0.227916
      0.436820

      2
      0.02
      0.98
      0.149416
      0.226041
      0.439814

      3
      0.03
      0.97
      0.149274
      0.224176
      0.442839

      4
      0.04
      0.96
      0.149132
      0.222322
      0.445894

```python
# Importar librerías de gráficos
from matplotlib import pyplot as plt
%matplotlib inline
```

```python
# Gráfica de dispersión de puntos coloreando
# de acuerdo a RS
plt.figure(figsize=(6, 4))
plt.scatter(portafolios['Vol'], portafolios['Media'], c=portafolios['RS'], cmap='RdYlBu')
plt.grid()
plt.xlabel('Volatilidad $\sigma$')
plt.ylabel('Rendimiento esperado $E[r]$')
plt.colorbar()
plt.axis([0.14, 0.20, 0.135, 0.15])
```

#### 2. Encontrar el portafolio que maximiza el radio de Sharpe (EMV)

Primero, encontramos este portafolio con la fórmula que obtuvimos:

$$w_{1,EMV}=\frac{(E[r_1]-r_f)\sigma_2^2-(E[r_2]-r_f)\sigma_{12}}{(E[r_2]-r_f)\sigma_1^2+(E[r_1]-r_f)\sigma_2^2-((E[r_1]-r_f)+(E[r_2]-r_f))\sigma_{12}}.$$

```python
# Fórmula que obtuvimos
w_EMV = ((E1 - rf) * s2**2 - (E2 - rf) * s12) / ((E2 - rf) * s1**2 + (E1 - rf) * s2**2 - (E1 - rf + E2 - rf) * s12)
w_EMV
```

    0.6983139170512034

Ahora sí, con la función scipy.optimize.minimize

```python
# Importar la función minimize del módulo optimize
from scipy.optimize import minimize
```

```python
# Función objetivo (-RS)
def menos_RS(w, E1, E2, s1, s2, s12, rf):
    Erp = w * E1 + (1 - w) * E2
    sp = ((w * s1)**2 + ((1 - w) * s2)**2 + 2 * w * (1 - w) * s12)**0.5
    RS = (Erp - rf) / sp
    return -RS
```

```python
# Dato inicial
w0 = 0.5
# Cotas de las variables
bnd = (0, 1)
```

```python
# Optimización numérica
EMV = minimize(fun=menos_RS,
               x0=w0,
               args=(E1, E2, s1, s2, s12, rf),
               bounds=(bnd, ))
# Resultado
EMV
```

          fun: array([-0.63087253])
     hess_inv: <1x1 LbfgsInvHessProduct with dtype=float64>
          jac: array([3.39728246e-06])
      message: b'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL'
         nfev: 10
          nit: 4
       status: 0
      success: True
            x: array([0.69831582])

```python
w_EMV2 = EMV.x
w_EMV2, w_EMV
```

    (array([0.69831582]), 0.6983139170512034)

Con lo anterior, podemos obtener datos de rendimiento esperado y volatilidad del portafolio EMV

```python
# Rendimiento esperado y volatilidad del portafolio EMV
E_EMV = w_EMV * E1 + (1 - w_EMV) * E2
s_EMV = ((w_EMV * s1)**2 + ((1 - w_EMV) * s2)**2 + 2 * w_EMV * (1 - w_EMV) * s12)**0.5
```

```python
E_EMV, s_EMV
```

    (0.13978394237787292, 0.14231708951606933)

```python
RS_EMV = (E_EMV - rf) / s_EMV
```

```python
# Gráfica de dispersión de puntos coloreando
# de acuerdo a SR, y portafolio EMV
plt.figure(figsize=(6, 4))
plt.scatter(portafolios['Vol'], portafolios['Media'], c=portafolios['RS'], cmap='RdYlBu', label='Portafolios')
plt.plot(s_EMV, E_EMV, '*y', ms=10, label='Portafolio EMV')
plt.grid()
plt.xlabel('Volatilidad $\sigma$')
plt.ylabel('Rendimiento esperado $E[r]$')
plt.legend(loc='best')
plt.colorbar()
plt.axis([0.14, 0.20, 0.135, 0.15])
```

#### 3. Construir LAC

Ahora, dibujamos la LAC, combinando el portafolio EMV con el activo libre de riesgo:

```python
# Vector de wp variando entre 0 y 1.5 con n pasos
sp = np.linspace(0, 0.2)
```

```python
# DataFrame de CAL:
# 1. Índice: i
# 2. Columnas 1-2: wp, wrf
# 3. Columnas 3-4: E[r], sigma
# 4. Columna 5: Sharpe ratio
LAC = pd.DataFrame(data={'Vol': sp,
                         'Media': RS_EMV * sp + rf})
LAC.head()
```

      Vol
      Media

      0
      0.000000
      0.050000

      1
      0.004082
      0.052575

      2
      0.008163
      0.055150

      3
      0.012245
      0.057725

      4
      0.016327
      0.060300

```python
# Gráfica de dispersión de puntos coloreando
# de acuerdo a SR, portafolio EMV y LAC
plt.figure(figsize=(6, 4))
plt.scatter(portafolios['Vol'], portafolios['Media'], c=portafolios['RS'], cmap='RdYlBu', label='Portafolios')
plt.plot(s_EMV, E_EMV, '*y', ms=10, label='Portafolio EMV')
plt.plot(LAC['Vol'], LAC['Media'], label='LAC')
plt.grid()
plt.xlabel('Volatilidad $\sigma$')
plt.ylabel('Rendimiento esperado $E[r]$')
plt.legend(loc='best')
plt.colorbar()
#plt.axis([0.14, 0.20, 0.135, 0.15])
```

#### 4. Combinación óptima de acuerdo a preferencias

Con los datos anteriores, y la caracterización de aversión al riesgo, se escoge la combinación óptima entre el portafolio EMV y el activo libre de riesgo de acuerdo a:

$$w^\ast=\frac{E[r_s-r_f]}{\gamma\sigma_s^2}.$$

```python
# Para gamma=7
g = 7
w_opt = (E_EMV - rf) / (g * s_EMV**2)
w_opt
```

    0.6332665142475

```python
w_opt * w_EMV, w_opt * (1 - w_EMV), 1 - w_opt
```

    (0.44221882010153346, 0.1910476941459666, 0.3667334857525)

Created with Jupyter by Esteban Jiménez Rodríguez.


```python
import os.path
import scipy.io
import pandas as pd
import numpy as np
import re
import glob
```

```python
Xsim = np.array([[  2.83717594235889647436,  11.63646180704327015576],
 [ -3.327921891190484871,    10.87319571410462337724],
 [  5.6266615323741531185,    9.06645711159280409674],
 [  6.43838210179274383904,  13.62895274146651303226],
 [ -0.73235675340731809513,   7.0584172849290567342 ],
 [ 10.95457732821499341469,  20.9159290909855073437 ],
 [ 10.94582100826051629383,   9.31802058456702120282],
 [  4.81183361703341194726,  10.56965656760404748127]])
```

```python
# Do PCA analysis
mu = np.mean(Xsim,axis=0) # calculate mean
w,v = np.linalg.eig(np.dot((Xsim-mu).T,(Xsim-mu))) # calculate eigenvalues of covariance matrix

np.dot((Xsim-mu).T,(Xsim-mu))
```

    array([[ 179.45107905,   75.51832891],
           [  75.51832891,  124.73769156]])

```python
print(np.dot(Xsim-mu,v))

```

    [[ -1.51866902   1.06885836]
     [ -7.00440235   3.98394604]
     [ -0.71056235  -2.63696376]
     [  2.57378213   0.6323386 ]
     [ -7.06980803  -0.6296442 ]
     [ 10.45543239   4.00511681]
     [  3.78876869  -5.48525481]
     [ -0.51454146  -0.93839703]]

```python
v[:,0] # first pca eigenvector
```

    array([ 0.81871654,  0.5741979 ])

```python
scores = np.dot((Xsim - mu),v[:,0]) # project into lower dimensional space
Xproj = np.outer(scores,v[:,0]) + mu # back project into original data space
```

```python
scores
```

    array([ -1.51866902,  -7.00440235,  -0.71056235,   2.57378213,
            -7.06980803,  10.45543239,   3.78876869,  -0.51454146])

```python
Xproj
```

    array([[  3.45091217,  10.76136979],
           [ -1.04034842,   7.61147321],
           [  4.11252247,  11.22538295],
           [  6.8014696 ,  13.11124667],
           [ -1.09389714,   7.57391741],
           [ 13.25430701,  17.63687373],
           [  7.79619919,  13.80888941],
           [  4.27300801,  11.33793773]])

```python
import matplotlib.pyplot as plt
%matplotlib inline
plt.plot(Xsim[:,0],Xsim[:,1],'o')
plt.plot(mu[0],mu[1],'go')
x = np.linspace(-4,15)
y = v[1,0]/v[0,0] * (x - mu[0]) + mu[1]
plt.plot(x,y,'k')

plt.plot(Xproj[:,0], Xproj[:,1], 'ro')
for x, y in zip(Xsim, Xproj):
    plt.plot([x[0],y[0]], [x[1],y[1]], 'r-')

plt.axis('equal')
plt.title('PCA dimensionality reduction')
```

### Latent variable model of Dimensionality Reduction (P-PCA)

 - Latent variable $\mathbf{z} \in \mathcal{R}^M \sim \mathcal{N}(\mathbf{0}, \mathbf{I})$
 - Data $\mathbf{x} \in \mathcal{R}^D = W \mathbf{x} + \mu + \epsilon$, where $\epsilon \sim
     \mathcal{N}(\mathbf{0}, \sigma^2 \mathbf{I})$.
 - Model parameters are $W \sim M \times D$ and $\sigma^2$

### _A posteriori_ distribution of latent variable

$$
\begin{align}
\Pr(\mathbf{z} \mid \mathbf{x}) &= \mathcal{N}( E[\mathbf{z}], \mathrm{Cov}(\mathbf{z})) \\
&= \mathcal{N}(W^T C^{-1} (\mathbf{x} - \mu), \mathbf{I} - W^T C^{-1} W)
\end{align}$$
where $C = W W^T + \sigma^2 \mathbf{I}$ is the covariance of the marginal $\Pr(x)$.

### EM algorithm for P-PCA
 1. Initialize parameters
 2. **E-step:** Calculate $\Pr(\mathbf{z}_n \mid \mathbf{x}_n)$ for each data point. You will need the mean of this
 distribution, $E[\mathbf{z}_n]$ and the outer product
 $E[\mathbf{z}_n \, \mathbf{z}_n^T] = \mathrm{Cov}(\mathbf{z}_n) +
     E[\mathbf{z}_n] E[\mathbf{z}_n]^T$
 3. **M-step:** Update $W$ and $\sigma^2$

     $$W_{new} = \left( \sum_{n=1}^N (\mathbf{x}-\mu) \; E[\mathbf{z}_n]^T \right)
     \left(\sum_{n=1}^N E[\mathbf{z} \, \mathbf{z}^T] \right)^{-1}$$

     $$\sigma_{new}^2 = \frac{1}{N D} \mathrm{Tr} \left( \sum_{n=1}^N (\mathbf{x}-\mu) (\mathbf{x}-\mu)^T -
     W_{new} \sum_{n=1}^N E[\mathbf{z}_n] (\mathbf{x}-\mu)^T \right)$$

 4. Iterate until convergence (or just a bunch of times)

```python
D = Xsim.shape[1]
N = Xsim.shape[0]
M = 1
# EM for P-PCA

# Initialize parameters
Wnew = np.random.randn(D,1)
sigNew = 0.5

# Note that differently from our normal convention, Xsim is an NxD matrix.
# That means each observation is a row vector rather than a column vector.
# The result is that Ez ends up being a row vector (EzzT is properly square).
for i in range(5000):
    Wold = Wnew
    sigOld = sigNew
    C = Wold.dot(Wold.T) + sigOld*np.eye(D) # covariance of P(x)
    Ez = Wold.T.dot(np.linalg.inv(C)).dot((Xsim - mu).T) # W^T C^-1 (x - mu)
    EzzT = (np.eye(M) - Wold.T.dot(np.linalg.inv(C)).dot(Wold))*N + (Ez.dot(Ez.T)) # Cov(z) + Ez Ez^T
    Wnew = ((Xsim - mu).T).dot(Ez.T) # break it into two lines - first product of (x-mu) and Ez
    Wnew = Wnew.dot(np.linalg.inv(EzzT)) # then scale down by EzzT
    sigNew = 1/(N*D) * np.trace((Xsim - mu).T.dot(Xsim-mu) - Wnew.dot(Ez.dot(Xsim-mu))) # Note that the transposes
                                                    # are in the opposite order than above because of dims of Xsim

```

```python
# Now, we want to compare PPCA dimensions to PCA dimensions
u1,s1,v1 = np.linalg.svd(Wnew)
u1[:,0]
```

    array([ 0.81871654,  0.5741979 ])

```python
C = Wnew.dot(Wnew.T) + sigNew*np.eye(D)
z_ppca = Wnew.T.dot(np.linalg.inv(C)).dot((Xsim - mu).T)
Xproj_ppca = (Wnew.dot(z_ppca)).T + mu
```

```python
np.linalg.inv(C)
```

    array([[ 0.05982159, -0.03621701],
           [-0.03621701,  0.08606098]])

```python
plt.plot(Xsim[:,0],Xsim[:,1],'o')
plt.plot(mu[0],mu[1],'go')
x = np.linspace(-4,15)
y = u1[1,0]/u1[0,0] * (x - mu[0]) + mu[1]
plt.plot(x,y,'k')

plt.plot(Xproj_ppca[:,0], Xproj_ppca[:,1], 'ro')
for x, y in zip(Xsim, Xproj_ppca):
    plt.plot([x[0],y[0]], [x[1],y[1]], 'r-')

plt.axis('equal')
plt.title('P-PCA')

```

### a. The lower dimensional space found by PCA and P-PCA are the same. So why are the lines in P-PCA not orthogonal as they are in the PCA case?

### Answer: the PC directions of P-PCA, W, are not orthonormal and ordered based on the amount of variance explained; while the PC directions of PCA, Um, have these attributions.

### Latent variable model of Dimensionality Reduction (Factor Analysis)

 - Latent variable $\mathbf{z} \in \mathcal{R}^M \sim \mathcal{N}(\mathbf{0}, \mathbf{I})$
 - Data $\mathbf{x} \in \mathcal{R}^D = W \mathbf{x} + \mu + \epsilon$, where $\epsilon \sim
     \mathcal{N}(\mathbf{0}, \Psi)$.
 - Model parameters are $W \sim M \times D$ and $\Psi$

The difference between P-PCA and FA is that in FA, the observation noise is a diagonal matrix, $\Psi$, rather
than a diagonal matrix with constant value, $\sigma^2 \mathbf{I}$.

### _A posteriori_ distribution of latent variable

$$
\begin{align}
\Pr(\mathbf{z} \mid \mathbf{x}) &= \mathcal{N}( E[\mathbf{z}], \mathrm{Cov}(\mathbf{z})) \\
&= \mathcal{N}(W^T C^{-1} (\mathbf{x} - \mu), \mathbf{I} - W^T C^{-1} W)
\end{align}$$
where $C = W W^T + \Psi$ is the covariance of the marginal $\Pr(x)$. **(Note that this is different than P-PCA!)**

### EM algorithm for P-PCA
 1. Initialize parameters
 2. **E-step:** Calculate $\Pr(\mathbf{z}_n \mid \mathbf{x}_n)$ for each data point. You will need the mean of this
 distribution, $E[\mathbf{z}_n]$ and the outer product
 $E[\mathbf{z}_n \, \mathbf{z}_n^T] = \mathrm{Cov}(\mathbf{z}_n) +
     E[\mathbf{z}_n] E[\mathbf{z}_n]^T$
 3. **M-step:** Update $W$ and $\sigma^2$

     $$W_{new} = \left( \sum_{n=1}^N (\mathbf{x}-\mu) \; E[\mathbf{z}_n]^T \right)
     \left(\sum_{n=1}^N E[\mathbf{z} \, \mathbf{z}^T] \right)^{-1}$$

     $$\Psi_{new} = \frac{1}{N} \mathrm{diag} \left( \sum_{n=1}^N (\mathbf{x}-\mu) (\mathbf{x}-\mu)^T -
     W_{new} \sum_{n=1}^N E[\mathbf{z}_n] (\mathbf{x}-\mu)^T \right)$$

     where "$\mathrm{diag}()$" means to zero the off-diagonal elements.

 4. Iterate until convergence (or just a bunch of times)

```python
D = Xsim.shape[1]
N = Xsim.shape[0]
M = 1
# EM for

# Initialize parameters
Wnew = np.random.randn(D,1)
PsiNew = np.diag(np.arange(D)+1)

# Note that differently from our normal convention, Xsim is an NxD matrix.
# That means each observation is a row vector rather than a column vector.
# The result is that Ez ends up being a row vector (EzzT is properly square).
for i in range(5000):
    Wold = Wnew
    PsiOld = PsiNew
    C = Wold.dot(Wold.T) + PsiOld # covariance of P(x)
    Ez = Wold.T.dot(np.linalg.inv(C)).dot((Xsim - mu).T) # W^T C^-1 (x - mu)
    EzzT = (np.eye(M) - Wold.T.dot(np.linalg.inv(C)).dot(Wold))*N + (Ez.dot(Ez.T)) # Cov(z) + Ez Ez^T
    Wnew = ((Xsim - mu).T).dot(Ez.T) # break it into two lines - first product of (x-mu) and Ez
    Wnew = Wnew.dot(np.linalg.inv(EzzT)) # then scale down by EzzT
    PsiNew = 1/N * np.diag(np.diag((Xsim - mu).T.dot(Xsim-mu) - Wnew.dot(Ez.dot(Xsim-mu)))) # Note that the transposes
                                                    # are in the opposite order than above because of dims of Xsim

```

```python
u1,s1,v1 = np.linalg.svd(Wnew)
u1[:,0]
```

    array([ 0.77264353,  0.63484012])

```python
C = Wnew.dot(Wnew.T) + PsiNew
z_fa = Wnew.T.dot(np.linalg.inv(C)).dot((Xsim - mu).T)
Xproj_fa = (Wnew.dot(z_fa)).T + mu
```

```python
plt.plot(Xsim[:,0],Xsim[:,1],'o')
plt.plot(mu[0],mu[1],'go')
x = np.linspace(-4,15)
y = u1[1,0]/u1[0,0] * (x - mu[0]) + mu[1]
plt.plot(x,y,'k')

plt.plot(Xproj_fa[:,0], Xproj_fa[:,1], 'ro')
for x, y in zip(Xsim, Xproj_fa):
    plt.plot([x[0],y[0]], [x[1],y[1]], 'r-')

plt.axis('equal')
plt.title('Factor Analysis')
```

### b. Note that the lower dimensional space found by FA will pass through the mean but will not be the same as in PCA/P-PCA. Why is it different?

### Answer: For FA:  $x \sim \mathcal{N}(\mu, WW^T+\Psi)$. where $\mu$ is same with PCA/P-PCA, but it has a different $\Psi$, which decides FA has a different lower dimensianl space

```python

```

HW7-Clustering through the Process of Spike Sorting Action potentials data2/HW7_3.ipynb


# 基于MindQuantum的Grover搜索算法

[](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/master/mindquantum/zh_cn/mindspore_grover_search_algorithm.ipynb)
[](https://mindspore-website.obs.cn-north-4.myhuaweicloud.com/notebook/master/mindquantum/zh_cn/mindspore_grover_search_algorithm.py)
[](https://gitee.com/mindspore/docs/blob/master/docs/mindquantum/docs/source_zh_cn/grover_search_algorithm.ipynb)

## 概述

如果你听过量子计算，那么你一定听说过Grover搜索算法。1996年，Lov Grover \[1\] 提出了Grover搜索算法，它是一种利用量子状态的叠加性进行并行计算并实现加速的算法。Grover搜索算法被公认为是继Shor算法后的第二大量子算法，也是第一个被完整的实验实现的量子算法，它解决的是无序数据库搜索问题。1997年，Bennett \[2\] 等人证明，对于非结构化的量子搜索问题，至少需要$\Omega(\sqrt{N})$次量子查询，因此Grover搜索算法对于该问题是渐进意义下的最优算法。

无序数据库搜索问题（Unordered  Database Search problem）就是从一个海量元素的无序数据库中，找到某些满足要求的元素。由于数据库中元素的数量是巨大的且这些元素是无序排列的，所以，要验证给定的元素是否满足要求很容易，但反过来，要找到这些元素却不是一件容易的事。

求解无序数据库搜索问题（不妨假设只有一个目标搜索数据），经典算法所需的时间复杂度为$\mathcal{O}(N)$，而Grover搜索算法所需的时间复杂度仅为$\mathcal{O}(\sqrt{N})$，相比经典算法具有平方加速，展示了量子计算的强大性能。此外，Grover搜索算法中用到的振幅扩大技巧，对许多启发式的经典搜索算法可以实现加速，因而具有广泛的应用。

本文档将会介绍Grover搜索算法的基本原理，以及通过两个具体的小例子来展示如何利用MindQuantum实现该算法。

## 问题描述

我们需要在一组无序的$N$元素集合（数据库）中进行搜索。将数据库中的元素与索引（从$0$到$N-1$之间的整数）建立一一对应，我们关注于搜索这些元素的索引。考虑将该搜索问题表示为一个关于输入$x$的函数$f(x)$，其中$x$为$0$到$N-1$之间的整数。那么，函数$f$定义为：

$$
\begin{equation}
f(x)=\begin{cases}0,x\neq x_{target}\\\\
1,x=x_{target}
\end{cases}
\end{equation}.
$$

不失一般性，假设$N=2^n$，那么在量子系统中，索引以量子态$|0\rangle,|1\rangle,...,|N-1\rangle$（或$|00...0\rangle,|00...1\rangle,...,|11...1\rangle$）表示，也即我们可以使用$n$个量子比特存储这些索引。

同时假设搜索问题只有一个目标态$|\omega\rangle$。Grover搜索算法的目标就是以极大的概率将$|\omega\rangle$搜索出来。

## Grover搜索算法的基本原理

Grover搜索算法的基本原理：首先通过`Hadamard`门产生均匀叠加态，然后反复调用Grover迭代（或称为$G$算子），以放大目标项的概率振幅同时抑制非目标项的概率振幅（该方法称之为振幅放大），最后对末态进行测量，那么就能以极大的概率得到目标态$|\omega\rangle$。

Grover搜索算法主要包括以下步骤：

- Step 1：数据库初始化

  对$|0\rangle^{\otimes n}$执行$H^{\otimes n}$操作，使得数据库被初始为一个均匀叠加态，即

  $$
  |\psi_0\rangle=H^{\otimes n}|0\rangle^{\otimes n}=\frac{1}{\sqrt{N}}\sum_{i=0}^{N-1}|i\rangle.
  $$

- Step 2：Grover迭代

  Grover迭代又可以分解为四步：

  1. 执行Oracle算子$U_{\omega}$，翻转目标态$|\omega \rangle$的相位

    为了将需要寻找的数据和其它的数据区别开，最简单的方法就是翻转目标态的相位（增加一个负号），此时我们需要构造一个Oracle算子$U_{\omega}$，其作用如下：

    $$
    \begin{equation}
    U_{\omega}|x\rangle=\begin{cases}
    &|x\rangle,x\neq \omega&\\\\
    -&|x\rangle,x=\omega&
    \end{cases}
    \end{equation}.
    $$

    由于当$x=\omega$时，$f(\omega)=1$，那么$U_{\omega}$的作用还可以表示成：

    $$
    U_{\omega}|x\rangle=(-1)^{f(x)}|x\rangle,
    $$

    其矩阵表达式为

    $$
    \begin{equation}
    U_{\omega}=
    \left[
    \begin{array}{ccc}
         (-1)^{f(0)} & 0           & \dots  & 0            \\\\
         0           & (-1)^{f(1)} & \dots  & 0            \\\\
         \vdots      & \vdots      & \ddots & \vdots       \\\\
         0           & 0           & \dots  & (-1)^{f(N-1)}
    \end{array}
    \right]
    \end{equation}.
    $$

  2. 执行$H^{\otimes n}$操作

    对$n$位量子比特执行$H^{\otimes n}$操作。

  3. 执行条件相移算子$P$

    条件相移算子$P$能使$|0\rangle$态以外的每个态的相位都翻转，其作用如下：

    $$
    \begin{equation}
    P|x\rangle=\begin{cases}&|0\rangle,x= 0&\\\\
    -&|x\rangle,x\neq0&
    \end{cases}
    \end{equation}.
    $$

    其矩阵表达式为

    $$
    \begin{equation}
    P = 2(|0\rangle\langle0|)^{\otimes n} - I_n =
    \left[
    \begin{array}{ccc}
         1      & 0      & \dots  & 0            \\\\
         0      & -1     & \dots  & 0            \\\\
         \vdots & \vdots & \ddots & \vdots       \\\\
         0      & 0      & \dots  & -1
    \end{array}
    \right]
    \end{equation}.
    $$

  4. 再次执行$H^{\otimes n}$操作

  至此，完整的$G$算子可以表示为

  $$
  G = H^{\otimes n} [2(|0\rangle\langle0|)^{\otimes n} - I_n] H^{\otimes n} U_{\omega}.
  $$

  注意：$G$算子需要迭代的次数为

  $$
  r = \left[ \frac{\pi}{4} \sqrt{\frac{N}{M}} \right] \sim O(\sqrt{N}),
  $$

  其中，M表示目标态的个数。

- Step 3：测量

  对末态进行$\\{|0\rangle,|1\rangle\\}$基测量，就能以极大的概率得到目标态$|\omega \rangle$。

Grover搜索算法的完整量子线路模型如下所示：

## 构造翻转量子比特相位的酉算子

通过上述介绍，我们发现，Grover搜索算法中最关键的部分就是存在可以翻转量子比特相位的酉算子，Oracle算子$U_{\omega}$可以翻转目标态的相位，条件相移算子$P$可以翻转$|0\rangle$态以外的每个态的相位。

接下来，我们将构造可以翻转某一位量子比特相位的酉算子，定义如下：

```python
def bitphaseflip_operator(phase_inversion_qubit, n_qubits):   # 定义可以翻转某一位量子比特相位的函数
    s = [1 for i in range(1 << n_qubits)]
    for i in phase_inversion_qubit:
        s[i] = -1
    if s[0] == -1:
        for i in range(len(s)):
            s[i] = -1 * s[i]
    circuit = Circuit()
    length = len(s)
    cz = []
    for i in range(length):
        if s[i] == -1:
            cz.append([])
            current = i
            t = 0
            while current != 0:
                if (current & 1) == 1:
                    cz[-1].append(t)
                t += 1
                current = current >> 1
            for j in range(i + 1, length):
                if i & j == i:
                    s[j] = -1 * s[j]
    for i in cz:
        if i:
            if len(i) > 1:
                circuit += Z.on(i[-1], i[:-1])
            else:
                circuit += Z.on(i[0])

    return circuit
```

现在， `bitphaseflip_operator()` 函数就可以实现翻转某一位量子比特的相位，只需要输入需要翻转相位的目标量子态和量子比特总数即可。

举个例子，我们现在生成3量子比特的均匀叠加态，运行如下代码：

```python
# pylint: disable=W0104
from mindquantum import Circuit, UN, H, Z
from mindquantum.simulator import Simulator

n_qubits = 3                                 # 设定量子比特数为3
sim = Simulator('projectq', n_qubits)        # 使用projectq模拟器，命名为sim

circuit = Circuit()                          # 初始化量子线路，命名为circuit
circuit += UN(H, n_qubits)                   # 每位量子比特上执行H门操作

sim.apply_circuit(circuit)                   # 通过模拟器sim运行搭建好的量子线路circuit

circuit.svg()                                # 打印此时的量子线路circuit
```

```python
print(sim.get_qs(True))                      # 打印模拟器sim中运行量子线路circuit后的末态
```

    √2/4¦000⟩
    √2/4¦001⟩
    √2/4¦010⟩
    √2/4¦011⟩
    √2/4¦100⟩
    √2/4¦101⟩
    √2/4¦110⟩
    √2/4¦111⟩

从运行的结果看到此时的量子线路，以及我们成功生成了3量子比特的均匀叠加态。

假设我们需要翻转$|4\rangle$态的相位，只需调用我们定义好的`bitphaseflip_operator()`函数即可，运行如下代码：

```python
# pylint: disable=W0104
sim.reset()                                                      # 重置模拟器sim维护好的量子态，使得初始化的量子态为|000>

phase_inversion_qubit = [4]                                      # 翻转|4>态的相位
operator = bitphaseflip_operator(phase_inversion_qubit, n_qubits)# 调用我们定义好的bitphaseflip_operator()函数

circuit += operator                                              # 在量子线路circuit中添加翻转|4>态的相位所需的量子门

sim.apply_circuit(circuit)                                       # 通过模拟器sim再次运行搭建好的量子线路circuit

circuit.svg()                                                    # 打印此时的量子线路circuit
```

```python
print(sim.get_qs(True))                                          # 打印模拟器sim中运行量子线路circuit后的末态
```

    √2/4¦000⟩
    √2/4¦001⟩
    √2/4¦010⟩
    √2/4¦011⟩
    -√2/4¦100⟩
    √2/4¦101⟩
    √2/4¦110⟩
    √2/4¦111⟩

从运行的结果看到此时的量子线路，以及$|100\rangle$的相位翻转为-1，运行如下代码：

```python
print(int('100', 2))
```

    4

从运行的结果看到，发生相位翻转的$|100\rangle$态即为我们希望相位翻转的$|4\rangle$态。

假设我们需要翻转除$|0\rangle$态以外的每个态的相位，运行如下代码：

```python
# pylint: disable=W0104
n_qubits = 3                                                     # 设定量子比特数为3
sim1 = Simulator('projectq', n_qubits)                           # 使用projectq模拟器，命名为sim1

operator1 = bitphaseflip_operator([i for i in range(1, pow(2, n_qubits))], n_qubits) # 调用我们定义好的bitphaseflip_operator()函数，翻转除|0>态以外的每个态的相位，命名为operator1

circuit1 = Circuit()                                             # 初始化量子线路，命名为circuit1
circuit1 += UN(H, n_qubits)                                      # 每位量子比特上执行H门操作
circuit1 += operator1                                            # 在量子线路circuit1中添加翻转除|0>态以外的每个态的相位所需的量子门

sim1.apply_circuit(circuit1)                                     # 通过模拟器sim1运行搭建好的量子线路circuit1

circuit1.svg()                                                         # 打印此时的量子线路circuit1
```

```python
print(sim1.get_qs(True))                                         # 打印模拟器sim1中运行量子线路circuit1后的末态
```

    √2/4¦000⟩
    -√2/4¦001⟩
    -√2/4¦010⟩
    -√2/4¦011⟩
    -√2/4¦100⟩
    -√2/4¦101⟩
    -√2/4¦110⟩
    -√2/4¦111⟩

从运行的结果看到此时的量子线路，以及我们成功翻转除$|0\rangle$态以外的每个态的相位。

也就是说，我们定义的函数`bitphaseflip_operator()`可以实现Grover搜素算法中的Oracle算子$U_{\omega}$和条件相移算子$P$。

## 利用MindQuantum实现Grover搜素算法实例

### 实例1：$n=3$，$|\omega\rangle=|2\rangle$（单目标）

首先，我们需要定义$G$算子，运行如下代码：

```python
def G(phase_inversion_qubit, n_qubits):           # 定义Grover搜索算法中的G算子
    operator = bitphaseflip_operator(phase_inversion_qubit, n_qubits)
    operator += UN(H, n_qubits)
    operator += bitphaseflip_operator([i for i in range(1, pow(2, n_qubits))], n_qubits)
    operator += UN(H, n_qubits)
    return operator
```

然后，我们根据Grover搜索算法的量子线路模型在MindQuantum中搭建对应的量子线路：

```python
# pylint: disable=W0104
from numpy import pi, sqrt

n_qubits = 3                                      # 设定量子比特数为3
phase_inversion_qubit = [2]                       # 设定需要翻转相位的目标态，在这里翻转|2>态的相位

N = 2 ** (n_qubits)                               # 计算出数据库中元素的总个数
M = len(phase_inversion_qubit)                    # 计算出目标态的总个数

r = int(pi / 4 * sqrt(N / M))                     # 设定G算子迭代次数为r

sim2 = Simulator('projectq', n_qubits)            # 使用projectq模拟器，命名为sim2

circuit2 = Circuit()                              # 初始化量子线路，命名为circuit2
circuit2 += UN(H, n_qubits)                       # 每位量子比特上执行H门操作

for i in range(r):                                # 循环执行G算子r次
    circuit2 += G(phase_inversion_qubit, n_qubits)

sim2.apply_circuit(circuit2)                      # 通过模拟器sim2运行搭建好的量子线路circuit2

circuit2.svg()                                    # 打印此时的量子线路circuit2
```

```python
print(sim2.get_qs(True))                          # 打印模拟器sim2中运行量子线路circuit2后的末态
```

    -√2/16¦000⟩
    -√2/16¦001⟩
    0.9722718241315036¦010⟩
    -√2/16¦011⟩
    -√2/16¦100⟩
    -√2/16¦101⟩
    -√2/16¦110⟩
    -√2/16¦111⟩

从运行的结果看到，$|010\rangle$态的振幅为0.9722718241315036，相比于其它的量子态，这是极大的振幅，也就是说，若我们测量此时的状态，将会以极大的概率得到目标态$|010\rangle$，运行如下代码进行测量：

```python
# pylint: disable=W0104
from mindquantum import Measure

sim2.reset()                                      # 重置模拟器sim2维护好的量子态，使得初始化的量子态为|000>

circuit2 += UN(Measure(), circuit2.n_qubits)      # 对量子线路circuit2中的每一位量子比特添加测量门

result = sim2.sampling(circuit2, shots=1000)      # 通过模拟器sim2对量子线路circuit2进行1000次的采样
result.svg()                                      # 打印采样结果
```

从运行的结果看到，1000次采样中有947次的采样结果为`010`，将其转化为10进制数，运行如下代码：

```python
print(int('010', 2))
```

    2

从运行的结果看到，我们成功地搜索出$|2\rangle$态。

### 实例2：$n=5$，$|\omega\rangle=|5\rangle$和$|11\rangle$（多目标）

实例1中实现的是单目标搜索，现在我们尝试实现多目标搜索。首先，$G$算子已经定义好了，我们只需设定量子比特数和需要翻转相位的目标态，然后搭建对应的量子线路即可，运行如下代码：

```python
# pylint: disable=W0104
n_qubits = 5                                      # 设定量子比特数为5
phase_inversion_qubit = [5, 11]                   # 设定需要翻转相位的目标态，在这里翻转|5>态和|11>态的相位

N = 2 ** (n_qubits)                               # 计算出数据库中元素的总个数
M = len(phase_inversion_qubit)                    # 计算出目标态的总个数

r = int(pi / 4 * sqrt(N / M))                     # 设定G算子迭代次数为r

sim3 = Simulator('projectq', n_qubits)            # 使用projectq模拟器，命名为sim3

circuit3 = Circuit()                              # 初始化量子线路，命名为circuit3
circuit3 += UN(H, n_qubits)                       # 每位量子比特上执行H门操作

for i in range(r):                                # 循环执行G算子r次
    circuit3 += G(phase_inversion_qubit, n_qubits)

sim3.apply_circuit(circuit3)                      # 通过模拟器sim3运行搭建好的量子线路circuit3

circuit3.svg()                                          # 打印此时的量子线路circuit3
```

```python
print(sim3.get_qs(True))                          # 打印模拟器sim3中运行量子线路circuit3后的末态
```

    -0.03590776623212942¦00000⟩
    -0.03590776623212939¦00001⟩
    -0.03590776623212932¦00010⟩
    -0.03590776623212946¦00011⟩
    -0.03590776623212935¦00100⟩
    0.6932961018664991¦00101⟩
    -0.035907766232129434¦00110⟩
    -0.03590776623212945¦00111⟩
    -0.035907766232129434¦01000⟩
    -0.03590776623212945¦01001⟩
    -0.03590776623212935¦01010⟩
    0.6932961018664991¦01011⟩
    -0.03590776623212932¦01100⟩
    -0.03590776623212946¦01101⟩
    -0.03590776623212939¦01110⟩
    -0.03590776623212939¦01111⟩
    -0.0359077662321294¦10000⟩
    -0.03590776623212941¦10001⟩
    -0.035907766232129414¦10010⟩
    -0.035907766232129434¦10011⟩
    -0.03590776623212944¦10100⟩
    -0.035907766232129434¦10101⟩
    -0.03590776623212944¦10110⟩
    -0.035907766232129434¦10111⟩
    -0.03590776623212943¦11000⟩
    -0.035907766232129434¦11001⟩
    -0.03590776623212943¦11010⟩
    -0.035907766232129434¦11011⟩
    -0.0359077662321294¦11100⟩
    -0.035907766232129434¦11101⟩
    -0.035907766232129414¦11110⟩
    -0.03590776623212941¦11111⟩

从运行的结果看到，$|00101\rangle$和$|01011\rangle$态的振幅均为0.6932961018664989，相比于其它的量子态，这是极大的振幅，也就是说，若我们测量此时的状态，将会以极大的概率得到目标态$|00101\rangle$和$|01011\rangle$态，运行如下代码进行测量：

```python
# pylint: disable=W0104
sim3.reset()                                      # 重置模拟器sim3维护好的量子态，使得初始化的量子态为|00000>

circuit3 += UN(Measure(), circuit3.n_qubits)      # 对量子线路circuit3中的每一位量子比特添加测量门

result1 = sim3.sampling(circuit3, shots=1000)     # 通过模拟器sim3对量子线路circuit3进行1000次的采样
result1.svg()                                     # 打印采样结果
```

从运行的结果看到，1000次采样中有463次的采样结果为`00101`和503次的采样结果为`01011`，将其转化为10进制数，运行如下代码：

```python
print(int('00101', 2))
print(int('01011', 2))
```

    5
    11

从运行的结果看到，我们成功地搜索出$|5\rangle$和$|11\rangle$态。

至此，我们介绍了Grover搜索算法的基本原理，以及通过两个具体的小例子来展示如何利用MindQuantum实现该算法！赶紧动手体验一下量子编程的乐趣吧！

若想查询更多关于MindQuantum的API，请点击：[https://mindspore.cn/mindquantum/](https://mindspore.cn/mindquantum/)。

### **参考文献：**

\[1\] L. K. Grover, A fast quantum mechanical algorithm for database search\[C\]// Proceedings of the twenty-eighth annual ACM symposium on Theory of computing. ACM, 1996: 212-219.

\[2\] G. Brassard, P. Hoyer, M. Mosca, et al. Quantum amplitude amplification and estimation\[J\]. Contemporary Mathematics, 2002, 305: 53-74.


# Selección óptima de portafolios I

En la clase pasada vimos que:
- La LAC describe las posibles selecciones de riesgo-rendimiento entre un activo libre de riesgo y un activo riesgoso.
- Su pendiente es igual al radio de Sharpe del activo riesgoso.
- La asignación óptima de capital para cualquier inversionista es el punto tangente de la curva de indiferencia del inversionista con la LAC (depende de las preferencias particulares - aversión al riesgo).

Para todo lo anterior, supusimos que ya teníamos el portafolio óptimo (activo riesgoso).

En el siguiente análisis:

**Objetivos:**
- ¿Cuál es el portafolio óptimo de activos riesgosos?
 - ¿Cuál es el mejor portafolio de activos riesgosos?
 - Es un portafolio eficiente en media-varianza.
- Problema: dado un conjunto de activos riesgosos, ¿cómo construimos la mejor combinación?

*Referencia:*
- Notas del curso "Portfolio Selection and Risk Management", Rice University, disponible en Coursera.
___

## 1. Maximizando el radio de Sharpe

### ¿Qué pasa si tenemos dos activos riesgosos?

Cuando tenemos dos o más activos riesgosos, tenemos disponibles diferentes LAC. ¿Qué significan sus pendientes?

 Ver en el tablero.

Pregunta:
- ¿Qué es lo que se quiere?

**Conclusión:**
- El mejor portafolio de activos no depende de las preferencias individuales, y por tanto va a ser igual para todos.
- Dicho mejor portafolio maximiza el radio de Sharpe.
- A este portafolio lo llamaremos el portafolio eficiente en media-varianza (EMV)

**Idea principal: el portafolio óptimo de activos riesgosos es independiente de las preferencias del inversionista.**
- El portafolio EMV determina el portafolio óptimo de activos riesgosos.
- Todos tendremos el mismo portafolio de activos riesgosos (EMV), y lo combinaremos con el activo libre de reisgo de acuerdo con las preferencias de cada uno de nosotros (aversión al riesgo).
- La LAC combinando el activo libre de riesgo y el portafolio EMV, se vuelve el conjunto de portafolios eficientes.

Entonces, se deben seguir los siguientes pasos:
1. Crear la frontera media-varianza.
2. Encontrar el portafolio que maximize el radio de Sharpe (portafolio EMV).
3. Construir la frontera eficiente (LAC) del punto $(0,r_f)$ al punto $(\sigma_s,E[r_s])$ del portafolio EMV.
4. Combinar de acuerdo a sus preferencias.
___

## 2. Solución analítica del portafolio EMV: caso con dos activos.

Queremos solucionar el siguiente problema:

\begin{align}
\max_{w_1,w_2} &\quad \frac{E[r_p]-r_f}{\sigma_p}\\
\text{s.a.}    &\quad E[r_p]=w_1E[r_1]+w_2E[r_2]\\
               &\quad \sigma_p=\sqrt{w_1^2\sigma_1^2+w_2^2\sigma_2^2+2w_1w_2\rho_{12}\sigma_1\sigma_2}\\
               &\quad w_1+w_2=1, \quad w_1,w_2\geq0
\end{align}

el cual es equivalente a

\begin{align}
\max_{w_1} &\quad \frac{w_1E[r_1]+(1-w_1)E[r_2]-r_f}{\sqrt{w_1^2\sigma_1^2+(1-w_1)^2\sigma_2^2+2w_1(1-w_1)\rho_{12}\sigma_1\sigma_2}}\\
\text{s.a.}    &\quad 0\leq w_1\leq1
\end{align}

**Actividad.**
El anterior es un problema de maximizar una función de una variable en un dominio cerrado. No debaría representar dificultad.

Encontrar la solución analítica a este problema.

Quien primero lo haga, y salga a explicarlo al tablero, le subo alguna tarea o quiz a 100.

Deben llegar a:

$$w_{1,EMV}=\frac{(E[r_1]-r_f)\sigma_2^2-(E[r_2]-r_f)\sigma_{12}}{(E[r_2]-r_f)\sigma_1^2+(E[r_1]-r_f)\sigma_2^2-((E[r_1]-r_f)+(E[r_2]-r_f))\sigma_{12}}.$$

Si nadie lo ha hecho en 30 min., procederé a hacerlo yo.

**Nota:**
- así como obtuvimos una expresión para el peso del portafolio de mínima varianza con dos activos, obtenemos una expresión para el peso del portafolio Eficiente en Media-Varianza.
- Estas actividades son sin duda un buen ejercicio, y se pueden replicar usando técnicas de varias variables (multiplicadores de Lagrange) cuando se tengan más de dos activos.
- Sin embargo, la complejidad del problema crece considerablemente con el número de variables, y la solución analítica deja de ser viable cuando mencionamos que un portafolio bien diversificado consta aproximadamente de 50-60 activos.
- En esos casos, este problema se soluciona con rutinas numéricas que hagan la optimización por nosotros.
- Por eso, les enseño cómo resolver este problema con optimizadores numéricos, porque son una solución viable y escalable a más variables.

## 3. Ejemplo ilustrativo.

Retomamos el ejemplo de mercados de acciones en los países integrantes del $G5$: EU, RU, Francia, Alemania y Japón.

```python
# Importamos pandas y numpy
import pandas as pd
import numpy as np
```

```python
# Resumen en base anual de rendimientos esperados y volatilidades
annual_ret_summ = pd.DataFrame(columns=['EU', 'RU', 'Francia', 'Alemania', 'Japon'], index=['Media', 'Volatilidad'])
annual_ret_summ.loc['Media'] = np.array([0.1355, 0.1589, 0.1519, 0.1435, 0.1497])
annual_ret_summ.loc['Volatilidad'] = np.array([0.1535, 0.2430, 0.2324, 0.2038, 0.2298])

annual_ret_summ.round(4)
```

      EU
      RU
      Francia
      Alemania
      Japon

      Media
      0.1355
      0.1589
      0.1519
      0.1435
      0.1497

      Volatilidad
      0.1535
      0.243
      0.2324
      0.2038
      0.2298

```python
# Matriz de correlación
corr = pd.DataFrame(data= np.array([[1.0000, 0.5003, 0.4398, 0.3681, 0.2663],
                                    [0.5003, 1.0000, 0.5420, 0.4265, 0.3581],
                                    [0.4398, 0.5420, 1.0000, 0.6032, 0.3923],
                                    [0.3681, 0.4265, 0.6032, 1.0000, 0.3663],
                                    [0.2663, 0.3581, 0.3923, 0.3663, 1.0000]]),
                    columns=annual_ret_summ.columns, index=annual_ret_summ.columns)
corr.round(4)
```

      EU
      RU
      Francia
      Alemania
      Japon

      EU
      1.0000
      0.5003
      0.4398
      0.3681
      0.2663

      RU
      0.5003
      1.0000
      0.5420
      0.4265
      0.3581

      Francia
      0.4398
      0.5420
      1.0000
      0.6032
      0.3923

      Alemania
      0.3681
      0.4265
      0.6032
      1.0000
      0.3663

      Japon
      0.2663
      0.3581
      0.3923
      0.3663
      1.0000

Supondremos, además, que la tasa libre de riesgo es $r_f=5\%$.

```python
# Tasa libre de riesgo
rf = 0.05
```

Entonces, supondremos que tenemos disponibles los activos correspondientes a los mercados de acciones de EU y Japón, y en adición el activo libre de riesgo.

#### 1. Construir la frontera de mínima varianza

```python
# Vector de w variando entre 0 y 1 con n pasos
N = 101
w = np.linspace(0, 1, N)
# Rendimientos esperados individuales
# Activo1: EU, Activo2:Japon
E1 = annual_ret_summ.iloc[0, 0]
E2 = annual_ret_summ.iloc[0, 4]
# Volatilidades individuales
s1 = annual_ret_summ.iloc[1, 0]
s2 = annual_ret_summ.iloc[1, 4]
# Correlacion
r12 = corr.iloc[0, 4]
# Covarianza
s12 = s1 * s2 * r12
```

```python
# DataFrame de portafolios:
# 1. Índice: i
# 2. Columnas 1-2: w, 1-w
# 3. Columnas 3-4: E[r], sigma
# 4. Columna 5: Sharpe ratio
portafolios = pd.DataFrame(data={'w': w,
                                 '1-w': 1 - w,
                                 'Media': w * E1 + (1 - w) * E2,
                                 'Vol': ((w * s1)**2 + ((1 - w) * s2)**2 + 2 * w * (1 - w) * s12)**0.5
                                })
portafolios['RS'] = (portafolios['Media'] - rf) / portafolios['Vol']
```

```python
portafolios.head()
```

      w
      1-w
      Media
      Vol
      RS

      0
      0.00
      1.00
      0.149700
      0.229800
      0.433856

      1
      0.01
      0.99
      0.149558
      0.227916
      0.436820

      2
      0.02
      0.98
      0.149416
      0.226041
      0.439814

      3
      0.03
      0.97
      0.149274
      0.224176
      0.442839

      4
      0.04
      0.96
      0.149132
      0.222322
      0.445894

```python
# Importar librerías de gráficos
from matplotlib import pyplot as plt
%matplotlib inline
```

```python
# Gráfica de dispersión de puntos coloreando
# de acuerdo a RS
plt.scatter(portafolios['Vol'],
            portafolios['Media'],
            c=portafolios['RS'],
            cmap='RdYlBu',
            label='Frontera Min-Var')
plt.plot(s1, E1, 'or', label='EU')
plt.plot(s2, E2, 'ok', label='Japon')
plt.colorbar()
plt.xlabel('Volatilidad $\sigma$')
plt.ylabel('Rendimiento esperado $E[r]$')
plt.grid()
plt.legend(loc='upper left', bbox_to_anchor=(1.3, 1))
```

#### 2. Encontrar el portafolio que maximiza el radio de Sharpe (EMV)

Primero, encontramos este portafolio con la fórmula que obtuvimos:

$$w_{1,EMV}=\frac{(E[r_1]-r_f)\sigma_2^2-(E[r_2]-r_f)\sigma_{12}}{(E[r_2]-r_f)\sigma_1^2+(E[r_1]-r_f)\sigma_2^2-((E[r_1]-r_f)+(E[r_2]-r_f))\sigma_{12}}.$$

```python
# Fórmula que obtuvimos
num = ((E1 - rf) * s2**2 - (E2 - rf) * s12)
den = (E2 - rf) * s1**2 + (E1 - rf) * s2**2 - (E1 - rf + E2 - rf) * s12
w_EMV = num / den
w_EMV, 1 - w_EMV
```

    (0.6983139170512034, 0.3016860829487966)

Ahora sí, con la función scipy.optimize.minimize

```python
# Importar la función minimize del módulo optimize
from scipy.optimize import minimize
```

```python
# Función objetivo (-RS)
def menos_RS(w, E1, E2, s1, s2, s12, rf):
    Ep = w * E1 + (1 - w) * E2
    sp = ((w * s1)**2 + ((1 - w) * s2)**2 + 2 * w * (1 - w) * s12)**0.5
    RS = (Ep - rf) / sp
    return -RS
```

```python
# Dato inicial
w0 = 0.5
# Cotas de las variables
bnds = ((0, 1),)
```

```python
# Optimización numérica
EMV = minimize(fun=menos_RS,
               x0=w0,
               args=(E1, E2, s1, s2, s12, rf),
               bounds=bnds)
# Resultado
EMV
```

          fun: array([-0.63087253])
     hess_inv: <1x1 LbfgsInvHessProduct with dtype=float64>
          jac: array([3.39728246e-06])
      message: b'CONVERGENCE: NORM_OF_PROJECTED_GRADIENT_<=_PGTOL'
         nfev: 10
          nit: 4
       status: 0
      success: True
            x: array([0.69831582])

```python
w_EMV2 = EMV.x
w_EMV2, 1 - w_EMV2
```

    (array([0.69831582]), array([0.30168418]))

Con lo anterior, podemos obtener datos de rendimiento esperado y volatilidad del portafolio EMV

```python
# Rendimiento esperado y volatilidad del portafolio EMV
E_EMV = w_EMV * E1 + (1 - w_EMV) * E2
s_EMV = ((w_EMV * s1)**2 + ((1 - w_EMV) * s2)**2 + 2 * w_EMV * (1 - w_EMV) * s12)**0.5
E_EMV, s_EMV
```

    (0.13978394237787292, 0.14231708951606933)

```python
RS_EMV = (E_EMV - rf) / s_EMV
RS_EMV
```

    0.6308725303698346

```python
# Gráfica de dispersión de puntos coloreando
# de acuerdo a SR, y portafolio EMV
plt.scatter(portafolios['Vol'],
            portafolios['Media'],
            c=portafolios['RS'],
            cmap='RdYlBu',
            label='Frontera Min-Var')
plt.plot(s1, E1, 'or', label='EU')
plt.plot(s2, E2, 'ok', label='Japon')
plt.plot(s_EMV, E_EMV, 'og', label='Portafolio EMV')
plt.colorbar()
plt.xlabel('Volatilidad $\sigma$')
plt.ylabel('Rendimiento esperado $E[r]$')
plt.grid()
plt.legend(loc='upper left', bbox_to_anchor=(1.3, 1))
```

#### 3. Construir LAC

Ahora, dibujamos la LAC, combinando el portafolio EMV con el activo libre de riesgo:

```python
# Vector de wp variando entre 0 y 1.5 con n pasos
wp = np.linspace(0, 1.5)
```

```python
# DataFrame de CAL:
# 1. Índice: i
# 2. Columnas 1-2: wp, wrf
# 3. Columnas 3-4: E[r], sigma
# 4. Columna 5: Sharpe ratio
LAC = pd.DataFrame(data={'wp': wp,
                         'wf': 1 - wp,
                         'Media': wp * E_EMV + (1 - wp) * rf,
                         'Vol': wp * s_EMV})
LAC['RS'] = (LAC['Media'] - rf) / LAC['Vol']
```

```python
LAC.sample(10)
```

      wp
      wf
      Media
      Vol
      RS

      29
      0.887755
      0.112245
      0.129706
      0.126343
      0.630873

      35
      1.071429
      -0.071429
      0.146197
      0.152483
      0.630873

      4
      0.122449
      0.877551
      0.060994
      0.017427
      0.630873

      23
      0.704082
      0.295918
      0.113215
      0.100203
      0.630873

      6
      0.183673
      0.816327
      0.066491
      0.026140
      0.630873

      13
      0.397959
      0.602041
      0.085730
      0.056636
      0.630873

      46
      1.408163
      -0.408163
      0.176430
      0.200406
      0.630873

      1
      0.030612
      0.969388
      0.052748
      0.004357
      0.630873

      12
      0.367347
      0.632653
      0.082982
      0.052280
      0.630873

      42
      1.285714
      -0.285714
      0.165436
      0.182979
      0.630873

```python
# Gráfica de dispersión de puntos coloreando
# de acuerdo a SR, portafolio EMV y LAC
plt.scatter(portafolios['Vol'],
            portafolios['Media'],
            c=portafolios['RS'],
            cmap='RdYlBu',
            label='Frontera Min-Var')
plt.plot(LAC['Vol'], LAC['Media'], '--', label='LAC')
plt.plot(s1, E1, 'or', label='EU')
plt.plot(s2, E2, 'ok', label='Japon')
plt.plot(s_EMV, E_EMV, 'og', label='Portafolio EMV')
plt.colorbar()
plt.xlabel('Volatilidad $\sigma$')
plt.ylabel('Rendimiento esperado $E[r]$')
plt.grid()
plt.legend(loc='upper left', bbox_to_anchor=(1.3, 1))
plt.axis([0.13, 0.20, 0.13, 0.18])
```

#### 4. Combinación óptima de acuerdo a preferencias

Con los datos anteriores, y la caracterización de aversión al riesgo, se escoge la combinación óptima entre el portafolio EMV y el activo libre de riesgo de acuerdo a:

$$w^\ast=\frac{E[r_s-r_f]}{\gamma\sigma_s^2}.$$

```python
# Para gamma=7
g = 7
w_opt = (E_EMV - rf) / (g * s_EMV**2)
w_opt, 1 - w_opt
```

    (0.6332665142475, 0.3667334857525)

```python
w_port_EMV = np.array([w_EMV, 1 - w_EMV])
w_port_EMV
```

    array([0.69831392, 0.30168608])

```python
w_opt * w_port_EMV, (w_opt * w_port_EMV).sum()
```

    (array([0.44221882, 0.19104769]), 0.6332665142475)

```python
# Ponderaciones finales: índice de EU, índice de Japón, tasa libre de riesgo
w_opt * w_EMV, w_opt * (1 - w_EMV), 1 - w_opt
```

    (0.44221882010153346, 0.1910476941459666, 0.3667334857525)

Created with Jupyter by Esteban Jiménez Rodríguez.


```python
from Exercise import Exercise, MarkdownBlock
from config import URL, TOKEN
import json

import numpy as np
import sympy as sp
import matplotlib.pyplot as plt
import pandas as pd
plt.rcParams.update({'font.size': 20})

from sklearn.datasets import load_digits
from sklearn import preprocessing
from sklearn.model_selection import train_test_split
from sklearn.metrics import accuracy_score

Exercise.URL = URL
Exercise.TOKEN = TOKEN
```

```python
m = "What is $@a + @b$?"

params = {}
params["a"] = sp.Matrix(np.arange(4))
params["b"] = sp.Matrix(np.flip(np.arange(4)))

e = Exercise(MarkdownBlock(m, params))
e.add_answer(params["a"] + params["b"], True, "Correct!")

e.play()
```

    Published succesfully, preview at: https://www.mscthesis.nl/preview?id=2191165c-df81-4b89-984a-d2459a1fcebf

```python

```


# Le-Net 1 based architecture

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

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

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

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

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

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

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

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

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

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

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

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

patchSize=41 # patchsize this must be 48 since our network can only handle this value
RGB=0 # determines the R,G or B value used
# Open forest
Amount_data= len(glob.glob('Forest/F*'))
for k in range (0, Amount_data):
    name="Forest/F%d.png" % (k+1)
    img = Image.open(name)
    data=img.convert('RGB')
    data = np.asarray( data, dtype="int32" )
    Yamount=data.shape[0]/patchSize # Counts how many times the windowsize fits in the picture
    Xamount=data.shape[1]/patchSize # Counts how many times the windowsize fits in the picture
    globals()['dataPatchedF%s' % (k+1)]=np.array([[data[j*patchSize:(j+1)*patchSize,i*patchSize:(i+1)*patchSize,RGB] for i in range(0,Xamount)] for j in range(0,Yamount)])

# Open city
Amount_data= len(glob.glob('City/C*'))
for k in range (0, Amount_data):
    name="City/C%d.png" % (k+1)
    img = Image.open(name)
    data=img.convert('RGB')
    data = np.asarray( data, dtype="int32" )
    Yamount=data.shape[0]/patchSize # Counts how many times the windowsize fits in the picture
    Xamount=data.shape[1]/patchSize # Counts how many times the windowsize fits in the picture
    globals()['dataPatchedC%s' % (k+1)]=np.array([[data[j*patchSize:(j+1)*patchSize,i*patchSize:(i+1)*patchSize,RGB] for i in range(0,Xamount)] for j in range(0,Yamount)])

# Open water
Amount_data= len(glob.glob('Water/W*'))
for k in range (0, Amount_data):
    name="Water/W%d.png" % (k+1)
    img = Image.open(name)
    data=img.convert('RGB')
    data = np.asarray( data, dtype="int32" )
    Yamount=data.shape[0]/patchSize # Counts how many times the windowsize fits in the picture
    Xamount=data.shape[1]/patchSize # Counts how many times the windowsize fits in the picture
    globals()['dataPatchedW%s' % (k+1)]=np.array([[data[j*patchSize:(j+1)*patchSize,i*patchSize:(i+1)*patchSize,RGB] for i in range(0,Xamount)] for j in range(0,Yamount)])

```

```python
dataPatchedC9.shape
```

    (13L, 32L, 41L, 41L)

```python
plots=[WC11,WC12,WC13,WC14]#WC15,WC16,WC17,WC18,WC19,WC110,WC111]
plt.figure(figsize=(2,2))
for i in range(0,len(plots)):
    plt.subplot(2,len(plots)/2,i)
    plt.imshow(plots[i])

```

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

def Sigmoid(x): # a is used to adjust the slope of the function
    return (1/(1+np.exp(-x)))

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

def makeGaussian(size, fwhm = 3, center=None):
    x = np.arange(0, size, 1, float)
    y = x[:,np.newaxis]

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

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

############## Update rule for convolution weights ##################################
def updateW2(W2rb, n, m):
    update=0
    n3=1
    for k in range(0,3): #sum over k
        for i in range(0,4): # sum over j so the elementen in the last-weights
            for j in range(0,4): # sum over j so the elementen in the last-weights
                for u in range(0,2): # sum over u which are represent the elements of the pooling L3
                        for v in range(0,2): # sum over j so the elementen in the last-weights
                            update=update+n3*(t[k]-f[k])*W_k[k,i,j]*y2rb[n+(2*j+u),m+(2*j+v)]
    return update

############ FILTERS #####################################################################################################################################
WC1=np.zeros((9,9))
WC1[4:5,:]=1
WC1[5:9,:]=2
WC2=np.zeros((9,9))
for i in range(0,len(WC2)):
    for j in range(0,len(WC2)):
        if(i==j):WC2[i,j]=1
        if(i>j): WC2[i,j]=2

WC11=WC1+WC2/2
WC3=makeGaussian(9,4)
WC4=np.zeros((9,9))
x=np.linspace(0,15*np.pi,9)
for i in range(0,9):
    WC4[:,i]=np.sin(x)
WC5=np.transpose(WC11)
WC6=np.fliplr(WC15)
WC7=np.ones((9,9))
WC8=dataPatchedF7[5,5,0:9,0:9] #take a patch from forest as filter!
WC9=np.zeros((9,9))
x=np.linspace(0,0.01*np.pi,9)
for i in range(0,9):
    WC9[i,:]=np.sin(x)
WC10=np.transpose(WC9)

W1rb=np.array([
    [1,1,1],
    [1,1,1],
    [1,1,1],
    ])

W2rb=np.random.rand(1,4,4)
W3rb=np.array([
    [1,1],
    [1,1]
    ])

################### Initialize random weights ##############################################################################################
W_k=np.random.rand(3,4,4)
y3rb=np.array([np.zeros((8,8)) for i in range(0,3)])
y4rb=np.array([np.zeros((4,4)) for i in range(0,3)])

bias=np.zeros((3,1))
################### Data settings for training #############################################################################################
data_list=([[dataPatchedF1,dataPatchedF2,dataPatchedF3,dataPatchedF4,dataPatchedF5,dataPatchedF6,dataPatchedF7,dataPatchedF8,dataPatchedF9],
           [dataPatchedC1,dataPatchedC2,dataPatchedC3,dataPatchedC4,dataPatchedC5,dataPatchedC6,dataPatchedC7,dataPatchedC8,dataPatchedC9],
           [dataPatchedG1,dataPatchedG2,dataPatchedG3,dataPatchedG4,dataPatchedG5,dataPatchedG6,dataPatchedG7,dataPatchedG8,dataPatchedG9]
            ])

class_list=np.array([
            [1,0,0], #forest
            [0,1,0], #City
            [0,0,1]  #Grass/Water
            ])

#################### Start Training ################################################################################################
py=0
px=0
d1=0
d2=0
for uu in range(0,100):
    if(d1==3): # amount of dataset classes (3: forest,city,water)
        d1=0
        d2=d2+1
    if(d2==9): #amount of datasets per class
        d2=0
        if(px==32 and py==12): # 32 and 12 are the amount of patches in dataPatched
            print('stop')
            break # If searchspace is achieved the loop will stop
        if(py==12):
            j=j+1 # selects new patch in x direction
            py=0
        py=py+1  # i must increase to cover all patches over y-direction

    inputPatch=data_list[d1][d2][py,px,:,:]
    t=class_list[d1]
    d1=d1+1
#     if(t2==1):continue
    #L1
    y1rb=sig.convolve(inputPatch,WC1,mode='valid')
    #L2
    y2rb=Pool(y1,W1rb) # reducing dimensionality by 2.
    for b in range(0,1): # here b is the amount of branches for each row
        y3rb[b]=sig.convolve(y2,W2rb[b,:,:],mode='valid')  #L3
        y4rb[b]=Pool(y3[b],W3rb) #L4

    n1=2*10**-4
    n2=1*10**-2

    # Now we have the outputs
    x1=(
        np.inner(y4rb[0].flatten(),W_k[0].flatten())
        )

    x2=(
        np.inner(y4rb[0].flatten(),W_k[1].flatten())
        )
    x3=(
        np.inner(y4rb[0].flatten(),W_k[2].flatten())
        )
    f=[Sigmoid(x1-b1),Sigmoid(x2-b2),Sigmoid(x3-b3)]

    for n in range(0,3):
        for m in range(0,3):
            W2rb[0,n,m]=W2rb[0,n,m]+updateW2(W2rb[0],n,m)

    for k in range(0,3):
        bias[k]=bias[k] -n1*(t[k]-f[k])

    for k in range(0,3):
        W_k[k]=W_k[k] +n1*(t[k]-f[k])*y4rb[0]

```

```python
data_list[0][9].shape
```

```python
y2rb.shape
```

    (11L, 11L)

```python
W2rb.shape
```

    (1L, 4L, 4L)

```python

```


# 12. Gyakorlat - 2 DoF gerjesztett rendszer
2021.04.25.

## Feladat:

A mellékelt ábrán egy 2 szabadságfokú mechanikai lengőrendszer látható, mely egy ingából és egy kocsiból áll. A kocsi egy kizárólag a vízszintes irányban elmozdulni képes merev testként van modellezve, melynek tömegét $m_1$ jelöli. Az inga, mely a B csukló körül képes csak elfordulni egy rúdként van modellezve: tömege $m_2$, hossza $l$. A rendszerre kétféle gerjesztő hatás hat: egy harmonikus erőgerjesztés $F(t) = F_0\sin(\omega t + \varepsilon)$ az inga C pontjában, valamint egy harmonikus útgerjesztés $r(t) = r_0\cos(\omega t)$ egy, a kocsihoz kapcsolódó $k$ rugómerevségű rugón keresztül. A rendszer mozgását az $x_1$ és $x_2$ általános koordináta választása mellett vizsgáljuk. Itt $x_1$ a kocsi súlypontjának vízszintes irányú elmozdulása, míg $x_2$ az inga C pontjának kitérése (az ábra szerint). A rendszer a függőleges síkban helyezkedik el, rá a $\mathbf{g}$ irányú gravitációs erő hat. Egyensúlyi helyzete az $x_1 = 0$, $x_2 = 0$ pozícióban található.
### Adatok:
|                              |                        |                        |
|:-----------------------------|:-----------------------|:-----------------------|
| $m_1$ = 2 kg                | $l$ = 0.5 m            | $\omega$ = 20 rad/s            |
| $m_2$ = 1 kg                | $F_0$ = 5 N           | $\varepsilon$ = $\pi$/3            |
| $k$ = 100 N/m                | $r_0$ = 0.01 m           |             |

### Részfeladatok:

1. Határozza meg a modell mátrix együtthatós mozgásegyenletét az egyensúlyi helyzet körüli kis kitéréseket feltételezve!
2. Adja meg a rendszer állandósult állapotbeli megoldását!
3. Számítsa ki a rúd maximális elfordulását az állandósult állapotbeli megoldás során!

## Megoldás:

### 1. Feladat

Mivel időfüggő kényszereink vannak (út- és erőgerjesztés), ezért közvetlenül nem tudjuk használni a mátrix együtthatós egyenlet meghatározása során használt összefüggéseinket. Itt a másodfajú Lagrange-egyenlethez kell fordulnunk.

```python
# Először adjuk meg a kinetikus energiát
import sympy as sp
sp.init_printing()
from IPython.display import Math

t = sp.Symbol('t')
x1 = sp.Function('x_1')(t)
x2 = sp.Function('x_2')(t)
φ = sp.Function('φ')(t) # ő nem lesz ált. koord. (de lehetne)

q = sp.Matrix([[x1],[x2]]) # az ált. koord. vektora

m1, m2, l, k, F0, r0, ω, ε, g = sp.symbols('m_1, m_2, l, k, F_0, r_0, ω, ε, g')

r = r0*sp.cos(ω*t)
F = F0*sp.sin(ω*t+ε)

# Készítsünk behelyettesítési listát az adatok alapján, SI-ben:
adatok = [(m1, 2), (m2, 1), (l, 0.5), (F0, 5), (ω, 20),
          (ε, sp.pi/3), (k, 100), (r0, 0.01), (g, 9.81)]
```

A **kinetikus energia** alakja a következő:
$$T=\underbrace{\frac{1}{2} m_{1} v_{\mathrm{B}}^{2}}_{T_{\text {kocsi }}}+\underbrace{\frac{1}{2} m_{2} v_{\mathrm{S}}^{2}+\frac{1}{2} \theta_{\mathrm{S}} \omega_{2}^{2}}_{T_{\text {inga }}},$$

ám nekünk ezt az általánosított koordinátákkal kell felírni!

```python
# Az egyszerűen megállapítható, hogy `v_B = x1.diff(t)`.
# Szükséges még `v_S és ω_2`:

ω2 = sp.symbols('ω_2')

# kis kitérések esetén:
x2_phi_eq = sp.Eq(x2,l*φ)
x2_phi_eq
```

```python
# Kifejezve φ-t, és deriválva mindkét oldalt:
φ_expr = sp.solve(x2_phi_eq,φ)[0]
ω2_expr = φ_expr.diff(t)
display(sp.Eq(φ,φ_expr))
display(sp.Eq(ω2,ω2_expr))  # most azzal ne foglalkozzunk, hogy `ω_2`-nél nincs jelölve az időfüggés
```

Sebességredukciós képlet segítségével már számítható $\mathbf{v}_\mathrm{S}$ (ismerjük a rúd sebességállapotát):

$$\mathbf{v}_\mathrm{S} = \mathbf{v}_\mathrm{B} + \boldsymbol{\omega}_2 \times \mathbf{r}_\mathrm{BS}.$$

```python
vB_vect = sp.Matrix([[sp.diff(x1,t)],[0],[0]])
ω2_vect = sp.Matrix([[0],[0],[ω2_expr]])
rBS_vect = sp.Matrix([[l/2*sp.sin(φ_expr)],[-l/2*sp.cos(φ_expr)],[0]])
vS_vect = vB_vect + ω2_vect.cross(rBS_vect)
```

```python
# kifejezve az általános koordinátákkal
display(Math('\mathbf{{v}}_\mathrm{{S}} = {}'.format(sp.latex(vS_vect))))
```

$\displaystyle \mathbf{v}_\mathrm{S} = \left[\begin{matrix}\frac{\cos{\left(\frac{\operatorname{x_{2}}{\left(t \right)}}{l} \right)} \frac{d}{d t} \operatorname{x_{2}}{\left(t \right)}}{2} + \frac{d}{d t} \operatorname{x_{1}}{\left(t \right)}\\\frac{\sin{\left(\frac{\operatorname{x_{2}}{\left(t \right)}}{l} \right)} \frac{d}{d t} \operatorname{x_{2}}{\left(t \right)}}{2}\\0\end{matrix}\right]$

```python
# a kinetikus energiához még számoljuk ki a rúd tehetetlenségi nyomatékát:
ΘS = sp.Rational(1,12)*m2*l**2

# Tipp: figyeljünk arra, hogy a theták ne tévesszenek meg minket:
# `θ` -> `\theta + tab` (aka. kis theta)
# `Θ` -> `\Theta + tab` (aka. nagy theta)
# Ember legyen a talpán, aki debugolásnál meglátja a különbséget.
```

```python
T = (sp.Rational(1,2)*m1*vB_vect.dot(vB_vect)
     + sp.Rational(1,2)*m2*vS_vect.dot(vS_vect)
     + sp.Rational(1,2)*ΘS*ω2_vect.dot(ω2_vect))
```

```python
""" Mint feljebb említettem, a mátrix együtthatós egyenletnél használatos
együttható mátrixok kiszámítási formuláit körültekintően kell használni,
ugyanis időfüggő gerjesztések hatnak a rendszerre. A tömegmátrix számításánál
azonban most ez megengedett, ugyanis a kinetikus energiában nincs explicit
időfüggő tag (pl. nincs kiegy. forgórész általi gerjesztés)."""

# Elsőnek parciálisan deriváljuk a kinetikus energiát a 2 ált. sebesség szerint,
# majd helyettesítsünk 0-t a helyükre.
# Szintax: egymásba ágyazott 2 db `list comprehension`. Olyan, mint 2 egymásba
# ágyazott `for` ciklus.
M_array = [[T.expand().diff(q1.diff()).diff(q2.diff()).simplify().subs([(q1,0),(q2,0)]) for q1 in q] for q2 in q]
M = sp.Matrix(M_array)
display(Math('\mathbf{{M}} = {}'.format(sp.latex(M))))
```

$\displaystyle \mathbf{M} = \left[\begin{matrix}m_{1} + m_{2} & \frac{m_{2}}{2}\\\frac{m_{2}}{2} & \frac{m_{2}}{3}\end{matrix}\right]$

```python
## Potenciális energia

# Itt a Lagrange-egyenlet formuláit kell használnunk, mert van explicit időfüggés
U = -m2*g*l/2*sp.cos(φ_expr) + sp.Rational(1,2)*k*(x1-r)**2

U_d_q = sp.Matrix([U.diff(alt_koord).expand() for alt_koord in q]) # parc. derivált vektor
display(Math('\\begin{{bmatrix}}\\frac{{\\partial{{U}}}}{{\\partial{{x_1}}}} \\\\ \\frac{{\\partial{{U}}}}{{\\partial{{x_2}}}}  \\end{{bmatrix}}= {}'
             .format(sp.latex(U_d_q))))

# A hosszadalmas LaTeX kóddal ne foglalkozzunk most
```

$\displaystyle \begin{bmatrix}\frac{\partial{U}}{\partial{x_1}} \\ \frac{\partial{U}}{\partial{x_2}}  \end{bmatrix}= \left[\begin{matrix}- k r_{0} \cos{\left(t ω \right)} + k \operatorname{x_{1}}{\left(t \right)}\\\frac{g m_{2} \sin{\left(\frac{\operatorname{x_{2}}{\left(t \right)}}{l} \right)}}{2}\end{matrix}\right]$

```python
""" Ami itt fontos nekünk, az a gerjesztést tartalmazó tag. Ez ugyanis
kiesne akkor, ha a márix együtthatós egyenlet formuláit használnánk.
"""
gerj = U_d_q[0].as_two_terms()
# két részre szedjük az összeget
display(gerj) # így egy listában kapjuk vissza az összeg két tagját
```

```python
# A gerjesztést tartalmazó második tag mínusz egyszerese
# megy az általános Q erők vektorába
Qr = sp.Matrix([[-gerj[1]],[0]])
display(Math('\mathbf{{Q}}^r(t) = {}'.format(sp.latex(Qr))))

# Most, hogy 'elmentettük' a gerjesztő tagot, nyugodtan számolhatunk a kényelmes
# mátrix együthatós formulával (ahol kiesik ez a tag):

K_array = [[U.expand().diff(q1).diff(q2).simplify().subs([(q1,0),(q2,0)]) for q1 in q] for q2 in q]
K = sp.Matrix(K_array)

display(Math('\mathbf{{K}} = {}'.format(sp.latex(K))))
```

$\displaystyle \mathbf{Q}^r(t) = \left[\begin{matrix}k r_{0} \cos{\left(t ω \right)}\\0\end{matrix}\right]$

$\displaystyle \mathbf{K} = \left[\begin{matrix}k & 0\\0 & \frac{g m_{2}}{2 l}\end{matrix}\right]$

```python
## Az általános erő vektora

""" Q egy részét már kiszámoltuk (Q_r). A másik tagot az erőgerjesztésből számíthatjuk (Q_F).
Írjuk fel ehhez a gerjesztő erő teljesítményét:"""

vC_y = sp.Symbol('vC_y') # igazából úgy is kiesik, de kell oda valami

vC_vect = sp.Matrix([[q[0].diff(t) + q[1].diff(t)],[vC_y]]) # `x1.diff(t)` mint szállító sebesség van jelen
P_F = sp.Matrix([[F],[0]]).dot(vC_vect)
display(Math('P_\mathrm{{F}} = {}'.format(sp.latex(P_F))))
```

$\displaystyle P_\mathrm{F} = F_{0} \left(\frac{d}{d t} \operatorname{x_{1}}{\left(t \right)} + \frac{d}{d t} \operatorname{x_{2}}{\left(t \right)}\right) \sin{\left(t ω + ε \right)}$

```python
# Ki kell fejezni a teljesítményt az általános sebességekkel is, és ez alapján
# lehet meghatározni az általános erők vektorát. Most egyszerű dolgunk van:
QF = sp.Matrix([P_F.expand().coeff(alt_coord.diff(t)) for alt_coord in q])
QF

Q = QF + sp.Matrix(Qr)
display(Math('\mathbf{{Q}}(t) = {}'.format(sp.latex(Q))))
```

$\displaystyle \mathbf{Q}(t) = \left[\begin{matrix}F_{0} \sin{\left(t ω + ε \right)} + k r_{0} \cos{\left(t ω \right)}\\F_{0} \sin{\left(t ω + ε \right)}\end{matrix}\right]$

Mivel az állandósult megoldásra vagyunk kíváncsiak, ezért az általános erő vektorát hozzuk az alábbi alakra:

$$\mathbf{Q}(t)=\mathbf{F}_{\mathrm{S}} \sin (\omega t)+\mathbf{F}_{\mathrm{C}} \cos (\omega t).$$

```python
# az `applyfunc` függvénnyel tudunk egy mátrixnak minden elemére
# alkalmazni egy adott függvényt (pl. `sp.simplify`).
Q_expand = Q.applyfunc(sp.expand_trig).expand()
display(Q_expand)

Fs = sp.Matrix([elem.coeff(sp.sin(ω*t)) for elem in Q_expand])
Fc = sp.Matrix([elem.coeff(sp.cos(ω*t)) for elem in Q_expand])
display(Math('\mathbf{{F}}_\mathrm{{S}} = {}'.format(sp.latex(Fs))))
display(Math('\mathbf{{F}}_\mathrm{{C}} = {}'.format(sp.latex(Fc))))
```

$\displaystyle \left[\begin{matrix}F_{0} \sin{\left(ε \right)} \cos{\left(t ω \right)} + F_{0} \sin{\left(t ω \right)} \cos{\left(ε \right)} + k r_{0} \cos{\left(t ω \right)}\\F_{0} \sin{\left(ε \right)} \cos{\left(t ω \right)} + F_{0} \sin{\left(t ω \right)} \cos{\left(ε \right)}\end{matrix}\right]$

$\displaystyle \mathbf{F}_\mathrm{S} = \left[\begin{matrix}F_{0} \cos{\left(ε \right)}\\F_{0} \cos{\left(ε \right)}\end{matrix}\right]$

$\displaystyle \mathbf{F}_\mathrm{C} = \left[\begin{matrix}F_{0} \sin{\left(ε \right)} + k r_{0}\\F_{0} \sin{\left(ε \right)}\end{matrix}\right]$

```python
# Végül a mátrix együthatós mozgásegyenlet
eom = sp.Eq(M*q.diff(t,2) + K*q, Fs*sp.cos(ω*t) + Fc*sp.sin(ω*t))
eom
```

$\displaystyle \left[\begin{matrix}k \operatorname{x_{1}}{\left(t \right)} + \frac{m_{2} \frac{d^{2}}{d t^{2}} \operatorname{x_{2}}{\left(t \right)}}{2} + \left(m_{1} + m_{2}\right) \frac{d^{2}}{d t^{2}} \operatorname{x_{1}}{\left(t \right)}\\\frac{g m_{2} \operatorname{x_{2}}{\left(t \right)}}{2 l} + \frac{m_{2} \frac{d^{2}}{d t^{2}} \operatorname{x_{1}}{\left(t \right)}}{2} + \frac{m_{2} \frac{d^{2}}{d t^{2}} \operatorname{x_{2}}{\left(t \right)}}{3}\end{matrix}\right] = \left[\begin{matrix}F_{0} \cos{\left(ε \right)} \cos{\left(t ω \right)} + \left(F_{0} \sin{\left(ε \right)} + k r_{0}\right) \sin{\left(t ω \right)}\\F_{0} \sin{\left(ε \right)} \sin{\left(t ω \right)} + F_{0} \cos{\left(ε \right)} \cos{\left(t ω \right)}\end{matrix}\right]$

```python
# Az olvashatóságot rontja, hogy a `sympy` automatikusan elvégzi az
# összeadásokat és a szorzásokat. Ezt ki tudjuk küszöbölni, ha
# `sp.MatAdd(tag_1, tag_2, ... )` és `sp.MatMul(tenyezo_1, tenyezo_2, ...)`
# függvényeket használunk.

sp.Eq(sp.MatAdd(sp.MatMul(M,q.diff(t,2)),sp.MatMul(K,q)),
      sp.MatAdd(sp.MatMul(Fc,sp.cos(ω*t)),sp.MatMul(Fs,sp.sin(ω*t))))
```

$\displaystyle \left[\begin{matrix}k & 0\\0 & \frac{g m_{2}}{2 l}\end{matrix}\right] \left[\begin{matrix}\operatorname{x_{1}}{\left(t \right)}\\\operatorname{x_{2}}{\left(t \right)}\end{matrix}\right] + \left[\begin{matrix}m_{1} + m_{2} & \frac{m_{2}}{2}\\\frac{m_{2}}{2} & \frac{m_{2}}{3}\end{matrix}\right] \left[\begin{matrix}\frac{d^{2}}{d t^{2}} \operatorname{x_{1}}{\left(t \right)}\\\frac{d^{2}}{d t^{2}} \operatorname{x_{2}}{\left(t \right)}\end{matrix}\right] = \left[\begin{matrix}F_{0} \cos{\left(ε \right)}\\F_{0} \cos{\left(ε \right)}\end{matrix}\right] \sin{\left(t ω \right)} + \left[\begin{matrix}F_{0} \sin{\left(ε \right)} + k r_{0}\\F_{0} \sin{\left(ε \right)}\end{matrix}\right] \cos{\left(t ω \right)}$

### 2. Feladat

A partikuláris (állandósult) megoldást keressük

$$\mathbf{q}_{\mathrm{p}}=\mathbf{L} \cos (\omega t)+\mathbf{N} \sin (\omega t)$$

alakban.

```python
L1, L2, N1, N2 = sp.symbols('L_1, L_2, N_1, N_2')
L = sp.Matrix([[L1],[L2]])
N = sp.Matrix([[N1],[N2]])

qp = L*sp.cos(ω*t) + N*sp.sin(ω*t)
```

```python
# Ennek deriváltjait helyettesítsük be a mozgásegyenletbe
eom.lhs.subs([(x1,qp[0]),(x2,qp[1])])
```

$\displaystyle \left[\begin{matrix}k \left(L_{1} \cos{\left(t ω \right)} + N_{1} \sin{\left(t ω \right)}\right) + \frac{m_{2} \frac{\partial^{2}}{\partial t^{2}} \left(L_{2} \cos{\left(t ω \right)} + N_{2} \sin{\left(t ω \right)}\right)}{2} + \left(m_{1} + m_{2}\right) \frac{\partial^{2}}{\partial t^{2}} \left(L_{1} \cos{\left(t ω \right)} + N_{1} \sin{\left(t ω \right)}\right)\\\frac{g m_{2} \left(L_{2} \cos{\left(t ω \right)} + N_{2} \sin{\left(t ω \right)}\right)}{2 l} + \frac{m_{2} \frac{\partial^{2}}{\partial t^{2}} \left(L_{1} \cos{\left(t ω \right)} + N_{1} \sin{\left(t ω \right)}\right)}{2} + \frac{m_{2} \frac{\partial^{2}}{\partial t^{2}} \left(L_{2} \cos{\left(t ω \right)} + N_{2} \sin{\left(t ω \right)}\right)}{3}\end{matrix}\right]$

```python
# Végeztessük el az idő szerinti deriváltakat (mint látható, még szimbolikus alakban vannak)
eom_stat = sp.Matrix([elem.doit().expand() for elem in eom.lhs.subs([(x1,qp[0]),(x2,qp[1])])])
eom_stat
# érdekes módon nem működik a `.applyfunc(sp.doit)` függvény, ezért
# egy `list comprehension`-ben kellet elemenként alkalmazni
```

$\displaystyle \left[\begin{matrix}L_{1} k \cos{\left(t ω \right)} - L_{1} m_{1} ω^{2} \cos{\left(t ω \right)} - L_{1} m_{2} ω^{2} \cos{\left(t ω \right)} - \frac{L_{2} m_{2} ω^{2} \cos{\left(t ω \right)}}{2} + N_{1} k \sin{\left(t ω \right)} - N_{1} m_{1} ω^{2} \sin{\left(t ω \right)} - N_{1} m_{2} ω^{2} \sin{\left(t ω \right)} - \frac{N_{2} m_{2} ω^{2} \sin{\left(t ω \right)}}{2}\\- \frac{L_{1} m_{2} ω^{2} \cos{\left(t ω \right)}}{2} + \frac{L_{2} g m_{2} \cos{\left(t ω \right)}}{2 l} - \frac{L_{2} m_{2} ω^{2} \cos{\left(t ω \right)}}{3} - \frac{N_{1} m_{2} ω^{2} \sin{\left(t ω \right)}}{2} + \frac{N_{2} g m_{2} \sin{\left(t ω \right)}}{2 l} - \frac{N_{2} m_{2} ω^{2} \sin{\left(t ω \right)}}{3}\end{matrix}\right]$

```python
# Válogassuk ki `cos(ωt)` és `sin(ωt)` együtthatóit
coscoeff = sp.Matrix([elem.coeff(sp.cos(ω*t)) for elem in eom_stat])
sincoeff = sp.Matrix([elem.coeff(sp.sin(ω*t)) for elem in eom_stat])

# Tegyük őket be egy vektorba, majd azt tegyük egyenlővé az általános erő
# kifejezésében lévő `cos(ωt)` és `sin(ωt)` együtthatóival. Helyettesítsük
# be a numerikus adatokat.

tmp = sp.Eq(sp.Matrix([coscoeff,sincoeff]).subs(adatok),sp.Matrix([[Fc],[Fs]]).subs(adatok))
tmp
```

$\displaystyle \left[\begin{matrix}- 1100 L_{1} - 200 L_{2}\\- 200 L_{1} - 123.523333333333 L_{2}\\- 1100 N_{1} - 200 N_{2}\\- 200 N_{1} - 123.523333333333 N_{2}\end{matrix}\right] = \left[\begin{matrix}1.0 + \frac{5 \sqrt{3}}{2}\\\frac{5 \sqrt{3}}{2}\\\frac{5}{2}\\\frac{5}{2}\end{matrix}\right]$

```python
# Oldjuk meg az egyenletrendszert
megold = sp.solve(tmp,L1,L2,N1,N2)
megold # méterben
```

```python
# Tehát a partikuláris megoldásunk:
eom_stat_num = sp.Eq(q,qp.subs(megold).subs(adatok).evalf(4))
eom_stat_num
```

$\displaystyle \left[\begin{matrix}\operatorname{x_{1}}{\left(t \right)}\\\operatorname{x_{2}}{\left(t \right)}\end{matrix}\right] = \left[\begin{matrix}0.001994 \sin{\left(20 t \right)} + 0.002166 \cos{\left(20 t \right)}\\- 0.02347 \sin{\left(20 t \right)} - 0.03856 \cos{\left(20 t \right)}\end{matrix}\right]$

### 3. Feladat

```python
# Az állandósult állapotbeli megoldás `x_2`-re nézve:
x2_stat = eom_stat_num.rhs[1]
x2_stat
# ennek keressük a maximumát

# Ezt analitikusan sokkal egyszerűbb megoldani, mint deriválással
x2_max = sp.sqrt(megold[L2]**2+megold[N2]**2).evalf(4)
display(Math('x_{{2,\mathrm{{max}}}} = {}\\ \mathrm{{m}}'.format(sp.latex(x2_max))))

φ_max = φ_expr.subs(adatok).subs(x2,x2_max).evalf(4)
display(Math('\\varphi_{{\mathrm{{max}}}} = {}\\ \mathrm{{rad}}'.format(sp.latex(φ_max))))
```

$\displaystyle x_{2,\mathrm{max}} = 0.04514\ \mathrm{m}$

$\displaystyle \varphi_{\mathrm{max}} = 0.09028\ \mathrm{rad}$

Készítette:

        Csuzdi Domonkos (Alkalmazott Mechanika Szakosztály)
        Molnár Csenge Andrea (BME MM) kidolgozása alapján.

        Hibák, javaslatok:
        amsz.bme@gmail.com
        csuzdi02@gmail.com

        2021.04.25