Generated reference › Nonlinear Curve Fit — Control Systems/Optimization
kind: generated#block#control-systems-optimization

Nonlinear Curve Fit — Control Systems/Optimization

F(x,t)

Control_Systems/Optimization/Nonlinear_Curve_Fit · 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.

Nonlinear Curve Fit

Control Systems / Optimization

Fits a model you type to a window of measurements that arrive on a port, and answers the fitted parameters – MATLAB's lsqcurvefit, solved again on every sample:

[x, resnorm, exitflag] = lsqcurvefit(F, x₀, xdata, ydata),   minimizing  Σi ( F(x, xdatai) − ydatai )² over the parameters x.

The abscissa is configuration and the ordinates are the signal: the model is sampled at fixed sites and the measurements at those sites arrive on the wire, which is the shape a streaming fit has – a spectrum at fixed wavelengths, a sensor bank at fixed positions, a decay sampled at fixed delays.

Ports

  • ydata – the measured ordinates, one per abscissa site, oldest site first. A COLUMN [W,1], where W is the number of entries in Abscissa. A size that disagrees stops the run and says both numbers.
  • x – the fitted parameters, a column [n,1] in the order they are named in Parameters. n and its size follow from the configuration alone, never from the input.
  • resnorm – the sum of squared residuals at the answer, [1,1]; MATLAB's second output.
  • exitflag – MATLAB's third output, [1,1]. See Notes for what each value means.

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.
  • Model – the expression fitted, over the parameter names and the independent variable: a*exp(b*t), a/(1 + exp(-b*(t - c))), a*sin(b*t + c). It is read by the console's own symbolic engine and accepts that engine's function set – sin cos tan sec csc cot asin acos atan acot sinh cosh tanh asinh acosh atanh exp log log2 log10 sqrt, the operators + − × / ^, and pi. abs and sign are refused by name, because the engine will not differentiate them without an assumption no block may depend on, and this block needs the derivative.
  • Parameters – the names fitted, separated by spaces or commas: a b. One to 4 of them, and the output column carries them in this order.
  • Independent Variable – the name the abscissa takes in the model, t by default. It must not be one of the parameter names.
  • Abscissa – the xdata, a row or a column of 2 to 40 numbers. Its length is W, the height the ydata port must have, and it need not be evenly spaced – that is the whole difference from the presets in Curve Fitting. It must have at least as many entries as there are parameters, because lsqcurvefit refuses a problem with fewer measurements than unknowns.
  • Start Point – x₀, one entry per parameter. The search is LOCAL, so this chooses which minimum is found.
  • Optimality Tolerance – MATLAB's OptimalityTolerance, default 1e-6. The search stops with flag 1 when the largest gradient entry falls below it.
  • Function Tolerance – MATLAB's FunctionTolerance, default 1e-6; flag 3.
  • Step Tolerance – MATLAB's StepTolerance, default 1e-6; flag 2.
  • Maximum Iterations – default 400, MATLAB's. The loop is emitted with a FIXED trip count, so a large value here costs every target the same run time whether the search needs it or not.
  • Maximum Function Evaluations – default 400. Reaching either limit is flag 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. The model and its derivatives are printed into each one in that target's own syntax, with the abscissa folded in as literals, so a generated core carries no expression reader and no table of sites. The three HDL targets – VHDL, Verilog and SystemVerilog – run the search in real arithmetic and are simulation-only, quantized at the port boundary: a trust region with a secular equation and a Brent root is not a Q16.16 datapath. Every setting above is baked in at export time; none of them is tunable on a generated core.

Simulink bridge

None. 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; lsqcurvefit is a MATLAB function. The block is reported rather than dropped when a model crosses. Sampling Time (s) → SampleTime, as on every block, does not apply here for the same reason.

Notes

  • Algebraic. No state: the whole fit is re-run from the start point on every sample, so the answer depends on the window and never on what came before it.
  • The Jacobian is exact, not a finite difference. The symbolic engine differentiates the model with respect to each parameter when the configuration loads. MATLAB's own default is the opposite – Jacobian is 'off', so lsqcurvefit estimates it by forward differences unless told otherwise – and the two take different steps on the same problem. Compare against Jacobian = 'on'.
  • Exit flags, MATLAB's: 1 the gradient is small enough, 2 the step is small enough, 3 the change in the sum of squares is small enough, 0 an iteration or evaluation limit was reached.
  • ⚠ LOCAL, and a small gradient is not a certificate. A different Start Point can reach a different fit, and a model that cannot describe the data walks the parameters outwards until a limit stops it – exit flag 0 with a large x is that case, not a defect.
  • ⚠ A model that can overflow is a software-only export. An exp can overflow at a parameter the search passes through, and lsqcurvefit handles that: the trial point is rejected and the trust region is cut by twenty instead of by four. The seven software targets do the same. The three HDL targets do not get that far – a real that overflows stops the simulation rather than carrying an infinity – so keep the abscissa and the model inside the range a real can hold if the generated core is going to hardware.
  • ⚠ The whole fit runs once per sample, at a fixed trip count. This is a real solver on the wire, not a recursion – budget for it.
  • The nine preset fits in Control Systems / Curve Fitting are cheaper where they apply: they fit a FIXED model on an EVENLY SPACED abscissa, and most of them collapse into a bank of constants at configuration load. Reach for this block when the model is not one of theirs, or the sites are not evenly spaced.

Code facts#

FactValue
registered typeControl_Systems/Optimization/Nonlinear_Curve_Fit
familyControl_Systems/Optimization
solver environment classICoreBlock_0_Control_Systems_1_Optimization_2_Nonlinear_Curve_Fit
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Optimization/Nonlinear_Curve_Fit/ICoreBlock_0_Control_Systems_1_Optimization_2_Nonlinear_Curve_Fit.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Control_Systems/Optimization/Nonlinear_Curve_Fit/ICoreBlock_0_Control_Systems_1_Optimization_2_Nonlinear_Curve_Fit.h
default size on canvas170 × 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
1inICoreDoubleydata
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
Modela*exp(b*t)—
Parametersa b—
Independent Variablet—
Abscissa[0.9 1.5 13.8 19.8 24.1 37.9 88.3 94.8 128.3 137.2 145.2 …—
Start Point[100; -1]—
Optimality Tolerance1e-6—
Function Tolerance1e-6—
Step Tolerance1e-6—
Maximum Iterations400—
Maximum Function Evaluations400—
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): 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; lsqcurvefit 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:

  • B0 no sample under docs/generated/samples/ — nothing to cross-check (P8.1)

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).

Nonlinear Curve Fit -- lsqcurvefit over a model you type, fitted to a window on a wire [x, resnorm, exitflag] = lsqcurvefit(F, x0, xdata, ydata) resnorm = sum over i of ( F(x, xdata_i) - ydata_i )^2

⚠ lsqcurvefit AND lsqnonlin ARE ONE SOLVER, AND MATLAB SAYS SO IN CODE RATHER THAN IN PROSE. lsqcurvefit.m (toolbox/shared/optimlib) wraps the user's F in a nested function called objective that evaluates it at the captured xdata and subtracts the captured ydata, and hands THAT to the same lsqncommon lsqnonlin uses. Nothing else differs: same default algorithm (trust-region-reflective, measured), same options, same exit flags. So the search here is the shared one -- ../ICoreTrustRegionSupport.h, where snls.m, trdog.m and trust.m are transcribed and where every claim about the ITERATION belongs -- and this file is the residual: the model, its Jacobian, and the block.

⚠ THE MODEL IS AN EXPRESSION THE USER TYPES, WHICH IS WHAT MAKES THIS A DIFFERENT BLOCK FROM ITS NINE NEIGHBOURS IN Curve_Fitting. Those fit a FIXED model on a REGULAR abscissa: exp1, power1, gauss1, fourier1, sin1, rat, weibull, a polynomial, a spline, with the sample sites at x0 + k*h. This one takes any expression the console's symbolic engine can read, over any abscissa at all, and reports lsqcurvefit's own three answers.

THE PIPELINE, all of it at configuration load and none of it per sample:

  1. "Model" is read by ICoreSymbolicProgram over the declared names -- the parameters

first, the independent variable last.

  1. ICoreSymbolicProgram::jacobian differentiates it with respect to every declared

name, through the console's own symbolic engine over exact rational arithmetic. The first n entries are the parameter derivatives, and they are the Jacobian the search gets. ⚠ SO THE JACOBIAN IS ANALYTIC, NOT A FINITE DIFFERENCE -- which is exactly the comparison below is made against (Jacobian = 'on'), because MATLAB's DEFAULT is 'off' and a finite-difference Jacobian is a different algorithm on the same problem.

  1. The abscissa is CONFIGURATION, so every one of the W model evaluations the emitted

code carries has its own xdata folded in as a literal, and the derivative of the model with respect to the abscissa -- which the engine computes and nothing needs -- is never printed.

⚠ THE ORDINATES ARE THE SIGNAL AND THE ABSCISSA IS NOT, WHICH IS A DECISION AND NOT AN OVERSIGHT. A streaming fit re-measures y at fixed sites -- a spectrum at fixed wavelengths, a sensor bank at fixed positions, a decay sampled at fixed delays -- and that is the shape every other block in this family has too: the thing that varies is on the wire, the thing that describes the problem is config. It also buys the fold in step 3: an xdata that arrived on a port could not be a literal in ten generated programs, and every model

Sample results#

No sample run is committed for this block. Samples come from the headless harness (DOCS_PLAN.md P8.1) into docs/generated/samples/; until one exists this block's behaviour is witnessed by the parity and export-verification suites, not by a plot here.