# Calculate Simplified Ikeda simulation

```python
#from jupyterthemes import jtplot
#jtplot.style(theme='onedork', context='notebook', ticks=True, grid=False)
```

```python
%matplotlib inline
%load_ext autoreload
%autoreload 2
```

```python
import pandas as pd
pd.options.display.max_rows = 999
pd.options.display.max_columns = 999
import numpy as np
import matplotlib.pyplot as plt
from pylab import rcParams
rcParams['figure.figsize'] = 15, 7

from rolldecayestimators.simplified_ikeda import calculate_roll_damping
from rolldecayestimators import equations
import sympy as sp
from rolldecayestimators import symbols
from rolldecayestimators.substitute_dynamic_symbols import lambdify
from mdldb.tables import Run
from rolldecayestimators.direct_linear_estimator import DirectLinearEstimator
from rolldecayestimators.direct_estimator import DirectEstimator
from rolldecayestimators.ikeda_estimator import IkedaEstimator
from rolldecayestimators.transformers import CutTransformer, LowpassFilterDerivatorTransformer, ScaleFactorTransformer, OffsetTransformer
from sklearn.pipeline import Pipeline
from rolldecayestimators.sensitivity import variate_ship, plot_variation, calculate, calculate_variation, _plot_result

import data
import copy
from rolldecay import database
from sympy.physics.vector.printing import vpprint, vlatex
from IPython.display import display, Math, Latex
from rolldecay.paper_writing import save_fig
```

```python
db = database.get_db()
```

```python
required = ['lpp','beam','TA','TF','BKL','BKB','omega0','kg','Volume','A0']
```

```python
df_rolldecay = database.load(rolldecay_table_name='rolldecay_linear')
df_rolldecay['V']=df_rolldecay['ship_speed']

df_rolldecay.fillna(0, inplace=True)
mask = ((df_rolldecay['ship_speed']==0) &  ## Zero speed!
        (df_rolldecay[required] > 0).all(axis=1)
       )

df = df_rolldecay.loc[mask].copy()
df['phi_max'] = np.rad2deg(df['phi_start'].abs())
df['DRAFT'] = (df.TA + df.TF)/2
df['CB'] = df.Volume/(df.lpp*df.beam*df.DRAFT)
```

```python
row = df.iloc[1]
```

```python
row.id
```

```python
db_run = db.session.query(Run).get(int(row.id))
```

```python
df = database.load_run(db_run=db_run)
```

```python
df.plot(y='phi')
```

```python
result = calculate(row)
result
```

```python
Math(vlatex(equations.B44_equation))
```

```python
sp.Eq(symbols.B_44, sp.solve(equations.B44_equation, symbols.B_44)[0])
```

```python
B44_lambda = lambdify(sp.solve(equations.B44_equation, symbols.B_44)[0])
```

```python
g=9.81
rho=1000
B_44 = B44_lambda(B_44_hat=result.B44HAT, Disp=row.Volume, beam=row.beam, g=g, rho=rho)
```

Equivalent linearized damping according to [Himeno]:

```python
equations.B_e_equation
```

```python
equations.zeta_equation
```

```python
equations.omega0_equation_linear
```

```python
eqs = [equations.zeta_equation,
      equations.omega0_equation_linear]
```

```python
zeta_lambda = lambdify(sp.solve(eqs,symbols.A_44,symbols.zeta)[0][1])
```

```python
m = row.Volume*rho
zeta=zeta_lambda(B_1=B_44, GM=row.gm, g=g, m=m,  omega0=row.omega0)
zeta
```

```python
row.zeta
```

```python
lowpass_filter = LowpassFilterDerivatorTransformer(cutoff=2, minimum_score=0.99)
scaler = ScaleFactorTransformer(scale_factor=None)  # dummy value None for now
cutter = CutTransformer(phi_max=np.deg2rad(9), phi_min=np.deg2rad(0.25))
offset_transformer = OffsetTransformer()
```

```python
scaler.scale_factor=row.scale_factor
```

```python
direct_linear_estimator = DirectLinearEstimator()

steps = [
    ('filter',lowpass_filter),
    ('scaler',scaler),
    ('cutter', cutter),
    ('offset_transformer',offset_transformer),]
    #('linear_estimator', direct_linear_estimator)]

preprocessor = Pipeline(steps) # define the pipeline object.
```

```python
preprocessor.fit(df)
X=preprocessor.transform(df)
```

```python
X.plot(y='phi')
```

```python
estimator = DirectLinearEstimator()
t=X.index
phi0=X.iloc[0]['phi']
phi1d0=X.iloc[0]['phi1d']

df_sim_ikeda = estimator.simulate(t=t, phi0=phi0, phi1d0=phi1d0, omega0=row.omega0, zeta=zeta)
df_sim = estimator.simulate(t=t, phi0=phi0, phi1d0=phi1d0, omega0=row.omega0, zeta=row.zeta)

```

```python
df_result = X.copy()
df_result = pd.merge(left=df_result, right=df_sim_ikeda, how='left', left_index=True, right_index=True,
                     suffixes =('','_ikeda'))

df_result = pd.merge(left=df_result, right=df_sim, how='left', left_index=True, right_index=True,
                     suffixes =('','_model'))

fig,ax=plt.subplots()
df_result.plot(y='phi',label='model test', style=':', ax=ax)
df_result.plot(y='phi_model',label='model', style='-',ax=ax)
df_result.plot(y='phi_ikeda',label='ikeda', style='--',ax=ax)
```

## Model scale...

```python
df = database.load_run(db_run=db_run)

direct_linear_estimator = DirectLinearEstimator()
cutter = CutTransformer(phi_max=np.deg2rad(9), phi_min=np.deg2rad(0.25), phi1d_start_tolerance=10)
steps = [
    ('filter',lowpass_filter),
    #('scaler',scaler),
    ('cutter', cutter),
    ('offset_transformer',offset_transformer),]
    #('linear_estimator', direct_linear_estimator)]

preprocessor = Pipeline(steps) # define the pipeline object.
```

```python
preprocessor.fit(df)
X_model=preprocessor.transform(df)
X_model.plot(y='phi')
```

```python
row2 = row.copy()
scale=db_run.model.scale_factor

row2['lpp']/=scale
row2['beam']/=scale
row2['TA']/=scale
row2['TF']/=scale

row2['BKL']/=scale
row2['BKB']/=scale
row2['omega0']/=1/np.sqrt(scale)
#row2['OG']/=scale
row2['kg']/=scale
row2['gm']/=scale
row2['Volume']/=(scale**3)
```

```python
ikeda_estimator_ship = IkedaEstimator(lpp=row.lpp,
                                 TA=row.TA,
                                 TF = row.TF,
                                 beam = row.beam,
                                 BKL = row.BKL,
                                 BKB = row.BKB,
                                 A0 = row.A0,
                                 kg = row.kg,
                                 Volume = row.Volume,
                                 gm = row.gm)

ikeda_estimator_model = IkedaEstimator(lpp=row2.lpp,
                                 TA=row2.TA,
                                 TF = row2.TF,
                                 beam = row2.beam,
                                 BKL = row2.BKL,
                                 BKB = row2.BKB,
                                 A0 = row2.A0,
                                 kg = row2.kg,
                                 Volume = row2.Volume,
                                 gm = row2.gm)
```

```python
ikeda_estimator_ship.fit(X=X)
df_ikeda_ship = ikeda_estimator_ship.predict(X)
df_ikeda_ship.index/=np.sqrt(row.scale_factor)  # Scale to model scale time

ikeda_estimator_model.fit(X=X_model)
df_ikeda_model = ikeda_estimator_model.predict(X_model)

fig,ax=plt.subplots()
df_ikeda_ship.plot(y='phi', ax=ax, label='ship')
df_ikeda_model.plot(y='phi', ax=ax, label='model')

```

...the friction in model scale seem to have a small impact

```python
ikeda_estimator_ship.fit(X=X)
df_ikeda_ship = ikeda_estimator_ship.predict(X)
df_ikeda_ship.index/=np.sqrt(row.scale_factor)  # Scale to model scale time
```

## Looking at the quadratic part of Ikeda

```python
df = database.load_run(db_run=db_run)
lowpass_filter = LowpassFilterDerivatorTransformer(cutoff=2, minimum_score=0.99)
scaler = ScaleFactorTransformer(scale_factor=row.scale_factor)  # dummy value None for now
cutter = CutTransformer(phi_max=np.deg2rad(2), phi_min=np.deg2rad(0.25))
offset_transformer = OffsetTransformer()

steps = [
    ('filter',lowpass_filter),
    ('scaler',scaler),
    ('cutter', cutter),
    ('offset_transformer',offset_transformer),]
    #('linear_estimator', direct_linear_estimator)]

preprocessor = Pipeline(steps) # define the pipeline object.

preprocessor.fit(df)
X2=preprocessor.transform(df)
```

```python
ikeda_estimator_ship.fit(X2)
df_sim_ikeda = ikeda_estimator_ship.predict(X2)
direct_estimator = DirectLinearEstimator()
direct_estimator.fit(X2)
df_sim_model = direct_estimator.predict(X2)

df_result = X2.copy()
df_result = pd.merge(left=df_result, right=df_sim_ikeda, how='left', left_index=True, right_index=True,
                     suffixes =('','_ikeda'))

df_result = pd.merge(left=df_result, right=df_sim_model, how='left', left_index=True, right_index=True,
                     suffixes =('','_model'))

fig,ax=plt.subplots()
df_result.plot(y='phi',label='model test', style=':', ax=ax)
df_result.plot(y='phi_model',label='model', style='-',ax=ax)
df_result.plot(y='phi_ikeda',label='ikeda', style='--',ax=ax)
```

```python
N=40
changes = np.linspace(1,0.0,N)
df_variation = variate_ship(ship=row, key='phi_max', changes=changes)
result = calculate_variation(df=df_variation)
df_variation['g']=9.81
df_variation['rho']=1000
result = pd.concat((result,df_variation), axis=1)
result['phi_a']=np.deg2rad(result['phi_max'])
```

```python
plot_variation(ship=row, key='phi_max', changes=changes)
```

```python
result['B_44'] = B44_lambda(B_44_hat=result.B44HAT, Disp=row.Volume, beam=row.beam, g=g, rho=rho)
result['zeta'] = zeta_lambda(B_1=result['B_44'], GM=row.gm, g=g, m=m,  omega0=row.omega0)
result.dropna(inplace=True)
```

```python
fig,ax=plt.subplots()
result.plot(y='zeta', ax=ax)
ylim=ax.get_ylim()
ax.set_ylim(0,ylim[1])
```

```python
B_e_lambda = lambdify(sp.solve(equations.B_e_equation_cubic,symbols.B_e)[0])
sp.solve(equations.B_e_equation_cubic,symbols.B_e)[0]
```

```python
def fit(df,B_1,B_2, B_3):
    omega0=df['omega0'].astype(float)
    phi_a=df['phi_max'].astype(float)
    return B_e_lambda(B_1, B_2, B_3, omega0, phi_a)
```

```python
from scipy.optimize import curve_fit
```

```python
coeffs,_=curve_fit(f=fit,xdata=result,ydata=result['B_44'])
coeffs
```

```python
B_1=coeffs[0]
B_2=coeffs[1]
B_3=coeffs[2]
result['B_44_fit'] = fit(result,*coeffs)
```

```python
fig,ax=plt.subplots()
result.plot(y='B_44', ax=ax)
result.plot(y='B_44_fit', ax=ax, style='--')

```

```python
B_e_lambda = lambdify(sp.solve(equations.B_e_equation,symbols.B_e)[0])
```

```python
sp.solve(equations.B_e_equation,symbols.B_e)[0]
```

```python
B_e_lambda
```

```python
result[['omega0','phi_max','phi_a']].dtypes
```

```python
result['omega0']=result['omega0'].astype(float)
```

```python
def fit2(df,B_1,B_2):
    omega0=df['omega0'].astype(float)
    phi_a=df['phi_a']
    return np.array(B_e_lambda(B_1, B_2,omega0, phi_a))
```

```python
coeffs,_=curve_fit(f=fit2,xdata=result,ydata=result['B_44'].astype(float),
                   p0=[result['B_44'].mean(),result['B_44'].mean()])
B_1=coeffs[0]
B_2=coeffs[1]
result['B_44_fit'] = fit2(result,*coeffs)
```

```python
coeffs
```

```python
row1=row.copy()
row1.phi_max*=0.5
row2=row.copy()
s1=calculate(row1)
s2=calculate(row2)
s1['B_44'] = B44_lambda(B_44_hat=s1.B44HAT, Disp=row1.Volume, beam=row1.beam, g=g, rho=rho)
s2['B_44'] = B44_lambda(B_44_hat=s2.B44HAT, Disp=row2.Volume, beam=row2.beam, g=g, rho=rho)
x = np.deg2rad([row1.phi_max,row2.phi_max])*8*row.omega0/(3*np.pi)
B_2 = (s2['B_44']-s1['B_44'])/(x[1]-x[0])
B_1 = s1['B_44'] - B_2*x[0]

```

```python
B_1/10**9
```

```python
B_2/10**10
```

```python
fig,ax=plt.subplots()

xlabel=r'$\frac{3\cdot\omega_0}{(8\cdot\pi)}\cdot\phi_a$'
result[xlabel]=8*result.omega0/(3*np.pi)*result['phi_a']
result.plot(x=xlabel, y='B_44', ax=ax)
result.plot(x=xlabel,y='B_44_fit', ax=ax, style='--')

ax.plot(x,B_1+B_2*x,'ro-', label='two point regression')
#ax.set_title('Simplified Ikeda roll amplitude variation')
ax.get_legend().remove()
ax.set_ylabel('$B_{44}$')
save_fig(fig=fig, name='ikeda_B_1_B_2')
```

```python
equations.zeta_B1_equation
```

```python
equations.d_B2_equation
```

```python
zeta_B1_lambda = lambdify(sp.solve(equations.zeta_B1_equation, symbols.zeta)[0])
d_B2_lambda = lambdify(sp.solve(equations.d_B2_equation, symbols.d)[0])
```

```python
m=result.Volume*result.rho
result['zeta'] = zeta_B1_lambda(B_1=B_1,GM=result.gm,g=result.g, m=m, omega0=result.omega0)
result['d'] = d_B2_lambda(B_2=B_2,GM=result.gm,g=result.g, m=m, omega0=result.omega0)
```

```python
result.plot(y=['zeta','d'])
```

```python
direct_estimator = DirectEstimator()
t = np.array(X.index,)
phi0=X.iloc[0]['phi']
phi1d0=X.iloc[0]['phi1d']
omega0=row.omega0
zeta=result.iloc[0]['zeta']
d=result.iloc[0]['d']
df_ikeda_quadratic_sim=direct_estimator.simulate(t=t, phi0=phi0, phi1d0=phi1d0, omega0=omega0,
                                                 zeta=zeta, d=d)
```

```python
df_result = X.copy()
df_result = pd.merge(left=df_result, right=df_ikeda_quadratic_sim, how='left', left_index=True, right_index=True,
                     suffixes =('','_ikeda_quadratic'))

fig,ax=plt.subplots()
df_result.plot(y='phi',label='model test', style='r-', ax=ax)
df_result.plot(y='phi_ikeda_quadratic',label='ikeda_quadratic', style='--',ax=ax)
```

```python

```

```python

```

```python

```

```python

```

```python

```


# **Gaussian Processes**

    Notebook version: 1.0 (Oct 06, 2015)

    Author: Jerónimo Arenas García (jarenas@tsc.uc3m.es)

    Changes: v.1.0 - First version
             v.1.1 - Figures changed to png (tiff not readable in Firefox)

```python
# Import some libraries that will be necessary for working with data and displaying plots

# To visualize plots in the notebook
%matplotlib inline

import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import scipy.io       # To read matlab files
import pylab
```

# 4. Gaussian Processes

## 4.1 The multivariate Gaussian distribution

Let us assume that ${\bf x}$ and ${\bf y}$ are two jointly Gaussian random vectors, whose joint pdf can be expressed as

\begin{equation}
\left[ \begin{array}{c} {\bf x} \\ {\bf y} \end{array} \right] \sim {\cal{N}} \left( \left[ \begin{array}{c} {\bf m}_{\bf x} \\ {\bf m}_{\bf y} \end{array} \right] , \left[ \begin{array}{cc} {\pmb \Sigma}_{{\bf x}{\bf x}} & {\pmb \Sigma}_{{\bf x}{\bf y}} \\ {\pmb \Sigma}_{{\bf x}{\bf y}}^T & {\pmb \Sigma}_{{\bf y}{\bf y}}\end{array}\right] \right) = {\cal{N}} \left( \left[ \begin{array}{c} {\bf m}_{\bf x} \\ {\bf m}_{\bf y} \end{array} \right] , \left[ \begin{array}{cc} \widetilde{{\pmb \Sigma}}_{{\bf x}{\bf x}} & \widetilde{{\pmb \Sigma}}_{{\bf x}{\bf y}} \\ \widetilde{{\pmb \Sigma}}_{{\bf x}{\bf y}}^T & \widetilde{{\pmb \Sigma}}_{{\bf y}{\bf y}}\end{array}\right]^{-1} \right)
\end{equation}

where

   - ${\bf m}_{\bf x}$: Mean of random vector ${\bf x}$
   - ${\bf m}_{\bf y}$: Mean of random vector ${\bf y}$
   - ${\pmb \Sigma}_{{\bf x}{\bf x}}$: Covariance matrix of random vector ${\bf x}$
   - ${\pmb \Sigma}_{{\bf x}{\bf y}}$: Cross-covariance matrix of ${\bf x}$ and ${\bf y}$

Note that all blocks of the covariance matrix and its inverse are of appropriate sizes, as indicated by the subindexes. We are using tildes for the blocks of the inverse of the covariance matrix. Please, be aware that $\widetilde{\pmb\Sigma}_{{\bf x}{\bf x}} \neq {\pmb\Sigma}_{{\bf x}{\bf x}}^{-1}$.

Since random vectors are jointly Gaussian, this means that marginal and conditional distributions are also Gaussian, with distributions that can be expresses as

$${\bf x} \sim {\cal{N}} ({\bf m}_{\bf x}, {\pmb \Sigma}_{{\bf x}{\bf x}}) \qquad \qquad {\bf y} \sim {\cal{N}} ({\bf m}_{\bf y}, {\pmb \Sigma}_{{\bf y}{\bf y}})$$

$${\bf x}|{\bf y} \sim {\cal{N}} ( {\bf m}_{\bf x} - \widetilde{{\pmb \Sigma}}_{{\bf x}{\bf x}}^{-1} \widetilde{{\pmb \Sigma}}_{{\bf x}{\bf y}} ({\bf y} - {\bf m}_{\bf y}), \widetilde{{\pmb \Sigma}}_{{\bf x}{\bf x}}^{-1})$$

$${\bf x}|{\bf y} \sim {\cal{N}} ( {\bf m}_{\bf x} + {{\pmb \Sigma}}_{{\bf x}{\bf y}} {{\pmb \Sigma}}_{{\bf y}{\bf y}}^{-1} ({\bf y} - {\bf m}_{\bf y}), {{\pmb \Sigma}}_{{\bf x}{\bf x}} - {\pmb \Sigma}_{{\bf x}{\bf y}} {{\pmb \Sigma}}_{{\bf y}{\bf y}}^{-1} {\pmb \Sigma}_{{\bf x}{\bf y}}^\top)$$

The demonstration of these expressions is left as an exercise

### Generation of Samples from a Multivariate Gaussian Distribution

Although there are python routines that can help to generate points from a multivariate Gaussian distribution, a standard procedure is based upon the generation of samples from the normal distribution with zero mean and unitary standard deviation, which are then transformed in an appropritate manner.

The Cholesky decomposition of a symmetric positive-definite matrix ${\bf A}$ is (the definition can be slightly modified for complex matrices, but here we restrict our selves to matrices with real values):

$${\bf A} = {\bf L} {\bf L}^\top$$

where ${\bf L}$ is a lower triangular matrix with positive diagonal entries. It can be shown that such decomposition exists and is unique, and can be computed in a very stable an efficient way.

Now, imaging the problem of drawing points from the Gaussian distribution

$$p({\bf x}) = {\cal N}\left({\bf m},{\pmb \Sigma} \right)$$

This distribution can be sampled by drawing points ${\bf x}'$ from ${\cal N}\left({\bf 0},{\bf I} \right)$, and then applying the transformation

$${\bf x} = {\bf L} {\bf x}'~+~{\bf m}$$

where ${\bf L}$ is obtained from the Cholesky decomposition of $\pmb \Sigma$.

The demonstration of the validity of the process goes along these lines:

   - Since ${\bf x}$ is a linear transformation of a Gaussian variable, it should also be normally distributed

   - ${\mathbb E}\{{\bf x}\} = {\bf L} \mathbb{E}\{{\bf x}'\} + {\bf m} = {\bf m}$

   - $\text{Cov}\{{\bf x}\} = \text{Cov}\{{\bf L} {\bf x}'\} = {\mathbb E} \{{\bf L} {\bf x}' {{\bf x}'}^\top {\bf L}^\top\} = {\bf L} {\bf L}^\top = {\pmb \Sigma}$

```python
#This code illustrates the generation of multivariate Gaussian points with arbitrary distribution
m = np.array([3,3])
V = np.array([[1,-0.95],[-0.95,1]])

L = np.linalg.cholesky(V)

#Generation of points from the normal distribution with zero mean and unit variance
n_points = 300
dim = 2
x_prime = np.random.randn(dim,n_points)

#Linear transformation
x = L.dot(x_prime) + np.matrix(m).T

#Plot the results
plt.plot(x_prime[0,],x_prime[1,],'b.');
plt.plot(x[0,],x[1,],'r.');
```

## 4.2 Non-parametric Inference with Gaussian Processes

In the previous lesson we assumed the following generative model for the patterns in the training set

$${\bf s} = {\bf f} + {\pmb \varepsilon}$$

where

   - ${\bf s}$ is a vector containing all target values in the training set

   - ${\bf f}$ is a vector containing the true values of the function we would like to approximate at all points ${\bf x}^{(k)}$ in the training set

   - ${\pmb \varepsilon}$ is a vector of i.i.d. noise samples, whose distribution is assumed ${\cal N}({\bf 0},\sigma_\varepsilon^2{\bf I})$

This model essentially tells us that the observations in the training set are the values of a hidden function $f({\bf x})$ contaminated by additive noise.

   - In the previous lesson, we assumed a parametric model ${\bf f} = {\bf Z}{\bf w}$, and use it to make Bayesian Inference on the weight vector characterizing the model

   - In this lesson we will proceed non-parametrically:

      * We do not assume any particular analytic shape for $f({\bf x})$

      * Inference is done directly on the values of the function, i.e., on ${\bf f}$

How can we made inference directly on the function values?  The answer is provided by the concept of Gaussian Process (GP)

### 4.2.1 Gaussian Processes

   - A Gaussian process is a collection of random variables, any finite number of which have (consistent) Gaussian distributions

   - You can think of a Gaussian Process as a collection of infinite random variables with joint Gaussian distribution

   - The standard way we will use them is to characterize the joint distribution of the values of $f({\bf x})$

   - In this way, we can generate the values of $f({\bf x})$ according to the joint distribution of the points, if the values of $({\bf x})$ are close enough, this will produce a regression curve

In other words, rather than having a parametric model, and sampling ${\bf w}$ from a given distribution, GPs allow us to directly sample the values of the function

#### Characterization of a Gaussian Process

   - A Gaussian distribution is characterized by a mean vector and and a covariance matrix

    $${\bf f}~\sim~{\cal N}\left({\bf m},{\pmb \Sigma}\right)$$

   - A Gaussian Process is characterized by a mean function and a covariance function

    $$f({\bf x})~\sim~{\cal GP} \left( m{\bf x}, k({\bf x},{\bf x'}) \right)$$

   - A typical selection for the covariance function is the Squared Exponential (SE) kernel

    $$k({\bf x},{\bf x'}) = \exp\left( -\frac{1}{2l} (\|{\bf x}-{\bf x'}\|^2)\right)$$

Intuitively, this kernel specifies that the values of $f({\bf x})$ and $f({\bf x}')$ should be more correlated the closer the two points ${\bf x}$ and ${\bf x}'$.  What we consider to be close or far, is modulated by the selection of the length-parameter $l$.

#### Example: Using a Gaussian Process to generate functions

The concept of Gaussian Process is probably better illustrated with the following code fragment, where we generate functions from a GP with mean $m({\bf x}) = 0$ and SE covariance function

```python
from scipy import spatial

pylab.subplots_adjust(hspace=0.7)
n_points = 300
n_curves = 5
l_values = [.001, .01, 0.1]

X_grid = np.linspace(-2,2,n_points)
X_grid = np.matrix(X_grid).T

color = 'rgbcm'

for i,l in enumerate(l_values):
    plt.subplot(len(l_values),1,i+1)

    #The GP allow us to obtain the Gaussian distribution of the
    #values of f for any particular selection of points x
    #We do so for the points in X_grid

    #We compute the distances among each pair of points in X_grid
    distances = spatial.distance.cdist(X_grid,X_grid,'euclidean')
    #And the covariance matrix
    K = np.exp(-np.power(distances,2)/(2*l))
    #For numerical reasons: we add a small constant along the main
    #diagonal to make sure K is positive-definite
    L = np.linalg.cholesky(K+1e-10*np.eye(n_points))

    #Next, we draw n_curves samples from the Gaussian distribution
    #with mean 0 and covariance matrix K
    for iter in range(n_curves):
        f_prime = np.random.randn(n_points,1)
        f = L.dot(f_prime)
        plt.plot(X_grid,f,color[iter%n_curves])

    plt.title('Length-scale parameter: '+str(l))
```

### 4.2.2 Modeling the posterior distribution of the targets

Let us assume that we wish to make predictions of the target variable at a collection of points $\{{{\bf x}^*}^{(1)}, {{\bf x}^*}^{(2)}, \dots, {{\bf x}^*}^{(P)}\}$. Let us put together the corresponding values of $f(\cdot)$ into a column vector ${\bf f}^*$. For the particular case in which we need to make predictions at a single point, such vector would become a scalar

#### Joint distribution of ${\bf f}$ and ${\bf f}^*$

Using the generative model:

$$s^{(k)} = f({\bf x}^{(k)}) + \varepsilon^{(k)}$$

and assuming further that $f(\bf x)$ is a GP with mean 0 and covariance function $k({\bf x},{\bf x}')$, we can model the joint distribution of ${\bf f}$ and ${\bf f}^*$ as

$$\left[\begin{array}{c}{\bf f}\\{\bf f}^*\end{array}\right]~\sim~{\cal N}\left({\bf 0},\left[\begin{array}{cc}{\bf K} & {\bf K}_*^\top \\ {\bf K}_* & {\bf K}_{**} \end{array}\right]\right)$$

where:

   - ${\bf K}$ contains the covariance among the components of ${\bf f}$
   - ${\bf K}_{**}$ contains the covariance among the components of ${\bf f}^*$
   - ${\bf K}_{*}$ contains the covariance between components of ${\bf f}^*$ and ${\bf f}$

#### Joint distribution of ${\bf s}$ and ${\bf f}^*$

Since ${\bf s} = {\bf f} + {\pmb \varepsilon}$, and since the noise is independent of the values of ${\bf f}$, we have that

$$\text{Cov}\{{\bf s}\} = \text{Cov}\{{\bf f}\} + \text{Cov}\{{\pmb \varepsilon}\} = {\bf K} + \sigma_\varepsilon^2 {\bf I}$$

Next, we can also see that

$$\text{Cov}\{{\bf f}^*,{\bf s}\} = \text{Cov}\{{\bf f}^*,({\bf f}+{\pmb \varepsilon})\} = \text{Cov}\{{\bf f}^*,{\bf f}\} = {\bf K}_*$$

Therefore, the joint distribution of ${\bf s}$ and ${\bf f}^*$ is

$$\left[\begin{array}{c}{\bf s}\\{\bf f}^*\end{array}\right]~\sim~{\cal N}\left({\bf 0},\left[\begin{array}{cc}{\bf K} + \sigma_\varepsilon^2 {\bf I}& {\bf K}_*^\top \\ {\bf K}_* & {\bf K}_{**} \end{array}\right]\right)$$

#### Predictive distribution of ${\bf f}^*$ given ${\bf s}$

Using the results of Section 4.1.1, it is now straightforward to obtain the distribution of ${\bf f}^*$ given ${\bf s}$:

$${\bf f}^* | {\bf s}~\sim~{\cal N}\left({\bf K}_*[{\bf K+\sigma_\varepsilon^2 {\bf I}}]^{-1}{\bf s}, {\bf K}_{**} - {\bf K}_* [{\bf K+\sigma_\varepsilon^2 {\bf I}}]^{-1} {\bf K}_*^\top\right)$$

### 4.2.3 Gaussian Processes in practice

#### Predictive distribution computation

```python
n_points = 15
n_grid = 200
frec = 3
std_n = 0.2

#Generating the training points
X_tr = 3 * np.random.random((n_points,1)) - 0.5
S_tr = - np.cos(frec*X_tr) + std_n * np.random.randn(n_points,1)
#These will be the points where the model will be evaluated
X_grid = np.linspace(-1,3,n_grid)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(X_tr,S_tr,'b.',markersize=10)
ax.set_xlim(-.5,2.5)

#GP hyperparameters
sigma_eps = 0.1
l = .1

#Covariance matrix computation
X_tr = np.matrix(X_tr)
X_grid = np.matrix(X_grid).T

dist = spatial.distance.cdist(X_tr,X_tr,'euclidean')
dist_ss = spatial.distance.cdist(X_grid,X_grid,'euclidean')
dist_s = spatial.distance.cdist(X_grid,X_tr,'euclidean')

K = np.exp(-np.power(dist,2)/(2*l))
K_ss = np.exp(-np.power(dist_ss,2)/(2*l))
K_s = np.exp(-np.power(dist_s,2)/(2*l))

#Posterior distribution of f_star
m = K_s.dot(np.linalg.inv(K + sigma_eps**2 * np.eye(n_points))).dot(S_tr)
m = np.array(m).flatten()
Cov = K_ss - K_s.dot(np.linalg.inv(K + sigma_eps**2 * np.eye(n_points))).dot(K_s.T)
#Variance of each prediction
v = np.diagonal(Cov)

plt.plot(X_grid,m,'r',label='Predictive mean')
plt.fill_between(np.array(X_grid).flatten(), m-np.sqrt(v), m+np.sqrt(v),
         alpha=0.2, edgecolor='#1B2ACC', facecolor='#089FFF',
         linewidth=4, linestyle='dashdot', antialiased=True)
plt.show()
```

You can play modify the values of $\mbox{sigma_eps}$ and $\mbox{l}$, to see how the predictive distribution changes

## 4.3 Maximum likelihood selection of hyperparameters

As we did in the previous unit, we can find select the value of the hyperparameters to maximize the likelihood of the model. To this end, let us note that the pdf of ${\bf s}$ for given hyperparameters $\sigma_\varepsilon$ and $l$ is given by

$$p({\bf s}|\sigma_\varepsilon,l) = {\cal N}({\bf 0},{\bf K} + \sigma_\varepsilon^2 {\bf I}) = \frac{1}{(2 \pi)^{K/2}\mid{\bf K} + \sigma_\varepsilon^2 {\bf I}\mid^{1/2}} \exp\left(-\frac{1}{2}{\bf s}^\top ({\bf K} + \sigma_\varepsilon^2 {\bf I})^{-1} {\bf s}\right)$$

Instead of directly maximizing this expression, it is convenient to take the log, arriving at

$$\log p({\bf s}|\sigma_\varepsilon,l) = - \frac{K}{2}\log(2\pi) - \frac{1}{2} \log \mid{\bf K} + \sigma_\varepsilon^2 {\bf I}\mid -\frac{1}{2}{\bf s}^\top ({\bf K} + \sigma_\varepsilon^2 {\bf I})^{-1} {\bf s}$$

(be aware of the difference between $K$, the number of points in the training dataset, and ${\bf K}$, the covariance matrix of ${\bf f}$)

Selection of hyperparameters can be done with the objective of maximize this marginal likelihood, as illustrated next

```python
from math import pi

n_points = 15
frec = 3
std_n = 0.2

#Generating the training points
X_tr = 3 * np.random.random((n_points,1)) - 0.5
S_tr = - np.cos(frec*X_tr) + std_n * np.random.randn(n_points,1)

#GP hyperparameters
sigma_eps = 0.3
l = np.logspace(-5,3,100)

#Covariance matrix computation
X_tr = np.matrix(X_tr)
X_grid = np.matrix(X_grid).T

dist = spatial.distance.cdist(X_tr,X_tr,'euclidean')

log_E = []

for l_iter in l:
    K = np.exp(-np.power(dist,2)/(2*l_iter))
    log_E_iter = -(n_points/2)*np.log(2*pi) \
        - np.log(np.linalg.det(K + sigma_eps**2 * np.eye(n_points))) \
        - 0.5 * S_tr.T.dot(np.linalg.inv(K + sigma_eps**2 * np.eye(n_points))).dot(S_tr)
    log_E.append(log_E_iter[0,0])

plt.semilogx(l,log_E),
plt.xlabel('length-scale hyperparameter')
plt.ylabel('Log likelihood')

selected_l = l[np.argmax(log_E)]

```

```python
n_grid = 200

#These will be the points where the model will be evaluated
X_grid = np.linspace(-1,3,n_grid)

fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(X_tr,S_tr,'b.',markersize=10)
ax.set_xlim(-.5,2.5)

#GP hyperparameters
sigma_eps = 0.3
l = selected_l

#Covariance matrix computation
X_tr = np.matrix(X_tr)
X_grid = np.matrix(X_grid).T

dist = spatial.distance.cdist(X_tr,X_tr,'euclidean')
dist_ss = spatial.distance.cdist(X_grid,X_grid,'euclidean')
dist_s = spatial.distance.cdist(X_grid,X_tr,'euclidean')

K = np.exp(-np.power(dist,2)/(2*l))
K_ss = np.exp(-np.power(dist_ss,2)/(2*l))
K_s = np.exp(-np.power(dist_s,2)/(2*l))

#Posterior distribution of f_star
m = K_s.dot(np.linalg.inv(K + sigma_eps**2 * np.eye(n_points))).dot(S_tr)
m = np.array(m).flatten()
Cov = K_ss - K_s.dot(np.linalg.inv(K + sigma_eps**2 * np.eye(n_points))).dot(K_s.T)
#Variance of each prediction
v = np.diagonal(Cov)

plt.plot(X_grid,m,'r',label='Predictive mean')
plt.fill_between(np.array(X_grid).flatten(), m-np.sqrt(v), m+np.sqrt(v),
         alpha=0.2, edgecolor='#1B2ACC', facecolor='#089FFF',
         linewidth=4, linestyle='dashdot', antialiased=True)
plt.show()
```

```python

```


# Solving Cart-Pole with SVP

This notebook shows how to use the proposed structured value-based planning (SVP) approach to generate the state-action $Q$-value function for the classic cart-pole problem. The correctness of the solution is verified by trajectory simulations as well as the final metrics comparisons.

## Problem definition

We here focus on the cart-pole problem, which has a higher state space dimension, i.e., 4 dims.

The cart-pole problem consists in balancing in upright position a pole attached to a cart moving on a frictionless track. The cart can be controlled by means of a limited force within 10$N$ that is possible to apply both to the left or to the right of the cart. The goal is to keep the pole on the upright equilibrium position.
The physical dynamics of the system is described by the angle and the angular speed of the pole, and the position and the speed of the cart, i.e., $(\theta, \dot{\theta}, x, \dot{x})$. Denote $\tau$ as the time interval between decisions, $u$ as the force input on the cart, the dynamics can be written as

\begin{align}
    & \ddot{\theta} := \frac{g\sin{\theta} - \frac{u+ml \dot{\theta}^2 \sin{\theta}}{m_c+m} \cos{\theta}}{l\left( \frac{4}{3} - \frac{m\cos^2{\theta}}{m_c+m} \right)},\\
    & \ddot{x} := \frac{u + ml\left( \dot{\theta}^2 \sin{\theta} - \ddot{\theta}\cos{\theta}\right) }{m_c+m},\\
    & \theta := \theta + \dot{\theta}~\tau,\\
    & \dot{\theta} := \dot{\theta} + \ddot{\theta}~\tau,\\
    & x := x + \dot{x}~\tau,\\
    & \dot{x} := \dot{x} + \ddot{x}~\tau,\\
\end{align}

where $g=9.8m/s^2$ corresponds to the gravity acceleration, $m_c = 1kg$ to the mass of the cart, $m = 0.1kg$ to the mass of the pole, $l = 0.5m$ to half of the pole length, and $u$ corresponds to the force applied to the cart.

A reward function that favors the pole in an upright position, i.e., characterized by keeping the pole in vertical position between $\left|\theta\right| \le \frac{12\pi}{180}$, is expressed as

\begin{equation}
    r(\theta) = \cos^4{(15 \theta)}.
\end{equation}

In the simulation, the state space is $[-\frac{\pi}{2}, \frac{\pi}{2}]$ for $\theta$, $[-3,3]$ for $\dot{\theta}$, $[-2.4,2.4]$ for $x$ and $[-3.5,3.5]$ for $\dot{x}$. We limit the input force in $[-10,10]$ and set $\tau=0.1$.
We discretize each dimension of the state space into 10 values, and action space into 1000 values, which forms an $Q$-value function  a matrix of dimension $10000\times 1000$.

## Structured Value-based Planning (SVP)

The proposed structured value-based planning (SVP) approach is based on the $Q$-value iteration. At the $t$-th iteration, instead of a full pass over all state-action pairs:
- SVP first randomly selects a subset $\Omega$ of the state-action pairs. In particular, each state-action pair in $\mathcal{S}\times\mathcal{A}$ is observed (i.e., included in $\Omega$) independently with probability $p$.
- For each selected $(s,a)$, the intermediate $\hat{Q}(s,a)$ is computed based on the $Q$-value iteration:
    \begin{equation*}\hat{Q}(s,a) \leftarrow \sum_{s'} P(s'|s,a) \left( r(s,a) + \gamma \max_{a'} Q^{(t)}(s',a') \right),\quad\forall\:(s,a)\in\Omega.
    				\end{equation*}
- The current iteration then ends by reconstructing the full $Q$ matrix with matrix estimation, from the set of observations in $\Omega$. That is, $Q^{(t+1)}=\textrm{ME}\big(\{\hat{Q}(s,a)\}_{(s,a)\in\Omega}\big).$

Overall, each iteration reduces the computation cost by roughly $1-p$.

Through SVP, we can compute the final state-action $Q$-value function.
To obtain the optimal policy for state $s$, we compute

\begin{align*}
    \pi^{\star} \left(s\right) = \mbox{argmax}_{a \in \mathcal{A}} Q^{\star}\left(s, a\right).
\end{align*}

### Generate state-action value function with SVP

```julia
push!(LOAD_PATH, ".")
using MDPs, CartPole, Printf, LinearAlgebra
mdp = MDP(state_space(), action_space(), transition, reward)
__init__()
policy = value_iteration(mdp, true, "../data/qcp_otf_0.2.csv", true)
print("")  # suppress output
```

### Visualize policy as a heat map

Note that the cart-pole problem is a 4-dimensional task, and the policy heat map should have 4 dims (but hard to visualize). Since the metric is the angle deviation, we here only plot the first two dims (i.e., the `angle` and `angular speed`) to visualize the heat map.

```julia
viz_policy(mdp, policy, "SVP policy (20% observed)", true, "cp/policy_cp_0.2.tex")
```

### Verify correctness
Simulate and visualize trajectory from initial state `[angle, angular_speed, position, x_speed]`.

```julia
ss, as = simulate(mdp, policy, [-0.5, 0.0, 0.0, 0.0])
viz_trajectory(ss, as, "SVP trajectory (20%)", "SVP input (20%)", true, "cp/traj_cp_0.2.tex")

```

```julia
nsim = 2000
deviation = 0
for sim = 1:nsim
    state = [rand(THETAMIN:0.001 * (THETAMAX - THETAMIN):THETAMAX),
             rand(THETADOTMIN:0.001 * (THETADOTMAX - THETADOTMIN):THETADOTMAX),
             rand(XMIN:0.001 * (XMAX - XMIN):XMAX),
             rand(XDOTMIN:0.001 * (XDOTMAX - XDOTMIN):XDOTMAX)]
    traj, _ = simulate(mdp, policy, copy(state))
    deviation += vecnorm(traj[51:end, 1])
end # for sim
deviation /= nsim
@printf("average deviation: %.3f\n", deviation)
```

    ┌ Warning: `vecnorm` is deprecated, use `norm` instead.
    │   caller = top-level scope at In[4]:9
    └ @ Core ./In[4]:9

    average deviation: 10.076

```julia

```


```python
import math
import numpy as np
from sympy import *
t, s, k = symbols('t, s, k')
```

```python
# Defino los métodos de suma de rectángulos - izquierda
def _Riemman_izq(Func, limA, limB, numI):
    """ Método de la Bisección para encontrar raíces en un intervalo.

        ## Parámetros:
            Func (function) : función que depende de una variable.
            limA (function) : derivada de la función.
            limB (int)      : semilla de la solución.
            numI (int)      : número máximo de iteraciones.

        ## Devoluciones:
            Area (float)       : valor de area encontrado.
    """
    Δx = (limB - limA) / numI
    Alturas = 0
    for i in range(numI):
        Alturas += Func(limA + i * Δx)

    Area = Δx * Alturas
    return Area

# Defino los métodos de suma de rectángulos - izquierda
def _Riemman_der(Func, limA, limB, numI):
    """ Método de la Bisección para encontrar raíces en un intervalo.

        ## Parámetros:
            Func (function) : función que depende de una variable.
            limA (function) : derivada de la función.
            limB (int)      : semilla de la solución.
            numI (int)      : número máximo de iteraciones.

        ## Devoluciones:
            Area (float)       : valor de area encontrado.
    """
    Δx = (limB - limA) / numI
    Alturas = 0
    for i in range(numI):
        Alturas += Func(limB - i * Δx)

    Area = Δx * Alturas
    return Area

# Defino los métodos de suma de rectángulos - izquierda
def _Riemman_med(Func, limA, limB, numI):
    """ Método de la Bisección para encontrar raíces en un intervalo.

        ## Parámetros:
            Func (function) : función que depende de una variable.
            limA (function) : derivada de la función.
            limB (int)      : semilla de la solución.
            numI (int)      : número máximo de iteraciones.

        ## Devoluciones:
            Area (float)       : valor de area encontrado.
    """
    Δx = (limB - limA) / numI
    Alturas = 0
    for i in range(numI):
        Alturas += Func(limA + (2*i + 1)/2 * Δx)

    Area = Δx * Alturas
    return Area
```

```python
# 1
A = [(1, 3), (0, 3)]
b = [14, 21]
x = np.matmul(np.linalg.inv(A), np.transpose(b))
x1 = x[0]
x2 = x[1]
ft = 2*t
Ft = Integral(ft, t)
Ft_ab = Integral(ft, (t, x1, x2))

# Evaluar la integral
Rt = Ft.doit()
Rt_ab = Ft_ab.doit()
display(Eq(Ft, Rt))
display(Ft_ab, Rt_ab)

```

$\displaystyle \int 2 t\, dt = t^{2}$

$\displaystyle \int\limits_{-7.0}^{7.0} 2 t\, dt$

$\displaystyle 0$

```python
# 2
A = [(1,  2),  (-3,  4)]
b = [14,  18]
M11 = 4
detA = np.linalg.det(A)
Zt = Integral(s, (s, t, M11))
funtion_Zt = Zt.doit()
Zt1 = detA * funtion_Zt.subs(t, 1)
display(Zt, funtion_Zt, Zt1)

# math.floor(Z11)
```

$\displaystyle \int\limits_{t}^{4} s\, ds$

$\displaystyle 8 - \frac{t^{2}}{2}$

$\displaystyle 75.0$

```python
# 3
Vr = math.e
Va = 2.72
er = 100 * abs(Va - Vr)/Vr
print(f"{er:7.1e}")

```

    6.3e-02

```python
# 5
F1 = lambda x: x**2
_Riemman_izq(F1, 0, 2, 3)
```

    1.4814814814814814

```python
# 6
F2 = lambda x: (x**3)**2
Va = _Riemman_der(F2, 0, 4, 5)
print(4250 - Va)
print(4500 - Va)
```

    -52.30732800000169
    197.6926719999983

```python
# 7
F3 = lambda k: math.e**k
vr = Integral(math.e**k, (k, 0, 3)).doit()
va = _Riemman_med(F3, 0, 3, 2)
er = 100 * abs(vr-va)/va
print(vr, va, er)
```

    19.0855369231877 17.407103779456797 9.64223092477733

```python

```


\title{myHDL Implementation of a CIC Filter}
\author{Steven K Armour}
\maketitle

Table of Contents

1  References

2  Preliminaries

3  To improve:

4  Delay

4.1  Theory

4.2  myHDL implementation

4.3  myHDL Testing

4.4  To Verilog

5  Comb

5.1  Theory

5.2  myHDL implementation

5.3  myHDL Testing

5.4  To Verilog

6  Integrator

6.1  Theory

6.2  myHDL  implementation

6.3  myHDL Testing

6.4  To Verilog

7  Decimator

7.1  Theory

7.2  myHDL implementation

7.3  Testing

7.4  To Verilog

8  Interpolator

8.1  Theory

8.2  myHDL implementation

8.3  muHDL Testing

8.4  To Verilog

9  Pass Through

9.1  myHDL implementation

9.2  myHDL Testing

9.3  To Verilog

10  Complete CIC Filter

10.1  Theory

11  myHDL implementation

11.1  myHDL Testing

11.1.1  (Need to come up with a test bench)

11.2  To Verilog

```python
import numpy as np
np.seterr(divide='ignore', invalid='ignore')

import pandas as pd
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
#import plotly.plotly as py
#import plotly.graph_objs as go
from sympy import *
from sympy import S; Zero=S.Zero
init_printing()

import scipy.signal as sig
%matplotlib notebook

import ipywidgets as widg

from myhdl import *
from myhdlpeek import Peeker
```

# References
Christopher Felton who designed the original `myHDL` CIC filter found here https://github.com/jandecaluwe/site-myhdl-retired/blob/master/_ori/pages/projects/gciccomplete.txt

West Coast DSP blog article on CIC filters
https://westcoastdsp.wordpress.com/2015/09/07/cascaded-integrator-comb-filters/

[Demystifying Hogenauer filters ](http://www.signalpro.biz/cic1.pdf)

# Preliminaries

Target Architecture size

```python
BitWidth=32
```

Code to read back the generated `Verilog`(`VHDL`)  from `myHDL` back into the Jupyter Notebook

```python
#helper  functions to read in the .v and .vhd generated files into python
def VerilogTextReader(loc, printresult=True):
    with open(f'{loc}.v', 'r') as vText:
        VerilogText=vText.read()
    if printresult:
        print(f'***Verilog modual from {loc}.v***\n\n', VerilogText)
    return VerilogText

def VHDLTextReader(loc, printresult=True):
    with open(f'{loc}.vhd', 'r') as vText:
        VerilogText=vText.read()
    if printresult:
        print(f'***VHDL modual from {loc}.vhd***\n\n', VerilogText)
    return VerilogText
```

*Z-Transform* preliminary math and *Z-Plane* graphing code

```python
z, r, DisAng=symbols('z, r, Omega')
zFunc=Eq(z, r*exp(1j*DisAng)); zFunc
```

```python
zFuncN=lambdify((r, DisAng), zFunc.rhs, dummify=False)

zr=np.arange(-1.5, 1.5+.03, .03); zi=np.copy(zr)
zR, zI=np.meshgrid(zr, zi)
zN=zR+1j*zI

rN=1.0
AngThetaN=np.arange(0, 1+.005, .005)*2*np.pi
zAtR1=zFuncN(rN, AngThetaN)
```

```python
%matplotlib notebook

def Zplot(zR, zI, HzNMag, HzAtR1NMag, HzNPhase, HzAtR1NPhase, title):

    fig = plt.figure()

    #plot the z space mag
    axZmag = fig.add_subplot(221, projection='3d')
    Mags=axZmag.plot_surface(zR, zI, HzNMag, cmap=plt.get_cmap('tab20'))
    axZmag.plot(np.real(zAtR1), np.imag(zAtR1), HzAtR1NMag, 'r-', label='r=1')
    axZmag.set_xlabel('Re'); axZmag.set_ylabel('Im'); axZmag.set_zlabel('Mag')
    axZmag.legend(loc='best')
    fig.colorbar(Mags)

    #plot the z space phase
    axZph = fig.add_subplot(222, projection='3d')
    Phase=axZph.plot_surface(zR, zI, HzNPhase, cmap=plt.get_cmap('tab20'))
    axZph.plot(np.real(zAtR1), np.imag(zAtR1), HzAtR1NPhase, 'r-', label='r=1')
    axZph.set_xlabel('Re'); axZph.set_ylabel('Im'); axZph.set_zlabel('Phase')
    axZph.legend(loc='best')
    fig.colorbar(Phase)

    axBodeM=fig.add_subplot(212)
    Mline=axBodeM.plot(AngThetaN, HzAtR1NMag, label='FTMag')
    axBodeM.set_ylabel('Mag')

    axBodeP=axBodeM.twinx()
    Pline=axBodeP.plot(AngThetaN, np.rad2deg(HzAtR1NPhase), 'g--', label='FTPhase')
    axBodeP.set_ylabel('Phase [deg]')
    axBodeP.set_xlabel('Ang')

    lns = Mline+Pline
    labs = [l.get_label() for l in lns]
    axBodeP.legend(lns, labs, loc='best')

    fig.suptitle(title)
    fig.show()

```

# To improve:
1. Come up with good test bench for CIC
2. Record all test to DataFrame and z-transform results

3. add widgets

4. Clean up theory

# Delay

## Theory

```python
M=symbols('M')
DelayH=z**(-M); DelayH
```

```python
DelaySupH=simplify(DelayH.subs(zFunc.lhs, zFunc.rhs)); DelaySupH
```

```python
DelaySupH=simplify(DelaySupH.subs(r, 1)); DelaySupH
```

```python
DelayHN=lambdify((z, M), DelayH, dummify=False)
Mvalue=2
HzN=DelayHN(zN, M=Mvalue); HzN.shape
HzNMag=np.abs(HzN); HzNPhase=np.angle(HzN)

HAtR1=zFuncN(rN, AngThetaN)
HzAtR1N=DelayHN(zAtR1, M=Mvalue)
HzAtR1NMag=np.abs(HzAtR1N); HzAtR1NPhase=np.angle(HzAtR1N)
```

```python
def DelayExplorer(M=1):
    Mvalue=M

    HzN=DelayHN(zN, M=Mvalue); HzN.shape
    HzNMag=np.abs(HzN); HzNPhase=np.angle(HzN)

    HAtR1=zFuncN(rN, AngThetaN)
    HzAtR1N=DelayHN(zAtR1, M=Mvalue)
    HzAtR1NMag=np.abs(HzAtR1N); HzAtR1NPhase=np.angle(HzAtR1N)
    Zplot(zR, zI, HzNMag, HzAtR1NMag, HzNPhase, HzAtR1NPhase,
          f'z Delay order M={M}')
```

```python
# will add widgets for interative later
DelayExplorer(M=1)
```

    <IPython.core.display.Javascript object>

## myHDL implementation

```python
def Delay(x, y, ena_in, clk):
    '''
        Z delay bulding block for a CIC filter

        Inputs:
            x (data): the x(n) data in feed
            ------------------------
            ena_in (bool): the exstiror calc hold input. calc is done only if
            `ena_in` is True

            clk(bool): clock feed
            rst(bool): reset feed

        Outputs:
            y (data):  the y(n+1) output of y(n+1)=x(n)

    '''
    @always(clk. posedge)
    def logic():
        if ena_in:
            y.next=x
    return logic
```

## myHDL Testing

```python
Peeker.clear()

x=Signal(modbv(0)[BitWidth:]); Peeker(x, 'x')
y=Signal(modbv(0)[BitWidth:]); Peeker(y, 'y')

ena_in, clk=[Signal(bool(0)) for  _ in range(2)]
Peeker(ena_in, 'ena_in'); Peeker(clk, 'clk')

DUT=Delay(x, y, ena_in, clk)

DateCol=pd.DataFrame(columns=['x', 'y', 'ena_in'])

def Delay_TB():

    @always(delay(1))  ## delay in nano seconds
    def clkGen():
        clk.next = not clk

    @instance
    def stimulus():
        Tested_ena=False
        count=0
        while 1:

            if Tested_ena==False and count<=2:
                print(f'Tested_ena: {Tested_ena}, count:{count}')
            elif Tested_ena==False and count>2:
                print(f'Tested_ena: {Tested_ena}, count:{count}')

                ena_in.next=True
                Tested_ena=True
                x.next=0

            if Tested_ena  and count>2:
                x.next=x+1

            if count> 2*BitWidth:
                raise StopSimulation

            DateCol.loc[count]=[int(x),int(y), int(ena_in)]

            count+=1
            yield clk.posedge

    return instances()
sim = Simulation(DUT, Delay_TB(), *Peeker.instances()).run()
```

    Tested_ena: False, count:0
    Tested_ena: False, count:1
    Tested_ena: False, count:2
    Tested_ena: False, count:3

```python
Peeker.to_wavedrom(start_time=0, stop_time=40, tock=True)
```

## To Verilog

```python
x=Signal(modbv(0)[BitWidth:])
y=Signal(modbv(0)[BitWidth:])
ena_in, clk=[Signal(bool(0)) for  _ in range(2)]
toVerilog(Delay, x, y, ena_in, clk)
VerilogTextReader('Delay');
```

    ***Verilog modual from Delay.v***

     // File: Delay.v
    // Generated by MyHDL 0.9.0
    // Date: Mon Feb 26 19:21:50 2018

    `timescale 1ns/10ps

    module Delay (
        x,
        y,
        ena_in,
        clk
    );
    // Z delay bulding block for a CIC filter
    //
    // Inputs:
    //     x (data): the x(n) data in feed
    //     ------------------------
    //     ena_in (bool): the exstiror calc hold input. calc is done only if
    //     `ena_in` is True
    //
    //     clk(bool): clock feed
    //     rst(bool): reset feed
    //
    // Outputs:
    //     y (data):  the y(n+1) output of y(n+1)=x(n)
    //

    input [31:0] x;
    output [31:0] y;
    reg [31:0] y;
    input ena_in;
    input clk;

    always @(posedge clk) begin: DELAY_LOGIC
        if (ena_in) begin
            y <= x;
        end
    end

    endmodule

# Comb

## Theory

```python
CombH=1-z**(-M); CombH
```

```python
CombSupH=simplify(CombH.subs(zFunc.lhs, zFunc.rhs)); CombSupH
```

```python
CombSupH=simplify(CombSupH.subs(r, 1)); CombSupH
```

```python
CombHN=lambdify((z, M), CombH, dummify=False)
def CombEx(M=2):
    Mvalue=M
    HzN=CombHN(zN, M=Mvalue); HzN.shape
    HzNMag=np.abs(HzN); HzNPhase=np.angle(HzN)

    HAtR1=zFuncN(rN, AngThetaN)
    HzAtR1N=DelayHN(zAtR1, M=Mvalue)
    HzAtR1NMag=np.abs(HzAtR1N); HzAtR1NPhase=np.angle(HzAtR1N)

    Zplot(zR, zI, HzNMag, HzAtR1NMag, HzNPhase, HzAtR1NPhase,
          f'Comb of Order M={Mvalue}')
CombEx(M=2)
```

    <IPython.core.display.Javascript object>

## myHDL implementation

```python
def Comb(x, y, ena_in, ena_out, clk, rst, M=2):
    '''
        the comb section of  a CIC filter relaying on `Delay` to create
        the Z dealy blocks

        Inputs:
            x (data): the x(n) data in feed
            ------------------------
            ena_in (bool): the exstiror calc hold input. calc is done only if
            `ena_in` is True

            clk(bool): clock feed
            rst(bool): reset feed

        Outputs:
            y (data):  the y(n) output of y(n)=y(n-1)+x(n)
            ----------------------
            ena_out: the exstior calc hold output. will be false if
            `ena_in` is False
        Parm:
            M: the nuumber of Z delays for this comb section
    '''

    #stage the the Zdelays
    Zdelay_i=[None for i in range(M)]

    #Parmters for sizeing the 2's comp interdaley wires
    WordLen_1=len(x)-1
    WireGuage=2**WordLen_1

    # Create the wiring between the delays
    Zwire_ij=[Signal(modbv(0, min=-WireGuage, max=WireGuage)) for j in range(M)]

    #instainsate and wire togather the Zdelays
    for i in range(M):
        if i==0:
            Zdelay_i[i]=Delay(x, Zwire_ij[i], ena_in, clk)
        else:
            Zdelay_i[i]=Delay(Zwire_ij[i-1], Zwire_ij[i], ena_in, clk)

    #make the last delay output unieq x(M-1)
    subx=Zwire_ij[M-1]

    @always(clk.posedge)
    def logc():
        if rst:
            y.next=0
        else:
            if ena_in:
                #y=x-x(M-1)
                y.next=x-subx
                ena_out.next=True
            else:
                ena_out.next=False
    return instances()
```

## myHDL Testing

```python
Peeker.clear()

x=Signal(modbv(1)[BitWidth:]); Peeker(x, 'x')
y=Signal(modbv(5)[BitWidth:]); Peeker(y, 'y')

ena_in, ena_out, clk, rst=[Signal(bool(0)) for  _ in range(4)]
Peeker(ena_in, 'ena_in'); Peeker(ena_out, 'ena_out'); Peeker(clk, 'clk'); Peeker(rst, 'rst')

DUT=Comb(x, y, ena_in, ena_out, clk, rst, M=2)

def Comb_TB():

    @always(delay(1))  ## delay in nano seconds
    def clkGen():
        clk.next = not clk

    @instance
    def stimulus():
        Tested_ena=False
        Tested_rst=False
        count=0
        while 1:

            if Tested_ena==False and count<=2:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')
            elif Tested_ena==False and count>2:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')

                ena_in.next=True
                Tested_ena=True

            if Tested_ena and Tested_rst==False:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')

                rst.next=True
                Tested_rst=True
            elif Tested_ena and Tested_rst and  count<=4:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')

                rst.next=False
                Tested_rst=True
                x.next=1

            if Tested_ena and Tested_rst and count>4:
                x.next=2*(x+1)

            if count> 2*BitWidth:
                raise StopSimulation
            count+=1
            yield clk.posedge

    return instances()
sim = Simulation(DUT, Comb_TB(), *Peeker.instances()).run()

```

    Tested_ena: False, Tested_rst:False, count:0
    Tested_ena: False, Tested_rst:False, count:1
    Tested_ena: False, Tested_rst:False, count:2
    Tested_ena: False, Tested_rst:False, count:3
    Tested_ena: True, Tested_rst:False, count:3
    Tested_ena: True, Tested_rst:True, count:4

```python
Peeker.to_wavedrom(start_time=0, stop_time=40, tock=True)
```

## To Verilog

```python
x=Signal(modbv(1)[BitWidth:])
y=Signal(modbv(5)[BitWidth:])

ena_in, ena_out, clk, rst=[Signal(bool(0)) for  _ in range(4)]
toVerilog(Comb, x, y, ena_in, ena_out, clk, rst, M=2)
VerilogTextReader('Comb');
```

    ***Verilog modual from Comb.v***

     // File: Comb.v
    // Generated by MyHDL 0.9.0
    // Date: Mon Feb 26 19:22:31 2018

    `timescale 1ns/10ps

    module Comb (
        x,
        y,
        ena_in,
        ena_out,
        clk,
        rst
    );
    // the comb section of  a CIC filter relaying on `Delay` to create
    // the Z dealy blocks
    //
    // Inputs:
    //     x (data): the x(n) data in feed
    //     ------------------------
    //     ena_in (bool): the exstiror calc hold input. calc is done only if
    //     `ena_in` is True
    //
    //     clk(bool): clock feed
    //     rst(bool): reset feed
    //
    // Outputs:
    //     y (data):  the y(n) output of y(n)=y(n-1)+x(n)
    //     ----------------------
    //     ena_out: the exstior calc hold output. will be false if
    //     `ena_in` is False
    // Parm:
    //     M: the nuumber of Z delays for this comb section

    input [31:0] x;
    output [31:0] y;
    reg [31:0] y;
    input ena_in;
    output ena_out;
    reg ena_out;
    input clk;
    input rst;

    reg signed [31:0] subx;
    reg signed [31:0] Zdelay_i_1_x;

    always @(posedge clk) begin: COMB_ZDELAY_I_0_LOGIC
        if (ena_in) begin
            Zdelay_i_1_x <= x;
        end
    end

    always @(posedge clk) begin: COMB_ZDELAY_I_1_LOGIC
        if (ena_in) begin
            subx <= Zdelay_i_1_x;
        end
    end

    always @(posedge clk) begin: COMB_LOGC
        if (rst) begin
            y <= 0;
        end
        else begin
            if (ena_in) begin
                y <= ($signed({1'b0, x}) - subx);
                ena_out <= 1'b1;
            end
            else begin
                ena_out <= 1'b0;
            end
        end
    end

    endmodule

# Integrator

## Theory

```python
IntegratorH=1/(1-z**-1); IntegratorH
```

```python
IntegratorSupH=simplify(IntegratorH.subs(zFunc.lhs, zFunc.rhs))
IntegratorSupH
```

```python
IntegratorSupH=simplify(IntegratorSupH.subs(r, 1))
IntegratorSupH
```

```python
IntegratorHN=lambdify(z, IntegratorH, dummify=False)
HzN=IntegratorHN(zN); HzN.shape
HzNMag=np.abs(HzN); HzNPhase=np.angle(HzN)

HAtR1=zFuncN(rN, AngThetaN)
HzAtR1N=IntegratorHN(zAtR1)
HzAtR1NMag=np.abs(HzAtR1N); HzAtR1NPhase=np.angle(HzAtR1N)

Zplot(zR, zI, HzNMag, HzAtR1NMag, HzNPhase, HzAtR1NPhase, 'Integrator')
```

    <IPython.core.display.Javascript object>

## myHDL  implementation

```python
def Integrator(x, y, ena_in, ena_out, clk, rst):
    '''
        Simple Integrator/ Accumultor with exstior hold contorls as part
        of the building blocks of a CIC filter

        Inputs:
            x (data): the x(n) data in feed
            ------------------------
            ena_in (bool): the exstiror calc hold input. calc is done only if
            `ena_in` is True

            clk(bool): clock feed
            rst(bool): reset feed

        Outputs:
            y (data):  the y(n) output of y(n)=y(n-1)+x(n)
            ----------------------
            ena_out: the exstior calc hold output. will be false if
            `ena_in` is False
    '''
    @always(clk.posedge)
    def logic():
        if rst:
            y.next=0
        else:
            if ena_in:
                #y(n)=y(n-1)+x(n)
                y.next=y+x
                ena_out.next=True
            else:
                ena_out.next=False

    return logic

```

## myHDL Testing

```python
Peeker.clear()

x=Signal(modbv(1)[BitWidth:]); Peeker(x, 'x')
y=Signal(modbv(5)[BitWidth:]); Peeker(y, 'y')

ena_in, ena_out, clk, rst=[Signal(bool(0)) for  _ in range(4)]
Peeker(ena_in, 'ena_in'); Peeker(ena_out, 'ena_out'); Peeker(clk, 'clk'); Peeker(rst, 'rst')

DUT=Integrator(x, y, ena_in, ena_out, clk, rst)

def Integrator_TB():

    @always(delay(1))  ## delay in nano seconds
    def clkGen():
        clk.next = not clk

    @instance
    def stimulus():
        Tested_ena=False
        Tested_rst=False
        count=0
        while 1:

            if Tested_ena==False and count<=2:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')
            elif Tested_ena==False and count>2:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')

                ena_in.next=True
                Tested_ena=True

            if Tested_ena and Tested_rst==False:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')

                rst.next=True
                Tested_rst=True
            elif Tested_ena and Tested_rst and  count<=4:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')

                rst.next=False
                Tested_rst=True
                x.next=0

            if Tested_ena and Tested_rst and count>4:
                x.next=x+1

            if count> 2*BitWidth:
                raise StopSimulation
            count+=1
            yield clk.posedge

    return instances()
sim = Simulation(DUT, Integrator_TB(), *Peeker.instances()).run()

```

    Tested_ena: False, Tested_rst:False, count:0
    Tested_ena: False, Tested_rst:False, count:1
    Tested_ena: False, Tested_rst:False, count:2
    Tested_ena: False, Tested_rst:False, count:3
    Tested_ena: True, Tested_rst:False, count:3
    Tested_ena: True, Tested_rst:True, count:4

```python
Peeker.to_wavedrom(start_time=0, stop_time=40, tock=True)
```

## To Verilog

```python
x=Signal(modbv(1)[BitWidth:])
y=Signal(modbv(5)[BitWidth:])

ena_in, ena_out, clk, rst=[Signal(bool(0)) for  _ in range(4)]

toVerilog(Integrator, x, y, ena_in, ena_out, clk, rst)
VerilogTextReader('Integrator');
```

    ***Verilog modual from Integrator.v***

     // File: Integrator.v
    // Generated by MyHDL 0.9.0
    // Date: Mon Feb 26 19:24:35 2018

    `timescale 1ns/10ps

    module Integrator (
        x,
        y,
        ena_in,
        ena_out,
        clk,
        rst
    );
    // Simple Integrator/ Accumultor with exstior hold contorls as part
    // of the building blocks of a CIC filter
    //
    // Inputs:
    //     x (data): the x(n) data in feed
    //     ------------------------
    //     ena_in (bool): the exstiror calc hold input. calc is done only if
    //     `ena_in` is True
    //
    //     clk(bool): clock feed
    //     rst(bool): reset feed
    //
    // Outputs:
    //     y (data):  the y(n) output of y(n)=y(n-1)+x(n)
    //     ----------------------
    //     ena_out: the exstior calc hold output. will be false if
    //     `ena_in` is False

    input [31:0] x;
    output [31:0] y;
    reg [31:0] y;
    input ena_in;
    output ena_out;
    reg ena_out;
    input clk;
    input rst;

    always @(posedge clk) begin: INTEGRATOR_LOGIC
        if (rst) begin
            y <= 0;
        end
        else begin
            if (ena_in) begin
                y <= (y + x);
                ena_out <= 1'b1;
            end
            else begin
                ena_out <= 1'b0;
            end
        end
    end

    endmodule

    ** ToVerilogWarning: Output port is read internally: y

# Decimator

## Theory

$$y(n)=x(Rn)$$

```python
R, k=symbols('R, k')
X=Function('X')(z); X
```

```python
Decimator=summation(X.subs(z, z**(1/R) *exp(2j*pi*k/R)), (k, 0, R-1))/R
Decimator
```

```python
Decimator=simplify(Decimator.subs(zFunc.lhs, zFunc.rhs))
Decimator
```

```python
Decimator.subs(r, 1)
```

## myHDL implementation

```python
 def Decimator(x, y, ena_in, ena_out, clk, rst, R=8):
    '''
        A decimation (down sampling) section for a CIC filter

        Inputs:
            x (data): the x(n) data in feed
            ------------------------
            ena_in (bool): the exstiror calc hold input. calc is done only if
            `ena_in` is True

            clk(bool): clock feed
            rst(bool): reset feed

        Outputs:
            y (data):  the y(n) output
            ----------------------
            ena_out: the exstior calc hold output. will be false if
            `ena_in` is False
        Parm:
            R: the decimation ratio
    '''
    countSize=2**np.ceil(np.log2(R))
    count=Signal(intbv(0, max=countSize, min=0))

    @always(clk.posedge)
    def PassControl():
        if rst:
            y.next=0
        else:
            if count==0:
                y.next=x
                ena_out.next=True
            else:
                y.next=0
                ena_out.next=False

    @always(clk.posedge)
    def CountControl():
        if rst:
            count.next=0
        else:
            if count==R-1:
                count.next=0
            else:
                count.next=count+1

    return instances()

```

## Testing

```python
Peeker.clear()

x=Signal(modbv(1)[BitWidth:]); Peeker(x, 'x')
y=Signal(modbv(0)[BitWidth:]); Peeker(y, 'y')

ena_in, ena_out, clk, rst=[Signal(bool(0)) for  _ in range(4)]
Peeker(ena_in, 'ena_in'); Peeker(ena_out, 'ena_out'); Peeker(clk, 'clk'); Peeker(rst, 'rst')

DUT=Decimator(x, y, ena_in, ena_out, clk, rst, R=2)

def Integrator_TB():

    @always(delay(1))  ## delay in nano seconds
    def clkGen():
        clk.next = not clk

    @instance
    def stimulus():
        Tested_ena=False
        Tested_rst=False
        count=0
        while 1:

            if Tested_ena==False and count<=2:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')
            elif Tested_ena==False and count>2:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')

                ena_in.next=True
                Tested_ena=True

            if Tested_ena and Tested_rst==False:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')

                rst.next=True
                Tested_rst=True
            elif Tested_ena and Tested_rst and  count<=4:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')

                rst.next=False
                Tested_rst=True
                x.next=1

            if Tested_ena and Tested_rst and count>4:
                x.next=x+1

            if count> 2*BitWidth:
                raise StopSimulation
            count+=1
            yield clk.posedge

    return instances()
sim = Simulation(DUT, Integrator_TB(), *Peeker.instances()).run()

```

    Tested_ena: False, Tested_rst:False, count:0
    Tested_ena: False, Tested_rst:False, count:1
    Tested_ena: False, Tested_rst:False, count:2
    Tested_ena: False, Tested_rst:False, count:3
    Tested_ena: True, Tested_rst:False, count:3
    Tested_ena: True, Tested_rst:True, count:4

```python
Peeker.to_wavedrom(start_time=0, stop_time=40, tock=True)
```

## To Verilog

```python
x=Signal(modbv(1)[BitWidth:])
y=Signal(modbv(0)[BitWidth:])

ena_in, ena_out, clk, rst=[Signal(bool(0)) for  _ in range(4)]

toVerilog(Decimator, x, y, ena_in, ena_out, clk, rst, R=2)
VerilogTextReader('Decimator');
```

    ***Verilog modual from Decimator.v***

     // File: Decimator.v
    // Generated by MyHDL 0.9.0
    // Date: Mon Feb 26 19:24:54 2018

    `timescale 1ns/10ps

    module Decimator (
        x,
        y,
        ena_in,
        ena_out,
        clk,
        rst
    );
    // A decimation (down sampling) section for a CIC filter
    //
    // Inputs:
    //     x (data): the x(n) data in feed
    //     ------------------------
    //     ena_in (bool): the exstiror calc hold input. calc is done only if
    //     `ena_in` is True
    //
    //     clk(bool): clock feed
    //     rst(bool): reset feed
    //
    // Outputs:
    //     y (data):  the y(n) output
    //     ----------------------
    //     ena_out: the exstior calc hold output. will be false if
    //     `ena_in` is False
    // Parm:
    //     R: the decimation ratio

    input [31:0] x;
    output [31:0] y;
    reg [31:0] y;
    input ena_in;
    output ena_out;
    reg ena_out;
    input clk;
    input rst;

    reg [0:0] count;

    always @(posedge clk) begin: DECIMATOR_COUNTCONTROL
        if (rst) begin
            count <= 0;
        end
        else begin
            if (($signed({1'b0, count}) == (2 - 1))) begin
                count <= 0;
            end
            else begin
                count <= (count + 1);
            end
        end
    end

    always @(posedge clk) begin: DECIMATOR_PASSCONTROL
        if (rst) begin
            y <= 0;
        end
        else begin
            if ((count == 0)) begin
                y <= x;
                ena_out <= 1'b1;
            end
            else begin
                y <= 0;
                ena_out <= 1'b0;
            end
        end
    end

    endmodule

    ** ToVerilogWarning: Port is not used: ena_in

# Interpolator

## Theory

$$y(n)=\sum_{k=-\infty}^{\infty}x(k)\delta(n-kR)$$

```python
InterpolatorTheory=X.subs(z, exp(1j*DisAng*R)); InterpolatorTheory
```

## myHDL implementation

```python
 def Interpolator(x, y, ena_in, ena_out,  clk, rst, R=8):
    '''
        A interpolation section for a CIC filter

        Inputs:
            x (data): the x(n) data in feed
            ------------------------
            ena_in (bool): the exstiror calc hold input. calc is done only if
            `ena_in` is True

            clk(bool): clock feed
            rst(bool): reset feed

        Outputs:
            y (data):  the y(n) output of y(n)=y(n-1)+x(n)
            ----------------------
            ena_out: the exstior calc hold output. will be false if
            `ena_in` is False
        Parm:
            R: the up-sampleing ratio
    '''
    countSize=2**np.ceil(np.log2(R))
    count=Signal(intbv(0, max=countSize, min=0))

    @always(clk.posedge)
    def PassControl():
        if rst:
            y.next=0
        else:
            if ena_in:
                y.next=x
                ena_out.next=True

            elif count>0:
                y.next=0
                ena_out.next=True

            else:
                y.next=0
                ena_out.next=False

    @always(clk.posedge)
    def CountControl():
        if rst:
            count.next=0
        else:
            if ena_in:
                count.next=R-1
            elif count>0:
                count.next=count+1

    return instances()

```

## muHDL Testing

```python
Peeker.clear()

x=Signal(modbv(1)[BitWidth:]); Peeker(x, 'x')
y=Signal(modbv(0)[BitWidth:]); Peeker(y, 'y')

ena_in, ena_out, clk, rst=[Signal(bool(0)) for  _ in range(4)]
Peeker(ena_in, 'ena_in'); Peeker(ena_out, 'ena_out'); Peeker(clk, 'clk'); Peeker(rst, 'rst')

DUT=Interpolator(x, y, ena_in, ena_out, clk, rst, R=2)

def Integrator_TB():

    @always(delay(1))  ## delay in nano seconds
    def clkGen():
        clk.next = not clk

    @instance
    def stimulus():
        Tested_ena=False
        Tested_rst=False
        count=0
        while 1:

            if Tested_ena==False and count<=2:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')
            elif Tested_ena==False and count>2:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')

                ena_in.next=True
                Tested_ena=True

            if Tested_ena and Tested_rst==False:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')

                rst.next=True
                Tested_rst=True
            elif Tested_ena and Tested_rst and  count<=4:
                print(f'Tested_ena: {Tested_ena}, Tested_rst:{Tested_rst}, count:{count}')

                rst.next=False
                Tested_rst=True
                x.next=1

            if Tested_ena and Tested_rst and count>4:
                x.next=x+1

            if count> 2*BitWidth:
                raise StopSimulation
            count+=1
            yield clk.posedge

    return instances()
sim = Simulation(DUT, Integrator_TB(), *Peeker.instances()).run()

```

    Tested_ena: False, Tested_rst:False, count:0
    Tested_ena: False, Tested_rst:False, count:1
    Tested_ena: False, Tested_rst:False, count:2
    Tested_ena: False, Tested_rst:False, count:3
    Tested_ena: True, Tested_rst:False, count:3
    Tested_ena: True, Tested_rst:True, count:4

```python
Peeker.to_wavedrom(start_time=0, stop_time=40, tock=True)
```

## To Verilog

```python
x=Signal(modbv(1)[BitWidth:])
y=Signal(modbv(0)[BitWidth:])

ena_in, ena_out, clk, rst=[Signal(bool(0)) for  _ in range(4)]

toVerilog(Interpolator, x, y, ena_in, ena_out, clk, rst, R=2)
VerilogTextReader('Interpolator');
```

    ***Verilog modual from Interpolator.v***

     // File: Interpolator.v
    // Generated by MyHDL 0.9.0
    // Date: Mon Feb 26 19:25:18 2018

    `timescale 1ns/10ps

    module Interpolator (
        x,
        y,
        ena_in,
        ena_out,
        clk,
        rst
    );
    // A interpolation section for a CIC filter
    //
    // Inputs:
    //     x (data): the x(n) data in feed
    //     ------------------------
    //     ena_in (bool): the exstiror calc hold input. calc is done only if
    //     `ena_in` is True
    //
    //     clk(bool): clock feed
    //     rst(bool): reset feed
    //
    // Outputs:
    //     y (data):  the y(n) output of y(n)=y(n-1)+x(n)
    //     ----------------------
    //     ena_out: the exstior calc hold output. will be false if
    //     `ena_in` is False
    // Parm:
    //     R: the up-sampleing ratio

    input [31:0] x;
    output [31:0] y;
    reg [31:0] y;
    input ena_in;
    output ena_out;
    reg ena_out;
    input clk;
    input rst;

    reg [0:0] count;

    always @(posedge clk) begin: INTERPOLATOR_COUNTCONTROL
        if (rst) begin
            count <= 0;
        end
        else begin
            if (ena_in) begin
                count <= (2 - 1);
            end
            else if ((count > 0)) begin
                count <= (count + 1);
            end
        end
    end

    always @(posedge clk) begin: INTERPOLATOR_PASSCONTROL
        if (rst) begin
            y <= 0;
        end
        else begin
            if (ena_in) begin
                y <= x;
                ena_out <= 1'b1;
            end
            else if ((count > 0)) begin
                y <= 0;
                ena_out <= 1'b1;
            end
            else begin
                y <= 0;
                ena_out <= 1'b0;
            end
        end
    end

    endmodule

# Pass Through

## myHDL implementation

```python
def PassThrough(x, y, ena_in, ena_out):
    '''
        A pass-throug (do nothing) section for a CIC filter

        Inputs:
            x (data): the x(n) data in feed
            ------------------------
            ena_in (bool): the exstiror calc hold input. calc is done only if
            `ena_in` is True

        Outputs:
            y (data):  the y(n) output of y(n)=y(n-1)+x(n)
            ----------------------
            ena_out: the exstior calc hold output. will be false if
            `ena_in` is False
    '''
    @always_comb
    def logic():
        y.next=x
        ena_out.next=ena_in
    return logic
```

## myHDL Testing

```python
Peeker.clear()

x=Signal(modbv(1)[BitWidth:]); Peeker(x, 'x')
y=Signal(modbv(0)[BitWidth:]); Peeker(y, 'y')

ena_in, ena_out, clk=[Signal(bool(0)) for  _ in range(3)]
Peeker(ena_in, 'ena_in'); Peeker(ena_out, 'ena_out'); Peeker(clk, 'clk')

DUT=PassThrough(x, y, ena_in, ena_out)

def Integrator_TB():

    @always(delay(1))  ## delay in nano seconds
    def clkGen():
        clk.next = not clk

    @instance
    def stimulus():
        count=0
        while 1:
            if count<=5:
                ena_in.next=True
            elif count<=10:
                ena_in.next=False
            elif count>=15:
                ena_in.next=True

            if count> 2*BitWidth:
                raise StopSimulation
            x.next=x+1
            count+=1
            yield clk.posedge

    return instances()
sim = Simulation(DUT, Integrator_TB(), *Peeker.instances()).run()

```

```python
Peeker.to_wavedrom(start_time=0, stop_time=40, tock=True)
```

## To Verilog

```python
x=Signal(modbv(1)[BitWidth:])
y=Signal(modbv(0)[BitWidth:])

ena_in, ena_out, clk=[Signal(bool(0)) for  _ in range(3)]

toVerilog(PassThrough, x, y, ena_in, ena_out)
VerilogTextReader('PassThrough');
```

    ***Verilog modual from PassThrough.v***

     // File: PassThrough.v
    // Generated by MyHDL 0.9.0
    // Date: Mon Feb 26 19:26:23 2018

    `timescale 1ns/10ps

    module PassThrough (
        x,
        y,
        ena_in,
        ena_out
    );
    // A pass-throug (do nothing) section for a CIC filter
    //
    // Inputs:
    //     x (data): the x(n) data in feed
    //     ------------------------
    //     ena_in (bool): the exstiror calc hold input. calc is done only if
    //     `ena_in` is True
    //
    // Outputs:
    //     y (data):  the y(n) output of y(n)=y(n-1)+x(n)
    //     ----------------------
    //     ena_out: the exstior calc hold output. will be false if
    //     `ena_in` is False

    input [31:0] x;
    output [31:0] y;
    wire [31:0] y;
    input ena_in;
    output ena_out;
    wire ena_out;

    assign y = x;
    assign ena_out = ena_in;

    endmodule

# Complete CIC Filter

## Theory

```python
N=symbols('N')
CIC=simplify(((1-z**(-R*M))**N)/((1-z**(-1))**N)); CIC
```

```python
CICsub=simplify(CIC.subs(zFunc.lhs, zFunc.rhs)); CICsub
```

```python
CICN=lambdify((z, M, R, N), CIC, dummify=False)
def CICEx(N=2, M=2, R=2):
    Mvalue=M; Rvalue=R; Nvalue=N
    HzN=CICN(zN, M=Mvalue, R=Rvalue, N=Nvalue); HzN.shape
    HzNMag=np.abs(HzN); HzNPhase=np.angle(HzN)

    HAtR1=zFuncN(rN, AngThetaN)
    HzAtR1N=CICN(zAtR1, M=Mvalue, R=Rvalue, N=Nvalue)
    HzAtR1NMag=np.abs(HzAtR1N); HzAtR1NPhase=np.angle(HzAtR1N)
    Zplot(zR, zI, HzNMag, HzAtR1NMag, HzNPhase, HzAtR1NPhase,
         f'CIC Dec of N={Nvalue}, M={Mvalue}, R={Rvalue}')
CICEx()
```

    <IPython.core.display.Javascript object>

# myHDL implementation

```python
def CICFilter(x, y, ena_in, ena_out, clk, rst, N=3, M=2, R=8, Type=None):
    '''
        The complet CIC filter

        Inputs:
            x (data): the x(n) data in feed
            ------------------------
            ena_in (bool): the exstiror calc hold input. calc is done only if
            `ena_in` is True

            clk(bool): clock feed
            rst(bool): reset feed

        Outputs:
            y (data):  the y(n) output of y(n)=y(n-1)+x(n)
            ----------------------
            ena_out: the exstior calc hold output. will be false if
            `ena_in` is False
        Parm:
            N: the number of stages
            M: the Z delay order
            R: the decimation/Interpolation ratio
            Type (None, 'Dec', 'Interp'): type of CIC filter

    '''
    #Parmters for sizeing the 2's comp data wires
    #assumes len(x)==len(y)
    WordLen_1=len(x)-1
    WireGuage=2**WordLen_1

    #------------------------------------------------
    #create the wires from the interpoltor (or lack of) to the comb sec
    #data wire
    Interp_Comb_DWire=Signal(intbv(0, min=-WireGuage, max=WireGuage))
    #enable wire
    Interp_Comp_EWire=Signal(bool(0))

    #instatiniat the interpolator (or lack of)
    if Type=='Interp':
        #                   x, y,                 ena_in, ena_out,       clk, rst, R
        Interp=Interpolator(x, Interp_Comb_DWire, ena_in, Interp_Comp_EWire, clk, rst, R)
    else:
                          #x, y, ena_in, ena_out
        Interp=PassThrough(x, Interp_Comb_DWire, ena_in, Interp_Comp_EWire)

    #------------------------------------------------------------

    #Data Wires
    Comb_DWireIJ=[Signal(intbv(0, min=-WireGuage, max=WireGuage)) for i in range(N)]
    #Enable Wires
    Comb_EWireIJ=[Signal(bool(0)) for i in range(N)]

    #instatintte the comb sections and wire them
    Comb_i=[]
    for i in range(N):
        if i==0:
            #                  x,                 y,                ena_in,            ena_out, clk, rst, M
            Comb_i.append(Comb(Interp_Comb_DWire, Comb_DWireIJ[i] , Interp_Comp_EWire, Comb_EWireIJ[i], clk, rst, M))
        else:
            #                  x,                 y,               ena_in,            ena_out,         clk, rst, M
            Comb_i.append(Comb(Comb_DWireIJ[i-1], Comb_DWireIJ[i], Comb_EWireIJ[i-1], Comb_EWireIJ[i], clk, rst, M))

    #------------------------------------------------------------
    #Data Wires
    Integrator_DWireIJ=[Signal(intbv(0, min=-WireGuage, max=WireGuage)) for i in range(N)]
    #Enable Wires
    Integrator_EWireIJ=[Signal(bool(0)) for i in range(N)]

    #instatintte the integrator sections and wire them
    Integrtor_i=[]
    for i in range(N):
        if i==0:
            #                             x,                 y,                     ena_in, ena_out,               clk, rst
            Integrtor_i.append(Integrator(Comb_DWireIJ[N-1], Integrator_DWireIJ[i], Comb_EWireIJ[N-1], Integrator_EWireIJ[i], clk, rst))
        else:
            #                             x,                       y,                     ena_in,                  ena_out,               clk, rst
            Integrtor_i.append(Integrator(Integrator_DWireIJ[i-1], Integrator_DWireIJ[i], Integrator_EWireIJ[i-1], Integrator_EWireIJ[i], clk, rst))

    #-------------------------------------------------------------
    #instatiniat the decimator (or lack of)
    if Type == 'Dec':
        #             x,                       y, ena_in,                  ena_out, clk, rst, R
        Dec=Decimator(Integrator_DWireIJ[N-1], y, Integrator_EWireIJ[N-1], ena_out, clk, rst, R)
    else:
        #               x,                       y, ena_in,                  ena_out
        Dec=PassThrough(Integrator_DWireIJ[N-1], y, Integrator_EWireIJ[N-1], ena_out)

    return instances()

```

## myHDL Testing
### (Need to come up with a test bench)

Chris: Would a square unit pulse be the best test of this

## To Verilog

```python
x=Signal(modbv(1)[BitWidth:])
y=Signal(modbv(0)[BitWidth:])

ena_in, ena_out, clk, rst=[Signal(bool(0)) for  _ in range(4)]

toVerilog(CICFilter, x, y, ena_in, ena_out, clk, rst, N=3, M=2, R=8, Type=None)

VerilogTextReader('CICFilter');
```

    ***Verilog modual from CICFilter.v***

     // File: CICFilter.v
    // Generated by MyHDL 0.9.0
    // Date: Mon Feb 26 19:27:06 2018

    `timescale 1ns/10ps

    module CICFilter (
        x,
        y,
        ena_in,
        ena_out,
        clk,
        rst
    );
    // The complet CIC filter
    //
    // Inputs:
    //     x (data): the x(n) data in feed
    //     ------------------------
    //     ena_in (bool): the exstiror calc hold input. calc is done only if
    //     `ena_in` is True
    //
    //     clk(bool): clock feed
    //     rst(bool): reset feed
    //
    // Outputs:
    //     y (data):  the y(n) output of y(n)=y(n-1)+x(n)
    //     ----------------------
    //     ena_out: the exstior calc hold output. will be false if
    //     `ena_in` is False
    // Parm:
    //     N: the number of stages
    //     M: the Z delay order
    //     R: the decimation/Interpolation ratio
    //     Type (None, 'Dec', 'Interp'): type of CIC filter

    input [31:0] x;
    output [31:0] y;
    wire [31:0] y;
    input ena_in;
    output ena_out;
    wire ena_out;
    input clk;
    input rst;

    wire Interp_Comp_EWire;
    wire signed [31:0] Interp_Comb_DWire;
    reg signed [31:0] Dec_x;
    reg Dec_ena_in;
    reg signed [31:0] Integrtor_i_2_x;
    reg Integrtor_i_2_ena_in;
    reg signed [31:0] Integrtor_i_1_x;
    reg Integrtor_i_1_ena_in;
    reg signed [31:0] Integrtor_i_0_x;
    reg Integrtor_i_0_ena_in;
    reg signed [31:0] Comb_i_2_x;
    reg Comb_i_2_ena_in;
    reg signed [31:0] Comb_i_2_subx;
    reg signed [31:0] Comb_i_2_Zdelay_i_1_x;
    reg signed [31:0] Comb_i_1_x;
    reg Comb_i_1_ena_in;
    reg signed [31:0] Comb_i_1_subx;
    reg signed [31:0] Comb_i_1_Zdelay_i_1_x;
    reg signed [31:0] Comb_i_0_subx;
    reg signed [31:0] Comb_i_0_Zdelay_i_1_x;

    always @(posedge clk) begin: CICFILTER_COMB_I_0_ZDELAY_I_0_LOGIC
        if (Interp_Comp_EWire) begin
            Comb_i_0_Zdelay_i_1_x <= Interp_Comb_DWire;
        end
    end

    always @(posedge clk) begin: CICFILTER_COMB_I_0_ZDELAY_I_1_LOGIC
        if (Interp_Comp_EWire) begin
            Comb_i_0_subx <= Comb_i_0_Zdelay_i_1_x;
        end
    end

    always @(posedge clk) begin: CICFILTER_COMB_I_0_LOGC
        if (rst) begin
            Comb_i_1_x <= 0;
        end
        else begin
            if (Interp_Comp_EWire) begin
                Comb_i_1_x <= (Interp_Comb_DWire - Comb_i_0_subx);
                Comb_i_1_ena_in <= 1'b1;
            end
            else begin
                Comb_i_1_ena_in <= 1'b0;
            end
        end
    end

    always @(posedge clk) begin: CICFILTER_COMB_I_1_ZDELAY_I_0_LOGIC
        if (Comb_i_1_ena_in) begin
            Comb_i_1_Zdelay_i_1_x <= Comb_i_1_x;
        end
    end

    always @(posedge clk) begin: CICFILTER_COMB_I_1_ZDELAY_I_1_LOGIC
        if (Comb_i_1_ena_in) begin
            Comb_i_1_subx <= Comb_i_1_Zdelay_i_1_x;
        end
    end

    always @(posedge clk) begin: CICFILTER_COMB_I_1_LOGC
        if (rst) begin
            Comb_i_2_x <= 0;
        end
        else begin
            if (Comb_i_1_ena_in) begin
                Comb_i_2_x <= (Comb_i_1_x - Comb_i_1_subx);
                Comb_i_2_ena_in <= 1'b1;
            end
            else begin
                Comb_i_2_ena_in <= 1'b0;
            end
        end
    end

    always @(posedge clk) begin: CICFILTER_COMB_I_2_ZDELAY_I_0_LOGIC
        if (Comb_i_2_ena_in) begin
            Comb_i_2_Zdelay_i_1_x <= Comb_i_2_x;
        end
    end

    always @(posedge clk) begin: CICFILTER_COMB_I_2_ZDELAY_I_1_LOGIC
        if (Comb_i_2_ena_in) begin
            Comb_i_2_subx <= Comb_i_2_Zdelay_i_1_x;
        end
    end

    always @(posedge clk) begin: CICFILTER_COMB_I_2_LOGC
        if (rst) begin
            Integrtor_i_0_x <= 0;
        end
        else begin
            if (Comb_i_2_ena_in) begin
                Integrtor_i_0_x <= (Comb_i_2_x - Comb_i_2_subx);
                Integrtor_i_0_ena_in <= 1'b1;
            end
            else begin
                Integrtor_i_0_ena_in <= 1'b0;
            end
        end
    end

    assign Interp_Comb_DWire = x;
    assign Interp_Comp_EWire = ena_in;

    always @(posedge clk) begin: CICFILTER_INTEGRTOR_I_0_LOGIC
        if (rst) begin
            Integrtor_i_1_x <= 0;
        end
        else begin
            if (Integrtor_i_0_ena_in) begin
                Integrtor_i_1_x <= (Integrtor_i_1_x + Integrtor_i_0_x);
                Integrtor_i_1_ena_in <= 1'b1;
            end
            else begin
                Integrtor_i_1_ena_in <= 1'b0;
            end
        end
    end

    always @(posedge clk) begin: CICFILTER_INTEGRTOR_I_1_LOGIC
        if (rst) begin
            Integrtor_i_2_x <= 0;
        end
        else begin
            if (Integrtor_i_1_ena_in) begin
                Integrtor_i_2_x <= (Integrtor_i_2_x + Integrtor_i_1_x);
                Integrtor_i_2_ena_in <= 1'b1;
            end
            else begin
                Integrtor_i_2_ena_in <= 1'b0;
            end
        end
    end

    always @(posedge clk) begin: CICFILTER_INTEGRTOR_I_2_LOGIC
        if (rst) begin
            Dec_x <= 0;
        end
        else begin
            if (Integrtor_i_2_ena_in) begin
                Dec_x <= (Dec_x + Integrtor_i_2_x);
                Dec_ena_in <= 1'b1;
            end
            else begin
                Dec_ena_in <= 1'b0;
            end
        end
    end

    assign y = Dec_x;
    assign ena_out = Dec_ena_in;

    endmodule