Nothing
## Dynamically generate all the restriction functions of
## OU and Brownian motions at package-preperation time.
pkg_env = environment()
restriction_names = list(
's' = 'sym',
'l' = 'logspd',
'c' = 'spd',
'd' = 'diag',
'e' = 'logdiag',
'k' = '+++PLACEHOLDER+++',
'0' = 'zero',
'f' = 'fixed'
)
avail_restrict_cmds = list(
M=c('s','l','c','d','e','k','0','f'),
V=c('0','k','f'),
L=c('k','d','f')
)
special_cases = list(brn=list(M='0',V='0'))
## a: k^2 term
## b: k term
## c: k*(k+1)/2 term
## d: 1 term
npar2k = function(y,a,b,c,d) {
if (2L*a+c!=0)
-(2L*b+c-as.integer(sqrt(16L*a*(y-d)+4L*b*(b+c)+c*c+8L*c*(y-d))))%/%(2L*(2L*a+c))
else
-(2L*(d-y))%/%(2L*b+c)
}
## Returns NULL if and only if either
## 1. all of m, v, l are 'k'
## 2. all of m, v, l are 'f'
## 3. one of m, v are zero but not both.
get_name_stub = function (pat) {
if ((pat[['M']] == '0' && pat[['V']] != '0')||(pat[['M']] != '0' && pat[['V']] == '0')) {
return(NULL)
}
if (pat[['M']] == 'f' && pat[['V']] == 'f' && pat[['L']] == 'f') {
return(NULL)
}
match = NULL
for (i in seq_along(special_cases)) {
matched = T
names_matched = list()
for (n in c('M','V','L'))
if (n %in% names(special_cases[[i]])) {
if (special_cases[[i]][[n]] != pat[[n]]) {
matched = F
} else {
names_matched = c(names_matched, list(n))
}
}
if (matched) {
match = i
for (n in names_matched)
pat[[n]] = NULL
break;
}
}
done_part = if (!is.null(match)) names(special_cases)[match]
else done_part = 'ou'
pat[which(pat=='k')] = NULL
for (i in seq_along(pat)) {
if (names(pat)[i] == 'M') {
done_part = paste0(done_part, '_', restriction_names[[pat[[i]]]], 'H')
} else if (names(pat)[i] == 'V') {
done_part = paste0(done_part, '_', restriction_names[[pat[[i]]]], 'theta')
} else if (names(pat)[i] == 'L') {
done_part = paste0(done_part, '_', restriction_names[[pat[[i]]]], 'Sig')
} else {
stop('Error compiling the names of restriction functions')
}
}
if (done_part == 'ou') return(NULL)
else return(list(par = done_part,
jac = paste0('d', done_part),
hess = paste0('h', done_part),
nparams= paste0('nparams_',done_part)))
}
mkcmd = function (pat) {
s = ''
for (i in seq_along(pat)) s = paste0(s, names(pat)[i], pat[[i]])
return(s)
}
cmd2abcd = function (cmd) {
cmd_s = strsplit(cmd, '')[[1]]
curstate = 1L
i = 1L
res = list(a=0L,b=0L,c=0L,d=0L)
repeat{
if (curstate == 1L) {
switch(cmd_s[[i]],
M={curstate = 2L},
V={curstate = 3L},
L={curstate = 4L})
} else if (curstate == 2L) { #M
switch(cmd_s[[i]],
s= {res[['c']]=res[['c']]+1L},
l= {res[['c']]=res[['c']]+1L},
c= {res[['c']]=res[['c']]+1L},
d= {res[['b']]=res[['b']]+1L},
e= {res[['b']]=res[['b']]+1L},
k= {res[['a']]=res[['a']]+1L},
'0'= {NULL;},
f= {NULL;})
curstate = 1L
} else if (curstate == 3L) { #V
switch(cmd_s[[i]],
k= {res[['b']]=res[['b']]+1L},
'0'= {NULL;},
f= {NULL;})
curstate = 1L
} else if (curstate == 4L) { #L
switch(cmd_s[[i]],
d= {res[['b']]=res[['b']]+1L},
k= {res[['c']]=res[['c']]+1L},
'0'= {NULL;},
f= {NULL;})
curstate = 1L
} else stop("Error pasing command line in cmd2abcd")
i=i+1
if (i > length(cmd_s)) break;
}
res
}
build_par = function (pat) {
cmd = mkcmd(pat)
body = quote({
parfn
BUILD_FIXED__;
f=function (par, ...) {
mode(par) = 'double'
parfn(.Call(Rparamrestrict, s, par, npar2k(length(par),a,b,c,d), FIXEDPART__), ...)
}
attr(f,'srcref') = NULL
f
})
outerargs = alist(parfn=)
tosub = c(list('s'=cmd),
cmd2abcd(cmd),
list('FIXEDPART__' =quote(NULL),
'BUILD_FIXED__'=quote(NULL)))
outerargs = c(outerargs, fixed_arg <- mk_fixed_arg(pat))
if (0L != length(fixed_arg)) {
tosub_f = alist(SUB_H__ =NULL,
SUB_THETA__=NULL,
SUB_SIG__ =NULL)
cnt = 1L
for (i in seq_along(names(fixed_arg))) {
if (names(fixed_arg)[i] == 'H')
tosub_f[['SUB_H__']] = substitute({
fixedpart[[j]] = {
if (!is.numeric(H)) stop('The argument `H` must be numeric')
mode(H)='double';
H
}
}, list(j=cnt))
else if (names(fixed_arg)[i] == 'theta')
tosub_f[['SUB_THETA__']] = substitute({
fixedpart[[j]] = {
if (!is.numeric(theta)) stop('The argument `theta` must be numeric')
mode(theta)='double';
theta
}
}, list(j=cnt))
else if (names(fixed_arg)[i] == 'Sig')
tosub_f[['SUB_SIG__']] = substitute({
fixedpart[[j]] = {
if (!is.numeric(Sig)) stop('The argument `Sig` must be numeric')
mode(Sig)='double';
Sig
}
}, list(j=cnt))
else stop('Error building fixed-parameter restriction functions')
cnt = cnt+1L
}
tosub[['BUILD_FIXED__']] = substitute({
fixedpart = list()
SUB_H__
SUB_THETA__
SUB_SIG__
}, tosub_f)
tosub[['FIXEDPART__']] = quote(fixedpart)
}
body = substituteDirect(body, tosub)
substitute(
`function`(A, B),
list(A=as.pairlist(outerargs), B=body))
}
build_jac = function (pat) {
cmd = mkcmd(pat)
body = quote({
jacfn
BUILD_FIXED__;
f=function (par, ...) {
mode(par) = 'double'
k = npar2k(length(par),a,b,c,d)
.Call(Rpostjacrestrict, s, par,
jacfn(.Call(Rparamrestrict, s, par, k, FIXEDPART__), ...), k)
}
attr(f,'srcref') = NULL
f
})
outerargs = alist(jacfn=)
tosub = c(list('s'=cmd),
cmd2abcd(cmd),
list('FIXEDPART__' =quote(NULL),
'BUILD_FIXED__'=quote(NULL)))
outerargs = c(outerargs, fixed_arg <- mk_fixed_arg(pat))
if (0L != length(fixed_arg)) {
tosub_f = alist(SUB_H__ =NULL,
SUB_THETA__=NULL,
SUB_SIG__ =NULL)
cnt = 1L
for (i in seq_along(names(fixed_arg))) {
if (names(fixed_arg)[i] == 'H')
tosub_f[['SUB_H__']] = substitute({
fixedpart[[j]] = {
if (!is.numeric(H)) stop('The argument `H` must be numeric')
mode(H)='double';
H
}
}, list(j=cnt))
else if (names(fixed_arg)[i] == 'theta')
tosub_f[['SUB_THETA__']] = substitute({
fixedpart[[j]] = {
if (!is.numeric(theta)) stop('The argument `theta` must be numeric')
mode(theta)='double';
theta
}
}, list(j=cnt))
else if (names(fixed_arg)[i] == 'Sig')
tosub_f[['SUB_SIG__']] = substitute({
fixedpart[[j]] = {
if (!is.numeric(Sig)) stop('The argument `Sig` must be numeric')
mode(Sig)='double';
Sig
}
}, list(j=cnt))
else stop('Error building fixed-parameter restriction functions')
cnt = cnt+1L
}
tosub[['BUILD_FIXED__']] = substitute({
fixedpart = list()
SUB_H__
SUB_THETA__
SUB_SIG__
}, tosub_f)
tosub[['FIXEDPART__']] = quote(fixedpart)
}
body = substituteDirect(body, tosub)
substitute(
`function`(A, B),
list(A=as.pairlist(outerargs), B=body))
}
mk_fixed_arg = function (pat) {
stopifnot(names(pat)[1] == 'M')
stopifnot(names(pat)[2] == 'V')
stopifnot(names(pat)[3] == 'L')
A = alist()
if (pat[[1]]=='f') A = c(A, alist(H=))
if (pat[[2]]=='f') A = c(A, alist(theta=))
if (pat[[3]]=='f') A = c(A, alist(Sig=))
return(A)
}
build_hess = function (pat) {
outerargs = alist(hessfn=)
body = quote({
hessfn;
JAC_GUARD_EVAL__;
BUILD_FIXED__;
f= function (par, ...) {
mode(par) = 'double';
k = npar2k(length(par), A__, B__, C__, D__)
par_orig = .Call(Rparamrestrict, CMD__, par, k, FIXEDPART__);
Hes = hessfn(par_orig, ...);
MAKE_JACTHIS__;
MAKE_JACLOWER__;
list(V = .Call(Rposthessrestrict, CMD__, par, Hes[['V']], k,
RJACLOWER__, RJLOWEROFFSET_V__, RJACTHIS__, RJTHISOFFSET_R_V__, RJTHISOFFSET_C__),
w = .Call(Rposthessrestrict, CMD__, par, Hes[['w']], k,
RJACLOWER__, RJLOWEROFFSET_W__, RJACTHIS__, RJTHISOFFSET_R_W__, RJTHISOFFSET_C__),
Phi = .Call(Rposthessrestrict, CMD__, par, Hes[['Phi']], k,
RJACLOWER__, RJLOWEROFFSET_PHI__, RJACTHIS__, RJTHISOFFSET_R_PHI__, RJTHISOFFSET_C__))
}
attr(f, 'srcref') = NULL
f
})
cmd = mkcmd(pat)
to_sub = list(
BUILD_FIXED__ = quote(NULL),
FIXEDPART__ = quote(NULL),
JAC_GUARD_EVAL__ = quote(NULL),
MAKE_JACTHIS__ = quote(NULL),
MAKE_JACLOWER__ = quote(NULL),
CMD__ = cmd,
RJACLOWER__ = quote(NULL),
RJLOWEROFFSET_V__ = quote(NULL),
RJLOWEROFFSET_W__ = quote(NULL),
RJLOWEROFFSET_PHI__ = quote(NULL),
RJACTHIS__ = quote(NULL),
RJTHISOFFSET_R_V__ = quote(NULL),
RJTHISOFFSET_R_W__ = quote(NULL),
RJTHISOFFSET_R_PHI__ = quote(NULL),
RJTHISOFFSET_C__ = quote(NULL)
)
## Detect if there are any cholesky or log-cholesky. If yes, add jacfn as argument
if (grepl('Ml', cmd, fixed = TRUE) || grepl('Mc', cmd, fixed = TRUE)) {
outerargs = c(outerargs, alist(jacfn=))
to_sub[['JAC_GUARD_EVAL__']] = quote(jacfn)
to_sub[['MAKE_JACLOWER__']] = quote(
{
J = jacfn(par_orig, ...);
ku = INFO__$mod$rawmod$dimtab[INFO__$node_id];
kv = INFO__$mod$rawmod$dimtab[INFO__$parent_id];
})
to_sub[['RJACLOWER__']] = quote(J)
to_sub[['RJLOWEROFFSET_V__']] = quote(ku*kv+ku)
to_sub[['RJLOWEROFFSET_W__']] = quote(ku*kv)
to_sub[['RJLOWEROFFSET_PHI__']] = quote(0L)
}
if (grepl('Me', cmd, fixed = TRUE)) {
to_sub[['MAKE_JACTHIS__']] = quote(
{
Jthis = INFO__[['reparametrisation_jacobian']];
gstart = INFO__$mod$gausssegments[INFO__$node_id,'start'];
gend = INFO__$mod$gausssegments[INFO__$node_id,'end'];
pstart = INFO__$mod$parsegments[INFO__$parfn_id,'start'];
phid = dim(Hes[['Phi']])[1];
wd = dim(Hes[['w']])[1];
})
to_sub[['RJACTHIS__']] = quote(Jthis)
to_sub[['RJTHISOFFSET_R_V__']] = quote(gstart+phid+wd-1L)
to_sub[['RJTHISOFFSET_R_W__']] = quote(gstart+phid-1L)
to_sub[['RJTHISOFFSET_R_PHI__']] = quote(gstart-1L)
to_sub[['RJTHISOFFSET_C__']] = quote(pstart-1L)
}
## If there is fixed part, add to formal arguments, and add a list builder on
## the outer function.
outerargs = c(outerargs, fixed_arg <- mk_fixed_arg(pat))
if (0L != length(fixed_arg)) {
tosub_f = alist(SUB_H__ =NULL,
SUB_THETA__=NULL,
SUB_SIG__ =NULL)
cnt = 1L
for (i in seq_along(names(fixed_arg))) {
if (names(fixed_arg)[i] == 'H')
tosub_f[['SUB_H__']] = substitute({
fixedpart[[j]] = {
if (!is.numeric(H)) stop('The argument `H` must be numeric')
mode(H)='double';
H
}
}, list(j=cnt))
else if (names(fixed_arg)[i] == 'theta')
tosub_f[['SUB_THETA__']] = substitute({
fixedpart[[j]] = {
if (!is.numeric(theta)) stop('The argument `theta` must be numeric')
mode(theta)='double';
theta
}
}, list(j=cnt))
else if (names(fixed_arg)[i] == 'Sig')
tosub_f[['SUB_SIG__']] = substitute({
fixedpart[[j]] = {
if (!is.numeric(Sig)) stop('The argument `Sig` must be numeric')
mode(Sig)='double';
Sig
}
}, list(j=cnt))
else stop('Error building fixed-parameter restriction functions')
cnt = cnt+1L
}
to_sub[['BUILD_FIXED__']] = substitute({
fixedpart = list()
SUB_H__
SUB_THETA__
SUB_SIG__
}, tosub_f)
to_sub[['FIXEDPART__']] = quote(fixedpart)
}
abcd = cmd2abcd(cmd)
to_sub[['A__']] = abcd$a
to_sub[['B__']] = abcd$b
to_sub[['C__']] = abcd$c
to_sub[['D__']] = abcd$d
body = substituteDirect(body, to_sub)
substitute(`function`(args, body), list(args=as.pairlist(outerargs), body=body))
}
build_nparams = function (pat) {
cmd = mkcmd(pat)
abcd = cmd2abcd(cmd)
substitute(function (k) { a*k*k+b*k+c*(k*(k+1L))%/%2L+d }, abcd)
}
#' @name parameter_restriction
#' @rdname parameter_restriction
#' @usage avail_restrictions
#' @export
avail_restrictions = list(nparams=character(0),
par =character(0),
jac =character(0),
hess =character(0))
## Now build all the no-measurement-error-variance OU/Brownian restrictor functions.
for (m in avail_restrict_cmds[['M']]) {
for (v in avail_restrict_cmds[['V']]) {
for (l in avail_restrict_cmds[['L']]) {
pat = list('M'=m,'V'=v,'L'=l)
name_stub = get_name_stub(pat)
if (is.null(name_stub)) next
assign(name_stub[['nparams']], eval(build_nparams(pat)))
assign(name_stub[['par']], eval(build_par(pat)))
assign(name_stub[['jac']], eval(build_jac(pat)))
assign(name_stub[['hess']], eval(build_hess(pat)))
## Change the enclosing environment of these function back to the package's environment.
eval(substitute({environment(f) = pkg_env}, list(f = as.name(name_stub[['nparams']]))))
eval(substitute({environment(f) = pkg_env}, list(f = as.name(name_stub[['par']]))))
eval(substitute({environment(f) = pkg_env}, list(f = as.name(name_stub[['jac']]))))
eval(substitute({environment(f) = pkg_env}, list(f = as.name(name_stub[['hess']]))))
## Fix print.function's behaviour.
eval(substitute({attr(f, "srcref") = NULL}), list(f= as.name(name_stub[['nparams']])))
eval(substitute({attr(f, "srcref") = NULL}), list(f= as.name(name_stub[['par']])))
eval(substitute({attr(f, "srcref") = NULL}), list(f= as.name(name_stub[['jac']])))
eval(substitute({attr(f, "srcref") = NULL}), list(f= as.name(name_stub[['hess']])))
avail_restrictions$nparams = c(avail_restrictions$nparams, name_stub[['nparams']])
avail_restrictions$par = c(avail_restrictions$par, name_stub[['par']])
avail_restrictions$jac = c(avail_restrictions$jac, name_stub[['jac']])
avail_restrictions$hess = c(avail_restrictions$hess, name_stub[['hess']])
}
}
}
## User convenience functions for getting the OU parameter restrictors that they want.
human_name_to_cmdchr = list(
'symmetric' = 's',
'logspd' = 'l',
'spd' = 'c',
'diag' = 'd',
'logdiag' = 'e',
'zero' = '0'
)
#' Convenience function for constructing restricted/reparameterised OU parameterisation function.
#'
#' \code{get_restricted_ou} is a convenience function for constructing restricted/reparameterised
#' OU parameterisation.
#'
#' \code{get_restricted_ou} is intended to provide a more convenient way to construct the
#' restrictions functions, restricted Jacobian and Hessian, than the more flexible methods
#' described in \code{\link{parameter_restriction}}.
#'
#' If either one of \code{H}, \code{theta} is 'zero' but not both, the function stops with error.
#' This is because former is statistically not sensible, and the latter can be done by directly
#' passing a vector of zero to the \code{theta} argument.
#'
#' If lossmiss is \code{NULL}, the returned functions does not have capability to handle missing or
#' lost values.
#'
#' @param H One of \code{NULL}, 'symmetric', 'logspd', 'spd', 'diag', 'logdiag', 'zero', or a
#' numerical vector specifying fixed parameters.
#' @param theta One of \code{NULL}, 'zero', or a numerical vector specifying fixed parameters.
#' @param Sig One of \code{NULL}, 'diag', or a numerical vector specifying fixed parameters.
#' @param lossmiss One of \code{NULL}, 'zap', 'halt'.
#' @return A list containing the following elements:
#' \item{par}{A reparameterisation function conforming to the format required by the \code{parfns}
#' argument of \code{glinv}.}
#' \item{jac}{A Jacobian function of the above reparameterisation function conforming to the format
#' required by the \code{parjacs} argument of \code{glinv}.}
#' \item{hess}{A Hessian function of the above reparameterisation function conforming to the format
#' required by the \code{parhess} argument of \code{glinv}.}
#' \item{nparams}{A function which accepts one integer argument, the total number of dimensions
#' of the multivariate traits, and returns the number of parameters of the restricted
#' model.}
#' @examples
#' ### --- STEP 1: Make an example tree
#' set.seed(0x5EEDL, kind='Super-Duper')
#' ntips = 200
#' k = 2 # No. of trait dimensions
#' tr = ape::rtree(ntips)
#' x0 = rnorm(k)
#'
#' ### --- STEP 2: Make a model which has unrestricted H, fixed theta and diagonal Sigma_x'.
#' repar = get_restricted_ou(H=NULL, theta=c(3,1), Sig='diag', lossmiss=NULL)
#' mod = glinv(tr, x0, X=NULL,
#' pardims =repar$nparams(k),
#' parfns =repar$par,
#' parjacs =repar$jac,
#' parhess =repar$hess)
#' # Actually, to save typing, the following short-cut call is the same as the above:
#' # mod = glinv(tr, x0, X=NULL, repar=repar)
#'
#' ### --- STEP 3: Set up parameters; H, theta, and sig_x needs to be concatenated
#' H = matrix(c(1,0,0,-1), k)
#' theta = c(3,1)
#' sig = matrix(c(0.25,0,0,0.25), k)
#' sig_x = t(chol(sig))
#' par_truth = c(H=H, sig_x=c(0.5,0.5))
#'
#' ### --- STEP 4: Get a simulated data set to toy with
#' X = rglinv(mod, par=par_truth)
#' set_tips(mod, X)
#'
#' ### --- STEP 5: Make an unrestricted model object to compare with the one
#' ### whose parameters are restricted.
#' mod_unrestricted = glinv(tr, x0, X,
#' pardims=nparams_ou(k),
#' parfns=oupar,
#' parjacs=oujac,
#' parhess=ouhess)
#'
#'
#' ### --- STEP 6: Confirm this is indeed the same as typing everything manually
#' ## Does the restricted model gives the same likelihood as the unrestricted? (Yes, it does.)
#' LIK = lik(mod)(par_truth)
#' LIK_unrestricted = lik(mod_unrestricted)(c(H,theta,sig_x[lower.tri(sig_x, diag=TRUE)]))
#' print(LIK == LIK_unrestricted)
#' # [1] TRUE
#' ## We can as well type everything manually as follows. This mod_manual should be
#' ## the same as the mod object; just a different way of calling the glinv function.
#' mod_manual = glinv(tr, x0, X,
#' pardims = nparams_ou_fixedtheta_diagSig(k),
#' parfns = ou_fixedtheta_diagSig(oupar, theta=c(3,1)),
#' parjacs = dou_fixedtheta_diagSig(oujac, theta=c(3,1)),
#' parhess = hou_fixedtheta_diagSig(ouhess, theta=c(3,1)))
#' LIK_manual = lik(mod_manual)(par_truth)
#' print(LIK == LIK_manual) #It's really the same
#' # [1] TRUE
#'
#' @export
get_restricted_ou = function (H=NULL, theta=NULL, Sig=NULL, lossmiss = 'halt') {
pat = list(M='k', V='k', L='k')
fixed_args = list()
if (!is.null(H)) {
if (is.character(H)) {
s = human_name_to_cmdchr[[H]]
if (is.null(s) || !(s %in% avail_restrict_cmds[[1]]))
stop(sprintf('`%s` restriction for H is not supported', H))
pat[[1]] = s
} else if (is.numeric(H)) {
fixed_args[['H']] = as.double(H)
pat[[1]] = 'f'
} else {
stop('`H` must be either a string or a numeric vector to be fixed.')
}
} else {
pat[[1]] = 'k'
}
if (!is.null(theta)) {
if (is.character(theta)) {
s = human_name_to_cmdchr[[theta]]
if (is.null(s) || !(s %in% avail_restrict_cmds[[2]]))
stop(sprintf('`%s` restriction for theta is not supported', theta))
pat[[2]] = s
} else if (is.numeric(theta)) {
fixed_args[['theta']] = as.double(theta)
pat[[2]] = 'f'
} else {
stop('`theta` must be either a string or a numeric vector to be fixed.')
}
} else {
pat[[2]] = 'k'
}
if (!is.null(Sig)) {
if (is.character(Sig)) {
s = human_name_to_cmdchr[[Sig]]
if (is.null(s) || !(s %in% avail_restrict_cmds[[3]]))
stop(sprintf('`%s` restriction for Sig is not supported', Sig))
pat[[3]] = s
} else if (is.numeric(Sig)) {
fixed_args[['Sig']] = as.double(Sig)
pat[[3]] = 'f'
} else {
stop('`Sig` must be either a string or a numeric vector to be fixed.')
}
} else {
pat[[3]] = 'k'
}
## If any of H or theta is zero but not both (which is Brownian motion) then throw an error.
if ((pat[[1]] == '0' && pat[[2]] != '0') || (pat[[1]] != '0' && pat[[2]] == '0'))
stop('zero H but non-zero theta, or non-zero H but zero theta is not supported. The former does not make statistical sense and the latter can be done by using fixed theta instead of zero theta.')
if (is.null(lossmiss)) {
ouparfn = oupar
oujacfn = oujac
ouhessfn = ouhess
} else if (lossmiss == 'halt') {
ouparfn = ou_haltlost(oupar)
oujacfn = dou_haltlost(oujac)
ouhessfn = hou_haltlost(ouhess)
} else if (lossmiss == 'zap') {
ouparfn = ou_zaplost(oupar)
oujacfn = dou_zaplost(oujac)
ouhessfn = hou_zaplost(ouhess)
} else {
stop('`lossmiss` is invalid')
}
if (pat[[1]] == 'k' && pat[[2]] == 'k' && pat[[3]] == 'k')
return(list(par=oupar, jac=oujac, hess=ouhess, nparams=nparams_ou))
cmd = mkcmd(pat)
namestub = get_name_stub(pat)
## Now call the function names in namestub with the correct arguments.
par_re = get(namestub[['par']])
jac_re = get(namestub[['jac']])
hess_re = get(namestub[['hess']])
nparams = get(namestub[['nparams']])
if (all(sapply(fixed_args, is.null)))
fixed_args = list()
if (grepl('Ml', cmd, fixed = TRUE) || grepl('Mc', cmd, fixed = TRUE)) {
ouparfn = do.call(par_re, c(list(parfn=ouparfn), fixed_args))
ouhessfn = do.call(hess_re, c(list(hessfn=ouhessfn, jacfn=oujacfn), fixed_args))
oujacfn = do.call(jac_re, c(list(jacfn=oujacfn), fixed_args))
} else {
ouparfn = do.call(par_re, c(list(parfn=ouparfn), fixed_args))
ouhessfn = do.call(hess_re, c(list(hessfn=ouhessfn), fixed_args))
oujacfn = do.call(jac_re, c(list(jacfn=oujacfn), fixed_args))
}
list(par=ouparfn, jac=oujacfn, hess=ouhessfn, nparams=nparams)
}
rm('mk_fixed_arg')
rm('m')
rm('v')
rm('l')
rm('pat')
rm('name_stub')
rm('pkg_env')
rm('cmd2abcd')
rm('build_par')
rm('build_jac')
rm('build_hess')
rm('build_nparams')
## Now deal with the documentation and exporting all of those functions...
#' @rawNamespace exportPattern("^ou_.*$")
#' @rawNamespace exportPattern("^dou_.*$")
#' @rawNamespace exportPattern("^hou_.*$")
#' @rawNamespace exportPattern("^brn.*$")
#' @rawNamespace exportPattern("^dbrn.*$")
#' @rawNamespace exportPattern("^hbrn.*$")
#' @rawNamespace exportPattern("^nparams_ou_.*$")
#' @rawNamespace exportPattern("^nparams_brn.*$")
NULL
#' Restrict the parameters space of OU and Brownian motion models.
#'
#' \code{ou_diagH}, \code{ou_diagH_fixedtheta_diagSig}, etc., restricts the OU model's
#' parameters. For example, \code{ou_diagH} restricts the drift \eqn{H} to diagonal matrix,
#' and \code{ou_diagH_fixedtheta_diagSig} further restricts theta to be a constant and
#' \eqn{\Sigma_x'} to be diagonal. A Brownian motion model can be made by these restriction.
#'
#' \subsection{How reparametrisation and restriction works}{
#'
#' In the simplest form, without any restriction or reparametrisation, the user typically
#' needs to pass \code{oupar}, \code{oujac}, \code{ouhess}, all of which are simply
#' functions which maps from the OU parameters \eqn{(H,\theta,\Sigma_x')} to the Gaussian
#' paramters \eqn{(\Phi_i,w_i,V'_i)} for each node. For example:
#' \preformatted{
#' mod.full = glinv(tree, x0, my_data,
#' parfns = oupar,
#' pardims = nparams_ou(k),
#' parjacs = oujac,
#' parhess = ouhess)
#' }
#' If one would like to restrict \eqn{H} to only positively definite diagonal matrices,
#' then the call should become
#' \preformatted{
#' mod.pddiag = glinv(tree, x0, my_data,
#' parfns = ou_logdiagH(oupar),
#' pardims = nparams_ou_logdiagH(k),
#' parjacs = dou_logdiagH(oujac),
#' parhess = hou_logdiagH(ouhess))
#' }
#' Note that there is a naming convention that \code{ou_*} should be applied to `oupar`,
#' \code{dou_*} to `oujac`, and \code{hou_*} to `ouhess`. \code{d} stands for `derivative'
#' and \code{h} stands for `Hessian'.
#'
#' In the above call, ou_logdiagH(oupar) accepts the \code{oupar} function as argument
#' and returns a new function. This new function behaves the same way as oupar itself,
#' except that it expects its first argument (which is the model parameters) to be of
#' lower dimension, only consisting of \eqn{(h,\theta,\Sigma_x')} where \eqn{h} is the
#' diagonal vector of \eqn{H}. The following example should be illustrative:
#' \preformatted{
#' f = ou_logdiagH(oupar)
#' par.full = list(H = matrix(c(3,0,0,2),2,2), # diagonal matrix
#' theta = c(4,5),
#' sig_x = c(1,0.1,1))
#' par.restricted = list(H = log(diag(par.full$H)),
#' theta = par.full$theta,
#' sig_x = par.full$sig_x)
#' print(all.equal(f(unlist(par.restricted),1,NULL,NULL),
#' oupar(unlist(par.full),1,NULL,NULL)))
#' # [1] TRUE
#' }
#' }
#'
#' \subsection{Pre-defined restrictions}{
#' The following table summarises all the pre-defined \code{ou_*} functions. See \code{\link{oupar}}
#' for precise meaning of the \eqn{(H,\theta,\Sigma_x')} mentioned below.
#' \tabular{ll}{
#' \strong{R function} \tab \strong{Parameter Format after Restriction}\cr
#' \code{brn*} \tab \eqn{\Sigma_x'}. The Brownian motion. \eqn{H} and \eqn{\theta} are zero, thus missing.\cr
#' \code{*_diagH_*} \tab \eqn{(h,\theta,\Sigma_x')}, with \eqn{h=diag(H)}, and H is a diagonal matrix\cr
#' \code{*_logdiagH_*} \tab \eqn{(log(h),\theta,\Sigma_x')}, with \eqn{h=diag(H)}, and H is a diagonal matrix\cr
#' \code{*_symH_*} \tab \eqn{(L,\theta,\Sigma_x')}, with \eqn{L} being lower-triangular part of the symmetric matrix \eqn{H}\cr
#' \code{*_spdH_*} \tab \eqn{(L,\theta,\Sigma_x')}, with \eqn{L} being Cholesky factor of the S.P.D. matrix \eqn{H}\cr
#' \code{*_logspdH_*} \tab \eqn{(L',\theta,\Sigma_x')} where \eqn{L'} equals \eqn{L}, except that on the diagonals \eqn{L'_i} = \eqn{log L_i}\cr
#' \code{*_fixedH_*} \tab \eqn{(\theta,\Sigma_x')}. \eqn{H} is constant, hence missing\cr
#' \code{*_fixedtheta_*} \tab \eqn{(H,\Sigma_x')}. \eqn{\theta} is constant, hence missing\cr
#' \code{*_fixedSig_*} \tab \eqn{(H,\theta)}. \eqn{\Sigma_x} is constant, hence missing\cr
#' \code{*_diagSig_*} \tab \eqn{(H,\theta,s)} where \eqn{s=diag(\Sigma_x'}, with \eqn{\Sigma_x'} being a diagonal matrix.
#' }
#' By Cholesky factor, we mean the only the non-zero part of the lower-triangular Cholesky factor. Restricting \eqn{\Sigma_x'} to a diagonal matrix
#' means that \eqn{\Sigma_x} is also diagonal; and the variance of the Brownian motion is \eqn{log(diag(\Sigma_x'))}. In other words, the diagonal
#' restriction is placed on \eqn{\Sigma_x'}, not \eqn{\Sigma_x}.
#' }
#' \subsection{Finding a list of these restriction functions}{
#' One can use \code{print(avail_restrictions)} to see a list of all of these restriction function names.
#' }
#' \subsection{Calling these restriction functions}{
#' All \code{*ou_*} or \code{*brn*} functions accepts the same arguemnts as \code{ou_logdiagH},
#' \code{dou_logdiagH}, \code{hou_logdiagH}, \code{nparams_ou_logdiagH} as shown in the Usage
#' and Arguments section, except that:
#' \enumerate{
#' \item If the reparametrisation contains any Cholesky decomposition (in other words, the function name
#' contains \code{spd} or \code{logspd}) then in the Hessian-level reparameterisation function
#' (named \code{hou_*}) an extra argument \code{jacfn} is required.
#' \item If the reparametrisation contains any fixed parameters, extra arguments \code{H}, \code{theta},
#' or \code{Sig} are required, depending what is fixed.
#' }
#' For example, in the Usage section, \code{ou_logspdH_fixedtheta} takes an extra argument \code{theta} because
#' of (2), and \code{hou_spdH_fixedSig} takes extra argument two extra arguments because of both (1) and (2) are
#' true.
#' }
#'
#' @name parameter_restriction
#' @usage brn_diagSig(parfn)
#' @usage ou_logdiagH(parfn)
#' @usage dou_logdiagH(jacfn)
#' @usage hou_logdiagH(hessfn)
#' @usage ou_logdiagH_diagSig(parfn)
#' @usage ou_logspdH_fixedtheta(parfn, theta)
#' @usage ou_spdH_fixedSig(parfn, Sig)
#' @usage ou_fixedH_diagSig(parfn, H)
#' @usage dou_logdiagH_diagSig(jacfn)
#' @usage dou_logspdH_fixedtheta(jacfn, theta)
#' @usage dou_spdH_fixedSig(jacfn, Sig)
#' @usage dou_fixedH_diagSig(jacfn, H)
#' @usage hou_logdiagH_diagSig(hessfn)
#' @usage hou_logspdH_fixedtheta(hessfn, jacfn, theta)
#' @usage hou_spdH_fixedSig(hessfn, jacfn, Sig)
#' @usage hou_spdH_fixedtheta_fixedSig(hessfn, jacfn, theta, Sig)
#' @usage hou_fixedH_diagSig(hessfn, H)
#' @usage nparams_ou_logdiagH(k)
#' @usage nparams_brn(k)
#' @usage nparams_ou_spdH_fixedSig(k)
#' @param parfn A function that maps from the user-parametrisation to the underlying Gaussian parameters.
#' Each of them returns a vector of concatenated \eqn{(\Phi, w, V')}, where \eqn{V'} is the lower triangular
#' part of \eqn{V}, and accepts four arguments: a vector of parameters whose length is specified
#' by the \code{pardims} argument to the \code{glinv_gauss} function, the branch length leading to the currently processing node,
#' a vector of factors with three levels indicating which dimensions are missing or lost in the mother of
#' the current node, and a vector of factors with the same three levels indicating missingness of the current
#' node.
#' @param jacfn A function that accepts the same arguments as \code{parfn} and returns the Jacobian
#' of \code{parfn}.
#' @param hessfn A function that accepts the same arguments as \code{parfns} and returns a list of three 3D arrays,
#' named \code{Phi}, \code{w}, \code{V} respectively inside the list. \code{((hessfn)(...))$Phi[m,i,j]}
#' contains the cross second-order partial derivative of \eqn{\Phi_m} (here we treat the matrix
#' \eqn{\Phi} as a column-major-flattened vector) with respect to the \eqn{i}-th and\eqn{j}-th parameters
#' in the joint \eqn{(H,\theta,\Sigma_x')} vector, and
#' \code{((hessfn)(...))$w[m,i,j]} and \code{((hessfn)(...))$V[m,i,j]}
#' analogously contains second-order derivative with respect to \eqn{w_m} and \eqn{V'_m}.
#' @param H A numerical vector containing the (flattened) fixed parameter \eqn{H}.
#' @param theta A numerical vector containing the (flattened) fixed parameter \eqn{theta}.
#' @param Sig A numerical vector containing the (flattened) fixed parameter \eqn{\Sigma_x'}.
#' @param k An integer. The total number of dimensions of the multivariate traits.
#' @rdname parameter_restriction
#' @aliases brn brn_diagSig brn_fixedSig dbrn dbrn_diagSig dbrn_fixedSig dou_diagH dou_diagH_diagSig dou_diagH_fixedSig dou_diagH_fixedtheta dou_diagH_fixedtheta_diagSig dou_diagH_fixedtheta_fixedSig dou_diagSig dou_fixedH dou_fixedH_diagSig dou_fixedH_fixedSig dou_fixedH_fixedtheta dou_fixedH_fixedtheta_diagSig dou_fixedSig dou_fixedtheta dou_fixedtheta_diagSig dou_fixedtheta_fixedSig dou_logdiagH dou_logdiagH_diagSig dou_logdiagH_fixedSig dou_logdiagH_fixedtheta dou_logdiagH_fixedtheta_diagSig dou_logdiagH_fixedtheta_fixedSig dou_logspdH dou_logspdH_diagSig dou_logspdH_fixedSig dou_logspdH_fixedtheta dou_logspdH_fixedtheta_diagSig dou_logspdH_fixedtheta_fixedSig dou_spdH dou_spdH_diagSig dou_spdH_fixedSig dou_spdH_fixedtheta dou_spdH_fixedtheta_diagSig dou_spdH_fixedtheta_fixedSig dou_symH dou_symH_diagSig dou_symH_fixedSig dou_symH_fixedtheta dou_symH_fixedtheta_diagSig dou_symH_fixedtheta_fixedSig hbrn hbrn_diagSig hbrn_fixedSig hou_diagH hou_diagH_diagSig hou_diagH_fixedSig hou_diagH_fixedtheta hou_diagH_fixedtheta_diagSig hou_diagH_fixedtheta_fixedSig hou_diagSig hou_fixedH hou_fixedH_diagSig hou_fixedH_fixedSig hou_fixedH_fixedtheta hou_fixedH_fixedtheta_diagSig hou_fixedSig hou_fixedtheta hou_fixedtheta_diagSig hou_fixedtheta_fixedSig hou_logdiagH hou_logdiagH_diagSig hou_logdiagH_fixedSig hou_logdiagH_fixedtheta hou_logdiagH_fixedtheta_diagSig hou_logdiagH_fixedtheta_fixedSig hou_logspdH hou_logspdH_diagSig hou_logspdH_fixedSig hou_logspdH_fixedtheta hou_logspdH_fixedtheta_diagSig hou_logspdH_fixedtheta_fixedSig hou_spdH hou_spdH_diagSig hou_spdH_fixedSig hou_spdH_fixedtheta hou_spdH_fixedtheta_diagSig hou_spdH_fixedtheta_fixedSig hou_symH hou_symH_diagSig hou_symH_fixedSig hou_symH_fixedtheta hou_symH_fixedtheta_diagSig hou_symH_fixedtheta_fixedSig nparams_brn nparams_brn_diagSig nparams_brn_fixedSig nparams_ou_diagH nparams_ou_diagH_diagSig nparams_ou_diagH_fixedSig nparams_ou_diagH_fixedtheta nparams_ou_diagH_fixedtheta_diagSig nparams_ou_diagH_fixedtheta_fixedSig nparams_ou_diagSig nparams_ou_fixedH nparams_ou_fixedH_diagSig nparams_ou_fixedH_fixedSig nparams_ou_fixedH_fixedtheta nparams_ou_fixedH_fixedtheta_diagSig nparams_ou_fixedSig nparams_ou_fixedtheta nparams_ou_fixedtheta_diagSig nparams_ou_fixedtheta_fixedSig nparams_ou_logdiagH nparams_ou_logdiagH_diagSig nparams_ou_logdiagH_fixedSig nparams_ou_logdiagH_fixedtheta nparams_ou_logdiagH_fixedtheta_diagSig nparams_ou_logdiagH_fixedtheta_fixedSig nparams_ou_logspdH nparams_ou_logspdH_diagSig nparams_ou_logspdH_fixedSig nparams_ou_logspdH_fixedtheta nparams_ou_logspdH_fixedtheta_diagSig nparams_ou_logspdH_fixedtheta_fixedSig nparams_ou_spdH nparams_ou_spdH_diagSig nparams_ou_spdH_fixedSig nparams_ou_spdH_fixedtheta nparams_ou_spdH_fixedtheta_diagSig nparams_ou_spdH_fixedtheta_fixedSig nparams_ou_symH nparams_ou_symH_diagSig nparams_ou_symH_fixedSig nparams_ou_symH_fixedtheta nparams_ou_symH_fixedtheta_diagSig nparams_ou_symH_fixedtheta_fixedSig ou_diagH ou_diagH_diagSig ou_diagH_fixedSig ou_diagH_fixedtheta ou_diagH_fixedtheta_diagSig ou_diagH_fixedtheta_fixedSig ou_diagSig ou_fixedH ou_fixedH_diagSig ou_fixedH_fixedSig ou_fixedH_fixedtheta ou_fixedH_fixedtheta_diagSig ou_fixedSig ou_fixedtheta ou_fixedtheta_diagSig ou_fixedtheta_fixedSig ou_logdiagH ou_logdiagH_diagSig ou_logdiagH_fixedSig ou_logdiagH_fixedtheta ou_logdiagH_fixedtheta_diagSig ou_logdiagH_fixedtheta_fixedSig ou_logspdH ou_logspdH_diagSig ou_logspdH_fixedSig ou_logspdH_fixedtheta ou_logspdH_fixedtheta_diagSig ou_logspdH_fixedtheta_fixedSig ou_spdH ou_spdH_diagSig ou_spdH_fixedSig ou_spdH_fixedtheta ou_spdH_fixedtheta_diagSig ou_spdH_fixedtheta_fixedSig ou_symH ou_symH_diagSig ou_symH_fixedSig ou_symH_fixedtheta ou_symH_fixedtheta_diagSig ou_symH_fixedtheta_fixedSig
NULL
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.