Skip to content

Fiddling with sumlog #1

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

Merged
merged 1 commit into from
May 7, 2022
Merged

Fiddling with sumlog #1

merged 1 commit into from
May 7, 2022

Conversation

mcabbott
Copy link

@mcabbott mcabbott commented May 7, 2022

Here if you want it is my attempt to make sumexp work with dims, and on arbitrary iterators. Surely not free of mistakes yet, but...

Looks like I touched every line, just about, in fiddling with this.

Float16 doesn't work so well yet, tests fail. Maybe it ought to accumulate in Float32 or something.

BigFloat has issues with NaN etc, tests skipped.

@inline function _sumlog_op((sig1, ex1), (sig2, ex2))
sig = sig1 * sig2
# sig = ifelse(sig2<0, sig2, sig1 * sig2)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here I was wondering whether, instead of xj < 0 && throw(... at every step, you could just ensure that sig ends up negative so that the final result throws. But it was slower.

@inline _sumlog(::Type{T}, x) where {T} = sum(log, x)
# 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
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This Base.@assume_effects I got from a comment in Base, but it doesn't do what I wanted here.

exponent(::BigFloat) is a C call, with the error check we'd like to skip in the same function.

Comment on lines +73 to +75
if isnothing(iter)
T = Base._return_type(first, Tuple{typeof(x)})
return T <: Number ? zero(float(T)) : 0.0
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the empty iterator case I think you can't avoid relying on inference.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this still infer ok? In another library (maybe it was MappedArrays?) I've seen this written something like

infer() = Base._return_type(first, Tuple{typeof(x)})
T = infer()

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have never seen this, I don't know why it would be different, but maybe there is some black magic.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

float_xj = float(xj)
significand(float_xj), _exponent(float_xj)
end
return log(sig) + IrrationalConstants.logtwo * T(ex)
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Previously, logtwo * ex was giving Float64 always.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, good catch!

Copy link
Owner

@cscherrer cscherrer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @mcabbott for working on this!

float_xj = float(xj)
significand(float_xj), _exponent(float_xj)
end
return log(sig) + IrrationalConstants.logtwo * T(ex)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, good catch!

Comment on lines +73 to +75
if isnothing(iter)
T = Base._return_type(first, Tuple{typeof(x)})
return T <: Number ? zero(float(T)) : 0.0
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this still infer ok? In another library (maybe it was MappedArrays?) I've seen this written something like

infer() = Base._return_type(first, Tuple{typeof(x)})
T = infer()

@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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
end
end


Does not accept a `dims` keyword.
"""
sumlog(f, x) = sumlog(Iterators.map(f, x))
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we include a sumlog(f, x; dims=:) version for arrays? Seems like an easy update to include f inside the function and have it default to identity

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wondered a little. But the way that's currently set up, it uses float(eltype(x)) to know whether it can run this algorithm. If f isn't type-stable, then perhaps things get awkward... surely it can be done but not 1 line.

@cscherrer cscherrer merged commit eb1b524 into cscherrer:master May 7, 2022
@mcabbott mcabbott deleted the iterate branch May 7, 2022 19:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants