Generated reference › Temporal Majority Vote — Machine Learning/Postprocessing
kind: generated#block#machine-learning-postprocessing

Temporal Majority Vote — Machine Learning/Postprocessing

Machine_Learning/Postprocessing/Temporal_Majority_Vote · 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.

Temporal Majority Vote

Machine Learning / Postprocessing

Reports the most common class among the last N indices – the current sample plus the N−1 before it. The index passes through unchanged in value; what changes is that a single odd sample no longer reaches the output.

Put it after Argmax Decision, whose index it takes and whose index it returns. A lone disagreeing sample is outvoted by the rest of the window, which is the SMOOTHING form of a decision stabiliser. If you would rather a lone sample were unable to move the output at all – and reset the evidence when it appears – use Debounced Decision, the latching form of the same idea.

Ties go to the lowest class index, which is Argmax Decision's rule. Over a short window ties are common rather than exceptional, so this decides a great many samples and not just a few.

Ports

  • k – the raw class index, a scalar [1,1]. Class i is the one whose window [i−0.5, i+0.5) the index falls in, after Index Base has been subtracted.
  • cls – a scalar [1,1]: the winning class, reported in the same base as the input.
  • votes – a scalar [1,1]: how many of the N window slots voted for the winner. It is at most N, and it is 0 only when no slot in the window holds a valid class at all – in which case cls falls back to Initial Class. Read it as a confidence: a winner with 2 votes of 5 is a much weaker answer than one with 5 of 5.

Parameters

  • Number Of Classes – K, a whole number ≥ 1. The classes are the K windows starting at the index base; anything outside them is not a class and votes for nothing.
  • Index Base – what the first class is called:
    • Zero-based (PyTorch, numpy) – the first class is 0. The default.
    • One-based (MATLAB) – the first class is 1.
    It shifts both the input window and the reported index by the same amount.
  • Window Length – N, a whole number ≥ 1: how many samples vote, including the current one. At N = 1 the block is a pass-through, which is the documented degenerate case. An odd N is usually what you want with two classes, since an even one can only tie.
  • Initial Class – the class position the window is seeded with, counted from 0 whatever the base. It must be less than K. It is what the block reports before any real evidence has arrived, and what it falls back to if the window ever holds no valid class.
  • 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. K, the base, N and the seed are baked into the body at export time – each of them changes which comparisons are emitted, so none could be a tunable parameter. The window and the tally are fully unrolled, at K×N comparisons.

The three HDL targets are genuine synthesizable Q16.16: comparisons and increments only, with no division and no transcendental. The window is a shift register of N−1 registers.

Simulink bridge

None. Simulink has no modal filter over a decision stream: Median Filter belongs to the DSP System Toolbox and answers a different question (the median of a numeric window is not the mode of a categorical one – it interpolates between classes, which for class labels is meaningless). There is therefore no parameter set this block could cross through, and 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

  • Stateful, and inherently discrete – the window advances once per sample, so the block declares setDiscreteOnlyBlock and takes its rate from its own Sampling Time (s). It carries no state space and model reduction correctly refuses it.
  • An index outside every class window votes for nothing, but it still occupies a slot and still ages the window along. A burst of invalid indices therefore weakens the vote rather than freezing it, which is what you want from a sensor dropout.
  • The window is seeded with Initial Class, so the first N samples are a blend of the seed and real evidence rather than undefined.
  • At a window boundary 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 an index within that distance of a ±0.5 boundary can fall in the neighbouring class. This is inherent to reducing a continuous quantity to a discrete class in fixed point, and it is the same caveat Argmax Decision and Embedding Lookup carry. Feed this block a genuinely integral index and no sample is ever near a boundary.

Code facts#

FactValue
registered typeMachine_Learning/Postprocessing/Temporal_Majority_Vote
familyMachine_Learning/Postprocessing
solver environment classICoreBlock_0_Machine_Learning_1_Postprocessing_2_Temporal_Majority_Vote
sourcesrc/ICoreSDK/ICoreBlockLibrary/Blocks/Machine_Learning/Postprocessing/Temporal_Majority_Vote/ICoreBlock_0_Machine_Learning_1_Postprocessing_2_Temporal_Majority_Vote.cpp
headersrc/ICoreSDK/ICoreBlockLibrary/Blocks/Machine_Learning/Postprocessing/Temporal_Majority_Vote/ICoreBlock_0_Machine_Learning_1_Postprocessing_2_Temporal_Majority_Vote.h
default size on canvas150 × 80 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
1inICoreDoublek
2outICoreDoublecls
3outICoreDoublevotes

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
Number Of Classes4
Index BaseZero-based (PyTorch, numpy)%~%One-based (MATLAB)~~Zero-ba…
Window Length5
Initial Class0

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 Simulink equivalent: nothing in the base library takes the MODE of a window, and the DSP System Toolbox's Median Filter answers a different question -- the median of a numeric window interpolates between neighbouring values, which for class labels produces a label nobody voted for. There is therefore no parameter set to map onto and no reference to run a parity testbench against

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

Temporal Majority Vote — the modal class of the last N decisions Class i is observed when (k - base) - i lies in [-0.5, +0.5) -- One_Hot_Encoder's rule verbatim, so no integer conversion appears in any of the ten backends.

The vote, in the order every backend spells it:

v = the current sample's slot value: position + 1, or 0 for "no class" tally_i = how many of {v} + the N-1 stored slots equal i + 1 best = the FIRST i with a strictly greater tally (ties -> LOWEST index, always) shift = the slots move down one and v takes the front

Two encodings carry the whole design and both are deliberate:

  • Slots hold position + 1 so that ZERO can mean "no vote". An index outside every class

window has to be able to occupy a slot without voting, and a zeroed HDL reset then reads as an empty window rather than as N votes for class 0.

  • The scan replaces its best only on a STRICTLY greater tally, scanning upwards, which is

Argmax_Decision's tie rule. Over a short window ties are the common case rather than the edge case, so this is what the block's answer mostly rests on.

The window is seeded with the Initial Class rather than with emptiness, so the output starts somewhere defined and decays away over the first N samples.

Sample results#

Temporal Majority Vote — Step: 0 -> 1 at t = 1 sTemporal Majority Vote — Step: 0 -> 1 at t = 1 s024012345t (s)in ICoreDouble-Out-0out ICoreDouble-Out-0out ICoreDouble-Out-1

The same rig also ran:

StimulusWhat it isOutput range
impulseImpulse: one sample of 1 at k = 5, 0 elsewhere (Repeating Sequence Stair)0 … 0
rampRamp: slope 1 from t = 00 … 3
sineSine Wave: amplitude 1, 2 rad/s, no phase, no bias0 … 1
tableRepeating Sequence Stair: [-2 -1 -0.5 0 0.5 1 2 3], one entry per sample0 … 1

Plotted: step — Step: 0 -> 1 at t = 1 s

Category dynamic · sample time 0.1 · 60 steps · commit ccf005c8 · produced by docsSample --out <folder> --steps 60 · data docs/generated/samples/Machine_Learning__Postprocessing__Temporal_Majority_Vote.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).