Custom model types

The abstract supertype for all models is AbstractSem, which has two subtypes, AbstractSemSingle{O, I, L, D} and AbstractSemCollection. Currently, there are 2 subtypes of AbstractSemSingle: Sem, SemFiniteDiff. All subtypes of AbstractSemSingle should have at least observed, imply, loss and optimizer fields, and share their types ({O, I, L, D}) with the parametric abstract supertype. For example, the SemFiniteDiff type is implemented as

struct SemFiniteDiff{
        O <: SemObserved, 
        I <: SemImply, 
        L <: SemLoss, 
        D <: SemOptimizer} <: AbstractSemSingle{O, I, L, D}
    observed::O
    imply::I
    loss::L
    optimizer::Dend

Additionally, we need to define a method to compute at least the objective value, and if you want to use gradient based optimizers (which you most probably will), we need also to define a method to compute the gradient. For example, the respective fallback methods for all AbstractSemSingle models are defined as

function objective!(model::AbstractSemSingle, parameters)
    objective!(imply(model), parameters, model)
    return objective!(loss(model), parameters, model)
end

function gradient!(gradient, model::AbstractSemSingle, parameters)
    fill!(gradient, zero(eltype(gradient)))
    gradient!(imply(model), parameters, model)
    gradient!(gradient, loss(model), parameters, model)
end

Note that the gradient! method takes a pre-allocated array that should be filled with the gradient values.

Additionally, we can define constructors like the one in "src/frontend/specification/Sem.jl".

It is also possible to add new subtypes for AbstractSemCollection.