Hide keyboard shortcuts

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 

3 

4Author: Chad Fulton 

5License: Simplified-BSD 

6""" 

7 

8import numpy as np 

9from .kalman_smoother import KalmanSmoother 

10from . import tools 

11 

12SIMULATION_STATE = 0x01 

13SIMULATION_DISTURBANCE = 0x04 

14SIMULATION_ALL = ( 

15 SIMULATION_STATE | SIMULATION_DISTURBANCE 

16) 

17 

18 

19class SimulationSmoother(KalmanSmoother): 

20 r""" 

21 State space representation of a time series process, with Kalman filter 

22 and smoother, and with simulation smoother. 

23 

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 """ 

48 

49 simulation_outputs = [ 

50 'simulate_state', 'simulate_disturbance', 'simulate_all' 

51 ] 

52 

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 ) 

59 

60 if simulation_smooth_results_class is None: 

61 simulation_smooth_results_class = SimulationSmoothResults 

62 self.simulation_smooth_results_class = simulation_smooth_results_class 

63 

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()) 

68 

69 # Holder for an model-level simulation smoother objects, to use in 

70 # simulating new time series. 

71 self._simulators = {} 

72 

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 

78 

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. 

82 

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 

103 

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 

110 

111 # Handle case of no information in kwargs 

112 if simulation_output == 0: 

113 

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.") 

124 

125 # Otherwise set simulation output to be the same as smoother 

126 # output 

127 simulation_output = self.smoother_output 

128 

129 return simulation_output 

130 

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()) 

136 

137 # Initialize the state 

138 self._initialize_state(prefix=prefix) 

139 

140 # Create the simulator if necessary 

141 if (prefix not in self._simulators or 

142 not nsimulations == self._simulators[prefix].nobs): 

143 

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 

155 

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] 

165 

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.') 

182 

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) 

188 

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) 

193 

194 simulated_obs = np.array(simulator.generated_obs, copy=True) 

195 simulated_state = np.array(simulator.generated_state, copy=True) 

196 

197 return ( 

198 simulated_obs[:, :nsimulations].T, 

199 simulated_state[:, :nsimulations].T 

200 ) 

201 

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. 

206 

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. 

221 

222 Returns 

223 ------- 

224 SimulationSmoothResults 

225 """ 

226 

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 

230 

231 # Instantiate a new results object 

232 if not issubclass(results_class, SimulationSmoothResults): 

233 raise ValueError('Invalid results class provided.') 

234 

235 # Make sure we have the required Statespace representation 

236 prefix, dtype, create_smoother, create_filter, create_statespace = ( 

237 self._initialize_smoother()) 

238 

239 # Simulation smoother parameters 

240 simulation_output = self.get_simulation_output(simulation_output, 

241 **kwargs) 

242 

243 # Kalman smoother parameters 

244 smoother_output = kwargs.get('smoother_output', simulation_output) 

245 

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) 

259 

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 ) 

268 

269 # Create results object 

270 results = results_class(self, simulation_smoother) 

271 

272 return results 

273 

274 

275class SimulationSmoothResults(object): 

276 r""" 

277 Results from applying the Kalman smoother and/or filter to a state space 

278 model. 

279 

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. 

286 

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 """ 

322 

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 

328 

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 

337 

338 @property 

339 def simulation_output(self): 

340 return self._simulation_smoother.simulation_output 

341 

342 @simulation_output.setter 

343 def simulation_output(self, value): 

344 self._simulation_smoother.simulation_output = value 

345 

346 @property 

347 def simulate_state(self): 

348 return bool(self.simulation_output & SIMULATION_STATE) 

349 

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 

356 

357 @property 

358 def simulate_disturbance(self): 

359 return bool(self.simulation_output & SIMULATION_DISTURBANCE) 

360 

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) 

369 

370 @property 

371 def simulate_all(self): 

372 return bool(self.simulation_output & SIMULATION_ALL) 

373 

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 

380 

381 @property 

382 def generated_measurement_disturbance(self): 

383 r""" 

384 Randomly drawn measurement disturbance variates 

385 

386 Used to construct `generated_obs`. 

387 

388 Notes 

389 ----- 

390 

391 .. math:: 

392 

393 \varepsilon_t^+ ~ N(0, H_t) 

394 

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 

405 

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`. 

411 

412 Notes 

413 ----- 

414 

415 .. math:: 

416 

417 \eta_t^+ ~ N(0, Q_t) 

418 

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 

429 

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. 

436 

437 Notes 

438 ----- 

439 

440 .. math:: 

441 

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 

449 

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. 

455 

456 Notes 

457 ----- 

458 

459 .. math:: 

460 

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 

468 

469 @property 

470 def simulated_state(self): 

471 r""" 

472 Random draw of the state vector from its conditional distribution. 

473 

474 Notes 

475 ----- 

476 

477 .. math:: 

478 

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 

486 

487 @property 

488 def simulated_measurement_disturbance(self): 

489 r""" 

490 Random draw of the measurement disturbance vector from its conditional 

491 distribution. 

492 

493 Notes 

494 ----- 

495 

496 .. math:: 

497 

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 

506 

507 @property 

508 def simulated_state_disturbance(self): 

509 r""" 

510 Random draw of the state disturbance vector from its conditional 

511 distribution. 

512 

513 Notes 

514 ----- 

515 

516 .. math:: 

517 

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 

526 

527 def simulate(self, simulation_output=-1, disturbance_variates=None, 

528 initial_state_variates=None, pretransformed_variates=False): 

529 r""" 

530 Perform simulation smoothing 

531 

532 Does not return anything, but populates the object's `simulated_*` 

533 attributes, as specified by simulation output. 

534 

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 

559 

560 # Re-initialize the _statespace representation 

561 prefix, dtype, create_smoother, create_filter, create_statespace = ( 

562 self.model._initialize_smoother()) 

563 

564 # Initialize the state 

565 self.model._initialize_state(prefix=prefix) 

566 

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() 

576 

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() 

586 

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)