nv_runif()’s lower/upper arguments are now min/max, nv_rnorm()’s mu/sigma are now mean/sd, and nv_rbinom()’s n is now size, matching the corresponding R functions.
Features
New nv_sample() samples from an arbitrary population.
New functions for the normal distribution: nv_dnorm(), nv_qnorm(), and nv_pnorm() thanks to Louis Aslett. They are implemented to be accurate far into either tail.
nv_rnorm()’s mean and sd now accept arrayish inputs.
Dimension arguments (dim, dims, dimension, permutation) now accept negative values that count from the end, so -1 refers to the last dimension.
Reshaping functions accept a single -1 in shape indicating a dimension to be inferred.
Added support for 1-3 dimensional convolutions, thanks to Troy Hernandez.
AnvlArray constructors and converters have gained a check argument that opts into scanning for NA values, see the “Gotchas” vignette for more information.
nv_var() and nv_sd() now default to dims = NULL, which reduces over all dimensions and returns a scalar, consistent with the other reductions.
trace_fn() gained an optimize argument controlling which graph optimization passes run on the traced graph. TRUE runs 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.
Improved the installation vignette
Performance
Most nv_*() API functions are now JIT-compiled internally (via a new @jit roxygen roclet), speeding up eager-mode execution.
Tracing (trace_fn()) performance has been improved.
StableHLO lowering has been sped up.
Calling jit()ted functions is now significantly faster.
Bug fixes
nv_sample_int() (formerly nv_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.
Reductions now reject dimensions that are out of range for the operand instead of silently ignoring them.
NULL is now treated as an empty node when flattening and unflattening trees.
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, and pjrt migrates 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_solve() no longer requires a to 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 to lower = FALSE (upper-triangular factor), matching base R’s chol(). Previously defaulted to lower = 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.
The overloaded %% operator now calls the new nv_mod() to be consistent with base R.
The reverse rule for prim_reduce_prod() no longer produces NaN / Inf gradients when the input contains zeros.
The CI now actually runs the torch-comparison tests.
nv_runif() not properly respects the lower argument.
anvl 0.2.0
Breaking Changes
The package was renamed from anvil to anvl to avoid a conflict with the Bioconductor package AnVIL.
AnvilTensor/nv_tensor were renamed to AnvlArray and nv_array to be more in line with R’s array(). Also, nv_aten() was renamed to nv_aval().
Subsetting with list() (e.g. x[list(1, 3)]) is no longer supported. Use array() 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 as true and false arguments.
Primitives renamed from nvl_* to prim_*. The underlying primitive object containing the rules and metadata is now part of the JitPrimitive function via the primitive attribute.
New Features
Better composability: jit()ted functions can now be used in other jit()-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 backend argument in jit() and nv_array() or via the anvl.default_backend option.
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_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 as device to array constructors like nv_fill() or nv_iota().
Many operations are now done asynchronously, which improves performance, especially on GPUs.
Bug Fixes
+-Inf/NaN are correctly created for f64 when inlined into the XLA exectuable (#182). This caused wrong results with e.g. nv_reduce_max() when working with f64.