Build by parts
You can always build a model by parts - that is, you construct the observed, implied, loss and optimizer part seperately.
As an example on how this works, we will build A first model in parts.
First, we specify the model just as usual:
using StructuralEquationModels
data = example_data("political_democracy")
obs_vars = [:x1, :x2, :x3, :y1, :y2, :y3, :y4, :y5, :y6, :y7, :y8]
lat_vars = [:ind60, :dem60, :dem65]
graph = @StenoGraph begin
# loadings
ind60 → fixed(1)*x1 + x2 + x3
dem60 → fixed(1)*y1 + y2 + y3 + y4
dem65 → fixed(1)*y5 + y6 + y7 + y8
# latent regressions
ind60 → dem60
dem60 → dem65
ind60 → dem65
# variances
_(obs_vars) ↔ _(obs_vars)
_(lat_vars) ↔ _(lat_vars)
# covariances
y1 ↔ y5
y2 ↔ y4 + y6
y3 ↔ y7
y8 ↔ y4 + y6
end
partable = ParameterTable(
graph,
latent_vars = lat_vars,
observed_vars = obs_vars)
-------- ---------- -------- ------- ------------- --------- ---------- -------
from relation to free value_fixed start estimate par ⋯
Symbol Symbol Symbol Bool Float64 Float64 Float64 Symb ⋯
-------- ---------- -------- ------- ------------- --------- ---------- -------
ind60 → x1 false 1.0 con ⋯
ind60 → x2 true θ ⋯
ind60 → x3 true θ ⋯
dem60 → y1 false 1.0 con ⋯
dem60 → y2 true θ ⋯
dem60 → y3 true θ ⋯
dem60 → y4 true θ ⋯
dem65 → y5 false 1.0 con ⋯
dem65 → y6 true θ ⋯
dem65 → y7 true θ ⋯
dem65 → y8 true θ ⋯
ind60 → dem60 true θ ⋯
dem60 → dem65 true θ_ ⋯
ind60 → dem65 true θ_ ⋯
x1 ↔ x1 true θ_ ⋯
⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋮ ⋱
-------- ---------- -------- ------- ------------- --------- ---------- -------
1 column and 19 rows omitted
Latent Variables: [:ind60, :dem60, :dem65]
Observed Variables: [:x1, :x2, :x3, :y1, :y2, :y3, :y4, :y5, :y6, :y7, :y8]
Now, we construct the different parts:
# observed ---------------------------------------------------------------------------------
observed = SemObservedData(specification = partable, data = data)
# implied ------------------------------------------------------------------------------------
implied_ram = RAM(specification = partable)
# loss -------------------------------------------------------------------------------------
ml = SemML(observed = observed)
loss_ml = SemLoss(ml)
# optimizer -------------------------------------------------------------------------------------
optimizer = SemOptimizerOptim()
# model ------------------------------------------------------------------------------------
model_ml = Sem(observed, implied_ram, loss_ml)
sem_fit(optimizer, model_ml)
Fitted Structural Equation Model
===============================================
--------------------- Model -------------------
Structural Equation Model
- Loss Functions
SemML
- Fields
observed: SemObservedData
implied: RAM
------------- Optimization result -------------
* Status: success
* Candidate solution
Final objective value: 2.120543e+01
* Found with
Algorithm: L-BFGS
* Convergence measures
|x - x'| = 6.67e-05 ≰ 1.5e-08
|x - x'|/|x'| = 8.93e-06 ≰ 0.0e+00
|f(x) - f(x')| = 1.14e-09 ≰ 0.0e+00
|f(x) - f(x')|/|f(x')| = 5.39e-11 ≤ 1.0e-10
|g(x)| = 1.36e-04 ≰ 1.0e-08
* Work counters
Seconds run: 0 (vs limit Inf)
Iterations: 127
f(x) calls: 387
∇f(x) calls: 387