Skip to contents

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(), or logical() scalar, vector, or array. Alternatively a raw() vector holding the native little-endian byte payload of prod(shape) elements of dtype; both dtype and shape are then required (only supported on the "pjrt" backend). Raw payloads are read in column-major element order, or row-major with byrow = TRUE.

dtype

(NULL | character(1) | DataType)
The data type at which to create the array: a tengen::DataType or one of bool, i8, i16, i32, i64, ui8, ui16, ui32, ui64, f32, f64. data is 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); a double at an integer data type is truncated. The default (NULL) uses the default data type of data'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, or

  • a device object as returned by nv_device(): a PJRTDevice for the "pjrt" backend or a quickr_device for the "quickr" backend. Because a device object is backend-specific, it also determines the backend.

The default (NULL) uses default_device().

shape

(NULL | integer())
The output shape of the array. The default (NULL) is to infer it from the data if possible. Note that nv_array interprets length 1 vectors as having shape (1). Empty data has no shape to infer – 0, c(2, 0) and c(0, 3) all hold no elements – so shape is required there. To create a "scalar" with no axes (shape ()), use nv_scalar or explicitly specify shape = 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, mirroring base::matrix()'s byrow. Only allowed when data is an R object — passing an existing AnvlArray together with byrow = TRUE is an error.

nrow

(NULL | integer(1))
Number of rows. Inferred from ncol and the data length if NULL. Defaults to 1 when data is a scalar.

ncol

(NULL | integer(1))
Number of columns. Inferred from nrow and the data length if NULL. Defaults to 1 when data is a scalar.

like

(AnvlArray)
An existing array. Any of dtype, device and shape that are NULL (the default) are taken from like.

Value

(AnvlArray)

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:

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:

  1. Creating float arrays where we convert the NA to NaN.

  2. When creating an i32 from an R integer(). There, we throw a warning, but the resulting AnvlArray gets the bit representation of NAinteger_, 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} ]