View Source Nx (Nx v0.5.3)
Numerical Elixir.
The Nx
library is a collection of functions and data
types to work with Numerical Elixir. This module defines
the main entry point for building and working with said
data-structures. For example, to create an n-dimensional
tensor, do:
iex> t = Nx.tensor([[1, 2], [3, 4]])
iex> Nx.shape(t)
{2, 2}
Nx
also provides the so-called numerical definitions under
the Nx.Defn
module. They are a subset of Elixir tailored for
numerical computations. For example, it overrides Elixir's
default operators so they are tensor-aware:
defn softmax(t) do
Nx.exp(t) / Nx.sum(Nx.exp(t))
end
Code inside defn
functions can also be given to custom compilers,
which can compile said functions just-in-time (JIT) to run on the
CPU or on the GPU.
references
References
Here is a general outline of the main references in this library:
For an introduction, see our Intro to Nx guide
This module provides the main API for working with tensors
Nx.Defn
provides numerical definitions, CPU/GPU compilation, gradients, and moreNx.LinAlg
provides functions related to linear algebraNx.Constants
declares many constants commonly used in numerical code
Continue reading this documentation for an overview of creating, broadcasting, and accessing/slicing Nx tensors.
creating-tensors
Creating tensors
The main APIs for creating tensors are tensor/2
, from_binary/2
,
iota/2
, eye/2
, random_uniform/2
, random_normal/2
, and
broadcast/3
.
The tensor types can be one of:
- unsigned integers (
u8
,u16
,u32
,u64
) - signed integers (
s8
,s16
,s32
,s64
) - floats (
f16
,f32
,f64
) - brain floats (
bf16
) - and complex numbers (
c64
,c128
)
The types are tracked as tuples:
iex> Nx.tensor([1, 2, 3], type: {:f, 32})
#Nx.Tensor<
f32[3]
[1.0, 2.0, 3.0]
>
But a shortcut atom notation is also available:
iex> Nx.tensor([1, 2, 3], type: :f32)
#Nx.Tensor<
f32[3]
[1.0, 2.0, 3.0]
>
The tensor dimensions can also be named, via the :names
option
available to all creation functions:
iex> Nx.iota({2, 3}, names: [:x, :y])
#Nx.Tensor<
s64[x: 2][y: 3]
[
[0, 1, 2],
[3, 4, 5]
]
>
Finally, for creating vectors and matrices, a sigil notation is available:
iex> import Nx, only: :sigils
iex> ~V[1 2 3]f32
#Nx.Tensor<
f32[3]
[1.0, 2.0, 3.0]
>
iex> import Nx, only: :sigils
iex> ~M'''
...> 1 2 3
...> 4 5 6
...> '''s32
#Nx.Tensor<
s32[2][3]
[
[1, 2, 3],
[4, 5, 6]
]
>
All other APIs accept exclusively numbers or tensors, unless explicitly noted otherwise.
broadcasting
Broadcasting
Broadcasting allows operations on two tensors of different shapes to match. For example, most often operations between tensors have the same shape:
iex> a = Nx.tensor([1, 2, 3])
iex> b = Nx.tensor([10, 20, 30])
iex> Nx.add(a, b)
#Nx.Tensor<
s64[3]
[11, 22, 33]
>
Now let's imagine you want to multiply a large tensor of dimensions 1000x1000x1000 by 2. If you had to create a similarly large tensor only to perform this operation, it would be inefficient. Therefore, you can simply multiply this large tensor by the scalar 2, and Nx will propagate its dimensions at the time the operation happens, without allocating a large intermediate tensor:
iex> Nx.multiply(Nx.tensor([1, 2, 3]), 2)
#Nx.Tensor<
s64[3]
[2, 4, 6]
>
In practice, broadcasting is not restricted only to scalars; it
is a general algorithm that applies to all dimensions of a tensor.
When broadcasting, Nx
compares the shapes of the two tensors,
starting with the trailing ones, such that:
If the dimensions have equal size, then they are compatible
If one of the dimensions have size of 1, it is "broadcast" to match the dimension of the other
In case one tensor has more dimensions than the other, the missing dimensions are considered to be of size one. Here are some examples of how broadcast would work when multiplying two tensors with the following shapes:
s64[3] * s64
#=> s64[3]
s64[255][255][3] * s64[3]
#=> s64[255][255][3]
s64[2][1] * s[1][2]
#=> s64[2][2]
s64[5][1][4][1] * s64[3][4][5]
#=> s64[5][3][4][5]
If any of the dimensions do not match or are not 1, an error is raised.
access-syntax-slicing
Access syntax (slicing)
Nx tensors implement Elixir's access syntax. This allows developers to slice tensors up and easily access sub-dimensions and values.
Access accepts integers:
iex> t = Nx.tensor([[1, 2], [3, 4]])
iex> t[0]
#Nx.Tensor<
s64[2]
[1, 2]
>
iex> t[1]
#Nx.Tensor<
s64[2]
[3, 4]
>
iex> t[1][1]
#Nx.Tensor<
s64
4
>
If a negative index is given, it accesses the element from the back:
iex> t = Nx.tensor([[1, 2], [3, 4]])
iex> t[-1][-1]
#Nx.Tensor<
s64
4
>
Out of bound access will raise:
iex> Nx.tensor([1, 2])[2]
** (ArgumentError) index 2 is out of bounds for axis 0 in shape {2}
iex> Nx.tensor([1, 2])[-3]
** (ArgumentError) index -3 is out of bounds for axis 0 in shape {2}
The index can also be another tensor but in such cases it must be a scalar between 0 and the dimension size. Out of bound dynamic indexes are always clamped to the tensor dimensions:
iex> two = Nx.tensor(2)
iex> t = Nx.tensor([[1, 2], [3, 4]])
iex> t[two][two]
#Nx.Tensor<
s64
4
>
For example, a minus_one
dynamic index will be clamped to zero:
iex> minus_one = Nx.tensor(-1)
iex> t = Nx.tensor([[1, 2], [3, 4]])
iex> t[minus_one][minus_one]
#Nx.Tensor<
s64
1
>
Access also accepts ranges. Ranges in Elixir are inclusive:
iex> t = Nx.tensor([[1, 2], [3, 4], [5, 6], [7, 8]])
iex> t[0..1]
#Nx.Tensor<
s64[2][2]
[
[1, 2],
[3, 4]
]
>
Ranges can receive negative positions and they will read from the back. In such cases, the range step must be explicitly given and the right-side of the range must be equal or greater than the left-side:
iex> t = Nx.tensor([[1, 2], [3, 4], [5, 6], [7, 8]])
iex> t[1..-2//1]
#Nx.Tensor<
s64[2][2]
[
[3, 4],
[5, 6]
]
>
As you can see, accessing with a range does not eliminate the accessed axis. This means that, if you try to cascade ranges, you will always be filtering the highest dimension:
iex> t = Nx.tensor([[1, 2], [3, 4], [5, 6], [7, 8]])
iex> t[1..-1//1] # Drop the first "row"
#Nx.Tensor<
s64[3][2]
[
[3, 4],
[5, 6],
[7, 8]
]
>
iex> t[1..-1//1][1..-1//1] # Drop the first "row" twice
#Nx.Tensor<
s64[2][2]
[
[5, 6],
[7, 8]
]
>
Therefore, if you want to slice across multiple dimensions, you can wrap the ranges in a list:
iex> t = Nx.tensor([[1, 2], [3, 4], [5, 6], [7, 8]])
iex> t[[1..-1//1, 1..-1//1]] # Drop the first "row" and the first "column"
#Nx.Tensor<
s64[3][1]
[
[4],
[6],
[8]
]
>
You can also use ..
as the full-slice range, which means you want to
keep a given dimension as is:
iex> t = Nx.tensor([[1, 2], [3, 4], [5, 6], [7, 8]])
iex> t[[.., 1..-1//1]] # Drop only the first "column"
#Nx.Tensor<
s64[4][1]
[
[2],
[4],
[6],
[8]
]
>
You can mix both ranges and integers in the list too:
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])
iex> t[[1..2, 2]]
#Nx.Tensor<
s64[2]
[6, 9]
>
If the list has less elements than axes, the remaining dimensions are returned in full:
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]])
iex> t[[1..2]]
#Nx.Tensor<
s64[2][3]
[
[4, 5, 6],
[7, 8, 9]
]
>
The access syntax also pairs nicely with named tensors. By using named tensors, you can pass only the axis you want to slice, leaving the other axes intact:
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]], names: [:x, :y])
iex> t[x: 1..2]
#Nx.Tensor<
s64[x: 2][y: 3]
[
[4, 5, 6],
[7, 8, 9]
]
>
iex> t[x: 1..2, y: 0..1]
#Nx.Tensor<
s64[x: 2][y: 2]
[
[4, 5],
[7, 8]
]
>
iex> t[x: 1, y: 0..1]
#Nx.Tensor<
s64[y: 2]
[4, 5]
>
For a more complex slicing rules, including strides, you
can always fallback to Nx.slice/4
.
backends
Backends
The Nx
library has built-in support for multiple backends.
A tensor is always handled by a backend, the default backend
being Nx.BinaryBackend
, which means the tensor is allocated
as a binary within the Erlang VM.
Most often backends are used to provide a completely different implementation of tensor operations, often accelerated to the GPU. In such cases, you want to guarantee all tensors are allocated in the new backend. This can be done by configuring your runtime:
# config/runtime.exs
import Config
config :nx, default_backend: EXLA.Backend
In your notebooks and on Mix.install/2
, you might:
Mix.install(
[
{:nx, ">= 0.0.0"}
],
config: [nx: [default_backend: EXLA.Backend]]
)
Or by calling Nx.global_default_backend/1
(less preferrable):
Nx.global_default_backend(EXLA.Backend)
To pass options to the backend, replacing EXLA.Backend
by
{EXLA.Backend, client: :cuda}
or similar. See the documentation
for EXLA and Torchx
for installation and GPU support.
To implement your own backend, check the Nx.Tensor
behaviour.
Link to this section Summary
Guards
Checks whether the value is a valid numerical value.
Functions: Aggregates
Returns a scalar tensor of value 1 if all of the tensor values are not zero. Otherwise the value is 0.
Returns a scalar tensor of value 1 if all element-wise values are within tolerance of b. Otherwise returns value 0.
Returns a scalar tensor of value 1 if any of the tensor values are not zero. Otherwise the value is 0.
Returns the indices of the maximum values.
Returns the indices of the minimum values.
Returns the mean for the tensor.
Returns the median for the tensor.
Returns the mode of a tensor.
Returns the product for the tensor.
Reduces over a tensor with the given accumulator.
Returns the maximum values of the tensor.
Returns the minimum values of the tensor.
Finds the standard deviation of a tensor.
Returns the sum for the tensor.
Finds the variance of a tensor.
Returns the weighted mean for the tensor and the weights.
Functions: Backend
Copies data to the given backend.
Deallocates data in a device.
Transfers data to the given backend.
Gets the default backend for the current process.
Sets the given backend
as default in the current process.
Sets the default backend globally.
Invokes the given function temporarily setting backend
as the
default backend.
Functions: Conversion
Deserializes a serialized representation of a tensor or a container with the given options.
Loads a .npy
file into a tensor.
Loads a .npz
archive into a list of tensors.
Serializes the given tensor or container of tensors to iodata.
Converts the underlying tensor to a stream of tensor batches.
Returns the underlying tensor as a binary.
Returns the underlying tensor as a flat list.
Returns a heatmap struct with the tensor data.
Converts the tensor into a list reflecting its structure.
Returns the underlying tensor as a number.
Converts a tensor (or tuples and maps of tensors) to tensor templates.
Converts the given number (or tensor) to a tensor.
Functions: Creation
Creates the identity matrix of size n
.
Creates a one-dimensional tensor from a binary
with the given type
.
Creates a tensor with the given shape which increments along the provided axis. You may optionally provide dimension names.
Creates a tensor of shape {n}
with linearly spaced samples between start
and stop
.
Creates a diagonal tensor from a 1D tensor.
Puts the individual values from a 1D diagonal into the diagonal indices of the given 2D tensor.
A convenient ~M
sigil for building matrices (two-dimensional tensors).
A convenient ~V
sigil for building vectors (one-dimensional tensors).
Extracts the diagonal of batched matrices.
Creates a tensor template.
Builds a tensor.
Functions: Cumulative
Returns the cumulative maximum of elements along an axis.
Returns the cumulative minimum of elements along an axis.
Returns the cumulative product of elements along an axis.
Returns the cumulative sum of elements along an axis.
Functions: Element-wise
Computes the absolute value of each element in the tensor.
Calculates the inverse cosine of each element in the tensor.
Calculates the inverse hyperbolic cosine of each element in the tensor.
Element-wise addition of two tensors.
Calculates the inverse sine of each element in the tensor.
Calculates the inverse hyperbolic sine of each element in the tensor.
Element-wise arc tangent of two tensors.
Calculates the inverse tangent of each element in the tensor.
Calculates the inverse hyperbolic tangent of each element in the tensor.
Element-wise bitwise AND of two tensors.
Applies bitwise not to each element in the tensor.
Element-wise bitwise OR of two tensors.
Element-wise bitwise XOR of two tensors.
Calculates the cube root of each element in the tensor.
Calculates the ceil of each element in the tensor.
Clips the values of the tensor on the closed
interval [min, max]
.
Constructs a complex tensor from two equally-shaped tensors.
Calculates the complex conjugate of each element in the tensor.
Calculates the cosine of each element in the tensor.
Calculates the hyperbolic cosine of each element in the tensor.
Counts the number of leading zeros of each element in the tensor.
Element-wise division of two tensors.
Element-wise equality comparison of two tensors.
Calculates the error function of each element in the tensor.
Calculates the inverse error function of each element in the tensor.
Calculates the one minus error function of each element in the tensor.
Calculates the exponential of each element in the tensor.
Calculates the exponential minus one of each element in the tensor.
Calculates the floor of each element in the tensor.
Element-wise greater than comparison of two tensors.
Element-wise greater than or equal comparison of two tensors.
Returns the imaginary component of each entry in a complex tensor as a floating point tensor.
Determines if each element in tensor
is Inf
or -Inf
.
Determines if each element in tensor
is a NaN
.
Element-wise left shift of two tensors.
Element-wise less than comparison of two tensors.
Element-wise less than or equal comparison of two tensors.
Calculates the natural log plus one of each element in the tensor.
Calculates the natural log of each element in the tensor.
Element-wise logical and of two tensors.
Element-wise logical not a tensor.
Element-wise logical or of two tensors.
Element-wise logical xor of two tensors.
Maps the given scalar function over the entire tensor.
Element-wise maximum of two tensors.
Element-wise minimum of two tensors.
Element-wise multiplication of two tensors.
Negates each element in the tensor.
Element-wise not-equal comparison of two tensors.
Calculates the complex phase angle of each element in the tensor. $$phase(z) = atan2(b, a), z = a + bi \in \Complex$$
Computes the bitwise population count of each element in the tensor.
Element-wise power of two tensors.
Element-wise integer division of two tensors.
Returns the real component of each entry in a complex tensor as a floating point tensor.
Element-wise remainder of two tensors.
Element-wise right shift of two tensors.
Calculates the round (away from zero) of each element in the tensor.
Calculates the reverse square root of each element in the tensor.
Constructs a tensor from two tensors, based on a predicate.
Calculates the sigmoid of each element in the tensor.
Computes the sign of each element in the tensor.
Calculates the sine of each element in the tensor.
Calculates the hyperbolic sine of each element in the tensor.
Calculates the square root of each element in the tensor.
Element-wise subtraction of two tensors.
Calculates the tangent of each element in the tensor.
Calculates the hyperbolic tangent of each element in the tensor.
Functions: Indexed
Builds a new tensor by taking individual values from the original tensor at the given indices.
Performs an indexed add
operation on the target
tensor,
adding the updates
into the corresponding indices
positions.
Puts individual values from updates
into the given tensor at the corresponding indices
.
Puts the given slice
into the given tensor
at the given
start_indices
.
Slices a tensor from start_indices
with lengths
.
Slices a tensor along the given axis.
Takes and concatenates slices along an axis.
Takes the values from a tensor given an indices
tensor, along the specified axis.
Functions: N-dim
Sorts the tensor along the given axis according to the given direction and returns the corresponding indices of the original tensor in the new sorted positions.
Concatenates tensors along the given axis.
Computes an n-D convolution (where n >= 3
) as used in neural networks.
Returns the dot product of two tensors.
Computes the generalized dot product between two tensors, given the contracting axes.
Computes the generalized dot product between two tensors, given the contracting and batch axes.
Calculates the DFT of the given tensor.
Calculates the Inverse DFT of the given tensor.
Computes the outer product of two tensors.
Reverses the tensor in the given dimensions.
Sorts the tensor along the given axis according to the given direction.
Stacks a list of tensors with the same shape along a new axis.
Returns a tuple of {values, indices}
for the top k
values in last dimension of the tensor.
Functions: Shape
Returns all of the axes in a tensor.
Returns the index of the given axis in the tensor.
Returns the size of a given axis of a tensor.
Broadcasts tensor
to the given broadcast_shape
.
Returns the byte size of the data in the tensor computed from its shape and type.
Checks if two tensors have the same shape, type, and compatible names.
Flattens a n-dimensional tensor to a 1-dimensional tensor.
Returns all of the names in a tensor.
Adds a new axis
of size 1 with optional name
.
Pads a tensor with a given value.
Returns the rank of a tensor.
Pads a tensor of rank 1 or greater along the given axes through periodic reflections.
Adds (or overrides) the given names to the tensor.
Changes the shape of a tensor.
Returns the shape of the tensor as a tuple.
Returns the number of elements in the tensor.
Squeezes the given size 1
dimensions out of the tensor.
Creates a new tensor by repeating the input tensor along the given axes.
Transposes a tensor to the given axes
.
Functions: Type
Changes the type of a tensor.
Changes the type of a tensor, using a bitcast.
Returns the type of the tensor.
Functions: Window
Returns the maximum over each window of size window_dimensions
in the given tensor, producing a tensor that contains the same
number of elements as valid positions of the window.
Averages over each window of size window_dimensions
in the
given tensor, producing a tensor that contains the same
number of elements as valid positions of the window.
Returns the minimum over each window of size window_dimensions
in the given tensor, producing a tensor that contains the same
number of elements as valid positions of the window.
Returns the product over each window of size window_dimensions
in the given tensor, producing a tensor that contains the same
number of elements as valid positions of the window.
Reduces over each window of size dimensions
in the given tensor, producing a tensor that contains the same
number of elements as valid positions of the window.
Performs a window_reduce
to select the maximum index in each
window of the input tensor according to and scatters source tensor
to corresponding maximum indices in the output tensor.
Performs a window_reduce
to select the minimum index in each
window of the input tensor according to and scatters source tensor
to corresponding minimum indices in the output tensor.
Sums over each window of size window_dimensions
in the
given tensor, producing a tensor that contains the same
number of elements as valid positions of the window.
Link to this section Types
@type axes() :: Nx.Tensor.axes()
@type axis() :: Nx.Tensor.axis()
@type shape() :: number() | Nx.Tensor.t() | Nx.Tensor.shape()
@type t() :: number() | Complex.t() | Nx.Tensor.t()
Represents a numerical value.
Can be a plain number, a Complex
number or an Nx.Tensor
.
See also: is_tensor/1
@type template() :: Nx.Tensor.t(%Nx.TemplateBackend{})
Link to this section Guards
Link to this section Functions: Aggregates
Returns a scalar tensor of value 1 if all of the tensor values are not zero. Otherwise the value is 0.
If the :axes
option is given, it aggregates over
the given dimensions, effectively removing them.
axes: [0]
implies aggregating over the highest order
dimension and so forth. If the axis is negative, then
counts the axis from the back. For example, axes: [-1]
will always aggregate all rows.
You may optionally set :keep_axes
to true, which will
retain the rank of the input tensor by setting the reduced
axes to size 1.
examples
Examples
iex> Nx.all(Nx.tensor([0, 1, 2]))
#Nx.Tensor<
u8
0
>
iex> Nx.all(Nx.tensor([[-1, 0, 1], [2, 3, 4]], names: [:x, :y]), axes: [:x])
#Nx.Tensor<
u8[y: 3]
[1, 0, 1]
>
iex> Nx.all(Nx.tensor([[-1, 0, 1], [2, 3, 4]], names: [:x, :y]), axes: [:y])
#Nx.Tensor<
u8[x: 2]
[0, 1]
>
keeping-axes
Keeping axes
iex> Nx.all(Nx.tensor([[-1, 0, 1], [2, 3, 4]], names: [:x, :y]), axes: [:y], keep_axes: true)
#Nx.Tensor<
u8[x: 2][y: 1]
[
[0],
[1]
]
>
Returns a scalar tensor of value 1 if all element-wise values are within tolerance of b. Otherwise returns value 0.
You may set the absolute tolerance, :atol
and relative tolerance
:rtol
. Given tolerances, this functions returns 1 if
absolute(a - b) <= (atol + rtol * absolute(b))
is true for all elements of a and b.
options
Options
:rtol
- relative tolerance between numbers, as described above. Defaults to 1.0e-5:atol
- absolute tolerance between numbers, as described above. Defaults to 1.0e-8:equal_nan
- iffalse
, NaN will always compare as false. OtherwiseNaN
will only equalNaN
. Defaults tofalse
examples
Examples
iex> Nx.all_close(Nx.tensor([1.0e10, 1.0e-7]), Nx.tensor([1.00001e10, 1.0e-8]))
#Nx.Tensor<
u8
0
>
iex> Nx.all_close(Nx.tensor([1.0e-8, 1.0e-8]), Nx.tensor([1.0e-8, 1.0e-9]))
#Nx.Tensor<
u8
1
>
Although NaN
by definition isn't equal to itself, so this implementation
also considers all NaN
s different from each other by default:
iex> Nx.all_close(Nx.tensor(:nan), Nx.tensor(:nan))
#Nx.Tensor<
u8
0
>
iex> Nx.all_close(Nx.tensor(:nan), Nx.tensor(0))
#Nx.Tensor<
u8
0
>
We can change this behavior with the :equal_nan
option:
iex> t = Nx.tensor([:nan, 1])
iex> Nx.all_close(t, t, equal_nan: true) # nan == nan -> true
#Nx.Tensor<
u8
1
>
iex> Nx.all_close(t, t, equal_nan: false) # nan == nan -> false, default behavior
#Nx.Tensor<
u8
0
>
Infinities behave as expected, being "close" to themselves but not to other numbers:
iex> Nx.all_close(Nx.tensor(:infinity), Nx.tensor(:infinity))
#Nx.Tensor<
u8
1
>
iex> Nx.all_close(Nx.tensor(:infinity), Nx.tensor(:neg_infinity))
#Nx.Tensor<
u8
0
>
iex> Nx.all_close(Nx.tensor(1.0e30), Nx.tensor(:infinity))
#Nx.Tensor<
u8
0
>
Returns a scalar tensor of value 1 if any of the tensor values are not zero. Otherwise the value is 0.
If the :axes
option is given, it aggregates over
the given dimensions, effectively removing them.
axes: [0]
implies aggregating over the highest order
dimension and so forth. If the axis is negative, then
counts the axis from the back. For example, axes: [-1]
will always aggregate all rows.
You may optionally set :keep_axes
to true, which will
retain the rank of the input tensor by setting the reduced
axes to size 1.
examples
Examples
iex> Nx.any(Nx.tensor([0, 1, 2]))
#Nx.Tensor<
u8
1
>
iex> Nx.any(Nx.tensor([[0, 1, 0], [0, 1, 2]], names: [:x, :y]), axes: [:x])
#Nx.Tensor<
u8[y: 3]
[0, 1, 1]
>
iex> Nx.any(Nx.tensor([[0, 1, 0], [0, 1, 2]], names: [:x, :y]), axes: [:y])
#Nx.Tensor<
u8[x: 2]
[1, 1]
>
keeping-axes
Keeping axes
iex> Nx.any(Nx.tensor([[0, 1, 0], [0, 1, 2]], names: [:x, :y]), axes: [:y], keep_axes: true)
#Nx.Tensor<
u8[x: 2][y: 1]
[
[1],
[1]
]
>
Returns the indices of the maximum values.
options
Options
:axis
- the axis to aggregate on. If no axis is given, returns the index of the absolute maximum value in the tensor.:keep_axis
- whether or not to keep the reduced axis with a size of 1. Defaults tofalse
.:tie_break
- how to break ties. one of:high
, or:low
. default behavior is to always return the lower index.
examples
Examples
iex> Nx.argmax(4)
#Nx.Tensor<
s64
0
>
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]])
iex> Nx.argmax(t)
#Nx.Tensor<
s64
10
>
If a tensor of floats is given, it still returns integers:
iex> Nx.argmax(Nx.tensor([2.0, 4.0]))
#Nx.Tensor<
s64
1
>
aggregating-over-an-axis
Aggregating over an axis
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]])
iex> Nx.argmax(t, axis: 0)
#Nx.Tensor<
s64[2][3]
[
[1, 0, 0],
[1, 1, 0]
]
>
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]], names: [:x, :y, :z])
iex> Nx.argmax(t, axis: :y)
#Nx.Tensor<
s64[x: 2][z: 3]
[
[0, 0, 0],
[0, 1, 0]
]
>
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]], names: [:x, :y, :z])
iex> Nx.argmax(t, axis: :z)
#Nx.Tensor<
s64[x: 2][y: 2]
[
[0, 2],
[0, 1]
]
>
tie-breaks
Tie breaks
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]], names: [:x, :y, :z])
iex> Nx.argmax(t, tie_break: :low, axis: :y)
#Nx.Tensor<
s64[x: 2][z: 3]
[
[0, 0, 0],
[0, 1, 0]
]
>
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]], names: [:x, :y, :z])
iex> Nx.argmax(t, tie_break: :high, axis: :y)
#Nx.Tensor<
s64[x: 2][z: 3]
[
[0, 0, 1],
[0, 1, 1]
]
>
keep-axis
Keep axis
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]], names: [:x, :y, :z])
iex> Nx.argmax(t, axis: :y, keep_axis: true)
#Nx.Tensor<
s64[x: 2][y: 1][z: 3]
[
[
[0, 0, 0]
],
[
[0, 1, 0]
]
]
>
Returns the indices of the minimum values.
options
Options
:axis
- the axis to aggregate on. If no axis is given, returns the index of the absolute minimum value in the tensor.:keep_axis
- whether or not to keep the reduced axis with a size of 1. Defaults tofalse
.:tie_break
- how to break ties. one of:high
, or:low
. Default behavior is to always return the lower index.
examples
Examples
iex> Nx.argmin(4)
#Nx.Tensor<
s64
0
>
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]])
iex> Nx.argmin(t)
#Nx.Tensor<
s64
4
>
If a tensor of floats is given, it still returns integers:
iex> Nx.argmin(Nx.tensor([2.0, 4.0]))
#Nx.Tensor<
s64
0
>
aggregating-over-an-axis
Aggregating over an axis
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]])
iex> Nx.argmin(t, axis: 0)
#Nx.Tensor<
s64[2][3]
[
[0, 0, 0],
[0, 0, 0]
]
>
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]], names: [:x, :y, :z])
iex> Nx.argmin(t, axis: 1)
#Nx.Tensor<
s64[x: 2][z: 3]
[
[1, 1, 0],
[1, 0, 0]
]
>
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]], names: [:x, :y, :z])
iex> Nx.argmin(t, axis: :z)
#Nx.Tensor<
s64[x: 2][y: 2]
[
[1, 1],
[1, 2]
]
>
tie-breaks
Tie breaks
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]], names: [:x, :y, :z])
iex> Nx.argmin(t, tie_break: :low, axis: :y)
#Nx.Tensor<
s64[x: 2][z: 3]
[
[1, 1, 0],
[1, 0, 0]
]
>
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]], names: [:x, :y, :z])
iex> Nx.argmin(t, tie_break: :high, axis: :y)
#Nx.Tensor<
s64[x: 2][z: 3]
[
[1, 1, 1],
[1, 0, 1]
]
>
keep-axis
Keep axis
iex> t = Nx.tensor([[[4, 2, 3], [1, -5, 3]], [[6, 2, 3], [4, 8, 3]]], names: [:x, :y, :z])
iex> Nx.argmin(t, axis: :y, keep_axis: true)
#Nx.Tensor<
s64[x: 2][y: 1][z: 3]
[
[
[1, 1, 0]
],
[
[1, 0, 0]
]
]
>
Returns the mean for the tensor.
If the :axes
option is given, it aggregates over
that dimension, effectively removing it. axes: [0]
implies aggregating over the highest order dimension
and so forth. If the axis is negative, then counts
the axis from the back. For example, axes: [-1]
will
always aggregate all rows.
You may optionally set :keep_axes
to true, which will
retain the rank of the input tensor by setting the averaged
axes to size 1.
examples
Examples
iex> Nx.mean(Nx.tensor(42))
#Nx.Tensor<
f32
42.0
>
iex> Nx.mean(Nx.tensor([1, 2, 3]))
#Nx.Tensor<
f32
2.0
>
aggregating-over-an-axis
Aggregating over an axis
iex> Nx.mean(Nx.tensor([1, 2, 3]), axes: [0])
#Nx.Tensor<
f32
2.0
>
iex> Nx.mean(Nx.tensor([1, 2, 3], type: :u8, names: [:x]), axes: [:x])
#Nx.Tensor<
f32
2.0
>
iex> t = Nx.tensor(Nx.iota({2, 2, 3}), names: [:x, :y, :z])
iex> Nx.mean(t, axes: [:x])
#Nx.Tensor<
f32[y: 2][z: 3]
[
[3.0, 4.0, 5.0],
[6.0, 7.0, 8.0]
]
>
iex> t = Nx.tensor(Nx.iota({2, 2, 3}), names: [:x, :y, :z])
iex> Nx.mean(t, axes: [:x, :z])
#Nx.Tensor<
f32[y: 2]
[4.0, 7.0]
>
iex> t = Nx.tensor(Nx.iota({2, 2, 3}), names: [:x, :y, :z])
iex> Nx.mean(t, axes: [-1])
#Nx.Tensor<
f32[x: 2][y: 2]
[
[1.0, 4.0],
[7.0, 10.0]
]
>
keeping-axes
Keeping axes
iex> t = Nx.tensor(Nx.iota({2, 2, 3}), names: [:x, :y, :z])
iex> Nx.mean(t, axes: [-1], keep_axes: true)
#Nx.Tensor<
f32[x: 2][y: 2][z: 1]
[
[
[1.0],
[4.0]
],
[
[7.0],
[10.0]
]
]
>
Returns the median for the tensor.
The median is the value in the middle of a data set.
If the :axis
option is given, it aggregates over
that dimension, effectively removing it. axis: 0
implies aggregating over the highest order dimension
and so forth. If the axis is negative, then the axis will
be counted from the back. For example, axis: -1
will
always aggregate over the last dimension.
You may optionally set :keep_axis
to true, which will
retain the rank of the input tensor by setting the reduced
axis to size 1.
examples
Examples
iex> Nx.median(Nx.tensor(42))
#Nx.Tensor<
s64
42
>
iex> Nx.median(Nx.tensor([1, 2, 3]))
#Nx.Tensor<
s64
2
>
iex> Nx.median(Nx.tensor([1, 2]))
#Nx.Tensor<
f32
1.5
>
aggregating-over-an-axis
Aggregating over an axis
iex> Nx.median(Nx.tensor([[1, 2, 3], [4, 5, 6]], names: [:x, :y]), axis: 0)
#Nx.Tensor<
f32[y: 3]
[2.5, 3.5, 4.5]
>
iex> Nx.median(Nx.tensor([[1, 2, 3], [4, 5, 6]], names: [:x, :y]), axis: :y)
#Nx.Tensor<
s64[x: 2]
[2, 5]
>
iex> t = Nx.tensor(Nx.iota({2, 2, 3}), names: [:x, :y, :z])
iex> Nx.median(t, axis: :x)
#Nx.Tensor<
f32[y: 2][z: 3]
[
[3.0, 4.0, 5.0],
[6.0, 7.0, 8.0]
]
>
iex> t = Nx.tensor([[[1, 2, 2], [3, 4, 2]], [[4, 5, 2], [7, 9, 2]]])
iex> Nx.median(t, axis: -1)
#Nx.Tensor<
s64[2][2]
[
[2, 3],
[4, 7]
]
>
keeping-axis
Keeping axis
iex> t = Nx.tensor([[[1, 2, 2], [3, 4, 2]], [[4, 5, 2], [7, 9, 2]]])
iex> Nx.median(t, axis: -1, keep_axis: true)
#Nx.Tensor<
s64[2][2][1]
[
[
[2],
[3]
],
[
[4],
[7]
]
]
>
Returns the mode of a tensor.
The mode is the value that appears most often.
If the :axis
option is given, it aggregates over
that dimension, effectively removing it. axis: 0
implies aggregating over the highest order dimension
and so forth. If the axis is negative, then the axis will
be counted from the back. For example, axis: -1
will
always aggregate over the last dimension.
You may optionally set :keep_axis
to true, which will
retain the rank of the input tensor by setting the reduced
axis to size 1.
examples
Examples
iex> Nx.mode(Nx.tensor(42))
#Nx.Tensor<
s64
42
>
iex> Nx.mode(Nx.tensor([[1]]))
#Nx.Tensor<
s64
1
>
iex> Nx.mode(Nx.tensor([1, 2, 2, 3, 5]))
#Nx.Tensor<
s64
2
>
iex> Nx.mode(Nx.tensor([[1, 2, 2, 3, 5], [1, 1, 76, 8, 1]]))
#Nx.Tensor<
s64
1
>
aggregating-over-an-axis
Aggregating over an axis
iex> Nx.mode(Nx.tensor([[1, 2, 2, 3, 5], [1, 1, 76, 8, 1]]), axis: 0)
#Nx.Tensor<
s64[5]
[1, 1, 2, 3, 1]
>
iex> Nx.mode(Nx.tensor([[1, 2, 2, 3, 5], [1, 1, 76, 8, 1]]), axis: 1)
#Nx.Tensor<
s64[2]
[2, 1]
>
iex> Nx.mode(Nx.tensor([[[1]]]), axis: 1)
#Nx.Tensor<
s64[1][1]
[
[1]
]
>
keeping-axis
Keeping axis
iex> Nx.mode(Nx.tensor([[1, 2, 2, 3, 5], [1, 1, 76, 8, 1]]), axis: 1, keep_axis: true)
#Nx.Tensor<
s64[2][1]
[
[2],
[1]
]
>
Returns the product for the tensor.
If the :axes
option is given, it aggregates over
the given dimensions, effectively removing them.
axes: [0]
implies aggregating over the highest order
dimension and so forth. If the axis is negative, then
counts the axis from the back. For example, axes: [-1]
will always aggregate all rows.
You may optionally set :keep_axes
to true, which will
retain the rank of the input tensor by setting the multiplied
axes to size 1.
examples
Examples
By default the product always returns a scalar:
iex> Nx.product(Nx.tensor(42))
#Nx.Tensor<
s64
42
>
iex> Nx.product(Nx.tensor([1, 2, 3]))
#Nx.Tensor<
s64
6
>
iex> Nx.product(Nx.tensor([[1.0, 2.0], [3.0, 4.0]]))
#Nx.Tensor<
f32
24.0
>
Giving a tensor with low precision casts it to a higher precision to make sure the sum does not overflow:
iex> Nx.product(Nx.tensor([[10, 20], [30, 40]], type: :u8, names: [:x, :y]))
#Nx.Tensor<
u64
240000
>
iex> Nx.product(Nx.tensor([[10, 20], [30, 40]], type: :s8, names: [:x, :y]))
#Nx.Tensor<
s64
240000
>
aggregating-over-an-axis
Aggregating over an axis
iex> Nx.product(Nx.tensor([1, 2, 3]), axes: [0])
#Nx.Tensor<
s64
6
>
Same tensor over different axes combinations:
iex> t = Nx.iota({2, 2, 3}, names: [:x, :y, :z])
iex> Nx.product(t, axes: [:x])
#Nx.Tensor<
s64[y: 2][z: 3]
[
[0, 7, 16],
[27, 40, 55]
]
>
iex> Nx.product(t, axes: [:y])
#Nx.Tensor<
s64[x: 2][z: 3]
[
[0, 4, 10],
[54, 70, 88]
]
>
iex> Nx.product(t, axes: [:x, :z])
#Nx.Tensor<
s64[y: 2]
[0, 59400]
>
iex> Nx.product(t, axes: [:z])
#Nx.Tensor<
s64[x: 2][y: 2]
[
[0, 60],
[336, 990]
]
>
iex> Nx.product(t, axes: [-3])
#Nx.Tensor<
s64[y: 2][z: 3]
[
[0, 7, 16],
[27, 40, 55]
]
>
keeping-axes
Keeping axes
iex> t = Nx.iota({2, 2, 3}, names: [:x, :y, :z])
iex> Nx.product(t, axes: [:z], keep_axes: true)
#Nx.Tensor<
s64[x: 2][y: 2][z: 1]
[
[
[0],
[60]
],
[
[336],
[990]
]
]
>
errors
Errors
iex> Nx.product(Nx.tensor([[1, 2]]), axes: [2])
** (ArgumentError) given axis (2) invalid for shape with rank 2
Reduces over a tensor with the given accumulator.
The given fun
will receive two tensors and it must
return the reduced value.
The tensor may be reduced in parallel and the reducer function can be called with arguments in any order, the initial accumulator may be given multiples, and it may be non-deterministic. Therefore, the reduction function should be associative (or as close as possible to associativity considered floats themselves are not strictly associative).
By default, it reduces all dimensions of the tensor and
return a scalar. If the :axes
option is given, it
aggregates over multiple dimensions, effectively removing
them. axes: [0]
implies aggregating over the highest
order dimension and so forth. If the axis is negative,
then counts the axis from the back. For example,
axes: [-1]
will always aggregate all rows.
The type of the returned tensor will be computed based on
the given tensor and the initial value. For example,
a tensor of integers with a float accumulator will be
cast to float, as done by most binary operators. You can
also pass a :type
option to change this behaviour.
You may optionally set :keep_axes
to true, which will
retain the rank of the input tensor by setting the reduced
axes to size 1.
limitations
Limitations
Given this function relies on anonymous functions, it
may not be available or efficient on all Nx backends.
Therefore, you should avoid using reduce/4
whenever
possible. Instead, use functions sum/2
, reduce_max/2
,
all/1
, and so forth.
Inside defn
, consider using Nx.Defn.Kernel.while/4
instead.
examples
Examples
iex> Nx.reduce(Nx.tensor(42), 0, fn x, y -> Nx.add(x, y) end)
#Nx.Tensor<
s64
42
>
iex> Nx.reduce(Nx.tensor([1, 2, 3]), 0, fn x, y -> Nx.add(x, y) end)
#Nx.Tensor<
s64
6
>
iex> Nx.reduce(Nx.tensor([[1.0, 2.0], [3.0, 4.0]]), 0, fn x, y -> Nx.add(x, y) end)
#Nx.Tensor<
f32
10.0
>
aggregating-over-axes
Aggregating over axes
iex> t = Nx.tensor([1, 2, 3], names: [:x])
iex> Nx.reduce(t, 0, [axes: [:x]], fn x, y -> Nx.add(x, y) end)
#Nx.Tensor<
s64
6
>
iex> t = Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]], names: [:x, :y, :z])
iex> Nx.reduce(t, 0, [axes: [:x]], fn x, y -> Nx.add(x, y) end)
#Nx.Tensor<
s64[y: 2][z: 3]
[
[8, 10, 12],
[14, 16, 18]
]
>
iex> t = Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]], names: [:x, :y, :z])
iex> Nx.reduce(t, 0, [axes: [:y]], fn x, y -> Nx.add(x, y) end)
#Nx.Tensor<
s64[x: 2][z: 3]
[
[5, 7, 9],
[17, 19, 21]
]
>
iex> t = Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]], names: [:x, :y, :z])
iex> Nx.reduce(t, 0, [axes: [:x, 2]], fn x, y -> Nx.add(x, y) end)
#Nx.Tensor<
s64[y: 2]
[30, 48]
>
iex> t = Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]], names: [:x, :y, :z])
iex> Nx.reduce(t, 0, [axes: [-1]], fn x, y -> Nx.add(x, y) end)
#Nx.Tensor<
s64[x: 2][y: 2]
[
[6, 15],
[24, 33]
]
>
iex> t = Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]], names: [:x, :y, :z])
iex> Nx.reduce(t, 0, [axes: [:x]], fn x, y -> Nx.add(x, y) end)
#Nx.Tensor<
s64[y: 2][z: 3]
[
[8, 10, 12],
[14, 16, 18]
]
>
iex> t = Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]], names: [:x, :y, :z])
iex> Nx.reduce(t, 0, [axes: [:x], keep_axes: true], fn x, y -> Nx.add(x, y) end)
#Nx.Tensor<
s64[x: 1][y: 2][z: 3]
[
[
[8, 10, 12],
[14, 16, 18]
]
]
>
Returns the maximum values of the tensor.
If the :axes
option is given, it aggregates over
the given dimensions, effectively removing them.
axes: [0]
implies aggregating over the highest order
dimension and so forth. If the axis is negative, then
counts the axis from the back. For example, axes: [-1]
will always aggregate all rows.
You may optionally set :keep_axes
to true, which will
retain the rank of the input tensor by setting the reduced
axes to size 1.
examples
Examples
iex> Nx.reduce_max(Nx.tensor(42))
#Nx.Tensor<
s64
42
>
iex> Nx.reduce_max(Nx.tensor(42.0))
#Nx.Tensor<
f32
42.0
>
iex> Nx.reduce_max(Nx.tensor([1, 2, 3]))
#Nx.Tensor<
s64
3
>
aggregating-over-an-axis
Aggregating over an axis
iex> t = Nx.tensor([[3, 1, 4], [2, 1, 1]], names: [:x, :y])
iex> Nx.reduce_max(t, axes: [:x])
#Nx.Tensor<
s64[y: 3]
[3, 1, 4]
>
iex> t = Nx.tensor([[3, 1, 4], [2, 1, 1]], names: [:x, :y])
iex> Nx.reduce_max(t, axes: [:y])
#Nx.Tensor<
s64[x: 2]
[4, 2]
>
iex> t = Nx.tensor([[[1, 2], [4, 5]], [[2, 4], [3, 8]]], names: [:x, :y, :z])
iex> Nx.reduce_max(t, axes: [:x, :z])
#Nx.Tensor<
s64[y: 2]
[4, 8]
>
keeping-axes
Keeping axes
iex> t = Nx.tensor([[[1, 2], [4, 5]], [[2, 4], [3, 8]]], names: [:x, :y, :z])
iex> Nx.reduce_max(t, axes: [:x, :z], keep_axes: true)
#Nx.Tensor<
s64[x: 1][y: 2][z: 1]
[
[
[4],
[8]
]
]
>
Returns the minimum values of the tensor.
If the :axes
option is given, it aggregates over
the given dimensions, effectively removing them.
axes: [0]
implies aggregating over the highest order
dimension and so forth. If the axis is negative, then
counts the axis from the back. For example, axes: [-1]
will always aggregate all rows.
You may optionally set :keep_axes
to true, which will
retain the rank of the input tensor by setting the reduced
axes to size 1.
examples
Examples
iex> Nx.reduce_min(Nx.tensor(42))
#Nx.Tensor<
s64
42
>
iex> Nx.reduce_min(Nx.tensor(42.0))
#Nx.Tensor<
f32
42.0
>
iex> Nx.reduce_min(Nx.tensor([1, 2, 3]))
#Nx.Tensor<
s64
1
>
aggregating-over-an-axis
Aggregating over an axis
iex> t = Nx.tensor([[3, 1, 4], [2, 1, 1]], names: [:x, :y])
iex> Nx.reduce_min(t, axes: [:x])
#Nx.Tensor<
s64[y: 3]
[2, 1, 1]
>
iex> t = Nx.tensor([[3, 1, 4], [2, 1, 1]], names: [:x, :y])
iex> Nx.reduce_min(t, axes: [:y])
#Nx.Tensor<
s64[x: 2]
[1, 1]
>
iex> t = Nx.tensor([[[1, 2], [4, 5]], [[2, 4], [3, 8]]], names: [:x, :y, :z])
iex> Nx.reduce_min(t, axes: [:x, :z])
#Nx.Tensor<
s64[y: 2]
[1, 3]
>
keeping-axes
Keeping axes
iex> t = Nx.tensor([[[1, 2], [4, 5]], [[2, 4], [3, 8]]], names: [:x, :y, :z])
iex> Nx.reduce_min(t, axes: [:x, :z], keep_axes: true)
#Nx.Tensor<
s64[x: 1][y: 2][z: 1]
[
[
[1],
[3]
]
]
>
@spec standard_deviation(tensor :: Nx.Tensor.t(), opts :: Keyword.t()) :: Nx.Tensor.t()
Finds the standard deviation of a tensor.
The standard deviation is taken as the square root of the variance.
If the :ddof
(delta degrees of freedom) option is given, the divisor
n - ddof
is used to calculate the variance. See variance/2
.
examples
Examples
iex> Nx.standard_deviation(Nx.tensor([[1, 2], [3, 4]]))
#Nx.Tensor<
f32
1.1180340051651
>
iex> Nx.standard_deviation(Nx.tensor([[1, 2], [3, 4]]), ddof: 1)
#Nx.Tensor<
f32
1.29099440574646
>
iex> Nx.standard_deviation(Nx.tensor([[1, 2], [10, 20]]), axes: [0])
#Nx.Tensor<
f32[2]
[4.5, 9.0]
>
iex> Nx.standard_deviation(Nx.tensor([[1, 2], [10, 20]]), axes: [1])
#Nx.Tensor<
f32[2]
[0.5, 5.0]
>
iex> Nx.standard_deviation(Nx.tensor([[1, 2], [10, 20]]), axes: [0], ddof: 1)
#Nx.Tensor<
f32[2]
[6.363961219787598, 12.727922439575195]
>
iex> Nx.standard_deviation(Nx.tensor([[1, 2], [10, 20]]), axes: [1], ddof: 1)
#Nx.Tensor<
f32[2]
[0.7071067690849304, 7.071067810058594]
>
keeping-axes
Keeping axes
iex> Nx.standard_deviation(Nx.tensor([[1, 2], [10, 20]]), keep_axes: true)
#Nx.Tensor<
f32[1][1]
[
[7.628073215484619]
]
>
Returns the sum for the tensor.
If the :axes
option is given, it aggregates over
the given dimensions, effectively removing them.
axes: [0]
implies aggregating over the highest order
dimension and so forth. If the axis is negative, then
counts the axis from the back. For example, axes: [-1]
will always aggregate all rows.
You may optionally set :keep_axes
to true, which will
retain the rank of the input tensor by setting the summed
axes to size 1.
examples
Examples
By default the sum always returns a scalar:
iex> Nx.sum(Nx.tensor(42))
#Nx.Tensor<
s64
42
>
iex> Nx.sum(Nx.tensor([1, 2, 3]))
#Nx.Tensor<
s64
6
>
iex> Nx.sum(Nx.tensor([[1.0, 2.0], [3.0, 4.0]]))
#Nx.Tensor<
f32
10.0
>
Giving a tensor with low precision casts it to a higher precision to make sure the sum does not overflow:
iex> Nx.sum(Nx.tensor([[101, 102], [103, 104]], type: :s8))
#Nx.Tensor<
s64
410
>
iex> Nx.sum(Nx.tensor([[101, 102], [103, 104]], type: :s16))
#Nx.Tensor<
s64
410
>
aggregating-over-an-axis
Aggregating over an axis
iex> Nx.sum(Nx.tensor([1, 2, 3]), axes: [0])
#Nx.Tensor<
s64
6
>
Same tensor over different axes combinations:
iex> t = Nx.iota({2, 2, 3}, names: [:x, :y, :z])
iex> Nx.sum(t, axes: [:x])
#Nx.Tensor<
s64[y: 2][z: 3]
[
[6, 8, 10],
[12, 14, 16]
]
>
iex> Nx.sum(t, axes: [:y])
#Nx.Tensor<
s64[x: 2][z: 3]
[
[3, 5, 7],
[15, 17, 19]
]
>
iex> Nx.sum(t, axes: [:z])
#Nx.Tensor<
s64[x: 2][y: 2]
[
[3, 12],
[21, 30]
]
>
iex> Nx.sum(t, axes: [:x, :z])
#Nx.Tensor<
s64[y: 2]
[24, 42]
>
iex> Nx.sum(t, axes: [-3])
#Nx.Tensor<
s64[y: 2][z: 3]
[
[6, 8, 10],
[12, 14, 16]
]
>
keeping-axes
Keeping axes
iex> t = Nx.tensor([[1, 2], [3, 4]], names: [:x, :y])
iex> Nx.sum(t, axes: [:x], keep_axes: true)
#Nx.Tensor<
s64[x: 1][y: 2]
[
[4, 6]
]
>
errors
Errors
iex> Nx.sum(Nx.tensor([[1, 2]]), axes: [2])
** (ArgumentError) given axis (2) invalid for shape with rank 2
@spec variance(tensor :: Nx.Tensor.t(), opts :: Keyword.t()) :: Nx.Tensor.t()
Finds the variance of a tensor.
The variance is the average of the squared deviations from the mean.
The mean is typically calculated as sum(tensor) / n
, where n
is the total
of elements. If, however, :ddof
(delta degrees of freedom) is specified, the
divisor n - ddof
is used instead.
examples
Examples
iex> Nx.variance(Nx.tensor([[1, 2], [3, 4]]))
#Nx.Tensor<
f32
1.25
>
iex> Nx.variance(Nx.tensor([[1, 2], [3, 4]]), ddof: 1)
#Nx.Tensor<
f32
1.6666666269302368
>
iex> Nx.variance(Nx.tensor([[1, 2], [3, 4]]), axes: [0])
#Nx.Tensor<
f32[2]
[1.0, 1.0]
>
iex> Nx.variance(Nx.tensor([[1, 2], [3, 4]]), axes: [1])
#Nx.Tensor<
f32[2]
[0.25, 0.25]
>
iex> Nx.variance(Nx.tensor([[1, 2], [3, 4]]), axes: [0], ddof: 1)
#Nx.Tensor<
f32[2]
[2.0, 2.0]
>
iex> Nx.variance(Nx.tensor([[1, 2], [3, 4]]), axes: [1], ddof: 1)
#Nx.Tensor<
f32[2]
[0.5, 0.5]
>
keeping-axes
Keeping axes
iex> Nx.variance(Nx.tensor([[1, 2], [3, 4]]), axes: [1], keep_axes: true)
#Nx.Tensor<
f32[2][1]
[
[0.25],
[0.25]
]
>
Returns the weighted mean for the tensor and the weights.
If the :axes
option is given, it aggregates over
those dimensions, effectively removing them. axes: [0]
implies aggregating over the highest order dimension
and so forth. If the axes are negative, then the axes will
be counted from the back. For example, axes: [-1]
will
always aggregate over the last dimension.
You may optionally set :keep_axes
to true, which will
retain the rank of the input tensor by setting the averaged
axes to size 1.
examples
Examples
iex> Nx.weighted_mean(Nx.tensor(42), Nx.tensor(2))
#Nx.Tensor<
f32
42.0
>
iex> Nx.weighted_mean(Nx.tensor([1, 2, 3]), Nx.tensor([3, 2, 1]))
#Nx.Tensor<
f32
1.6666666269302368
>
aggregating-over-axes
Aggregating over axes
iex> Nx.weighted_mean(Nx.tensor([1, 2, 3], names: [:x]), Nx.tensor([4, 5, 6]), axes: [0])
#Nx.Tensor<
f32
2.133333444595337
>
iex> Nx.weighted_mean(Nx.tensor([1, 2, 3], type: :u8, names: [:x]), Nx.tensor([1, 3, 5]), axes: [:x])
#Nx.Tensor<
f32
2.444444417953491
>
iex> t = Nx.iota({3, 4})
iex> weights = Nx.tensor([1, 2, 3, 4])
iex> Nx.weighted_mean(t, weights, axes: [1])
#Nx.Tensor<
f32[3]
[2.0, 6.0, 10.0]
>
iex> t = Nx.iota({2, 4, 4, 1})
iex> weights = Nx.broadcast(2, {4, 4})
iex> Nx.weighted_mean(t, weights, axes: [1, 2])
#Nx.Tensor<
f32[2][1]
[
[7.5],
[23.5]
]
>
keeping-axes
Keeping axes
iex> t = Nx.tensor(Nx.iota({2, 2, 3}), names: [:x, :y, :z])
iex> weights = Nx.tensor([[[0, 1, 2], [1, 1, 0]], [[-1, 1, -1], [1, 1, -1]]])
iex> Nx.weighted_mean(t, weights, axes: [-1], keep_axes: true)
#Nx.Tensor<
f32[x: 2][y: 2][z: 1]
[
[
[1.6666666269302368],
[3.5]
],
[
[7.0],
[8.0]
]
]
>
Link to this section Functions: Backend
Copies data to the given backend.
If a backend is not given, Nx.Tensor
is used, which means
the given tensor backend will pick the most appropriate
backend to copy the data to.
Note this function keeps the data in the original backend.
Therefore, use this function with care, as it may duplicate
large amounts of data across backends. Generally speaking,
you may want to use backend_transfer/2
, unless you explicitly
want to copy the data.
For convenience, this function accepts tensors and any container
(such as maps and tuples as defined by the Nx.Container
protocol)
and recursively copies all tensors in them. This behaviour exists
as it is common to transfer data before and after defn
functions.
Note:
Nx.default_backend/1
does not affect the behaviour of this function.- This function cannot be used in
defn
.
examples
Examples
iex> Nx.backend_copy(Nx.tensor([[1, 2, 3], [4, 5, 6]])) #Nx.Tensor<
s64[2][3]
[
[1, 2, 3],
[4, 5, 6]
]
Deallocates data in a device.
It returns either :ok
or :already_deallocated
.
For convenience, this function accepts tensors and any container
(such as maps and tuples as defined by the Nx.Container
protocol)
and deallocates all devices in them. This behaviour exists as it is
common to deallocate data after defn
functions.
Note: This function cannot be used in defn
.
backend_transfer(tensor_or_container, backend \\ Nx.BinaryBackend)
View SourceTransfers data to the given backend.
This operation can be seen as an equivalent to backend_copy/3
followed by a backend_deallocate/1
on the initial tensor:
new_tensor = Nx.backend_copy(old_tensor, new_backend)
Nx.backend_deallocate(old_tensor)
If a backend is not given, Nx.Tensor
is used, which means
the given tensor backend will pick the most appropriate
backend to transfer to.
For Elixir's builtin tensor, transferring to another backend
will call new_backend.from_binary(tensor, binary, opts)
.
Transferring from a mutable backend, such as GPU memory,
implies the data is copied from the GPU to the Erlang VM
and then deallocated from the device.
For convenience, this function accepts tensors and any container
(such as maps and tuples as defined by the Nx.Container
protocol)
and transfers all tensors in them. This behaviour exists as it is
common to transfer data from tuples and maps before and after defn
functions.
Note:
Nx.default_backend/1
does not affect the behaviour of this function.- This function cannot be used in
defn
.
examples
Examples
Transfer a tensor to an EXLA device backend, stored in the GPU:
device_tensor = Nx.backend_transfer(tensor, {EXLA.Backend, client: :cuda})
Transfer the device tensor back to an Elixir tensor:
tensor = Nx.backend_transfer(device_tensor)
Gets the default backend for the current process.
Note: This function cannot be used in defn
.
Sets the given backend
as default in the current process.
The default backend is stored only in the process dictionary.
This means if you start a separate process, such as Task
,
the default backend must be set on the new process too.
Due to this reason, this function is mostly used for scripting and testing. In your applications, you must prefer to set the backend in your config files:
config :nx, :default_backend, {EXLA.Backend, device: :cuda}
In your notebooks and on Mix.install/2
, you might:
Mix.install(
[
{:nx, ">= 0.0.0"}
],
config: [nx: [default_backend: {EXLA.Backend, device: :cuda}]]
)
Or use Nx.global_default_backend/1
as it changes the
default backend on all processes.
The function returns the value that was previously set as backend.
Note: This function cannot be used in defn
.
examples
Examples
Nx.default_backend({EXLA.Backend, device: :cuda})
#=> {Nx.BinaryBackend, []}
Sets the default backend globally.
You must avoid calling this function at runtime. It is mostly useful during scripts or code notebooks to set a default.
If you need to configure a global default backend in your
applications, it is generally preferred to do so in your
config/*.exs
files:
config :nx, :default_backend, {EXLA.Backend, []}
In your notebooks and on Mix.install/2
, you might:
Mix.install(
[
{:nx, ">= 0.0.0"}
],
config: [nx: [default_backend: {EXLA.Backend, device: :cuda}]]
)
The function returns the value that was previously set as global backend.
Invokes the given function temporarily setting backend
as the
default backend.
Link to this section Functions: Conversion
Deserializes a serialized representation of a tensor or a container with the given options.
It is the opposite of Nx.serialize/2
.
Note: This function cannot be used in defn
.
examples
Examples
iex> a = Nx.tensor([1, 2, 3])
iex> serialized_a = Nx.serialize(a)
iex> Nx.deserialize(serialized_a)
#Nx.Tensor<
s64[3]
[1, 2, 3]
>
iex> container = {Nx.tensor([1, 2, 3]), %{b: Nx.tensor([4, 5, 6])}}
iex> serialized_container = Nx.serialize(container)
iex> {a, %{b: b}} = Nx.deserialize(serialized_container)
iex> a
#Nx.Tensor<
s64[3]
[1, 2, 3]
>
iex> b
#Nx.Tensor<
s64[3]
[4, 5, 6]
>
@spec load_numpy!(data :: binary()) :: Nx.Tensor.t()
Loads a .npy
file into a tensor.
An .npy
file stores a single array created from Python's
NumPy library. This function can be useful for loading data
originally created or intended to be loaded from NumPy into
Elixir.
This function will raise if the archive or any of its contents are invalid.
Note: This function cannot be used in defn
.
examples
Examples
"array.npy"
|> File.read!()
|> Nx.load_numpy!()
#=>
#Nx.Tensor<
s64[3]
[1, 2, 3]
>
@spec load_numpy_archive!(data :: binary()) :: [{name :: binary(), Nx.Tensor.t()}]
Loads a .npz
archive into a list of tensors.
An .npz
file is a zipped, possibly compressed
archive containing multiple .npy
files.
It returns a list of two elements tuples, where
the tensor name is first and the serialized tensor
is second. The list is returned in the same order
as in the archive. Use Map.new/1
afterwards if
you want to access the list elements by name.
It will raise if the archive or any of its contents are invalid.
Note: This function cannot be used in defn
.
examples
Examples
"archive.npz"
|> File.read!()
|> Nx.load_numpy_archive!()
#=>
[
{"foo",
#Nx.Tensor<
s64[3]
[1, 2, 3]
>},
{"bar",
#Nx.Tensor<
f64[5]
[-1.0, -0.5, 0.0, 0.5, 1.0]
>}
]
Serializes the given tensor or container of tensors to iodata.
You may pass any tensor or Nx.Container
to serialization.
opts
controls the serialization options. For example, you can choose
to compress the given tensor or container of tensors by passing a
compression level:
Nx.serialize(tensor, compressed: 9)
Compression level corresponds to compression options in :erlang.term_to_iovec/2
.
iodata
is a list of binaries that can be written to any io device,
such as a file or a socket. You can ensure the result is a binary by
calling IO.iodata_to_binary/1
.
Note: This function cannot be used in defn
.
examples
Examples
iex> a = Nx.tensor([1, 2, 3])
iex> serialized_a = Nx.serialize(a)
iex> Nx.deserialize(serialized_a)
#Nx.Tensor<
s64[3]
[1, 2, 3]
>
iex> container = {Nx.tensor([1, 2, 3]), %{b: Nx.tensor([4, 5, 6])}}
iex> serialized_container = Nx.serialize(container)
iex> {a, %{b: b}} = Nx.deserialize(serialized_container)
iex> a
#Nx.Tensor<
s64[3]
[1, 2, 3]
>
iex> b
#Nx.Tensor<
s64[3]
[4, 5, 6]
>
Converts the underlying tensor to a stream of tensor batches.
The first dimension (axis 0) is divided by batch_size
.
In case the dimension cannot be evenly divided by
batch_size
, you may specify what to do with leftover
data using :leftover
. :leftover
must be one of :repeat
or :discard
. :repeat
repeats the first n
values to
make the last batch match the desired batch size. :discard
discards excess elements.
Note: This function cannot be used in defn
.
examples
Examples
In the examples below we immediately pipe to Enum.to_list/1
for convenience, but in practice you want to lazily traverse
the batches to avoid allocating multiple tensors at once in
certain backends:
iex> [first, second] = Nx.to_batched(Nx.iota({2, 2, 2}), 1) |> Enum.to_list()
iex> first
#Nx.Tensor<
s64[1][2][2]
[
[
[0, 1],
[2, 3]
]
]
>
iex> second
#Nx.Tensor<
s64[1][2][2]
[
[
[4, 5],
[6, 7]
]
]
>
If the batch size would result in uneven batches, you can repeat or discard excess data. By default, we repeat:
iex> [first, second, third] = Nx.to_batched(Nx.iota({5, 2}, names: [:x, :y]), 2) |> Enum.to_list()
iex> first
#Nx.Tensor<
s64[x: 2][y: 2]
[
[0, 1],
[2, 3]
]
>
iex> second
#Nx.Tensor<
s64[x: 2][y: 2]
[
[4, 5],
[6, 7]
]
>
iex> third
#Nx.Tensor<
s64[x: 2][y: 2]
[
[8, 9],
[0, 1]
]
>
But you can also discard:
iex> [first, second] = Nx.to_batched(Nx.iota({5, 2}, names: [:x, :y]), 2, leftover: :discard) |> Enum.to_list()
iex> first
#Nx.Tensor<
s64[x: 2][y: 2]
[
[0, 1],
[2, 3]
]
>
iex> second
#Nx.Tensor<
s64[x: 2][y: 2]
[
[4, 5],
[6, 7]
]
>
Returns the underlying tensor as a binary.
Warning: converting a tensor to a binary can potentially be a very expensive operation, as it may copy a GPU tensor fully to the machine memory.
It returns the in-memory binary representation of the tensor in a row-major fashion. The binary is in the system endianness, which has to be taken into account if the binary is meant to be serialized to other systems.
Note: This function cannot be used in defn
.
options
Options
:limit
- limit the number of entries represented in the binary
examples
Examples
iex> Nx.to_binary(1)
<<1::64-native>>
iex> Nx.to_binary(Nx.tensor([1.0, 2.0, 3.0]))
<<1.0::float-32-native, 2.0::float-32-native, 3.0::float-32-native>>
iex> Nx.to_binary(Nx.tensor([1.0, 2.0, 3.0]), limit: 2)
<<1.0::float-32-native, 2.0::float-32-native>>
Returns the underlying tensor as a flat list.
Negative infinity (-Inf), infinity (Inf), and "not a number" (NaN)
will be represented by the atoms :neg_infinity
, :infinity
, and
:nan
respectively.
Note: This function cannot be used in defn
.
examples
Examples
iex> Nx.to_flat_list(1)
[1]
iex> Nx.to_flat_list(Nx.tensor([1.0, 2.0, 3.0]))
[1.0, 2.0, 3.0]
iex> Nx.to_flat_list(Nx.tensor([1.0, 2.0, 3.0]), limit: 2)
[1.0, 2.0]
Non-finite numbers are returned as atoms:
iex> t = Nx.tensor([:neg_infinity, :nan, :infinity])
iex> Nx.to_flat_list(t)
[:neg_infinity, :nan, :infinity]
Returns a heatmap struct with the tensor data.
On terminals, coloring is done via ANSI colors. If ANSI is not enabled, the tensor is normalized to show numbers between 0 and 9.
terminal-coloring
Terminal coloring
Coloring is enabled by default on most Unix terminals. It is also available on Windows consoles from Windows 10, although it must be explicitly enabled for the current user in the registry by running the following command:
reg add HKCU\Console /v VirtualTerminalLevel /t REG_DWORD /d 1
After running the command above, you must restart your current console.
options
Options
:ansi_enabled
- forces ansi to be enabled or disabled. Defaults toIO.ANSI.enabled?/0
:ansi_whitespace
- which whitespace character to use when printing. By default it uses"\u3000"
, which is a full-width whitespace which often prints more precise shapes
Converts the tensor into a list reflecting its structure.
Negative infinity (-Inf), infinity (Inf), and "not a number" (NaN)
will be represented by the atoms :neg_infinity
, :infinity
, and
:nan
respectively.
It raises if a scalar tensor is given, use to_number/1
instead.
Note: This function cannot be used in defn
.
examples
Examples
iex> Nx.iota({2, 3}) |> Nx.to_list()
[
[0, 1, 2],
[3, 4, 5]
]
iex> Nx.tensor(123) |> Nx.to_list()
** (ArgumentError) cannot convert a scalar tensor to a list, got: #Nx.Tensor<
s64
123
>
Returns the underlying tensor as a number.
Negative infinity (-Inf), infinity (Inf), and "not a number" (NaN)
will be represented by the atoms :neg_infinity
, :infinity
, and
:nan
respectively.
If the tensor has a dimension, it raises.
Note: This function cannot be used in defn
.
examples
Examples
iex> Nx.to_number(1)
1
iex> Nx.to_number(Nx.tensor([1.0, 2.0, 3.0]))
** (ArgumentError) cannot convert tensor of shape {3} to number
Converts a tensor (or tuples and maps of tensors) to tensor templates.
Templates are useful when you need to pass types and shapes to operations and the data is not yet available.
For convenience, this function accepts tensors and any container
(such as maps and tuples as defined by the Nx.LazyContainer
protocol)
and recursively converts all tensors to templates.
examples
Examples
iex> Nx.iota({2, 3}) |> Nx.to_template()
#Nx.Tensor<
s64[2][3]
Nx.TemplateBackend
>
iex> {int, float} = Nx.to_template({1, 2.0})
iex> int
#Nx.Tensor<
s64
Nx.TemplateBackend
>
iex> float
#Nx.Tensor<
f32
Nx.TemplateBackend
>
Although note it is impossible to perform any operation on a tensor template:
iex> t = Nx.iota({2, 3}) |> Nx.to_template()
iex> Nx.abs(t)
** (RuntimeError) cannot perform operations on a Nx.TemplateBackend tensor
To build a template from scratch, use template/3
.
Converts the given number (or tensor) to a tensor.
This function only converts types which are automatically cast to tensors throughout Nx API: numbers, complex numbers, and tensors themselves.
If your goal is to create tensors from lists, see tensor/2
.
If you want to create a tensor from binary, see from_binary/3
.
If you want to convert non-tensor data structures or Nx.Container
s
into tensors, see stack/2
or concatenate/2
instead.
Link to this section Functions: Creation
Creates the identity matrix of size n
.
examples
Examples
iex> Nx.eye(2)
#Nx.Tensor<
s64[2][2]
[
[1, 0],
[0, 1]
]
>
iex> Nx.eye(3, type: :f32, names: [:height, :width])
#Nx.Tensor<
f32[height: 3][width: 3]
[
[1.0, 0.0, 0.0],
[0.0, 1.0, 0.0],
[0.0, 0.0, 1.0]
]
>
The first argument can also be a shape of a matrix:
iex> Nx.eye({1, 2})
#Nx.Tensor<
s64[1][2]
[
[1, 0]
]
>
The shape can also represent a tensor batch. In this case, the last two axes will represent the same identity matrix.
iex> Nx.eye({2, 4, 3})
#Nx.Tensor<
s64[2][4][3]
[
[
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
[0, 0, 0]
],
[
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
[0, 0, 0]
]
]
>
options
Options
:type
- the type of the tensor:names
- the names of the tensor dimensions:backend
- the backend to allocate the tensor on. It is either an atom or a tuple in the shape{backend, options}
. This option is ignored insidedefn
Creates a one-dimensional tensor from a binary
with the given type
.
If the binary size does not match its type, an error is raised.
examples
Examples
iex> Nx.from_binary(<<1, 2, 3, 4>>, :s8)
#Nx.Tensor<
s8[4]
[1, 2, 3, 4]
>
The atom notation for types is also supported:
iex> Nx.from_binary(<<12.3::float-64-native>>, :f64)
#Nx.Tensor<
f64[1]
[12.3]
>
An error is raised for incompatible sizes:
iex> Nx.from_binary(<<1, 2, 3, 4>>, :f64)
** (ArgumentError) binary does not match the given size
options
Options
:backend
- the backend to allocate the tensor on. It is either an atom or a tuple in the shape{backend, options}
. This option is ignored insidedefn
Creates a tensor with the given shape which increments along the provided axis. You may optionally provide dimension names.
If no axis is provided, index counts up at each element.
If a tensor or a number are given, the shape and names are taken from the tensor.
examples
Examples
iex> Nx.iota({})
#Nx.Tensor<
s64
0
>
iex> Nx.iota({5})
#Nx.Tensor<
s64[5]
[0, 1, 2, 3, 4]
>
iex> Nx.iota({3, 2, 3}, names: [:batch, :height, :width])
#Nx.Tensor<
s64[batch: 3][height: 2][width: 3]
[
[
[0, 1, 2],
[3, 4, 5]
],
[
[6, 7, 8],
[9, 10, 11]
],
[
[12, 13, 14],
[15, 16, 17]
]
]
>
iex> Nx.iota({3, 3}, axis: 1, names: [:batch, nil])
#Nx.Tensor<
s64[batch: 3][3]
[
[0, 1, 2],
[0, 1, 2],
[0, 1, 2]
]
>
iex> Nx.iota({3, 3}, axis: -1)
#Nx.Tensor<
s64[3][3]
[
[0, 1, 2],
[0, 1, 2],
[0, 1, 2]
]
>
iex> Nx.iota({3, 4, 3}, axis: 0, type: :f64)
#Nx.Tensor<
f64[3][4][3]
[
[
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0]
],
[
[1.0, 1.0, 1.0],
[1.0, 1.0, 1.0],
[1.0, 1.0, 1.0],
[1.0, 1.0, 1.0]
],
[
[2.0, 2.0, 2.0],
[2.0, 2.0, 2.0],
[2.0, 2.0, 2.0],
[2.0, 2.0, 2.0]
]
]
>
iex> Nx.iota({1, 3, 2}, axis: 2)
#Nx.Tensor<
s64[1][3][2]
[
[
[0, 1],
[0, 1],
[0, 1]
]
]
>
options
Options
:type
- the type of the tensor:axis
- an axis to repeat the iota over:names
- the names of the tensor dimensions:backend
- the backend to allocate the tensor on. It is either an atom or a tuple in the shape{backend, options}
. This option is ignored insidedefn
Creates a tensor of shape {n}
with linearly spaced samples between start
and stop
.
options
Options
:n
- The number of samples in the tensor.:name
- Optional name for the output axis.:type
- Optional type for the output. Defaults to{:f, 32}
:endpoint
- Boolean that indicates whether to includestop
as the last point in the output. Defaults totrue
examples
Examples
iex> Nx.linspace(5, 8, n: 5)
#Nx.Tensor<
f32[5]
[5.0, 5.75, 6.5, 7.25, 8.0]
>
iex> Nx.linspace(0, 10, n: 5, endpoint: false, name: :x)
#Nx.Tensor<
f32[x: 5]
[0.0, 2.0, 4.0, 6.0, 8.0]
>
For integer types, the results might not be what's expected.
When endpoint: true
(the default), the step is given by
step = (stop - start) / (n - 1)
, which means that instead
of a step of 3
in the example below, we get a step close to
3.42
. The results are calculated first and only cast in the
end, so that the :endpoint
condition is respected.
iex> Nx.linspace(0, 24, n: 8, type: {:u, 8}, endpoint: true)
#Nx.Tensor<
u8[8]
[0, 3, 6, 10, 13, 17, 20, 24]
>
iex> Nx.linspace(0, 24, n: 8, type: {:s, 64}, endpoint: false)
#Nx.Tensor<
s64[8]
[0, 3, 6, 9, 12, 15, 18, 21]
>
One can also pass two higher order tensors with the same shape {j, k, ...}
, in which case
the output will be of shape {j, k, ..., n}
.
iex> Nx.linspace(Nx.tensor([[[0, 10]]]), Nx.tensor([[[10, 100]]]), n: 10, name: :samples, type: {:u, 8}) #Nx.Tensor<
u8[1][1][2][samples: 10]
[
[
[
[0, 1, 2, 3, 4, 5, 6, 7, 8, 10],
[10, 20, 30, 40, 50, 60, 70, 80, 90, 100]
]
]
]
error-cases
Error cases
iex> Nx.linspace(0, 24, n: 1.0)
** (ArgumentError) expected n to be a non-negative integer, got: 1.0
iex> Nx.linspace(Nx.tensor([[0, 1]]), Nx.tensor([1, 2, 3]), n: 2)
** (ArgumentError) expected start and stop to have the same shape. Got shapes {1, 2} and {3}
Creates a diagonal tensor from a 1D tensor.
Converse of take_diagonal/2
.
The returned tensor will be a square matrix of dimensions equal to the size of the tensor. If an offset is given, the absolute value of the offset is added to the matrix dimensions sizes.
examples
Examples
Given a 1D tensor:
iex> Nx.make_diagonal(Nx.tensor([1, 2, 3, 4]))
#Nx.Tensor<
s64[4][4]
[
[1, 0, 0, 0],
[0, 2, 0, 0],
[0, 0, 3, 0],
[0, 0, 0, 4]
]
>
Given a 1D tensor with an offset:
iex> Nx.make_diagonal(Nx.tensor([1, 2, 3]), offset: 1)
#Nx.Tensor<
s64[4][4]
[
[0, 1, 0, 0],
[0, 0, 2, 0],
[0, 0, 0, 3],
[0, 0, 0, 0]
]
>
iex> Nx.make_diagonal(Nx.tensor([1, 2, 3]), offset: -1)
#Nx.Tensor<
s64[4][4]
[
[0, 0, 0, 0],
[1, 0, 0, 0],
[0, 2, 0, 0],
[0, 0, 3, 0]
]
>
You can also have offsets with an abs greater than the tensor length:
iex> Nx.make_diagonal(Nx.tensor([1, 2, 3]), offset: -4)
#Nx.Tensor<
s64[7][7]
[
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[1, 0, 0, 0, 0, 0, 0],
[0, 2, 0, 0, 0, 0, 0],
[0, 0, 3, 0, 0, 0, 0]
]
>
iex> Nx.make_diagonal(Nx.tensor([1, 2, 3]), offset: 4)
#Nx.Tensor<
s64[7][7]
[
[0, 0, 0, 0, 1, 0, 0],
[0, 0, 0, 0, 0, 2, 0],
[0, 0, 0, 0, 0, 0, 3],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0]
]
>
options
Options
:offset
- offset used for making the diagonal. Use offset > 0 for diagonals above the main diagonal, and offset < 0 for diagonals below the main diagonal. Defaults to 0.
error-cases
Error cases
iex> Nx.make_diagonal(Nx.tensor([[0, 0], [0, 1]]))
** (ArgumentError) make_diagonal/2 expects tensor of rank 1, got tensor of rank: 2
Puts the individual values from a 1D diagonal into the diagonal indices of the given 2D tensor.
See also: take_diagonal/2
, make_diagonal/2
.
examples
Examples
Given a 2D tensor and a 1D diagonal:
iex> t = Nx.broadcast(0, {4, 4})
#Nx.Tensor<
s64[4][4]
[
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
>
iex> Nx.put_diagonal(t, Nx.tensor([1, 2, 3, 4]))
#Nx.Tensor<
s64[4][4]
[
[1, 0, 0, 0],
[0, 2, 0, 0],
[0, 0, 3, 0],
[0, 0, 0, 4]
]
>
iex> t = Nx.broadcast(0, {4, 3})
#Nx.Tensor<
s64[4][3]
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
]
>
iex> Nx.put_diagonal(t, Nx.tensor([1, 2, 3]))
#Nx.Tensor<
s64[4][3]
[
[1, 0, 0],
[0, 2, 0],
[0, 0, 3],
[0, 0, 0]
]
>
Given a 2D tensor and a 1D diagonal with a positive offset:
iex> Nx.put_diagonal(Nx.broadcast(0, {4, 4}), Nx.tensor([1, 2, 3]), offset: 1)
#Nx.Tensor<
s64[4][4]
[
[0, 1, 0, 0],
[0, 0, 2, 0],
[0, 0, 0, 3],
[0, 0, 0, 0]
]
>
iex> Nx.put_diagonal(Nx.broadcast(0, {4, 3}), Nx.tensor([1, 2]), offset: 1)
#Nx.Tensor<
s64[4][3]
[
[0, 1, 0],
[0, 0, 2],
[0, 0, 0],
[0, 0, 0]
]
>
Given a 2D tensor and a 1D diagonal with a negative offset:
iex> Nx.put_diagonal(Nx.broadcast(0, {4, 4}), Nx.tensor([1, 2, 3]), offset: -1)
#Nx.Tensor<
s64[4][4]
[
[0, 0, 0, 0],
[1, 0, 0, 0],
[0, 2, 0, 0],
[0, 0, 3, 0]
]
>
iex> Nx.put_diagonal(Nx.broadcast(0, {4, 3}), Nx.tensor([1, 2, 3]), offset: -1)
#Nx.Tensor<
s64[4][3]
[
[0, 0, 0],
[1, 0, 0],
[0, 2, 0],
[0, 0, 3]
]
>
options
Options
:offset
- offset used for putting the diagonal. Use offset > 0 for diagonals above the main diagonal, and offset < 0 for diagonals below the main diagonal. Defaults to 0.
error-cases
Error cases
Given an invalid tensor:
iex> Nx.put_diagonal(Nx.iota({3, 3, 3}), Nx.iota({3}))
** (ArgumentError) put_diagonal/3 expects tensor of rank 2, got tensor of rank: 3
Given invalid diagonals:
iex> Nx.put_diagonal(Nx.iota({3, 3}), Nx.iota({3, 3}))
** (ArgumentError) put_diagonal/3 expects diagonal of rank 1, got tensor of rank: 2
iex> Nx.put_diagonal(Nx.iota({3, 3}), Nx.iota({2}))
** (ArgumentError) expected diagonal tensor of length: 3, got diagonal tensor of length: 2
iex> Nx.put_diagonal(Nx.iota({3, 3}), Nx.iota({3}), offset: 1)
** (ArgumentError) expected diagonal tensor of length: 2, got diagonal tensor of length: 3
Given invalid offsets:
iex> Nx.put_diagonal(Nx.iota({3, 3}), Nx.iota({3}), offset: 4)
** (ArgumentError) offset must be less than length of axis 1 when positive, got: 4
iex> Nx.put_diagonal(Nx.iota({3, 3}), Nx.iota({3}), offset: -3)
** (ArgumentError) absolute value of offset must be less than length of axis 0 when negative, got: -3
A convenient ~M
sigil for building matrices (two-dimensional tensors).
examples
Examples
Before using sigils, you must first import them:
import Nx, only: :sigils
Then you use the sigil to create matrices. The sigil:
~M<
-1 0 0 1
0 2 0 0
0 0 3 0
0 0 0 4
>
Is equivalent to:
Nx.tensor([
[-1, 0, 0, 1],
[0, 2, 0, 0],
[0, 0, 3, 0],
[0, 0, 0, 4]
])
If the tensor has any complex type, it defaults to c64. If the tensor has any float type, it defaults to f32. Otherwise, it is s64. You can specify the tensor type as a sigil modifier:
iex> import Nx, only: :sigils
iex> ~M[0.1 0.2 0.3 0.4]f16
#Nx.Tensor<
f16[1][4]
[
[0.0999755859375, 0.199951171875, 0.300048828125, 0.39990234375]
]
>
iex> ~M[1+1i 2-2.0i -3]
#Nx.Tensor<
c64[1][3]
[
[1.0+1.0i, 2.0-2.0i, -3.0+0.0i]
]
>
iex> ~M[1 Inf NaN]
#Nx.Tensor<
f32[1][3]
[
[1.0, Inf, NaN]
]
>
iex> ~M[1i Inf NaN]
#Nx.Tensor<
c64[1][3]
[
[0.0+1.0i, Inf+0.0i, NaN+0.0i]
]
>
iex> ~M[1i Inf+2i NaN-Infi]
#Nx.Tensor<
c64[1][3]
[
[0.0+1.0i, Inf+2.0i, NaN-Infi]
]
>
A convenient ~V
sigil for building vectors (one-dimensional tensors).
examples
Examples
Before using sigils, you must first import them:
import Nx, only: :sigils
Then you use the sigil to create vectors. The sigil:
~V[-1 0 0 1]
Is equivalent to:
Nx.tensor([-1, 0, 0, 1])
If the tensor has any complex type, it defaults to c64. If the tensor has any float type, it defaults to f32. Otherwise, it is s64. You can specify the tensor type as a sigil modifier:
iex> import Nx, only: :sigils
iex> ~V[0.1 0.2 0.3 0.4]f16
#Nx.Tensor<
f16[4]
[0.0999755859375, 0.199951171875, 0.300048828125, 0.39990234375]
>
iex> ~V[1+1i 2-2.0i -3]
#Nx.Tensor<
c64[3]
[1.0+1.0i, 2.0-2.0i, -3.0+0.0i]
>
iex> ~V[1 Inf NaN]
#Nx.Tensor<
f32[3]
[1.0, Inf, NaN]
>
iex> ~V[1i Inf NaN]
#Nx.Tensor<
c64[3]
[0.0+1.0i, Inf+0.0i, NaN+0.0i]
>
iex> ~V[1i Inf+2i NaN-Infi]
#Nx.Tensor<
c64[3]
[0.0+1.0i, Inf+2.0i, NaN-Infi]
>
Extracts the diagonal of batched matrices.
Converse of make_diagonal/2
.
examples
Examples
Given a matrix without offset:
iex> Nx.take_diagonal(Nx.tensor([
...> [0, 1, 2],
...> [3, 4, 5],
...> [6, 7, 8]
...> ]))
#Nx.Tensor<
s64[3]
[0, 4, 8]
>
And if given a matrix along with an offset:
iex> Nx.take_diagonal(Nx.iota({3, 3}), offset: 1)
#Nx.Tensor<
s64[2]
[1, 5]
>
iex> Nx.take_diagonal(Nx.iota({3, 3}), offset: -1)
#Nx.Tensor<
s64[2]
[3, 7]
>
Given batched matrix:
iex> Nx.take_diagonal(Nx.iota({3, 2, 2}))
#Nx.Tensor<
s64[3][2]
[
[0, 3],
[4, 7],
[8, 11]
]
>
iex> Nx.take_diagonal(Nx.iota({3, 2, 2}), offset: -1)
#Nx.Tensor<
s64[3][1]
[
[2],
[6],
[10]
]
>
options
Options
:offset
- offset used for extracting the diagonal. Use offset > 0 for diagonals above the main diagonal, and offset < 0 for diagonals below the main diagonal. Defaults to 0.
error-cases
Error cases
iex> Nx.take_diagonal(Nx.tensor([0, 1, 2]))
** (ArgumentError) take_diagonal/2 expects tensor of rank 2 or higher, got tensor of rank: 1
iex> Nx.take_diagonal(Nx.iota({3, 3}), offset: 3)
** (ArgumentError) offset must be less than length of axis 1 when positive, got: 3
iex> Nx.take_diagonal(Nx.iota({3, 3}), offset: -4)
** (ArgumentError) absolute value of offset must be less than length of axis 0 when negative, got: -4
Creates a tensor template.
You can't perform any operation on this tensor. It exists exclusively to define APIs that say a tensor with a certain type, shape, and names is expected in the future.
examples
Examples
iex> Nx.template({2, 3}, :f32)
#Nx.Tensor<
f32[2][3]
Nx.TemplateBackend
>
iex> Nx.template({2, 3}, {:f, 32}, names: [:rows, :columns])
#Nx.Tensor<
f32[rows: 2][columns: 3]
Nx.TemplateBackend
>
Although note it is impossible to perform any operation on a tensor template:
iex> t = Nx.template({2, 3}, {:f, 32}, names: [:rows, :columns])
iex> Nx.abs(t)
** (RuntimeError) cannot perform operations on a Nx.TemplateBackend tensor
To convert existing tensors to templates, use to_template/1
.
Builds a tensor.
The argument must be one of:
- a tensor
- a number (which means the tensor is scalar/zero-dimensional)
- a boolean (also scalar/zero-dimensional)
- an arbitrarily nested list of numbers and booleans
If a new tensor has to be allocated, it will be allocated in
Nx.default_backend/0
, unless the :backend
option is given,
which overrides the default one.
examples
Examples
A number returns a tensor of zero dimensions:
iex> Nx.tensor(0)
#Nx.Tensor<
s64
0
>
iex> Nx.tensor(1.0)
#Nx.Tensor<
f32
1.0
>
Giving a list returns a vector (a one-dimensional tensor):
iex> Nx.tensor([1, 2, 3])
#Nx.Tensor<
s64[3]
[1, 2, 3]
>
iex> Nx.tensor([1.2, 2.3, 3.4, 4.5])
#Nx.Tensor<
f32[4]
[1.2000000476837158, 2.299999952316284, 3.4000000953674316, 4.5]
>
The type can be explicitly given. Integers and floats bigger than the given size overflow:
iex> Nx.tensor([300, 301, 302], type: :s8)
#Nx.Tensor<
s8[3]
[44, 45, 46]
>
Mixed types give higher priority to floats:
iex> Nx.tensor([1, 2, 3.0])
#Nx.Tensor<
f32[3]
[1.0, 2.0, 3.0]
>
Boolean values are also accepted, where true
is
converted to 1
and false
to 0
, with the type
being inferred as {:u, 8}
iex> Nx.tensor(true)
#Nx.Tensor<
u8
1
>
iex> Nx.tensor(false)
#Nx.Tensor<
u8
0
>
iex> Nx.tensor([true, false])
#Nx.Tensor<
u8[2]
[1, 0]
>
Multi-dimensional tensors are also possible:
iex> Nx.tensor([[1, 2, 3], [4, 5, 6]])
#Nx.Tensor<
s64[2][3]
[
[1, 2, 3],
[4, 5, 6]
]
>
iex> Nx.tensor([[1, 2], [3, 4], [5, 6]])
#Nx.Tensor<
s64[3][2]
[
[1, 2],
[3, 4],
[5, 6]
]
>
iex> Nx.tensor([[[1, 2], [3, 4], [5, 6]], [[-1, -2], [-3, -4], [-5, -6]]])
#Nx.Tensor<
s64[2][3][2]
[
[
[1, 2],
[3, 4],
[5, 6]
],
[
[-1, -2],
[-3, -4],
[-5, -6]
]
]
>
floats-and-complex-numbers
Floats and complex numbers
Besides single-precision (32 bits), floats can also have half-precision (16) or double-precision (64):
iex> Nx.tensor([1, 2, 3], type: :f16)
#Nx.Tensor<
f16[3]
[1.0, 2.0, 3.0]
>
iex> Nx.tensor([1, 2, 3], type: :f64)
#Nx.Tensor<
f64[3]
[1.0, 2.0, 3.0]
>
Brain-floating points are also supported:
iex> Nx.tensor([1, 2, 3], type: :bf16)
#Nx.Tensor<
bf16[3]
[1.0, 2.0, 3.0]
>
In all cases, the non-finite values negative infinity (-Inf),
infinity (Inf), and "not a number" (NaN) can be represented by
the atoms :neg_infinity
, :infinity
, and :nan
respectively:
iex> Nx.tensor([:neg_infinity, :nan, :infinity])
#Nx.Tensor<
f32[3]
[-Inf, NaN, Inf]
>
Finally, complex numbers are also supported in tensors:
iex> Nx.tensor(Complex.new(1, -1))
#Nx.Tensor<
c64
1.0-1.0i
>
naming-dimensions
Naming dimensions
You can provide names for tensor dimensions. Names are atoms:
iex> Nx.tensor([[1, 2, 3], [4, 5, 6]], names: [:x, :y])
#Nx.Tensor<
s64[x: 2][y: 3]
[
[1, 2, 3],
[4, 5, 6]
]
>
Names make your code more expressive:
iex> Nx.tensor([[[1, 2, 3], [4, 5, 6], [7, 8, 9]]], names: [:batch, :height, :width])
#Nx.Tensor<
s64[batch: 1][height: 3][width: 3]
[
[
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
]
>
You can also leave dimension names as nil
:
iex> Nx.tensor([[[1, 2, 3], [4, 5, 6], [7, 8, 9]]], names: [:batch, nil, nil])
#Nx.Tensor<
s64[batch: 1][3][3]
[
[
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
]
>
However, you must provide a name for every dimension in the tensor:
iex> Nx.tensor([[[1, 2, 3], [4, 5, 6], [7, 8, 9]]], names: [:batch])
** (ArgumentError) invalid names for tensor of rank 3, when specifying names every dimension must have a name or be nil
tensors
Tensors
Tensors can also be given as inputs:
iex> Nx.tensor(Nx.tensor([1, 2, 3]))
#Nx.Tensor<
s64[3]
[1, 2, 3]
>
If the :backend
and :type
options are given, the tensor will
compared against those values and raise in case of mismatch:
iex> Nx.tensor(Nx.tensor([1, 2, 3]), type: :f32)
** (ArgumentError) Nx.tensor/2 expects a tensor with type :f32 but it was given a tensor of type {:s, 64}
The :backend
option will check only against the backend name
and not specific backend configuration such as device and client.
In case the backend differs, it will also raise.
The names in the given tensor are always discarded but Nx will raise in case the tensor already has names that conflict with the assigned ones:
iex> Nx.tensor(Nx.tensor([1, 2, 3]), names: [:row])
#Nx.Tensor<
s64[row: 3]
[1, 2, 3]
>
iex> Nx.tensor(Nx.tensor([1, 2, 3], names: [:column]))
#Nx.Tensor<
s64[3]
[1, 2, 3]
>
iex> Nx.tensor(Nx.tensor([1, 2, 3], names: [:column]), names: [:row])
** (ArgumentError) cannot merge name :column on axis 0 with name :row on axis 0
options
Options
:type
- sets the type of the tensor. If one is not given, one is automatically inferred based on the input.:names
- dimension names. If you wish to specify dimension names you must specify a name for every dimension in the tensor. Onlynil
and atoms are supported as dimension names.:backend
- the backend to allocate the tensor on. It is either an atom or a tuple in the shape{backend, options}
. It defaults toNx.default_backend/0
for new tensors
Link to this section Functions: Cumulative
Returns the cumulative maximum of elements along an axis.
options
Options
:axis
- the axis to compare elements along. Defaults to0
:reverse
- whether to perform accumulation in the opposite direction. Defaults tofalse
examples
Examples
iex> Nx.cumulative_max(Nx.tensor([3, 4, 2, 1]))
#Nx.Tensor<
s64[4]
[3, 4, 4, 4]
>
iex> Nx.cumulative_max(Nx.tensor([[2, 3, 1], [1, 3, 2], [2, 1, 3]]), axis: 0)
#Nx.Tensor<
s64[3][3]
[
[2, 3, 1],
[2, 3, 2],
[2, 3, 3]
]
>
iex> Nx.cumulative_max(Nx.tensor([[2, 3, 1], [1, 3, 2], [2, 1, 3]]), axis: 1)
#Nx.Tensor<
s64[3][3]
[
[2, 3, 3],
[1, 3, 3],
[2, 2, 3]
]
>
iex> Nx.cumulative_max(Nx.tensor([[2, 3, 1], [1, 3, 2], [2, 1, 3]]), axis: 0, reverse: true)
#Nx.Tensor<
s64[3][3]
[
[2, 3, 3],
[2, 3, 3],
[2, 1, 3]
]
>
iex> Nx.cumulative_max(Nx.tensor([[2, 3, 1], [1, 3, 2], [2, 1, 3]]), axis: 1, reverse: true)
#Nx.Tensor<
s64[3][3]
[
[3, 3, 1],
[3, 3, 2],
[3, 3, 3]
]
>
Returns the cumulative minimum of elements along an axis.
options
Options
:axis
- the axis to compare elements along. Defaults to0
:reverse
- whether to perform accumulation in the opposite direction. Defaults tofalse
examples
Examples
iex> Nx.cumulative_min(Nx.tensor([3, 4, 2, 1]))
#Nx.Tensor<
s64[4]
[3, 3, 2, 1]
>
iex> Nx.cumulative_min(Nx.tensor([[2, 3, 1], [1, 3, 2], [2, 1, 3]]), axis: 0)
#Nx.Tensor<
s64[3][3]
[
[2, 3, 1],
[1, 3, 1],
[1, 1, 1]
]
>
iex> Nx.cumulative_min(Nx.tensor([[2, 3, 1], [1, 3, 2], [2, 1, 3]]), axis: 1)
#Nx.Tensor<
s64[3][3]
[
[2, 2, 1],
[1, 1, 1],
[2, 1, 1]
]
>
iex> Nx.cumulative_min(Nx.tensor([[2, 3, 1], [1, 3, 2], [2, 1, 3]]), axis: 0, reverse: true)
#Nx.Tensor<
s64[3][3]
[
[1, 1, 1],
[1, 1, 2],
[2, 1, 3]
]
>
iex> Nx.cumulative_min(Nx.tensor([[2, 3, 1], [1, 3, 2], [2, 1, 3]]), axis: 1, reverse: true)
#Nx.Tensor<
s64[3][3]
[
[1, 1, 1],
[1, 2, 2],
[1, 1, 3]
]
>
Returns the cumulative product of elements along an axis.
options
Options
:axis
- the axis to multiply elements along. Defaults to0
:reverse
- whether to perform accumulation in the opposite direction. Defaults tofalse
examples
Examples
iex> Nx.cumulative_product(Nx.tensor([1, 2, 3, 4]))
#Nx.Tensor<
s64[4]
[1, 2, 6, 24]
>
iex> Nx.cumulative_product(Nx.iota({3, 3}), axis: 0)
#Nx.Tensor<
s64[3][3]
[
[0, 1, 2],
[0, 4, 10],
[0, 28, 80]
]
>
iex> Nx.cumulative_product(Nx.iota({3, 3}), axis: 1)
#Nx.Tensor<
s64[3][3]
[
[0, 0, 0],
[3, 12, 60],
[6, 42, 336]
]
>
iex> Nx.cumulative_product(Nx.iota({3, 3}), axis: 0, reverse: true)
#Nx.Tensor<
s64[3][3]
[
[0, 28, 80],
[18, 28, 40],
[6, 7, 8]
]
>
iex> Nx.cumulative_product(Nx.iota({3, 3}), axis: 1, reverse: true)
#Nx.Tensor<
s64[3][3]
[
[0, 2, 2],
[60, 20, 5],
[336, 56, 8]
]
>
Returns the cumulative sum of elements along an axis.
options
Options
:axis
- the axis to sum elements along. Defaults to0
:reverse
- whether to perform accumulation in the opposite direction. Defaults tofalse
examples
Examples
iex> Nx.cumulative_sum(Nx.tensor([1, 2, 3, 4]))
#Nx.Tensor<
s64[4]
[1, 3, 6, 10]
>
iex> Nx.cumulative_sum(Nx.iota({3, 3}), axis: 0)
#Nx.Tensor<
s64[3][3]
[
[0, 1, 2],
[3, 5, 7],
[9, 12, 15]
]
>
iex> Nx.cumulative_sum(Nx.iota({3, 3}), axis: 1)
#Nx.Tensor<
s64[3][3]
[
[0, 1, 3],
[3, 7, 12],
[6, 13, 21]
]
>
iex> Nx.cumulative_sum(Nx.iota({3, 3}), axis: 0, reverse: true)
#Nx.Tensor<
s64[3][3]
[
[9, 12, 15],
[9, 11, 13],
[6, 7, 8]
]
>
iex> Nx.cumulative_sum(Nx.iota({3, 3}), axis: 1, reverse: true)
#Nx.Tensor<
s64[3][3]
[
[3, 3, 2],
[12, 9, 5],
[21, 15, 8]
]
>
Link to this section Functions: Element-wise
Computes the absolute value of each element in the tensor.
examples
Examples
iex> Nx.abs(Nx.tensor([-2, -1, 0, 1, 2], names: [:x]))
#Nx.Tensor<
s64[x: 5]
[2, 1, 0, 1, 2]
>
Calculates the inverse cosine of each element in the tensor.
It is equivalent to:
$$acos(cos(z)) = z$$
examples
Examples
iex> Nx.acos(0.10000000149011612)
#Nx.Tensor<
f32
1.4706288576126099
>
iex> Nx.acos(Nx.tensor([0.10000000149011612, 0.5, 0.8999999761581421], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[1.4706288576126099, 1.0471975803375244, 0.4510268568992615]
>
Calculates the inverse hyperbolic cosine of each element in the tensor.
It is equivalent to:
$$acosh(cosh(z)) = z$$
examples
Examples
iex> Nx.acosh(1)
#Nx.Tensor<
f32
0.0
>
iex> Nx.acosh(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.0, 1.316957950592041, 1.7627471685409546]
>
Element-wise addition of two tensors.
If a number is given, it is converted to a tensor.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the +
operator
in place of this function: left + right
.
examples
Examples
adding-scalars
Adding scalars
iex> Nx.add(1, 2)
#Nx.Tensor<
s64
3
>
iex> Nx.add(1, 2.2)
#Nx.Tensor<
f32
3.200000047683716
>
adding-a-scalar-to-a-tensor
Adding a scalar to a tensor
iex> Nx.add(Nx.tensor([1, 2, 3], names: [:data]), 1)
#Nx.Tensor<
s64[data: 3]
[2, 3, 4]
>
iex> Nx.add(1, Nx.tensor([1, 2, 3], names: [:data]))
#Nx.Tensor<
s64[data: 3]
[2, 3, 4]
>
Given a float scalar converts the tensor to a float:
iex> Nx.add(Nx.tensor([1, 2, 3], names: [:data]), 1.0)
#Nx.Tensor<
f32[data: 3]
[2.0, 3.0, 4.0]
>
iex> Nx.add(Nx.tensor([1.0, 2.0, 3.0], names: [:data]), 1)
#Nx.Tensor<
f32[data: 3]
[2.0, 3.0, 4.0]
>
iex> Nx.add(Nx.tensor([1.0, 2.0, 3.0], type: :f32, names: [:data]), 1)
#Nx.Tensor<
f32[data: 3]
[2.0, 3.0, 4.0]
>
Unsigned tensors become signed and double their size if a negative number is given:
iex> Nx.add(Nx.tensor([0, 1, 2], type: :u8, names: [:data]), -1)
#Nx.Tensor<
s16[data: 3]
[-1, 0, 1]
>
adding-tensors-of-the-same-shape
Adding tensors of the same shape
iex> left = Nx.tensor([[1, 2], [3, 4]], names: [:x, :y])
iex> right = Nx.tensor([[10, 20], [30, 40]], names: [nil, :y])
iex> Nx.add(left, right)
#Nx.Tensor<
s64[x: 2][y: 2]
[
[11, 22],
[33, 44]
]
>
adding-tensors-with-broadcasting
Adding tensors with broadcasting
iex> left = Nx.tensor([[1], [2]], names: [nil, :y])
iex> right = Nx.tensor([[10, 20]], names: [:x, nil])
iex> Nx.add(left, right)
#Nx.Tensor<
s64[x: 2][y: 2]
[
[11, 21],
[12, 22]
]
>
iex> left = Nx.tensor([[10, 20]], names: [:x, nil])
iex> right = Nx.tensor([[1], [2]], names: [nil, :y])
iex> Nx.add(left, right)
#Nx.Tensor<
s64[x: 2][y: 2]
[
[11, 21],
[12, 22]
]
>
iex> left = Nx.tensor([[1], [2]], names: [:x, nil])
iex> right = Nx.tensor([[10, 20], [30, 40]])
iex> Nx.add(left, right)
#Nx.Tensor<
s64[x: 2][2]
[
[11, 21],
[32, 42]
]
>
iex> left = Nx.tensor([[1, 2]])
iex> right = Nx.tensor([[10, 20], [30, 40]])
iex> Nx.add(left, right)
#Nx.Tensor<
s64[2][2]
[
[11, 22],
[31, 42]
]
>
Calculates the inverse sine of each element in the tensor.
It is equivalent to:
$$asin(sin(z)) = z$$
examples
Examples
iex> Nx.asin(0.10000000149011612)
#Nx.Tensor<
f32
0.1001674234867096
>
iex> Nx.asin(Nx.tensor([0.10000000149011612, 0.5, 0.8999999761581421], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.1001674234867096, 0.5235987901687622, 1.1197694540023804]
>
Calculates the inverse hyperbolic sine of each element in the tensor.
It is equivalent to:
$$asinh(sinh(z)) = z$$
examples
Examples
iex> Nx.asinh(1)
#Nx.Tensor<
f32
0.8813735842704773
>
iex> Nx.asinh(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.8813735842704773, 1.4436354637145996, 1.8184465169906616]
>
Element-wise arc tangent of two tensors.
If a number is given, it is converted to a tensor.
It always returns a float tensor. If any of the input tensors are not float, they are converted to f32.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
examples
Examples
arc-tangent-between-scalars
Arc tangent between scalars
iex> Nx.atan2(1, 2)
#Nx.Tensor<
f32
0.46364760398864746
>
arc-tangent-between-tensors-and-scalars
Arc tangent between tensors and scalars
iex> Nx.atan2(Nx.tensor([1, 2, 3], names: [:data]), 1)
#Nx.Tensor<
f32[data: 3]
[0.7853981852531433, 1.1071487665176392, 1.249045729637146]
>
iex> Nx.atan2(1, Nx.tensor([1.0, 2.0, 3.0], names: [:data]))
#Nx.Tensor<
f32[data: 3]
[0.7853981852531433, 0.46364760398864746, 0.32175055146217346]
>
arc-tangent-between-tensors
Arc tangent between tensors
iex> neg_and_pos_zero_columns = Nx.tensor([[-0.0], [0.0]], type: :f64)
iex> neg_and_pos_zero_rows = Nx.tensor([-0.0, 0.0], type: :f64)
iex> Nx.atan2(neg_and_pos_zero_columns, neg_and_pos_zero_rows)
#Nx.Tensor<
f64[2][2]
[
[-3.141592653589793, -0.0],
[3.141592653589793, 0.0]
]
>
Calculates the inverse tangent of each element in the tensor.
It is equivalent to:
$$atan(tan(z)) = z$$
examples
Examples
iex> Nx.atan(0.10000000149011612)
#Nx.Tensor<
f32
0.09966865181922913
>
iex> Nx.atan(Nx.tensor([0.10000000149011612, 0.5, 0.8999999761581421], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.09966865181922913, 0.46364760398864746, 0.7328150868415833]
>
Calculates the inverse hyperbolic tangent of each element in the tensor.
It is equivalent to:
$$atanh(tanh(z)) = z$$
examples
Examples
iex> Nx.atanh(0.10000000149011612)
#Nx.Tensor<
f32
0.10033535212278366
>
iex> Nx.atanh(Nx.tensor([0.10000000149011612, 0.5, 0.8999999761581421], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.10033535212278366, 0.5493061542510986, 1.4722193479537964]
>
Element-wise bitwise AND of two tensors.
Only integer tensors are supported. If a float or complex tensor is given, an error is raised.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the &&&
operator
in place of this function: left &&& right
.
examples
Examples
bitwise-and-between-scalars
bitwise and between scalars
iex> Nx.bitwise_and(1, 0)
#Nx.Tensor<
s64
0
>
bitwise-and-between-tensors-and-scalars
bitwise and between tensors and scalars
iex> Nx.bitwise_and(Nx.tensor([0, 1, 2], names: [:data]), 1)
#Nx.Tensor<
s64[data: 3]
[0, 1, 0]
>
iex> Nx.bitwise_and(Nx.tensor([0, -1, -2], names: [:data]), -1)
#Nx.Tensor<
s64[data: 3]
[0, -1, -2]
>
bitwise-and-between-tensors
bitwise and between tensors
iex> Nx.bitwise_and(Nx.tensor([0, 0, 1, 1], names: [:data]), Nx.tensor([0, 1, 0, 1]))
#Nx.Tensor<
s64[data: 4]
[0, 0, 0, 1]
>
error-cases
Error cases
iex> Nx.bitwise_and(Nx.tensor([0, 0, 1, 1]), 1.0)
** (ArgumentError) bitwise operators expect integer tensors as inputs and outputs an integer tensor, got: {:f, 32}
Applies bitwise not to each element in the tensor.
If you're using Nx.Defn.defn/2
, you can use the ~~~
operator
in place of this function: ~~~tensor
.
examples
Examples
iex> Nx.bitwise_not(1)
#Nx.Tensor<
s64
-2
>
iex> Nx.bitwise_not(Nx.tensor([-1, 0, 1], type: :s8, names: [:x]))
#Nx.Tensor<
s8[x: 3]
[0, -1, -2]
>
iex> Nx.bitwise_not(Nx.tensor([0, 1, 254, 255], type: :u8, names: [:x]))
#Nx.Tensor<
u8[x: 4]
[255, 254, 1, 0]
>
error-cases
Error cases
iex> Nx.bitwise_not(Nx.tensor([0.0, 1.0]))
** (ArgumentError) bitwise operators expect integer tensors as inputs and outputs an integer tensor, got: {:f, 32}
Element-wise bitwise OR of two tensors.
Only integer tensors are supported. If a float or complex tensor is given, an error is raised.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the |||
operator
in place of this function: left ||| right
.
examples
Examples
bitwise-or-between-scalars
bitwise or between scalars
iex> Nx.bitwise_or(1, 0)
#Nx.Tensor<
s64
1
>
bitwise-or-between-tensors-and-scalars
bitwise or between tensors and scalars
iex> Nx.bitwise_or(Nx.tensor([0, 1, 2], names: [:data]), 1)
#Nx.Tensor<
s64[data: 3]
[1, 1, 3]
>
iex> Nx.bitwise_or(Nx.tensor([0, -1, -2], names: [:data]), -1)
#Nx.Tensor<
s64[data: 3]
[-1, -1, -1]
>
bitwise-or-between-tensors
bitwise or between tensors
iex> Nx.bitwise_or(Nx.tensor([0, 0, 1, 1], names: [:data]), Nx.tensor([0, 1, 0, 1], names: [:data]))
#Nx.Tensor<
s64[data: 4]
[0, 1, 1, 1]
>
error-cases
Error cases
iex> Nx.bitwise_or(Nx.tensor([0, 0, 1, 1]), 1.0)
** (ArgumentError) bitwise operators expect integer tensors as inputs and outputs an integer tensor, got: {:f, 32}
Element-wise bitwise XOR of two tensors.
Only integer tensors are supported. If a float or complex tensor is given, an error is raised.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
examples
Examples
bitwise-xor-between-scalars
Bitwise xor between scalars
iex> Nx.bitwise_xor(1, 0)
#Nx.Tensor<
s64
1
>
bitwise-xor-and-between-tensors-and-scalars
Bitwise xor and between tensors and scalars
iex> Nx.bitwise_xor(Nx.tensor([1, 2, 3], names: [:data]), 2)
#Nx.Tensor<
s64[data: 3]
[3, 0, 1]
>
iex> Nx.bitwise_xor(Nx.tensor([-1, -2, -3], names: [:data]), 2)
#Nx.Tensor<
s64[data: 3]
[-3, -4, -1]
>
bitwise-xor-between-tensors
Bitwise xor between tensors
iex> Nx.bitwise_xor(Nx.tensor([0, 0, 1, 1]), Nx.tensor([0, 1, 0, 1], names: [:data]))
#Nx.Tensor<
s64[data: 4]
[0, 1, 1, 0]
>
error-cases
Error cases
iex> Nx.bitwise_xor(Nx.tensor([0, 0, 1, 1]), 1.0)
** (ArgumentError) bitwise operators expect integer tensors as inputs and outputs an integer tensor, got: {:f, 32}
Calculates the cube root of each element in the tensor.
It is equivalent to:
$$cbrt(z) = z^{\frac{1}{3}}$$
examples
Examples
iex> Nx.cbrt(1)
#Nx.Tensor<
f32
1.0
>
iex> Nx.cbrt(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[1.0, 1.2599210739135742, 1.4422495365142822]
>
Calculates the ceil of each element in the tensor.
If a non-floating tensor is given, it is returned as is. If a floating tensor is given, then we apply the operation, but keep its type.
examples
Examples
iex> Nx.ceil(Nx.tensor([-1, 0, 1], names: [:x]))
#Nx.Tensor<
s64[x: 3]
[-1, 0, 1]
>
iex> Nx.ceil(Nx.tensor([-1.5, -0.5, 0.5, 1.5], names: [:x]))
#Nx.Tensor<
f32[x: 4]
[-1.0, 0.0, 1.0, 2.0]
>
Clips the values of the tensor on the closed
interval [min, max]
.
You can pass a tensor to min
or max
as long
as the tensor has a scalar shape.
examples
Examples
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6]], names: [:x, :y])
iex> Nx.clip(t, 2, 4)
#Nx.Tensor<
s64[x: 2][y: 3]
[
[2, 2, 3],
[4, 4, 4]
]
>
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6]], names: [:x, :y])
iex> Nx.clip(t, 2.0, 3)
#Nx.Tensor<
f32[x: 2][y: 3]
[
[2.0, 2.0, 3.0],
[3.0, 3.0, 3.0]
]
>
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6]], names: [:x, :y])
iex> Nx.clip(t, Nx.tensor(2.0), Nx.max(1.0, 3.0))
#Nx.Tensor<
f32[x: 2][y: 3]
[
[2.0, 2.0, 3.0],
[3.0, 3.0, 3.0]
]
>
iex> t = Nx.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], names: [:x, :y])
iex> Nx.clip(t, 2, 6.0)
#Nx.Tensor<
f32[x: 2][y: 3]
[
[2.0, 2.0, 3.0],
[4.0, 5.0, 6.0]
]
>
iex> t = Nx.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], type: :f32, names: [:x, :y])
iex> Nx.clip(t, 1, 4)
#Nx.Tensor<
f32[x: 2][y: 3]
[
[1.0, 2.0, 3.0],
[4.0, 4.0, 4.0]
]
>
Constructs a complex tensor from two equally-shaped tensors.
Does not accept complex tensors as inputs.
examples
Examples
iex> Nx.complex(Nx.tensor(1), Nx.tensor(2))
#Nx.Tensor<
c64
1.0+2.0i
>
iex> Nx.complex(Nx.tensor([1, 2]), Nx.tensor([3, 4]))
#Nx.Tensor<
c64[2]
[1.0+3.0i, 2.0+4.0i]
>
Calculates the complex conjugate of each element in the tensor.
If $$z = a + bi = r e^\theta$$, $$conjugate(z) = z^* = a - bi = r e^{-\theta}$$
examples
Examples
iex> Nx.conjugate(Complex.new(1, 2))
#Nx.Tensor<
c64
1.0-2.0i
>
iex> Nx.conjugate(1)
#Nx.Tensor<
c64
1.0+0.0i
>
iex> Nx.conjugate(Nx.tensor([Complex.new(1, 2), Complex.new(2, -4)]))
#Nx.Tensor<
c64[2]
[1.0-2.0i, 2.0+4.0i]
>
Calculates the cosine of each element in the tensor.
It is equivalent to:
$$cos(z) = \frac{e^{iz} + e^{-iz}}{2}$$
examples
Examples
iex> Nx.cos(1)
#Nx.Tensor<
f32
0.5403022766113281
>
iex> Nx.cos(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.5403022766113281, -0.416146844625473, -0.9899924993515015]
>
Calculates the hyperbolic cosine of each element in the tensor.
It is equivalent to:
$$cosh(z) = \frac{e^z + e^{-z}}{2}$$
examples
Examples
iex> Nx.cosh(1)
#Nx.Tensor<
f32
1.5430806875228882
>
iex> Nx.cosh(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[1.5430806875228882, 3.762195587158203, 10.067662239074707]
>
Counts the number of leading zeros of each element in the tensor.
examples
Examples
iex> Nx.count_leading_zeros(1)
#Nx.Tensor<
s64
63
>
iex> Nx.count_leading_zeros(-1)
#Nx.Tensor<
s64
0
>
iex> Nx.count_leading_zeros(Nx.tensor([0, 0xF, 0xFF, 0xFFFF], names: [:x]))
#Nx.Tensor<
s64[x: 4]
[64, 60, 56, 48]
>
iex> Nx.count_leading_zeros(Nx.tensor([0xF000000000000000, 0x0F00000000000000], names: [:x]))
#Nx.Tensor<
s64[x: 2]
[0, 4]
>
iex> Nx.count_leading_zeros(Nx.tensor([0, 0xF, 0xFF, 0xFFFF], type: :s32, names: [:x]))
#Nx.Tensor<
s32[x: 4]
[32, 28, 24, 16]
>
iex> Nx.count_leading_zeros(Nx.tensor([0, 0xF, 0xFF, 0xFFFF], type: :s16, names: [:x]))
#Nx.Tensor<
s16[x: 4]
[16, 12, 8, 0]
>
iex> Nx.count_leading_zeros(Nx.tensor([0, 1, 2, 4, 8, 16, 32, 64, -1, -128], type: :s8, names: [:x]))
#Nx.Tensor<
s8[x: 10]
[8, 7, 6, 5, 4, 3, 2, 1, 0, 0]
>
iex> Nx.count_leading_zeros(Nx.tensor([0, 1, 2, 4, 8, 16, 32, 64, 128], type: :u8, names: [:x]))
#Nx.Tensor<
u8[x: 9]
[8, 7, 6, 5, 4, 3, 2, 1, 0]
>
error-cases
Error cases
iex> Nx.count_leading_zeros(Nx.tensor([0.0, 1.0]))
** (ArgumentError) bitwise operators expect integer tensors as inputs and outputs an integer tensor, got: {:f, 32}
Element-wise division of two tensors.
If a number is given, it is converted to a tensor.
It always returns a float tensor. If any of the input tensors are not float, they are converted to f32. Division by zero raises, but it may trigger undefined behaviour on some compilers.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the /
operator
in place of this function: left / right
.
examples
Examples
dividing-scalars
Dividing scalars
iex> Nx.divide(1, 2)
#Nx.Tensor<
f32
0.5
>
dividing-tensors-and-scalars
Dividing tensors and scalars
iex> Nx.divide(Nx.tensor([1, 2, 3], names: [:data]), 1)
#Nx.Tensor<
f32[data: 3]
[1.0, 2.0, 3.0]
>
iex> Nx.divide(1, Nx.tensor([1.0, 2.0, 3.0], names: [:data]))
#Nx.Tensor<
f32[data: 3]
[1.0, 0.5, 0.3333333432674408]
>
dividing-tensors
Dividing tensors
iex> left = Nx.tensor([[1], [2]], names: [:x, nil])
iex> right = Nx.tensor([[10, 20]], names: [nil, :y])
iex> Nx.divide(left, right)
#Nx.Tensor<
f32[x: 2][y: 2]
[
[0.10000000149011612, 0.05000000074505806],
[0.20000000298023224, 0.10000000149011612]
]
>
iex> left = Nx.tensor([[1], [2]], type: :s8)
iex> right = Nx.tensor([[10, 20]], type: :s8, names: [:x, :y])
iex> Nx.divide(left, right)
#Nx.Tensor<
f32[x: 2][y: 2]
[
[0.10000000149011612, 0.05000000074505806],
[0.20000000298023224, 0.10000000149011612]
]
>
iex> left = Nx.tensor([[1], [2]], type: :f32, names: [:x, nil])
iex> right = Nx.tensor([[10, 20]], type: :f32, names: [nil, :y])
iex> Nx.divide(left, right)
#Nx.Tensor<
f32[x: 2][y: 2]
[
[0.10000000149011612, 0.05000000074505806],
[0.20000000298023224, 0.10000000149011612]
]
>
Element-wise equality comparison of two tensors.
If a number is given, it is converted to a tensor.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the ==
operator
in place of this function: left == right
.
examples
Examples
comparison-of-scalars
Comparison of scalars
iex> Nx.equal(1, 2)
#Nx.Tensor<
u8
0
>
comparison-of-tensors-and-scalars
Comparison of tensors and scalars
iex> Nx.equal(1, Nx.tensor([1, 2, 3], names: [:data]))
#Nx.Tensor<
u8[data: 3]
[1, 0, 0]
>
comparison-of-tensors
Comparison of tensors
iex> left = Nx.tensor([1, 2, 3], names: [:data])
iex> right = Nx.tensor([1, 2, 5])
iex> Nx.equal(left, right)
#Nx.Tensor<
u8[data: 3]
[1, 1, 0]
>
iex> left = Nx.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], names: [:x, nil])
iex> right = Nx.tensor([1, 2, 3])
iex> Nx.equal(left, right)
#Nx.Tensor<
u8[x: 2][3]
[
[1, 1, 1],
[0, 0, 0]
]
>
Calculates the error function of each element in the tensor.
It is equivalent to:
$$erf(z) = \frac{2}{\sqrt{\pi}} \int_{0}^{z} e^{-t^2}dt$$
examples
Examples
iex> Nx.erf(1)
#Nx.Tensor<
f32
0.8427007794380188
>
iex> Nx.erf(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.8427007794380188, 0.9953222870826721, 0.9999778866767883]
>
Calculates the inverse error function of each element in the tensor.
It is equivalent to:
$$erf\text{\textunderscore}inv(erf(z)) = z$$
examples
Examples
iex> Nx.erf_inv(0.10000000149011612)
#Nx.Tensor<
f32
0.08885598927736282
>
iex> Nx.erf_inv(Nx.tensor([0.10000000149011612, 0.5, 0.8999999761581421], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.08885598927736282, 0.4769362807273865, 1.163087010383606]
>
Calculates the one minus error function of each element in the tensor.
It is equivalent to:
$$erfc(z) = 1 - erf(z)$$
examples
Examples
iex> Nx.erfc(1)
#Nx.Tensor<
f32
0.15729920566082
>
iex> Nx.erfc(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.15729920566082, 0.004677734803408384, 2.2090496713644825e-5]
>
Calculates the exponential of each element in the tensor.
It is equivalent to:
$$exp(z) = e^z$$
examples
Examples
iex> Nx.exp(1)
#Nx.Tensor<
f32
2.7182817459106445
>
iex> Nx.exp(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[2.7182817459106445, 7.389056205749512, 20.08553695678711]
>
Calculates the exponential minus one of each element in the tensor.
It is equivalent to:
$$expm1(z) = e^z - 1$$
examples
Examples
iex> Nx.expm1(1)
#Nx.Tensor<
f32
1.718281865119934
>
iex> Nx.expm1(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[1.718281865119934, 6.389056205749512, 19.08553695678711]
>
Calculates the floor of each element in the tensor.
If a non-floating tensor is given, it is returned as is. If a floating tensor is given, then we apply the operation, but keep its type.
examples
Examples
iex> Nx.floor(Nx.tensor([-1, 0, 1], names: [:x]))
#Nx.Tensor<
s64[x: 3]
[-1, 0, 1]
>
iex> Nx.floor(Nx.tensor([-1.5, -0.5, 0.5, 1.5], names: [:x]))
#Nx.Tensor<
f32[x: 4]
[-2.0, -1.0, 0.0, 1.0]
>
Element-wise greater than comparison of two tensors.
If a number is given, it is converted to a tensor.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the >
operator
in place of this function: left > right
.
examples
Examples
comparison-of-scalars
Comparison of scalars
iex> Nx.greater(1, 2)
#Nx.Tensor<
u8
0
>
comparison-of-tensors-and-scalars
Comparison of tensors and scalars
iex> Nx.greater(1, Nx.tensor([1, 2, 3], names: [:data]))
#Nx.Tensor<
u8[data: 3]
[0, 0, 0]
>
comparison-of-tensors
Comparison of tensors
iex> left = Nx.tensor([1, 2, 3], names: [:data])
iex> right = Nx.tensor([1, 2, 2])
iex> Nx.greater(left, right)
#Nx.Tensor<
u8[data: 3]
[0, 0, 1]
>
iex> left = Nx.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], names: [:x, :y])
iex> right = Nx.tensor([1, 2, 3])
iex> Nx.greater(left, right)
#Nx.Tensor<
u8[x: 2][y: 3]
[
[0, 0, 0],
[1, 1, 1]
]
>
Element-wise greater than or equal comparison of two tensors.
If a number is given, it is converted to a tensor.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the >=
operator
in place of this function: left >= right
.
examples
Examples
comparison-of-scalars
Comparison of scalars
iex> Nx.greater_equal(1, 2)
#Nx.Tensor<
u8
0
>
comparison-of-tensors-and-scalars
Comparison of tensors and scalars
iex> Nx.greater_equal(1, Nx.tensor([1, 2, 3], names: [:data]))
#Nx.Tensor<
u8[data: 3]
[1, 0, 0]
>
comparison-of-tensors
Comparison of tensors
iex> left = Nx.tensor([1, 2, 3], names: [:data])
iex> right = Nx.tensor([1, 2, 2])
iex> Nx.greater_equal(left, right)
#Nx.Tensor<
u8[data: 3]
[1, 1, 1]
>
iex> left = Nx.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]], names: [:x, :y])
iex> right = Nx.tensor([1, 2, 3])
iex> Nx.greater_equal(left, right)
#Nx.Tensor<
u8[x: 2][y: 3]
[
[1, 1, 1],
[1, 1, 1]
]
>
Returns the imaginary component of each entry in a complex tensor as a floating point tensor.
examples
Examples
iex> Nx.imag(Complex.new(1, 2))
#Nx.Tensor<
f32
2.0
>
iex> Nx.imag(Nx.tensor(1))
#Nx.Tensor<
f32
0.0
>
iex> Nx.imag(Nx.tensor(1, type: :bf16))
#Nx.Tensor<
bf16
0.0
>
iex> Nx.imag(Nx.tensor([Complex.new(1, 2), Complex.new(2, -4)]))
#Nx.Tensor<
f32[2]
[2.0, -4.0]
>
Determines if each element in tensor
is Inf
or -Inf
.
For complex tensors, if either of the components is infinity, the entry is deemed infinity as well.
examples
Examples
iex> Nx.is_infinity(Nx.tensor([:infinity, :nan, :neg_infinity, 1, 0]))
#Nx.Tensor<
u8[5]
[1, 0, 1, 0, 0]
>
iex> Nx.is_infinity(Nx.tensor([:infinity, 1, Complex.new(0, :infinity), :neg_infinity]))
#Nx.Tensor<
u8[4]
[1, 0, 1, 1]
>
iex> Nx.is_infinity(Nx.tensor([1, 0]))
#Nx.Tensor<
u8[2]
[0, 0]
>
Determines if each element in tensor
is a NaN
.
For complex tensors, if either of the components is NaN
,
the entry is deemed NaN
as well.
examples
Examples
iex> Nx.is_nan(Nx.tensor([:nan, 1, 0]))
#Nx.Tensor<
u8[3]
[1, 0, 0]
>
iex> Nx.is_nan(Nx.tensor([:nan, :infinity, Complex.new(0, :nan)]))
#Nx.Tensor<
u8[3]
[1, 0, 1]
>
iex> Nx.is_nan(Nx.tensor([1, 0]))
#Nx.Tensor<
u8[2]
[0, 0]
>
Element-wise left shift of two tensors.
Only integer tensors are supported. If a float or complex tensor is given, an error is raised. If the right side is negative, it will raise, but it may trigger undefined behaviour on some compilers.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible. If the number of shifts are negative, Nx's default backend will raise, but it may trigger undefined behaviour in other backends.
If you're using Nx.Defn.defn/2
, you can use the <<<
operator
in place of this function: left <<< right
.
examples
Examples
left-shift-between-scalars
Left shift between scalars
iex> Nx.left_shift(1, 0)
#Nx.Tensor<
s64
1
>
left-shift-between-tensors-and-scalars
Left shift between tensors and scalars
iex> Nx.left_shift(Nx.tensor([1, 2, 3], names: [:data]), 2)
#Nx.Tensor<
s64[data: 3]
[4, 8, 12]
>
left-shift-between-tensors
Left shift between tensors
iex> left = Nx.tensor([1, 1, -1, -1], names: [:data])
iex> right = Nx.tensor([1, 2, 3, 4], names: [:data])
iex> Nx.left_shift(left, right)
#Nx.Tensor<
s64[data: 4]
[2, 4, -8, -16]
>
error-cases
Error cases
iex> Nx.left_shift(Nx.tensor([0, 0, 1, 1]), 1.0)
** (ArgumentError) bitwise operators expect integer tensors as inputs and outputs an integer tensor, got: {:f, 32}
Element-wise less than comparison of two tensors.
If a number is given, it is converted to a tensor.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the <
operator
in place of this function: left < right
.
examples
Examples
comparison-of-scalars
Comparison of scalars
iex> Nx.less(1, 2)
#Nx.Tensor<
u8
1
>
comparison-of-tensors-and-scalars
Comparison of tensors and scalars
iex> Nx.less(1, Nx.tensor([1, 2, 3], names: [:data]))
#Nx.Tensor<
u8[data: 3]
[0, 1, 1]
>
comparison-of-tensors
Comparison of tensors
iex> Nx.less(Nx.tensor([1, 2, 1]), Nx.tensor([1, 2, 2], names: [:data]))
#Nx.Tensor<
u8[data: 3]
[0, 0, 1]
>
iex> Nx.less(Nx.tensor([[1.0, 2.0, 3.0], [4.0, 2.0, 1.0]], names: [:x, :y]), Nx.tensor([1, 2, 3]))
#Nx.Tensor<
u8[x: 2][y: 3]
[
[0, 0, 0],
[0, 0, 1]
]
>
Element-wise less than or equal comparison of two tensors.
If a number is given, it is converted to a tensor.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the <=
operator
in place of this function: left <= right
.
examples
Examples
comparison-of-scalars
Comparison of scalars
iex> Nx.less_equal(1, 2)
#Nx.Tensor<
u8
1
>
comparison-of-tensors-and-scalars
Comparison of tensors and scalars
iex> Nx.less_equal(1, Nx.tensor([1, 2, 3], names: [:data]))
#Nx.Tensor<
u8[data: 3]
[1, 1, 1]
>
comparison-of-tensors
Comparison of tensors
iex> left = Nx.tensor([1, 2, 3], names: [:data])
iex> right = Nx.tensor([1, 2, 2])
iex> Nx.less_equal(left, right)
#Nx.Tensor<
u8[data: 3]
[1, 1, 0]
>
iex> left = Nx.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
iex> right = Nx.tensor([1, 2, 3], names: [:y])
iex> Nx.less_equal(left, right)
#Nx.Tensor<
u8[2][y: 3]
[
[1, 1, 1],
[0, 0, 0]
]
>
Calculates the natural log plus one of each element in the tensor.
It is equivalent to:
$$log1p(z) = log(z + 1)$$
examples
Examples
iex> Nx.log1p(1)
#Nx.Tensor<
f32
0.6931471824645996
>
iex> Nx.log1p(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.6931471824645996, 1.0986123085021973, 1.3862943649291992]
>
Calculates the natural log of each element in the tensor.
It is equivalent to:
$$log(z) = ln(z),\quad \text{if z} \in \Reals$$
$$log(z) = ln(r) + i\theta,\quad\text{if }z = re^{i\theta} \in \Complex$$
examples
Examples
iex> Nx.log(1)
#Nx.Tensor<
f32
0.0
>
iex> Nx.log(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.0, 0.6931471824645996, 1.0986123085021973]
>
Element-wise logical and of two tensors.
Zero is considered false, any other number is considered true.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the and
operator
in place of this function: left and right
.
examples
Examples
iex> Nx.logical_and(1, Nx.tensor([-1, 0, 1], names: [:data]))
#Nx.Tensor<
u8[data: 3]
[1, 0, 1]
>
iex> left = Nx.tensor([-1, 0, 1], names: [:data])
iex> right = Nx.tensor([[-1], [0], [1]])
iex> Nx.logical_and(left, right)
#Nx.Tensor<
u8[3][data: 3]
[
[1, 0, 1],
[0, 0, 0],
[1, 0, 1]
]
>
iex> left = Nx.tensor([-1.0, 0.0, 1.0], names: [:data])
iex> right = Nx.tensor([[-1], [0], [1]])
iex> Nx.logical_and(left, right)
#Nx.Tensor<
u8[3][data: 3]
[
[1, 0, 1],
[0, 0, 0],
[1, 0, 1]
]
>
Element-wise logical not a tensor.
Zero is considered false, any other number is considered true.
If you're using Nx.Defn.defn/2
, you can use the not
operator
in place of this function: not tensor
.
examples
Examples
iex> Nx.logical_not(Nx.tensor([-1, 0, 1], names: [:data]))
#Nx.Tensor<
u8[data: 3]
[0, 1, 0]
>
iex> Nx.logical_not(Nx.tensor([-1.0, 0.0, 1.0], names: [:data]))
#Nx.Tensor<
u8[data: 3]
[0, 1, 0]
>
Element-wise logical or of two tensors.
Zero is considered false, any other number is considered true.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the or
operator
in place of this function: left or right
.
examples
Examples
iex> Nx.logical_or(0, Nx.tensor([-1, 0, 1], names: [:data]))
#Nx.Tensor<
u8[data: 3]
[1, 0, 1]
>
iex> left = Nx.tensor([-1, 0, 1], names: [:data])
iex> right = Nx.tensor([[-1], [0], [1]])
iex> Nx.logical_or(left, right)
#Nx.Tensor<
u8[3][data: 3]
[
[1, 1, 1],
[1, 0, 1],
[1, 1, 1]
]
>
iex> left = Nx.tensor([-1.0, 0.0, 1.0], names: [:data])
iex> right = Nx.tensor([[-1], [0], [1]])
iex> Nx.logical_or(left, right)
#Nx.Tensor<
u8[3][data: 3]
[
[1, 1, 1],
[1, 0, 1],
[1, 1, 1]
]
>
Element-wise logical xor of two tensors.
Zero is considered false, any other number is considered true.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
examples
Examples
iex> Nx.logical_xor(0, Nx.tensor([-1, 0, 1], names: [:data]))
#Nx.Tensor<
u8[data: 3]
[1, 0, 1]
>
iex> left = Nx.tensor([-1, 0, 1], names: [:data])
iex> right = Nx.tensor([[-1], [0], [1]])
iex> Nx.logical_xor(left, right)
#Nx.Tensor<
u8[3][data: 3]
[
[0, 1, 0],
[1, 0, 1],
[0, 1, 0]
]
>
iex> left = Nx.tensor([-1.0, 0.0, 1.0], names: [:data])
iex> right = Nx.tensor([[-1], [0], [1]])
iex> Nx.logical_xor(left, right)
#Nx.Tensor<
u8[3][data: 3]
[
[0, 1, 0],
[1, 0, 1],
[0, 1, 0]
]
>
Maps the given scalar function over the entire tensor.
The type of the returned tensor will be of the same type
as the input tensor, unless the :type
option is given.
Therefore, you may need to explicitly cast the tensor to
avoid errors. For example, if you have an integer tensor
and you convert it to a float, as below, it will fail:
tensor = Nx.tensor([[1, 2, 3], [4, 5, 6]]),
Nx.map(tensor, fn x -> Nx.multiply(x, 1.0) end)
You need to explicitly pass the output type in such cases:
iex> tensor = Nx.tensor([[1, 2, 3], [4, 5, 6]])
iex> Nx.map(tensor, [type: :f32], fn x -> Nx.multiply(x, 1.0) end)
#Nx.Tensor<
f32[2][3]
[
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0]
]
>
limitations
Limitations
Given this function relies on anonymous functions, it
may not be available or efficient on all Nx backends.
Therefore, you should avoid using map/2
whenever possible
and use other functions in the Nx
module to achieve the
desired result.
Inside defn
, consider using Nx.Defn.Kernel.while/4
instead.
examples
Examples
iex> Nx.map(Nx.tensor([[1, 2, 3], [4, 5, 6]]), fn x -> Nx.add(x, 1) end)
#Nx.Tensor<
s64[2][3]
[
[2, 3, 4],
[5, 6, 7]
]
>
iex> Nx.map(Nx.tensor(1), fn x -> Nx.add(x, 1) end)
#Nx.Tensor<
s64
2
>
iex> Nx.map(Nx.tensor([[1, 2, 3], [4, 5, 6]]), [type: :f64], fn x -> Nx.add(x, 1) end)
#Nx.Tensor<
f64[2][3]
[
[2.0, 3.0, 4.0],
[5.0, 6.0, 7.0]
]
>
Element-wise maximum of two tensors.
If a number is given, it is converted to a tensor.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the max/2
function
in place of this function: max(left, right)
.
examples
Examples
max-between-scalars
Max between scalars
iex> Nx.max(1, 2)
#Nx.Tensor<
s64
2
>
max-between-tensors-and-scalars
Max between tensors and scalars
iex> Nx.max(Nx.tensor([1, 2, 3], names: [:data]), 1)
#Nx.Tensor<
s64[data: 3]
[1, 2, 3]
>
iex> Nx.max(1, Nx.tensor([1.0, 2.0, 3.0], names: [:data]))
#Nx.Tensor<
f32[data: 3]
[1.0, 2.0, 3.0]
>
max-between-tensors
Max between tensors
iex> left = Nx.tensor([[1], [2]], names: [:x, :y])
iex> right = Nx.tensor([[10, 20]])
iex> Nx.max(left, right)
#Nx.Tensor<
s64[x: 2][y: 2]
[
[10, 20],
[10, 20]
]
>
iex> left = Nx.tensor([[1], [2]], type: :s8, names: [:x, nil])
iex> right = Nx.tensor([[10, 20]], type: :s8)
iex> Nx.max(left, right)
#Nx.Tensor<
s8[x: 2][2]
[
[10, 20],
[10, 20]
]
>
iex> left = Nx.tensor([[1], [2]], type: :f32, names: [:x, nil])
iex> right = Nx.tensor([[10, 20]], type: :f32, names: [nil, :y])
iex> Nx.max(left, right)
#Nx.Tensor<
f32[x: 2][y: 2]
[
[10.0, 20.0],
[10.0, 20.0]
]
>
Element-wise minimum of two tensors.
If a number is given, it is converted to a tensor.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the min/2
function
in place of this function: min(left, right)
.
examples
Examples
min-between-scalars
Min between scalars
iex> Nx.min(1, 2)
#Nx.Tensor<
s64
1
>
min-between-tensors-and-scalars
Min between tensors and scalars
iex> Nx.min(Nx.tensor([1, 2, 3], names: [:data]), 1)
#Nx.Tensor<
s64[data: 3]
[1, 1, 1]
>
iex> Nx.min(1, Nx.tensor([1.0, 2.0, 3.0], names: [:data]))
#Nx.Tensor<
f32[data: 3]
[1.0, 1.0, 1.0]
>
min-between-tensors
Min between tensors
iex> left = Nx.tensor([[1], [2]], names: [:x, nil])
iex> right = Nx.tensor([[10, 20]])
iex> Nx.min(left, right)
#Nx.Tensor<
s64[x: 2][2]
[
[1, 1],
[2, 2]
]
>
iex> left = Nx.tensor([[1], [2]], type: :s8, names: [:x, :y])
iex> right = Nx.tensor([[10, 20]], type: :s8)
iex> Nx.min(left, right)
#Nx.Tensor<
s8[x: 2][y: 2]
[
[1, 1],
[2, 2]
]
>
iex> left = Nx.tensor([[1], [2]], type: :f32, names: [:x, nil])
iex> right = Nx.tensor([[10, 20]], type: :f32, names: [nil, :y])
iex> Nx.min(left, right)
#Nx.Tensor<
f32[x: 2][y: 2]
[
[1.0, 1.0],
[2.0, 2.0]
]
>
Element-wise multiplication of two tensors.
If a number is given, it is converted to a tensor.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the *
operator
operator in place of this function as left * right
.
examples
Examples
multiplying-scalars
Multiplying scalars
iex> Nx.multiply(1, 2)
#Nx.Tensor<
s64
2
>
multiplying-tensors-and-scalars
Multiplying tensors and scalars
iex> Nx.multiply(Nx.tensor([1, 2, 3], names: [:data]), 1)
#Nx.Tensor<
s64[data: 3]
[1, 2, 3]
>
iex> Nx.multiply(1, Nx.tensor([1.0, 2.0, 3.0], names: [:data]))
#Nx.Tensor<
f32[data: 3]
[1.0, 2.0, 3.0]
>
multiplying-tensors
Multiplying tensors
iex> left = Nx.tensor([[1], [2]], names: [:x, :y])
iex> right = Nx.tensor([[10, 20]], names: [:x, :y])
iex> Nx.multiply(left, right)
#Nx.Tensor<
s64[x: 2][y: 2]
[
[10, 20],
[20, 40]
]
>
iex> left = Nx.tensor([[1], [2]], type: :s8, names: [:x, nil])
iex> right = Nx.tensor([[10, 20]], type: :s8, names: [nil, :y])
iex> Nx.multiply(left, right)
#Nx.Tensor<
s8[x: 2][y: 2]
[
[10, 20],
[20, 40]
]
>
iex> left = Nx.tensor([[1], [2]], type: :f32, names: [nil, :y])
iex> right = Nx.tensor([[10, 20]], type: :f32, names: [:x, nil])
iex> Nx.multiply(left, right)
#Nx.Tensor<
f32[x: 2][y: 2]
[
[10.0, 20.0],
[20.0, 40.0]
]
>
Negates each element in the tensor.
If you're using Nx.Defn.defn/2
, you can use the -
unary operator
in place of this function: -tensor
.
examples
Examples
iex> Nx.negate(1)
#Nx.Tensor<
s64
-1
>
iex> Nx.negate(Nx.tensor([-1, 0, 1]))
#Nx.Tensor<
s64[3]
[1, 0, -1]
>
iex> Nx.negate(Nx.tensor([1.0, 2.0, 3.0], type: :f32))
#Nx.Tensor<
f32[3]
[-1.0, -2.0, -3.0]
>
If an unsigned tensor is given, it works as bitwise_not
:
iex> Nx.negate(Nx.tensor([0, 1, 2], type: :u8, names: [:x]))
#Nx.Tensor<
u8[x: 3]
[0, 255, 254]
>
Element-wise not-equal comparison of two tensors.
If a number is given, it is converted to a tensor.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the !=
operator
in place of this function: left != right
.
examples
Examples
comparison-of-scalars
Comparison of scalars
iex> Nx.not_equal(1, 2)
#Nx.Tensor<
u8
1
>
comparison-of-tensor-and-scalar
Comparison of tensor and scalar
iex> Nx.not_equal(Nx.tensor([1, 2, 3], names: [:data]), Nx.tensor(1))
#Nx.Tensor<
u8[data: 3]
[0, 1, 1]
>
comparison-of-tensors
Comparison of tensors
iex> left = Nx.tensor([1, 1, 2])
iex> right = Nx.tensor([1, 2, 3], names: [:data])
iex> Nx.not_equal(left, right)
#Nx.Tensor<
u8[data: 3]
[0, 1, 1]
>
iex> left = Nx.tensor([[1, 4, 2], [4, 5, 6]], names: [:x, :y])
iex> right = Nx.tensor([[1, 3, 2], [4, 2, 1]], names: [:x, :y])
iex> Nx.not_equal(left, right)
#Nx.Tensor<
u8[x: 2][y: 3]
[
[0, 1, 0],
[0, 1, 1]
]
>
Calculates the complex phase angle of each element in the tensor. $$phase(z) = atan2(b, a), z = a + bi \in \Complex$$
examples
Examples
iex> Nx.phase(Complex.new(1, 2))
#Nx.Tensor<
f32
1.1071487665176392
>
iex> Nx.phase(1)
#Nx.Tensor<
f32
0.0
>
iex> import Nx, only: [sigil_V: 2]
iex> Nx.phase(~V[1+2i -2+1i])
#Nx.Tensor<
f32[2]
[1.1071487665176392, 2.677945137023926]
>
Computes the bitwise population count of each element in the tensor.
examples
Examples
iex> Nx.population_count(1)
#Nx.Tensor<
s64
1
>
iex> Nx.population_count(-128)
#Nx.Tensor<
s64
57
>
iex> Nx.population_count(Nx.tensor([0, 1, 254, 255], names: [:x]))
#Nx.Tensor<
s64[x: 4]
[0, 1, 7, 8]
>
iex> Nx.population_count(Nx.tensor([0, 1, 126, 127, -1, -127, -128], type: :s8, names: [:x]))
#Nx.Tensor<
s8[x: 7]
[0, 1, 6, 7, 8, 2, 1]
>
error-cases
Error cases
iex> Nx.population_count(Nx.tensor([0.0, 1.0]))
** (ArgumentError) bitwise operators expect integer tensors as inputs and outputs an integer tensor, got: {:f, 32}
Element-wise power of two tensors.
If a number is given, it is converted to a tensor.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If both tensors are integers and the exponent is negative, it will raise, but it may trigger undefined behaviour on some compilers.
examples
Examples
power-of-scalars
Power of scalars
iex> Nx.pow(2, 4)
#Nx.Tensor<
s64
16
>
power-of-tensors-and-scalars
Power of tensors and scalars
iex> Nx.pow(Nx.tensor([1, 2, 3], names: [:data]), 2)
#Nx.Tensor<
s64[data: 3]
[1, 4, 9]
>
iex> Nx.pow(2, Nx.tensor([1.0, 2.0, 3.0], names: [:data]))
#Nx.Tensor<
f32[data: 3]
[2.0, 4.0, 8.0]
>
power-of-tensors
Power of tensors
iex> Nx.pow(Nx.tensor([[2], [3]], names: [:x, nil]), Nx.tensor([[4, 5]], names: [nil, :y]))
#Nx.Tensor<
s64[x: 2][y: 2]
[
[16, 32],
[81, 243]
]
>
Element-wise integer division of two tensors.
If a number is given, it is converted to a tensor.
It always returns an integer tensor. Input tensors and numbers must be integer types. Division by zero raises, but it may trigger undefined behaviour on some compilers.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
caveat-for-grad
Caveat for grad
The grad
operation is not supported for quotient/2
.
Since integer division is, by definition, a closed operation
for the set of integers and grad involves floating points,
grad
is undefined.
If you need to support gradients, you might consider using floor division, but beware of precision errors caused by floating points:
a |> Nx.divide(b) |> Nx.floor()
examples
Examples
integer-dividing-scalars
Integer dividing scalars
iex> Nx.quotient(11, 2)
#Nx.Tensor<
s64
5
>
integer-dividing-tensors-and-scalars
Integer dividing tensors and scalars
iex> Nx.quotient(Nx.tensor([2, 4, 5], names: [:data]), 2)
#Nx.Tensor<
s64[data: 3]
[1, 2, 2]
>
iex> Nx.quotient(10, Nx.tensor([1, 2, 3], names: [:data]))
#Nx.Tensor<
s64[data: 3]
[10, 5, 3]
>
dividing-tensors
Dividing tensors
iex> left = Nx.tensor([[10, 20]], names: [nil, :y])
iex> right = Nx.tensor([[1], [2]], names: [:x, nil])
iex> Nx.quotient(left, right)
#Nx.Tensor<
s64[x: 2][y: 2]
[
[10, 20],
[5, 10]
]
>
iex> left = Nx.tensor([[10, 20]], type: :s8, names: [:x, :y])
iex> right = Nx.tensor([[1], [2]], type: :s8)
iex> Nx.quotient(left, right)
#Nx.Tensor<
s8[x: 2][y: 2]
[
[10, 20],
[5, 10]
]
>
iex> left = Nx.tensor([[10, 20]], type: :u8, names: [:x, :y])
iex> right = Nx.tensor([[1], [2]], type: :u32)
iex> Nx.quotient(left, right)
#Nx.Tensor<
u32[x: 2][y: 2]
[
[10, 20],
[5, 10]
]
>
Returns the real component of each entry in a complex tensor as a floating point tensor.
examples
Examples
iex> Nx.real(Complex.new(1, 2))
#Nx.Tensor<
f32
1.0
>
iex> Nx.real(Nx.tensor(1))
#Nx.Tensor<
f32
1.0
>
iex> Nx.real(Nx.tensor(1, type: :bf16))
#Nx.Tensor<
bf16
1.0
>
iex> Nx.real(Nx.tensor([Complex.new(1, 2), Complex.new(2, -4)]))
#Nx.Tensor<
f32[2]
[1.0, 2.0]
>
Element-wise remainder of two tensors.
If a number is given, it is converted to a tensor.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the rem/2
function
in place of this function: rem(left, right)
.
examples
Examples
remainder-of-scalars
Remainder of scalars
iex> Nx.remainder(1, 2)
#Nx.Tensor<
s64
1
>
remainder-of-tensors-and-scalars
Remainder of tensors and scalars
iex> Nx.remainder(Nx.tensor([1, 2, 3], names: [:data]), 2)
#Nx.Tensor<
s64[data: 3]
[1, 0, 1]
>
iex> Nx.remainder(2, Nx.tensor([1.0, 2.0, 3.0], names: [:data]))
#Nx.Tensor<
f32[data: 3]
[0.0, 0.0, 2.0]
>
remainder-of-tensors
Remainder of tensors
iex> left = Nx.tensor([[10], [20]], names: [:x, :y])
iex> right = Nx.tensor([[3, 4]], names: [nil, :y])
iex> Nx.remainder(left, right)
#Nx.Tensor<
s64[x: 2][y: 2]
[
[1, 2],
[2, 0]
]
>
remainder-involving-negative-values
Remainder involving negative values
If given a negative value as the right operand, the operation will return the negative image of the remainder.
For the example below, note that in modulo-10, adding 20 shouldn't change the result, but in this case it does because the sign changes.
iex> left = Nx.tensor(-11, type: :s8)
iex> right = Nx.tensor(10, type: :u8)
iex> Nx.remainder(left, right)
#Nx.Tensor<
s16
-1
>
iex> Nx.remainder(Nx.add(left, Nx.tensor(20, type: :s8)), right)
#Nx.Tensor<
s16
9
>
iex> positive_left = Nx.tensor(9, type: :u8)
iex> Nx.remainder(positive_left, right)
#Nx.Tensor<
u8
9
>
iex> Nx.remainder(Nx.add(positive_left, Nx.tensor(20, type: :u8)), right)
#Nx.Tensor<
u8
9
>
Element-wise right shift of two tensors.
Only integer tensors are supported. If a float or complex tensor is given, an error is raised. If the right side is negative, it will raise, but it may trigger undefined behaviour on some compilers.
It performs an arithmetic shift if the tensor is made of signed integers, it performs a logical shift otherwise. In other words, it preserves the sign for signed integers.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible. If the number of shifts are negative, Nx's default backend will raise, but it may trigger undefined behaviour in other backends.
If you're using Nx.Defn.defn/2
, you can use the >>>
operator
in place of this function: left >>> right
.
examples
Examples
right-shift-between-scalars
Right shift between scalars
iex> Nx.right_shift(1, 0)
#Nx.Tensor<
s64
1
>
right-shift-between-tensors-and-scalars
Right shift between tensors and scalars
iex> Nx.right_shift(Nx.tensor([2, 4, 8], names: [:data]), 2)
#Nx.Tensor<
s64[data: 3]
[0, 1, 2]
>
right-shift-between-tensors
Right shift between tensors
iex> left = Nx.tensor([16, 32, -64, -128], names: [:data])
iex> right = Nx.tensor([1, 2, 3, 4])
iex> Nx.right_shift(left, right)
#Nx.Tensor<
s64[data: 4]
[8, 8, -8, -8]
>
error-cases
Error cases
iex> Nx.right_shift(Nx.tensor([0, 0, 1, 1]), 1.0)
** (ArgumentError) bitwise operators expect integer tensors as inputs and outputs an integer tensor, got: {:f, 32}
Calculates the round (away from zero) of each element in the tensor.
If a non-floating tensor is given, it is returned as is. If a floating tensor is given, then we apply the operation, but keep its type.
examples
Examples
iex> Nx.round(Nx.tensor([-1, 0, 1], names: [:x]))
#Nx.Tensor<
s64[x: 3]
[-1, 0, 1]
>
iex> Nx.round(Nx.tensor([-1.5, -0.5, 0.5, 1.5], names: [:x]))
#Nx.Tensor<
f32[x: 4]
[-2.0, -1.0, 1.0, 2.0]
>
Calculates the reverse square root of each element in the tensor.
It is equivalent to:
$$rsqrt(z) = \frac{1}{\sqrt{z}}$$
examples
Examples
iex> Nx.rsqrt(1)
#Nx.Tensor<
f32
1.0
>
iex> Nx.rsqrt(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[1.0, 0.7071067690849304, 0.5773502588272095]
>
Constructs a tensor from two tensors, based on a predicate.
The resulting tensor is built by evaluating each element of
pred
and returning either the corresponding element from
on_true
or on_false
.
pred
must either be 1
or 0
or a tensor of predicates
with a shape that matches the largest shape between s1
or s2
.
If the shape of on_true
or on_false
do not match the shape of
pred
, attempts to broadcast both so they match the shape of pred
.
examples
Examples
When the first argument is a scalar:
iex> Nx.select(1, Nx.tensor([1, 2, 3], names: [:x]), Nx.tensor([4, 5, 6], names: [:x]))
#Nx.Tensor<
s64[x: 3]
[1, 2, 3]
>
iex> Nx.select(0, Nx.tensor([1, 2, 3], names: [:y]), Nx.tensor([4, 5, 6], names: [:y]))
#Nx.Tensor<
s64[y: 3]
[4, 5, 6]
>
iex> Nx.select(0, Nx.tensor([[1, 2]], names: [:x, :y]), Nx.tensor([[3], [4]], names: [:x, :y]))
#Nx.Tensor<
s64[x: 2][y: 2]
[
[3, 3],
[4, 4]
]
>
When the first argument is a tensor:
iex> Nx.select(Nx.tensor([0, 1, 0], names: [:x]), Nx.tensor([1, 2, 3], names: [:y]), Nx.tensor([4, 5, 6], names: [:z]))
#Nx.Tensor<
s64[x: 3]
[4, 2, 6]
>
iex> x = Nx.tensor([2, 4, 6], names: [:x])
iex> y = Nx.tensor([3, 2, 1])
iex> Nx.select(Nx.greater(x, y), Nx.tensor([2, 4, 6], names: [:i]), Nx.tensor([1, 3, 5], names: [:j]))
#Nx.Tensor<
s64[x: 3]
[1, 4, 6]
>
iex> x = Nx.tensor([2, 4, 6, 8, 10], names: [:x])
iex> y = Nx.tensor([1, 6, 2, 11, 2], names: [:x])
iex> Nx.select(Nx.greater(x, y), Nx.tensor(2), Nx.tensor([1, 3, 5, 7, 9], names: [:x]))
#Nx.Tensor<
s64[x: 5]
[2, 3, 2, 7, 2]
>
If the tensor has other values, any non-zero value is considered true:
iex> Nx.select(Nx.tensor([0, 1, 2], type: :u8), Nx.tensor([0, 0, 0]), Nx.tensor([1, 1, 1]))
#Nx.Tensor<
s64[3]
[1, 0, 0]
>
iex> Nx.select(Nx.tensor([0, 1, 0]), Nx.tensor([1, 1, 1]), Nx.tensor([2.0, 2.0, 2.0]))
#Nx.Tensor<
f32[3]
[2.0, 1.0, 2.0]
>
Calculates the sigmoid of each element in the tensor.
It is equivalent to:
$$sigmoid(z) = \frac{1}{1 + e^{-z}}$$
examples
Examples
iex> Nx.sigmoid(1)
#Nx.Tensor<
f32
0.7310585975646973
>
iex> Nx.sigmoid(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.7310585975646973, 0.8807970881462097, 0.9525741338729858]
>
Computes the sign of each element in the tensor.
If a number is less than zero, it returns -1. If a number is more than zero, it returns 1. Otherwise it returns zero (which may either be positive or negative for floats).
examples
Examples
iex> Nx.sign(Nx.tensor([-2, -1, 0, 1, 2], names: [:x]))
#Nx.Tensor<
s64[x: 5]
[-1, -1, 0, 1, 1]
>
Calculates the sine of each element in the tensor.
It is equivalent to:
$$sin(z) = \frac{e^{iz} - e^{-iz}}{2i}$$
examples
Examples
iex> Nx.sin(1)
#Nx.Tensor<
f32
0.8414709568023682
>
iex> Nx.sin(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.8414709568023682, 0.9092974066734314, 0.14112000167369843]
>
Calculates the hyperbolic sine of each element in the tensor.
It is equivalent to:
$$sinh(z) = \frac{e^z - e^{-z}}{2}$$
examples
Examples
iex> Nx.sinh(1)
#Nx.Tensor<
f32
1.175201177597046
>
iex> Nx.sinh(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[1.175201177597046, 3.6268603801727295, 10.017874717712402]
>
Calculates the square root of each element in the tensor.
It is equivalent to:
$$sqrt(z) = \sqrt{z}$$
examples
Examples
iex> Nx.sqrt(1)
#Nx.Tensor<
f32
1.0
>
iex> Nx.sqrt(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[1.0, 1.4142135381698608, 1.7320507764816284]
>
Element-wise subtraction of two tensors.
If a number is given, it is converted to a tensor.
It will broadcast tensors whenever the dimensions do not match and broadcasting is possible.
If you're using Nx.Defn.defn/2
, you can use the -
operator
in place of this function: left - right
.
examples
Examples
subtracting-scalars
Subtracting scalars
iex> Nx.subtract(1, 2)
#Nx.Tensor<
s64
-1
>
subtracting-tensors-and-scalars
Subtracting tensors and scalars
iex> Nx.subtract(Nx.tensor([1, 2, 3], names: [:data]), 1)
#Nx.Tensor<
s64[data: 3]
[0, 1, 2]
>
iex> Nx.subtract(1, Nx.tensor([1.0, 2.0, 3.0], names: [:data]))
#Nx.Tensor<
f32[data: 3]
[0.0, -1.0, -2.0]
>
subtracting-tensors
Subtracting tensors
iex> left = Nx.tensor([[1], [2]], names: [:x, :y])
iex> right = Nx.tensor([[10, 20]], names: [:x, :y])
iex> Nx.subtract(left, right)
#Nx.Tensor<
s64[x: 2][y: 2]
[
[-9, -19],
[-8, -18]
]
>
iex> left = Nx.tensor([[1], [2]], type: :s8, names: [:x, nil])
iex> right = Nx.tensor([[10, 20]], type: :s8, names: [nil, :y])
iex> Nx.subtract(left, right)
#Nx.Tensor<
s8[x: 2][y: 2]
[
[-9, -19],
[-8, -18]
]
>
iex> left = Nx.tensor([[1], [2]], type: :f32, names: [nil, :y])
iex> right = Nx.tensor([[10, 20]], type: :f32, names: [:x, nil])
iex> Nx.subtract(left, right)
#Nx.Tensor<
f32[x: 2][y: 2]
[
[-9.0, -19.0],
[-8.0, -18.0]
]
>
Calculates the tangent of each element in the tensor.
It is equivalent to:
$$tan(z) = \frac{sin(z)}{cos(z)}$$
examples
Examples
iex> Nx.tan(1)
#Nx.Tensor<
f32
1.5574077367782593
>
iex> Nx.tan(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[1.5574077367782593, -2.185039758682251, -0.14254654943943024]
>
Calculates the hyperbolic tangent of each element in the tensor.
It is equivalent to:
$$sinh(z) = \frac{e^z - e^{-z}}{e^z + e^{-z}}$$
examples
Examples
iex> Nx.tanh(1)
#Nx.Tensor<
f32
0.7615941762924194
>
iex> Nx.tanh(Nx.tensor([1, 2, 3], names: [:x]))
#Nx.Tensor<
f32[x: 3]
[0.7615941762924194, 0.9640275835990906, 0.9950547814369202]
>
Link to this section Functions: Indexed
Builds a new tensor by taking individual values from the original tensor at the given indices.
The last dimension in indices must have the same size as the tensor rank, think of it as one value per axis.
examples
Examples
iex> t = Nx.tensor([[1, 2], [3, 4]])
iex> Nx.gather(t, Nx.tensor([[1, 1], [0, 1], [1, 0]]))
#Nx.Tensor<
s64[3]
[4, 2, 3]
>
iex> t = Nx.tensor([[1, 2], [3, 4]])
iex> Nx.gather(t, Nx.tensor([[[1, 1], [0, 0]], [[1, 0], [0, 1]]]))
#Nx.Tensor<
s64[2][2]
[
[4, 1],
[3, 2]
]
>
iex> t = Nx.tensor([[[1, 2], [11, 12]], [[101, 102], [111, 112]]])
iex> Nx.gather(t, Nx.tensor([[0, 0, 0], [0, 1, 1], [1, 1, 1]]))
#Nx.Tensor<
s64[3]
[1, 12, 112]
>
error-cases
Error cases
iex> Nx.gather(Nx.tensor([[1, 2], [3, 4]]), Nx.tensor([[0, 0]], type: :f32))
** (ArgumentError) indices must be an integer tensor, got {:f, 32}
Performs an indexed add
operation on the target
tensor,
adding the updates
into the corresponding indices
positions.
This operation is the grad for gather/2
and gather-like operations such as
take/3
and take_along_axis/3
.
indices
must be a fully qualified tensor of shape {n, Nx.rank(target)}
, with n
being an arbitrary number of indices, while updates
must have a compatible {n}
shape.
See also: indexed_add/3
, gather/2
, take/3
, take_along_axis/3
examples
Examples
iex> t = Nx.iota({1, 2, 3})
#Nx.Tensor<
s64[1][2][3]
[
[
[0, 1, 2],
[3, 4, 5]
]
]
>
iex> indices = Nx.tensor([[0, 0, 0], [0, 1, 1], [0, 0, 0], [0, 0, 2], [0, 1, 2]])
iex> updates = Nx.tensor([1, 3, 1, -2, 5])
iex> Nx.indexed_add(t, indices, updates)
#Nx.Tensor<
s64[1][2][3]
[
[
[2, 1, 0],
[3, 7, 10]
]
]
>
Type promotions should happen automatically, with the resulting type being the combination
of the target
type and the updates
type.
iex> Nx.indexed_add(Nx.tensor([1.0]), Nx.tensor([[0], [0]]), Nx.tensor([1, 1]))
#Nx.Tensor<
f32[1]
[3.0]
>
iex> Nx.indexed_add(Nx.tensor([1]), Nx.tensor([[0], [0]]), Nx.tensor([1.0, 1.0]))
#Nx.Tensor<
f32[1]
[3.0]
>
iex> Nx.indexed_add(Nx.tensor([1], type: :s32), Nx.tensor([[0], [0]]), Nx.tensor([1, 1], type: :s64))
#Nx.Tensor<
s64[1]
[3]
>
error-cases
Error cases
iex> Nx.indexed_add(Nx.tensor([[1], [2]]), Nx.tensor([[[1, 2, 3]]]), Nx.tensor([0]))
** (ArgumentError) indices must be a rank 2 tensor, got: 3
iex> Nx.indexed_add(Nx.tensor([[1], [2]]), Nx.tensor([[1, 2]]), Nx.tensor([[0]]))
** (ArgumentError) updates must be a rank 1 tensor, got: 2
iex> Nx.indexed_add(Nx.tensor([[1], [2]]), Nx.tensor([[1, 2, 3]]), Nx.tensor([0]))
** (ArgumentError) expected indices to have shape {*, 2}, got: {1, 3}
iex> Nx.indexed_add(Nx.tensor([[1], [2]]), Nx.tensor([[1, 2]]), Nx.tensor([0, 1]))
** (ArgumentError) expected updates tensor to match the first axis of indices tensor with shape {1, 2}, got {2}
Puts individual values from updates
into the given tensor at the corresponding indices
.
indices
must be a fully qualified tensor of shape {n, Nx.rank(target)}
, with n
being an arbitrary number of indices, while updates
must have a compatible {n}
shape.
In case of repeating indices, the result is non-determinstic, since the operation happens in parallel when running on devices such as the GPU.
See also: indexed_add/3
, put_slice/3
.
examples
Examples
iex> Nx.indexed_put(Nx.tensor([0, 0, 0]), Nx.tensor([[1], [2]]), Nx.tensor([2, 4]))
#Nx.Tensor<
s64[3]
[0, 2, 4]
>
iex> Nx.indexed_put(Nx.tensor([0, 0, 0]), Nx.tensor([[1], [2], [1]]), Nx.tensor([3, 4, 2]))
#Nx.Tensor<
s64[3]
[0, 2, 4]
>
iex> t = Nx.iota({1, 2, 3})
#Nx.Tensor<
s64[1][2][3]
[
[
[0, 1, 2],
[3, 4, 5]
]
]
>
iex> indices = Nx.tensor([[0, 0, 0], [0, 1, 1], [0, 0, 2]])
iex> updates = Nx.tensor([1, 3, -2])
iex> Nx.indexed_put(t, indices, updates)
#Nx.Tensor<
s64[1][2][3]
[
[
[1, 1, -2],
[3, 3, 5]
]
]
>
Type promotions should happen automatically, with the resulting type being the combination
of the target
type and the updates
type.
iex> Nx.indexed_put(Nx.tensor([1.0]), Nx.tensor([[0]]), Nx.tensor([3]))
#Nx.Tensor<
f32[1]
[3.0]
>
iex> Nx.indexed_put(Nx.tensor([1]), Nx.tensor([[0]]), Nx.tensor([3.0]))
#Nx.Tensor<
f32[1]
[3.0]
>
iex> Nx.indexed_put(Nx.tensor([1], type: :s32), Nx.tensor([[0]]), Nx.tensor([3], type: :s64))
#Nx.Tensor<
s64[1]
[3]
>
error-cases
Error cases
iex> Nx.indexed_put(Nx.tensor([[1], [2]]), Nx.tensor([[[1, 2, 3]]]), Nx.tensor([0]))
** (ArgumentError) indices must be a rank 2 tensor, got: 3
iex> Nx.indexed_put(Nx.tensor([[1], [2]]), Nx.tensor([[1, 2]]), Nx.tensor([[0]]))
** (ArgumentError) updates must be a rank 1 tensor, got: 2
iex> Nx.indexed_put(Nx.tensor([[1], [2]]), Nx.tensor([[1, 2, 3]]), Nx.tensor([0]))
** (ArgumentError) expected indices to have shape {*, 2}, got: {1, 3}
iex> Nx.indexed_put(Nx.tensor([[1], [2]]), Nx.tensor([[1, 2]]), Nx.tensor([0, 1]))
** (ArgumentError) expected updates tensor to match the first axis of indices tensor with shape {1, 2}, got {2}
Puts the given slice
into the given tensor
at the given
start_indices
.
The given slice must be of the same rank as tensor. Each axis must be less than or equal to the size to the equivalent axis in the tensor.
The number of elements in start_indices
should match the
rank of the tensor.
See also: indexed_add/3
, put_slice/3
.
examples
Examples
iex> t = Nx.tensor([0, 1, 2, 3, 4])
iex> Nx.put_slice(t, [2], Nx.tensor([5, 6]))
#Nx.Tensor<
s64[5]
[0, 1, 5, 6, 4]
>
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6]])
iex> Nx.put_slice(t, [0, 1], Nx.tensor([[7, 8], [9, 10]]))
#Nx.Tensor<
s64[2][3]
[
[1, 7, 8],
[4, 9, 10]
]
>
Similar to slice/3
, dynamic start indexes are also supported:
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6]])
iex> Nx.put_slice(t, [Nx.tensor(0), Nx.tensor(1)], Nx.tensor([[10.0, 11.0]]))
#Nx.Tensor<
f32[2][3]
[
[1.0, 10.0, 11.0],
[4.0, 5.0, 6.0]
]
>
Also similar to slice/3
, if start_index + slice_dimension > dimension
,
the start index will be clipped in order to put the whole slice:
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6]])
iex> Nx.put_slice(t, [1, 1], Nx.tensor([[7, 8], [9, 10]]))
#Nx.Tensor<
s64[2][3]
[
[1, 7, 8],
[4, 9, 10]
]
>
Slices a tensor from start_indices
with lengths
.
You can optionally provide a stride
to specify the amount
of stride in each dimension.
Both start indices and lengths must match the rank of the
input tensor shape. All start indexes must be greater than
or equal to zero. All lengths must be strictly greater than
zero. If start_index + length
exceeds the tensor dimension,
the start_index
will be clipped in order to guarantee the
length
is the requested one. See the "Clipping" section below.
It is possible for start_indices
to be a list of tensors.
However, lengths
must always be a list of integers. If you
want to specify a tensor as the list of indices, see take/3
.
If the :strides
is given, it must be strictly greater than zero.
The resulting tensor will have the shape of length
unless
:strides
are given.
It is not possible to slice in reverse. See gather/2
,
slice_along_axis/4
, take/3
, and take_along_axis/3
for other ways
to retrieve values from a tensor.
examples
Examples
iex> Nx.slice(Nx.tensor([1, 2, 3, 4, 5, 6]), [0], [3])
#Nx.Tensor<
s64[3]
[1, 2, 3]
>
iex> Nx.slice(Nx.tensor([1, 2, 3, 4, 5, 6]), [0], [6], strides: [2])
#Nx.Tensor<
s64[3]
[1, 3, 5]
>
iex> Nx.slice(Nx.tensor([[1, 2], [3, 4], [5, 6]]), [0, 0], [3, 2], strides: [2, 1])
#Nx.Tensor<
s64[2][2]
[
[1, 2],
[5, 6]
]
>
Strides can also be a number that applies to all dimensions:
iex> t = Nx.tensor([[1, 2], [3, 4], [5, 6]])
iex> Nx.slice(t, [0, 0], [3, 2], strides: 2)
#Nx.Tensor<
s64[2][1]
[
[1],
[5]
]
>
A more complex example:
iex> t = Nx.iota({900})
iex> t = Nx.reshape(t, {2, 15, 30})
iex> Nx.slice(t, [0, 4, 11], [2, 3, 9], strides: [2, 1, 3])
#Nx.Tensor<
s64[1][3][3]
[
[
[131, 134, 137],
[161, 164, 167],
[191, 194, 197]
]
]
>
tensors-as-start_indices
Tensors as start_indices
The start_indices
list can be made of scalar tensors:
iex> Nx.slice(Nx.tensor([[1, 2, 3], [4, 5, 6]]), [Nx.tensor(1), Nx.tensor(2)], [1, 1])
#Nx.Tensor<
s64[1][1]
[
[6]
]
>
iex> t = Nx.tensor([
...> [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
...> [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
...> [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
...> [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
...> [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0],
...> [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
...> ])
iex> Nx.slice(t, [Nx.tensor(0), Nx.tensor(0)], [6, 7], strides: [5, 3])
#Nx.Tensor<
f32[2][3]
[
[0.0, 0.0, 0.0],
[1.0, 1.0, 1.0]
]
>
clipping
Clipping
slice/3
will always guarantee the return tensor has the
given lengths
. See the following example:
iex> Nx.slice(Nx.iota({3, 3}), [2, 2], [1, 1])
#Nx.Tensor<
s64[1][1]
[
[8]
]
>
In the example above, start_index + length <= dimension
,
so there is no clipping. However, if the start_index + length
is to exceed the dimension, the index will be clipped in order
to guarantee the given lengths:
iex> Nx.slice(Nx.iota({3, 3}), [2, 2], [2, 2])
#Nx.Tensor<
s64[2][2]
[
[4, 5],
[7, 8]
]
>
This also applies when the start index is given by tensors:
iex> Nx.slice(Nx.iota({3, 3}), [Nx.tensor(2), Nx.tensor(2)], [2, 2])
#Nx.Tensor<
s64[2][2]
[
[4, 5],
[7, 8]
]
>
error-cases
Error cases
iex> Nx.slice(Nx.tensor([[1, 2, 3], [4, 5, 6]]), [Nx.tensor([1, 2]), Nx.tensor(1)], [1, 1])
** (ArgumentError) index must be scalar, got shape {2} for axis 0
iex> Nx.slice(Nx.tensor([[1, 2, 3], [4, 5, 6]]), [Nx.tensor(1.0), Nx.tensor(0)], [1, 1])
** (ArgumentError) index must be integer type, got {:f, 32} for axis 0
Slices a tensor along the given axis.
You can optionally provide a stride
to specify the amount
of stride in along the given dimension.
Start index must be greater than or equal to zero. It can be an
integer or a scalar tensor. Length must be strictly greater than
zero. start_index + length
must not exceed the respective tensor
dimension.
The axis will be normalized with the dimensions and names of the given tensor.
If the :strides
is given, it must be strictly greater than zero.
It is not possible to slice in reverse. See gather/2
, slice/3
,
take/3
, and take_along_axis/3
for other ways to retrieve values
from a tensor.
options
Options
:axis
- The axis along which to take the values from. Defaults to0
.:strides
- The stride to slice the axis along of. Defaults to1
.
examples
Examples
iex> Nx.slice_along_axis(Nx.iota({5, 2}), 1, 2, axis: 0)
#Nx.Tensor<
s64[2][2]
[
[2, 3],
[4, 5]
]
>
iex> Nx.slice_along_axis(Nx.iota({2, 5}), 1, 2, axis: 1)
#Nx.Tensor<
s64[2][2]
[
[1, 2],
[6, 7]
]
>
iex> Nx.slice_along_axis(Nx.iota({2, 5}, names: [:x, :y]), 0, 1, axis: :x)
#Nx.Tensor<
s64[x: 1][y: 5]
[
[0, 1, 2, 3, 4]
]
>
iex> Nx.slice_along_axis(Nx.iota({2, 5}, names: [:x, :y]), Nx.tensor(0), 1, axis: :x)
#Nx.Tensor<
s64[x: 1][y: 5]
[
[0, 1, 2, 3, 4]
]
>
iex> Nx.slice_along_axis(Nx.iota({2, 5}), 0, 3, axis: -1, strides: 2)
#Nx.Tensor<
s64[2][2]
[
[0, 2],
[5, 7]
]
>
Takes and concatenates slices along an axis.
Intuitively speaking, take/3
reorders tensor slices along
the given axis based on the given indices, possibly duplicating
and removing slices.
Passing a multi-dimensional indices tensor only affects the resulting shape. Specifically, the given axis in the input shape gets replaced with the indices shape.
See gather/2
, slice/3
, slice_along_axis/4
, and take_along_axis/3
for other ways to retrieve values from a tensor.
options
Options
:axis
- an axis to take tensor slices over. Defaults to 0.
examples
Examples
iex> t = Nx.tensor([[1, 2], [3, 4]])
iex> Nx.take(t, Nx.tensor([1, 0, 1]))
#Nx.Tensor<
s64[3][2]
[
[3, 4],
[1, 2],
[3, 4]
]
>
iex> t = Nx.tensor([[1, 2], [3, 4]])
iex> Nx.take(t, Nx.tensor([1, 0, 1]), axis: 1)
#Nx.Tensor<
s64[2][3]
[
[2, 1, 2],
[4, 3, 4]
]
>
iex> t = Nx.tensor([[1, 2], [3, 4]], names: [:x, :y])
iex> Nx.take(t, Nx.tensor([1, 0, 1]), axis: :y)
#Nx.Tensor<
s64[x: 2][y: 3]
[
[2, 1, 2],
[4, 3, 4]
]
>
iex> t = Nx.tensor([[[1, 2], [11, 12]], [[101, 102], [111, 112]]])
iex> Nx.take(t, Nx.tensor([1, 0, 1]), axis: 1)
#Nx.Tensor<
s64[2][3][2]
[
[
[11, 12],
[1, 2],
[11, 12]
],
[
[111, 112],
[101, 102],
[111, 112]
]
]
>
Multi-dimensional indices tensor:
iex> t = Nx.tensor([[1, 2], [11, 12]])
iex> Nx.take(t, Nx.tensor([[0, 0], [1, 1], [0, 0]]), axis: 1)
#Nx.Tensor<
s64[2][3][2]
[
[
[1, 1],
[2, 2],
[1, 1]
],
[
[11, 11],
[12, 12],
[11, 11]
]
]
>
iex> t = Nx.tensor([[[1, 2], [11, 12]], [[101, 102], [111, 112]]])
iex> Nx.take(t, Nx.tensor([[0, 0, 0], [1, 1, 1], [0, 0, 0]]), axis: 1)
#Nx.Tensor<
s64[2][3][3][2]
[
[
[
[1, 2],
[1, 2],
[1, 2]
],
[
[11, 12],
[11, 12],
[11, 12]
],
[
[1, 2],
[1, 2],
[1, 2]
]
],
[
[
[101, 102],
[101, 102],
[101, 102]
],
[
[111, 112],
[111, 112],
[111, 112]
],
[
[101, 102],
[101, 102],
[101, 102]
]
]
]
>
error-cases
Error cases
iex> Nx.take(Nx.tensor([[1, 2], [3, 4]]), Nx.tensor([1, 0, 1], type: :f32))
** (ArgumentError) indices must be an integer tensor, got {:f, 32}
Takes the values from a tensor given an indices
tensor, along the specified axis.
The indices
shape must be the same as the tensor
's shape, with the exception for
the axis
dimension, which can have arbitrary size. The returned tensor will have the
same shape as the indices
tensor.
See gather/2
, slice/3
, slice_along_axis/4
, and take/3
for other ways to retrieve
values from a tensor.
options
Options
:axis
- The axis along which to take the values from. Defaults to0
.
examples
Examples
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6]])
iex> Nx.take_along_axis(t, Nx.tensor([[0, 0, 2, 2, 1, 1], [2, 2, 1, 1, 0, 0]]), axis: 1)
#Nx.Tensor<
s64[2][6]
[
[1, 1, 3, 3, 2, 2],
[6, 6, 5, 5, 4, 4]
]
>
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6]])
iex> Nx.take_along_axis(t, Nx.tensor([[0, 1, 1], [1, 0, 0], [0, 1, 0]]), axis: 0)
#Nx.Tensor<
s64[3][3]
[
[1, 5, 6],
[4, 2, 3],
[1, 5, 3]
]
>
The indices returned from Nx.argsort/2
can be used with Nx.take_along_axis/3
to
produce the sorted tensor (or to sort more tensors according to the same criteria).
iex> tensor = Nx.tensor([[[1, 2], [3, 4], [5, 6]]])
#Nx.Tensor<
s64[1][3][2]
[
[
[1, 2],
[3, 4],
[5, 6]
]
]
>
iex> idx1 = Nx.argsort(tensor, axis: 1, direction: :desc)
#Nx.Tensor<
s64[1][3][2]
[
[
[2, 2],
[1, 1],
[0, 0]
]
]
>
iex> Nx.take_along_axis(tensor, idx1, axis: 1)
#Nx.Tensor<
s64[1][3][2]
[
[
[5, 6],
[3, 4],
[1, 2]
]
]
>
iex> idx2 = Nx.argsort(tensor, axis: 2, direction: :desc)
#Nx.Tensor<
s64[1][3][2]
[
[
[1, 0],
[1, 0],
[1, 0]
]
]
>
iex> Nx.take_along_axis(tensor, idx2, axis: 2)
#Nx.Tensor<
s64[1][3][2]
[
[
[2, 1],
[4, 3],
[6, 5]
]
]
>
error-cases
Error cases
iex> tensor = Nx.iota({3, 3})
iex> idx = Nx.tensor([[2.0], [1.0], [2.0]], type: :f32)
iex> Nx.take_along_axis(tensor, idx, axis: 1)
** (ArgumentError) indices must be an integer tensor, got {:f, 32}
Link to this section Functions: N-dim
Sorts the tensor along the given axis according to the given direction and returns the corresponding indices of the original tensor in the new sorted positions.
If no axis is given, defaults to 0
.
options
Options
:axis
- The name or number of the corresponding axis on which the sort should be applied:direction
- Can be:asc
or:desc
. Defaults to:asc
examples
Examples
iex> Nx.argsort(Nx.tensor([16, 23, 42, 4, 8, 15]))
#Nx.Tensor<
s64[6]
[3, 4, 5, 0, 1, 2]
>
iex> t = Nx.tensor([[3, 1, 7], [2, 5, 4]], names: [:x, :y])
iex> Nx.argsort(t, axis: :x)
#Nx.Tensor<
s64[x: 2][y: 3]
[
[1, 0, 1],
[0, 1, 0]
]
>
iex> t = Nx.tensor([[3, 1, 7], [2, 5, 4]], names: [:x, :y])
iex> Nx.argsort(t, axis: :y)
#Nx.Tensor<
s64[x: 2][y: 3]
[
[1, 0, 2],
[0, 2, 1]
]
>
iex> t = Nx.tensor([[3, 1, 7], [2, 5, 4]], names: [:x, :y])
iex> Nx.argsort(t, axis: :y, direction: :asc)
#Nx.Tensor<
s64[x: 2][y: 3]
[
[1, 0, 2],
[0, 2, 1]
]
>
Same tensor sorted over different axes:
iex> t = Nx.tensor(
...> [
...> [
...> [4, 5, 2],
...> [2, 5, 3],
...> [5, 0, 2]
...> ],
...> [
...> [1, 9, 8],
...> [2, 1, 3],
...> [2, 1, 4]
...> ]
...> ],
...> names: [:x, :y, :z]
...> )
iex> Nx.argsort(t, axis: :x)
#Nx.Tensor<
s64[x: 2][y: 3][z: 3]
[
[
[1, 0, 0],
[0, 1, 0],
[1, 0, 0]
],
[
[0, 1, 1],
[1, 0, 1],
[0, 1, 1]
]
]
>
iex> Nx.argsort(t, axis: :y)
#Nx.Tensor<
s64[x: 2][y: 3][z: 3]
[
[
[1, 2, 0],
[0, 0, 2],
[2, 1, 1]
],
[
[0, 1, 1],
[1, 2, 2],
[2, 0, 0]
]
]
>
iex> Nx.argsort(t, axis: :z)
#Nx.Tensor<
s64[x: 2][y: 3][z: 3]
[
[
[2, 0, 1],
[0, 2, 1],
[1, 2, 0]
],
[
[0, 2, 1],
[1, 0, 2],
[1, 0, 2]
]
]
>
Concatenates tensors along the given axis.
Tensors can be a tuple or any Nx.Container
or Nx.LazyContainer
.
This means you can easily concatenate all columns in a datafrane
and other data structures. For convenience, this function also allows
a list of tensors to be given, which may be common outside of defn
.
If no axis is provided, defaults to 0. All tensors must have the same rank and all of their axis except the concatenated one must match.
If tensors with mixed types are given, the types will be merged to a higher type and all of the tensors will be cast to the higher type before concatenating. If tensors are named, the names must match.
examples
Examples
Giving a single tensor is a no-op:
iex> Nx.concatenate([Nx.tensor([1, 2, 3])])
#Nx.Tensor<
s64[3]
[1, 2, 3]
>
Multiple tensors are concatented:
iex> Nx.concatenate([Nx.tensor([1, 2, 3]), Nx.tensor([4, 5, 6])])
#Nx.Tensor<
s64[6]
[1, 2, 3, 4, 5, 6]
>
Types are merged and names must match:
iex> t1 = Nx.iota({2, 2, 2}, names: [:x, :y, :z], type: :f32)
iex> t2 = Nx.iota({1, 2, 2}, names: [:x, :y, :z], type: :u8)
iex> t3 = Nx.iota({1, 2, 2}, names: [:x, :y, :z], type: :s64)
iex> Nx.concatenate([t1, t2, t3], axis: :x)
#Nx.Tensor<
f32[x: 4][y: 2][z: 2]
[
[
[0.0, 1.0],
[2.0, 3.0]
],
[
[4.0, 5.0],
[6.0, 7.0]
],
[
[0.0, 1.0],
[2.0, 3.0]
],
[
[0.0, 1.0],
[2.0, 3.0]
]
]
>
And you can pick a different axis:
iex> t1 = Nx.iota({1, 3, 2}, names: [:x, :y, :z])
iex> t2 = Nx.iota({1, 1, 2}, names: [:x, :y, :z])
iex> t3 = Nx.iota({1, 2, 2}, names: [:x, :y, :z])
iex> Nx.concatenate([t1, t2, t3], axis: :y)
#Nx.Tensor<
s64[x: 1][y: 6][z: 2]
[
[
[0, 1],
[2, 3],
[4, 5],
[0, 1],
[0, 1],
[2, 3]
]
]
>
You can also pass any container (or lazy container) as first argument and they are recursively traversed:
iex> Nx.concatenate({Nx.tensor([1, 2]), {Nx.tensor([3, 4]), Nx.tensor([5, 6])}})
#Nx.Tensor<
s64[6]
[1, 2, 3, 4, 5, 6]
>
error-cases
Error cases
Shapes must have the same rank and match on the non-concatenating axis.
For example, the tensors below work if we concatenate on axis 1, but not on axis 0:
iex> t1 = Nx.iota({1, 2, 3})
iex> t2 = Nx.iota({1, 1, 3})
iex> result = Nx.concatenate([t1, t2], axis: 1)
iex> Nx.shape(result)
{1, 3, 3}
iex> Nx.concatenate([t1, t2], axis: 0)
** (ArgumentError) expected all shapes to match {*, 2, 3}, got unmatching shape: {1, 1, 3}
If the ranks are different, it doesn't work, regardless of the axis choice:
iex> t1 = Nx.iota({1, 2, 3})
iex> t2 = Nx.iota({1, 1})
iex> Nx.concatenate([t1, t2])
** (ArgumentError) expected all shapes to match {*, 2, 3}, got unmatching shape: {1, 1}
Computes an n-D convolution (where n >= 3
) as used in neural networks.
This function can be thought of as sliding an n-D kernel across the input, producing a new tensor that has the same number of elements as the number of valid windows in the input tensor. Each element is the result of summing the element-wise products in the window across each input channel.
The ranks of both input
and kernel
must match. By
default, both input
and kernel
are expected to have shapes
of the following form:
input
-{batch_size, input_channels, input_d0, ..., input_dn}
kernel
-{output_channels, input_channels, kernel_d0, ..., kernel_dn}
Where input_d0...input_dn
and kernel_d0...kernel_dn
represent
an arbitrary number of spatial dimensions. You can alter this configuration
using one of the *_permutation
configuration options. Permutations
are input, kernel, and output specifications for the layout of the
convolution. For example, if your input tensor is configured with
"channels last", you can specify the input permutation with:
Nx.conv(img, kernel, input_permutation: [0, 3, 1, 2])
Permutations expect configurations that specify the location of dimensions in the following orders:
input_permutation
-[batch_dim, input_channel_dim, ...spatial_dims...]
kernel_permutation
-[output_channel_dim, input_channel_dim, ...spatial_dims...]
output_permutation
-[batch_dim, output_channel_dim, ...spatial_dims...]
Using named tensors, it's a bit easier to see how permutations
help you configure the convolution. Given input tensor with names
[:batch, :height, :width, :channels]
(channels last) and kernel
tensor with names [:input, :output, :height, :width]
, you can
configure the convolution with the following permutations:
Nx.conv(img, kernel,
input_permutation: [:batch, :channels, :height, :width],
kernel_permutation: [:output, :input, :height, :width],
output_permutation: [:batch, :channels, :height, :width]
)
Notice that output_permutation
is normalized with respect to
the input permutation names. We cannot guarantee that every
permutation is supported in every backend or compiler.
To configure how the window slides along the input tensor, you
can specify :strides
. :strides
must be a positive integer
or tuple of positive integers for each spatial dimension
in the input and kernel. For each spatial dimension, the
window will slide by the configuration specified in :strides
.
As an example, for a 2-D convolution with strides: [2, 1]
,
the window will slide 2 positions along the first spatial
dimension until it reaches the end of the dimension and then
1 position along the second spatial dimension.
You may specify a padding configuration using :padding
,
which will zero-pad the input tensor. Acceptable padding
configurations are:
:valid
- no padding:same
- pad input spatial dimensions such that they will remain unchanged in the output tensor[{d0_hi, d0_lo}, ..., {dn_hi, dn_lo}]
- a general padding configuration of edge high and edge low padding values. You may only specify padding for the edges of spatial dimensions of the input tensor. Padding values may be negative.
You can dilate convolutions by setting :input_dilation
or
:kernel_dilation
. Both :input_dilation
and :kernel_dilation
must either be positive integers or tuples of positive integers
for each spatial dimension in the input and kernel tensors. Dilations
can be thought of as applying dilation - 1
interior padding to the
input or kernel tensor.
You can split both the input and kernel tensor into feature groups
using :feature_group_size
. This will split both the input and kernel
tensor channels and compute a grouped convolution. The size of the
kernel input feature channels times the size of the feature group must
match the size of the input tensor feature channels. Additionally,
the size of the kernel output feature channels must be evenly divisible
by the group size.
You can also split the input tensor along the batch dimension by
specifying :batch_group_size
. This will compute a grouped convolution
in the same way as with :feature_group_size
, however, the input
tensor will be split into groups along the batch dimension.
examples
Examples
iex> left = Nx.iota({9})
iex> left = Nx.reshape(left, {1, 1, 3, 3})
iex> right = Nx.iota({4})
iex> right = Nx.reshape(right, {4, 1, 1, 1})
iex> Nx.conv(left, right, strides: [1, 1])
#Nx.Tensor<
f32[1][4][3][3]
[
[
[
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0]
],
[
[0.0, 1.0, 2.0],
[3.0, 4.0, 5.0],
[6.0, 7.0, 8.0]
],
[
[0.0, 2.0, 4.0],
[6.0, 8.0, 10.0],
[12.0, 14.0, 16.0]
],
[
[0.0, 3.0, 6.0],
[9.0, 12.0, 15.0],
[18.0, 21.0, 24.0]
]
]
]
>
iex> left = Nx.iota({9})
iex> left = Nx.reshape(left, {1, 1, 3, 3})
iex> right = Nx.iota({8})
iex> right = Nx.reshape(right, {4, 1, 2, 1})
iex> Nx.conv(left, right, strides: 2, padding: :same, kernel_dilation: [2, 1])
#Nx.Tensor<
f32[1][4][2][2]
[
[
[
[3.0, 5.0],
[0.0, 0.0]
],
[
[9.0, 15.0],
[6.0, 10.0]
],
[
[15.0, 25.0],
[12.0, 20.0]
],
[
[21.0, 35.0],
[18.0, 30.0]
]
]
]
>
Complex tensors are also supported:
iex> left = Nx.tensor([[[Complex.new(1, 1), 2, Complex.new(3, -3)]]])
iex> right = Nx.tensor([[[1, Complex.new(0, 2), Complex.new(0, 3)]]])
iex> Nx.conv(left, right, padding: [{2, 2}])
#Nx.Tensor<
c64[1][1][5]
[
[
[-3.0+3.0i, -2.0+8.0i, 10.0+14.0i, 8.0+6.0i, 3.0-3.0i]
]
]
>
Returns the dot product of two tensors.
Given a
and b
, computes the dot product according to
the following rules:
If both
a
andb
are scalars, it is equivalent toa * b
.If
a
is a scalar andb
is a tensor, it is equivalent toNx.multiply(a, b)
.If
a
is a tensor andb
is a scalar, it is equivalent toNx.multiply(a, b)
.If both
a
andb
are 1-D tensors (vectors), it is the sum of the element-wise product betweena
andb
. The lengths ofa
andb
must be equal.If both
a
andb
are 2-D tensors (matrices), it is equivalent to matrix-multiplication.If either
a
orb
is a 1-D tensor, and the other is an n-D tensor, it is the sum of the element-wise product along the last axis ofa
orb
. The length of the 1-D tensor must match the last dimension of the n-D tensor.If
a
is an n-D tensor andb
is an m-D tensor, it is the sum of the element-wise product along the last axis ofa
and the second-to-last axis ofb
. The last dimension ofa
must match the second-to-last dimension ofb
.
For a more general dot
function where you control which axes contract,
see dot/4
.
examples
Examples
dot-product-of-scalars
Dot product of scalars
iex> Nx.dot(5, 5)
#Nx.Tensor<
s64
25
>
iex> Nx.dot(-2.0, 5.0)
#Nx.Tensor<
f32
-10.0
>
iex> Nx.dot(2, 2.0)
#Nx.Tensor<
f32
4.0
>
dot-product-of-vectors
Dot product of vectors
iex> Nx.dot(Nx.tensor([1, 2, 3]), Nx.tensor([4, 5, 6]))
#Nx.Tensor<
s64
32
>
iex> Nx.dot(Nx.tensor([2.0, 4.0, 3.0, 5.0]), Nx.tensor([1.0, 2.0, 3.0, 4.0]))
#Nx.Tensor<
f32
39.0
>
iex> Nx.dot(Nx.tensor([1.0, 2.0, 3.0]), Nx.tensor([1, 2, 3]))
#Nx.Tensor<
f32
14.0
>
dot-product-of-matrices
Dot product of matrices
iex> left = Nx.tensor([[1, 2, 3], [4, 5, 6]], names: [:i, :j])
iex> right = Nx.tensor([[7, 8], [9, 10], [11, 12]], names: [:x, :y])
iex> Nx.dot(left, right)
#Nx.Tensor<
s64[i: 2][y: 2]
[
[58, 64],
[139, 154]
]
>
iex> left = Nx.tensor([[10.0, 13.0, 14.0, 15.0], [59.0, 20.0, 10.0, 30.0]], names: [:i, :j])
iex> right = Nx.tensor([[2.0, 4.0], [5.0, 1.0], [6.0, 8.0], [9.0, 10.0]], names: [:x, :y])
iex> Nx.dot(left, right)
#Nx.Tensor<
f32[i: 2][y: 2]
[
[304.0, 315.0],
[548.0, 636.0]
]
>
iex> left = Nx.tensor([[1, 2, 3], [4, 5, 6]], names: [:i, :j])
iex> right = Nx.tensor([[7.0, 8.0], [9.0, 10.0], [11.0, 12.0]], names: [:x, :y])
iex> Nx.dot(left, right)
#Nx.Tensor<
f32[i: 2][y: 2]
[
[58.0, 64.0],
[139.0, 154.0]
]
>
dot-product-of-vector-and-n-d-tensor
Dot product of vector and n-d tensor
iex> left = Nx.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]], names: [:i, :j, :k])
iex> right = Nx.tensor([5, 10], names: [:x])
iex> Nx.dot(left, right)
#Nx.Tensor<
s64[i: 2][j: 2]
[
[25, 55],
[85, 115]
]
>
iex> left = Nx.tensor([5, 10], names: [:x])
iex> right = Nx.tensor([[1, 2, 3], [4, 5, 6]], names: [:i, :j])
iex> Nx.dot(left, right)
#Nx.Tensor<
s64[j: 3]
[45, 60, 75]
>
iex> left = Nx.tensor([[[[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0], [7.0, 8.0]]]]], names: [:shard, :batch, :x, :y, :z])
iex> right = Nx.tensor([2.0, 2.0], names: [:data])
iex> Nx.dot(left, right)
#Nx.Tensor<
f32[shard: 1][batch: 1][x: 2][y: 2]
[
[
[
[6.0, 14.0],
[22.0, 30.0]
]
]
]
>
dot-product-of-n-d-and-m-d-tensor
Dot product of n-D and m-D tensor
iex> left = Nx.tensor([[[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[1, 2, 3], [4, 5, 6], [7, 8, 9]]], names: [:x, :y, :z])
iex> right = Nx.tensor([[[1, 2, 3], [3, 4, 5], [5, 6, 7]]], names: [:i, :j, :k])
iex> Nx.dot(left, right)
#Nx.Tensor<
s64[x: 2][y: 3][i: 1][k: 3]
[
[
[
[22, 28, 34]
],
[
[49, 64, 79]
],
[
[76, 100, 124]
]
],
[
[
[22, 28, 34]
],
[
[49, 64, 79]
],
[
[76, 100, 124]
]
]
]
>
error-cases
Error cases
iex> Nx.dot(Nx.tensor([1, 2, 3]), Nx.tensor([1, 2]))
** (ArgumentError) dot/zip expects shapes to be compatible, dimension 0 of left-side (3) does not equal dimension 0 of right-side (2)
Computes the generalized dot product between two tensors, given the contracting axes.
This is equivalent to calling Nx.dot/6
with no batching dimensions:
Nx.dot(t1, contract_axes1, [], t2, contract_axes2, [])
examples
Examples
iex> t1 = Nx.tensor([[1, 2], [3, 4]], names: [:x, :y])
iex> t2 = Nx.tensor([[10, 20], [30, 40]], names: [:height, :width])
iex> Nx.dot(t1, [0], t2, [0])
#Nx.Tensor<
s64[y: 2][width: 2]
[
[100, 140],
[140, 200]
]
>
iex> t1 = Nx.tensor([[0.0, 1.0, 2.0], [3.0, 4.0, 5.0]])
iex> t2 = Nx.tensor([[0.0, 1.0], [2.0, 3.0], [4.0, 5.0]])
iex> Nx.dot(t1, [0, 1], t2, [1, 0])
#Nx.Tensor<
f32
50.0
>
dot(t1, contract_axes1, batch_axes1, t2, contract_axes2, batch_axes2)
View SourceComputes the generalized dot product between two tensors, given the contracting and batch axes.
The dot product is computed by multiplying the values from t1
given by contract_axes1
against the values from t2
given by
contract_axes2
, considering batch axes of batch_axes1
and
batch_axes2
. For instance, the first axis in contract_axes1
will be matched against the first axis in contract_axes2
and
so on. The axes given by contract_axes1
and contract_axes2
are effectively removed from the final tensor, which is why they
are often called the contraction axes.
If no contracting axes are given, the final product works like
Nx.outer/2
.
Specifying batch axes will compute a vectorized dot product
along the given batch dimensions. The length of batch_axes1
and batch_axes2
must match. Additionally, batch_axes1
and
batch_axes2
must be a list of successive dimension numbers,
where each batch axis matches the dimension of the corresponding
batch axis in the other input.
The contracting axes must be dot-product compatible and the batch dimensions must always have the same number of elements.
examples
Examples
contracting-along-axes
Contracting along axes
iex> t1 = Nx.tensor([[1, 2], [3, 4]], names: [:x, :y])
iex> t2 = Nx.tensor([[10, 20], [30, 40]], names: [:height, :width])
iex> Nx.dot(t1, [0], [], t2, [0], [])
#Nx.Tensor<
s64[y: 2][width: 2]
[
[100, 140],
[140, 200]
]
>
iex> Nx.dot(t1, [0], [], t2, [1], [])
#Nx.Tensor<
s64[y: 2][height: 2]
[
[70, 150],
[100, 220]
]
>
iex> Nx.dot(t1, [1], [], t2, [0], [])
#Nx.Tensor<
s64[x: 2][width: 2]
[
[70, 100],
[150, 220]
]
>
iex> Nx.dot(t1, [1], [], t2, [1], [])
#Nx.Tensor<
s64[x: 2][height: 2]
[
[50, 110],
[110, 250]
]
>
iex> Nx.dot(t1, [0, 1], [], t2, [0, 1], [])
#Nx.Tensor<
s64
300
>
If no axes are given, it works like outer/2
:
iex> t1 = Nx.tensor([[1, 2], [3, 4]])
iex> t2 = Nx.tensor([[10, 20], [30, 40]])
iex> Nx.dot(t1, [], [], t2, [], [])
#Nx.Tensor<
s64[2][2][2][2]
[
[
[
[10, 20],
[30, 40]
],
[
[20, 40],
[60, 80]
]
],
[
[
[30, 60],
[90, 120]
],
[
[40, 80],
[120, 160]
]
]
]
>
dot-product-between-two-batched-tensors
Dot product between two batched tensors
iex> u = Nx.tensor([[[1]], [[2]]])
iex> v = Nx.tensor([[[3]], [[4]]])
iex> Nx.dot(u, [2], [0], v, [2], [0])
#Nx.Tensor<
s64[2][1][1]
[
[
[3]
],
[
[8]
]
]
>
iex> u = Nx.tensor([[[1, 1]], [[2, 2]]])
iex> v = Nx.tensor([[[3], [3]], [[4], [4]]])
iex> Nx.dot(u, [2], [0], v, [1], [0])
#Nx.Tensor<
s64[2][1][1]
[
[
[6]
],
[
[16]
]
]
>
error-cases
Error cases
iex> u = Nx.tensor([[[1, 1]], [[2, 2]]])
iex> v = Nx.tensor([[[3], [3]], [[4], [4]]])
iex> Nx.dot(u, [2], [0], v, [1], [])
** (ArgumentError) right tensor must be batched if left tensor is batched
iex> u = Nx.tensor([[[1, 1]], [[2, 2]]])
iex> v = Nx.tensor([[[3], [3]], [[4], [4]]])
iex> Nx.dot(u, [2], [], v, [1], [0])
** (ArgumentError) left tensor must be batched if right tensor is batched
iex> u = Nx.tensor([[[1, 1]], [[2, 2]]])
iex> v = Nx.tensor([[[3], [3]], [[4], [4]]])
iex> Nx.dot(u, [2], [1], v, [1], [0])
** (ArgumentError) invalid dot batch axis for the left tensor, batch axes must be successive dimensions starting from 0, got [1]
iex> u = Nx.tensor([[[1, 1]], [[2, 2]]])
iex> v = Nx.tensor([[[3], [3]], [[4], [4]]])
iex> Nx.dot(u, [2], [0], v, [1], [1])
** (ArgumentError) invalid dot batch axis for the right tensor, batch axes must be successive dimensions starting from 0, got [1]
iex> u = Nx.tensor([[[1, 1]], [[2, 2]]])
iex> v = Nx.tensor([[[3], [3]], [[4], [4]]])
iex> Nx.dot(u, [0], [0], v, [1], [0])
** (ArgumentError) dot batch axes for left tensor ([0]) cannot be in contract axes ([0])
iex> u = Nx.tensor([[[1, 1]], [[2, 2]]])
iex> v = Nx.tensor([[[3], [3]], [[4], [4]]])
iex> Nx.dot(u, [2], [0], v, [0], [0])
** (ArgumentError) dot batch axes for right tensor ([0]) cannot be in contract axes ([0])
Calculates the DFT of the given tensor.
options
Options
:eps
- Threshold which backends can use for cleaning-up results. Defaults to1.0e-10
.:length
- Either a positive integer or:power_of_two
. Will pad or slice the tensor accordingly.:power_of_two
will automatically pad to the next power of two.
examples
Examples
iex> Nx.fft(Nx.tensor([1, 1, 0, 0]))
#Nx.Tensor<
c64[4]
[2.0+0.0i, 1.0-1.0i, 0.0+0.0i, 1.0+1.0i]
>
iex> Nx.fft(Nx.tensor([1, 1, 0, 0, 0]))
#Nx.Tensor<
c64[5]
[2.0+0.0i, 1.3090169429779053-0.9510565400123596i, 0.19098301231861115-0.5877852439880371i, 0.19098301231861115+0.5877852439880371i, 1.3090169429779053+0.9510565400123596i]
>
iex> Nx.fft(Nx.tensor([1, 1, 1, 0, 1, 1]))
#Nx.Tensor<
c64[6]
[5.0+0.0i, 1.0+0.0i, -1.0+0.0i, 1.0+0.0i, -1.0+0.0i, 1.0+0.0i]
>
Padding and slicing can be introduced through :length
:
iex> Nx.fft(Nx.tensor([1, 1]), length: 4)
#Nx.Tensor<
c64[4]
[2.0+0.0i, 1.0-1.0i, 0.0+0.0i, 1.0+1.0i]
>
iex> Nx.fft(Nx.tensor([1, 1, 0]), length: :power_of_two)
#Nx.Tensor<
c64[4]
[2.0+0.0i, 1.0-1.0i, 0.0+0.0i, 1.0+1.0i]
>
iex> Nx.fft(Nx.tensor([1, 1, 0, 0, 2, 3]), length: 4)
#Nx.Tensor<
c64[4]
[2.0+0.0i, 1.0-1.0i, 0.0+0.0i, 1.0+1.0i]
>
If an N-dimensional tensor is passed, the DFT is applied to its last axis:
iex> Nx.fft(Nx.tensor([[1, 1, 0, 0, 2, 3], [1, 0, 0, 0, 2, 3]]), length: 4)
#Nx.Tensor<
c64[2][4]
[
[2.0+0.0i, 1.0-1.0i, 0.0+0.0i, 1.0+1.0i],
[1.0+0.0i, 1.0+0.0i, 1.0+0.0i, 1.0+0.0i]
]
>
error-cases
Error Cases
iex> Nx.fft(Nx.tensor([1, 1]), length: :invalid)
** (RuntimeError) expected an integer or :power_of_two as length, got: :invalid
Calculates the Inverse DFT of the given tensor.
options
Options
:eps
- Threshold which backends can use for cleaning-up results. Defaults to1.0e-10
.:length
- Either a positive integer or:power_of_two
. Will pad or slice the tensor accordingly.:power_of_two
will automatically pad to the next power of two.
examples
Examples
iex> Nx.ifft(Nx.tensor([2, Complex.new(1, -1), 0, Complex.new(1, 1)]))
#Nx.Tensor<
c64[4]
[1.0+0.0i, 1.0+0.0i, 0.0+0.0i, 0.0+0.0i]
>
iex> Nx.ifft(Nx.tensor([5, 1, -1, 1, -1, 1]))
#Nx.Tensor<
c64[6]
[1.0+0.0i, 1.0+0.0i, 1.0+0.0i, 0.0+0.0i, 1.0+0.0i, 1.0+0.0i]
>
Padding and slicing can be introduced through :length
:
iex> Nx.ifft(Nx.tensor([1, 1]), length: 4)
#Nx.Tensor<
c64[4]
[0.5+0.0i, 0.25+0.25i, 0.0+0.0i, 0.25-0.25i]
>
iex> Nx.ifft(Nx.tensor([1, 1, 0]), length: :power_of_two)
#Nx.Tensor<
c64[4]
[0.5+0.0i, 0.25+0.25i, 0.0+0.0i, 0.25-0.25i]
>
iex> Nx.ifft(Nx.tensor([1, 1, 0, 0, 2, 3]), length: 4)
#Nx.Tensor<
c64[4]
[0.5+0.0i, 0.25+0.25i, 0.0+0.0i, 0.25-0.25i]
>
If an N-dimensional tensor is passed, the Inverse DFT is applied to its last axis:
iex> Nx.ifft(Nx.tensor([[1, 1, 0, 0, 2, 3], [1, 0, 0, 0, 2, 3]]), length: 4)
#Nx.Tensor<
c64[2][4]
[
[0.5+0.0i, 0.25+0.25i, 0.0+0.0i, 0.25-0.25i],
[0.25+0.0i, 0.25+0.0i, 0.25+0.0i, 0.25+0.0i]
]
>
error-cases
Error Cases
iex> Nx.ifft(Nx.tensor([1, 1]), length: :invalid)
** (RuntimeError) expected an integer or :power_of_two as length, got: :invalid
Computes the outer product of two tensors.
The output is always a two-dimensional tensor.
examples
Examples
iex> Nx.outer(Nx.tensor([1, 2, 3], names: [:x]), 100)
#Nx.Tensor<
s64[x: 3][1]
[
[100],
[200],
[300]
]
>
iex> Nx.outer(Nx.tensor([1, 2, 3], names: [:x]), Nx.tensor([10, 20], names: [:y]))
#Nx.Tensor<
s64[x: 3][y: 2]
[
[10, 20],
[20, 40],
[30, 60]
]
>
iex> Nx.outer(Nx.tensor([[1, 2], [3, 4]], names: [:x, :y]), Nx.tensor([10, 20, 30], names: [:z]))
#Nx.Tensor<
s64[x: 4][z: 3]
[
[10, 20, 30],
[20, 40, 60],
[30, 60, 90],
[40, 80, 120]
]
>
Reverses the tensor in the given dimensions.
If no axes are provided, reverses every axis.
You can pass either names or numbers for the reverse dimensions. Dimensions must be unique, but they do not have to be successive.
examples
Examples
iex> Nx.reverse(Nx.tensor([1, 2, 3]))
#Nx.Tensor<
s64[3]
[3, 2, 1]
>
iex> Nx.reverse(Nx.tensor([[1, 2, 3], [4, 5, 6]]))
#Nx.Tensor<
s64[2][3]
[
[6, 5, 4],
[3, 2, 1]
]
>
iex> Nx.reverse(Nx.tensor([1, 2, 3], names: [:x]), axes: [:x])
#Nx.Tensor<
s64[x: 3]
[3, 2, 1]
>
iex> Nx.reverse(Nx.tensor([[1, 2, 3], [4, 5, 6]], names: [:x, :y]), axes: [:x])
#Nx.Tensor<
s64[x: 2][y: 3]
[
[4, 5, 6],
[1, 2, 3]
]
>
iex> Nx.reverse(Nx.tensor([[1, 2, 3], [4, 5, 6]], names: [:x, :y]), axes: [:y])
#Nx.Tensor<
s64[x: 2][y: 3]
[
[3, 2, 1],
[6, 5, 4]
]
>
iex> Nx.reverse(Nx.iota({2, 2, 2}, type: :f32, names: [:x, :y, :z]), axes: [:x, :z])
#Nx.Tensor<
f32[x: 2][y: 2][z: 2]
[
[
[5.0, 4.0],
[7.0, 6.0]
],
[
[1.0, 0.0],
[3.0, 2.0]
]
]
>
Sorts the tensor along the given axis according to the given direction.
If no axis is given, defaults to 0
.
options
Options
:axis
- The name or number of the corresponding axis on which the sort should be applied:direction
- Can be:asc
or:desc
. Defaults to:asc
examples
Examples
iex> Nx.sort(Nx.tensor([16, 23, 42, 4, 8, 15]))
#Nx.Tensor<
s64[6]
[4, 8, 15, 16, 23, 42]
>
iex> t = Nx.tensor([[3, 1, 7], [2, 5, 4]], names: [:x, :y])
iex> Nx.sort(t, axis: :x)
#Nx.Tensor<
s64[x: 2][y: 3]
[
[2, 1, 4],
[3, 5, 7]
]
>
iex> t = Nx.tensor([[3, 1, 7], [2, 5, 4]], names: [:x, :y])
iex> Nx.sort(t, axis: :y)
#Nx.Tensor<
s64[x: 2][y: 3]
[
[1, 3, 7],
[2, 4, 5]
]
>
iex> t = Nx.tensor([[3, 1, 7], [2, 5, 4]], names: [:x, :y])
iex> Nx.sort(t, axis: :y, direction: :asc)
#Nx.Tensor<
s64[x: 2][y: 3]
[
[1, 3, 7],
[2, 4, 5]
]
>
iex> t = Nx.tensor(
...> [
...> [[4, 5], [2, 5], [5, 0]],
...> [[1, 9], [2, 1], [2, 1]],
...> [[0, -1], [-1, 0], [0, -1]],
...> [[-1, 0], [0, -1], [-1, 0]]
...> ],
...> names: [:x, :y, :z]
...> )
iex> Nx.sort(t, axis: :x)
#Nx.Tensor<
s64[x: 4][y: 3][z: 2]
[
[
[-1, -1],
[-1, -1],
[-1, -1]
],
[
[0, 0],
[0, 0],
[0, 0]
],
[
[1, 5],
[2, 1],
[2, 0]
],
[
[4, 9],
[2, 5],
[5, 1]
]
]
>
Same tensor sorted over different axes:
iex> t = Nx.tensor(
...> [
...> [
...> [4, 5, 2],
...> [2, 5, 3],
...> [5, 0, 2]
...> ],
...> [
...> [1, 9, 8],
...> [2, 1, 3],
...> [2, 1, 4]
...> ]
...> ],
...> names: [:x, :y, :z]
...> )
iex> Nx.sort(t, axis: :x)
#Nx.Tensor<
s64[x: 2][y: 3][z: 3]
[
[
[1, 5, 2],
[2, 1, 3],
[2, 0, 2]
],
[
[4, 9, 8],
[2, 5, 3],
[5, 1, 4]
]
]
>
iex> Nx.sort(t, axis: :y)
#Nx.Tensor<
s64[x: 2][y: 3][z: 3]
[
[
[2, 0, 2],
[4, 5, 2],
[5, 5, 3]
],
[
[1, 1, 3],
[2, 1, 4],
[2, 9, 8]
]
]
>
iex> Nx.sort(t, axis: :z)
#Nx.Tensor<
s64[x: 2][y: 3][z: 3]
[
[
[2, 4, 5],
[2, 3, 5],
[0, 2, 5]
],
[
[1, 8, 9],
[1, 2, 3],
[1, 2, 4]
]
]
>
Stacks a list of tensors with the same shape along a new axis.
Tensors can be a tuple or any Nx.Container
or Nx.LazyContainer
.
This means you can easily concatenate all columns in a datafrane
and other data structures. For convenience, this function also allows
a list of tensors to be given, which may be common outside of defn
.
If no axis is provided, defaults to 0. All tensors must have the same shape.
If tensors with mixed types are given, the types will be merged to a higher type and all of the tensors will be cast to the higher type before concatenating. If tensors are named, the names must match.
options
Options
:axis
- optional index of the axis along which the tensors are stacked. Defaults to 0.:name
- optional name for the added dimension. Defaults to an unnamed axis.
examples
Examples
Stacking always creates a new dimension:
iex> Nx.stack([1, 2, 3])
#Nx.Tensor<
s64[3]
[1, 2, 3]
>
iex> Nx.stack([Nx.tensor([1, 2, 3]), Nx.tensor([4, 5, 6])])
#Nx.Tensor<
s64[2][3]
[
[1, 2, 3],
[4, 5, 6]
]
>
The axis option can be given:
iex> t1 = Nx.iota({2, 1, 4})
iex> t2 = Nx.iota({2, 1, 4})
iex> t3 = Nx.iota({2, 1, 4})
iex> Nx.stack([t1, t2, t3], axis: -1)
#Nx.Tensor<
s64[2][1][4][3]
[
[
[
[0, 0, 0],
[1, 1, 1],
[2, 2, 2],
[3, 3, 3]
]
],
[
[
[4, 4, 4],
[5, 5, 5],
[6, 6, 6],
[7, 7, 7]
]
]
]
>
And a name can be given for the new dimension:
iex> Nx.stack([Nx.tensor(1), Nx.tensor(2)], name: :x)
#Nx.Tensor<
s64[x: 2]
[1, 2]
>
You can also pass any container (or lazy container) as first argument and they are recursively traversed:
iex> Nx.stack({Nx.tensor([1, 2]), {Nx.tensor([3, 4]), Nx.tensor([5, 6])}})
#Nx.Tensor<
s64[3][2]
[
[1, 2],
[3, 4],
[5, 6]
]
>
Returns a tuple of {values, indices}
for the top k
values in last dimension of the tensor.
:k
is an option and must be at least 1, and less than
or equal to the size of the last dimension of the tensor.
It defaults to 1
.
examples
Examples
iex> a = Nx.tensor([1, 2, 3, 4, 5])
iex> {values, indices} = Nx.top_k(a, k: 2)
iex> values
#Nx.Tensor<
s64[2]
[5, 4]
>
iex> indices
#Nx.Tensor<
s64[2]
[4, 3]
>
:k
defaults to 1:
iex> a = Nx.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
iex> {values, indices} = Nx.top_k(a)
iex> values
#Nx.Tensor<
f32[2][1]
[
[3.0],
[6.0]
]
>
iex> indices
#Nx.Tensor<
s64[2][1]
[
[2],
[2]
]
>
error-cases
Error cases
iex> a = Nx.tensor([1, 2, 3, 4, 5])
iex> Nx.top_k(a, k: 6)
** (ArgumentError) top_k input last axis size must be greater than or equal to k, got size=5 and k=6
iex> a = Nx.tensor(1)
iex> Nx.top_k(a, k: 1)
** (ArgumentError) top_k input must have at least rank 1
Link to this section Functions: Shape
Returns all of the axes in a tensor.
If a shape is given, it returns the axes for the given shape.
examples
Examples
iex> Nx.axes(Nx.tensor([[1, 2, 3], [4, 5, 6]]))
[0, 1]
iex> Nx.axes(1)
[]
iex> Nx.axes({1, 2, 3})
[0, 1, 2]
Returns the index of the given axis in the tensor.
examples
Examples
iex> Nx.axis_index(Nx.iota({100, 10, 20}), 0)
0
iex> Nx.axis_index(Nx.iota({100, 10, 20}), -1)
2
iex> Nx.axis_index(Nx.iota({100, 10, 20}, names: [:batch, :x, :y]), :x)
1
error-cases
Error cases
iex> Nx.axis_index(Nx.iota({100, 10, 20}), 3)
** (ArgumentError) given axis (3) invalid for shape with rank 3
iex> Nx.axis_index(Nx.iota({100, 10, 20}, names: [:batch, :x, :y]), :z)
** (ArgumentError) name :z not found in tensor with names [:batch, :x, :y]
Returns the size of a given axis of a tensor.
It accepts either an atom as the name or an integer as the axis. It raises if the axis/name does not exist.
examples
Examples
iex> Nx.axis_size(Nx.iota({100, 10, 20}), 0)
100
iex> Nx.axis_size(Nx.iota({100, 10, 20}, names: [:batch, :x, :y]), :y)
20
Broadcasts tensor
to the given broadcast_shape
.
The new shape is either a tuple or a tensor which we will retrieve the current shape from. The broadcast shape must be of equal or higher rank than the current shape.
An optional :axes
can be given to customize how broadcasting
happens. axes
must be a list with the same length as the
tensor shape. Each axis
in the list maps to the dimension
in the broadcast shape that must match. For example, an axis
of [1, 2]
says the 0 dimension of the tensor matches to
the 1 dimension of the broadcast shape and the 1 dimension
of the tensor matches the 2 dimension of the broadcast shape.
Each matching dimension must either be 1, for implicit
broadcasting, or match the dimension in the broadcast shape.
Broadcasting is destructive with respect to names. You can
optionally provide new :names
for the new tensor. If you
pass a tensor with named dimensions, the new tensor will
inherit names from that tensor.
examples
Examples
without-axes
Without axes
examples-1
Examples
iex> Nx.broadcast(1, {1, 2, 3})
#Nx.Tensor<
s64[1][2][3]
[
[
[1, 1, 1],
[1, 1, 1]
]
]
>
iex> Nx.broadcast(Nx.tensor([[1], [2]], names: [:x, :y]), Nx.tensor([[10, 20], [30, 40]], names: [:i, :j]))
#Nx.Tensor<
s64[i: 2][j: 2]
[
[1, 1],
[2, 2]
]
>
iex> Nx.broadcast(Nx.tensor([[1, 2]], names: [:x, :y]), Nx.tensor([[10, 20], [30, 40]], names: [:i, :j]))
#Nx.Tensor<
s64[i: 2][j: 2]
[
[1, 2],
[1, 2]
]
>
Note that, even if there is no broadcasting because the shape is the same, names are discarded if none are given:
iex> Nx.broadcast(Nx.iota({2, 2}, names: [:x, :y]), {2, 2})
#Nx.Tensor<
s64[2][2]
[
[0, 1],
[2, 3]
]
>
iex> Nx.broadcast(Nx.iota({2, 2}, names: [:x, :y]), {2, 2}, names: [:i, :j])
#Nx.Tensor<
s64[i: 2][j: 2]
[
[0, 1],
[2, 3]
]
>
with-axes
With axes
Using the default broadcast rules, we cannot broadcast a
tensor of shape (3) to the shape (3, 2), because the lower
dimensions must match. But with Nx.broadcast/3
we can
configure how the dimensions match:
iex> t = Nx.tensor([1, 2, 3])
iex> Nx.broadcast(t, {3, 2}, axes: [0], names: [:x, :y])
#Nx.Tensor<
s64[x: 3][y: 2]
[
[1, 1],
[2, 2],
[3, 3]
]
>
Or a more complex example:
iex> t = Nx.tensor([1, 2, 3])
iex> Nx.broadcast(t, {2, 3, 2}, axes: [1], names: [:x, :y, :z])
#Nx.Tensor<
s64[x: 2][y: 3][z: 2]
[
[
[1, 1],
[2, 2],
[3, 3]
],
[
[1, 1],
[2, 2],
[3, 3]
]
]
>
Returns the byte size of the data in the tensor computed from its shape and type.
examples
Examples
iex> Nx.byte_size(Nx.tensor([[1, 2, 3], [4, 5, 6]]))
48
iex> Nx.byte_size(Nx.tensor([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]))
24
iex> Nx.byte_size(Nx.tensor([[1, 2, 3], [4, 5, 6]], type: :u8))
6
iex> Nx.byte_size(1)
8
Checks if two tensors have the same shape, type, and compatible names.
The data in the tensor is ignored.
For convenience, this function accepts tensors and any container
(such as maps and tuples as defined by the Nx.Container
protocol)
and recursively compares them, observing their container data
structures are also the same.
Note: This function cannot be used in defn
.
examples
Examples
iex> Nx.compatible?(Nx.iota({3, 2}), Nx.iota({3, 2}))
true
iex> Nx.compatible?(Nx.iota({3, 2}), Nx.iota({3, 2}, names: [:rows, :columns]))
true
iex> Nx.compatible?(
...> Nx.iota({3, 2}, names: [:rows, nil]),
...> Nx.iota({3, 2}, names: [nil, :columns])
...> )
true
iex> Nx.compatible?(
...> Nx.iota({3, 2}, names: [:foo, :bar]),
...> Nx.iota({3, 2}, names: [:rows, :columns])
...> )
false
iex> Nx.compatible?(Nx.iota({3, 2}), Nx.iota({2, 3}))
false
iex> Nx.compatible?(Nx.iota({2, 2}), Nx.iota({2, 2}, type: :f32))
false
Using collections:
iex> Nx.compatible?({Nx.iota({3, 2}), {1, 2}}, {Nx.iota({3, 2}), {3, 4}})
true
iex> Nx.compatible?(%{foo: Nx.iota({3, 2})}, %{foo: Nx.iota({3, 2})})
true
iex> Nx.compatible?(%{foo: Nx.iota({3, 2})}, %{bar: Nx.iota({3, 2})})
false
Flattens a n-dimensional tensor to a 1-dimensional tensor.
Flattening only changes the tensor metadata, it doesn't copy the underlying structure.
Flatten is a destructive operation with respect to names.
examples
Examples
iex> t = Nx.iota({2, 2, 2, 2})
#Nx.Tensor<
s64[2][2][2][2]
[
[
[
[0, 1],
[2, 3]
],
[
[4, 5],
[6, 7]
]
],
[
[
[8, 9],
[10, 11]
],
[
[12, 13],
[14, 15]
]
]
]
>
iex> Nx.flatten(t)
#Nx.Tensor<
s64[16]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>
And if the tensor is already 1-dimensional:
iex> t = Nx.iota({16})
#Nx.Tensor<
s64[16]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>
iex> Nx.flatten(t)
#Nx.Tensor<
s64[16]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
>
You may also pass :axes
to Nx.flatten/2
, to specify which consecutive
axes to flatten:
iex> t = Nx.iota({1, 2, 3})
#Nx.Tensor<
s64[1][2][3]
[
[
[0, 1, 2],
[3, 4, 5]
]
]
>
iex> Nx.flatten(t, axes: [1, 2])
#Nx.Tensor<
s64[1][6]
[
[0, 1, 2, 3, 4, 5]
]
>
:axes
must be consecutive, otherwise it will raise:
iex> t = Nx.iota({1, 2, 3})
#Nx.Tensor<
s64[1][2][3]
[
[
[0, 1, 2],
[3, 4, 5]
]
]
>
iex> Nx.flatten(t, axes: [0, 2])
** (ArgumentError) flatten axes must be consecutive
Returns all of the names in a tensor.
examples
Examples
iex> Nx.names(Nx.tensor([[1, 2, 3], [4, 5, 6]], names: [:batch, :data]))
[:batch, :data]
iex> Nx.names(Nx.tensor([1, 2, 3]))
[nil]
iex> Nx.names(5)
[]
Adds a new axis
of size 1 with optional name
.
examples
Examples
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6]])
iex> Nx.new_axis(t, 0, :new)
#Nx.Tensor<
s64[new: 1][2][3]
[
[
[1, 2, 3],
[4, 5, 6]
]
]
>
iex> Nx.new_axis(t, 1, :new)
#Nx.Tensor<
s64[2][new: 1][3]
[
[
[1, 2, 3]
],
[
[4, 5, 6]
]
]
>
iex> Nx.new_axis(t, 2, :new)
#Nx.Tensor<
s64[2][3][new: 1]
[
[
[1],
[2],
[3]
],
[
[4],
[5],
[6]
]
]
>
Axis can also be negative, which will start from the back:
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6]])
iex> Nx.new_axis(t, -1, :new)
#Nx.Tensor<
s64[2][3][new: 1]
[
[
[1],
[2],
[3]
],
[
[4],
[5],
[6]
]
]
>
Pads a tensor with a given value.
You must specify a padding configuration. A padding
configuration is a list of tuples consisting of
{pad_width_low, pad_width_high, pad_width_interior}
for each dimension in the input tensor. The padding
configuration must be of the same length as the tensor shape.
Padding widths can be negative. If they are negative, the tensor is clipped on either end according to the padding width. Interior padding widths cannot be negative.
See also: reflect/2
examples
Examples
iex> Nx.pad(Nx.tensor(1), 0, [])
#Nx.Tensor<
s64
1
>
iex> Nx.pad(Nx.tensor([1, 2, 3], names: [:data]), 0, [{1, 1, 0}])
#Nx.Tensor<
s64[data: 5]
[0, 1, 2, 3, 0]
>
iex> Nx.pad(Nx.tensor([[1, 2, 3], [4, 5, 6]]), 0, [{0, 0, 1}, {0, 0, 1}])
#Nx.Tensor<
s64[3][5]
[
[1, 0, 2, 0, 3],
[0, 0, 0, 0, 0],
[4, 0, 5, 0, 6]
]
>
iex> Nx.pad(Nx.tensor([[1, 2, 3], [4, 5, 6]]), 0, [{1, 1, 0}, {1, 1, 0}])
#Nx.Tensor<
s64[4][5]
[
[0, 0, 0, 0, 0],
[0, 1, 2, 3, 0],
[0, 4, 5, 6, 0],
[0, 0, 0, 0, 0]
]
>
iex> tensor = Nx.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
iex> Nx.pad(tensor, 0, [{0, 2, 0}, {1, 1, 0}, {1, 0, 0}])
#Nx.Tensor<
s64[4][4][3]
[
[
[0, 0, 0],
[0, 1, 2],
[0, 3, 4],
[0, 0, 0]
],
[
[0, 0, 0],
[0, 5, 6],
[0, 7, 8],
[0, 0, 0]
],
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
],
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
]
]
>
iex> tensor = Nx.tensor([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])
iex> Nx.pad(tensor, 0, [{1, 0, 0}, {1, 1, 0}, {0, 1, 0}])
#Nx.Tensor<
s64[3][4][3]
[
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
],
[
[0, 0, 0],
[1, 2, 0],
[3, 4, 0],
[0, 0, 0]
],
[
[0, 0, 0],
[5, 6, 0],
[7, 8, 0],
[0, 0, 0]
]
]
>
iex> tensor = Nx.tensor([[[1.0, 2.0], [3.0, 4.0]], [[5.0, 6.0], [7.0, 8.0]]])
iex> Nx.pad(tensor, 0.0, [{1, 2, 0}, {1, 0, 0}, {0, 1, 0}])
#Nx.Tensor<
f32[5][3][3]
[
[
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0]
],
[
[0.0, 0.0, 0.0],
[1.0, 2.0, 0.0],
[3.0, 4.0, 0.0]
],
[
[0.0, 0.0, 0.0],
[5.0, 6.0, 0.0],
[7.0, 8.0, 0.0]
],
[
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0]
],
[
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0]
]
]
>
iex> Nx.pad(Nx.tensor([0, 1, 2, 3, 0]), 0, [{-1, -1, 0}])
#Nx.Tensor<
s64[3]
[1, 2, 3]
>
iex> tensor = Nx.tensor([
...> [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]],
...> [[0, 0, 0], [1, 2, 0], [3, 4, 0], [0, 0, 0]],
...> [[0, 0, 0], [5, 6, 0], [7, 8, 0], [0, 0, 0]]
...> ])
iex> Nx.pad(tensor, 0, [{-1, 0, 0}, {-1, -1, 0}, {0, -1, 0}])
#Nx.Tensor<
s64[2][2][2]
[
[
[1, 2],
[3, 4]
],
[
[5, 6],
[7, 8]
]
]
>
iex> tensor = Nx.tensor([[0, 1, 2, 3], [0, 4, 5, 6]])
iex> Nx.pad(tensor, 0, [{0, 0, 0}, {-1, 1, 0}])
#Nx.Tensor<
s64[2][4]
[
[1, 2, 3, 0],
[4, 5, 6, 0]
]
>
iex> tensor = Nx.tensor([[0, 1, 2], [3, 4, 5]], type: :f32)
iex> Nx.pad(tensor, 0, [{-1, 2, 0}, {1, -1, 0}])
#Nx.Tensor<
f32[3][3]
[
[0.0, 3.0, 4.0],
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0]
]
>
Returns the rank of a tensor.
If a tuple is given as a shape, it computes the rank of the given tuple.
examples
Examples
iex> Nx.rank(Nx.tensor(1))
0
iex> Nx.rank(Nx.tensor([[1, 2, 3], [4, 5, 6]]))
2
iex> Nx.rank(1)
0
iex> Nx.rank({1, 2, 3})
3
Pads a tensor of rank 1 or greater along the given axes through periodic reflections.
options
Options
:padding_config
- A list of tuples in the format{pre, post}
, which specify the length (0 or greater) of the reflection before and after the tensor along a each axis.
See also: pad/3
examples
Examples
iex> Nx.reflect(Nx.tensor([0, 1, 2]), padding_config: [{3, 1}])
#Nx.Tensor<
s64[7]
[1, 2, 1, 0, 1, 2, 1]
>
iex> Nx.reflect(Nx.tensor([[0, 1, 2], [3, 4, 5]], names: [:x, :y]), padding_config: [{2, 0}, {2, 1}])
#Nx.Tensor<
s64[x: 4][y: 6]
[
[2, 1, 0, 1, 2, 1],
[5, 4, 3, 4, 5, 4],
[2, 1, 0, 1, 2, 1],
[5, 4, 3, 4, 5, 4]
]
>
Adds (or overrides) the given names to the tensor.
examples
Examples
iex> Nx.rename(Nx.iota({2, 3}), [:foo, :bar])
#Nx.Tensor<
s64[foo: 2][bar: 3]
[
[0, 1, 2],
[3, 4, 5]
]
>
Changes the shape of a tensor.
The new shape is either a tuple or a tensor which we will retrieve the current shape from. The shapes must be compatible: the product of each dimension in the shape must be equal.
You may specify one of the dimensions as :auto
. Nx will compute
the size of the dimension based on the original shape and new shape.
Reshaping only changes the tensor metadata, it doesn't copy the underlying structure.
Reshape is a destructive operation with respect to names. You
can optionally provide :names
for each of the dimensions
in the reshaped tensor. If you do not provide :names
, they
will be taken from the tensor the shape is taken from or
all of the dimension names will be set to nil
.
examples
Examples
iex> t = Nx.tensor([1, 2, 3, 4], names: [:x])
iex> Nx.reshape(t, {2, 2}, names: [:x, :y])
#Nx.Tensor<
s64[x: 2][y: 2]
[
[1, 2],
[3, 4]
]
>
The shape can also be an existing tensor:
iex> shape = Nx.tensor([[0], [0], [0], [0]], names: [:x, :y])
iex> Nx.reshape(Nx.tensor([1, 2, 3, 4]), shape)
#Nx.Tensor<
s64[x: 4][y: 1]
[
[1],
[2],
[3],
[4]
]
>
Even a scalar can be transformed into a 3-dimensional tensor:
iex> t = Nx.tensor(1)
iex> Nx.reshape(t, {1, 1, 1}, names: [:x, :y, :z])
#Nx.Tensor<
s64[x: 1][y: 1][z: 1]
[
[
[1]
]
]
>
You can use :auto
to infer dimension sizes. This is useful when you
don't know the size some dimension should be ahead of time:
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6]])
iex> Nx.reshape(t, {:auto, 2}, names: [:x, :y])
#Nx.Tensor<
s64[x: 3][y: 2]
[
[1, 2],
[3, 4],
[5, 6]
]
>
Returns the shape of the tensor as a tuple.
The size of this tuple gives the rank of the tensor.
If a shape as a tuple is given, it returns the shape itself.
examples
Examples
iex> Nx.shape(Nx.tensor(1))
{}
iex> Nx.shape(Nx.tensor([[1, 2, 3], [4, 5, 6]]))
{2, 3}
iex> Nx.shape(1)
{}
iex> Nx.shape({1, 2, 3})
{1, 2, 3}
Returns the number of elements in the tensor.
If a tuple is given, it returns the number of elements in a tensor with that shape.
examples
Examples
iex> Nx.size(Nx.tensor([[1, 2, 3], [4, 5, 6]]))
6
iex> Nx.size(1)
1
iex> Nx.size({1, 2, 3, 2})
12
Squeezes the given size 1
dimensions out of the tensor.
If no axes are given, squeezes all size 1
dimensions
from the tensor.
While this is equivalent to a reshape which eliminates
the size 1
axes, squeeze preserves important information
about which axes were squeezed out which can then be used
later on in transformations.
examples
Examples
iex> Nx.squeeze(Nx.tensor([[[[[1]]]]]))
#Nx.Tensor<
s64
1
>
iex> Nx.squeeze(Nx.tensor([[[[1]]], [[[2]]]], names: [:x, :y, :z, :i]))
#Nx.Tensor<
s64[x: 2]
[1, 2]
>
iex> Nx.squeeze(Nx.tensor([[1, 2, 3]], names: [:x, :y]), axes: [:x])
#Nx.Tensor<
s64[y: 3]
[1, 2, 3]
>
iex> Nx.squeeze(Nx.tensor([[1], [2]], names: [:x, :y]), axes: [:y])
#Nx.Tensor<
s64[x: 2]
[1, 2]
>
error-cases
Error cases
iex> Nx.squeeze(Nx.tensor([[1, 2, 3], [4, 5, 6]]), axes: [1])
** (ArgumentError) cannot squeeze dimensions whose sizes are not 1, got 3 for dimension 1
iex> Nx.squeeze(Nx.tensor([[[[[1]]]]]), axes: [0, 0])
** (ArgumentError) axes [0, 0] must be unique integers between 0 and 4
Creates a new tensor by repeating the input tensor along the given axes.
If the tensor
has less dimensions than the repetitions given,
the tensor will grow in dimensionality.
If the tensor
has more dimensions than the repetitions given,
tiling is done from the rightmost dimensions (i.e. if the input
shape is {1,2,3}
and repetitions = [2]
, the result is the same
as if repetitions = [1,1,2]
).
examples
Examples
iex> a = Nx.tensor([0, 1, 2])
iex> Nx.tile(a, [2])
#Nx.Tensor<
s64[6]
[0, 1, 2, 0, 1, 2]
>
iex> Nx.tile(a, [1, 2])
#Nx.Tensor<
s64[1][6]
[
[0, 1, 2, 0, 1, 2]
]
>
iex> Nx.tile(a, [2, 2])
#Nx.Tensor<
s64[2][6]
[
[0, 1, 2, 0, 1, 2],
[0, 1, 2, 0, 1, 2]
]
>
iex> Nx.tile(a, [2, 1])
#Nx.Tensor<
s64[2][3]
[
[0, 1, 2],
[0, 1, 2]
]
>
iex> Nx.tile(a, [2, 1, 2])
#Nx.Tensor<
s64[2][1][6]
[
[
[0, 1, 2, 0, 1, 2]
],
[
[0, 1, 2, 0, 1, 2]
]
]
>
iex> b = Nx.tensor([[1,2],[3,4]])
iex> Nx.tile(b, [2])
#Nx.Tensor<
s64[2][4]
[
[1, 2, 1, 2],
[3, 4, 3, 4]
]
>
iex> Nx.tile(b, [2, 1])
#Nx.Tensor<
s64[4][2]
[
[1, 2],
[3, 4],
[1, 2],
[3, 4]
]
>
iex> Nx.tile(b, [1, 2])
#Nx.Tensor<
s64[2][4]
[
[1, 2, 1, 2],
[3, 4, 3, 4]
]
>
iex> c = Nx.tensor([1,2,3,4])
iex> Nx.tile(c, [4,1])
#Nx.Tensor<
s64[4][4]
[
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4],
[1, 2, 3, 4]
]
>
error-cases
Error cases
iex> Nx.tile(Nx.tensor([1,2]), 1.0)
** (ArgumentError) repetitions must be a list of integers, got: 1.0
iex> Nx.tile(Nx.tensor([1,2]), [1, 1.0])
** (ArgumentError) repetitions must be a list of integers, got: [1, 1.0]
iex> Nx.tile(Nx.tensor([1,2]), nil)
** (ArgumentError) repetitions must be a list of integers, got: nil
Transposes a tensor to the given axes
.
If no axes are given, the default behavior is to reverse the order of the original tensor's axes.
The axes is a list of integers or dimension names containing how the new dimensions must be ordered. The highest dimension is zero.
examples
Examples
iex> Nx.transpose(Nx.tensor(1))
#Nx.Tensor<
s64
1
>
iex> Nx.transpose(Nx.iota({2, 3, 4}, names: [:x, :y, :z]))
#Nx.Tensor<
s64[z: 4][y: 3][x: 2]
[
[
[0, 12],
[4, 16],
[8, 20]
],
[
[1, 13],
[5, 17],
[9, 21]
],
[
[2, 14],
[6, 18],
[10, 22]
],
[
[3, 15],
[7, 19],
[11, 23]
]
]
>
iex> Nx.transpose(Nx.tensor(1), axes: [])
#Nx.Tensor<
s64
1
>
iex> Nx.transpose(Nx.iota({2, 3, 4}, names: [:batch, :x, :y]), axes: [2, 1, :batch])
#Nx.Tensor<
s64[y: 4][x: 3][batch: 2]
[
[
[0, 12],
[4, 16],
[8, 20]
],
[
[1, 13],
[5, 17],
[9, 21]
],
[
[2, 14],
[6, 18],
[10, 22]
],
[
[3, 15],
[7, 19],
[11, 23]
]
]
>
iex> Nx.transpose(Nx.iota({2, 3, 4}, names: [:batch, :x, :y]), axes: [:y, :batch, :x])
#Nx.Tensor<
s64[y: 4][batch: 2][x: 3]
[
[
[0, 4, 8],
[12, 16, 20]
],
[
[1, 5, 9],
[13, 17, 21]
],
[
[2, 6, 10],
[14, 18, 22]
],
[
[3, 7, 11],
[15, 19, 23]
]
]
>
iex> Nx.transpose(Nx.iota({2, 3, 4}, names: [:batch, :x, :y]), axes: [:batch, :y, :x])
#Nx.Tensor<
s64[batch: 2][y: 4][x: 3]
[
[
[0, 4, 8],
[1, 5, 9],
[2, 6, 10],
[3, 7, 11]
],
[
[12, 16, 20],
[13, 17, 21],
[14, 18, 22],
[15, 19, 23]
]
]
>
errors
Errors
iex> Nx.transpose(Nx.iota({2, 2}, names: [:batch, :x]), axes: [:batch])
** (ArgumentError) expected length of permutation (1) to match rank of shape (2)
iex> Nx.transpose(Nx.iota({2, 2}), axes: [1, 2])
** (ArgumentError) given axis (2) invalid for shape with rank 2
Link to this section Functions: Type
Changes the type of a tensor.
Note conversion between float and integers truncates the
result. Consider using round/1
, floor/1
, or ceil/1
before casting from float to integer to guarantee consistent
behavior.
Casting from a higher precision may lead to an overflow or underflow, which is platform and compiler dependent behaviour.
Casting of non-finite types to integer types are handled such as:
- negative infinity becomes the minimum value for said type
- positive infinity becomes the maximum value for said type
- nan becomes zero
examples
Examples
iex> Nx.as_type(Nx.tensor([0, 1, 2], names: [:data]), :f32)
#Nx.Tensor<
f32[data: 3]
[0.0, 1.0, 2.0]
>
iex> Nx.as_type(Nx.tensor([0.0, 1.0, 2.0], names: [:data]), :bf16)
#Nx.Tensor<
bf16[data: 3]
[0.0, 1.0, 2.0]
>
iex> Nx.as_type(Nx.tensor([0.0, 1.0, 2.0], names: [:data]), :s64)
#Nx.Tensor<
s64[data: 3]
[0, 1, 2]
>
Casting numbers as complex will return the corresponding complex with 0 imaginary component:
iex> Nx.as_type(Nx.tensor([1, -2]), :c64)
#Nx.Tensor<
c64[2]
[1.0+0.0i, -2.0+0.0i]
>
Casting complex numbers will return their real parts as the target type:
iex> Nx.as_type(Nx.tensor([Complex.new(1, 2), Complex.new(0, 3), Complex.new(4, 5)]), :f64)
#Nx.Tensor<
f64[3]
[1.0, 0.0, 4.0]
>
iex> Nx.as_type(Nx.tensor([Complex.new(-1, 2), Complex.new(-2, 3), Complex.new(3, -4)]), :s64)
#Nx.Tensor<
s64[3]
[-1, -2, 3]
>
Casting of non-finite values to integer types convert to pre-determined integer values:
iex> non_finite = Nx.tensor([:infinity, :nan, :neg_infinity])
iex> Nx.as_type(non_finite, :u8)
#Nx.Tensor<
u8[3]
[255, 0, 0]
>
iex> Nx.as_type(non_finite, :s32)
#Nx.Tensor<
s32[3]
[2147483647, 0, -2147483648]
>
Non-finite values between float types are preserved:
iex> non_finite = Nx.tensor([:infinity, :nan])
iex> Nx.as_type(non_finite, :f64)
#Nx.Tensor<
f64[2]
[Inf, NaN]
>
iex> Nx.as_type(non_finite, :f16)
#Nx.Tensor<
f16[2]
[Inf, NaN]
>
Changes the type of a tensor, using a bitcast.
The width of input tensor's type must match the width
of the output type. bitcast/1
does not change the
underlying tensor data, but instead changes how
the tensor data is viewed.
Machines with different floating-point representations will give different results.
For complex numbers, the last axis will change in size depending on whether you are upcasting or downcasting.
examples
Examples
iex> t = Nx.bitcast(Nx.tensor([0, 0, 0], names: [:data], type: :s32), :f32)
#Nx.Tensor<
f32[data: 3]
[0.0, 0.0, 0.0]
>
iex> Nx.bitcast(t, :s32)
#Nx.Tensor<
s32[data: 3]
[0, 0, 0]
>
error-cases
Error cases
iex> Nx.bitcast(Nx.tensor([0, 1, 2], names: [:data], type: :s16), :f32)
** (ArgumentError) input type width must match new type width, got input type {:s, 16} and output type {:f, 32}
iex> Nx.bitcast(Nx.tensor([0], type: :c64), :s64)
** (ArgumentError) Nx.bitcast/2 does not support complex inputs
iex> Nx.bitcast(Nx.tensor([0], type: :s64), :c64)
** (ArgumentError) Nx.bitcast/2 does not support complex inputs
Returns the type of the tensor.
See Nx.Type
for more information.
examples
Examples
iex> Nx.type(Nx.tensor([1, 2, 3]))
{:s, 64}
iex> Nx.type(Nx.tensor([1, 2, 3], type: :f32))
{:f, 32}
iex> Nx.type(1)
{:s, 64}
iex> Nx.type(1.0)
{:f, 32}
Link to this section Functions: Window
Returns the maximum over each window of size window_dimensions
in the given tensor, producing a tensor that contains the same
number of elements as valid positions of the window.
You may optionally specify :strides
which is a tuple
of non-zero steps to take along each axis between
each window.
You may also optionally specify :padding
which is either
one of :valid
(no padding) or :same
(pad so output shape
is the same as input shape) or a general padding configuration
for each dimension in the input tensor. Your padding configuration
cannot include any negative pad values. You may only specify
padding for the high and low edges of the given dimension. Pads
with the minimum value for the type of the given tensor.
examples
Examples
iex> Nx.window_max(Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]]), {1, 2, 1})
#Nx.Tensor<
s64[2][1][3]
[
[
[4, 5, 6]
],
[
[4, 5, 6]
]
]
>
iex> t = Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]])
iex> Nx.window_max(t, {2, 2, 1}, strides: [1, 2, 3], padding: [{0, 1}, {2, 0}, {1, 1}])
#Nx.Tensor<
s64[2][2][2]
[
[
[-9223372036854775808, -9223372036854775808],
[-9223372036854775808, 6]
],
[
[-9223372036854775808, -9223372036854775808],
[-9223372036854775808, 6]
]
]
>
iex> t = Nx.tensor([[[4.0, 2.0, 3.0], [2.0, 5.0, 6.5]], [[1.2, 2.2, 3.2], [4.0, 5.0, 6.2]]])
iex> Nx.window_max(t, {2, 1, 1}, strides: [2, 1, 1], padding: [{1, 1}, {0, 0}, {1, 1}])
#Nx.Tensor<
f32[2][2][5]
[
[
[-Inf, 4.0, 2.0, 3.0, -Inf],
[-Inf, 2.0, 5.0, 6.5, -Inf]
],
[
[-Inf, 1.2000000476837158, 2.200000047683716, 3.200000047683716, -Inf],
[-Inf, 4.0, 5.0, 6.199999809265137, -Inf]
]
]
>
iex> t = Nx.tensor([[[4, 2, 1, 3], [4, 2, 1, 7]], [[1, 2, 5, 7], [1, 8, 9, 2]]])
iex> opts = [strides: [2, 1, 1], padding: :valid, window_dilations: [1, 2, 2]]
iex> Nx.window_max(t, {1, 1, 2}, opts)
#Nx.Tensor<
s64[1][2][2]
[
[
[4, 3],
[4, 7]
]
]
>
Averages over each window of size window_dimensions
in the
given tensor, producing a tensor that contains the same
number of elements as valid positions of the window.
You may optionally specify :strides
which is a tuple
of non-zero steps to take along each axis between
each window.
You may also optionally specify :padding
which is either
one of :valid
(no padding) or :same
(pad so output shape
is the same as input shape) or a general padding configuration
for each dimension in the input tensor. Your padding configuration
cannot include any negative pad values. You may only specify
padding for the high and low edges of the given dimension. Pads
with 0
.
examples
Examples
iex> t = Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]])
iex> Nx.window_mean(t, {1, 2, 1})
#Nx.Tensor<
f32[2][1][3]
[
[
[2.5, 3.5, 4.5]
],
[
[2.5, 3.5, 4.5]
]
]
>
iex> t = Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]])
iex> Nx.window_mean(t, {2, 2, 1}, strides: [1, 2, 3], padding: [{0, 1}, {2, 0}, {1, 1}])
#Nx.Tensor<
f32[2][2][2]
[
[
[0.0, 0.0],
[0.0, 4.5]
],
[
[0.0, 0.0],
[0.0, 2.25]
]
]
>
iex> t = Nx.tensor([[[4.0, 2.0, 3.0], [2.0, 5.0, 6.5]], [[1.2, 2.2, 3.2], [4.0, 5.0, 6.2]]])
iex> Nx.window_mean(t, {2, 1, 1}, strides: [2, 1, 1], padding: [{1, 1}, {0, 0}, {1, 1}])
#Nx.Tensor<
f32[2][2][5]
[
[
[0.0, 2.0, 1.0, 1.5, 0.0],
[0.0, 1.0, 2.5, 3.25, 0.0]
],
[
[0.0, 0.6000000238418579, 1.100000023841858, 1.600000023841858, 0.0],
[0.0, 2.0, 2.5, 3.0999999046325684, 0.0]
]
]
>
iex> t = Nx.tensor([[[4, 2, 1, 3], [4, 2, 1, 7]], [[1, 2, 5, 7], [1, 8, 9, 2]]])
iex> opts = [strides: [2, 1, 1], padding: :valid, window_dilations: [1, 2, 1]]
iex> Nx.window_mean(t, {1, 1, 2}, opts)
#Nx.Tensor<
f32[1][2][3]
[
[
[3.0, 1.5, 2.0],
[3.0, 1.5, 4.0]
]
]
>
iex> t = Nx.tensor([[[4, 2, 1, 3], [4, 2, 1, 7]], [[1, 2, 5, 7], [1, 8, 9, 2]]])
iex> opts = [strides: [2, 1, 1], padding: :valid, window_dilations: [1, 2, 2]]
iex> Nx.window_mean(t, {1, 1, 2}, opts)
#Nx.Tensor<
f32[1][2][2]
[
[
[2.5, 2.5],
[2.5, 4.5]
]
]
>
Returns the minimum over each window of size window_dimensions
in the given tensor, producing a tensor that contains the same
number of elements as valid positions of the window.
You may optionally specify :strides
which is a tuple
of non-zero steps to take along each axis between
each window.
You may also optionally specify :padding
which is either
one of :valid
(no padding) or :same
(pad so output shape
is the same as input shape) or a general padding configuration
for each dimension in the input tensor. Your padding configuration
cannot include any negative pad values. You may only specify
padding for the high and low edges of the given dimension. Pads
with the maximum value for the type of the given tensor.
examples
Examples
iex> Nx.window_min(Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]]), {1, 2, 1})
#Nx.Tensor<
s64[2][1][3]
[
[
[1, 2, 3]
],
[
[1, 2, 3]
]
]
>
iex> t = Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]])
iex> Nx.window_min(t, {2, 2, 1}, strides: [1, 2, 3], padding: [{0, 1}, {2, 0}, {1, 1}])
#Nx.Tensor<
s64[2][2][2]
[
[
[9223372036854775807, 9223372036854775807],
[9223372036854775807, 3]
],
[
[9223372036854775807, 9223372036854775807],
[9223372036854775807, 3]
]
]
>
iex> t = Nx.tensor([[[4.0, 2.0, 3.0], [2.0, 5.0, 6.5]], [[1.2, 2.2, 3.2], [4.0, 5.0, 6.2]]])
iex> Nx.window_min(t, {2, 1, 1}, strides: [2, 1, 1], padding: [{1, 1}, {0, 0}, {1, 1}])
#Nx.Tensor<
f32[2][2][5]
[
[
[Inf, 4.0, 2.0, 3.0, Inf],
[Inf, 2.0, 5.0, 6.5, Inf]
],
[
[Inf, 1.2000000476837158, 2.200000047683716, 3.200000047683716, Inf],
[Inf, 4.0, 5.0, 6.199999809265137, Inf]
]
]
>
iex> t = Nx.tensor([[[4, 2, 1, 3], [4, 2, 1, 7]], [[1, 2, 5, 7], [1, 8, 9, 2]]])
iex> opts = [strides: [2, 1, 1], padding: :valid, window_dilations: [1, 2, 2]]
iex> Nx.window_min(t, {1, 1, 2}, opts)
#Nx.Tensor<
s64[1][2][2]
[
[
[1, 2],
[1, 2]
]
]
>
Returns the product over each window of size window_dimensions
in the given tensor, producing a tensor that contains the same
number of elements as valid positions of the window.
The rank of the input tensor and the window dimensions must match.
You may optionally specify :strides
which is a tuple
of non-zero steps to take along each axis between
each window.
You may also optionally specify :padding
which is either
one of :valid
(no padding) or :same
(pad so output shape
is the same as input shape) or a general padding configuration
for each dimension in the input tensor. Your padding configuration
cannot include any negative pad values. You may only specify
padding for the high and low edges of the given dimension. Pads
with 1.
examples
Examples
iex> Nx.window_product(Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]]), {1, 2, 1})
#Nx.Tensor<
s64[2][1][3]
[
[
[4, 10, 18]
],
[
[4, 10, 18]
]
]
>
iex> t = Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]])
iex> Nx.window_product(t, {2, 2, 1}, strides: [1, 2, 3], padding: [{0, 1}, {2, 0}, {1, 1}])
#Nx.Tensor<
s64[2][2][2]
[
[
[1, 1],
[1, 324]
],
[
[1, 1],
[1, 18]
]
]
>
iex> t = Nx.tensor([[[4.0, 2.0, 3.0], [2.0, 5.0, 6.5]], [[1.2, 2.2, 3.2], [4.0, 5.0, 6.2]]])
iex> Nx.window_product(t, {2, 1, 1}, strides: [2, 1, 1], padding: [{1, 1}, {0, 0}, {1, 1}])
#Nx.Tensor<
f32[2][2][5]
[
[
[1.0, 4.0, 2.0, 3.0, 1.0],
[1.0, 2.0, 5.0, 6.5, 1.0]
],
[
[1.0, 1.2000000476837158, 2.200000047683716, 3.200000047683716, 1.0],
[1.0, 4.0, 5.0, 6.199999809265137, 1.0]
]
]
>
iex> t = Nx.tensor([[[4, 2, 1, 3], [4, 2, 1, 7]], [[1, 2, 5, 7], [1, 8, 9, 2]]])
iex> opts = [strides: [2, 1, 1], padding: :valid, window_dilations: [1, 2, 2]]
iex> Nx.window_product(t, {1, 1, 2}, opts)
#Nx.Tensor<
s64[1][2][2]
[
[
[4, 6],
[4, 14]
]
]
>
Reduces over each window of size dimensions
in the given tensor, producing a tensor that contains the same
number of elements as valid positions of the window.
The rank of the input tensor and the window dimensions must match.
You may optionally specify :strides
which is a tuple
of non-zero steps to take along each axis between
each window.
You may also optionally specify :padding
which is either
one of :valid
(no padding) or :same
(pad so output shape
is the same as input shape) or a general padding configuration
for each dimension in the input tensor. Your padding configuration
cannot include any negative pad values. You may only specify
padding for the high and low edges of the given dimension. The
padding value is equal to the initial value passed to acc
.
The initial value must be a number or a scalar shaped tensor.
examples
Examples
iex> init_value = Nx.Constants.min_finite(:s64)
iex> t = Nx.tensor([[1, 2, 3, 4], [4, 5, 6, 7], [7, 8, 9, 10], [11, 12, 13, 14]])
iex> Nx.window_reduce(t, init_value, {2, 2}, fn x, acc -> Nx.max(x, acc) end)
#Nx.Tensor<
s64[3][3]
[
[5, 6, 7],
[8, 9, 10],
[12, 13, 14]
]
>
iex> init_value = Nx.Constants.min_finite(:s64)
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
iex> opts = [padding: :same, strides: [1, 1]]
iex> Nx.window_reduce(t, init_value, {2, 2}, opts, fn x, acc -> Nx.max(x, acc) end)
#Nx.Tensor<
s64[3][3]
[
[5, 6, 6],
[8, 9, 9],
[8, 9, 9]
]
>
iex> t = Nx.tensor([[1, 2, 3], [4, 5, 6]])
iex> opts = [padding: :same, strides: [1, 1]]
iex> Nx.window_reduce(t, 0, {1, 2}, opts, fn x, acc -> Nx.add(x, acc) end)
#Nx.Tensor<
s64[2][3]
[
[3, 5, 3],
[9, 11, 6]
]
>
iex> t = Nx.tensor([[[4, 2, 1, 3], [4, 2, 1, 7]], [[1, 2, 5, 7], [1, 8, 9, 2]]])
iex> opts = [padding: :valid, strides: [2, 1, 1], window_dilations: [1, 1, 2]]
iex> Nx.window_reduce(t, 0, {1, 1, 2}, opts, fn x, acc -> Nx.add(x, acc) end)
#Nx.Tensor<
s64[1][2][2]
[
[
[5, 5],
[5, 9]
]
]
>
window_scatter_max(tensor, source, init_value, window_dimensions, opts \\ [])
View SourcePerforms a window_reduce
to select the maximum index in each
window of the input tensor according to and scatters source tensor
to corresponding maximum indices in the output tensor.
Output tensor is initialized as a full tensor with values
init_value
. If indices overlap, adds overlapping source values.
The shape of the source tensor must match the valid windows in the
input tensor. This means the shape of the source tensor must match
the shape of the input tensor after a window_reduce
op with padding
padding
and strides strides
.
This function is the gradient of window_max
.
examples
Examples
iex> t = Nx.tensor([
...> [7, 2, 5, 3, 10, 2],
...> [3, 8, 9, 3, 4, 2],
...> [1, 5, 7, 5, 6, 1],
...> [0, 6, 2, 7, 2, 8]
...> ])
iex> opts = [strides: [2, 3], padding: :valid]
iex> Nx.window_scatter_max(t, Nx.tensor([[2, 6], [3, 1]]), 0, {2, 3}, opts)
#Nx.Tensor<
s64[4][6]
[
[0, 0, 0, 0, 6, 0],
[0, 0, 2, 0, 0, 0],
[0, 0, 3, 0, 0, 0],
[0, 0, 0, 0, 0, 1]
]
>
iex> t = Nx.tensor([
...> [7, 2, 5, 3, 8],
...> [3, 8, 9, 3, 4],
...> [1, 5, 7, 5, 6],
...> [0, 6, 2, 10, 2]
...> ])
iex> opts = [strides: [2, 2], padding: :valid]
iex> Nx.window_scatter_max(t, Nx.tensor([[2, 6], [3, 1]]), 0, {2, 3}, opts)
#Nx.Tensor<
s64[4][5]
[
[0, 0, 0, 0, 0],
[0, 0, 8, 0, 0],
[0, 0, 3, 0, 0],
[0, 0, 0, 1, 0]
]
>
window_scatter_min(tensor, source, init_value, window_dimensions, opts \\ [])
View SourcePerforms a window_reduce
to select the minimum index in each
window of the input tensor according to and scatters source tensor
to corresponding minimum indices in the output tensor.
Output tensor is initialized as a full tensor with values
init_value
. If indices overlap, adds overlapping source values.
The shape of the source tensor must match the valid windows in the
input tensor. This means the shape of the source tensor must match
the shape of the input tensor after a window_reduce
op with padding
padding
and strides strides
.
This function is the gradient of window_min
.
examples
Examples
iex> t = Nx.tensor([
...> [7, 2, 5, 3, 10, 2],
...> [3, 8, 9, 3, 4, 2],
...> [1, 5, 7, 5, 6, 1],
...> [0, 6, 2, 7, 2, 8]
...> ])
iex> opts = [strides: [2, 3], padding: :valid]
iex> Nx.window_scatter_min(t, Nx.tensor([[2, 6], [3, 1]]), 0, {2, 3}, opts)
#Nx.Tensor<
s64[4][6]
[
[0, 2, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 6],
[0, 0, 0, 0, 0, 1],
[3, 0, 0, 0, 0, 0]
]
>
iex> t = Nx.tensor([
...> [7, 2, 5, 3, 8],
...> [3, 8, 9, 3, 4],
...> [1, 5, 7, 5, 6],
...> [0, 6, 2, 10, 2]
...> ])
iex> opts = [strides: [2, 2], padding: :valid]
iex> Nx.window_scatter_min(t, Nx.tensor([[2, 6], [3, 1]]), 0, {2, 3}, opts)
#Nx.Tensor<
s64[4][5]
[
[0, 2, 0, 0, 0],
[0, 0, 0, 6, 0],
[0, 0, 0, 0, 0],
[3, 0, 0, 0, 1]
]
>
Sums over each window of size window_dimensions
in the
given tensor, producing a tensor that contains the same
number of elements as valid positions of the window.
You may optionally specify :strides
which is a tuple
of non-zero steps to take along each axis between
each window.
You may also optionally specify :padding
which is either
one of :valid
(no padding) or :same
(pad so output shape
is the same as input shape) or a general padding configuration
for each dimension in the input tensor. Your padding configuration
cannot include any negative pad values. You may only specify
padding for the high and low edges of the given dimension. Pads
with 0
.
examples
Examples
iex> t = Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]])
iex> Nx.window_sum(t, {1, 2, 1})
#Nx.Tensor<
s64[2][1][3]
[
[
[5, 7, 9]
],
[
[5, 7, 9]
]
]
>
iex> t = Nx.tensor([[[1, 2, 3], [4, 5, 6]], [[1, 2, 3], [4, 5, 6]]])
iex> Nx.window_sum(t, {2, 2, 1}, strides: [1, 2, 3], padding: [{0, 1}, {2, 0}, {1, 1}])
#Nx.Tensor<
s64[2][2][2]
[
[
[0, 0],
[0, 18]
],
[
[0, 0],
[0, 9]
]
]
>
iex> t = Nx.tensor([[[4.0, 2.0, 3.0], [2.0, 5.0, 6.5]], [[1.2, 2.2, 3.2], [4.0, 5.0, 6.2]]])
iex> Nx.window_sum(t, {2, 1, 1}, strides: [2, 1, 1], padding: [{1, 1}, {0, 0}, {1, 1}])
#Nx.Tensor<
f32[2][2][5]
[
[
[0.0, 4.0, 2.0, 3.0, 0.0],
[0.0, 2.0, 5.0, 6.5, 0.0]
],
[
[0.0, 1.2000000476837158, 2.200000047683716, 3.200000047683716, 0.0],
[0.0, 4.0, 5.0, 6.199999809265137, 0.0]
]
]
>
iex> t = Nx.tensor([[[4, 2, 1, 3], [4, 2, 1, 7]], [[1, 2, 5, 7], [1, 8, 9, 2]]])
iex> opts = [strides: [2, 1, 1], padding: :valid, window_dilations: [1, 2, 1]]
iex> Nx.window_sum(t, {1, 1, 2}, opts)
#Nx.Tensor<
s64[1][2][3]
[
[
[6, 3, 4],
[6, 3, 8]
]
]
>
iex> t = Nx.tensor([[[4, 2, 1, 3], [4, 2, 1, 7]], [[1, 2, 5, 7], [1, 8, 9, 2]]])
iex> opts = [strides: [2, 1, 1], padding: :valid, window_dilations: [1, 2, 2]]
iex> Nx.window_sum(t, {1, 1, 2}, opts)
#Nx.Tensor<
s64[1][2][2]
[
[
[5, 5],
[5, 9]
]
]
>
iex> t = Nx.tensor([[[4, 2, 1, 3], [4, 2, 1, 7]], [[1, 2, 5, 7], [1, 8, 9, 2]]])
iex> opts = [strides: [2, 1, 1], padding: [{2, 1}, {3, 1}, {1, 0}], window_dilations: [1, 2, 2]]
iex> Nx.window_sum(t, {2, 1, 2}, opts)
#Nx.Tensor<
s64[2][6][3]
[
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[0, 0, 0]
],
[
[0, 0, 0],
[0, 0, 0],
[0, 0, 0],
[4, 11, 14],
[10, 15, 19],
[0, 0, 0]
]
]
>