Skip to content
Merged

Devel #218

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/StructuralEquationModels.jl
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ export AbstractSem,
Sem,
SemFiniteDiff,
SemEnsemble,
MeanStruct,
NoMeanStruct,
HasMeanStruct,
HessianEval,
ExactHessian,
ApproxHessian,
SemImply,
RAMSymbolic,
RAM,
Expand Down
10 changes: 1 addition & 9 deletions src/additional_functions/helper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,7 @@ function semvec(observed, imply, loss, optimizer)
return sem_vec
end

function get_observed(rowind, data, semobserved; args = (), kwargs = NamedTuple())
observed_vec = Vector{semobserved}(undef, length(rowind))
for i in 1:length(rowind)
observed_vec[i] = semobserved(args...; data = Matrix(data[rowind[i], :]), kwargs...)
end
return observed_vec
end

skipmissing_mean(mat::AbstractMatrix) =
skipmissing_mean(mat::AbstractMatrix) =
[mean(skipmissing(coldata)) for coldata in eachcol(mat)]

function F_one_person(imp_mean, meandiff, inverse, data, logdet)
Expand Down
68 changes: 32 additions & 36 deletions src/frontend/fit/standard_errors/hessian.jl
Original file line number Diff line number Diff line change
@@ -1,58 +1,54 @@
"""
se_hessian(semfit::SemFit; hessian = :finitediff)
se_hessian(fit::SemFit; method = :finitediff)

Return hessian based standard errors.
Return hessian-based standard errors.

# Arguments
- `hessian`: how to compute the hessian. Options are
- `method`: how to compute the hessian. Options are
- `:analytic`: (only if an analytic hessian for the model can be computed)
- `:finitediff`: for finite difference approximation
"""
function se_hessian(sem_fit::SemFit; hessian = :finitediff)
c = H_scaling(sem_fit.model)

if hessian == :analytic
par = solution(sem_fit)
H = zeros(eltype(par), length(par), length(par))
hessian!(H, sem_fit.model, sem_fit.solution)
elseif hessian == :finitediff
H = FiniteDiff.finite_difference_hessian(
Base.Fix1(objective!, sem_fit.model),
sem_fit.solution,
)
elseif hessian == :optimizer
throw(
ArgumentError(
"standard errors from the optimizer hessian are not implemented yet",
),
)
elseif hessian == :expected
throw(
ArgumentError(
"standard errors based on the expected hessian are not implemented yet",
),
function se_hessian(fit::SemFit; method = :finitediff)
c = H_scaling(fit.model)
params = solution(fit)
H = similar(params, (length(params), length(params)))

if method == :analytic
evaluate!(nothing, nothing, H, fit.model, params)
elseif method == :finitediff
FiniteDiff.finite_difference_hessian!(
H,
p -> evaluate!(zero(eltype(H)), nothing, nothing, fit.model, p),
params,
)
elseif method == :optimizer
error("Standard errors from the optimizer hessian are not implemented yet")
elseif method == :expected
error("Standard errors based on the expected hessian are not implemented yet")
else
throw(ArgumentError("I don't know how to compute `$hessian` standard-errors"))
throw(ArgumentError("Unsupported hessian calculation method :$method"))
end

invH = c * inv(H)
se = sqrt.(diag(invH))

return se
H_chol = cholesky!(Symmetric(H))
H_inv = LinearAlgebra.inv!(H_chol)
return [sqrt(c * H_inv[i]) for i in diagind(H_inv)]
end

# Addition functions -------------------------------------------------------------
H_scaling(model::AbstractSemSingle) =
H_scaling(model, model.observed, model.imply, model.optimizer, model.loss.functions...)
function H_scaling(model::AbstractSemSingle)
if length(model.loss.functions) > 1
@warn "Hessian scaling for multiple loss functions is not implemented yet"
end
return H_scaling(model.loss.functions[1], model)
end

H_scaling(model, obs, imp, optimizer, lossfun::SemML) = 2 / (nsamples(model) - 1)
H_scaling(lossfun::SemML, model::AbstractSemSingle) = 2 / (nsamples(model) - 1)

function H_scaling(model, obs, imp, optimizer, lossfun::SemWLS)
function H_scaling(lossfun::SemWLS, model::AbstractSemSingle)
@warn "Standard errors for WLS are only correct if a GLS weight matrix (the default) is used."
return 2 / (nsamples(model) - 1)
end

H_scaling(model, obs, imp, optimizer, lossfun::SemFIML) = 2 / nsamples(model)
H_scaling(lossfun::SemFIML, model::AbstractSemSingle) = 2 / nsamples(model)

H_scaling(model::SemEnsemble) = 2 / nsamples(model)
8 changes: 4 additions & 4 deletions src/frontend/specification/ParameterTable.jl
Original file line number Diff line number Diff line change
Expand Up @@ -362,22 +362,22 @@ end
update_se_hessian!(
partable::AbstractParameterTable,
fit::SemFit;
hessian = :finitediff)
method = :finitediff)

Write hessian standard errors computed for `fit` to the `:se` column of `partable`

# Arguments
- `hessian::Symbol`: how to compute the hessian, see [se_hessian](@ref) for more information.
- `method::Symbol`: how to compute the hessian, see [se_hessian](@ref) for more information.

# Examples

"""
function update_se_hessian!(
partable::AbstractParameterTable,
fit::SemFit;
hessian = :finitediff,
method = :finitediff,
)
se = se_hessian(fit; hessian = hessian)
se = se_hessian(fit; method)
return update_partable!(partable, :se, params(fit), se)
end

Expand Down
9 changes: 3 additions & 6 deletions src/frontend/specification/StenoGraphs.jl
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,9 @@ function ParameterTable(
)
end
if element isa ModifiedEdge
if any(Base.Fix2(isa, Fixed), values(element.modifiers)) & any(Base.Fix2(isa, Label), values(element.modifiers))
throw(
ArgumentError(
"It is not allowed to label fixed parameters."
)
)
if any(Base.Fix2(isa, Fixed), values(element.modifiers)) &&
any(Base.Fix2(isa, Label), values(element.modifiers))
throw(ArgumentError("It is not allowed to label fixed parameters."))
end
for modifier in values(element.modifiers)
if isnothing(group) &&
Expand Down
Loading
Loading