burn_tensor/tensor/linalg/
vector_norm.rs1use crate::check::unwrap_dim_index;
2use crate::tensor::Tensor;
3use crate::{
4 AsIndex, ElementConversion,
5 kind::{Numeric, Ordered},
6};
7#[allow(unused_imports)]
8use num_traits::float::Float;
9#[derive(Debug, Clone, Copy, PartialEq)]
11pub enum Norm {
12 L0,
14
15 L1,
17
18 L2,
20
21 LInf,
23
24 LNegInf,
26
27 Lp(f64),
29}
30
31impl Norm {
32 pub fn to_exponent(self) -> f64 {
34 use Norm::*;
35 match self {
36 L0 => 0.0,
37 L1 => 1.0,
38 L2 => 2.0,
39 LInf => f64::INFINITY,
40 LNegInf => f64::NEG_INFINITY,
41 Lp(p) => p,
42 }
43 }
44}
45
46impl From<u32> for Norm {
47 fn from(value: u32) -> Self {
48 use Norm::*;
49 match value {
50 0 => L0,
51 1 => L1,
52 2 => L2,
53 u32::MAX => LInf,
54 _ => Lp(value as f64),
55 }
56 }
57}
58
59impl From<i32> for Norm {
60 fn from(value: i32) -> Self {
61 use Norm::*;
62 match value {
63 0 => L0,
64 1 => L1,
65 2 => L2,
66 i32::MAX => LInf,
67 i32::MIN => LNegInf,
68 _ => Lp(value as f64),
69 }
70 }
71}
72
73impl From<f32> for Norm {
74 fn from(value: f32) -> Self {
75 use Norm::*;
76 match value {
77 0.0 => L0,
78 1.0 => L1,
79 2.0 => L2,
80 f32::INFINITY => LInf,
81 f32::NEG_INFINITY => LNegInf,
82 _ => Lp(value as f64),
83 }
84 }
85}
86
87impl From<f64> for Norm {
88 fn from(value: f64) -> Self {
89 use Norm::*;
90 match value {
91 0.0 => L0,
92 1.0 => L1,
93 2.0 => L2,
94 f64::INFINITY => LInf,
95 f64::NEG_INFINITY => LNegInf,
96 _ => Lp(value),
97 }
98 }
99}
100
101pub fn vector_norm<const D: usize>(
120 x: Tensor<D>,
121 norm: impl Into<Norm>,
122 dim: impl AsIndex,
123) -> Tensor<D> {
124 let dim = unwrap_dim_index(dim.try_dim_index(D), "Vector Norm");
125 lp_norm_impl(x, norm.into().to_exponent(), dim)
126}
127
128pub fn lp_norm<const D: usize>(x: Tensor<D>, p: f64, dim: impl AsIndex) -> Tensor<D> {
149 let dim = unwrap_dim_index(dim.try_dim_index(D), "Lp Norm");
150 lp_norm_impl(x, p, dim)
151}
152
153fn lp_norm_impl<const D: usize>(x: Tensor<D>, p: f64, dim: usize) -> Tensor<D> {
154 match p {
155 0.0 => l0_norm_impl(x, dim),
156 1.0 => l1_norm_impl(x, dim),
157 2.0 => l2_norm_impl(x, dim),
158 p if is_even_integer(p) => lp_signed_norm(x, p as u32, dim),
159 f64::INFINITY => max_abs_norm_impl(x, dim),
160 f64::NEG_INFINITY => min_abs_norm_impl(x, dim),
161 _ => lp_norm_base(x, p, dim),
162 }
163}
164
165pub fn vector_normalize<const D: usize, E: ElementConversion>(
181 x: Tensor<D>,
182 norm: impl Into<Norm>,
183 dim: impl AsIndex,
184 eps: E,
185) -> Tensor<D> {
186 let dim = unwrap_dim_index(dim.try_dim_index(D), "Vector Normalize");
187 let norm = lp_norm_impl(x.clone(), norm.into().to_exponent(), dim).clamp_min(eps);
188 x / norm
189}
190
191pub fn l0_norm<const D: usize, K>(x: Tensor<D, K>, dim: impl AsIndex) -> Tensor<D, K>
203where
204 K: Numeric,
205{
206 let dim = unwrap_dim_index(dim.try_dim_index(D), "L0 Norm");
207 l0_norm_impl(x, dim)
208}
209
210fn l0_norm_impl<const D: usize, K>(x: Tensor<D, K>, dim: usize) -> Tensor<D, K>
211where
212 K: Numeric,
213{
214 x.zeros_like()
215 .mask_fill(x.not_equal_scalar(0), 1)
216 .sum_dim(dim)
217}
218
219pub fn l1_norm<const D: usize, K>(x: Tensor<D, K>, dim: impl AsIndex) -> Tensor<D, K>
233where
234 K: Numeric,
235{
236 let dim = unwrap_dim_index(dim.try_dim_index(D), "L1 Norm");
237 l1_norm_impl(x, dim)
238}
239
240fn l1_norm_impl<const D: usize, K>(x: Tensor<D, K>, dim: usize) -> Tensor<D, K>
241where
242 K: Numeric,
243{
244 x.abs().sum_dim(dim)
245}
246
247pub fn l2_norm<const D: usize>(x: Tensor<D>, dim: impl AsIndex) -> Tensor<D> {
259 let dim = unwrap_dim_index(dim.try_dim_index(D), "L2 Norm");
260 l2_norm_impl(x, dim)
261}
262
263pub(super) fn l2_norm_impl<const D: usize>(x: Tensor<D>, dim: usize) -> Tensor<D> {
264 x.square().sum_dim(dim).sqrt()
265}
266
267fn is_even_integer(x: f64) -> bool {
268 x.fract() == 0.0 && (x as i64) % 2 == 0
269}
270
271fn lp_signed_norm<const D: usize>(x: Tensor<D>, p: u32, dim: usize) -> Tensor<D> {
275 x.powi_scalar(p).sum_dim(dim).powf_scalar(1. / (p as f64))
276}
277
278fn lp_norm_base<const D: usize>(x: Tensor<D>, p: f64, dim: usize) -> Tensor<D> {
285 x.abs().powf_scalar(p).sum_dim(dim).powf_scalar(1. / p)
286}
287
288pub fn max_abs_norm<const D: usize, K>(x: Tensor<D, K>, dim: impl AsIndex) -> Tensor<D, K>
300where
301 K: Ordered,
302{
303 let dim = unwrap_dim_index(dim.try_dim_index(D), "Max Abs Norm");
304 max_abs_norm_impl(x, dim)
305}
306
307fn max_abs_norm_impl<const D: usize, K>(x: Tensor<D, K>, dim: usize) -> Tensor<D, K>
308where
309 K: Ordered,
310{
311 x.max_abs_dim(dim)
312}
313
314pub fn min_abs_norm<const D: usize, K>(x: Tensor<D, K>, dim: impl AsIndex) -> Tensor<D, K>
326where
327 K: Ordered,
328{
329 let dim = unwrap_dim_index(dim.try_dim_index(D), "Min Abs Norm");
330 min_abs_norm_impl(x, dim)
331}
332
333fn min_abs_norm_impl<const D: usize, K>(x: Tensor<D, K>, dim: usize) -> Tensor<D, K>
334where
335 K: Ordered,
336{
337 x.abs().min_dim(dim)
338}