Skip to content

fix: constrain PoweredExponential.power to (0, 2] - #816

Merged
thomaspinder merged 1 commit into
mainfrom
fix/powered-exponential-power
Oct 4, 2026
Merged

thomaspinder merged 1 commit into
mainfrom
fix/powered-exponential-power

Conversation

@thomaspinder

Copy link
Copy Markdown
Collaborator

Summary

PoweredExponential.power is now constrained to $(0, 2]$, where the kernel is positive definite in every dimension. Fixes #813.

 PoweredExponential.__init__(power)
-  self.power = power                         # any value; a float is not trained
+  self.power = _wrap_power(power)
+
+_wrap_power(power)
+  if not 0 < val(power) <= 2:  raise ValueError
+  if power is a parameter:     return power                 # user's choice
+  if power == 2.0:             return non_trainable(2.0)    # RBF; cannot sit on a sigmoid bound
+  return SigmoidBounded(power, low=0, high=2)               # trainable, stays in (0, 2)

Evidence

  • Before:
    power=3.0 accepted; min eigenvalue -3.299
    power=1.5: type float | trainable array leaves: 2
    
    After:
    power=3.0 -> Expected `power` in (0, 2], where the powered exponential kernel is positive definite. Got 3.0.
    power=1.5: type SigmoidBounded | trainable array leaves: 3
    
  • New tests in tests/test_kernels/test_stationary.py:
    • a large unconstrained step keeps the power in $(0, 2]$
    • power=2.0 is fixed
    • −1, 0, 2.5, 3 and PositiveReal(3.0) raise ValueError
    • a valid user parameter passes through unchanged
    • the Gram matrix is positive definite for powers 0.3–2.0
  • The existing docstring test with power=2.0 still passes.
  • uv run poe test: 3245 passed, 1 skipped. Lint, doctests and the CI docs build (-E -W) pass.

Merge Danger

Door: two-way

Blast Radius: small

  • Fitted models change. A float power in $(0, 2)$ is now trained by fit. Before this change, fit did not train it, because it was a plain Python float. This makes it consistent with Periodic.period and RationalQuadratic.alpha, but a model that used to keep its initial power will now learn it. To keep the old behaviour, pass paramax.non_trainable(jnp.array(power)).
  • Code can break. Code that read kernel.power as a float must now use val(kernel.power). A power outside $(0, 2]$ now raises an error instead of giving an invalid kernel.
  • Nothing in the library or the example notebooks reads kernel.power.
  • The changelog entry is under Fixed.

🤖 Generated with Claude Code

@thomaspinder thomaspinder added bug Something isn't working numerical-stability labels Oct 4, 2026
@github-actions github-actions Bot added documentation Improvements or additions to documentation tests release kernels size/s ci Continuous Integration labels Oct 4, 2026
@thomaspinder
thomaspinder enabled auto-merge (squash) October 4, 2026 16:49
@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown

📖 Docs preview: https://pr-816--endearing-crepe-c2d5fe.netlify.app

Smoke render — the expensive notebooks run with reduced budgets, so
figures are not publication fidelity. /render-mode.txt says smoke.

@thomaspinder
thomaspinder disabled auto-merge October 4, 2026 18:19
@thomaspinder
thomaspinder enabled auto-merge (squash) October 4, 2026 18:20
The powered exponential kernel is positive definite in every dimension only
for 0 < power <= 2, but any value was accepted: power=3.0 gave a Gram matrix
with a minimum eigenvalue of -3.3.

A float power in (0, 2) is now a trainable SigmoidBounded parameter, so fit
learns it and cannot leave the interval. Before, a float power was a plain
Python float that fit did not train, unlike Periodic.period and
RationalQuadratic.alpha. power=2.0 (the RBF case) stays fixed, because a
bounded parameter cannot sit on its bound. A value outside the interval, or a
parameter whose value is outside it, raises ValueError.

Fixes #813

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@thomaspinder
thomaspinder force-pushed the fix/powered-exponential-power branch from fdbaf7b to 594082b Compare October 4, 2026 18:31
@thomaspinder
thomaspinder merged commit 3de1d72 into main Oct 4, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working ci Continuous Integration documentation Improvements or additions to documentation kernels numerical-stability release size/s tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix: constrain PoweredExponential.power to (0, 2]

1 participant