Skip to content

Commit 5c4ea4b

Browse files
author
andre_ramos
committed
add simulation functions for unobserved components with explanatory
1 parent 7a219e3 commit 5c4ea4b

File tree

4 files changed

+99
-5
lines changed

4 files changed

+99
-5
lines changed

src/StateSpaceModels.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,6 @@ export SparseUnivariateKalmanFilter
8888
export StateSpaceModel
8989
export UnivariateKalmanFilter
9090
export UnobservedComponents
91-
export UnobservedComponentsExplanatory
9291
export VehicleTracking
9392

9493
# Exported functions

src/forecast.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -89,13 +89,13 @@ function forecast(
8989
for i in 1:steps_ahead
9090
if isunivariate(model)
9191
expected_value[i] = [
92-
dot(model.system.Z[end - steps_ahead + i - 1], fo.a[end - steps_ahead + i - 1]) +
93-
model.system.d[end - steps_ahead + i - 1],
92+
dot(forecasting_model.system.Z[end - steps_ahead + i], fo.a[end - steps_ahead + i - 1]) +
93+
forecasting_model.system.d[end - steps_ahead + i],
9494
]
9595
else
9696
expected_value[i] =
97-
model.system.Z[end - steps_ahead + i - 1] * fo.a[end - steps_ahead + i - 1] +
98-
model.system.d[end - steps_ahead + i - 1]
97+
forecasting_model.system.Z[end - steps_ahead + i] * fo.a[end - steps_ahead + i - 1] +
98+
forecasting_model.system.d[end - steps_ahead + i]
9999
end
100100
covariance[i] = fo.F[end - steps_ahead + i]
101101
end

src/models/unobserved_components.jl

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,4 +684,86 @@ function reinstantiate(model::UnobservedComponents, y::Vector{Fl}, X::Matrix{Fl}
684684
trend = model.trend,
685685
seasonal = model.seasonal,
686686
cycle = model.cycle)
687+
end
688+
689+
690+
# UnobservedComponents with explanatory requires a custom simulation
691+
692+
function simulate_scenarios(
693+
model::UnobservedComponents,
694+
steps_ahead::Int,
695+
n_scenarios::Int,
696+
new_exogenous::Matrix{Fl};
697+
filter::KalmanFilter=default_filter(model),
698+
) where Fl
699+
@assert steps_ahead == size(new_exogenous, 1) "new_exogenous must have the same dimension as steps_ahead"
700+
# Query the type of model elements
701+
fo = kalman_filter(model)
702+
last_state = fo.a[end]
703+
num_series = size(model.system.y, 2)
704+
705+
scenarios = Array{Fl,3}(undef, steps_ahead, num_series, n_scenarios)
706+
for s in 1:n_scenarios
707+
scenarios[:, :, s] = simulate(model, last_state, steps_ahead, new_exogenous)
708+
end
709+
return scenarios
710+
end
711+
712+
function simulate_scenarios(
713+
model::UnobservedComponents,
714+
steps_ahead::Int,
715+
n_scenarios::Int,
716+
new_exogenous::Array{Fl, 3};
717+
filter::KalmanFilter=default_filter(model),
718+
) where Fl
719+
@assert steps_ahead == size(new_exogenous, 1) "new_exogenous must have the same dimension of steps_ahead"
720+
@assert n_scenarios == size(new_exogenous, 3) "new_exogenous must have the same number of scenarios of n_scenarios"
721+
# Query the type of model elements
722+
fo = kalman_filter(model)
723+
last_state = fo.a[end]
724+
num_series = size(model.system.y, 2)
725+
726+
scenarios = Array{Fl,3}(undef, steps_ahead, num_series, n_scenarios)
727+
for s in 1:n_scenarios
728+
scenarios[:, :, s] = simulate(model, last_state, steps_ahead, new_exogenous[:, :, s])
729+
end
730+
return scenarios
731+
end
732+
733+
function simulate(
734+
model::UnobservedComponents,
735+
initial_state::Vector{Fl},
736+
n::Int,
737+
new_exogenous::Matrix{Fl};
738+
return_simulated_states::Bool=false,
739+
) where Fl
740+
sys = model.system
741+
m = size(sys.T[1], 1)
742+
y = Vector{Fl}(undef, n)
743+
alpha = Matrix{Fl}(undef, n + 1, m)
744+
# Sampling errors
745+
chol_H = sqrt(sys.H[1])
746+
chol_Q = cholesky_decomposition(sys.Q[1])
747+
standard_ε = randn(n)
748+
standard_η = randn(n + 1, size(sys.Q[1], 1))
749+
750+
num_exogenous = size(model.exogenous, 2)
751+
@assert num_exogenous == size(new_exogenous, 2) "You must have the same number of exogenous variables of the model."
752+
753+
# The first state of the simulation is the update of a_0
754+
alpha[1, :] .= initial_state
755+
sys.Z[1][end-num_exogenous+1:end] .= new_exogenous[1, :]
756+
y[1] = dot(sys.Z[1], initial_state) + sys.d[1] + chol_H * standard_ε[1]
757+
alpha[2, :] = sys.T[1] * initial_state + sys.c[1] + sys.R[1] * chol_Q.L * standard_η[1, :]
758+
# Simulate scenarios
759+
for t in 2:n
760+
sys.Z[t][end-num_exogenous+1:end] .= new_exogenous[t, :]
761+
y[t] = dot(sys.Z[t], alpha[t, :]) + sys.d[t] + chol_H * standard_ε[t]
762+
alpha[t + 1, :] = sys.T[t] * alpha[t, :] + sys.c[t] + sys.R[t] * chol_Q.L * standard_η[t, :]
763+
end
764+
765+
if return_simulated_states
766+
return y, alpha[1:n, :]
767+
end
768+
return y
687769
end

test/models/unobserved_components.jl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,4 +97,17 @@ end
9797
alpha = get_smoothed_state(smoother)
9898
@test size(alpha) == (144, 16)
9999

100+
@test_throws AssertionError simulate_scenarios(model, 10, 1000, ones(5, 2))
101+
model_sim = deepcopy(model)
102+
scenarios = simulate_scenarios(model_sim, 10, 100000, X_test)
103+
test_scenarios_adequacy_with_forecast(forec, scenarios)
104+
#build X_test as 3D array, where all the scenarios are the same
105+
X_test_3d = ones(10, 3, 500)
106+
for i in 1:500
107+
X_test_3d[:, :, i] = X_test
108+
end
109+
model_sim2 = deepcopy(model)
110+
scenarios = simulate_scenarios(model_sim2, 10, 500, X_test_3d)
111+
test_scenarios_adequacy_with_forecast(forec, scenarios)
112+
100113
end

0 commit comments

Comments
 (0)