Skip to content

Commit

Permalink
Simplified broadcast() for julia-0.5, sin.(n) now works, fixes #39
Browse files Browse the repository at this point in the history
  • Loading branch information
davidavdav committed Oct 19, 2016
1 parent 6062a65 commit 749a1c5
Show file tree
Hide file tree
Showing 7 changed files with 83 additions and 82 deletions.
4 changes: 4 additions & 0 deletions src/constructors.jl
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,7 @@ function NamedArray(T::DataType, dims::Int...)
a = Array(T, dims...)
NamedArray(a, tuple(names...), tuple(dimnames...))
end

if VERSION >= v"0.5.0-dev"

This comment has been minimized.

Copy link
@tkelman

tkelman Oct 27, 2016

Contributor

what is the actual cutoff? should be more specific about this

This comment has been minimized.

Copy link
@davidavdav

davidavdav Oct 27, 2016

Author Owner

I have no clue. Would v"0.5.0" be more specific?

This comment has been minimized.

Copy link
@tkelman

tkelman Oct 27, 2016

Contributor

It depends what the consequences of not including the file (or taking the other branch if there were one) are. Would the package still pass its tests without it? If the package triggers a bug in Base, it could be necessary to bisect across Julia versions to identify what caused the bug.

Usually you can go to Compat and see exactly what version cutoffs correspond to each feature. This looks like https://github.com/JuliaLang/Compat.jl/blob/25344358800310ac9797401d32ffc8060a0a4e1d/src/Compat.jl#L416

This comment has been minimized.

Copy link
@davidavdav

davidavdav Oct 27, 2016

Author Owner

Thanks for figuring that out.

I am trying to keep the package pass it own tests for the latest julia-0.5 and julia-0.4. Sometimes there is certain behaviour in 0.5 that is different from 0.4, then I need these version branches. I don't know exactly when such a feature is introduced, but it will be hard to test the package for all version ranges that will result from specifying the cutoff in each case.

The point about the version branch is that it separates the julia-0.4 line from julia-0.5. I believe I use 0.5.0-dev because that is always bigger than anything in 0.4. Now that 0.5 is out, it can be upped to 0.5. I don't think this package will or should support versions julia-0.5-dev < 0.5.0, that is where the cutoff v"0.5.0-dev+2396 will be.

But to answer you question: I don't think there are any consequences of not including the file for 0.5-dev version of julia before 2396, besides that maybe the package will not pass its test, or even load correctly (I think the reason I had to opt for a conditional include is that the statement wouldn't parse in 0.4).

This comment has been minimized.

Copy link
@tkelman

tkelman Oct 27, 2016

Contributor

I'm not suggesting that you test or expect the package to work at every single intermediate prerelease version of Julia, just think a bit about what each VERSION switch is actually testing for and try to be more specific since it doesn't take much work to be more accurate - there are several ways to get a build number for specific changes, I'm happy to help if you make it clear what each switch is supposed to be testing for.

include("typedconstructor.jl")
end
3 changes: 3 additions & 0 deletions src/index.jl
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ setindex!{T}(a::NamedArray{T}, x, i1::Real, i2::Real, i3::Real, i4::Real, i5::Re
# n[1:4] = 5
setindex!{T<:Real}(A::NamedArray, x, I::AbstractVector{T}) = setindex!(A.array, x, I)

# n[:] = m
setindex!(n::NamedArray, x, ::Colon) = setindex!(n.array, x, :)

# n[1:4] = 1:4
## shamelessly copied from array.jl
function setindex!{T}(A::NamedArray{T}, X::ArrayOrNamed{T}, I::Range{Int})
Expand Down
75 changes: 23 additions & 52 deletions src/keepnames.jl
Original file line number Diff line number Diff line change
Expand Up @@ -48,74 +48,45 @@ function Base.vcat(N::NamedVector...)
end
end

## helper function for broadcast
function verify_names(a::NamedArray...)
nargs = length(a)
@assert nargs>1
bigi = indmax(map(length, a)) # find biggest dimension
big = a[bigi]
for i in setdiff(bigi, 1:nargs)
println("i ", i)
for d=1:ndims(a[i])
println("d ", d)
if size(a[i],d) > 1
@assert names(big,d) == names(a(i),d)
end
end
end
return bigi::Int, big
end

## broadcast
import Base.broadcast, Base.broadcast!
function broadcast(f::Function, a::NamedArray...)
## verify that the names are consistent
bigi, big = verify_names(a...)
arrays = map(x->x.array, a)
NamedArray(broadcast(f, arrays...), big.dicts, big.dimnames)
end

function broadcast!(f::Function, dest::NamedArray, a::NamedArray, b::NamedArray...)
ab = tuple(a, b...)
## verify that the names are consistent, we assume dest is the right size
bigi, big = verify_names(ab...)
arrays = map(x->x.array, ab)
broadcast!(f, dest.array, arrays...)
dest
if VERSION < v"0.5.0-dev"
Base.Broadcast.broadcast(f, n::NamedArray, As...) = broadcast!(f, similar(n, Base.Broadcast.broadcast_shape(n, As...)), n, As...)
else
Base.Broadcast.broadcast_t(f, T, n::NamedArray, As...) = broadcast!(f, similar(n, T, Base.Broadcast.broadcast_shape(n, As...)), n, As...)
end

## keep names intact
for f in (:sin, :cos, :tan, :sind, :cosd, :tand, :sinpi, :cospi, :sinh, :cosh, :tanh, :asin, :acos, :atan, :asind, :acosd, :sec, :csc, :cot, :secd, :cscd, :cotd, :asec, :acsc, :asecd, :acscd, :acotd, :sech, :csch, :coth, :asinh, :acosh, :atanh, :asech, :acsch, :acoth, :sinc, :cosc, :deg2rad, :log, :log2, :log10, :log1p, :exp, :exp2, :exp10, :expm1, :ceil, :floor, :trunc, :round, :abs, :abs2, :sign, :signbit, :sqrt, :isqrt, :cbrt, :erf, :erfc, :erfcx, :erfi, :dawson, :erfinv, :erfcinv, :real, :imag, :conj, :angle, :cis, :gamma, :lgamma, :digamma, :invdigamma, :trigamma, :airyai, :airyprime, :airyaiprime, :airybi, :airybiprime, :besselj0, :besselj1, :bessely0, :bessely1, :eta, :zeta)
eval(Expr(:import, :Base, f))
@eval ($f)(a::NamedArray) = NamedArray(($f)(a.array), a.dicts, a.dimnames)
if VERSION < v"0.5-dev"
for f in (:sin, :cos, :tan, :sind, :cosd, :tand, :sinpi, :cospi, :sinh, :cosh, :tanh, :asin, :acos, :atan, :asind, :acosd, :sec, :csc, :cot, :secd, :cscd, :cotd, :asec, :acsc, :asecd, :acscd, :acotd, :sech, :csch, :coth, :asinh, :acosh, :atanh, :asech, :acsch, :acoth, :sinc, :cosc, :deg2rad, :log, :log2, :log10, :log1p, :exp, :exp2, :exp10, :expm1, :ceil, :floor, :trunc, :round, :abs, :abs2, :sign, :signbit, :sqrt, :isqrt, :cbrt, :erf, :erfc, :erfcx, :erfi, :dawson, :erfinv, :erfcinv, :real, :imag, :conj, :angle, :cis, :gamma, :lgamma, :digamma, :invdigamma, :trigamma, :airyai, :airyprime, :airyaiprime, :airybi, :airybiprime, :besselj0, :besselj1, :bessely0, :bessely1, :eta, :zeta)
eval(Expr(:import, :Base, f))
@eval ($f)(a::NamedArray) = NamedArray(($f)(a.array), a.dicts, a.dimnames)
end
end

## reorder names
import Base: sort, sort!
function sort!(a::NamedVector; kws...)
i = sortperm(a.array; kws...)
newnames = names(a, 1)[i]
empty!(a.dicts[1])
function sort!(v::NamedVector; kws...)
i = sortperm(v.array; kws...)
newnames = names(v, 1)[i]
empty!(v.dicts[1])
for (ind, k) in enumerate(newnames)
a.dicts[1][k] = ind
v.dicts[1][k] = ind
end
a.array = a.array[i]
a
v.array = v.array[i]
return v
end

function sort(a::NamedVector; kws...)
return sort!(copy(a); kws...)
end
sort(v::NamedVector; kws...) = sort!(copy(v); kws...)

## Note: I can't think of a sensible way to define sort!(a::NamedArray, dim>1)

## drop name of sorted dimension, as each index along that dimension is sorted individually
function sort(a::NamedArray, dim::Integer; kws...)
if ndims(a)==1 && dim==1
return sort(a; kws...)
function sort(n::NamedArray, dim::Integer; kws...)
if ndims(n)==1 && dim==1
return sort(n; kws...)
else
n = names(a)
n[dim] = [string(i) for i in 1:size(a,dim)]
return NamedArray(sort(a.array, dim; kws...), tuple(n...), a.dimnames)
nms = names(n)
nms[dim] = [string(i) for i in 1:size(n, dim)]
return NamedArray(sort(n.array, dim; kws...), tuple(nms...), n.dimnames)
end
end
2 changes: 1 addition & 1 deletion src/namedarraytypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,6 @@ end
typealias NamedVector{T} NamedArray{T,1}
typealias NamedMatrix{T} NamedArray{T,2}
@compat typealias NamedVecOrMat{T} Union{NamedVector{T},NamedMatrix{T}}
@compat typealias ArrayOrNamed{T,N} Union{Array{T,N}, NamedArray{T,N}}
@compat typealias ArrayOrNamed{T,N} Union{Array{T,N}, NamedArray{T,N,Array}}

end
35 changes: 34 additions & 1 deletion test/index.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import Base.indices
print("getindex, ")
## getindex
## Test Integer indices up to 5 dimensions, as well as CartesianIndexes
for i in 1:5
for i in 1:7
dims = fill(3, i)
n1 = NamedArray(rand(dims...))
for i in CartesianRange(tuple(dims...))
Expand Down Expand Up @@ -45,3 +45,36 @@ for i in 1:2
@test names(n[:, bi], 2) == ["b", "d"]
bi = BitArray(bi)
end

m = copy(n)
print("setindex, ")
## setindex
m[1,1] = 0
m[2,:] = 1:4
m[:,"c"] = -1
m[1,[2,3]] = [10,20]
m["one", 4] = 5

@test m.array == [0. 10 20 5; 1 2 -1 4]
m2 = copy(m)
for i in eachindex(m)
m[i] = 2*m[i]
end
@test 2*(m2.array) == m.array

m[:B=>"c", :A=>"one"] = π
@test m[1,3] == Float64(π)
m[:] = n
@test m == n

m[:] = 1:8
@test m[:] == collect(1:8)

m = NamedArray(rand(Int, 10))
m[2:5] = -1
m[6:8] = 2:4
m[[1,9,10]] = 0:2
@test m == [0, -1, -1, -1, -1, 2, 3, 4, 1, 2]

n[] = π
@test n.array[] == Float64(π)
2 changes: 2 additions & 0 deletions test/names.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ end

setdimnames!(n, ("thing1", :thing2))
@test dimnames(n) == ["thing1", :thing2]
setdimnames!(n, ["magnificent", 7])
@test dimnames(n) == ["magnificent", 7]

println(n)

Expand Down
44 changes: 16 additions & 28 deletions test/test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -25,33 +25,6 @@ copy!(m, n)
m = deepcopy(n)
@test m == n

print("setindex, ")
## setindex
m[1,1] = 0
m[2,:] = 1:4
m[:,"c"] = -1
m[1,[2,3]] = [10,20]
if VERSION < v"0.4-dev"
m["one", 4//1] = 5
else
m["one", 4] = 5
end
@test m.array == [0. 10 20 5; 1 2 -1 4]
if VERSION >= v"0.4.0-dev"
m2 = copy(m)
for i in eachindex(m)
m[i] = 2*m[i]
end
@test 2*(m2.array) == m.array
end
m[:B=>"c", :A=>"one"] = π
@test m[1,3] == Float64(π)
m = NamedArray(rand(Int, 10))
m[2:5] = -1
m[6:8] = 2:4
m[[1,9,10]] = 0:2
@test m == [0, -1, -1, -1, -1, 2, 3, 4, 1, 2]

print("sum, ")
## sum
@test sum(n) == sum(n.array)
Expand Down Expand Up @@ -127,9 +100,24 @@ print("broadcast, ")
@test broadcast(-, n, mean(n,1)).array == broadcast(-, n.array, mean(n.array,1))

print("vectorized, ")

## a selection of vectorized functions
for f in (:sin, :cos, :tan, :sinpi, :cospi, :sinh, :cosh, :tanh, :asin, :acos, :atan, :sinc, :cosc, :deg2rad, :log, :log2, :log10, :log1p, :exp, :exp2, :exp10, :expm1, :abs, :abs2, :sign, :sqrt, :erf, :erfc, :erfcx, :erfi, :dawson, :erfinv, :erfcinv, :gamma, :lgamma, :digamma, :invdigamma, :trigamma, :besselj0, :besselj1, :bessely0, :bessely1, :eta, :zeta)
@eval @test ($f)(n).array == ($f)(n.array)
if VERSION < v"0.5.0-dev"
@eval @test ($f)(n).array == ($f)(n.array)
else
@eval begin
m = ($f).(n)
@test m.array == ($f).(n.array)
@test namesanddim(m) == namesanddim(n)
end
end
end
#39
if VERSION >= v"0.5.0-dev"
v = n[1,:]
@test sin.(v).array == sin.(v.array)
@test namesanddim(sin.(v)) == namesanddim(v)
end

include("rearrange.jl")
Expand Down

0 comments on commit 749a1c5

Please sign in to comment.