-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sumlog #48
base: master
Are you sure you want to change the base?
sumlog #48
Changes from all commits
581b9df
5725aa9
77aa3d9
88d6fb1
9ecf589
9db732f
76533e1
5747205
0f5a927
977723d
4d488cd
afa5d94
e400483
cc1aaac
07809b7
16ee153
1af518b
0eaf8d2
1f478d0
0807f7a
2a0004d
e5809d1
eb1b524
6fe8bb1
0def97d
3ad95b2
a0a9348
fa667ec
989a111
a54a024
dc48433
39ca989
207fce2
55d125e
bef4728
9572e48
3848848
c4c3e89
e0f410e
23b5bf1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
name = "LogExpFunctions" | ||
uuid = "2ab3a3ac-af41-5b50-aa03-7779005ae688" | ||
authors = ["StatsFun.jl contributors, Tamas K. Papp <[email protected]>"] | ||
version = "0.3.14" | ||
version = "0.3.15" | ||
|
||
[deps] | ||
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,6 +26,8 @@ logaddexp | |
logsubexp | ||
logsumexp | ||
logsumexp! | ||
logprod | ||
logabsprod | ||
softmax! | ||
softmax | ||
``` |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,83 @@ | ||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||
logprod(X::AbstractArray{T}; dims) | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
Compute `log(prod(x))` efficiently. | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
This is faster than computing `sum(log, X)`, especially for large `X`. | ||||||||||||||||||||||||||||||
cscherrer marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||||||||||||||
It works by representing the `j`th element of `x` as ``x_j = a_j 2^{b_j}`, | ||||||||||||||||||||||||||||||
allowing us to write | ||||||||||||||||||||||||||||||
```math | ||||||||||||||||||||||||||||||
\\log \\prod_k x_j = \\log(\\prod_j a_j) + \\log{2} \\sum_j b_j. | ||||||||||||||||||||||||||||||
``` | ||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||
function logprod(x) | ||||||||||||||||||||||||||||||
y, s = logabsprod(x) | ||||||||||||||||||||||||||||||
y isa Real && s < 0 && throw(DomainError(x, "`prod(x)` must be non-negative")) | ||||||||||||||||||||||||||||||
return y | ||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
export logabsprod | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
Comment on lines
+19
to
+20
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
function logabsprod(x::AbstractArray{T}) where {T} | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This does not work in general, e.g., if
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you also add a docstring? |
||||||||||||||||||||||||||||||
sig, ex = mapreduce(_logabsprod_op, x; init=frexp(one(T))) do xj | ||||||||||||||||||||||||||||||
float_xj = float(xj) | ||||||||||||||||||||||||||||||
frexp(float_xj) | ||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||
return (log(abs(sig)) + IrrationalConstants.logtwo * T(ex), sign(sig)) | ||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
@inline function _logabsprod_op((sig1, ex1), (sig2, ex2)) | ||||||||||||||||||||||||||||||
sig = sig1 * sig2 | ||||||||||||||||||||||||||||||
ex = ex1 + ex2 | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
# The significand from `frexp` has magnitude in the range [0.5, 1), | ||||||||||||||||||||||||||||||
# so multiplication will eventually underflow | ||||||||||||||||||||||||||||||
may_underflow(sig::T) where {T} = sig < sqrt(floatmin(T)) | ||||||||||||||||||||||||||||||
if may_underflow(sig) | ||||||||||||||||||||||||||||||
(sig, Δex) = frexp(sig) | ||||||||||||||||||||||||||||||
ex += Δex | ||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||
return sig, ex | ||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||
logprod(x) | ||||||||||||||||||||||||||||||
logprod(f, x, ys...) | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
For any iterator which produces `AbstractFloat` elements, | ||||||||||||||||||||||||||||||
this can use `logprod`'s fast reduction strategy. | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
Signature with `f` is equivalent to `sum(log, map(f, x, ys...))` | ||||||||||||||||||||||||||||||
or `mapreduce(log∘f, +, x, ys...)`, without intermediate allocations. | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
Does not accept a `dims` keyword. | ||||||||||||||||||||||||||||||
""" | ||||||||||||||||||||||||||||||
logprod(f, x) = logprod(Iterators.map(f, x)) | ||||||||||||||||||||||||||||||
logprod(f, x, ys...) = logprod(f(xy...) for xy in zip(x, ys...)) | ||||||||||||||||||||||||||||||
Comment on lines
+43
to
+56
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
# Iterator version, uses the same `_logabsprod_op`, should be the same speed. | ||||||||||||||||||||||||||||||
function logabsprod(x) | ||||||||||||||||||||||||||||||
iter = iterate(x) | ||||||||||||||||||||||||||||||
if isnothing(iter) | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @devmotion can you help me understand why you prefer this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because it's more efficient in older Julia versions (IIRC it doesn't matter in more recent versions, probably >= 1.6?). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. See e.g. JuliaLang/julia#35585 |
||||||||||||||||||||||||||||||
y = prod(x) | ||||||||||||||||||||||||||||||
return log(abs(y)), sign(y) | ||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||
x1 = float(iter[1]) | ||||||||||||||||||||||||||||||
if !(x1 isa AbstractFloat) | ||||||||||||||||||||||||||||||
y = prod(x) | ||||||||||||||||||||||||||||||
return log(abs(y)), sign(y) | ||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||
sig, ex = significand(x1), _exponent(x1) | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
nonfloat = zero(x1) | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
iter = iterate(x, iter[2]) | ||||||||||||||||||||||||||||||
while iter !== nothing | ||||||||||||||||||||||||||||||
xj = float(iter[1]) | ||||||||||||||||||||||||||||||
if xj isa AbstractFloat | ||||||||||||||||||||||||||||||
sig, ex = _logprod_op((sig, ex), (significand(xj), _exponent(xj))) | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
else | ||||||||||||||||||||||||||||||
nonfloat += log(xj) | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||
iter = iterate(x, iter[2]) | ||||||||||||||||||||||||||||||
end | ||||||||||||||||||||||||||||||
return log(sig) + IrrationalConstants.logtwo * oftype(sig, ex) + nonfloat | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||
end |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,94 @@ | ||||||||||||||||||||
""" | ||||||||||||||||||||
sumlog(X::AbstractArray{T}; dims) | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I agree with I think in general it makes sense to match the Julia docs for We've already discussed that we don't need |
||||||||||||||||||||
|
||||||||||||||||||||
Compute `sum(log.(X))` with a single `log` evaluation, | ||||||||||||||||||||
provided `float(T) <: AbstractFloat`. | ||||||||||||||||||||
Comment on lines
+4
to
+5
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This sounds as if the function requires
Suggested change
|
||||||||||||||||||||
|
||||||||||||||||||||
This is faster than computing `sum(log, X)`, especially for large `X`. | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||
It works by representing the `j`th element of `x` as ``x_j = a_j 2^{b_j}`, | ||||||||||||||||||||
allowing us to write | ||||||||||||||||||||
```math | ||||||||||||||||||||
\\sum_j \\log{x_j} = \\log(\\prod_j a_j) + \\log{2} \\sum_j b_j | ||||||||||||||||||||
``` | ||||||||||||||||||||
""" | ||||||||||||||||||||
sumlog(x::AbstractArray{T}; dims=:) where T = _sumlog(float(T), dims, x) | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be better to keep this restricted to In any case, the type parameter is not necessary it seems and the dispatch on
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Real would be fine. The tests use complex numbers only as examples of things for which |
||||||||||||||||||||
|
||||||||||||||||||||
function _sumlog(::Type{T}, ::Colon, x) where {T<:AbstractFloat} | ||||||||||||||||||||
sig, ex = mapreduce(_sumlog_op, x; init=(one(T), 0)) do xj | ||||||||||||||||||||
xj < 0 && Base.Math.throw_complex_domainerror(:log, xj) | ||||||||||||||||||||
float_xj = float(xj) | ||||||||||||||||||||
significand(float_xj), _exponent(float_xj) | ||||||||||||||||||||
end | ||||||||||||||||||||
return log(sig) + IrrationalConstants.logtwo * T(ex) | ||||||||||||||||||||
end | ||||||||||||||||||||
|
||||||||||||||||||||
function _sumlog(::Type{T}, dims, x) where {T<:AbstractFloat} | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||
sig_ex = mapreduce(_sumlog_op, x; dims=dims, init=(one(T), 0)) do xj | ||||||||||||||||||||
xj < 0 && Base.Math.throw_complex_domainerror(:log, xj) | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's better to not rely on some internal error function in base, it seems simple enough to throw a custom error message.
Suggested change
|
||||||||||||||||||||
float_xj = float(xj) | ||||||||||||||||||||
significand(float_xj), _exponent(float_xj) | ||||||||||||||||||||
end | ||||||||||||||||||||
map(sig_ex) do (sig, ex) | ||||||||||||||||||||
log(sig) + IrrationalConstants.logtwo * T(ex) | ||||||||||||||||||||
end | ||||||||||||||||||||
end | ||||||||||||||||||||
|
||||||||||||||||||||
# Fallback: `float(T)` is not always `<: AbstractFloat`, e.g. complex, dual numbers or symbolics | ||||||||||||||||||||
_sumlog(::Type, dims, x) = sum(log, x; dims) | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||
|
||||||||||||||||||||
@inline function _sumlog_op((sig1, ex1), (sig2, ex2)) | ||||||||||||||||||||
sig = sig1 * sig2 | ||||||||||||||||||||
# sig = ifelse(sig2<0, sig2, sig1 * sig2) | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This, BTW, was my alternative attempt at error checking. Instead of checking on every iteration, if you ensure that I think maybe focusing on the big questions first might be better than immediately nitpicking. The other big one is how to handle Float16. At the moment it's super-inaccurate. Maybe accumulation should happen in higher precision. Maybe that should happen for Float32 too for accuracy. But I have not run any careful accuracy tests. |
||||||||||||||||||||
ex = ex1 + ex2 | ||||||||||||||||||||
# Significands are in the range [1,2), so multiplication will eventually overflow | ||||||||||||||||||||
if sig > floatmax(typeof(sig)) / 2 | ||||||||||||||||||||
ex += _exponent(sig) | ||||||||||||||||||||
sig = significand(sig) | ||||||||||||||||||||
end | ||||||||||||||||||||
return sig, ex | ||||||||||||||||||||
end | ||||||||||||||||||||
|
||||||||||||||||||||
# The exported `exponent(x)` checks for `NaN` etc, this function doesn't, which is fine as `sig` keeps track. | ||||||||||||||||||||
_exponent(x::Base.IEEEFloat) = Base.Math._exponent_finite_nonzero(x) | ||||||||||||||||||||
Base.@assume_effects :nothrow _exponent(x::AbstractFloat) = Int(exponent(x)) # e.g. for BigFloat | ||||||||||||||||||||
Comment on lines
+51
to
+53
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm strongly against using any internal non-exported functions in Base in a package such as LogExpFunctions. I really think we should just use |
||||||||||||||||||||
|
||||||||||||||||||||
""" | ||||||||||||||||||||
sumlog(x) | ||||||||||||||||||||
sumlog(f, x, ys...) | ||||||||||||||||||||
|
||||||||||||||||||||
For any iterator which produces `AbstractFloat` elements, | ||||||||||||||||||||
this can use `sumlog`'s fast reduction strategy. | ||||||||||||||||||||
|
||||||||||||||||||||
Signature with `f` is equivalent to `sum(log, map(f, x, ys...))` | ||||||||||||||||||||
or `mapreduce(log∘f, +, x, ys...)`, without intermediate allocations. | ||||||||||||||||||||
|
||||||||||||||||||||
Does not accept a `dims` keyword. | ||||||||||||||||||||
""" | ||||||||||||||||||||
sumlog(f, x) = sumlog(Iterators.map(f, x)) | ||||||||||||||||||||
sumlog(f, x, ys...) = sumlog(f(xy...) for xy in zip(x, ys...)) | ||||||||||||||||||||
Comment on lines
+55
to
+68
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this really needed? Users could just create these iterators themselves and call There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lets you use a do block, which is tidier than writing a generator yourself. |
||||||||||||||||||||
|
||||||||||||||||||||
# Iterator version, uses the same `_sumlog_op`, should be the same speed. | ||||||||||||||||||||
function sumlog(x) | ||||||||||||||||||||
iter = iterate(x) | ||||||||||||||||||||
if isnothing(iter) | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In older Julia versions
Suggested change
should be better |
||||||||||||||||||||
T = Base._return_type(first, Tuple{typeof(x)}) | ||||||||||||||||||||
return T <: Number ? zero(float(T)) : 0.0 | ||||||||||||||||||||
Comment on lines
+74
to
+75
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, I don't think we should use such internals.
Suggested change
or
Suggested change
Unfortunately, |
||||||||||||||||||||
end | ||||||||||||||||||||
x1 = float(iter[1]) | ||||||||||||||||||||
x1 isa AbstractFloat || return sum(log, x) | ||||||||||||||||||||
x1 < 0 && Base.Math.throw_complex_domainerror(:log, x1) | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Again, no internal function:
Suggested change
|
||||||||||||||||||||
sig, ex = significand(x1), _exponent(x1) | ||||||||||||||||||||
nonfloat = zero(x1) | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is this needed?
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because there is no guarantee that half way through the iterator, you won't encounter one non-Float. There's a test for this exact case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sure but in this case returning There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's avoiding re-starting the iterator. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But why is it not restarted in the case of the first element? In general, reiterating is not guaranteed to yield the same values. Maybe we should just error if |
||||||||||||||||||||
iter = iterate(x, iter[2]) | ||||||||||||||||||||
while iter !== nothing | ||||||||||||||||||||
xj = float(iter[1]) | ||||||||||||||||||||
if xj isa AbstractFloat | ||||||||||||||||||||
xj < 0 && Base.Math.throw_complex_domainerror(:log, xj) | ||||||||||||||||||||
sig, ex = _sumlog_op((sig, ex), (significand(xj), _exponent(xj))) | ||||||||||||||||||||
else | ||||||||||||||||||||
nonfloat += log(xj) | ||||||||||||||||||||
end | ||||||||||||||||||||
Comment on lines
+85
to
+90
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||
iter = iterate(x, iter[2]) | ||||||||||||||||||||
end | ||||||||||||||||||||
return log(sig) + IrrationalConstants.logtwo * oftype(sig, ex) + nonfloat | ||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You already know sig is an AbstractFloat. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, if we assume that
Suggested change
The assumption should hold in almost all cases, but it didn't seem harmful to not rely on this fact. |
||||||||||||||||||||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
@testset "sumlog" begin | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some surprises: julia> sumlog([1,2,-0.1,-0.2])
-3.2188758248682
julia> sumlog([1,2,NaN])
ERROR: DomainError with NaN:
Cannot be NaN or Inf.
Stacktrace:
[1] (::Base.Math.var"#throw1#5")(x::Float64)
@ Base.Math ./math.jl:845
[2] exponent
@ ./math.jl:848 [inlined]
julia> sumlog([-0.0])
ERROR: DomainError with -0.0:
Cannot be ±0.0.
julia> sum(log, -0.0)
-Inf There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Calling Adding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, note that tests right now only test Float64 |
||
@testset for T in [Float16, Float32, Float64, BigFloat] | ||
for x in ( | ||
T[1,2,3], | ||
10 .* rand(T, 1000), | ||
fill(nextfloat(T(1.0)), 1000), | ||
fill(prevfloat(T(2.0)), 1000), | ||
) | ||
@test sumlog(x) isa T | ||
|
||
@test (@inferred sumlog(x)) ≈ sum(log, x) | ||
|
||
y = @view x[1:min(end, 100)] | ||
@test (@inferred sumlog(y')) ≈ sum(log, y) | ||
|
||
tup = tuple(y...) | ||
@test (@inferred sumlog(tup)) ≈ sum(log, tup) | ||
# | ||
# gen = (sqrt(a) for a in y) | ||
# # `eltype` of a `Base.Generator` returns `Any` | ||
# @test_broken (@inferred sumlog(gen)) ≈ sum(log, gen) | ||
|
||
# nt = NamedTuple{tuple(Symbol.(1:100)...)}(tup) | ||
# @test (@inferred sumlog(y)) ≈ sum(log, y) | ||
|
||
z = x .+ im .* Random.shuffle(x) | ||
@test (@inferred sumlog(z)) ≈ sum(log, z) | ||
end | ||
|
||
# With dims | ||
m = 1 .+ rand(T, 10, 10) | ||
sumlog(m; dims=1) ≈ sum(log, m; dims=1) | ||
sumlog(m; dims=2) ≈ sum(log, m; dims=2) | ||
|
||
# Iterator | ||
@test sumlog(x^2 for x in m) ≈ sumlog(abs2, m) ≈ sumlog(*, m, m) ≈ sum(log.(m.^2)) | ||
@test sumlog(x for x in Any[1, 2, 3+im, 4]) ≈ sum(log, Any[1, 2, 3+im, 4]) | ||
|
||
# NaN, Inf | ||
if T != BigFloat # exponent fails here | ||
@test isnan(sumlog(T[1, 2, NaN])) | ||
@test isinf(sumlog(T[1, 2, Inf])) | ||
@test sumlog(T[1, 2, 0.0]) == -Inf | ||
@test sumlog(T[1, 2, -0.0]) == -Inf | ||
end | ||
|
||
# Empty | ||
@test sumlog(T[]) isa T | ||
@test eltype(sumlog(T[]; dims=1)) == T | ||
@test sumlog(x for x in T[]) isa T | ||
|
||
# Negative | ||
@test_throws DomainError sumlog(T[1, -2, 3]) # easy | ||
@test_throws DomainError sumlog(T[1, -2, -3]) # harder | ||
|
||
end | ||
@testset "Int" begin | ||
@test sumlog([1,2,3]) isa Float64 | ||
@test sumlog([1,2,3]) ≈ sum(log, [1,2,3]) | ||
@test sumlog([1 2; 3 4]; dims=1) ≈ sum(log, [1 2; 3 4]; dims=1) | ||
@test sumlog(Int(x) for x in Float64[1,2,3]) ≈ sum(log, [1,2,3]) | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.