Skip to content

Add Exp and Softplus#67

Open
gvcallen wants to merge 3 commits into
lockwo:mainfrom
gvcallen:exp_and_softplus
Open

Add Exp and Softplus#67
gvcallen wants to merge 3 commits into
lockwo:mainfrom
gvcallen:exp_and_softplus

Conversation

@gvcallen

@gvcallen gvcallen commented Apr 4, 2026

Copy link
Copy Markdown
Contributor

Adds the exponential and softplus bijectors like Tensorflow's tfp.bijectors.Exp and tfp.bijectors.Softplus.

Comment thread distreqx/bijectors/_softplus.py Outdated
"""

_is_constant_jacobian: bool = True
_is_constant_log_det: bool = True

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

These depend on x right, so should be False?

Comment thread tests/exp_test.py
self.assertion_fn()(log_det, -jnp.log(y))
self.assertEqual(x.dtype, dtype)
self.assertEqual(log_det.dtype, dtype)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

I like these round trip tests, e.g. the inverse(forward(x)) == x style, can we add one for exp (since there is one for soft plus)?

gvcallen added a commit to gvcallen/distreqx that referenced this pull request Jul 1, 2026
Softplus's log-det depends on x, so _is_constant_jacobian/_is_constant_log_det
must be False (matching lockwo's observation), not True. Add a forward/inverse
round-trip test to exp_test.py matching the existing softplus test pattern.
Exponential and softplus bijectors modeled after TensorFlow
Probability's implementations, mapping the real line to the positive
domain.
@gvcallen gvcallen force-pushed the exp_and_softplus branch from 89d7d61 to 98b5be0 Compare July 1, 2026 20:57
gvcallen added 2 commits July 7, 2026 12:53
_more_stable_sigmoid and _more_stable_softplus used jnp.where(cond, a, b)
to switch to a numerically stable approximation for very negative inputs.
jnp.where evaluates both branches unconditionally, so for large positive
inputs the unselected branch (jnp.exp(x) / log1p(exp(x))) overflowed to
inf, and the 0 * inf this produces in the backward pass poisoned the
gradient with NaN even though that branch was never used for the forward
value.

Fix by feeding the unselected branch a safe input via a second
jnp.where, the standard remedy for this "double where" trap. Forward
values are unchanged; only the previously-poisoned gradient at large
positive inputs is fixed.

Adds a regression test exercising both helpers (via Sigmoid.forward and
Sigmoid.forward_log_det_jacobian) across a range including extreme
positive and negative inputs.
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