Confused with type explicit declaration

question

#1

Hi All,

I am a little confused with the error I am getting with passing arguments to functions that have explicit type declaration. Per my understanding one should be able to pass an Array{Float64,2} to a function that expects AbstractMatrix{AbstractFloat,2}. Below is a sample code that explains the issue.

The question is why am I getting error with predict1() and why I need to call convert(AbstractMatrix{AbstractFloat}, X) in predict2() to make the function execute successfully.

function rescale{T<:AbstractFloat}(X::AbstractMatrix{T}, μ::Vector{T}, σ::Vector{T})
  Xnew = (X .- μ') ./ σ'
end


immutable Scale
  μ::Vector{AbstractFloat}
  σ::Vector{AbstractFloat}

  function Scale{T<:AbstractFloat}(μ::Vector{T}, σ::Vector{T})
    new(μ, σ)
  end
end

function Scale{T<:AbstractFloat}(X::AbstractMatrix{T})
  μ = mean(X, 1) |> vec
  σ = std(X, 1) |> vec
  Scale(μ, σ)
end


function fit{T<:AbstractFloat}(::Type{Scale}, X::AbstractMatrix{T})
  cs = Scale(X)
end


function predict1{T<:AbstractFloat}(cs::Scale, X::AbstractMatrix{T})
  Xnew = rescale(X, cs.μ, cs.σ)
end

function predict2{T<:AbstractFloat}(cs::Scale, X::AbstractMatrix{T})
  Xnew = rescale(convert(AbstractMatrix{AbstractFloat}, X), cs.μ, cs.σ)
end


function test1()
  X = rand(100, 80)
  clf = Scale(X)
  predict1(clf, X)
end

function test2()
  X = rand(100, 80)
  clf = Scale(X)
  predict2(clf, X)
end

Error with predict1()

julia> test1()
ERROR: MethodError: no method matching rescale(::Array{Float64,2}, ::Array{AbstractFloat,1}, ::Array{AbstractFloat,1})
Closest candidates are:
  rescale{T<:AbstractFloat}(::AbstractArray{T<:AbstractFloat,2}, ::Array{T<:AbstractFloat,1}, ::Array{T<:AbstractFloat,1}) at REPL[1]:2
Stacktrace:
 [1] predict1 at ./REPL[5]:2 [inlined]
 [2] test1() at ./REPL[7]:4

Thanks


#3

The reason is that in your definition Scale holds AbstracFloat, X holds Float64 and rescale requires common type T in three arguments.

If you define Scale as:

immutable Scale{T<:AbstractFloat}
  μ::Vector{T}
  σ::Vector{T}
end

Then Scale in test1 will hold Float64 and test1 will work (but then test2 will throw an error).

You might want to read about invariance in Julia documentation http://docs.julialang.org/en/stable/manual/types/#man-parametric-types.


#4

Also note that this is just better for performance anyways. You never want to have unstrict typing of fields (when you can avoid it).