Generated reference › Nonnegative Least Squares — Control Systems/Optimization
kind: generated#block#control-systems-optimization

Nonnegative Least Squares — Control Systems/Optimization

x≥0

Control_Systems/Optimization/Nonnegative_Least_Squares · 1 input / 3 output port(s) at insert · exports to Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Description#

The block's own DESCRIPTION_HTML, rendered verbatim — the same text the config dialog's info panel and the library navigator show. Fix a wrong sentence in the block's .cpp (R-D9), never here.

Nonnegative Least Squares

Control Systems / Optimization

Solves minx ||C·x − d|| subject to x ≥ 0 on every step – MATLAB's lsqnonneg(C, d). The matrix C is a setting, the right-hand side d arrives on the port, and the answer leaves with the residual norm ||C·x − d||² and an exit flag. The method is the Lawson-Hanson active set: it grows the set of positive entries one at a time, taking the entry whose multiplier w = C'(d − C·x) is largest, and backs off whenever an entry would go negative.

Ports

  • d – the right-hand side, an [m,1] column with one entry per ROW of C.
  • x – the minimizer, [n,1], one entry per COLUMN of C; every entry is zero or positive.
  • resnorm – ||C·x − d||², [1,1], as lsqnonneg's second output: on an exit flag of 0 it is the residual of the last accepted iterate, not of the x that comes out.
  • exitflag – [1,1]: 1 the answer is optimal, 0 the iteration limit was reached. Read it before using x.

Parameters

  • Options – empty (default), or the path of a Solver Options block (Home/Solver Options), MATLAB’s options argument: for the run, every option it sets replaces this block’s parameter of the same name; one it leaves at default, or one this block does not have, changes nothing.
  • Coefficient Matrix C – the matrix, [m,n] with m ≥ n and at most 48 rows and 12 columns. It must have full column rank, so that the answer is unique; a C whose columns are numerically dependent is refused with that reason when the model is built.
  • X Tolerance – lsqnonneg's TolX: an entry of x below it is taken as zero and leaves the positive set, and a multiplier below it stops the search. 0 selects MATLAB's own default, 10·eps·||C||1·max(m,n).
  • Maximum Iterations – the limit on INNER iterations, which is what lsqnonneg itself limits; 0 selects its 3n. When the limit is reached the exit flag is 0 and x is the intermediate solution of the positive set, exactly as lsqnonneg returns it – which may hold entries that are zero or negative.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text, printed from the same program the block runs, so every target does the same arithmetic in the same order as the simulation. C, its Gram matrix C'C and the resolved tolerance are baked in as constants at export time; re-export after changing them.

The whole solve runs in every generated body, and its cost is bounded rather than merely finite: at most (n+1)·(K+1)+1 passes, each one a Cholesky of an n×n matrix, where K is the resolved iteration limit. The loop is that length in the emitted code, and it exits early through a flag.

The three HDL targets carry the arithmetic in real and quantize only at the ports: simulation-only, not offered as synthesizable. A Cholesky has a division by a pivot and a square root, and the active set is a data-dependent branch, none of which belongs in a Q16.16 datapath.

Simulink bridge

None (Support::None). lsqnonneg is a MATLAB function and the Optimization Toolbox ships no Simulink library at all, so there is no path a diagram could name. The bridge reports this block rather than dropping it silently, and it has no parity testbench; code export verification covers all ten languages.

Notes

  • Algebraic: the answer depends on this step's d alone, and nothing is carried between steps. It is not linear in d, so it carries no state space.
  • ⚠ The subproblem is solved through the normal equations (a Cholesky of the masked C'C), where MATLAB's backslash uses a QR factorization. That squares the condition number of C, so a nearly dependent C loses digits this way that MATLAB would keep. Measured over 1450 random problems against R2026a: worst |Δx|/(1+|x|) 2.7e-13.
  • ⚠ An exit flag of 0 does not mean "nearly converged": x is then lsqnonneg's intermediate z and may hold negative entries, and resnorm belongs to the previous iterate. That is what MATLAB returns, and it is reproduced rather than tidied.
  • The measured agreement with R2026a, the problem sets behind it and the emitted MATLAB body's own check are on the source banner.

Code facts#

FactValue
registered typeControl_Systems/Optimization/Nonnegative_Least_Squares
familyControl_Systems/Optimization
solver environment classICoreBlock_0_Control_Systems_1_Optimization_2_Nonnegative_Least_Squares
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Optimization/Nonnegative_Least_Squares/ICoreBlock_0_Control_Systems_1_Optimization_2_Nonnegative_Least_Squares.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Optimization/Nonnegative_Least_Squares/ICoreBlock_0_Control_Systems_1_Optimization_2_Nonnegative_Least_Squares.h
default size on canvas160 × 90 px
ports at insert1 in, 3 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubled
2outICoreDoublex
3outICoreDoubleresnorm
4outICoreDoubleexitflag

Ports the constructor creates. A block whose port list changes with its configuration adds or removes ports at load time; the count above is the one a freshly inserted block has.

Configuration variables#

Config variableDefaultSimulink parameter
Coefficient Matrix C[1 0.4; 0.3 1; 0.5 0.8]—
X Tolerance0—
Maximum Iterations0—
Options——

Every block also carries Sampling Time (s) from ICoreBlockSolverEnvironment: zero or less inherits the solver's rate, a positive value runs the block at that period.

supportSupport::None
Simulink path—
port-count rulePortsParam::None
SampleTime parameteryes

Caveat (shown to the user): lsqnonneg is a MATLAB function and the Optimization Toolbox ships no Simulink library, so there is no path a diagram could name; the block is reported rather than dropped when a model crosses

Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h

Description vs code#

The lists agree. check_block_descriptions.py finds no disagreement between the description's Ports, Parameters, Code export and Simulink bridge lists and the code's.

The verdict above is tools/docs/check_block_descriptions.py (P7.1), which compares LISTS. It cannot read a sentence: "stateless" on a block with a state, an initial-value semantic the recursion does not implement, a "not synthesizable" caveat the HDL banner contradicts. That is the agent audit (P7.3) on BLOCK_DESCRIPTION_AUDIT.md, and this tool's green is not a substitute for one.

File banner (developer view)#

The top comment of the block's .cpp — the maths, the realization and the export strategy, addressed to whoever changes it. It must not contradict the description above (P7.5).

Nonnegative Least Squares -- MATLAB's lsqnonneg, on every step minimize ||C x - d|| subject to x >= 0

The Lawson-Hanson active set, read out of the installed lsqnonneg.m (R2026a) and kept branch for branch: the entering variable is the FIRST largest entry of w = C'(d - Cx) over the zero set, the inner step moves x towards the unconstrained solution of the positive set until one of its entries reaches zero, a variable leaves when |x(j)| < TolX, and after 3n inner iterations lsqnonneg gives up -- returning the intermediate z, not the feasible x, with an exit flag of 0. All of that is reproduced, including the giving up.

The subproblem is solved through the NORMAL EQUATIONS of the positive set (a Cholesky of the masked C'C) where MATLAB's backslash uses a QR. Both give the least-squares answer; they differ by rounding, which grows with how ill-conditioned C is.

Measured against R2026a, 1450 random problems (n <= 8, m up to n + 8, and sets built for the hard cases -- exact nonnegative solutions, tied multipliers, near-collinear columns, 300 that hit the iteration limit): worst |dx|/(1+|x|) = 2.7e-13, every exit flag equal, and resnorm to 4.4e-16. The emitted MATLAB body, run in R2026a, is bit-identical to this C++ on 150 of those problems and agrees with lsqnonneg itself to 4.2e-14.

Support::None: lsqnonneg is an Optimization/MATLAB function and neither product ships a Simulink library, so there is no library path a diagram could name.

Sample results#

Nonnegative Least Squares — Sine Wave, [3,1]: amplitudes 1/2/3 at 2 rad/s (tried only because every scalar stimulus was refused)Nonnegative Least Squares — Sine Wave, [3,1]: amplitudes 1/2/3 at 2 rad/s (tried only because every scalar stimulus was refused)051015012345t (s)in ICoreDouble-Out-0 [3x1] entry 0out ICoreDouble-Out-0 [2x1] entry 0out ICoreDouble-Out-1out ICoreDouble-Out-2

Plotted: vector — Sine Wave, [3,1]: amplitudes 1/2/3 at 2 rad/s (tried only because every scalar stimulus was refused)

Category dynamic · sample time 0.1 · 60 steps · commit 6e86c1ef7f9cbef66d471271096a3bb1dc78e0c1 · produced by docsSample --out <folder> --blocks Nonnegative_Least_Squares Quadratic_Program Constrained_Linear_Least_Squares --steps 60 · data docs/generated/samples/Control_Systems__Optimization__Nonnegative_Least_Squares.json · the SVG is generated from those numbers by tools/docs/plot_svg.py, so it is a run and not a drawing (R-D10).