Skip to content

Rewrite log(sqr(x)) to 2 * log(abs(x)) to avoid underflow - #2380

Open
jessegrabowski wants to merge 1 commit into
pymc-devs:mainfrom
jessegrabowski:fix-log-sqr-prod-stabilization
Open

Rewrite log(sqr(x)) to 2 * log(abs(x)) to avoid underflow#2380
jessegrabowski wants to merge 1 commit into
pymc-devs:mainfrom
jessegrabowski:fix-log-sqr-prod-stabilization

Conversation

@jessegrabowski

Copy link
Copy Markdown
Member

Taking the log of a squared product materializes the product first, and in float32 a product of a few hundred terms underflows to zero once squared, so log(abs(sqr(prod(x)))) returns -inf. Shows up in GP code with a few hundred inducing points, on every backend.

Two new rewrites, local_log_sqr and local_abs_sqr, peel the square off so the existing log(abs(prod(x))) -> sum(log(abs(x))) case can fire before anything is squared. Both skip complex inputs, where abs is real-valued and would change the output dtype.

x = pt.vector("x", dtype="float32")
f = pytensor.function([x], pt.log(pt.abs(pt.sqr(pt.prod(x)))))
f(np.full(250, 0.7, dtype="float32"))  # -inf before, -89.2 after

A squared product underflows to zero in float32 at a few hundred elements, sending log(abs(sqr(prod(x)))) to -inf; unwrapping the square lets the existing log(abs(prod(x))) -> sum(log(abs(x))) case fire before the product is ever materialized.
@register_canonicalize
@register_specialize
@node_rewriter([pt_abs])
def local_abs_sqr(fgraph, node):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Include any scalar op that is non negative? Also use the new syntax x.owner_op_inputs?

@register_stabilize
@register_specialize
@node_rewriter([log])
def local_log_sqr(fgraph, node):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

extend the sqrt rewrite instead? it's the same concept?

pytest.param(lambda x: pt_abs(sqr(x)), id="abs_sqr"),
],
)
def test_sqr_rewrites_skip_complex(original_fn):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

tell your bot about RewriteTest or whatever it's called...

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