Overview
Berry–Levinsohn–Pakes (BLP) demand estimation models differentiated products by
combining aggregate market shares with heterogeneous consumer preferences. The
blprs crate keeps the standard BLP structure while exposing ergonomic Rust
abstractions for each component: product data validation, integration over
random coefficients, the contraction mapping that recovers mean utilities, and a
two-step GMM estimator.
This page sketches the key identities that guide the implementation. Wherever
possible we point to the corresponding blprs module so theory and code stay in
sync.
Consumers and utility
Each consumer draw $i$ who observes product $j$ in market $t$ has indirect utility
[U_{ijt} = \delta_{jt} + \mu_{ijt}(\theta_2, \nu_i) + \varepsilon_{ijt},]
where:
- $\delta_{jt}$ is the mean utility of product $j$ in market $t$.
- $\mu_{ijt}$ captures deviations from the market mean via random coefficients
with parameters $\theta_2$ (called
sigmain the library) and consumer draw $\nu_i$. - $\varepsilon_{ijt}$ follows an i.i.d. Type-I Extreme Value distribution.
In blprs, market-level primitives live in data::ProductData, and the
heterogeneous draws $\nu_i$ are packaged by integration::SimulationDraws.
Market shares
Conditional on a draw $\nu_i$, the probability that the consumer purchases product $j$ in market $t$ is
[P_{ijt}(\delta, \theta_2, \nu_i) = \frac{\exp\left(\delta_{jt} + \mu_{ijt}\right)}{1 + \sum_{k \in \mathcal{J}t} \exp\left(\delta{kt} + \mu_{ikt}\right)}.]
Aggregate shares integrate these probabilities over the distribution of draws:
[\hat{s}{jt}(\delta, \theta_2) = \int P{ijt}(\delta, \theta_2, \nu) \, dF(\nu) \approx \sum_{i=1}^I w_i P_{ijt}(\delta, \theta_2, \nu_i),]
with weights $w_i$ that sum to one. The approximation above is exactly what
demand::predict_shares computes using the draws supplied to the
BlpProblem.
Contraction mapping
Observed shares $s_{jt}$ give us equilibrium conditions that pin down mean utilities. BLP proposes iterating on
[\delta^{(m+1)}{jt} = \delta^{(m)}{jt} + \ln s_{jt} - \ln \hat{s}_{jt}(\delta^{(m)}, \theta_2).]
Under standard assumptions this fixed-point iteration converges to the unique
vector $\delta(\theta_2)$ that rationalizes the data. In blprs,
demand::solve_delta implements this contraction with configurable tolerance,
maximum iterations, damping, and minimum share guards exposed through
ContractionOptions. Convergence diagnostics are returned as a
ContractionSummary.
Linear parameters and unobserved quality
Once we obtain $\delta$, linear taste parameters $\beta$ follow from the moment condition
[E[Z_t^\top \xi_t] = 0, \qquad \xi_t = \delta_t - X_{1t} \beta,]
where $X_{1t}$ are the observed linear characteristics and $Z_t$ are instruments.
blprs solves for $\beta$ via two-stage least squares inside
estimation::compute_linear_parameters (invoked internally by
BlpProblem::estimate). The resulting structural shocks $\xi$ are
exposed to users so supply-side models or diagnostics can be layered on later.
GMM objective
The demand-side estimator minimizes the quadratic form
[Q(\theta_2) = g(\theta_2)^\top W g(\theta_2), \qquad g(\theta_2) = \frac{1}{T} \sum_{t} Z_t^\top \xi_t(\theta_2),]
with $W$ a positive-definite weighting matrix. The current implementation offers
an inverse $Z^\top Z$ weighting by default and accepts custom positive-definite
matrices through WeightingMatrix. Future releases will incorporate heteroskedasticity-robust and optimal weighting strategies.
Connecting theory to the codebase
| Theory object | Library representation |
|---|---|
| Product characteristics $(X_1, X_2)$ | ProductData::x1, ProductData::x2 |
| Market shares $s_{jt}$ | ProductData::shares |
| Draws $\nu_i$, weights $w_i$ | SimulationDraws |
| Mean utility $\delta$ | Output of solve_delta, stored in EstimationResult::delta |
| Structural error $\xi$ | EstimationResult::xi |
| GMM objective $Q$ | EstimationResult::gmm_value |
The guides page tracks upcoming additions such as supply-side
pricing, optimal instruments, and counterfactual engines.
Further reading
- Berry, Levinsohn, and Pakes (1995), Automobile Prices in Market Equilibrium.
- pyBLP documentation for a comprehensive Python treatment.
- Nevo (2000), A Practitioner’s Guide to Estimation of Random Coefficients Logit Models.