Skip to content

FIX Handle zero-norm blocks in proximal operators - #342

Open
ktz03 wants to merge 1 commit into
scikit-learn-contrib:mainfrom
ktz03:fix-zero-block-prox
Open

ktz03 wants to merge 1 commit into
scikit-learn-contrib:mainfrom
ktz03:fix-zero-block-prox

Conversation

@ktz03

@ktz03 ktz03 commented Oct 13, 2026

Copy link
Copy Markdown

Context of the PR

Zero-norm blocks cause the radial proximal operators to divide by zero. L2_05 raises ZeroDivisionError, while BlockMCPenalty and BlockSCAD produce NaNs that can propagate through a multi-task fit. Block soft-thresholding also fails when both the block norm and threshold are zero, including unregularized L2_1 and GroupLasso fits.

For example, an orthogonal design with a nonzero response on one row and zero responses on the other rows reaches this case:

import numpy as np
from skglm import GeneralizedLinearEstimator
from skglm.datafits import QuadraticMultiTask
from skglm.penalties import BlockMCPenalty
from skglm.solvers import MultiTaskBCD

X = np.asfortranarray(np.sqrt(3) * np.eye(3))
Y = np.asfortranarray([[3., 4.], [0., 0.], [0., 0.]])
estimator = GeneralizedLinearEstimator(
    datafit=QuadraticMultiTask(),
    penalty=BlockMCPenalty(alpha=0.1, gamma=4.),
    solver=MultiTaskBCD(fit_intercept=False, use_acc=False,
                        max_iter=5, max_epochs=20),
).fit(X, Y)
assert np.isfinite(estimator.coef_).all()

Contributions of the PR

Return zero for zero-norm blocks before radial scaling, and include the zero-threshold boundary in block soft-thresholding. Add regression tests for plain and compiled penalties and dense/sparse multi-task fits, comparing against the same problem without the zero-response features.

Validation on Windows with Python 3.12.14, NumPy 2.2.6, SciPy 1.15.3, Numba 0.61.2 and scikit-learn 1.7.2:

  • The new tests give 22 failures and 2 passing controls on the unchanged implementation; all 24 pass after the fix. The full penalty module passes 43 tests.
  • The complete pytest -q skglm/ run passes 357 tests with 17 existing xfails; the pristine tree passes 333 with the same 17 xfails.
  • 304 direct proximal-operator checks against a radial objective oracle pass, including nonzero controls and zero/positive regularization. Eight dense/sparse, positive/unconstrained GroupLasso checks pass against the orthogonal-design solution.
  • Whole-codebase flake8 and pydocstyle commands from the lint workflow pass.
  • The documented html-noplot build exits successfully on both trees with the same existing diagnostics (Sphinx reports 56 warnings, including the existing index.rst:123 ERROR: Unexpected indentation). Example plots were not executed.

Other platforms, Python versions and performance were not tested. The existing float32 block soft-thresholding compilation issue is outside this fix.

Checks before merging PR

  • added documentation for any new feature (not applicable: bug fix)
  • added unit tests
  • edited the what's new (if applicable)

This branch has not been deployed

No deployments
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