15.1.250. tablet_qt/maths/linkfunctionfamily.h

/*
    Copyright (C) 2012, University of Cambridge, Department of Psychiatry.
    Created by Rudolf Cardinal (rnc1001@cam.ac.uk).

    This file is part of CamCOPS.

    CamCOPS is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    CamCOPS is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with CamCOPS. If not, see <https://www.gnu.org/licenses/>.
*/

#pragma once

// #define LINK_FUNCTION_FAMILY_USE_AIC

#include <functional>
#include <QString>
#include "maths/include_eigen_dense.h"  // IWYU pragma: keep


// Represents a link function family for a generalized linear model.

class LinkFunctionFamily
{
public:
    using LinkFnType = std::function<Eigen::ArrayXXd(const Eigen::ArrayXXd&)>;
    using InvLinkFnType = std::function<Eigen::ArrayXXd(const Eigen::ArrayXXd&)>;
    using DerivativeInvLinkFnType = std::function<Eigen::ArrayXXd(const Eigen::ArrayXXd&)>;
    using VarianceFnType = std::function<Eigen::ArrayXXd(const Eigen::ArrayXXd&)>;
    using DevResidsFnType = std::function<Eigen::ArrayXd(
            const Eigen::ArrayXd&,  // y
            const Eigen::ArrayXd&,  // mu
            const Eigen::ArrayXd&  // wt
        )>;
    using ValidEtaFnType = std::function<bool(const Eigen::ArrayXd&)>;
    using ValidMuFnType = std::function<bool(const Eigen::ArrayXd&)>;
    using InitializeFnType = std::function<bool(  // returns: OK?
        QStringList&,  // errors
        const LinkFunctionFamily&,  // family
        Eigen::ArrayXd&,  // y
        Eigen::ArrayXd&,  // n
        Eigen::ArrayXd&,  // m
        Eigen::ArrayXd&,  // weights
        Eigen::ArrayXd&,  // start
        Eigen::ArrayXd&,  // etastart
        Eigen::ArrayXd&   // mustart
        )>;
#ifdef LINK_FUNCTION_FAMILY_USE_AIC
    using AICFnType = std::function<double(
            const Eigen::ArrayXd&,  // y, definitely vector (from glm.fit)
            const Eigen::ArrayXd&,  // n (NO IDEA from R glm.fit)
            const Eigen::ArrayXd&,  // mu, definitely vector (from glm.fit)
            const Eigen::ArrayXd&,  // wt, definitely vector (from glm.fit)
            double  // dev, definitely scalar (from glm.fit)
        )>;
#endif

    LinkFunctionFamily(
            const QString& family_name,
            const LinkFnType& link_fn,
            const InvLinkFnType& inv_link_fn,
            const DerivativeInvLinkFnType& derivative_inv_link_fn,
            const VarianceFnType& variance_fn,
            const DevResidsFnType& dev_resids_fn,
            const ValidEtaFnType& valid_eta_fn,
            const ValidMuFnType& valid_mu_fn,
            const InitializeFnType& initialize_fn
#ifdef LINK_FUNCTION_FAMILY_USE_AIC
            , const AICFnType& aic_fn
#endif
            );
public:
    // For nasty hacks, like R does ;)
    QString family_name;  // "family" in R

    // Link function, giving eta = link(mu):
    LinkFnType link_fn;

    // Inverse link function, giving mu = invlink(eta):
    InvLinkFnType inv_link_fn;

    // Derivative of the inverse link function ("mu.eta" in R).
    // This gives d(mu)/d(eta) as a function of eta.
    DerivativeInvLinkFnType derivative_inv_link_fn;

    // Variance function: gives the variance as a function of the mean; "the
    // part of the variance that depends on" the mean; V(mu).
    // https://en.wikipedia.org/wiki/Variance_function
    // If the variance is independent of the mean, then this should return a
    // constant, probably 1.
    VarianceFnType variance_fn;

    // As per "dev.resids" in "?family" in R:
    //      Function giving the deviance for each observation as a
    //      function of ‘(y, mu, wt)’, used by the ‘residuals’ method
    //      when computing deviance residuals.
    // For example, the unit deviance for the normal distribution is given by
    //      d(y, mu) = (y - mu)^2
    // ... https://en.wikipedia.org/wiki/Deviance_(statistics)
    // and so that is what statsfunc::gaussianDevResids() provides, as one of
    // the functions that might be used here.
    DevResidsFnType dev_resids_fn;

    // Validate the linear predictors
    ValidEtaFnType valid_eta_fn;

    // Validate the means
    ValidMuFnType valid_mu_fn;

    // GLM initialization (ugly eval() code in R)
    InitializeFnType initialize_fn;

#ifdef LINK_FUNCTION_FAMILY_USE_AIC
    // AIC (Aikake information criterion) calculation ("aic" in R)
    AICFnType aic_fn;
#endif
};


extern const QString LINK_FAMILY_NAME_GAUSSIAN;
extern const QString LINK_FAMILY_NAME_BINOMIAL;
extern const QString LINK_FAMILY_NAME_POISSON;

// Some concrete link function families:

extern const LinkFunctionFamily LINK_FN_FAMILY_GAUSSIAN;  // default for glm() in R
extern const LinkFunctionFamily LINK_FN_FAMILY_LOGIT;
extern const LinkFunctionFamily LINK_FN_FAMILY_POISSON;