Package 'Riemann'

Title: Learning with Data on Riemannian Manifolds
Description: We provide a variety of algorithms for manifold-valued data, including Fréchet summaries, hypothesis testing, clustering, visualization, and other learning tasks. See Bhattacharya and Bhattacharya (2012) <doi:10.1017/CBO9781139094764> for general exposition to statistics on manifolds.
Authors: Kisung You [aut, cre] (ORCID: <https://orcid.org/0000-0002-8584-459X>)
Maintainer: Kisung You <[email protected]>
License: GPL-3
Version: 0.2.0
Built: 2026-10-02 19:42:13 UTC
Source: https://github.com/kisungyou/riemann

Help Index


Riemann package

Description

We provide a variety of algorithms for manifold-valued data, including Frechet summaries, hypothesis testing, clustering, visualization, and other learning tasks.

License

Author-owned software in the September 21, 2026 revision of version 0.2.0 is licensed under the GNU General Public License, version 3 (GPL-3). Earlier MIT releases and preserved archives retain their original licenses. Third-party notices are in the installed ‘COPYRIGHTS’ file. Dataset attribution and any data-specific terms are separate; consult the dataset help pages. The separate manuscript and replication workspace has its own license notices.

Author(s)

Maintainer: Kisung You [email protected] (ORCID)

Authors:

See Also

Useful links:


Angular Central Gaussian Distribution

Description

For a hypersphere Sp−1\mathcal{S}^{p-1} in Rp\mathbf{R}^p, Angular Central Gaussian (ACG) distribution ACGp(A)ACG_p (A) is defined via a density

f(x∣A)=∣A∣−1/2(x⊤A−1x)−p/2f(x\vert A) = |A|^{-1/2} (x^\top A^{-1} x)^{-p/2}

with respect to the uniform measure on Sp−1\mathcal{S}^{p-1} and AA is a symmetric positive-definite matrix. Since f(x∣A)=f(−x∣A)f(x\vert A) = f(-x\vert A), it can also be used as an axial distribution on real projective space, which is unit sphere modulo {+1,−1}\lbrace{+1,-1\rbrace}. One constraint we follow is that f(x∣A)=f(x∣cA)f(x\vert A) = f(x\vert cA) for c>0c > 0 in that we use a normalized version for numerical stability by restricting tr(A)=ptr(A)=p.

Usage

dacg(datalist, A, log = FALSE)

racg(n, A)

mle.acg(datalist, ...)

Arguments

datalist

a list of length-pp unit-norm vectors.

A

a (p×p)(p\times p) symmetric positive-definite matrix.

log

logical; return log densities when TRUE.

n

a positive integer number of samples.

...

extra parameters for computations, including

maxiter

maximum number of iterations to be run (default:50).

eps

tolerance level for stopping criterion (default: 1e-5).

Details

The reference measure is the uniform probability measure (total mass one), so the density equals one for the identity parameter. Density evaluation uses Cholesky factors and log determinants; log=TRUE avoids ordinary density overflow. The shape is normalized to trace pp.

The fixed-point estimate carries convergence, iteration, step, and negative mean-log-likelihood attributes. Failed convergence warns. Existence and uniqueness require sufficient dispersion across all proper linear subspaces; full observed span alone does not establish these statistical conditions. Degenerate updates error, and the implementation does not claim a global existence or uniqueness certificate.

Value

dacg gives a vector of evaluated densities given samples. racg generates unit-norm vectors in Rp\mathbf{R}^p wrapped in a list. mle.acg estimates the SPD matrix AA.

References

Tyler DE (1987). “Statistical analysis for the angular central Gaussian distribution on the sphere.” Biometrika, 74(3), 579–589. ISSN 0006-3444, 1464-3510.

Mardia KV, Jupp PE (eds.) (1999). Directional Statistics, Wiley Series in Probability and Statistics. John Wiley and Sons, Inc., Hoboken, NJ, USA. ISBN 978-0-470-31697-9 978-0-471-95333-3.

Examples

# -------------------------------------------------------------------
#          Example with Angular Central Gaussian Distribution
#
# Given a fixed A, generate samples and estimate A via ML.
# -------------------------------------------------------------------
## GENERATE AND MLE in R^5
#  Generate data
Atrue = diag(5)          # true SPD matrix
sam1  = racg(50,  Atrue) # random samples
sam2  = racg(100, Atrue)

#  MLE
Amle1 = mle.acg(sam1)
Amle2 = mle.acg(sam2)

#  Visualize
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,3), pty="s")
image(Atrue[,5:1], axes=FALSE, main="true SPD")
image(Amle1[,5:1], axes=FALSE, main="MLE with n=50")
image(Amle2[,5:1], axes=FALSE, main="MLE with n=100")
par(opar)

Data : Populated Cities in the U.S.

Description

As of January 2006, there are 60 cities in the contiguous U.S. with population size larger than 300000300000. We extracted information of the cities from the data delivered by maps package. Variables coord and cartesian are two identical representations of locations, which can be mutually converted by sphere.convert.

Usage

data(cities)

Format

a named list containing

names

a length-6060 vector of city names.

coord

a (60×2)(60\times 2) matrix of latitude and longitude.

cartesian

a (60×3)(60\times 3) matrix of cartesian coordinates on the unit sphere.

population

a length-6060 vector of cities' populations.

See Also

wrap.sphere

Examples

## LOAD THE DATA AND WRAP AS RIEMOBJ
data(cities)
myriem = wrap.sphere(cities$cartesian)

## COMPUTE INTRINSIC/EXTRINSIC MEANS
intmean = as.vector(riem.mean(myriem, geometry="intrinsic")$mean)
extmean = as.vector(riem.mean(myriem, geometry="extrinsic")$mean)

## CONVERT TO GEOGRAPHIC COORDINATES (Lat/Lon)
geo.int = sphere.xyz2geo(intmean)
geo.ext = sphere.xyz2geo(extmean)

S3 method for mixture model : evaluate density

Description

Compute density for a fitted mixture model.

Usage

density(object, newdata)

Arguments

object

a fitted mixture model of riemmix class.

newdata

data of nn objects (vectors, matrices) that can be wrapped by one of wrap.* functions in the Riemann package.

Value

a length-nn vector of class labels.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

# ---------------------------------------------------- #
#            FIT A MODEL & APPLY THE METHOD
# ---------------------------------------------------- #
# Load the 'city' data and wrap as 'riemobj'
data(cities)
locations = cities$cartesian
embed2    = array(0,c(60,2)) 
for (i in 1:60){
   embed2[i,] = sphere.xyz2geo(locations[i,])
}

# Fit a model
k3 = moSN(locations, k=3)

# Evaluate 
newdensity = density(k3, locations)

Data: xDAWN Covariances of MNE Sample MEG Epochs

Description

A collection of 216 symmetric positive-definite 32-by-32 matrices with four event-label groups. The matrices represent test epochs from one participant in the MNE sample audiovisual experiment, after supervised xDAWN spatial filtering and prototype augmentation. The 32 coordinates comprise 16 class-prototype features and 16 filtered trial features, rather than 32 original EEG or MEG sensors.

Usage

data(ERP)

Format

a named list containing

covariance

an (32×32×216)(32\times 32\times 216) array of covariance matrices.

label

a length-216216 factor with levels LA, LV, RA, RV and counts 57, 57, 53, 49.

Details

A September 21, 2026 reconstruction used the historical pyRiemann 0.2.7 example and numerical routines at commit 176e766f540bd4c7846f38573165fc3d27fc69ca. All 216 stored matrices matched with maximum relative Frobenius error 5.56×10−125.56\times10^{-12}, and the complete label sequence matched exactly. No rescaling, reordering, or eigenvector-sign correction was needed. The reconstruction used recorded contemporary dependencies; it does not claim byte identity or recovery of the original 2021 runtime.

The recipe reads MNE's prefiltered 0–40 Hz sample recording, sampled at approximately 150.15 Hz after fourfold decimation, applies a 2 Hz IIR high-pass filter, excludes MEG 2443, and selects 305 MEG channels, with EEG excluded. Epochs cover 0–1 second without baseline correction or projection, giving 288 epochs with 151 samples each. A random 25 percent training split (seed 42, without stratification) supplies 72 epochs for supervised xDAWN fitting with four filters per class. The remaining 216 epochs supply the packaged matrices. Covariance estimation uses centered empirical covariance with denominator 151, on the combined prototype and filtered-trial features.

These are repeated epochs from one participant, not independent participants. The representation was learned with labels from the separate 72-epoch training set, so it must not be described as an unsupervised transformation of the original sensors. A later split of the packaged matrices does not recreate raw-data preprocessing or establish performance for new participants.

The matrices retain their original numerical scale; small absolute eigenvalues alone do not imply poor conditioning. No unit conversion, regularization, data values, or labels were changed in this revision. The MNE sample data's public OpenNeuro record, ds000248 version 1.2.4, declares CC0. See the source record for attribution and terms.

Source

Historical pyRiemann ERP recipe, by Pedro Rodrigues (the historical filename says EEG, but the recipe explicitly selects MEG and excludes EEG).

MNE sample data on OpenNeuro, version 1.2.4; pinned source metadata and CC0 declaration. The MNE sample description identifies the audiovisual experiment. The separate writing workspace records the reconstruction in maintenance/reports/erp-provenance-followup-2026-09-21.md.

References

Gramfort A et al. (2013). "MEG and EEG data analysis with MNE-Python." Frontiers in Neuroscience, 7, 267. doi:10.3389/fnins.2013.00267.

Gramfort A et al. (2014). "MNE software for processing MEG and EEG data." NeuroImage, 86, 446–460. doi:10.1016/j.neuroimage.2013.10.027.

See Also

wrap.spd

Examples

## LOAD THE DATA AND WRAP AS RIEMOBJ
data(ERP)
myriem = wrap.spd(ERP$covariance)

Data : Gorilla Skull

Description

This is 29 male and 30 female gorillas' skull landmark data where each individual is represented as 8-ad/landmarks in 2 dimensions. This is a re-arranged version of the data from shapes package.

Usage

data(gorilla)

Format

a named list containing

male

a 3d array of size (8×2×29)(8\times 2\times 29)

female

a 3d array of size (8×2×30)(8\times 2\times 30)

Details

These are raw landmark configurations. wrap.landmark removes translation and scale and uses orthogonal alignment, including reflections. The resulting shape analysis is different from analysis of the original sizes or an orientation-preserving quotient. Preserve the documented landmark order in comparisons with shapes.

Source

The arrays match shapes::gorm.dat and shapes::gorf.dat after reordering landmarks by c(1, 5, 4, 3, 2, 8, 7, 6). This exact correspondence was verified against shapes 1.2.8; coordinates and individual ordering are otherwise unchanged. The upstream manual attributes the data to Paul O'Higgins and cites O'Higgins and Dryden (1993). https://cran.r-project.org/package=shapes

References

Dryden IL, Mardia KV (2016). Statistical shape analysis with applications in R, Wiley series in probability and statistics, Second edition edition. John Wiley and Sons, Chichester, UK ; Hoboken, NJ. ISBN 978-1-119-07251-5 978-1-119-07250-8.

O'Higgins P, Dryden IL (1993). "Sexual dimorphism in hominoids: further studies of craniofacial shape differences in Pan, Gorilla, Pongo." Journal of Human Evolution, 24:183–205.

See Also

wrap.landmark

Examples

data(gorilla)                               # load the data
riem.female = wrap.landmark(gorilla$female) # wrap as RIEMOBJ
opar <- par(no.readonly=TRUE)
for (i in 1:30){
  if (i < 2){
    plot(riem.female$data[[i]], cex=0.5, 
         xlim=c(-1,1)/2, ylim=c(-2,2)/5,
         main="30 female gorilla skull preshapes",
         xlab="dimension 1", ylab="dimension 2")
    lines(riem.female$data[[i]])
  } else {
    points(riem.female$data[[i]], cex=0.5)
    lines(riem.female$data[[i]])
  }
}
par(opar)

Estimation of Distribution Algorithm with MACG Distribution

Description

For a function f:Gr(k,p)→Rf : Gr(k,p) \rightarrow \mathbf{R}, find the minimizer and the attained minimum value with estimation of distribution algorithm using MACG distribution.

Usage

grassmann.optmacg(func, p, k, ...)

Arguments

func

a function to be minimized, returning one finite numeric value.

p

dimension parameter as in Gr(k,p)Gr(k,p).

k

dimension parameter as in Gr(k,p)Gr(k,p).

...

extra parameters including

n.start

number of runs; algorithm is executed n.start times (default: 10).

maxiter

maximum number of iterations for each run (default: 100).

popsize

the number of samples generated at each step for stochastic search (default: 100).

ratio

ratio in (0,1)(0,1) where top ratio*popsize samples are chosen for parameter update (default: 0.25).

print.progress

a logical; if TRUE, it prints each iteration (default: FALSE).

Value

a named list containing:

cost

smallest function value attained during the iterative search runs; a global minimum is not guaranteed.

solution

a (p×k)(p\times k) matrix that attains the cost.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

#-------------------------------------------------------------------
#               Optimization for Eigen-Decomposition
#
# Given (5x5) covariance matrix S, eigendecomposition is can be 
# considered as an optimization on Grassmann manifold. Here, 
# we are trying to find top 3 eigenvalues and compare.
#-------------------------------------------------------------------

## PREPARE
A = cov(matrix(rnorm(100*5), ncol=5)) # define covariance
myfunc <- function(p){                # cost function to minimize
  return(sum(-diag(t(p)%*%A%*%p)))
} 

## SOLVE THE OPTIMIZATION PROBLEM
Aout = grassmann.optmacg(myfunc, p=5, k=3, popsize=100, n.start=30)

## COMPUTE EIGENVALUES
#  1. USE SOLUTIONS TO THE ABOVE OPTIMIZATION 
abase   = Aout$solution
eig3sol = sort(diag(t(abase)%*%A%*%abase), decreasing=TRUE)

#  2. USE BASIC 'EIGEN' FUNCTION
eig3dec = sort(eigen(A)$values, decreasing=TRUE)[1:3]

## VISUALIZE
opar <- par(no.readonly=TRUE)
yran = c(min(min(eig3sol),min(eig3dec))*0.95,
         max(max(eig3sol),max(eig3dec))*1.05)
plot(1:3, eig3sol, type="b", col="red",  pch=19, ylim=yran,
     xlab="index", ylab="eigenvalue", main="compare top 3 eigenvalues")
lines(1:3, eig3dec, type="b", col="blue", pch=19)
legend(1.55, max(yran), legend=c("optimization","decomposition"), col=c("red","blue"),
       lty=rep(1,2), pch=19)
par(opar)

Generate Uniform Samples on Grassmann Manifold

Description

It generates nn random samples from Grassmann manifold Gr(k,p)Gr(k,p).

Usage

grassmann.runif(n, k, p, type = c("list", "array", "riemdata"))

Arguments

n

number of samples to be generated.

k

dimension of the subspace.

p

original dimension (of the ambient space).

type

return type;

"list"

a length-nn list of (p×k)(p\times k) basis of kk-subspaces.

"array"

a (p×k×n)(p\times k\times n) 3D array whose slices are kk-subspace basis.

"riemdata"

a S3 object. See wrap.grassmann for more details.

Value

an object from one of the above by type option.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Chikuse Y (2003). Statistics on Special Manifolds, volume 174 of Lecture Notes in Statistics. Springer New York, New York, NY. ISBN 978-0-387-00160-9 978-0-387-21540-2.

See Also

stiefel.runif, wrap.grassmann

Examples

#-------------------------------------------------------------------
#                 Draw Samples on Grassmann Manifold 
#-------------------------------------------------------------------
#  Multiple Return Types with 3 Observations of 5-dim subspaces in R^10
dat.list = grassmann.runif(n=3, k=5, p=10, type="list")
dat.arr3 = grassmann.runif(n=3, k=5, p=10, type="array")
dat.riem = grassmann.runif(n=3, k=5, p=10, type="riemdata")

Test of Uniformity on Grassmann Manifold

Description

Given the data on Grassmann manifold Gr(k,p)Gr(k,p), it tests whether the data is distributed uniformly.

Usage

grassmann.utest(grobj, method = c("Bing", "BingM"))

Arguments

grobj

a S3 "riemdata" class of Grassmann-valued data.

method

(case-insensitive) name of the test method containing

"Bing"

Bingham statistic.

"BingM"

modified Bingham statistic with better order of error.

Details

This is an asymptotic moment-based test, requiring independent identically distributed observations. It need not detect every nonuniform distribution. The modified expansion is unavailable for ambient dimension two, where its displayed coefficients are singular; full-dimensional subspaces are also excluded. Small-sample calibration and the modified expansion remain experimental; a nominal chi-squared p-value does not certify finite-sample size control.

Value

a (list) object of S3 class htest containing:

statistic

a test statistic.

p.value

pp-value under H0H_0.

alternative

alternative hypothesis.

method

name of the test.

data.name

name(s) of provided sample data.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Chikuse Y (2003). Statistics on Special Manifolds, volume 174 of Lecture Notes in Statistics. Springer New York, New York, NY. ISBN 978-0-387-00160-9 978-0-387-21540-2.

Mardia KV, Jupp PE (eds.) (1999). Directional Statistics, Wiley Series in Probability and Statistics. John Wiley and Sons, Inc., Hoboken, NJ, USA. ISBN 978-0-470-31697-9 978-0-471-95333-3.

See Also

wrap.grassmann

Examples

#-------------------------------------------------------------------
#   Compare Bingham's original and modified versions of the test
# 
# Test 1. sample uniformly from Gr(2,4)
# Test 2. use perturbed principal components from 'iris' data in R^4
#         which is concentrated around a point to reject H0.
#-------------------------------------------------------------------
## Data Generation
#  1. uniform data
myobj1 = grassmann.runif(n=100, k=2, p=4)

#  2. perturbed principal components
data(iris)
irdat = list()
for (n in 1:100){
   tmpdata    = iris[1:50,1:4] + matrix(rnorm(50*4,sd=0.5),ncol=4)
   irdat[[n]] = eigen(cov(tmpdata))$vectors[,1:2]
}
myobj2 = wrap.grassmann(irdat)

## Test 1 : uniform data
grassmann.utest(myobj1, method="Bing")
grassmann.utest(myobj1, method="BingM")

## Tests : iris data
grassmann.utest(myobj2, method="bINg")   # method names are 
grassmann.utest(myobj2, method="BiNgM")  # CASE - INSENSITIVE !

Data : Left Hands

Description

This dataset contains 10 shapes of 4 subjects's left hands where each shape is represented by 56 landmark points. For each person, first six shapes are equally spaced sequence from maximally to minimally spread fingures. The rest are arbitrarily chosen with two constraints; (1) the palm should face the support and (2) the contour should contain no crossins.

Usage

data(hands)

Format

a named list containing

data

an (56×2×40)(56\times 2\times 40) array of landmarks for 40 subjects.

person

a length-4040 vector of subject indices.

References

Stegmann M, Gomez D (2002) "A Brief Introduction to Statistical Shape Analysis." Informatics and Mathematical Modelling, Technical University of Denmark, DTU.

See Also

wrap.landmark

Examples

## LOAD THE DATA 
data(hands)

## VISUALIZE 6 HANDS OF PERSON 1
opar <- par(no.readonly=TRUE)
par(mfrow=c(2,3))
for (i in 1:6){
  xx = hands$data[,1,i]
  yy = hands$data[,2,i]
  plot(xx,yy,"b", cex=0.9)
}
par(opar)

S3 method for mixture model : predict labels

Description

Given a fitted mixture model of KK components, predict labels of observations accordingly.

Usage

label(object, newdata)

Arguments

object

a fitted mixture model of riemmix class.

newdata

data of nn objects (vectors, matrices) that can be wrapped by one of wrap.* functions in the Riemann package.

Value

a length-nn vector of class labels.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

# ---------------------------------------------------- #
#            FIT A MODEL & APPLY THE METHOD
# ---------------------------------------------------- #
# Load the 'city' data and wrap as 'riemobj'
data(cities)
locations = cities$cartesian
embed2    = array(0,c(60,2)) 
for (i in 1:60){
   embed2[i,] = sphere.xyz2geo(locations[i,])
}

# Fit a model
k3 = moSN(locations, k=3)

# Evaluate
newlabel = label(k3, locations)

S3 method for mixture model : log-likelihood

Description

Given a fitted mixture model f(x)f(x) and observations x1,…,xn∈Mx_1, \ldots, x_n \in \mathcal{M}, compute the log-likelihood

L=log⁡∏i=1nf(xi)=∑i=1nlog⁡f(xi)L = \log \prod_{i=1}^n f(x_i) = \sum_{i=1}^n \log f(x_i)

.

Usage

loglkd(object, newdata)

Arguments

object

a fitted mixture model of riemmix class.

newdata

data of nn objects (vectors, matrices) that can be wrapped by one of wrap.* functions in the Riemann package.

Value

the log-likelihood.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

# ---------------------------------------------------- #
#            FIT A MODEL & APPLY THE METHOD
# ---------------------------------------------------- #
# Load the 'city' data and wrap as 'riemobj'
data(cities)
locations = cities$cartesian
embed2    = array(0,c(60,2)) 
for (i in 1:60){
   embed2[i,] = sphere.xyz2geo(locations[i,])
}

# Fit a model
k3 = moSN(locations, k=3)

# Evaluate
newloglkd = round(loglkd(k3, locations), 3)
print(paste0("Log-likelihood for K=3 model fit : ", newloglkd))

Methods for Scalar-Response Kernel Regression Fits

Description

Methods for Scalar-Response Kernel Regression Fits

Usage

## S3 method for class 'm2skreg'
fitted(object, ...)

## S3 method for class 'm2skreg'
residuals(object, ...)

## S3 method for class 'm2skreg'
summary(object, ...)

## S3 method for class 'm2skreg'
print(x, ...)

## S3 method for class 'summary.m2skreg'
print(x, ...)

Arguments

object, x

A fitted m2skreg object, or its summary for the summary printing method.

...

Additional arguments; currently unused.

Value

fitted and residuals return numeric vectors; residuals are observed responses minus training fitted values. summary returns a summary.m2skreg list. Printing returns its input invisibly.


Matrix Angular Central Gaussian Distribution

Description

For Stiefel and Grassmann manifolds St(r,p)St(r,p) and Gr(r,p)Gr(r,p), the matrix variant of ACG distribution is known as Matrix Angular Central Gaussian (MACG) distribution MACGp,r(Σ)MACG_{p,r}(\Sigma) with density

f(X∣Σ)=∣Σ∣−r/2∣X⊤Σ−1X∣−p/2f(X\vert \Sigma) = |\Sigma|^{-r/2} |X^\top \Sigma^{-1} X|^{-p/2}

where Σ\Sigma is a (p×p)(p\times p) symmetric positive-definite matrix. Similar to vector-variate ACG case, we follow a convention that tr(Σ)=ptr(\Sigma)=p.

Usage

dmacg(datalist, Sigma, log = FALSE)

rmacg(n, r, Sigma)

mle.macg(datalist, ...)

Arguments

datalist

a list of (p×r)(p\times r) orthonormal matrices.

Sigma

a (p×p)(p\times p) symmetric positive-definite matrix.

log

logical; return log densities when TRUE.

n

a positive integer number of samples.

r

the number of basis.

...

extra parameters for computations, including

maxiter

maximum number of iterations to be run (default:50).

eps

tolerance level for stopping criterion (default: 1e-5).

Details

The density is relative to the invariant uniform probability measure on the Stiefel manifold, or its push-forward on the Grassmann quotient, and equals one at Σ=I\Sigma=I. Cholesky/log-determinant evaluation avoids determinant products that overflow at extreme parameter scales.

Sampling uses the polar factor of a Gaussian matrix. The fixed-point estimate is trace normalized and carries convergence, iteration, step, and negative mean-log-likelihood attributes. Failed convergence warns; degenerate updates error. Full observed span does not certify the subspace-dispersion conditions needed for an identifiable, unique interior estimate. When r=p>1r=p>1, the density is uniform regardless of Σ\Sigma, so estimation errors.

Value

dmacg gives a vector of evaluated densities given samples. rmacg generates (p×r)(p\times r) orthonormal matrices wrapped in a list. mle.macg estimates the SPD matrix Σ\Sigma.

References

Chikuse Y (1990). “The matrix angular central Gaussian distribution.” Journal of Multivariate Analysis, 33(2), 265–274. ISSN 0047259X.

Mardia KV, Jupp PE (eds.) (1999). Directional Statistics, Wiley Series in Probability and Statistics. John Wiley and Sons, Inc., Hoboken, NJ, USA. ISBN 978-0-470-31697-9 978-0-471-95333-3.

Kent JT, Ganeiber AM, Mardia KV (2013). "A new method to simulate the Bingham and related distributions in directional data analysis with applications." arXiv:1310.8110.

See Also

acg

Examples

# -------------------------------------------------------------------
#          Example with Matrix Angular Central Gaussian Distribution
#
# Given a fixed Sigma, generate samples and estimate Sigma via ML.
# -------------------------------------------------------------------
## GENERATE AND MLE in St(2,5)/Gr(2,5)
#  Generate data
Strue = diag(5)                  # true SPD matrix
sam1  = rmacg(n=50,  r=2, Strue) # random samples
sam2  = rmacg(n=100, r=2, Strue) # random samples

#  MLE
Smle1 = mle.macg(sam1)
Smle2 = mle.macg(sam2)

#  Visualize
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,3), pty="s")
image(Strue[,5:1], axes=FALSE, main="true SPD")
image(Smle1[,5:1], axes=FALSE, main="MLE with n=50")
image(Smle2[,5:1], axes=FALSE, main="MLE with n=100")
par(opar)

Finite Mixture of Spherical Laplace Distributions

Description

For nn observations on a (p−1)(p-1) sphere in Rp\mathbf{R}^p, a finite mixture model is fitted whose components are spherical Laplace distributions via the following model

f(x;{wk,μk,σk}k=1K)=∑k=1KwkSL(x;μk,σk)f(x; \left\lbrace w_k, \mu_k, \sigma_k \right\rbrace_{k=1}^K) = \sum_{k=1}^K w_k SL(x; \mu_k, \sigma_k)

with parameters wkw_k's for component weights, μk\mu_k's for component locations, and σk\sigma_k's for component scales.

Usage

moSL(
  data,
  k = 2,
  same.sigma = FALSE,
  variants = c("soft", "hard", "stochastic"),
  ...
)

## S3 method for class 'moSL'
loglkd(object, newdata)

## S3 method for class 'moSL'
label(object, newdata)

## S3 method for class 'moSL'
density(object, newdata)

Arguments

data

data vectors in form of either an (n×p)(n\times p) matrix or a length-nn list. See wrap.sphere for descriptions on supported input types.

k

the number of clusters (default: 2).

same.sigma

a logical; TRUE to use same scale parameter across all components, or FALSE otherwise.

variants

type of the class assignment methods, one of "soft","hard", and "stochastic".

...

extra parameters including

maxiter

the maximum number of iterations (default: 50).

eps

stopping criterion for the EM algorithm (default: 1e-6).

printer

a logical; TRUE to show history of the algorithm, FALSE otherwise.

object

a fitted moSL model from the moSL function.

newdata

data vectors in form of either an (m×p)(m\times p) matrix or a length-mm list. See wrap.sphere for descriptions on supported input types.

Value

a named list of S3 class riemmix containing

cluster

a length-nn vector of class labels (from 1:k1:k).

loglkd

log likelihood of the fitted model.

criteria

a vector of information criteria.

parameters

a list containing proportion, location, and scale. See the section for more details.

membership

an (n×k)(n\times k) row-stochastic matrix of membership.

Parameters of the fitted model

A fitted model is characterized by three parameters. For kk-mixture model on a (p−1)(p-1) sphere in Rp\mathbf{R}^p, (1) proportion is a length-kk vector of component weight that sums to 1, (2) location is an (k×p)(k\times p) matrix whose rows are per-cluster locations, and (3) concentration is a length-kk vector of scale parameters for each component.

Note on S3 methods

There are three S3 methods; loglkd, label, and density. Given a random sample of size mm as newdata, (1) loglkd returns a scalar value of the computed log-likelihood, (2) label returns a length-mm vector of cluster assignments, and (3) density evaluates densities of every observation according ot the model fit.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

# ---------------------------------------------------- #
#                 FITTING THE MODEL
# ---------------------------------------------------- #
# Load the 'city' data and wrap as 'riemobj'
data(cities)
locations = cities$cartesian
embed2    = array(0,c(60,2)) 
for (i in 1:60){
   embed2[i,] = sphere.xyz2geo(locations[i,])
}

# Fit the model with different numbers of clusters
k2 = moSL(locations, k=2)
k3 = moSL(locations, k=3)
k4 = moSL(locations, k=4)

# Visualize
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,3))
plot(embed2, col=k2$cluster, pch=19, main="K=2")
plot(embed2, col=k3$cluster, pch=19, main="K=3")
plot(embed2, col=k4$cluster, pch=19, main="K=4")
par(opar)

# ---------------------------------------------------- #
#                   USE S3 METHODS
# ---------------------------------------------------- #
# Use the same 'locations' data as new data 
# (1) log-likelihood
newloglkd = round(loglkd(k3, locations), 5)
fitloglkd = round(k3$loglkd, 5)
print(paste0("Log-likelihood for K=3 fitted    : ", fitloglkd))
print(paste0("Log-likelihood for K=3 predicted : ", newloglkd))

# (2) label
newlabel = label(k3, locations)

# (3) density
newdensity = density(k3, locations)

Finite Mixture of Spherical Normal Distributions

Description

For nn observations on a (p−1)(p-1) sphere in Rp\mathbf{R}^p, a finite mixture model is fitted whose components are spherical normal distributions via the following model

f(x;{wk,μk,λk}k=1K)=∑k=1KwkSN(x;μk,λk)f(x; \left\lbrace w_k, \mu_k, \lambda_k \right\rbrace_{k=1}^K) = \sum_{k=1}^K w_k SN(x; \mu_k, \lambda_k)

with parameters wkw_k's for component weights, μk\mu_k's for component locations, and λk\lambda_k's for component concentrations.

Usage

moSN(
  data,
  k = 2,
  same.lambda = FALSE,
  variants = c("soft", "hard", "stochastic"),
  ...
)

## S3 method for class 'moSN'
loglkd(object, newdata)

## S3 method for class 'moSN'
label(object, newdata)

## S3 method for class 'moSN'
density(object, newdata)

Arguments

data

data vectors in form of either an (n×p)(n\times p) matrix or a length-nn list. See wrap.sphere for descriptions on supported input types.

k

the number of clusters (default: 2).

same.lambda

a logical; TRUE to use same concentration parameter across all components, or FALSE otherwise.

variants

type of the class assignment methods, one of "soft","hard", and "stochastic".

...

extra parameters including

maxiter

the maximum number of iterations (default: 50).

eps

stopping criterion for the EM algorithm (default: 1e-6).

printer

a logical; TRUE to show history of the algorithm, FALSE otherwise.

object

a fitted moSN model from the moSN function.

newdata

data vectors in form of either an (m×p)(m\times p) matrix or a length-mm list. See wrap.sphere for descriptions on supported input types.

Value

a named list of S3 class riemmix containing

cluster

a length-nn vector of class labels (from 1:k1:k).

loglkd

log likelihood of the fitted model.

criteria

a vector of information criteria.

parameters

a list containing proportion, center, and concentration. See the section for more details.

membership

an (n×k)(n\times k) row-stochastic matrix of membership.

Parameters of the fitted model

A fitted model is characterized by three parameters. For kk-mixture model on a (p−1)(p-1) sphere in Rp\mathbf{R}^p, (1) proportion is a length-kk vector of component weight that sums to 1, (2) center is an (k×p)(k\times p) matrix whose rows are cluster centers, and (3) concentration is a length-kk vector of concentration parameters for each component.

Note on S3 methods

There are three S3 methods; loglkd, label, and density. Given a random sample of size mm as newdata, (1) loglkd returns a scalar value of the computed log-likelihood, (2) label returns a length-mm vector of cluster assignments, and (3) density evaluates densities of every observation according ot the model fit.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

You K, Suh C (2022). “Parameter Estimation and Model-Based Clustering with Spherical Normal Distribution on the Unit Hypersphere.” Computational Statistics and Data Analysis, 107457. ISSN 01679473.

Examples

# ---------------------------------------------------- #
#                 FITTING THE MODEL
# ---------------------------------------------------- #
# Load the 'city' data and wrap as 'riemobj'
data(cities)
locations = cities$cartesian
embed2    = array(0,c(60,2)) 
for (i in 1:60){
   embed2[i,] = sphere.xyz2geo(locations[i,])
}

# Fit the model with different numbers of clusters
k2 = moSN(locations, k=2)
k3 = moSN(locations, k=3)
k4 = moSN(locations, k=4)

# Visualize
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,3))
plot(embed2, col=k2$cluster, pch=19, main="K=2")
plot(embed2, col=k3$cluster, pch=19, main="K=3")
plot(embed2, col=k4$cluster, pch=19, main="K=4")
par(opar)

# ---------------------------------------------------- #
#                   USE S3 METHODS
# ---------------------------------------------------- #
# Use the same 'locations' data as new data 
# (1) log-likelihood
newloglkd = round(loglkd(k3, locations), 3)
print(paste0("Log-likelihood for K=3 model fit : ", newloglkd))

# (2) label
newlabel = label(k3, locations)

# (3) density
newdensity = density(k3, locations)

Data : Normal Vectors to the Orbital Planes of the 9 Planets

Description

The 9 planets in our solar system are evolving the sun via their own orbits. This data provides normal vector of the orbital planes. Normal vectors are unit-norm vectors, so that they are thought to reside on 2-dimensional sphere.

Usage

data(orbital)

Format

an (9×3)(9\times 3) matrix where each row is a normal vector for a planet.

See Also

wrap.sphere

Examples

## LOAD THE DATA AND WRAP AS RIEMOBJ
data(orbital)
myorb = wrap.sphere(orbital)

## VISUALIZE
mds2d = riem.mds(myorb)$embed
opar <- par(no.readonly=TRUE)
plot(mds2d, main="9 Planets", pch=19, xlab="x", ylab="y")
par(opar)

Data : Passiflora Leaves

Description

Passiflora is a genus of about 550 species of flowering plants. This dataset contains 15 landmarks in 2 dimension of 3319 leaves of 40 species. Papers listed in the reference section analyzed the data and found 7 clusters.

Usage

data(passiflora)

Format

a named list containing

data

a 3d array of size (15×2×3319)(15\times 2\times 3319).

species

a length-33193319 vector of 40 species factors.

class

a length-33193319 vector of 7 cluster factors.

References

Chitwood DH, Otoni WC (2017). “Divergent leaf shapes among Passiflora species arise from a shared juvenile morphology.” Plant Direct, 1(5), e00028. ISSN 24754455.

Chitwood DH, Otoni WC (2017). “Morphometric analysis of Passiflora leaves: the relationship between landmarks of the vasculature and elliptical Fourier descriptors of the blade.” GigaScience, 6(1). ISSN 2047-217X.

See Also

wrap.landmark

Examples

data(passiflora)                         # load the data
riemobj = wrap.landmark(passiflora$data) # wrap as RIEMOBJ
pga2d   = riem.pga(riemobj)$embed        # embedding via PGA

opar <- par(no.readonly=TRUE)            # visualize
plot(pga2d, col=passiflora$class, pch=19, cex=0.7,
     main="PGA Embedding of Passiflora Leaves",
     xlab="dimension 1", ylab="dimension 2")
par(opar)

Prediction for Manifold-to-Scalar Kernel Regression

Description

Predicts under the fitted geometry and bandwidth without refitting or using prediction-batch statistics. Training predictors and responses are required.

Usage

## S3 method for class 'm2skreg'
predict(
  object,
  newdata,
  geometry = NULL,
  diagnostics = FALSE,
  block_size = 256L,
  ...
)

Arguments

object

A fitted m2skreg object.

newdata

A compatible riemdata object containing new predictors.

geometry

Usually NULL, which uses the fitted geometry. An explicit value must resolve to the same geometry. An old serialized object without geometry metadata requires an explicit value and produces a warning, because its original geometry cannot be inferred reliably.

diagnostics

Whether to return support diagnostics with predictions.

block_size

Positive integer limiting the number of new observations in each cross-distance calculation. It does not change the fitted model.

...

Reserved for future arguments; unknown arguments are rejected.

Value

By default, a numeric vector with one prediction per observation. With diagnostics=TRUE, a list containing prediction and a data frame diagnostics with effective_n, nearest_distance, and nearest_over_bandwidth. The last quantity may be infinite when the ratio is not representable even though the prediction remains finite.

See Also

riem.m2skreg


Inspect Wrapped Observations and Geometric Summaries

Description

Standard methods report the observation space, selected geometry, and actual numerical termination. A finite estimate is not by itself proof of convergence. Summary plots display matrix entries or ambient coordinates; they do not imply an isometric view of the manifold.

Usage

## S3 method for class 'riemdata'
print(x, ...)

## S3 method for class 'riemdata'
summary(object, ...)

## S3 method for class 'riem_summary'
print(x, ...)

## S3 method for class 'riem_summary'
summary(object, ...)

## S3 method for class 'riem_summary'
plot(x, ...)

Arguments

x, object

A wrapped dataset or fitted geometric summary.

...

Additional arguments passed to the plotting function where relevant.

Value

Print methods return their object invisibly. Summary methods return structured metadata and numerical diagnostics. Plot methods return invisibly.


Method Contracts and Validation Status

Description

Riemann distinguishes validated input representations, available geometry primitives, algorithm contracts, and statistical calibration. A wrapped point or available distance does not establish every algorithm's assumptions.

Details

The installed file ‘method-contracts.csv’ contains one disposition for every exported function and registered S3 method, including its domain, assumptions, restrictions, and evidence scope. Read it with the example below. The four possible dispositions are:

core

The consolidated input, geometry, summary, clustering, tangent PCA, or scalar regression contract, within its documented capabilities.

validated_restricted

A specific legacy contract checked against independent formulas or reference fixtures; the stated restrictions apply.

experimental

A retained legacy interface whose full numerical, mathematical, or statistical contract has not been independently verified.

disabled

An unavailable interface with a documented incompatible implementation. Current geometry restrictions can disable combinations without disabling an entire exported function.

A test-file reference is an evidence pointer, not branch coverage or a proof. No disposition guarantees a global optimum, an identifiable model, finite sample calibration, or validity for every manifold. Experimental methods are retained for compatibility and exploration; their source references and examples should not be read as completed validation.

Common distinctions are particularly relevant: nonnegative graph affinities or regression weights need not form a positive-semidefinite kernel; a metric dissimilarity need not be Euclidean; a local tangent representation need not solve exact nonlinear principal geodesic analysis; and a permutation test requires exchangeability under its specified null. Rayleigh/Bingham and Frechet mean/variance tests need not detect all distributional alternatives.

riem.capabilities() records primitive availability and geometry status. The table here separately records method status. The package maintenance inventory records source and dependency ownership; the release/check logs supply actual execution evidence for each environment.

See Also

riem.capabilities, riem.geometry

Examples

contracts <- read.csv(system.file("method-contracts.csv", package = "Riemann"))
contracts[contracts$name %in% c("riem.kpca", "riem.fanova"),
          c("name", "disposition", "limitations")]

Competitive Learning Riemannian Quantization

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, perform clustering via Competitive Learning Riemannian Quantization (CLRQ). Originally, the algorithm is designed for finding voronoi cells that are used in domain quantization. Given the discrete measure of data, centers of the cells play a role of cluster centers and data are labeled accordingly based on the distance to voronoi centers. For an iterative update of centers, gradient descent algorithm adapted for the Riemannian manifold setting is used with the gain factor sequence

γt=a1+bt\gamma_t = \frac{a}{1 + b \sqrt{t}}

where two parameters a,ba,b are represented by par.a and par.b. For initialization, we provide k-means++ and random seeding options as in k-means.

Usage

riem.clrq(riemobj, k = 2, init = c("plus", "random"), gain.a = 1, gain.b = 1)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

k

the number of clusters.

init

(case-insensitive) name of an initialization scheme. (default: "plus".)

gain.a

parameter aa for gain factor sequence.

gain.b

parameter bb for gain factor sequence.

Value

a named list containing

centers

a 3d array where each slice along 3rd dimension is a matrix representation of class centers.

cluster

a length-NN vector of class labels (from 1:k1:k).

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Le Brigant A, Puechmorel S (2019). “Quantization and clustering on Riemannian manifolds with an application to air traffic analysis.” Journal of Multivariate Analysis, 173, 685–703. ISSN 0047259X.

Bonnabel S (2013). “Stochastic Gradient Descent on Riemannian Manifolds.” IEEE Transactions on Automatic Control, 58(9), 2217–2229. ISSN 0018-9286, 1558-2523.

See Also

riem.kmeans

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# class 1 : 10 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 10 perturbed data points near (0,1,0) on S^2 in R^3
# class 3 : 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
mylabs = rep(c(1,2,3), each=10)

## CLRQ WITH K=2,3,4
clust2 = riem.clrq(myriem, k=2)
clust3 = riem.clrq(myriem, k=3)
clust4 = riem.clrq(myriem, k=4)

## MDS FOR VISUALIZATION
mds2d = riem.mds(myriem, ndim=2)$embed

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(2,2), pty="s")
plot(mds2d, pch=19, main="true label", col=mylabs)
plot(mds2d, pch=19, main="K=2", col=clust2$cluster)
plot(mds2d, pch=19, main="K=3", col=clust3$cluster)
plot(mds2d, pch=19, main="K=4", col=clust4$cluster)
par(opar)

Build Lightweight Coreset

Description

Given manifold-valued data X1,X2,…,XN∈MX_1,X_2,\ldots,X_N \in \mathcal{M}, this algorithm draws a weighted coreset using the lightweight importance-sampling scheme proposed by the reference below. The Euclidean approximation theorem is not asserted for arbitrary manifold geometries.

Usage

riem.coreset18B(
  riemobj,
  M = max(1L, ceiling(length(riemobj$data)/2)),
  geometry = c("intrinsic", "extrinsic"),
  ...
)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

M

positive integer number of independent draws (default: ⌈N/2⌉\lceil N/2 \rceil). Values greater than NN are permitted.

geometry

(case-insensitive) name or saved specification of a geometry supporting means. Legacy aliases "intrinsic" and "extrinsic" are accepted; see riem.geometry.

...

extra parameters including

maxiter

maximum number of iterations to be run (default:50).

eps

tolerance level for stopping criterion (default: 1e-5).

Details

Each index is drawn independently with replacement, with probability qi=1/(2N)+d(Xi,μ)2/(2∑jd(Xj,μ)2)q_i = 1/(2N) + d(X_i,\mu)^2/(2\sum_j d(X_j,\mu)^2), where μ\mu is the computed mean in the selected geometry. When all distances are zero the probabilities are uniform. Thus, for any fixed centers, the importance-weighted coreset cost is an unbiased estimate of the full-data squared-distance cost. This identity does not require the computed mean to be globally optimal. Duplicate draws count separately and must not be discarded without summing their weights. Use set.seed() for reproducibility.

Value

a named list containing

coreid

a length-MM index vector; repeated indices are retained.

weight

a length-MM vector of importance weights, 1/(Mqi)1/(M q_i) for each drawn index ii.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Bachem O, Lucic M, Krause A (2018). “Scalable k -Means Clustering via Lightweight Coresets.” In Proceedings of the 24th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining, 1119–1127. ISBN 978-1-4503-5552-0.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# * 10 perturbed data points near (1,0,0) on S^2 in R^3
# * 10 perturbed data points near (0,1,0) on S^2 in R^3
# * 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)

## MDS FOR VISUALIZATION
embed2 = riem.mds(myriem, ndim=2)$embed

## FIND CORESET OF SIZES 3, 6, 9
core1 = riem.coreset18B(myriem, M=3)
core2 = riem.coreset18B(myriem, M=6)
core3 = riem.coreset18B(myriem, M=9)

col1 = rep(1,30); col1[core1$coreid] = 2
col2 = rep(1,30); col2[core2$coreid] = 2
col3 = rep(1,30); col3[core3$coreid] = 2

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,3), pty="s")
plot(embed2, pch=19, col=col1, main="coreset size=3")
plot(embed2, pch=19, col=col2, main="coreset size=6")
plot(embed2, pch=19, col=col3, main="coreset size=9")
par(opar)

Distance between Two Curves on Manifolds

Description

Given two curves γ1,γ2:I→M\gamma_1, \gamma_2 : I \rightarrow \mathcal{M}, we are interested in measuring the discrepancy of two curves. Usually, data are given as discrete observations so we are offering several methods to perform the task. See the section below for detailed description.

Usage

riem.distlp(
  riemobj1,
  riemobj2,
  vect = NULL,
  geometry = c("intrinsic", "extrinsic"),
  ...
)

Arguments

riemobj1

a S3 "riemdata" class for NN manifold-valued data along the curve.

riemobj2

a S3 "riemdata" class for NN manifold-valued data along the curve.

vect

a vector of domain values. If given Null (default), sequence 1:N is set.

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

...

extra parameters including

p

an exponent (default: 2).

Value

the distance value.

Default Method

Trapezoidal Approximation Assume γ1(ti)=Xi\gamma_1 (t_i) = X_i and γ2(ti)=Yi\gamma_2 (t_i) = Y_i for i=1,2,…,Ni=1,2,\ldots,N. In the Euclidean space, LpL_p distance between two scalar-valued functions is defined as

Lpp(γ1(x),γ2(x)=∫X∣γ1(x)−γ2(x)∣pdxL_p^p (\gamma_1 (x), \gamma_2 (x) = \int_{\mathcal{X}} |\gamma_1 (x) - \gamma_2 (x)|^p dx

. We extend this approach to manifold-valued curves

Lpp(γ1(t),γ2(t))=∫t∈Idp(γ1(t),γ2(t))dtL_p^p (\gamma_1 (t), \gamma_2 (t)) = \int_{t\in I} d^p (\gamma_1 (t), \gamma_2 (t)) dt

where d(⋅,⋅)d(\cdot,\cdot) is an intrinsic/extrinsic distance on manifolds. With the given representations, the above integral is approximated using trapezoidal rule.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

#-------------------------------------------------------------------
#                          Curves on Sphere
#
#  curve1 : y = 0.5*cos(x) on the tangent space at (0,0,1)
#  curve2 : y = 0.5*cos(x) on the tangent space at (0,0,1)
#  curve3 : y = 0.5*sin(x) on the tangent space at (0,0,1)
#
# * distance between curve1 & curve2 should be close to 0.
# * distance between curve1 & curve3 should be large.
#-------------------------------------------------------------------
## GENERATION
vecx  = seq(from=-0.9, to=0.9, length.out=50)
vecy1 = 0.5*cos(vecx) + rnorm(50, sd=0.05)
vecy2 = 0.5*cos(vecx) + rnorm(50, sd=0.05)
vecy3 = 0.5*sin(vecx) + rnorm(50, sd=0.05)

## WRAP AS RIEMOBJ
mat1 = cbind(vecx, vecy1, 1); mat1 = mat1/sqrt(rowSums(mat1^2))
mat2 = cbind(vecx, vecy2, 1); mat2 = mat2/sqrt(rowSums(mat2^2))
mat3 = cbind(vecx, vecy3, 1); mat3 = mat3/sqrt(rowSums(mat3^2))

rcurve1 = wrap.sphere(mat1)
rcurve2 = wrap.sphere(mat2)
rcurve3 = wrap.sphere(mat3)

## COMPUTE DISTANCES
riem.distlp(rcurve1, rcurve2, vect=vecx)
riem.distlp(rcurve1, rcurve3, vect=vecx)

Dynamic Time Warping Distance

Description

Given two time series - a query X=(X1,X2,…,XN)X = (X_1,X_2,\ldots,X_N) and a reference Y=(Y1,Y2,…,YM)Y = (Y_1,Y_2,\ldots,Y_M), riem.dtw computes the most basic version of Dynamic Time Warping (DTW) distance between two series using a symmetric step pattern, meaning no window constraints and others at all. Although the scope of DTW in Euclidean space-valued objects is rich, it is scarce for manifold-valued curves. If you are interested in the topic, we refer to dtw package.

Usage

riem.dtw(riemobj1, riemobj2, geometry = c("intrinsic", "extrinsic"))

Arguments

riemobj1

a S3 "riemdata" class for MM manifold-valued data along the curve.

riemobj2

a S3 "riemdata" class for NN manifold-valued data along the curve.

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

Value

the distance value.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

#-------------------------------------------------------------------
#                          Curves on Sphere
#
#  curve1 : y = 0.5*cos(x) on the tangent space at (0,0,1)
#  curve2 : y = 0.5*sin(x) on the tangent space at (0,0,1)
# 
#  we will generate two sets for curves of different sizes.
#-------------------------------------------------------------------
## GENERATION
clist = list()
for (i in 1:10){ # curve type 1
  vecx = seq(from=-0.9, to=0.9, length.out=sample(10:50, 1))
  vecy = 0.5*cos(vecx) + rnorm(length(vecx), sd=0.1)
  mats = cbind(vecx, vecy, 1)
  clist[[i]] = wrap.sphere(mats/sqrt(rowSums(mats^2)))
}
for (i in 1:10){ # curve type 2
  vecx = seq(from=-0.9, to=0.9, length.out=sample(10:50, 1))
  vecy = 0.5*sin(vecx) + rnorm(length(vecx), sd=0.1)
  mats = cbind(vecx, vecy, 1)
  clist[[i+10]] = wrap.sphere(mats/sqrt(rowSums(mats^2)))
}

## COMPUTE DISTANCES
outint = array(0,c(20,20))
outext = array(0,c(20,20))
for (i in 1:19){
  for (j in 2:20){
    outint[i,j] <- outint[j,i] <- riem.dtw(clist[[i]], clist[[j]], 
                                           geometry="intrinsic")
    outext[i,j] <- outext[j,i] <- riem.dtw(clist[[i]], clist[[j]],
                                           geometry="extrinsic")
  }
}

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,2), pty="s")
image(outint[,20:1], axes=FALSE, main="intrinsic DTW Distance")
image(outext[,20:1], axes=FALSE, main="extrinsic DTW Distance")
par(opar)

Frechet Analysis of Variance

Description

Compares population Frechet means and variances using the statistic of Dubey and Muller (2019), equation (14). The asymptotic chi-squared calibration targets equality of those summaries, not arbitrary equality of distributions. The permutation version requires exchangeable observations under the stronger null of identical group distributions, and targets alternatives detected by the same mean-and-variance statistic.

Usage

riem.fanova(..., maxiter = 50, eps = 1e-05, geometry = NULL)

riem.fanovaP(..., maxiter = 50, eps = 1e-05, nperm = 99, geometry = NULL)

Arguments

...

At least two compatible riemdata objects, each containing at least three observations. Observations must be independent within and across groups for the documented calibration; paired, repeated or clustered observations require a separate restricted resampling procedure.

maxiter

Positive integer iteration budget for each Frechet mean.

eps

Finite positive tolerance for the shared mean solver.

geometry

A geometry name or saved specification with a compatible mean and distance implementation. By default all groups must resolve to the same geometry.

nperm

Positive integer number of random label permutations.

Details

The asymptotic result requires unique population/sample means, positive variances of the squared distances to each group mean, suitable moment/metric-entropy conditions, and group proportions bounded away from zero. The cited paper gives sufficient bounded-space conditions; accepting an input manifold does not establish them for every population distribution. The function rejects failed mean fits and a degenerate empirical denominator.

For group proportions λj=nj/n\lambda_j=n_j/n, the two denominators are ∑jλj/σ^j2\sum_j\lambda_j/\widehat\sigma_j^2 and ∑jλj2σ^j2\sum_j\lambda_j^2\widehat\sigma_j^2. Distances are jointly rescaled before forming the statistic; this leaves it unchanged and avoids unit-scale overflow. Numerical rescaling does not transform the underlying geometry.

riem.fanovaP refits all group means for every sampled label assignment. It uses (1+#{Tb≥Tobs})/(nperm+1)(1+\#\{T_b\geq T_{obs}\})/(nperm+1), including ties. The pooled fit is unchanged by label permutation and is reused. Permutations follow R's current random-number state. The returned Monte Carlo standard error is a plug-in diagnostic for simulation variability, not inferential uncertainty in the scientific effect.

Value

An htest object with statistic, p-value, null/alternative, resolved geometry, calibration, group sizes and mean diagnostics. The permutation version additionally retains permuted statistics, nperm, and mc_se.

References

Dubey P, Müller H (2019). “Fréchet analysis of variance for random objects.” Biometrika, 106(4), 803–821. ISSN 0006-3444, 1464-3510.

Examples

X <- wrap.euclidean(matrix(c(-1, 0, 2, 3), ncol = 1))
Y <- wrap.euclidean(matrix(c(0, 1, 4, 8), ncol = 1))
riem.fanova(X, Y)
set.seed(17)
riem.fanovaP(X, Y, nperm = 19)

Inspect and Resolve a Statistical Geometry

Description

Geometry is resolved once when a model is fitted and retained for prediction. For SPD matrices, "intrinsic" means "affine_invariant" and "extrinsic" means "log_euclidean". On the sphere the corresponding names are "round" and "chordal". Euclidean aliases coincide. Landmark geometry currently identifies configurations under the full orthogonal group, including reflections. A capability records an available operation; its validation status must also be considered before interpreting results.

Usage

riem.geometry(riemobj, geometry = NULL)

riem.capabilities()

Arguments

riemobj

A wrapped riemdata object.

geometry

A geometry name, a saved riem_geometry specification, or NULL for the default intrinsic geometry.

Value

riem.geometry returns a serializable geometry specification. riem.capabilities returns the operation registry as a data frame.

Examples

x <- wrap.spd(list(diag(2), 2 * diag(2)))
riem.geometry(x, "log_euclidean")
riem.capabilities()

Hierarchical Agglomerative Clustering

Description

Given NN observations X1,X2,…,XM∈MX_1, X_2, \ldots, X_M \in \mathcal{M}, perform hierarchical agglomerative clustering with stats::hclust. The supplied manifold distances are treated as dissimilarities.

Usage

riem.hclust(
  riemobj,
  geometry = NULL,
  method = c("single", "complete", "average", "mcquitty", "ward.D", "ward.D2",
    "centroid", "median"),
  members = NULL
)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

geometry

A geometry name or saved specification.

method

agglomeration method to be used. This must be one of "single", "complete", "average", "mcquitty", "ward.D", "ward.D2", "centroid" or "median".

members

NULL or a vector whose length equals the number of observations. See hclust for details.

Details

Ward, centroid and median linkage require care when interpreted as Euclidean sums of squares. In particular, Ward linkage of geodesic distances is not manifold k-means. ward.D is the historical R update and does not implement the Ward criterion implemented by ward.D2. See the stats::hclust documentation for distance powers and member weights.

Value

an object of class hclust. See hclust for details.

References

Müllner D (2013). “fastcluster : Fast Hierarchical, Agglomerative Clustering Routines for R and Python.” Journal of Statistical Software, 53(9). ISSN 1548-7660.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# class 1 : 10 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 10 perturbed data points near (0,1,0) on S^2 in R^3
# class 3 : 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)

## COMPUTE SINGLE AND COMPLETE LINKAGE
hc.sing <- riem.hclust(myriem, method="single")
hc.comp <- riem.hclust(myriem, method="complete")

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,2))
plot(hc.sing, main="single linkage")
plot(hc.comp, main="complete linkage")
par(opar)

Geodesic Interpolation

Description

Given 2 observations X1,X2∈MX_1, X_2 \in \mathcal{M}, find the interpolated point of a geodesic γ(t)\gamma(t) for t∈(0,1)t \in (0,1) which assumes two endpoints γ(0)=X1\gamma(0)=X_1 and γ(1)=X2\gamma(1)=X_2.

Usage

riem.interp(riemobj, t = 0.5, geometry = c("intrinsic", "extrinsic"))

Arguments

riemobj

a S3 "riemdata" class for 22 manifold-valued data where the first object is the starting point.

t

a scalar in (0,1)(0,1) for which the interpolation is taken.

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

Value

an interpolated object in matrix representation on M\mathcal{M}.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

#-------------------------------------------------------------------
#       Geodesic Interpolation between (1,0) and (0,1) in S^1
#-------------------------------------------------------------------
## PREPARE DATA
sp.start = c(1,0)
sp.end   = c(0,1)
sp.data  = wrap.sphere(rbind(sp.start, sp.end))

## FIND THE INTERPOLATED POINT AT "t=0.25"
mid.int = as.vector(riem.interp(sp.data, t=0.25, geometry="intrinsic"))
mid.ext = as.vector(riem.interp(sp.data, t=0.25, geometry="extrinsic"))

## VISUALIZE
#  Prepare Lines and Points
thetas  = seq(from=0, to=pi/2, length.out=100)
quarter = cbind(cos(thetas), sin(thetas))
pic.pts = rbind(sp.start, mid.int, mid.ext, sp.end)
pic.col = c("black","red","green","black")

# Draw
opar <- par(no.readonly=TRUE)
par(pty="s")
plot(quarter, main="two interpolated points at t=0.25",
     xlab="x", ylab="y", type="l")
points(pic.pts, col=pic.col, pch=19)
text(mid.int[1]-0.1, mid.int[2], "intrinsic", col="red")
text(mid.ext[1]-0.1, mid.ext[2], "extrinsic", col="green")
par(opar)

Geodesic Interpolation of Multiple Points

Description

Given 2 observations X1,X2∈MX_1, X_2 \in \mathcal{M}, find the interpolated points of a geodesic γ(t)\gamma(t) for t∈(0,1)t \in (0,1) which assumes two endpoints γ(0)=X1\gamma(0)=X_1 and γ(1)=X2\gamma(1)=X_2.

Usage

riem.interps(
  riemobj,
  vect = c(0.25, 0.5, 0.75),
  geometry = c("intrinsic", "extrinsic")
)

Arguments

riemobj

a S3 "riemdata" class for 22 manifold-valued data where the first object is the starting point.

vect

a length-TT vector in (0,1)(0,1) for which the interpolations are taken.

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

Value

a 3d array where TT slices along 3rd dimension are interpolated objects in matrix representation.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

#-------------------------------------------------------------------
#       Geodesic Interpolation between (1,0) and (0,1) in S^1
#-------------------------------------------------------------------
## PREPARE DATA
sp.start = c(1,0)
sp.end   = c(0,1)
sp.data  = wrap.sphere(rbind(sp.start, sp.end))

## FIND THE INTERPOLATED POINT AT FOR t=0.1, 0.2, ..., 0.9.
myvect  = seq(from=0.1, to=0.9, by=0.1)
geo.int = riem.interps(sp.data, vect=myvect, geometry="intrinsic")
geo.ext = riem.interps(sp.data, vect=myvect, geometry="extrinsic")

geo.int = matrix(geo.int, byrow=TRUE, ncol=2) # re-arrange for plotting
geo.ext = matrix(geo.ext, byrow=TRUE, ncol=2)

## VISUALIZE
#  Prepare Lines and Points
thetas  = seq(from=0, to=pi/2, length.out=100)
quarter = cbind(cos(thetas), sin(thetas))

pts.int = rbind(sp.start, geo.int, sp.end)
pts.ext = rbind(sp.start, geo.ext, sp.end)
col.int = c("black", rep("red",9),  "black")
col.ext = c("black", rep("blue",9), "black")

# Draw
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,2), pty="s")
plot(quarter, main="intrinsic interpolation", # intrinsic geodesic
     xlab="x", ylab="y", type="l")
points(pts.int, col=col.int, pch=19)
for (i in 1:9){
  text(geo.int[i,1]*0.9, geo.int[i,2]*0.9, 
       paste0(round(i/10,2)), col="red")
}
plot(quarter, main="extrinsic interpolation", # intrinsic geodesic
     xlab="x", ylab="y", type="l")
points(pts.ext, col=col.ext, pch=19)
for (i in 1:9){
  text(geo.ext[i,1]*0.9, geo.ext[i,2]*0.9, 
       paste0(round(i/10,2)), col="blue")
}
par(opar)

Isometric Feature Mapping

Description

ISOMAP - isometric feature mapping - is a dimensionality reduction method to apply classical multidimensional scaling to the geodesic distance that is computed on a weighted nearest neighborhood graph. Nearest neighbor is defined by kk-NN where two observations are said to be connected when they are mutually included in each other's nearest neighbor. Note that it is possible for geodesic distances to be Inf when nearest neighbor graph construction incurs separate connected components. When an extra parameter padding=TRUE, infinite distances are explicitly replaced by 2 times the maximal finite geodesic distance.

Usage

riem.isomap(riemobj, ndim = 2, nnbd = 5, geometry = NULL, ...)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

ndim

an integer-valued target dimension (default: 2).

nnbd

the size of nearest neighborhood (default: 5).

geometry

A geometry name or saved specification.

...

extra parameters including

padding

a logical, default FALSE; if TRUE, disconnected distances are replaced by twice the largest finite graph distance, with a warning.

Details

Self-neighbors are excluded and distance ties use observation order. An edge is retained only when each endpoint selects the other among its nearest neighbors. Zero-length edges between duplicate observations are retained. The result records components, adjacency, and whether padding changed disconnected distances. Padding produces an artificial dissimilarity and is not an estimate of the original disconnected graph distance.

Value

a named list containing

embed

an (N×ndim)(N\times ndim) matrix whose rows are embedded observations.

References

Silva VD and Tenenbaum JB (2003). "Global Versus Local Methods in Nonlinear Dimensionality Reduction." Advances in Neural Information Processing Systems 15, 721–728. MIT Press.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# 10 perturbed data points near (1,0,0) on S^2 in R^3
# 10 perturbed data points near (0,1,0) on S^2 in R^3
# 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
mylabs = rep(c(1,2,3), each=10)

## MDS AND ISOMAP WITH DIFFERENT NEIGHBORHOOD SIZE
mdss = riem.mds(myriem)$embed
iso1 = riem.isomap(myriem, nnbd=5, padding=TRUE)$embed
iso2 = riem.isomap(myriem, nnbd=10, padding=TRUE)$embed

## VISUALIZE
opar = par(no.readonly=TRUE)
par(mfrow=c(1,3), pty="s")
plot(mdss, col=mylabs, pch=19, main="MDS")
plot(iso1, col=mylabs, pch=19, main="ISOMAP:nnbd=5")
plot(iso2, col=mylabs, pch=19, main="ISOMAP:nnbd=10")
par(opar)

K-Means Clustering

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, perform k-means clustering by minimizing within-cluster sum of squares (WCSS). Since the problem is NP-hard and sensitive to the initialization, we provide an option with multiple starts and return the best result with respect to WCSS.

Usage

riem.kmeans(riemobj, k = 2, geometry = NULL, ...)

## S3 method for class 'riem_kmeans'
predict(object, newdata, type = c("class", "distance"), ...)

## S3 method for class 'riem_kmeans'
print(x, ...)

## S3 method for class 'riem_kmeans'
summary(object, ...)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

k

the number of clusters.

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

...

extra parameters including

algorithm

(case-insensitive) name of an algorithm; "Lloyd" (default), or "MacQueen".

init

"plus" for squared-distance k-means++ (default), "random" for sampled observations, or a vector of k distinct observation indices.

maxiter

maximum number of iterations to be run (default:50).

nstart

the number of starts (default: 5).

eps

relative objective-increase tolerance (default: 1e-8).

mean.maxiter

maximum iterations for each cluster mean (default: 200).

mean.eps

cluster-mean convergence tolerance (default: 1e-8).

object

a fitted clustering object.

newdata

compatible wrapped observations.

type

return cluster labels ("class") or distances to all trained centers ("distance").

x

a fitted clustering object.

Details

The default is now Lloyd iteration, with each cluster updated by the shared Fréchet-mean solver. A Lloyd objective increase beyond tolerance is rejected and reported. The retained MacQueen option sequentially assigns observations and recomputes affected cluster means; no global monotonicity guarantee is claimed for approximate or nonconvex manifold means. Tied distances choose the lowest cluster index. Empty clusters are reseeded using a maximal-residual observation from a nonsingleton cluster; impossible separation and failed mean solves invalidate that start. Returned labels always assign observations to the final stored centers. Nonconverged finite starts remain visible, and returning one produces a warning. Use set.seed() to reproduce random initializations.

Value

a fitted riem_kmeans object containing

cluster

a length-NN vector of class labels (from 1:k1:k).

means

a 3d array where each slice along 3rd dimension is a matrix representation of class mean.

score

within-cluster sum of squares (WCSS).

References

Lloyd S (1982). “Least squares quantization in PCM.” IEEE Transactions on Information Theory, 28(2), 129–137. ISSN 0018-9448.

MacQueen J (1967). “Some methods for classification and analysis of multivariate observations.” In Proceedings of the fifth berkeley symposium on mathematical statistics and probability, volume 1: Statistics, 281–297.

See Also

riem.kmeanspp

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# class 1 : 10 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 10 perturbed data points near (0,1,0) on S^2 in R^3
# class 3 : 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
mylabs = rep(c(1,2,3), each=10)

## K-MEANS WITH K=2,3,4
clust2 = riem.kmeans(myriem, k=2)
clust3 = riem.kmeans(myriem, k=3)
clust4 = riem.kmeans(myriem, k=4)

## MDS FOR VISUALIZATION
mds2d = riem.mds(myriem, ndim=2)$embed

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(2,2), pty="s")
plot(mds2d, pch=19, main="true label", col=mylabs)
plot(mds2d, pch=19, main="K=2", col=clust2$cluster)
plot(mds2d, pch=19, main="K=3", col=clust3$cluster)
plot(mds2d, pch=19, main="K=4", col=clust4$cluster)
par(opar)

K-Means Clustering with Lightweight Coreset

Description

Apply weighted Lloyd iterations to an independently sampled lightweight coreset of manifold-valued observations. The Euclidean coreset approximation theorem is not asserted for arbitrary manifold geometries.

Usage

riem.kmeans18B(
  riemobj,
  k = 2,
  M = max(k, ceiling(length(riemobj$data)/2)),
  geometry = c("intrinsic", "extrinsic"),
  ...
)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

k

the number of clusters.

M

integer number of independent draws, at least kk (default: the larger of kk and ⌈N/2⌉\lceil N/2 \rceil). Values greater than NN are permitted.

geometry

(case-insensitive) name or saved specification of a geometry supporting means. Legacy aliases "intrinsic" and "extrinsic" are accepted; see riem.geometry.

...

extra parameters including

maxiter

maximum number of weighted Lloyd passes per start (default:50).

nstart

the number of random starts (default: 5).

Details

Sampling is with replacement, using the same probabilities and weights as riem.coreset18B. Repeated indices remain separate weighted observations. Each start uses weighted squared-distance initialization and weighted cluster means. Mean solves use at most 200 iterations and tolerance 1e-8; failed mean solves invalidate that start. A Lloyd step that materially increases weighted coreset cost is rejected. No global minimum is guaranteed. Final labels assign every original observation to its nearest stored center, with distance ties choosing the lowest cluster index. A coreset can contain fewer than kk distinct locations; empty centers are retained rather than redrawing and conditioning the sampling distribution. A warning identifies an empty cluster or a nonconverged selected start. Use set.seed() for reproducibility.

Value

a named list containing

cluster

a length-NN vector of class labels (from 1:k1:k).

means

a 3d array where each slice along 3rd dimension is a matrix representation of class mean.

score

unweighted full-data within-cluster sum of squares (WCSS), used to select the best start.

coreset

the selected start's sampled indices and importance weights.

iterations,converged,termination

iteration count, convergence flag, and stopping reason for the selected start.

objective_history

weighted coreset costs, including the initialization.

empty_clusters

cluster indices unoccupied by the full data.

starts

per-start validity, full-data score, convergence, and errors.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Bachem O, Lucic M, Krause A (2018). “Scalable k -Means Clustering via Lightweight Coresets.” In Proceedings of the 24th ACM SIGKDD International Conference on Knowledge Discovery & Data Mining, 1119–1127. ISBN 978-1-4503-5552-0.

See Also

riem.coreset18B

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# class 1 : 10 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 10 perturbed data points near (0,1,0) on S^2 in R^3
# class 3 : 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
mylabs = rep(c(1,2,3), each=10)

## TRY DIFFERENT SIZES OF CORESET WITH K=3 FIXED
core1 = riem.kmeans18B(myriem, k=3, M=5)
core2 = riem.kmeans18B(myriem, k=3, M=10)
core3 = riem.kmeans18B(myriem, k=3, M=15)

## MDS FOR VISUALIZATION
mds2d = riem.mds(myriem, ndim=2)$embed

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(2,2), pty="s")
plot(mds2d, pch=19, main="true label", col=mylabs)
plot(mds2d, pch=19, main="kmeans18B: M=5",  col=core1$cluster)
plot(mds2d, pch=19, main="kmeans18B: M=10", col=core2$cluster)
plot(mds2d, pch=19, main="kmeans18B: M=15", col=core3$cluster)
par(opar)

K-Means++ Clustering

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, perform k-means++ clustering algorithm using pairwise distances. The algorithm was originally designed as an efficient initialization method for k-means algorithm.

Usage

riem.kmeanspp(riemobj, k = 2, geometry = NULL)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

k

the number of clusters.

geometry

a name or saved specification accepted by riem.geometry.

Value

a named list containing

centers

a length-kk vector of sampled centers' indices.

cluster

a length-NN vector of class labels (from 1:k1:k).

References

Arthur D, Vassilvitskii S (2007). “K-Means++: The advantages of careful seeding.” In Proceedings of the eighteenth annual ACM-SIAM symposium on discrete algorithms, SODA '07, 1027–1035. ISBN 978-0-89871-624-5. Number of pages: 9 Place: New Orleans, Louisiana.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# class 1 : 10 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 10 perturbed data points near (0,1,0) on S^2 in R^3
# class 3 : 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
mylabs = rep(c(1,2,3), each=10)

## K-MEANS++ WITH K=2,3,4
clust2 = riem.kmeanspp(myriem, k=2)
clust3 = riem.kmeanspp(myriem, k=3)
clust4 = riem.kmeanspp(myriem, k=4)

## MDS FOR VISUALIZATION
mds2d = riem.mds(myriem, ndim=2)$embed

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(2,2), pty="s")
plot(mds2d, pch=19, main="true label", col=mylabs)
plot(mds2d, pch=19, main="K=2", col=clust2$cluster)
plot(mds2d, pch=19, main="K=3", col=clust3$cluster)
plot(mds2d, pch=19, main="K=4", col=clust4$cluster)
par(opar)

K-Medoids Clustering

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, perform k-medoids clustering using pairwise distances.

Usage

riem.kmedoids(riemobj, k = 2, geometry = c("intrinsic", "extrinsic"))

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

k

the number of clusters.

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

Value

a named list containing

medoids

a length-kk vector of medoids' indices.

cluster

a length-NN vector of class labels (from 1:k1:k).

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

See Also

pam

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# class 1 : 10 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 10 perturbed data points near (0,1,0) on S^2 in R^3
# class 3 : 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
mylabs = rep(c(1,2,3), each=10)

## K-MEDOIDS WITH K=2,3,4
clust2 = riem.kmedoids(myriem, k=2)
clust3 = riem.kmedoids(myriem, k=3)
clust4 = riem.kmedoids(myriem, k=4)

## MDS FOR VISUALIZATION
mds2d = riem.mds(myriem, ndim=2)$embed

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(2,2), pty="s")
plot(mds2d, pch=19, main="true label", col=mylabs)
plot(mds2d, pch=19, main="K=2", col=clust2$cluster)
plot(mds2d, pch=19, main="K=3", col=clust3$cluster)
plot(mds2d, pch=19, main="K=4", col=clust4$cluster)
par(opar)

Find K-Nearest Neighbors

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, riem.knn constructs kk-nearest neighbors. Each observation is excluded from its own neighbor set. Distinct observations at zero distance remain eligible; distance ties are resolved by input index.

Usage

riem.knn(riemobj, k = 2, geometry = c("intrinsic", "extrinsic"))

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

k

an integer between one and N−1N-1, the number of neighbors to find.

geometry

a supported geometry name or saved specification; see riem.geometry. The default is "intrinsic".

Value

a named list containing

nn.idx

an (N×k)(N \times k) neighborhood index matrix.

nn.dists

an (N×k)(N\times k) distances from a point to its neighbors.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# * 10 perturbed data points near (1,0,0) on S^2 in R^3
# * 10 perturbed data points near (0,1,0) on S^2 in R^3
# * 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
mylabs = rep(c(2,3,4), each=10)

## K-NN CONSTRUCTION WITH K=5 & K=10
knn1 = riem.knn(myriem, k=5)
knn2 = riem.knn(myriem, k=10)

## MDS FOR VISUALIZATION
embed2 = riem.mds(myriem, ndim=2)$embed

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,2), pty="s")
plot(embed2, pch=19, main="knn with k=4", col=mylabs)
for (i in 1:30){
  for (j in 1:5){
    lines(embed2[c(i,knn1$nn.idx[i,j]),])
  }
}
plot(embed2, pch=19, main="knn with k=8", col=mylabs)
for (i in 1:30){
  for (j in 1:10){
    lines(embed2[c(i,knn2$nn.idx[i,j]),])
  }
}
par(opar)

Kernel Principal Component Analysis

Description

Although the method of Kernel Principal Component Analysis (KPCA) was originally developed to visualize non-linearly distributed data in Euclidean space, we graft this to the case for manifolds where extrinsic geometry is explicitly available. The algorithm uses Gaussian kernel with

K(Xi,Xj)=exp⁡(−d2(Xi,Xj)2σ2)K(X_i, X_j) = \exp\left( - \frac{d^2 (X_i, X_j)}{2 \sigma^2} \right )

where σ\sigma is a bandwidth parameter and d(⋅,⋅)d(\cdot, \cdot) is an extrinsic distance defined on a specific manifold.

Usage

riem.kpca(riemobj, ndim = 2, sigma = 1, negative = c("truncate", "error"))

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

ndim

an integer-valued target dimension (default: 2).

sigma

A finite, strictly positive Gaussian bandwidth.

negative

Treatment of an empirically indefinite kernel: "truncate" warns and returns a positive-spectrum embedding; "error" stops.

Details

Scores are eigenvectors multiplied by square roots of the centered Gram eigenvalues. They are ordered by decreasing eigenvalue. A Gaussian of an arbitrary manifold dissimilarity is not automatically a positive-definite kernel. For an indefinite Gram matrix, truncated coordinates are an approximation, not exact kernel principal component scores. The full spectrum, negative eigenvalues, and empirical kernel status are returned. This function remains a training-data embedding, without a predict method.

Value

a named list containing

embed

an (N×ndim)(N\times ndim) matrix whose rows are embedded observations.

vars

a length-NN vector of eigenvalues from kernelized covariance matrix.

References

Schölkopf B, Smola A, Müller K (1997). “Kernel principal component analysis.” In Goos G, Hartmanis J, van Leeuwen J, Gerstner W, Germond A, Hasler M, Nicoud J (eds.), Artificial Neural Networks — ICANN'97, volume 1327, 583–588. Springer Berlin Heidelberg, Berlin, Heidelberg. ISBN 978-3-540-63631-1 978-3-540-69620-9.

Examples

#-------------------------------------------------------------------
#          Example for Gorilla Skull Data : 'gorilla'
#-------------------------------------------------------------------
## PREPARE THE DATA
#  Aggregate two classes into one set
data(gorilla)

mygorilla = array(0,c(8,2,59))
for (i in 1:29){
  mygorilla[,,i] = gorilla$male[,,i]
}
for (i in 30:59){
  mygorilla[,,i] = gorilla$female[,,i-29]
}

gor.riem = wrap.landmark(mygorilla)
gor.labs = c(rep("red",29), rep("blue",30))

## APPLY KPCA WITH DIFFERENT KERNEL BANDWIDTHS
kpca1 = riem.kpca(gor.riem, sigma=0.01)
kpca2 = riem.kpca(gor.riem, sigma=1)
kpca3 = riem.kpca(gor.riem, sigma=100)
## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,3), pty="s")
plot(kpca1$embed, pch=19, col=gor.labs, main="sigma=1/100")
plot(kpca2$embed, pch=19, col=gor.labs, main="sigma=1")
plot(kpca3$embed, pch=19, col=gor.labs, main="sigma=100")
par(opar)

Manifold-to-Scalar Kernel Regression

Description

Fits the Nadaraya–Watson smoother for manifold-valued predictors and finite real scalar responses. For bandwidth h>0h>0, weights at xx are proportional to exp⁡{−d(x,Xi)2/(2h2)}\exp\{-d(x,X_i)^2/(2h^2)\}. The selected distance and training observations are retained for prediction. Training fitted values include the observation's own weight; they are not cross-validated predictions.

Usage

riem.m2skreg(riemobj, y, bandwidth = 0.5, geometry = NULL)

Arguments

riemobj

A riemdata object containing the training predictors.

y

A finite numeric response vector with one entry per predictor.

bandwidth

One finite, strictly positive bandwidth.

geometry

Geometry name or specification. NULL resolves the input's geometry, defaulting to its intrinsic geometry when unspecified. Legacy "intrinsic" and "extrinsic" aliases are accepted.

Details

Weights are normalized after subtracting the smallest squared distance in the exponent. The difference is evaluated without first squaring the distances, avoiding all-zero weights for small bandwidths or distant predictions. Numerically negligible relative weights can still underflow to zero. This is floating-point evaluation of the Gaussian smoother, not a change to a nearest-neighbor model.

The effective weight count and nearest training distance divided by the bandwidth describe the weights and distance scale. They are not confidence intervals or assurances of adequate statistical support.

Value

An object of class m2skreg. Legacy fields ypred, bandwidth, and inputs are retained. Additional fields include resolved geometry, call, input dimensions, schema and package versions, and training_diagnostics. No distance matrix is retained.

Examples

theta <- seq(0, pi / 2, length.out = 8)
X <- wrap.sphere(cbind(cos(theta), sin(theta)))
fit <- riem.m2skreg(X, sin(2 * theta), bandwidth = 0.3)
predict(fit, X)
summary(fit)

Manifold-to-Scalar Kernel Regression with K-Fold Cross Validation

Description

Selects a Gaussian kernel bandwidth by the sum of squared held-out prediction errors over all folds, then fits the selected smoother to all observations. A candidate is eligible only if every fold has a finite loss. Failed candidates remain in the returned tables with infinite total loss and a recorded reason. If no candidate succeeds, fitting stops with an error. Equal losses are resolved by choosing the first candidate in the supplied order.

Usage

riem.m2skregCV(
  riemobj,
  y,
  bandwidths = seq(0.01, 1, length.out = 10),
  geometry = NULL,
  kfold = 5,
  foldid = NULL
)

Arguments

riemobj

A riemdata object containing the training predictors.

y

A finite numeric response vector with one entry per predictor.

bandwidths

A nonempty vector of finite, strictly positive bandwidths.

geometry

Geometry name or specification. NULL resolves the input's geometry, defaulting to its intrinsic geometry when unspecified. Legacy "intrinsic" and "extrinsic" aliases are accepted.

kfold

Integer number of folds between two and the sample size. Random balanced folds use R's current random-number state. When foldid is supplied, an explicitly supplied kfold must agree with its number of distinct folds.

foldid

Optional vector of fold identifiers, one per observation, with at least two distinct nonmissing identifiers. Numeric identifiers must be finite; character and factor identifiers must be nonempty. Observations with the same identifier are held out together. Use this to encode grouped or otherwise scientifically appropriate validation splits.

Details

Distances are computed once under the resolved geometry. This is appropriate only when preprocessing defining that geometry was fixed independently of the held-out observations. This function does not fit data-dependent alignment, scaling, or other preprocessing inside each fold; such pipelines require an external resampling loop. The selected CV loss is a tuning criterion, not an unbiased estimate of final predictive performance.

Value

An m2skreg fit retaining ypred, bandwidth, and inputs, with the geometry metadata of riem.m2skreg. ypred contains full-training fitted values. errors is a two-column matrix of all candidate bandwidths and total CV SSE; fold_errors and fold_failure have candidates in rows and folds in columns. candidate_status records success or failure, foldid stores the supplied/generated fold identifiers, and cv_prediction contains held-out predictions for the selected candidate.

Examples

X <- wrap.euclidean(matrix(0:5, ncol = 1))
fit <- riem.m2skregCV(X, c(0, 3, 0, 0, 0, 0),
  bandwidths = c(0.1, 0.5, 1, 2, 10), foldid = rep(1:3, each = 2))
fit$errors
fit$cv_prediction

Multidimensional Scaling

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, apply multidimensional scaling to get low-dimensional embedding in Euclidean space. Usually, ndim=2,3 are chosen for visualization.

Usage

riem.mds(riemobj, ndim = 2, geometry = NULL, negative = c("truncate", "error"))

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

ndim

an integer-valued target dimension (default: 2).

geometry

A geometry name or saved specification.

negative

Treatment of materially negative eigenvalues: "truncate" returns positive-spectrum coordinates and retains the full spectrum; "error" rejects a non-Euclidean dissimilarity matrix.

Details

Coordinates are ordered by decreasing positive eigenvalue. Requested dimensions beyond positive rank are zero-filled and recorded separately. The returned spectrum reports non-Euclidean dissimilarity rather than taking square roots of negative values. Stress is normalized distance stress; it is zero for constant data. No new-data prediction is defined by this display.

Value

a named list containing

embed

an (N×ndim)(N\times ndim) matrix whose rows are embedded observations.

stress

discrepancy between embedded and original distances as a measure of error.

References

Torgerson WS (1952). “Multidimensional scaling: I. Theory and method.” Psychometrika, 17(4), 401–419. ISSN 0033-3123, 1860-0980.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# 10 perturbed data points near (1,0,0) on S^2 in R^3
# 10 perturbed data points near (0,1,0) on S^2 in R^3
# 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
mylabs = rep(c(1,2,3), each=10)

## MDS EMBEDDING WITH TWO GEOMETRIES
embed2int = riem.mds(myriem, geometry="intrinsic")$embed
embed2ext = riem.mds(myriem, geometry="extrinsic")$embed

## VISUALIZE
opar = par(no.readonly=TRUE)
par(mfrow=c(1,2), pty="s")
plot(embed2int, main="intrinsic MDS", ylim=c(-2,2), col=mylabs, pch=19)
plot(embed2ext, main="extrinsic MDS", ylim=c(-2,2), col=mylabs, pch=19)
par(opar)

Fréchet Mean and Variation

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, compute Fréchet mean and variation with respect to the geometry by minimizing

minx∑n=1Nwnρ2(x,xn),x∈M\textrm{min}_x \sum_{n=1}^N w_n \rho^2 (x, x_n),\quad x\in\mathcal{M}

where ρ(x,y)\rho (x, y) is a distance for two points x,y∈Mx,y\in\mathcal{M}. If non-uniform weights are given, normalized version of the mean is computed and if weight=NULL, it automatically sets equal weights (wi=1/nw_i = 1/n) for all observations.

Usage

riem.mean(riemobj, weight = NULL, geometry = NULL, ...)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

weight

Finite nonnegative observation weights with positive sum. They are normalized internally. NULL uses equal weights. Zero-weight observations are validated but do not enter the calculation.

geometry

Geometry name or specification. NULL selects the default geometry. Legacy "intrinsic" and "extrinsic" aliases remain available for supported combinations. SPD names include "affine_invariant" and "log_euclidean".

...

Named controls:

maxiter

Positive maximum number of accepted iterations (default 50).

eps

Positive absolute tolerance for the compatible gradient norm (default 1e-5).

max_backtrack

Positive maximum number of trial steps per iteration, at most 1024 (default 50).

trace

Whether to retain the iteration history (default FALSE).

init

Optional initial matrix with the observation dimensions. Closed-form calculations do not use an initializer.

Unknown controls are errors.

Details

Intrinsic iteration uses the average logarithm direction and an Armijo backtracking rule for the stated squared-distance objective. Euclidean and log-Euclidean SPD means use closed forms. On spaces with nonconvex objectives, termination with "stationary" establishes first-order stationarity to the requested tolerance only. A stationary point may be a local minimum, a saddle, or a local maximum; the criterion does not establish local minimality, uniqueness, or global optimality.

Value

A riem_summary object retaining mean and variation, plus objective, resolved geometry, normalized weights, converged, termination, iterations, gradient_norm, step_norm, controls, and an optional trace. variation equals the normalized weighted sum of squared distances at the returned mean. An unsuccessful iteration warns and returns the last accepted estimate. For a curved extrinsic embedding, diagnostics and trace refer to the ambient calculation, as recorded in diagnostic_scope.

Examples

#-------------------------------------------------------------------
#        Example on Sphere : points near (0,1) on S^1 in R^2
#-------------------------------------------------------------------
## GENERATE DATA
ndata = 50
mydat = array(0,c(ndata,2))
for (i in 1:ndata){
  tgt = c(stats::rnorm(1, sd=2), 1)
  mydat[i,] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydat)

## COMPUTE TWO MEANS
mean.int = as.vector(riem.mean(myriem, geometry="intrinsic")$mean)
mean.ext = as.vector(riem.mean(myriem, geometry="extrinsic")$mean)

## VISUALIZE
opar <- par(no.readonly=TRUE)
plot(mydat[,1], mydat[,2], pch=19, xlim=c(-1.1,1.1), ylim=c(0,1.1),
     main="BLUE-extrinsic vs RED-intrinsic")
arrows(x0=0,y0=0,x1=mean.int[1],y1=mean.int[2],col="red")
arrows(x0=0,y0=0,x1=mean.ext[1],y1=mean.ext[2],col="blue")
par(opar)

Fréchet Median and Variation

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, compute Fréchet median and variation with respect to the geometry by minimizing

minx∑n=1Nwnρ(x,xn),x∈M\textrm{min}_x \sum_{n=1}^N w_n \rho (x, x_n),\quad x\in\mathcal{M}

where ρ(x,y)\rho (x, y) is a distance for two points x,y∈Mx,y\in\mathcal{M}. If non-uniform weights are given, normalized version of the median is computed and if weight=NULL, it automatically sets equal weights for all observations.

Usage

riem.median(riemobj, weight = NULL, geometry = NULL, ...)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

weight

Finite nonnegative observation weights with positive sum. They are normalized internally. NULL uses equal weights. Zero-weight observations are validated but do not enter the calculation.

geometry

Geometry name or specification. NULL selects the default geometry. Legacy "intrinsic" and "extrinsic" aliases remain available for supported combinations. SPD names include "affine_invariant" and "log_euclidean".

...

Named controls:

maxiter

Positive maximum number of accepted iterations (default 50).

eps

Positive absolute tolerance for the compatible subgradient residual (default 1e-5).

max_backtrack

Positive maximum number of trial steps per iteration, at most 1024 (default 50).

trace

Whether to retain the iteration history (default FALSE).

init

Optional initial matrix with the observation dimensions. Closed-form calculations do not use an initializer.

Unknown controls are errors.

Details

Intrinsic medians use a modified Weiszfeld direction with backtracking. Coincident observations retain their subgradient mass, and an observation is accepted as a nonsmooth solution only after the relevant certificate is checked. On nonconvex manifolds, stationarity is local.

For the log-Euclidean SPD chart, the extrinsic route computes the geometric median of matrix logarithms and exponentiates it. For a curved embedding such as the sphere or Grassmann manifold, the returned estimator is the projection of an ambient geometric median. It need not minimize the sum of chordal distances constrained to the manifold. The estimand and diagnostic_scope fields identify this distinction; convergence and trace describe the ambient optimization. ambient_objective records its cost before projection. An ambiguous inverse projection is an error.

Value

A riem_summary object retaining median and variation, plus objective, resolved geometry, normalized weights, converged, termination, iterations, subgradient_residual when applicable, controls, and an optional trace. variation equals the normalized weighted sum of distances at the returned matrix; for a projected ambient median this is a descriptive manifold cost, not the optimized ambient objective. An unsuccessful iteration warns and returns the last accepted estimate.

Examples

#-------------------------------------------------------------------
#        Example on Sphere : points near (0,1) on S^1 in R^2
#-------------------------------------------------------------------
## GENERATE DATA
ndata = 50
mydat = array(0,c(ndata,2))
for (i in 1:ndata){
  tgt = c(stats::rnorm(1, sd=2), 1)
  mydat[i,] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydat)

## COMPUTE TWO MEANS
med.int = as.vector(riem.median(myriem, geometry="intrinsic")$median)
med.ext = as.vector(riem.median(myriem, geometry="extrinsic")$median)

## VISUALIZE
opar <- par(no.readonly=TRUE)
plot(mydat[,1], mydat[,2], pch=19, xlim=c(-1.1,1.1), ylim=c(0,1.1),
     main="BLUE-extrinsic vs RED-intrinsic")
arrows(x0=0,y0=0,x1=med.int[1],y1=med.int[2],col="red")
arrows(x0=0,y0=0,x1=med.ext[1],y1=med.ext[2],col="blue")
par(opar)

Nonlinear Mean Shift

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, perform clustering of the data based on the nonlinear mean shift algorithm. Gaussian kernel is used with the bandwidth hh as of

G(xi,xj)∝exp⁡(−ρ2(xi,xj)h2)G(x_i, x_j) \propto \exp \left( - \frac{\rho^2 (x_i,x_j)}{h^2} \right)

where ρ(x,y)\rho(x,y) is geodesic distance between two points x,y∈Mx,y\in\mathcal{M}. Numerically, some of the limiting points that collapse into the same cluster are not exact. For such purpose, we require maxk parameter to search the optimal number of clusters based on kk-medoids clustering algorithm in conjunction with silhouette criterion.

Usage

riem.nmshift(riemobj, h = 1, maxk = 5, maxiter = 50, eps = 1e-05)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

h

bandwidth parameter. The larger the hh is, the more blurring is applied.

maxk

maximum number of clusters to determine the optimal number of clusters.

maxiter

maximum number of iterations to be run.

eps

tolerance level for stopping criterion.

Value

a named list containing

distance

an (N×N)(N\times N) distance between modes corresponding to each data point.

cluster

a length-NN vector of class labels.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Subbarao R, Meer P (2009). “Nonlinear Mean Shift over Riemannian Manifolds.” International Journal of Computer Vision, 84(1), 1–20. ISSN 0920-5691, 1573-1405.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# class 1 : 10 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 10 perturbed data points near (0,1,0) on S^2 in R^3
# class 3 : 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
set.seed(496)
ndata  = 10
mydata = list()
for (i in 1:ndata){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in (ndata+1):(2*ndata)){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in ((2*ndata)+1):(3*ndata)){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
mylabs = rep(c(1,2,3), each=ndata)

## RUN NONLINEAR MEANSHIFT FOR DIFFERENT 'h' VALUES
run1 = riem.nmshift(myriem, maxk=10, h=0.1)
run2 = riem.nmshift(myriem, maxk=10, h=1)
run3 = riem.nmshift(myriem, maxk=10, h=10)

## MDS FOR VISUALIZATION
mds2d = riem.mds(myriem, ndim=2)$embed

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(2,3), pty="s")
plot(mds2d, pch=19, main="label : h=0.1", col=run1$cluster)
plot(mds2d, pch=19, main="label : h=1",   col=run2$cluster)
plot(mds2d, pch=19, main="label : h=10",  col=run3$cluster)
image(run1$distance[,30:1], axes=FALSE, main="distance : h=0.1")
image(run2$distance[,30:1], axes=FALSE, main="distance : h=1")
image(run3$distance[,30:1], axes=FALSE, main="distance : h=10")
par(opar)

Compute Pairwise Distances for Data

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, compute pairwise distances.

Usage

riem.pdist(riemobj, geometry = NULL, as.dist = FALSE)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

geometry

A name or saved specification accepted by riem.geometry. NULL uses the object's saved geometry, or intrinsic geometry for an ordinary wrapper.

as.dist

logical; if TRUE, it returns dist object, else it returns a symmetric matrix.

Value

a S3 dist object or (N×N)(N\times N) symmetric matrix of pairwise distances according to as.dist parameter.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with two types
#
#  group1 : perturbed data points near (0,0,1) on S^2 in R^3
#  group2 : perturbed data points near (1,0,0) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
sdval  = 0.1
for (i in 1:10){
  tgt = c(stats::rnorm(2, sd=sdval), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(1, stats::rnorm(2, sd=sdval))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)

## COMPARE TWO DISTANCES
dint = riem.pdist(myriem, geometry="intrinsic", as.dist=FALSE)
dext = riem.pdist(myriem, geometry="extrinsic", as.dist=FALSE)

## VISUALIZE
opar = par(no.readonly=TRUE)
par(mfrow=c(1,2), pty="s")
image(dint[,nrow(dint):1], main="intrinsic", axes=FALSE)
image(dext[,nrow(dext):1], main="extrinsic", axes=FALSE)
par(opar)

Compute Pairwise Distances for Two Sets of Data

Description

Given MM observations X1,X2,…,XM∈MX_1, X_2, \ldots, X_M \in \mathcal{M} and NN observations Y1,Y2,…,YN∈MY_1, Y_2, \ldots, Y_N \in \mathcal{M}, compute pairwise distances between two sets' elements.

Usage

riem.pdist2(riemobj1, riemobj2, geometry = NULL)

Arguments

riemobj1

a S3 "riemdata" class for MM manifold-valued data.

riemobj2

a S3 "riemdata" class for NN manifold-valued data.

geometry

A name or saved specification accepted by riem.geometry.

Value

an (M×N)(M\times N) matrix of distances.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with two types
#
#  group1 : 10 perturbed data points near (0,0,1) on S^2 in R^3
#  group2 : 10 perturbed data points near (1,0,0) on S^2 in R^3
#           10 perturbed data points near (0,1,0) on S^2 in R^3
#           10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata1 = list()
mydata2 = list()
for (i in 1:10){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata1[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata2[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata2[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata2[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem1 = wrap.sphere(mydata1)
myriem2 = wrap.sphere(mydata2)

## COMPARE TWO DISTANCES
dint = riem.pdist2(myriem1, myriem2, geometry="intrinsic")
dext = riem.pdist2(myriem1, myriem2, geometry="extrinsic")

## VISUALIZE
opar = par(no.readonly=TRUE)
par(mfrow=c(1,2))
image(dint[nrow(dint):1,], main="intrinsic", axes=FALSE)
image(dext[nrow(dext):1,], main="extrinsic", axes=FALSE)
par(opar)

Tangent Principal Component Analysis

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, Principal Geodesic Analysis (PGA) finds a low-dimensional embedding by decomposing 2nd-order information in tangent space at an intrinsic mean of the data.

Usage

riem.pga(
  riemobj,
  ndim = 2,
  geometry = NULL,
  weight = NULL,
  maxiter = 200,
  eps = 1e-08,
  rank.tol = sqrt(.Machine$double.eps),
  center.tangent = TRUE
)

## S3 method for class 'riem_pga'
predict(object, newdata, ...)

## S3 method for class 'riem_pga'
riem.reconstruct(object, scores = object$embed, ...)

## S3 method for class 'riem_pga'
print(x, ...)

## S3 method for class 'riem_pga'
summary(object, ...)

## S3 method for class 'summary.riem_pga'
print(x, ...)

## S3 method for class 'riem_pga'
plot(x, components = c(1L, 2L), ...)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

ndim

positive integer requested number of components. Requests above numerical rank are truncated with a warning.

geometry

a supported geometry name or saved geometry specification. Defaults to the geometry of the data. Supports affine-invariant and log-Euclidean SPD, round spheres, Euclidean observations, and regular orthogonal-quotient landmark shapes.

weight

nonnegative observation weights, normalized to sum to one.

maxiter

maximum iterations for the reference mean.

eps

convergence tolerance for the reference mean.

rank.tol

relative singular-value tolerance for numerical rank.

center.tangent

whether to subtract the weighted tangent-coordinate mean before decomposition. The fitted offset is retained for reuse.

object

a fitted tangent PCA object.

newdata

a compatible riemdata object.

...

additional arguments; currently unused.

scores

numeric matrix with one column per retained component; by default, the fitted training scores are reconstructed.

x

a fitted tangent PCA object.

components

two retained component indices to display.

Details

This is tangent PCA, a local linear approximation, rather than an optimization over principal geodesic submanifolds. SPD coordinates preserve the selected metric using symmetric vectorization with square-root-of-two off-diagonal weights. Affine-invariant coordinates are whitened at the mean; log-Euclidean coordinates use the matrix-log chart. Sphere coordinates use an orthonormal tangent basis. Landmark coordinates use horizontal log vectors after alignment to the trained reference under the full orthogonal group (reflections included); singular alignments are rejected.

Component variances use normalized weights divided by 1 - sum(weight^2), agreeing with sample PCA for equal weights. This denominator is evaluated as twice the sum of positive pair products to avoid cancellation with highly unequal weights. Positive weights that underflow to zero during normalization are rejected. With only one positive weight, the centered fit has zero rank and zero variance by convention; this is not an estimate of a sample covariance. An uncentered fit requires at least two positive weights. Explained variance describes the tangent representation, not exact global manifold variation. A nonconverged reference mean causes an error. Prediction retains the trained mean, coordinate convention, offset and loadings. Reconstruction returns wrapped observations in this same frame. Sphere reconstruction is restricted to tangent norms below pi; landmark reconstruction is restricted below pi/2 and to regular shapes.

Value

a named list containing

center

an intrinsic mean in a matrix representation form.

embed

an NN-by-effective-rank matrix of component scores.

loadings

orthonormal directions in the stored tangent coordinates.

geometry

the persistent geometry specification.

offset

the fitted tangent-coordinate offset.

rank

numerical rank of the weighted coordinate matrix.

variance

component variances in decreasing order.

diagnostics

reference-mean convergence and approximation information.

References

Fletcher PT, Lu C, Pizer SM, Joshi S (2004). “Principal Geodesic Analysis for the Study of Nonlinear Statistics of Shape.” IEEE Transactions on Medical Imaging, 23(8), 995–1005. ISSN 0278-0062.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# 10 perturbed data points near (1,0,0) on S^2 in R^3
# 10 perturbed data points near (0,1,0) on S^2 in R^3
# 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
mylabs = rep(c(1,2,3), each=10)

## EMBEDDING WITH MDS AND PGA
embed2mds = riem.mds(myriem, ndim=2, geometry="intrinsic")$embed
embed2pga = riem.pga(myriem, ndim=2)$embed

## VISUALIZE
opar = par(no.readonly=TRUE)
par(mfrow=c(1,2), pty="s")
plot(embed2mds, main="Multidimensional Scaling",    col=mylabs, pch=19)
plot(embed2pga, main="Principal Geodesic Analysis", col=mylabs, pch=19)
par(opar)

PHATE

Description

PHATE is a nonlinear manifold learning method that is specifically targeted at improving diffusion maps by incorporating data-adaptive kernel construction, detection of optimal time scale, and information-theoretic metric measures.

Usage

riem.phate(riemobj, ndim = 2, geometry = c("intrinsic", "extrinsic"), ...)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

ndim

an integer-valued target dimension (default: 2).

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

...

extra parameters for PHATE including

nbdk

size of nearest neighborhood (default: 5).

alpha

decay parameter for Gaussian kernel exponent (default: 2).

potential

type of potential distance transformation; "log" or "sqrt" (default: "log").

Value

a named list containing

embed

an (N×ndim)(N\times ndim) matrix whose rows are embedded observations.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Moon KR, van Dijk D, Wang Z, Gigante S, Burkhardt DB, Chen WS, Yim K, van den Elzen A, Hirn MJ, Coifman RR, Ivanova NB, Wolf G, Krishnaswamy S (2019). “Visualizing Structure and Transitions in High-Dimensional Biological Data.” Nature Biotechnology, 37(12), 1482–1492. ISSN 1087-0156, 1546-1696.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# 10 perturbed data points near (1,0,0) on S^2 in R^3
# 10 perturbed data points near (0,1,0) on S^2 in R^3
# 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
mylabs = rep(c(1,2,3), each=10)

## PHATE EMBEDDING WITH LOG & SQRT POTENTIAL 
phate_log  = riem.phate(myriem, potential="log")$embed
phate_sqrt = riem.phate(myriem, potential="sqrt")$embed
embed_mds  = riem.mds(myriem)$embed

## VISUALIZE
opar = par(no.readonly=TRUE)
par(mfrow=c(1,3), pty="s")
plot(embed_mds,  col=mylabs, pch=19, main="MDS" )
plot(phate_log,  col=mylabs, pch=19, main="PHATE+Log")
plot(phate_sqrt, col=mylabs, pch=19, main="PHATE+Sqrt")
par(opar)

Reconstruct Manifold Observations from Fitted Coordinates

Description

Reconstruct observations using the trained coordinate system of a model.

Usage

riem.reconstruct(object, ...)

Arguments

object

a fitted model with a reconstruction method.

...

arguments passed to a reconstruction method.

Value

a riemdata object in the fitted geometry and representation.


Riemannian Manifold Metric Learning

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M} and corresponding label information, riem.rmml computes pairwise distance of data under Riemannian Manifold Metric Learning (RMML) framework based on equivariant embedding. When the number of data points is not sufficient, an inverse of scatter matrix does not exist analytically so the small regularization parameter λ\lambda is recommended with default value of λ=0.1\lambda=0.1.

Usage

riem.rmml(riemobj, label, lambda = 0.1, as.dist = FALSE)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

label

a length-NN vector of class labels. NA values are omitted.

lambda

regularization parameter. If λ≤0\lambda \leq 0, no regularization is applied.

as.dist

logical; if TRUE, it returns dist object, else it returns a symmetric matrix.

Details

Embedding dimensions are determined from the manifold's actual equivariant embedding. In particular, a Grassmann frame is represented by its full orthogonal projector, so the result is unchanged by a change of basis within each represented subspace.

Value

a S3 dist object or symmetric matrix of pairwise distances for the observations with nonmissing labels, in their original order, according to as.dist. At least two labeled observations are required.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Zhu P, Cheng H, Hu Q, Wang Q, Zhang C (2018). “Towards Generalized and Efficient Metric Learning on Riemannian Manifold.” In Proceedings of the Twenty-Seventh International Joint Conference on Artificial Intelligence, 3235–3241. ISBN 978-0-9992411-2-7.

Examples

#-------------------------------------------------------------------
#            Distance between Two Classes of SPD Matrices
#
#  Class 1 : Empirical Covariance from Standard Normal Distribution
#  Class 2 : Empirical Covariance from Perturbed 'iris' dataset
#-------------------------------------------------------------------
## DATA GENERATION
data(iris)
ndata  = 10
mydata = list()
for (i in 1:ndata){
  mydata[[i]] = stats::cov(matrix(rnorm(100*4),ncol=4))
}
for (i in (ndata+1):(2*ndata)){
  tmpdata = as.matrix(iris[,1:4]) + matrix(rnorm(150*4,sd=0.5),ncol=4)
  # This matrix-entry demonstration uses positional coordinates in both groups.
  mydata[[i]] = unname(stats::cov(tmpdata))
}
myriem = wrap.spd(mydata)
mylabs = rep(c(1,2), each=ndata)

## COMPUTE GEODESIC AND RMML PAIRWISE DISTANCE
pdgeo = riem.pdist(myriem)
pdmdl = riem.rmml(myriem, label=mylabs)

## VISUALIZE
opar = par(no.readonly=TRUE)
par(mfrow=c(1,2), pty="s")
image(pdgeo[,(2*ndata):1], main="geodesic distance", axes=FALSE)
image(pdmdl[,(2*ndata):1], main="RMML distance", axes=FALSE)
par(opar)

Sammon Mapping

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, apply Sammon mapping, a non-linear dimensionality reduction method. Since the method depends only on the pairwise distances of the data, it can be adapted to the manifold-valued data.

Usage

riem.sammon(riemobj, ndim = 2, geometry = c("intrinsic", "extrinsic"), ...)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

ndim

a positive integer target dimension smaller than the number of observations (default: 2).

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

...

named controls including

maxiter

positive maximum number of iterations (default: 50).

eps

nonnegative tolerance for the root mean squared coordinate step, after distances are divided by their maximum (default: 1e-5).

Unknown controls are errors.

Details

The optimized Sammon loss is ∑i<j(Dij−dij)2/Dij / ∑i<jDij\sum_{i<j}(D_{ij}-d_{ij})^2/D_{ij}\,/\,\sum_{i<j}D_{ij}, where DD and dd are the original and embedded distances. Every off-diagonal original distance must be strictly positive and finite; coincident observations (including different representations of the same point) must be removed before fitting because this loss divides by DijD_{ij}. Distances are normalized internally, making the stopping tolerance independent of a common change of measurement units. Classical scaling initializes the coordinates, with negative eigenvalues truncated to zero and coincident projected points separated by a small deterministic perturbation. Diagonal-Hessian updates use backtracking to decrease the Sammon loss. If no finite decreasing step can be found, the last accepted coordinates are returned. This local procedure does not establish a global minimum.

Value

a named list containing

embed

an (N×ndim)(N\times ndim) matrix whose rows are embedded observations.

stress

normalized distance stress, ∑i<j(Dij−dij)2/∑i<jDij2\sqrt{\sum_{i<j}(D_{ij}-d_{ij})^2/\sum_{i<j}D_{ij}^2}. This legacy diagnostic differs from the optimized Sammon loss.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Sammon JW (1969). “A Nonlinear Mapping for Data Structure Analysis.” IEEE Transactions on Computers, C-18(5), 401–409. ISSN 0018-9340.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# 10 perturbed data points near (1,0,0) on S^2 in R^3
# 10 perturbed data points near (0,1,0) on S^2 in R^3
# 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
mylabs = rep(c(1,2,3), each=10)

## COMPARE SAMMON WITH MDS
embed2mds = riem.mds(myriem, ndim=2)$embed
embed2sam = riem.sammon(myriem, ndim=2)$embed

## VISUALIZE
opar = par(no.readonly=TRUE)
par(mfrow=c(1,2), pty="s")
plot(embed2mds, col=mylabs, pch=19, main="MDS")
plot(embed2sam, col=mylabs, pch=19, main="Sammon mapping")
par(opar)

Spectral Clustering by Zelnik-Manor and Perona (2005)

Description

Zelnik-Manor and Perona proposed a method to define a set of data-driven bandwidth parameters where σi\sigma_i is the distance from a point xix_i to its nnbd-th nearest neighbor. Then the affinity matrix is defined as

Aij=exp⁡(−d(xi,dj)2/σiσj)A_{ij} = \exp(-d(x_i, d_j)^2 / \sigma_i \sigma_j)

and the standard spectral clustering of Ng, Jordan, and Weiss (riem.scNJW) is applied.

Usage

riem.sc05Z(riemobj, k = 2, nnbd = 7, geometry = c("intrinsic", "extrinsic"))

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

k

the number of clusters (default: 2).

nnbd

neighborhood size to define data-driven bandwidth parameter (default: 7).

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

Value

a named list containing

cluster

a length-NN vector of class labels (from 1:k1:k).

eigval

eigenvalues of the graph laplacian's spectral decomposition.

embeds

an (N×k)(N\times k) low-dimensional embedding.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Zelnik-manor L, Perona P (2005). "Self-Tuning Spectral Clustering." In Saul LK, Weiss Y, Bottou L (eds.), Advances in Neural Information Processing Systems 17, 1601-1608. MIT Press.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# class 1 : 10 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 10 perturbed data points near (0,1,0) on S^2 in R^3
# class 3 : 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
lab    = rep(c(1,2,3), each=10)

## CLUSTERING WITH DIFFERENT K VALUES
cl2 = riem.sc05Z(myriem, k=2)$cluster
cl3 = riem.sc05Z(myriem, k=3)$cluster
cl4 = riem.sc05Z(myriem, k=4)$cluster

## MDS FOR VISUALIZATION
mds2d = riem.mds(myriem, ndim=2)$embed

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,4), pty="s")
plot(mds2d, col=lab, pch=19, main="true label")
plot(mds2d, col=cl2, pch=19, main="riem.sc05Z: k=2")
plot(mds2d, col=cl3, pch=19, main="riem.sc05Z: k=3")
plot(mds2d, col=cl4, pch=19, main="riem.sc05Z: k=4")
par(opar)

Spectral Clustering by Ng, Jordan, and Weiss (2002)

Description

The version of Ng, Jordan, and Weiss first constructs the affinity matrix

Aij=exp⁡(−d(xi,dj)2/σ2)A_{ij} = \exp(-d(x_i, d_j)^2 / \sigma^2)

where σ\sigma is a common bandwidth parameter and performs k-means clustering on the row-space of eigenvectors for the symmetric graph laplacian matrix

L=D−1/2(D−A)D−1/2L=D^{-1/2}(D-A)D^{-1/2}

.

Usage

riem.scNJW(riemobj, k = 2, sigma = 1, geometry = c("intrinsic", "extrinsic"))

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

k

the number of clusters (default: 2).

sigma

bandwidth parameter (default: 1).

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

Value

a named list containing

cluster

a length-NN vector of class labels (from 1:k1:k).

eigval

eigenvalues of the graph laplacian's spectral decomposition.

embeds

an (N×k)(N\times k) low-dimensional embedding.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Ng AY, Jordan MI, Weiss Y (2002). "On Spectral Clustering: Analysis and an Algorithm." In Dietterich TG, Becker S, Ghahramani Z (eds.), Advances in Neural Information Processing Systems 14, 849-856. MIT Press.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# class 1 : 10 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 10 perturbed data points near (0,1,0) on S^2 in R^3
# class 3 : 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
lab    = rep(c(1,2,3), each=10)

## CLUSTERING WITH DIFFERENT K VALUES
cl2 = riem.scNJW(myriem, k=2)$cluster
cl3 = riem.scNJW(myriem, k=3)$cluster
cl4 = riem.scNJW(myriem, k=4)$cluster

## MDS FOR VISUALIZATION
mds2d = riem.mds(myriem, ndim=2)$embed

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,4), pty="s")
plot(mds2d, col=lab, pch=19, main="true label")
plot(mds2d, col=cl2, pch=19, main="riem.scNJW: k=2")
plot(mds2d, col=cl3, pch=19, main="riem.scNJW: k=3")
plot(mds2d, col=cl4, pch=19, main="riem.scNJW: k=4")
par(opar)

Spectral Clustering by Shi and Malik (2000)

Description

The version of Shi and Malik first constructs the affinity matrix

Aij=exp⁡(−d(xi,dj)2/σ2)A_{ij} = \exp(-d(x_i, d_j)^2 / \sigma^2)

where σ\sigma is a common bandwidth parameter and performs k-means clustering on the row-space of eigenvectors for the random-walk graph laplacian matrix

L=D−1(D−A)L=D^{-1}(D-A)

.

Usage

riem.scSM(riemobj, k = 2, sigma = 1, geometry = c("intrinsic", "extrinsic"))

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

k

the number of clusters (default: 2).

sigma

bandwidth parameter (default: 1).

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

Value

a named list containing

cluster

a length-NN vector of class labels (from 1:k1:k).

eigval

eigenvalues of the graph laplacian's spectral decomposition.

embeds

an (N×k)(N\times k) low-dimensional embedding.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Shi J, Malik J (2000). "Normalized Cuts and Image Segmentation." IEEE Transactions on Pattern Analysis and Machine Intelligence, 22(8):888-905.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# class 1 : 10 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 10 perturbed data points near (0,1,0) on S^2 in R^3
# class 3 : 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
lab    = rep(c(1,2,3), each=10)

## CLUSTERING WITH DIFFERENT K VALUES
cl2 = riem.scSM(myriem, k=2)$cluster
cl3 = riem.scSM(myriem, k=3)$cluster
cl4 = riem.scSM(myriem, k=4)$cluster

## MDS FOR VISUALIZATION
mds2d = riem.mds(myriem, ndim=2)$embed

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,4), pty="s")
plot(mds2d, col=lab, pch=19, main="true label")
plot(mds2d, col=cl2, pch=19, main="riem.scSM: k=2")
plot(mds2d, col=cl3, pch=19, main="riem.scSM: k=3")
plot(mds2d, col=cl4, pch=19, main="riem.scSM: k=4")
par(opar)

Spectral Clustering with Unnormalized Laplacian

Description

The version of Shi and Malik first constructs the affinity matrix

Aij=exp⁡(−d(xi,dj)2/σ2)A_{ij} = \exp(-d(x_i, d_j)^2 / \sigma^2)

where σ\sigma is a common bandwidth parameter and performs k-means clustering on the row-space of eigenvectors for the unnormalized graph laplacian matrix

L=D−AL=D-A

.

Usage

riem.scUL(riemobj, k = 2, sigma = 1, geometry = c("intrinsic", "extrinsic"))

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

k

the number of clusters (default: 2).

sigma

bandwidth parameter (default: 1).

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

Value

a named list containing

cluster

a length-NN vector of class labels (from 1:k1:k).

eigval

eigenvalues of the graph laplacian's spectral decomposition.

embeds

an (N×k)(N\times k) low-dimensional embedding.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

von Luxburg U (2007). "A Tutorial on Spectral Clustering." Statistics and Computing, 17(4):395-416.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# class 1 : 10 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 10 perturbed data points near (0,1,0) on S^2 in R^3
# class 3 : 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:10){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 11:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:30){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
lab    = rep(c(1,2,3), each=10)

## CLUSTERING WITH DIFFERENT K VALUES
cl2 = riem.scUL(myriem, k=2)$cluster
cl3 = riem.scUL(myriem, k=3)$cluster
cl4 = riem.scUL(myriem, k=4)$cluster

## MDS FOR VISUALIZATION
mds2d = riem.mds(myriem, ndim=2)$embed

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,4), pty="s")
plot(mds2d, col=lab, pch=19, main="true label")
plot(mds2d, col=cl2, pch=19, main="riem.scUL: k=2")
plot(mds2d, col=cl3, pch=19, main="riem.scUL: k=3")
plot(mds2d, col=cl4, pch=19, main="riem.scUL: k=4")
par(opar)

Find the Smallest Enclosing Ball

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, find the smallest enclosing ball.

Usage

riem.seb(riemobj, method = c("aa2013"), ...)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

method

(case-insensitive) name of the algorithm to be used as follows;

"aa2013"

Arnaudon and Nielsen (2013).

...

extra parameters including

maxiter

maximum number of iterations to be run (default:50).

eps

tolerance level for stopping criterion (default: 1e-5).

Value

a named list containing

center

a matrix on M\mathcal{M} that minimizes the radius.

radius

the minimal radius with respect to the center.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Bâdoiu M, Clarkson KL (2003). “Smaller core-sets for balls.” In Proceedings of the fourteenth annual ACM-SIAM symposium on discrete algorithms, SODA '03, 801–802. ISBN 0-89871-538-5.

Arnaudon M, Nielsen F (2013). “On approximating the Riemannian 1-center.” Computational Geometry, 46(1), 93–104. ISSN 09257721.

Examples

#-------------------------------------------------------------------
#       Euclidean Example : samples from Standard Normal in R^2
#-------------------------------------------------------------------
## GENERATE 25 OBSERVATIONS FROM N(0,I)
ndata  = 25
mymats = array(0,c(ndata, 2))
mydata = list()
for (i in 1:ndata){
  mydata[[i]] = stats::rnorm(2)
  mymats[i,]  = mydata[[i]]
}
myriem = wrap.euclidean(mydata)

## COMPUTE
sebobj = riem.seb(myriem)
center = as.vector(sebobj$center) 
radius = sebobj$radius

## VISUALIZE
#  1. prepare the circle for drawing
theta  = seq(from=0, to=2*pi, length.out=100)
coords = radius*cbind(cos(theta), sin(theta))
coords = coords + matrix(rep(center, each=100), ncol=2)

#  2. draw
opar <- par(no.readonly=TRUE)
par(pty="s")
plot(coords, type="l", lwd=2, col="red",
     main="Euclidean SEB", xlab="x", ylab="y")
points(mymats, pch=19)                           # data
points(center[1], center[2], pch=19, col="blue") # center 
par(opar)

Two-Sample Test modified from Biswas and Ghosh (2014)

Description

Given MM observations X1,X2,…,XM∈MX_1, X_2, \ldots, X_M \in \mathcal{M} and NN observations Y1,Y2,…,YN∈MY_1, Y_2, \ldots, Y_N \in \mathcal{M}, perform the permutation test of equal distribution

H0 : PX=PYH_0~:~\mathcal{P}_X = \mathcal{P}_Y

by the method from Biswas and Ghosh (2014). The method, originally proposed for Euclidean-valued data, is adapted to the general Riemannian manifold with intrinsic/extrinsic distance.

Usage

riem.test2bg14(riemobj1, riemobj2, geometry = NULL, ...)

Arguments

riemobj1

a S3 "riemdata" class for MM manifold-valued data.

riemobj2

a S3 "riemdata" class for NN manifold-valued data.

geometry

A geometry name or saved specification.

...

extra parameters including

nperm

the number of permutations (default: 999).

Details

The statistic compares within-group and cross-group mean distances. Random-label calibration requires exchangeability under the equal-distribution null, with independent observations; it is not valid for unaccounted paired or repeated observations. Generalizing the distance to a manifold does not establish consistency against every alternative. Monte Carlo p-values include the observed assignment and all ties, and cannot be zero. Each group must contain at least two observations.

Value

a (list) object of S3 class htest containing:

statistic

a test statistic.

p.value

pp-value under H0H_0.

alternative

alternative hypothesis.

method

name of the test.

data.name

name(s) of provided sample data.

References

Biswas M, Ghosh AK (2014). “A nonparametric two-sample test applicable to high dimensional data.” Journal of Multivariate Analysis, 123, 160–171. ISSN 0047259X.

You K, Park H (2020). “Re-visiting Riemannian geometry of symmetric positive definite matrices for the analysis of functional connectivity.” NeuroImage, 117464. ISSN 10538119.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with two types
#
# class 1 : 20 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 30 perturbed data points near (0,1,0) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata1 = list()
mydata2 = list()
for (i in 1:20){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata1[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 1:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata2[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem1 = wrap.sphere(mydata1)
myriem2 = wrap.sphere(mydata2)

## PERFORM PERMUTATION TEST
#  it is expected to return a very small number.

riem.test2bg14(myriem1, myriem2, nperm=999)


## Not run: 
## CHECK WITH EMPIRICAL TYPE-1 ERROR
set.seed(777)
ntest = 1000
pvals = rep(0,ntest)

for (i in 1:ntest){
  X = cbind(matrix(rnorm(30*2, sd=0.1),ncol=2), rep(1,30))
  Y = cbind(matrix(rnorm(30*2, sd=0.1),ncol=2), rep(1,30))
  Xnorm = X/sqrt(rowSums(X^2))
  Ynorm = Y/sqrt(rowSums(Y^2))
  
  Xriem = wrap.sphere(Xnorm)
  Yriem = wrap.sphere(Ynorm)
  pvals[i] = riem.test2bg14(Xriem, Yriem, nperm=999)$p.value
}

emperr = round(sum((pvals <= 0.05))/ntest, 5)
print(paste0("* EMPIRICAL TYPE-1 ERROR=", emperr))

## End(Not run)

Two-Sample Test with Wasserstein Metric

Description

Given MM observations X1,X2,…,XM∈MX_1, X_2, \ldots, X_M \in \mathcal{M} and NN observations Y1,Y2,…,YN∈MY_1, Y_2, \ldots, Y_N \in \mathcal{M}, permutation test based on the Wasserstein metric (see riem.wasserstein for more details) is applied to test whether two distributions are same or not, i.e.,

H0 : PX=PYH_0~:~\mathcal{P}_X = \mathcal{P}_Y

with Wasserstein metric Wp\mathcal{W}_p being the measure of discrepancy between two samples.

Usage

riem.test2wass(riemobj1, riemobj2, p = 2, geometry = NULL, ...)

Arguments

riemobj1

a S3 "riemdata" class for MM manifold-valued data.

riemobj2

a S3 "riemdata" class for NN manifold-valued data.

p

an exponent for Wasserstein distance Wp\mathcal{W}_p (default: 2).

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

...

extra parameters including

nperm

the number of permutations (default: 999).

use.smooth

a logical, default FALSE. TRUE requests an experimental IPOT approximation, without an optimality certificate.

Details

Random label permutations require independent observations that are exchangeable under the identical-distributions null. This procedure does not handle paired, clustered or repeated observations. The Monte Carlo p-value includes ties and uses the plus-one correction. All assignments use the same solver and exponent. Saved geometry, permuted statistics, and the Monte Carlo standard-error diagnostic are retained.

Value

a (list) object of S3 class htest containing:

statistic

a test statistic.

p.value

pp-value under H0H_0.

alternative

alternative hypothesis.

method

name of the test.

data.name

name(s) of provided sample data.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with two types
#
# class 1 : 20 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 30 perturbed data points near (0,1,0) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata1 = list()
mydata2 = list()
for (i in 1:20){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata1[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 1:20){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata2[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem1 = wrap.sphere(mydata1)
myriem2 = wrap.sphere(mydata2)

## PERFORM PERMUTATION TEST
#  it is expected to return a very small number, but 
#  small number of 'nperm' may not give a reasonable p-value.

riem.test2wass(myriem1, myriem2, nperm=99, use.smooth=FALSE)


## Not run: 
## CHECK WITH EMPIRICAL TYPE-1 ERROR
set.seed(777)
ntest = 1000
pvals = rep(0,ntest)

for (i in 1:ntest){
  X = cbind(matrix(rnorm(30*2, sd=0.1),ncol=2), rep(1,30))
  Y = cbind(matrix(rnorm(30*2, sd=0.1),ncol=2), rep(1,30))
  Xnorm = X/sqrt(rowSums(X^2))
  Ynorm = Y/sqrt(rowSums(Y^2))
  
  Xriem = wrap.sphere(Xnorm)
  Yriem = wrap.sphere(Ynorm)
  pvals[i] = riem.test2wass(Xriem, Yriem, nperm=999)$p.value
  print(paste0("iteration ",i,"/",ntest," complete.."))
}

emperr = round(sum((pvals <= 0.05))/ntest, 5)
print(paste0("* EMPIRICAL TYPE-1 ERROR=", emperr))

## End(Not run)

t-distributed Stochastic Neighbor Embedding

Description

Given NN observations X1,X2,…,XN∈MX_1, X_2, \ldots, X_N \in \mathcal{M}, t-SNE mimicks the pattern of probability distributions over pairs of manifold-valued objects on low-dimensional target embedding space by minimizing Kullback-Leibler divergence.

Usage

riem.tsne(riemobj, ndim = 2, geometry = c("intrinsic", "extrinsic"), ...)

Arguments

riemobj

a S3 "riemdata" class for NN manifold-valued data.

ndim

an integer-valued target dimension.

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

...

extra parameters for Rtsne algorithm from Rtsne package, such as perplexity, momentum, and others.

Value

a named list containing

embed

an (N×ndim)(N\times ndim) matrix whose rows are embedded observations.

stress

discrepancy between embedded and original distances as a measure of error.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with three types
#
# 10 perturbed data points near (1,0,0) on S^2 in R^3
# 10 perturbed data points near (0,1,0) on S^2 in R^3
# 10 perturbed data points near (0,0,1) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata = list()
for (i in 1:20){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 21:40){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 41:60){
  tgt = c(stats::rnorm(2, sd=0.1), 1)
  mydata[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem = wrap.sphere(mydata)
mylabs = rep(c(1,2,3), each=20)

## RUN THE ALGORITHM IN TWO GEOMETRIES
mypx = 5
embed2int = riem.tsne(myriem, ndim=2, geometry="intrinsic", perplexity=mypx)
embed2ext = riem.tsne(myriem, ndim=2, geometry="extrinsic", perplexity=mypx)

## VISUALIZE
opar = par(no.readonly=TRUE)
par(mfrow=c(1,2), pty="s")
plot(embed2int$embed, main="intrinsic t-SNE", col=mylabs, pch=19)
plot(embed2ext$embed, main="extrinsic t-SNE", col=mylabs, pch=19)
par(opar)

Wasserstein Distance between Empirical Measures

Description

Given two empirical measures μ,ν\mu, \nu consisting of MM and NN observations, pp-Wasserstein distance for p≥1p\geq 1 between two empirical measures is defined as

Wp(μ,ν)=(inf⁡γ∈Γ(μ,ν)∫M×Md(x,y)pdγ(x,y))1/p\mathcal{W}_p (\mu, \nu) = \left( \inf_{\gamma \in \Gamma(\mu, \nu)} \int_{\mathcal{M}\times \mathcal{M}} d(x,y)^p d \gamma(x,y) \right)^{1/p}

where Γ(μ,ν)\Gamma(\mu, \nu) denotes the collection of all measures/couplings on M×M\mathcal{M}\times \mathcal{M} whose marginals are μ\mu and ν\nu on the first and second factors, respectively.

Usage

riem.wasserstein(riemobj1, riemobj2, p = 2, geometry = NULL, ...)

Arguments

riemobj1

a S3 "riemdata" class for MM manifold-valued data, which are atoms of μ\mu.

riemobj2

a S3 "riemdata" class for NN manifold-valued data, which are atoms of ν\nu.

p

an exponent for Wasserstein distance Wp\mathcal{W}_p (default: 2).

geometry

(case-insensitive) name of geometry; either geodesic ("intrinsic") or embedded ("extrinsic") geometry.

...

extra parameters including

weight1

a length-MM weight vector for μ\mu; if NULL (default), uniform weight is set.

weight2

a length-NN weight vector for ν\nu; if NULL (default), uniform weight is set.

Details

Weights must be finite and nonnegative with positive total mass; they are normalized to probability vectors. The ground distance follows the saved geometry. The linear-program transport solver receives jointly scaled distances to reduce overflow, and the returned plan is checked for finite entries and correct marginals. A numerical solution is not an independent optimality certificate.

Value

a named list containing

distance

Wp\mathcal{W_p} distance between two empirical measures.

plan

an (M×N)(M\times N) matrix whose rowSums and columnSums are weight1 and weight2 respectively.

Examples

#-------------------------------------------------------------------
#          Example on Sphere : a dataset with two types
#
# class 1 : 20 perturbed data points near (1,0,0) on S^2 in R^3
# class 2 : 30 perturbed data points near (0,1,0) on S^2 in R^3
#-------------------------------------------------------------------
## GENERATE DATA
mydata1 = list()
mydata2 = list()
for (i in 1:20){
  tgt = c(1, stats::rnorm(2, sd=0.1))
  mydata1[[i]] = tgt/sqrt(sum(tgt^2))
}
for (i in 1:30){
  tgt = c(rnorm(1,sd=0.1),1,rnorm(1,sd=0.1))
  mydata2[[i]] = tgt/sqrt(sum(tgt^2))
}
myriem1 = wrap.sphere(mydata1)
myriem2 = wrap.sphere(mydata2)

## COMPUTE p-WASSERSTEIN DISTANCES
dist1 = riem.wasserstein(myriem1, myriem2, p=1)
dist2 = riem.wasserstein(myriem1, myriem2, p=2)
dist5 = riem.wasserstein(myriem1, myriem2, p=5)

pm1 = paste0("p=1: dist=",round(dist1$distance,3))
pm2 = paste0("p=2: dist=",round(dist2$distance,3))
pm5 = paste0("p=5: dist=",round(dist5$distance,3))

## VISUALIZE TRANSPORT PLAN AND DISTANCE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,3))
image(dist1$plan, axes=FALSE, main=pm1)
image(dist2$plan, axes=FALSE, main=pm2)
image(dist5$plan, axes=FALSE, main=pm5)
par(opar)

Generate Random Samples from Multivariate Normal Distribution

Description

In Rp\mathbf{R}^p, random samples are drawn

X1,X2,…,Xn ∼ N(μ,Σ)X_1,X_2,\ldots,X_n~ \sim ~ \mathcal{N}(\mu, \Sigma)

where μ∈Rp\mu \in \mathbf{R}^p is a mean vector and Σ∈SPD(p)\Sigma \in \textrm{SPD}(p) is a positive definite covariance matrix.

Usage

rmvnorm(n = 1, mu, sigma)

Arguments

n

the number of samples to be generated.

mu

mean vector.

sigma

covariance matrix.

Value

either (1) a length-pp vector (n=1n=1) or (2) an (n×p)(n\times p) matrix where rows are random samples.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

#-------------------------------------------------------------------
#   Generate Random Data and Compare with Empirical Covariances
#
# In R^5 with zero mean and diagonal covariance, 
# generate 100 and 200 observations and compute MLE covariance.
#-------------------------------------------------------------------
## GENERATE DATA
mymu  = rep(0,5)
mysig = diag(5)

## MLE FOR COVARIANCE
smat1 = stats::cov(rmvnorm(n=100, mymu, mysig))
smat2 = stats::cov(rmvnorm(n=200, mymu, mysig))

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,3), pty="s")
image(mysig[,5:1], axes=FALSE, main="true covariance")
image(smat1[,5:1], axes=FALSE, main="empirical cov with n=100")
image(smat2[,5:1], axes=FALSE, main="empirical cov with n=200")
par(opar)

Supported Geometries on SPD Manifold

Description

SPD manifold is a well-studied space in that there have been many geometries proposed on the space. For special functions on under SPD category, this function finds whether there exists a matching name that is currently supported in Riemann. If there is none, it will return an error message.

Usage

spd.geometry(geometry)

Arguments

geometry

name of supported geometries, including

AIRM

Affine-Invariant Riemannian Metric.

LERM

Log-Euclidean Riemannian Metric.

Jeffrey

Jeffrey's divergence.

Stein

Stein's metric.

Wasserstein

2-Wasserstein geometry.

Value

a matching name in lower-case.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

# it just returns a small-letter string.
mygeom = spd.geometry("stein")

Pairwise Distance on SPD Manifold

Description

Given NN observations X1,X2,…,XNX_1, X_2, \ldots, X_N in SPD manifold, compute pairwise distances among observations. Stein distances use a scaled relative spectrum, and Wasserstein distances use an aligned Cholesky-factor residual, avoiding determinant overflow and cancellation of nearly equal traces. Numerically singular inputs can still prevent these factorizations and are reported as errors.

Usage

spd.pdist(spdobj, geometry, as.dist = FALSE)

Arguments

spdobj

a S3 "riemdata" class of SPD-valued data.

geometry

name of the geometry to be used. See spd.geometry for supported geometries.

as.dist

logical; if TRUE, it returns a dist object. Else, it returns a symmetric matrix.

Value

a S3 dist object or (N×N)(N\times N) symmetric matrix of pairwise distances according to as.dist parameter.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

#-------------------------------------------------------------------
#                   Two Types of Covariances
#
#  group1 : perturbed from data by N(0,1) in R^3
#  group2 : perturbed from data by [sin(x); cos(x); sin(x)*cos(x)]
#-------------------------------------------------------------------
## GENERATE DATA
spd_mats = array(0,c(3,3,20))
for (i in 1:10){
  spd_mats[,,i] = stats::cov(matrix(rnorm(50*3), ncol=3))
}
for (j in 11:20){
  randvec = stats::rnorm(50, sd=3)
  randmat = cbind(sin(randvec), cos(randvec), sin(randvec)*cos(randvec))
  spd_mats[,,j] = stats::cov(randmat + matrix(rnorm(50*3, sd=0.1), ncol=3))
}

## WRAP IT AS SPD OBJECT
spd_obj = wrap.spd(spd_mats)

## COMPUTE PAIRWISE DISTANCES
#  Geometries are case-insensitive.
pdA = spd.pdist(spd_obj, "airM")
pdL = spd.pdist(spd_obj, "lErm")
pdJ = spd.pdist(spd_obj, "Jeffrey")
pdS = spd.pdist(spd_obj, "stEin")
pdW = spd.pdist(spd_obj, "wasserstein")

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(2,3), pty="s")
image(pdA, axes=FALSE, main="AIRM")
image(pdL, axes=FALSE, main="LERM")
image(pdJ, axes=FALSE, main="Jeffrey")
image(pdS, axes=FALSE, main="Stein")
image(pdW, axes=FALSE, main="Wasserstein")
par(opar)

Wasserstein Barycenter of SPD Matrices

Description

Given NN observations X1,X2,…,XNX_1, X_2, \ldots, X_N in SPD manifold, compute the L2L_2-Wasserstein barycenter that minimizes

∑n=1NλiW2(N(X),N(Xi))2\sum_{n=1}^N \lambda_i \mathcal{W}_2 (N(X), N(X_i))^2

where N(X)N(X) denotes the zero-mean Gaussian measure with covariance XX.

Usage

spd.wassbary(spdobj, weight = NULL, method = c("RU02", "AE16"), ...)

Arguments

spdobj

a S3 "riemdata" class of SPD-valued data of (p×p)(p\times p) matrices.

weight

weight of observations; if NULL it assumes equal weights, or a nonnegative length-NN vector that sums to 1 should be given.

method

name of the algortihm to be used; one of the "RU02", "AE16".

...

extra parameters including

maxiter

maximum number of iterations to be run (default:20).

abstol

tolerance level for stopping criterion (default: 1e-8).

Value

a (p×p)(p\times p) Wasserstein barycenter matrix.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

#-------------------------------------------------------------------
#        Covariances from standard multivariate Gaussians.
#-------------------------------------------------------------------
## GENERATE DATA
ndata = 20
pdim  = 10
mydat = array(0,c(pdim,pdim,ndata))
for (i in 1:ndata){
  mydat[,,i] = stats::cov(matrix(rnorm(100*pdim), ncol=pdim))
}
myriem = wrap.spd(mydat)

## COMPUTE BY DIFFERENT ALGORITHMS
baryRU <- spd.wassbary(myriem, method="RU02")
baryAE <- spd.wassbary(myriem, method="AE16")

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,3), pty="s")
image(diag(pdim), axes=FALSE, main="True Covariance")
image(baryRU, axes=FALSE, main="by RU02")
image(baryAE, axes=FALSE, main="by AE16")
par(opar)

Convert between Cartesian Coordinates and Geographic Coordinates

Description

In geospatial data analysis, it is common to consider locations on the Earth as data. These locations, usually provided by latitude and longitude, are not directly applicable for spherical data analysis. We provide two functions - sphere.geo2xyz and sphere.xyz2geo - that convert geographic coordinates in longitude/latitude into a unit-norm vector on S2\mathcal{S}^2, and vice versa. As a convention, latitude and longitude are represented as decimal degrees.

Usage

sphere.geo2xyz(lat, lon)

sphere.xyz2geo(xyz)

Arguments

lat

latitude (in decimal degrees).

lon

longitude (in decimal degrees).

xyz

a unit-norm vector in S2\mathcal{S}^{2}.

Value

transformed data.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

## EXAMPLE DATA WITH POPULATED US CITIES
data(cities)

## SELECT ALBUQUERQUE
geo = cities$coord[1,]
xyz = cities$cartesian[1,]

## CHECK TWO INPUT TYPES AND THEIR CONVERSIONS
sphere.geo2xyz(geo[1], geo[2])
sphere.xyz2geo(xyz)

Generate Uniform Samples on Sphere

Description

It generates nn random samples from Sp−1\mathcal{S}^{p-1}. For convenient usage of users, we provide a number of options in terms of the return type.

Usage

sphere.runif(n, p, type = c("list", "matrix", "riemdata"))

Arguments

n

number of samples to be generated.

p

original dimension (of the ambient space).

type

return type;

"list"

a length-nn list of length-pp vectors.

"matrix"

a (n×p)(n\times p) where rows are unit vectors.

"riemdata"

a S3 object. See wrap.sphere for more details (Default).

Value

an object from one of the above by type option.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Chikuse Y (2003). Statistics on Special Manifolds, volume 174 of Lecture Notes in Statistics. Springer New York, New York, NY. ISBN 978-0-387-00160-9 978-0-387-21540-2.

See Also

wrap.sphere

Examples

#-------------------------------------------------------------------
#                       Draw Samples on Sphere
#
# Multiple return types on S^4 in R^5
#-------------------------------------------------------------------
dat.list = sphere.runif(n=10, p=5, type="list")
dat.matx = sphere.runif(n=10, p=5, type="matrix")
dat.riem = sphere.runif(n=10, p=5, type="riemdata")

Test of Uniformity on Sphere

Description

Given NN observations {{X1,X2,…,XM}\lbrace X_1, X_2, \ldots, X_M \brace on Sp−1\mathcal{S}^{p-1}, it tests whether the data is distributed uniformly on the sphere.

Usage

sphere.utest(spobj, method = c("Rayleigh", "RayleighM"))

Arguments

spobj

a S3 "riemdata" class for NN Sphere-valued data.

method

(case-insensitive) name of the test method containing

"Rayleigh"

original Rayleigh statistic.

"RayleighM"

modified Rayleigh statistic with better order of error.

Value

a (list) object of S3 class htest containing:

statistic

a test statistic.

p.value

pp-value under H0H_0.

alternative

alternative hypothesis.

method

name of the test.

data.name

name(s) of provided sample data.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Chikuse Y (2003). Statistics on Special Manifolds, volume 174 of Lecture Notes in Statistics. Springer New York, New York, NY. ISBN 978-0-387-00160-9 978-0-387-21540-2.

Mardia KV, Jupp PE (eds.) (1999). Directional Statistics, Wiley Series in Probability and Statistics. John Wiley and Sons, Inc., Hoboken, NJ, USA. ISBN 978-0-470-31697-9 978-0-471-95333-3.

See Also

wrap.sphere

Examples

#-------------------------------------------------------------------
#   Compare Rayleigh's original and modified versions of the test
#-------------------------------------------------------------------
#  Data Generation
myobj = sphere.runif(n=100, p=5, type="riemdata")

#  Compare 2 versions : Original vs Modified Rayleigh
sphere.utest(myobj, method="rayleigh")
sphere.utest(myobj, method="rayleighm")

Spherical Laplace Distribution

Description

This is a collection of tools for learning with spherical Laplace (SL) distribution on a (p−1)(p-1)-dimensional sphere in Rp\mathbf{R}^p including sampling, density evaluation, and maximum likelihood estimation of the parameters. The SL distribution is characterized by the following density function,

fSL(x;μ,σ)=1C(σ)exp⁡(−d(x,μ)σ)f_{SL}(x; \mu, \sigma) = \frac{1}{C(\sigma)} \exp \left( -\frac{d(x,\mu)}{\sigma} \right)

for location and scale parameters μ\mu and σ\sigma respectively.

Usage

dsplaplace(data, mu, sigma, log = FALSE)

rsplaplace(n, mu, sigma)

mle.splaplace(data, method = c("DE", "Optimize", "Newton"), ...)

Arguments

data

data vectors in form of either an (n×p)(n\times p) matrix or a length-nn list. See wrap.sphere for descriptions on supported input types.

mu

a length-pp unit-norm vector of location.

sigma

a positive scale parameter; Inf gives the uniform limit.

log

a logical; TRUE to return log-density, FALSE for densities without logarithm applied.

n

the number of samples to be generated.

method

an algorithm name for scale parameter estimation. It should be one of "Newton", "Optimize", and "DE" (case-sensitive).

...

extra parameters for computations, including

maxiter

iteration budget for each location, likelihood-search, or polishing stage; rounded and raised to at least 10 (default: 50).

eps

positive tolerance, capped at 1e-6; the relative likelihood-score tolerance is additionally floored at 1e-10 (default: 1e-6).

use.exact

for Newton, use moment-based derivatives (TRUE) or finite differences of the score (FALSE, default). Both use adaptive radial quadrature.

Details

Scale estimation uses an adaptively bracketed likelihood in the reciprocal scale. Newton updates are safeguarded by that bracket; Optimize and DE search the log reciprocal scale and use safeguarded Newton polishing if their score has not reached the requested tolerance. The uniform boundary is returned as sigma = Inf. For coincident observations the likelihood is unbounded and sigma = 0 is returned with a warning; this point-mass limit has no surface-area density and is not accepted by dsplaplace or rsplaplace. The location is obtained by local intrinsic-median optimization and need not be globally optimal for data spread across the sphere. Failure of the location iteration to converge produces a warning; the returned scale then optimizes the likelihood conditional on that last location estimate. Log densities use the log kernel and logarithmic normalizer directly.

Value

dsplaplace gives a vector of evaluated densities given samples. rsplaplace generates unit-norm vectors in Rp\mathbf{R}^p wrapped in a list. mle.splaplace computes MLEs and returns a list containing estimates of location (mu) and scale (sigma) parameters.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

# -------------------------------------------------------------------
#          Example with Spherical Laplace Distribution
#
# Given a fixed set of parameters, generate samples and acquire MLEs.
# Especially, we will see the evolution of estimation accuracy.
# -------------------------------------------------------------------
## DEFAULT PARAMETERS
true.mu  = c(1,0,0,0,0)
true.sig = 1

## GENERATE A RANDOM SAMPLE OF SIZE N=1000
big.data = rsplaplace(1000, true.mu, true.sig)

## ITERATE FROM 50 TO 1000 by 10
idseq = seq(from=50, to=1000, by=10)
nseq  = length(idseq)

hist.mu  = rep(0, nseq)
hist.sig = rep(0, nseq)

for (i in 1:nseq){
  small.data = big.data[1:idseq[i]]             # data subsetting
  small.MLE  = mle.splaplace(small.data)        # compute MLE
  
  hist.mu[i]  = acos(sum(small.MLE$mu*true.mu)) # difference in mu
  hist.sig[i] = small.MLE$sigma
}

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,2))
plot(idseq, hist.mu,  "b", pch=19, cex=0.5, 
     main="difference in location", xlab="sample size")
plot(idseq, hist.sig, "b", pch=19, cex=0.5, 
     main="scale parameter", xlab="sample size")
abline(h=true.sig, lwd=2, col="red")
par(opar)

Spherical Normal Distribution

Description

We provide tools for an isotropic spherical normal (SN) distributions on a (p−1)(p-1)-sphere in Rp\mathbf{R}^p for sampling, density evaluation, and maximum likelihood estimation of the parameters where the density is defined as

fSN(x;μ,λ)=1Z(λ)exp⁡(−λ2d2(x,μ))f_{SN}(x; \mu, \lambda) = \frac{1}{Z(\lambda)} \exp \left( -\frac{\lambda}{2} d^2(x,\mu) \right)

for location and concentration parameters μ\mu and λ\lambda respectively and the normalizing constant Z(λ)Z(\lambda).

Usage

dspnorm(data, mu, lambda, log = FALSE)

rspnorm(n, mu, lambda)

mle.spnorm(data, method = c("Newton", "Halley", "Optimize", "DE"), ...)

Arguments

data

data vectors in form of either an (n×p)(n\times p) matrix or a length-nn list. See wrap.sphere for descriptions on supported input types.

mu

a length-pp unit-norm vector of location.

lambda

a finite nonnegative concentration parameter; zero is the uniform distribution.

log

a logical; TRUE to return log-density, FALSE for densities without logarithm applied.

n

the number of samples to be generated.

method

an algorithm name for concentration parameter estimation. It should be one of "Newton","Halley","Optimize", and "DE" (case sensitive).

...

extra parameters for computations, including

maxiter

iteration budget for each location, likelihood-search, or polishing stage; rounded and raised to at least 10 (default: 50).

eps

positive tolerance, capped at 1e-6; the relative likelihood-score tolerance is additionally floored at 1e-10 (default: 1e-6).

Details

Concentration estimation uses an adaptively bracketed likelihood score. Newton and Halley updates are safeguarded by that bracket; Optimize and DE search the log concentration and use safeguarded Newton polishing if their score has not reached the requested tolerance. All methods check the uniform boundary, returning lambda = 0 when appropriate. For coincident observations the likelihood is unbounded and lambda = Inf is returned with a warning; this point-mass limit has no density with respect to spherical surface area and is not accepted by dspnorm or rspnorm. The location is obtained by local intrinsic-mean optimization; a globally optimal location is not guaranteed for data spread across the sphere. Failure of the location iteration to converge produces a warning; the returned concentration then optimizes the likelihood conditional on that last location estimate. Log densities are evaluated directly, including a logarithmic normalizer.

Value

dspnorm gives a vector of evaluated densities given samples. rspnorm generates unit-norm vectors in Rp\mathbf{R}^p wrapped in a list. mle.spnorm computes MLEs and returns a list containing estimates of location (mu) and concentration (lambda) parameters.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Hauberg S (2018). “Directional Statistics with the Spherical Normal Distribution.” In 2018 21st International Conference on Information Fusion (FUSION), 704–711. ISBN 978-0-9964527-6-2.

You K, Suh C (2022). “Parameter Estimation and Model-Based Clustering with Spherical Normal Distribution on the Unit Hypersphere.” Computational Statistics and Data Analysis, 107457. ISSN 01679473.

Examples

# -------------------------------------------------------------------
#          Example with Spherical Normal Distribution
#
# Given a fixed set of parameters, generate samples and acquire MLEs.
# Especially, we will see the evolution of estimation accuracy.
# -------------------------------------------------------------------
## DEFAULT PARAMETERS
true.mu  = c(1,0,0,0,0)
true.lbd = 5

## GENERATE DATA N=1000
big.data = rspnorm(1000, true.mu, true.lbd)

## ITERATE FROM 50 TO 1000 by 10
idseq = seq(from=50, to=1000, by=10)
nseq  = length(idseq)

hist.mu  = rep(0, nseq)
hist.lbd = rep(0, nseq)

for (i in 1:nseq){
  small.data = big.data[1:idseq[i]]          # data subsetting
  small.MLE  = mle.spnorm(small.data) # compute MLE
  
  hist.mu[i]  = acos(sum(small.MLE$mu*true.mu)) # difference in mu
  hist.lbd[i] = small.MLE$lambda
}

## VISUALIZE
opar <- par(no.readonly=TRUE)
par(mfrow=c(1,2))
plot(idseq, hist.mu,  "b", pch=19, cex=0.5, main="difference in location")
plot(idseq, hist.lbd, "b", pch=19, cex=0.5, main="concentration param")
abline(h=true.lbd, lwd=2, col="red")
par(opar)

Simulated Annealing on Stiefel Manifold

Description

Simulated Annealing is a black-box, derivative-free optimization algorithm that iterates via stochastic search in the neighborhood of current position. stiefel.optSA solves the following problem

min⁡Xf(X),X∈St(p,k)\min_X f(X),\quad X \in St(p,k)

without any other auxiliary information such as gradient or hessian involved.

Usage

stiefel.optSA(func, p, k, ...)

Arguments

func

a function to be minimized.

p

dimension parameter as in St(k,p)St(k,p).

k

dimension parameter as in St(k,p)St(k,p).

...

extra parameters for SA algorithm including

n.start

number of runs; algorithm is executed n.start times (default: 5).

stepsize

size of random walk on each component (default: 0.1).

maxiter

maximum number of iterations for each run (default: 100).

cooling

triplet for cooling schedule. See the section for the usage.

init.val

if NULL, starts from a random point. Otherwise, a Stiefel matrix of size (p,k)(p,k) should be provided for fixed starting point.

print.progress

a logical; if TRUE, it prints each iteration.

Details

Random starts and proposals use QR orthonormalization with a positive diagonal in the triangular factor, preserving column signs of the input frame. Thus the search is not restricted to a hemisphere or, for square frames, to one component of the orthogonal group. Stochastic search does not guarantee a global minimum. The objective must return one finite number.

Value

a named list containing:

cost

minimized function value.

solution

a (p×k)(p\times k) matrix that attains the cost.

accfreq

frequency of acceptance moves.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

Examples

#-------------------------------------------------------------------
#               Optimization for Eigen-Decomposition
#
# Given (5x5) covariance matrix S, eigendecomposition is indeed 
# an optimization problem cast on the stiefel manifold. Here, 
# we are trying to find top 3 eigenvalues and compare.
#-------------------------------------------------------------------
## PREPARE
set.seed(121)                         # set seed
A = cov(matrix(rnorm(100*5), ncol=5)) # define covariance
myfunc <- function(p){                # cost function to minimize
  return(sum(-diag(t(p)%*%A%*%p)))
} 

## SOLVE THE OPTIMIZATION PROBLEM
Aout = stiefel.optSA(myfunc, p=5, k=3, n.start=40, maxiter=200)

## COMPUTE EIGENVALUES
#  1. USE SOLUTIONS TO THE ABOVE OPTIMIZATION 
abase   = Aout$solution
eig3sol = sort(diag(t(abase)%*%A%*%abase), decreasing=TRUE)

#  2. USE BASIC 'EIGEN' FUNCTION
eig3dec = sort(eigen(A)$values, decreasing=TRUE)[1:3]

## VISUALIZE
opar <- par(no.readonly=TRUE)
yran = c(min(min(eig3sol),min(eig3dec))*0.95,
         max(max(eig3sol),max(eig3dec))*1.05)
plot(1:3, eig3sol, type="b", col="red",  pch=19, ylim=yran,
     xlab="index", ylab="eigenvalue", main="compare top 3 eigenvalues")
lines(1:3, eig3dec, type="b", col="blue", pch=19)
legend(1, 1, legend=c("optimization","decomposition"), col=c("red","blue"),
       lty=rep(1,2), pch=19)
par(opar)

Generate Uniform Samples on Stiefel Manifold

Description

It generates nn random samples from Stiefel manifold St(k,p)St(k,p).

Usage

stiefel.runif(n, k, p, type = c("list", "array", "riemdata"))

Arguments

n

number of samples to be generated.

k

dimension of the frame.

p

original dimension (of the ambient space).

type

return type;

"list"

a length-nn list of (p×k)(p\times k) basis of kk-frames.

"array"

a (p×k×n)(p\times k\times n) 3D array whose slices are kk-frame basis.

"riemdata"

a S3 object. See wrap.stiefel for more details.

Value

an object from one of the above by type option.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Chikuse Y (2003). Statistics on Special Manifolds, volume 174 of Lecture Notes in Statistics. Springer New York, New York, NY. ISBN 978-0-387-00160-9 978-0-387-21540-2.

See Also

wrap.stiefel

Examples

#-------------------------------------------------------------------
#                 Draw Samples on Stiefel Manifold 
#
# Try Different Return Types with 3 Observations of 5-frames in R^10
#-------------------------------------------------------------------
#  GENERATION
dat.list = stiefel.runif(n=3, k=5, p=10, type="list")
dat.arr3 = stiefel.runif(n=3, k=5, p=10, type="array")
dat.riem = stiefel.runif(n=3, k=5, p=10, type="riemdata")

Test of Uniformity on Stiefel Manifold

Description

Given the data on Stiefel manifold St(k,p)St(k,p), it tests whether the data is distributed uniformly.

Usage

stiefel.utest(stobj, method = c("Rayleigh", "RayleighM"))

Arguments

stobj

a S3 "riemdata" class for NN Stiefel-valued data.

method

(case-insensitive) name of the test method containing

"Rayleigh"

original Rayleigh statistic.

"RayleighM"

modified Rayleigh statistic with better order of error.

Value

a (list) object of S3 class htest containing:

statistic

a test statistic.

p.value

pp-value under H0H_0.

alternative

alternative hypothesis.

method

name of the test.

data.name

name(s) of provided sample data.

Validation status

This retained legacy interface is experimental. Its full numerical and statistical contract has not been independently verified across supported inputs. See riem-method-contracts and the installed contract table for method-specific assumptions, restrictions, and evidence scope.

References

Chikuse Y (2003). Statistics on Special Manifolds, volume 174 of Lecture Notes in Statistics. Springer New York, New York, NY. ISBN 978-0-387-00160-9 978-0-387-21540-2.

Mardia KV, Jupp PE (eds.) (1999). Directional Statistics, Wiley Series in Probability and Statistics. John Wiley and Sons, Inc., Hoboken, NJ, USA. ISBN 978-0-470-31697-9 978-0-471-95333-3.

See Also

wrap.stiefel

Examples

#-------------------------------------------------------------------
#   Compare Rayleigh's original and modified versions of the test
# 
# Test 1. sample uniformly from St(2,4)
# Test 2. use perturbed principal components from 'iris' data in R^4
#         which is concentrated around a point to reject H0.
#-------------------------------------------------------------------
## DATA GENERATION
#  1. uniform data
myobj1 = stiefel.runif(n=100, k=2, p=4)

#  2. perturbed principal components
data(iris)
irdat = list()
for (n in 1:100){
   tmpdata    = iris[1:50,1:4] + matrix(rnorm(50*4,sd=0.5),ncol=4)
   irdat[[n]] = eigen(cov(tmpdata))$vectors[,1:2]
}
myobj2 = wrap.stiefel(irdat)

## TEST
#  1. uniform data
stiefel.utest(myobj1, method="Rayleigh")
stiefel.utest(myobj1, method="RayleighM")

#  2. concentrated data
stiefel.utest(myobj2, method="rayleIgh")   # method names are 
stiefel.utest(myobj2, method="raYleiGhM")  # CASE - INSENSITIVE !

Prepare Data on Correlation Manifold

Description

The collection of correlation matrices is considered as a subset (and quotient) of the well-known SPD manifold. In our package, it is defined as

C++p={X∈Rp×p ∣ X⊤=X, X≻0, diag(X)=1}\mathcal{C}_{++}^p = \lbrace X \in \mathbf{R}^{p\times p} ~\vert~ X^\top = X,~ X \succ 0,~ \textrm{diag}(X) = 1 \rbrace

where X≻0X \succ 0 means strictly positive definite. Please note that the geometry involving semi-definite correlation matrices is not the objective here.

Usage

wrap.correlation(input)

Arguments

input

correlation data matrices to be wrapped as riemdata class. Following inputs are considered,

array

an (p×p×n)(p\times p\times n) array where each slice along 3rd dimension is a correlation matrix.

list

a length-nn list whose elements are (p×p)(p\times p) correlation matrices.

Value

a named riemdata S3 object containing

data

a list of (p×p)(p\times p) correlation matrices.

size

size of each correlation matrix.

name

name of the manifold of interests, "correlation"

Examples

#-------------------------------------------------------------------
#                 Checker for Two Types of Inputs
#
#  5 observations; empirical correlation of normal observations.
#-------------------------------------------------------------------
#  Data Generation
d1 = array(0,c(3,3,5))
d2 = list()
for (i in 1:5){
  dat = matrix(rnorm(10*3),ncol=3)
  d1[,,i] = stats::cor(dat)
  d2[[i]] = d1[,,i]
}

#  Run
test1 = wrap.correlation(d1)
test2 = wrap.correlation(d2)

Prepare Data on Euclidean Space

Description

Euclidean space Rp\mathbf{R}^p is the most common space for data analysis, which can be considered as a Riemannian manifold with flat metric. Since the space of matrices is isomorphic to Euclidean space after vectorization, we consider the inputs as pp-dimensional vectors.

Usage

wrap.euclidean(input)

Arguments

input

data vectors to be wrapped as riemdata class. Following inputs are considered,

matrix

an (n×p)(n \times p) matrix of row observations.

list

a length-nn list whose elements are length-pp vectors.

Value

a named riemdata S3 object containing

data

a list of (p×1)(p\times 1) matrices in Rp\mathbf{R}^p.

size

dimension of the ambient space.

name

name of the manifold of interests, "euclidean"

Examples

#-------------------------------------------------------------------
#                 Checker for Two Types of Inputs
#
#  Generate 5 observations in R^3 in Matrix and List.
#-------------------------------------------------------------------
## DATA GENERATION
d1 = array(0,c(5,3))
d2 = list()
for (i in 1:5){
  single  = stats::rnorm(3)
  d1[i,]  = single
  d2[[i]] = single
}

## RUN
test1 = wrap.euclidean(d1)
test2 = wrap.euclidean(d2)

Prepare Data on Grassmann Manifold

Description

Grassmann manifold Gr(k,p)Gr(k,p) is the set of kk-planes, or kk-dimensional subspaces in RpR^p, which means that for a given matrix Y∈Rp×kY \in \mathbf{R}{p\times k}, the column space SPAN(Y)SPAN(Y) is an element in Grassmann manifold. We use a convention that each element in Gr(k,p)Gr(k,p) is represented as an orthonormal basis (ONB) X∈Rp×kX \in \mathbf{R}^{p\times k} where

X⊤X=Ik.X^\top X = I_k.

If not provided in such a form, this wrapper takes a QR decomposition of the given data to recover a corresponding ONB.

Usage

wrap.grassmann(input)

Arguments

input

data matrices to be wrapped as riemdata class. Following inputs are considered,

array

an (p×k×n)(p\times k\times n) array where each slice along 3rd dimension is a kk-subspace basis in dimension pp.

list

a length-nn list whose elements are (p×k)(p\times k) basis for kk-subspace.

Value

a named riemdata S3 object containing

data

a list of k-subspace basis matrices.

size

size of each k-subspace basis matrix.

name

name of the manifold of interests, "grassmann"

Examples

#-------------------------------------------------------------------
#                 Checker for Two Types of Inputs
#
#  Generate 5 observations in Gr(2,4)
#-------------------------------------------------------------------
#  Generation
d1 = array(0,c(4,2,5))
d2 = list()
for (i in 1:5){
  d1[,,i] = matrix(rnorm(4*2), ncol=2)
  d2[[i]] = d1[,,i]
}

#  Run
test1 = wrap.grassmann(d1)
test2 = wrap.grassmann(d2)

Wrap Landmark Data on Shape Space

Description

One of the frameworks used in shape space is to represent the data as landmarks. Each shape is a point set of kk points in Rp\mathbf{R}^p where each point is a labeled object. We consider general landmarks in p=2,3,…p=2,3,\ldots. Note that when p>2p > 2, it is stratified space but we assume singularities do not exist or are omitted. The wrapper takes translation and scaling out from the data to make it preshape (centered, unit-norm). Also, for convenience, orthogonal Procrustes analysis is applied with the first observation being the reference so that all the other data are aligned to that reference. The full orthogonal group O(p), including reflections, is used. This is not an SO(p)-only orientation-preserving analysis. Singular configurations are rejected. Fitted tangent models retain and apply their own training reference.

Usage

wrap.landmark(input, reference = NULL)

Arguments

input

data matrices to be wrapped as riemdata class. Following inputs are considered,

array

a (k×p×n)(k\times p\times n) array where each slice along 3rd dimension is a kk-ad in Rp\mathbf{R}^p.

list

a length-nn list whose elements are kk-ads.

reference

optional reference configuration for alignment. If NULL, the first observation is used.

Value

a named riemdata S3 object containing

data

a list of preshapes in Rp\mathbf{R}^p.

size

size of each preshape.

name

name of the manifold of interests, "landmark"

References

Dryden IL, Mardia KV (2016). Statistical shape analysis with applications in R, Wiley series in probability and statistics, Second edition edition. John Wiley and Sons, Chichester, UK ; Hoboken, NJ. ISBN 978-1-119-07251-5 978-1-119-07250-8.

Examples

## USE 'GORILLA' DATA
data(gorilla)
riemobj = wrap.landmark(gorilla$male)

Prepare Data on Multinomial Manifold

Description

Multinomial manifold is referred to the strictly positive data that sums to 1. Also known as probability simplex or positive orthant, we denote (p−1)(p-1) simplex in Rp\mathbf{R}^p by

Δp−1={x∈Rp ∣ ∑i=1pxi=1,xi>0}\Delta^{p-1} = \lbrace x \in \mathbf{R}^p~\vert~ \sum_{i=1}^p x_i = 1, x_i > 0 \rbrace

in that data are positive L1L_1 unit-norm vectors. In wrap.multinomial, normalization is applied when each data point is not on the simplex, accepting positive counts or masses. Zero, negative, and nonfinite inputs are rejected before normalization; no pseudocount is added.

Usage

wrap.multinomial(input)

Arguments

input

data vectors to be wrapped as riemdata class. Following inputs are considered,

matrix

an (n×p)(n \times p) matrix of row observations.

list

a length-nn list whose elements are length-pp vectors.

Value

a named riemdata S3 object containing

data

a list of (p×1)(p\times 1) matrices in Δp−1\Delta^{p-1}.

size

dimension of the ambient space.

name

name of the manifold of interests, "multinomial"

Examples

#-------------------------------------------------------------------
#                 Checker for Two Types of Inputs
#-------------------------------------------------------------------
## DATA GENERATION
d1 = array(0,c(5,3))
d2 = list()
for (i in 1:5){
  single  = abs(stats::rnorm(3))
  d1[i,]  = single
  d2[[i]] = single
}

## RUN
test1 = wrap.multinomial(d1)
test2 = wrap.multinomial(d2)

Prepare Data on Rotation Group

Description

Rotation group, also known as special orthogonal group, is a Riemannian manifold

SO(p)={Q∈Rp×p ∣ Q⊤Q=I,det(Q)=1}SO(p) = \lbrace Q \in \mathbf{R}^{p\times p}~\vert~ Q^\top Q = I, \textrm{det}(Q)=1 \rbrace

where the name originates from an observation that when p=2,3p=2,3 these matrices are rotation of shapes/configurations.

Usage

wrap.rotation(input)

Arguments

input

data matrices to be wrapped as riemdata class. Following inputs are considered,

array

a (p×p×n)(p\times p\times n) array where each slice along 3rd dimension is a rotation matrix.

list

a length-nn list whose elements are (p×p)(p\times p) rotation matrices.

Value

a named riemdata S3 object containing

data

a list of (p×p)(p\times p) rotation matrices.

size

size of each rotation matrix.

name

name of the manifold of interests, "rotation"

Examples

#-------------------------------------------------------------------
#                 Checker for Two Types of Inputs
#-------------------------------------------------------------------
## DATA GENERATION
d1 = array(0,c(3,3,5))
d2 = list()
for (i in 1:5){
  single  = qr.Q(qr(matrix(rnorm(9),nrow=3)))
  d1[,,i] = single
  d2[[i]] = single
}

## RUN
test1 = wrap.rotation(d1)
test2 = wrap.rotation(d2)

Prepare Data on Symmetric Positive-Definite (SPD) Manifold

Description

The collection of symmetric positive-definite matrices is a well-known example of matrix manifold. It is defined as

S++p={X∈Rp×p ∣ X⊤=X, v⊤Xv>0 for all v≠0}\mathcal{S}_{++}^p = \lbrace X \in \mathbf{R}^{p\times p} ~\vert~ X^\top = X,~ v^\top Xv>0~\textrm{for all}~v\ne0 \rbrace

where positivity is verified by Cholesky factorization after a scale-relative symmetry check. No ridge or positive-definite projection is applied. Tiny physical units alone do not make a matrix ill-conditioned. Please note that the geometry involving semi-definite matrices is considered in wrap.spdk.

Usage

wrap.spd(input)

Arguments

input

SPD data matrices to be wrapped as riemdata class. Following inputs are considered,

array

an (p×p×n)(p\times p\times n) array where each slice along 3rd dimension is a SPD matrix.

list

a length-nn list whose elements are (p×p)(p\times p) SPD matrices.

Value

a named riemdata S3 object containing

data

a list of (p×p)(p\times p) SPD matrices.

size

size of each SPD matrix.

name

name of the manifold of interests, "spd"

Examples

#-------------------------------------------------------------------
#                 Checker for Two Types of Inputs
#
#  Generate 5 observations; empirical covariance of normal observations.
#-------------------------------------------------------------------
#  Data Generation
d1 = array(0,c(3,3,5))
d2 = list()
for (i in 1:5){
  dat = matrix(rnorm(10*3),ncol=3)
  d1[,,i] = stats::cov(dat)
  d2[[i]] = d1[,,i]
}

#  Run
test1 = wrap.spd(d1)
test2 = wrap.spd(d2)

Prepare Data on Positive Semidefinite Manifold of Fixed Rank

Description

When (p×p)(p\times p) positive semidefinite matrices are of fixed rank k<pk < p, they form a geometric structure represented by (p×k)(p\times k) matrices,

SPD(k,p)={X∈R(p×p) ∣ YY⊤=X,rank(X)=k}SPD(k,p) = \lbrace X \in \mathbf{R}^{(p\times p)}~\vert~ Y Y^\top = X, \textrm{rank}(X) = k \rbrace

It's key difference from S++p\mathcal{S}_{++}^p is that all matrices should be of fixed rank kk where kk is usually smaller than pp. Inputs are given as (p×p)(p\times p) matrices with specified kk and wrap.spdk decomposes matrices whose numerical rank is exactly kk. Higher-rank inputs are rejected; rank reduction must be performed explicitly before wrapping. Numerical rank uses eigenvalues exceeding 64p64p times machine precision relative to the largest absolute eigenvalue. Negative eigenvalues beyond this roundoff tolerance are rejected.

Usage

wrap.spdk(input, k)

Arguments

input

data matrices to be wrapped as riemdata class. Following inputs are considered,

array

a (p×p×n)(p\times p\times n) array where each slice along 3rd dimension is a rank-kk matrix.

list

a length-nn list whose elements are (p×p)(p\times p) matrices of rank-kk.

k

rank of the positive semidefinite matrices, an integer in [1,p−1][1,p-1].

Value

a named riemdata S3 object containing

data

a list of (p×k)(p\times k) representation of the corresponding rank-kk SPSD matrices.

size

size of each representation matrix.

name

name of the manifold of interests, "spdk"

References

Journée M, Bach F, Absil P, Sepulchre R (2010). “Low-rank optimization on the cone of positive semidefinite matrices.” SIAM Journal on Optimization, 20(5), 2327–2351.

Examples

#-------------------------------------------------------------------
#                 Checker for Two Types of Inputs
#-------------------------------------------------------------------
#  Data Generation
d1 = array(0,c(10,10,3))
d2 = list()
for (i in 1:3){
  dat = matrix(rnorm(10*2),nrow=10)
  d1[,,i] = tcrossprod(dat)
  d2[[i]] = d1[,,i]
}

#  Run
test1 = wrap.spdk(d1, k=2)
test2 = wrap.spdk(d2, k=2)

Prepare Data on Sphere

Description

The unit hypersphere (sphere, for short) is one of the most fundamental curved space in studying geometry. Precisely, we denote (p−1)(p-1) sphere in Rp\mathbf{R}^p by

Sp−1={x∈Rp ∣ x⊤x=∥x∥2=1}\mathcal{S}^{p-1} = \lbrace x \in \mathbf{R}^p ~ \vert ~ x^\top x = \|x\|^2 = 1 \rbrace

where vectors are of unit norm. In wrap.sphere, normalization is applied when each data point is not on the unit sphere.

Usage

wrap.sphere(input)

Arguments

input

data vectors to be wrapped as riemdata class. Following inputs are considered,

matrix

an (n×p)(n \times p) matrix of row observations of unit norm.

list

a length-nn list whose elements are length-pp vectors of unit norm.

Value

a named riemdata S3 object containing

data

a list of (p×1)(p\times 1) matrices in Sp−1\mathcal{S}^{p-1}.

size

dimension of the ambient space.

name

name of the manifold of interests, "sphere"

Examples

#-------------------------------------------------------------------
#                 Checker for Two Types of Inputs
#
#  Generate 5 observations in S^2 embedded in R^3.
#-------------------------------------------------------------------
## DATA GENERATION
d1 = array(0,c(5,3))
d2 = list()
for (i in 1:5){
  single  = stats::rnorm(3)
  d1[i,]  = single
  d2[[i]] = single
}

## RUN
test1 = wrap.sphere(d1)
test2 = wrap.sphere(d2)

Prepare Data on (Compact) Stiefel Manifold

Description

Stiefel manifold St(k,p)St(k,p) is the set of kk-frames in Rp\mathbf{R}^p, which is indeed a Riemannian manifold. For usage in Riemann package, each data point is represented as a matrix by the convention

St(k,p)={X∈Rp×k ∣ X⊤X=Ik}St(k,p) = \lbrace X \in \mathbf{R}^{p\times k} ~\vert~ X^\top X = I_k \rbrace

which means that columns are orthonormal. When the provided matrix is not an orthonormal basis as above, wrap.stiefel applies orthogonalization to extract valid basis information.

Usage

wrap.stiefel(input)

Arguments

input

data matrices to be wrapped as riemdata class. Following inputs are considered,

array

a (p×k×n)(p\times k\times n) array where each slice along 3rd dimension is a kk-frame.

list

a length-nn list whose elements are (p×k)(p\times k) kk-frames.

Value

a named riemdata S3 object containing

data

a list of kk-frame orthonormal matrices.

size

size of each kk-frame basis matrix.

name

name of the manifold of interests, "stiefel"

Examples

#-------------------------------------------------------------------
#                 Checker for Two Types of Inputs
#
#  Generate 5 observations in St(2,4)
#-------------------------------------------------------------------
#  Data Generation by QR Decomposition
d1 = array(0,c(4,2,5))
d2 = list()
for (i in 1:5){
  d1[,,i] = qr.Q(qr(matrix(rnorm(4*2),ncol=2)))
  d2[[i]] = d1[,,i]
}

#  Run
test1 = wrap.stiefel(d1)
test2 = wrap.stiefel(d2)