Skip to content

Commit 5d7692e

Browse files
author
Ben J. Ward
committed
Implement Base.broadcastable for BioSequence types.
1 parent 6f8c351 commit 5d7692e

File tree

2 files changed

+156
-0
lines changed

2 files changed

+156
-0
lines changed

src/biosequence/biosequence.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,3 +157,4 @@ include("printing.jl")
157157
include("transformations.jl")
158158
include("counting.jl")
159159
include("copying.jl")
160+
include("seqbroadcast.jl")

src/biosequence/seqbroadcast.jl

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
#=
2+
3+
Let's have a look at the example of broadcasting addition with a vector.
4+
5+
@less broadcast(+, [1,2,3], 1)
6+
===
7+
broadcast(f::Tf, As...) where {Tf} = materialize(broadcasted(f, As...))
8+
9+
10+
@less Base.broadcasted(+, [1,2,3], 1)
11+
===
12+
@inline function broadcasted(f, arg1, arg2, args...)
13+
arg1′ = broadcastable(arg1)
14+
arg2′ = broadcastable(arg2)
15+
args′ = map(broadcastable, args)
16+
broadcasted(combine_styles(arg1′, arg2′, args′...), f, arg1′, arg2′, args′...)
17+
end
18+
@inline broadcasted(::S, f, args...) where S<:BroadcastStyle = Broadcasted{S}(f, args)
19+
20+
bc = Base.broadcasted(Base.Broadcast.combine_styles(arg1, arg2), +, arg1, arg2)
21+
Base.Broadcast.Broadcasted(+, ([1, 2, 3], 1))
22+
23+
typeof(bc)
24+
Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}, Nothing, typeof(+), Tuple{Vector{Int64}, Int64}}
25+
26+
27+
@less Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{1}}(+, (arg1, arg2))
28+
===
29+
function Broadcasted{Style}(f::F, args::Args, axes=nothing) where {Style, F, Args<:Tuple}
30+
# using Core.Typeof rather than F preserves inferrability when f is a type
31+
Broadcasted{Style, typeof(axes), Core.Typeof(f), Args}(f, args, axes)
32+
end
33+
34+
35+
@less materialize(bc)
36+
===
37+
@inline materialize(bc::Broadcasted) = copy(instantiate(bc))
38+
materialize(x) = x
39+
40+
41+
@less Base.Broadcast.instantiate(bc)
42+
===
43+
@inline function instantiate(bc::Broadcasted{Style}) where {Style}
44+
if bc.axes isa Nothing # Not done via dispatch to make it easier to extend instantiate(::Broadcasted{Style})
45+
axes = combine_axes(bc.args...)
46+
else
47+
axes = bc.axes
48+
check_broadcast_axes(axes, bc.args...)
49+
end
50+
return Broadcasted{Style}(bc.f, bc.args, axes)
51+
end
52+
53+
54+
@less Base.Broadcast.combine_axes(bc.args...)
55+
===
56+
@inline combine_axes(A, B...) = broadcast_shape(axes(A), combine_axes(B...))
57+
@inline combine_axes(A, B) = broadcast_shape(axes(A), axes(B))
58+
59+
60+
@less @less Base.Broadcast.broadcast_shape(axes([1,2,3]), axes(1))
61+
===
62+
broadcast_shape(shape::Tuple) = shape
63+
broadcast_shape(shape::Tuple, shape1::Tuple, shapes::Tuple...) = broadcast_shape(_bcs(shape, shape1), shapes...)
64+
65+
66+
@less Base.Broadcast._bcs(axes([1,2,3]), axes(1))
67+
===
68+
_bcs(shape::Tuple, ::Tuple{}) = (shape[1], _bcs(tail(shape), ())...)
69+
70+
_bcs consolidates two shapes into a single output shape
71+
72+
shape = axes([1,2,3])
73+
@less Base.Broadcast._bcs(Base.tail(shape), ())
74+
===
75+
_bcs(::Tuple{}, ::Tuple{}) = ()
76+
()
77+
78+
(shape[1], _bcs(tail(shape), ())...)
79+
80+
81+
@less copy(Base.Broadcast.instantiate(bc))
82+
===
83+
@inline function copy(bc::Broadcasted{Style}) where {Style}
84+
ElType = combine_eltypes(bc.f, bc.args)
85+
if Base.isconcretetype(ElType)
86+
# We can trust it and defer to the simpler `copyto!`
87+
return copyto!(similar(bc, ElType), bc)
88+
end
89+
# When ElType is not concrete, use narrowing. Use the first output
90+
# value to determine the starting output eltype; copyto_nonleaf!
91+
# will widen `dest` as needed to accommodate later values.
92+
bc′ = preprocess(nothing, bc)
93+
iter = eachindex(bc′)
94+
y = iterate(iter)
95+
if y === nothing
96+
# if empty, take the ElType at face value
97+
return similar(bc′, ElType)
98+
end
99+
# Initialize using the first value
100+
I, state = y
101+
@inbounds val = bc′[I]
102+
dest = similar(bc′, typeof(val))
103+
@inbounds dest[I] = val
104+
# Now handle the remaining values
105+
# The typeassert gives inference a helping hand on the element type and dimensionality
106+
# (work-around for #28382)
107+
ElType′ = ElType === Union{} ? Any : ElType <: Type ? Type : ElType
108+
RT = dest isa AbstractArray ? AbstractArray{<:ElType′, ndims(dest)} : Any
109+
return copyto_nonleaf!(dest, bc′, iter, state, 1)::RT
110+
end
111+
112+
113+
@less Base.Broadcast.combine_eltypes(bc.f, bc.args)
114+
===
115+
combine_eltypes(f, args::Tuple) = promote_typejoin_union(Base._return_type(f, eltypes(args)))
116+
117+
@less Base.Broadcast.eltypes(bc.args)
118+
119+
@less Base._return_type(+, Base.Broadcast.eltypes(bc.args))
120+
121+
@less Base.Broadcast.promote_typejoin_union(Base._return_type(+, Base.Broadcast.eltypes(bc.args)))
122+
===
123+
function promote_typejoin_union(::Type{T}) where T
124+
if T === Union{}
125+
return Union{}
126+
elseif T isa UnionAll
127+
return Any # TODO: compute more precise bounds
128+
elseif T isa Union
129+
return promote_typejoin(promote_typejoin_union(T.a), promote_typejoin_union(T.b))
130+
elseif T <: Tuple
131+
return typejoin_union_tuple(T)
132+
else
133+
return T
134+
end
135+
end
136+
137+
138+
@less similar(bc, Base.Broadcast.combine_eltypes(bc.f, bc.args))
139+
===
140+
Base.similar(bc::Broadcasted, ::Type{T}) where {T} = similar(bc, T, axes(bc))
141+
Base.similar(::Broadcasted{DefaultArrayStyle{N}}, ::Type{ElType}, dims) where {N,ElType} = similar(Array{ElType}, dims)
142+
Base.similar(::Broadcasted{DefaultArrayStyle{N}}, ::Type{Bool}, dims) where N = similar(BitArray, dims)
143+
144+
@less copyto!(similar(bc, Base.Broadcast.combine_eltypes(bc.f, bc.args)), bc)
145+
146+
147+
148+
=#
149+
150+
Base.broadcastable(x::BioSequence) = x
151+
152+
153+
154+
155+

0 commit comments

Comments
 (0)