Nothing
library(eigencore)
`%||%` <- function(x, y) if (is.null(x)) y else x
benchmark_safe_ratio <- function(numerator, denominator) {
out <- rep(NA_real_, length(numerator))
ok <- !is.na(numerator) & !is.na(denominator) & denominator != 0
out[ok] <- numerator[ok] / denominator[ok]
out
}
benchmark_row_sum <- function(...) {
values <- cbind(...)
out <- rep(NA_real_, nrow(values))
ok <- rowSums(!is.na(values)) > 0L
out[ok] <- rowSums(values[ok, , drop = FALSE], na.rm = TRUE)
out
}
benchmark_arg_value <- function(args, prefix) {
hit <- grep(paste0("^", prefix), args, value = TRUE)
if (!length(hit)) {
return(NULL)
}
sub(paste0("^", prefix), "", hit[[1L]])
}
benchmark_arg_csv <- function(args, prefix) {
value <- benchmark_arg_value(args, prefix)
if (is.null(value) || !nzchar(value)) {
return(NULL)
}
trimws(strsplit(value, ",", fixed = TRUE)[[1L]])
}
benchmark_args <- function(args = commandArgs(trailingOnly = TRUE)) {
iterations <- benchmark_arg_value(args, "--iterations=")
iterations <- if (is.null(iterations)) {
NA_integer_
} else {
as.integer(iterations)
}
if (!is.na(iterations) && iterations < 1L) {
stop("--iterations must be a positive integer.", call. = FALSE)
}
list(
quick = "--quick" %in% args,
save = "--save" %in% args,
strict = "--strict" %in% args,
block_candidate = "--block-candidate" %in% args,
include_dense = "--include-dense" %in% args,
svd_projected_stop = "--projected-stop" %in% args,
h_candidate = "--h-candidate" %in% args,
iterations = iterations,
subject = benchmark_arg_value(args, "--subject="),
methods = benchmark_arg_csv(args, "--methods="),
cases = benchmark_arg_csv(args, "--cases=")
)
}
benchmark_case_field <- function(case, name) {
case[[name, exact = TRUE]]
}
benchmark_case_id <- function(case) {
id <- benchmark_case_field(case, "id")
if (!is.null(id)) {
return(id)
}
case_name <- benchmark_case_field(case, "case")
m <- benchmark_case_field(case, "m")
n <- benchmark_case_field(case, "n")
if (!is.null(m) && !is.null(n)) {
return(paste0(case_name, ":", m, "x", n))
}
if (is.null(n)) {
case_name
} else {
paste0(case_name, ":", n)
}
}
filter_benchmark_cases <- function(cases, selected = NULL) {
if (is.null(selected) || !length(selected)) {
return(cases)
}
ids <- vapply(cases, benchmark_case_id, character(1))
names <- vapply(cases, benchmark_case_field, character(1), name = "case")
keep <- ids %in% selected | names %in% selected
if (!any(keep)) {
stop(
"--cases did not match any benchmark cases. Available cases: ",
paste(ids, collapse = ", "),
call. = FALSE
)
}
cases[keep]
}
message_benchmark_case <- function(label, case) {
requested <- case$k %||% case$rank %||% NA_integer_
requested_name <- if (!is.na(requested)) {
if (!is.null(case$rank)) " rank=" else " k="
} else {
" requested="
}
message(
"[", format(Sys.time(), "%Y-%m-%d %H:%M:%S"), "] ",
label, ": ", benchmark_case_id(case), requested_name, requested
)
flush.console()
}
svd_surface_default_methods <- function(args) {
methods <- c(
"eigencore",
"eigencore_golub_kahan",
"eigencore_randomized",
"RSpectra",
"irlba",
"rsvd",
"base"
)
if (isTRUE(args$svd_projected_stop)) {
methods <- append(methods, "eigencore_golub_kahan_projected", after = 2L)
}
if (isTRUE(args$h_candidate)) {
methods <- c(
"eigencore",
"eigencore_golub_kahan",
"eigencore_golub_kahan_one_sided",
"eigencore_irlba_lbd_one_sided",
"eigencore_irlba_lbd_retained_native",
"eigencore_irlba_lbd_retained_bpro",
"eigencore_irlba_lbd_retained_bpro_one_sided_guarded",
"eigencore_irlba_lbd_retained_bpro_block_guarded",
"eigencore_irlba_lbd_normal_scout",
"eigencore_golub_kahan_projected",
"eigencore_implicit_normal_lanczos",
"eigencore_gram_dsyevx",
"eigencore_block_golub_kahan_cycle",
"eigencore_block_golub_kahan_cycle_cached",
"eigencore_block_golub_kahan_cycle_cached_random",
"eigencore_block_golub_kahan_cycle_residual",
"eigencore_block_golub_kahan_cycle_lean",
"eigencore_block_golub_kahan_retained",
"eigencore_block_golub_kahan_retained_cached",
"eigencore_block_golub_kahan_retained_deflated",
"RSpectra",
"irlba",
"rsvd",
"base"
)
}
if (!is.null(args$methods)) {
methods <- args$methods
}
methods
}
svd_surface_gate_subject <- function(args, methods) {
subject <- args$subject %||% if (isTRUE(args$h_candidate)) {
"eigencore"
} else {
"eigencore"
}
if (!subject %in% methods) {
stop(
"SVD gate subject `", subject, "` is not in the selected methods. ",
"Use --methods=... to include it, --projected-stop, or --h-candidate.",
call. = FALSE
)
}
subject
}
svd_internal_methods <- function() {
c(
"eigencore",
"eigencore_smallest",
"eigencore_interior",
"eigencore_golub_kahan",
"eigencore_golub_kahan_smallest",
"eigencore_golub_kahan_interior",
"eigencore_golub_kahan_one_sided",
"eigencore_irlba_lbd_one_sided",
"eigencore_irlba_lbd_retained_native",
"eigencore_irlba_lbd_retained_bpro",
"eigencore_irlba_lbd_retained_bpro_one_sided_guarded",
"eigencore_irlba_lbd_retained_bpro_block_guarded",
"eigencore_irlba_lbd_normal_scout",
"eigencore_golub_kahan_projected",
"eigencore_implicit_normal_lanczos",
"eigencore_gram_dsyevx",
"eigencore_block_golub_kahan_cycle",
"eigencore_block_golub_kahan_cycle_cached",
"eigencore_block_golub_kahan_cycle_cached_random",
"eigencore_block_golub_kahan_cycle_residual",
"eigencore_block_golub_kahan_cycle_lean",
"eigencore_block_golub_kahan_retained",
"eigencore_block_golub_kahan_retained_cached",
"eigencore_block_golub_kahan_retained_deflated",
"eigencore_randomized"
)
}
svd_crossover_evidence_contract <- function(rows, summary,
subject = "eigencore",
orientations = c("tall", "wide")) {
required_row_fields <- c(
"case", "method", "rank", "certificate_passed", "nconv"
)
required_summary_fields <- c(
"case", "orientation", "long_side", "best_certified_reference",
"raw_speed_ratio", "certified_total_speed_ratio"
)
missing_fields <- c(
setdiff(required_row_fields, names(rows)),
setdiff(required_summary_fields, names(summary))
)
if (length(missing_fields)) {
stop(
"crossover evidence is missing required fields: ",
paste(unique(missing_fields), collapse = ", "),
call. = FALSE
)
}
subject_cases <- unique(rows$case[rows$method == subject])
external <- rows[
rows$method != subject & !rows$method %in% svd_internal_methods(),
,
drop = FALSE
]
external_certified <- nrow(external) > 0L && all(
!is.na(external$certificate_passed) & external$certificate_passed &
!is.na(external$nconv) & external$nconv >= external$rank
)
summary_complete <- length(subject_cases) > 0L &&
nrow(summary) == length(subject_cases) &&
identical(sort(as.character(summary$case)), sort(as.character(subject_cases))) &&
all(!is.na(summary$best_certified_reference)) &&
all(nzchar(summary$best_certified_reference)) &&
all(is.finite(summary$raw_speed_ratio) & summary$raw_speed_ratio > 0) &&
all(
is.finite(summary$certified_total_speed_ratio) &
summary$certified_total_speed_ratio > 0
)
orientations_complete <- all(orientations %in% unique(summary$orientation))
sampled_crossover <- function(field) {
orientations_complete && all(vapply(orientations, function(orientation) {
slice <- summary[summary$orientation == orientation, , drop = FALSE]
slice <- slice[order(slice$long_side), , drop = FALSE]
if (nrow(slice) < 2L) {
return(FALSE)
}
ratios <- slice[[field]]
faster <- which(ratios > 1)
any(vapply(faster, function(index) {
index > 1L && any(ratios[seq_len(index - 1L)] <= 1)
}, logical(1)))
}, logical(1)))
}
raw_crossover <- summary_complete && sampled_crossover("raw_speed_ratio")
certified_crossover <- summary_complete &&
sampled_crossover("certified_total_speed_ratio")
data.frame(
subject = subject,
external_reference_rows = nrow(external),
external_references_certified = external_certified,
summary_complete = summary_complete,
orientations_complete = orientations_complete,
raw_sampled_crossover = raw_crossover,
certified_total_sampled_crossover = certified_crossover,
passed = external_certified && summary_complete &&
orientations_complete && certified_crossover,
stringsAsFactors = FALSE
)
}
release_speed_gate <- function(kind) {
switch(
kind,
hermitian = 1.25,
svd = 1.1,
randomized_svd = 2.0,
generalized_eigen = 1.0,
lobpcg = 1.0,
1.0
)
}
release_memory_gate <- function(kind) {
switch(
kind,
generalized_eigen = 0.25,
lobpcg = 0.25,
1.0
)
}
path_laplacian <- function(n) {
Matrix::bandSparse(
n,
k = c(-1, 0, 1),
diagonals = list(rep(-1, n - 1L), c(1, rep(2, n - 2L), 1), rep(-1, n - 1L))
)
}
tall_skinny_sparse <- function(m, n, density = 0.01, seed = 1L) {
set.seed(seed)
Matrix::rsparsematrix(m, n, density = density)
}
dense_low_rank_spd <- function(n, rank = 20L, diagonal_shift = 1, seed = 1L) {
set.seed(seed)
X <- matrix(stats::rnorm(n * rank), nrow = n, ncol = rank)
tcrossprod(X) + diag(diagonal_shift, n)
}
slow_decay_svd_matrix <- function(m, n, decay = 0.35, seed = 1L) {
set.seed(seed)
r <- min(m, n)
U <- qr.Q(qr(matrix(stats::rnorm(m * r), nrow = m, ncol = r)))
V <- qr.Q(qr(matrix(stats::rnorm(n * r), nrow = n, ncol = r)))
d <- seq_len(r)^(-decay)
U %*% (d * t(V))
}
nearly_low_rank_svd_matrix <- function(m, n, rank, noise = 1e-3, seed = 1L) {
set.seed(seed)
U <- qr.Q(qr(matrix(stats::rnorm(m * rank), nrow = m, ncol = rank)))
V <- qr.Q(qr(matrix(stats::rnorm(n * rank), nrow = n, ncol = rank)))
signal <- U %*% (seq(rank, 1) * t(V))
signal + noise * matrix(stats::rnorm(m * n), nrow = m, ncol = n)
}
randomized_rsvd_benchmark_cases <- function(quick = FALSE) {
if (isTRUE(quick)) {
return(list(
list(
case = "exact_low_rank_dense",
id = "exact_low_rank_dense:120x80",
A = nearly_low_rank_svd_matrix(120L, 80L, rank = 8L, noise = 0, seed = 1601L),
rank = 6L,
seed = 1601L,
release_gate_required = FALSE,
release_gate_note = "small dense diagnostic; fixed public-result overhead dominates this size"
),
list(
case = "slow_decay_dense",
id = "slow_decay_dense:140x90",
A = slow_decay_svd_matrix(140L, 90L, decay = 0.25, seed = 1602L),
rank = 8L,
seed = 1602L,
release_gate_required = FALSE,
release_gate_note = "diagnostic row; rsvd baseline fails eigencore certification"
),
list(
case = "low_rank_sparse",
id = "low_rank_sparse:140x90",
A = {
set.seed(1603L)
Matrix::rsparsematrix(140L, 12L, density = 0.12) %*%
Matrix::rsparsematrix(12L, 90L, density = 0.12)
},
rank = 8L,
seed = 1603L,
release_gate_required = FALSE,
release_gate_note = "sparse native-path diagnostic; quick timing is fixed-overhead sensitive"
)
))
}
list(
list(
case = "exact_low_rank_dense",
id = "exact_low_rank_dense:2000x500",
A = nearly_low_rank_svd_matrix(2000L, 500L, rank = 60L, noise = 0, seed = 1701L),
rank = 50L,
seed = 1701L,
release_gate_required = TRUE,
release_gate_note = "large exact-low-rank randomized planner candidate"
),
list(
case = "nearly_low_rank_dense",
id = "nearly_low_rank_dense:2000x500",
A = nearly_low_rank_svd_matrix(2000L, 500L, rank = 80L, noise = 1e-3, seed = 1702L),
rank = 50L,
seed = 1702L,
release_gate_required = FALSE,
release_gate_note = "diagnostic row; rsvd baseline fails eigencore certification"
),
list(
case = "slow_decay_dense",
id = "slow_decay_dense:2000x500",
A = slow_decay_svd_matrix(2000L, 500L, decay = 0.25, seed = 1703L),
rank = 50L,
seed = 1703L,
release_gate_required = FALSE,
release_gate_note = "diagnostic row; rsvd baseline fails eigencore certification"
),
list(
case = "low_rank_sparse",
id = "low_rank_sparse:2000x500",
A = {
set.seed(1705L)
Matrix::rsparsematrix(2000L, 40L, density = 0.05) %*%
Matrix::rsparsematrix(40L, 500L, density = 0.05)
},
rank = 30L,
seed = 1705L,
release_gate_required = FALSE,
release_gate_note = "native sparse CSC controller contract; sparse baselines are diagnostic rather than promoted release gates"
)
)
}
generalized_spd_pair <- function(n, rank = 12L, sparse = FALSE, seed = 1L) {
set.seed(seed)
if (isTRUE(sparse)) {
A <- path_laplacian(n) + Matrix::Diagonal(n, 0.25)
weights <- stats::runif(n, min = 0.5, max = 2)
B <- Matrix::Diagonal(n, weights)
} else {
X <- matrix(stats::rnorm(n * rank), nrow = n, ncol = rank)
Y <- matrix(stats::rnorm(n * rank), nrow = n, ncol = rank)
A <- tcrossprod(X) + diag(0.5, n)
B <- tcrossprod(Y) + diag(1, n)
}
list(A = A, B = B)
}
dense_hermitian_with_spectrum <- function(values, seed = 1L) {
n <- length(values)
set.seed(seed)
Q <- qr.Q(qr(matrix(stats::rnorm(n * n), nrow = n, ncol = n)))
Q %*% (values * t(Q))
}
g1_candidate_baseline_cases <- function(quick = FALSE) {
path_n <- if (isTRUE(quick)) 80L else 120L
list(
list(
case = "path_laplacian",
A = path_laplacian(path_n),
k = 4L,
target = smallest(),
seed = 1200L + path_n
),
list(
case = "dense_hermitian",
A = dense_hermitian_with_spectrum(c(9, 6, 4, 2, 1, 0.5, 0.25, 0.1), seed = 1208L),
k = 3L,
target = largest(),
seed = 1208L
),
list(
case = "clustered",
A = dense_hermitian_with_spectrum(c(9, 8.999, 8.998, 2, 1, 0.5, 0.25, 0.1), seed = 1210L),
k = 3L,
target = largest(),
seed = 1210L
),
list(
case = "ill_conditioned_diag",
A = diag(c(1e8, 1e4, 1, 1e-4, 1e-8, 1e-12)),
k = 3L,
target = largest(),
seed = 1206L
)
)
}
benchmark_g1_candidate_baseline <- function(quick = FALSE,
iterations = if (isTRUE(quick)) 1L else 5L,
methods = c("eigencore", "eigencore_block_candidate", "RSpectra", "PRIMME"),
tol = 1e-8) {
rows <- lapply(g1_candidate_baseline_cases(quick = quick), function(case) {
out <- benchmark_eigen_case(
case$A,
k = case$k,
target = case$target,
methods = methods,
iterations = iterations,
tol = tol,
seed = case$seed
)
out$case <- case$case
out$n <- nrow(case$A)
out$k <- case$k
out
})
rows <- do.call(rbind, rows)
row.names(rows) <- NULL
rows
}
bench_methods <- function(kind, requested = NULL) {
methods <- if (identical(kind, "eigen")) {
benchmark_available_eigen_methods()
} else {
benchmark_available_svd_methods()
}
if (is.null(requested)) {
return(methods)
}
missing <- setdiff(requested, methods)
if (length(missing)) {
label <- if (identical(kind, "eigen")) "eigen" else "SVD"
stop(
"Requested ", label, " benchmark method(s) unavailable in the loaded ",
"eigencore namespace: ", paste(missing, collapse = ", "), ". ",
"Available methods: ", paste(methods, collapse = ", "), ". ",
"If this is a source checkout benchmark, reinstall eigencore or run the ",
"script under pkgload::load_all().",
call. = FALSE
)
}
requested
}
benchmark_available_eigen_methods <- function() {
c(
eigencore:::available_eigen_methods(),
if (requireNamespace("PRIMME", quietly = TRUE)) "PRIMME"
)
}
benchmark_available_svd_methods <- function() {
c(
eigencore:::available_svd_methods(),
if (requireNamespace("PRIMME", quietly = TRUE)) "PRIMME"
)
}
run_benchmark_eigen_method <- function(method, A, k, target, tol) {
if (identical(method, "PRIMME")) {
which <- eigencore:::target_to_rspectra_which(target, symmetric = TRUE)
return(PRIMME::eigs_sym(A, NEig = k, which = which, tol = tol))
}
eigencore:::run_eigen_method(method, A, k = k, target = target, tol = tol)
}
run_benchmark_svd_method <- function(method, A, rank, tol, seed = NULL) {
if (identical(method, "PRIMME")) {
return(PRIMME::svds(A, NSvals = rank, tol = tol))
}
eigencore:::run_svd_method(method, A, rank = rank, tol = tol, seed = seed)
}
run_timed <- function(expr, iterations = 3L, seed = NULL) {
expr <- substitute(expr)
env <- parent.frame()
if (!requireNamespace("bench", quietly = TRUE)) {
stop("bench is required for release benchmarks.", call. = FALSE)
}
value <- NULL
eval_once <- function() {
if (!is.null(seed)) {
set.seed(seed)
}
eval(expr, envir = env)
}
mark <- bench::mark(
value <- eval_once(),
iterations = iterations,
check = FALSE,
time_unit = "s",
# bench::mark(memory = TRUE) calls utils::Rprofmem(), which errors on R
# builds without --enable-memory-profiling (e.g. r-devel fedora).
memory = isTRUE(capabilities("profmem")),
filter_gc = FALSE
)
list(
value = value,
median = stats::median(as.numeric(mark$time[[1L]])),
min = min(as.numeric(mark$time[[1L]])),
mem_alloc = as.numeric(mark$mem_alloc[[1L]]),
bench = mark
)
}
certify_eigen_result <- function(A, fit, tol = 1e-8) {
if (!is.null(fit$certificate)) {
return(fit$certificate)
}
vals <- eigencore:::method_values(fit, kind = "eigen")
vecs <- fit$vectors
if (is.null(vals) || is.null(vecs)) return(eigencore:::empty_certificate(tol, "vectors unavailable"))
eigencore:::certify_eigen_operator(as_operator(A), vals, vecs, tol = tol)
}
certify_svd_result <- function(A, fit, tol = 1e-8) {
if (!is.null(fit$certificate)) {
return(fit$certificate)
}
d <- eigencore:::method_values(fit, kind = "svd")
u <- fit$u
v <- fit$v
if (is.null(d) || is.null(u) || is.null(v)) {
return(eigencore:::empty_certificate(tol, "both singular-vector sides unavailable"))
}
eigencore:::certify_svd_operator(as_operator(A), d, u, v, tol = tol)
}
svd_certificate_source <- function(fit) {
if (is.null(fit$certificate)) {
"eigencore_recomputed"
} else {
"method_certificate"
}
}
certificate_residual_max <- function(cert, side) {
residuals <- cert$residuals %||% list()
value <- residuals[[side]]
if (is.null(value) || !length(value)) {
return(NA_real_)
}
max(value, na.rm = TRUE)
}
certificate_orthogonality_component <- function(cert, side) {
orth <- cert$orthogonality %||% numeric()
if (!length(orth)) {
return(NA_real_)
}
if (!is.null(names(orth)) && side %in% names(orth)) {
return(as.numeric(orth[[side]]))
}
idx <- match(side, c("U", "V"))
if (!is.na(idx) && length(orth) >= idx) {
return(as.numeric(orth[[idx]]))
}
NA_real_
}
svd_result_sorted_descending <- function(fit) {
d <- eigencore:::method_values(fit, kind = "svd")
if (is.null(d) || length(d) < 2L) {
return(TRUE)
}
all(diff(as.numeric(d)) <= sqrt(.Machine$double.eps) * max(1, abs(d), na.rm = TRUE))
}
svd_subspace_error <- function(observed, oracle) {
if (is.null(observed) || is.null(oracle) || !ncol(observed) || !ncol(oracle)) {
return(NA_real_)
}
observed <- qr.Q(qr(as.matrix(observed)))
oracle <- qr.Q(qr(as.matrix(oracle)))
sqrt(sum((tcrossprod(observed) - tcrossprod(oracle))^2))
}
svd_oracle_accuracy <- function(A, fit, rank, oracle = NULL) {
if (is.null(oracle)) {
oracle <- svd(as.matrix(A), nu = rank, nv = rank)
}
idx <- seq_len(min(rank, length(oracle$d)))
observed <- eigencore:::method_values(fit, kind = "svd")
observed <- observed[seq_len(min(length(observed), length(idx)))]
expected <- oracle$d[idx][seq_along(observed)]
singular_value_relative_error <- if (length(observed)) {
max(abs(observed - expected) / pmax(abs(expected), .Machine$double.eps))
} else {
Inf
}
u <- fit$u
v <- fit$v
list(
singular_value_relative_error = singular_value_relative_error,
left_subspace_error = svd_subspace_error(u, oracle$u[, idx, drop = FALSE]),
right_subspace_error = svd_subspace_error(v, oracle$v[, idx, drop = FALSE])
)
}
time_certified_eigen_method <- function(method, A, k, target, tol, seed,
iterations) {
set.seed(seed)
warm_fit <- run_benchmark_eigen_method(method, A, k = k, target = target, tol = tol)
warm_cert <- certify_eigen_result(A, warm_fit, tol = tol)
rm(warm_fit, warm_cert)
gc()
set.seed(seed)
solver <- run_timed(
run_benchmark_eigen_method(method, A, k = k, target = target, tol = tol),
iterations = iterations,
seed = seed
)
fit_for_cert <- solver$value
certificate <- run_timed(
certify_eigen_result(A, fit_for_cert, tol = tol),
iterations = iterations
)
set.seed(seed)
total <- run_timed({
fit <- run_benchmark_eigen_method(method, A, k = k, target = target, tol = tol)
cert <- certify_eigen_result(A, fit, tol = tol)
list(fit = fit, cert = cert)
}, iterations = iterations, seed = seed)
list(
fit = total$value$fit,
cert = total$value$cert,
solver = solver,
certificate = certificate,
total = total
)
}
time_certified_svd_method <- function(method, A, rank, tol, seed, iterations) {
set.seed(seed)
warm_fit <- run_benchmark_svd_method(method, A, rank = rank, tol = tol, seed = seed)
warm_cert <- certify_svd_result(A, warm_fit, tol = tol)
rm(warm_fit, warm_cert)
gc()
set.seed(seed)
solver <- run_timed(
run_benchmark_svd_method(method, A, rank = rank, tol = tol, seed = seed),
iterations = iterations,
seed = seed
)
fit_for_cert <- solver$value
certificate <- run_timed(
certify_svd_result(A, fit_for_cert, tol = tol),
iterations = iterations
)
set.seed(seed)
total <- run_timed({
fit <- run_benchmark_svd_method(method, A, rank = rank, tol = tol, seed = seed)
cert <- certify_svd_result(A, fit, tol = tol)
list(fit = fit, cert = cert)
}, iterations = iterations, seed = seed)
list(
fit = total$value$fit,
cert = total$value$cert,
solver = solver,
certificate = certificate,
total = total
)
}
result_preconditioner_field <- function(x, field) {
info <- x$preconditioner %||% x$restart$preconditioner %||% NULL
if (is.null(info) || is.null(info[[field]])) {
return(NA)
}
info[[field]]
}
result_preconditioner_calls <- function(x) {
x$preconditioner_calls %||% x$restart$preconditioner_calls %||% NA_integer_
}
result_restart_field <- function(x, field) {
info <- x$restart %||% NULL
if (is.null(info) || is.null(info[[field]])) {
return(NA)
}
info[[field]]
}
result_restart_logical <- function(x, field) {
value <- result_restart_field(x, field)
if (length(value) != 1L || is.na(value)) {
return(NA)
}
isTRUE(value)
}
result_restart_numeric <- function(x, field) {
value <- result_restart_field(x, field)
if (length(value) != 1L || is.na(value)) {
return(NA_real_)
}
as.numeric(value)
}
result_iterations <- function(x) {
x$iterations %||% x$niter %||% NA_integer_
}
result_matvecs <- function(x) {
x$matvecs %||% x$nops %||% NA_integer_
}
result_restarts <- function(x) {
x$restarts %||% result_restart_field(x, "restarts_used")
}
result_ortho_passes <- function(x) {
x$ortho_passes %||% result_restart_field(x, "ortho_passes")
}
result_locking_events <- function(x) {
x$locking_events %||% result_restart_field(x, "locking_events")
}
result_block_size <- function(x) {
x$block %||% result_restart_field(x, "block")
}
result_restart_character <- function(x, field) {
value <- result_restart_field(x, field)
if (!length(value) || all(is.na(value))) {
return(NA_character_)
}
paste(as.character(value), collapse = ",")
}
result_restart_integer <- function(x, field) {
value <- result_restart_field(x, field)
if (length(value) != 1L || is.na(value)) {
return(NA_integer_)
}
as.integer(value)
}
result_attempt_history <- function(x) {
history <- result_restart_field(x, "attempt_history")
if (is.data.frame(history)) {
history
} else {
NULL
}
}
result_attempt_history_max <- function(x, field) {
history <- result_attempt_history(x)
if (is.null(history) || !field %in% names(history) || !nrow(history)) {
return(NA_integer_)
}
value <- max(history[[field]], na.rm = TRUE)
if (!is.finite(value)) NA_integer_ else as.integer(value)
}
result_attempt_history_count_true <- function(x, field) {
history <- result_attempt_history(x)
if (is.null(history) || !field %in% names(history) || !nrow(history)) {
return(NA_integer_)
}
value <- history[[field]]
if (!is.logical(value)) {
return(NA_integer_)
}
as.integer(sum(value, na.rm = TRUE))
}
result_certified_attempt <- function(x) {
history <- result_attempt_history(x)
if (is.null(history) || !"certificate_passed" %in% names(history) || !nrow(history)) {
return(NA_integer_)
}
if (!is.logical(history$certificate_passed)) {
return(NA_integer_)
}
passed <- which(history$certificate_passed)
if (length(passed)) as.integer(history$attempt[[passed[[1L]]]]) else NA_integer_
}
result_stage_seconds <- function(x, field) {
stages <- x$stage_seconds %||% result_restart_field(x, "stage_seconds") %||% NULL
if (is.null(stages) || !field %in% names(stages)) {
return(NA_real_)
}
as.numeric(stages[[field]])
}
benchmark_failed_eigen_row <- function(method, seed, error) {
data.frame(
method = method,
median = Inf,
min = Inf,
mem_alloc = NA_real_,
solver_median = Inf,
solver_min = Inf,
solver_mem_alloc = NA_real_,
certificate_median = Inf,
certificate_min = Inf,
certificate_mem_alloc = NA_real_,
total_median = Inf,
total_min = Inf,
total_mem_alloc = NA_real_,
max_residual = Inf,
max_backward_error = Inf,
orthogonality_loss = Inf,
certificate_passed = FALSE,
certificate_type = "method_error",
norm_bound_type = NA_character_,
scale_is_estimate = NA,
nconv = 0L,
iterations = NA_integer_,
matvecs = NA_integer_,
restarts = NA_integer_,
ortho_passes = NA_integer_,
locking_events = NA_integer_,
block_size = NA_integer_,
native = NA,
native_kernels = NA,
generalized = NA,
orthogonalization_native = NA,
orthogonalization_methods = NA_character_,
q_rank_final = NA_integer_,
constrained = NA,
constraints_rank = NA_integer_,
stage_apply_seconds = NA_real_,
stage_recurrence_seconds = NA_real_,
stage_reorthogonalization_seconds = NA_real_,
stage_projected_solve_seconds = NA_real_,
stage_projection_update_seconds = NA_real_,
stage_projection_copy_seconds = NA_real_,
stage_projected_eigensolve_seconds = NA_real_,
stage_selected_vector_copy_seconds = NA_real_,
stage_ritz_residual_seconds = NA_real_,
stage_ritz_vector_form_seconds = NA_real_,
stage_ritz_operator_apply_seconds = NA_real_,
stage_ritz_norm_seconds = NA_real_,
stage_ritz_final_polish_seconds = NA_real_,
stage_locking_seconds = NA_real_,
stage_restart_seconds = NA_real_,
preconditioner_kind = NA_character_,
preconditioner_native = NA,
preconditioner_calls = NA_integer_,
seed = seed,
pkg_version = as.character(utils::packageVersion("eigencore")),
error = conditionMessage(error),
stringsAsFactors = FALSE
)
}
benchmark_eigen_case <- function(A, k, target = largest(), methods = NULL,
iterations = 3L, tol = 1e-8, seed = 1L) {
methods <- bench_methods("eigen", methods)
rows <- lapply(methods, function(method) {
timed <- tryCatch(
time_certified_eigen_method(method, A, k, target, tol, seed, iterations),
error = function(e) e
)
if (inherits(timed, "error")) {
return(benchmark_failed_eigen_row(method, seed, timed))
}
fit <- timed$fit
cert <- timed$cert
data.frame(
method = method,
median = timed$total$median,
min = timed$total$min,
mem_alloc = timed$total$mem_alloc,
solver_median = timed$solver$median,
solver_min = timed$solver$min,
solver_mem_alloc = timed$solver$mem_alloc,
certificate_median = timed$certificate$median,
certificate_min = timed$certificate$min,
certificate_mem_alloc = timed$certificate$mem_alloc,
total_median = timed$total$median,
total_min = timed$total$min,
total_mem_alloc = timed$total$mem_alloc,
max_residual = cert$max_residual,
max_backward_error = cert$max_backward_error,
orthogonality_loss = cert$max_orthogonality_loss,
certificate_passed = cert$passed,
certificate_type = cert$certificate_type,
norm_bound_type = cert$norm_bound_type,
scale_is_estimate = cert$scale_is_estimate,
nconv = sum(cert$converged),
iterations = result_iterations(fit),
matvecs = result_matvecs(fit),
restarts = result_restarts(fit),
ortho_passes = result_ortho_passes(fit),
locking_events = result_locking_events(fit),
block_size = result_block_size(fit),
native = result_restart_logical(fit, "native"),
native_kernels = result_restart_logical(fit, "native_kernels"),
generalized = result_restart_logical(fit, "generalized"),
orthogonalization_native = result_restart_logical(fit, "orthogonalization_native"),
orthogonalization_methods = result_restart_character(fit, "orthogonalization_methods"),
q_rank_final = result_restart_integer(fit, "q_rank_final"),
constrained = result_restart_logical(fit, "constrained"),
constraints_rank = result_restart_integer(fit, "constraints_rank"),
stage_apply_seconds = result_stage_seconds(fit, "apply"),
stage_recurrence_seconds = result_stage_seconds(fit, "recurrence"),
stage_reorthogonalization_seconds = result_stage_seconds(fit, "reorthogonalization"),
stage_projected_solve_seconds = result_stage_seconds(fit, "projected_solve"),
stage_projection_update_seconds = result_stage_seconds(fit, "projection_update"),
stage_projection_copy_seconds = result_stage_seconds(fit, "projection_copy"),
stage_projected_eigensolve_seconds = result_stage_seconds(fit, "projected_eigensolve"),
stage_selected_vector_copy_seconds = result_stage_seconds(fit, "selected_vector_copy"),
stage_ritz_residual_seconds = result_stage_seconds(fit, "ritz_residual"),
stage_ritz_vector_form_seconds = result_stage_seconds(fit, "ritz_vector_form"),
stage_ritz_operator_apply_seconds = result_stage_seconds(fit, "ritz_operator_apply"),
stage_ritz_norm_seconds = result_stage_seconds(fit, "ritz_norm"),
stage_ritz_final_polish_seconds = result_stage_seconds(fit, "ritz_final_polish"),
stage_locking_seconds = result_stage_seconds(fit, "locking"),
stage_restart_seconds = result_stage_seconds(fit, "restart"),
preconditioner_kind = result_preconditioner_field(fit, "kind"),
preconditioner_native = result_preconditioner_field(fit, "native"),
preconditioner_calls = result_preconditioner_calls(fit),
seed = seed,
pkg_version = as.character(utils::packageVersion("eigencore")),
error = "",
stringsAsFactors = FALSE
)
})
do.call(rbind, rows)
}
benchmark_generalized_eigen_case <- function(A, B, k, target = smallest(),
methods = "eigencore",
iterations = 3L, tol = 1e-8,
seed = 1L) {
methods <- intersect(methods, c("eigencore", "base"))
rows <- lapply(methods, function(method) {
timed <- run_timed({
if (identical(method, "eigencore")) {
eig_partial(A, B = B, k = k, target = target, tol = tol)
} else {
eig <- eigencore:::dense_generalized_spd_eigen(as.matrix(A), as.matrix(B))
idx <- eigencore:::order_indices(eig$values, target)
idx <- idx[seq_len(k)]
list(values = eig$values[idx], vectors = eig$vectors[, idx, drop = FALSE])
}
}, iterations = iterations, seed = seed)
cert <- eigencore:::certify_eigen(as.matrix(A), eigencore:::method_values(timed$value, kind = "eigen"),
timed$value$vectors, B = as.matrix(B), tol = tol)
data.frame(
method = method,
median = timed$median,
min = timed$min,
mem_alloc = timed$mem_alloc,
max_residual = cert$max_residual,
max_backward_error = cert$max_backward_error,
orthogonality_loss = cert$max_orthogonality_loss,
certificate_passed = cert$passed,
certificate_type = cert$certificate_type,
norm_bound_type = cert$norm_bound_type,
scale_is_estimate = cert$scale_is_estimate,
nconv = sum(cert$converged),
iterations = result_iterations(timed$value),
matvecs = result_matvecs(timed$value),
restarts = result_restarts(timed$value),
ortho_passes = result_ortho_passes(timed$value),
locking_events = result_locking_events(timed$value),
block_size = result_block_size(timed$value),
native = result_restart_logical(timed$value, "native"),
native_kernels = result_restart_logical(timed$value, "native_kernels"),
generalized = result_restart_logical(timed$value, "generalized"),
orthogonalization_native = result_restart_logical(timed$value, "orthogonalization_native"),
orthogonalization_methods = result_restart_character(timed$value, "orthogonalization_methods"),
q_rank_final = result_restart_integer(timed$value, "q_rank_final"),
constrained = result_restart_logical(timed$value, "constrained"),
constraints_rank = result_restart_integer(timed$value, "constraints_rank"),
preconditioner_kind = result_preconditioner_field(timed$value, "kind"),
preconditioner_native = result_preconditioner_field(timed$value, "native"),
preconditioner_calls = result_preconditioner_calls(timed$value),
seed = seed,
pkg_version = as.character(utils::packageVersion("eigencore")),
stringsAsFactors = FALSE
)
})
do.call(rbind, rows)
}
evaluate_native_hermitian_gate <- function(rows, k,
subject = "eigencore",
speed_ratio_required = release_speed_gate("hermitian"),
memory_ratio_required = 1.0,
reference_methods = "RSpectra",
parity_methods = "PRIMME",
parity_ratio_required = 1.0) {
eig <- rows[rows$method == subject, , drop = FALSE]
speed_refs <- rows[
rows$method %in% reference_methods & rows$certificate_passed,
,
drop = FALSE
]
parity_present <- any(rows$method %in% parity_methods)
parity_refs <- rows[
rows$method %in% parity_methods & rows$certificate_passed,
,
drop = FALSE
]
memory_ref_methods <- unique(c(reference_methods, parity_methods))
memory_refs <- rows[
rows$method %in% memory_ref_methods & rows$certificate_passed,
,
drop = FALSE
]
if (nrow(eig) != 1L) {
stop("gate requires exactly one subject row", call. = FALSE)
}
speed_ratio <- if (nrow(speed_refs)) {
min(speed_refs$median) / eig$median
} else {
NA_real_
}
memory_ratio <- if (nrow(memory_refs)) {
min(memory_refs$mem_alloc) / eig$mem_alloc
} else {
NA_real_
}
parity_required <- parity_present && length(parity_methods) > 0L
parity_ratio <- if (nrow(parity_refs)) {
min(parity_refs$median) / eig$median
} else {
NA_real_
}
eig_certified <- isTRUE(eig$certificate_passed) && eig$nconv >= k
speed_gate <- nrow(speed_refs) > 0L && isTRUE(speed_ratio >= speed_ratio_required)
memory_gate <- nrow(memory_refs) > 0L && isTRUE(memory_ratio >= memory_ratio_required)
parity_gate <- if (isTRUE(parity_required)) {
isTRUE(parity_ratio >= parity_ratio_required)
} else {
TRUE
}
notes <- character()
if (!nrow(speed_refs)) {
notes <- c(notes, "no certified speed reference rows")
}
if (!nrow(memory_refs)) {
notes <- c(notes, "no certified memory reference rows")
}
if (isTRUE(parity_required) && !nrow(parity_refs)) {
notes <- c(notes, "parity reference present but uncertified")
} else if (!isTRUE(parity_required)) {
notes <- c(notes, "parity reference not present")
}
note <- paste(notes, collapse = "; ")
data.frame(
eigencore_certified = eig_certified,
eigencore_nconv = eig$nconv,
subject = subject,
requested = k,
speed_ratio_vs_best_reference = speed_ratio,
memory_ratio_vs_best_reference = memory_ratio,
parity_ratio_vs_best_reference = parity_ratio,
speed_reference_methods = paste(reference_methods, collapse = ","),
memory_reference_methods = paste(memory_ref_methods, collapse = ","),
parity_reference_methods = paste(parity_methods, collapse = ","),
parity_required = parity_required,
speed_gate = speed_gate,
memory_gate = memory_gate,
parity_gate = parity_gate,
passed = eig_certified && speed_gate && memory_gate && parity_gate,
note = note,
stringsAsFactors = FALSE
)
}
evaluate_preconditioned_lobpcg_gate <- function(rows, k,
speed_ratio_required = release_speed_gate("lobpcg"),
memory_ratio_required = release_memory_gate("lobpcg")) {
lob <- rows[rows$method %in% c(
"eigencore_lobpcg_preconditioned",
"eigencore_lobpcg_tridiagonal"
), , drop = FALSE]
scalar <- rows[rows$method == "eigencore", , drop = FALSE]
refs <- rows[
rows$method %in% c("RSpectra", "PRIMME") & rows$certificate_passed,
,
drop = FALSE
]
if (nrow(lob) < 1L || nrow(scalar) != 1L) {
stop("gate requires exactly one scalar and at least one preconditioned LOBPCG row", call. = FALSE)
}
if (!nrow(refs)) {
stop("gate requires at least one certified reference row", call. = FALSE)
}
lob <- lob[order(lob$median), , drop = FALSE][1L, , drop = FALSE]
best_time <- min(refs$median)
best_mem <- min(refs$mem_alloc)
speed_ratio_vs_scalar <- scalar$median / lob$median
speed_ratio_vs_best_reference <- best_time / lob$median
memory_ratio_vs_best_reference <- best_mem / lob$mem_alloc
lob_certified <- isTRUE(lob$certificate_passed) && lob$nconv >= k
scalar_speed_gate <- isTRUE(speed_ratio_vs_scalar >= speed_ratio_required)
reference_speed_gate <- isTRUE(speed_ratio_vs_best_reference >= speed_ratio_required)
memory_gate <- isTRUE(memory_ratio_vs_best_reference >= memory_ratio_required)
data.frame(
lobpcg_certified = lob_certified,
lobpcg_nconv = lob$nconv,
requested = k,
selected_method = lob$method,
preconditioner_kind = lob$preconditioner_kind,
preconditioner_native = lob$preconditioner_native,
preconditioner_calls = lob$preconditioner_calls,
speed_ratio_vs_scalar = speed_ratio_vs_scalar,
speed_ratio_vs_best_reference = speed_ratio_vs_best_reference,
memory_ratio_vs_best_reference = memory_ratio_vs_best_reference,
scalar_speed_gate = scalar_speed_gate,
reference_speed_gate = reference_speed_gate,
memory_gate = memory_gate,
passed = lob_certified && scalar_speed_gate && reference_speed_gate && memory_gate,
stringsAsFactors = FALSE
)
}
benchmark_native_hermitian_gate <- function(A, k, target = smallest(),
iterations = 3L, tol = 1e-8,
seed = 1L,
methods = c("eigencore", "RSpectra", "PRIMME"),
subject = "eigencore",
reference_methods = "RSpectra",
parity_methods = "PRIMME") {
rows <- benchmark_eigen_case(
A,
k = k,
target = target,
methods = methods,
iterations = iterations,
tol = tol,
seed = seed
)
gate <- evaluate_native_hermitian_gate(
rows,
k = k,
subject = subject,
reference_methods = reference_methods,
parity_methods = parity_methods
)
list(rows = rows, gate = gate)
}
evaluate_reference_gate <- function(rows, subject = "eigencore", references = setdiff(unique(rows$method), subject),
requested, speed_ratio_required = release_speed_gate("hermitian"),
memory_ratio_required = 1.0) {
eig <- rows[rows$method == subject, , drop = FALSE]
refs <- rows[rows$method %in% references & rows$certificate_passed, , drop = FALSE]
if (nrow(eig) != 1L) {
stop("gate requires exactly one subject row", call. = FALSE)
}
if (!nrow(refs)) {
return(data.frame(
subject = subject,
subject_certified = isTRUE(eig$certificate_passed) && eig$nconv >= requested,
subject_nconv = eig$nconv,
requested = requested,
speed_ratio_vs_best_reference = NA_real_,
memory_ratio_vs_best_reference = NA_real_,
speed_gate = FALSE,
memory_gate = FALSE,
passed = FALSE,
note = "no certified reference rows",
stringsAsFactors = FALSE
))
}
best_time <- min(refs$median)
best_mem <- min(refs$mem_alloc)
speed_ratio <- best_time / eig$median
memory_ratio <- best_mem / eig$mem_alloc
subject_certified <- isTRUE(eig$certificate_passed) && eig$nconv >= requested
speed_gate <- isTRUE(speed_ratio >= speed_ratio_required)
memory_gate <- isTRUE(memory_ratio >= memory_ratio_required)
data.frame(
subject = subject,
subject_certified = subject_certified,
subject_nconv = eig$nconv,
requested = requested,
speed_ratio_vs_best_reference = speed_ratio,
memory_ratio_vs_best_reference = memory_ratio,
speed_gate = speed_gate,
memory_gate = memory_gate,
passed = subject_certified && speed_gate && memory_gate,
note = "",
stringsAsFactors = FALSE
)
}
evaluate_memory_diagnostics <- function(rows, subject = "eigencore",
references = setdiff(unique(rows$method), subject),
requested) {
eig <- rows[rows$method == subject, , drop = FALSE]
refs <- rows[rows$method %in% references & rows$certificate_passed, , drop = FALSE]
if (nrow(eig) != 1L) {
stop("memory diagnostics require exactly one subject row", call. = FALSE)
}
subject_certified <- isTRUE(eig$certificate_passed) && eig$nconv >= requested
if (!nrow(refs)) {
return(data.frame(
subject = subject,
subject_certified = subject_certified,
subject_nconv = eig$nconv,
requested = requested,
subject_total_mem_alloc = eig$mem_alloc,
subject_solver_mem_alloc = eig$solver_mem_alloc,
subject_certificate_mem_alloc = eig$certificate_mem_alloc,
subject_solver_memory_fraction = eig$solver_mem_alloc / eig$mem_alloc,
subject_certificate_memory_fraction = eig$certificate_mem_alloc / eig$mem_alloc,
best_reference = NA_character_,
best_reference_total_mem_alloc = NA_real_,
total_memory_gap_bytes = NA_real_,
solver_memory_gap_bytes = NA_real_,
certificate_memory_gap_bytes = NA_real_,
total_memory_ratio_vs_best_reference = NA_real_,
solver_memory_ratio_vs_best_reference = NA_real_,
certificate_memory_ratio_vs_best_reference = NA_real_,
note = "no certified reference rows",
stringsAsFactors = FALSE
))
}
ref <- refs[order(refs$mem_alloc), , drop = FALSE][1L, , drop = FALSE]
data.frame(
subject = subject,
subject_certified = subject_certified,
subject_nconv = eig$nconv,
requested = requested,
subject_total_mem_alloc = eig$mem_alloc,
subject_solver_mem_alloc = eig$solver_mem_alloc,
subject_certificate_mem_alloc = eig$certificate_mem_alloc,
subject_solver_memory_fraction = eig$solver_mem_alloc / eig$mem_alloc,
subject_certificate_memory_fraction = eig$certificate_mem_alloc / eig$mem_alloc,
best_reference = ref$method,
best_reference_total_mem_alloc = ref$mem_alloc,
total_memory_gap_bytes = eig$mem_alloc - ref$mem_alloc,
solver_memory_gap_bytes = eig$solver_mem_alloc - ref$solver_mem_alloc,
certificate_memory_gap_bytes = eig$certificate_mem_alloc - ref$certificate_mem_alloc,
total_memory_ratio_vs_best_reference = ref$mem_alloc / eig$mem_alloc,
solver_memory_ratio_vs_best_reference = ref$solver_mem_alloc / eig$solver_mem_alloc,
certificate_memory_ratio_vs_best_reference = if (eig$certificate_mem_alloc > 0) {
ref$certificate_mem_alloc / eig$certificate_mem_alloc
} else {
NA_real_
},
note = "",
stringsAsFactors = FALSE
)
}
benchmark_randomized_rsvd_case <- function(A, rank, methods = c("eigencore_randomized", "rsvd"),
iterations = 3L, tol = 1e-8,
seed = 1L) {
methods <- bench_methods("svd", methods)
oracle <- svd(as.matrix(A), nu = rank, nv = rank)
rows <- lapply(methods, function(method) {
timed <- time_certified_svd_method(method, A, rank, tol, seed, iterations)
fit <- timed$fit
cert <- timed$cert
accuracy <- svd_oracle_accuracy(A, fit, rank = rank, oracle = oracle)
data.frame(
method = method,
median = timed$total$median,
min = timed$total$min,
mem_alloc = timed$total$mem_alloc,
solver_median = timed$solver$median,
solver_mem_alloc = timed$solver$mem_alloc,
certificate_median = timed$certificate$median,
certificate_mem_alloc = timed$certificate$mem_alloc,
max_residual = cert$max_residual,
max_backward_error = cert$max_backward_error,
orthogonality_loss = cert$max_orthogonality_loss,
certificate_passed = cert$passed,
certificate_type = cert$certificate_type,
norm_bound_type = cert$norm_bound_type,
scale_is_estimate = cert$scale_is_estimate,
nconv = sum(cert$converged),
stage_random_seconds = result_stage_seconds(fit, "random"),
stage_apply_seconds = result_stage_seconds(fit, "apply"),
stage_normalize_seconds = result_stage_seconds(fit, "normalize"),
stage_small_svd_seconds = result_stage_seconds(fit, "small_svd"),
stage_vector_form_seconds = result_stage_seconds(fit, "vector_form"),
stage_internal_certificate_seconds = result_stage_seconds(fit, "certificate"),
stage_refinement_seconds = result_stage_seconds(fit, "refinement"),
randomized_native_sketch = result_restart_logical(fit, "native_sketch"),
randomized_sketch_kind = result_restart_character(fit, "sketch_kind"),
randomized_controller_native = result_restart_logical(fit, "controller_native"),
randomized_controller_kind = result_restart_character(fit, "controller_kind"),
randomized_dense_native_controller = result_restart_logical(fit, "dense_native_controller"),
randomized_sparse_native_controller = result_restart_logical(fit, "sparse_native_controller"),
randomized_native_certificate_diagnostics =
result_restart_logical(fit, "native_certificate_diagnostics"),
randomized_adaptive_stop_used = result_restart_logical(fit, "adaptive_stop_used"),
randomized_core_solver = result_restart_character(fit, "core_solver"),
randomized_projection_kind = result_restart_character(fit, "projection_kind"),
randomized_projection_transposed = result_restart_logical(fit, "projection_transposed"),
singular_value_relative_error = accuracy$singular_value_relative_error,
left_subspace_error = accuracy$left_subspace_error,
right_subspace_error = accuracy$right_subspace_error,
seed = seed,
pkg_version = as.character(utils::packageVersion("eigencore")),
stringsAsFactors = FALSE
)
})
out <- do.call(rbind, rows)
row.names(out) <- NULL
out
}
evaluate_randomized_rsvd_gate <- function(rows, subject = "eigencore_randomized",
baseline = "rsvd", requested,
speed_ratio_required = release_speed_gate("randomized_svd"),
accuracy_multiplier = 1.05,
accuracy_floor = 1e-12) {
eig <- rows[rows$method == subject, , drop = FALSE]
ref <- rows[rows$method == baseline, , drop = FALSE]
if (nrow(eig) != 1L) {
stop("randomized rsvd gate requires exactly one subject row", call. = FALSE)
}
if (nrow(ref) != 1L) {
return(data.frame(
subject = subject,
baseline = baseline,
subject_certified = isTRUE(eig$certificate_passed) && eig$nconv >= requested,
baseline_certified = FALSE,
subject_nconv = eig$nconv,
baseline_nconv = NA_integer_,
requested = requested,
speed_ratio_vs_rsvd = NA_real_,
singular_value_error_ratio_vs_rsvd = NA_real_,
left_subspace_error_ratio_vs_rsvd = NA_real_,
right_subspace_error_ratio_vs_rsvd = NA_real_,
speed_gate = FALSE,
accuracy_gate = FALSE,
passed = FALSE,
note = "rsvd baseline row unavailable",
stringsAsFactors = FALSE
))
}
sv_ratio <- eig$singular_value_relative_error /
max(ref$singular_value_relative_error, accuracy_floor)
left_ratio <- eig$left_subspace_error / max(ref$left_subspace_error, accuracy_floor)
right_ratio <- eig$right_subspace_error / max(ref$right_subspace_error, accuracy_floor)
speed_ratio <- ref$median / eig$median
subject_certified <- isTRUE(eig$certificate_passed) && eig$nconv >= requested
baseline_certified <- isTRUE(ref$certificate_passed) && ref$nconv >= requested
accuracy_gate <- isTRUE(sv_ratio <= accuracy_multiplier) &&
(is.na(left_ratio) || isTRUE(left_ratio <= accuracy_multiplier)) &&
(is.na(right_ratio) || isTRUE(right_ratio <= accuracy_multiplier))
speed_gate <- baseline_certified && isTRUE(speed_ratio >= speed_ratio_required)
data.frame(
subject = subject,
baseline = baseline,
subject_certified = subject_certified,
baseline_certified = baseline_certified,
subject_nconv = eig$nconv,
baseline_nconv = ref$nconv,
requested = requested,
speed_ratio_vs_rsvd = speed_ratio,
singular_value_error_ratio_vs_rsvd = sv_ratio,
left_subspace_error_ratio_vs_rsvd = left_ratio,
right_subspace_error_ratio_vs_rsvd = right_ratio,
speed_gate = speed_gate,
accuracy_gate = accuracy_gate,
passed = subject_certified && baseline_certified && speed_gate && accuracy_gate,
note = if (!baseline_certified) {
"rsvd baseline did not satisfy eigencore certificate"
} else {
""
},
stringsAsFactors = FALSE
)
}
randomized_controller_contract <- function(rows) {
eig <- rows[rows$method == "eigencore_randomized", , drop = FALSE]
if (!nrow(eig)) {
return(data.frame())
}
out <- lapply(seq_len(nrow(eig)), function(idx) {
row <- eig[idx, , drop = FALSE]
case_name <- if ("case" %in% names(row)) as.character(row$case) else ""
is_sparse_case <- grepl("sparse", case_name, fixed = TRUE)
expected_kind <- if (is_sparse_case) {
"native_csc_randomized_controller"
} else {
"native_dense_randomized_controller"
}
provenance_gate <- isTRUE(row$randomized_controller_native) &&
isTRUE(row$randomized_native_certificate_diagnostics) &&
identical(as.character(row$randomized_controller_kind), expected_kind) &&
if (is_sparse_case) {
isTRUE(row$randomized_sparse_native_controller) &&
!isTRUE(row$randomized_dense_native_controller)
} else {
isTRUE(row$randomized_dense_native_controller) &&
!isTRUE(row$randomized_sparse_native_controller)
}
certificate_gate <- isTRUE(row$certificate_passed) &&
isTRUE(row$nconv >= row$rank) &&
!isTRUE(row$scale_is_estimate)
data.frame(
case = case_name,
rank = row$rank,
controller_kind = as.character(row$randomized_controller_kind),
native_certificate_diagnostics =
isTRUE(row$randomized_native_certificate_diagnostics),
certificate_gate = certificate_gate,
provenance_gate = provenance_gate,
passed = certificate_gate && provenance_gate,
stringsAsFactors = FALSE
)
})
out <- do.call(rbind, out)
row.names(out) <- NULL
out
}
benchmark_svd_case <- function(A, rank, methods = NULL, iterations = 3L,
tol = 1e-8, seed = 1L) {
methods <- bench_methods("svd", methods)
rows <- lapply(methods, function(method) {
timed <- time_certified_svd_method(method, A, rank, tol, seed, iterations)
fit <- timed$fit
cert <- timed$cert
cert_source <- svd_certificate_source(fit)
data.frame(
method = method,
solver_label = fit$method %||% NA_character_,
median = timed$total$median,
min = timed$total$min,
mem_alloc = timed$total$mem_alloc,
raw_solver_median = timed$solver$median,
raw_solver_min = timed$solver$min,
raw_solver_mem_alloc = timed$solver$mem_alloc,
eigencore_certificate_median = timed$certificate$median,
eigencore_certificate_min = timed$certificate$min,
eigencore_certificate_mem_alloc = timed$certificate$mem_alloc,
eigencore_certified_total_median = timed$total$median,
eigencore_certified_total_min = timed$total$min,
eigencore_certified_total_mem_alloc = timed$total$mem_alloc,
certificate_source = cert_source,
certificate_recomputed_by_eigencore =
identical(cert_source, "eigencore_recomputed"),
solver_median = timed$solver$median,
solver_min = timed$solver$min,
solver_mem_alloc = timed$solver$mem_alloc,
certificate_median = timed$certificate$median,
certificate_min = timed$certificate$min,
certificate_mem_alloc = timed$certificate$mem_alloc,
total_median = timed$total$median,
total_min = timed$total$min,
total_mem_alloc = timed$total$mem_alloc,
max_left_residual = certificate_residual_max(cert, "left"),
max_right_residual = certificate_residual_max(cert, "right"),
max_cyclic_residual = certificate_residual_max(cert, "combined"),
max_residual = cert$max_residual,
max_backward_error = cert$max_backward_error,
orthogonality_loss = cert$max_orthogonality_loss,
orthogonality_U = certificate_orthogonality_component(cert, "U"),
orthogonality_V = certificate_orthogonality_component(cert, "V"),
singular_values_sorted = svd_result_sorted_descending(fit),
certificate_passed = cert$passed,
certificate_type = cert$certificate_type,
norm_bound_type = cert$norm_bound_type,
scale_is_estimate = cert$scale_is_estimate,
nconv = sum(cert$converged),
iterations = result_iterations(fit),
matvecs = result_matvecs(fit),
restarts = result_restarts(fit),
ortho_passes = result_ortho_passes(fit),
locking_events = result_locking_events(fit),
block_size = result_block_size(fit),
stage_apply_seconds = result_stage_seconds(fit, "apply"),
stage_recurrence_seconds = result_stage_seconds(fit, "recurrence"),
stage_reorthogonalization_seconds = result_stage_seconds(fit, "reorthogonalization"),
stage_projected_solve_seconds = result_stage_seconds(fit, "projected_solve"),
stage_projection_update_seconds = result_stage_seconds(fit, "projection_update"),
stage_projection_copy_seconds = result_stage_seconds(fit, "projection_copy"),
stage_projected_eigensolve_seconds = result_stage_seconds(fit, "projected_eigensolve"),
stage_selected_vector_copy_seconds = result_stage_seconds(fit, "selected_vector_copy"),
stage_ritz_residual_seconds = result_stage_seconds(fit, "ritz_residual"),
stage_ritz_vector_form_seconds = result_stage_seconds(fit, "ritz_vector_form"),
stage_ritz_operator_apply_seconds = result_stage_seconds(fit, "ritz_operator_apply"),
stage_ritz_norm_seconds = result_stage_seconds(fit, "ritz_norm"),
stage_ritz_final_polish_seconds = result_stage_seconds(fit, "ritz_final_polish"),
stage_locking_seconds = result_stage_seconds(fit, "locking"),
stage_restart_seconds = result_stage_seconds(fit, "restart"),
stage_gram_seconds = result_stage_seconds(fit, "gram"),
stage_gram_eigensolve_seconds = result_stage_seconds(fit, "eigensolve"),
stage_gram_vector_form_seconds = result_stage_seconds(fit, "vector_form"),
stage_gram_diagnostics_seconds = result_stage_seconds(fit, "diagnostics"),
restart_attempts = result_restart_field(fit, "attempts"),
final_iterations = result_restart_field(fit, "final_iterations"),
final_matvecs = result_restart_field(fit, "final_matvecs"),
projected_stop_requested = result_restart_logical(fit, "projected_stop_requested"),
projected_stop_enabled = result_restart_logical(fit, "projected_stop_enabled"),
projected_stop_disable_reason = result_restart_field(fit, "projected_stop_disable_reason"),
projected_stop = result_restart_logical(fit, "projected_stop"),
projected_nconv = result_restart_field(fit, "projected_nconv"),
projected_max_residual = result_restart_numeric(fit, "projected_max_residual"),
projected_checks = result_restart_field(fit, "projected_checks"),
projected_seconds = result_restart_numeric(fit, "projected_seconds"),
native_workspace_bytes = result_restart_numeric(fit, "native_workspace_bytes"),
native_workspace_allocator = result_restart_character(fit, "native_workspace_allocator"),
basis_returned = result_restart_logical(fit, "basis_returned"),
retained_restart = result_restart_logical(fit, "retained_restart"),
retained_restart_native = result_restart_logical(fit, "retained_restart_native"),
retained_av_cache = result_restart_logical(fit, "retained_av_cache"),
retained_deflation = result_restart_logical(fit, "retained_deflation"),
retained_locked_count = result_restart_integer(fit, "retained_locked_count"),
native_attempt_certification = result_restart_logical(fit, "native_attempt_certification"),
native_early_stop = result_restart_logical(fit, "native_early_stop"),
retained_converged_count = result_attempt_history_max(fit, "converged_count"),
retained_leading_converged_count = result_attempt_history_max(fit, "leading_converged_count"),
reorthogonalization_passes = result_restart_field(fit, "reorthogonalization_passes"),
first_certified_prefix = result_restart_field(fit, "first_certified_prefix"),
final_prefix_iteration_overshoot = result_restart_field(fit, "final_prefix_iteration_overshoot"),
final_prefix_matvec_overshoot = result_restart_field(fit, "final_prefix_matvec_overshoot"),
stage_native_iteration_seconds = result_stage_seconds(fit, "native_iteration"),
stage_golub_kahan_ritz_seconds = result_stage_seconds(fit, "ritz"),
stage_retry_overhead_seconds = result_stage_seconds(fit, "retry_overhead"),
attempted_subspaces = result_restart_character(fit, "attempted_subspaces"),
max_attempted_subspace = result_attempt_history_max(fit, "max_subspace"),
max_start_cols = result_attempt_history_max(fit, "start_cols"),
warm_started_attempts = result_attempt_history_count_true(fit, "warm_started"),
cached_start_attempts = result_attempt_history_count_true(fit, "cached_start_used"),
irlba_lbd_small_work_matvecs =
result_restart_integer(fit, "irlba_lbd_small_work_matvecs"),
irlba_lbd_fallback_matvecs =
result_restart_integer(fit, "irlba_lbd_fallback_matvecs"),
irlba_lbd_scout_matvec_overhead_fraction =
result_restart_numeric(fit, "irlba_lbd_scout_matvec_overhead_fraction"),
irlba_lbd_small_work_accounted_seconds =
result_restart_numeric(fit, "irlba_lbd_small_work_accounted_seconds"),
irlba_lbd_fallback_accounted_seconds =
result_restart_numeric(fit, "irlba_lbd_fallback_accounted_seconds"),
irlba_lbd_retained_native_attempted =
result_restart_logical(fit, "irlba_lbd_retained_native_attempted"),
irlba_lbd_retained_matvecs =
result_restart_integer(fit, "irlba_lbd_retained_matvecs"),
irlba_lbd_total_matvecs =
result_restart_integer(fit, "irlba_lbd_total_matvecs"),
irlba_lbd_retained_native_fallback_reason =
result_restart_character(fit, "irlba_lbd_retained_native_fallback_reason"),
irlba_lbd_locking_policy =
result_restart_character(fit, "irlba_lbd_locking_policy"),
irlba_lbd_lock_source =
result_restart_character(fit, "irlba_lbd_lock_source"),
irlba_lbd_soft_locked_count =
result_restart_integer(fit, "irlba_lbd_soft_locked_count"),
irlba_lbd_hard_locked_count =
result_restart_integer(fit, "irlba_lbd_hard_locked_count"),
irlba_lbd_locked_triplets_certified =
result_restart_logical(fit, "irlba_lbd_locked_triplets_certified"),
irlba_lbd_locked_orthogonality_loss =
result_restart_numeric(fit, "irlba_lbd_locked_orthogonality_loss"),
irlba_lbd_future_vectors_orthogonal_to_locks =
result_restart_logical(fit, "irlba_lbd_future_vectors_orthogonal_to_locks"),
irlba_lbd_lock_fallback_reason =
result_restart_character(fit, "irlba_lbd_lock_fallback_reason"),
irlba_lbd_restart_state_kind =
result_restart_character(fit, "irlba_lbd_restart_state_kind"),
irlba_lbd_recurrence_available =
result_restart_logical(fit, "irlba_lbd_recurrence_available"),
irlba_lbd_augmented_recurrence =
result_restart_logical(fit, "irlba_lbd_augmented_recurrence"),
irlba_lbd_residual_augmented_cols =
result_restart_integer(fit, "irlba_lbd_residual_augmented_cols"),
irlba_lbd_augmented_tail_steps =
result_restart_integer(fit, "irlba_lbd_augmented_tail_steps"),
irlba_lbd_augmented_basis_cols =
result_restart_integer(fit, "irlba_lbd_augmented_basis_cols"),
irlba_lbd_augmented_restart_cycles =
result_restart_integer(fit, "irlba_lbd_augmented_restart_cycles"),
irlba_lbd_augmented_kept_vectors =
result_restart_integer(fit, "irlba_lbd_augmented_kept_vectors"),
irlba_lbd_augmented_small_svds =
result_restart_integer(fit, "irlba_lbd_augmented_small_svds"),
irlba_lbd_augmented_cached_aq_cols =
result_restart_integer(fit, "irlba_lbd_augmented_cached_aq_cols"),
irlba_lbd_augmented_from_scratch_matvecs =
result_restart_integer(fit, "irlba_lbd_augmented_from_scratch_matvecs"),
irlba_lbd_augmented_matvec_savings =
result_restart_integer(fit, "irlba_lbd_augmented_matvec_savings"),
irlba_lbd_augmented_min_cheap_residual =
result_restart_numeric(fit, "irlba_lbd_augmented_min_cheap_residual"),
irlba_lbd_augmented_final_cheap_residual =
result_restart_numeric(fit, "irlba_lbd_augmented_final_cheap_residual"),
irlba_lbd_augmented_reduces_from_scratch_work =
result_restart_logical(fit, "irlba_lbd_augmented_reduces_from_scratch_work"),
irlba_lbd_native_certificate_diagnostics_reused =
result_restart_logical(fit, "irlba_lbd_native_certificate_diagnostics_reused"),
irlba_lbd_native_certificate_diagnostics_swapped =
result_restart_logical(fit, "irlba_lbd_native_certificate_diagnostics_swapped"),
irlba_lbd_bpro_policy =
result_restart_logical(fit, "irlba_lbd_bpro_policy"),
irlba_lbd_bpro_passes_per_append =
result_restart_integer(fit, "irlba_lbd_bpro_passes_per_append"),
irlba_lbd_bpro_monitoring_threshold =
result_restart_numeric(fit, "irlba_lbd_bpro_monitoring_threshold"),
irlba_lbd_bpro_monitored_appends =
result_restart_integer(fit, "irlba_lbd_bpro_monitored_appends"),
irlba_lbd_bpro_threshold_reorthogonalizations =
result_restart_integer(fit, "irlba_lbd_bpro_threshold_reorthogonalizations"),
irlba_lbd_bpro_max_estimated_orthogonality_loss =
result_restart_numeric(fit, "irlba_lbd_bpro_max_estimated_orthogonality_loss"),
irlba_lbd_bpro_max_post_append_orthogonality_loss =
result_restart_numeric(fit, "irlba_lbd_bpro_max_post_append_orthogonality_loss"),
irlba_lbd_bpro_basis_orthogonality_loss =
result_restart_numeric(fit, "irlba_lbd_bpro_basis_orthogonality_loss"),
irlba_lbd_bpro_escalation_recommended =
result_restart_logical(fit, "irlba_lbd_bpro_escalation_recommended"),
irlba_lbd_reorth_mode =
result_restart_character(fit, "irlba_lbd_reorth_mode"),
irlba_lbd_one_sided_reorth_used =
result_restart_logical(fit, "irlba_lbd_one_sided_reorth_used"),
irlba_lbd_bpro_block_size =
result_restart_integer(fit, "irlba_lbd_bpro_block_size"),
irlba_lbd_bpro_exact_orthogonality_loss =
result_restart_numeric(fit, "irlba_lbd_bpro_exact_orthogonality_loss"),
irlba_lbd_bpro_exact_orthogonality_passed =
result_restart_logical(fit, "irlba_lbd_bpro_exact_orthogonality_passed"),
irlba_lbd_bpro_guard_fallback_reason =
result_restart_character(fit, "irlba_lbd_bpro_guard_fallback_reason"),
irlba_lbd_retained_seed_strategy =
result_restart_character(fit, "irlba_lbd_retained_seed_strategy"),
irlba_lbd_retained_from_scout =
result_restart_integer(fit, "irlba_lbd_retained_from_scout"),
irlba_lbd_retained_padding =
result_restart_integer(fit, "irlba_lbd_retained_padding"),
irlba_lbd_retained_fixed_work_attempts =
result_restart_integer(fit, "irlba_lbd_retained_fixed_work_attempts"),
irlba_lbd_scout_matvecs =
result_restart_integer(fit, "irlba_lbd_scout_matvecs"),
irlba_lbd_scout_certificate_passed =
result_restart_logical(fit, "irlba_lbd_scout_certificate_passed"),
irlba_lbd_normal_scout_attempted =
result_restart_logical(fit, "irlba_lbd_normal_scout_attempted"),
irlba_lbd_normal_scout_steps =
result_restart_character(fit, "irlba_lbd_normal_scout_steps"),
irlba_lbd_normal_scout_chosen_steps =
result_restart_integer(fit, "irlba_lbd_normal_scout_chosen_steps"),
irlba_lbd_normal_scout_count =
result_restart_integer(fit, "irlba_lbd_normal_scout_count"),
irlba_lbd_normal_scout_side =
result_restart_character(fit, "irlba_lbd_normal_scout_side"),
irlba_lbd_normal_scout_materialized =
result_restart_logical(fit, "irlba_lbd_normal_scout_materialized"),
irlba_lbd_normal_scout_certificate_trusted =
result_restart_logical(fit, "irlba_lbd_normal_scout_certificate_trusted"),
irlba_lbd_normal_scout_matvecs =
result_restart_integer(fit, "irlba_lbd_normal_scout_matvecs"),
irlba_lbd_normal_scout_operator_matvecs =
result_restart_integer(fit, "irlba_lbd_normal_scout_operator_matvecs"),
irlba_lbd_normal_scout_iterations =
result_restart_integer(fit, "irlba_lbd_normal_scout_iterations"),
irlba_lbd_normal_scout_accounted_seconds =
result_restart_numeric(fit, "irlba_lbd_normal_scout_accounted_seconds"),
irlba_lbd_normal_scout_polish_matvecs =
result_restart_integer(fit, "irlba_lbd_normal_scout_polish_matvecs"),
irlba_lbd_normal_scout_polish_iterations =
result_restart_integer(fit, "irlba_lbd_normal_scout_polish_iterations"),
irlba_lbd_normal_scout_polish_accounted_seconds =
result_restart_numeric(fit, "irlba_lbd_normal_scout_polish_accounted_seconds"),
certified_attempt = result_certified_attempt(fit),
final_attempt_matvecs = result_restart_integer(fit, "final_attempt_matvecs"),
final_attempt_ortho_passes = result_restart_integer(fit, "final_attempt_ortho_passes"),
total_ortho_passes = result_restart_integer(fit, "total_ortho_passes"),
fallback_attempted = result_restart_logical(fit, "fallback_attempted"),
fallback_used = result_restart_logical(fit, "fallback_used"),
fallback_method = result_restart_field(fit, "fallback_method"),
gram_max_backward_error = result_restart_numeric(fit, "gram_max_backward_error"),
gram_certificate_passed = result_restart_logical(fit, "gram_certificate_passed"),
gram_dimension = result_restart_integer(fit, "gram_dimension"),
native_gram_kernel = result_restart_character(fit, "native_gram_kernel"),
native_gram_eigensolver = result_restart_character(fit, "native_gram_eigensolver"),
native_gram_subspace_max_backward_error =
result_restart_numeric(fit, "native_gram_subspace_max_backward_error"),
normal_operator_implicit = result_restart_logical(fit, "normal_operator_implicit"),
materialized_gram = result_restart_logical(fit, "materialized_gram"),
certified_in_original_coordinates =
result_restart_logical(fit, "certified_in_original_coordinates"),
native_implicit_normal_lanczos_max_backward_error =
result_restart_numeric(fit, "native_implicit_normal_lanczos_max_backward_error"),
native_implicit_normal_lanczos_iterations =
result_restart_integer(fit, "native_implicit_normal_lanczos_iterations"),
explicit_gram_retry_used =
result_restart_logical(fit, "explicit_gram_retry_used"),
fallback_max_backward_error = result_restart_numeric(fit, "fallback_max_backward_error"),
preconditioner_kind = result_preconditioner_field(fit, "kind"),
preconditioner_native = result_preconditioner_field(fit, "native"),
preconditioner_calls = result_preconditioner_calls(fit),
seed = seed,
pkg_version = as.character(utils::packageVersion("eigencore")),
stringsAsFactors = FALSE
)
})
out <- do.call(rbind, rows)
out$native_stage_accounted_seconds <- benchmark_row_sum(
out$stage_apply_seconds,
out$stage_recurrence_seconds,
out$stage_reorthogonalization_seconds,
out$stage_projected_solve_seconds
)
out$stage_reorthogonalization_fraction <- benchmark_safe_ratio(
out$stage_reorthogonalization_seconds,
out$native_stage_accounted_seconds
)
out$reorthogonalization_seconds_per_pass <- benchmark_safe_ratio(
out$stage_reorthogonalization_seconds,
out$reorthogonalization_passes
)
out$reorthogonalization_passes_per_iteration <- benchmark_safe_ratio(
out$reorthogonalization_passes,
out$final_iterations
)
out$native_seconds_per_matvec <- benchmark_safe_ratio(
out$native_stage_accounted_seconds,
out$final_matvecs
)
out$projected_seconds_per_check <- benchmark_safe_ratio(
out$projected_seconds,
out$projected_checks
)
out
}
tiny_gram_fixture <- function(n, width = max(4L * n, n + 16L),
density = 0.03, seed = 1L) {
set.seed(seed)
A <- Matrix::rsparsematrix(n, width, density = density)
gram <- as.matrix(Matrix::tcrossprod(A))
0.5 * (gram + t(gram))
}
tiny_gram_backend_fit <- function(gram, k, backend) {
backend <- match.arg(backend, c(
"lapack_dsyevr_selected",
"lapack_dsyevx_selected",
"lapack_dsyev_full",
"lapack_dsyevd_full"
))
if (identical(backend, "lapack_dsyevr_selected")) {
eigencore:::native_dense_symmetric_eigen_selected(gram, k, largest())
} else if (identical(backend, "lapack_dsyevx_selected")) {
.Call(
"eigencore_dense_symmetric_eigen_dsyevx_selected",
gram,
as.integer(k),
as.integer(1L),
PACKAGE = "eigencore"
)
} else {
fit <- if (identical(backend, "lapack_dsyevd_full")) {
eigencore:::native_dense_symmetric_eigen_dsyevd(gram)
} else {
eigencore:::native_dense_symmetric_eigen(gram)
}
idx <- order(fit$values, decreasing = TRUE)
idx <- idx[seq_len(min(k, length(idx)))]
list(
values = fit$values[idx],
vectors = fit$vectors[, idx, drop = FALSE]
)
}
}
benchmark_tiny_gram_eigensolvers <- function(dimensions = c(32L, 64L, 90L, 128L),
ranks = c(5L, 8L, 16L),
backends = c(
"lapack_dsyevr_selected",
"lapack_dsyevx_selected",
"lapack_dsyev_full",
"lapack_dsyevd_full"
),
iterations = 20L,
seed = 810L) {
if (!requireNamespace("bench", quietly = TRUE)) {
stop("bench is required for tiny Gram eigensolver benchmarks.", call. = FALSE)
}
rows <- list()
out_idx <- 1L
for (n in as.integer(dimensions)) {
gram <- tiny_gram_fixture(n, seed = seed + n)
oracle_rank <- min(max(as.integer(ranks)), n)
oracle <- eigencore:::native_dense_symmetric_eigen_selected(
gram,
oracle_rank,
largest()
)
for (k in as.integer(ranks)) {
k <- min(k, n)
oracle_values <- oracle$values[seq_len(k)]
for (backend in backends) {
fit <- tiny_gram_backend_fit(gram, k, backend)
mark <- bench::mark(
tiny_gram_backend_fit(gram, k, backend),
iterations = iterations,
check = FALSE,
time_unit = "s",
# bench::mark(memory = TRUE) calls utils::Rprofmem(), which errors on
# R builds without --enable-memory-profiling (e.g. r-devel fedora).
memory = isTRUE(capabilities("profmem")),
filter_gc = FALSE
)
rows[[out_idx]] <- data.frame(
dimension = n,
rank = k,
backend = backend,
median = stats::median(as.numeric(mark$time[[1L]])),
min = min(as.numeric(mark$time[[1L]])),
mem_alloc = as.numeric(mark$mem_alloc[[1L]]),
max_value_error = max(abs(fit$values - oracle_values)),
values_sorted = all(diff(fit$values) <=
sqrt(.Machine$double.eps) *
max(1, abs(fit$values), na.rm = TRUE)),
stringsAsFactors = FALSE
)
out_idx <- out_idx + 1L
}
}
}
out <- do.call(rbind, rows)
out$winner <- as.logical(ave(
out$median,
out$dimension,
out$rank,
FUN = function(x) as.integer(x == min(x, na.rm = TRUE))
))
row.names(out) <- NULL
out
}
save_benchmark_result <- function(result, name) {
dir.create("inst/benchmarks/results", recursive = TRUE, showWarnings = FALSE)
path <- file.path(
"inst/benchmarks/results",
paste0(format(Sys.Date(), "%Y%m%d"), "-", name, ".rds")
)
saveRDS(result, path)
path
}
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.