Behaviour of ones(size(x))

324 views
Skip to first unread message

Bill McLean

unread,
Nov 10, 2013, 10:45:38 PM11/10/13
to julia...@googlegroups.com


A Matlab programmer learning Julia might attempt something like

function f(n, x)
    if n==0
        return ones(size(x))
    else
        return 2x
    end
end

expecting the code to work as expected whether x is an array or
a scalar.  However, in the latter case the call to ones raises

ERROR: no method convert(Type{()},Int64)

because size returns () for a scalar. What is the recommended approach
in this situation?

Stefan Karpinski

unread,
Nov 10, 2013, 11:21:27 PM11/10/13
to julia...@googlegroups.com
It seems reasonable to me that ones(()) return a scalar one. This code would still not be well behaved though since it would presumably return 0.0 regardless of the type of x.

Bill McLean

unread,
Nov 11, 2013, 1:00:16 AM11/11/13
to julia...@googlegroups.com
I guess I could have explained myself more clearly. The desired behaviour is that f(0,x)=1 if x is a scalar, but if x is an array then f(0,x)=an array of 1s with the same dimensions as x. This is what you get in Matlab by returning ones(size(x)), since Matlab treats a scalar like a 1x1 array. Is there a simple way to produce the same effect in Julia, or should I instead define a separate function f(n,x::Array)? 

Ivar Nesje

unread,
Nov 11, 2013, 5:06:42 AM11/11/13
to julia...@googlegroups.com
The problem with ones(size(x)), with a scalar x, is that it matches the wrong method and thinks the empty tuple is the type argument instead of the shape argument because tuple() <: Type. As tuple(4) is not a subtype of Type it works for higher dimensions.

I found a few solutions for you.
  1. ones(size(x)...) Add ... to expand the tuple returned from size so that it matches how the signature for ones is written (not documented)
  2. ones(Float64, size(x)) Add a specific type parameter.
  3. ones(eltype(x), size(x)) Make the ones array type match the element type of x
For julia Base I think we should fix this by making tuple() not be a type or by adding a new method to ones so that an empty tuple is not interpreted as a type.

ones(dims::NTuple{0}) = fill!(Array(Float64, dims...), 1.0)

As ones() already returns a 0-dimensional Array{Float64,0}: [0.1] I think that changing it to be a scalar is a bigger change in the language, that should be discussed separately.

Another possibility is to remove the unexpanded tuple syntax so that we require the ... to be used with the size tuple, but that would probably break lots of existing code.

Stefan Karpinski

unread,
Nov 11, 2013, 1:23:20 PM11/11/13
to Julia Users
Honestly, I think that writing ones(()) should probably just be fixed to return a zero-dimensional array containing 1.0.

Bill McLean

unread,
Nov 11, 2013, 5:34:53 PM11/11/13
to julia...@googlegroups.com

Thanks for your 3 solutions, Ivar.  Another option would be to steal an idea from numpy, and let

ones_like(x::Number) = one(typeof(x))
ones_like(x::Array) = ones(eltype(x), size(x))

This way you could, for example, evaluate Tchebyshev polynomials using

 function T(n::Integer, x)
    if n == 0
        return ones_like(x)
    elseif n == 1
        return x
    elseif n > 1
        return 2x .* T(n-1,x) - T(n-2,x)
    else
        error("Negative n")
    end
end

with T(n,x) always having the same type as x.

Jameson Nash

unread,
Nov 11, 2013, 7:23:31 PM11/11/13
to julia...@googlegroups.com
your first definition in ones_like is redundant with your second:
`ones(eltype(x), size(x))` is sufficient for both numbers and arrays

i have found that it is good to be in the habit of providing type
information as inputs to functions, to reduce surprises. often, this
information is implicit in the arguments themselves, but sometimes the
arguments need to be supplemented with a type, as we see in many of
the array creation methods.

> Honestly, I think that writing ones(()) should probably just be fixed to return a zero-dimensional array containing 1.0.
That sounds reasonable, since we probably wanted the limiting case to
be (1,2) .. (1,) .. (), and don't care much about the limiting case
(Int32,) ... () or whatever that alternative is. Although, it could be
vaguely annoying if someone is trying to wrap ones() or zeros() and
makes the same mistake.

I'm also wondering why `size(::Int) = ()` instead of `size(::Int) =
1`. Is it because `ndims(::Int) = 0`? Array(Int64) appears to have the
same behavior, so I will assume this rationale is correct.
Reply all
Reply to author
Forward
0 new messages