In the following code
import Base: ^
struct cccbbb
v::Matrix{Float64}
end
^(cb::cccbbb, p::Int) = cb.v^p
Base.Broadcast.broadcastable(cb::cccbbb) = Ref(cb)
n = 5
r = LinRange(0.0, 10.0, 5)
cb = cccbbb(rand(n,n))
tmp = cb.^2
@show typeof(cb.^2), typeof(cb.^2 ./r), typeof(tmp./r)
the result is
(typeof(cb .^ 2), typeof(cb .^ 2 ./ r), typeof(tmp ./ r)) = (Matrix{Float64}, Vector{Matrix{Float64}}, Matrix{Float64})
The type of (cb .^ 2 ./ r) is not as expected. Is this a bug?
Yeah, that’s a little confusing, but I think the result is correct. Here’s a slightly simplified version of your example:
import Base: ^
struct A
m::Matrix{Float64}
end
^(a::A, p::Int) = a.m ^ p
Base.broadcastable(a::A) = Ref(a)
m = [1 2
3 4]
a = A(m)
julia> m ^ 2
2×2 Array{Int64,2}:
7 10
15 22
julia> m .^ 2
2×2 Array{Int64,2}:
1 4
9 16
julia> m .^ 2 ./ [1, 2]
2×2 Array{Float64,2}:
1.0 4.0
4.5 8.0
julia> a .^ 2 ./ [1, 2]
2-element Array{Array{Float64,2},1}:
[7.0 10.0; 15.0 22.0]
[3.5 5.0; 7.5 11.0]
The Base.broadcastable(a::A) = Ref(a)
line tells Julia to treat an object of type A
as a scalar for broadcasting. So
a .^ 2 ./ [1, 2]
is effectively equivalent to
[(a^2)/1, (a^2)/2]
And they way you’ve defined ^
on your new type means that it is doing a matrix power (m * m
), not an element by element power.
Can you elaborate on what output you expected and/or what output you are trying to achieve?
3 Likes
As in your example,
julia> b = a.^2
2×2 Matrix{Float64}:
7.0 10.0
15.0 22.0
and
julia> b ./ [ 1, 2]
2×2 Matrix{Float64}:
7.0 10.0
7.5 11.0
The result is the Matrix{Float64} type as expected. But the
a .^ 2 ./ [1, 2]
type is
2-element Array{Array{Float64,2},1}
which is not expected.
Can the Base.broadcastable(a::A) = Ref(a)
just affect the operator ^
?
What we’re trying to tell you is that b ./ [1, 2]
and a .^ 2 ./ [1, 2]
are two distinct computations. An important feature of the dot syntax is that vectorized functions are fused into a single loop. In the second case, you’re thinking Julia would first broadcast exponentiation to obtain the matrix, but as @CameronBieganek noted in his reply, the operations are fused into [(a^2)/1, (a^2/2)]
. This is the expected behavior, since element-wise exponentiation is different from exponentiation after all (take, matrices, for example). The behavior here is more confusing than usual because you’re wrapping a matrix, but also tell Julia to treat your struct as a scalar.
If you want the same result with the first case, try a ^ 2 ./ [1,2]
instead.
3 Likes