Skip to content

Systematization of returned types in .*intensity methods #72

Closed

Description

@JoseKling

Currently the return types of the methods related to intensity do not follow any explicit logic.

As an example, If I have a process pp = PoissonProcess(Float32(1), Uniform()), then ground_intensity(pp) = 10f0, but intensity(pp, 0.5) = 1.0. Not too bad, but makes it hard to reason about types, especially considering other methods may include marks or other user input.

What are sensible return types for these methods? I think the process should be the only argument to determine the type of the intensity, so using pp as in the example above, the methods intensity, ground_intensity and integrated_ground_intensity should all return a Float32.

The only caveat is that, in general, the intensity is a Float, even if the process parameters are Ints. So, if pp = PoissonProcess(T(1), Uniform()), where T<:Real, then the return type of the three methods above should be float(T).

Thoughts?

Activity

  1. rsenne commented on Dec 20, 2025

    @rsenne
    Collaborator

    I agree with this. I think we want to promote to the type of the parameters. Though yes, I can see how an issue might arise if a user provides an Int as a parameter, in which case it’s a bit nonsensical for the intensities themselves to be integer-typed. That said, this also raises the question of why a user would want an Int as a parameter type in the first place.

    So, to your point, we probably need to define how this edge case should be handled. Admittedly, I’m not completely sure what the best choice is. In practice, the most reasonable parameter types are things like Float32, Float64, BigFloat, etc. Given that, the possible options seem to be:

    1. Make the intensity type match the parameter type, except in the case where the parameter type is an Int, in which case we promote to float(T).

    2. Always make the intensity type match the parameter type and simply accept that using an Int parameter is a strange (but technically allowed) choice.

    3. Restrict parameter types to <: AbstractFloat.

    My instinct is that (3) is not a great solution, since it unnecessarily constrains users and makes assumptions about what they might want to do, and would also mess up AD. Between (1) and (2), I lean toward (1): it gives sensible behavior in the common case, avoids obviously nonsensical return types, and still preserves the idea that the process parameters determine the numeric type of the intensity.

  2. JoseKling commented on Dec 21, 2025

    @JoseKling
    OwnerAuthor

    I agree that (3) is not ideal.

    (2) has the problem that if the parameters are Integers, then groind_intensity(pp, t) would produce a InexactError. So (1) seems like the best approach.

    Or we could use the idea of (3), but without actually restricting the types, just converting them before instantiation. At least for the cases we implemented, it does not make much sense for the types of the parameter to be Int anyway.

    I did not yet check the other methods, like simulate or fit. I believe fit is fine, but I think simulate has issues because of Distributions.jl.

  3. rsenne commented on Dec 21, 2025

    @rsenne
    Collaborator

    @gdalle I bet would have good insight here. He is the typing wizard. But yes, my gut says that something like (1) would be good/give the least friction between user desires and things that are reasonable in this context

  4. gdalle commented on Jan 6, 2026

    @gdalle
    Collaborator

    There are two questions here:

    • In functions like intensity(process, history), which of the arguments should determine the return type?
    • If the process has integer parameters, should we modify the return type ourselves?

    For both, I suggest you let Julia do its job and promote arguments to the more generic type.

    • If the process is in Float32 and the history in Float64, then you'll get Float64.
    • If the process has Int components and Float32 components, you'll get Float32.

    You'll save yourselves many headaches while preserving full type generality. Anything else will dramatically reduce the possibilities of your package. For instance, if you decide that only the process determines the type of the output, then you won't be able to differentiate a log-likelihood with respect to history dates, only with respect to process parameters.

  5. JoseKling commented on Jan 6, 2026

    @JoseKling
    OwnerAuthor
    • In functions like intensity(process, history), which of the arguments should determine the return type?

    I would argue that only the process should determine the return type, but if this is detrimental to the package, then it is not worth it.

    • If the process has Int components and Float32 components, you'll get Float32.

    The parameters of a process are already promoted to a single type, so this is not a problem. The issue is only if all the parameters are Int. HawkesProcess(1.0, 2, 3) returns a HawkesProcess{Float64}, but HawkesProcess(1, 2, 3)returns aHawkesProcess{Int}`.

    Then we just leave as it is and let the return types be determined by whatever calculations are done inside the methods, letting Julia promote the types when needed.

  6. gdalle commented on Jan 9, 2026

    @gdalle
    Collaborator

    I really wouldn't give too much thought to the case where the user inputs an integer for quantities like a Hawkes intensity or a history start date. In most cases is semantically wrong to use that type, so they'll find out soon enough either way

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions