Contents
Deep Breakdown
What it tells me (Situation Awareness): This rule converts a base raised to a sum of exponents into a product of that base raised to each individual term. It is the generalization of the basic exponent rule to an arbitrary summation over . In DA/DS, this shows up constantly when working with likelihoods, survival probabilities, and compound growth: the log-likelihood of independent observations is a sum of log-probabilities, and exponentiating it recovers the product of individual probabilities. Understanding this identity is what connects the sum-of-logs form (used for numerical stability) to the product form (the actual probability expression).
Flowchart
Real-world use
Where it is used
Log-likelihood computation in machine learning and statistics, compound survival probabilities in customer lifetime value models, computing joint probabilities of independent events, and converting between sum-of-logs (numerically stable) and product-of-probabilities (interpretable) representations.
Problem it solves
In ML, you compute the log-likelihood for numerical stability. But the actual likelihood is the product . This identity is the bridge: . The identity also explains why the geometric mean of rates equals the single compounded rate over periods.
How it solves the problem
From the basic exponent rule: . Apply this inductively to a sum of terms: . Each term in the sum becomes a factor in the product.
Example in a business
A user retention model gives daily survival rates: day 1 = 0.95, day 2 = 0.92, day 3 = 0.88. The log-survival scores are . Exponentiate: . This equals . That is the identity: .
The formula, piece by piece
- : the base (any positive real, often in statistics)
- : a sum of terms — this is the exponent
- : the product operator — the capital-pi notation
- : the base raised to each individual term Full identity: Special case (): — this is the log-to-probability bridge.
The formula in words
"A base raised to a sum of terms equals the product of that base raised to each individual term." . Also: . Same result via the identity.
Critical Concepts
- This is just scaled up. The summation notation is not new math; it is the same rule applied times.
- The case is the most common in DA/DS: . Log-sum to probability product.
- Used for numerical stability: products of small probabilities underflow to zero in floating point; summing their logs avoids this. The identity lets you interpret the result.
- Requires . The base must be positive for real-valued exponentiation across all .
- Order of the product does not matter (multiplication is commutative), so the product can be evaluated in any order.
Step-by-step calculation (tiny example)
Step 1: Write out the sum in the exponent Let , , , . Sum: . Left side: .
Step 2: Convert to product form using the identity Right side: .
Step 3: Verify both sides are equal . Confirmed. Now try the log version: . Exponentiate: . Check: . Confirmed.
Code examples
Beginner example (Python)
import math
import numpy as np # Identity: c^(sum of f(n)) = product of c^f(n) # Example 1: integer powers
c = 2.0
f_values = [1.0, 2.0, 3.0]
exponent_sum = sum(f_values)
left_side = c ** exponent_sum
right_side = math.prod(c ** f for f in f_values)
print("Identity: c^(sum f(n)) = product of c^f(n)")
print(f"c = {c}, f(n) = {f_values}")
print(f"Sum of f(n) = {exponent_sum}")
print(f"Left side: c^{exponent_sum:.0f} = {left_side}")
print(f"Right side: product c^f(n) = {right_side}")
print(f"Equal: {math.isclose(left_side, right_side)}") # Example 2: log-sum to probability product (e as base)
print("\nLog-sum to probability product (base e):")
probs = [0.95, 0.92, 0.88]
log_sum = sum(math.log(p) for p in probs)
via_identity = math.exp(log_sum)
direct_product = math.prod(probs)
print(f"Probabilities: {probs}")
print(f"Sum of logs: {log_sum:.6f}")
print(f"exp(log_sum): {via_identity:.6f}")
print(f"Direct product: {direct_product:.6f}")
print(f"Match: {math.isclose(via_identity, direct_product)}")Beginner example (R)
# Identity: c^(sum f(n)) = product of c^f(n) # Example 1: integer powers
c_base <- 2
f_values <- c(1, 2, 3)
left_side <- c_base ^ sum(f_values)
right_side <- prod(c_base ^ f_values)
cat(sprintf("c = %d, f(n) = %s\n", c_base, paste(f_values, collapse=", ")))
cat(sprintf("Left: c^%d = %.0f\n", sum(f_values), left_side))
cat(sprintf("Right: product c^f(n) = %.0f\n", right_side))
cat(sprintf("Equal: %s\n", isTRUE(all.equal(left_side, right_side)))) # Example 2: log-sum to probability product
probs <- c(0.95, 0.92, 0.88)
log_sum <- sum(log(probs))
via_id <- exp(log_sum)
direct_prod <- prod(probs)
cat(sprintf("\nexp(sum of logs): %.6f\n", via_id))
cat(sprintf("Direct product: %.6f\n", direct_prod))
cat(sprintf("Match: %s\n", isTRUE(all.equal(via_id, direct_prod))))Professional example (Python)
import numpy as np
import pandas as pd # Scenario: user retention across multiple cohorts
# For each cohort, daily survival rates are given
# The identity converts the log-sum survival score back to the actual survival probability
np.random.seed(42)
cohorts = { "Cohort_Jan": [0.95, 0.92, 0.90, 0.88, 0.87], "Cohort_Feb": [0.97, 0.94, 0.91, 0.89, 0.85], "Cohort_Mar": [0.93, 0.91, 0.89, 0.86, 0.84],
}
records = []
for cohort_name, daily_rates in cohorts.items():
# Log-sum: numerically stable computation
log_sum_survival = sum(np.log(r) for r in daily_rates) # Via identity: exp(sum of logs) = product of rates
survival_via_identity = np.exp(log_sum_survival) # Direct product (same result, but can underflow for many small probs)
survival_direct = np.prod(daily_rates)
records.append({ "cohort": cohort_name, "n_days": len(daily_rates), "log_sum_survival": round(log_sum_survival, 6), "survival_via_identity": round(survival_via_identity, 6), "survival_direct": round(survival_direct, 6), "match": np.isclose(survival_via_identity, survival_direct), })
df = pd.DataFrame(records)
print("Retention Survival: exp(sum log rates) = product(rates)")
print("Identity: c^(sum f(n)) = prod c^f(n), here c=e, f(n)=log(rate_n)")
print()
print(df.to_string(index=False))
print(f"\nAll identity checks passed: {df['match'].all()}")Professional example (R)
# exp(sum log rates) = product(rates)
set.seed(42)
rates <- runif(5, 0.85, 0.99)
sum_log_rates <- sum(log(rates))
product_rates <- prod(rates)
exp_of_sum <- exp(sum_log_rates)
match <- isTRUE(all.equal(exp_of_sum, product_rates))
cat(sprintf("exp(sum(log(rates))) = %.6f product(rates) = %.6f Match: %s\n", exp_of_sum, product_rates, match))Source: AIO p. 44