Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/statsmodels/tsa/statespace/simulation_smoother.py : 18%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2State Space Representation, Kalman Filter, Smoother, and Simulation Smoother
4Author: Chad Fulton
5License: Simplified-BSD
6"""
8import numpy as np
9from .kalman_smoother import KalmanSmoother
10from . import tools
12SIMULATION_STATE = 0x01
13SIMULATION_DISTURBANCE = 0x04
14SIMULATION_ALL = (
15 SIMULATION_STATE | SIMULATION_DISTURBANCE
16)
19class SimulationSmoother(KalmanSmoother):
20 r"""
21 State space representation of a time series process, with Kalman filter
22 and smoother, and with simulation smoother.
24 Parameters
25 ----------
26 k_endog : {array_like, int}
27 The observed time-series process :math:`y` if array like or the
28 number of variables in the process if an integer.
29 k_states : int
30 The dimension of the unobserved state process.
31 k_posdef : int, optional
32 The dimension of a guaranteed positive definite covariance matrix
33 describing the shocks in the measurement equation. Must be less than
34 or equal to `k_states`. Default is `k_states`.
35 simulation_smooth_results_class : class, optional
36 Default results class to use to save output of simulation smoothing.
37 Default is `SimulationSmoothResults`. If specified, class must extend
38 from `SimulationSmoothResults`.
39 simulation_smoother_classes : dict, optional
40 Dictionary with BLAS prefixes as keys and classes as values.
41 **kwargs
42 Keyword arguments may be used to provide default values for state space
43 matrices, for Kalman filtering options, for Kalman smoothing
44 options, or for Simulation smoothing options.
45 See `Representation`, `KalmanFilter`, and `KalmanSmoother` for more
46 details.
47 """
49 simulation_outputs = [
50 'simulate_state', 'simulate_disturbance', 'simulate_all'
51 ]
53 def __init__(self, k_endog, k_states, k_posdef=None,
54 simulation_smooth_results_class=None,
55 simulation_smoother_classes=None, **kwargs):
56 super(SimulationSmoother, self).__init__(
57 k_endog, k_states, k_posdef, **kwargs
58 )
60 if simulation_smooth_results_class is None:
61 simulation_smooth_results_class = SimulationSmoothResults
62 self.simulation_smooth_results_class = simulation_smooth_results_class
64 self.prefix_simulation_smoother_map = (
65 simulation_smoother_classes
66 if simulation_smoother_classes is not None
67 else tools.prefix_simulation_smoother_map.copy())
69 # Holder for an model-level simulation smoother objects, to use in
70 # simulating new time series.
71 self._simulators = {}
73 def get_simulation_output(self, simulation_output=None,
74 simulate_state=None, simulate_disturbance=None,
75 simulate_all=None, **kwargs):
76 r"""
77 Get simulation output bitmask
79 Helper method to get final simulation output bitmask from a set of
80 optional arguments including the bitmask itself and possibly boolean
81 flags.
83 Parameters
84 ----------
85 simulation_output : int, optional
86 Simulation output bitmask. If this is specified, it is simply
87 returned and the other arguments are ignored.
88 simulate_state : bool, optional
89 Whether or not to include the state in the simulation output.
90 simulate_disturbance : bool, optional
91 Whether or not to include the state and observation disturbances
92 in the simulation output.
93 simulate_all : bool, optional
94 Whether or not to include all simulation output.
95 \*\*kwargs
96 Additional keyword arguments. Present so that calls to this method
97 can use \*\*kwargs without clearing out additional arguments.
98 """
99 # If we do not explicitly have simulation_output, try to get it from
100 # kwargs
101 if simulation_output is None:
102 simulation_output = 0
104 if simulate_state:
105 simulation_output |= SIMULATION_STATE
106 if simulate_disturbance:
107 simulation_output |= SIMULATION_DISTURBANCE
108 if simulate_all:
109 simulation_output |= SIMULATION_ALL
111 # Handle case of no information in kwargs
112 if simulation_output == 0:
114 # If some arguments were passed, but we still do not have any
115 # simulation output, raise an exception
116 argument_set = not all([
117 simulate_state is None, simulate_disturbance is None,
118 simulate_all is None
119 ])
120 if argument_set:
121 raise ValueError("Invalid simulation output options:"
122 " given options would result in no"
123 " output.")
125 # Otherwise set simulation output to be the same as smoother
126 # output
127 simulation_output = self.smoother_output
129 return simulation_output
131 def _simulate(self, nsimulations, measurement_shocks, state_shocks,
132 initial_state):
133 # Initialize the filter and representation
134 prefix, dtype, create_smoother, create_filter, create_statespace = (
135 self._initialize_smoother())
137 # Initialize the state
138 self._initialize_state(prefix=prefix)
140 # Create the simulator if necessary
141 if (prefix not in self._simulators or
142 not nsimulations == self._simulators[prefix].nobs):
144 simulation_output = 0
145 # Kalman smoother parameters
146 smoother_output = -1
147 # Kalman filter parameters
148 filter_method = self.filter_method
149 inversion_method = self.inversion_method
150 stability_method = self.stability_method
151 conserve_memory = self.conserve_memory
152 filter_timing = self.filter_timing
153 loglikelihood_burn = self.loglikelihood_burn
154 tolerance = self.tolerance
156 # Create a new simulation smoother object
157 cls = self.prefix_simulation_smoother_map[prefix]
158 self._simulators[prefix] = cls(
159 self._statespaces[prefix],
160 filter_method, inversion_method, stability_method,
161 conserve_memory, filter_timing, tolerance, loglikelihood_burn,
162 smoother_output, simulation_output, nsimulations
163 )
164 simulator = self._simulators[prefix]
166 # Set the disturbance variates
167 if measurement_shocks is not None and state_shocks is not None:
168 disturbance_variates = np.atleast_1d(np.array(
169 np.r_[measurement_shocks.ravel(), state_shocks.ravel()],
170 dtype=self.dtype
171 ).squeeze())
172 simulator.set_disturbance_variates(disturbance_variates,
173 pretransformed=True)
174 elif measurement_shocks is None and state_shocks is None:
175 pass
176 elif measurement_shocks is not None:
177 raise ValueError('Must set `state_shocks` if `measurement_shocks`'
178 ' is set.')
179 elif state_shocks is not None:
180 raise ValueError('Must set `measurement_shocks` if `state_shocks`'
181 ' is set.')
183 # Set the intial state vector
184 initial_state = np.atleast_1d(np.array(
185 initial_state, dtype=self.dtype
186 ).squeeze())
187 simulator.set_initial_state(initial_state)
189 # Perform simulation smoothing
190 # Note: simulation_output=-1 corresponds to whatever was setup when
191 # the simulation smoother was constructed
192 simulator.simulate(-1)
194 simulated_obs = np.array(simulator.generated_obs, copy=True)
195 simulated_state = np.array(simulator.generated_state, copy=True)
197 return (
198 simulated_obs[:, :nsimulations].T,
199 simulated_state[:, :nsimulations].T
200 )
202 def simulation_smoother(self, simulation_output=None,
203 results_class=None, prefix=None, **kwargs):
204 r"""
205 Retrieve a simulation smoother for the statespace model.
207 Parameters
208 ----------
209 simulation_output : int, optional
210 Determines which simulation smoother output is calculated.
211 Default is all (including state and disturbances).
212 simulation_smooth_results_class : class, optional
213 Default results class to use to save output of simulation
214 smoothing. Default is `SimulationSmoothResults`. If specified,
215 class must extend from `SimulationSmoothResults`.
216 prefix : str
217 The prefix of the datatype. Usually only used internally.
218 **kwargs
219 Additional keyword arguments, used to set the simulation output.
220 See `set_simulation_output` for more details.
222 Returns
223 -------
224 SimulationSmoothResults
225 """
227 # Set the class to be the default results class, if None provided
228 if results_class is None:
229 results_class = self.simulation_smooth_results_class
231 # Instantiate a new results object
232 if not issubclass(results_class, SimulationSmoothResults):
233 raise ValueError('Invalid results class provided.')
235 # Make sure we have the required Statespace representation
236 prefix, dtype, create_smoother, create_filter, create_statespace = (
237 self._initialize_smoother())
239 # Simulation smoother parameters
240 simulation_output = self.get_simulation_output(simulation_output,
241 **kwargs)
243 # Kalman smoother parameters
244 smoother_output = kwargs.get('smoother_output', simulation_output)
246 # Kalman filter parameters
247 filter_method = kwargs.get('filter_method', self.filter_method)
248 inversion_method = kwargs.get('inversion_method',
249 self.inversion_method)
250 stability_method = kwargs.get('stability_method',
251 self.stability_method)
252 conserve_memory = kwargs.get('conserve_memory',
253 self.conserve_memory)
254 filter_timing = kwargs.get('filter_timing',
255 self.filter_timing)
256 loglikelihood_burn = kwargs.get('loglikelihood_burn',
257 self.loglikelihood_burn)
258 tolerance = kwargs.get('tolerance', self.tolerance)
260 # Create a new simulation smoother object
261 cls = self.prefix_simulation_smoother_map[prefix]
262 simulation_smoother = cls(
263 self._statespaces[prefix],
264 filter_method, inversion_method, stability_method, conserve_memory,
265 filter_timing, tolerance, loglikelihood_burn, smoother_output,
266 simulation_output
267 )
269 # Create results object
270 results = results_class(self, simulation_smoother)
272 return results
275class SimulationSmoothResults(object):
276 r"""
277 Results from applying the Kalman smoother and/or filter to a state space
278 model.
280 Parameters
281 ----------
282 model : Representation
283 A Statespace representation
284 simulation_smoother : {{prefix}}SimulationSmoother object
285 The Cython simulation smoother object with which to simulation smooth.
287 Attributes
288 ----------
289 model : Representation
290 A Statespace representation
291 dtype : dtype
292 Datatype of representation matrices
293 prefix : str
294 BLAS prefix of representation matrices
295 simulation_output : int
296 Bitmask controlling simulation output.
297 simulate_state : bool
298 Flag for if the state is included in simulation output.
299 simulate_disturbance : bool
300 Flag for if the state and observation disturbances are included in
301 simulation output.
302 simulate_all : bool
303 Flag for if simulation output should include everything.
304 generated_measurement_disturbance : ndarray
305 Measurement disturbance variates used to genereate the observation
306 vector.
307 generated_state_disturbance : ndarray
308 State disturbance variates used to genereate the state and
309 observation vectors.
310 generated_obs : ndarray
311 Generated observation vector produced as a byproduct of simulation
312 smoothing.
313 generated_state : ndarray
314 Generated state vector produced as a byproduct of simulation smoothing.
315 simulated_state : ndarray
316 Simulated state.
317 simulated_measurement_disturbance : ndarray
318 Simulated measurement disturbance.
319 simulated_state_disturbance : ndarray
320 Simulated state disturbance.
321 """
323 def __init__(self, model, simulation_smoother):
324 self.model = model
325 self.prefix = model.prefix
326 self.dtype = model.dtype
327 self._simulation_smoother = simulation_smoother
329 # Output
330 self._generated_measurement_disturbance = None
331 self._generated_state_disturbance = None
332 self._generated_obs = None
333 self._generated_state = None
334 self._simulated_state = None
335 self._simulated_measurement_disturbance = None
336 self._simulated_state_disturbance = None
338 @property
339 def simulation_output(self):
340 return self._simulation_smoother.simulation_output
342 @simulation_output.setter
343 def simulation_output(self, value):
344 self._simulation_smoother.simulation_output = value
346 @property
347 def simulate_state(self):
348 return bool(self.simulation_output & SIMULATION_STATE)
350 @simulate_state.setter
351 def simulate_state(self, value):
352 if bool(value):
353 self.simulation_output = self.simulation_output | SIMULATION_STATE
354 else:
355 self.simulation_output = self.simulation_output & ~SIMULATION_STATE
357 @property
358 def simulate_disturbance(self):
359 return bool(self.simulation_output & SIMULATION_DISTURBANCE)
361 @simulate_disturbance.setter
362 def simulate_disturbance(self, value):
363 if bool(value):
364 self.simulation_output = (
365 self.simulation_output | SIMULATION_DISTURBANCE)
366 else:
367 self.simulation_output = (
368 self.simulation_output & ~SIMULATION_DISTURBANCE)
370 @property
371 def simulate_all(self):
372 return bool(self.simulation_output & SIMULATION_ALL)
374 @simulate_all.setter
375 def simulate_all(self, value):
376 if bool(value):
377 self.simulation_output = self.simulation_output | SIMULATION_ALL
378 else:
379 self.simulation_output = self.simulation_output & ~SIMULATION_ALL
381 @property
382 def generated_measurement_disturbance(self):
383 r"""
384 Randomly drawn measurement disturbance variates
386 Used to construct `generated_obs`.
388 Notes
389 -----
391 .. math::
393 \varepsilon_t^+ ~ N(0, H_t)
395 If `disturbance_variates` were provided to the `simulate()` method,
396 then this returns those variates (which were N(0,1)) transformed to the
397 distribution above.
398 """
399 if self._generated_measurement_disturbance is None:
400 end = self.model.nobs * self.model.k_endog
401 self._generated_measurement_disturbance = np.array(
402 self._simulation_smoother.disturbance_variates[:end],
403 copy=True).reshape(self.model.nobs, self.model.k_endog)
404 return self._generated_measurement_disturbance
406 @property
407 def generated_state_disturbance(self):
408 r"""
409 Randomly drawn state disturbance variates, used to construct
410 `generated_state` and `generated_obs`.
412 Notes
413 -----
415 .. math::
417 \eta_t^+ ~ N(0, Q_t)
419 If `disturbance_variates` were provided to the `simulate()` method,
420 then this returns those variates (which were N(0,1)) transformed to the
421 distribution above.
422 """
423 if self._generated_state_disturbance is None:
424 start = self.model.nobs * self.model.k_endog
425 self._generated_state_disturbance = np.array(
426 self._simulation_smoother.disturbance_variates[start:],
427 copy=True).reshape(self.model.nobs, self.model.k_posdef)
428 return self._generated_state_disturbance
430 @property
431 def generated_obs(self):
432 r"""
433 Generated vector of observations by iterating on the observation and
434 transition equations, given a random initial state draw and random
435 disturbance draws.
437 Notes
438 -----
440 .. math::
442 y_t^+ = d_t + Z_t \alpha_t^+ + \varepsilon_t^+
443 """
444 if self._generated_obs is None:
445 self._generated_obs = np.array(
446 self._simulation_smoother.generated_obs, copy=True
447 )
448 return self._generated_obs
450 @property
451 def generated_state(self):
452 r"""
453 Generated vector of states by iterating on the transition equation,
454 given a random initial state draw and random disturbance draws.
456 Notes
457 -----
459 .. math::
461 \alpha_{t+1}^+ = c_t + T_t \alpha_t^+ + \eta_t^+
462 """
463 if self._generated_state is None:
464 self._generated_state = np.array(
465 self._simulation_smoother.generated_state, copy=True
466 )
467 return self._generated_state
469 @property
470 def simulated_state(self):
471 r"""
472 Random draw of the state vector from its conditional distribution.
474 Notes
475 -----
477 .. math::
479 \alpha ~ p(\alpha \mid Y_n)
480 """
481 if self._simulated_state is None:
482 self._simulated_state = np.array(
483 self._simulation_smoother.simulated_state, copy=True
484 )
485 return self._simulated_state
487 @property
488 def simulated_measurement_disturbance(self):
489 r"""
490 Random draw of the measurement disturbance vector from its conditional
491 distribution.
493 Notes
494 -----
496 .. math::
498 \varepsilon ~ N(\hat \varepsilon, Var(\hat \varepsilon \mid Y_n))
499 """
500 if self._simulated_measurement_disturbance is None:
501 self._simulated_measurement_disturbance = np.array(
502 self._simulation_smoother.simulated_measurement_disturbance,
503 copy=True
504 )
505 return self._simulated_measurement_disturbance
507 @property
508 def simulated_state_disturbance(self):
509 r"""
510 Random draw of the state disturbance vector from its conditional
511 distribution.
513 Notes
514 -----
516 .. math::
518 \eta ~ N(\hat \eta, Var(\hat \eta \mid Y_n))
519 """
520 if self._simulated_state_disturbance is None:
521 self._simulated_state_disturbance = np.array(
522 self._simulation_smoother.simulated_state_disturbance,
523 copy=True
524 )
525 return self._simulated_state_disturbance
527 def simulate(self, simulation_output=-1, disturbance_variates=None,
528 initial_state_variates=None, pretransformed_variates=False):
529 r"""
530 Perform simulation smoothing
532 Does not return anything, but populates the object's `simulated_*`
533 attributes, as specified by simulation output.
535 Parameters
536 ----------
537 simulation_output : int, optional
538 Bitmask controlling simulation output. Default is to use the
539 simulation output defined in object initialization.
540 disturbance_variates : array_likes, optional
541 Random values to use as disturbance variates, distributed standard
542 Normal. Usually only specified if results are to be replicated
543 (e.g. to enforce a seed) or for testing. If not specified, random
544 variates are drawn.
545 initial_state_variates : array_likes, optional
546 Random values to use as initial state variates. Usually only
547 specified if results are to be replicated (e.g. to enforce a seed)
548 or for testing. If not specified, random variates are drawn.
549 """
550 # Clear any previous output
551 self._generated_measurement_disturbance = None
552 self._generated_state_disturbance = None
553 self._generated_state = None
554 self._generated_obs = None
555 self._generated_state = None
556 self._simulated_state = None
557 self._simulated_measurement_disturbance = None
558 self._simulated_state_disturbance = None
560 # Re-initialize the _statespace representation
561 prefix, dtype, create_smoother, create_filter, create_statespace = (
562 self.model._initialize_smoother())
564 # Initialize the state
565 self.model._initialize_state(prefix=prefix)
567 # Draw the (independent) random variates for disturbances in the
568 # simulation
569 if disturbance_variates is not None:
570 self._simulation_smoother.set_disturbance_variates(
571 np.array(disturbance_variates, dtype=self.dtype),
572 pretransformed=pretransformed_variates
573 )
574 else:
575 self._simulation_smoother.draw_disturbance_variates()
577 # Draw the (independent) random variates for the initial states in the
578 # simulation
579 if initial_state_variates is not None:
580 self._simulation_smoother.set_initial_state_variates(
581 np.array(initial_state_variates, dtype=self.dtype),
582 pretransformed=pretransformed_variates
583 )
584 else:
585 self._simulation_smoother.draw_initial_state_variates()
587 # Perform simulation smoothing
588 # Note: simulation_output=-1 corresponds to whatever was setup when
589 # the simulation smoother was constructed
590 self._simulation_smoother.simulate(simulation_output)