scijit.optimize.lsq_linear¶
- scijit.optimize.lsq_linear(A, b, bounds=(-inf, inf), method='trf', tol=1e-10, lsq_solver=None, lsmr_tol=None, max_iter=None, verbose=0, *, lsmr_maxiter=None)¶
Bounded-variable linear least squares.
Solves
min 0.5 * ||A x - b||^2subject tolb <= x <= ub, by the trust-region-reflective method by default and by Stark and Parker’s BVLS active-set method on request. Callable from Python and from inside@njit; both entries run the same compiled cores.- Parameters:
- Aarray_like, shape (m, n)
Design matrix. Dense only. A one-dimensional A is read as a single row.
- barray_like, shape (m,)
Right-hand side.
- bounds2-tuple, optional
(lb, ub), exactly two elements. Each is a scalar or an array of lengthn. Default(-inf, inf), unbounded.- method{‘trf’, ‘bvls’}, optional
'trf', the default, is the trust-region-reflective solver, whose iterates stay strictly inside the box.'bvls'is the active-set solver, which reaches the bounds exactly. Inside@njitthis must be a literal string, since the two report nit and active_mask differently.- tolfloat, optional
Termination tolerance on the first-order optimality measure and on the relative change in cost. Default 1e-10.
- lsq_solver{None, ‘exact’}, optional
The inner solve.
Noneand'exact'both run the dense pivoted QR.'lsmr'raises: onmethod='bvls'it is not a valid pairing, and onmethod='trf'it selects an iterative solver this package does not have. Any other string raises.- lsmr_tolNone, ‘auto’ or float, optional
Tunes the LSMR inner solver, which the
'lsmr'value of lsq_solver selects, so it is not read here. Validated toNone,'auto'or a positive float; an integer is refused whatever its value. DefaultNone.- max_iterint or None, optional
Iteration cap.
None(default) resolves to 100 on'trf'and tonon'bvls'. Anything at or below zero raises.- verbose{0, 1, 2}, optional
Progress reporting on stdout.
0, the default, is silent.1prints the termination message and a summary line after the solve.2adds a five-column table with one row per iteration. See Notes.- lsmr_maxiterint or None, keyword-only, optional
Tunes the LSMR inner solver, so it is not read here. Validated to
Noneor an integer at least 1. DefaultNone.
- Returns:
- resLsqLinearResult
x,fun,cost,optimality,active_mask,nit,status,unbounded_sol,message,success, reached as attributes.statusis-1no progress on the last iteration, reachable on'trf'only,0iteration limit,1optimality belowtol,2cost change belowtol,3the unconstrained solution was already optimal.optimalityis the uniform norm of the scaled gradient on'trf'and the KKT measure on'bvls'.active_maskis-1on a lower bound,+1on an upper one and0free, and is all zero wheneverstatusis3. On'trf'every iterate is strictly interior, so the mask is decided withintolof a bound rather than on equality.unbounded_solis the wholenumpy.linalg.lstsqtuple,(solution, residuals, rank, singular_values), taken atrcond=-1.
- Raises:
- ValueError
If method is neither
'trf'nor'bvls'; if lsq_solver is notNone,'exact'or'lsmr', or is'lsmr'; if verbose is outside0,1,2; if A has more than two dimensions; if max_iter is at or below zero; if b has more than one dimension or a length other thanA.shape[0]; if bounds does not hold exactly two elements, a bound has the wrong length, or a lower bound is not strictly below its upper one; if lsmr_maxiter is below 1 or lsmr_tol is neitherNone,'auto'nor a positive float; or if either dimension of A is zero.- TypeError
If bounds has no length at all, which is CPython’s own message.
- numpy.linalg.LinAlgError
On a non-finite entry in A or b, from the unconstrained solve.
See also
scipy.optimize.lsq_linearThe scipy routine this mirrors.
scijit.optimize.nnlsThe
x >= 0special case.scijit.optimize.leastsqNonlinear least squares.
scijit.optimize.curve_fitFit a model to data.
Notes
The two methods solve the same problem and stop on different criteria, so they land in different places and report different accounting. On the 3x2 problem below,
'trf'takes 16 iterations tox[0] = 1.4999954223632812and'bvls'takes 2 to an exact1.5, the two 4.578e-06 apart. scipy behaves the same way, and the default is'trf'on both sides.res carries the ten fields in ONE order on every exit path:
unbounded_solsits between active_mask and nit. scipy has two orders, that one on thestatus = 3short circuit and a second elsewhere withunbounded_solafter status, because its two paths build the object differently. Reaching a field by name or by attribute is unaffected on both sides; what differs islist(res.keys())on the paths wherestatusis not3.unbounded_sol’s third member, the rank, is anint64here and anint32in scipy.A sparse or
LinearOperatorA is not accepted, and neither is a bounds given as ascipy.optimize.Boundsobject; pass the pair, norlsq_solver='lsmr', which selects an iterative inner solve.verbose above
0prints, and printing takes the GIL, so aprangeloop over solves is serialized for the duration of each line.verbose=0reaches no printing code.An A with a zero dimension raises. scipy 1.18 answers instead: shape
(12, 0)returns[]while LAPACK printsOn entry to DLASCL parameter number 4 had an illegal value. That answer is produced through an invalid LAPACK call.No callback of either style and no module state, so it is safe to call from a
numba.prangeloop;'trf'reaches LAPACKdgeqp3throughscijit.linalg.qr_pivot, which is prange-safe too (verified: 24 concurrent solves reproduce the serial answer to 2.78e-16).https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.lsq_linear.html
Examples
>>> import numpy as np >>> from numba import njit >>> from scijit.optimize import lsq_linear >>> A = np.array([[1.0, 0.0], [1.0, 0.0], [0.0, 1.0]]) >>> b = np.array([2.0, 1.0, -1.0]) >>> @njit ... def run(): ... return lsq_linear(A, b, (np.array([0.0, 0.0]), ... np.array([1.5, 1.0]))) >>> res = run() >>> res.x array([1.49999542e+000, 4.94065646e-324]) >>> res.status, res.success (1, True)
The active-set method reaches the bound exactly, in two iterations:
>>> bvls = lsq_linear(A, b, (np.array([0.0, 0.0]), ... np.array([1.5, 1.0])), 'bvls') >>> bvls.x, bvls.nit (array([1.5, 0. ]), 2)