Skip to content

Multivariate history#79

Open
JoseKling wants to merge 7 commits intomainfrom
MultivariateHistory
Open

Multivariate history#79
JoseKling wants to merge 7 commits intomainfrom
MultivariateHistory

Conversation

@JoseKling
Copy link
Copy Markdown
Owner

@JoseKling JoseKling commented Mar 27, 2026

Implementation of multivariate processes. Discussion in Issue #73

  • History

Contains an array times with the event times, an array marks with corresponding marks, and another array dims with corresponding dimension

  • UnivariatePointProcess and MultivariatePointProcess

All previous implementations remained unchanged, but now these processes are subtypes of UnivariatePointProcess.
Multivariate processes were implemented separetely as MultivariatePointProcess. This allowed the implementation of IndependentMultivariateProcess and MultivariatePoissonProcess simply using an array of univariate processes.

- Change the models to hold vectors of parameters instead of single
  parameters
- Update methods to return multivariate histories
- Update tests

Steps
- Update general interface for `AbstractPointProcess`
- Update `simulation.jl`
- Update homogeneous Poisson and related methods
- Update inhomogeneous Poisson and related methods
- Update hypothesis tests
    - They work only for univariate processes. Generalization for the
      futrue
- Ignore Hawkes processes, since they will be implemented in another PR
- Takes an array of parameters
- Methods like `ground_intensity` return an array
- Added methods with an argument `d::Int` to get the values from
  specific dimensions
This reverts commit 90f08d6.
Instead of making all processes multivariate, implement tem separately.
- `AbstractMultivariateProcess`
- `IndependenteMultivariateProcess`
- `DependentMultivariateProcess`?
- Other processes, such as `MultivariateHawkesProcess`
Univariate and multivariate processes are different
types.

Implementation for univariate processes remained unchanged.
Copy link
Copy Markdown
Contributor

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remaining comments which cannot be posted as a review comment to avoid GitHub Rate Limit

JuliaFormatter

[JuliaFormatter] reported by reviewdog 🐶

InhomogeneousPoissonProcess{
PiecewiseConstantIntensity{Float64},Dirac{Nothing}
},


[JuliaFormatter] reported by reviewdog 🐶

InhomogeneousPoissonProcess{
PiecewiseConstantIntensity{Float64},Dirac{Nothing}
},


[JuliaFormatter] reported by reviewdog 🐶

InhomogeneousPoissonProcess{
PiecewiseConstantIntensity{Float64},Dirac{Nothing}
},


[JuliaFormatter] reported by reviewdog 🐶

InhomogeneousPoissonProcess{
PiecewiseConstantIntensity{Float64},Dirac{Nothing}
},


[JuliaFormatter] reported by reviewdog 🐶

InhomogeneousPoissonProcess{
PiecewiseConstantIntensity{Float32},Dirac{Nothing}
},


[JuliaFormatter] reported by reviewdog 🐶

pp_est.intensity_function.coefficients[i] -
intensity_true.coefficients[i],


[JuliaFormatter] reported by reviewdog 🐶

mark_dists = [Normal(), Exponential(1.0), Uniform(0,1)]


[JuliaFormatter] reported by reviewdog 🐶

@test intensity(pp1, 0, 0, h) == [intensity(pp1.processes[d], 0, 0, h) for d in 1:ndims(pp1)]


[JuliaFormatter] reported by reviewdog 🐶

@test log_intensity(pp1, 0, 0, h) [log_intensity(pp1.processes[d], 0, 0, h) for d in 1:ndims(pp1)]


[JuliaFormatter] reported by reviewdog 🐶

pp_est1 = fit(fill(PoissonProcess{Float64, Normal}, 3), h1)
pp_est2 = fit(fill(PoissonProcess{Float64, Normal}, 3), h2)


[JuliaFormatter] reported by reviewdog 🐶


[JuliaFormatter] reported by reviewdog 🐶


[JuliaFormatter] reported by reviewdog 🐶

Comment on lines +36 to +37
ground_intensity_bound(bpp::BoundedPointProcess, args...) = ground_intensity_bound(bpp.pp, args...)
integrated_ground_intensity(bpp::BoundedPointProcess, args...) = integrated_ground_intensity(bpp.pp, args...)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
ground_intensity_bound(bpp::BoundedPointProcess, args...) = ground_intensity_bound(bpp.pp, args...)
integrated_ground_intensity(bpp::BoundedPointProcess, args...) = integrated_ground_intensity(bpp.pp, args...)
function ground_intensity_bound(bpp::BoundedPointProcess, args...)
ground_intensity_bound(bpp.pp, args...)
end
function integrated_ground_intensity(bpp::BoundedPointProcess, args...)
integrated_ground_intensity(bpp.pp, args...)
end

vec_dims = vcat(dims...)
end
return History(
vec_times[perm], tmin, tmax, vec_marks[perm], vec_dims[perm], length(times); check_args=check_args
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
vec_times[perm], tmin, tmax, vec_marks[perm], vec_dims[perm], length(times); check_args=check_args
vec_times[perm],
tmin,
tmax,
vec_marks[perm],
vec_dims[perm],
length(times);
check_args=check_args,


Return the sorted vector of event times for `h` in dimension `d`.
"""
event_times(h::History, d::Int) = h.N == 1 && d == 1 ? h.times : (@view h.times[h.dims .== d])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
event_times(h::History, d::Int) = h.N == 1 && d == 1 ? h.times : (@view h.times[h.dims .== d])
function event_times(h::History, d::Int)
h.N == 1 && d == 1 ? h.times : (@view h.times[h.dims .== d])
end

times = event_times(h, d)::AbstractVector
i_min = searchsortedfirst(times, tmin)
i_max = searchsortedfirst(times, tmax)
return @view times[i_min : (i_max - 1)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
return @view times[i_min : (i_max - 1)]
return @view times[i_min:(i_max - 1)]


Return the vector of event marks in dimension `d` of `h`, sorted according to their event times.
"""
event_marks(h::History, d::Int) = h.N == 1 && d == 1 ? h.marks : (@view h.marks[h.dims .== d])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
event_marks(h::History, d::Int) = h.N == 1 && d == 1 ? h.marks : (@view h.marks[h.dims .== d])
function event_marks(h::History, d::Int)
h.N == 1 && d == 1 ? h.marks : (@view h.marks[h.dims .== d])
end

vcat, (fill(w, nb_events(h)) for (w, h) in zip(weights, histories))
)
return PoissonProcessStats(total_nb_events, total_duration, total_marks, total_weights)
return PoissonProcessStats(total_nb_events, total_duration, total_marks, total_dims, total_weights)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
return PoissonProcessStats(total_nb_events, total_duration, total_marks, total_dims, total_weights)
return PoissonProcessStats(
total_nb_events, total_duration, total_marks, total_dims, total_weights
)

Comment on lines +456 to +458
InhomogeneousPoissonProcess{
ExponentialIntensity{Float64},Normal,IntegrationConfig
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
InhomogeneousPoissonProcess{
ExponentialIntensity{Float64},Normal,IntegrationConfig
},
InhomogeneousPoissonProcess{ExponentialIntensity{Float64},Normal,IntegrationConfig},

Comment on lines +469 to +471
@test 0.5 * intensity_true.a <=
pp_est.intensity_function.a <=
3.0 * intensity_true.a
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
@test 0.5 * intensity_true.a <=
pp_est.intensity_function.a <=
3.0 * intensity_true.a
@test 0.5 * intensity_true.a <= pp_est.intensity_function.a <= 3.0 * intensity_true.a

Comment on lines +485 to +487
InhomogeneousPoissonProcess{
SinusoidalIntensity{Float64},Uniform,IntegrationConfig
},
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
InhomogeneousPoissonProcess{
SinusoidalIntensity{Float64},Uniform,IntegrationConfig
},
InhomogeneousPoissonProcess{SinusoidalIntensity{Float64},Uniform,IntegrationConfig},

Comment on lines +583 to +585
intensity_cov3 = LinearCovariateIntensity(
2.0, [0.1, 0.2, 0.3], [cov1, cov2, cov3]
)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
intensity_cov3 = LinearCovariateIntensity(
2.0, [0.1, 0.2, 0.3], [cov1, cov2, cov3]
)
intensity_cov3 = LinearCovariateIntensity(2.0, [0.1, 0.2, 0.3], [cov1, cov2, cov3])

@codecov
Copy link
Copy Markdown

codecov bot commented Apr 12, 2026

Codecov Report

❌ Patch coverage is 94.05405% with 11 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/multivariate/independent_multivariate.jl 85.00% 6 Missing ⚠️
src/history.jl 96.55% 3 Missing ⚠️
src/multivariate/multivariate_poisson_process.jl 84.61% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

# Fields
- `processes::Vector{P}`: vector of univariate point processes, one for each dimension.
"""
struct IndependentMultivariateProcess{P<:AbstractUnivariateProcess} <: AbstractMultivariateProcess
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
struct IndependentMultivariateProcess{P<:AbstractUnivariateProcess} <: AbstractMultivariateProcess
struct IndependentMultivariateProcess{P<:AbstractUnivariateProcess} <:
AbstractMultivariateProcess

return [mark_distribution(pp.processes[d], t) for d in 1:ndims(pp)]
end

function intensity(pp::IndependentMultivariateProcess, m, t, h, d)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
function intensity(pp::IndependentMultivariateProcess, m, t, h, d)
function intensity(pp::IndependentMultivariateProcess, m, t, h, d)

return intensity(pp.processes[d], m, t, h)
end

function intensity(pp::IndependentMultivariateProcess, m, t, h)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
function intensity(pp::IndependentMultivariateProcess, m, t, h)
function intensity(pp::IndependentMultivariateProcess, m, t, h)

return [intensity(pp, m, t, h, d) for d in 1:ndims(pp)]
end

log_intensity(pp::IndependentMultivariateProcess, m, t, h, d) = log(intensity(pp, m, t, h, d))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
log_intensity(pp::IndependentMultivariateProcess, m, t, h, d) = log(intensity(pp, m, t, h, d))
function log_intensity(pp::IndependentMultivariateProcess, m, t, h, d)
log(intensity(pp, m, t, h, d))
end

end

function DensityInterface.logdensityof(pp::IndependentMultivariateProcess, h::History)
return sum(logdensityof(pp.processes[d], History(event_times(h, d), h.tmin, h.tmax, event_marks(h, d))) for d in 1:ndims(pp))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
return sum(logdensityof(pp.processes[d], History(event_times(h, d), h.tmin, h.tmax, event_marks(h, d))) for d in 1:ndims(pp))
return sum(
logdensityof(
pp.processes[d], History(event_times(h, d), h.tmin, h.tmax, event_marks(h, d))
) for d in 1:ndims(pp)
)

@test mark_distribution(pp1, 0, h, 2) == mark_dists[2]
@test intensity(pp1, 0, 0, h) == [intensity(pp1.processes[d], 0, 0, h) for d in 1:ndims(pp1)]
@test intensity(pp1, 0, 0, h, 1) == intensity(pp1.processes[1], 0, 0, h)
@test log_intensity(pp1, 0, 0, h) ≈ [log_intensity(pp1.processes[d], 0, 0, h) for d in 1:ndims(pp1)]
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
@test log_intensity(pp1, 0, 0, h) [log_intensity(pp1.processes[d], 0, 0, h) for d in 1:ndims(pp1)]
@test log_intensity(pp1, 0, 0, h)
[log_intensity(pp1.processes[d], 0, 0, h) for d in 1:ndims(pp1)]

Comment on lines +70 to +71
pp_est1 = fit(fill(PoissonProcess{Float64, Normal}, 3), h1)
pp_est2 = fit(fill(PoissonProcess{Float64, Normal}, 3), h2)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
pp_est1 = fit(fill(PoissonProcess{Float64, Normal}, 3), h1)
pp_est2 = fit(fill(PoissonProcess{Float64, Normal}, 3), h2)
pp_est1 = fit(fill(PoissonProcess{Float64,Normal}, 3), h1)
pp_est2 = fit(fill(PoissonProcess{Float64,Normal}, 3), h2)


@test l isa Real
@test isfinite(l)
end No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
end
end

@test σ_error1 < 0.1
@test μ_error2 < 0.1
@test σ_error2 < 0.1

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change

l = logdensityof(pp, h1)
l_est = logdensityof(pp_est1, h1)
@test l_est > l
end No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
end
end

@test event_marks(h_multi) == ["a", "c", "b", "d"]
@test event_dims(h_multi) == [1, 2, 1, 2]

@test_throws DomainError History(rand(3), 0, 1, rand(3), [1,2,3], 2)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
@test_throws DomainError History(rand(3), 0, 1, rand(3), [1,2,3], 2)
@test_throws DomainError History(rand(3), 0, 1, rand(3), [1, 2, 3], 2)

@test nb_events(h_multi) == 7
@test event_times(h_multi, 1) == [0.1, 0.5, 0.85, 0.9]
@test event_marks(h_multi, 1) == ["a", "b", "e", "g"]
end No newline at end of file
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
end
end

@JoseKling JoseKling marked this pull request as ready for review April 13, 2026 16:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant