The main array object. Its type is determined by a data type and a shape and lives on a device, which can be a CPU or a GPU.
Usage
nv_array(data, dtype = NULL, device = NULL, shape = NULL, byrow = FALSE)
nv_scalar(data, dtype = NULL, device = NULL)
nv_matrix(
data,
nrow = NULL,
ncol = NULL,
dtype = NULL,
device = NULL,
byrow = FALSE
)
nv_empty(dtype, shape, device = NULL)
nv_array_like(like, data, dtype = NULL, device = NULL, shape = NULL)
nv_scalar_like(like, data, dtype = NULL, device = NULL)
nv_empty_like(like, dtype = NULL, shape = NULL, device = NULL)Arguments
- data
(any)
integer(),double(), orlogical()scalar, vector, or array. Alternatively araw()vector holding the native little-endian byte payload ofprod(shape)elements ofdtype; bothdtypeandshapeare then required (only supported on the"pjrt"backend). Raw payloads are read in column-major element order, or row-major withbyrow = TRUE.- dtype
(
NULL|character(1)|DataType)
The data type at which to create the array: atengen::DataTypeor one of bool, i8, i16, i32, i64, ui8, ui16, ui32, ui64, f32, f64.datais built at it rather than converted to it, and a value it cannot hold at all is an error (nv_array(3e9, dtype = "i32")overflows); adoubleat an integer data type is truncated. The default (NULL) uses the default data type ofdata's category.- device
(
NULL|character(1)| device)
The device the data lives on, given either as:a device string naming the platform (e.g.
"cpu","cuda","cuda:<n>"), which is resolved against the backend in use, ora device object as returned by
nv_device(): aPJRTDevicefor the"pjrt"backend or aquickr_devicefor the"quickr"backend. Because a device object is backend-specific, it also determines the backend.
The default (
NULL) usesdefault_device().- shape
(
NULL|integer())
The output shape of the array. The default (NULL) is to infer it from the data if possible. Note thatnv_arrayinterprets length 1 vectors as having shape(1). Empty data has no shape to infer –0,c(2, 0)andc(0, 3)all hold no elements – soshapeis required there. To create a "scalar" with no axes (shape()), usenv_scalaror explicitly specifyshape = c().- byrow
(
logical(1))
When constructing from an R object and the result has at least two axes, fill the array in row-major order rather than the default column-major order, mirroringbase::matrix()'sbyrow. Only allowed whendatais an R object — passing an existingAnvlArraytogether withbyrow = TRUEis an error.- nrow
(
NULL|integer(1))
Number of rows. Inferred fromncoland the data length ifNULL. Defaults to1whendatais a scalar.- ncol
(
NULL|integer(1))
Number of columns. Inferred fromnrowand the data length ifNULL. Defaults to1whendatais a scalar.- like
(
AnvlArray)
An existing array. Any ofdtype,deviceandshapethat areNULL(the default) are taken fromlike.
Terminology
An array's axes are the indices that identify its directions, numbered
1, 2, 3, ... The size of an axis (its axis size) is the extent
along that axis, and the shape is the vector of all axis sizes. For
example, nv_array(1:6, shape = c(2, 3)) has two axes; the size of axis 1
is 2 and the size of axis 2 is 3, so its shape is c(2, 3). Use
naxes() for the number of axes and
shape() for the axis sizes. We speak of the size of an
axis rather than an array's "dimensions", as the latter is generally
overloaded as it is used to refer to both the axis and its size.
Extractors
The following generic functions can be used to extract information from an AnvlArray:
dtype(): Get the data type of the array.shape(): Get the shape (axis sizes) of the array.naxes(): Get the number of axes.device(): Get the device of the array.platform(): Get the platform (e.g."cpu","cuda").
Serialization
Arrays can be serialized to and from the safetensors format:
nv_serialize()/nv_unserialize(): Serialize/deserialize arrays to/from raw vectors.
Backend
An AnvlArray is backend-dependent: it belongs to exactly one backend
("pjrt" or the experimental "quickr") and lives on a device of that backend.
The supported data types and devices differ between backends.
Missing values
XLA, the compiler that is used by the "pjrt" (the default) backend
has no notion of a missing (NA) value.
When creating a new AnvlArray, the input is therefore checked for the
presence of such values.
NAs are always rejected, except when:
Creating
floatarrays where we convert theNAtoNaN.When creating an
i32from an Rinteger(). There, we throw a warning, but the resultingAnvlArraygets the bit representation ofNAinteger_, which is-INT_MIN. Disallowing this would prevent round-trips between the data types.
See the "Gotchas" vignette for more information.
Out of Range values
Because base R has fewer data types than anvl, creating AnvlArrays from R often involves
type conversions.
When such conversions are performed, anvl performs a scan of the inputs to ensure that the
requested data type can actually hold the input data.
For example, trying to create an unsigned integer from a negative R integer() fails.
The same holds where an R value takes its data type from the array it meets
rather than from an argument: nv_scalar(1L, "ui8") + (-2L) is refused, where
converting an array with nv_convert() wraps around.
Examples
# a 1-d array (vector) with shape (4), at the default data type for integers
nv_array(1:4)
#> AnvlArray
#> 1
#> 2
#> 3
#> 4
#> [ CPUi32{4} ]
# specify a dtype
nv_array(c(1.5, 2.5, 3.5), dtype = "f64")
#> AnvlArray
#> 1.5000
#> 2.5000
#> 3.5000
#> [ CPUf64{3} ]
# a 2x3 matrix
nv_array(1:6, shape = c(2L, 3L))
#> AnvlArray
#> 1 3 5
#> 2 4 6
#> [ CPUi32{2,3} ]
# a 2x3 matrix filled by row, like `matrix(1:6, 2, 3, byrow = TRUE)`.
nv_array(1:6, shape = c(2L, 3L), byrow = TRUE)
#> AnvlArray
#> 1 2 3
#> 4 5 6
#> [ CPUi32{2,3} ]
# a scalar array
nv_scalar(3.14)
#> AnvlArray
#> 3.1400
#> [ CPUf32{} ]
# an uninitialized 2x3 array (contents are unspecified)
nv_empty("f32", shape = c(2L, 3L))
#> AnvlArray
#> 3.5677e-04 3.0743e-41 3.5677e-04
#> 3.0743e-41 3.5677e-04 3.0743e-41
#> [ CPUf32{2,3} ]
# --- Extractors ---
x <- nv_array(1:6, shape = c(2L, 3L))
dtype(x)
#> <i32>
shape(x)
#> [1] 2 3
naxes(x)
#> [1] 2
device(x)
#> <CpuDevice(id=0)>
platform(x)
#> [1] "cpu"
# --- Transforming arrays with jit ---
add_one <- jit(function(x) x + 1)
add_one(nv_array(1:4))
#> AnvlArray
#> 2
#> 3
#> 4
#> 5
#> [ CPUf32{4} ]
# --- Eager mode (calling operations directly) ---
nv_add(nv_array(1:3), nv_array(4:6))
#> AnvlArray
#> 5
#> 7
#> 9
#> [ CPUi32{3} ]