Julia cannot match function method, seems it cannot tell ::Vector{Num}

Viewed 76

This is the function I wrote to use Symbolics.jl, where variables are of type Num.

function inverseDynamics(θs::Vector{T}, θ̇s::Vector{T}, θ̈s::Vector{T}, 
                         g::Vector{T}, Ftip::Vector{T}, Mlist::Vector{T}, 
                         Glist::Vector{T}, Slist::Vector{T}) where T <:Union{Int64, Float64, Num, Vector{Float64}, Matrix{Float64}}

However, in a call like this:

@variables θ₁ θ₂ θ₃ θ₄ θ₅ θ₆ θ₇
θs = [θ₁, θ₂, θ₃, θ₄, θ₅, θ₆, θ₇]
n = size(θs, 1)
M = Matrix{typeof(θs)}(undef, n, n)
θ̇s = [0 for i in 1:n]
θ̈s = [0 for i in 1:n]
θ̈s[1] = 1
M[:, 1] .= inverseDynamics(θs, θ̇s, θ̈s, [0, 0, 0], [0, 0, 0, 0, 0, 0], Mlist, Glist, Slist)

given declared variables of type Num: @variables θ₁ θ₂ θ₃ θ₄ θ₅ θ₆ θ₇ and θs is of type Vector{Num}, and θs = [θ₁, θ₂, θ₃, θ₄, θ₅, θ₆, θ₇]

It gives me the error that no method is matching the call. And here is the exact erros:

ERROR: MethodError: no method matching inverseDynamics(::Vector{Num}, ::Vector{Int64}, 
::Vector{Int64}, ::Vector{Int64}, ::Vector{Int64}, ::Vector{Matrix{Float64}},
::Vector{Matrix{Float64}}, ::Vector{Vector{Float64}})
Closest candidates are:
  inverseDynamics(::Vector{T}, !Matched::Vector{T}, !Matched::Vector{T},
!Matched::Vector{T}, !Matched::Vector{T}, !Matched::Vector{T}, !Matched::Vector{T},
!Matched::Vector{T}) where T<:Union{Float64, Int64, Num, VecOrMat{Float64}} 
at ~/Julia-scripts/symbolicDynamics/symbolicDynamics.jl:124

I am very new to Julia. I have been scratching my head but cannot find where went wrong. According to the closest candidates, only the first argument θs is a mismatch. I don't quite understand. Isn't θs a sub-type of the Union type in the function definition?

In addition, any Julia coding advice is welcome! Thanks in advance!!

2 Answers

If I understand correctly, your issue is that the function signature you provided uses the same T for all of its parameters, but the arguments you provide have different values of T. Here a simple example demonstrating the issue:

julia> function f(x::T, y::T) where T<:Union{Int,Float64}
       println("x is a $(typeof(x)) and y is a $(typeof(y))")
       end
f (generic function with 1 method)

julia> f(1, 2.0)
ERROR: MethodError: no method matching f(::Int64, ::Float64)
Closest candidates are:
  f(::T, ::T) where T<:Union{Float64, Int64} at REPL[1]:1
...

julia> f(1, 2)
x is a Int64 and y is a Int64

As you can see, x::T, y::T does not merely mean "typeof(x) and typeof(y) each satisfy <:Union{Int,Float64}", but instead means the stronger condition "x and y are both of concrete type T, where T may be chosen to satisfy T<:Union{Int,Float64}". Once T is chosen, it can't be re-chosen.

In this toy example, the correct signature would be

julia> MyNum = Union{Int,Float64}
Union{Float64, Int64}

julia> function f(x::MyNum, y::MyNum)
       println("x is a $(typeof(x)) and y is a $(typeof(y))")
       end
f (generic function with 2 methods)

julia> f(1, 2.0)
x is a Int64 and y is a Float64

So for your own example you'd want to do

MyNum = Union{Int64, Float64, Num, Vector{Float64}, Matrix{Float64}}
function inverseDynamics(θs::Vector{<:MyNum}, θ̇s::Vector{<:MyNum}, ...)

According to the closest candidates, only the first argument θs is a mismatch.

In this message:

Closest candidates are:
  inverseDynamics(::Vector{T}, !Matched::Vector{T}, !Matched::Vector{T},
!Matched::Vector{T}, !Matched::Vector{T}, !Matched::Vector{T}, !Matched::Vector{T},
!Matched::Vector{T}) where T<:Union{Float64, Int64, Num, VecOrMat{Float64}} 

the !Matched refers to arguments whose types didn't match. So it's actually only the first argument that did match, and all the rest are mismatches.

When Julia encounters your call, it sees that your first argument is a Vector{Num}, so it attempts to match it with this method you've written for inverseDynamics, by fixing T to Num. So the first argument matches because T accepts Num. But then, none of the rest of the arguments can match, because none of them are of Vector{Num} type (and T is now fixed to Num).

To fix the issue, and for the sake of clarity and correctness, it would be best to just write out the actual types in place of T:

function inverseDynamics(θs::Vector{Num}, θ̇s::Vector{Float64}, θ̈s::Vector{Float64}, 
                         g::Vector{Int64}, Ftip::Vector{Int64}, Mlist::Vector{Matrix{Float64}}, 
                         Glist::Vector{Vector{Float64}}, Slist::Vector{Vector{Float64}})

I've made assumptions about which of the parameters you want to be floats and which ones int, and similarly about the vector and matrix ones. (That is what I meant by "clarity" above, since your existing method definition muddles these distinctions between the parameters.) But the general idea is that when you have specific, concrete types that you want to match on, you don't need type variables like T.

Related