Skip to main content

nalgebra/base/
norm.rs

1#[cfg(all(feature = "alloc", not(feature = "std")))]
2use alloc::vec::Vec;
3
4use num::Zero;
5use std::ops::Neg;
6
7use crate::allocator::Allocator;
8use crate::base::{DefaultAllocator, Dim, DimName, Matrix, Normed, OMatrix, OVector};
9use crate::constraint::{SameNumberOfColumns, SameNumberOfRows, ShapeConstraint};
10use crate::storage::{Storage, StorageMut};
11use crate::{ComplexField, Scalar, SimdComplexField, Unit};
12use simba::scalar::ClosedNeg;
13use simba::simd::{SimdOption, SimdPartialOrd, SimdValue};
14
15// TODO: this should be be a trait on alga?
16/// A trait for abstract matrix norms.
17///
18/// This may be moved to the alga crate in the future.
19pub trait Norm<T: SimdComplexField> {
20    /// Apply this norm to the given matrix.
21    fn norm<R, C, S>(&self, m: &Matrix<T, R, C, S>) -> T::SimdRealField
22    where
23        R: Dim,
24        C: Dim,
25        S: Storage<T, R, C>;
26    /// Use the metric induced by this norm to compute the metric distance between the two given matrices.
27    fn metric_distance<R1, C1, S1, R2, C2, S2>(
28        &self,
29        m1: &Matrix<T, R1, C1, S1>,
30        m2: &Matrix<T, R2, C2, S2>,
31    ) -> T::SimdRealField
32    where
33        R1: Dim,
34        C1: Dim,
35        S1: Storage<T, R1, C1>,
36        R2: Dim,
37        C2: Dim,
38        S2: Storage<T, R2, C2>,
39        ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2>;
40}
41
42/// Euclidean norm of a vector, or Frobenius norm of a matrix.
43///
44/// Computes sqrt(sum |a_ij|^2) over all elements.
45///
46/// <div class="warning">
47/// For matrices, this is the Frobenius norm, not the matrix 2-norm (spectral norm).
48/// </div>
49#[derive(Copy, Clone, Debug)]
50pub struct EuclideanNorm;
51
52/// Entrywise Lp norm of a matrix or vector.
53///
54/// Computes (sum |a_ij|^p)^(1/p) over all elements.
55///
56/// <div class="warning">
57/// This does not match the standard mathematical definition of the matrix Lp norm.
58/// </div>
59#[derive(Copy, Clone, Debug)]
60pub struct LpNorm(pub i32);
61
62/// Induced matrix 1-norm (maximum absolute column sum).
63///
64/// Computes max_j sum_i |a_ij|.
65///
66/// For a column vector, this is the L1 norm.
67/// For a row vector, this is the L-infinity norm.
68#[derive(Copy, Clone, Debug)]
69pub struct OneNorm;
70
71/// Entrywise L-infinity norm of a matrix or vector.
72///
73/// Computes max |a_ij| over all elements.
74///
75/// For a vector this is the standard L-infinity norm.
76///
77/// <div class="warning">
78/// For matrices, this is the entrywise maximum, not the induced matrix infinity-norm.
79/// </div>
80#[derive(Copy, Clone, Debug)]
81pub struct UniformNorm;
82
83impl<T: SimdComplexField> Norm<T> for EuclideanNorm {
84    #[inline]
85    fn norm<R, C, S>(&self, m: &Matrix<T, R, C, S>) -> T::SimdRealField
86    where
87        R: Dim,
88        C: Dim,
89        S: Storage<T, R, C>,
90    {
91        m.norm_squared().simd_sqrt()
92    }
93
94    #[inline]
95    fn metric_distance<R1, C1, S1, R2, C2, S2>(
96        &self,
97        m1: &Matrix<T, R1, C1, S1>,
98        m2: &Matrix<T, R2, C2, S2>,
99    ) -> T::SimdRealField
100    where
101        R1: Dim,
102        C1: Dim,
103        S1: Storage<T, R1, C1>,
104        R2: Dim,
105        C2: Dim,
106        S2: Storage<T, R2, C2>,
107        ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2>,
108    {
109        m1.zip_fold(m2, T::SimdRealField::zero(), |acc, a, b| {
110            let diff = a - b;
111            acc + diff.simd_modulus_squared()
112        })
113        .simd_sqrt()
114    }
115}
116
117impl<T: SimdComplexField> Norm<T> for LpNorm {
118    #[inline]
119    fn norm<R, C, S>(&self, m: &Matrix<T, R, C, S>) -> T::SimdRealField
120    where
121        R: Dim,
122        C: Dim,
123        S: Storage<T, R, C>,
124    {
125        m.fold(T::SimdRealField::zero(), |a, b| {
126            a + b.simd_modulus().simd_powi(self.0)
127        })
128        .simd_powf(crate::convert(1.0 / (self.0 as f64)))
129    }
130
131    #[inline]
132    fn metric_distance<R1, C1, S1, R2, C2, S2>(
133        &self,
134        m1: &Matrix<T, R1, C1, S1>,
135        m2: &Matrix<T, R2, C2, S2>,
136    ) -> T::SimdRealField
137    where
138        R1: Dim,
139        C1: Dim,
140        S1: Storage<T, R1, C1>,
141        R2: Dim,
142        C2: Dim,
143        S2: Storage<T, R2, C2>,
144        ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2>,
145    {
146        m1.zip_fold(m2, T::SimdRealField::zero(), |acc, a, b| {
147            let diff = a - b;
148            acc + diff.simd_modulus().simd_powi(self.0)
149        })
150        .simd_powf(crate::convert(1.0 / (self.0 as f64)))
151    }
152}
153
154impl<T: SimdComplexField> Norm<T> for OneNorm {
155    #[inline]
156    fn norm<R, C, S>(&self, m: &Matrix<T, R, C, S>) -> T::SimdRealField
157    where
158        R: Dim,
159        C: Dim,
160        S: Storage<T, R, C>,
161    {
162        m.column_iter()
163            .map(|col| col.fold(T::SimdRealField::zero(), |a, b| a + b.simd_modulus()))
164            .fold(T::SimdRealField::zero(), T::SimdRealField::simd_max)
165    }
166
167    #[inline]
168    fn metric_distance<R1, C1, S1, R2, C2, S2>(
169        &self,
170        m1: &Matrix<T, R1, C1, S1>,
171        m2: &Matrix<T, R2, C2, S2>,
172    ) -> T::SimdRealField
173    where
174        R1: Dim,
175        C1: Dim,
176        S1: Storage<T, R1, C1>,
177        R2: Dim,
178        C2: Dim,
179        S2: Storage<T, R2, C2>,
180        ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2>,
181    {
182        m1.column_iter()
183            .zip(m2.column_iter())
184            .map(|(c1, c2)| {
185                c1.zip_fold(&c2, T::SimdRealField::zero(), |acc, a, b| {
186                    acc + (a - b).simd_modulus()
187                })
188            })
189            .fold(T::SimdRealField::zero(), T::SimdRealField::simd_max)
190    }
191}
192
193impl<T: SimdComplexField> Norm<T> for UniformNorm {
194    #[inline]
195    fn norm<R, C, S>(&self, m: &Matrix<T, R, C, S>) -> T::SimdRealField
196    where
197        R: Dim,
198        C: Dim,
199        S: Storage<T, R, C>,
200    {
201        // NOTE: we don't use `m.amax()` here because for the complex
202        // numbers this will return the max norm1 instead of the modulus.
203        m.fold(T::SimdRealField::zero(), |acc, a| {
204            acc.simd_max(a.simd_modulus())
205        })
206    }
207
208    #[inline]
209    fn metric_distance<R1, C1, S1, R2, C2, S2>(
210        &self,
211        m1: &Matrix<T, R1, C1, S1>,
212        m2: &Matrix<T, R2, C2, S2>,
213    ) -> T::SimdRealField
214    where
215        R1: Dim,
216        C1: Dim,
217        S1: Storage<T, R1, C1>,
218        R2: Dim,
219        C2: Dim,
220        S2: Storage<T, R2, C2>,
221        ShapeConstraint: SameNumberOfRows<R1, R2> + SameNumberOfColumns<C1, C2>,
222    {
223        m1.zip_fold(m2, T::SimdRealField::zero(), |acc, a, b| {
224            let val = (a - b).simd_modulus();
225            acc.simd_max(val)
226        })
227    }
228}
229
230/// # Magnitude and norms
231///
232/// Unless otherwise noted, the norm used throughout is the L2 norm for vectors and
233/// the Frobenius norm for matrices.
234impl<T: Scalar, R: Dim, C: Dim, S: Storage<T, R, C>> Matrix<T, R, C, S> {
235    /// Squared L2 norm of this vector, or squared Frobenius norm of this matrix.
236    #[inline]
237    #[must_use]
238    pub fn norm_squared(&self) -> T::SimdRealField
239    where
240        T: SimdComplexField,
241    {
242        let mut res = T::SimdRealField::zero();
243
244        for i in 0..self.ncols() {
245            let col = self.column(i);
246            res += col.dotc(&col).simd_real()
247        }
248
249        res
250    }
251
252    /// L2 norm of this vector, or Frobenius norm of this matrix.
253    ///
254    /// Use `.apply_norm` to apply a custom norm.
255    #[inline]
256    #[must_use]
257    pub fn norm(&self) -> T::SimdRealField
258    where
259        T: SimdComplexField,
260    {
261        self.norm_squared().simd_sqrt()
262    }
263
264    /// Distance between `self` and `rhs` using the L2 norm for vectors, or the Frobenius for matrices.
265    ///
266    /// Use `.apply_metric_distance` to apply a custom norm.
267    #[inline]
268    #[must_use]
269    pub fn metric_distance<R2, C2, S2>(&self, rhs: &Matrix<T, R2, C2, S2>) -> T::SimdRealField
270    where
271        T: SimdComplexField,
272        R2: Dim,
273        C2: Dim,
274        S2: Storage<T, R2, C2>,
275        ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
276    {
277        self.apply_metric_distance(rhs, &EuclideanNorm)
278    }
279
280    /// Uses the given `norm` to compute the norm of `self`.
281    ///
282    /// # Example
283    ///
284    /// ```
285    /// # use nalgebra::{Vector3, UniformNorm, LpNorm, EuclideanNorm};
286    ///
287    /// let v = Vector3::new(1.0, 2.0, 3.0);
288    /// assert_eq!(v.apply_norm(&UniformNorm), 3.0);
289    /// assert_eq!(v.apply_norm(&LpNorm(1)), 6.0);
290    /// assert_eq!(v.apply_norm(&EuclideanNorm), v.norm());
291    /// ```
292    #[inline]
293    #[must_use]
294    pub fn apply_norm(&self, norm: &impl Norm<T>) -> T::SimdRealField
295    where
296        T: SimdComplexField,
297    {
298        norm.norm(self)
299    }
300
301    /// Uses the metric induced by the given `norm` to compute the metric distance between `self` and `rhs`.
302    ///
303    /// # Example
304    ///
305    /// ```
306    /// # use nalgebra::{Vector3, UniformNorm, LpNorm, EuclideanNorm};
307    ///
308    /// let v1 = Vector3::new(1.0, 2.0, 3.0);
309    /// let v2 = Vector3::new(10.0, 20.0, 30.0);
310    ///
311    /// assert_eq!(v1.apply_metric_distance(&v2, &UniformNorm), 27.0);
312    /// assert_eq!(v1.apply_metric_distance(&v2, &LpNorm(1)), 27.0 + 18.0 + 9.0);
313    /// assert_eq!(v1.apply_metric_distance(&v2, &EuclideanNorm), (v1 - v2).norm());
314    /// ```
315    #[inline]
316    #[must_use]
317    pub fn apply_metric_distance<R2, C2, S2>(
318        &self,
319        rhs: &Matrix<T, R2, C2, S2>,
320        norm: &impl Norm<T>,
321    ) -> T::SimdRealField
322    where
323        T: SimdComplexField,
324        R2: Dim,
325        C2: Dim,
326        S2: Storage<T, R2, C2>,
327        ShapeConstraint: SameNumberOfRows<R, R2> + SameNumberOfColumns<C, C2>,
328    {
329        norm.metric_distance(self, rhs)
330    }
331
332    /// A synonym for the norm of this matrix.
333    ///
334    /// Aka the length.
335    ///
336    /// This function is simply implemented as a call to `norm()`
337    #[inline]
338    #[must_use]
339    pub fn magnitude(&self) -> T::SimdRealField
340    where
341        T: SimdComplexField,
342    {
343        self.norm()
344    }
345
346    /// A synonym for the squared norm of this matrix.
347    ///
348    /// Aka the squared length.
349    ///
350    /// This function is simply implemented as a call to `norm_squared()`
351    #[inline]
352    #[must_use]
353    pub fn magnitude_squared(&self) -> T::SimdRealField
354    where
355        T: SimdComplexField,
356    {
357        self.norm_squared()
358    }
359
360    /// Sets the magnitude of this vector.
361    #[inline]
362    pub fn set_magnitude(&mut self, magnitude: T::SimdRealField)
363    where
364        T: SimdComplexField,
365        S: StorageMut<T, R, C>,
366    {
367        let n = self.norm();
368        self.scale_mut(magnitude / n)
369    }
370
371    /// Returns a normalized version of this matrix.
372    #[inline]
373    #[must_use = "Did you mean to use normalize_mut()?"]
374    pub fn normalize(&self) -> OMatrix<T, R, C>
375    where
376        T: SimdComplexField,
377        DefaultAllocator: Allocator<R, C>,
378    {
379        self.unscale(self.norm())
380    }
381
382    /// Entrywise Lp norm of this matrix or vector.
383    ///
384    /// Computes (sum |a_ij|^p)^(1/p) over all elements.
385    ///
386    /// <div class="warning">
387    /// For matrices, this does not match the standard mathematical definition of the matrix Lp norm.
388    /// </div>
389    #[inline]
390    #[must_use]
391    pub fn lp_norm(&self, p: i32) -> T::SimdRealField
392    where
393        T: SimdComplexField,
394    {
395        self.apply_norm(&LpNorm(p))
396    }
397
398    /// Induced matrix 1-norm (maximum absolute column sum).
399    ///
400    /// Computes max_j sum_i |a_ij|.
401    ///
402    /// For a column vector, this is the L1 norm.
403    /// For a row vector, this is the L-infinity norm.
404    #[inline]
405    #[must_use]
406    pub fn one_norm(&self) -> T::SimdRealField
407    where
408        T: SimdComplexField,
409    {
410        self.apply_norm(&OneNorm)
411    }
412
413    /// Attempts to normalize `self`.
414    ///
415    /// The components of this matrix can be SIMD types.
416    #[inline]
417    #[must_use = "Did you mean to use simd_try_normalize_mut()?"]
418    pub fn simd_try_normalize(&self, min_norm: T::SimdRealField) -> SimdOption<OMatrix<T, R, C>>
419    where
420        T: SimdComplexField,
421        T::Element: Scalar,
422        DefaultAllocator: Allocator<R, C>,
423    {
424        let n = self.norm();
425        let le = n.clone().simd_le(min_norm);
426        let val = self.unscale(n);
427        SimdOption::new(val, le)
428    }
429
430    /// Sets the magnitude of this vector unless it is smaller than `min_magnitude`.
431    ///
432    /// If `self.magnitude()` is smaller than `min_magnitude`, it will be left unchanged.
433    /// Otherwise this is equivalent to: `*self = self.normalize() * magnitude`.
434    #[inline]
435    pub fn try_set_magnitude(&mut self, magnitude: T::RealField, min_magnitude: T::RealField)
436    where
437        T: ComplexField,
438        S: StorageMut<T, R, C>,
439    {
440        let n = self.norm();
441
442        if n > min_magnitude {
443            self.scale_mut(magnitude / n)
444        }
445    }
446
447    /// Returns a new vector with the same magnitude as `self` clamped between `0.0` and `max`.
448    #[inline]
449    #[must_use]
450    pub fn cap_magnitude(&self, max: T::RealField) -> OMatrix<T, R, C>
451    where
452        T: ComplexField,
453        DefaultAllocator: Allocator<R, C>,
454    {
455        let n = self.norm();
456
457        if n > max {
458            self.scale(max / n)
459        } else {
460            self.clone_owned()
461        }
462    }
463
464    /// Returns a new vector with the same magnitude as `self` clamped between `0.0` and `max`.
465    #[inline]
466    #[must_use]
467    pub fn simd_cap_magnitude(&self, max: T::SimdRealField) -> OMatrix<T, R, C>
468    where
469        T: SimdComplexField,
470        T::Element: Scalar,
471        DefaultAllocator: Allocator<R, C>,
472    {
473        let n = self.norm();
474        let scaled = self.scale(max.clone() / n.clone());
475        let use_scaled = n.simd_gt(max);
476        scaled.select(use_scaled, self.clone_owned())
477    }
478
479    /// Returns a normalized version of this matrix unless its norm as smaller or equal to `eps`.
480    ///
481    /// The components of this matrix cannot be SIMD types (see `simd_try_normalize`) instead.
482    #[inline]
483    #[must_use = "Did you mean to use try_normalize_mut()?"]
484    pub fn try_normalize(&self, min_norm: T::RealField) -> Option<OMatrix<T, R, C>>
485    where
486        T: ComplexField,
487        DefaultAllocator: Allocator<R, C>,
488    {
489        let n = self.norm();
490
491        if n <= min_norm {
492            None
493        } else {
494            Some(self.unscale(n))
495        }
496    }
497}
498
499/// # In-place normalization
500impl<T: Scalar, R: Dim, C: Dim, S: StorageMut<T, R, C>> Matrix<T, R, C, S> {
501    /// Normalizes this matrix in-place and returns its norm.
502    ///
503    /// The components of the matrix cannot be SIMD types (see `simd_try_normalize_mut` instead).
504    #[inline]
505    pub fn normalize_mut(&mut self) -> T::SimdRealField
506    where
507        T: SimdComplexField,
508    {
509        let n = self.norm();
510        self.unscale_mut(n.clone());
511
512        n
513    }
514
515    /// Normalizes this matrix in-place and return its norm.
516    ///
517    /// The components of the matrix can be SIMD types.
518    #[inline]
519    #[must_use = "Did you mean to use simd_try_normalize_mut()?"]
520    pub fn simd_try_normalize_mut(
521        &mut self,
522        min_norm: T::SimdRealField,
523    ) -> SimdOption<T::SimdRealField>
524    where
525        T: SimdComplexField,
526        T::Element: Scalar,
527        DefaultAllocator: Allocator<R, C>,
528    {
529        let n = self.norm();
530        let le = n.clone().simd_le(min_norm);
531        self.apply(|e| *e = e.clone().simd_unscale(n.clone()).select(le, e.clone()));
532        SimdOption::new(n, le)
533    }
534
535    /// Normalizes this matrix in-place or does nothing if its norm is smaller or equal to `eps`.
536    ///
537    /// If the normalization succeeded, returns the old norm of this matrix.
538    #[inline]
539    pub fn try_normalize_mut(&mut self, min_norm: T::RealField) -> Option<T::RealField>
540    where
541        T: ComplexField,
542    {
543        let n = self.norm();
544
545        if n <= min_norm {
546            None
547        } else {
548            self.unscale_mut(n.clone());
549            Some(n)
550        }
551    }
552}
553
554impl<T: SimdComplexField, R: Dim, C: Dim> Normed for OMatrix<T, R, C>
555where
556    DefaultAllocator: Allocator<R, C>,
557{
558    type Norm = T::SimdRealField;
559
560    #[inline]
561    fn norm(&self) -> T::SimdRealField {
562        self.norm()
563    }
564
565    #[inline]
566    fn norm_squared(&self) -> T::SimdRealField {
567        self.norm_squared()
568    }
569
570    #[inline]
571    fn scale_mut(&mut self, n: Self::Norm) {
572        self.scale_mut(n)
573    }
574
575    #[inline]
576    fn unscale_mut(&mut self, n: Self::Norm) {
577        self.unscale_mut(n)
578    }
579}
580
581impl<T: Scalar + ClosedNeg, R: Dim, C: Dim> Neg for Unit<OMatrix<T, R, C>>
582where
583    DefaultAllocator: Allocator<R, C>,
584{
585    type Output = Unit<OMatrix<T, R, C>>;
586
587    #[inline]
588    fn neg(self) -> Self::Output {
589        Unit::new_unchecked(-self.value)
590    }
591}
592
593// TODO: specialization will greatly simplify this implementation in the future.
594// In particular:
595//   − use `x()` instead of `::canonical_basis_element`
596//   − use `::new(x, y, z)` instead of `::from_slice`
597/// # Basis and orthogonalization
598impl<T: ComplexField, D: DimName> OVector<T, D>
599where
600    DefaultAllocator: Allocator<D>,
601{
602    /// The i-the canonical basis element.
603    #[inline]
604    fn canonical_basis_element(i: usize) -> Self {
605        let mut res = Self::zero();
606        res[i] = T::one();
607        res
608    }
609
610    /// Orthonormalizes the given family of vectors. The largest free family of vectors is moved at
611    /// the beginning of the array and its size is returned. Vectors at an indices larger or equal to
612    /// this length can be modified to an arbitrary value.
613    #[inline]
614    pub fn orthonormalize(vs: &mut [Self]) -> usize {
615        let mut nbasis_elements = 0;
616
617        for i in 0..vs.len() {
618            {
619                let (elt, basis) = vs[..i + 1].split_last_mut().unwrap();
620
621                for basis_element in &basis[..nbasis_elements] {
622                    *elt -= basis_element * elt.dot(basis_element)
623                }
624            }
625
626            if vs[i].try_normalize_mut(T::RealField::zero()).is_some() {
627                // TODO: this will be efficient on dynamically-allocated vectors but for
628                // statically-allocated ones, `.clone_from` would be better.
629                vs.swap(nbasis_elements, i);
630                nbasis_elements += 1;
631
632                // All the other vectors will be dependent.
633                if nbasis_elements == D::DIM {
634                    break;
635                }
636            }
637        }
638
639        nbasis_elements
640    }
641
642    /// Applies the given closure to each element of the orthonormal basis of the subspace
643    /// orthogonal to free family of vectors `vs`. If `vs` is not a free family, the result is
644    /// unspecified.
645    // TODO: return an iterator instead when `-> impl Iterator` will be supported by Rust.
646    #[inline]
647    pub fn orthonormal_subspace_basis<F>(vs: &[Self], mut f: F)
648    where
649        F: FnMut(&Self) -> bool,
650    {
651        // TODO: is this necessary?
652        assert!(
653            vs.len() <= D::DIM,
654            "The given set of vectors has no chance of being a free family."
655        );
656
657        match D::DIM {
658            1 => {
659                if vs.is_empty() {
660                    let _ = f(&Self::canonical_basis_element(0));
661                }
662            }
663            2 => {
664                if vs.is_empty() {
665                    let _ = f(&Self::canonical_basis_element(0))
666                        && f(&Self::canonical_basis_element(1));
667                } else if vs.len() == 1 {
668                    let v = &vs[0];
669                    let res = Self::from_column_slice(&[-v[1].clone(), v[0].clone()]);
670
671                    let _ = f(&res.normalize());
672                }
673
674                // Otherwise, nothing.
675            }
676            3 => {
677                if vs.is_empty() {
678                    let _ = f(&Self::canonical_basis_element(0))
679                        && f(&Self::canonical_basis_element(1))
680                        && f(&Self::canonical_basis_element(2));
681                } else if vs.len() == 1 {
682                    let v = &vs[0];
683                    let mut a;
684
685                    if v[0].clone().norm1() > v[1].clone().norm1() {
686                        a = Self::from_column_slice(&[v[2].clone(), T::zero(), -v[0].clone()]);
687                    } else {
688                        a = Self::from_column_slice(&[T::zero(), -v[2].clone(), v[1].clone()]);
689                    };
690
691                    let _ = a.normalize_mut();
692
693                    if f(&a.cross(v)) {
694                        let _ = f(&a);
695                    }
696                } else if vs.len() == 2 {
697                    let _ = f(&vs[0].cross(&vs[1]).normalize());
698                }
699            }
700            _ => {
701                #[cfg(any(feature = "std", feature = "alloc"))]
702                {
703                    // XXX: use a GenericArray instead.
704                    let mut known_basis = Vec::new();
705
706                    for v in vs.iter() {
707                        known_basis.push(v.normalize())
708                    }
709
710                    for i in 0..D::DIM - vs.len() {
711                        let mut elt = Self::canonical_basis_element(i);
712
713                        for v in &known_basis {
714                            elt -= v * elt.dot(v)
715                        }
716
717                        if let Some(subsp_elt) = elt.try_normalize(T::RealField::zero()) {
718                            if !f(&subsp_elt) {
719                                return;
720                            };
721
722                            known_basis.push(subsp_elt);
723                        }
724                    }
725                }
726                #[cfg(all(not(feature = "std"), not(feature = "alloc")))]
727                {
728                    panic!(
729                        "Cannot compute the orthogonal subspace basis of a vector with a dimension greater than 3 \
730                            if #![no_std] is enabled and the 'alloc' feature is not enabled."
731                    )
732                }
733            }
734        }
735    }
736}