anvl (development version)
Breaking changes
- Renamed
dim/dimstoaxis/axesthroughout the package (an axis is an index, a dimension is a size);ndims()is nownaxes(). - Renamed the primary array argument of
prim_*/nv_*functions fromoperandtox. - Renamed the
"xla"backend to"pjrt". -
xla()has been removed; usejit()instead. -
nv_rdunif()has been renamed tonv_sample_int(), mirroring R’ssample.int(). -
nv_runif()’slower/upperarguments are nowmin/max,nv_rnorm()’smu/sigmaare nowmean/sd, andnv_rbinom()’snis nowsize, matching the corresponding R functions.
Bug fixes
-
nv_sample_int()(formerlynv_rdunif()) was off by one: the first integer was drawn twice as often as it should have been, and the last integer was never drawn at all.
Features
- New
nv_sample()samples from an arbitrary 1-D population, like R’ssample(). Unlike in R, the population argument is never overloaded with a count – sampling the integers1tonisnv_sample_int(). -
nv_rnorm()’smeanandsdare now arrayish, so they may vary across the sample instead of being scalars. - Dimension arguments (
dim,dims,dimension,permutation) now accept negative values that count from the end, so-1refers to the last dimension. This works at both layers: in theprim_*primitives (prim_transpose(),prim_concatenate(),prim_reduce_*(),prim_reduce(),prim_cumsum()/prim_cumprod()/prim_cummax()/prim_cummin(),prim_argmax(),prim_argmin(),prim_reverse(),prim_iota(),prim_sort()) and in thenv_*functions built on top of them, includingnv_squeeze(),nv_unsqueeze(),nv_select(),nv_top_k(),nv_quantile()andnv_median()(#396). -
nv_reshape()andprim_reshape()accept a single-1inshape, whose extent is then inferred from the number of elements of the input, e.g.nv_reshape(x, c(2, -1))(#396). - Reductions now reject dimensions that are out of range for the operand instead of silently ignoring them (#396).
- New
nv_lower_tri()andnv_upper_tri()(withnv_lower_tri_like()/nv_upper_tri_like()) return a boolean triangular mask for a given shape, mirroring base R’slower.tri()/upper.tri(). As in base R, the main diagonal is excluded by default; passdiagonal = 0Lto include it. Usenv_tril()/nv_triu()to zero out a triangle of an existing array. -
trace_fn()gained anoptimizeargument controlling which graph optimization passes run on the traced graph.TRUEruns all passes,FALSE(default) runs none, and a character vector (e.g.c("inline_scalars", "remove_unused_constants")) selects a subset.jit()always traces with all passes enabled. - New
nv_dnorm()computes the normal distribution’s probability density function (or, withlog = TRUE, its log-density). - New
nv_pnorm()computes the normal distribution’s cumulative distribution function (lower_tail = FALSEfor the upper tail,log_p = TRUEfor the log-probability, staying accurate far into either tail). - New
nv_qnorm()computes the normal distribution’s quantile function, with the samelower_tail/log_parguments.log_p = TRUEaccepts log-probabilities below the smallest representablep, remaining stable far into tail probabilities. -
nv_array(),nv_scalar(),as_array(), and theas.integer()/as.double()/as.logical()/as.vector()methods forAnvlArraygained acheckargument that opts into scanning forNAvalues during host -> device and device -> host transfers. See the “Gotchas” vignette. -
nv_var()andnv_sd()now default todims = NULL, which reduces over all dimensions and returns a scalar, consistent with the other reductions. - Supports 1-3d convolutions.
Performance
- Most
nv_*()API functions are now JIT-compiled internally (via a new@jitroxygen roclet), speeding up eager-mode execution. - Tracing (
trace_fn()) performance has been improved. - Tracing now accumulates primitive calls in a
fastmap::fastqueue(amortised-O(1) append) instead of an R list grown withc()(copy-on-modify, O(n^2)). Tracing large unrolled graphs is substantially faster, e.g. ~1.36x for an 8000-op chain, with the gain growing with graph size. - StableHLO lowering forwards the trace-time output types to the
hlo_*builders (via anoutput_typesargument passed to the lowering rules), so stablehlo skips redundant type inference when lowering the graph. - Calling
jit()ted functions is now significantly faster.
Bug fixes
NULLis now treated as an empty node when flattening and unflattening trees. It contributes no leaves but is preserved structurally, so functions with optional arguments (e.g.function(x, y = NULL)) round-trip correctly.nv_argmax()/nv_argmin()andnv_cummax()/nv_cummin()now break ties order-independently, so they return the same result on GPU as on CPU (#368).nv_argmax()/nv_argmin()prefer the smallest index;nv_cummax()/nv_cummin()prefer the last occurrence.nv_diag()now errors on non-1-D input instead of silently producing an incorrect result.Errors raised while tracing are now phrased in anvl’s own vocabulary (#298). Messages originating in the
stablehlopackage used the StableHLO spec’s terminology; they now speak of arrays instead of tensors, and ofxinstead ofoperand. For example,`operand` must have dtype floatbecame`x` must have dtype float.jit()now rejects static arguments with reference semantics – an environment (and therefore also an R6 or reference class object) or an external pointer, including one nested inside a static list (#17). Such a value can be mutated in place while the compilation cache key stays equal, which silently reused a program compiled from its old contents. Functions and device objects remain valid static values.Errors raised while tracing now speak of arrays instead of tensors (#298). Previously, messages that originated in the
stablehlopackage used the StableHLO spec’s terminology, e.g.`lhs` and `rhs` must have the same tensor type. x Got tensor<4xi32> and tensor<4xf32>.
anvl 0.3.0
Breaking Changes
-
nv_empty()/nv_empty_like()return arrays with unspecified contents (no longer zero-initialized).
New Features
- On CPU, jitted XLA functions now back every non-aliased output with an R-owned RAWSXP. anvl appends a phantom donated input per unaliased output during lowering, allocates
pjrt::pjrt_empty()buffers at execute time, andpjrtmigrates the keepalive onto the output XPtr. The output’s host bytes are then managed by R’s GC. - Renamed user-facing API functions to match base R names:
nv_sine()->nv_sin(),nv_cosine()->nv_cos(),nv_ceil()->nv_ceiling(),nv_cholesky()->nv_chol(). The corresponding primitives were renamed in step:prim_sine()->prim_sin(),prim_cosine()->prim_cos(),prim_cholesky()->prim_chol(). -
nv_reduce_mean()was renamed tonv_mean(). -
nv_solve()no longer requiresato be symmetric positive-definite as it uses LU instead of Cholesky decomposition. Because of this, it is no longer differentiable, as the reverse rule for LU is not implemented yet. -
nv_chol()/prim_chol()now default tolower = FALSE(upper-triangular factor), matching base R’schol(). Previously defaulted tolower = TRUE.
New Features
Linear algebra
- New matrix-decomposition primitives and corresponding
nv_*()functions:qr,lu,svd,eigh. None of them implement a reverse rule yet. - New API functions:
-
nv_triangular_solve()(wraps the already-existingprim_triangular_solve()). -
nv_det()andnv_determinant(). The latter can also be called via thedeterminant()generic. -
nv_inv(), which can also be called viasolve(operand)(missing second argument).
-
-
qr,chol, andsolvefrom base R now dispatch tonv_qr(),nv_chol(), andnv_solve()onAnvlArray/AnvlBoxinputs.
Element-wise math
- New unary primitives and corresponding
nv_*()functions:acos,acosh,asin,asinh,atan,atanh,cosh,sinh,digamma,lgamma,polygamma,erf,erf_inv,erfc. - New API functions
nv_mod()(flooring remainder) andnv_trunc()(truncation toward zero).
Cumulative reductions
- New primitives and corresponding
nv_*()functions:cumsum,cumprod,cummax,cummin.prim_cumprod()does not yet have a reverse rule.
Sorting and searching
- New primitives
prim_sort(),prim_top_k(),prim_reduce(),prim_argmax(),prim_argmin(). - New API functions:
-
nv_sort()/nv_argsort()– sort along a dimension, or return the permutation that does. -
nv_top_k()– theklargest values along a dimension. -
nv_median()/nv_quantile()– median / quantiles along a dimension.median()dispatches tonv_median(). -
nv_argmax()/nv_argmin()– index of the maximum / minimum along a dimension (ties broken by smallest index). -
nv_select()– select a slice along a dimension by index.
-
Array construction / shape
-
nv_array()gained abyrowargument that fills the array from an R object in row-major order, mirroringmatrix(byrow = TRUE)(#165). - New
nv_matrix(data, nrow, ncol, ...)which works like R’smatrix(). - New API functions
nv_rbind()andnv_cbind()and correspondingrbind()/cbind()generics. - New API function
nv_flatten()for flattening to 1-D.
Misc
- New
AnvlArray-> Rvectorconverters:as.numeric(),as.double(),as.integer(),as.logical(),as.vector(). - New function
await()that blocks until the underlying computation has finished. - New tree utilities
map_tree()andpmap_tree()for applying functions leaf-wise over (possibly nested) lists. - Added support for
rangegeneric. - Improved NaN handling across various primitives and API functions.
Other
-
nv_reduce_sum(),nv_reduce_prod(),nv_reduce_max(),nv_reduce_min(),nv_reduce_any(),nv_reduce_all()andnv_mean()now defaultdims = NULL, which reduces over all dimensions and returns a scalar. Previously,dimswas required.
Bug Fixes
- The overloaded
%%operator now calls the newnv_mod()to be consistent with base R. - The reverse rule for
prim_reduce_prod()no longer producesNaN/Infgradients when the input contains zeros. - The CI now actually runs the torch-comparison tests.
-
nv_runif()not properly respects thelowerargument.
anvl 0.2.0
Breaking Changes
- The package was renamed from
anviltoanvlto avoid a conflict with the Bioconductor packageAnVIL. -
AnvilTensor/nv_tensorwere renamed toAnvlArrayandnv_arrayto be more in line with R’sarray(). Also,nv_aten()was renamed tonv_aval(). - Subsetting with
list()(e.g.x[list(1, 3)]) is no longer supported. Usearray()to wrap the indices instead, e.g.x[array(c(1L, 3L))]. This mirrors the input convention used everywhere else in the package. - Removed debug mode.
- Remove NSE support for
nvl_if. It now requires passing 0-argument closures astrueandfalsearguments. - Primitives renamed from
nvl_*toprim_*. The underlying primitive object containing the rules and metadata is now part of theJitPrimitivefunction via theprimitiveattribute.
New Features
- Better composability:
jit()ted functions can now be used in otherjit()-calls. This is the mechanism underlying the new eager mode. -
Eager mode was added: This means, you can now do
nv_add(1, nv_array(1:2))and it will actually perform the computation and not only do type inference. - An experimental {quickr} backend was added It only runs on CPU for now and supports a subset of available operations. You can enable it via the
backendargument injit()andnv_array()or via theanvl.default_backendoption. - New primitives:
-
nvl_cholesky()to compute the Cholesky decomposition of a matrix. -
nvl_triangular_solve()to solve a system of linear equations with a triangular matrix.
-
- New API functions (+ corresponding R generic implementations):
-
nv_diag()to create a diagonal matrix from a 1-D tensor. -
nv_eye()to create an identity matrix. -
nv_solve()to solve a system of linear equations. -
nv_cholesky()to compute the Cholesky decomposition of a matrix. -
nv_device()constructs a backend-specific device object (e.g.nv_device("cpu")) that can be passed asdeviceto array constructors likenv_fill()ornv_iota(). -
nv_crossprod()andnv_tcrossprod()for matrix cross-products. -
nv_outer()for the outer product. -
nv_extract_diag()to extract the diagonal of a matrix. -
nv_trace()to compute the trace of a matrix. -
nv_tril()andnv_triu()to extract lower/upper triangular parts. -
nv_squeeze()andnv_unsqueeze()to drop or add length-1 dimensions. -
nv_log2()andnv_log10(). -
nv_is_infinite()andnv_is_nan(). -
nv_sd()andnv_var()for standard deviation and variance.
-
-
jit()now accepts integer positions for thestaticargument. - New S3 methods
dim(),nrow(),ncol(), andlength()for anvl arrays. - Printing tensors via
nv_print()now also works on GPUs. - R vectors of length 1 and arrays are now auto-converted when being passed to
jitted functions. - Improved device handling in
jit()
Performance
- Many operations are now done asynchronously, which improves performance, especially on GPUs.
Bug Fixes
- +-Inf/NaN are correctly created for
f64when inlined into the XLA exectuable (#182). This caused wrong results with e.g.nv_reduce_max()when working withf64. - Corrected argument checks in
nv_iota(). - Fix check that
wrtarguments ingradient()must be floats. -
nv_subset()andnv_subset_assign()now error on trailing-comma subscripts (#273).