Estimation
For model estimation, this package implements estimators as subtypes of an abstract Estimator type. The Models section shows for each model which estimator is available. Currently, only simulated maximum likelihood is available, but I hope to implement more estimators (e.g., indirect inference) in the future.
Simulated Maximum Likelihood
The baseline Simulated Maximum Likelihood Estimator can be created with e = SMLE(n_draws) where n_draws specifies the number of simulation draws used for numerical integration.
StructuralSearchModels.SMLE — Type
SMLE(n_draws::Int; kwargs...)
SMLE(; numerical_integration_method, kwargs...)Simulated maximum likelihood estimator. The number of simulation draws is specified either via positional argument SMLE(n_draws), which creates a DefaultNI(n_draws) integration method, or explicitly via SMLE(; numerical_integration_method = DefaultNI(100)).
Arguments
n_draws::Int: (Positional constructor only) Number of simulation draws; createsDefaultNI(n_draws).
Keyword Arguments
numerical_integration_method::NIMethod: Numerical integration method for the main integration over shocks. Required if not using the positional constructor.options_optimization::NamedTuple: Options passed toOptimizationFunctionin Optimization.jl. Uses LBFGS by default.options_problem::NamedTuple: Options passed toOptimizationProblemin Optimization.jl.options_solver::NamedTuple: Options passed tosolvein Optimization.jl. Defaults to 100,000 iterations.numerical_integration_method_heterogeneity::NIMethod: Integration method for unobserved heterogeneity. Defaults toQMC(n_draws = 30).conditional_on_search::Bool: Whether the likelihood is conditional on at least one search. Defaults tofalse.parameter_rescaling::Union{Nothing, AbstractVector}: Optional scaling vector; the optimizer works in the rescaled spaceφ = θ ./ scale. Seebuild_inverse_hessian_scalerfor a helper to construct a suitable vector. Defaults tonothing.
Examples
e = SMLE(100)
e = SMLE(200; conditional_on_search = true)
e = SMLE(; numerical_integration_method = QMC(n_draws = 500))Options
Various options for the optimization and numerical integration procedures used in estimation can be specified. Moreover, the conditional_on_search option allows specifying whether the sample is selected based on there being at least one click.
Starting Values
By default, the starting values for estimation are taken from the model directly. However, it is also possible to specify different starting values by passing the startvals keyword argument to the estimate function. The starting values need to be passed as a vector of parameter values which has to have the expected order.
The following first extracts the parameter values from the model m and then passes it as a starting values into the estimate function. This will lead to the same starting values as the default.
startvals = vectorize_parameters(m)
estimate(m, SMLE(100), d; startvals)Optimization
Simulated maximum likelhood finds the parameter estimates by numerically maximizing the model likelihood function. This is implemented using the amazing Optimization.jl package, which allows to specify a range of optimizers (from other packages) and solvers. The SMLE struct is set up to pass the respective options to the optimization package as keyword arguments.
Specifically, this is how the different fields are passed into the respective constructors and functions of the Optimization.jl package:
f = OptimizationFunction(loglikelihood, options_optimization.differentiation)
p = OptimizationProblem(f, startvals; options_problem...)
r = solve(p, options_optimization.algorithm; options_solver...)Numerical Integration
Simulated maximum likelihood uses numerical integration to compute a likelihood function that has no closed-form. SMLE uses the DefaultNI, which is also the only option so far. For now, the DefaultNI uses the method introduced in Greminger (2025) to integrate over the unobserved shocks for all available models. This approach has the advantage that it does not require smoothing parameters (as KSFS) and is quite fast, but does not use the search order (as the GHK simulator does). In the future, more numerical integration methods, such as the GHK simulator that additionally uses the search order, should be implemented and made available as new types. The DefaultNI then should default to the appropriate method for each model and available data.
StructuralSearchModels.DefaultNI — Type
DefaultNI(n_draws::Int)Default numerical integration method for integrating over the idiosyncratic shocks.
Arguments
n_draws::Int: Number of simulation draws.
Estimation of Shock Variances
Options whether to estimate the variances of the unobserved shocks are directly passed as keyword arguments to the estimate function, rather than being part of the Estimator type. This is because it is not specific to any estimator and a rather general option.
By default, the variances are not estimated. To estimate them, pass the keyword argument estimation_shock_variances to the estimate function. The following are valid options:
estimation_shock_variances = [:σ_dE]: estimates the variance of the unobserved shockdEestimation_shock_variances = [:σ_dV]: estimates the variance of the unobserved shockdV.estimation_shock_variances = [:dUequaldE, :σ_dE]: estimates the variance of the unobserved shockdEand assumes that the distribution of the outside option shockdU0is equal to that ofdE.estimation_shock_variances = [:dUequaldV, :σ_dV]: estimates the variance of the unobserved shockdVand assumes that the distribution of the outside option shockdU0is equal to that ofdV.
The code currently does not check for the validity of this option and will try to estimate them as specified, even if it is not possible. For example, setting estimation_shock_variances = [:σ_dE, :σ_dV] will run the estimation and try to estimate both variances, even though they are not separately identified.
The keyword also needs to be passed on to the vectorize_parameters function so that it can appropriately extract the parameters from the model. For example, to get the vector of all estimated parameters after having an estimated model m_hat that used the estimation_shock_variances keyword, the following code can be used:
vectorize_parameters(m_hat; estimation_shock_variances)Fixing Parameters
By passing a fixed_parameters argument to the estimate function, parameters can be fixed so that they are not estimated. This is useful to evaluate identification of different parameters. For example, estimating the model as follows will fix the parameter β to the values provided in the model m:
estimate(m, SMLE(100), d; fixed_parameters = [:β])Estimation Functions
For model estimation, the following abstract functions are available. These functions should be implemented for all available (and sensible) combinations of Model, Estimator, and Data types.
StructuralSearchModels.calculate_likelihood — Function
calculate_likelihood(model::Model, estimator::MLE, data::Data; kwargs...)Calculate the log-likelihood of model given data using estimator.
Returns
A Float64 log-likelihood value.
Distributions.estimate — Function
estimate(model::Model, estimator::Estimator, data::Data; kwargs...)Estimate the model using data and estimator.
Returns
A 5-tuple (model_hat, estimates, likelihood_at_estimates, result_solver, std_errors) where:
model_hat: estimated model of the same type asmodel, with parameters set to the estimatesestimates:Vector{Float64}of parameter estimates in the order defined byvectorize_parameterslikelihood_at_estimates:Float64log-likelihood value at the estimatesresult_solver: raw solver result object from Optimization.jlstd_errors:Vector{Float64}standard errors (givenestimator), ornothingif the computation fails (e.g., Hessian not invertible) or whencompute_std_errors = false.
Example
using Distributions, StructuralSearchModels
m = SD(β = [-0.05, 3.0], Ξ = 3.5, ρ = [-0.1], ξ = 2.5,
dE = Normal(), dV = Normal(), dU0 = Uniform(), zdfun = "log")
d = generate_data(m, 1000, 1; seed = 1)
e = SMLE(200)
m_hat, estimates, likelihood_at_estimates, result_solver, std_errors = estimate(m, e, d; seed = 1)
# Compare estimates with true values and standard errors
true_params = vectorize_parameters(m)
hcat(estimates, true_params, std_errors)StructuralSearchModels.calculate_standard_errors — Function
calculate_standard_errors(model::Model, estimator::Estimator, data::Data; kwargs...)Calculate asymptotic standard errors for the estimated model using data and estimator. Standard errors are computed for the respective estimator. Pass the same seed used during estimation to ensure consistent simulation draws.
Returns
A Vector{Float64} of standard errors in the same order as vectorize_parameters. Returns nothing (with a warning) if the Hessian is not invertible.
Example
# Compute standard errors after estimation (use the same seed)
std_errors = calculate_standard_errors(m_hat, e, d; seed = 1)
hcat(vectorize_parameters(m_hat), std_errors)