"isomorphic" datastructures

Hi,

Here is a X/Y problem

Problem X:
I handle data structures which are a nesting of Tuples, NamedTuples and SVector, of various Reals.

I want to create a function isomorph(a,b) that is equivalent to typeof(a)==typeof(b) except that it must also return true if the concrete Real types are different. For example a NTuple{3,Float64} and a NTuple{3,Int64} are, I say, isomorph.

I want to implement it in the form of a set of methods, recursing down the datastructure.

Problem Y
I decide to create methods that operate on DataTypes, like isomorph(typeof(a),typeof(b)) instead of variables because

  1. otherwise I do not sea how to compare two SVectors of length 0.
  2. this emphasizes the intention of having this code run at compile time

I start with

isomorph(::Type{<:Any }         ,::Type{<:Any }) = false # in doubt, no
isomorph(::Type{<:Real}         ,::Type{<:Real}) = true

So the method for SVector is simply

isomorph(::Type{<:SArray{S,Ta}},::Type{<:SArray{S,Tb}}) where{S,Ta,Tb} = 
                     isomorph(Ta,Tb)

For Tuples, I use a hack (ugh! Julia internal) to get the types of the Tuple components: Ta.types, as in

isomorph(Ta::Type{<:Tuple},Tb::Type{<:Tuple}) = length(Ta.types) == length(Tb.types) && all(isomorph.(Ta.types,Tb.types))

The same Ta.types works for NameTuples, but how to I get the keys from the type?

:slightly_smiling_face:

fieldnames(Ta) :grin:

You can say

julia> fieldtypes(Tuple{Bool,Char})
(Bool, Char)

You realize that “XY problem” refers to a question where you have mis-identified the root problem, right?

I handle data structures which are a nesting of Tuples, NamedTuples and SVector, of various Reals. I want to create a function isomorph(a,b) that is equivalent to typeof(a)==typeof(b) except that it must also return true if the concrete Real types are different.

What is the underlying problem that you are trying to solve that requires this?

Ah great. And that’s API-julia, not a hack. Thanks!

Hm, so I stretched the use XY. I wanted to express that I decided to solve X by solving subproblem Y, but… maybe there is a way to attack X that does not require solving Y.

And then you zoom you again asking why solve X? :smiley: . Let my try to answer.

General answer:
I write code that operates on the stack (se below about the specific context pushing in this direction), so I have various data structures that nest SVectors, Tuples and NamedTuples, with some concrete Reals at the bottom.

I create functions that can do specific operation on the individual Reals, for arbitrary data structures. This includes binary operations (between two structures), requiring me to check if they are isomorph.

Specific context
I develop Muscade.jl which relies heavily on forward automatic differentiation. Going beyond what ForwardDiff.jl can do today, in file Taylor.jl I create functionality to speed up automatic differentiation by differentiating subfunctions separately and apply the differentiation chain rule… to all sorts of nested structure (there we have it).

The performance gain has been huge in my specific application (see the calls to apply in BeamElement.jl).

If anyone wants understand Taylor.jl or talk about bringing this to ForwardDiff.jl , I’m happy to share and help.