Skip to content

Conversation

alyst
Copy link
Contributor

@alyst alyst commented Nov 24, 2024

This is another set of changes from #193 and my staging branch:

  • optimizer is removed from the Sem object. Sem defines the SEM model, which is independent from how the parameters of this model are fit.
    Now the optimizer has to be specified for sem_fit() call.
    (I guess the optimizer was a part of Sem, because some optimizers allow specifying parameter constraints, which is the part of the model.
    But that constraints specification was optimizer-dependent. In future, it should be possible to implement optimizer-agnostic constraints specification.)

    That change simplifies the code a bit, since Sem.optimizer was never really used except to call sem_fit().
    Plus, it allows fitting the same model with different optimizer backends without recreating the Sem object.
  • Added support for different optimization backends via package extension mechanisms.
    Implementation details:
    • SemOptimizer{E}: engine type parameter E was added to the abstract SemOptimizer class,
      i.e. SemOptimizer{E}(args...; kwargs...) constructs the optimizer object for the E backend (e.g. E = :Optim for Optim.jl).
    • To add support for a given backend, one needs to implement:
      1. SemOptimizer{E}(...) constructor, e.g. SemOptimizer{:Optim}(...) creates SemOptimizerOptim <: SemOptimizer{:Optim} object;
      2. sem_fit(optim::O, model, ...) where O <: SemOptimizer{E} call
    • There is also a call sem_fit(model; engine, kwargs...), which first creates SemOptimizer{engine}(;kwargs...) optimizer and then calls its sem_fit()
    • Optim.jl is the default backend (E = :Optim), so it remains a hard dependency of SEM.jl
    • NLopt.jl support (E = :NLopt) is moved into the SEMNLOptExt extension (it gets automatically activated once the NLopt package is loaded into an activate session)
    • Converted the ProximalSEM.jl code into the SEMProximalOptExt extension (E = :Proximal) to enable the ProximalAlgorithms.jl backend support.
  • Added support for specifying lower and upper bounds of model parameters when calling sem_fit() for the Optim.jl backend.

Copy link

codecov bot commented Nov 24, 2024

Codecov Report

Attention: Patch coverage is 55.76923% with 69 lines in your changes missing coverage. Please review.

Project coverage is 73.28%. Comparing base (8c26c35) to head (c5b48c7).
Report is 19 commits behind head on devel.

Files with missing lines Patch % Lines
src/optimizer/abstract.jl 23.52% 26 Missing ⚠️
ext/SEMNLOptExt/NLopt.jl 65.30% 17 Missing ⚠️
ext/SEMProximalOptExt/ProximalAlgorithms.jl 50.00% 12 Missing ⚠️
src/optimizer/optim.jl 50.00% 7 Missing ⚠️
src/types.jl 60.00% 4 Missing ⚠️
src/frontend/specification/Sem.jl 81.81% 2 Missing ⚠️
src/optimizer/Empty.jl 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            devel     #228      +/-   ##
==========================================
- Coverage   74.14%   73.28%   -0.87%     
==========================================
  Files          52       49       -3     
  Lines        2197     2231      +34     
==========================================
+ Hits         1629     1635       +6     
- Misses        568      596      +28     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@alyst
Copy link
Contributor Author

alyst commented Nov 25, 2024

@Maximilian-Stefan-Ernst Also, I haven't touched the sources layout, but you may consider merging the optimizer parts in diff folder (SemOptimizer-derived type) into the optimizer folder (sem_fit() implementation for that type).
If you think that's a good idea, I can merge the folders in this PR.

@Maximilian-Stefan-Ernst
Copy link
Collaborator

I think that's definitely a good idea, the "diff"-folder is a misnomer anyways^^

@alyst
Copy link
Contributor Author

alyst commented Dec 11, 2024

@Maximilian-Stefan-Ernst I've moved the code from the diff folder into optimizer. The PR should be ready review.

@Maximilian-Stefan-Ernst
Copy link
Collaborator

I finally got to the review, thanks for the great changes (again)! I've made some minor comments, I think the only thing that would be left before merging would be to move the tests from the ProximalSEM.jl package to the tests of this package.

@Maximilian-Stefan-Ernst
Copy link
Collaborator

I moved the tests from ProximalSEM.

@alyst
Copy link
Contributor Author

alyst commented Dec 19, 2024

@Maximilian-Stefan-Ernst Thank you for a thorough review and the fixes! I will address the remaining ones in 1-2 days.

BTW, have you tried/considered using Manopt.jl for SEM optimization?
With Manifolds.jl it should be possible to construct a combined manifold that restricts S to SPD.
Anyway, with this PR it should be easier to try.

@alyst
Copy link
Contributor Author

alyst commented Dec 20, 2024

@Maximilian-Stefan-Ernst I think I have addressed all of your comments, thanks again for the review!
Let me know if there are still things to tweak. If you think it's ready, also let me know -- I will squash the fixup commits to cleanup the history before merging it.

Alexey Stukalov and others added 14 commits December 20, 2024 14:49
to consolidate ENV variable check
* convert to argument type-dependent dispatch
* replace start_val() function with
  prepare_start_params()
* refactor start_parameter_table() into
  prepare_start_params(start_val::ParameterTable, ...)
  and use the SEM model param indices
* unify processing of starting values by all optimizers
* support dictionaries of values
Co-authored-by: Maximilian Ernst <[email protected]>
NLopt minimum was 18.11, below what the test expected
@alyst
Copy link
Contributor Author

alyst commented Dec 21, 2024

I've cleaned up the commit history, so it should be ok to merge if no other changes are required.

@Maximilian-Stefan-Ernst Maximilian-Stefan-Ernst merged commit b839635 into StructuralEquationModels:devel Dec 22, 2024
2 of 5 checks passed
@Maximilian-Stefan-Ernst
Copy link
Collaborator

@alyst: W.r.t. Manopt.jl: The problem why I did not manage to implement the SPD constraint with ProximalAlgorithms.jl is because we have this situation where the model-implied covariance matrix is a function of the parameters, so we have parameters -> cov matrix -> loss, and we have to constrain in the middle instead of the parameter space. I am not sure how we could alleviate this with Manopt - I believe we would have to figure out the manifold of the parameter space?

@alyst alyst deleted the optim_extensions branch February 2, 2025 20:38
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