Add filer and smoothing for regime switching model #250
Add filer and smoothing for regime switching model #250Shunsuke-Hori wants to merge 17 commits intoQuantEcon:masterfrom
Conversation
cc7768
left a comment
There was a problem hiding this comment.
This looks cool! Thanks for adding this @Shunsuke-Hori
I haven't walked through and double checked the math, but have added some code comments.
Are the tests based on other code that make you relatively confident they're correct?
| end | ||
|
|
||
| """ | ||
| - `g::Function`: Parameterized `Function` that recieves three arguments |
There was a problem hiding this comment.
We should break these lines a little more frequently than they're currently broken -- I don't remember what the maximum number of characters per line we decided to enforce for Jula though.
@sglyon should remember.
There was a problem hiding this comment.
@cc7768 It totally slipped my mind. I think it is 80.
| mutable struct RegimeSwitchingModel{TD <: AbstractArray, TP <: AbstractMatrix, TPS <: AbstractMatrix} | ||
| g::Function | ||
| y::TD | ||
| parameter |
There was a problem hiding this comment.
Is there a reason that we don't want this typed? By having a mutable structure and not having this argument typed, we can change it to anything we want -- For example, I can set rsm.parameter = 1 and it won't complain. Is there a case where this changes types?
There was a problem hiding this comment.
Is there a reason that we don't want this typed?
Kind of, I thought some may want to use user-defined struct like this rather than NamedTuple. But I also think it should be typed. Maybe I can force it to be NamedTuple.
There was a problem hiding this comment.
Perfect -- This is a use case Julia should handle very cleanly! You can add a type parameter to RegimeSwitchingModel and then just refer to that type parameter when typing the argument. I'd make suggestion, but I can only figure out how to suggest one line change, but here's what it would look like:
mutable struct RegimeSwitchingModel{TD <: AbstractArray, TP <: AbstractMatrix, TPS <: AbstractMatrix, TPRM}
g::Function
y::TD
parameter::TPRM
...This will allow the functions to specialize to the user's type (there will be a new type of RegimeSwitchingModel for every type that gets put in as a parameter) and still prevent people from doing "weird" changes to the parameter data.
There was a problem hiding this comment.
Oh I see, that makes sense and is way better. Thank you!!
| period 0. | ||
|
|
||
| ##### Returns | ||
| - `p`: likelihood at each period |
There was a problem hiding this comment.
It wouldn't hurt to emphasize that the returns are each of size T x M where T is the number of observations and M is the number of regimes.
There was a problem hiding this comment.
That's fair. I'll do so.
| T = size(y, 1) | ||
| p = Vector{Float64}(undef, T) | ||
| logL = 0 | ||
| for t in 1:T |
There was a problem hiding this comment.
I like how the code is structured that allows you to write such a simple (and beautiful!) for-loop to do the core of the computation.
| rsm.prob_smoothed[t, s] = sum(p_s_joint_smoothed[t, s, :]) | ||
| end | ||
| end | ||
| verbose ? (return p_s_joint_smoothed) : (return nothing) |
There was a problem hiding this comment.
Does this if statement in return cause confusion to the compiler? This could prevent it from inferring the type of the output of this function. Is there a reason to not just return p_s_joint_smoothed?
There was a problem hiding this comment.
I don't think it does create a problem -- It identifies it as a union of nothing and the array type:
julia> @code_warntype test(rsm, true)
Variables
#self#::Core.Compiler.Const(test, false)
rsm::Main.QuantEcon.RegimeSwitchingModel{Array{Float64,2},Array{Float64,2},Array{Float64,2}}
verbose::Bool
Body::Union{Nothing, Array{Float64,1}}
1 ─ %1 = Main.randn(10)::Array{Float64,1}
│ %2 = (:verbose,)::Core.Compiler.Const((:verbose,), false)
│ %3 = Core.apply_type(Core.NamedTuple, %2)::Core.Compiler.Const(NamedTuple{(:verbose,),T} where T<:Tuple, false)
│ %4 = Core.tuple(verbose)::Tuple{Bool}
│ %5 = (%3)(%4)::NamedTuple{(:verbose,),Tuple{Bool}}
│ %6 = Main.QuantEcon.smooth!::Core.Compiler.Const(Main.QuantEcon.smooth!, false)
│ %7 = Core.kwfunc(%6)::Core.Compiler.Const(getfield(Main.QuantEcon, Symbol("#kw##smooth!"))(), false)
│ %8 = Main.QuantEcon.smooth!::Core.Compiler.Const(Main.QuantEcon.smooth!, false)
│ %9 = (%7)(%5, %8, rsm, %1)::Union{Nothing, Array{Float64,1}}
└── return %9
julia> @code_warntype test(rsm, false)
Variables
#self#::Core.Compiler.Const(test, false)
rsm::Main.QuantEcon.RegimeSwitchingModel{Array{Float64,2},Array{Float64,2},Array{Float64,2}}
verbose::Bool
Body::Union{Nothing, Array{Float64,1}}
1 ─ %1 = Main.randn(10)::Array{Float64,1}
│ %2 = (:verbose,)::Core.Compiler.Const((:verbose,), false)
│ %3 = Core.apply_type(Core.NamedTuple, %2)::Core.Compiler.Const(NamedTuple{(:verbose,),T} where T<:Tuple, false)
│ %4 = Core.tuple(verbose)::Tuple{Bool}
│ %5 = (%3)(%4)::NamedTuple{(:verbose,),Tuple{Bool}}
│ %6 = Main.QuantEcon.smooth!::Core.Compiler.Const(Main.QuantEcon.smooth!, false)
│ %7 = Core.kwfunc(%6)::Core.Compiler.Const(getfield(Main.QuantEcon, Symbol("#kw##smooth!"))(), false)
│ %8 = Main.QuantEcon.smooth!::Core.Compiler.Const(Main.QuantEcon.smooth!, false)
│ %9 = (%7)(%5, %8, rsm, %1)::Union{Nothing, Array{Float64,1}}
└── return %9
It does add "complexity" relative to return p_s_joint_smoothed though, so is there a reason not to return it always?
There was a problem hiding this comment.
is there a reason not to return it always?
No, when I realized p_s_joint_smoothed should be returned, I left return nothing case so that I didn't need to make change to existing parts, but that was a silly idea. Now I agree with you, it's just confusing. I'll fix it.
|
@cc7768 Thank you for your review!! Your comments improve the codes a lot. The test is based on python statsmodels, which I assumed correct. (https://www.statsmodels.org/dev/examples/notebooks/generated/markov_autoregression.html) It seems I was more stupid than I thought. I'll update the codes soon. |
|
Excellent -- That sounds like a great place to find a test for the code :) Many thanks for putting this together and for addressing the "nitpicks" that I have. |
|
I updated the code. Could anyone do another round of review? |
Filtering and smoothing features for regime switching model are added. Some tests for them are added too.