Skip to content

Convergence, bootstrap, p-values#316

Open
Maximilian-Stefan-Ernst wants to merge 15 commits intodevelfrom
add_pvalue
Open

Convergence, bootstrap, p-values#316
Maximilian-Stefan-Ernst wants to merge 15 commits intodevelfrom
add_pvalue

Conversation

@Maximilian-Stefan-Ernst
Copy link
Collaborator

  • convergence assessment
    • adds the method converged for SemFit objects
    • now, converged returns whether the model is converged in general
    • convergence returns the convergence flags and can be used to inspect why the model converged (or not)
  • boostrap refactoring
    • bug fix: each boostrap sample is now checked for convergence
    • bootstrap can now use multiple threads by setting parallel = true
    • there is now an additional generic bootstrap function that can be used to boostrap any statistic that can be extracted from a fitted model
  • convenience functions
    • z_test to compute p-values for individual parameters
    • normal_CI to compute a normal theory confidence interval

@Maximilian-Stefan-Ernst Maximilian-Stefan-Ernst changed the title Add pvalue Convergence, boostrap, p-values Mar 9, 2026
@Maximilian-Stefan-Ernst
Copy link
Collaborator Author

@alyst this is a relatively big PR - would you like to review it before I merge?

Maximilian-Stefan-Ernst and others added 2 commits March 9, 2026 23:20
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
@alyst
Copy link
Contributor

alyst commented Mar 9, 2026

@Maximilian-Stefan-Ernst Please proceed as you planned, I think these are nice changes. As I have mentioned before, I am finishing preparing the PR that potentially enhances the support for multi-group and "multi-regularization" SEMs. Because of those changes, I also have to update replace_observed() and bootstrap_se(), which relies on it. I will rebase that WIP PR on the new devel branch once you merge this one, so any suggestions to this PR could be a part of my upcoming PR. :)

@Maximilian-Stefan-Ernst Maximilian-Stefan-Ernst changed the title Convergence, boostrap, p-values Convergence, bootstrap, p-values Mar 9, 2026
observed = observed,
gradient_required = !isnothing(implied.∇A),
meanstructure = MeanStruct(implied) == HasMeanStruct,
kwargs...)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
kwargs...)
kwargs...,
)

Comment on lines +217 to +223
observed = observed,
vech = implied.Σ isa Vector,
gradient = !isnothing(implied.∇Σ),
hessian = !isnothing(implied.∇²Σ),
meanstructure = MeanStruct(implied) == HasMeanStruct,
approximate_hessian = isnothing(implied.∇²Σ),
kwargs...)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
observed = observed,
vech = implied.Σ isa Vector,
gradient = !isnothing(implied.∇Σ),
hessian = !isnothing(implied.∇²Σ),
meanstructure = MeanStruct(implied) == HasMeanStruct,
approximate_hessian = isnothing(implied.∇²Σ),
kwargs...)
observed = observed,
vech = implied.Σ isa Vector,
gradient = !isnothing(implied.∇Σ),
hessian = !isnothing(implied.∇²Σ),
meanstructure = MeanStruct(implied) == HasMeanStruct,
approximate_hessian = isnothing(implied.∇²Σ),
kwargs...,
)

Comment on lines +241 to +243
observed = observed,
approximate_hessian = HessianEval(lossfun) == ApproxHessian,
kwargs...)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
observed = observed,
approximate_hessian = HessianEval(lossfun) == ApproxHessian,
kwargs...)
observed = observed,
approximate_hessian = HessianEval(lossfun) == ApproxHessian,
kwargs...,
)

update_observed(lossfun::SemWLS, observed::SemObserved; kwargs...) = SemWLS(;
observed = observed,
meanstructure = MeanStruct(kwargs[:implied]) == HasMeanStruct,
kwargs...)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
kwargs...)
kwargs...,
)

Comment on lines +140 to +146
model_fit,
spec;
compare_hessian = true,
rtol_hessian = 0.2,
compare_bs = true,
rtol_bs = 0.1,
n_boot = 500)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
model_fit,
spec;
compare_hessian = true,
rtol_hessian = 0.2,
compare_bs = true,
rtol_bs = 0.1,
n_boot = 500)
model_fit,
spec;
compare_hessian = true,
rtol_hessian = 0.2,
compare_bs = true,
rtol_bs = 0.1,
n_boot = 500,
)

if compare_bs
bs_samples = bootstrap(model_fit, spec; n_boot = n_boot)
@test bs_samples[:n_converged] > 0.95*n_boot
bs_samples = cat(bs_samples[:samples][BitVector(bs_samples[:converged])]..., dims = 2)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
bs_samples = cat(bs_samples[:samples][BitVector(bs_samples[:converged])]..., dims = 2)
bs_samples =
cat(bs_samples[:samples][BitVector(bs_samples[:converged])]..., dims = 2)

)

model_ls_multigroup = SemEnsemble(model_ls_g1, model_ls_g2; optimizer = semoptimizer)
model_ls_multigroup = SemEnsemble(model_ls_g1, model_ls_g2; groups = [:Pasteur, :Grant_White], optimizer = semoptimizer)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change
model_ls_multigroup = SemEnsemble(model_ls_g1, model_ls_g2; groups = [:Pasteur, :Grant_White], optimizer = semoptimizer)
model_ls_multigroup = SemEnsemble(
model_ls_g1,
model_ls_g2;
groups = [:Pasteur, :Grant_White],
optimizer = semoptimizer,
)

lav_groups = Dict(:Pasteur => 1, :Grant_White => 2),
)


Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[JuliaFormatter] reported by reviewdog 🐶

Suggested change

)
```
"""
function se_bootstrap(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be possible to make se_bootstrap() a wrapper around bootstrap()

Comment on lines -80 to -82
lock(lk) do
n_failed[] += 1
end
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid locking, you can also use Threads.@atomic n_failed[] += 1, where Threads.atomic_add!(n_failed, 1) (see atomic operations)

put!(model_pool, thread_model)
end
end
return Dict(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's better to use NamedTuple here.
Also, while it might be fragile to use Base.return_types() to infer the result of statistic() before it is called, here it should be in principle possible to use T = mapreduce(typeof, promote_type, out) and Vector{T}(out) to help with the type inference and faster code generation downstream.

data = prepare_data_bootstrap(data, fitted.model)
start = solution(fitted)
# pre-allocations
out = []
Copy link
Contributor

@alyst alyst Mar 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can also preallocate out = Vector{Any}(nothing, n_boot) and conv = fill(false, n_boot) (or conv = Vector{Union{Missing, Bool}}(missing, n_boot)), then you can avoid locks and just set the results directly out[i] = ....
The failed iterations should have out[i] === nothing, you can filter them out when generating the result.
If you are using conv with true/false/missing logic, you also don't have to count failed iterations as they will have conv[i] === missing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants