`logsumexp` using Accessors?

Is it possible to do logsumexp in a cool way using Accessors?

using Accessors
julia> @modify(log, 1:1.0:9 |> sum(_[∗]))
ERROR: This should be unreachable. You probably need to overload
`Accessors.set(obj::Float64, ::typeof(sum), val::Float64)`

As far as I know, the point of Accessors.jl is to update parts of objects. I.e. the output of a modify should be something similar to the input (in this case some AbstractArray). A reduction (like sum) does not fit into this picture.

3 Likes

I concur with @eldee, I don’t understand what you are trying to do.

Also, note that LogExpFunctions.logsumexp is not simply log(sum(exp.(input))), under the hood it ensures numerical stability and handles various corner cases and magnitude discrepancies.

4 Likes

As for why I want this in general—there are a lot of common patterns that do work under a certain transformation, and I would like a way to reify that intent (x |> g⁻¹ ∘ f ∘ g) concisely, such as with a binary operator expression like f⨟g.

It won’t always be optimal – here it requires an awkward @o exp.(_) and produces overflow – but I expect often enough it will be a nice and effective reification of intent. I’d at least like to experiment with it.

This is what I was looking for:

julia> using InverseFunctions, Accessors

julia> f ⨟ g = inverse(g) ∘ f ∘ g;

julia> exps = @o exp.(_);

julia> (1:9) |> sum⨟exps
9.458551727967379

julia> log(sum(exp.(1:9)))
9.458551727967379

Indeed—In this case the “parts of an object” are elements of the UnitRange on the input side and the sole element of a broadcast over a Float64 on the output side.

That’s a good point:

julia> using LogExpFunctions;
julia> x = 1:1000
       ((sum⨟exps)(x), log(sum(exp.(x))), logsumexp(x))
(Inf, Inf, 1000.4586751453871)

Thanks.

1 Like

I like the operator idea, but still fail to see the connection with Accessors.jl. Since you use all elements of the iterable, you could use Base methods like map or broadcast. Now these do seem to fail out of the box with InverseFunctions.jl, but Broadcast.BroadcastFunction, which is what exps evaluates to, has no issues. Of course, I admit that @o exp.(_) is more convenient to type than Broadcast.BroadcastFunction(exp) :slight_smile: . (Though you could always alias the latter to something shorter.)

1 Like

It’s just that I’m viewing logsumexp as a lensed operation. I think of Accessors.jl as the Julia lens library, generalizing its purpose from picking out parts of an object to transforming into an alternate space, working on it, and transforming back.

It is indeed :slight_smile: Do you have examples of how this operation looks in other languages with lens libraries?

FYI, the same symbol is defined in CompositionsBase with a different meaning.

1 Like