tests/testthat/test-Jacobian.R

test_that("Jacobians are correct", {
  f <- function(x) c(sine = sum(sin(x)), expon = sum(exp(x)))
  expect_equal(as.numeric(Jacobian(x = 1:3, f)), as.numeric(rbind(sine = cos(1:3), expon = exp(1:3))),
               tolerance = 1e-9)

  x <- structure(1:3, names = c("A", "B", "C"))
  g <- Jacobian(f, x)
  expect_identical(colnames(g), names(x))
  expect_identical(rownames(g), c("sine", "expon"))
  expect_identical(attr(g, "step.size.method"), "default")
  expect_identical(attr(Jacobian(f, x, h = 0.01), "step.size.method"), "user-supplied")
})

test_that("Jacobian works for scalar functions", {
  h1 <- function(x) {
    h <- rep(NA, 1)
    h[1] <- x[1] + x[2] + x[3] - 1
    h
  }
  h2 <- function(x) {
    h <- rep(NA, 1)
    h[1] <- 6*x[2] + 4*x[3] - x[1]^3 - 3
    h[2] <- x[1]
    h[3] <- x[2]
    h[4] <- x[3]
    h
  }
  p0 <- structure(c(0.1, 0.2, 0.3), names = c("A", "B", "C"))
  h1.jac <- Jacobian(h1, p0)
  h2.jac <- pnd::Jacobian(h2, p0)
  expect_true(inherits(h1.jac, "GenD"))
  expect_true(inherits(h2.jac, "GenD"))
  expect_identical(dim(h1.jac), c(1L, 3L))
  expect_identical(dim(h2.jac), c(4L, 3L))
  expect_identical(colnames(h1.jac), c("A", "B", "C"))
  expect_identical(colnames(h2.jac), c("A", "B", "C"))
})

test_that("Jacobian works well with multi-valued functions", {
  set.seed(1)
  X <- rchisq(20, 3)
  Y <- 1 + X + rnorm(20)
  d <- data.frame(Y, X)
  mod <- lm(Y ~ X, data = d)
  # Least-squares moment conditions
  g <- function(theta, data) {
    res <- Y - theta[1] - theta[2]*X
    cbind(U = res, UX = res*data$X)
  }
  # colMeans(g(mod$coefficients, d))  # Around 1e-16
  ghat <- function(theta) colMeans(g(theta, data = d))
  # ghat(mod$coefficients)
  jac <- Jacobian(ghat, x = mod$coefficients, elementwise = FALSE, multivalued = TRUE, vectorised = FALSE)
  expect_equal(-c(1, mean(X), mean(X), mean(X^2)), as.numeric(jac), tolerance = 1e-8)
  # TODO: make it work without checks
})

test_that("Jacobian works with automatic step sizes", {
  f <- function(x) c(x^2 - 2*x + 2, exp(x))
  expect_error(Jacobian(f, x = 0.75, h = "CR"), "step selection works only when")
})

test_that("Jacobian can accept dot arguments", {
  # ... must be accepted and used by checkDimensions() and gradstep()
  f <- function(x, a0) c(sin(x + a0), cos(x + a0))
  f1 <- function(x) c(sin(x + 1), cos(x + 1))
  expect_identical(Jacobian(f, x = 1, a0 = 1, h = 1e-5), Jacobian(f1, x = 1, h = 1e-5))
})

test_that("Jacobian can work on an arbitrary stencil", {
  f <- function(x) c(sum(sin(x[1:2])), exp(x[3]))
  jac <- rbind(c(cos(1:2), 0), c(0, 0, exp(3)))
  d2 <- Jacobian(f, 1:3, h = 1e-4, stencil = c(-1, 1))
  d4 <- Jacobian(f, 1:3, h = 1e-4, stencil = c(-2, -1, 1, 2))  # More accurate for a large step
  expect_equal(d2, d4, tolerance = 1e-7)
  # Comparing errors of non-zero elements
  dd2 <- abs(d2-jac)
  dd4 <- abs(d4-jac)
  zeros <- jac == 0
  expect_true(all(dd2[!zeros] > dd4[!zeros]))
})

# TODO: compatibility with numDeriv

# TODO: parallelisation

Try the pnd package in your browser

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

pnd documentation built on Sept. 9, 2025, 5:44 p.m.