Custom imply types
We recommend to first read the part Custom loss functions, as the overall implementation is the same and we will describe it here more briefly.
Imply types are of subtype SemImply
. To implement your own imply type, you should define a struct
struct MyImply <: SemImply
...
end
and at least a method to compute the objective
import StructuralEquationModels: objective!
function objective!(imply::MyImply, par, model::AbstractSemSingle)
...
return nothing
end
This method should compute and store things you want to make available to the loss functions, and returns nothing
. For example, as we have seen in Second example - maximum likelihood, the RAM
imply type computes the model-implied covariance matrix and makes it available via Σ(imply)
. To make stored computations available to loss functions, simply write a function - for example, for the RAM
imply type we defined
Σ(imply::RAM) = imply.Σ
Additionally, you can specify methods for gradient
and hessian
as well as the combinations described in Custom loss functions.
The last thing nedded to make it work is a method for n_par
that takes your imply type and returns the number of parameters of the model:
n_par(imply::MyImply) = ...
Just as described in Custom loss functions, you may define a constructor. Typically, this will depend on the specification = ...
argument that can be a ParameterTable
or a RAMMatrices
object.
We implement an ImplyEmpty
type in our package that does nothing but serving as an imply field in case you are using a loss function that does not need any imply type at all. You may use it as a template for defining your own imply type, as it also shows how to handle the specification objects:
############################################################################
### Types
############################################################################
struct ImplyEmpty{V, V2} <: SemImply
identifier::V2
n_par::V
end
############################################################################
### Constructors
############################################################################
function ImplyEmpty(;
specification,
kwargs...)
ram_matrices = RAMMatrices(specification)
identifier = StructuralEquationModels.identifier(ram_matrices)
n_par = length(ram_matrices.parameters)
return ImplyEmpty(identifier, n_par)
end
############################################################################
### methods
############################################################################
objective!(imply::ImplyEmpty, par, model) = nothing
gradient!(imply::ImplyEmpty, par, model) = nothing
hessian!(imply::ImplyEmpty, par, model) = nothing
############################################################################
### Recommended methods
############################################################################
identifier(imply::ImplyEmpty) = imply.identifier
n_par(imply::ImplyEmpty) = imply.n_par
update_observed(imply::ImplyEmpty, observed::SemObserved; kwargs...) = imply
As you see, similar to Custom loss functions we implement a method for update_observed
. Additionally, you should store the identifier
from the specification object and write a method for identifier
, as this will make it possible to access parameter indices by label.