Generated reference › Random Forest — Machine Learning/Classical Models
kind: generated#block#machine-learning-classical-models

Random Forest — Machine Learning/Classical Models

Machine_Learning/Classical_Models/Random_Forest · 1 input / 1 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.

Random Forest

Machine Learning / Classical Models

A fitted forest, evaluated at inference. Every tree is walked exactly as Decision Tree walks one – left when u[Feature] ≤ Threshold – and the leaves the T trees reach are combined:

  • Average (regression)y = (1/T)·Σ leafValue, the mean of the trees' predictions.
  • Vote (classification) – each tree's leaf names a class, and y is the class the most trees chose.

The whole forest arrives as one stacked table: every tree's nodes laid end to end in the same five columns a single tree uses, plus a Tree Roots column naming the row each tree starts at. That is what RandomForestClassifier.estimators_ gives you once flattened, and it is the same table Gradient Boosted Trees and Isolation Forest Score read.

Ports

  • u – the feature column, [d,1]. Every entry of Feature Index must name a row of it.
  • y – a [1,1] scalar: the averaged prediction, or the winning class label.

Parameters

The five node columns are all [N,1], one row per node across the whole forest, in the layout scikit-learn's tree_ already uses.

  • Feature Index – which entry of u the node tests, zero-based. Ignored at leaves.
  • Threshold – the value it is tested against. Ignored at leaves.
  • Left Child – the row taken when u[Feature] ≤ Threshold. A negative entry marks a leaf, scikit-learn's own convention.
  • Right Child – the row taken otherwise. Ignored at leaves.
  • Leaf Value – under Average, the value this leaf predicts; under Vote, the class index it votes for, a whole number from 0 to Class Count−1.
  • Tree Roots – a [T,1] column: the row each tree's root sits at. Its height is the number of trees, and it is the only thing that tells one tree from the next in the stacked table.
  • Aggregation – how the T leaves are combined. Each is a different code path, so each is verified as a mode of its own:
    • Average (regression) – the mean of the leaf values.
    • Vote (classification) – the most-chosen class.
  • Class Count – how many classes the vote ranges over, used by Vote only. Leaf values must then be whole numbers in [0, Class Count−1].
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.

Children must have a higher row number than their parent, counted in the stacked table's own numbering. Every fitted-tree exporter emits them that way, and requiring it means a malformed table is reported at the start of the run rather than sending the code generator into an endless walk.

Ties in the vote go to the lowest class index – the scan replaces its best only on a strictly greater count. That is Argmax Decision's rule, used here deliberately rather than re-decided, so a diagram holding both blocks cannot get two different answers to the same question.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. Every target is exact, and the three HDL targets are genuinely synthesizable Q16.16: the body is comparisons, additions of baked constants, and one constant multiply by 1/T, which is folded at configuration load so that no divider ever appears.

The forest is emitted as nested if cascades, one per tree, for the reason Decision Tree documents: a literal walk needs a data-dependent index into five arrays per level, which in hardware is a multiplexer over every node. Under Vote the block emits one cascade per class, in which a leaf contributes 1 if it votes for that class and 0 if it does not – so the emitted size is proportional to nodes × classes, and a forest with many classes produces a correspondingly large core.

Simulink bridge

None, for exactly the reasons Decision Tree gives: the reference would be the Statistics and Machine Learning Toolbox, which is not installed on this machine, so a bridge could not be run against a parity testbench even if one were written – and its predict blocks take a fitted model object from a workspace variable, which no parameter mapping can carry. That is why the forest is pasted here as plain columns. The bridge reports the block rather than dropping it silently, and it has no parity testbench, which is the documented consequence of Support::None. Code export verification still covers it across all ten languages, in both modes.

Notes

  • Algebraic and stateless: the output depends only on the current sample.
  • Piecewise constant in both modes, so no state space, for the reason Decision Tree gives: no A/B/C/D is true of a staircase, and model reduction correctly refuses the block.
  • Averaging is what a forest is for. A single tree's output jumps at every split; T of them averaged jump by roughly 1/T as much, which is the whole reason a forest is smoother and more accurate than its members.
  • The comparison is ≤ going left, matching scikit-learn. A sample landing exactly on a threshold goes left, and in fixed point that is not rare: an HDL port delivers Q16.16, so an exactly representable threshold is landed on rather than approached.
  • Under Vote the HDL targets can disagree at a near-tie, the way Argmax Decision documents: they compare Q16.16 counts, and a sample whose features sit within one quantum of a split can send a tree the other way and move the winning class by a whole index. That is inherent to reducing a continuous quantity to a discrete decision in fixed point.

Code facts#

FactValue
registered typeMachine_Learning/Classical_Models/Random_Forest
familyMachine_Learning/Classical_Models
solver environment classICoreBlock_0_Machine_Learning_1_Classical_Models_2_Random_Forest
sourcesrc/ICoreSDK/ICoreBlockLibrary/Blocks/Machine_Learning/Classical_Models/Random_Forest/ICoreBlock_0_Machine_Learning_1_Classical_Models_2_Random_Forest.cpp
headersrc/ICoreSDK/ICoreBlockLibrary/Blocks/Machine_Learning/Classical_Models/Random_Forest/ICoreBlock_0_Machine_Learning_1_Classical_Models_2_Random_Forest.h
default size on canvas140 × 85 px
ports at insert1 in, 1 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubleu
2outICoreDoubley

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
Feature Index[0; 1; -2; -2; -2; 1; 2; -2; -2; -2; 2; -2; -2]
Threshold[0.35; -0.2; 0; 0; 0; 0.1; 0.5; 0; 0; 0; -0.3; 0; 0]
Left Child[1; 2; -1; -1; -1; 6; 7; -1; -1; -1; 11; -1; -1]
Right Child[4; 3; -1; -1; -1; 9; 8; -1; -1; -1; 12; -1; -1]
Leaf Value[0; 0; -1.5; 0.4; 1.75; 0; 0; 0.6; -0.8; 1.2; 0; -0.5; 1.4]
Tree Roots[0; 5; 10]
AggregationAverage (regression)%~%Vote (classification)~~Average (re…
Class Count3

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): no verifiable Simulink equivalent. The reference would be the Statistics and Machine Learning Toolbox (TreeBagger / fitcensemble and their predict blocks), which is not installed on this machine, so a bridge could not be run against a parity testbench even if one were written. Those blocks also take a fitted model OBJECT from a workspace variable, which no parameter mapping can carry -- which is exactly why this block takes the forest as plain stacked columns instead

Catalog contract: src/ICoreSDK/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 every 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).

Random Forest — Decision_Tree's walk over a STACKED table, accumulated across the trees Average: y = (1/T) * SUM over trees of leafValue(leaf reached) Vote: y = argmax over classes k of (how many trees reached a leaf labelled k)

⚠ ONE EMITTED SHAPE SERVES BOTH MODES: every leaf action is acc = acc + <constant>. Average adds the leaf's value; Vote emits one cascade per class in which a leaf adds 1.0 when its label is that class and 0.0 when it is not. Adding a literal zero rather than pruning the branch is deliberate -- a pruned branch leaves an empty block, which is a syntax error in Python and a special case in the other nine, and the pruning rule would then be a tenth thing ten backends could disagree about. Adding zero is exact everywhere, Q16.16 included.

⚠ COUNTS ARE COMPUTED ONE CLASS AT A TIME, never held in an array, so the vote needs exactly three live values: the running count, the best count, and the winning class. That is what makes it emittable in VHDL at all, where a process has acc, acc2 and the output field and nothing else (§4's HDL base). It costs K passes over the table in emitted size and buys a body that is the same shape in all ten languages.

⚠ Every target is EXACT and the three HDLs are genuinely SYNTHESIZABLE: comparisons, adds of baked constants, and one constant multiply by 1/T at the end. The reciprocal is folded at config load rather than divided per sample -- Standard_Scaler's rule, and the reason no divider appears in the datapath.

Sample results#

No stimulus produced a sampled output in this rig — Feature index out of range at: ICore Blocks/Home/Random Forest. 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 ccf005c8 · produced by docsSample --out <folder> --steps 60

Sample data: docs/generated/samples/Machine_Learning__Classical_Models__Random_Forest.json