View Source Nx.LinAlg (Nx v0.4.0)

Nx conveniences for linear algebra.

Link to this section Summary

Functions

Returns the adjoint of a given tensor.

Performs a Cholesky decomposition of a batch of square matrices.

Calculates the determinant of batched square matrices.

Calculates the Eigenvalues and Eigenvectors of batched Hermitian 2-D matrices.

Inverts a batch of square matrices.

Calculates the A = PLU decomposition of batched square 2-D matrices A.

Produces the tensor taken to the given power by matrix dot-product.

Calculates the p-norm of a tensor.

Calculates the QR decomposition of a tensor with shape {..., M, N}.

Solves the system AX = B.

Calculates the Singular Value Decomposition of batched 2-D matrices.

Solve the equation a x = b for x, assuming a is a batch of triangular matrices. Can also solve x a = b for x. See the :left_side option below.

Link to this section Functions

Returns the adjoint of a given tensor.

If the input tensor is real it transposes it's two inner-most axes. If the input tensor is complex, it additionally applies Nx.conjugate/1 to it.

examples

Examples

iex> Nx.LinAlg.adjoint(Nx.tensor([[1, 2], [3, 4]]))
#Nx.Tensor<
  s64[2][2]
  [
    [1, 3],
    [2, 4]
  ]
>

iex> Nx.LinAlg.adjoint(Nx.tensor([[1, Complex.new(0, 2)], [3, Complex.new(0, -4)]]))
#Nx.Tensor<
  c64[2][2]
  [
    [1.0+0.0i, 3.0+0.0i],
    [0.0-2.0i, 0.0+4.0i]
  ]
>

Performs a Cholesky decomposition of a batch of square matrices.

The matrices must be positive-definite and either Hermitian if complex or symmetric if real. An error is raised by the default backend if those conditions are not met. Other backends may emit undefined behaviour.

examples

Examples

iex> Nx.LinAlg.cholesky(Nx.tensor([[20.0, 17.6], [17.6, 16.0]]))
#Nx.Tensor<
  f32[2][2]
  [
    [4.4721360206604, 0.0],
    [3.9354796409606934, 0.7155413031578064]
  ]
>

iex> Nx.LinAlg.cholesky(Nx.tensor([[[2.0, 3.0], [3.0, 5.0]], [[1.0, 0.0], [0.0, 1.0]]]))
#Nx.Tensor<
  f32[2][2][2]
  [
    [
      [1.4142135381698608, 0.0],
      [2.1213202476501465, 0.7071067690849304]
    ],
    [
      [1.0, 0.0],
      [0.0, 1.0]
    ]
  ]
>

iex> t = Nx.tensor([
...>   [6.0, 3.0, 4.0, 8.0],
...>   [3.0, 6.0, 5.0, 1.0],
...>   [4.0, 5.0, 10.0, 7.0],
...>   [8.0, 1.0, 7.0, 25.0]
...> ])
iex> Nx.LinAlg.cholesky(t)
#Nx.Tensor<
  f32[4][4]
  [
    [2.4494898319244385, 0.0, 0.0, 0.0],
    [1.2247449159622192, 2.1213202476501465, 0.0, 0.0],
    [1.632993221282959, 1.4142135381698608, 2.309401035308838, 0.0],
    [3.265986442565918, -1.4142135381698608, 1.5877132415771484, 3.132491111755371]
  ]
>

iex> Nx.LinAlg.cholesky(Nx.tensor([[1.0, Complex.new(0, -2)], [Complex.new(0, 2), 5.0]]))
#Nx.Tensor<
  c64[2][2]
  [
    [1.0+0.0i, 0.0+0.0i],
    [0.0+2.0i, 1.0+0.0i]
  ]
>

error-cases

Error cases

iex> Nx.LinAlg.cholesky(Nx.tensor([[1.0, 2.0], [3.0, 4.0]]))
** (ArgumentError) matrix must be hermitian, a matrix is hermitian iff X = adjoint(X)

Calculates the determinant of batched square matrices.

examples

Examples

For 2x2 and 3x3, the results are given by the closed formulas:

iex> Nx.LinAlg.determinant(Nx.tensor([[1, 2], [3, 4]]))
#Nx.Tensor<
  f32
  -2.0
>

iex> Nx.LinAlg.determinant(Nx.tensor([[1.0, 2.0, 3.0], [1.0, -2.0, 3.0], [7.0, 8.0, 9.0]]))
#Nx.Tensor<
  f32
  48.0
>

When there are linearly dependent rows or columns, the determinant is 0:

iex> Nx.LinAlg.determinant(Nx.tensor([[1.0, 0.0], [3.0, 0.0]]))
#Nx.Tensor<
  f32
  0.0
>

iex> Nx.LinAlg.determinant(Nx.tensor([[1.0, 2.0, 3.0], [-1.0, -2.0, -3.0], [4.0, 5.0, 6.0]]))
#Nx.Tensor<
  f32
  0.0
>

The determinant can also be calculated when the axes are bigger than 3:

iex> Nx.LinAlg.determinant(Nx.tensor([
...> [1, 0, 0, 0],
...> [0, 1, 2, 3],
...> [0, 1, -2, 3],
...> [0, 7, 8, 9.0]
...> ]))
#Nx.Tensor<
  f32
  48.0
>

iex> Nx.LinAlg.determinant(Nx.tensor([
...> [0, 0, 0, 0, -1],
...> [0, 1, 2, 3, 0],
...> [0, 1, -2, 3, 0],
...> [0, 7, 8, 9, 0],
...> [1, 0, 0, 0, 0]
...> ]))
#Nx.Tensor<
  f32
  48.0
>

iex> Nx.LinAlg.determinant(Nx.tensor([
...> [[2, 4, 6, 7], [5, 1, 8, 8], [1, 7, 3, 1], [3, 9, 2, 4]],
...> [[2, 5, 1, 3], [4, 1, 7, 9], [6, 8, 3, 2], [7, 8, 1, 4]]
...> ]))
#Nx.Tensor<
  f32[2]
  [630.0, 630.0]
>

If the axes are named, their names are not preserved in the output:

iex> two_by_two = Nx.tensor([[1, 2], [3, 4]], names: [:x, :y])
iex> Nx.LinAlg.determinant(two_by_two)
#Nx.Tensor<
  f32
  -2.0
>

iex> three_by_three = Nx.tensor([[1.0, 2.0, 3.0], [1.0, -2.0, 3.0], [7.0, 8.0, 9.0]], names: [:x, :y])
iex> Nx.LinAlg.determinant(three_by_three)
#Nx.Tensor<
  f32
  48.0
>

Also supports complex inputs:

iex> t = Nx.tensor([[1, 0, 0], [0, Complex.new(0, 2), 0], [0, 0, 3]])
iex> Nx.LinAlg.determinant(t)
#Nx.Tensor<
  c64
  0.0+6.0i
>

iex> t = Nx.tensor([[0, 0, 0, 1], [0, Complex.new(0, 2), 0, 0], [0, 0, 3, 0], [1, 0, 0, 0]])
iex> Nx.LinAlg.determinant(t)
#Nx.Tensor<
  c64
  0.0-6.0i
>
Link to this function

eigh(tensor, opts \\ [])

View Source

Calculates the Eigenvalues and Eigenvectors of batched Hermitian 2-D matrices.

It returns {eigenvals, eigenvecs}.

options

Options

  • :max_iter - integer. Defaults to 50_000 Number of maximum iterations before stopping the decomposition

  • :eps - float. Defaults to 1.0e-10 Tolerance applied during the decomposition

Note not all options apply to all backends, as backends may have specific optimizations that render these mechanisms unnecessary.

examples

Examples

iex> {eigenvals, eigenvecs} = Nx.LinAlg.eigh(Nx.tensor([[1, 0], [0, 2]]))
iex> Nx.round(eigenvals)
#Nx.Tensor<
  f32[2]
  [1.0, 2.0]
>
iex> eigenvecs
#Nx.Tensor<
  f32[2][2]
  [
    [1.0, 0.0],
    [0.0, 1.0]
  ]
>

iex> {eigenvals, eigenvecs} = Nx.LinAlg.eigh(Nx.tensor([[0, 1, 2], [1, 0, 2], [2, 2, 3]]))
iex> Nx.round(eigenvals)
#Nx.Tensor<
  f32[3]
  [5.0, -1.0, -1.0]
>
iex> eigenvecs
#Nx.Tensor<
  f32[3][3]
  [
    [0.4082472324371338, 0.9128734469413757, 0.0],
    [0.40824851393699646, -0.18257413804531097, 0.8944271802902222],
    [0.8164970278739929, -0.36514827609062195, -0.4472135901451111]
  ]
>

iex> {eigenvals, eigenvecs} = Nx.LinAlg.eigh(Nx.tensor([[[2, 5],[5, 6]], [[1, 0], [0, 4]]]))
iex> Nx.round(eigenvals)
#Nx.Tensor<
  f32[2][2]
  [
    [9.0, -1.0],
    [1.0, 4.0]
  ]
>
iex> eigenvecs
#Nx.Tensor<
  f32[2][2][2]
  [
    [
      [0.5606290698051453, -0.828070342540741],
      [0.8280670642852783, 0.5606313347816467]
    ],
    [
      [1.0, 0.0],
      [0.0, 1.0]
    ]
  ]
>

error-cases

Error cases

iex> Nx.LinAlg.eigh(Nx.tensor([[1, 2, 3], [4, 5, 6]]))
** (ArgumentError) tensor must be a square matrix or a batch of square matrices, got shape: {2, 3}

iex> Nx.LinAlg.eigh(Nx.tensor([[1, 2], [3, 4]]))
** (ArgumentError) matrix must be hermitian, a matrix is hermitian iff X = adjoint(X)

Inverts a batch of square matrices.

For non-square matrices, use svd/2 for pseudo-inverse calculations.

examples

Examples

iex> a = Nx.tensor([[1, 2, 1, 1], [0, 1, 0, 1], [0, 0, 1, 1], [0 , 0, 0, 1]])
iex> a_inv = Nx.LinAlg.invert(a)
#Nx.Tensor<
  f32[4][4]
  [
    [1.0, -2.0, -1.0, 2.0],
    [0.0, 1.0, 0.0, -1.0],
    [0.0, 0.0, 1.0, -1.0],
    [0.0, 0.0, 0.0, 1.0]
  ]
>
iex> Nx.dot(a, a_inv)
#Nx.Tensor<
  f32[4][4]
  [
    [1.0, 0.0, 0.0, 0.0],
    [0.0, 1.0, 0.0, 0.0],
    [0.0, 0.0, 1.0, 0.0],
    [0.0, 0.0, 0.0, 1.0]
  ]
>
iex> Nx.dot(a_inv, a)
#Nx.Tensor<
  f32[4][4]
  [
    [1.0, 0.0, 0.0, 0.0],
    [0.0, 1.0, 0.0, 0.0],
    [0.0, 0.0, 1.0, 0.0],
    [0.0, 0.0, 0.0, 1.0]
  ]
>

iex> a = Nx.tensor([[[1, 2], [0, 1]], [[1, 1], [0, 1]]])
iex> a_inv = Nx.LinAlg.invert(a)
#Nx.Tensor<
  f32[2][2][2]
  [
    [
      [1.0, -2.0],
      [0.0, 1.0]
    ],
    [
      [1.0, -1.0],
      [0.0, 1.0]
    ]
  ]
>
iex> Nx.dot(a, [2], [0], a_inv, [1], [0])
#Nx.Tensor<
  f32[2][2][2]
  [
    [
      [1.0, 0.0],
      [0.0, 1.0]
    ],
    [
      [1.0, 0.0],
      [0.0, 1.0]
    ]
  ]
>
iex> Nx.dot(a_inv, [2], [0], a, [1], [0])
#Nx.Tensor<
  f32[2][2][2]
  [
    [
      [1.0, 0.0],
      [0.0, 1.0]
    ],
    [
      [1.0, 0.0],
      [0.0, 1.0]
    ]
  ]
>

error-cases

Error cases

iex> Nx.LinAlg.invert(Nx.tensor([[3, 0, 0, 0], [2, 1, 0, 0]]))
** (ArgumentError) invert/1 expects a square matrix or a batch of square matrices, got tensor with shape: {2, 4}

iex> Nx.LinAlg.invert(Nx.tensor([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 1]]))
** (ArgumentError) can't solve for singular matrix

Calculates the A = PLU decomposition of batched square 2-D matrices A.

options

Options

  • :eps - Rounding error threshold that can be applied during the factorization

examples

Examples

iex> {p, l, u} = Nx.LinAlg.lu(Nx.tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]]))
iex> p
#Nx.Tensor<
  s64[3][3]
  [
    [0, 0, 1],
    [0, 1, 0],
    [1, 0, 0]
  ]
>
iex> l
#Nx.Tensor<
  f32[3][3]
  [
    [1.0, 0.0, 0.0],
    [0.5714285969734192, 1.0, 0.0],
    [0.1428571492433548, 2.0, 1.0]
  ]
>
iex> u
#Nx.Tensor<
  f32[3][3]
  [
    [7.0, 8.0, 9.0],
    [0.0, 0.4285714328289032, 0.8571428656578064],
    [0.0, 0.0, 0.0]
  ]
>
iex> p |> Nx.dot(l) |> Nx.dot(u)
#Nx.Tensor<
  f32[3][3]
  [
    [1.0, 2.0, 3.0],
    [4.0, 5.0, 6.0],
    [7.0, 8.0, 9.0]
  ]
>

iex> {p, l, u} = Nx.LinAlg.lu(Nx.tensor([[1, 0, 1], [-1, 0, -1], [1, 1, 1]]))
iex> p
#Nx.Tensor<
  s64[3][3]
  [
    [1, 0, 0],
    [0, 0, 1],
    [0, 1, 0]
  ]
>
iex> l
#Nx.Tensor<
  f32[3][3]
  [
    [1.0, 0.0, 0.0],
    [1.0, 1.0, 0.0],
    [-1.0, 0.0, 1.0]
  ]
>
iex> u
#Nx.Tensor<
  f32[3][3]
  [
    [1.0, 0.0, 1.0],
    [0.0, 1.0, 0.0],
    [0.0, 0.0, 0.0]
  ]
>
iex> p |> Nx.dot(l) |> Nx.dot(u)
#Nx.Tensor<
  f32[3][3]
  [
    [1.0, 0.0, 1.0],
    [-1.0, 0.0, -1.0],
    [1.0, 1.0, 1.0]
  ]
>

iex> {p, l, u} = Nx.LinAlg.lu(Nx.tensor([[[9, 8, 7], [6, 5, 4], [3, 2, 1]], [[-1, 0, -1], [1, 0, 1], [1, 1, 1]]]))
iex> p
#Nx.Tensor<
  s64[2][3][3]
  [
    [
      [1, 0, 0],
      [0, 1, 0],
      [0, 0, 1]
    ],
    [
      [1, 0, 0],
      [0, 0, 1],
      [0, 1, 0]
    ]
  ]
>
iex> l
#Nx.Tensor<
  f32[2][3][3]
  [
    [
      [1.0, 0.0, 0.0],
      [0.6666666865348816, 1.0, 0.0],
      [0.3333333432674408, 2.0, 1.0]
    ],
    [
      [1.0, 0.0, 0.0],
      [-1.0, 1.0, 0.0],
      [-1.0, 0.0, 1.0]
    ]
  ]
>
iex> u
#Nx.Tensor<
  f32[2][3][3]
  [
    [
      [9.0, 8.0, 7.0],
      [0.0, -0.3333333432674408, -0.6666666865348816],
      [0.0, 0.0, 0.0]
    ],
    [
      [-1.0, 0.0, -1.0],
      [0.0, 1.0, 0.0],
      [0.0, 0.0, 0.0]
    ]
  ]
>
iex> p |> Nx.dot([2], [0], l, [1], [0]) |> Nx.dot([2], [0], u, [1], [0])
#Nx.Tensor<
  f32[2][3][3]
  [
    [
      [9.0, 8.0, 7.0],
      [6.0, 5.0, 4.0],
      [3.0, 2.0, 1.0]
    ],
    [
      [-1.0, 0.0, -1.0],
      [1.0, 0.0, 1.0],
      [1.0, 1.0, 1.0]
    ]
  ]
>

error-cases

Error cases

iex> Nx.LinAlg.lu(Nx.tensor([[1, 1, 1, 1], [-1, 4, 4, -1], [4, -2, 2, 0]]))
** (ArgumentError) tensor must be a square matrix or a batch of square matrices, got shape: {3, 4}
Link to this function

matrix_power(tensor, power)

View Source

Produces the tensor taken to the given power by matrix dot-product.

The input is always a tensor of batched square matrices and an integer, and the output is a tensor of the same dimensions as the input tensor.

The dot-products are unrolled inside defn.

examples

Examples

iex> Nx.LinAlg.matrix_power(Nx.tensor([[1, 2], [3, 4]]), 0)
#Nx.Tensor<
  s64[2][2]
  [
    [1, 0],
    [0, 1]
  ]
>

iex> Nx.LinAlg.matrix_power(Nx.tensor([[1, 2], [3, 4]]), 6)
#Nx.Tensor<
  s64[2][2]
  [
    [5743, 8370],
    [12555, 18298]
  ]
>

iex> Nx.LinAlg.matrix_power(Nx.eye(3), 65535)
#Nx.Tensor<
  s64[3][3]
  [
    [1, 0, 0],
    [0, 1, 0],
    [0, 0, 1]
  ]
>

iex> Nx.LinAlg.matrix_power(Nx.tensor([[1, 2], [3, 4]]), -1)
#Nx.Tensor<
  f32[2][2]
  [
    [-2.0, 1.0],
    [1.5, -0.5]
  ]
>

iex> Nx.LinAlg.matrix_power(Nx.iota({2, 2, 2}), 3)
#Nx.Tensor<
  s64[2][2][2]
  [
    [
      [6, 11],
      [22, 39]
    ],
    [
      [514, 615],
      [738, 883]
    ]
  ]
>

iex> Nx.LinAlg.matrix_power(Nx.iota({2, 2, 2}), -3)
#Nx.Tensor<
  f32[2][2][2]
  [
    [
      [-4.875, 1.375],
      [2.75, -0.75]
    ],
    [
      [-110.375, 76.875],
      [92.25, -64.25]
    ]
  ]
>

iex> Nx.LinAlg.matrix_power(Nx.tensor([[1, 2], [3, 4], [5, 6]]), 1)
** (ArgumentError) matrix_power/2 expects a square matrix or a batch of square matrices, got tensor with shape: {3, 2}
Link to this function

norm(tensor, opts \\ [])

View Source

Calculates the p-norm of a tensor.

For the 0-norm, the norm is the number of non-zero elements in the tensor.

options

Options

  • :axes - defines the axes upon which the norm will be calculated. Applies only on 2-norm for 2-D tensors. Default: nil.
  • :ord - defines which norm will be calculated according to the table below. Default: 2
ord2-D1-D
nilFrobenius norm2-norm
:nuclearNuclear norm-
:frobeniusFrobenius norm-
:infmax(sum(abs(x), axes: [1]))max(abs(x))
:neg_infmin(sum(abs(x), axes: [1]))min(abs(x))
0-Number of non-zero elements
1max(sum(abs(x), axes: [0]))as below
-1min(sum(abs(x), axes: [0]))as below
22-normas below
-2smallest singular valueas below
other-power(sum(power(abs(x), p)), 1/p)

examples

Examples

vector-norms

Vector norms

iex> Nx.LinAlg.norm(Nx.tensor([3, 4]))
#Nx.Tensor<
  f32
  5.0
>

iex> Nx.LinAlg.norm(Nx.tensor([3, 4]), ord: 1)
#Nx.Tensor<
  f32
  7.0
>

iex> Nx.LinAlg.norm(Nx.tensor([3, -4]), ord: :inf)
#Nx.Tensor<
  f32
  4.0
>

iex> Nx.LinAlg.norm(Nx.tensor([3, -4]), ord: :neg_inf)
#Nx.Tensor<
  f32
  3.0
>

iex> Nx.LinAlg.norm(Nx.tensor([3, -4, 0, 0]), ord: 0)
#Nx.Tensor<
  f32
  2.0
>

matrix-norms

Matrix norms

iex> Nx.LinAlg.norm(Nx.tensor([[3, -1], [2, -4]]), ord: -1)
#Nx.Tensor<
  f32
  5.0
>

iex> Nx.LinAlg.norm(Nx.tensor([[3, -2], [2, -4]]), ord: 1)
#Nx.Tensor<
  f32
  6.0
>

iex> Nx.LinAlg.norm(Nx.tensor([[3, -2], [2, -4]]), ord: :neg_inf)
#Nx.Tensor<
  f32
  5.0
>

iex> Nx.LinAlg.norm(Nx.tensor([[3, -2], [2, -4]]), ord: :inf)
#Nx.Tensor<
  f32
  6.0
>

iex> Nx.LinAlg.norm(Nx.tensor([[3, 0], [0, -4]]), ord: :frobenius)
#Nx.Tensor<
  f32
  5.0
>

iex> Nx.LinAlg.norm(Nx.tensor([[1, 0, 0], [0, -4, 0], [0, 0, 9]]), ord: :nuclear)
#Nx.Tensor<
  f32
  14.0
>

iex> Nx.LinAlg.norm(Nx.tensor([[1, 0, 0], [0, -4, 0], [0, 0, 9]]), ord: -2)
#Nx.Tensor<
  f32
  1.0
>

iex> Nx.LinAlg.norm(Nx.tensor([[3, 0], [0, -4]]))
#Nx.Tensor<
  f32
  5.0
>

iex> Nx.LinAlg.norm(Nx.tensor([[3, 4], [0, -4]]), axes: [1])
#Nx.Tensor<
  f32[2]
  [5.0, 4.0]
>

iex> Nx.LinAlg.norm(Nx.tensor([[Complex.new(0, 3), 4], [4, 0]]), axes: [0])
#Nx.Tensor<
  f32[2]
  [5.0, 4.0]
>

iex> Nx.LinAlg.norm(Nx.tensor([[Complex.new(0, 3), 0], [4, 0]]), ord: :neg_inf)
#Nx.Tensor<
  f32
  3.0
>

iex> Nx.LinAlg.norm(Nx.tensor([[0, 0], [0, 0]]))
#Nx.Tensor<
  f32
  0.0
>

error-cases

Error cases

iex> Nx.LinAlg.norm(Nx.tensor([3, 4]), ord: :frobenius)
** (ArgumentError) expected a 2-D tensor for ord: :frobenius, got a 1-D tensor

Calculates the QR decomposition of a tensor with shape {..., M, N}.

options

Options

  • :mode - Can be one of :reduced, :complete. Defaults to :reduced For the following, K = min(M, N)

    • :reduced - returns q and r with shapes {..., M, K} and {..., K, N}
    • :complete - returns q and r with shapes {..., M, M} and {..., M, N}
  • :eps - Rounding error threshold that can be applied during the triangularization

examples

Examples

iex> {q, r} = Nx.LinAlg.qr(Nx.tensor([[-3, 2, 1], [0, 1, 1], [0, 0, -1]]))
iex> q
#Nx.Tensor<
  f32[3][3]
  [
    [1.0, 0.0, 0.0],
    [0.0, 1.0, 0.0],
    [0.0, 0.0, 1.0]
  ]
>
iex> r
#Nx.Tensor<
  f32[3][3]
  [
    [-3.0, 2.0, 1.0],
    [0.0, 1.0, 1.0],
    [0.0, 0.0, -1.0]
  ]
>

iex> t = Nx.tensor([[3, 2, 1], [0, 1, 1], [0, 0, 1]])
iex> {q, r} = Nx.LinAlg.qr(t)
iex> q
#Nx.Tensor<
  f32[3][3]
  [
    [1.0, 0.0, 0.0],
    [0.0, 1.0, 0.0],
    [0.0, 0.0, 1.0]
  ]
>
iex> r
#Nx.Tensor<
  f32[3][3]
  [
    [3.0, 2.0, 1.0],
    [0.0, 1.0, 1.0],
    [0.0, 0.0, 1.0]
  ]
>

iex> {qs, rs} = Nx.LinAlg.qr(Nx.tensor([[[-3, 2, 1], [0, 1, 1], [0, 0, -1]],[[3, 2, 1], [0, 1, 1], [0, 0, 1]]]))
iex> qs
#Nx.Tensor<
  f32[2][3][3]
  [
    [
      [1.0, 0.0, 0.0],
      [0.0, 1.0, 0.0],
      [0.0, 0.0, 1.0]
    ],
    [
      [1.0, 0.0, 0.0],
      [0.0, 1.0, 0.0],
      [0.0, 0.0, 1.0]
    ]
  ]
>
iex> rs
#Nx.Tensor<
  f32[2][3][3]
  [
    [
      [-3.0, 2.0, 1.0],
      [0.0, 1.0, 1.0],
      [0.0, 0.0, -1.0]
    ],
    [
      [3.0, 2.0, 1.0],
      [0.0, 1.0, 1.0],
      [0.0, 0.0, 1.0]
    ]
  ]
>

iex> t = Nx.tensor([[3, 2, 1], [0, 1, 1], [0, 0, 1], [0, 0, 1]], type: :f32)
iex> {q, r} = Nx.LinAlg.qr(t, mode: :reduced)
iex> q
#Nx.Tensor<
  f32[4][3]
  [
    [1.0, 0.0, 0.0],
    [0.0, 1.0, 0.0],
    [0.0, 0.0, 0.7071067690849304],
    [0.0, 0.0, 0.7071067690849304]
  ]
>
iex> r
#Nx.Tensor<
  f32[3][3]
  [
    [3.0, 2.0, 1.0],
    [0.0, 1.0, 1.0],
    [0.0, 0.0, 1.4142135381698608]
  ]
>

iex> t = Nx.tensor([[3, 2, 1], [0, 1, 1], [0, 0, 1], [0, 0, 0]], type: :f32)
iex> {q, r} = Nx.LinAlg.qr(t, mode: :complete)
iex> q
#Nx.Tensor<
  f32[4][4]
  [
    [1.0, 0.0, 0.0, 0.0],
    [0.0, 1.0, 0.0, 0.0],
    [0.0, 0.0, 1.0, 0.0],
    [0.0, 0.0, 0.0, 1.0]
  ]
>
iex> r
#Nx.Tensor<
  f32[4][3]
  [
    [3.0, 2.0, 1.0],
    [0.0, 1.0, 1.0],
    [0.0, 0.0, 1.0],
    [0.0, 0.0, 0.0]
  ]
>

error-cases

Error cases

iex> Nx.LinAlg.qr(Nx.tensor([[[1, 1, 1, 1], [-1, 4, 4, -1], [4, -2, 2, 0]]]))
** (ArgumentError) tensor must have at least as many rows as columns in the last two axes, got 3 rows and 4 columns

iex> Nx.LinAlg.qr(Nx.tensor([1, 2, 3, 4, 5]))
** (ArgumentError) tensor must have at least rank 2, got rank 1 with shape {5}

Solves the system AX = B.

A must have shape {..., n, n} and B must have shape {..., n, m} or {..., n}. X has the same shape as B.

examples

Examples

iex> a = Nx.tensor([[1, 3, 2, 1], [2, 1, 0, 0], [1, 0, 1, 0], [1, 1, 1, 1]])
iex> Nx.LinAlg.solve(a, Nx.tensor([-3, 0, 4, -2])) |> Nx.round()
#Nx.Tensor<
  f32[4]
  [1.0, -2.0, 3.0, -4.0]
>

iex> a = Nx.tensor([[1, 0, 1], [1, 1, 0], [1, 1, 1]], type: :f64)
iex> Nx.LinAlg.solve(a, Nx.tensor([0, 2, 1])) |> Nx.round()
#Nx.Tensor<
  f64[3]
  [1.0, 1.0, -1.0]
>

iex> a = Nx.tensor([[1, 0, 1], [1, 1, 0], [0, 1, 1]])
iex> b = Nx.tensor([[2, 2, 3], [2, 2, 4], [2, 0, 1]])
iex> Nx.LinAlg.solve(a, b) |> Nx.round()
#Nx.Tensor<
  f32[3][3]
  [
    [1.0, 2.0, 3.0],
    [1.0, 0.0, 1.0],
    [1.0, 0.0, 0.0]
  ]
>

iex> a = Nx.tensor([[[14, 10], [9, 9]], [[4, 11], [2, 3]]])
iex> b = Nx.tensor([[[2, 4], [3, 2]], [[1, 5], [-3, -1]]])
iex> Nx.LinAlg.solve(a, b) |> Nx.round()
#Nx.Tensor<
  f32[2][2][2]
  [
    [
      [0.0, 0.0],
      [1.0, 0.0]
    ],
    [
      [-4.0, -3.0],
      [1.0, 1.0]
    ]
  ]
>

If the axes are named, their names are not preserved in the output:

iex> a = Nx.tensor([[1, 0, 1], [1, 1, 0], [1, 1, 1]], names: [:x, :y])
iex> Nx.LinAlg.solve(a, Nx.tensor([0, 2, 1], names: [:z])) |> Nx.round()
#Nx.Tensor<
  f32[3]
  [1.0, 1.0, -1.0]
>

error-cases

Error cases

iex> Nx.LinAlg.solve(Nx.tensor([[1, 0], [0, 1]]), Nx.tensor([4, 2, 4, 2]))
** (ArgumentError) `b` tensor has incompatible dimensions, expected {2, 2} or {2}, got: {4}

iex> Nx.LinAlg.solve(Nx.tensor([[3, 0, 0, 0], [2, 1, 0, 0], [1, 1, 1, 1]]), Nx.tensor([4]))
** (ArgumentError) `a` tensor has incompatible dimensions, expected a square matrix or a batch of square matrices, got: {3, 4}

Calculates the Singular Value Decomposition of batched 2-D matrices.

It returns {u, s, vt} where the elements of s are sorted from highest to lowest.

options

Options

  • :max_iter - integer. Defaults to 1000 Number of maximum iterations before stopping the decomposition

  • :eps - float. Defaults to 1.0e-10 Tolerance applied during the decomposition

Note not all options apply to all backends, as backends may have specific optimizations that render these mechanisms unnecessary.

examples

Examples

iex> {u, s, v} = Nx.LinAlg.svd(Nx.tensor([[1, 0, 0], [0, 1, 0], [0, 0, -1]]))
iex> u
#Nx.Tensor<
  f32[3][3]
  [
    [1.0, 0.0, 0.0],
    [0.0, 1.0, 0.0],
    [0.0, 0.0, 1.0]
  ]
>
iex> s
#Nx.Tensor<
  f32[3]
  [1.0, 1.0, 1.0]
>
iex> v
#Nx.Tensor<
  f32[3][3]
  [
    [1.0, 0.0, 0.0],
    [0.0, 1.0, 0.0],
    [0.0, 0.0, -1.0]
  ]
>

iex> {u, s, vt} = Nx.LinAlg.svd(Nx.tensor([[2, 0, 0], [0, 3, 0], [0, 0, -1], [0, 0, 0]]))
iex> u
#Nx.Tensor<
  f32[4][4]
  [
    [1.0, 0.0, 0.0, 0.0],
    [0.0, 1.0, 0.0, 0.0],
    [0.0, 0.0, 1.0, 0.0],
    [0.0, 0.0, 0.0, 1.0]
  ]
>
iex> s
#Nx.Tensor<
  f32[3]
  [3.0, 2.0, 1.0]
>
iex> vt
#Nx.Tensor<
  f32[3][3]
  [
    [0.0, 1.0, 0.0],
    [1.0, 0.0, 0.0],
    [0.0, 0.0, -1.0]
  ]
>
Link to this function

triangular_solve(a, b, opts \\ [])

View Source

Solve the equation a x = b for x, assuming a is a batch of triangular matrices. Can also solve x a = b for x. See the :left_side option below.

b must either be a batch of square matrices with the same dimensions as a or a batch of 1-D tensors with as many rows as a. Batch dimensions of a and b must be the same.

options

Options

The following options are defined in order of precedence

  • :transform_a - Defines op(a), depending on its value. Can be one of:
    • :none -> op(a) = a
    • :transpose -> op(a) = transpose(a) Defaults to :none
  • :lower - When true, defines the a matrix as lower triangular. If false, a is upper triangular. Defaults to true
  • :left_side - When true, solves the system as op(A).X = B. Otherwise, solves X.op(A) = B. Defaults to true.

examples

Examples

iex> a = Nx.tensor([[3, 0, 0, 0], [2, 1, 0, 0], [1, 0, 1, 0], [1, 1, 1, 1]])
iex> Nx.LinAlg.triangular_solve(a, Nx.tensor([4, 2, 4, 2]))
#Nx.Tensor<
  f32[4]
  [1.3333333730697632, -0.6666666865348816, 2.6666667461395264, -1.3333333730697632]
>

iex> a = Nx.tensor([[1, 0, 0], [1, 1, 0], [1, 1, 1]], type: :f64)
iex> Nx.LinAlg.triangular_solve(a, Nx.tensor([1, 2, 1]))
#Nx.Tensor<
  f64[3]
  [1.0, 1.0, -1.0]
>

iex> a = Nx.tensor([[1, 0, 0], [1, 1, 0], [0, 1, 1]])
iex> b = Nx.tensor([[1, 2, 3], [2, 2, 4], [2, 0, 1]])
iex> Nx.LinAlg.triangular_solve(a, b)
#Nx.Tensor<
  f32[3][3]
  [
    [1.0, 2.0, 3.0],
    [1.0, 0.0, 1.0],
    [1.0, 0.0, 0.0]
  ]
>

iex> a = Nx.tensor([[1, 1, 1, 1], [0, 1, 0, 1], [0, 0, 1, 2], [0, 0, 0, 3]])
iex> Nx.LinAlg.triangular_solve(a, Nx.tensor([2, 4, 2, 4]), lower: false)
#Nx.Tensor<
  f32[4]
  [-1.3333333730697632, 2.6666667461395264, -0.6666666865348816, 1.3333333730697632]
>

iex> a = Nx.tensor([[1, 0, 0], [1, 1, 0], [1, 2, 1]])
iex> b = Nx.tensor([[0, 2, 1], [1, 1, 0], [3, 3, 1]])
iex> Nx.LinAlg.triangular_solve(a, b, left_side: false)
#Nx.Tensor<
  f32[3][3]
  [
    [-1.0, 0.0, 1.0],
    [0.0, 1.0, 0.0],
    [1.0, 1.0, 1.0]
  ]
>

iex> a = Nx.tensor([[1, 1, 1], [0, 1, 1], [0, 0, 1]], type: :f64)
iex> Nx.LinAlg.triangular_solve(a, Nx.tensor([1, 2, 1]), transform_a: :transpose, lower: false)
#Nx.Tensor<
  f64[3]
  [1.0, 1.0, -1.0]
>

iex> a = Nx.tensor([[1, 0, 0], [1, 1, 0], [1, 1, 1]], type: :f64)
iex> Nx.LinAlg.triangular_solve(a, Nx.tensor([1, 2, 1]), transform_a: :none)
#Nx.Tensor<
  f64[3]
  [1.0, 1.0, -1.0]
>

iex> a = Nx.tensor([[1, 0, 0], [1, 1, 0], [1, 2, 1]])
iex> b = Nx.tensor([[0, 1, 3], [2, 1, 3]])
iex> Nx.LinAlg.triangular_solve(a, b, left_side: false)
#Nx.Tensor<
  f32[2][3]
  [
    [2.0, -5.0, 3.0],
    [4.0, -5.0, 3.0]
  ]
>

iex> a = Nx.tensor([[1, 0, 0], [1, 1, 0], [1, 2, 1]])
iex> b = Nx.tensor([[0, 2], [3, 0], [0, 0]])
iex> Nx.LinAlg.triangular_solve(a, b, left_side: true)
#Nx.Tensor<
  f32[3][2]
  [
    [0.0, 2.0],
    [3.0, -2.0],
    [-6.0, 2.0]
  ]
>

iex> a = Nx.tensor([
...> [1, 0, 0],
...> [1, Complex.new(0, 1), 0],
...> [Complex.new(0, 1), 1, 1]
...>])
iex> b = Nx.tensor([1, -1, Complex.new(3, 3)])
iex> Nx.LinAlg.triangular_solve(a, b)
#Nx.Tensor<
  c64[3]
  [1.0+0.0i, 0.0+2.0i, 3.0+0.0i]
>

iex> a = Nx.tensor([[[1, 0], [2, 3]], [[4, 0], [5, 6]]])
iex> b = Nx.tensor([[2, -1], [3, 7]])
iex> Nx.LinAlg.triangular_solve(a, b)
#Nx.Tensor<
  f32[2][2]
  [
    [2.0, -1.6666666269302368],
    [0.75, 0.5416666865348816]
  ]
>

error-cases

Error cases

iex> Nx.LinAlg.triangular_solve(Nx.tensor([[3, 0, 0, 0], [2, 1, 0, 0]]), Nx.tensor([4, 2, 4, 2]))
** (ArgumentError) triangular_solve/3 expected a square matrix or a batch of square matrices, got tensor with shape: {2, 4}

iex> Nx.LinAlg.triangular_solve(Nx.tensor([[3, 0, 0, 0], [2, 1, 0, 0], [1, 1, 1, 1], [1, 1, 1, 1]]), Nx.tensor([4]))
** (ArgumentError) incompatible dimensions for a and b on triangular solve

iex> Nx.LinAlg.triangular_solve(Nx.tensor([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 1]]), Nx.tensor([4, 2, 4, 2]))
** (ArgumentError) can't solve for singular matrix

iex> a = Nx.tensor([[1, 0, 0], [1, 1, 0], [1, 1, 1]], type: :f64)
iex> Nx.LinAlg.triangular_solve(a, Nx.tensor([1, 2, 1]), transform_a: :conjugate)
** (ArgumentError) complex numbers not supported yet

iex> a = Nx.tensor([[1, 0, 0], [1, 1, 0], [1, 1, 1]], type: :f64)
iex> Nx.LinAlg.triangular_solve(a, Nx.tensor([1, 2, 1]), transform_a: :other)
** (ArgumentError) invalid value for :transform_a option, expected :none, :transpose, or :conjugate, got: :other