Generated reference › Isolation Forest Score — Machine Learning/Anomaly Detection
kind: generated#block#machine-learning-anomaly-detection

Isolation Forest Score — Machine Learning/Anomaly Detection

Machine_Learning/Anomaly_Detection/Isolation_Forest_Score · 1 input / 2 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.

Isolation Forest Score

Machine Learning / Anomaly Detection

A fitted isolation forest, evaluated at inference. Every tree is walked as Decision Tree walks one, and what is accumulated is how deep the sample had to be buried before it was isolated:

h(x) = (1/T)·Σ ( depth(leaf) + adjustment(leaf) ), then score = 2−h(x)/c(n)

The idea is an inversion worth stating plainly: an anomaly is easy to isolate. A few random splits already separate it, so it comes to rest in a shallow leaf, h is small and the score approaches 1. A normal point sits among its neighbours and needs many splits, so h is large and the score falls towards 0.5 and below. Around 0.5 means "as hard to isolate as an average training point"; near 1 means "isolated almost immediately".

Unlike Mahalanobis Distance, which needs a covariance and assumes one elliptical blob, this makes no distributional assumption at all – which is why it is the usual first choice on data whose shape nobody wants to commit to.

Ports

  • u – the feature column, [d,1]. Every entry of Feature Index must name a row of it.
  • score – a [1,1] scalar in (0, 1]: the anomaly score above. Higher is more anomalous.
  • path – a [1,1] scalar: the mean path length h(x) itself, before the exponential. Lower is more anomalous. See Code export – on hardware this is the output to use.

Parameters

The four node columns and the adjustment are all [N,1], one row per node across the whole forest – the same stacked layout Random Forest reads.

  • Feature Index – which entry of u the node tests, zero-based. Ignored at leaves.
  • Threshold – the split value. In a fitted isolation forest this is a random value between two observed points, not an optimised one.
  • Left Child – the row taken when u[Feature] ≤ Threshold. A negative entry marks a leaf.
  • Right Child – the row taken otherwise.
  • Leaf Path Adjustment – at each leaf, the c(size) term for the samples that were never separated there: the path length they would still have needed. It is 0 for a leaf holding one sample, and for internal nodes it is ignored. This is what stops a depth-limited forest from pretending that a crowded leaf isolated anything.
  • Tree Roots – a [T,1] column giving the row each tree's root sits at. Its height is the number of trees.
  • Sample Countn, the number of samples each tree was fitted on (scikit-learn's max_samples, commonly 256). It sets the normalizer c(n) = 2(ln(n−1) + γ) − 2(n−1)/n, which is the average path length of an ordinary point – the yardstick the score is measured against.
  • Sampling Time (s) – zero or less inherits the solver's rate; a positive value runs the block at that period.

The depth is not configured: it is a property of the table, so the block computes it by walking each tree once. Asking for it as a column would be a second source of truth that could disagree with the one the walk uses.

Children must have a higher row number than their parent, in the stacked table's numbering – the rule Decision Tree documents, which here also guarantees the depth walk terminates.

Code export

All ten targets: Python, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog and PLC Structured Text. Each leaf's depth + adjustment is a single baked constant, so the body is one comparison cascade per tree adding constants, then one multiply and one exponential. c(n), the change of base and the 1/T average are folded into a single constant at configuration load; no harmonic number is ever computed at run time.

The three HDL targets are simulation-only for score: they evaluate the exponential in real and quantize once at the port boundary, exactly as Mahalanobis Distance does. The walk and the path output stay exact fixed point, and that is the reason there are two outputs – 2−h/c is strictly decreasing in h, so thresholding path from below is exactly equivalent to thresholding score from above. A synthesizable deployment uses path and pays for no exponential at all.

Simulink bridge

None, for the reasons Decision Tree and Random Forest give: the reference would be the Statistics and Machine Learning Toolbox (iforest), which is not installed on this machine, so a bridge could not be run against a parity testbench even if one were written – and it takes a fitted model object, which no parameter mapping can carry. 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.

Notes

  • Algebraic and stateless: both outputs depend only on the current sample.
  • Piecewise constant, so no state space – the score changes only when a sample crosses a split. Model reduction correctly refuses the block.
  • The score is a ranking, not a probability. 2−h/c is bounded by (0, 1] and is calibrated so that an average training point sits near 0.5, but it is not the probability of anything. Choose an operating point by looking at scores on known-good data, which is what path makes easy.
  • A depth-limited forest needs its adjustments. If every leaf carries adjustment 0 while the trees were cut short at a depth limit, every deep sample reports the same saturated path length and the score stops discriminating among exactly the points it was supposed to rank.

Code facts#

FactValue
registered typeMachine_Learning/Anomaly_Detection/Isolation_Forest_Score
familyMachine_Learning/Anomaly_Detection
solver environment classICoreBlock_0_Machine_Learning_1_Anomaly_Detection_2_Isolation_Forest_Score
sourcesrc/ICoreSDK/ICoreBlockLibrary/Blocks/Machine_Learning/Anomaly_Detection/Isolation_Forest_Score/ICoreBlock_0_Machine_Learning_1_Anomaly_Detection_2_Isolation_Forest_Score.cpp
headersrc/ICoreSDK/ICoreBlockLibrary/Blocks/Machine_Learning/Anomaly_Detection/Isolation_Forest_Score/ICoreBlock_0_Machine_Learning_1_Anomaly_Detection_2_Isolation_Forest_Score.h
default size on canvas150 × 85 px
ports at insert1 in, 2 out
code generators implementedPython, MATLAB, Java, Rust, C, C++, VHDL, Verilog, SystemVerilog, PLC Structured Text

Ports#

#DirectionSignal typeDescription label
1inICoreDoubleu
2outICoreDoublescore
3outICoreDoublepath

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 Path Adjustment[0; 0; 0; 0; 0.4; 0; 0; 0; 0; 0.7; 0; 0; 1.2]
Tree Roots[0; 5; 10]
Sample Count256

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 (iforest), which is not installed on this machine, so a bridge could not be run against a parity testbench even if one were written -- and it takes a fitted model OBJECT from a workspace variable, which no parameter mapping can carry. That is why the forest is pasted here as plain stacked columns

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

Isolation Forest Score — how few splits it took to isolate this sample h(x) = (1/T) * SUM over trees of ( depth(leaf) + adjustment(leaf) ) score = 2^( -h(x) / c(n) ) c(n) = 2*(ln(n-1) + gamma) - 2*(n-1)/n

Random_Forest's stacked table and Decision_Tree's walk, with a different accumulator. An anomaly is easy to isolate and comes to rest SHALLOW, so it scores near 1; a normal point needs many splits and scores near 0.5 or below.

Three folds make this cheap, and each is a decision rather than an optimization:

  1. EVERY LEAF'S PATH LENGTH IS A COMPILE-TIME CONSTANT. Depth is a property of the table, so

it is computed here by walking each tree once; the adjustment comes from the fit. Their sum is baked into the cascade, so no target counts steps and no target indexes anything -- the emitted body has exactly Random_Forest's shape, acc = acc + <constant>.

  1. THE WHOLE NORMALIZER IS ONE CONSTANT: score = exp(-acc * kExp) with kExp = ln2/(c(n)*T).

The harmonic number, the change of base and the 1/T average are all resolved before a line of code is generated.

  1. path IS A SECOND OUTPUT, not a debug aid. It is the mean path length in pure fixed

point, and 2^(-h/c) is strictly DECREASING in h -- so thresholding path from below is exactly equivalent to thresholding score from above, and a synthesizable deployment can drop the exponential entirely.

⚠ The three HDL targets are SIMULATION-ONLY for score because of that exponential, the way Mahalanobis_Distance and the activation blocks are. The walk itself, and path, stay exact Q16.16 -- which is the point of splitting the outputs.

Sample results#

No stimulus produced a sampled output in this rig — Feature index out of range at: ICore Blocks/Home/Isolation Forest Score. 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__Anomaly_Detection__Isolation_Forest_Score.json