Generated reference › Gaussian Naive Bayes — Machine Learning/Classical Models
kind: generated#block#machine-learning-classical-models

Gaussian Naive Bayes — Machine Learning/Classical Models

Machine_Learning/Classical_Models/Gaussian_Naive_Bayes · 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.

Gaussian Naive Bayes

Machine Learning / Classical Models

A fitted Gaussian naive Bayes classifier, evaluated one sample at a time. Each class scores the sample by its own per-feature Gaussian, and the highest score wins:

logp[k] = log(prior[k]) − ½ Σj [ log(2π·var[k][j]) + (u[j] − mu[k][j])² ÷ var[k][j] ]

It is also quadratic discriminant analysis. Set Covariance to Full (QDA), paste one inverse covariance per class into Class Precisions, and the per-feature sum above becomes the full quadratic form

logp[k] = log(prior[k]) − ½ log|Σk| − ½·d·log(2π) − ½ (u − muk)T Σk−1 (u − muk)

– which is sklearn.discriminant_analysis.QuadraticDiscriminantAnalysis written out. Naive Bayes is the special case where every Σk is diagonal, so the two settings are one block rather than two, and they agree exactly where they overlap: give the Full mode a diagonal precision and it lands on the Diagonal mode's numbers, term for term.

This is sklearn.naive_bayes.GaussianNB with theta_, var_ and class_prior_ pasted in. It is the cheapest probabilistic classifier there is – no matrix inverse, no kernel, no tree walk – and it reports a calibrated-ish score per class rather than only a decision, which is what lets a controller refuse to act when nothing scored well.

Everything logarithmic is folded at configuration time, so the sample loop is subtract, square and multiply-accumulate and nothing else. That is what keeps this block synthesizable in fixed point.

Ports

  • u – the sample, a [d,1] column, where d is the number of COLUMNS of Class Means. One sample per step; it is checked rather than broadcast.
  • idx – [1,1], the winning class, plus Index Base. This is what a controller switches on.
  • logp – [K,1], every class's log-score in class order, where K is the number of ROWS of Class Means. The winner's own score is logp[idx], so nothing downstream has to recompute it; feed the whole vector to Softmax for posteriors, or to Confidence Gate or Top K for a decision with a threshold.

Parameters

  • Class Means – mu, a [K,d] matrix, one class per ROW. This is sklearn's theta_ orientation, so it pastes in without transposing. Its shape decides both K and d.
  • Class Variances – var, [K,d] to match: sklearn's var_. Per class AND per feature – that the features are treated as independent within a class is exactly what makes the model "naive". Read only when Covariance is Diagonal.
  • Covariance – which of the two models this is:
    • Diagonal (naive Bayes) – each feature has its own variance within a class and the features are treated as independent. Reads Class Variances; Class Precisions is not read at all.
    • Full (QDA) – each class has a whole covariance, so features may correlate. Reads Class Precisions; Class Variances and Variance Floor are not read at all, and are left alone rather than having to be cleared.
  • Class Precisions – the K inverse covariances Σk−1, each [d,d], stacked one under another in class order as a [K·d,d] matrix: rows d·k to d·k+d−1 are class k. Read only when Covariance is Full.
    • It is the inverse, and this block inverts nothing – the same convention Mahalanobis Distance uses, and for the same reason: inverting a fitted covariance is a job for wherever the model was fitted, done once against the data, not once per configuration load in a block that must also write out ten literal tables.
    • Each block must be symmetric and positive definite, which the inverse of a genuine covariance always is. A non-positive determinant is refused with the class named rather than producing a score that is not a log-probability.
    • Only the upper triangle is used, with each off-diagonal coefficient carrying both of the symmetric pair – the same number for the symmetric matrix a precision always is, and half as many multiplies in hardware.
  • Class Priors – [K,1], sklearn's class_prior_. Every entry must be greater than zero; a class with a zero prior can never win, and the log of it is not a number.
  • Variance Floor – the smallest variance used, applied before the reciprocal is taken. sklearn's var_smoothing exists for the same reason: a feature that never varied in training has zero variance and would divide by zero here. It is applied ONCE at configuration load, so it costs the generated code nothing. Read only when Covariance is Diagonal.
  • Index Base – what the first class is numbered, spelled exactly as Argmax Decision and K Means Assign spell it:
    • Zero-based (PyTorch, numpy) – the first class is 0.
    • One-based (MATLAB) – the first class is 1.
  • 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. Every class's score is unrolled at export time, and the constants baked into it are the FOLDED ones – one c[k] per class carrying the log prior and all d log normalizers together, and one −½/var[k][j] per feature. No logarithm, no division and no exponential survives into the generated code.

Full (QDA) costs the generated code no new KIND of arithmetic, only more of the same. A class's score is a list of terms coefficient · (u[i] − mu[i]) · (u[j] − mu[j]); Diagonal is the case where every term has i = j. The log determinant folds into the same c[k] the log normalizers already folded into, so it too is gone before any code is written. The cost is the term count: d terms per class in Diagonal, d(d+1)/2 in Full, since only the upper triangle is emitted.

The three HDL targets are therefore genuine synthesizable Q16.16 in both settings, not the simulation-only real arithmetic this family's activation and distance blocks need: what is left in the datapath is a subtract, a multiply and an add per term, then a comparison chain.

Simulink bridge

None. Simulink's classifier blocks live in the Statistics and Machine Learning Toolbox, which is not installed on this machine – so there is no ClassificationNaiveBayes predict block here to map onto, and a bridge could not be verified even if it were written, since the parity suite would need that toolbox on the machine running it. Independently of that, those blocks take a fitted model OBJECT rather than parameter matrices, which no configuration value can carry across the bridge. The block therefore has no parity testbench, which is the documented consequence of Support::None. Code export verification still covers it across all ten languages.

Notes

  • Stateless and algebraic: the answer depends on the current sample only.
  • No state space: the score is quadratic in the input and the decision is a comparison, so no A/B/C/D describes it and model reduction correctly refuses the block.
  • Ties go to the LOWEST class index. The scan keeps its running best and replaces it only on a strictly greater score – the same rule Argmax Decision states and K Means Assign mirrors for its argmin. Every backend spells the identical scan, because the samples that tie are exactly the ones where two implementations that both "find the maximum" disagree.
  • The index is a DISCONTINUOUS function of the input. The three HDL targets compare Q16.16 values where the reference compares doubles, so two classes whose scores sit within one quantum (about 1.5×10−5) of each other can rank the other way round, and the index then differs by a whole step. That is a property of the block, not of the generated code; the logp output is continuous and does not have it.
  • "Naive" is the Diagonal setting only. There the features are assumed independent within a class. When they are not, switch Covariance to Full (QDA) – or use Mahalanobis Distance for an unsupervised distance, or a Gaussian Mixture Model for several components per class.
  • A linear discriminant (LDA) is not a setting here, and does not need to be. LDA is QDA with one covariance SHARED by every class, so it is this block with the same precision block pasted K times – and since the quadratic term is then identical for every class, it cancels in the comparison and the discriminant is linear, exactly as the textbook says.

Code facts#

FactValue
registered typeMachine_Learning/Classical_Models/Gaussian_Naive_Bayes
familyMachine_Learning/Classical_Models
solver environment classICoreBlock_0_Machine_Learning_1_Classical_Models_2_Gaussian_Naive_Bayes
sourcesrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Machine_Learning/Classical_Models/Gaussian_Naive_Bayes/ICoreBlock_0_Machine_Learning_1_Classical_Models_2_Gaussian_Naive_Bayes.cpp
headersrc/ICoreBlocks/ICoreBlockLibrary/Blocks/Machine_Learning/Classical_Models/Gaussian_Naive_Bayes/ICoreBlock_0_Machine_Learning_1_Classical_Models_2_Gaussian_Naive_Bayes.h
default size on canvas134 × 88 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
2outICoreDoubleidx
3outICoreDoublelogp

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
Class Means[0.4 -0.3; -0.6 0.8; 1.1 0.2]—
Class Variances[0.5 0.7; 0.6 0.4; 0.8 0.55]—
Class Priors[0.35; 0.4; 0.25]—
Class Precisions[2.1 -0.45; -0.45 1.5; 1.8 0.3; 0.3 2.6; 1.3 -0.2; -0.2 1.9]—
CovarianceDiagonal (naive Bayes)%~%Full (QDA)~~Diagonal (naive Bayes)—
Variance Floor1e-9—
Index BaseZero-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.

supportSupport::None
Simulink path—
port-count rulePortsParam::None
SampleTime parameteryes

Caveat (shown to the user): no Simulink equivalent available here: its naive Bayes predict block lives in the Statistics and Machine Learning Toolbox, which is not installed on this machine, so no mapping could be verified; and those blocks take a fitted model OBJECT rather than the parameter matrices this block configures

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

Gaussian Naive Bayes — a fitted GaussianNB evaluated at inference logp[k] = c[k] + SUM_j w[k][j] * (u[j] - mu[k][j])^2 idx = argmax_k logp[k] + base

Read the header before this file: it records the fold (every log is config-only, so none of it reaches the sample loop and the three HDL targets stay genuine Q16.16), the two-output shape, and the tie rule this block shares with Argmax_Decision and K_Means_Assign.

⚠ The FOLDED tables are what every generator emits -- _c and _w, never _means/_vars directly, and never anything a run has touched. The C++ reference below folds once in loadBlockConfig() and then computes from the same two tables, so the simulation and all ten exports evaluate the identical expression in the identical order.

Sample results#

No stimulus produced a sampled output in this rig — Invalid input size at: ICore Blocks/Home/Gaussian Naive Bayes. 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 c01902987 · produced by docsSample --out <folder> --steps 60

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