Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/StateSpaceLearning.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module StateSpaceLearning

using LinearAlgebra, Statistics, GLMNet, Distributions, SparseArrays
using LinearAlgebra, Statistics, GLMNet, Distributions, SparseArrays, Random

abstract type StateSpaceLearningModel end

Expand Down
7 changes: 6 additions & 1 deletion src/fit_forecast.jl
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,12 @@ function fit!(

ε, fitted = get_fit_and_residuals(estimation_ε, coefs, model.X, valid_indexes, T)

components_ts = get_components_ts(model, components)

if typeof(model.y) <: Vector
output = Output(coefs, ε, fitted, residuals_variances, valid_indexes, components)
output = Output(
coefs, ε, fitted, residuals_variances, valid_indexes, components, components_ts
)
else
output = Output[]
for i in eachindex(coefs)
Expand All @@ -77,6 +81,7 @@ function fit!(
residuals_variances[i],
valid_indexes,
components[i],
components_ts[i],
),
)
end
Expand Down
277 changes: 277 additions & 0 deletions src/models/structural_model.jl
Original file line number Diff line number Diff line change
Expand Up @@ -956,4 +956,281 @@ function get_innovation_simulation_X(
end
end

"""
get_μ(model::StructuralModel, components::Dict, ν::Vector{AbstractFloat})::Tuple{Vector{AbstractFloat}, Vector{AbstractFloat}}

Returns the level component and associated innovation vectors.

# Arguments
- `model::StructuralModel`: StructuralModel object.
- `components::Dict` : Components dict.
- `ν::Vector{AbstractFloat}`: Time-series of the slope component.

# Returns
- `Tuple{Vector{AbstractFloat}, Vector{AbstractFloat}}`: Tuple of vectors containing the time series state and innovations.

"""
function get_μ(
model::StructuralModel, components::Dict, ν::Vector{AbstractFloat}
)::Tuple{Vector{AbstractFloat},Vector{AbstractFloat}}
T = size(model.y, 1)
μ = Vector{AbstractFloat}(undef, T)

if model.level
μ[1] = components["μ1"]["Coefs"][1]
else
μ[1] = 0.0
end

if model.stochastic_level
ξ = vcat(0.0, components["ξ"]["Coefs"], 0.0)
else
ξ = zeros(AbstractFloat, T)
end

for t in 2:T
μ[t] = μ[t - 1] + ν[t - 1] + ξ[t]
end

return μ, ξ
end

"""
get_ν(model::StructuralModel, components::Dict)::Tuple{Vector{AbstractFloat}, Vector{AbstractFloat}}

Returns the slope component and associated innovation vectors.

# Arguments
- `model::StructuralModel`: StructuralModel object.
- `components::Dict`: Components dict..

# Returns
- `Tuple{Vector{AbstractFloat}, Vector{AbstractFloat}}`: Tuple of vectors containing the time series state and innovations.

"""
function get_ν(
model::StructuralModel, components::Dict
)::Tuple{Vector{AbstractFloat},Vector{AbstractFloat}}
T = size(model.y, 1)
ν = Vector{AbstractFloat}(undef, T)

ζ_ω_threshold = model.ζ_ω_threshold

if model.trend
ν[1] = components["ν1"]["Coefs"][1]
else
ν[1] = 0.0
end

if model.stochastic_trend
ζ = vcat(0.0, 0.0, components["ζ"]["Coefs"], zeros(ζ_ω_threshold))
else
ζ = zeros(AbstractFloat, T)
end

for t in 2:T
ν[t] = ν[t - 1] + ζ[t]
end

return ν, ζ
end

"""
get_γ(model::StructuralModel, components::Dict, s::Int)::Tuple{Vector{AbstractFloat}, Vector{AbstractFloat}}

Returns the seasonality component and associated innovation vectors.

# Arguments
- `model::StructuralModel`: StructuralModel object.
- `components::Dict`: Components dict.
- `s::Int`: Seasonal frequency.

# Returns
- `Tuple{Vector{AbstractFloat}, Vector{AbstractFloat}}`: Tuple of vectors containing the time series state and innovations.

"""
function get_γ(
model::StructuralModel, components::Dict, s::Int
)::Tuple{Vector{AbstractFloat},Vector{AbstractFloat}}
T = size(model.y, 1)
γ = Vector{AbstractFloat}(undef, T)

ζ_ω_threshold = model.ζ_ω_threshold

if model.seasonal
γ[1:s] = components["γ1_$(s)"]["Coefs"]
else
γ[1:s] = zeros(AbstractFloat, s)
end

if model.stochastic_seasonal
ω = vcat(zeros(s - 1), components["ω_$(s)"]["Coefs"], zeros(ζ_ω_threshold))
else
ω = zeros(AbstractFloat, T)
end

for t in (s + 1):T
γ[t] = -sum(γ[t - j] for j in 1:(s - 1)) + ω[t]
end

return γ, ω
end

"""
get_components_ts(model::StructuralModel, components::Dict)::Dict

Returns a dictionary with the time series state and innovations for each component.

# Arguments
- `model::StructuralModel`: StructuralModel object.
- `components::Dict`: Components dict.

# Returns
- `Dict`: Dictionary of time-series states and innovations.

"""
function get_components_ts(model::StructuralModel, components::Dict)::Dict
freq_seasonal = model.freq_seasonal
components_ts_dict = Dict()

ν, ζ = get_ν(model, components)
μ, ξ = get_μ(model, components, ν)

components_ts_dict["ν"] = ν
components_ts_dict["μ"] = μ
components_ts_dict["ζ"] = ζ
components_ts_dict["ξ"] = ξ

for s in freq_seasonal
γ, ω = get_γ(model, components, s)

components_ts_dict["γ_$s"] = γ
components_ts_dict["ω_$s"] = ω
end

return components_ts_dict
end

"""
get_components_ts(model::StructuralModel, components::Vector{Dict})::Vector{Dict}

Returns a vector of dictionaries with the time series state and innovations for each component, of each dependent time-series.

# Arguments
- `model::StructuralModel`: StructuralModel object.
- `components::Vector{Dict}`: Vector of components dict.

# Returns
- `Vector{Dict}`: Vector of dictionaries with the time-series states and innovations.

"""
function get_components_ts(
model::StructuralModel, components::Vector{Dict}
)::Vector{Dict} where {Al}
components_ts = []
freq_seasonal = model.freq_seasonal

for component in components
components_ts_dict = Dict()

ν, ζ = get_ν(model, component)
μ, ξ = get_μ(model, component, ν)

components_ts_dict["ν"] = ν
components_ts_dict["μ"] = μ
components_ts_dict["ζ"] = ζ
components_ts_dict["ξ"] = ξ

for s in freq_seasonal
γ, ω = get_γ(model, component, s)

components_ts_dict["γ_$s"] = γ
components_ts_dict["ω_$s"] = ω
end

push!(components_ts, components_ts_dict)
end

return components_ts
end

"""
simulate_states(model::StructuralModel, steps_ahead::Int, N_scenarios::Int)::Vector{Dict}

Returns a vector of dictionaries with the scenarios of each component, for each dependent time-series.

# Arguments
- `model::StructuralModel`: StructuralModel object.
- `steps_ahead::Int`: Number of steps ahead for forecasting.
- `N_scenarios::Int`: Number of scenarios.

# Returns
- `Vector{Dict}`: Vector of dictionaries with the scenarios of the components of each dependent time-series.

"""
function simulate_states(
model::StructuralModel, steps_ahead::Int, N_scenarios::Int
)::Vector{Dict}
Random.seed!(1234)

freq_seasonal = model.freq_seasonal
T = size(model.y, 1)
idxs = rand(1:T, steps_ahead, N_scenarios)

components_ts_vec = []
scenarios_ts = []
if typeof(model.output) == Vector{StateSpaceLearning.Output}
for i in eachindex(model.output)
push!(components_ts_vec, get_components_ts(model, model.output[i].components))
end
else
push!(components_ts_vec, get_components_ts(model, model.output.components))
end

for components_ts in components_ts_vec
scenarios_ts_dict = Dict()

scenarios_ts_dict["μ"] = Matrix{AbstractFloat}(undef, steps_ahead, N_scenarios)
scenarios_ts_dict["ν"] = Matrix{AbstractFloat}(undef, steps_ahead, N_scenarios)

for f in freq_seasonal
scenarios_ts_dict["γ_$f"] = Matrix{AbstractFloat}(
undef, steps_ahead, N_scenarios
)
end

for s in 1:N_scenarios
ν_vec = vcat(components_ts["ν"], zeros(steps_ahead))
μ_vec = vcat(components_ts["μ"], zeros(steps_ahead))
γ_matrix = vcat(
hcat([components_ts["γ_$s"] for s in freq_seasonal]...),
zeros(steps_ahead, length(freq_seasonal)),
)

for t in 1:steps_ahead
ν_vec[T + t] = ν_vec[T + t - 1] + components_ts["ζ"][idxs[t, s]]
μ_vec[T + t] =
μ_vec[T + t - 1] + ν_vec[T + t - 1] + components_ts["ξ"][idxs[t, s]]

for (i, f) in enumerate(freq_seasonal)
γ_matrix[T + t, i] =
-sum(γ_matrix[T + t - j, i] for j in 1:(f - 1)) +
components_ts["ω_$f"][idxs[t, s]]
end
end

scenarios_ts_dict["μ"][:, s] = μ_vec[(T + 1):(T + steps_ahead)]
scenarios_ts_dict["ν"][:, s] = ν_vec[(T + 1):(T + steps_ahead)]

for (i, f) in enumerate(freq_seasonal)
scenarios_ts_dict["γ_$f"][:, s] = γ_matrix[(T + 1):(T + steps_ahead), i]
end
end

push!(scenarios_ts, scenarios_ts_dict)
end

return scenarios_ts
end

isfitted(model::StructuralModel) = isnothing(model.output) ? false : true
1 change: 1 addition & 0 deletions src/structs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@ mutable struct Output
residuals_variances::Dict
valid_indexes::Vector{Int}
components::Dict
components_ts::Dict
end
Loading