Skip to content

Add softmax-reparameterized inversion algorithms (4, 5, 6)#8

Merged
NiklasPhabian merged 1 commit into
edwardbair:masterfrom
NiklasPhabian:cpp-softmax-inversion
May 28, 2026
Merged

Add softmax-reparameterized inversion algorithms (4, 5, 6)#8
NiklasPhabian merged 1 commit into
edwardbair:masterfrom
NiklasPhabian:cpp-softmax-inversion

Conversation

@NiklasPhabian

Copy link
Copy Markdown
Collaborator

Summary

Adds three new C++ algorithm codes that replace COBYLA's inequality constraint with parameter reparameterizations:

  • 4 = LN_NELDERMEAD on full softmax
  • 5 = LN_BOBYQA on full softmax
  • 6 = LN_NELDERMEAD on hybrid (softmax for fractions, clip for dust/grain) — recommended

The existing algorithms 1-3 are unchanged.

Performance (50×50 real Sentinel-2 patch, max_eval=100)

Algorithm Median residual Time vs COBYLA
1: COBYLA 0.1013 215 ms 1.0×
4: NELDERMEAD-softmax 0.0951 94 ms 2.3×
6: NELDERMEAD-hybrid 0.0893 84 ms 2.6×

Why the hybrid

The full sigmoid (algorithms 4, 5) is asymptotically flat near the LUT bounds, so the optimizer can drift z_grain → ∞ indefinitely. Saturation count grows from 4/2500 to 376/2500 between max_eval 100 and 500.

The hybrid keeps dust/grain in physical units with a clip inside the objective. The clip turns the bound into a true flat plateau, so the simplex contracts against it and terminates. Saturation count grows from 13/2500 to only 17/2500 between max_eval 100 and 500. Verified on every hybrid-saturated pixel that grain ≈ 1200 produces a lower residual than COBYLA's interior solution — these are real boundary cases, not artifacts.

Credit

Credit to Dr. Brent Wilder at JPL for suggesting this approach

Test plan

  • pytest tests/ — 20/20 passing locally
  • New test_hybrid_saturation_is_stable_under_max_eval pins the
    key stability property (saturation Δ ≤ 1% when max_eval 100→500)
  • New parametric test_invert_softmax covers algorithms 4, 5, 6
  • New test_softmax_beats_cobyla_on_real_imagery uses the LFS
    Sentinel-2 subset to exercise the algorithms on real data
  • Manual benchmark on full 50×50 patch confirms README numbers
  • Reviewer to verify on macOS (cross-platform numerical drift is a
    known concern — tests assert residual quality, not pinned coords)

Adds three new C++ algorithm codes that replace COBYLA's inequality
constraint with parameter reparameterizations, enabling unconstrained
NLopt solvers and lower-residual fits. On a 50x50 real Sentinel-2 patch
at the default max_eval=100, algorithm 6 runs 2.6x faster than COBYLA
with a 12% lower median residual.

  4 = LN_NELDERMEAD on full softmax (sigmoid for dust/grain too)
  5 = LN_BOBYQA on full softmax
  6 = LN_NELDERMEAD on hybrid: softmax for fractions only, clip-on-
      entry for dust/grain (recommended)

Algorithms 4-5 suffer grain-bound saturation drift at high max_eval
because the sigmoid is asymptotically flat near the LUT bounds. The
hybrid avoids this by keeping dust/grain in physical units and clipping
inside the objective, turning the bound into a true flat plateau that
the simplex contracts against and terminates. The hybrid's saturated
pixels are verified to have lower residual at grain=1200 than COBYLA's
interior solution -- they're real boundary signal, not optimizer noise.

The existing constrained algorithms (1=COBYLA, 2=NELDERMEAD, 3=SLSQP)
are unchanged.

Tests added:
- Parametric test_invert_softmax covers all three new algorithms
- test_softmax_beats_cobyla_on_real_imagery (uses LFS subset)
- test_hybrid_saturation_is_stable_under_max_eval pins the stability
  property that distinguishes hybrid from full softmax: saturation
  count must not grow >1% when max_eval goes 100 -> 500
- test_invert_unknown_algorithm_raises locks in the algorithm-
  fallthrough fix from the prior refactor

README updated with a comparison table and explanation of why
saturation differs across algorithms.
@NiklasPhabian NiklasPhabian merged commit e6de962 into edwardbair:master May 28, 2026
13 checks passed
@NiklasPhabian NiklasPhabian deleted the cpp-softmax-inversion branch May 28, 2026 20:01
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.

1 participant