Tuples of "any container" of a parametric type - give me the parameter


#1

I wish to write a function foo, which takes, among arguments, a Tuple. Each element in the Tuple can be a scalar or an Array of arbitrary dimension. The scalars and array elements are all of the same type Mytype{Nmy} (Nmy shall be common for all elements in the Tuple).

My function will return a scalar or an Array of scalars, where the scalars are of type Mytype{Nmy}

I need to know N, so that I can write typestable code

using     StaticArrays

struct Mytype{N}
    x  :: Float64
    dx :: SVector{N,Float64}
end
Mytype(x,dx::Vector{Float64})  =Mytype(x,SVector{size(dx,1),Float64}(dx))
Base.:(+)(a::Mytype{N},b::Mytype{N}) where {N} = Mytype{N}(a.x+b.x,a.dx+b.dx)

MytypeContainer{N} = Union{Mytype{N},Array{Mytype{N}}}

function foo(args::NTuple{nx,MytypeContainer{N}}) where {N,nx}
    s = Mytype{N}(0.,zeros(N))
    for arg in args 
        if isa(arg,Mytype{N})
            s += arg
        else
            for x in arg
                s += x
            end
        end
    end
    return s
end


v1 = Mytype{2}(3.,[1.,2.])
v2 = Mytype{2}(4.,[5.,6.])
v = [v1,v2]
@show foo((v,))
@show foo((v1,))
@show foo((v1,v2))
@show foo((v,v))
@show foo((v1,v))

In ways I do not understand,

::Tuple{Union{Mytype{N},Array{Mytype{N}}}}

is not expressing what I intend: a tuple of any type of containers of Mytype{N}, and give me N for typestable allocation.

P.S. The code above is now updated and seems to pass the test.


#2

It is not entirely clear what you want, but

  1. Mytype(0.,zeros(N)) is not type stable to start with,
  2. use @code_warntype to investigate type stability,
  3. why are you wrapping a single argument in a Tuple? it should be inferrable, but hard to see the reason for it.

#3

Use

Mytype(x,dx::NTuple{N,Float64}) where {N}

instead. And use

s = Mytype{N}(0.,ntuple((i)->0., Val{N}))

to initialize in a type-stable way.

Also give a minimum working example (MWE) to make it easier to help you.


#4

Tamas, Mohamed,

I see from your answers that I have been very poor at explaining myself! Apologies and thank your for your effort.

I have provided some code above, intended as a MWE. Note that I have updated it since my first post. At present, it works (as in: it compiles, and gives all the expected answers). Whether the code is typestable is an open question, I am still learning the ropes there.

Typing

@code_warntype foo((v,))

gives me

Variables:
  #self#::#foo
  args::Tuple{Array{Mytype{2},1}}
  x::Mytype{2}
  #temp#@_4::Int64
  arg::Array{Mytype{2},1}
  #temp#@_6::Int64
  s::Mytype{2}

Body:
  begin 
      s::Mytype{2} = $(Expr(:invoke, MethodInstance for Mytype{2}(::Float64, ::Array{Float64,1}), Mytype{2}, 0.0, :($(Expr(:invoke, MethodInstance for fill!(::Array{Float64,1}, ::Float64), :(Base.fill!), :($(Expr(:foreigncall, :(:jl_alloc_array_1d), Array{Float64,1}, svec(Any, Int64), Array{Float64,1}, 0, :($(Expr(:static_parameter, 1))), 0))), :((Base.sitofp)(Float64, 0)::Float64)))))) # line 14:
      #temp#@_6::Int64 = 1
      4: 
      unless (Base.not_int)((Base.slt_int)(1, #temp#@_6::Int64)::Bool)::Bool goto 28
      SSAValue(5) = (Base.getfield)(args::Tuple{Array{Mytype{2},1}}, #temp#@_6::Int64)::Array{Mytype{2},1}
      SSAValue(6) = (Base.add_int)(#temp#@_6::Int64, 1)::Int64
      arg::Array{Mytype{2},1} = SSAValue(5)
      #temp#@_6::Int64 = SSAValue(6) # line 15:
      goto 13 # line 16:
      13:  # line 18:
      #temp#@_4::Int64 = 1
      16: 
      unless (Base.not_int)((#temp#@_4::Int64 === (Base.add_int)((Base.arraylen)(arg::Array{Mytype{2},1})::Int64, 1)::Int64)::Bool)::Bool goto 26
      SSAValue(7) = (Base.arrayref)(arg::Array{Mytype{2},1}, #temp#@_4::Int64)::Mytype{2}
      SSAValue(8) = (Base.add_int)(#temp#@_4::Int64, 1)::Int64
      x::Mytype{2} = SSAValue(7)
      #temp#@_4::Int64 = SSAValue(8) # line 19:
      s::Mytype{2} = $(Expr(:invoke, MethodInstance for +(::Mytype{2}, ::Mytype{2}), :(Main.+), :(s), :(x)))
      24: 
      goto 16
      26: 
      goto 4
      28:  # line 23:
      return s::Mytype{2}
  end::Mytype{2}

which could be bad news or good news!
:slightly_smiling_face:


#5

Please give some code I can copy and paste to REPL :slight_smile:

Edit: oh it’s above, sorry didn’t see it.


#6

You are not calling the above function at all by running Mytype{2}(3.,[1.,2.]). Instead, you are calling the inner constructor, that is you are telling Julia that you want an instance of the concrete type MyType{2} thus N is now known to the compiler, and a conversion of the second argument happens behind the scene from the array you are passing to SVector{2,Float64} as N is 2 in your example. Your code therefore seems type stable because you are avoiding this type unstable function.


#7

:grin: