Last active
August 29, 2015 14:19
-
-
Save ChadFulton/91168487d102a62a8e56 to your computer and use it in GitHub Desktop.
Statespace - Transfer Function
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"%matplotlib inline\n", | |
"import numpy as np\n", | |
"from scipy import signal\n", | |
"import matplotlib.pyplot as plt\n", | |
"import statsmodels.api as sm" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [], | |
"source": [ | |
"# Simulate the data\n", | |
"freq = 10\n", | |
"omega = 2*np.pi*freq\n", | |
"num, den, dt = signal.cont2discrete(([omega], [1, omega]), 0.001)\n", | |
"\n", | |
"time = np.linspace(0, 1, 1001)\n", | |
"time, (y1,) = signal.dstep((num[0,1:], den, dt), t=time) # this\n", | |
"time, y2 = signal.step(([omega], [1, omega]), T=time) # or this" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## SISO\n", | |
"\n", | |
"Suppose we have the following transfer function:\n", | |
"\n", | |
"$$\n", | |
"y_t = \\frac{b(L)}{a(L)} u_t\n", | |
"$$\n", | |
"\n", | |
"this can be rewritten as\n", | |
"\n", | |
"$$\n", | |
"\\begin{align}\n", | |
"y_t & = b(L) x_t \\\\\n", | |
"x_t & = \\frac{1}{a(L)} u_t\n", | |
"\\end{align}\n", | |
"$$\n", | |
"\n", | |
"or as \n", | |
"\n", | |
"$$\n", | |
"\\begin{align}\n", | |
"y_t & = b(L) x_t \\\\\n", | |
"a(L) x_t & = u_t\n", | |
"\\end{align}\n", | |
"$$\n", | |
"\n", | |
"Note that the lag polynomial $a(L)$ contains the zero lag (and assume that the coefficient equals one, if not you can rescale). Then rewrite again:\n", | |
"\n", | |
"$$\n", | |
"\\begin{align}\n", | |
"y_t & = b(L) x_t \\\\\n", | |
"x_t & = \\tilde a(L) x_{t-1} + u_t\n", | |
"\\end{align}\n", | |
"$$\n", | |
"\n", | |
"And if $a(L)$ or $b_(L)$ was not an $AR(1)$, we can use the usual stacking method to create a $VAR(1)$ transition system in $x_t$." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## MISO with errors\n", | |
"\n", | |
"Suppose we have the following transfer function:\n", | |
"\n", | |
"$$\n", | |
"a(L) y_t = \\sum_{i=1}^k b_i(L) u_{i,t} + c(L) e_t\n", | |
"$$\n", | |
"\n", | |
"this can be rewritten as\n", | |
"\n", | |
"$$\n", | |
"\\begin{align}\n", | |
"y_t & = \\sum_{i=1}^k b_i(L) x_{i,t} + c(L) \\varepsilon_t\\\\\n", | |
"x_{i,t} & = \\frac{1}{a(L)} u_{i,t} \\\\\n", | |
"\\varepsilon_t & = \\frac{1}{a(L)} e_t\n", | |
"\\end{align}\n", | |
"$$\n", | |
"\n", | |
"or as \n", | |
"\n", | |
"$$\n", | |
"\\begin{align}\n", | |
"y_t & = \\begin{bmatrix}b_1(L) & \\dots & b_k(L) & c(L) \\end{bmatrix} \\begin{bmatrix}\n", | |
"x_{1,t} \\\\\n", | |
"\\vdots \\\\\n", | |
"x_{k,t} \\\\\n", | |
"\\varepsilon_t\n", | |
"\\end{bmatrix} \\\\\n", | |
"a(L) \\begin{bmatrix}\n", | |
"x_{1,t} \\\\\n", | |
"\\vdots \\\\\n", | |
"x_{k,t} \\\\\n", | |
"\\varepsilon_t\n", | |
"\\end{bmatrix} & = \\begin{bmatrix}\n", | |
"u_{1,t} \\\\\n", | |
"\\vdots \\\\\n", | |
"u_{k,t} \\\\\n", | |
"e_t\n", | |
"\\end{bmatrix}\n", | |
"\\end{align}\n", | |
"$$\n", | |
"\n", | |
"Note that the lag polynomial $a(L)$ contains the zero lag (and assume that the coefficient equals one, if not you can rescale). Then rewrite again:\n", | |
"\n", | |
"$$\n", | |
"\\begin{align}\n", | |
"y_t & = \\begin{bmatrix}b_1(L) & \\dots & b_k(L) & c(L) \\end{bmatrix} \\begin{bmatrix}\n", | |
"x_{1,t} \\\\\n", | |
"\\vdots \\\\\n", | |
"x_{k,t} \\\\\n", | |
"\\varepsilon_t\n", | |
"\\end{bmatrix} \\\\\n", | |
"\\begin{bmatrix}\n", | |
"x_{1,t} \\\\\n", | |
"\\vdots \\\\\n", | |
"x_{k,t} \\\\\n", | |
"\\varepsilon_t\n", | |
"\\end{bmatrix} & = \\tilde a(L) \\begin{bmatrix}\n", | |
"x_{1,t-1} \\\\\n", | |
"\\vdots \\\\\n", | |
"x_{k,t-1} \\\\\n", | |
"\\varepsilon_{t-1}\n", | |
"\\end{bmatrix} + \\begin{bmatrix}\n", | |
"u_{1,t} \\\\\n", | |
"\\vdots \\\\\n", | |
"u_{k,t} \\\\\n", | |
"0\n", | |
"\\end{bmatrix} + \n", | |
"\\begin{bmatrix}\n", | |
"0 \\\\ \\vdots \\\\ 0 \\\\ 1\n", | |
"\\end{bmatrix} e_t\n", | |
"\\end{align}\n", | |
"$$\n", | |
"\n", | |
"Finally if $a(L)$ (or $b_i(L)$) was not an $AR(1)$, we can use the usual stacking method to create a $VAR(1)$ transition system for each state, like so:\n", | |
"\n", | |
"$$\n", | |
"\\begin{align}\n", | |
"y_t & = \\begin{bmatrix}b_1(L) & \\dots & b_k(L) & c(L) \\end{bmatrix} \\begin{bmatrix}\n", | |
"\\tilde x_{1,t} \\\\\n", | |
"\\vdots \\\\\n", | |
"\\tilde x_{k,t} \\\\\n", | |
"\\tilde \\varepsilon_t\n", | |
"\\end{bmatrix} \\\\\n", | |
"\\begin{bmatrix}\n", | |
"\\tilde x_{1,t} \\\\\n", | |
"\\vdots \\\\\n", | |
"\\tilde x_{k,t} \\\\\n", | |
"\\tilde \\varepsilon_t\n", | |
"\\end{bmatrix} & = I_{k+1} \\otimes C_{\\tilde a} \\begin{bmatrix}\n", | |
"\\tilde x_{1,t-1} \\\\\n", | |
"\\vdots \\\\\n", | |
"\\tilde x_{k,t-1} \\\\\n", | |
"\\tilde \\varepsilon_{t-1}\n", | |
"\\end{bmatrix} + \\begin{bmatrix}\n", | |
"u_{1,t} \\\\\n", | |
"\\vdots \\\\\n", | |
"u_{k,t} \\\\\n", | |
"0\n", | |
"\\end{bmatrix} + \n", | |
"\\begin{bmatrix}\n", | |
"0 \\\\ \\vdots \\\\ 0 \\\\ 1\n", | |
"\\end{bmatrix} e_t\n", | |
"\\end{align}\n", | |
"$$\n", | |
"\n", | |
"where $\\tilde x_{i,t} = \\begin{pmatrix} x_{i,t} & x_{i,t-1} & \\dots & x_{i,t-p-1} \\end{pmatrix}'$ (similarly for $\\tilde \\varepsilon_t$) and where $C_{\\tilde a}$ is the companion matrix associated with $\\tilde a(L)$." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": { | |
"collapsed": false | |
}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
" Statespace Model Results \n", | |
"==============================================================================\n", | |
"Dep. Variable: y No. Observations: 1001\n", | |
"Model: Step Log Likelihood 26962.946\n", | |
"Date: Thu, 23 Apr 2015 AIC -53917.891\n", | |
"Time: 12:16:26 BIC -53898.256\n", | |
"Sample: 0 HQIC -53910.429\n", | |
" - 1001 \n", | |
"================================================================================\n", | |
" OPG \n", | |
" coef std err z P>|z| [95.0% Conf. Int.]\n", | |
"--------------------------------------------------------------------------------\n", | |
"num.L1 0.0609 6e-13 1.01e+11 0.000 0.061 0.061\n", | |
"den.L1 0.9391 3.68e-13 2.55e+12 0.000 0.939 0.939\n", | |
"sigma2.meas 4.035e-25 4.36e-11 9.25e-15 1.000 -8.55e-11 8.55e-11\n", | |
"sigma2.state 4.31e-24 4.36e-11 9.88e-14 1.000 -8.55e-11 8.55e-11\n", | |
"================================================================================\n", | |
"Numerator 0.0608986325757 0.0608986325758\n", | |
"Denominator 0.939101367424 0.939101367424\n" | |
] | |
} | |
], | |
"source": [ | |
"from statsmodels.tsa.statespace import tools\n", | |
"\n", | |
"class Step(sm.tsa.statespace.MLEModel):\n", | |
" def __init__(self, outputs, inputs, order=(1,1)):\n", | |
" # Model orders\n", | |
" self.order = order\n", | |
" self.k_num = order[0]\n", | |
" self.k_den = order[1]\n", | |
" \n", | |
" # Model dimensions\n", | |
" k_states = np.max(self.order)\n", | |
" \n", | |
" # Initialize statespace\n", | |
" super(Step, self).__init__(outputs, k_states, k_posdef=1)\n", | |
" \n", | |
" # Stationary initial states\n", | |
" self.initialize_stationary()\n", | |
" \n", | |
" # Check output\n", | |
" inputs = np.array(inputs)\n", | |
" if inputs.ndim == 0:\n", | |
" inputs = inputs[None]\n", | |
" elif inputs.ndim == 1 and len(inputs) > 1:\n", | |
" inputs = inputs[None,:]\n", | |
" elif inputs.shape == (self.nobs, 1):\n", | |
" inputs = inputs.transpose()\n", | |
" \n", | |
" # Initialize the matrices\n", | |
" companion = sm.tsa.statespace.tools.companion_matrix(np.r_[1, np.zeros(self.k_states)])\n", | |
" self['transition'] = companion.transpose()\n", | |
" self['state_intercept'] = inputs\n", | |
" self['selection', 0, 0] = 1\n", | |
" \n", | |
" # Cache some indices\n", | |
" self._design_idx = np.s_['design', :self.k_den]\n", | |
" self._transition_idx = np.s_['transition', :self.k_num]\n", | |
" \n", | |
" @property\n", | |
" def param_names(self):\n", | |
" return ['num.L1', 'den.L1', 'sigma2.meas', 'sigma2.state']\n", | |
" \n", | |
" @property\n", | |
" def start_params(self):\n", | |
" return np.r_[[0] * (self.k_num + self.k_den), [0.1] * 2]\n", | |
"\n", | |
" def transform_params(self, unconstrained):\n", | |
" constrained = np.zeros(unconstrained.shape, dtype=unconstrained.dtype)\n", | |
" constrained[:self.k_num] = tools.constrain_stationary_univariate(unconstrained[:self.k_num])\n", | |
" constrained[self.k_num:-2] = tools.constrain_stationary_univariate(unconstrained[self.k_num:-2])\n", | |
" constrained[-2:] = unconstrained[-2:]**2\n", | |
" return constrained\n", | |
"\n", | |
" def untransform_params(self, constrained):\n", | |
" unconstrained = np.zeros(constrained.shape, dtype=constrained.dtype)\n", | |
" unconstrained[:self.k_num] = tools.unconstrain_stationary_univariate(constrained[:self.k_num])\n", | |
" unconstrained[self.k_num:-2] = tools.unconstrain_stationary_univariate(constrained[self.k_num:-2])\n", | |
" unconstrained[-2:] = constrained[-2:]**0.5\n", | |
" return unconstrained\n", | |
"\n", | |
" def update(self, params, *args, **kwargs):\n", | |
" params = super(Step, self).update(params, *args, **kwargs)\n", | |
"\n", | |
" self[self._design_idx] = params[:self.k_num]\n", | |
" self[self._transition_idx] = params[self.k_num:-2]\n", | |
" self['obs_cov', 0, 0] = params[-2]\n", | |
" self['state_cov', 0, 0] = params[-1]\n", | |
" \n", | |
"mod = Step(outputs=y1, inputs=1)\n", | |
"\n", | |
"# First find better starting parameters with lbfgs\n", | |
"res = mod.fit(disp=False, maxiter=1000, warn_convergence=False)\n", | |
"# Next, use Powell, because the likelihood is poorly behaved\n", | |
"# (i.e there is no error component) and so the score is not a good\n", | |
"# guide for final optimization\n", | |
"res = mod.fit(res.params, method='powell', disp=False, maxiter=1000)\n", | |
"print res.summary()\n", | |
"\n", | |
"print 'Numerator', num[0,1], res.params[0]\n", | |
"print 'Denominator', -den[1], res.params[1]" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 2", | |
"language": "python", | |
"name": "python2" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 2 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython2", | |
"version": "2.7.6" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 0 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment