DM_LOGSUM_NORM
This function uses a log-domain normalization step to transform unnormalized log-scores into categorical probabilities. It is useful for Bayesian workflows where posterior quantities are accumulated in log space.
Given log-values \ell_1,\ldots,\ell_K, it computes:
\log Z = \log\left(\sum_{i=1}^K e^{\ell_i}\right), \qquad p_i = \frac{e^{\ell_i}}{\sum_{j=1}^K e^{\ell_j}}
Computing \log Z with a stable log-sum-exp routine prevents overflow and underflow.
Excel Usage
=DM_LOGSUM_NORM(log_values)
log_values(list[list], required): Unnormalized log-scores as a 2D numeric range.
Returns (list[list]): 2D array with one row for log normalizer and one row for normalized probabilities.
Example 1: Three-category log-scores normalize to probabilities
Inputs:
| log_values | ||
|---|---|---|
| 0 | -1 | -2 |
Excel formula:
=DM_LOGSUM_NORM({0,-1,-2})
Expected output:
| Result | ||
|---|---|---|
| 0.407606 | ||
| 0.665241 | 0.244728 | 0.0900306 |
Example 2: Mixed-scale scores remain numerically stable
Inputs:
| log_values | |||
|---|---|---|---|
| 15 | 2 | -5 | -10 |
Excel formula:
=DM_LOGSUM_NORM({15,2,-5,-10})
Expected output:
| Result | |||
|---|---|---|---|
| 15 | |||
| 0.999998 | 0.00000226032 | 2.06115e-9 | 1.38879e-11 |
Example 3: Matrix-shaped log-values are flattened for normalization
Inputs:
| log_values | |
|---|---|
| 1.2 | 0.4 |
| -0.6 | -2.5 |
Excel formula:
=DM_LOGSUM_NORM({1.2,0.4;-0.6,-2.5})
Expected output:
| Result | |||
|---|---|---|---|
| 1.6943 | |||
| 0.609997 | 0.274089 | 0.100832 | 0.0150813 |
Example 4: Very negative log-values avoid underflow issues
Inputs:
| log_values | ||
|---|---|---|
| -900 | -901 | -903 |
Excel formula:
=DM_LOGSUM_NORM({-900,-901,-903})
Expected output:
| Result | ||
|---|---|---|
| -899.651 | ||
| 0.705385 | 0.259496 | 0.035119 |
Python Code
import numpy as np
from scipy.special import logsumexp as scipy_logsumexp
def dm_logsum_norm(log_values):
"""
Compute a stable log normalizer and normalized probabilities from log-values.
See: https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.logsumexp.html
This example function is provided as-is without any representation of accuracy.
Args:
log_values (list[list]): Unnormalized log-scores as a 2D numeric range.
Returns:
list[list]: 2D array with one row for log normalizer and one row for normalized probabilities.
"""
try:
def to2d(v):
return [[v]] if not isinstance(v, list) else v
log_values = to2d(log_values)
if not isinstance(log_values, list) or not all(isinstance(row, list) for row in log_values):
return "Error: log_values must be a 2D list"
flat = []
for row in log_values:
for val in row:
try:
flat.append(float(val))
except (TypeError, ValueError):
continue
if len(flat) == 0:
return "Error: log_values must contain at least one numeric value"
arr = np.asarray(flat, dtype=float)
log_z = float(scipy_logsumexp(arr))
probs = np.exp(arr - log_z).tolist()
width = len(probs)
first_row = [log_z] + [""] * max(0, width - 1)
return [first_row, probs]
except Exception as e:
return f"Error: {str(e)}"