1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
//! Kullback–Leibler divergence.
use ;
use crateHistogram;
use crateResult;
/// Kullback–Leibler divergence of the `live` distribution *from* the `reference`
/// distribution, in nats.
///
/// ```text
/// D_KL(live ‖ reference) = Σ_bin live_i · ln(live_i / ref_i)
/// ```
///
/// # Directional asymmetry
///
/// KL is **not symmetric**: `D_KL(live ‖ reference) ≠ D_KL(reference ‖ live)`.
/// This function fixes the direction as `D_KL(live ‖ reference)` — it weights
/// each bin by the *live* probability, answering "how surprising is the live
/// data under the reference model?", which is the natural question when the
/// reference is the trusted baseline. If you need the other direction, swap the
/// arguments. For a symmetric, bounded alternative use
/// [`js_divergence`](crate::js_divergence).
///
/// Both distributions are epsilon-smoothed first (see
/// [`DEFAULT_EPSILON`](crate::metrics::DEFAULT_EPSILON)), so an empty bin never
/// yields `inf`/`NaN`.
///
/// # Errors
/// Returns [`DriftError::BinCountMismatch`](crate::DriftError::BinCountMismatch)
/// if the histograms have different bin counts.
/// [`kl_divergence`] with an explicit epsilon smoothing constant.
///
/// # Errors
/// Same as [`kl_divergence`], plus
/// [`DriftError::InvalidConfig`](crate::DriftError::InvalidConfig) if `epsilon`
/// is negative or non-finite.
/// Raw KL divergence `D_KL(p ‖ q)` over two smoothed probability vectors.
pub