Skip to content

Commit 30b6a35

Browse files
committed
Fix with_logabsdet_jacobian for mapped/broadcasted functions
Handle NoLogAbsDetJacobian properly.
1 parent 8bb9cfa commit 30b6a35

3 files changed

Lines changed: 59 additions & 12 deletions

File tree

src/with_ladj.jl

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -117,37 +117,61 @@ with_logabsdet_jacobian(f, x) = NoLogAbsDetJacobian(f, x)
117117
end
118118

119119

120-
function _with_ladj_on_mapped(@nospecialize(map_or_bc::F), y_with_ladj::NoLogAbsDetJacobian) where {F<:Union{typeof(map),typeof(broadcast)}}
121-
return y_with_ladj
122-
end
120+
_with_ladj_on_mapped(y_with_ladj::NoLogAbsDetJacobian) = y_with_ladj
123121

124-
function _with_ladj_on_mapped(map_or_bc::F, y_with_ladj::Tuple{Any,Real}) where {F<:Union{typeof(map),typeof(broadcast)}}
125-
return y_with_ladj
126-
end
122+
_with_ladj_on_mapped(y_with_ladj::Tuple{Any,Real}) = y_with_ladj
123+
124+
_with_ladj_on_mapped(::AbstractArray{T}) where {T<:NoLogAbsDetJacobian} = T()
127125

128126
_get_all_first(x) = map(first, x)
129-
# Use x -> x[2] instead of last, using last causes horrible performance in Zygote here:
130-
_sum_over_second(x) = sum(x -> x[2], x)
127+
_get_second(x) = x[2]
128+
# Use _get_second instead of last, using last causes horrible performance in Zygote here:
129+
_sum_over_second(x) = sum(_get_second, x)
131130

132-
function _with_ladj_on_mapped(map_or_bc::F, y_with_ladj) where {F<:Union{typeof(map),typeof(broadcast)}}
131+
function _with_ladj_on_mapped(y_with_ladj)
133132
y = _get_all_first(y_with_ladj)
134133
ladj = _sum_over_second(y_with_ladj)
135134
(y, ladj)
136135
end
137136

137+
_combine_y_ladj(l::NoLogAbsDetJacobian, @nospecialize(r::NoLogAbsDetJacobian)) = l
138+
_combine_y_ladj(l::NoLogAbsDetJacobian, @nospecialize(r::Tuple{Any,Real})) = l
139+
_combine_y_ladj(@nospecialize(l::Tuple{Any,Real}), r::NoLogAbsDetJacobian) = r
140+
_combine_y_ladj(l::Tuple{Any,Real}, r::Tuple{Any,Real}) = ((l[1]..., r[1]), l[2] + r[2])
141+
142+
function _with_ladj_on_mapped(y_with_ladj::Tuple{NoLogAbsDetJacobian, Vararg{Union{NoLogAbsDetJacobian, Tuple{Any,Real}}}})
143+
return first(y_with_ladj)
144+
end
145+
146+
@static if VERSION < v"1.6"
147+
# Hand-rolled foldl, foldl over tuples is not inferrable on Julia < 1.6:
148+
_foldl_combine_y_ladj(acc) = acc
149+
_foldl_combine_y_ladj(acc, b, bs...) = _foldl_combine_y_ladj(_combine_y_ladj(acc, b), bs...)
150+
151+
function _with_ladj_on_mapped(y_with_ladj::Tuple{Tuple{Any,Real}, Vararg{Union{NoLogAbsDetJacobian, Tuple{Any,Real}}}})
152+
a = first(y_with_ladj)
153+
return _foldl_combine_y_ladj(((a[1],), a[2]), Base.tail(y_with_ladj)...)
154+
end
155+
else
156+
function _with_ladj_on_mapped(y_with_ladj::Tuple{Tuple{Any,Real}, Vararg{Union{NoLogAbsDetJacobian, Tuple{Any,Real}}}})
157+
a, bs = first(y_with_ladj), Base.tail(y_with_ladj)
158+
return foldl(_combine_y_ladj, bs, init = ((a[1],), a[2]))
159+
end
160+
end
161+
138162
@static if VERSION >= v"1.6"
139163
function with_logabsdet_jacobian(mapped_f::Base.Broadcast.BroadcastFunction, X)
140164
f = mapped_f.f
141165
y_with_ladj = broadcast(Base.Fix1(with_logabsdet_jacobian, f), X)
142-
_with_ladj_on_mapped(broadcast, y_with_ladj)
166+
_with_ladj_on_mapped(y_with_ladj)
143167
end
144168
end
145169

146170
function with_logabsdet_jacobian(mapped_f::Base.Fix1{<:Union{typeof(map),typeof(broadcast)}}, X)
147171
map_or_bc = mapped_f.f
148172
f = mapped_f.x
149173
y_with_ladj = map_or_bc(Base.Fix1(with_logabsdet_jacobian, f), X)
150-
_with_ladj_on_mapped(map_or_bc, y_with_ladj)
174+
_with_ladj_on_mapped(y_with_ladj)
151175
end
152176

153177

test/getjacobian.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,3 +43,12 @@ if !isdefined(Main, :foo)
4343
foo(x) = inv(exp(-x) + 1)
4444

4545
end # !isdefined(Main, :foo)
46+
47+
48+
if !isdefined(Main, :bar)
49+
50+
# with_logabsdet_jacobian for floats but not for integers
51+
bar(x) = x
52+
ChangesOfVariables.with_logabsdet_jacobian(::typeof(bar), x::AbstractFloat) = (x, zero(x))
53+
54+
end # !isdefined(Main, :bar)

test/test_with_ladj.jl

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,26 @@ include("getjacobian.jl")
5555

5656
@testset "with_logabsdet_jacobian on mapped and broadcasted" begin
5757
for f in (_bc_func(foo), Base.Fix1(map, foo), Base.Fix1(broadcast, foo))
58-
for arg in (x, fill(x,), Ref(x), (x,), X)
58+
for arg in (x, fill(x,), Ref(x), (x,), (x, 2*x, 3*x), X)
5959
test_with_logabsdet_jacobian(f, arg, getjacobian, compare = isaprx)
6060
end
6161
end
6262
end
6363

64+
@testset "with_logabsdet_jacobian on mapped and broadcasted without ladj" begin
65+
for f in (_bc_func(sin), Base.Fix1(map, sin), Base.Fix1(broadcast, sin))
66+
for arg in (x, (x,), (x, x), X)
67+
@test with_logabsdet_jacobian(f, arg) isa NoLogAbsDetJacobian{typeof(sin)}
68+
end
69+
end
70+
end
71+
72+
@testset "with_logabsdet_jacobian on mapped and broadcasted with mixed ladj" begin
73+
@test with_logabsdet_jacobian(_bc_func(bar), (1.0, 2.0)) == ((1.0, 2.0), 0.0)
74+
@test with_logabsdet_jacobian(_bc_func(bar), (1.0, 2)) isa NoLogAbsDetJacobian
75+
@test with_logabsdet_jacobian(Base.Fix1(map, bar), (1, 2.0)) isa NoLogAbsDetJacobian
76+
end
77+
6478
@testset "with_logabsdet_jacobian on identity, adjoint and transpose" begin
6579
for f in (identity, adjoint, transpose)
6680
for arg in (x, A)

0 commit comments

Comments
 (0)