Monty.Frameworks.Models.EvidenceMatching.FeatureEvidence.Calculator.DefaultFeatureEvidenceCalculator (ExTbpMonty.Core v0.1.0)

Copy Markdown View Source

Feature evidence as "how far inside the tolerance is this value".

Mirrors tbp/monty/frameworks/models/evidence_matching/feature_evidence/calculator.py::DefaultFeatureEvidenceCalculator.

Each stored value is compared with the observation, and the difference is read as evidence: no difference is 1, a difference at the tolerance is 0, and it falls off linearly in between. The per-feature scores are then averaged with the feature weights, so a feature an experiment distrusts can be made quiet without being switched off.

Three kinds of comparison

  • numeric — the absolute difference;
  • circular — the difference the short way round, so a hue of 0.99 and one of 0.01 are neighbours rather than 0.98 apart. Only the first value of a circular feature wraps (hsv's hue); saturation and value do not;
  • categorical — equal or not, with the same linear falloff applied to that 0-or-1 difference.

Summary

Types

One comparison: which value of a row it reads, how, and what the answer is worth.

Functions

The comparisons one observation makes against every row.

A native kernel's answers, as this module's: a NaN is nil.

The columns as the two binaries a native kernel reads.

Types

column()

@type column() :: %{
  index: non_neg_integer(),
  kind: :numeric | :circular | :categorical,
  value: float(),
  tolerance: float(),
  weight: float()
}

One comparison: which value of a row it reads, how, and what the answer is worth.

The index counts positions in the row, so the columns of a call are numbered 0, 1, 2, ... as a whole: a three-valued feature after a one-valued one starts at one.

Functions

columns(feature_order, feature_weights, query_features, tolerances)

@spec columns([String.t()], map(), map(), map()) :: [column()]

The comparisons one observation makes against every row.

Public because it is the whole description of the arithmetic: the rows are compared column by column and there is nothing else to say about it. A native kernel is handed these columns rather than deciding for itself what to compare.

decode_answers(binary)

@spec decode_answers(binary()) :: [float() | nil]

A native kernel's answers, as this module's: a NaN is nil.

The answers cross as bytes; ExTbpMonty.Core.MatcherNif.decode/1 is the one place that turns them back into values, because a NaN there means the same thing for every kernel: no answer for this row.

encode_columns(columns)

@spec encode_columns([column()]) :: {binary(), binary()}

The columns as the two binaries a native kernel reads.

specs carries each column's row index and what kind of comparison applies (two u32); params carries its observed value, tolerance and weight (three f64). Split in two because a kind is a small integer and the rest are doubles, and because the kernel reads both in the same order the weights are summed in.