Get type containing the types of a set?

Given a set of variables [a,b,c...], I want to find the type T such that typeof(a)<:T, typeof(b)<:T, ....

The reason I want to do this is that I have cache variables in my ODE system, which need to be able to adapt if one of the parameters or initial conditions is of type Particles or Dual. I know there is a dualcache thing in DiffEqBase.jl, but I would like a similar type of thing for arbitrary types.

Thanks!

Your question and use case seem a bit different. Your question is asking for the most specific type T such that all elements of x=[a,b,c,...] are subtypes. Here’s a solution

typetree(T::DataType) = typetree!([T],supertype(T))
function typetree!(list, T::DataType)
    push!(list,supertype(T))
    return typetree!(list,supertype(T))
end
typetree!(list,T::Type{Any}) = return list

x = Any[2.0,1,UInt8(0)]
types = typetree.(typeof.(x))
umbrella_type = first(intersect(types...))

This recursively builds type trees for each type in x and then finds the set intersection. Since intersect preserves ordering, the first element of the intersection is the “umbrella” type for the collection.

I’m sure there’s a more elegant solution. However, it seems like your application just needs to know if any one of the elements of x = [a,b,c...] are of a specific type, which you can do with any(typeof.(x).==Particle) or any(typeof.(x).==Dual).

1 Like

Your question and use case seem a bit different. Your question is asking for the most specific type T such that all elements of x=[a,b,c,...] are subtypes. Here’s a solution…

Thanks!

However, it seems like your application just needs to know if any one of the elements of x = [a,b,c...] are of a specific type, which you can do with any(typeof.(x).==Particle) or any(typeof.(x).==Dual)

Well, no I don’t think this works exactly. My use case was for my ODE function to work with any T <: Real as a parameter, so I need my cache to be some concrete type T2 such that T<:T2. I agree that it seems like overkill to use your solution for a problem where at most, I probably just have to add one or two lines if I want a new parameter type. I guess I was hoping there was a quick solution I was missing.

My use case was for my ODE function to work with any T <: Real as a parameter, so I need my cache to be some concrete type T2 such that T<:T2

Ah, gotcha. I didn’t realize how the cache factored in.

Check out “typejoin” as well (or maybe it’s called jointype… try both)

Does this answer the question?

Unfortunately not all concrete types <:Real are isbits so I can’t reinterpret… and usually typejoin doesn’t give concrete types.

I think promote_type is what I was looking for, thanks @phg!
Here is my exploration if anyone is curious, or knows if I made an error somewhere. This minimal example uses the initial conditions, but I think the problem is similar with a cache variable.

using OrdinaryDiffEq
using Revise
using ForwardDiff

function f(du,u,p,t)
    du .= -p[2]*p[1]*u
end

u_0 = [10.0]
params = (1.0,2.0)
prob = ODEProblem(f,u_0,(0.0,10.0),params)
solve(prob,Tsit5()) #make sure it works with no fancy stuff



using MonteCarloMeasurements
params = (1.0,Particles(10,Normal(1.0,2.0)))
prob = ODEProblem(f,u_0,(0.0,10.0),params)
#solve(prob,Tsit5()) #this does not work

u_0 = typeof(params[2])[10.0]
prob = ODEProblem(f,u_0,(0.0,10.0),params)
# solve(prob,Tsit5()) #this does work!

#how to do this for (most) general types T<:Real?

element_types = typeof.(params)
u_0_2 = promote_type(element_types...)[10.0]
prob2 = ODEProblem(f,u_0_2,(0.0,10.0),params)
solve(prob2,Tsit5()) #this works, but maybe it's slower?

using BenchmarkTools
@btime solve(prob,Tsit5())
@btime solve(prob2,Tsit5())

#seems to work?
#  252.864 μs (6876 allocations: 841.17 KiB)
# 253.666 μs (6876 allocations: 841.17 KiB)

I wanted this to cover not just like, Particles, but also ForwardDiff with respect to particles, so Dual{Particles} and whatnot.

I don’t think promoting the initial state to Particles will make it slower, since if your parameters are Particles, the state will become Particles eventually. By promoting the state manually, the ODE solver knows what types to use for internal variables.

On an unrelated note, maybe this is an MWE, but if not, you’ll probably see much better performance with StaticParticles if your really only want 10 particles. If you really want a lot of particles, stick with the regular ones.

I did test it, and the performance is the same.

And yes, it’s a MWE.