tests/testthat/test-citation-helpers.R

library(Matrix)
library(RSpectra)

test_that("p_omega_f_norm_ut", {

  # important: make sure to test when there are elements on the diagonal
  # in practice this shouldn't happen, but we still want correct
  # calculations when this is the case. must be diagonal!
  M <- Matrix(
    rbind(
      c(4, 0, 3, 1, 0),
      c(3, 0, 0, 8, 0),
      c(0, -1, 0, 0, 0),
      c(0, 2, 0, 0, 0),
      c(5, 0, 7, 0, 4)
    )
  )

  # NOTE: not treating nodes as citing themselves!
  Y <- rbind(
    c(1, 1, 1, 1, 1),
    c(1, 0, 1, 1, 1),
    c(0, 1, 0, 1, 1),
    c(0, 1, 0, 0, 1),
    c(1, 0, 1, 0, 1)
  )

  s <- svds(M, 2)

  # triplet form
  # compressed column matrix form even better but don't
  # understand the format
  Y <- as(Y, "TsparseMatrix")

  Z <- s$u %*% diag(s$d) %*% t(s$v)
  approximation <- Z * Y

  expected <- sum(approximation@x^2)

  impl_result <- p_omega_f_norm_ut_impl(s$u, s$d, s$v, Y@i, Y@j, 1L)
  wrapped_result <- p_omega_f_norm_ut(s, Y)

  expect_equal(impl_result, expected)
  expect_equal(wrapped_result, expected)
})

test_that("citation svd", {

  set.seed(27)

  # matrix to build an SVD from
  M <- rsparsematrix(40, 40, nnz = 50)
  x <- rnorm(40)

  r <- 5
  s <- svds(M, r)

  # expand the SVD only at observed values by hand

  Z <- s$u %*% diag(s$d) %*% t(s$v)  # full SVD
  Y <- M != 0                        # observed indicator

  # add the upper triangle back to Y since we're in the citation
  # setting
  Y <- Y | upper.tri(Y)

  # KEY! M_tilde = P_\Omega(M) + P_\Omega^\perp(Z_t) x
  M_tilde <- M + as(!Y, "CsparseMatrix") * Z

  ### SVD: reference calculation

  expected_svd <- svd(M_tilde, r, r)

  ### SVD: fancy calculation

  args <- list(u = s$u, d = s$d, v = s$v, M = M)

  s_new <- svds(
    Ax_citation,
    k = r,
    Atrans = Atx_citation,
    dim = dim(M),
    args = args
  )

  expect_true(equal_svds(s_new, expected_svd))

  ### alpha: reference calculation

  expected_alpha <- mean(expected_svd$d[(r+1):length(expected_svd$d)]^2)

  ### alpha: fancy calculation

  f_norm_M <- sum(M@x^2)

  # p_omega_f_norm_ut() expects r singular values where r is the rank
  # of the decomposition, but svd() always returns *all* singular values
  # be sure to use s, corresponding to Z_(t-1) here
  clean_svd <- s
  clean_svd$d <- clean_svd$d[1:r]

  # s is the SVD of M
  M_tilde_f_norm <- f_norm_M + sum(s$d^2) - p_omega_f_norm_ut(clean_svd, M)

  # make sure we got the norm of M tilde right
  expect_equal(M_tilde_f_norm, sum(M_tilde^2))

  # as a general rule we want to find the average of the remaining
  # singular values. but in this case, we are working with a wide
  # matrix instead of a tall matrix, so it is impossible to calculate
  # d = ncol(M) singular values. this serves as a brief reminder of
  # that:

  num_s_values <- min(dim(M))

  fancy_alpha <- (M_tilde_f_norm - sum(s_new$d^2)) / (num_s_values - r)

  expect_equal(
    fancy_alpha,
    expected_alpha
  )
})

Try the fastadi package in your browser

Any scripts or data that you put into this service are public.

fastadi documentation built on June 8, 2025, 12:44 p.m.