nalgebra_lapack/qr/
abstraction.rs

1use crate::{Transposition, qr::QrReal, qr_util, sealed::Sealed};
2use na::{
3    DefaultAllocator, Dim, DimMin, DimMinimum, IsContiguous, Matrix, OMatrix, OVector,
4    RawStorageMut, RealField, Scalar, Storage, allocator::Allocator,
5};
6use num::{ConstOne, Zero};
7use qr_util::Error;
8
9/// Common functionality for the QR decomposition of an m × n matrix `A` with or
10/// without column-pivoting.
11pub trait QrDecomposition<T, R, C>: Sealed
12where
13    DefaultAllocator: Allocator<R, C> + Allocator<DimMinimum<R, C>> + Allocator<C>,
14    R: DimMin<C, Output = C>,
15    C: Dim,
16    T: Scalar + RealField + QrReal,
17{
18    #[doc(hidden)]
19    /// Get a reference to the internal representation of the QR decomposition
20    /// with the output of the LAPACK QR decomposition.
21    fn __lapack_qr_ref(&self) -> &OMatrix<T, R, C>;
22
23    #[doc(hidden)]
24    /// Get a reference to the Householder coefficients vector as computed by
25    /// LAPACK.
26    fn __lapack_tau_ref(&self) -> &OVector<T, DimMinimum<R, C>>;
27
28    #[inline]
29    /// The number of rows of the original matrix `A`.
30    fn nrows(&self) -> usize {
31        self.__lapack_qr_ref().nrows()
32    }
33
34    #[inline]
35    /// The number of columns of the original matrix `A`.
36    fn ncols(&self) -> usize {
37        self.__lapack_qr_ref().ncols()
38    }
39
40    #[inline]
41    /// Shape of the original matrix `A`.
42    fn shape_generic(&self) -> (R, C) {
43        self.__lapack_qr_ref().shape_generic()
44    }
45
46    /// Solve the overdetermined linear system with the given right hand side
47    /// in a least squares sense, see the comments on [Self::solve_mut].
48    fn solve<C2: Dim, S>(&self, rhs: Matrix<T, R, C2, S>) -> Result<OMatrix<T, C, C2>, Error>
49    where
50        S: RawStorageMut<T, R, C2> + IsContiguous + Storage<T, R, C2>,
51        T: Zero,
52        DefaultAllocator: Allocator<C, C2> + Allocator<R, C2>,
53    {
54        let (_, c2) = rhs.shape_generic();
55        let (_, c) = self.shape_generic();
56        let mut x = OMatrix::zeros_generic(c, c2);
57        self.solve_mut(&mut x, rhs)?;
58        Ok(x)
59    }
60
61    /// Solve the square or overdetermined system in `A X = B`, where `X ∈ R^(n × k)`,
62    /// `B ∈ R^(m × k)` in a least-squares sense, such that `|| A X - B||^2`
63    /// is minimized. The solution is placed into the matrix `X ∈ R^(n × k)`.
64    ///
65    /// Note that QR decomposition _does not_ typically give the minimum norm solution
66    /// for `X`, only the residual is minimized which is typically what we want.
67    ///
68    /// This function might perform a small allocation.
69    fn solve_mut<C2: Dim, S, S2>(
70        &self,
71        x: &mut Matrix<T, C, C2, S2>,
72        b: Matrix<T, R, C2, S>,
73    ) -> Result<(), Error>
74    where
75        S: RawStorageMut<T, R, C2> + IsContiguous,
76        S2: RawStorageMut<T, C, C2> + IsContiguous,
77        T: Zero;
78
79    /// Efficiently calculate the matrix product `Q B` of the factor `Q` with a
80    /// given matrix `B`. `Q` acts as if it is a matrix of dimension `m × m`, so
81    /// we require `B ∈ R^(m × k)`. The product is calculated in place and
82    /// must only be considered valid when the function returns without error.
83    fn q_mul_mut<C2, S>(&self, b: &mut Matrix<T, R, C2, S>) -> Result<(), Error>
84    where
85        C2: Dim,
86        S: RawStorageMut<T, R, C2> + IsContiguous,
87    {
88        qr_util::q_mul_mut(self.__lapack_qr_ref(), self.__lapack_tau_ref(), b)?;
89        Ok(())
90    }
91    /// Efficiently calculate the matrix product `Q^T B` of the factor `Q` with a
92    /// given matrix `B`. `Q` acts as if it is a matrix of dimension `m × m`, so
93    /// we require `B ∈ R^(m × k)`. The product is calculated in place and
94    /// must only be considered valid when the function returns without error.
95    fn q_tr_mul_mut<C2, S>(&self, b: &mut Matrix<T, R, C2, S>) -> Result<(), Error>
96    where
97        C2: Dim,
98        S: RawStorageMut<T, R, C2> + IsContiguous,
99    {
100        qr_util::q_tr_mul_mut(self.__lapack_qr_ref(), self.__lapack_tau_ref(), b)?;
101        Ok(())
102    }
103
104    /// Efficiently calculate the matrix product `B Q` of the factor `Q` with a
105    /// given matrix `B`. `Q` acts as if it is a matrix of dimension `m × m`, so
106    /// we require `B ∈ R^(k × m)`. The product is calculated in place and
107    /// must only be considered valid when the function returns without error.
108    fn mul_q_mut<R2, S>(&self, b: &mut Matrix<T, R2, R, S>) -> Result<(), Error>
109    where
110        R2: Dim,
111        S: RawStorageMut<T, R2, R> + IsContiguous,
112    {
113        qr_util::mul_q_mut(self.__lapack_qr_ref(), self.__lapack_tau_ref(), b)?;
114        Ok(())
115    }
116
117    /// Efficiently calculate the matrix product `B Q^T` of the factor `Q` with a
118    /// given matrix `B`. `Q` acts as if it is a matrix of dimension `m × m`, so
119    /// we require `B ∈ R^(k × m)`. The product is calculated in place and
120    /// must only be considered valid when the function returns without error.
121    fn mul_q_tr_mut<R2, S>(&self, b: &mut Matrix<T, R2, R, S>) -> Result<(), Error>
122    where
123        R2: Dim,
124        S: RawStorageMut<T, R2, R> + IsContiguous,
125    {
126        qr_util::mul_q_tr_mut(self.__lapack_qr_ref(), self.__lapack_tau_ref(), b)?;
127        Ok(())
128    }
129
130    /// Multiply `R*B` and place the result in `B`. R is treated as an m × m
131    /// upper triangular matrix. The product is calculated in place and must
132    /// only be considered valid when the function returns
133    /// without error.
134    ///
135    /// Prefer this over `qr.r() * B`, since its faster and allocation-free.
136    fn r_mul_mut<C2, S2>(&self, b: &mut Matrix<T, C, C2, S2>) -> Result<(), Error>
137    where
138        C2: Dim,
139        S2: RawStorageMut<T, C, C2> + IsContiguous,
140        T: ConstOne,
141    {
142        if self.nrows() < self.ncols() {
143            return Err(Error::Underdetermined);
144        }
145        qr_util::r_xx_mul_mut(self.__lapack_qr_ref(), Transposition::No, b)
146    }
147
148    /// Multiply `R^T * B` and place the result in `B`. R is treated as an m × m
149    /// upper triangular matrix. The product is calculated in place and must
150    /// only be considered valid when the function returns
151    /// without error.
152    ///
153    /// Prefer this over `qr.r().transpose() * B`, since its faster and allocation-free.
154    fn r_tr_mul_mut<C2, S2>(&self, b: &mut Matrix<T, C, C2, S2>) -> Result<(), Error>
155    where
156        C2: Dim,
157        S2: RawStorageMut<T, C, C2> + IsContiguous,
158        T: ConstOne,
159    {
160        if self.nrows() < self.ncols() {
161            return Err(Error::Underdetermined);
162        }
163        qr_util::r_xx_mul_mut(self.__lapack_qr_ref(), Transposition::Transpose, b)
164    }
165
166    /// Multiply `B*R` and place the result in `B`. R is treated as an m × m
167    /// upper triangular matrix. The product is calculated in place and must
168    /// only be considered valid when the function returns
169    /// without error.
170    ///
171    /// Prefer this over `B * qr.r()`, since its faster and allocation-free.
172    fn mul_r_mut<R2, S2>(&self, b: &mut Matrix<T, R2, C, S2>) -> Result<(), Error>
173    where
174        R2: Dim,
175        S2: RawStorageMut<T, R2, C> + IsContiguous,
176        T: ConstOne,
177    {
178        if self.nrows() < self.ncols() {
179            return Err(Error::Underdetermined);
180        }
181        qr_util::mul_r_xx_mut(self.__lapack_qr_ref(), Transposition::No, b)
182    }
183
184    /// Multiply `B*R^T` and place the result in `B`. R is treated as an m × m
185    /// upper triangular matrix. The product is calculated in place and must
186    /// only be considered valid when the function returns
187    /// without error.
188    ///
189    /// Prefer this over `B * qr.r()`, since its faster and allocation-free.
190    fn mul_r_tr_mut<R2, S2>(&self, b: &mut Matrix<T, R2, C, S2>) -> Result<(), Error>
191    where
192        R2: Dim,
193        S2: RawStorageMut<T, R2, C> + IsContiguous,
194        T: ConstOne,
195    {
196        if self.nrows() < self.ncols() {
197            return Err(Error::Underdetermined);
198        }
199        qr_util::mul_r_xx_mut(self.__lapack_qr_ref(), Transposition::Transpose, b)
200    }
201
202    /// Computes the orthonormal matrix `Q ∈ R^(m × n)` of this decomposition.
203    /// Note that this matrix has _economy_ dimensions, which means it is not
204    /// square unless `A` is square. It satisfies `Q^T Q = I`. Note further
205    /// that it is typically not necessary to compute `Q` explicitly. Rather,
206    /// check if some of the provided multiplication functions can help to
207    /// calculate the matrix products `Q B`, `B Q`, `Q^T B`, `B Q^T` more efficiently.
208    ///
209    /// This function allocates.
210    fn q(&self) -> OMatrix<T, R, DimMinimum<R, C>>
211    where
212        DefaultAllocator: Allocator<R, <R as DimMin<C>>::Output>,
213        T: Zero,
214    {
215        let (nrows, ncols) = self.shape_generic();
216        let min_nrows_ncols = nrows.min(ncols);
217
218        if min_nrows_ncols.value() == 0 {
219            return OMatrix::from_element_generic(nrows, min_nrows_ncols, T::zero());
220        }
221
222        let mut q = self
223            .__lapack_qr_ref()
224            .generic_view((0, 0), (nrows, min_nrows_ncols))
225            .into_owned();
226
227        let nrows = nrows
228            .value()
229            .try_into()
230            .expect("integer dimensions out of bounds");
231
232        let lwork = unsafe {
233            T::xorgqr_work_size(
234                nrows,
235                min_nrows_ncols.value() as i32,
236                self.__lapack_tau_ref().len() as i32,
237                q.as_mut_slice(),
238                nrows,
239                self.__lapack_tau_ref().as_slice(),
240            )
241            .expect("unexpected error in lapack backend")
242        };
243
244        let mut work = vec![T::zero(); lwork as usize];
245
246        unsafe {
247            T::xorgqr(
248                nrows,
249                min_nrows_ncols.value() as i32,
250                self.__lapack_tau_ref().len() as i32,
251                q.as_mut_slice(),
252                nrows,
253                self.__lapack_tau_ref().as_slice(),
254                &mut work,
255                lwork,
256            )
257            .expect("unexpected error in lapack backend")
258        };
259        q
260    }
261
262    /// Retrieves the upper trapezoidal submatrix `R` of this decomposition.
263    /// Note that it's typically not necessary to construct this matrix directly
264    /// and check if any of the provided multiplication functions can be used
265    /// instead.
266    ///
267    /// This function allocates.
268    #[inline]
269    #[must_use]
270    fn r(&self) -> OMatrix<T, DimMinimum<R, C>, C>
271    where
272        DefaultAllocator: Allocator<R, C>
273            + Allocator<R, DimMinimum<R, C>>
274            + Allocator<DimMinimum<R, C>, C>
275            + Allocator<DimMinimum<R, C>>,
276    {
277        let (nrows, ncols) = self.shape_generic();
278        self.__lapack_qr_ref()
279            .rows_generic(0, nrows.min(ncols))
280            .upper_triangle()
281    }
282}