anvl (development version)
Breaking changes
-
transform_gradient()is renamed tograph_gradient(). -
prim_rng_bit_generator()is replaced byprim_sample_bits(). - The RNG state is now handled by {anvl} itself and RNG states no longer have to be passed around. This comes with functions such as
nv_set_seed(). See the random numbers article for more details.
Features
-
AnvlArrays can now be serialized. - New
nv_bool(),nv_int{8,16,32,64}(),nv_uint{8,16,32,64}(),nv_float32()andnv_float64()create an array of the data type in their name. - New functions for the binomial distribution:
nv_{d,p,q}binom(). - New functions for the exponential distribution:
nv_{d,p,q}exp(). - A side effects mechanism was added to anvl, which drives the RNG handling and provides a basis for more side effects in the future. See the side effects section of the internals article for more details.
- Registering custom classes that can be (un-)flattened is now supported via
pjrt::register_tree_node(). - Bugfix:
nv_cumsum(),nv_cumprod(),nv_cummax(),nv_cummin(),nv_extract_diag(),nv_trace()andnv_quantile()now accept an array with a size-0 axis;nv_quantile()reducing over one is an informative error. - New
prim_custom_gradient()computes a value with one function and differentiates it with another. Arule_reverse()with a forward pass can take the place of the latter, so that the backward pass reads what the forward pass computed. -
prim_scatter()’supdate_fncan be given by name:"replace"(the new default, replacingNULL),"add","sub","mul","max"or"min". The gradient of a scatter is implemented for each of them, not only for replacement; anupdate_fnthat is a function is not differentiated. -
nv_subset_assign()and[<-takeupdate_fntoo, sonv_subset_assign(x, i, value = v, update_fn = "add")addsvto the subset, every value of an index that is repeated ini.
Bug fixes
-
nv_rnorm()returnsNaNfor aNaNmeanor a non-finite or negativesd, likernorm(). - An unsigned dynamic index of 0 (in
[,[<-,prim_dynamic_slice(),prim_dynamic_update_slice()) no longer wraps around to the last position. -
prim_convolution()andnv_conv1d()/nv_conv2d()/nv_conv3d()are now differentiable. -
AnvlArrays can now be saved withsaveRDS()and friends; they load onto the default device, not the one they were saved from. Arrays of the pjrt backend no longer cache their device in a$devicefield. - A jitted function called without array inputs whose program is pinned to a device by an array it closes over no longer recompiles when the default device changes.
- A classed R value, such as a
bit64::integer64array, is an error underjit()instead of being read through its storage type. -
nv_rnorm()returnsNaNfor aNaNmeanor a non-finite or negativesd, likernorm(). -
nv_rbinom()acceptssize = 0, likerbinom(), andnv_sample()gives a clear error for an empty population. -
nv_seq()counts down at an unsigned data type instead of erroring. - The gradient of an empty strided
prim_static_slice()has the shape of its input. - The gradient of
x^yis 0 instead ofNaNinyat base 0 and inxat exponent 0. - The gradient of
nv_pad()/prim_pad()works for negative edge padding. - The gradient of
prim_broadcast_in_axes()is no longer permuted for broadcast axes that are not in increasing order. - An
NAin anarray()subset index now gives an informative error. - The common data type of arrays and R values no longer depends on the order of the arguments; the R values take the data type the arrays agree on.
-
nv_squeeze(x, axes = integer(0))no longer drops every axis. -
nv_subset()accepts an R value asx, likenv_subset_assign()does. -
nv_rnorm()no longer returnsInf/-Infwhen a uniform draw is exactly 0. -
nv_mean(),nv_var()andnv_sd()no longer overflow on an integer input, which is now - The gradient of
prim_dot_general()with respect tolhsis right when -
nv_squeeze(x, axes = integer(0))no longer drops every axis. -
nv_subset()accepts an R value asx, likenv_subset_assign()does. -
nv_rnorm()no longer returnsInf/-Infwhen a uniform draw is exactly 0. - nv_mean()
,nv_var()andnv_sd()` no longer overflow on an integer input, which is now summed at the default float. - New
nv_bool(),nv_int{8,16,32,64}(),nv_uint{8,16,32,64}(),nv_float32()andnv_float64()create an array of the data type in their name. - New functions for the Poisson distribution:
nv_dpois(),nv_ppois(), andnv_qpois(). - New functions for the binomial distribution:
nv_dbinom(),nv_pbinom()andnv_qbinom(). - New functions for the exponential distribution:
nv_dexp(),nv_pexp(), andnv_qexp(). -
nv_chol()/prim_chol()now lower to a LAPACK (CPU) / cuSOLVER (CUDA)potrfcustom call, which is about ten times faster on CPU. -
nv_triangular_solve()/prim_triangular_solve(), and with themnv_solve(), now lower to a BLAStrsmcustom call on CPU, which runs several times faster from about 100 rows on and compiles faster. - Bugfix: the gradient of
prim_dot_general()with respect tolhsis right whenlhshas batching axes but no free axes. -
nv_broadcast_arrays()broadcasts a size-1 axis that meets a size-0 axis to size 0. - An update that
prim_scatter()drops as out of bounds no longer gets a gradient whenunique_indices = TRUE.
anvl 0.5.0
This release contained many breaking changes. From now on, the API will be more stable. Because of the amount of changes in this release, we only list some highlights and top-level overviews here.
Breaking changes
- Many internal functions and classes were renamed, some no longer needed ones removed.
- The main API functions and primitives, as well as their parameters were renamed to be more consistent with base R.
- The package now uses col-major semantics to be more consistent with base R. This is e.g. noticeable in
prim_flatten, the broadcasting functions,prim_bitcast_convert, and theprim_cum*primitives. - The
@jitroxygen tag was removed; wrap functions injit()at the definition instead. - The type system of {anvl} was changed to avoid the problems reported in issue #373. Specifically, the ambiguity system was replaced with the
RDatasystem and a new system of rules for type promotions. With it, also the promotion behavior of various primitives and API functions was improved. - When creating an
AnvlArrayfrom R data, the inputs are now always checked forNAs, as well as out-of-range issues. -
as_array()and theas.double()/as.integer()/bit64::as.integer64()/as.logical()methods takecheck = "warn","err"orFALSEinstead of a flag, following {pjrt}, and warn by default about a value R’s type cannot hold. Writecheck = "err"where you wrotecheck = TRUE, andcheck = FALSEto materialize silently. -
common_dtype()now errors forui64and a signed integer instead of returningi64, which could not hold everyui64value. Convert one of them withnv_convert(). - There is now exactly one backend used at a time and it is configured via the
anvl.backendoption. With this change thedevice_argparameter was removed fromjit()as it is no longer needed. - The internal graph representation is now pure. Only the top-level graph can now have constants.
Features
- Many new API functions and extensions to existing ones.
- Some new primitives (including
prim_scan). - Added support for configuring the default device.
- Added support for configuring the default data types.
-
as_anvl_array()gained a.promoteargument, naming the data type the input is brought to, asas_anvl_arrays()already had. - Error messages are improved across the package.
-
nv_array()now accepts araw()vector. -
as.vector()now returns abit64::integer64for integer data types that do not fit into R’s 32 bit integers. - Added support for more base R generics.
- Improved documentation of API functions and primitives.
Performance
-
nv_quantile()andnv_median()select the needed order statistics withtop_kinstead of a full sort. -
prim_top_k()gainedindices; without them the CUDA lowering uses an unstable sort of the values and a slice instead of the CHLO op, which costs no more than a full sort there.nv_top_k(indices = FALSE)and the quantile fast path use it.
Bug fixes
- Many bugs in the API functions and primitives have been fixed.
-
gradient()now handles values thatprim_if()/prim_scan()sub-graphs close over, and treats values the differentiated function closes over as constants, as in JAX. Several individual gradient rules were fixed, too. -
jit()no longer evaluates its arguments a second time. - Arrays created inside a trace or loaded from disk are now placed on the right device.
- Type promotion was fixed in several API functions, and floating-point functions now refuse boolean inputs.
- Input validation was tightened across the API and primitives, so invalid arguments now give a clear error instead of failing inside the backend, aborting the R session, or silently returning a wrong result.
- Edge cases of subsetting, such as empty selections and ranges that count down, now work.
- Improved the numerics of some functions, e.g.
nv_mod()andnv_qnorm().
Tests
- The environment variables that configure only the test suite are now spelled with an
ANVL_TESTprefix:ANVL_TEST_SKIP_QUICKR.ANVL_TESTitself is unchanged. - The suite can be run with
ANVL_DEFAULT_DEVICE=cpu:1, which makes anything allocating on the first CPU device rather than following the trace land on a device of its own instead of agreeing with everything else by accident. Thedefault-deviceworkflow runs it that way on thefull-testlabel. - Moved some of pjrt’s dispatcher tests into anvl.
anvl 0.4.0
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 population. - 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(). - New functions for the normal distribution:
nv_dnorm(),nv_qnorm(), andnv_pnorm()thanks to Louis Aslett. They are implemented to be accurate far into either tail. -
nv_rnorm()’smeanandsdnow accept arrayish inputs. - Dimension arguments (
dim,dims,dimension,permutation) now accept negative values that count from the end, so-1refers to the last dimension. - Reshaping functions accept a single
-1in shape indicating a dimension to be inferred. - Added support for 1-3 dimensional convolutions, thanks to Troy Hernandez.
-
AnvlArrayconstructors and converters have gained acheckargument that opts into scanning forNAvalues, see the “Gotchas” vignette for more information. -
nv_var()andnv_sd()now default todims = NULL, which reduces over all dimensions and returns a scalar, consistent with the other reductions. -
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. - Improved the installation vignette
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. - StableHLO lowering has been sped up.
- Calling
jit()ted functions is now significantly faster.
Bug fixes
- Reductions now reject dimensions that are out of range for the operand instead of silently ignoring them.
-
NULLis now treated as an empty node when flattening and unflattening trees. -
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. -
jit()now rejects static arguments with reference semantics. - Error messages now speak of arrays instead of tensors.
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).