Models
At the core of the StructuralSearchModels.jl are different search models, implemented as subtypes of the abstract SearchModel <: Model type. Currently, the following two main search models are implemented:
- Search and Discovery Model
- Weitzman Model
Because the Weitzman model is a special case of the Search and Discovery model, specifications for both are implemented as subtypes of the general SDModel type.
Search and Discovery Model
The empirical Search and Discovery model is introduced in Greminger (2025), building on the theory introduced in Greminger (2022).
The model requires specifying ξ and Ξ, rather than the costs cs and cd. These costs can be computed and added to the model using calculate_costs!(model, data).
By default, the model is specified so that all observable product characteristics are revealed on the list, and only a shock $\varepsilon_{ij}$ is revealed on search. Moreover, note that the last element in $\beta$ is for the outside option dummy. The specification for which product characteristics are revealed where can be set through the information_structure, as described below.
The functional form for how the discovery value $z^d(h)$ depends on position $h$ is specified through the string zdfun. This is done so that the model struct can be readily saved to disk (e.g., using the JLD2 package) without having to save a function definition. Currently, the following functions are available:
"linear": $z^d(h) = \Xi + \rho h$"log": $z^d(h) = \Xi + \rho \log(1+h)$"exp": $z^d(h) = \Xi + \rho \exp(1+h)$
Note that the heterogeneity specification for now is a placeholder that will be used in future versions to implement observed and unobserved heterogeneity across parameters.
StructuralSearchModels.SD — Type
SD{T} <: SDModelSearch and Discovery model with the following parameterization:
- uᵢⱼ = xⱼβ + xⱼκ + νᵢⱼ + εᵢⱼ, εᵢⱼ ~ dE, νᵢⱼ ~ dV
- zsᵢⱼ = xⱼβ + xⱼγ + ξ + νᵢⱼ
- uᵢ₀ = β0 + ηᵢ, ηᵢ ~ dU0
- zd(h) = zdfun(Ξ, ρ, h) with ρ ≤ 0
The specification of xⱼβ, xⱼκ, and xⱼγ is determined by information_structure.
Fields
β::Vector{T}: Preference weights.Ξ::T: Baseline discovery value for position 1 (not demeaned).ρ::Union{T, Vector{T}}: Parameters governing decrease of Ξ across positions.ξ::T: Baseline search value.dE::Distribution: Distribution of εᵢⱼ.dV::Distribution: Distribution of νᵢⱼ.dU0::Distribution: Distribution of ηᵢ.zdfun::String: Functional form f(Ξ, ρ, h) for the discovery value at position h. Available options:""(constant),"linear","log","exp","linear-k","log-k"(wherekis an integer).information_structure::InformationStructureSpecification{T}: Information structure specification. SeeInformationStructureSpecification.cs::Union{Vector{Vector{T}}, Nothing}: Search costs per product, populated bycalculate_costs!.nothinguntil computed.cd::Union{Vector{T}, Nothing}: Discovery costs per session, populated bycalculate_costs!.nothinguntil computed.heterogeneity::HeterogeneitySpecification: Heterogeneity specification. Defaults to homogeneous model.
Which product characteristics are revealed where can be specified through specifying the information_structure. This specification has parameters $\gamma$ and $\kappa$ and allows specifying indices for which elements in product_characteristics[i] enter $x_j\beta$ (on list), $x_j\gamma$ (beliefs about search), and $x_j\kappa$ (revealed upon search).
Where characteristics are revealed is specified through indices_characteristics_β_individual (and the others), which implies that all sessions reveal the same characteristics in the same place. Note that $\beta$ needs to have the same length as all product_characteristics[i], with zeros for product characteristics that are only revealed upon search. The last entry in product_characteristics[i] is for the outside option.
If sessions differ in where they reveal characteristics, then this can be specified by specifying indices_characteristics_β_individual as a vector that needs to have the same length as the number of sessions in the data. The same holds for the indices for the other parameters, and indices_characteristics_β_union as the union of all characteristics revealed on the list for at least one session. The same holds for indices_characteristics_γ_individual and indices_characteristics_κ_individual.
StructuralSearchModels.InformationStructureSpecification — Type
InformationStructureSpecification{T} <: AbstractSpecificationSpecification of the information structure for the Search and Discovery model. Controls which product characteristics enter the search value (via γ) and hidden utility (via κ), and which characteristics from β are used in each component. By default all fields are empty, meaning γ and κ are unused and all characteristics enter through β.
Fields
γ::Vector{T}: Parameters for the search value componentxγ. Empty by default (no search value component).κ::Vector{T}: Parameters for the hidden utility componentxκ. Empty by default.indices_characteristics_β_union: Indices of characteristics entering throughxβin at least one session.indices_characteristics_γ_union: Indices of characteristics entering throughxγin at least one session.indices_characteristics_κ_union: Indices of characteristics entering throughxκin at least one session.indices_characteristics_β_individual: Per-session indices forxβ. Defaults toindices_characteristics_β_union.indices_characteristics_γ_individual: Per-session indices forxγ. Defaults toindices_characteristics_γ_union.indices_characteristics_κ_individual: Per-session indices forxκ. Defaults toindices_characteristics_κ_union.
Estimation
The model can be estimated using simulated maximum likelihood. By default, the approach of Greminger (2025) is used. This approach constructs a smooth likelihood function, is relatively fast to estimate, and does not use the search order. It requires the two shocks to be normally distributed (dE <: Normal and dV <: Normal).
The package also provides the following function to combine the model parameters into a vector.
StructuralSearchModels.vectorize_parameters — Function
vectorize_parameters(model::Model; kwargs...)Vectorize the parameters of the model model.
Reservation value and cost calculations
The following function is available to compute costs and reservation values in the Search and Discovery model. These can be used to compute the search costs cs and discovery costs cd from the parameters ξ and Ξ, respectively.
StructuralSearchModels.calculate_costs! — Function
calculate_costs!(model::SDModel, data::DataSD, n_draws...; force_recompute = true, kwargs...)Compute search and discovery costs for model using data and store them in-place on model. Pass a single n_draws to use the same number of draws for both costs, or two values to use different draws for search costs and discovery costs respectively. If force_recompute = false, skips recomputation when costs are already present.
Discovery costs are E[max{0, xβγdemeaned + ε - Ξ}], sampled from the characteristic distribution in data. Search costs are E[max{0, xβdemeaned + ε - ξⱼ}] (or E[1 - F(ξⱼ)] when all characteristics are revealed on the list). See Greminger (2022), equation (7) and online appendix EC.2.
Returns nothing. Call this before calculate_welfare.
Example
calculate_costs!(m_hat, d, 100_000; seed = 1)Weitzman Model
The empirical Weitzman model is a special case of the Search and Discovery model. It also requires specifying ξ rather than the search cost cs, where calculate_costs!(model, data) can be used to fill in the search costs.
Position specific search costs follow from the functional form on how $\xi(h)$ changes with position h. This functional form is specified through the string zsfun. The following options are available:
"linear": $\xi(h) = \xi + \rho h$"log": $\xi(h) = \xi + \rho \log(1+h)$"exp": $\xi(h) = \xi + \rho \exp(1+h)$
StructuralSearchModels.WM — Type
WM{T} <: WMModelWeitzman model with the following parameterization:
- uᵢⱼ = xⱼβ + xⱼκ + νᵢⱼ + εᵢⱼ, εᵢⱼ ~ dE, νᵢⱼ ~ dV
- zsᵢⱼ(h) = xⱼβ + xⱼγ + ξ(h) + νᵢⱼ
- uᵢ₀ = x₀'β + ηᵢ, ηᵢ ~ dU0
- ξ(h) = zsfun(ξ, ρ, h)
The specification of xⱼβ, xⱼκ, and xⱼγ is determined by information_structure.
Fields
β::Vector{T}: Preference weights.ξ::T: Baseline search value.ρ::Union{T, Vector{T}}: Parameters governing decrease of ξ across positions.dE::Distribution: Distribution of εᵢⱼ.dV::Distribution: Distribution of νᵢⱼ.dU0::Distribution: Distribution of ηᵢ.zsfun::String: Functional form f(ξ, ρ, h) for the search value at position h. Available options:""(constant),"linear","log","exp","linear-k","log-k"(wherekis an integer).information_structure::InformationStructureSpecification{T}: Information structure specification. SeeInformationStructureSpecification.cs::Union{Vector{Vector{T}}, Nothing}: Search costs per product, populated bycalculate_costs!.nothinguntil computed.heterogeneity::HeterogeneitySpecification: Heterogeneity specification. Defaults to homogeneous model.
As with the SD model, the information_structure determines which characteristics are revealed on the list, and which ones on the detail page.
Estimation
The model can be estimated using the simulated maximum likelihood. By default, the approach of Greminger (2025) is used. This approach constructs a smooth likelihood function, is fast to estimate, and does not use the search order. It requires the two shocks to be normally distributed (dE <: Normal and dV <: Normal).
Reservation value and cost calculations
For the Weitzman model, the same functions to compute search costs and reservation values are available as for the SD model. The difference is that it computes the position-specific search costs as a vector cs_h, rather than a discovery cost.
I don't like Greek unicode letters
To facilitate setting up the models, in particular from the Python wrapper, the package also implements the following non-unicode versions that do not use Greek unicode letters for the parameters. For this, it implements a NUModel <: Model type and the specifications below.
StructuralSearchModels.SDNU — Type
Search and Discovery model SDNU{T} <: NUModel. This is the same as SD without Greek unicode letters for the fields. The parameterization of the SD model is as follows:
- uᵢⱼ = xⱼβ + xⱼκ + νᵢⱼ + εᵢⱼ, εᵢⱼ ~ dE, νᵢⱼ ~ dV
- zsᵢⱼ = xⱼβ + xⱼγ + ξ + νᵢⱼ
- uᵢ₀ = x₀'β + ηᵢ , ηᵢ ~ dU0
- zd(h) = zdfun(Ξ, ρ, pos) with ρ ≤ 0
Fields:
beta::Vector{T}: Vector of preference weights.Xi::T: Baseline Ξ for position 1 (not demeaned).rho::Union{T, Vector{T}}: Parameter(s) governing decrease of Ξ across positions.xi::T: Baseline ξ.dE::Distribution: Distribution of εᵢⱼ.dV::Distribution: Distribution of νᵢⱼ.dU0::Distribution: Distribution of ηᵢ.zdfun::String: Select functional form f(Ξ, ρ, h) that determines the discovery value in position h.information_structure::InformationStructureSpecificationNU{T}: Specification of information structure, includinggamma,kappaand characteristics forbeta,gamma, andkappa. SeeInformationStructureSpecificationNUfor details.cs::Union{T, Nothing}=nothing: Search costs. Initialized asnothingand only used for welfare calculations. Can be added throughcalculate_costs!(m, data; kwargs...).cd::Union{T, Nothing}=nothing: Discovery costs. Initialized in the same way ascs, and is also added incalculate_costs!(m, data; kwargs...).heterogeneity::HeterogeneitySpecificationNU: Specification of heterogeneity (unobserved and observed) in the model. By default assumes homogeneous model.
StructuralSearchModels.WMNU — Type
Weitzman model WMNU{T} <: NUModel. This is the same as WM without Greek unicode letters for the fields. The parameterization of the WM model is as follows:
- uᵢⱼ = xⱼβ + xⱼκ + νᵢⱼ + εᵢⱼ, εᵢⱼ ~ dE, νᵢⱼ ~ dV
- zsᵢⱼ(h) = xⱼβ + xⱼγ + ξ(h) + νᵢⱼ
- uᵢ₀ = x₀'β + ηᵢ , ηᵢ ~ dU0
- ξ(h) = zsfun(ξ, ρ, pos)
Fields:
beta::Vector{T}: Vector of preference weights.xi::T: Baseline xi.rho::Union{T, Vector{T}}: Parameters governing decrease of ξ across positions.dE::Distribution: Distribution of εᵢⱼ.dV::Distribution: Distribution of νᵢⱼ.dU0::Distribution: Distribution of ηᵢ.zsfun::String: Select functional form f(ξ, ρ, h) that determines the search value in position h.information_structure::InformationStructureSpecificationNU{T}: Specification of information structure, includinggamma,kappaand characteristics forbeta,gamma, andkappa. SeeInformationStructureSpecificationNUfor details.cs::Union{T, Nothing}: Search costs. Initialized asnothingand only used for welfare calculations. Can be added throughcalculate_costs!(m, data; kwargs...).cs_h::Union{Vector{T}, Nothing}: Initialized in the same way ascs, and is also added incalculate_costs!(m, data; kwargs...).heterogeneity::HeterogeneitySpecificationNU: Specification of heterogeneity (unobserved and observed) in the model. By default assumes homogeneous model.
StructuralSearchModels.InformationStructureSpecificationNU — Type
InformationStructureSpecificationNU{T} <: AbstractSpecificationNon-unicode version of the specification for the information structure in the Search and Discovery model. This specification includes the parameter gamma for the search value, as well as selectors for the characteristics that enter both the search value and utility through xbeta, and the characteristics that enter only the search value through xgamma. By default, is initialized with everything as nothing, which means gamma is not used, and all characteristics are used for both xbeta.
Fields:
gamma::Vector{T}: Vector of parameters for the search value. Ifempty(default), no search value is used.kappa::Vector{T}: Vector of parameters for the utility only characteristics. Ifempty, no utility only characteristics.indices_characteristics_beta_union::Union{UnitRange{Int}, Vector{Int}}: All characteristics that enter the search value and utility throughxbetain at least one session.indices_characteristics_gamma_union::Union{UnitRange{Int}, Vector{Int}}: All characteristics that enter the search value and utility throughxgammain at least one session.indices_characteristics_kappa_union::Union{UnitRange{Int}, Vector{Int}}: All characteristics that enter the search value and utility throughxkappain at least one session.indices_characteristics_beta_individual::Union{UnitRange{Int}, Vector{Int}, Vector{UnitRange{Int}}, Vector{Vector{Int}}}. By default is the same asindices_characteristics_beta_union, but can be set to individual indices for each session.indices_characteristics_gamma_individual::Union{UnitRange{Int}, Vector{Int}, Vector{UnitRange{Int}}, Vector{Vector{Int}}}. By default is the same asindices_characteristics_gamma_union, but can be set to individual indices for each session.indices_characteristics_kappa_individual::Union{UnitRange{Int}, Vector{Int}, Vector{UnitRange{Int}}, Vector{Vector{Int}}}. By default is the same asindices_characteristics_kappa_union, but can be set to individual indices for each session.