Nothing
# test-onnx-boundary.R
#
# Boundary / regression tests targeting four bug classes:
# 1. Aliasing — gallocr reuses buffers (repeated runs)
# 2. Tile boundary — shader reads/writes outside tile at small K/N
# 3. 4D truncation — 5D tensor copy drops last dim
# 4. Wrong op dispatch — direct conv instead of MUL_MAT 1x1 fast path
#
# Each section: unit (single op), graph (3-5 nodes), and where possible
# CPU vs GPU comparison (skips gracefully when no Vulkan device).
# ── helpers ──────────────────────────────────────────────────────────────────
run_onnx <- function(path, inputs, device = "cpu") {
m <- onnx_load(path, device = device)
onnx_run(m, inputs)[[1]]
}
# Run same model on CPU and GPU, return list(cpu, gpu)
run_cpu_gpu <- function(path, inputs) {
cpu <- run_onnx(path, inputs, device = "cpu")
gpu <- tryCatch(run_onnx(path, inputs, device = "gpu"), error = function(e) NULL)
list(cpu = cpu, gpu = gpu)
}
# Compare CPU vs GPU with cosine + max abs tolerance
expect_cpu_gpu_close <- function(path, inputs, tol = 1e-3) {
r <- run_cpu_gpu(path, inputs)
skip_if(is.null(r$gpu), "No Vulkan device")
cpu <- as.numeric(r$cpu)
gpu <- as.numeric(r$gpu)
expect_equal(length(cpu), length(gpu))
max_abs <- max(abs(cpu - gpu))
expect_true(max_abs < tol,
label = sprintf("CPU/GPU max abs diff = %.2e (tol %.2e)", max_abs, tol))
}
# ── matmul helper with explicit K ────────────────────────────────────────────
make_matmul_k <- function(M, K, N, a_data = NULL, b_data = NULL) {
if (is.null(a_data)) a_data <- seq_len(M * K) * 0.1
if (is.null(b_data)) b_data <- seq_len(K * N) * 0.1
inp_a <- .onnx_value_info("A", 1L, c(M, K))
inp_b <- .onnx_value_info("B", 1L, c(K, N))
outp <- .onnx_value_info("Y", 1L, c(M, N))
node <- .onnx_node("MatMul", c("A", "B"), "Y")
graph <- .onnx_graph("test", list(node), list(inp_a, inp_b), list(outp))
path <- tempfile(fileext = ".onnx")
writeBin(.onnx_model(graph), path)
list(path = path, a = a_data, b = b_data)
}
# ── Conv helper ───────────────────────────────────────────────────────────────
make_conv2d <- function(N, C, H, W, OC, KH, KW,
w_data = NULL, b_data = NULL,
pads = c(0L, 0L, 0L, 0L)) {
OH <- H - KH + 1L + pads[1] + pads[3]
OW <- W - KW + 1L + pads[2] + pads[4]
if (is.null(w_data)) w_data <- rep(1.0 / (C * KH * KW), OC * C * KH * KW)
inp <- .onnx_value_info("X", 1L, c(N, C, H, W))
outp <- .onnx_value_info("Y", 1L, c(N, OC, OH, OW))
w_raw <- unlist(lapply(w_data, .float_bytes))
w_t <- .onnx_tensor("W", c(OC, C, KH, KW), 1L, w_raw)
w_vi <- .onnx_value_info("W", 1L, c(OC, C, KH, KW))
inputs <- list("X", "W")
inits <- list(w_t)
graph_ins <- list(inp, w_vi)
if (!is.null(b_data)) {
b_raw <- unlist(lapply(b_data, .float_bytes))
b_t <- .onnx_tensor("B", c(OC), 1L, b_raw)
b_vi <- .onnx_value_info("B", 1L, c(OC))
inputs <- c(inputs, list("B"))
inits <- c(inits, list(b_t))
graph_ins <- c(graph_ins, list(b_vi))
}
attrs <- list(.onnx_attr_ints("pads", pads))
node <- .onnx_node("Conv", inputs, "Y", attrs = attrs)
graph <- .onnx_graph("test", list(node), graph_ins, list(outp), inits)
path <- tempfile(fileext = ".onnx")
writeBin(.onnx_model(graph), path)
list(path = path, x = seq_len(N * C * H * W) * 0.1)
}
# ── 1. ALIASING: repeated runs must return identical results ──────────────────
test_that("aliasing: MatMul repeated run [4,3]@[3,4] produces same output", {
m_info <- make_matmul_k(4L, 3L, 4L)
mod <- onnx_load(m_info$path, device = "cpu")
r1 <- onnx_run(mod, list(A = m_info$a, B = m_info$b))[[1]]
r2 <- onnx_run(mod, list(A = m_info$a, B = m_info$b))[[1]]
r3 <- onnx_run(mod, list(A = m_info$a, B = m_info$b))[[1]]
expect_equal(as.numeric(r1), as.numeric(r2), tolerance = 1e-6)
expect_equal(as.numeric(r1), as.numeric(r3), tolerance = 1e-6)
})
test_that("aliasing: Conv 3x3 repeated run [1,1,5,5] produces same output", {
cv <- make_conv2d(1L, 1L, 5L, 5L, 1L, 3L, 3L)
mod <- onnx_load(cv$path, device = "cpu")
r1 <- onnx_run(mod, list(X = cv$x))[[1]]
r2 <- onnx_run(mod, list(X = cv$x))[[1]]
expect_equal(as.numeric(r1), as.numeric(r2), tolerance = 1e-6)
})
test_that("aliasing: Gemm repeated run with bias stays stable", {
path <- .onnx_make_gemm(M = 3L, K = 4L, N = 5L,
weight_data = rep(1.0, 20),
bias_data = rep(0.5, 5L))
a <- seq_len(12) * 0.1
mod <- onnx_load(path, device = "cpu")
r1 <- onnx_run(mod, list(A = a))[[1]]
r2 <- onnx_run(mod, list(A = a))[[1]]
expect_equal(as.numeric(r1), as.numeric(r2), tolerance = 1e-6)
})
test_that("aliasing: chain Relu→Sigmoid repeated run [1,8]", {
path <- .onnx_make_chain("Relu", "Sigmoid", dims = c(1L, 8L))
mod <- onnx_load(path, device = "cpu")
x <- c(-3, -1, 0, 0.5, 1, 2, 3, 4)
r1 <- onnx_run(mod, list(X = x))[[1]]
r2 <- onnx_run(mod, list(X = x))[[1]]
expect_equal(as.numeric(r1), as.numeric(r2), tolerance = 1e-6)
})
# ── 2. TILE BOUNDARY: small / odd K and N ────────────────────────────────────
# K=1 is the worst tile boundary case — single column
test_that("tile boundary: MatMul [4,1]@[1,4] K=1", {
m_info <- make_matmul_k(4L, 1L, 4L,
a_data = c(1, 2, 3, 4),
b_data = c(1, 2, 3, 4))
result <- run_onnx(m_info$path, list(A = m_info$a, B = m_info$b))
# outer product: result[i,j] = a[i]*b[j]
expected <- outer(c(1, 2, 3, 4), c(1, 2, 3, 4))
expect_equal(as.numeric(result), as.numeric(t(expected)), tolerance = 1e-5)
})
test_that("tile boundary: MatMul [8,16]@[16,8] K=N=16 (exact tile)", {
m_info <- make_matmul_k(8L, 16L, 8L)
result <- run_onnx(m_info$path, list(A = m_info$a, B = m_info$b))
a_mat <- matrix(m_info$a, nrow = 8, ncol = 16, byrow = TRUE)
b_mat <- matrix(m_info$b, nrow = 16, ncol = 8, byrow = TRUE)
expected <- a_mat %*% b_mat
expect_equal(as.numeric(result), as.numeric(t(expected)), tolerance = 1e-3)
})
test_that("tile boundary: MatMul [8,32]@[32,8] K=N=32 (double tile)", {
m_info <- make_matmul_k(8L, 32L, 8L)
result <- run_onnx(m_info$path, list(A = m_info$a, B = m_info$b))
a_mat <- matrix(m_info$a, nrow = 8, ncol = 32, byrow = TRUE)
b_mat <- matrix(m_info$b, nrow = 32, ncol = 8, byrow = TRUE)
expected <- a_mat %*% b_mat
expect_equal(as.numeric(result), as.numeric(t(expected)), tolerance = 1e-3)
})
test_that("tile boundary: MatMul [8,33]@[33,8] K=33 crosses tile", {
m_info <- make_matmul_k(8L, 33L, 8L)
result <- run_onnx(m_info$path, list(A = m_info$a, B = m_info$b))
a_mat <- matrix(m_info$a, nrow = 8, ncol = 33, byrow = TRUE)
b_mat <- matrix(m_info$b, nrow = 33, ncol = 8, byrow = TRUE)
expected <- a_mat %*% b_mat
expect_equal(as.numeric(result), as.numeric(t(expected)), tolerance = 1e-3)
})
test_that("tile boundary: Conv 3x3 odd H=7 W=3", {
cv <- make_conv2d(1L, 1L, 7L, 3L, 2L, 3L, 3L)
result <- run_onnx(cv$path, list(X = cv$x))
expect_equal(length(as.numeric(result)), 2L * 5L * 1L)
})
test_that("tile boundary: Conv 1x1 C=55 (non-power-of-two channels)", {
cv <- make_conv2d(1L, 55L, 4L, 4L, 8L, 1L, 1L)
result <- run_onnx(cv$path, list(X = cv$x))
expect_equal(length(as.numeric(result)), 8L * 4L * 4L)
})
test_that("tile boundary: MatMul CPU vs GPU K=1", {
m_info <- make_matmul_k(4L, 1L, 4L,
a_data = c(1, 2, 3, 4),
b_data = c(1, 2, 3, 4))
expect_cpu_gpu_close(m_info$path, list(A = m_info$a, B = m_info$b))
})
test_that("tile boundary: MatMul CPU vs GPU K=33", {
m_info <- make_matmul_k(8L, 33L, 8L)
expect_cpu_gpu_close(m_info$path, list(A = m_info$a, B = m_info$b))
})
# ── 3. 4D TRUNCATION: 5D tensors must not lose last dimension ────────────────
test_that("4D truncation: 5D MatMul [2,4,3,1,1] preserved", {
# Flatten to [8,3] before matmul, reshape output back
inp_a <- .onnx_value_info("A", 1L, c(2L, 4L, 3L, 1L, 1L))
inp_b <- .onnx_value_info("B", 1L, c(3L, 5L))
shape_flat_raw <- c(.int64_bytes(8L), .int64_bytes(3L))
sf_t <- .onnx_tensor("sf", c(2L), 7L, shape_flat_raw)
sf_vi <- .onnx_value_info("sf", 7L, c(2L))
shape_out_raw <- c(.int64_bytes(2L), .int64_bytes(4L), .int64_bytes(5L),
.int64_bytes(1L), .int64_bytes(1L))
so_t <- .onnx_tensor("so", c(5L), 7L, shape_out_raw)
so_vi <- .onnx_value_info("so", 7L, c(5L))
outp <- .onnx_value_info("Y", 1L, c(2L, 4L, 5L, 1L, 1L))
n_flat <- .onnx_node("Reshape", c("A", "sf"), "A2d")
n_mm <- .onnx_node("MatMul", c("A2d", "B"), "Y2d")
n_back <- .onnx_node("Reshape", c("Y2d", "so"), "Y")
graph <- .onnx_graph("test", list(n_flat, n_mm, n_back),
list(inp_a, inp_b, sf_vi, so_vi), list(outp),
list(sf_t, so_t))
path <- tempfile(fileext = ".onnx")
writeBin(.onnx_model(graph), path)
a <- seq_len(24) * 0.1
b <- seq_len(15) * 0.1
result <- run_onnx(path, list(A = a, B = b))
expect_equal(length(as.numeric(result)), 40L)
})
test_that("4D truncation: Relu on 5D [1,2,3,4,2] preserves all 48 elements", {
inp <- .onnx_value_info("X", 1L, c(1L, 2L, 3L, 4L, 2L))
outp <- .onnx_value_info("Y", 1L, c(1L, 2L, 3L, 4L, 2L))
node <- .onnx_node("Relu", "X", "Y")
graph <- .onnx_graph("test", list(node), list(inp), list(outp))
path <- tempfile(fileext = ".onnx")
writeBin(.onnx_model(graph), path)
x <- seq(-24, 23) * 0.1
result <- run_onnx(path, list(X = x))
expect_equal(length(as.numeric(result)), 48L)
expect_equal(as.numeric(result), pmax(x, 0), tolerance = 1e-5)
})
test_that("4D truncation: Add two 5D tensors [1,2,3,4,2]", {
inp_a <- .onnx_value_info("A", 1L, c(1L, 2L, 3L, 4L, 2L))
inp_b <- .onnx_value_info("B", 1L, c(1L, 2L, 3L, 4L, 2L))
outp <- .onnx_value_info("Y", 1L, c(1L, 2L, 3L, 4L, 2L))
node <- .onnx_node("Add", c("A", "B"), "Y")
graph <- .onnx_graph("test", list(node), list(inp_a, inp_b), list(outp))
path <- tempfile(fileext = ".onnx")
writeBin(.onnx_model(graph), path)
a <- seq_len(48) * 0.1
b <- rep(1.0, 48)
result <- run_onnx(path, list(A = a, B = b))
expect_equal(length(as.numeric(result)), 48L)
expect_equal(as.numeric(result), a + b, tolerance = 1e-5)
})
test_that("4D truncation: Reshape to 5D then back preserves all values [60]", {
inp <- .onnx_value_info("X", 1L, c(60L))
to5d_raw <- c(.int64_bytes(2L), .int64_bytes(3L), .int64_bytes(5L),
.int64_bytes(2L), .int64_bytes(1L))
s5d_t <- .onnx_tensor("s5d", c(5L), 7L, to5d_raw)
s5d_vi <- .onnx_value_info("s5d", 7L, c(5L))
back_raw <- c(.int64_bytes(60L))
sb_t <- .onnx_tensor("sb", c(1L), 7L, back_raw)
sb_vi <- .onnx_value_info("sb", 7L, c(1L))
outp <- .onnx_value_info("Y", 1L, c(60L))
n1 <- .onnx_node("Reshape", c("X", "s5d"), "X5d")
n2 <- .onnx_node("Relu", "X5d", "X5r")
n3 <- .onnx_node("Reshape", c("X5r", "sb"), "Y")
graph <- .onnx_graph("test", list(n1, n2, n3),
list(inp, s5d_vi, sb_vi), list(outp),
list(s5d_t, sb_t))
path <- tempfile(fileext = ".onnx")
writeBin(.onnx_model(graph), path)
x <- seq(-30, 29) * 0.1
result <- run_onnx(path, list(X = x))
expect_equal(length(as.numeric(result)), 60L)
expect_equal(as.numeric(result), pmax(x, 0), tolerance = 1e-5)
})
# ── 4. WRONG OP DISPATCH: Conv 1×1 must use MUL_MAT fast path ────────────────
# Correctness is the proxy — if dispatch is wrong, values differ.
test_that("dispatch: Conv 1x1 [1,4,4,4] correctness (MUL_MAT path)", {
cv <- make_conv2d(1L, 4L, 4L, 4L, 8L, 1L, 1L)
result <- run_onnx(cv$path, list(X = cv$x))
# 1x1 conv with all-same weights: each output pixel = mean of C inputs * C
expect_equal(length(as.numeric(result)), 8L * 4L * 4L)
expect_true(all(is.finite(as.numeric(result))))
})
test_that("dispatch: Conv 1x1 matches manual MatMul [1,C,H,W]", {
C <- 3L; H <- 4L; W <- 4L; OC <- 5L
w_data <- seq_len(OC * C) * 0.1
cv <- make_conv2d(1L, C, H, W, OC, 1L, 1L, w_data = w_data)
result_conv <- as.numeric(run_onnx(cv$path, list(X = cv$x)))
# Manual reference: onnx_run returns data in ggml col-major order [W,H,OC,N].
# x arrives as ONNX [N,C,H,W] row-major → ggml stores it as [W,H,C,N].
# For each spatial position (w,h), output[oc] = sum_c w[oc,c] * x[c,h,w].
# Iterate spatial positions in ggml order (w fastest, then h) to match output.
w_mat <- matrix(w_data, nrow = OC, ncol = C, byrow = TRUE)
# x in ggml [W,H,C,N] col-major: position (w,h,c) = x[(c-1)*H*W + (h-1)*W + w]
x_arr <- array(cv$x, dim = c(W, H, C)) # col-major fill matches ggml layout
expected <- apply(x_arr, c(1, 2), function(xvec) as.numeric(w_mat %*% xvec))
# apply returns [OC, W, H]; flatten col-major → [OC,W,H] which is [W,H,OC] in ggml order
expected <- as.numeric(aperm(array(expected, dim = c(OC, W, H)), c(2, 3, 1)))
expect_equal(result_conv, expected, tolerance = 1e-3)
})
test_that("dispatch: Conv 3x3 vs Conv 1x1 differ on same input [1,2,5,5]", {
cv1 <- make_conv2d(1L, 2L, 5L, 5L, 4L, 1L, 1L)
cv3 <- make_conv2d(1L, 2L, 5L, 5L, 4L, 3L, 3L)
r1 <- as.numeric(run_onnx(cv1$path, list(X = cv1$x)))
r3 <- as.numeric(run_onnx(cv3$path, list(X = cv3$x)))
expect_equal(length(r1), 4L * 5L * 5L)
expect_equal(length(r3), 4L * 3L * 3L)
# They must differ (different receptive fields)
expect_true(!isTRUE(all.equal(r1[seq_along(r3)], r3, tolerance = 1e-3)))
})
test_that("dispatch: Conv 1x1 CPU vs GPU [1,8,6,6]", {
cv <- make_conv2d(1L, 8L, 6L, 6L, 16L, 1L, 1L)
expect_cpu_gpu_close(cv$path, list(X = cv$x), tol = 1e-2)
})
# ── 5. GRAPH CHAINS: accumulated error across 3-5 nodes ──────────────────────
test_that("graph chain: Relu→LayerNorm→MatMul [4,4] stable", {
inp_a <- .onnx_value_info("X", 1L, c(4L, 4L))
# LayerNorm weights: scale=1, bias=0
sc_raw <- unlist(lapply(rep(1.0, 4), .float_bytes))
sc_t <- .onnx_tensor("sc", c(4L), 1L, sc_raw)
sc_vi <- .onnx_value_info("sc", 1L, c(4L))
bi_raw <- unlist(lapply(rep(0.0, 4), .float_bytes))
bi_t <- .onnx_tensor("bi", c(4L), 1L, bi_raw)
bi_vi <- .onnx_value_info("bi", 1L, c(4L))
# MatMul weight: identity
w_data <- as.numeric(diag(4))
w_raw <- unlist(lapply(w_data, .float_bytes))
w_t <- .onnx_tensor("W", c(4L, 4L), 1L, w_raw)
w_vi <- .onnx_value_info("W", 1L, c(4L, 4L))
outp <- .onnx_value_info("Y", 1L, c(4L, 4L))
n1 <- .onnx_node("Relu", "X", "r1")
n2 <- .onnx_node("LayerNormalization", c("r1", "sc", "bi"), "r2",
attrs = list(.onnx_attr_float("epsilon", 1e-5)))
n3 <- .onnx_node("MatMul", c("r2", "W"), "Y")
graph <- .onnx_graph("test", list(n1, n2, n3),
list(inp_a, sc_vi, bi_vi, w_vi), list(outp),
list(sc_t, bi_t, w_t))
path <- tempfile(fileext = ".onnx")
writeBin(.onnx_model(graph), path)
x <- c(-2, -1, 1, 2, 3, 4, -3, 0, 1, -1, 2, 3, 0, 1, 2, 3)
result <- run_onnx(path, list(X = x))
expect_equal(length(as.numeric(result)), 16L)
expect_true(all(is.finite(as.numeric(result))))
})
test_that("graph chain: Split→Relu→Concat round-trip [1,8]", {
inp <- .onnx_value_info("X", 1L, c(1L, 8L))
outp <- .onnx_value_info("Y", 1L, c(1L, 8L))
# Split [1,8] → s0[1,4] + s1[1,4]
s0_vi <- .onnx_value_info("s0", 1L, c(1L, 4L))
s1_vi <- .onnx_value_info("s1", 1L, c(1L, 4L))
r0_vi <- .onnx_value_info("r0", 1L, c(1L, 4L))
r1_vi <- .onnx_value_info("r1", 1L, c(1L, 4L))
split_raw <- c(.int64_bytes(4L), .int64_bytes(4L))
split_t <- .onnx_tensor("split_sz", c(2L), 7L, split_raw)
split_vi <- .onnx_value_info("split_sz", 7L, c(2L))
n_split <- .onnx_node("Split", c("X", "split_sz"), c("s0", "s1"),
attrs = list(.onnx_attr_int("axis", 1L)))
n_relu0 <- .onnx_node("Relu", "s0", "r0")
n_relu1 <- .onnx_node("Relu", "s1", "r1")
n_concat <- .onnx_node("Concat", c("r0", "r1"), "Y",
attrs = list(.onnx_attr_int("axis", 1L)))
graph <- .onnx_graph("test",
list(n_split, n_relu0, n_relu1, n_concat),
list(inp, split_vi), list(outp),
list(split_t))
path <- tempfile(fileext = ".onnx")
writeBin(.onnx_model(graph), path)
x <- c(-4, -3, -2, -1, 1, 2, 3, 4)
result <- as.numeric(run_onnx(path, list(X = x)))
expect_equal(length(result), 8L)
expect_equal(result, pmax(x, 0), tolerance = 1e-5)
})
test_that("graph chain: Split→Relu→Concat last output not aliased [1,12]", {
inp <- .onnx_value_info("X", 1L, c(1L, 12L))
outp <- .onnx_value_info("Y", 1L, c(1L, 12L))
# Split into 3 chunks of 4
split_raw <- c(.int64_bytes(4L), .int64_bytes(4L), .int64_bytes(4L))
split_t <- .onnx_tensor("ss", c(3L), 7L, split_raw)
split_vi <- .onnx_value_info("ss", 7L, c(3L))
n_split <- .onnx_node("Split", c("X", "ss"), c("s0", "s1", "s2"),
attrs = list(.onnx_attr_int("axis", 1L)))
n_r0 <- .onnx_node("Relu", "s0", "r0")
n_r1 <- .onnx_node("Relu", "s1", "r1")
n_r2 <- .onnx_node("Relu", "s2", "r2")
n_cc <- .onnx_node("Concat", c("r0", "r1", "r2"), "Y",
attrs = list(.onnx_attr_int("axis", 1L)))
graph <- .onnx_graph("test",
list(n_split, n_r0, n_r1, n_r2, n_cc),
list(inp, split_vi), list(outp),
list(split_t))
path <- tempfile(fileext = ".onnx")
writeBin(.onnx_model(graph), path)
x <- c(-6:-1, 1:6) * 1.0
result <- as.numeric(run_onnx(path, list(X = x)))
expect_equal(length(result), 12L)
# last chunk (indices 9-12) must not be aliased to first chunk
expect_equal(result, pmax(x, 0), tolerance = 1e-5)
})
test_that("graph chain: Split 4 outputs last output correct [1,16]", {
inp <- .onnx_value_info("X", 1L, c(1L, 16L))
outp <- .onnx_value_info("Y", 1L, c(1L, 16L))
split_raw <- c(.int64_bytes(4L), .int64_bytes(4L),
.int64_bytes(4L), .int64_bytes(4L))
split_t <- .onnx_tensor("ss4", c(4L), 7L, split_raw)
split_vi <- .onnx_value_info("ss4", 7L, c(4L))
n_split <- .onnx_node("Split", c("X", "ss4"), c("s0","s1","s2","s3"),
attrs = list(.onnx_attr_int("axis", 1L)))
n_r <- lapply(0:3, function(i)
.onnx_node("Relu", paste0("s", i), paste0("r", i)))
n_cc <- .onnx_node("Concat", c("r0","r1","r2","r3"), "Y",
attrs = list(.onnx_attr_int("axis", 1L)))
graph <- .onnx_graph("test",
c(list(n_split), n_r, list(n_cc)),
list(inp, split_vi), list(outp),
list(split_t))
path <- tempfile(fileext = ".onnx")
writeBin(.onnx_model(graph), path)
x <- c(-8:-1, 1:8) * 1.0
result <- as.numeric(run_onnx(path, list(X = x)))
expect_equal(length(result), 16L)
expect_equal(result, pmax(x, 0), tolerance = 1e-5)
})
# ── 6. BATCH DIMENSION: batch=1 vs batch>1 ───────────────────────────────────
test_that("batch: MatMul batch=1 [1,4,3]@[3,5] correct", {
# Treated as 2D [4,3]@[3,5] by ggml
m_info <- make_matmul_k(4L, 3L, 5L)
result <- as.numeric(run_onnx(m_info$path, list(A = m_info$a, B = m_info$b)))
a_mat <- matrix(m_info$a, nrow = 4, ncol = 3, byrow = TRUE)
b_mat <- matrix(m_info$b, nrow = 3, ncol = 5, byrow = TRUE)
expected <- as.numeric(t(a_mat %*% b_mat))
expect_equal(result, expected, tolerance = 1e-3)
})
test_that("batch: Relu batch=4 [4,8] all elements processed", {
path <- .onnx_make_unary("Relu", c(4L, 8L))
x <- c(seq(-16, -1), seq(1, 16)) * 0.1
result <- as.numeric(run_onnx(path, list(X = x)))
expect_equal(length(result), 32L)
expect_equal(result, pmax(x, 0), tolerance = 1e-5)
})
test_that("batch: Conv batch=2 [2,1,5,5] processes both images", {
cv <- make_conv2d(2L, 1L, 5L, 5L, 2L, 3L, 3L)
result <- as.numeric(run_onnx(cv$path, list(X = cv$x)))
expect_equal(length(result), 2L * 2L * 3L * 3L)
expect_true(all(is.finite(result)))
})
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.