Inconsistency concerning size(Array) and size(Sparse)

Hi ! I could not find the explanation of why size([1;1;1]) returns the wrong answer since julia 0.4 → 1.2.0 while okay for a sparse matrix ? Look:

julia> size([1;1;1]) # Expected to have (3,1) or (3,1,) but ok for (3,)
(3,)

julia> (a,b) = size([1;1;1]) # Set 1 to b!
ERROR: BoundsError: attempt to access (3,)
  at index [2]
 in indexed_next at tuple.jl:21

julia> size([1 1 1]) # good answer !
(1,3)

julia> size(sparse([1;1;1])) # good answer ! Why different answer from dense array???
(3,1)

julia> size(sparse([1 1 1])) # good answer !
(1,3)

julia> size([1;1;1],1) # good answer !
3

julia> size([1;1;1],2) # good answer ! Finally ... !?
1

julia> size([1;1;1],3) # yep I understand that's for simulating other dimensions
1

julia> size([1 1 1],1) # idem for size(sparse([1 1 1]),1)
1

julia> size([1 1 1],2) # idem for size(sparse([1 1 1]),2)
3

julia> size([1 1 1],3) # idem for size(sparse([1 1 1]),3)
1

My problem is that I do (a,b) = size(M) with M matrices but if M has one dimension (vector) this is not working. It is sad that size([1;1;1]) and size(sparse([1 1 1])) have different behavior. To be complety consistent (a,b,c,d) = size(M) would return (3,1,1,1) in the same way that size([1;1;1],3) returns 1. and sader to have to write long version a = size(M, 1); b = size(M, 2)

If a Julia maintainer is agreed feel free to reopen https://github.com/JuliaLang/julia/issues/33265

What Julia version are you running? I get:

julia> size(sparse([1;1;1]))
(3,)

just as for the dense case.

Here you are comparing a vector with a matrix, why should they behave the same?

Also note that (3, 1) is not a “good answer”. If anything, it’s at most an “matlab answer”.
AFAICT matlab doesn’t really have anything less than 2D (size(1) is [1, 1]) but that doesn’t mean that’s the only correct/good way.

Julia, and many other languages (at least python I believe, didn’t check) allow true lower dimentional arrays and scalars, such object will simply not have a second dimension and so you won’t see it in size. In general, in julia , you should expect size on an AbstractArray{T,N} (matrix being N=2 and vector being N=1) to return an tuple of length N. As long as you see that, what you are seeing is consistent and is simply reflecting the type of the object.

1 Like

Thanks! I was using Julia 1.0 and effectively I just noticed that on Julia 1.2 size(sparse([1;1;1])) returns your answer. Good fix :slight_smile:

Ok for Vector vs. Matrix but is not it Vector a specialized Matrix for a single dimension?

julia> typeof([1;1;1])
Array{Int64,1}

julia> typeof([1 1 1])
Array{Int64,2}

What about my idea of extending (a,b,c,d,e,f,g) = size(M) returning the tuple (3,1,1,1,1,1,1,1) in the same way that size(M,n) always return 1 when n > dim(M).

No and your code clearly shows that.

Vector is Array{T,1} and Matrix is Array{T,2} and since 1 is not a specialized 2, Array{T,1} is not a specialized Array{T,2}. Both are specialized Array, however, since both 1 and 2 are specialized N.

That’s impossible. Julia does not support dispatch on number of return values (in fact each function can only return one value) and it’s a very good property that it’s not supported…

Sorry I did not see your answer first!

You are right Julia may have its own specific specification but I was talking about consistency between:

  • to have the same answer between sparse and dense matrix for Julia. Fredrikekre is right I was working on the wrong version and they make it consistent for Julia 1.2 (bad point for me)
  • my second point was the consistency between syntax size(A{T,n}, m) with m > n returning 1 and tuple = size(A) filling all variables inside the tuple when number of variables inside the tuple > m.

Ok thank can you tell me how to write efficiently a = size(M, 1); b = size(M, 2) when M is either a vector or a matrix? For me it was shorter way to write (a, b) = size(M). Plus the confusion with the sparse fixed in 1.2

I don’t have 1.0 and I didn’t watch any sparse related issue to remember what the issue was but

  1. If sparse() is returning a vector that’s definately a bug in size
  2. If sparse() returns an matrix that won’t be an inconsistency in size.

I’m just saying that size(Matrix([1, 2, 3])) returning a different result than size([1, 2, 3]) won’t be inconsistent. It depends entirely on what the function returns.

That’s not an inconsistency and is just a convinience. size(..., n) wasn’t defined as indexing into the size return value. Or equivalently, if you view everything as infinite dim array, size is then simply returning the minimum for the type.

And if you still think it’s an inconsistency the solution will be to make size() throw an error for out of range dimension. This isn’t as much a big deal though since the current behavior won’t stop people from doing anything. In fact, it will strictly allow more code to run without error compared to a size that does throw.

This is also very different from returning at least 2 dimensions for size. It’s basically the worse solution and makes it hard to distinguish between types.

What’s inefficient about it?

Sparse is 3 vectors: 2 for axis and 1 for values. julia> sparse([1;1;1]) 3-element SparseVector{Int64,Int64} with 3 stored entries:

I’m just saying that size(Matrix([1, 2, 3])) returning a different result than size([1, 2, 3]) won’t be inconsistent.

It is fine for me.

size is then simply returning the minimum for the type .

it is also fine for me.

That’s not inconsistency and is just a convenience.

Ok maybe my terms were not correct. size([1;1;1],3) is convenient for tensor with the same reason of yours “… won’t stop people from doing anything.” So why not convenience for tuples? This is why I used the term inconsistency (of convenience) :slight_smile:

What’s inefficient about it?

It’s too long to type, I like Julia for writing small code. Fortunately, I have only two dimensions :slight_smile:

That does sounds like a size issue.

Because that’s

  1. Impossible (unless you want to return a infinitely long tuple)
  2. It actually stops people from doing things since the return value will be missing information about the dimension of the type. (The fact that size returns a length 1 tuple tells you that the array is a vector)

Of course you can also invent a type that does the correct thing on indexing. However, that means that people won’t get a simple tuple back for the size so it’s also “stop people from doing things”. Returning something when it otherwise errors doesn’t have this problem since people that just uses size(A, 1) won’t be affected.

This is indeed one of the left over Matlab-ism and I think it just seems benign and useful enough so it’s staying for now.

If you are only dealing with two dimensions, then, really, it’s not longer to type. Also, this kind of thing is why you can define custom functions. You can define your own size2(a) = size(a, 1), size(b, 1) if you are actually typing this enough that you see this significantly slowing down your typing.

Thank yuyicha for your patience, I’m new and I have to catch Julia automatism. Particularity coming from C++ with code row-major way of thinking and where I used to code with fixed-sized Matrix{T, M, N} where MxN are dimensions and Vector{T, N} == Matrix{T, 1, N} (or specialized class for optimization) and where 4×2 Array{Float64,2} means Matrix4x2 but not where x2 means the 2 of template {T,2}. Finally, it’s maybe not a good practice of mine to write my functions this way foo(x::Array{T}) where T = ... that’s why I finally confused with size(x)