Contents
Deep Breakdown
What it tells me (Situation Awareness): This is the natural-base specialization of the cancellation identity: for , and its symmetric counterpart for all real . These two statements say that and are exact inverses of each other. In the context of DA/DS and ML, the natural log is the default logarithm because is the base of the exponential distribution, the Gaussian, and exponential family distributions generally. The identity is used constantly in: (1) simplifying expressions in log-likelihood functions for normal and exponential distributions; (2) the softmax function and its numerically stable form involving ; (3) the parameterization of strictly positive quantities (e.g., so optimizing over avoids the constraint); (4) the exponential distribution survival function (circular but useful for manipulation); and (5) computing gradients of log-normal and Poisson likelihoods. The key operational difference from the general cancellation: requires (since is undefined for ), while works for all real (since always, so the argument to is always valid).
Flowchart
Real-world use
Where it is used
Softmax and log-softmax: in classification, the numerically stable log-softmax is . To recover probabilities from log-softmax values: — directly applying and . Unconstrained optimization of positive parameters: instead of constraining , re-parameterize as and optimize over . At the optimum, , using in reverse.
Problem it solves
Expressions involving inside a logarithm (or vice versa) look complex until the identity removes the composed function entirely. For example: the log-likelihood of the exponential distribution is — the second step uses directly.
How it solves the problem
From the definition: . Substituting into gives by definition. The symmetric direction: — since is the inverse of , applying to recovers the exponent . Both are just the statement that inverse functions composed in either order return the identity.
Example in a business
A data scientist is implementing a Poisson regression model. The model is , or equivalently . During gradient computation, they need . The cancellation is used to substitute back in, simplifying the Newton-Raphson update without needing to expand the exponential explicitly.
The formula, piece by piece
Breaking the notation:
- : Euler's number, . The base of the natural logarithm and the exponential function.
- : natural logarithm of ; the exponent to which must be raised to get . Requires .
- : "raise to the power ." Since is the exponent of that gives , raising to that exponent gives back exactly.
- : the result. The composed operation is the identity function. Two directions:
- for : exponentiating a natural log recovers the argument.
- for all real : taking the natural log of recovers the exponent. Asymmetry in domain:
- Direction 1 requires (can't take of non-positive numbers).
- Direction 2 has no restriction on (since for all real , is always defined). TRAP: base must be : .
Since , we get . The cancellation only works when the exponential base and the logarithm base are the same.
The formula in words
": raising to the natural log of gives back . The natural log and the exponential with base are exact inverses. : the natural log of to the gives back . Use these to: strip from an expression by exponentiating both sides; strip by taking of both sides; simplify directly; re-parameterize positive quantities. TRAP: mismatched bases break the identity."
Critical Concepts
- Mutual inverse functions: and satisfy and . Their graphs are mirror images across . This is the deepest geometric fact about logarithms and exponentials.
- Asymmetric domains: needs ; works for all real . In ML, parameters that must be positive (variance, scale, rate) are often re-parameterized as and then recovered via this identity.
- Log-likelihood simplification: exponential family distributions have likelihood functions of the form . Taking uses directly, converting the likelihood to a linear function of the natural parameter .
- Numerically stable softmax: computing directly overflows for large . The stable form subtracts : , valid because and the cancels. The identity underpins the equivalence.
- TRAP: does not mean and commute with all operations: .
The cancellation only applies when is the entire argument to , or is the entire exponent.
Step-by-step calculation (tiny example)
Task: simplify for the exponential distribution likelihood.
Step 1: Expand using the product rule for logs .
Step 2: Apply to the second term .
Step 3: Final result .
Step 4: Verify at , Direct: . Formula: .
Step 5: Reverse — reconstruct from log space If , then . Verify: .
Code examples
Beginner example (Python)
# Natural log cancellation: e^(ln x) = x and ln(e^x) = x
import math
import numpy as np
# Direction 1: e^(ln x) = x (requires x > 0)
print("Direction 1: e^(ln x) = x")
test_vals = [0.1, 1.0, math.e, 5.0, 100.0, 1e-4]
print(f"{'x':>10} {'ln(x)':>10} {'e^(ln x)':>12} {'matches x':>10}")
for x in test_vals:
ln_x = math.log(x)
recovered = math.exp(ln_x)
print(f"{x:>10.6f} {ln_x:>10.6f} {recovered:>12.6f} {str(abs(recovered - x) < 1e-9):>10}")
# Direction 2: ln(e^x) = x (all real x)
print("\nDirection 2: ln(e^x) = x")
exponents = [-5.0, -1.0, 0.0, 0.5, 1.0, 3.0, 10.0]
print(f"{'x':>8} {'e^x':>14} {'ln(e^x)':>10} {'matches x':>10}")
for x in exponents:
exp_x = math.exp(x)
recovered = math.log(exp_x)
print(f"{x:>8.2f} {exp_x:>14.6f} {recovered:>10.6f} {str(abs(recovered - x) < 1e-9):>10}")
# Exponential distribution log-likelihood simplification
# f(t; lambda) = lambda * e^(-lambda*t)
# ln f = ln(lambda) + ln(e^(-lambda*t)) = ln(lambda) - lambda*t
print("\nExponential distribution: ln(lambda * e^(-lambda*t))")
lam = 2.0
for t in [0.1, 0.5, 1.0, 2.0]:
direct = math.log(lam * math.exp(-lam * t))
formula = math.log(lam) - lam * t # using ln(e^x) = x
print(f" t={t:.1f}: direct={direct:.6f} formula={formula:.6f} match={abs(direct - formula) < 1e-10}")
# TRAP: mismatched base
print("\nTRAP: e^(log2 x) ≠ x (mismatched base)")
for x in [2.0, 4.0, 8.0]:
log2_x = math.log2(x)
e_pow_log2 = math.exp(log2_x) # e^(log2 x) ≠ x
print(f" x={x:.0f}: log2(x)={log2_x:.4f} e^(log2 x)={e_pow_log2:.4f} equals x: {abs(e_pow_log2 - x) < 1e-9}")Beginner example (R)
# Natural log cancellation in R: e^(ln x) = x and ln(e^x) = x # Direction 1
cat("Direction 1: exp(log(x)) = x\n")
test_vals <- c(0.1, 1, exp(1), 5, 100)
for (x in test_vals) { recovered <- exp(log(x))
cat(sprintf(" x=%-10.4f ln(x)=%-10.6f exp(ln x)=%-10.6f match=%s\n", x, log(x), recovered, abs(recovered - x) < 1e-9))
} # Direction 2
cat("\nDirection 2: log(exp(x)) = x\n")
exponents <- c(-5, -1, 0, 0.5, 1, 3, 10)
for (x in exponents) { recovered <- log(exp(x))
cat(sprintf(" x=%-6.2f exp(x)=%-14.6f log(exp(x))=%-10.6f match=%s\n", x, exp(x), recovered, abs(recovered - x) < 1e-9))
} # Exponential distribution log-likelihood
lam <- 2.0
cat("\nExponential log-likelihood: ln(lambda) - lambda*t\n")
for (t in c(0.1, 0.5, 1.0, 2.0)) { direct <- log(lam * exp(-lam * t))
formula <- log(lam) - lam * t
cat(sprintf(" t=%.1f: direct=%.6f formula=%.6f match=%s\n", t, direct, formula, abs(direct - formula) < 1e-10))
}Professional example (Python)
import numpy as np
import pandas as pd
# Scenario: unconstrained optimization of a positive parameter
# using the reparameterization sigma = exp(alpha)
# This is the core trick behind log-normal MLE and GLM scale estimation
np.random.seed(42)
N_OBS = 300
TRUE_MU = 3.5
TRUE_SIGMA = 1.2
# Generate log-normal data
raw_data = np.random.lognormal(mean=TRUE_MU, sigma=TRUE_SIGMA, size=N_OBS)
def normal_log_likelihood(data: np.ndarray, mu: float, log_sigma: float) -> float:
"""Log-likelihood of a normal distribution using the reparameterization sigma = exp(log_sigma).
Applies e^(ln sigma) = sigma to work in unconstrained log_sigma space,
avoiding the sigma > 0 constraint during optimization.
Args:
data: Array of observations (log-transformed raw data).
mu: Mean parameter (unconstrained).
log_sigma: Log of standard deviation (unconstrained; sigma = exp(log_sigma)).
Returns:
Log-likelihood value.
"""
sigma = np.exp(log_sigma) # e^(ln sigma) = sigma — cancellation identity
n = len(data)
return float(
-n * log_sigma # -n * ln(sigma) = ln(sigma^{-n})
- n / 2 * np.log(2 * np.pi)
- 0.5 * np.sum((data - mu) ** 2) / sigma**2
)
# Evaluate on a grid to find MLE
log_data = np.log(raw_data) # ln(x) = y such that e^y = x
mu_grid = np.linspace(3.0, 4.0, 50)
log_sig_grid = np.linspace(0.0, 1.0, 50)
best_ll, best_mu, best_log_sigma = -np.inf, 0.0, 0.0
for mu_val in mu_grid:
for ls_val in log_sig_grid:
ll = normal_log_likelihood(log_data, mu_val, ls_val)
if ll > best_ll:
best_ll, best_mu, best_log_sigma = ll, mu_val, ls_val
best_sigma = np.exp(best_log_sigma) # recover sigma from log_sigma using e^(ln x) = x
# Report
summary = pd.DataFrame({
"parameter": ["mu", "sigma", "log_sigma"],
"true_value": [TRUE_MU, TRUE_SIGMA, np.log(TRUE_SIGMA)],
"mle_estimate": [best_mu, best_sigma, best_log_sigma],
"sample_est": [log_data.mean(), log_data.std(), np.log(log_data.std())],
})
print("Log-normal MLE with reparameterization: sigma = exp(log_sigma)")
print("Cancellation identity: exp(ln(sigma)) = sigma used at recovery step")
print("=" * 66)
print(summary.to_string(index=False))
print(f"\nBest grid log-likelihood: {best_ll:.4f}")
print(f"Recovered sigma = exp({best_log_sigma:.4f}) = {best_sigma:.4f} " f"(true: {TRUE_SIGMA:.4f})")Professional example (R)
# Log-normal MLE: sigma = exp(log_sigma), exp(ln(sigma)) = sigma
set.seed(42)
TRUE_SIGMA <- 1.5
x <- exp(rnorm(200, 0, TRUE_SIGMA))
log_sigma_grid <- seq(0.1, 2, by = 0.05)
ll <- sapply(log_sigma_grid, function(ls) {
sigma <- exp(ls)
-sum(log(dlnorm(x, 0, sigma)))
})
best_idx <- which.max(ll)
best_log_sigma <- log_sigma_grid[best_idx]
best_sigma <- exp(best_log_sigma)
cat(sprintf("Recovered sigma = exp(%.4f) = %.4f (true: %.4f)\n", best_log_sigma, best_sigma, TRUE_SIGMA))Source: MFB p. 143