Unconstrained Minimization — Control Systems/Optimization
Control_Systems/Optimization/Unconstrained_Minimization · 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.
Unconstrained Minimization
Control Systems / Optimization
Finds the x that minimizes a multivariate polynomial whose
coefficients arrive on a port – MATLAB's fminunc at its
default quasi-Newton (BFGS) algorithm, solved again on every sample:
f(x) = Σt ct · x1e(t,1) · … · xne(t,n)
The exponents e are configuration – they are the polynomial's shape, and shape fixes the loop bounds every export unrolls. The coefficients c are the signal, one per term, so the surface being minimized can change on every sample while its form does not. That is the same seam Nelder-Mead Search takes, on purpose: the two blocks accept the same problem and can be put side by side on one diagram.
What this block adds is the derivative. A polynomial's gradient is another polynomial, so it is computed exactly rather than by finite differences, and the search uses it: a BFGS inverse-Hessian approximation, a Wolfe line search, and a first-order optimality test. Nelder-Mead uses no derivative at all and is the block to reach for when the surface is noisy or kinked; this one is the block to reach for when it is smooth, and it gets there in far fewer evaluations.
The factory setting is Rosenbrock's function, expanded:
100(x₂−x₁²)² + (1−x₁)² is the six
terms [0 2; 2 1; 4 0; 0 0; 1 0; 2 0] against coefficients
[100; −200; 100; 1; −2; 1] from [−1.2; 1] –
fminunc's own documented example, so the block out of the library is
a worked one. It takes 36 iterations and 46 evaluations there, against
Nelder-Mead's 85 and 159 on the same surface.
Ports
- c – the term coefficients, a column [T,1] with one entry per ROW of Term Exponents, in the same order. A row vector is refused, because the block reads the column entry by entry.
- x – the minimizer, [n,1], one entry per variable (n = the columns of Term Exponents).
- f(x) – the value there, a scalar [1,1].
- exitflag – a scalar [1,1],
fminunc's own: 1 the gradient test below was met; 2 the step got small first; 5 the line search could not improve on the point it had; 0 a limit below stopped the search; −3 the polynomial fell below −1e20, which for a polynomial means it is unbounded below and has no minimum to find.
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 atdefault, or one this block does not have, changes nothing. - Term Exponents – a [T,n] matrix of whole numbers from 0 to 4: row t, column i is the power of xi in term t. T is 1 to 8 terms, n is 1 to 4 variables, and no single term may total more than degree 4. Those three caps are the export's, not the search's – see Code export.
- Start Point – x0,
fminunc's second argument, a vector of n entries. The search is LOCAL: it descends into the valley it starts in, so on a surface with several minima this parameter chooses which one is reported. - Optimality Tolerance –
optimoptions' OptimalityTolerance, positive; default 1e-6. The search stops with flag 1 once ∥g∥∞ < tol · (1 + ∥g₁∥∞) – the largest entry of the gradient, measured RELATIVE to the gradient at the start point, which isfminunc's own test and not an absolute one. - Step Tolerance – StepTolerance, positive; default 1e-6. Checked only AFTER the gradient test, on the RELATIVE displacement ∥Δx / (1+|x|)∥∞, and it reports flag 2, not flag 1: a step that got small while the line search was still limited is not a stationary point and this block does not call it one.
- Maximum Iterations – MaxIterations, 1 to 2000, default 400, which is MATLAB's own.
- Maximum Function Evaluations – MaxFunctionEvaluations, same range, default 400. Whichever limit binds first stops the search and the exit flag is 0.
- 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 one description of the iteration, so every target does the same arithmetic in the same order as the simulation.
The exponent pattern is unrolled: a power becomes repeated multiplication, because no target can take a loop bound out of a real array. One evaluation computes f and all n partial derivatives in the same pass – about T·(1+n)·(2+degree) statements – and the body holds three of them, which is why T, n and the degree are capped where they are. The main loop and both halves of the line search are emitted with fixed trip counts and a flag that stops the arithmetic once the answer is found, because none of the ten has an unbounded loop a hardware target could also carry.
⚠ The three HDL targets run the search in simulation-only real arithmetic, quantizing only at the port boundaries: a quasi-Newton step divides and compares across many orders of magnitude, which a Q16.16 datapath does not carry.
Simulink bridge
None (Support::None). The Optimization Toolbox ships no
Simulink library at all and fminunc is a MATLAB function, so there is
no block to map onto; the bridge reports this block rather than dropping it
silently, and it has no parity testbench. Code export verification still covers
it across all ten languages.
Notes
- Stateless: the search restarts from Start Point on every sample, so the answer depends only on the coefficients present at that step, exactly as its three neighbours do.
- ⚠ LOCAL, and a gradient method does not certify a minimum either. It reports a point where the gradient is small, which on a non-convex surface can be a different valley for a different Start Point – and, in principle, a saddle. That is the method, not a defect.
- ⚠ A polynomial that is unbounded below has no answer to give, and this is the common case with freely varying coefficients: any odd-degree term can dominate. The block reports exit flag −3 rather than a large number dressed up as a minimizer. Keep the highest-degree term EVEN with a coefficient that cannot change sign if every sample is to have a minimum.
- ⚠ Where MATLAB throws, this block answers. If rounding leaves the
search direction pointing uphill,
fminuncstops with an error ("Search direction is not a descent direction"). A block cannot do that – every port carries a number on every sample – so it stops there and reports flag 5 with the best point it reached. - eps has no spelling in nine of the ten targets, so MATLAB's
epsandsqrt(eps)in the Hessian-update test are the exact binary constants 2−52 and 2−26 here.
Code facts#
| Fact | Value |
|---|---|
| registered type | Control_Systems/Optimization/Unconstrained_Minimization |
| family | Control_Systems/Optimization |
| solver environment class | ICoreBlock_0_Control_Systems_1_Optimization_2_Unconstrained_Minimization |
| source | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Optimization/Unconstrained_Minimization/ICoreBlock_0_Control_Systems_1_Optimization_2_Unconstrained_Minimization.cpp |
| header | src/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Optimization/Unconstrained_Minimization/ICoreBlock_0_Control_Systems_1_Optimization_2_Unconstrained_Minimization.h |
| default size on canvas | 170 × 90 px |
| ports at insert | 1 in, 3 out |
| code generators implemented | Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text |
Ports#
| # | Direction | Signal type | Description label |
|---|---|---|---|
| 1 | in | ICoreDouble | c |
| 2 | out | ICoreDouble | x |
| 3 | out | ICoreDouble | f(x) |
| 4 | out | ICoreDouble | exitflag |
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 variable | Default | Simulink parameter |
|---|---|---|
Term Exponents | [0 2; 2 1; 4 0; 0 0; 1 0; 2 0] | — |
Start Point | [-1.2; 1] | — |
Optimality Tolerance | 1e-6 | — |
Step Tolerance | 1e-6 | — |
Maximum Iterations | 400 | — |
Maximum Function Evaluations | 400 | — |
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.
Simulink bridge#
| support | Support::None |
| Simulink path | — |
| port-count rule | PortsParam::None |
SampleTime parameter | yes |
Caveat (shown to the user): the Optimization Toolbox ships no Simulink library at all, so there is no block to map onto and no library path a diagram could name; fminunc is a MATLAB function. The block is reported rather than dropped when a model crosses
Catalog contract: src/ICoreBlocks/ICoreCoder/ICoreCommandSystem/SimulinkBridge/ICoreSimulinkBlockCatalog.h
Description vs code#
The checker has a blind spot here — it could not resolve something (a grouped port bullet, a computed config name), which is reported and never counted as a pass. A reader has to settle it:
B0every stimulus in the sample errored — cross-checks skipped
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).
Unconstrained Minimization -- fminunc over a multivariate polynomial whose coefficients are a wire f(x) = sum over terms of c(t) * x1^e(t,1) * ... * xn^e(t,n) c is the input signal [x, fval, exitflag] = fminunc(f, x0) Algorithm = 'quasi-newton' (the default)
THE OBJECTIVE SEAM IS NELDER-MEAD SEARCH'S, and it is taken rather than re-decided. A solver needs a FUNCTION and a wire carries NUMBERS; this family answered that once by making the exponent pattern CONFIGURATION (it is the polynomial's shape, and shape fixes the loop bounds every export unrolls) and the coefficient column the SIGNAL. Four blocks now share it, so the same problem can be put in front of two solvers on one diagram and the answers compared.
⚠⚠ WHAT THIS ROW WAS BELIEVED TO BE, AND WHAT IT ACTUALLY IS. The board carried this row as a DESIGN -- "there is no readable MATLAB reference for this row, and that is the finding ... whoever takes this row is DESIGNING a solver, not transcribing one". That is WRONG, and the mistake is one directory deep:
toolbox/optim/optimandoptim/+coderhold only the drivers and the .p engines, but fminunc's ENGINE is not there. It istoolbox/shared/optimlib/fminusub.m 571 lines, READABLE .../+optim/+internal/+fminunc/BFGSHessianApproximation.m READABLE .../+optim/+internal/+fminunc/AbstractDenseHessianApproximation.m READABLE .../+optim/+internal/+fminunc/AbstractHessianApproximation.m READABLE toolbox/shared/optimlib/private/relDeltaXFminunc.m READABLE toolbox/shared/optimlib/lineSearch.p the ONE opaque half
so the driver, the Hessian update, the initial rescaling, the update-skip test and ALL FIVE stopping branches are transcribed here, not invented.
lsqnonlin'ssnls.mandlevenbergMarquardt.mandfmincon'snlconst.mare in that same directory and are readable too, which is the same correction for the two neighbouring rows.⚠ THE STOPPING RULE WAS THE ROW'S OPEN QUESTION AND THE SOURCE ANSWERS IT. The board recorded a prototype stopping EARLY and concluded "StepTolerance cannot be read as convergence the way OptimalityTolerance can -- decide that rule before writing C++". fminunc does not choose between them; it ORDERS them, and both tests are RELATIVE where the prototype's were absolute:
||g||inf < TolFun * (1 + ||g0||inf) -> exit flag 1 the FIRST test, every pass f <= ObjectiveLimit -> exit flag -3 || dX ./ (1 + |x|) ||inf < TolX -> exit flag 2 relDeltaXFminunc.m line search could not improve -> exit flag 5 funcCount >= MaxFunEvals / iter >= MaxIter -> exit flag 0
A step test that fires far from a stationary point therefore does not report CONVERGENCE here:
Sample results#
No stimulus produced a sampled output in this rig — Invalid input size at: ICore Blocks/Home/Unconstrained Minimization. That is a fact about the single-block rig, not a verdict on the block: an offline batch fit, a block whose output only appears at onSolverFinish, or one that needs a driven environment cannot be exercised alone.
Category unsampled · sample time 0.1 · 60 steps · commit 9f0d5a563 · produced by docsSample --out <folder> --blocks Control_Systems/Optimization/Unconstrained_Minimization --steps 60
Sample data: docs/generated/samples/Control_Systems__Optimization__Unconstrained_Minimization.json