Interface for Multivariate processes - #92
Conversation
- Added mark distribution to the process
- Added `univariate_process.jl` and `multivariate_process.jl` files defining the common interfaces - Updated the multivariate processes to comply with the interface - Split univariate Hawkes implementation into thre files
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
|
I figured it would be a good idea to split this PR in two. |
|
Is this ready for me to review? |
|
Sorry. I guess I forgot to request the review. But it should be ready, yes |
| descendants = T[] | ||
| next_gen = immigrants | ||
| while !isempty(next_gen) | ||
| # OPTIMIZE: Can this be improved by avoiding allocations of `curr_gen` and `next_gen`? Or does the compiler take care of that? |
There was a problem hiding this comment.
probably. we would want to make a workspace type that we use inplace operations on
| """ | ||
| abstract type AbstractMultivariateProcess <: AbstractPointProcess end | ||
|
|
||
| Base.ndims(pp::AbstractMultivariateProcess) = length(pp.mark_dist) |
There was a problem hiding this comment.
This assumes there is a mark_dist field but IndependentMultivariate does not have one?
There was a problem hiding this comment.
Yes, IndependentMultivariateProcess is a bit different, since we are not really building a new process, but only gluing a bunch of processes together.
For new multivariate processes, I would expect we use the interface for mark distributions. The idea is that the mark distributions at time t are independent conditional on the history up to t, so we can model them separetely (well, technically not really true, only for processes in which the probability of simultaneous events is 0, which are all interesting processes I am aware of)
| @@ -0,0 +1,102 @@ | |||
| """ | |||
| StatsAPI.fit(rng, ::Type{HawkesProcess{T}}, h::History; step_tol::Float64 = 1e-6, max_iter::Int = 1000) where {T<:Real} | |||
| end | ||
|
|
||
| pp_est = fit(PP, h) | ||
| pp_est = fit(PP, h)::AbstractPointProcess # JET complains if not specified |
There was a problem hiding this comment.
this is bein caused by the type stability issue i'm p sure
There was a problem hiding this comment.
This is the message I get.
┌ MonteCarloTest(S::Type{<:Statistic}, PP::Type{<:AbstractPointProcess}, h::History; kwargs...) @ PointProcesses /home/jkling/projects/PointProcesses.jl/src/HypothesisTests/PPTests/monte_carlo_test.jl:114
│ no matching method found kwcall(::NamedTuple, ::Type{MonteCarloTest}, ::Type{<:Statistic}, ::Rician, ::History) (1/4 union split): Core.kwcall(merge(NamedTuple()::@NamedTuple{}, kwargs::@kwargs{…})::NamedTuple, MonteCarloTest, S::Type{<:Statistic}, pp_est::Union{Rician, HawkesProcess, IndependentMultivariateProcess, PoissonProcess}, h::History)
└────────────────────
Apparently, the static analysis thinks that Rician is a possible return type of fit (Rician is a Distributions.Distribution). Not sure how to deal with this if not by type annotating this line.
|
|
||
| # Estimate process and calculate statistic from data | ||
| pp_est = fit(PP, h; rng=rng) | ||
| pp_est = fit(PP, h; rng=rng)::AbstractPointProcess # JET complains if not specified |
c3eec46 to
774c595
Compare
774c595 to
4052588
Compare
| end | ||
|
|
||
| """ | ||
| function StatsAPI.fit( |
There was a problem hiding this comment.
These fit methods e.g., fit(HawkesProcess, h) were delted yet still referenced in docs (see index.md, hawkes.md, and montecarlo.md)
| sort!(sim) | ||
| h = History(eltype(sim)[], tmin, tmax, eltype(hp.mark_dist)[], Nothing[], 1) | ||
| for event in sim | ||
| push!(h, event, sample_mark(hp, event, h)) |
There was a problem hiding this comment.
| push!(h, event, sample_mark(hp, event, h)) | |
| push!(h, event, sample_mark(rng, hp, event, h)) |
| transformed_times = [zeros(T, nb_events(h, d)) for d in 1:ndims(h)] | ||
| for d in 1:ndims(pp) |
There was a problem hiding this comment.
A 3-dim process with a history whose keyword constructor computed N=length(unique(dims)) = 2, which split_into_chunks would do, would cause a BBounds error. I think changiing transformed tiems to index by 1:ndims(pp) would fix.
There was a problem hiding this comment.
but should we allow for time_change to be applied with process and history with different dimensions? I think it is better for split_into_chunks to guarantee all the chunks have the same number of dimensions as the original history.
There was a problem hiding this comment.
sure, that makes sense. In that case we probably want to thread N through:
chunk = History(times, a, b, marks, dims, h.N; check_args=false)
| return sum(log.(hp.μ .+ (hp.α .* A))) - # Value of intensity at each event | ||
| (hp.μ * duration(h)) - # Integral of base rate | ||
| ((hp.α / hp.ω) * sum(1 .- exp.(-hp.ω .* (duration(h) .- h.times)))) + # Integral of each kernel | ||
| sum(log.([densityof(hp.mark_dist, t, h, m) for (t, m) in zip(h.times, h.marks)])) # Loglikelihood of marks |
There was a problem hiding this comment.
| sum(log.([densityof(hp.mark_dist, t, h, m) for (t, m) in zip(h.times, h.marks)])) # Loglikelihood of marks | |
| sum(log(densityof(hp.mark_dist, t, h, m)) for (t, m) in zip(h.times, h.marks); init=0.0) # Loglikelihood of marks |
| return PoissonProcess(λ, mark_dist) | ||
| end | ||
|
|
||
| function StatsAPI.fit(pptype::Type{PoissonProcess{R,D}}, args...; kwargs...) where {R,D} |
There was a problem hiding this comment.
this deletion means we no longer support weighted fitting. is that intentional?
There was a problem hiding this comment.
I think I was testing to see if this was the origin of the problem with JET and forgot to add back. Anyway, good catch
There was a problem hiding this comment.
Even though this is not really in tune with the rest of the package anymore. Not sure if we should keep it, or try to implement something similar for the other processes, or get rid of it...
| end | ||
| return sum(log.(hp.μ .+ (hp.α .* A))) - # Value of intensity at each event | ||
| (hp.μ * duration(h)) - # Integral of base rate | ||
| ((hp.α / hp.ω) * sum(1 .- exp.(-hp.ω .* (duration(h) .- h.times)))) + # Integral of each kernel |
There was a problem hiding this comment.
should be h.tmax .- h.times ?
| function ground_intensity(hp::HawkesProcess, t, h::History) | ||
| activation = sum(exp.(hp.ω .* (@view h.times[1:(searchsortedfirst(h.times, t) - 1)]))) | ||
| return hp.μ + (hp.α * activation / exp(hp.ω * t)) | ||
| end |
There was a problem hiding this comment.
need the shift invariant form
| dims = fill(nothing, length(times)) | ||
| N = 1 | ||
| else | ||
| N = length(unique(dims)) |
There was a problem hiding this comment.
This counts distinct dim values present, not the actual dimension count
There was a problem hiding this comment.
yes. In the case we are passing vectors, instead of one vector per dimension, this is the only way to deduce the number of dimensions in the history.
I agree this has some weird edge cases. We should probably think in a more structured way about the interface for the constructor. Perhaps this is trying to be too general.
There was a problem hiding this comment.
some ideas:
- Deduce with
N = maximum(dims)instead oflength(unique(dims)) - Additionally accept an explicit
N/ndimskeyword so callers likesplit_into_chunkscan preserve the parent's dimension count
There was a problem hiding this comment.
In the split_into_chunks I planned to do exactly this.
About deducing the dimensions with max(dims), this also has edge cases in which there are no events in the dimensions with higher values. Perhaps for multivariate histories the dimension must be passed, if the number of dimensions is nos passed we assume it is univariate?
| any((μ, α, ω) .< 0) && | ||
| throw(DomainError((μ, α, ω), "All parameters must be non-negative.")) | ||
| (α > 0 && α >= ω) && | ||
| throw(DomainError((α, ω), "Parameter ω must be strictly smaller than α")) |
|
found a few more bugs. i'll see if i can figure out the JET thing |
- Fixed constructor for empty history - History does not deduce number of dimensions - Small fixes for Hawkes processes
1328b5e to
5d08bc9
Compare
|
I think I got everything, except for the JET warning. |
|
i'll try to look at this soon |
This PR has the objective of implementing multivariate Hawkes processes.
In the process, some other modifications were carried out.
AbstractUnivariateProcessandAbstractMultivariateProcess