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.SMLEType
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; creates DefaultNI(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 to OptimizationFunction in Optimization.jl. Uses LBFGS by default.
  • options_problem::NamedTuple: Options passed to OptimizationProblem in Optimization.jl.
  • options_solver::NamedTuple: Options passed to solve in Optimization.jl. Defaults to 100,000 iterations.
  • numerical_integration_method_heterogeneity::NIMethod: Integration method for unobserved heterogeneity. Defaults to QMC(n_draws = 30).
  • conditional_on_search::Bool: Whether the likelihood is conditional on at least one search. Defaults to false.
  • parameter_rescaling::Union{Nothing, AbstractVector}: Optional scaling vector; the optimizer works in the rescaled space φ = θ ./ scale. See build_inverse_hessian_scaler for a helper to construct a suitable vector. Defaults to nothing.

Examples

e = SMLE(100)
e = SMLE(200; conditional_on_search = true)
e = SMLE(; numerical_integration_method = QMC(n_draws = 500))
source

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.DefaultNIType
DefaultNI(n_draws::Int)

Default numerical integration method for integrating over the idiosyncratic shocks.

Arguments

  • n_draws::Int: Number of simulation draws.
source

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 shock dE
  • estimation_shock_variances = [:σ_dV]: estimates the variance of the unobserved shock dV.
  • estimation_shock_variances = [:dUequaldE, :σ_dE]: estimates the variance of the unobserved shock dE and assumes that the distribution of the outside option shock dU0 is equal to that of dE.
  • estimation_shock_variances = [:dUequaldV, :σ_dV]: estimates the variance of the unobserved shock dV and assumes that the distribution of the outside option shock dU0 is equal to that of dV.

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.

Distributions.estimateFunction
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 as model, with parameters set to the estimates
  • estimates: Vector{Float64} of parameter estimates in the order defined by vectorize_parameters
  • likelihood_at_estimates: Float64 log-likelihood value at the estimates
  • result_solver: raw solver result object from Optimization.jl
  • std_errors: Vector{Float64} standard errors (given estimator), or nothing if the computation fails (e.g., Hessian not invertible) or when compute_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)
source
StructuralSearchModels.calculate_standard_errorsFunction
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)
source