Randomized sketches use Gaussian draws and use a deterministic complete basis when the sketch spans a full matrix dimension. Metric orthonormalization verifies the original Gram matrix, corrects residual normalization error, and removes dependent sketch directions without inventing rank through jitter. This fixes seed- and C++-library-dependent component loss on Windows.
gpca_mle() now separates the penalty effect of
reciprocal rescaling (loglik_rescale_delta, exactly zero
with scale_fix = "none") from final refitting and objective
reevaluation (loglik_refit_delta). The reported likelihood
remains evaluated at the returned fit and metrics.
Strictly positive diagonal weights are retained in both forward and inverse metric factors. Previously a small positive weight could contribute a large singular value while its recovered loading was zero. The fix covers GPCA, covariance PCA and PLS operators, including base-matrix diagonal inputs.
Both deflation implementations now separate
rank_rtol from the iteration threshold. All
backends also apply a common final component filter. Empty C++ deflation
results return empty singular-value and variance vectors.
Incomplete eigencore results raise
genpca_solver_nonconvergence. Existing dense fallbacks
handle this error; operator callers without a fallback stop instead of
returning an unchecked fit.
Explicit "clip" repairs remove negative eigenvalues
even within the PSD validation tolerance and report the change, up to
reconstruction roundoff.
geigen_cov() documents its projected equation for
singular metrics and the range constraint on its vectors. Commuting
metrics and covariances need not give the same leading component under
GMD and generalized eigenanalysis.
Rank-cutoff equations render in both PDF and HTML manuals. Local diagnostic artifacts are excluded from source packages.
X or a
metric. New rank_rtol argument in genpca(),
genpca_cov() and the internal solvers: component
j is dropped when d_j <= rank_rtol * d_1
(default 1e-6), for every method. Metric validation and
null-space detection use a separate relative tolerance,
sqrt(.Machine$double.eps). The previous absolute cutoffs
(1e-8 on eigenvalues, 1e-9 on singular values,
a 1e-8 total-variance floor, an absolute
jitter_metric) could change the number of components
returned, or return wrong values from the randomized backend, when
X was rescaled.is_psd(), is_pd(), both
shifted Cholesky probes with relative tolerance).
ensure_spd() always returns a positive definite matrix and
forwards its tolerance; previously a zero or singular matrix could be
returned unchanged.constraints_remedy. The relative asymmetry
||A - A'|| / ||A|| is measured; below 1e-10
the two triangles are averaged, above it the call stops. Previously one
triangle was silently used, so the result depended on which triangle the
caller had filled. The same applies to C and R
in genpca_cov().constraints_remedy = "clip" now guarantees a PSD
result; the previous tolerant fast path could return a matrix with a
small negative eigenvalue.constraints_remedy. Previously the eigen path
rejected what the other backends silently accepted or clamped.method = "eigen" never truncates a metric any more. A
positive definite general metric is factored exactly by Cholesky at any
size; a singular general metric needs a dense eigendecomposition, which
is refused above maxeig rows (default now
5000) with a message naming the alternatives. The previous
behaviour, keeping only the maxeig largest eigenvectors of
the metric before looking at X, could discard the dominant
component entirely (a direction with a small metric eigenvalue and large
data variance). warn_approx is deprecated and ignored.genpca_cov(method = "gmd") requires C to
be PSD within tolerance and no longer clamps negative metric eigenvalues
silently. Its tol argument is deprecated in favor of
rank_rtol and metric_rtol.repair_metric() returns a repaired metric
together with a metric_repair_report (minimum eigenvalue
before and after, Gershgorin bound, applied shift, rank, condition
number).genpca() now defaults to
constraints_remedy = "error": an indefinite metric stops
the fit instead of being silently shifted. Opt into a repair with
constraints_remedy = "ridge", "clip" or
"identity"; every repair that changes the metric emits a
warning of class genpca_metric_repaired whose
report field is the repair_metric()
diagnostic. gpca_mle() inherits the new default.genpls(), genplsc() and
gplssvd_op() gain the same constraints_remedy
argument (default "error"). Previously their metric
operators applied a Gershgorin ridge to any indefinite weight matrix
with no message and no way to opt out.verbose = TRUE is no longer needed to find out that a
metric was replaced by the identity; the warning always fires.gpca_mle()genpca_cov() is now the GMD estimator only
(eigendecomposition of R^{1/2} C R^{1/2}). The generalized
eigenproblem C v = lambda R v, a different estimator, is
the new exported geigen_cov().
genpca_cov(method = "geigen") still works for one release
and forwards with a deprecation warning.gpca_mle() defaults to scale_fix = "none".
The ridge penalty already identifies the overall scale of the learned
metrics, and the previous default "trace" rescaled the
metrics after convergence in a way the penalized objective is not
invariant to, while still reporting the pre-rescale value.
loglik is now always the penalized objective at the
returned metrics; loglik_unpenalized and
loglik_rescale_delta (the change caused by a
"trace"/"det" rescale) are new.method = "spectra" computes the top-k
singular triplets of the metric-whitened data F_M' X F_A
(M = F_M F_M', A = F_A F_A') as an implicit
operator: diagonal, dense Cholesky, sparse (CHOLMOD) Cholesky and eigen
factors (for singular metrics) are all supported, and a dense SVD is
used when the iterative solver does not converge. The C++ Spectra kernel
and the LinkingTo: RSpectra dependency are gone.
gmd_fast_cpp() remains as an alias of the new
gmd_spectra(); its maxit and seed
arguments are ignored.genpls(), genplsc() and
gplssvd_op() accept svd_backend = "eigencore"
(new default); "RSpectra" is accepted as a deprecated
alias..Random.seed.M and column metric
A (genpca()), following Allen, Grosenick &
Taylor (2014).C = X' M X
(genpca_cov()), with eigen and
gmd paths.eigen,
spectra (matrix-free C++ via RSpectra),
randomized, and deflation. The
auto heuristic picks among them.genpls(),
genplsc()) and an operator-level interface
(gplssvd_op()) that avoids materialising whitened
matrices.sfpca()), regularised PLS
(rpls()), and matrix-normal PCA via maximum residual
likelihood (mnpca_mrl()). sfpca() solves each
rank-1 subproblem exactly via C++ coordinate descent in the constraint
form of Allen & Weylandt (2019), deflates implicitly (sparse inputs
never densify), and selects sparsity penalties per component by BIC
along a warm-started lambda path; smoothness weights
default to the scale-free 1 / lambda_max(Omega). The former
uthresh/vthresh quantile heuristics are
deprecated. sfpca() now returns a multivarious
bi_projector (class "sfpca"), so
scores(), components(), sdev(),
and reconstruct() work as they do for
genpca(). The pre-0.1 list fields $d and
$u remain readable but emit a deprecation warning.gpca_mle())."ridge",
"clip", and "identity" remediation
strategies.gpca_mle() now optimizes a single penalized (MAP)
matrix-normal objective with exact block updates (sequential flip-flop
covariance updates; the ridge lambda is part of the
objective), so loglik_path is monotone non-decreasing.
scale_fix is applied once at exit as a joint,
likelihood-preserving rescale (row covariance normalized, factor
absorbed into the column covariance) instead of normalizing both factors
independently during iteration, and the returned fit is
computed with the returned canonicalized metrics. Reported
log-likelihood values now include the penalty term, so they differ
numerically from previous versions.scores() on genpca fits now matches Allen,
Grosenick & Taylor (2014)’s convention
(z_k = X A ov_k = ou_k d_k) and is identical to
project(fit, X) on the training data; previously the two
could disagree.reconstruct.genpca(fit, colind = ) now applies the
inverse pre-processing transform to the selected columns, rather than to
the first length(colind) columns.threshold argument of
genpca(method = "deflation") and the internal SFPCA solver)
are now relative to the scale of the problem rather than absolute, so
results are invariant to rescaling the input data.constraints_remedy = "clip" now performs a real
spectral clip to the PSD cone (previously it did not clip); it requires
a dense eigendecomposition and refuses sparse input larger than 2000
rows/columns, recommending "ridge" instead.genpca(method = "randomized") no longer mutates the
caller’s .Random.seed; the C++ kernel seeds its own stream
from seed_randomized, which now fully controls
reproducibility.scad_a range, strictly positive diagonal) to the
internal C++ coordinate-descent solver used by
sfpca().genpls()’s $ncomp field now reports the
number of components actually extracted, which may be less than the
requested ncomp.