Confidence Gate — Machine Learning/Postprocessing
Machine_Learning/Postprocessing/Confidence_Gate · 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.
Confidence Gate
Machine Learning / Postprocessing
Reports the winning class only when it is worth acting on:
max = uargmax(u), valid = 1 when max ≥ Threshold and 0 otherwise, and idx = argmax(u) + base when valid, the configured Fallback Index when not.
This is what lets a controller ignore a low-confidence classification instead of switching on it. Put it after Softmax, where the threshold reads as a probability, or after a bare Dense Layer, where it reads as a logit margin. It is Argmax Decision plus one comparison, and it breaks ties the same way – the lowest index wins.
Ports
- u – the scores, a column [m,1] with m ≥ 1 – logits, probabilities or any comparable quantity.
- idx – a scalar [1,1]: the winning position plus Index Base when the gate opens, and Fallback Index exactly as configured when it does not. It is carried as a real number like every other signal in the model; it is always a whole one.
- valid – a scalar [1,1], 1 or 0: whether the winning score cleared the threshold. Emitted as a number rather than a boolean, so no backend needs a type the others lack.
- max – a scalar [1,1]: the winning entry's value, in whatever units u carried. Reported whether or not the gate opened, so a downstream monitor can see how close a rejected sample came.
Parameters
- Threshold – a scalar. The gate opens when the winning score is at or above it; the comparison is on the raw score, in the units u carries, so after a softmax it is a probability in [0,1] and after a dense layer it is a logit. Default 0.5.
- Fallback Index – a scalar, reported on idx when the gate stays shut. It is passed through exactly as configured and is not shifted by Index Base – it names no class, so there is nothing to shift. Default −1, which no zero- or one-based class can take and so reads as "no decision"; set it to a real class index instead if the graph should fall back to a default class.
- Index Base – what the first element is called:
- Zero-based (PyTorch, numpy) – the first class is 0. The default, and what a model exported from a Python framework expects.
- One-based (MATLAB) – the first class is 1, for a graph whose downstream lookup or switch is written in MATLAB's convention.
- 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. The threshold, the fallback and the index base are baked into the body at export time; there is no tunable parameter, so retuning the operating point means re-exporting.
The three HDL targets are genuine Q16.16 fixed point – the block only compares and copies, with no transcendental in it – but see the Notes on what that costs at a near-tie and at the threshold itself. A Fallback Index outside roughly ±32768 cannot be represented there and will saturate; the software targets carry it exactly.
Simulink bridge
None. The closest block on this installation is
dspstat3/Maximum, whose Value and Index mode does reproduce
the reduction and even carries an indexBase parameter – but it
has no threshold, no fallback and no validity output, and its dialog (probed in
R2026a, not read from documentation) offers no parameter any of the three could
map onto. Gating a decision on its own confidence is the whole of this block and
none of it would cross. The bridge reports the block rather than dropping it
silently, and it has no parity testbench, which is the documented
consequence of Support::None rather than a gap. Code export
verification still covers it across all ten languages.
Notes
- Algebraic and stateless: the outputs depend only on the current input, so the block cannot break an algebraic loop. In particular the gate has no hysteresis – a score hovering at the threshold makes valid chatter, exactly as a comparison should. Follow it with Debounced Decision or a Relay on max if the application needs the decision to stick.
- Nonlinear and discontinuous, and deliberately carries no state space. Both idx and valid step rather than vary, so no A/B/C/D describes the block and model reduction correctly refuses it.
- Ties go to the lowest index, the same rule Argmax Decision and K Means Assign use. The scan replaces its running best only on a strictly greater value.
- At a near-tie, or at the threshold, the HDL targets can decide differently. They compare Q16.16 values, one quantum of which is about 1.5e-5, where the in-app run compares doubles – so two scores closer than that can rank the other way round, and a score within a quantum of the threshold can fall on the other side of the gate. idx then differs by a whole index and valid by 1, rather than by a rounding error. This is inherent to reducing a continuous quantity to a discrete decision in fixed point, not a codegen defect: place the threshold away from where the scores pile up, or make the decision downstream in floating point, if your application must agree bit for bit with the simulation.
Code facts#
| Fact | Value |
|---|---|
| registered type | Machine_Learning/Postprocessing/Confidence_Gate |
| family | Machine_Learning/Postprocessing |
| solver environment class | ICoreBlock_0_Machine_Learning_1_Postprocessing_2_Confidence_Gate |
| source | src/ICoreSDK/ICoreBlockLibrary/Blocks/Machine_Learning/Postprocessing/Confidence_Gate/ICoreBlock_0_Machine_Learning_1_Postprocessing_2_Confidence_Gate.cpp |
| header | src/ICoreSDK/ICoreBlockLibrary/Blocks/Machine_Learning/Postprocessing/Confidence_Gate/ICoreBlock_0_Machine_Learning_1_Postprocessing_2_Confidence_Gate.h |
| default size on canvas | 130 × 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 | u |
| 2 | out | ICoreDouble | idx |
| 3 | out | ICoreDouble | valid |
| 4 | out | ICoreDouble | max |
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 |
|---|---|---|
Threshold | 0.5 | — |
Fallback Index | -1 | — |
Index Base | Zero-based (PyTorch, numpy)%~%One-based (MATLAB)~~Zero-ba… | — |
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): no Simulink equivalent: the closest block installed here, dspstat3/Maximum in its "Value and Index" mode, reproduces the argmax reduction but its dialog carries no threshold, no fallback index and no validity output -- probed in R2026a -- so the gate, which is the whole of this block, has no parameter to map onto
Catalog contract: src/ICoreSDK/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).
Confidence Gate — the winning class, but only when it is worth acting on max = u[argmax(u)] (ties: LOWEST index wins, Argmax Decision's rule) valid = 1 if max >= Threshold else 0 idx = argmax(u) + base if valid, else Fallback Index
Every backend performs the identical scan -- seed the running best with element 0, replace it only on a STRICTLY greater value -- and then applies ONE comparison to it. The gate is written everywhere as
if (max < Threshold), never as>=in one language and<in another: the two disagree on a sample that lands exactly on the threshold, which is precisely the sample a fixed-point backend is most likely to produce.Zero scratch anywhere. The OUTPUT ELEMENTS are the accumulators --
idxcarries the running winner during the scan and is overwritten by the fallback afterwards -- which is legal in the three HDLs and in PLC ST because those are process VARIABLES (VHDL:=, Verilog/SV blocking=, ST:=) read back within the same tick. §4's registered-write rule does not apply: nothing here crosses a clock edge. Reference for the whole shape: Argmax Decision.
Sample results#
| t | in ICoreDouble-Out-0 | out ICoreDouble-Out-0 | out ICoreDouble-Out-1 | out ICoreDouble-Out-2 |
|---|---|---|---|---|
| 0 | -2 | -1 | 0 | -2 |
| 0.4 | 0.5 | 0 | 1 | 0.5 |
| 0.8 | -2 | -1 | 0 | -2 |
| 1.2 | 0.5 | 0 | 1 | 0.5 |
| 1.6 | -2 | -1 | 0 | -2 |
| 2 | 0.5 | 0 | 1 | 0.5 |
| 2.4 | -2 | -1 | 0 | -2 |
| 2.8 | 0.5 | 0 | 1 | 0.5 |
| 3.2 | -2 | -1 | 0 | -2 |
| 3.6 | 0.5 | 0 | 1 | 0.5 |
| 4 | -2 | -1 | 0 | -2 |
| 4.4 | 0.5 | 0 | 1 | 0.5 |
| 4.8 | -2 | -1 | 0 | -2 |
| 5.2 | 0.5 | 0 | 1 | 0.5 |
Every 4th of 60 samples, from the table stimulus.
The same rig also ran:
| Stimulus | What it is | Output range |
|---|---|---|
impulse | Impulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair) | -1 … 0 |
ramp | Ramp: slope 1 from t = 0 | -1 … 0 |
sine | Sine Wave: amplitude 1, 2 rad/s, no phase, no bias | -1 … 0 |
step | Step: 0 -> 1 at t = 1 s | -1 … 0 |
Plotted: table — Repeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample
Category static · sample time 0.1 · 60 steps · commit ccf005c8 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/Machine_Learning__Postprocessing__Confidence_Gate.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).