rom.ecsw.custom_nnls
Purpose: Provides a flexible, robust bounded Non-Negative Least Squares (NNLS) solver for computing positive cubature weights in ECSW-based hyperreduction pipelines. Designed to find non-negative weight vectors that satisfy a linear system within per-entry bounds, with precise control over sparsity, convergence criteria, and solver diagnostics.
Summary: Implements an active-set NNLS algorithm supporting per-entry lower and upper bounds on the right-hand side, configurable sparsity constraints (minimum and maximum nonzeros), stall detection, and two convergence criteria (L2 norm or L∞ norm). The NNLSSolver class is the primary entry point for ECSW weight computation, and the NNLS_termination enum selects the stopping condition. This module is a Python adaptation of the libROM NNLS implementation from the LLNL team.
Original C++ Reference: libROM team, LLNL
Python Adaptation: Suparno Bhattacharyya
Classes
| Name | Description |
|---|---|
| NNLSSolver | Active-set NNLS solver with configurable bounds, sparsity constraints, and convergence criteria. |
| NNLS_termination | Enumeration for selecting the solver termination criterion (L2 or L∞). |
NNLSSolver
rom.ecsw.custom_nnls.NNLSSolver(
const_tol=1e-06,
min_nnz=1,
max_nnz=0,
verbosity=1,
res_change_termination_tol=1e-10,
zero_tol=1e-15,
n_outer=1000,
n_inner=400,
criterion=NNLS_termination.LINF,
)Summary: Solves the bounded non-negative least squares problem: find ( ) such that ( A ), where each entry of ( ) may be constrained to a range ([{},, {}]). The active-set algorithm iteratively adapts the set of active (nonzero) variables while enforcing non-negativity, per-entry bounds, sparsity constraints, and stall detection. Key algorithmic properties include:
- Active-set method: Dynamically maintains the set of nonzero variables, updating it at each outer iteration to maximize constraint satisfaction.
- Per-entry bound support: Each right-hand side entry can independently have a lower and upper bound, providing flexible constraint specification beyond simple non-negativity.
- Dual termination criteria: Convergence can be assessed via the L2 norm (sum-of-squares residual) or the L∞ norm (maximum single constraint violation), selectable via
criterion. - Stall detection: If the mean residual change over 50 consecutive steps falls below
res_change_termination_tol, the solver exits early with a stall flag. - Sparsity control:
min_nnzandmax_nnzallow enforcement of sparse solutions, which is critical for ECSW cubature rules where a minimal element subset is desired.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| const_tol | float | Tolerance for constraint violations evaluated under the infinity norm criterion. | 1e-06 |
| min_nnz | int | Minimum number of nonzero entries required in the solution before the solver may terminate. | 1 |
| max_nnz | int | Maximum number of nonzero entries permitted in the solution; 0 imposes no upper limit. |
0 |
| verbosity | int | Console output level: 0 for silent, 1 for summary only, ≥2 for detailed per-iteration diagnostics. |
1 |
| res_change_termination_tol | float | Stall detection threshold; if the mean residual change over 50 steps falls below this value, the solver exits with a stall exit code. | 1e-10 |
| zero_tol | float | Numerical zero threshold; values with absolute magnitude below this are treated as zero to suppress numerical noise. | 1e-15 |
| n_outer | int | Maximum number of active-set (outer) iterations before termination. | 1000 |
| n_inner | int | Maximum number of inner iterations per active-set update step. | 400 |
| criterion | NNLS_termination | Stopping condition: L2 (residual sum-of-squares below threshold) or LINF (maximum constraint violation below const_tol). |
LINF |
Attributes
| Name | Type | Description |
|---|---|---|
| const_tol_ | float | Active constraint violation tolerance. |
| min_nnz_ | int | Minimum required number of nonzero solution entries. |
| max_nnz_ | int | Maximum permitted number of nonzero solution entries. |
| verbosity_ | int | Current output verbosity level. |
| res_change_termination_tol_ | float | Stall detection threshold for residual change monitoring. |
| zero_tol_ | float | Numerical zero threshold for noise suppression. |
| n_outer_ | int | Maximum outer (active-set) iteration count. |
| n_inner_ | int | Maximum inner iteration count per active-set step. |
| d_criterion | NNLS_termination | Active convergence criterion. |
Example
>>> from nnls_solver import NNLSSolver, NNLS_termination
>>> import numpy as np
>>> A = np.random.rand(20, 10)
>>> const_tol_ = 1e-3
>>> lb = b - const_tol_
>>> ub = b + const_tol_
>>> solver = NNLSSolver(const_tol=const_tol_, verbosity=2)
>>> x, flag = solver.solve(A, lb, ub)
>>> print("Exit flag:", flag)Methods
| Name | Description |
|---|---|
| set_verbosity | Update the output verbosity level for subsequent solver runs. |
| solve | Solve the bounded non-negative least squares problem using the active-set method. |
set_verbosity
rom.ecsw.custom_nnls.NNLSSolver.set_verbosity(verbosity_in)Summary: Updates the solver’s verbosity level, controlling the volume of diagnostic output printed during subsequent calls to solve. Setting 0 suppresses all output; higher values progressively enable more detailed per-iteration reporting.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| verbosity_in | int | New output verbosity level; 0 for silent, 1 for summary, ≥2 for detailed diagnostics. |
required |
solve
rom.ecsw.custom_nnls.NNLSSolver.solve(mat, rhs_lb, rhs_ub)Summary: Solves the bounded non-negative least squares problem: find ( ) such that ( A ) with ( ) entry-wise. The active-set algorithm iterates over candidate nonzero sets, updating the active variables to progressively reduce constraint violations until the selected termination criterion (L2 or LINF) is satisfied, sparsity limits are met, or a maximum iteration count or stall condition is reached.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| mat | array_like, shape (m, n) | System matrix ( A ) for the least squares problem. | required |
| rhs_lb | array_like, shape (m,) | Per-entry lower bounds on the right-hand side ( ). | required |
| rhs_ub | array_like, shape (m,) | Per-entry upper bounds on the right-hand side ( ). | required |
Returns
| Name | Type | Description |
|---|---|---|
| final_soln | ndarray, shape (n,) | Non-negative solution vector ( ). |
| exit_flag | int | Solver status code: 0 = converged successfully; 1 = reached maximum iterations; 2 = stalled (residual change below res_change_termination_tol); 3 = failed. |
NNLS_termination
rom.ecsw.custom_nnls.NNLS_termination()Summary: Enumeration for selecting the convergence criterion used by NNLSSolver. Determines how the solver assesses whether the current solution satisfies the constraint tolerance.
| Name | Type | Description |
|---|---|---|
| L2 | int | Terminate when the L2 norm (sum of squared residuals) falls below the constraint tolerance. Corresponds to standard least-squares convergence. |
| LINF | int | Terminate when the L∞ norm (largest single constraint violation) falls below const_tol. Stricter than L2—ensures no individual constraint is significantly violated. Default criterion. |
Notes
LINF (default) is the more conservative criterion and is recommended for ECSW weight computation where individual constraint violations can produce physically inconsistent cubature rules. L2 may be preferred when a globally small residual is sufficient and isolated violations are acceptable.