Skip to content

Commit

Permalink
Implement Base.broadcastable for BioSequence types.
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben J. Ward committed Jul 20, 2021
1 parent 6f8c351 commit 5d7692e
Show file tree
Hide file tree
Showing 2 changed files with 156 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/biosequence/biosequence.jl
Original file line number Diff line number Diff line change
Expand Up @@ -157,3 +157,4 @@ include("printing.jl")
include("transformations.jl")
include("counting.jl")
include("copying.jl")
include("seqbroadcast.jl")
155 changes: 155 additions & 0 deletions src/biosequence/seqbroadcast.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
#=
Let's have a look at the example of broadcasting addition with a vector.
@less broadcast(+, [1,2,3], 1)
===
broadcast(f::Tf, As...) where {Tf} = materialize(broadcasted(f, As...))
@less Base.broadcasted(+, [1,2,3], 1)
===
@inline function broadcasted(f, arg1, arg2, args...)
arg1′ = broadcastable(arg1)
arg2′ = broadcastable(arg2)
args′ = map(broadcastable, args)
broadcasted(combine_styles(arg1′, arg2′, args′...), f, arg1′, arg2′, args′...)
end
@inline broadcasted(::S, f, args...) where S<:BroadcastStyle = Broadcasted{S}(f, args)
bc = Base.broadcasted(Base.Broadcast.combine_styles(arg1, arg2), +, arg1, arg2)
Base.Broadcast.Broadcasted(+, ([1, 2, 3], 1))
typeof(bc)
Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(+), Tuple{Vector{Int64}, Int64}}
@less Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}}(+, (arg1, arg2))
===
function Broadcasted{Style}(f::F, args::Args, axes=nothing) where {Style, F, Args<:Tuple}
# using Core.Typeof rather than F preserves inferrability when f is a type
Broadcasted{Style, typeof(axes), Core.Typeof(f), Args}(f, args, axes)
end
@less materialize(bc)
===
@inline materialize(bc::Broadcasted) = copy(instantiate(bc))
materialize(x) = x
@less Base.Broadcast.instantiate(bc)
===
@inline function instantiate(bc::Broadcasted{Style}) where {Style}
if bc.axes isa Nothing # Not done via dispatch to make it easier to extend instantiate(::Broadcasted{Style})
axes = combine_axes(bc.args...)
else
axes = bc.axes
check_broadcast_axes(axes, bc.args...)
end
return Broadcasted{Style}(bc.f, bc.args, axes)
end
@less Base.Broadcast.combine_axes(bc.args...)
===
@inline combine_axes(A, B...) = broadcast_shape(axes(A), combine_axes(B...))
@inline combine_axes(A, B) = broadcast_shape(axes(A), axes(B))
@less @less Base.Broadcast.broadcast_shape(axes([1,2,3]), axes(1))
===
broadcast_shape(shape::Tuple) = shape
broadcast_shape(shape::Tuple, shape1::Tuple, shapes::Tuple...) = broadcast_shape(_bcs(shape, shape1), shapes...)
@less Base.Broadcast._bcs(axes([1,2,3]), axes(1))
===
_bcs(shape::Tuple, ::Tuple{}) = (shape[1], _bcs(tail(shape), ())...)
_bcs consolidates two shapes into a single output shape
shape = axes([1,2,3])
@less Base.Broadcast._bcs(Base.tail(shape), ())
===
_bcs(::Tuple{}, ::Tuple{}) = ()
()
(shape[1], _bcs(tail(shape), ())...)
@less copy(Base.Broadcast.instantiate(bc))
===
@inline function copy(bc::Broadcasted{Style}) where {Style}
ElType = combine_eltypes(bc.f, bc.args)
if Base.isconcretetype(ElType)
# We can trust it and defer to the simpler `copyto!`
return copyto!(similar(bc, ElType), bc)
end
# When ElType is not concrete, use narrowing. Use the first output
# value to determine the starting output eltype; copyto_nonleaf!
# will widen `dest` as needed to accommodate later values.
bc′ = preprocess(nothing, bc)
iter = eachindex(bc′)
y = iterate(iter)
if y === nothing
# if empty, take the ElType at face value
return similar(bc′, ElType)
end
# Initialize using the first value
I, state = y
@inbounds val = bc′[I]
dest = similar(bc′, typeof(val))
@inbounds dest[I] = val
# Now handle the remaining values
# The typeassert gives inference a helping hand on the element type and dimensionality
# (work-around for #28382)
ElType′ = ElType === Union{} ? Any : ElType <: Type ? Type : ElType
RT = dest isa AbstractArray ? AbstractArray{<:ElType′, ndims(dest)} : Any
return copyto_nonleaf!(dest, bc′, iter, state, 1)::RT
end
@less Base.Broadcast.combine_eltypes(bc.f, bc.args)
===
combine_eltypes(f, args::Tuple) = promote_typejoin_union(Base._return_type(f, eltypes(args)))
@less Base.Broadcast.eltypes(bc.args)
@less Base._return_type(+, Base.Broadcast.eltypes(bc.args))
@less Base.Broadcast.promote_typejoin_union(Base._return_type(+, Base.Broadcast.eltypes(bc.args)))
===
function promote_typejoin_union(::Type{T}) where T
if T === Union{}
return Union{}
elseif T isa UnionAll
return Any # TODO: compute more precise bounds
elseif T isa Union
return promote_typejoin(promote_typejoin_union(T.a), promote_typejoin_union(T.b))
elseif T <: Tuple
return typejoin_union_tuple(T)
else
return T
end
end
@less similar(bc, Base.Broadcast.combine_eltypes(bc.f, bc.args))
===
Base.similar(bc::Broadcasted, ::Type{T}) where {T} = similar(bc, T, axes(bc))
Base.similar(::Broadcasted{DefaultArrayStyle{N}}, ::Type{ElType}, dims) where {N,ElType} = similar(Array{ElType}, dims)
Base.similar(::Broadcasted{DefaultArrayStyle{N}}, ::Type{Bool}, dims) where N = similar(BitArray, dims)
@less copyto!(similar(bc, Base.Broadcast.combine_eltypes(bc.f, bc.args)), bc)
=#

Base.broadcastable(x::BioSequence) = x





0 comments on commit 5d7692e

Please sign in to comment.