MethodError when dispatching on a parametric vector of vectors - julia

I've written a function that dispatches on a vector of vectors of Integers. However, I get a MethodError when I try to use it:
julia> foo(x::Vector{Vector{<:Integer}}) = last(last(x));
julia> x = [[1], [2, 3], [4, 5, 6]]
3-element Array{Array{Int64,1},1}:
[1]
[2, 3]
[4, 5, 6]
julia> foo(x)
ERROR: MethodError: no method matching foo(::Array{Array{Int64,1},1})
Closest candidates are:
foo(::Array{Array{#s17,1} where #s17<:Integer,1}) at REPL[1]:1
Why doesn't this work?

The notation here is a little subtle. The parametric type that you've declared for the x argument, Vector{Vector{<:Integer}} is a shorthand notation for Vector{Vector{T} where T<:Integer}:
julia> Vector{Vector{<:Integer}}
Array{Array{#s17,1} where #s17<:Integer,1}
julia> Vector{Vector{T} where T<:Integer}
Array{Array{#s17,1} where #s17<:Integer,1}
Most importantly, note that Vector{Vector{T} where T<:Integer} is not equivalent to Vector{Vector{T}} where T<:Integer. In the former type, the concrete integer type of the inner vector elements can be different for each inner vector. In the latter type, all the inner vectors have elements of the same concrete integer type.
Furthermore, it is tricky to instantiate a literal array of type Vector{Vector{T} where T<:Integer}, because the literal array constructor promotes the types of its arguments:
julia> typeof([Int8(1), Int16(2)])
Array{Int16,1}
julia> typeof([Int8[1], Int16[2, 3]])
Array{Array{Int16,1},1}
However, it can be done as follows,
julia> foo(x::Vector{Vector{<:Integer}}) = last(last(x));
julia> y = Vector{<:Integer}[Int8[1], Int16[2, 3], Int32[4, 5, 6]]
3-element Array{Array{#s17,1} where #s17<:Integer,1}:
Int8[1]
Int16[2, 3]
Int32[4, 5, 6]
julia> foo(y)
6
where we have made extensive use of typed array initializers.
Alternatively, if you're fine with requiring the elements of each inner array to have the same concrete integer type, you could define your function as follows:
julia> bar(x::Vector{Vector{T}}) where T<:Integer = last(last(x));
julia> x = [[1], [2, 3], [4, 5, 6]]
3-element Array{Array{Int64,1},1}:
[1]
[2, 3]
[4, 5, 6]
julia> bar(x)
6
Note that this method won't accept a vector of vectors where the concrete integer types are different:
julia> bar(y)
ERROR: MethodError: no method matching bar(::Array{Array{#s17,1} where #s17<:Integer,1})
Closest candidates are:
bar(::Array{Array{T,1},1}) where T<:Integer at REPL[35]:1
For a related discussion, see the section of the Julia manual on UnionAll types.

Related

Find length of array of functions in Julia

I want to find the length, nc, of this "vector of functions". I should be 2.
comp(x) = [([x[5], x[6], x[7], x[8],x[9], x[10]], tmp(x)) ; ([x[1],x[2]], [x[3],x[4]])];
nc = ....
I tried with length(comp) and length(comp(x)) but it doesn't work. I get "x not defined" and "no method matching length(::typeof(comp))", respectively.
Pulling together some of the comments to hopefully make things clearer:
What you have written is essentially
function comp(x)
a = [x[5], x[6], x[7], x[8],x[9], x[10]]
b = [x[1],x[2]]
c = [x[3],x[4]]
return [(a, tmp(x)); (b, c)]
end
that is, you have defined a function comp which takes one argument x and then returns a 2-element vector of 2-element tuples, with the first tuple holding values 5 to 10 of x and the result of tmp(x) (this function is not defined in your code so we don't know what it returns), and the second tuple holding the first and second, and third and fourth elements of x, respectively.
To illustrate, assume tmp(x) just sums up the elements of x, then we can pass some array (in the below example a range) of numbers to comp and see it in action:
julia> tmp(x) = sum(x)
tmp (generic function with 1 method)
julia> comp(1:20)
2-element Vector{Tuple{Vector{Int64}, Any}}:
([5, 6, 7, 8, 9, 10], 210)
([1, 2], [3, 4])
and you can get the result of the return value:
julia> length(comp(1:20))
2

How do I declare a matrix in a struct?

In my code
mutable struct frame
Lx::Float64
Ly::Float64
T::Matrix{Float64} #I think the error is here
function frame(
Lx::Float64,
Ly::Float64,
T::Matrix{Float64}
)
return new(Lx, Ly, T)
end
end
frames = frame[]
push!(frames, frame(1.0, 1.0, undef)) #ERROR here I try nothing, undef, any
push!(frames, frame(2.0, 1.0, undef)) #ERROR here I try nothing, undef, any
frames[1].T = [1 1 2]
frames[2].T = [[2 4 5 6] [7 6 1 8]]
I got the following error in ::Matrix
ERROR: MethodError: no method matching frame(::Float64, ::Float64, ::UndefInitializer)
Closest candidates are:
frame(::Float64, ::Float64, ::Matrix)
I need to define the dimensionless matrix inside the structure and then pass the matrices with different dimensions, but I'm having an error when I push!
The error is because there is no method for the types you call:
julia> methods(frame)
# 1 method for type constructor:
[1] frame(Lx::Float64, Ly::Float64, T::Matrix{Float64})
julia> typeof(undef)
UndefInitializer
It is possible to make mutable structs with undefined fields, by calling new with fewer arguments:
julia> mutable struct Frame2
Lx::Float64
Ly::Float64
T::Matrix{Float64}
Frame2(x,y) = new(x,y)
Frame2(x,y,z) = new(x,y,z)
end
julia> a = Frame2(1,2)
Frame2(1.0, 2.0, #undef)
julia> b = Frame2(3,4,[5 6])
Frame2(3.0, 4.0, [5.0 6.0])
julia> a.T = b.T;
julia> a
Frame2(1.0, 2.0, [5.0 6.0])
You want frame(1.0, 1.0, Matrix{Float64}(undef, 0, 0))

Julia--unrecognized subtype of nested unionall

While writing a function with the following signature,
f(x::Vector{Tuple{Vector{<:Real}, Vector{<:Real}}})
I ran into an error I do not understand. I tried calling this function f on z, defined as follows:
z = [([1,2], [3,4])]
(This is an array of tuples, where each tuple contains two arrays of real numbers; z as defined above only contains one such tuple.)
The type of z is
Array{Tuple{Array{Int64,1},Array{Float64,1}},1}
(as found by calling typeof(z)). I had expected this to be a subtype of
Vector{Tuple{Vector{<:Real}, Vector{<:Real}}}
, the type in the function f above.
However, when I run the code
z::Vector{Tuple{Vector{<:Real}, Vector{<:Real}}}
I see the following error:
ERROR: TypeError: in typeassert, expected Array{Tuple{Array{#s6,1} where #s6<:Real,Array{#s5,1} where #s5<:Real},1}, gotArray{Tuple{Array{Int64,1},Array{Float64,1}},1}
Likewise, I get a method error when calling f(z). Why isn't Array{Tuple{Array{Int64,1},Array{Int64,1}},1} a subtype of Vector{Tuple{Vector{<:Real}, Vector{<:Real}}}?
The reason is that:
julia> Tuple{Array{Int64,1},Array{Int64,1}} <: Tuple{Vector{<:Real}, Vector{<:Real}}
true
but clearly:
julia> Tuple{Array{Int64,1},Array{Int64,1}} >: Tuple{Vector{<:Real}, Vector{<:Real}}
false
and becasue types in Julia (except a Tuple, but here we have a Vector) are invariant (see here), you have that Vector{S} is not as subtype of Vector{T} even if S <: T.
so you need to write one additional subtyping qualification:
f(x::Vector{<:Tuple{Vector{<:Real}, Vector{<:Real}}})
and similarly:
julia> z::Vector{<:Tuple{Vector{<:Real}, Vector{<:Real}}}
1-element Array{Tuple{Array{Int64,1},Array{Int64,1}},1}:
([1, 2], [3, 4])
or extract the parameter using where:
julia> f2(x::Vector{Tuple{Vector{T}, Vector{T}}}) where {T<:Real} = x
f2 (generic function with 1 method)
julia> f2(z)
1-element Array{Tuple{Array{Int64,1},Array{Int64,1}},1}:
([1, 2], [3, 4])
or
julia> f3(x::Vector{Tuple{Vector{T}, Vector{S}}}) where {T<:Real, S<:Real} = x
f3 (generic function with 1 method)
julia> f3(z)
1-element Array{Tuple{Array{Int64,1},Array{Int64,1}},1}:
([1, 2], [3, 4])
(choose the first of second form against your decision if two elements of the tuple must have the same type or not)

Transform nested array into new dimension

Given an array as follows:
A = Array{Array{Int}}(2,2)
A[1,1] = [1,2]
A[1,2] = [3,4]
A[2,1] = [5,6]
A[2,2] = [7,8]
We then have that A is a 2x2 array with elements of type Array{Int}:
2×2 Array{Array{Int64,N} where N,2}:
[1, 2] [3, 4]
[5, 6] [7, 8]
It is possible to access the entries with e.g. A[1,2] but A[1,2,2] would not work since the third dimension is not present in A. However, A[1,2][2] works, since A[1,2] returns an array of length 2.
The question is then, what is a nice way to convert A into a 3-dimensional array, B, so that B[i,j,k] refers the the i,j-th array and the k-th element in that array. E.g. B[2,1,2] = 6.
There is a straightforward way to do this using 3 nested loops and reconstructing the array, element-by-element, but I'm hoping there is a nicer construction. (Some application of cat perhaps?)
You can construct a 3-d array from A using an array comprehension
julia> B = [ A[i,j][k] for i=1:2, j=:1:2, k=1:2 ]
2×2×2 Array{Int64,3}:
[:, :, 1] =
1 3
5 7
[:, :, 2] =
2 4
6 8
julia> B[2,1,2]
6
However a more general solution would be to overload the getindex function for arrays with the same type of A. This is more efficient since there is no need to copy the original data.
julia> import Base.getindex
julia> getindex(A::Array{Array{Int}}, i::Int, j::Int, k::Int) = A[i,j][k]
getindex (generic function with 179 methods)
julia> A[2,1,2]
6
With thanks to Dan Getz's comments, I think the following works well and is succinct:
cat(3,(getindex.(A,i) for i=1:2)...)
where 2 is the length of the nested array. It would also work for higher dimensions.
permutedims(reshape(collect(Base.Iterators.flatten(A)), (2,2,2)), (2,3,1))
also does the job and appears to be faster than the accepted cat() answer for me.
EDIT: I'm sorry, I just saw that this has already been suggested in the comments.

How to use the map function in haskell?

I'm trying to use map to return a list of lists. But i keep getting an error. I know map takes in a function and then uses that function. But i keep getting an error on it.
map (take 3) [1,2,3,4,5]
This is supposed to return [[1,2,3],[2,3,4],[3,4,5]], but it returns this error
<interactive>:6:1: error:
• Non type-variable argument in the constraint: Num [a]
(Use FlexibleContexts to permit this)
• When checking the inferred type
it :: forall a. Num [a] => [[a]]
is it hitting null is that why?
Let's take a look at exactly what the error message is saying.
map (take 3) [1, 2, 3, 4, 5]
map's type signature is
map :: (a -> b) -> [a] -> [b]
So it takes a function from a to b and returns a function from [a] to [b]. In your case, the function is take 3, which takes a list and returns a list. So a and b are both [t]. Therefore, the second argument to map should be [[t]], a list of lists. Now, Haskell looks at the second argument and sees that it's a list of numbers. So it says "How can I make a number into a list?" Haskell doesn't know of any good way to do that, so it complains that it doesn't know any type Num [t].
Now, as for what you meant to do, I believe it was mentioned in the comments. The tails function1 takes a list and returns the list of all tails of that list. So
tails [1, 2, 3, 4, 5]
-- ==> [[1, 2, 3, 4, 5], [2, 3, 4, 5], [3, 4, 5], [4, 5], [5], []]
Now you can apply the take function to each argument.
map (take 3) (tails [1, 2, 3, 4, 5])
-- ==> [[1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5], [5], []]
Oops! We've got some extra values we don't want. We only want the values that have three elements in them. So let's filter out the ones we don't want. filter takes a predicate (which is just a fancy way of saying "a function that returns a Boolean) and a list and returns a list containing only the elements that satisfy the predicate. The predicate we want is one that takes a list and returns whether or not that list has three elements.
\x -> ... -- We want only the lists
\x -> length x ... -- whose length
\x -> length x == 3 -- is exactly equal to 3
So that's our function. Now we pass that to filter.
filter (\x -> length x == 3) (map (take 3) (tails [1, 2, 3, 4, 5]))
-- ==> [[1, 2, 3], [2, 3, 4], [3, 4, 5]]
[1] Note that you may need to import Data.List to get the tails function.

Resources