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