Skip to content

Commit 514c78a

Browse files
Recognize Mapreducer/Mapper operations for sum/map over symbolic arrays
On Symbolics v7 / SymbolicUtils v4, sum(x) and map(f, x) over symbolic arrays trace to terms whose operation is a SymbolicUtils.Mapreducer / SymbolicUtils.Mapper callable rather than sum/map itself, so the rule table never matched them and every such reduction analyzed as UnknownCurvature. Plain sums (mapreduce(identity, add_sum, x) for any dims/init) now delegate to the registered sum rule, and map(f, xs...) delegates to f's rule the same way broadcast does. Co-Authored-By: Chris Rackauckas <accounts@chrisrackauckas.com> Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 4490d96 commit 514c78a

2 files changed

Lines changed: 36 additions & 0 deletions

File tree

src/atoms.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -583,3 +583,20 @@ hasdcprule(::typeof(broadcast)) = true
583583

584584
add_dcprule(LinearAlgebra.adjoint, array_domain(RealLine(), 1), AnySign, Affine, Increasing)
585585
add_dcprule(Base.getindex, array_domain(RealLine(), 1), AnySign, Affine, AnyMono)
586+
587+
# On Symbolics v7 / SymbolicUtils v4, reductions and maps over symbolic arrays
588+
# trace to `SymbolicUtils.Mapreducer`/`SymbolicUtils.Mapper` operations rather
589+
# than to `sum`/`map` themselves, so the static rule table never sees them.
590+
# A plain sum — `mapreduce(identity, add_sum, x)`
591+
# for any `dims`/`init` — delegates to the registered `sum` rule (an `init`
592+
# only shifts by a constant, which preserves the affine composition), and
593+
# `map(f, xs...)` delegates to `f`'s rule exactly like `broadcast`.
594+
hasdcprule(::SymbolicUtils.Mapreducer{typeof(identity), typeof(Base.add_sum)}) = true
595+
function dcprule(
596+
::SymbolicUtils.Mapreducer{typeof(identity), typeof(Base.add_sum)}, args...
597+
)
598+
return dcprules_dict[sum], args
599+
end
600+
601+
hasdcprule(op::SymbolicUtils.Mapper) = hasdcprule(op.f)
602+
dcprule(op::SymbolicUtils.Mapper, args...) = dcprule(op.f, args...)

test/test.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,3 +142,22 @@ ex = propagate_curvature(ex)
142142
ex = norm(z, -1) |> unwrap
143143
ex = propagate_curvature(propagate_sign(ex))
144144
@test getcurvature(ex) == SymbolicAnalysis.UnknownCurvature
145+
146+
# sum/map over symbolic arrays trace to SymbolicUtils.Mapreducer/Mapper
147+
# operations, not to `sum`/`map` themselves; previously they always analyzed
148+
# as UnknownCurvature.
149+
ex = sum(exp.(z)) |> unwrap
150+
ex = propagate_curvature(propagate_sign(ex))
151+
@test getcurvature(ex) == SymbolicAnalysis.Convex
152+
153+
ex = sum(log.(z)) |> unwrap
154+
ex = propagate_curvature(propagate_sign(ex))
155+
@test getcurvature(ex) == SymbolicAnalysis.Concave
156+
157+
ex = sum(z) |> unwrap
158+
ex = propagate_curvature(propagate_sign(ex))
159+
@test getcurvature(ex) == SymbolicAnalysis.Affine
160+
161+
ex = map(exp, z) |> unwrap
162+
ex = propagate_curvature(propagate_sign(ex))
163+
@test getcurvature(ex) == SymbolicAnalysis.Convex

0 commit comments

Comments
 (0)