Skip to contents

This document follows a genetic evaluation in the order it actually happens, and at each step it says three things: what the matrices are and what they mean, what the algorithm does with them, and which function runs it. The formulas are the ones the package computes, not the textbook version they descend from; where the implementation makes a choice, the choice is stated.

The data for every example is simulated here, so each result has its truth beside it.

s <- simulate_breeding(n_founders = 60, n_generations = 3,
                       offspring_per_generation = 150, h2 = 0.4,
                       n_markers = 600, seed = 1)
str(s, max.level = 1)
#> List of 4
#>  $ pedigree :'data.frame':   510 obs. of  3 variables:
#>  $ data     :'data.frame':   510 obs. of  3 variables:
#>  $ tbv      : Named num [1:510] -0.396 0.116 -0.528 1.009 0.208 ...
#>   ..- attr(*, "names")= chr [1:510] "F001" "F002" "F003" "F004" ...
#>  $ genotypes:List of 2

1. The model

Every fit in this package is a linear mixed model,

y = X b + Z u + e,     u ~ N(0, D),     e ~ N(0, R)

where y holds the observations, b the fixed effects, u all the random effects stacked, and X and Z their incidence matrices. What separates one model from another is not the algebra but the shape of D and R: an animal model puts the pedigree relationship matrix inside D, a repeatability model adds a second block, a multi-trait model gives R a full covariance between traits, a reaction norm gives each animal a vector of coefficients instead of a scalar. Most of the literature calls that covariance G; this document reserves G for the genomic relationship matrix of section 6 and writes D for the covariance of the random effects throughout.

The package writes all of that in one formula. Fixed terms are bare, cov() marks a fixed covariate, and the marked terms are random: animal(), maternal(), sire() carry a relationship matrix, pe() and random() are independent, rn() is a random regression, indirect() is the associative effect of pen mates.

pe() is worth its full definition, because it is the term the repeatability model is named after. It is the permanent environment of a subject with repeated records: what all the records of that subject share and is NOT additive genetic, so the non-additive genetic effects land there too (Mrode and Pocrnic, 2023, Eqn 5.1). The repeatability itself is (var(animal) + var(pe)) / total, the two share cells of the printed table added together.

The unit of layout is the covariance group, not the term. Two terms that share group = go into ONE covariance matrix with the covariance between them estimated:

Var(u_g) = C_g (x) K_g

where C_g is the small covariance among the terms of the group and K_g is the relationship matrix among their levels; D is the block diagonal of these, one block per group. This is why direct-maternal, the reaction norm and the associative model have no dedicated fitter: they are the same engine with a different incidence and the same penalty.

2. The pedigree: A, F, and A inverse

The additive relationship matrix A holds twice the kinship between every pair of animals. It is never built. What the equations need is its inverse, and Henderson (1976) showed that the inverse has a closed form: for each animal i with Mendelian sampling variance d_i,

A^-1 = sum_i (1/d_i) v_i v_i',    v_i = +1 at i, -1/2 at sire, -1/2 at dam

so A^-1 is built by adding one small outer product per animal, sparse from the start, never through a dense A. The Mendelian variance depends on how much of the parents’ ancestry is already shared:

d_i = 1/2  - 1/4 (F_sire + F_dam)   both parents known
d_i = 3/4  - 1/4 F_known            one parent known
d_i = 1                             neither

F is Wright’s (1922) inbreeding coefficient, computed by the Meuwissen and Luo (1992) trace: for animal i, walk the ancestors in decreasing index order accumulating l_j contributions, and F_i = sum_j l_j^2 d_j - 1. A max-heap over the ancestors is what makes a single pass correct: every descendant of an ancestor has a larger index and has therefore already deposited its share. This trace and the earlier strategy of Quaas (1976) share the point: the diagonal of A comes out of the decomposition while A^-1 is being built, instead of storing A to read it back. Mrode and Pocrnic (2023, ch. 3) carry both rules through a small pedigree by hand, which is where an implementation gets checked against printed numbers.

p <- pedigree(s$pedigree)
head(p, 3)
#> Pedigree with 3 animals, 3 founder(s)
#> Mean F 0, maximum 0, 0 animal(s) with F > 0
#>     id sire dam F
#> 1 F001   NA  NA 0
#> 2 F002   NA  NA 0
#> 3 F003   NA  NA 0
ai <- a_inverse(s$pedigree)      # i, j, x, n: the lower triangle in triplets
c(animals = ai$n, nonzeros = length(ai$x), mean_F = round(mean(p$F), 4))
#>   animals  nonzeros    mean_F 
#>  510.0000 1842.0000    0.0063

Two details in the implementation are worth knowing because they are silent when wrong. A parent that is cited but has no line of its own is an error, not a new founder. And when sire and dam are the same animal, the coefficients are merged BEFORE the outer product, which puts 1/d on that parent’s diagonal; emitting the terms one by one into a half-stored triangle would give 3/4 d^-1 and no error message.

Metafounders. When the base population is not one homogeneous unrelated pool (different lines, different years, animals whose parents are simply unknown), Legarra et al. (2015) replace the unknown parents with metafounders: virtual base animals with a self-relationship gamma that measures how related that base actually was. The implementation reduces to one line: a metafounder row is preloaded with F = gamma - 1 and d = gamma, and because every formula above reads only the parents’ F, the whole generalization propagates through the Mendelian variances, the trace and A^-1 with no special case. gamma must be strictly inside (0, 2), and the two ends are refused for different reasons. The upper one is Mendelian: the offspring of two copies of the same metafounder has d = 1/2 - 1/4 (2 (gamma - 1)) = 1 - gamma/2, which vanishes at gamma = 2, a base pool completely homozygous by descent, where every base animal is the same animal. The lower one is that a base pool needs a positive self-relationship to be a pool at all, and the package’s error message names both. Zero is a limit with a meaning rather than a degeneracy: gamma -> 0 is complete heterozygosity by descent, an unrelated base, and the recursion collapses onto the classical unknown parent of Meuwissen and Luo (1992) (Legarra et al., 2015; Mrode and Pocrnic, 2023, sec. 3.8).

ped_mf <- s$pedigree
ped_mf$sire[ped_mf$sire == "0"] <- "L1"
ped_mf$dam[ped_mf$dam == "0"] <- "L1"
p_mf <- pedigree(ped_mf, metafounders = "L1", gamma = 0.7)
round(mean(p_mf$F[p_mf$id %in% s$pedigree$id]), 4)   # base relatedness now shows up as F
#> [1] 0.3541

Sires and maternal grandsires. A sire model often has only the sire and the maternal grandsire of each animal. Then u_i = u_s / 2 + u_k / 4 + m_i, with the maternal granddam unknown: the grandsire path weighs 1/4, the Mendelian variance is 11/16 - F_s/4 - F_k/16, and the inverse gets (1, -1/2, -1/4) in the rules above (Mrode and Pocrnic, 2023, secs. 3.6 and 3.7). Read as a dam, the grandsire would enter with 1/2, and the A^-1 would still be symmetric and positive definite, so nothing downstream could catch it. The format is therefore declared, with sire_mgs() or pedigree(type = "sire_mgs"), and a third column named like a grandsire column is refused until it is.

3. The mixed model equations

Henderson’s equations (Henderson, 1950) solve for b and u at once. Writing W = [X Z], the package assembles

C_s = W'W + blockdiag_g [ kron(s2e * C_g^-1, K_g^-1) ],     rhs = W'y

and solves C_s sol = rhs. Two conventions matter. The system is scaled by the residual variance (the penalty carries s2e * C_g^-1, not C_g^-1), so the solution is the same BLUP but the matrix is in units of s2e. And inside a term, the column of Z for coefficient a and level l is a * n_levels + l: the level varies fastest. Reversing that convention does not break anything visibly: the model still assembles and converges, and estimates something else.

W'W is accumulated one record at a time. Each record touches a handful of columns and contributes a small clique among them, so the cost is the sum of squared row-lengths, not anything quadratic in the number of columns. The penalty is added afterwards into the same triplet list, and the triplet-to-sparse conversion sums duplicates, which is not a convenience but a requirement: in A^-1 the mate-mate term arrives once per offspring, and a build where the last write wins would silently discard part of the matrix.

The restricted likelihood, from the MME:

-2logL = (n - p) log s2e + log|D| + log|C_s| + (e'e + u'D^-1 u) / s2e

e = y - Xb - Zu are the residuals at the MME solution. At that solution e'e + u'D^-1 u equals y'y - sol'rhs, the form the MME suggest, but the package forms it from the residuals. y'y - sol'rhs subtracts two numbers of the order of n (mean y)^2 and inherits the error of the solution at first order. So a constant added to y, which the intercept absorbs and REML ignores, used to move the -2logL: 1.1e-8 relative at 1000 standard deviations and 1.1e-6 at 1e4. The residual form is stationary in the solution and does not carry the mean.

D is the covariance of the random effects of section 1, and it carries the same s2e scaling as C_s: per group the code accumulates nl log|C_g / s2e| + dim log|K_g|. With weights =, a - sum_i log w_i term joins the expression, the Jacobian of the weighted rescaling, constant in theta, so it moves the value and not the optimum.

The package computes the same quantity a second way, through the dense V = Z D Z' + R, and the tests demand the two agree. They share nothing but the model, so agreement is evidence; a single path checked against itself would be none.

d <- s$data
ev <- eval_internal(y ~ cg + animal(id), d, s$pedigree, theta = c(0.4, 0.6))
c(mme = ev$neg2logl, dense_V = ev$neg2logl_V, difference = abs(ev$neg2logl - ev$neg2logl_V))
#>          mme      dense_V   difference 
#> 4.681071e+02 4.681071e+02 1.597300e-11

4. Solving: sparse Cholesky and the selected inverse

C_s is factored as L L' by an up-looking sparse Cholesky, preceded by a minimum degree ordering (George and Liu, 1989) that reduces fill-in and by a symbolic analysis that computes the pattern of L once. On a clique, minimum degree would cost the cube of its size rather than scale with the nonzeros, so a node whose degree passes 80 percent of the nodes still alive leaves the graph and goes to the end of the order; at the start, a node is set aside when its degree passes both 10 sqrt(n), the dense-row rule of AMD (Amestoy, Davis and Duff, 1996), and 0.8 of the 99th percentile of the degrees, provided the set aside is at most a quarter of the graph. Ordering never changes a result, only speed and fill. The symbolic factorization is cached across REML iterations, which is sound only because the pattern of C_s does not depend on the variance components: the assembly pushes the same slots every time, explicit zeros included.

What the dense nodes leave behind is a completely full block at the end of L: k columns that cost about k^3 / 3 flops per factorization, which dominates the fit once k is large. The fit reports it as fit$dense_block = c(dense = k, columns = n); in a single step without APY, k is at least the number of genotyped animals. That block is factored by scalar C++, not by BLAS, and nothing in the package runs in parallel.

Prediction error variances need the diagonal of C_s^-1, and the full inverse of a sparse matrix is dense, and for a pedigree of any size unaffordable. The identity of Takahashi, Fagan and Chin (1973) gives exactly the elements of the inverse that sit on the pattern of L, which is all the diagonal needs:

ch <- sparse_chol(ai)
si <- selected_inverse(ai)
c(logdet = round(ch$logdet, 4), dense_block = ch$dense_block)
#>      logdet dense_block 
#>    312.6306     69.0000

A position outside the pattern is not zero; it is unknown. The function returns only what it computed.

5. Estimating the variance components

REML maximizes the likelihood of error contrasts instead of the likelihood of y. What that removes is not a bias in b (the fixed effects come out as the same GLS solution under either criterion) but the downward bias maximum likelihood carries IN THE VARIANCE COMPONENTS, whose cause is that those fixed effects were estimated from the same records. Mrode and Pocrnic (2023, ch. 17) write the restricted likelihood as three terms: a weighted sum of squares of the residuals, log|V|, and log|X'V^-1X|, the last of them a penalty for having estimated b and the one maximum likelihood leaves out (Patterson and Thompson, 1971). The n - p in the exponent of section 3 is that same correction seen from the side of the mixed model equations.

The package maximizes that restricted likelihood by average information REML (Gilmour, Thompson and Cullis, 1995; the animal-breeding derivation is in Mrode and Pocrnic, 2023, ch. 17), which needs three ingredients per iteration.

The score. For a covariance group with matrix C_g, define the quadratic of the solution and the trace from the selected inverse,

Q[a,b] = u_a' K^-1 u_b
T[a,b] = s2e * tr( K^-1 [C_s^-1]_{a,b} )
M_g    = nl * C_g^-1 - C_g^-1 (Q + T) C_g^-1

and the derivative of -2logL with respect to the element (i,j) of C_g is M_g[i,i] on the diagonal and M_g[i,j] + M_g[j,i] off it, because a symmetric element appears twice. For the residual,

d(-2logL)/ds2e = (n - p - q + trpen)/s2e - e'e/s2e^2

where q is the number of random columns and trpen the trace of the penalty against C_s^-1. The - q + trpen is not decoration: the random effects absorb degrees of freedom too, and a version without it still converges, to the wrong place, reporting convergence. That is the worst failure mode available, which is why the score is checked against central finite differences on every parameter.

The step. The average information matrix is the average of the observed and expected information, cheap because it needs no second derivatives, and the step is theta <- theta - (AI + lambda diag(AI))^-1 score. This lambda is the Levenberg-Marquardt damping (Levenberg, 1944; Marquardt, 1963), and it is a third meaning of the letter: not the s2e/s2a ratio that scales K^-1 inside the mixed model equations of section 3, and not the ridge that apy_inverse() calls lambda. The damping starts at 1e-3, multiplies by ten whenever a candidate is rejected (inadmissible covariance, singular damped matrix, or a likelihood that got worse) and divides by ten when one is accepted. Before the AI loop, a few EM rounds run: EM (Dempster, Laird and Rubin, 1977) is monotone and stays inside the cone, so it costs little and lands the search in the right basin.

Stopping. Convergence is judged on the relative change of the components, sqrt(sum(dtheta^2)/sum(theta^2)) < tol, never on an absolute threshold on the score, whose norm grows with the number of records. A small step is not an optimum, though, so converged also requires the Newton decrement g' AI^-1 g, about twice the remaining gap in -2logL, to fall under 2e-4, components resting at a boundary excluded (in model_mt() and model_ar1(), after checks of the Karush-Kuhn-Tucker condition and of the boundary face on the likelihood, a second walk from inside the cone and a polish along the face); it is reported as newton_dec. At maxiter with the decrement already under tolerance the likelihood is flat along some direction, and the fit declares convergence and says so. Standard errors come from the inverse of the AI matrix, SE_k = sqrt(2 [AI^-1]_kk). When AI is singular at the end (an eigenvalue below 1e-8 of the largest; in model_mt() and model_ar1() the matrix is first scaled by the standard deviation of each trait, so the verdict does not depend on the unit of a trait), the fit names the components involved and their standard errors are NaN: the data do not separate them, the reported point depends on start=, and only combinations are estimable (for the indirect-effect model, the identifiability question of Cantet and Cappa, 2008).

fit <- model(y ~ cg + animal(id), d, s$pedigree)
fit
#> AI-REML fit of 'y'
#>   converged in 7 iteration(s), relDelta 1.83e-09, Newton decrement 7.26e-19, 0.008 s
#>   -2logL 468.1000386
#>   510 record(s), 513 column(s) in the equations
#>   fixed column(s) removed for linear dependence: cg=g2
#> 
#>      component estimate std_error  share correlation
#>    var(animal) 0.400864 0.0956138 0.4024            
#>  var(residual) 0.595274 0.0688334 0.5976            
#> 
#> fixed effects (implicit intercept; dropped columns are zero; compare by contrast):
#>   intercept       cg=g1       cg=g3 
#> 10.00302128  0.03647810  0.00221236

Two rules protect the result. A covariance that stops being positive-definite ends the fit rather than being nudged back, and the starting point comes from var(y) unless start= gives one, so no run inherits a neighbour’s answer.

6. Genomics: from markers to H

G. VanRaden (2008) builds the genomic relationship matrix from centred marker dosages. With p_j the allele frequency at marker j and Z = M - 2p,

G = Z Z' / (2 sum_j p_j (1 - p_j))

Missing genotypes are imputed with the marker mean and never with zero: zero is a valid genotype, and confusing the two changes both the frequencies and every element of G with no error to notice. Markers that are fixed or entirely missing are dropped and counted.

A22, and the trap. The single step needs the pedigree relationships among the genotyped animals, A22. That is the inverse of the genotyped block of the inverse, not the genotyped block of A^-1, two matrices of the same shape, both symmetric, only one correct. The package computes it by a sparse Schur complement,

A22^-1 = B22 - B21 B11^-1 B12

where B11 is the non-genotyped block. B11^-1 is never formed: one sparse factorization and one triangular solve per genotyped animal do the same work.

H. Genomic and pedigree information describe the same animals on two different scales, so G is first brought to the scale of A22 by an affine adjustment that matches both the mean diagonal and the mean off-diagonal, then blended:

b = (mean_diag(A22) - mean_off(A22)) / (mean_diag(G) - mean_off(G))
a = mean_off(A22) - b * mean_off(G)
G* = (1 - w) (a + b G) + w A22

The blend w (5 percent by default) keeps G* invertible and admits that markers are not the whole truth. Then the single-step inverse of Aguilar et al. (2010) and Christensen and Lund (2010) says the whole relationship matrix H has an inverse that differs from A^-1 only in the genotyped block:

H^-1 = A^-1 + [ 0        0            ]
              [ 0   G*^-1 - A22^-1    ]

This is the identity that makes the single step practical: nothing dense is needed for the non-genotyped animals, and the correction is a small block added to a sparse matrix. It is also self-checking: with blend = 1, G* becomes A22 exactly, the correction block vanishes, and H^-1 must equal A^-1 through the whole fit. That collapse is one of the package’s tests. Mrode and Pocrnic (2023, ch. 12) work the same identity through a small herd, number by number.

gen <- list(ids = s$genotypes$ids, m = s$genotypes$m)
f_ss <- model(y ~ cg + animal(id), d, s$pedigree, genotypes = gen)
f_id <- model(y ~ cg + animal(id), d, s$pedigree, genotypes = gen, blend = 1)
c(single_step = f_ss$neg2logl, blend_one = f_id$neg2logl, pedigree = fit$neg2logl)
#> single_step   blend_one    pedigree 
#>    480.9321    468.1000    468.1000

Inverting G* without paying the cube. Two approximations are available, and they are the same idea at different granularity. APY (Misztal, Legarra and Aguilar, 2014; Mrode and Pocrnic, 2023, sec. 12.4) splits the genotyped into a core and the rest; every non-core animal is regressed on the core, and what is left is a scalar Mendelian residual:

P   = Gcc^-1 Gcn
m_j = g_jj - g_jc P_j
G_APY^-1 = [ Gcc^-1 + P Mnn^-1 P'   -P Mnn^-1 ]
           [ -Mnn^-1 P'              Mnn^-1   ]

The Vecchia recursion (Vecchia, 1988) generalizes it: each animal conditions on its own k nearest relatives among the previous ones rather than on one shared core. Henderson’s A^-1 is this same recursion with the parents as the conditioning set, exact there, because a pedigree is Markovian. Schafer, Katzfuss and Owhadi (2021) prove the factor is optimal in Kullback-Leibler divergence given the pattern, and that enlarging the sets never hurts.

An honest limit: both approximations cut arithmetic, not memory. G*^-1 is a dense matrix of the genotyped animals on all three paths. What H^-1 keeps of it is only the nonzeros, so with APY the dense block of the factor is at least the core size plus one rather than the whole genotyped set.

How large a core? The dimensionality of the genomic information bounds it: Pocrnic et al. (2016) take as many animals as eigenvalues of G explain 98% of its trace, drawn at random. apy_core_select() applies that rule, reports the cost of one factorization with the core against the exact one, and takes size = and include = (proven sires, say) to override the draw; apy_core = "auto" calls it inside the fit, which then keeps the core in fit$apy. Choose the core once and pass it to every fit.

gid <- gen$ids
f_apy <- model(y ~ cg + animal(id), d, s$pedigree, genotypes = gen,
               apy_core = gid[seq(1, length(gid), by = 3)])
f_vec <- model(y ~ cg + animal(id), d, s$pedigree, genotypes = gen, vecchia_k = 60)
round(c(apy = cor(ebv(f_ss, "animal"), ebv(f_apy, "animal")),
        vecchia = cor(ebv(f_ss, "animal"), ebv(f_vec, "animal"))), 4)
#>     apy vecchia 
#>  0.9753  0.9675

Markers as equations. The other route never forms G at all. Liu et al. (2014) write the equivalent model with the marker effects as unknowns, u_g = Z g + p_g, and the joint precision turns the Schur term into blocks of A^-1, so most of the system is the same sparse matrix the pedigree already gives. snp_blup() solves it by preconditioned conjugate gradients, which need only matrix-vector products, and every application of A22^-1 uses the matrix-free identity of Masuda et al. (2017):

A22^-1 v = A^22 v - A^21 (A^11)^-1 A^12 v

one sparse factorization of the non-genotyped block, one triangular solve per iteration. Nothing dense is ever built. The variance components are given rather than estimated: this is the solver of routine evaluation, where components come from a periodic REML run.

7. What the formula can say

With the covariance group as the unit, several classical models are incidences rather than fitters.

Direct-maternal (Willham, 1972): the animal’s own genes and its dam’s maternal ability in one 2x2 matrix, with the direct-maternal covariance, usually negative, and the number the model exists for.

Reaction norm (Kirkpatrick, Lofsvold and Bulmer, 1990; in animal models, Schaeffer and Dekkers, 1994): each animal gets a vector of coefficients on an environmental gradient through rn(id, base = ...), with legendre() building the orthogonal basis. The genetic variance becomes a function of the environment, which h2_curve() reads, evaluating every random regression at the point, a permanent environment on the same basis included; h2() refuses a reaction norm, because its heritability is a curve and not a number.

Indirect genetic effects (Griffing, 1967; Muir and Schinckel, 2002; Bijma, Muir and Van Arendonk, 2007): the incidence of a record marks the animal’s PEN MATES, so a phenotype carries its own genes plus the associative effects of those it lives with. Direct and indirect share a group, so the fit returns var(animal), var(indirect) and the covariance between them, and the SIGN of that covariance is what separates heritable competition from heritable co-operation.

What decides the response is not that covariance on its own but the total breeding value, into which the group size enters twice:

TBV    = A_D + (n - 1) A_S
s2_TBV = s2_AD + 2 (n - 1) s_ADS + (n - 1)^2 s2_AS
s2_P   = s2_AD + (n - 1) [1 + (n - 2) r] s2_AS + 2 (n - 1) r s_ADS + (the other components)
T2     = s2_TBV / s2_P

Because (n - 1) multiplies the covariance and (n - 1)^2 the associative variance, the group size sets both the magnitude of the response and, under strong heritable competition, its direction: the response need not follow the selection pressure (Bijma, Muir and Van Arendonk, 2007; Mrode and Pocrnic, 2023, ch. 9). The phenotypic variance of a record carries the indirect effects of its n - 1 mates, with r the relationship between them, so heritability here is not var(animal) over the plain sum of the components, and the share column of the printed fit is left blank. h2(fit, n = , r = ) divides by s2_P, and t2(fit, n, r) returns s2_TBV, s2_P, T2 and the direct heritability with delta-method standard errors.

The book fixes n = 3; real pens are unequal, and with a coefficient of 1 per mate the social sum grows with n_i - 1. indirect(id, pen = "pen", dilution = d) scales every mate’s entry to (n_i - 1)^(-d): d = 0 (the default) is the book’s sum, d = 1 the mate mean, and the regime in between is the dilution of Bijma (2010), chosen empirically by fitting a small grid of d and comparing -2logL, which stays comparable because only the incidence changes. Under dilution the (n - 1) of the TBV and of the covariance term of s2_P becomes (n - 1)^(1 - d), and the one that multiplies s2_AS in s2_P becomes (n - 1)^(1 - 2d) (Bijma, 2010); h2() and t2() read d from the term. The residual inherits the same heterogeneity, var(e_i) = s2_ED + (n_i - 1)^(1 - 2d) s2_ES, the same d diluting the social environmental deviation; indirect_residual() estimates the ratio s2_ES / s2_ED by profile REML over weighted fits, and its documentation says which of the two the data actually pins. associative_matrix(dilution = d) writes the same residual as an exact covariance for a kernel() term.

Whether the three genetic components can be told apart is a property of the design, not of the fitter (Cantet and Cappa, 2008). With pens of one size built from two full-sib families, the data identify only two combinations, the TBV variance and s2_AD - 2 s_ADS + s2_AS: fits from different starts land on different components with the same -2logL, and the fit says SINGULAR and names them. Pens of different sizes, or relatives spread across pens, separate them.

Multi-trait: cbind() on the left, a full residual covariance between traits, and missingness handled by the pattern of each record rather than by dropping it.

AR(1)/CAR(1) residual (Wade and Quaas, 1993): for repeated measures along time, the residual correlation decays as rho^|dt|. The inverse of that correlation matrix is tridiagonal (the classical Markov result), so the model costs no more than the iid one, and rho comes out with its standard error. On a grid, rho^k is the correlation of records k steps apart and rho may be negative; it is negative only between records an odd number of steps apart. The step is the largest number that divides every gap (Pinheiro and Bates, 2000, corAR1; the AR1 of ASReml for equally spaced levels). Off a grid the correlation is rho^dt with rho in [0, 1) (corCAR1; EXP of ASReml), since a continuous-time Markov process has no negative correlation. A few records whose time is off the grid of the others can therefore turn a negative correlation into a wrong positive or null one; model_ar1() checks for that after the fit and stops naming those records.

The textbook treatment of these models is Mrode and Pocrnic (2023): the repeatability model in ch. 5, maternal traits in ch. 8, social interaction in ch. 9, random regression in ch. 10.

model(weight ~ cg + animal(id, group = "g") + maternal(dam, group = "g"), d, ped)
model(y ~ cg + rn(id, base = c("phi0", "phi1")) + pe(id), d2, ped)
model(y ~ cg + animal(id, group = "g") + indirect(id, pen = "pen", group = "g"), d, ped)
model_mt(cbind(t1, t2) ~ cg + animal(id), d, ped)
model_ar1(y ~ cg + animal(id) + pe(id), d, ped, subject = "id", time = "day")

8. Reading the evaluation

The solution of the MME gives the breeding values directly. Their reliability comes from the prediction error variance, which is where the selected inverse pays off, and the accuracy of an animal’s EBV is

acc = sqrt( 1 - PEV / ((1 + F) sigma2_a) )

with the inbreeding coefficient in the denominator because an inbred animal has more additive variance to predict: var(u_i) = a_ii s2a = (1 + F_i) s2a (Mrode and Pocrnic, 2023, ch. 3). The identity underneath, PEV = var(a - a^) = C22 s2e, is Henderson (1975).

accuracy() divides by the prior the animal actually has. For a pedigree animal that is 1 + F. For a genotyped animal in a single step it is the diagonal of H, which in that block is the diagonal of G*, and that is not 1 + F_ped: on the population simulated at the top of this vignette, every animal genotyped, the two diagonals differ by up to 0.18 and an individual accuracy by up to 0.067. The herd mean does not move; the individual does. A fit with genotypes= carries the diagonal of G*, and accuracy() uses it.

Heritability is read off theta, and both halves of the ratio have to be picked rather than assumed. theta is a NAMED vector, and its first entry is the animal variance only when the formula happens to put that term first: random(lot) + animal(id) puts var(random) there. It also carries the rho(residual) of model_ar1(), a correlation that belongs in no variance denominator. The covariances of a group = are another matter. A direct-maternal covariance is part of the phenotypic variance of a record, with coefficient 1 (Willham, 1972); in the associative model the coefficients are those of s2_P in section 7, which depend on the pen size. h2() takes the numerator by name and divides by the phenotypic variance of the trait, covariances included, and the share column of the printed fit uses the same denominator, left blank where that denominator needs the pen size.

The fixed effects come with the same solution vector: fit$b, named term=level, also reachable as coef(fit, effects = "fixed"). The parametrization is the implicit intercept, with linearly dependent columns dropped (dropped_x) and a dropped level holding the zero solution, while the book’s examples zero one level per factor and fit no intercept. The two conventions agree only on CONTRASTS, differences between levels of the same factor, never on the raw values; contrasts are invariant to the parametrization, individual entries are not.

e <- ebv(fit)
a <- accuracy(fit, s$pedigree)
round(c(cor_with_truth = cor(e[names(s$tbv)], s$tbv),
        mean_accuracy = mean(a), h2 = h2(fit)), 3)
#> cor_with_truth  mean_accuracy             h2 
#>          0.719          0.736          0.402

From there the decisions: selection_index() combines traits with economic weights, and rank_drift() answers what an updated evaluation actually changed.

9. The Bayesian alternative

The same models can be sampled instead of maximized. gibbs() draws all locations in one block through the same sparse Cholesky, then draws each covariance from its conjugate inverse-Wishart and the residual from a scaled inverse chi-square. The prior is a choice, prior =: "jeffreys", the default, is p(s2) ~ 1/s2; "flat" is uniform on the variance; "uniform_sd" is uniform on the standard deviation, the one Gelman (2006) recommends for a variance with few levels; and c(df =, scale =) is the proper scaled inverse chi-square of the BLUPF90 Gibbs programs. The improper ones can leave the posterior of a weakly identified component with spurious mass near zero (Hobert and Casella, 1996). For a covariance group the default is worse: |C|^-(d+1)/2 grows without bound as C nears a singular matrix, so the posterior has infinite mass at a correlation of +-1 even when the data identify every component, and a chain that gets there stays. With direct and indirect effects in one group on pens of 2 to 8 (1600 records, 30 data sets) that happened in 4 of 30 under "jeffreys" and in none under "flat", whose posterior means landed beside REML; gibbs() warns when a chain sits there. On a Gaussian trait the flat prior or a proper one avoids that boundary; with family = "probit" only a proper one does, because the binary likelihood stays positive as the variances grow and the flat prior leaves that posterior improper too. The chain also has no SINGULAR verdict of its own, so a design that only the likelihood fails to identify runs without one (pens all of 4 holding two full sibs from each of two families, or records on unrelated founders only: the message of the chain was empty in 29 and in 30 of 30 data sets, where model() on the same formula says SINGULAR in all 60). Where REML gives a point and a standard error from the information matrix, the sampler gives the whole posterior, worth the cost when a component sits near a boundary, where the normal approximation behind the standard error is least trustworthy. summary() of the chain gives the posterior quantiles and the heritability computed draw by draw. The sampler goes back to Geman and Geman (1984); Wang, Rutledge and Gianola (1993, 1994) brought it to variance components in animal models, and Mrode and Pocrnic (2023, ch. 18) set out the same sampler and the conjugate draws it uses. With family = "probit" it samples a binary trait on its liability, each record’s liability drawn from a normal truncated at the threshold and the residual fixed at 1 (Albert and Chib, 1993; Sorensen et al., 1995), which avoids the Laplace approximation. It does not rescue every design: with indirect() and the components estimated on binary records (3200 records, 30 data sets) the chains reached a bulk ESS of 400 in none, and the Laplace minimum sat at |r| > 0.99 in 29, where REML on the liability itself recovers the components.

g <- gibbs(y ~ cg + animal(id), d, s$pedigree, n_iter = 2000, burnin = 400, thin = 4)
data.frame(component = colnames(g$samples),
           posterior_mean = round(unname(g$mean), 3),
           reml = round(unname(fit$theta), 3))
#>       component posterior_mean  reml
#> 1   var(animal)          0.411 0.401
#> 2 var(residual)          0.593 0.595

10. A declared covariance: dominance, epistasis, breed origin

Sections 2 and 6 derived the covariance of a random term from the pedigree or from the markers. kernel(id, K = ) removes the derivation: the term’s covariance matrix is DECLARED, symmetric positive-definite, its rownames naming the levels, and every animal in K gets an equation whether or not it has a record. Nothing else changes (the same kron(C^-1, K^-1) penalty, the same score, the same selected inverse), so any model whose only novelty is the covariance structure is a constructor plus a marker, not a new fitter (Mrode and Pocrnic, 2023, chs 13-14).

The constructors cover the classical cases. dominance_matrix() builds Cockerham’s (1954) D from the pedigree, d_xy = 0.25 (a_sf a_dm + a_sd a_fm) with diagonal 1, the covariance of the dominance deviations, fitted BESIDE the additive term so the two partition the genetic variance. The genomic versions are g_dominance() (Vitezica, Varona and Legarra, 2013) and g_epistasis(), the Hadamard square of G with its diagonal averaged to one; genomic_inbreeding() reads f = 1 - het/N per animal, and fitted as a cov() its coefficient in fit$b is the inbreeding depression.

For a multibreed pedigree, partial_a() splits A by breed origin (Garcia-Cortes and Toro, 2006): one partial matrix per founder breed and one per segregating pair, built by the tabular recursion with the breed contribution on the diagonal. A purebred contributes nothing to the other breed’s matrix, and its row there is entirely zero, which kernel() reads as “no equation for this level, keep its records”; so each matrix enters as its own term and the breed-specific solutions sum to the combined breeding value.

D <- dominance_matrix(s$pedigree)
f10 <- model(y ~ cg + animal(id) + kernel(id, K = D), d, s$pedigree)
round(f10$theta, 3)
#>   var(animal)   var(kernel) var(residual) 
#>         0.400         0.434         0.164

Read that split with care. This data has ONE record per animal and was simulated with no dominance, and D has a unit diagonal, so var(kernel) and var(residual) are separated only by the sib covariances, a weakly identified partition, and the likelihood is nearly flat between them. The estimate above is one point on that plateau, not a discovery of dominance. The hands-on vignette plants a real dominance signal on repeated records, where the partition is estimable and the fit recovers it.

11. The threshold model: a categorical trait on a liability

A calving score has ordered categories, not a variance. The threshold model puts a Gaussian LIABILITY l = Xb + Zu + e underneath and maps it to the observed category through thresholds: y = j when t_{j-1} < l <= t_j. Only signs and orderings of l are observable, so the residual is FIXED at 1 (probit identifiability) and the thresholds replace the intercept. model_threshold() fits it by Fisher scoring on the Gianola and Foulley (1983) system, with the variance components on the liability scale GIVEN via start=: the Gaussian REML of section 5 has no likelihood for a categorical trait, and the package says so rather than pretending. With estimate = TRUE the components are estimated instead, by approximate marginal maximum likelihood: the locations are integrated by the Laplace approximation around the mode, and the estimate is the MINIMUM of that Laplace -2logL, the number the fit reports, with standard errors from its numerical Hessian. (The EM step of Foulley, Im, Gianola and Hoeschele, 1987, s2 = (u' K^-1 u + tr(K^-1 C^uu)) / q, was the estimator before; its fixed point is not that minimum, because the step ignores how the weights move with s2.) Its known weakness is a binary trait with few records per level of the random effect, where the Laplace approximation underestimates the variance (Tempelman, 1998); on 80 sires with 50 daughters each and a planted variance of 0.15 it averaged 0.161 (SE 0.007) over 10 replicates, and gibbs(family = "probit") avoids that approximation. cbind(quant, binary) fits the joint quantitative-binary analysis of Foulley, Gianola and Thompson (1983), with the PEV of u1 and of the ranking value u2, and predict() the probability of Eqn 15.25; Mrode and Pocrnic (2023, ch. 15) carry both through worked examples, which are the package’s gates.

The scale matters when comparing heritabilities: a 0/1 trait analysed linearly reports an OBSERVED-scale h2, related to the liability one by the Dempster and Lerner (1950) factor z^2 / (p(1-p)) at incidence p.

d11 <- d
d11$cat <- as.integer(cut(d11$y, quantile(d11$y, c(0, 0.6, 0.85, 1)),
                          include.lowest = TRUE))
f11 <- model_threshold(cat ~ cg + animal(id), d11, s$pedigree, start = 0.35,
                       verbose = FALSE)
round(f11$thresholds, 3)
#>    t1    t2 
#> 0.249 1.165
round(h2_observed(0.35 / 1.35, 0.4), 3)    # what that liability h2 shows on a 0/1 scale
#> [1] 0.161

12. Time until failure

Longevity is a time, and part of it is unobserved: a cow still alive at culling day has a LOWER BOUND on her length of productive life, not a missing value. The Weibull frailty model (Kachman, 1999; Mrode and Pocrnic, 2023, ch. 16) writes the hazard as h(t) = h0(t) exp(x'b + z'u) with a Weibull baseline; a complete record contributes its density, a censored one its survival S(t), and that difference is where the censoring information lives: model_survival() therefore refuses to run without censor=. Solutions are LOG RELATIVE RISKS (exp() gives the relative risk of failing), the Weibull rho is estimated by Newton alongside the effects, and the frailty variance comes from the minimum of the Laplace -2logL of the marginal likelihood (one or several components, indirect() included), or is given, as the book does.

A covariate that changes during a life is constant by pieces, and each piece is an elementary record (entry, stop] of the same subject, with the failure only on the last piece. The cumulative hazard of a piece is Lambda0(stop) - Lambda0(entry), so a life split into pieces with the same covariates has exactly the likelihood of the unsplit record. The pieces are checked: no overlap, the event only at the end, the frailty constant within the subject, and gaps refused unless gaps = "allow". A first entry above zero is left truncation, accepted and flagged, because with a frailty its conditional likelihood is naive.

cows <- data.frame(
  cow  = as.character(8:19),
  herd = as.character(c(1,1,1,1,1,1,2,2,2,2,2,2)),
  ysp  = as.character(c(3,4,1,2,3,1,4,1,2,3,4,2)),
  code = c(0,1,0,1,1,1,1,1,0,1,0,1),
  lpl  = c(40,47,22,28,50,33,49,29,23,37,35,30))
peds <- data.frame(
  animal = as.character(1:19),
  sire = c(rep("0", 7), "1","1","4","4","5","5","1","1","5","5","4","4"),
  dam  = c(rep("0", 7), "2","3","2","9","3","8","6","7","14","6","7","3"))
f12 <- model_survival(lpl ~ herd + ysp + animal(cow), cows, peds, censor = "code",
                      rho = 1, lambda = 1, sigma2 = 0.4, verbose = FALSE)
round(coef(f12, "fixed"), 3)   # Example 16.1: the p.291 solutions of the book
#> herd=2  ysp=2  ysp=3  ysp=4 
#> -1.631 -2.346 -3.149 -2.982

References

The complete list is in the package README. The works this walkthrough leans on directly: Henderson (1950) for the MME; Henderson (1976) for A^-1; Wright (1922) and Meuwissen and Luo (1992) for inbreeding; Patterson and Thompson (1971) and Gilmour, Thompson and Cullis (1995) for REML and average information; Legarra et al. (2015) for metafounders; VanRaden (2008), Aguilar et al. (2010) and Christensen and Lund (2010) for the single step; Misztal, Legarra and Aguilar (2014), Pocrnic et al. (2016) and Schafer, Katzfuss and Owhadi (2021) for the approximate inverses; Liu et al. (2014), Masuda et al. (2017) and Vandenplas et al. (2018) for markers as equations; George and Liu (1989) and Amestoy, Davis and Duff (1996) for the ordering; Takahashi, Fagan and Chin (1973) for the selected inverse; Willham (1972), Kirkpatrick et al. (1990), Schaeffer and Dekkers (1994), Griffing (1967), Muir and Schinckel (2002), Bijma, Muir and Van Arendonk (2007), Bijma (2010), Cantet and Cappa (2008) and Wade and Quaas (1993) for the models of section 7; Geman and Geman (1984) and Wang, Rutledge and Gianola (1993, 1994) for the sampler, Gelman (2006) and Hobert and Casella (1996) for its priors, and Albert and Chib (1993) and Sorensen et al. (1995) for its probit mode; Cockerham (1954), Vitezica et al. (2013) and Garcia-Cortes and Toro (2006) for the declared covariances of section 10; Gianola and Foulley (1983), Foulley, Gianola and Thompson (1983), Foulley, Im, Gianola and Hoeschele (1987), Tempelman (1998) and Dempster and Lerner (1950) for the threshold model; Kachman (1999) for survival. Mrode and Pocrnic (2023) is the textbook behind the whole sequence, chapter by chapter.