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}