The math behind choicer: multinomial logit
Source:vignettes/articles/multinomial_logit_math.Rmd
multinomial_logit_math.RmdThis document provides a detailed mathematical description of the
multinomial logit (MNL) model as implemented in
src/mnlogit.cpp.
Table of Contents
- Notation
- Model Definition
- Log-Likelihood Function
- Gradient Computation
- Hessian Computation
- Elasticity Computation
- Diversion Ratio Computation
- BLP Contraction Mapping
- Willingness to Pay
- Consumer Surplus and the Logsum
- Goodness of Fit
- Choice-Based Sampling and WESML Weighting
- Implementation Details
Notation
| Symbol | Description |
|---|---|
| Index for individuals (choice situations) | |
| Index for alternatives available to individual | |
| Choice set for situation ; it contains inside alternatives and, when requested, the implicit outside alternative 0 | |
| The alternative chosen by individual | |
| Weight for individual | |
| Row vector of covariates for individual and alternative () | |
| Coefficient vector () | |
| Alternative-specific constant (ASC) for alternative | |
| Systematic (deterministic) utility | |
| Probability that individual chooses alternative |
1. Model Definition
1.1 Utility Specification
The utility that individual derives from alternative is:
where the systematic utility is:
and is an i.i.d. Type I Extreme Value (Gumbel) error term with location 0 and scale 1.
1.2 Alternative-Specific Constants (ASCs)
The ASC parameters capture the average effect of unobserved factors for each alternative. For identification, one ASC must be normalized:
Without outside option (
include_outside_option = FALSE): The first inside alternative’s ASC is fixed to zero (). The parameter vector contains free ASC parameters.With outside option (
include_outside_option = TRUE): The outside option (alternative 0) has utility , serving as the reference. All inside alternatives have free ASC parameters.
Code reference: src/mnlogit.cpp
2. Log-Likelihood Function
2.1 Choice Probability
Under the Type I Extreme Value distribution assumption, the probability that individual chooses alternative follows the multinomial logit (softmax) formula:
If an outside option is included, the denominator includes
.
The outside option is implicit: it has no row in
,
no free coefficient, and is prepended internally to the vector of inside
utilities. Global alternative IDs (alt_idx) need not equal
an alternative’s local row position in
;
the former map ASCs across situations, whereas choice_idx
records the local chosen position (or 0 for the outside option).
Code reference: src/mnlogit.cpp
2.2 Log-Likelihood
The log-likelihood is the weighted sum of the log-probabilities of the observed choices:
where is the full parameter vector and is the alternative chosen by individual .
Expanding the log-probability:
Code reference: src/mnlogit.cpp
2.3 Log-Sum-Exp Trick for Numerical Stability
To prevent numerical overflow when computing the denominator, the implementation subtracts the maximum utility before exponentiation:
where .
This gives:
The log-probability becomes:
Code reference: src/mnlogit.cpp
3. Gradient Computation
3.1 General Formula
The gradient of the log-probability of the chosen alternative with respect to utility is:
where is the indicator function (1 if is the chosen alternative, 0 otherwise).
3.2 Gradient with Respect to
Since , the contribution to the gradient from individual is:
This simplifies to:
The full gradient is:
Implementation note. Define the difference vector
with entries
.
Then the sum above is
,
a single BLAS matrix-vector product (X_i.t() * diff_vec).
When an outside option is present,
covers only the
inside alternatives and
is sliced accordingly (diff_vec.subvec(1, m_i)).
Code reference: src/mnlogit.cpp
3.3 Gradient with Respect to
Since , the contribution to the gradient from individual for alternative is:
The gradient is computed only for the free ASC parameters: - Without outside option: (fixed), so we compute for - With outside option: we compute for all inside alternatives
The delta gradient reuses the same difference vector computed for the beta block; its entries are scattered into the appropriate positions of the gradient vector via an irregular index mapping.
Code reference: src/mnlogit.cpp
4. Hessian Computation
4.1 Analytical Hessian — Full-Vector Form
The Hessian of the log-likelihood for individual is:
where is individual ’s choice set (including the outside option when present) and is the “score vector” for alternative :
Here is the covariate vector for alternative , is the standard basis vector in indicating the free ASC position for alternative , and is the number of free ASC parameters ( without outside option, with outside option). For the outside option itself, (no covariates, no free ASC).
This can be written compactly as:
where the variance is taken under the discrete distribution . The full Hessian is and the function returns (Hessian of the negative log-likelihood).
4.2 Block Decomposition
The parameter vector is with (covariate coefficients) and (free ASC parameters). Because has at most nonzero entries — in the block and at most one in the block — the dense outer product has only three nontrivial submatrices. Summing over alternatives, the individual Hessian decomposes into three blocks:
Beta-beta block (, symmetric):
where the sum runs over the inside alternatives only (since ). This is the probability-weighted covariance of the covariates:
Delta-delta block (, symmetric):
Because at most one alternative maps to each free ASC index, is the vector of choice probabilities for ASC-bearing alternatives. The diagonal of is and the off-diagonal entry is .
Beta-delta block (, generally not symmetric):
This block measures the probability-weighted covariance between the covariates and the ASC indicators.
Why the decomposition equals the full-vector form. Partition . Then:
Subtracting block by block yields exactly , , above.
4.3 Symmetry Exploitation
and are symmetric. The implementation accumulates only the upper triangles of these blocks during the inner loop over alternatives and reflects them once after the loop, halving the write operations for those blocks:
-
BB: the double loop
for r in 0..K-1, for c in r..K-1accumulates into the upper triangle; the lower triangle is filled by mirroring. - DD: is a length- vector; the matrix is assembled from the vector after all alternatives are processed, with upper triangle only and then mirrored.
- BD: rectangular — the full block is computed (no symmetry available across the two index spaces).
The existing final symmetrization
global_hess = 0.5*(global_hess + global_hess^T) remains as
a numerical tidy-up.
Complexity. For situation , the block assembly costs for BB, for BD scatter, and for DD, versus for a naive dense outer product for every alternative. Constant factors are reduced by triangular accumulation in the symmetric blocks. The exact speedup depends on , , and ; it is largest when the ASC block is large enough that repeating its dense outer products over alternatives would dominate.
4.4 ASC Normalization and Indexing
The free-delta position for inside alternative (0-based local index within individual ’s choice set, with ) is:
where alt_idx0_i[a] is the 0-based global alternative ID
from alt_idx0. When there is no outside option, the first
inside alternative
()
has its ASC fixed to zero by the identification convention, so it
contributes no free ASC entry and is skipped in the BD and DD
accumulation.
Implementation note. The optimization is internal and numerically equivalent (within in absolute terms, well within the tolerance verified at test time) to the prior dense outer-product implementation. The public results — vcov, se, logLik, and all post-estimation quantities — are unchanged.
Code reference: src/mnlogit.cpp, function
mnl_loglik_hessian_parallel.
5. Elasticity Computation
5.1 Elasticity Definitions
The elasticity measures the percentage change in choice probability with respect to a percentage change in an attribute. For attribute with coefficient :
Own-Elasticity (elasticity of with respect to ):
Cross-Elasticity (elasticity of with respect to where ):
5.2 Derivation
Starting from the choice probability:
Taking the derivative with respect to :
For own-elasticity ():
Therefore:
For cross-elasticity ():
Therefore:
5.3 Weighted Average of Individual Elasticities
The implementation computes a weighted average of the individual probability elasticities:
where is the elasticity for individual .
This estimand is not, in general, the elasticity of the aggregate share . The latter is
and requires a definition of the common market-level perturbation
when the attribute varies over individuals. elasticities()
deliberately reports the average of individual elasticities above; rows
index responding alternatives and columns index perturbed alternatives.
With an outside option, index 0 is included in the returned matrix; its
attribute value is zero, so the outside-option perturbation column is
zero.
Code reference: src/mnlogit.cpp
6. Diversion Ratio Computation
6.1 Definition
The diversion ratio from alternative to alternative measures the fraction of demand lost by that is captured by when becomes less attractive (e.g., due to a price increase). It is a key metric in antitrust analysis and merger simulation.
where is the (weighted) aggregate demand for alternative and is the price of .
6.2 Derivation for MNL
From the MNL choice probability derivatives (Section 5.2), the demand derivatives with respect to price (with coefficient ) are:
Substituting into the diversion ratio formula:
The price coefficient cancels, so the diversion ratio does not depend on which covariate we differentiate with respect to. This is a consequence of the IIA property.
6.3 Aggregate Diversion Ratio Matrix
The implementation computes the full diversion ratio matrix where:
6.4 Properties
Column sums equal one. For a given alternative , the off-diagonal entries in column sum to 1, meaning all diverted demand is accounted for:
where we used .
IIA proportionality. When all individuals face the same choice set and the same covariates (so does not vary across ), the diversion ratio simplifies to:
where is the market share of alternative . This means diversion is proportional to the receiving alternative’s market share, independent of any characteristics of the losing alternative other than its own share. This is a well-known limitation of MNL due to IIA.
6.5 Implementation
The function accumulates two quantities in parallel across individuals:
- Numerator matrix: for each pair
- Denominator vector: for each
The final matrix is computed as for , with .
Code reference: src/mnlogit.cpp
7. BLP Contraction Mapping
7.1 Problem Statement
Given observed market shares , find the ASC parameters such that the model-predicted shares match the observed shares:
where is the predicted market share for alternative .
7.2 Contraction Mapping Algorithm
The Berry–Levinsohn–Pakes fixed-point iteration is:
This is equivalent to:
The standard convergence result presumes a feasible vector of strictly positive shares and the usual connected-substitutes conditions. The low-level kernel checks positivity and length but does not check that target shares sum to one; a target outside the share simplex need not correspond to any utility vector.
7.3 Full-Vector Normalization and Implementation Details
The contraction operates on a full vector of mean utilities, which differs from the free ASC block stored in the fitted parameter vector:
-
No outside option.
deltahas length (theblp()method prepends the normalized zero to the fitted length- ASC block). The initial vector and final result are normalized by subtracting their first element. The returned vector has length and therefore includes the baseline zero; it is not the length- free-parameter block. -
Outside option. The supplied and returned
deltavectors have length and contain inside alternatives only. Internally the kernel prepends the outside mean utility , applies the update in the length- share space, and pins the outside entry to zero after every update. It does not subtract the first inside ASC. -
Prediction and stopping. Predicted shares are
weighted by
.
Iteration stops when the largest absolute change in the full working
delta vector is below
tol, or aftermax_iter. All target shares must be strictly positive.
When an outside option is present, its target share does not receive
a free update; it is matched indirectly when the inside targets and
outside target form a coherent share vector summing to one. On
exhaustion of max_iter the C++ kernel prints a warning and
returns its last iterate rather than attaching a convergence flag.
Code reference: src/mnlogit.cpp
8. Willingness to Pay
8.1 Definition
When one covariate is a price with coefficient , the willingness to pay (WTP) for attribute is the marginal rate of substitution between the attribute and price — the price change that leaves utility unchanged after a unit change in the attribute:
Since utility is linear, the same ratio applies to ASCs: the WTP for alternative ’s unobserved quality is .
8.2 Delta-Method Standard Errors
WTP is a nonlinear function of the estimated coefficients. By the delta method, with the estimated coefficient covariance matrix,
where is the block of for . The gradient is analytic (exact), so no numerical differentiation is involved. Confidence intervals use the normal approximation:
Because the estimates and covariance are stored in natural (unscaled)
units, no scaling adjustment is needed even when the model was estimated
with scale_vars.
Caveat. The ratio of two asymptotically normal estimators has heavy tails when the denominator is imprecisely estimated; the delta-method interval is a first-order approximation that deteriorates as falls. For a weakly identified price coefficient, simulation methods (Krinsky–Robb) or Fieller intervals are more reliable.
Code reference: R/wtp.R
9. Consumer Surplus and the Logsum
9.1 The Logsum (Expected Maximum Utility)
Under Type I Extreme Value errors, the expected maximum utility over individual ’s choice set is, up to an additive constant (Euler’s constant ):
When the model includes an outside option with normalized utility , the sum includes its term. The implementation uses the same max-subtraction trick as the log-likelihood (Section 2.3).
9.2 Expected Consumer Surplus
With utility linear in price (no income effects), the marginal utility of income is (positive for a negative price coefficient), and expected consumer surplus in money units is (Train 2009, Ch. 3):
Identification caveat. Like utility itself, the logsum is only defined up to an additive normalization (in particular the ASC normalization), so CS levels are not interpretable on their own. The economically meaningful quantity is the difference between scenarios,
computed by evaluating the logsum on counterfactual data
(newdata) and on the baseline. The normalization constant
cancels in the difference. The routine does not impose
;
interpreting the denominator as positive marginal utility of income
requires the analyst to verify the fitted price sign.
9.3 Delta-Method SE for the Mean Consumer Surplus
For the weighted mean , the implementation reports a delta-method standard error. The building block is the derivative of the logsum, which by the envelope-style identity is a probability-weighted average of utility derivatives:
with , , and a zero contribution from the outside option row (). Then:
- For non-price parameters: .
- For the price coefficient, which enters both the logsum and the factor:
The SE is , where is the weighted average of the per-individual gradient vectors and the full coefficient covariance. All ingredients (, , ) come from a single prediction pass.
Code reference: R/surplus.R
10. Goodness of Fit
10.1 McFadden Pseudo R-Squared
where is the number of estimated parameters and is a null-model log-likelihood. Two nulls are available:
Equal shares (default). Every alternative in individual ’s choice set is equally likely:
where is the number of inside alternatives. This closed form is exact for unbalanced choice sets and arbitrary weights.
Market shares. The maximized log-likelihood of a constants-only (ASC-only) model. When every alternative is available in every choice situation, the ASC-only model’s fitted probabilities equal the observed market shares , giving the closed form
with the unweighted choice counts and the common observation weight (usually after normalization). The outside option enters as its own category when present; never-chosen alternatives contribute . This closed form requires identical choice-set composition across individuals — equal set sizes are not sufficient — and uniform weights; otherwise an ASC-only model must be refit explicitly.
10.2 Hit Rate
The hit rate is the weighted share of choice situations in which the observed choice has the highest predicted probability:
With an outside option, the outside good competes for the maximum
with probability
,
and a predicted outside choice is a hit when the outside good was in
fact chosen
().
Ties among inside alternatives are resolved by the first local row
(which.max in R). The outside option replaces that inside
winner only when its probability is strictly larger, not when tied.
Code reference: R/gof.R
11. Choice-Based Sampling and WESML Weighting
11.1 Endogenous Stratified (Choice-Based) Sampling
Under random (exogenous) sampling each choice situation is drawn independently of its outcome. Under choice-based (endogenous stratified) sampling the strata are defined by the chosen alternative , and situations are drawn with stratum-specific frequencies — for example, deliberately oversampling individuals who chose a rare alternative. Let be the population share of alternative and the sample share of choosers of .
For the multinomial logit there is a well-known special case: with a full set of alternative-specific constants, choice-based sampling biases only the constants, and the slope coefficients remain consistent (Manski & McFadden 1981). Outside that case — most importantly when the ASCs are not saturated, or when the constants themselves are of interest (welfare, counterfactual entry) — naive (unweighted) maximum likelihood is inconsistent for the population parameters, and the weighting below is required.
11.2 The WESML Weighted Log-Likelihood
Manski and Lerman (1977) restore consistency by weighting each situation’s contribution by the ratio of population to sample share of its chosen alternative,
The maximizer is invariant to multiplying every by a common positive constant, so the weights may be normalized to mean 1 without changing the estimates.
11.3 Robust (Sandwich) Variance: the Meat
WESML is a weighted M-estimator solving , where is the per-situation score ($H_i = ^2 P_{i j_i}/,^$ its Hessian ($$4). Its robust (Huber–White) asymptotic variance is the sandwich
The weight enters the bread linearly, but the meat carries the weight squared: the contribution of situation to the estimating equation is , so the variance of that contribution involves .
This is exactly why the two naive variances are wrong under weighting:
- the inverse-Hessian assumes the information-matrix equality , which fails once the are non-degenerate;
- the ordinary BHHH/OPG uses the weight to the first power, not the second.
A consistency check: rescaling all weights by a constant sends and , so — the sandwich is invariant to the weight scale, whereas is not.
11.4 Scope: Robust vs. Design-Based Variance
The estimator implemented here is the robust weighted-M-estimator (Huber–White) variance, whose meat is uncentered. This is the asymptotic variance under variable-probability sampling (each population unit sampled independently with a stratum-dependent probability). Under a fixed-quota design the design-based variance centers the scores within strata and may be smaller; that stratum-centered estimator is out of scope here. See Manski & McFadden (1981) for the design-based treatment and Cosslett (1981) for the asymptotically efficient conditional-maximum-likelihood alternative.
11.5 Implementation and Cluster-Robust Extension
The common post-estimation path first obtains the
matrix
whose row
is the weight-free score
from mnl_scores_parallel. It then forms
The eager fit-time path is algebraically identical and may obtain
by calling the BHHH accumulator with weights = w^2. Both
routes combine the matrices as
.
For cluster labels
,
se_method = "cluster" replaces the meat by
No
or other finite-cluster correction is applied. Clustering changes the
estimated covariance, not the likelihood or point estimate. User-facing
entry points: wesml_weights() computes the Manski–Lerman
weights from the population shares
,
while sample_by_choice() draws a choice-based sample and
attaches those weights;
run_mnlogit(..., se_method = "sandwich") estimates with the
robust variance; wesml_vcov() returns it post hoc. Passing
run_mnlogit(..., weights_col = ) collapses a row-level
weight column to one weight per choice situation (validated constant
within id), and a provenance guard prevents a WESML-labeled
sample from being silently fit unweighted.
Code reference: mnl_scores_parallel() /
mnl_bhhh_parallel() in src/mnlogit.cpp;
compute_sandwich_vcov(), .score_meat(), and
.assemble_score_vcov() in R/classes.R;
wesml_weights() / sample_by_choice() in R/sampling.R.
12. Implementation Details
12.1 Parameter Vector Structure
The full parameter vector is organized as:
| Block | Indices | Length | Description |
|---|---|---|---|
| Coefficients for design matrix | |||
| , , or | Free alternative-specific constants |
where
when use_asc = FALSE,
without outside option (first ASC normalized to 0), or
with outside option.
Code reference: src/mnlogit.cpp
12.2 Data Organization
- Design matrix : Stacked matrix of dimension , where is the number of (inside) alternatives for individual
- Alternative indices: 1-based indexing in R, converted to 0-based in C++
- Choice indices: 1-based local row positions within each situation’s block for inside alternatives, 0 for the outside option when included
- Prefix sums : Used for efficient indexing into the stacked data:
12.3 OpenMP Parallelization
The implementation parallelizes over individuals using OpenMP: - Each
thread maintains local accumulators for log-likelihood, gradient, and
Hessian - Thread results are combined using
#pragma omp critical sections - Dynamic scheduling is used
for load balancing: #pragma omp for schedule(dynamic)
Code reference: src/mnlogit.cpp
12.4 Negated Objectives
The likelihood/gradient kernel and analytical-Hessian kernel return, respectively: - (negated log-likelihood) - (negated gradient) - (negated Hessian)
This is for compatibility with minimization routines (e.g.,
nloptr) that expect a loss function to minimize rather than
a likelihood to maximize.
12.5 Finite-Value Guards
If a situation’s chosen log probability is non-finite, the likelihood kernel substitutes for that contribution and emits one warning after leaving the OpenMP region. If the assembled negated objective is still non-finite, it returns and a zero gradient; isolated non-finite gradient entries are set to zero. These sentinels let a line search backtrack from pathological trial parameters and are not part of the regular finite-utility likelihood.
Code reference: src/mnlogit.cpp
References
- McFadden, D. (1974). Conditional logit analysis of qualitative choice behavior. In P. Zarembka (Ed.), Frontiers in Econometrics (pp. 105-142). Academic Press.
- Train, K. E. (2009). Discrete Choice Methods with Simulation (2nd ed.). Cambridge University Press.
- Berry, S., Levinsohn, J., & Pakes, A. (1995). Automobile prices in market equilibrium. Econometrica, 63(4), 841-890.
- Manski, C. F., & Lerman, S. R. (1977). The estimation of choice probabilities from choice based samples. Econometrica, 45(8), 1977-1988.
- Manski, C. F., & McFadden, D. (1981). Alternative estimators and sample designs for discrete choice analysis. In C. F. Manski & D. McFadden (Eds.), Structural Analysis of Discrete Data with Econometric Applications (pp. 2-50). MIT Press.
- Cosslett, S. R. (1981). Maximum likelihood estimator for choice-based samples. Econometrica, 49(5), 1289-1316.