# Enhanced Comprehensive Tests for Sobol R Package # Tests for API usability, edge cases, output validation, and comparison with C++ backend # Test S3 Constructor and Object Creation test_that("sobol_generator creates valid S3 object", { gen <- sobol_generator(dimensions = 3) expect_s3_class(gen, "sobol_generator") expect_type(gen, "list") expect_named(gen, c("generator", "dimensions", "initial_skip")) expect_equal(gen$dimensions, 3L) expect_equal(gen$initial_skip, 0) }) test_that("sobol_generator accepts skip parameter", { gen <- sobol_generator(dimensions = 2, skip = 100) expect_s3_class(gen, "sobol_generator") expect_equal(gen$initial_skip, 100) expect_equal(sobol_index(gen), 100) }) test_that("sobol_generator validates dimensions", { expect_error(sobol_generator(dimensions = 0), "Assertion on 'dimensions'") expect_error(sobol_generator(dimensions = -1), "Assertion on 'dimensions'") expect_error(sobol_generator(dimensions = 1.5), "Assertion on 'dimensions'") expect_error(sobol_generator(dimensions = "3"), "Assertion on 'dimensions'") }) test_that("sobol_generator validates skip", { expect_error(sobol_generator(dimensions = 2, skip = -1), "Assertion on 'skip' failed") expect_error(sobol_generator(dimensions = 2, skip = 1.5), "Assertion on 'skip' failed") expect_error(sobol_generator(dimensions = 2, skip = "10"), "Assertion on 'skip' failed") }) # Test Point Generation test_that("sobol_next generates single point", { gen <- sobol_generator(dimensions = 3) point <- sobol_next(gen) expect_type(point, "double") expect_length(point, 3) expect_true(all(point >= 0 & point < 1)) }) test_that("sobol_next advances state", { gen <- sobol_generator(dimensions = 2) expect_equal(sobol_index(gen), 0) sobol_next(gen) expect_equal(sobol_index(gen), 1) sobol_next(gen) expect_equal(sobol_index(gen), 2) }) test_that("sobol_next validates input", { gen <- sobol_generator(dimensions = 2) expect_error(sobol_next("not a generator"), "Assertion on 'x'") expect_error(sobol_next(list(a = 1)), "Assertion on 'x' failed: Must inherit from class 'sobol_generator',") }) test_that("sobol_next_n generates multiple points", { gen <- sobol_generator(dimensions = 2) points <- sobol_next_n(gen, n = 10) expect_type(points, "double") expect_true(is.matrix(points)) expect_equal(dim(points), c(10, 2)) expect_true(all(points >= 0 & points < 1)) }) test_that("sobol_next_n handles n=0", { gen <- sobol_generator(dimensions = 3) points <- sobol_next_n(gen, n = 0) expect_true(is.matrix(points)) expect_equal(dim(points), c(0, 3)) }) test_that("sobol_next_n advances state correctly", { gen <- sobol_generator(dimensions = 2) expect_equal(sobol_index(gen), 0) sobol_next_n(gen, n = 5) expect_equal(sobol_index(gen), 5) sobol_next_n(gen, n = 3) expect_equal(sobol_index(gen), 8) }) test_that("sobol_next_n validates inputs", { gen <- sobol_generator(dimensions = 2) expect_error(sobol_next_n("not a generator", 5), "Assertion on 'x' failed: Must inherit from class 'sobol_generator',") expect_error(sobol_next_n(gen, n = -1), "Assertion on 'n' failed: Element 1 is not >= 0.") expect_error(sobol_next_n(gen, n = 1.5), "Assertion on 'n' failed: Must be of type 'single integerish value', not 'double'.") expect_error(sobol_next_n(gen, n = "5"), "Assertion on 'n' failed: Must be of type 'single integerish value', not 'character'.") }) # Test Skip Functionality test_that("sobol_skip_to changes state", { gen <- sobol_generator(dimensions = 2) expect_equal(sobol_index(gen), 0) sobol_skip_to(gen, 100) expect_equal(sobol_index(gen), 100) sobol_skip_to(gen, 50) expect_equal(sobol_index(gen), 50) }) test_that("sobol_skip_to returns object invisibly", { gen <- sobol_generator(dimensions = 2) result <- withVisible(sobol_skip_to(gen, 10)) expect_false(result$visible) expect_identical(result$value, gen) }) test_that("sobol_skip_to validates inputs", { gen <- sobol_generator(dimensions = 2) expect_error(sobol_skip_to("not a generator", 10), "Assertion on 'x' failed: Must inherit from class 'sobol_generator', but has class 'character'.") expect_error(sobol_skip_to(gen, -1), "Assertion on 'index' failed: Element 1 is not >= 0.") expect_error(sobol_skip_to(gen, 1.5), "Failed to skip to index: SobolGenerator::skip_to failed: point_index must be an integer value") expect_error(sobol_skip_to(gen, "10"), "Assertion on 'index' failed: Must be of type 'number', not 'character'.") }) # Test Query Functions test_that("sobol_index returns current index", { gen <- sobol_generator(dimensions = 2, skip = 50) expect_equal(sobol_index(gen), 50) sobol_next(gen) expect_equal(sobol_index(gen), 51) }) test_that("sobol_dimensions returns dimensions", { gen1 <- sobol_generator(dimensions = 2) gen2 <- sobol_generator(dimensions = 5) expect_equal(sobol_dimensions(gen1), 2L) expect_equal(sobol_dimensions(gen2), 5L) }) test_that("query functions validate input", { expect_error(sobol_index("not a generator"), "Assertion on 'x' failed: Must inherit from class 'sobol_generator', but has class 'character'.") expect_error(sobol_dimensions("not a generator"), "Assertion on 'x' failed: Must inherit from class 'sobol_generator', but has class 'character'.") }) # Test Batch Function test_that("sobol_points generates correct dimensions", { points <- sobol_points(n = 10, dim = 3) expect_true(is.matrix(points)) expect_equal(dim(points), c(10, 3)) expect_true(all(points >= 0 & points < 1)) }) test_that("sobol_points handles skip parameter", { # Points without skip (first 5) points1 <- sobol_points(n = 5, dim = 2, skip = 0) # Points with skip (skip first 5, get next 5) points2 <- sobol_points(n = 5, dim = 2, skip = 5) # They should be different expect_false(identical(points1, points2)) }) test_that("sobol_points handles n=0", { points <- sobol_points(n = 0, dim = 3) expect_true(is.matrix(points)) expect_equal(dim(points), c(0, 3)) }) # Test S3 Methods test_that("print.sobol_generator works", { gen <- sobol_generator(dimensions = 3, skip = 10) expect_output(print(gen), "Sobol Sequence Generator") expect_output(print(gen), "Dimensions: 3") expect_output(print(gen), "Initial skip: 10") expect_output(print(gen), "Current index: 10") # Print returns invisibly result <- withVisible(print(gen)) expect_false(result$visible) expect_identical(result$value, gen) }) test_that("summary.sobol_generator creates summary object", { gen <- sobol_generator(dimensions = 4) sobol_next_n(gen, n = 20) summ <- summary(gen) expect_s3_class(summ, "summary.sobol_generator") expect_type(summ, "list") expect_named(summ, c("dimensions", "initial_skip", "current_index", "points_generated")) expect_equal(summ$dimensions, 4L) expect_equal(summ$initial_skip, 0) expect_equal(summ$current_index, 20) expect_equal(summ$points_generated, 20) }) test_that("print.summary.sobol_generator works", { gen <- sobol_generator(dimensions = 2, skip = 5) sobol_next_n(gen, n = 10) summ <- summary(gen) expect_output(print(summ), "Sobol Sequence Generator Summary") expect_output(print(summ), "Dimensions:\\s+2") expect_output(print(summ), "Initial skip:\\s+5") expect_output(print(summ), "Current index:\\s+15") expect_output(print(summ), "Points generated:\\s+10") # Print returns invisibly result <- withVisible(print(summ)) expect_false(result$visible) expect_identical(result$value, summ) }) # Test Reproducibility test_that("generators with same parameters produce same sequence", { gen1 <- sobol_generator(dimensions = 3) gen2 <- sobol_generator(dimensions = 3) points1 <- sobol_next_n(gen1, n = 10) points2 <- sobol_next_n(gen2, n = 10) expect_identical(points1, points2) }) test_that("skip produces consistent results", { # Generate first 10, then next 5 gen1 <- sobol_generator(dimensions = 2) sobol_next_n(gen1, n = 10) points1 <- sobol_next_n(gen1, n = 5) # Generate with skip to get points 10-14 gen2 <- sobol_generator(dimensions = 2, skip = 10) points2 <- sobol_next_n(gen2, n = 5) expect_identical(points1, points2) }) test_that("batch and incremental produce same results", { # Batch generation batch <- sobol_points(n = 10, dim = 3) # Incremental generation gen <- sobol_generator(dimensions = 3) incremental <- sobol_next_n(gen, n = 10) expect_identical(batch, incremental) }) # Test Edge Cases test_that("handles large dimensions", { gen <- sobol_generator(dimensions = 100) point <- sobol_next(gen) expect_length(point, 100) expect_true(all(point >= 0 & point < 1)) }) test_that("handles very large dimensions (1000)", { gen <- sobol_generator(dimensions = 1000) point <- sobol_next(gen) expect_length(point, 1000) expect_true(all(point >= 0 & point < 1)) }) test_that("handles large skip values", { # Test with a large skip value gen <- sobol_generator(dimensions = 2, skip = 1e6) expect_equal(sobol_index(gen), 1e6) point <- sobol_next(gen) expect_length(point, 2) expect_equal(sobol_index(gen), 1e6 + 1) }) test_that("handles very large skip values", { # Test with an even larger skip value (10 million) gen <- sobol_generator(dimensions = 3, skip = 1e7) expect_equal(sobol_index(gen), 1e7) point <- sobol_next(gen) expect_length(point, 3) expect_true(all(point >= 0 & point < 1)) }) # Test Mathematical Correctness with Reference Values test_that("1D sequence matches reference values", { gen <- sobol_generator(dimensions = 1) expected <- c(0.0, 0.5, 0.75, 0.25, 0.375, 0.875, 0.625, 0.125) for (i in seq_along(expected)) { point <- sobol_next(gen) expect_equal(point[1], expected[i], tolerance = 1e-10) } }) test_that("2D sequence matches reference values", { gen <- sobol_generator(dimensions = 2) expected <- matrix(c( 0.0, 0.0, 0.5, 0.5, 0.75, 0.25, 0.25, 0.75 ), ncol = 2, byrow = TRUE) for (i in seq_len(nrow(expected))) { point <- sobol_next(gen) expect_equal(point, expected[i, ], tolerance = 1e-10) } }) test_that("3D sequence matches reference values", { gen <- sobol_generator(dimensions = 3) expected <- matrix(c( 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 0.75, 0.25, 0.25, 0.25, 0.75, 0.75, 0.375, 0.625, 0.125, 0.875, 0.125, 0.625, 0.625, 0.875, 0.375, 0.125, 0.375, 0.875 ), ncol = 3, byrow = TRUE) for (i in 1:nrow(expected)) { point <- sobol_next(gen) expect_equal(point, expected[i, ], tolerance = 1e-10) } }) # Test Sequence Properties test_that("first point is all zeros", { for (dim in c(1, 2, 3, 5, 10)) { gen <- sobol_generator(dimensions = dim) first_point <- sobol_next(gen) expect_equal(first_point, rep(0, dim), tolerance = 1e-10) } }) test_that("second point is all 0.5s", { for (dim in c(1, 2, 3, 5, 10)) { gen <- sobol_generator(dimensions = dim) sobol_next(gen) # Skip first point second_point <- sobol_next(gen) expect_equal(second_point, rep(0.5, dim), tolerance = 1e-10) } }) test_that("all points are in [0, 1) range", { gen <- sobol_generator(dimensions = 5) for (i in 1:1000) { point <- sobol_next(gen) expect_true(all(point >= 0)) expect_true(all(point < 1)) } }) test_that("sequence has low discrepancy", { # Generate many points and check distribution gen <- sobol_generator(dimensions = 2) points <- sobol_next_n(gen, n = 1024) # Check each dimension separately for (d in 1:2) { dim_points <- points[, d] # Split into 8 bins and check distribution bins <- cut(dim_points, breaks = seq(0, 1, length.out = 9), include.lowest = TRUE) bin_counts <- table(bins) # Each bin should have approximately 1024/8 = 128 points # Allow for variation but should be reasonably uniform expect_true(all(bin_counts > 64)) expect_true(all(bin_counts < 192)) } }) # Test Consistency with C++ Backend test_that("R matches C++ for multiple dimensions", { for (dim in c(1, 2, 5, 10, 50, 100)) { gen <- sobol_generator(dimensions = dim) points_r <- sobol_next_n(gen, n = 20) # Generate again from scratch points_batch <- sobol_points(n = 20, dim = dim) expect_identical(points_r, points_batch) } }) test_that("skip_to produces correct results", { # Generate using skip_to gen1 <- sobol_generator(dimensions = 3) sobol_skip_to(gen1, 50) point1 <- sobol_next(gen1) # Generate by advancing incrementally gen2 <- sobol_generator(dimensions = 3) for (i in 1:50) { sobol_next(gen2) } point2 <- sobol_next(gen2) expect_equal(point1, point2, tolerance = 1e-10) }) # Test API Usability test_that("generator can be reused after skip_to", { gen <- sobol_generator(dimensions = 2) # Generate some points points1 <- sobol_next_n(gen, n = 5) # Skip back sobol_skip_to(gen, 0) # Generate again points2 <- sobol_next_n(gen, n = 5) expect_identical(points1, points2) }) test_that("multiple generators are independent", { gen1 <- sobol_generator(dimensions = 2) gen2 <- sobol_generator(dimensions = 2) # Advance gen1 sobol_next_n(gen1, n = 10) # gen2 should still be at index 0 expect_equal(sobol_index(gen2), 0) # gen1 and gen2 should produce different next points point1 <- sobol_next(gen1) point2 <- sobol_next(gen2) expect_false(identical(point1, point2)) }) # Test Error Handling test_that("handles invalid dimensions gracefully", { expect_error(sobol_generator(dimensions = 0)) expect_error(sobol_generator(dimensions = -5)) expect_error(sobol_generator(dimensions = NA)) expect_error(sobol_generator(dimensions = NULL)) }) test_that("handles invalid skip gracefully", { expect_error(sobol_generator(dimensions = 2, skip = -1)) expect_error(sobol_generator(dimensions = 2, skip = NA)) expect_error(sobol_generator(dimensions = 2, skip = NULL)) }) test_that("sobol_points validates parameters", { expect_error(sobol_points(n = -1, dim = 2), "non-negative") expect_error(sobol_points(n = 10, dim = 0), "greater than zero") # Note: C++ implementation may not validate that n is an integer # This is acceptable as fractional n will be truncated to integer }) # Performance and Scalability Tests test_that("can generate large batches efficiently", { gen <- sobol_generator(dimensions = 10) # This should complete without error or timeout points <- sobol_next_n(gen, n = 10000) expect_equal(nrow(points), 10000) expect_equal(ncol(points), 10) }) test_that("batch generation matches incremental for large n", { dim <- 5 n <- 1000 # Batch generation batch <- sobol_points(n = n, dim = dim) # Incremental generation gen <- sobol_generator(dimensions = dim) incremental <- sobol_next_n(gen, n = n) expect_identical(batch, incremental) }) # Additional Coverage Tests test_that("column names are preserved in matrix output", { gen <- sobol_generator(dimensions = 3) points <- sobol_next_n(gen, n = 5) # Matrix should have proper dimensions expect_equal(ncol(points), 3) expect_equal(nrow(points), 5) }) test_that("works with single dimension", { gen <- sobol_generator(dimensions = 1) points <- sobol_next_n(gen, n = 10) expect_equal(dim(points), c(10, 1)) expect_true(all(points >= 0 & points < 1)) })