├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── bench
└── bench_sparse_qr_extra.cpp
├── examples
└── ellipse_fitting.cpp
├── imgs
├── benchmark_graph.png
├── benchmark_table.png
├── optim_conv.m4v
├── sparsity_patterns.png
└── sparsity_patterns_2.png
├── src
└── QRKit
│ ├── BandedBlockedSparseQR.h
│ ├── BlockAngularSparseQR.h
│ ├── BlockDiagonalSparseQR.h
│ ├── BlockMatrix1x2.h
│ ├── BlockYTY.h
│ ├── BlockedThinDenseQR.h
│ ├── BlockedThinQRBase.h
│ ├── BlockedThinSparseQR.h
│ ├── QRKit
│ ├── SparseBlockCOO.h
│ ├── SparseBlockDiagonal.h
│ ├── SparseBlockYTY.h
│ ├── SparseQROrdering.h
│ └── SparseQRUtils.h
├── test
├── CMakeLists.txt
├── test-qrkit.cpp
├── test-utils.cpp
└── test.h
└── user.cmake.in
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | build_linux/
3 | user.cmake
4 |
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.0.0)
2 | project(QRKit VERSION 0.1.0)
3 |
4 | include(user.cmake) # Create this from user.cmake.in
5 | find_package (Eigen3 3.3 REQUIRED NO_MODULE)
6 |
7 | #set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c++17 -O2 -fpermissive")
8 | #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -O2 -fpermissive")
9 |
10 | include_directories("src")
11 |
12 | add_subdirectory(test)
13 | # add_subdirectory(src)
14 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 Jan Svoboda, Tom Cashman, Andrew Fitzgibbon, Sergio Garrido Jurado
2 |
3 | Mozilla Public License Version 2.0
4 | ==================================
5 |
6 | 1. Definitions
7 | --------------
8 |
9 | 1.1. "Contributor"
10 | means each individual or legal entity that creates, contributes to
11 | the creation of, or owns Covered Software.
12 |
13 | 1.2. "Contributor Version"
14 | means the combination of the Contributions of others (if any) used
15 | by a Contributor and that particular Contributor's Contribution.
16 |
17 | 1.3. "Contribution"
18 | means Covered Software of a particular Contributor.
19 |
20 | 1.4. "Covered Software"
21 | means Source Code Form to which the initial Contributor has attached
22 | the notice in Exhibit A, the Executable Form of such Source Code
23 | Form, and Modifications of such Source Code Form, in each case
24 | including portions thereof.
25 |
26 | 1.5. "Incompatible With Secondary Licenses"
27 | means
28 |
29 | (a) that the initial Contributor has attached the notice described
30 | in Exhibit B to the Covered Software; or
31 |
32 | (b) that the Covered Software was made available under the terms of
33 | version 1.1 or earlier of the License, but not also under the
34 | terms of a Secondary License.
35 |
36 | 1.6. "Executable Form"
37 | means any form of the work other than Source Code Form.
38 |
39 | 1.7. "Larger Work"
40 | means a work that combines Covered Software with other material, in
41 | a separate file or files, that is not Covered Software.
42 |
43 | 1.8. "License"
44 | means this document.
45 |
46 | 1.9. "Licensable"
47 | means having the right to grant, to the maximum extent possible,
48 | whether at the time of the initial grant or subsequently, any and
49 | all of the rights conveyed by this License.
50 |
51 | 1.10. "Modifications"
52 | means any of the following:
53 |
54 | (a) any file in Source Code Form that results from an addition to,
55 | deletion from, or modification of the contents of Covered
56 | Software; or
57 |
58 | (b) any new file in Source Code Form that contains any Covered
59 | Software.
60 |
61 | 1.11. "Patent Claims" of a Contributor
62 | means any patent claim(s), including without limitation, method,
63 | process, and apparatus claims, in any patent Licensable by such
64 | Contributor that would be infringed, but for the grant of the
65 | License, by the making, using, selling, offering for sale, having
66 | made, import, or transfer of either its Contributions or its
67 | Contributor Version.
68 |
69 | 1.12. "Secondary License"
70 | means either the GNU General Public License, Version 2.0, the GNU
71 | Lesser General Public License, Version 2.1, the GNU Affero General
72 | Public License, Version 3.0, or any later versions of those
73 | licenses.
74 |
75 | 1.13. "Source Code Form"
76 | means the form of the work preferred for making modifications.
77 |
78 | 1.14. "You" (or "Your")
79 | means an individual or a legal entity exercising rights under this
80 | License. For legal entities, "You" includes any entity that
81 | controls, is controlled by, or is under common control with You. For
82 | purposes of this definition, "control" means (a) the power, direct
83 | or indirect, to cause the direction or management of such entity,
84 | whether by contract or otherwise, or (b) ownership of more than
85 | fifty percent (50%) of the outstanding shares or beneficial
86 | ownership of such entity.
87 |
88 | 2. License Grants and Conditions
89 | --------------------------------
90 |
91 | 2.1. Grants
92 |
93 | Each Contributor hereby grants You a world-wide, royalty-free,
94 | non-exclusive license:
95 |
96 | (a) under intellectual property rights (other than patent or trademark)
97 | Licensable by such Contributor to use, reproduce, make available,
98 | modify, display, perform, distribute, and otherwise exploit its
99 | Contributions, either on an unmodified basis, with Modifications, or
100 | as part of a Larger Work; and
101 |
102 | (b) under Patent Claims of such Contributor to make, use, sell, offer
103 | for sale, have made, import, and otherwise transfer either its
104 | Contributions or its Contributor Version.
105 |
106 | 2.2. Effective Date
107 |
108 | The licenses granted in Section 2.1 with respect to any Contribution
109 | become effective for each Contribution on the date the Contributor first
110 | distributes such Contribution.
111 |
112 | 2.3. Limitations on Grant Scope
113 |
114 | The licenses granted in this Section 2 are the only rights granted under
115 | this License. No additional rights or licenses will be implied from the
116 | distribution or licensing of Covered Software under this License.
117 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
118 | Contributor:
119 |
120 | (a) for any code that a Contributor has removed from Covered Software;
121 | or
122 |
123 | (b) for infringements caused by: (i) Your and any other third party's
124 | modifications of Covered Software, or (ii) the combination of its
125 | Contributions with other software (except as part of its Contributor
126 | Version); or
127 |
128 | (c) under Patent Claims infringed by Covered Software in the absence of
129 | its Contributions.
130 |
131 | This License does not grant any rights in the trademarks, service marks,
132 | or logos of any Contributor (except as may be necessary to comply with
133 | the notice requirements in Section 3.4).
134 |
135 | 2.4. Subsequent Licenses
136 |
137 | No Contributor makes additional grants as a result of Your choice to
138 | distribute the Covered Software under a subsequent version of this
139 | License (see Section 10.2) or under the terms of a Secondary License (if
140 | permitted under the terms of Section 3.3).
141 |
142 | 2.5. Representation
143 |
144 | Each Contributor represents that the Contributor believes its
145 | Contributions are its original creation(s) or it has sufficient rights
146 | to grant the rights to its Contributions conveyed by this License.
147 |
148 | 2.6. Fair Use
149 |
150 | This License is not intended to limit any rights You have under
151 | applicable copyright doctrines of fair use, fair dealing, or other
152 | equivalents.
153 |
154 | 2.7. Conditions
155 |
156 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
157 | in Section 2.1.
158 |
159 | 3. Responsibilities
160 | -------------------
161 |
162 | 3.1. Distribution of Source Form
163 |
164 | All distribution of Covered Software in Source Code Form, including any
165 | Modifications that You create or to which You contribute, must be under
166 | the terms of this License. You must inform recipients that the Source
167 | Code Form of the Covered Software is governed by the terms of this
168 | License, and how they can obtain a copy of this License. You may not
169 | attempt to alter or restrict the recipients' rights in the Source Code
170 | Form.
171 |
172 | 3.2. Distribution of Executable Form
173 |
174 | If You distribute Covered Software in Executable Form then:
175 |
176 | (a) such Covered Software must also be made available in Source Code
177 | Form, as described in Section 3.1, and You must inform recipients of
178 | the Executable Form how they can obtain a copy of such Source Code
179 | Form by reasonable means in a timely manner, at a charge no more
180 | than the cost of distribution to the recipient; and
181 |
182 | (b) You may distribute such Executable Form under the terms of this
183 | License, or sublicense it under different terms, provided that the
184 | license for the Executable Form does not attempt to limit or alter
185 | the recipients' rights in the Source Code Form under this License.
186 |
187 | 3.3. Distribution of a Larger Work
188 |
189 | You may create and distribute a Larger Work under terms of Your choice,
190 | provided that You also comply with the requirements of this License for
191 | the Covered Software. If the Larger Work is a combination of Covered
192 | Software with a work governed by one or more Secondary Licenses, and the
193 | Covered Software is not Incompatible With Secondary Licenses, this
194 | License permits You to additionally distribute such Covered Software
195 | under the terms of such Secondary License(s), so that the recipient of
196 | the Larger Work may, at their option, further distribute the Covered
197 | Software under the terms of either this License or such Secondary
198 | License(s).
199 |
200 | 3.4. Notices
201 |
202 | You may not remove or alter the substance of any license notices
203 | (including copyright notices, patent notices, disclaimers of warranty,
204 | or limitations of liability) contained within the Source Code Form of
205 | the Covered Software, except that You may alter any license notices to
206 | the extent required to remedy known factual inaccuracies.
207 |
208 | 3.5. Application of Additional Terms
209 |
210 | You may choose to offer, and to charge a fee for, warranty, support,
211 | indemnity or liability obligations to one or more recipients of Covered
212 | Software. However, You may do so only on Your own behalf, and not on
213 | behalf of any Contributor. You must make it absolutely clear that any
214 | such warranty, support, indemnity, or liability obligation is offered by
215 | You alone, and You hereby agree to indemnify every Contributor for any
216 | liability incurred by such Contributor as a result of warranty, support,
217 | indemnity or liability terms You offer. You may include additional
218 | disclaimers of warranty and limitations of liability specific to any
219 | jurisdiction.
220 |
221 | 4. Inability to Comply Due to Statute or Regulation
222 | ---------------------------------------------------
223 |
224 | If it is impossible for You to comply with any of the terms of this
225 | License with respect to some or all of the Covered Software due to
226 | statute, judicial order, or regulation then You must: (a) comply with
227 | the terms of this License to the maximum extent possible; and (b)
228 | describe the limitations and the code they affect. Such description must
229 | be placed in a text file included with all distributions of the Covered
230 | Software under this License. Except to the extent prohibited by statute
231 | or regulation, such description must be sufficiently detailed for a
232 | recipient of ordinary skill to be able to understand it.
233 |
234 | 5. Termination
235 | --------------
236 |
237 | 5.1. The rights granted under this License will terminate automatically
238 | if You fail to comply with any of its terms. However, if You become
239 | compliant, then the rights granted under this License from a particular
240 | Contributor are reinstated (a) provisionally, unless and until such
241 | Contributor explicitly and finally terminates Your grants, and (b) on an
242 | ongoing basis, if such Contributor fails to notify You of the
243 | non-compliance by some reasonable means prior to 60 days after You have
244 | come back into compliance. Moreover, Your grants from a particular
245 | Contributor are reinstated on an ongoing basis if such Contributor
246 | notifies You of the non-compliance by some reasonable means, this is the
247 | first time You have received notice of non-compliance with this License
248 | from such Contributor, and You become compliant prior to 30 days after
249 | Your receipt of the notice.
250 |
251 | 5.2. If You initiate litigation against any entity by asserting a patent
252 | infringement claim (excluding declaratory judgment actions,
253 | counter-claims, and cross-claims) alleging that a Contributor Version
254 | directly or indirectly infringes any patent, then the rights granted to
255 | You by any and all Contributors for the Covered Software under Section
256 | 2.1 of this License shall terminate.
257 |
258 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
259 | end user license agreements (excluding distributors and resellers) which
260 | have been validly granted by You or Your distributors under this License
261 | prior to termination shall survive termination.
262 |
263 | ************************************************************************
264 | * *
265 | * 6. Disclaimer of Warranty *
266 | * ------------------------- *
267 | * *
268 | * Covered Software is provided under this License on an "as is" *
269 | * basis, without warranty of any kind, either expressed, implied, or *
270 | * statutory, including, without limitation, warranties that the *
271 | * Covered Software is free of defects, merchantable, fit for a *
272 | * particular purpose or non-infringing. The entire risk as to the *
273 | * quality and performance of the Covered Software is with You. *
274 | * Should any Covered Software prove defective in any respect, You *
275 | * (not any Contributor) assume the cost of any necessary servicing, *
276 | * repair, or correction. This disclaimer of warranty constitutes an *
277 | * essential part of this License. No use of any Covered Software is *
278 | * authorized under this License except under this disclaimer. *
279 | * *
280 | ************************************************************************
281 |
282 | ************************************************************************
283 | * *
284 | * 7. Limitation of Liability *
285 | * -------------------------- *
286 | * *
287 | * Under no circumstances and under no legal theory, whether tort *
288 | * (including negligence), contract, or otherwise, shall any *
289 | * Contributor, or anyone who distributes Covered Software as *
290 | * permitted above, be liable to You for any direct, indirect, *
291 | * special, incidental, or consequential damages of any character *
292 | * including, without limitation, damages for lost profits, loss of *
293 | * goodwill, work stoppage, computer failure or malfunction, or any *
294 | * and all other commercial damages or losses, even if such party *
295 | * shall have been informed of the possibility of such damages. This *
296 | * limitation of liability shall not apply to liability for death or *
297 | * personal injury resulting from such party's negligence to the *
298 | * extent applicable law prohibits such limitation. Some *
299 | * jurisdictions do not allow the exclusion or limitation of *
300 | * incidental or consequential damages, so this exclusion and *
301 | * limitation may not apply to You. *
302 | * *
303 | ************************************************************************
304 |
305 | 8. Litigation
306 | -------------
307 |
308 | Any litigation relating to this License may be brought only in the
309 | courts of a jurisdiction where the defendant maintains its principal
310 | place of business and such litigation shall be governed by laws of that
311 | jurisdiction, without reference to its conflict-of-law provisions.
312 | Nothing in this Section shall prevent a party's ability to bring
313 | cross-claims or counter-claims.
314 |
315 | 9. Miscellaneous
316 | ----------------
317 |
318 | This License represents the complete agreement concerning the subject
319 | matter hereof. If any provision of this License is held to be
320 | unenforceable, such provision shall be reformed only to the extent
321 | necessary to make it enforceable. Any law or regulation which provides
322 | that the language of a contract shall be construed against the drafter
323 | shall not be used to construe this License against a Contributor.
324 |
325 | 10. Versions of the License
326 | ---------------------------
327 |
328 | 10.1. New Versions
329 |
330 | Mozilla Foundation is the license steward. Except as provided in Section
331 | 10.3, no one other than the license steward has the right to modify or
332 | publish new versions of this License. Each version will be given a
333 | distinguishing version number.
334 |
335 | 10.2. Effect of New Versions
336 |
337 | You may distribute the Covered Software under the terms of the version
338 | of the License under which You originally received the Covered Software,
339 | or under the terms of any subsequent version published by the license
340 | steward.
341 |
342 | 10.3. Modified Versions
343 |
344 | If you create software not governed by this License, and you want to
345 | create a new license for such software, you may create and use a
346 | modified version of this License if you rename the license and remove
347 | any references to the name of the license steward (except to note that
348 | such modified license differs from this License).
349 |
350 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
351 | Licenses
352 |
353 | If You choose to distribute Source Code Form that is Incompatible With
354 | Secondary Licenses under the terms of this version of the License, the
355 | notice described in Exhibit B of this License must be attached.
356 |
357 | Exhibit A - Source Code Form License Notice
358 | -------------------------------------------
359 |
360 | This Source Code Form is subject to the terms of the Mozilla Public
361 | License, v. 2.0. If a copy of the MPL was not distributed with this
362 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
363 |
364 | If it is not possible or desirable to put the notice in a particular
365 | file, then You may include the notice in a location (such as a LICENSE
366 | file in a relevant directory) where a recipient would be likely to look
367 | for such a notice.
368 |
369 | You may add additional accurate notices of copyright ownership.
370 |
371 | Exhibit B - "Incompatible With Secondary Licenses" Notice
372 | ---------------------------------------------------------
373 |
374 | This Source Code Form is "Incompatible With Secondary Licenses", as
375 | defined by the Mozilla Public License, v. 2.0.
376 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # QRKit
2 |
3 | This repository contains implementation of our WACV 2018 work [QRkit: Sparse, Composable QR Decompositions for Efficient and Stable Solutions to Problems in Computer Vision
4 | ](https://arxiv.org/abs/1802.03773).
5 |
6 | QRKit is an extension of Eigen C++ library that allows for efficient QR decomposition of sparse matrices with common sparsity patterns.
7 |
8 | ## Building
9 | ```sh
10 | $ cp user.cmake.in user.cmake
11 | $ vi user.cmake # Edit user.cmake to point to your Eigen distro
12 | $ mkdir build
13 | $ cd build
14 | $ cmake ..
15 | $ cmake --build .
16 | ```
17 |
18 | ## Common sparsity patterns
19 | The common sparsity patterns QRKit can bring significant speed-up for are shown below.
20 |
21 |
22 |
23 |
24 |
25 | ## Benchmark
26 | A simple benchmark showing speed-up potential of QRKit for matrix with a block-diagonal sparsity pattern.
27 |
28 |
29 |
30 |
31 |
32 | ## Reference
33 | If you make any use of our code or data, please cite the following:
34 | ```
35 | @conference{svoboda2018qrkit,
36 | title={QRkit: Sparse, Composable QR Decompositions for Efficient and Stable Solutions to Problems in Computer Vision},
37 | author={Svoboda, J. and Cashman, T. J. and Fitzgibbon, A. W.},
38 | booktitle={2018 IEEE Winter Conference on Applications of Computer Vision (WACV)},
39 | year={2018},
40 | }
41 | ```
42 |
43 | ## Acknowledgments
44 | For any reuse and or redistribution of the code in this repository please follow the [license agreement](./LICENSE) attached to this repository.
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/bench/bench_sparse_qr_extra.cpp:
--------------------------------------------------------------------------------
1 | // This file is part of Eigen, a lightweight C++ template library
2 | // for linear algebra.
3 | //
4 | // Copyright (C) 2017 Jan Svoboda
5 | // Copyright (C) 2016 Andrew Fitzgibbon
6 | // Copyright (C) 2016 Sergio Garrido Jurado
7 | //
8 | // This Source Code Form is subject to the terms of the Mozilla
9 | // Public License v. 2.0. If a copy of the MPL was not distributed
10 | // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 |
12 |
13 | // import basic and product tests for deprectaed DynamicSparseMatrix
14 | #define EIGEN_NO_DEPRECATED_WARNING
15 |
16 | //#define WITH_SPQR
17 |
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 |
24 | #include "eigen/test/main.h"
25 |
26 | #include
27 | #include
28 | #include
29 | #include
30 |
31 | #ifdef WITH_SPQR
32 | #include
33 | #endif
34 |
35 | #include
36 | #include
37 |
38 |
39 | using namespace Eigen;
40 | using namespace QRKit;
41 |
42 | #ifdef WITH_SPQR
43 | typedef SuiteSparse_long IndexType;
44 | #else
45 | typedef int IndexType;
46 | #endif
47 |
48 | typedef SparseMatrix JacobianType;
49 | typedef Matrix MatrixType;
50 |
51 | template
52 | struct EllipseFitting : SparseFunctor<_Scalar, IndexType>
53 | {
54 | // Class data: 2xN matrix with each column a 2D point
55 | Matrix2Xd ellipsePoints;
56 |
57 | // Number of parameters in the model, to which will be added
58 | // one latent variable per point.
59 | static const int nParamsModel = 5;
60 |
61 | // Constructor initializes points, and tells the base class how many parameters there are in total
62 | EllipseFitting(const Matrix2Xd& points) :
63 | SparseFunctor<_Scalar, IndexType>(nParamsModel + points.cols(), points.cols() * 2),
64 | ellipsePoints(points)
65 | {
66 | }
67 |
68 | // Functor functions
69 | int operator()(const InputType& uv, ValueType& fvec) const {
70 | // Ellipse parameters are the last 5 entries
71 | auto params = uv.tail(nParamsModel);
72 | double a = params[0];
73 | double b = params[1];
74 | double x0 = params[2];
75 | double y0 = params[3];
76 | double r = params[4];
77 |
78 | // Correspondences (t values) are the first N
79 | for (int i = 0; i < ellipsePoints.cols(); i++) {
80 | double t = uv(i);
81 | double x = a*cos(t)*cos(r) - b*sin(t)*sin(r) + x0;
82 | double y = a*cos(t)*sin(r) + b*sin(t)*cos(r) + y0;
83 | fvec(2 * i + 0) = ellipsePoints(0, i) - x;
84 | fvec(2 * i + 1) = ellipsePoints(1, i) - y;
85 | }
86 |
87 | return 0;
88 | }
89 |
90 | // Functor jacobian
91 | int df(const InputType& uv, JacobianType& fjac) {
92 | // X_i - (a*cos(t_i) + x0)
93 | // Y_i - (b*sin(t_i) + y0)
94 | int npoints = ellipsePoints.cols();
95 | auto params = uv.tail(nParamsModel);
96 | double a = params[0];
97 | double b = params[1];
98 | double r = params[4];
99 |
100 | TripletArray triplets(npoints * 2 * 5); // npoints * rows_per_point * nonzeros_per_row
101 | for (int i = 0; i < npoints; i++) {
102 | double t = uv(i);
103 | triplets.add(2 * i, i, +a*cos(r)*sin(t) + b*sin(r)*cos(t));
104 | triplets.add(2 * i, npoints + 0, -cos(t)*cos(r));
105 | triplets.add(2 * i, npoints + 1, +sin(t)*sin(r));
106 | triplets.add(2 * i, npoints + 2, -1);
107 | triplets.add(2 * i, npoints + 4, +a*cos(t)*sin(r) + b*sin(t)*cos(r));
108 |
109 | triplets.add(2 * i + 1, i, +a*sin(r)*sin(t) - b*cos(r)*cos(t));
110 | triplets.add(2 * i + 1, npoints + 0, -cos(t)*sin(r));
111 | triplets.add(2 * i + 1, npoints + 1, -sin(t)*cos(r));
112 | triplets.add(2 * i + 1, npoints + 3, -1);
113 | triplets.add(2 * i + 1, npoints + 4, -a*cos(t)*cos(r) + b*sin(t)*sin(r));
114 | }
115 |
116 | fjac.setFromTriplets(triplets.begin(), triplets.end());
117 | return 0;
118 | }
119 | };
120 |
121 | template
122 | struct SparseQR_EllipseFitting : public EllipseFitting<_Scalar> {
123 | // For generic Jacobian, one might use this Dense QR solver.
124 | typedef SparseQR > GeneralQRSolver;
125 | typedef GeneralQRSolver QRSolver;
126 |
127 | SparseQR_EllipseFitting(const Matrix2Xd& points) :
128 | EllipseFitting<_Scalar>(points) {
129 | }
130 |
131 | void initQRSolver(GeneralQRSolver &qr) {
132 | // set left block size
133 | }
134 | };
135 |
136 | #ifdef WITH_SPQR
137 | template
138 | struct SPQR_EllipseFitting : public EllipseFitting<_Scalar> {
139 | typedef SPQR SPQRSolver;
140 | typedef SPQRSolver QRSolver;
141 |
142 | SPQR_EllipseFitting(const Matrix2Xd& points) :
143 | EllipseFitting<_Scalar>(points) {
144 | }
145 |
146 | // And tell the algorithm how to set the QR parameters.
147 | void initQRSolver(SPQRSolver &qr) {
148 |
149 | }
150 | };
151 | #endif
152 |
153 | template
154 | struct SparseBlockDiagonalQR_EllipseFitting : public EllipseFitting<_Scalar> {
155 | // QR for J1 subblocks is 2x1
156 | typedef ColPivHouseholderQR > DenseQRSolver2x1;
157 | // QR for J1 is block diagonal
158 | typedef BlockDiagonalSparseQR LeftSuperBlockSolver;
159 | // QR for J1'J2 is general dense (faster than general sparse by about 1.5x for n=500K)
160 | typedef ColPivHouseholderQR > RightSuperBlockSolver;
161 | // QR for J is concatenation of the above.
162 | typedef BlockAngularSparseQR SchurlikeQRSolver;
163 |
164 | typedef SchurlikeQRSolver QRSolver;
165 |
166 |
167 | SparseBlockDiagonalQR_EllipseFitting(const Matrix2Xd& points) :
168 | EllipseFitting<_Scalar>(points) {
169 | }
170 |
171 | // And tell the algorithm how to set the QR parameters.
172 | void initQRSolver(SchurlikeQRSolver &qr) {
173 | }
174 | };
175 |
176 | template
177 | struct SparseBlockBandedQR_EllipseFitting : public EllipseFitting<_Scalar> {
178 | typedef HouseholderQR> BandBlockQRSolver;
179 | typedef BandedBlockedSparseQR BandedBlockedQRSolver;
180 | // QR for J1 is banded blocked QR
181 | typedef BandedBlockedQRSolver LeftSuperBlockSolver;
182 | // QR for J1'J2 is general dense (faster than general sparse by about 1.5x for n=500K)
183 | typedef ColPivHouseholderQR> RightSuperBlockSolver;
184 | // QR solver for sparse block angular matrix
185 | typedef BlockAngularSparseQR BlockAngularQRSolver;
186 |
187 | typedef BlockAngularQRSolver QRSolver;
188 |
189 | SparseBlockBandedQR_EllipseFitting(const Matrix2Xd& points) :
190 | EllipseFitting<_Scalar>(points) {
191 | }
192 |
193 | void initQRSolver(BlockAngularQRSolver &qr) {
194 | }
195 | };
196 |
197 | typedef EllipseFitting::InputType ParamsType;
198 |
199 | void printParamsHeader() {
200 | std::cout << "a \t";
201 | std::cout << "b \t";
202 | std::cout << "x0\t";
203 | std::cout << "y0\t";
204 | std::cout << "r \t";
205 | std::cout << "Duration";
206 | std::cout << std::endl;
207 | }
208 |
209 | void printParams(ParamsType ¶ms, int nDataPoints, double duration = -1.) {
210 | std::cout << params(nDataPoints) << "\t";
211 | std::cout << params(nDataPoints + 1) << "\t";
212 | std::cout << params(nDataPoints + 2) << "\t";
213 | std::cout << params(nDataPoints + 3) << "\t";
214 | std::cout << params(nDataPoints + 4)*180. / EIGEN_PI << "\t";
215 | if (duration >= 0) {
216 | std::cout << duration << "s";
217 | }
218 | std::cout << std::endl;
219 | }
220 |
221 | void initializeParams(int nDataPoints, const Matrix2Xd &ellipsePoints, double incr, ParamsType ¶ms) {
222 | params.resize(EllipseFitting::nParamsModel + nDataPoints);
223 | double minX, minY, maxX, maxY;
224 | minX = maxX = ellipsePoints(0, 0);
225 | minY = maxY = ellipsePoints(1, 0);
226 | for (int i = 0; i ruled out for now...
247 | int NumSamplePoints[NumTests] = { 500, 1000, 2000, 5000, 10000, 50000, 100000, 500000 };
248 |
249 | int main() {
250 | /***************************************************************************/
251 | std::cout << "################### Ellipse fitting benchmark #######################" << std::endl;
252 | std::cout << "#####################################################################" << std::endl;
253 | std::cout << "N - Number of data points" << std::endl;
254 | std::cout << "Sparse QR - Eigen's Sparse QR" << std::endl;
255 | std::cout << "SuiteSparse QR - SuiteSparse QR" << std::endl;
256 | std::cout << "Bl Diag Sp QR - Block Diagonal Sparse QR" << std::endl;
257 | std::cout << "Sp Band Bl QR - Sparse Banded Blocked QR" << std::endl;
258 | std::cout << "#####################################################################" << std::endl;
259 | for (int i = 0; i < NumTests; i++) {
260 | // Create the ellipse paramteers and data points
261 | // ELLIPSE PARAMETERS
262 | double a, b, x0, y0, r;
263 | a = 7.5;
264 | b = 2;
265 | x0 = 17.;
266 | y0 = 23.;
267 | r = 0.23;
268 |
269 | std::cout << "N = " << NumSamplePoints[i] << " \t";
270 | printParamsHeader();
271 | std::cout << "=====================================================================" << std::endl;
272 |
273 | // CREATE DATA SAMPLES
274 | int nDataPoints = NumSamplePoints[i];
275 | Matrix2Xd ellipsePoints;
276 | ellipsePoints.resize(2, nDataPoints);
277 | Scalar incr = 1.3*EIGEN_PI / Scalar(nDataPoints);
278 | for (int i = 0; i SparseQRFunctor;
307 | SparseQRFunctor functor1(ellipsePoints);
308 | Eigen::LevenbergMarquardt< SparseQRFunctor > lm1(functor1);
309 | lm1.setVerbose(LM_VERBOSE);
310 | begin = clock();
311 | info = lm1.minimize(params);
312 | duration = double(clock() - begin) / CLOCKS_PER_SEC;
313 | std::cout << "Sparse QR:\t";
314 | printParams(params, nDataPoints, duration);
315 | }
316 |
317 | #ifdef WITH_SPQR
318 | if (NumSamplePoints[i] <= SPQR_LimitN) {
319 | initializeParams(nDataPoints, ellipsePoints, incr, params);
320 | typedef SPQR_EllipseFitting SPQRFunctor;
321 | SPQRFunctor functor2(ellipsePoints);
322 | Eigen::LevenbergMarquardt< SPQRFunctor > lm2(functor2);
323 | lm2.setVerbose(LM_VERBOSE);
324 | begin = clock();
325 | info = lm2.minimize(params);
326 | duration = double(clock() - begin) / CLOCKS_PER_SEC;
327 | std::cout << "SuiteSparse QR:\t";
328 | printParams(params, nDataPoints, duration);
329 | }
330 | #endif
331 |
332 | initializeParams(nDataPoints, ellipsePoints, incr, params);
333 | typedef SparseBlockDiagonalQR_EllipseFitting SparseBlockDiagonalQRFunctor;
334 | SparseBlockDiagonalQRFunctor functor3(ellipsePoints);
335 | Eigen::LevenbergMarquardt< SparseBlockDiagonalQRFunctor > lm3(functor3);
336 | lm3.setVerbose(LM_VERBOSE);
337 | begin = clock();
338 | info = lm3.minimize(params);
339 | duration = double(clock() - begin) / CLOCKS_PER_SEC;
340 | std::cout << "Bl Diag Sp QR:\t";
341 | printParams(params, nDataPoints, duration);
342 |
343 | initializeParams(nDataPoints, ellipsePoints, incr, params);
344 | typedef SparseBlockBandedQR_EllipseFitting SparseBlockBandedQRFunctor;
345 | SparseBlockBandedQRFunctor functor4(ellipsePoints);
346 | Eigen::LevenbergMarquardt< SparseBlockBandedQRFunctor > lm4(functor4);
347 | lm4.setVerbose(LM_VERBOSE);
348 | begin = clock();
349 | info = lm4.minimize(params);
350 | duration = double(clock() - begin) / CLOCKS_PER_SEC;
351 | std::cout << "Sp Band Bl QR:\t";
352 | printParams(params, nDataPoints, duration);
353 | std::cout << "#####################################################################" << std::endl;
354 | }
355 |
356 | return 0;
357 | }
358 |
359 |
--------------------------------------------------------------------------------
/examples/ellipse_fitting.cpp:
--------------------------------------------------------------------------------
1 | // This file is part of Eigen, a lightweight C++ template library
2 | // for linear algebra.
3 | //
4 | // Copyright (C) 2017 Jan Svoboda
5 | // Copyright (C) 2016 Andrew Fitzgibbon
6 | // Copyright (C) 2016 Sergio Garrido Jurado
7 | //
8 | // This Source Code Form is subject to the terms of the Mozilla
9 | // Public License v. 2.0. If a copy of the MPL was not distributed
10 | // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 |
12 |
13 | // import basic and product tests for deprecated DynamicSparseMatrix
14 | #define EIGEN_NO_DEPRECATED_WARNING
15 |
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 |
22 | #include "main.h"
23 | //#include "sparse_basic.cpp"
24 | //#include "sparse_product.cpp"
25 |
26 | #include
27 | #include
28 | #include
29 | #include
30 |
31 | using namespace Eigen;
32 | using namespace QRKit;
33 |
34 | // Eigen's better DenseQR:
35 | typedef ColPivHouseholderQR > DenseQR;
36 |
37 | typedef int IndexType;
38 |
39 | typedef double Scalar;
40 |
41 | typedef SparseMatrix JacobianType;
42 | typedef Matrix DenseMatrixType;
43 |
44 | template
45 | struct EllipseFitting : SparseFunctor<_Scalar, IndexType>
46 | {
47 | // Class data: 2xN matrix with each column a 2D point
48 | Matrix2Xd ellipsePoints;
49 |
50 | // Number of parameters in the model, to which will be added
51 | // one latent variable per point.
52 | static const int nParamsModel = 5;
53 |
54 | // Constructor initializes points, and tells the base class how many parameters there are in total
55 | EllipseFitting(const Matrix2Xd& points) :
56 | SparseFunctor<_Scalar, IndexType>(nParamsModel + points.cols(), points.cols() * 2),
57 | ellipsePoints(points)
58 | {
59 | }
60 |
61 | // Functor functions
62 | int operator()(const InputType& uv, ValueType& fvec) const
63 | {
64 | // Ellipse parameters are the last 5 entries
65 | auto params = uv.tail(nParamsModel);
66 | double a = params[0];
67 | double b = params[1];
68 | double x0 = params[2];
69 | double y0 = params[3];
70 | double r = params[4];
71 |
72 | // Correspondences (t values) are the first N
73 | for (int i = 0; i < ellipsePoints.cols(); i++) {
74 | double t = uv(i);
75 | double x = a*cos(t)*cos(r) - b*sin(t)*sin(r) + x0;
76 | double y = a*cos(t)*sin(r) + b*sin(t)*cos(r) + y0;
77 | fvec(2 * i + 0) = ellipsePoints(0, i) - x;
78 | fvec(2 * i + 1) = ellipsePoints(1, i) - y;
79 | }
80 |
81 | return 0;
82 | }
83 |
84 | // Functor jacobian
85 | int df(const InputType& uv, JacobianType& fjac)
86 | {
87 | // X_i - (a*cos(t_i) + x0)
88 | // Y_i - (b*sin(t_i) + y0)
89 | int npoints = ellipsePoints.cols();
90 | auto params = uv.tail(nParamsModel);
91 | double a = params[0];
92 | double b = params[1];
93 | double r = params[4];
94 |
95 | TripletArray triplets(npoints * 2 * 5); // npoints * rows_per_point * nonzeros_per_row
96 | for (int i = 0; i < npoints; i++) {
97 | double t = uv(i);
98 | triplets.add(2 * i, i, +a*cos(r)*sin(t) + b*sin(r)*cos(t));
99 | triplets.add(2 * i, npoints + 0, -cos(t)*cos(r));
100 | triplets.add(2 * i, npoints + 1, +sin(t)*sin(r));
101 | triplets.add(2 * i, npoints + 2, -1);
102 | triplets.add(2 * i, npoints + 4, +a*cos(t)*sin(r) + b*sin(t)*cos(r));
103 |
104 | triplets.add(2 * i + 1, i, +a*sin(r)*sin(t) - b*cos(r)*cos(t));
105 | triplets.add(2 * i + 1, npoints + 0, -cos(t)*sin(r));
106 | triplets.add(2 * i + 1, npoints + 1, -sin(t)*cos(r));
107 | triplets.add(2 * i + 1, npoints + 3, -1);
108 | triplets.add(2 * i + 1, npoints + 4, -a*cos(t)*cos(r) + b*sin(t)*sin(r));
109 | }
110 |
111 | fjac.setFromTriplets(triplets.begin(), triplets.end());
112 | return 0;
113 | }
114 | };
115 |
116 | template
117 | struct SparseBlockDiagonalQR_EllipseFitting : public EllipseFitting<_Scalar> {
118 | // QR for J1 subblocks is 2x1
119 | typedef Matrix DenseMatrix2x1;
120 | typedef ColPivHouseholderQR DenseQRSolver2x1;
121 | // QR for J1 is block diagonal
122 | typedef BlockDiagonalSparseQR LeftSuperBlockSolver;
123 | // QR for J1'J2 is general dense (faster than general sparse by about 1.5x for n=500K)
124 | typedef DenseQR RightSuperBlockSolver;
125 |
126 | class QRSolver : public BlockAngularSparseQR {
127 | const int RightBlockColumns = nParamsModel;
128 | public:
129 | // Solver has to know how to treat input general SparseMatrix
130 | QRSolver(const JacobianType& mat)
131 | {
132 | // Left block is sparse block diagonal matrix
133 | JacobianType leftBlock = mat.block(0, 0, mat.rows(), mat.cols() - RightBlockColumns);
134 | SparseBlockDiagonal leftBlockDiag;
135 | leftBlockDiag.fromBlockDiagonalPattern(leftBlock, 2, 1);
136 | // Right block is general DenseMatrix
137 | DenseMatrixType rightBlock = mat.block(0, mat.cols() - RightBlockColumns, mat.rows(), RightBlockColumns);
138 | // Input to the block angular solver is sparse block angular matrix
139 | BlockMatrix1x2, DenseMatrixType> blkAngular(leftBlockDiag, rightBlock);
140 | this->compute(blkAngular);
141 | }
142 | };
143 |
144 | SparseBlockDiagonalQR_EllipseFitting(const Matrix2Xd& points) :
145 | EllipseFitting<_Scalar>(points) {
146 | }
147 | };
148 |
149 | template
150 | struct SparseBlockBandedQR_EllipseFitting : public EllipseFitting<_Scalar>
151 | {
152 | typedef Matrix DenseMatrix2x1;
153 | typedef HouseholderQR BandBlockQRSolver;
154 | typedef BandedBlockedSparseQR BandedBlockedQRSolver;
155 | // QR for J1 is banded blocked QR
156 | typedef BandedBlockedQRSolver LeftSuperBlockSolver;
157 | // QR for J1'J2 is general dense (faster than general sparse by about 1.5x for n=500K)
158 | typedef ColPivHouseholderQR> RightSuperBlockSolver;
159 |
160 | // Define the full QR solver
161 | class QRSolver : public BlockAngularSparseQR {
162 | const int RightBlockColumns = 5;
163 | public:
164 | // Solver has to know how to treat input general SparseMatrix
165 | QRSolver(const JacobianType& mat)
166 | {
167 | // Left block is general SparseMatrix
168 | JacobianType leftBlock = mat.block(0, 0, mat.rows(), mat.cols() - RightBlockColumns);
169 | // Right block is general DenseMatrix
170 | DenseMatrixType rightBlock = mat.block(0, mat.cols() - RightBlockColumns, mat.rows(), RightBlockColumns);
171 | // Input to the block angular solver is sparse block angular matrix
172 | BlockMatrix1x2 blkAngular(leftBlock, rightBlock);
173 | this->compute(blkAngular);
174 | }
175 | };
176 |
177 | SparseBlockBandedQR_EllipseFitting(const Matrix2Xd& points) :
178 | EllipseFitting<_Scalar>(points) {
179 | }
180 | };
181 |
182 | typedef EllipseFitting::InputType ParamsType;
183 |
184 | void printParamsHeader()
185 | {
186 | std::cout << "a \t";
187 | std::cout << "b \t";
188 | std::cout << "x0\t";
189 | std::cout << "y0\t";
190 | std::cout << "r \t";
191 | std::cout << "Duration";
192 | std::cout << std::endl;
193 | }
194 |
195 | void printParams(ParamsType ¶ms, int npoints, double duration = -1.)
196 | {
197 | std::cout << params(npoints) << "\t";
198 | std::cout << params(npoints + 1) << "\t";
199 | std::cout << params(npoints + 2) << "\t";
200 | std::cout << params(npoints + 3) << "\t";
201 | std::cout << params(npoints + 4)*180. / EIGEN_PI << "\t";
202 | if (duration >= 0) {
203 | std::cout << duration << "s";
204 | }
205 | std::cout << std::endl;
206 | }
207 |
208 | void initializeParams(const Matrix2Xd &ellipsePoints, double incr, ParamsType ¶ms)
209 | {
210 | int npoints = ellipsePoints.cols();
211 |
212 | params.resize(EllipseFitting::nParamsModel + npoints);
213 |
214 | // TODO: use Eigen's max/min over columns mat.colwise().maxCoeff()
215 | double minX, minY, maxX, maxY;
216 | minX = maxX = ellipsePoints(0, 0);
217 | minY = maxY = ellipsePoints(1, 0);
218 | for (int i = 0; i fabs(params(npoints))) {
241 | std::swap(params(npoints), params(npoints + 1));
242 | params(npoints + 4) -= 0.5*EIGEN_PI;
243 | }
244 | // a and b should be positive
245 | if (params(npoints)<0) {
246 | params(npoints) *= -1.;
247 | params(npoints + 1) *= -1.;
248 | params(npoints + 4) += EIGEN_PI;
249 | }
250 | // fix rotation angle range
251 | while (params(npoints + 4) < 0) params(npoints + 4) += 2.*EIGEN_PI;
252 | while (params(npoints + 4) > EIGEN_PI) params(npoints + 4) -= EIGEN_PI;
253 | }
254 |
255 | void test_block_diagonal(const Ellipse &el, const Matrix2Xd &ellipsePoints, ParamsType ¶ms)
256 | {
257 | Eigen::LevenbergMarquardtSpace::Status info;
258 | typedef SparseBlockDiagonalQR_EllipseFitting SparseBlockDiagonalQRFunctor;
259 | SparseBlockDiagonalQRFunctor functor3(ellipsePoints);
260 | Eigen::LevenbergMarquardt< SparseBlockDiagonalQRFunctor > lm3(functor3);
261 | info = lm3.minimize(params);
262 |
263 | checkParamsAmbiguity(ellipsePoints, params);
264 |
265 | VERIFY_IS_APPROX(el.a, params(ellipsePoints.cols()));
266 | VERIFY_IS_APPROX(el.b, params(ellipsePoints.cols() + 1));
267 | VERIFY_IS_APPROX(el.x0, params(ellipsePoints.cols() + 2));
268 | VERIFY_IS_APPROX(el.y0, params(ellipsePoints.cols() + 3));
269 | VERIFY_IS_APPROX(el.r, params(ellipsePoints.cols() + 4));
270 | }
271 |
272 | void test_banded_blocked(const Ellipse &el, const Matrix2Xd &ellipsePoints, ParamsType ¶ms)
273 | {
274 | Eigen::LevenbergMarquardtSpace::Status info;
275 | typedef SparseBlockBandedQR_EllipseFitting SparseBlockBandedQRFunctor;
276 | SparseBlockBandedQRFunctor functor4(ellipsePoints);
277 | Eigen::LevenbergMarquardt lm4(functor4);
278 | info = lm4.minimize(params);
279 |
280 | checkParamsAmbiguity(ellipsePoints, params);
281 |
282 | VERIFY_IS_APPROX(el.a, params(ellipsePoints.cols()));
283 | VERIFY_IS_APPROX(el.b, params(ellipsePoints.cols() + 1));
284 | VERIFY_IS_APPROX(el.x0, params(ellipsePoints.cols() + 2));
285 | VERIFY_IS_APPROX(el.y0, params(ellipsePoints.cols() + 3));
286 | VERIFY_IS_APPROX(el.r, params(ellipsePoints.cols() + 4));
287 | }
288 |
289 | // TODO: this looks like it's not used below?
290 | struct Ellipse {
291 | Ellipse()
292 | : a(1), b(1), x0(0), y0(0), r(1) {
293 | }
294 |
295 | Ellipse(const double a, const double b, const double x0, const double y0, const double r)
296 | : a(a), b(b), x0(x0), y0(y0), r(r) {
297 | }
298 |
299 | double a;
300 | double b;
301 | double x0;
302 | double y0;
303 | double r;
304 | };
305 |
306 | void test_sparse_qr_extra_lm_fitting()
307 | {
308 | const int NumSamplePoints = 50000;
309 | // Create the ellipse parameters and data points
310 | // ELLIPSE PARAMETERS
311 | Ellipse el(7.5, 2, 17, 23., 0.23);
312 |
313 | // CREATE DATA SAMPLES
314 | int npoints = NumSamplePoints;
315 | Matrix2Xd ellipsePoints;
316 | ellipsePoints.resize(2, npoints);
317 | Scalar incr = 1.3*EIGEN_PI / Scalar(npoints);
318 | for (int i = 0; i
5 | // Copyright (C) 2016 Andrew Fitzgibbon
6 | // Copyright (C) 2016 Sergio Garrido Jurado
7 | // Copyright (C) 2012-2013 Desire Nuentsa
8 | // Copyright (C) 2012-2014 Gael Guennebaud
9 | //
10 | // This Source Code Form is subject to the terms of the Mozilla
11 | // Public License v. 2.0. If a copy of the MPL was not distributed
12 | // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
13 |
14 | #ifndef QRKIT_BLOCK_DIAGONAL_SPARSE_QR_H
15 | #define QRKIT_BLOCK_DIAGONAL_SPARSE_QR_H
16 |
17 | #include "SparseQRUtils.h"
18 | #include "SparseQROrdering.h"
19 | #include "SparseBlockDiagonal.h"
20 |
21 | namespace QRKit {
22 |
23 | /**
24 | * \ingroup SparseQR_Module
25 | * \class BlockDiagonalSparseQR
26 | * \brief QR factorization of block-diagonal matrix
27 | *
28 | * Performs QR factorization of a block-diagonal matrix and creates matrices Q and R accordingly.
29 | * Template parameter _QFormat specifies the ordering of Q and R:
30 | * MatrixQFormat::FullQ - orthogonal Q, corresponding R is upper triangular (default)
31 | * MatrixQFormat::BlockDiagonalQ - orthogonal block diagonal Q, corresponding R is not upper triangular
32 | * There exists a permutation that allows to convert between FulLQ and BlockDiagonalQ.
33 | *
34 | * \implsparsesolverconcept
35 | *
36 | */
37 | template
38 | class BlockDiagonalSparseQR : public SparseSolverBase >
39 | {
40 | protected:
41 | typedef SparseSolverBase > Base;
42 | using Base::m_isInitialized;
43 | public:
44 | using Base::_solve_impl;
45 | typedef _BlockQRSolver BlockQRSolver;
46 | typedef typename BlockQRSolver::MatrixType BlockMatrixType;
47 | typedef SparseBlockDiagonal MatrixType;
48 | typedef typename MatrixType::Scalar Scalar;
49 | typedef typename MatrixType::RealScalar RealScalar;
50 | typedef typename MatrixType::StorageIndex StorageIndex;
51 | typedef typename MatrixType::Index Index;
52 | typedef Matrix IndexVector;
53 | typedef Matrix ScalarVector;
54 |
55 | typedef SparseMatrix MatrixQType;
56 | typedef SparseMatrix MatrixRType;
57 | typedef PermutationMatrix PermutationType;
58 |
59 | enum MatrixQFormat {
60 | FullQ = 0,
61 | BlockDiagonalQ = 1
62 | };
63 | /*
64 | * Stores information about a dense block in a block sparse matrix.
65 | * Holds the position of the block (row index, column index) and its size (number of rows, number of columns).
66 | */
67 | typedef typename SparseQRUtils::BlockBandedMatrixInfo BlockBandedMatrixInfo;
68 |
69 | enum {
70 | ColsAtCompileTime = Dynamic,//MatrixType::ColsAtCompileTime,
71 | MaxColsAtCompileTime = Dynamic//MatrixType::MaxColsAtCompileTime
72 | };
73 |
74 | public:
75 | BlockDiagonalSparseQR() : m_analysisIsok(false), m_factorizationIsok(false)
76 | { }
77 |
78 | /** Construct a QR factorization of the matrix \a mat.
79 | *
80 | * \sa compute()
81 | */
82 | explicit BlockDiagonalSparseQR(const MatrixType& mat) : m_analysisIsok(false), m_factorizationIsok(false)
83 | {
84 | compute(mat);
85 | }
86 |
87 | /** Computes the QR factorization of the sparse matrix \a mat.
88 | *
89 | *
90 | * If input pattern analysis has been successfully performed before, it won't be run again by default.
91 | * forcePatternAnalysis - if true, forces reruning pattern analysis of the input matrix
92 | * \sa analyzePattern(), factorize()
93 | */
94 | void compute(const MatrixType& mat, const PermutationType &rowPerm = PermutationType(), bool forcePatternAlaysis = false)
95 | {
96 | analyzePattern(mat, rowPerm);
97 |
98 | // Reset variables before the factorization
99 | m_isInitialized = false;
100 | m_factorizationIsok = false;
101 | factorize(mat);
102 | }
103 | void analyzePattern(const MatrixType& mat, const PermutationType &rowPerm = PermutationType());
104 | void factorize(const MatrixType& mat);
105 |
106 | /** \returns the number of rows of the represented matrix.
107 | */
108 | inline Index rows() const { return m_R.rows(); }
109 |
110 | /** \returns the number of columns of the represented matrix.
111 | */
112 | inline Index cols() const { return m_R.cols();}
113 |
114 | /** \returns a const reference to the \b sparse upper triangular matrix R of the QR factorization.
115 | *
116 | * The structure of R is typically very sparse and contains upper triangular blocks on the diagonal.
117 | * A typical pattern of R might look like:
118 | *
119 | * QFormat == FullQ
120 | *
121 | * X X X
122 | * X X
123 | * X
124 | * X X X
125 | * X X
126 | * X
127 | * X X X
128 | * X X
129 | * X
130 | * X X X
131 | * X X
132 | * X
133 | *
134 | * QFormat == BlockDiagonalQ
135 | * \warning Note that forming block diagonal Q, matrix R will not be upper triangular anymore (it will be upper triangular only up to a row permutation)
136 | *
137 | * X X X
138 | * X X
139 | * X
140 | *
141 | *
142 | *
143 | * X X X
144 | * X X
145 | * X
146 | * X X X
147 | * X X
148 | * X
149 | *
150 | *
151 | * X X X
152 | * X X
153 | * X
154 | *
155 | */
156 | const MatrixRType& matrixR() const { return m_R; }
157 |
158 | /** \returns the number of non linearly dependent columns.
159 | * \note Will be always equal to number of columns (full rank) in this case because the solver is not rank-revealing.
160 | */
161 | Index rank() const
162 | {
163 | eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
164 | return m_nonzeropivots;
165 | }
166 |
167 | /** \returns a SparseMatrix holding the explicit representation of the matrix Q.
168 | * The common usage of this function is to apply it to a dense matrix or vector
169 | * \code
170 | * VectorXd B1, B2;
171 | * // Initialize B1
172 | * B2 = matrixQ() * B1;
173 | * \endcode
174 | *
175 | * To get a plain SparseMatrix representation of Q:
176 | * \code
177 | * SparseMatrix Q;
178 | * Q = SparseQR >(A).matrixQ();
179 | * \endcode
180 | *
181 | * Internally, matrix Q is composed explicitly and stored as SparseMatrix for block diagonal matrices. The structure of the output Q
182 | * is known a-priori and is known to be extremely sparse, and it is therefore not expensive to form it explicitly.
183 | *
184 | * The typical structure of the matrix Q will contain two shifted diagonals. The first diagonal is the "economy" matrix Q and the second is the orthogonal complement. It will look something like this:
185 | *
186 | * QFormat == FullQ
187 | * X X X X X X X
188 | * X X X X X X X
189 | * X X X X X X X
190 | * X X X X X X X
191 | * X X X X X X X
192 | * X X X X X X X
193 | * X X X X X X X
194 | * X X X X X X X
195 | * X X X X X X X
196 | * X X X X X X X
197 | * X X X X X X X
198 | * X X X X X X X
199 | * X X X X X X X
200 | * X X X X X X X
201 | * X X X X X X X
202 | * X X X X X X X
203 | * X X X X X X X
204 | * X X X X X X X
205 | * X X X X X X X
206 | * X X X X X X X
207 | * X X X X X X X
208 | *
209 | * QFormat == BlockDiagonalQ
210 | * \warning Note that forming block diagonal Q, matrix R will not be upper triangular anymore (it will be upper triangular only up to a row permutation)
211 | *
212 | * X X X X X X X
213 | * X X X X X X X
214 | * X X X X X X X
215 | * X X X X X X X
216 | * X X X X X X X
217 | * X X X X X X X
218 | * X X X X X X X
219 | * X X X X X X X
220 | * X X X X X X X
221 | * X X X X X X X
222 | * X X X X X X X
223 | * X X X X X X X
224 | * X X X X X X X
225 | * X X X X X X X
226 | * X X X X X X X
227 | * X X X X X X X
228 | * X X X X X X X
229 | * X X X X X X X
230 | * X X X X X X X
231 | * X X X X X X X
232 | * X X X X X X X
233 | *
234 | */
235 | MatrixQType matrixQ() const {
236 | return m_Q;
237 | }
238 |
239 | /** \returns a const reference to the column permutation P that was applied to A such that A*P = Q*R
240 | * Right now, this method is not rank revealing and column permutation is always identity.
241 | */
242 | const PermutationType& colsPermutation() const
243 | {
244 | eigen_assert(m_isInitialized && "Decomposition is not initialized.");
245 | return m_outputPerm_c;
246 | }
247 |
248 | /** \returns a const reference to the row permutation P that was applied to A such that P*A = Q*R
249 | * Permutation that reorders the rows of the input matrix so that it has some block banded structure.
250 | */
251 | const PermutationType& rowsPermutation() const {
252 | eigen_assert(m_isInitialized && "Decomposition is not initialized.");
253 | return m_rowPerm;
254 | }
255 |
256 | /** \internal */
257 | template
258 | bool _solve_impl(const MatrixBase &B, MatrixBase &dest) const
259 | {
260 | eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
261 | eigen_assert(this->rows() == B.rows() && "SparseQR::solve() : invalid number of rows in the right hand side matrix");
262 |
263 | Index rank = this->rank();
264 |
265 | // Compute Q^T * b;
266 | typename Dest::PlainObject y = this->matrixQ().transpose() * B;
267 | typename Dest::PlainObject b = y;
268 |
269 | // Solve with the triangular matrix R
270 | y.resize((std::max)(cols(),y.rows()),y.cols());
271 | y.topRows(rank) = this->matrixR().topLeftCorner(rank, rank).template triangularView().solve(b.topRows(rank));
272 | y.bottomRows(y.rows()-rank).setZero();
273 |
274 | // Apply the column permutation
275 | if (colsPermutation().size()) dest = colsPermutation() * y.topRows(cols());
276 | else dest = y.topRows(cols());
277 |
278 | m_info = Success;
279 | return true;
280 | }
281 |
282 | /** \returns the solution X of \f$ A X = B \f$ using the current decomposition of A.
283 | *
284 | * \sa compute()
285 | */
286 | template
287 | inline const Solve solve(const MatrixBase& B) const
288 | {
289 | eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
290 | eigen_assert(this->rows() == B.rows() && "SparseQR::solve() : invalid number of rows in the right hand side matrix");
291 | return Solve(*this, B.derived());
292 | }
293 | template
294 | inline const Solve solve(const SparseMatrixBase& B) const
295 | {
296 | eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
297 | eigen_assert(this->rows() == B.rows() && "SparseQR::solve() : invalid number of rows in the right hand side matrix");
298 | return Solve(*this, B.derived());
299 | }
300 |
301 | /** \brief Reports whether previous computation was successful.
302 | *
303 | * \returns \c Success if computation was successful,
304 | * \c NumericalIssue if the QR factorization reports a numerical problem
305 | * \c InvalidInput if the input matrix is invalid
306 | *
307 | * \sa iparm()
308 | */
309 | ComputationInfo info() const
310 | {
311 | eigen_assert(m_isInitialized && "Decomposition is not initialized.");
312 | return m_info;
313 | }
314 |
315 | protected:
316 | mutable ComputationInfo m_info;
317 |
318 | MatrixRType m_R; // The triangular factor matrix
319 | MatrixQType m_Q; // The orthogonal reflectors
320 | ScalarVector m_hcoeffs; // The Householder coefficients
321 |
322 | PermutationType m_outputPerm_c; // The final column permutation
323 | PermutationType m_rowPerm; // Row permutation matrix, always identity as solver does not perform row permutations
324 |
325 | Index m_nonzeropivots; // Number of non zero pivots found
326 |
327 | bool m_analysisIsok;
328 | bool m_factorizationIsok;
329 | Index m_numNonZeroQ;
330 |
331 | /*
332 | * Structures filled during sparse matrix pattern analysis.
333 | */
334 | BlockBandedMatrixInfo m_blockInfo;
335 | };
336 |
337 | template
338 | struct SparseQRUtils::HasRowsPermutation> {
339 | static const bool value = true;
340 | };
341 |
342 | /** \brief Preprocessing step of a QR factorization
343 | *
344 | * \warning The matrix \a mat must be in compressed mode (see SparseMatrix::makeCompressed()).
345 | *
346 | * In this step, matrix pattern analysis is performed based on the type of the block QR solver.
347 | *
348 | * (a) BlockQRSolver::RowsAtCompileTime != Dynamic && BlockQRSolver::ColsAtCompileTime != Dynamic
349 | * In this case, it is assumed that the matrix has a known block diagonal structure with
350 | * constant block size and no overlaps.
351 | *
352 | * This step assumes that the user knows the structure beforehand and can specify it
353 | * in form of input parameters.
354 | * If called, the block diagonal pattern is automatically computed from the user input
355 | * and there is no need to call analyzePattern later on, and some unnecessary analysis
356 | * can be saved.
357 | *
358 | * An example of such block diagonal structure for a matrix could be:
359 | * (in this example we could assume having blocks 7x2)
360 | *
361 | * X X
362 | * X X
363 | * X X
364 | * X X
365 | * X X
366 | * X X
367 | * X X
368 | * X X
369 | * X X
370 | * X X
371 | * X X
372 | * X X
373 | * X X
374 | * X X
375 | * X X
376 | * X X
377 | * X X
378 | * X X
379 | * X X
380 | * X X
381 | * X X
382 | *
383 | * (b) BlockQRSolver::RowsAtCompileTime == Dynamic && BlockQRSolver::ColsAtCompileTime == Dynamic
384 | * In this case, row-reordering permutation of A is computed and matrix banded structure is analyzed.
385 | * This is neccessary preprocessing step before the matrix factorization is carried out.
386 | *
387 | * This step assumes there is some sort of banded structure in the matrix.
388 | *
389 | * \note In this step it is assumed that there is no empty row in the matrix \a mat.
390 | */
391 | template
392 | void BlockDiagonalSparseQR::analyzePattern(const MatrixType& mat, const PermutationType &rowPerm) {
393 | // Save possible row permutation
394 | if (rowPerm.rows() == 0) {
395 | // If there's no row permutation given, set it to identity
396 | m_rowPerm.setIdentity(mat.rows());
397 | } else {
398 | // Otherwise use the given row permutation
399 | m_rowPerm = rowPerm;
400 | }
401 | // No need to analyze the matrix, it's already block diagonal
402 | m_R.resize(mat.rows(), mat.cols());
403 |
404 | m_analysisIsok = true;
405 | }
406 |
407 | /** \brief Performs the numerical QR factorization of the input matrix
408 | *
409 | * The function SparseQR::analyzePattern(const MatrixType&) must have been called beforehand with
410 | * a matrix having the same sparsity pattern than \a mat.
411 | *
412 | * \param mat The sparse column-major matrix
413 | */
414 | template
415 | void BlockDiagonalSparseQR::factorize(const MatrixType& mat) {
416 | // Initialize the matrix for column permutations (will be filled during the factorization)
417 | m_outputPerm_c.setIdentity(mat.cols());
418 |
419 | Index numBlocks = mat.size();
420 | Index rank = 0;
421 |
422 | // Q is rows x rows, R is rows x cols
423 | BlockMatrixType block = mat[0];
424 | std::vector > tripletsR(numBlocks * block.rows() * block.cols());
425 | m_Q.resize(mat.rows(), mat.rows());
426 | m_Q.reserve(this->m_blockInfo.nonZeroQEstimate);
427 |
428 | Index m1 = 0;
429 | Index N_start = mat.cols();
430 | Index base_row = 0;
431 | Index base_col = 0;
432 | for (int i = 0; i < numBlocks; i++) {
433 | // Copy current block
434 | block = mat[i];
435 |
436 | // Perform QR
437 | BlockQRSolver blockSolver;
438 | blockSolver.compute(block);
439 | //rank += blockSolver.rank();
440 | rank += blockSolver.cols();
441 | // FixMe: jasvob - only inner block is rank revealing for now.
442 | // The zero columns of each block are not shifted to the end.
443 | // Therefore the method as a whole is not and ...
444 | // ... we cannot treat the result by viewing only first "rank" columns
445 |
446 | typename BlockQRSolver::MatrixQType Qi = blockSolver.matrixQ();
447 | typename BlockQRSolver::MatrixRType Ri = blockSolver.matrixR();
448 |
449 | // Assemble into final Q
450 | if (block.rows() >= block.cols()) {
451 | // each rectangular Qi is partitioned into [U N] where U is rxc and N is rx(r-c)
452 | // All the Us are gathered in the leftmost nc columns of Q, all Ns to the right
453 |
454 | // Q
455 | if(QFormat == MatrixQFormat::FullQ) {
456 | Index curr_m1 = (block.rows() - block.cols());
457 | for (Index j = 0; j < block.rows(); j++) {
458 | assert(base_row + j < m_Q.rows());
459 | m_Q.startVec(base_row + j);
460 | // Us
461 | for (Index k = 0; k < block.cols(); k++) {
462 | assert(base_col + k < m_Q.cols());
463 | m_Q.insertBack(base_row + j, base_col + k) = Qi.coeff(j, k);
464 | }
465 | // Ns
466 | for (Index k = 0; k < curr_m1; k++) {
467 | assert(N_start + m1 + k < m_Q.cols());
468 | m_Q.insertBack(base_row + j, N_start + m1 + k) = Qi.coeff(j, block.cols() + k);
469 | }
470 | }
471 | m1 += curr_m1;
472 |
473 | // R
474 | // Only the top cxc of R is nonzero, so c rows at a time
475 | for (Index j = 0; j < block.cols(); j++) {
476 | for (Index k = j; k < block.cols(); k++) {
477 | tripletsR.emplace_back(base_col + j, base_col + k, Ri.coeff(j, k));
478 | }
479 | }
480 | } else if(QFormat == MatrixQFormat::BlockDiagonalQ) {
481 | // Q diag
482 | Index curr_m1 = (block.rows() - block.cols());
483 | for (Index j = 0; j < block.rows(); j++) {
484 | assert(base_row + j < m_Q.rows());
485 | m_Q.startVec(base_row + j);
486 | // Us
487 | for (Index k = 0; k < block.rows(); k++) {
488 | assert(base_col + k < m_Q.cols());
489 | m_Q.insertBack(base_row + j, base_row + k) = Qi.coeff(j, k);
490 | }
491 | }
492 | m1 += curr_m1;
493 |
494 | // R
495 | // Only the top cxc of R is nonzero, so c rows at a time
496 | for (Index j = 0; j < block.cols(); j++) {
497 | for (Index k = j; k < block.cols(); k++) {
498 | tripletsR.emplace_back(base_row + j, base_col + k, Ri.coeff(j, k));
499 | }
500 | }
501 | } else {
502 | // Non-existing format requested?
503 | eigen_assert(false && "Block Diagonal QR decomposition ... non-existing format of matrix Q requested!");
504 | m_info = InvalidInput;
505 | return;
506 | }
507 |
508 | }
509 | else {
510 | // Just concatenate everything -- it's upper triangular anyway (although not rank-revealing... xxfixme with colperm?)
511 | // xx and indeed for landscape, don't even need to compute QR after we've done the leftmost #rows columns
512 |
513 | eigen_assert(false && "Block Diagonal QR decomposition ... case mat.cols() > mat.rows() not implemented!");
514 | m_info = InvalidInput;
515 | return;
516 | }
517 |
518 | // fill cols permutation
519 | for (Index j = 0; j < block.cols(); j++) {
520 | m_outputPerm_c.indices()(base_col + j, 0) = base_col + blockSolver.colsPermutation().indices()(j, 0);
521 | }
522 |
523 | // Update base row and base col
524 | base_row += block.rows();
525 | base_col += block.cols();
526 | }
527 | // Check last bi and if it doesn't reach until the last row of input matrix, it means there's a 0 block below
528 | // That will mean zeros in R and so no change needed
529 | // It will mean identity in Q -> add it
530 | for (int i = base_row; i < mat.rows(); i++) {
531 | m_Q.startVec(i);
532 | m_Q.insertBack(i, i) = Scalar(1.0);
533 | }
534 |
535 | // Now build Q and R from Qs and Rs of each block
536 | m_Q.finalize();
537 |
538 | m_R.resize(mat.rows(), mat.cols());
539 | m_R.setZero();
540 | m_R.setFromTriplets(tripletsR.begin(), tripletsR.end());
541 | m_R.makeCompressed();
542 |
543 | m_nonzeropivots = rank;
544 | m_isInitialized = true;
545 | m_info = Success;
546 | m_factorizationIsok = true;
547 | }
548 |
549 |
550 | } // end namespace QRKit
551 |
552 | #endif
553 |
--------------------------------------------------------------------------------
/src/QRKit/BlockMatrix1x2.h:
--------------------------------------------------------------------------------
1 | // This file is part of Eigen, a lightweight C++ template library
2 | // for linear algebra.
3 | //
4 | // Copyright (C) 2017 Jan Svoboda
5 | // Copyright (C) 2016 Andrew Fitzgibbon
6 | //
7 | // This Source Code Form is subject to the terms of the Mozilla
8 | // Public License v. 2.0. If a copy of the MPL was not distributed
9 | // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 |
11 | #ifndef BLOCK_MATRIX_1X2_H
12 | #define BLOCK_MATRIX_1X2_H
13 |
14 | namespace QRKit {
15 | /*
16 | * Container for storing 2x1 block matrices.
17 | * Such matrices are composed from two subblocks of arbitrary types specified as template arguments.
18 | *
19 | * An example would be block angular matrix, which typically contains a very
20 | * sparse left block and dense (or almost dense) right block. An example:
21 | *
22 | * XXX XXX
23 | * XXX XXX
24 | * XXX XXX
25 | * XXX XXX
26 | * XXX XXX
27 | * XXX XXX
28 | * XXXXXX
29 | *
30 | */
31 | template
32 | class BlockMatrix1x2 {
33 | public:
34 | BlockMatrix1x2(LeftBlockMatrixType &leftBlock, RightBlockMatrixType &rightBlock)
35 | : leftBlockNat(leftBlock), rightBlockMat(rightBlock) {
36 |
37 | eigen_assert(leftBlockNat.rows() == rightBlockMat.rows());
38 | }
39 |
40 | RightBlockMatrixType& rightBlock() {
41 | return this->rightBlockMat;
42 | }
43 |
44 | LeftBlockMatrixType& leftBlock() {
45 | return this->leftBlockNat;
46 | }
47 |
48 | const RightBlockMatrixType& rightBlock() const {
49 | return this->rightBlockMat;
50 | }
51 |
52 | const LeftBlockMatrixType& leftBlock() const {
53 | return this->leftBlockNat;
54 | }
55 |
56 | int rows() const {
57 | return this->leftBlockNat.rows();
58 | }
59 |
60 | int cols() const {
61 | return this->leftBlockNat.cols() + this->rightBlockMat.cols();
62 | }
63 |
64 | protected:
65 | RightBlockMatrixType &rightBlockMat;
66 | LeftBlockMatrixType &leftBlockNat;
67 | };
68 | }
69 |
70 | #endif
71 |
72 |
--------------------------------------------------------------------------------
/src/QRKit/BlockYTY.h:
--------------------------------------------------------------------------------
1 | // This file is part of Eigen, a lightweight C++ template library
2 | // for linear algebra.
3 | //
4 | // Copyright (C) 2017 Jan Svoboda
5 | // Copyright (C) 2016 Andrew Fitzgibbon
6 | //
7 | // This Source Code Form is subject to the terms of the Mozilla
8 | // Public License v. 2.0. If a copy of the MPL was not distributed
9 | // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 |
11 | #ifndef BLOCK_YTY_H
12 | #define BLOCK_YTY_H
13 |
14 | /*
15 | * A dense block of the compressed WY representation (YTY) of the Householder product.
16 | * Stores matrices Y (m x n) and T (n x n) and number of zeros between main diagonal and subdiagonal parts of the block YTY.
17 | * Provides overloaded multiplication operator (*) allowing to easily perform the multiplication with a dense vector (Y * (T * (Y' * v)))
18 | */
19 | namespace QRKit {
20 | template class BlockYTY;
21 | template struct BlockYTYProductReturnType;
22 | template struct BlockYTYProductTransposeReturnType;
23 | template struct BlockYTY_VecProduct;
24 | }
25 |
26 | namespace Eigen {
27 | namespace internal {
28 |
29 | // traits
30 | template struct traits >
31 | {
32 | typedef typename BlockYTYType::MatrixType ReturnType;
33 | typedef typename ReturnType::StorageIndex StorageIndex;
34 | typedef typename ReturnType::StorageKind StorageKind;
35 | enum {
36 | RowsAtCompileTime = Dynamic,
37 | ColsAtCompileTime = Dynamic
38 | };
39 | };
40 |
41 | template struct traits >
42 | {
43 | typedef typename BlockYTYType::MatrixType ReturnType;
44 | };
45 |
46 | template struct traits >
47 | {
48 | typedef typename Derived::PlainObject ReturnType;
49 | };
50 |
51 | // BlockYTY_traits
52 | template struct BlockYTY_traits { };
53 |
54 | } // End namespace internal
55 | } // End namespace Eigen
56 |
57 | namespace QRKit {
58 | template
59 | class BlockYTYTranspose {
60 | public:
61 | typedef typename BlockYTY::VectorType VectorType;
62 |
63 | BlockYTYTranspose() : yty(NULL) {
64 | }
65 |
66 | BlockYTYTranspose(BlockYTY* blk) : yty(blk) {
67 | }
68 |
69 | VectorType operator*(const VectorType& other) const {
70 | return (yty->Y() * (yty->T().transpose() * (yty->Y().transpose() * other)));
71 | }
72 |
73 | private:
74 | BlockYTY* yty;
75 | };
76 |
77 | template
78 | class BlockYTY {
79 | public:
80 | typedef Eigen::Matrix MatrixType;
81 | typedef ScalarType Scalar;
82 | typedef Eigen::Matrix VectorType;
83 |
84 | BlockYTY() {
85 | }
86 |
87 | BlockYTY(const MatrixType& Y, const MatrixType& T, const IndexType row, const IndexType col, const IndexType numZeros)
88 | : matY(Y), matT(T), ridx(row), cidx(col), nzrs(numZeros) {
89 |
90 | }
91 |
92 | const MatrixType& Y() const {
93 | return this->matY;
94 | }
95 |
96 | const MatrixType& T() const {
97 | return this->matT;
98 | }
99 |
100 | IndexType rowIndex() const {
101 | return this->ridx;
102 | }
103 | IndexType colIndex() const {
104 | return this->cidx;
105 | }
106 |
107 | IndexType rows() const {
108 | return this->matY.rows();
109 | }
110 | IndexType cols() const {
111 | return this->matY.cols();
112 | }
113 |
114 | IndexType numZeros() const {
115 | return this->nzrs;
116 | }
117 |
118 | BlockYTYTranspose> transpose() const {
119 | return BlockYTYTranspose>(this);
120 | }
121 |
122 | VectorType operator*(const VectorType& other) const {
123 | return (this->matY * (this->matT * (this->matY.transpose() * other)));
124 | }
125 |
126 | BlockYTYProductReturnType yty() const {
127 | return BlockYTYProductReturnType(*this);
128 | }
129 |
130 | protected:
131 | MatrixType matY;
132 | MatrixType matT;
133 |
134 | IndexType ridx;
135 | IndexType cidx;
136 | IndexType nzrs;
137 |
138 | template friend struct BlockYTY_VecProduct;
139 | };
140 |
141 | /************************************ Expression templates for the evaluation of single YTY product ***************************************/
142 | template
143 | struct BlockYTY_VecProduct : Eigen::ReturnByValue >
144 | {
145 | // Get the references
146 | BlockYTY_VecProduct(const BlockYTYType& yty, const Derived& other, bool transpose) :
147 | m_yty(yty), m_other(other), m_transpose(transpose) {}
148 | inline Index rows() const { return m_other.rows(); }
149 | inline Index cols() const { return m_other.cols(); }
150 |
151 | // Assign to a vector
152 | template
153 | void evalTo(DesType& res) const
154 | {
155 | res = m_other;
156 |
157 | Derived segmentVec;
158 | SparseQRUtils::SegmentDescriptors segDescs = {
159 | { m_yty.rowIndex(), m_yty.cols() },
160 | { m_yty.rowIndex() + m_yty.cols() + m_yty.numZeros(), m_yty.rows() - m_yty.cols() }
161 | };
162 | SparseQRUtils::getVectorSegments(segmentVec, res, segDescs, m_yty.rows());
163 | if (m_transpose) {
164 | // Non-aliasing expr -> noalias()
165 | segmentVec.noalias() += (m_yty.matY * (m_yty.matT.transpose() * (m_yty.matY.transpose() * segmentVec)));
166 | }
167 | else {
168 | // Non-aliasing expr -> noalias()
169 | segmentVec.noalias() += (m_yty.matY * (m_yty.matT * (m_yty.matY.transpose() * segmentVec)));
170 | }
171 | SparseQRUtils::setVectorSegments(res, segmentVec, segDescs);
172 | }
173 |
174 | const BlockYTYType& m_yty;
175 | const Derived& m_other;
176 | bool m_transpose;
177 | };
178 |
179 | template
180 | struct BlockYTYProductReturnType : public EigenBase >
181 | {
182 | typedef typename BlockYTYType::Scalar Scalar;
183 | typedef typename BlockYTYType::MatrixType DenseMatrix;
184 | enum {
185 | RowsAtCompileTime = Dynamic,
186 | ColsAtCompileTime = Dynamic
187 | };
188 | explicit BlockYTYProductReturnType(const BlockYTYType& yty) : m_yty(yty) {}
189 |
190 | template
191 | BlockYTY_VecProduct operator*(const MatrixBase& other)
192 | {
193 | return BlockYTY_VecProduct(m_yty, other.derived(), false);
194 | }
195 | template
196 | BlockYTY_VecProduct> operator*(const SparseMatrix<_Scalar, _Options, _Index>& other)
197 | {
198 | return BlockYTY_VecProduct>(m_yty, other, false);
199 | }
200 | BlockYTYProductTransposeReturnType adjoint() const
201 | {
202 | return BlockYTYProductTransposeReturnType(m_yty);
203 | }
204 | BlockYTYProductTransposeReturnType transpose() const
205 | {
206 | return BlockYTYProductTransposeReturnType(m_yty);
207 | }
208 |
209 | const BlockYTYType& m_yty;
210 | };
211 |
212 | template
213 | struct BlockYTYProductTransposeReturnType
214 | {
215 | explicit BlockYTYProductTransposeReturnType(const BlockYTYType& yty) : m_yty(yty) {}
216 | template
217 | BlockYTY_VecProduct operator*(const MatrixBase& other)
218 | {
219 | return BlockYTY_VecProduct(m_yty, other.derived(), true);
220 | }
221 | template
222 | BlockYTY_VecProduct> operator*(const SparseMatrix<_Scalar, _Options, _Index>& other)
223 | {
224 | return BlockYTY_VecProduct>(m_yty, other, true);
225 | }
226 | const BlockYTYType& m_yty;
227 | };
228 | } // End namespace QRKit
229 |
230 | namespace Eigen {
231 | namespace internal {
232 | template
233 | struct evaluator_traits >
234 | {
235 | typedef typename BlockYTYType::MatrixType MatrixType;
236 | typedef typename storage_kind_to_evaluator_kind::Kind Kind;
237 | typedef SparseShape Shape;
238 | };
239 | }
240 | /************************************************************************************************************************************/
241 | }
242 |
243 | #endif
244 |
245 |
--------------------------------------------------------------------------------
/src/QRKit/BlockedThinDenseQR.h:
--------------------------------------------------------------------------------
1 | // This file is part of Eigen, a lightweight C++ template library
2 | // for linear algebra.
3 | //
4 | // Copyright (C) 2017 Jan Svoboda
5 | // Copyright (C) 2016 Andrew Fitzgibbon
6 | //
7 | // This Source Code Form is subject to the terms of the Mozilla
8 | // Public License v. 2.0. If a copy of the MPL was not distributed
9 | // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 |
11 | #ifndef QRKIT_BLOCKED_THIN_DENSE_QR_H
12 | #define QRKIT_BLOCKED_THIN_DENSE_QR_H
13 |
14 | #include
15 | #include
16 | #include
17 | #include "BlockedThinQRBase.h"
18 |
19 | namespace QRKit {
20 | template class BlockedThinDenseQR;
21 | }
22 |
23 | namespace Eigen {
24 | namespace internal {
25 | // BlockedThinDenseQR_traits
26 | template struct BlockedThinDenseQR_traits { };
27 | template struct BlockedThinDenseQR_traits> {
28 | typedef Matrix Vector;
29 | };
30 | template struct BlockedThinDenseQR_traits> {
31 | typedef SparseVector Vector;
32 | };
33 | } // End namespace internal
34 | } // End namespace Eigen
35 |
36 | namespace QRKit {
37 | /**
38 | * \ingroup BlockedThinDenseQR_Module
39 | * \class BlockedThinDenseQR
40 | * \brief Sparse Householder QR Factorization for banded matrices operating on dense matrix.
41 | *
42 | * In some cases, it is faster to represent the input matrix as dense matrix and treat its sparsity pattern internally.
43 | * Obviously, such approach requires more memory, it is however computationally much more efficient.
44 | *
45 | * This implementation is not rank revealing and uses Eigen::HouseholderQR for solving the dense blocks.
46 | *
47 | * Q is the orthogonal matrix represented as products of Householder reflectors.
48 | * Use matrixQ() to get an expression and matrixQ().transpose() to get the transpose.
49 | * You can then apply it to a vector.
50 | *
51 | * R is the sparse triangular or trapezoidal matrix. The later occurs when A is rank-deficient.
52 | * matrixR().topLeftCorner(rank(), rank()) always returns a triangular factor of full rank.
53 | *
54 | * \tparam _MatrixType The type of the sparse matrix A, must be a column-major SparseMatrix<>
55 | * \implsparsesolverconcept
56 | *
57 | * \warning The input sparse matrix A must be in compressed mode (see SparseMatrix::makeCompressed()).
58 | *
59 | */
60 |
61 | template
62 | class BlockedThinDenseQR : public BlockedThinQRBase<_MatrixType, Eigen::HouseholderQR>,
63 | Eigen::Matrix, _SuggestedBlockCols>
64 | {
65 | protected:
66 | typedef BlockedThinQRBase<_MatrixType, Eigen::HouseholderQR>,
67 | Eigen::Matrix, _SuggestedBlockCols> Base;
68 |
69 | public:
70 | typedef _MatrixType MatrixType;
71 | typedef typename Base::Scalar Scalar;
72 | typedef typename Base::RealScalar RealScalar;
73 | typedef typename Base::StorageIndex StorageIndex;
74 | typedef typename Base::IndexVector IndexVector;
75 | typedef typename Base::DenseVectorType DenseVectorType;
76 | typedef typename Base::DenseMatrixType DenseMatrixType;
77 |
78 | typedef typename Base::MatrixQType MatrixQType;
79 | typedef typename Base::MatrixRType MatrixRType;
80 | typedef typename Base::PermutationType PermutationType;
81 |
82 | typedef typename Base::DenseBlockQR DenseBlockQR;
83 |
84 | typedef typename Base::BlockBandedMatrixInfo BlockBandedMatrixInfo;
85 |
86 | enum {
87 | ColsAtCompileTime = MatrixType::ColsAtCompileTime,
88 | MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
89 | };
90 |
91 | public:
92 | BlockedThinDenseQR() : Base() {
93 | }
94 |
95 | explicit BlockedThinDenseQR(const MatrixType& mat) : Base(mat) {
96 | }
97 |
98 | /** Computes the QR factorization of the sparse matrix \a mat.
99 | *
100 | * \warning The matrix \a mat must be in compressed mode (see SparseMatrix::makeCompressed()).
101 | *
102 | * \sa analyzePattern(), factorize()
103 | */
104 | virtual void compute(const MatrixType& mat)
105 | {
106 | // Reset variables in case this method is called multiple times
107 | this->m_isInitialized = false;
108 | this->m_factorizationIsok = false;
109 | this->m_blocksYT.clear();
110 |
111 | // Analyze input matrix pattern and perform row and column permutations
112 | // Stores input matrix to m_pmat
113 | analyzePattern(mat);
114 |
115 | // Create dense version of the already permuted input matrix
116 | // It is much faster to do the permutations on the sparse version
117 | this->m_R = mat;
118 |
119 | // And start factorizing block-by-block
120 | Index solvedCols = 0;
121 | Index cntr = 0;
122 | // As long as there are some unsolved columns
123 | while (solvedCols < this->m_R.cols()) {
124 | // Get next block info
125 | this->updateBlockInfo(solvedCols, this->m_R, 0, _SuggestedBlockCols);
126 |
127 | // Factorize current block
128 | factorize(this->m_R);
129 | solvedCols += this->denseBlockInfo.numCols;
130 | }
131 |
132 | this->m_nonzeroPivots = this->m_R.cols();
133 | this->m_isInitialized = true;
134 | this->m_info = Success;
135 | }
136 | virtual void analyzePattern(const MatrixType& mat, bool rowPerm = true, bool colPerm = true) {
137 | // No column permutation here
138 | this->m_outputPerm_c.setIdentity(mat.cols());
139 |
140 | // And no row permutatio neither
141 | this->m_rowPerm.setIdentity(mat.rows());
142 |
143 | this->m_analysisIsok = true;
144 | }
145 |
146 | virtual void updateBlockInfo(const Index solvedCols, const MatrixType& mat, const Index newPivots, const Index blockCols = -1) {
147 | Index newCols = (blockCols > 0) ? blockCols : _SuggestedBlockCols;
148 | Index colIdx = solvedCols + newCols;
149 | Index numRows = mat.rows() - solvedCols;
150 | if (colIdx >= mat.cols()) {
151 | colIdx = mat.cols() - 1;
152 | newCols = mat.cols() - solvedCols;
153 | numRows = mat.rows() - solvedCols;
154 | }
155 |
156 | this->denseBlockInfo = typename BlockBandedMatrixInfo::MatrixBlockInfo(solvedCols, numRows, newCols);
157 | }
158 |
159 | virtual void factorize(DenseMatrixType& mat) {
160 | // Dense QR solver used for each dense block
161 | DenseBlockQR houseqr;
162 |
163 | /*********** Process the block ***********/
164 | // 1) Factorize the block
165 | houseqr.compute(mat.block(this->denseBlockInfo.idxRow, this->denseBlockInfo.idxRow, this->denseBlockInfo.numRows, this->denseBlockInfo.numCols));
166 |
167 | // 2) Create matrices Y and T
168 | DenseMatrixType Y, T;
169 | this->computeBlockedRepresentation(houseqr, Y, T);
170 |
171 | // Save current Y and T. The block YTY contains a main diagonal and subdiagonal part separated by (numZeros) zero rows.
172 | this->m_blocksYT.insert(typename SparseBlockYTY::Element(this->denseBlockInfo.idxRow, this->denseBlockInfo.idxRow, BlockYTY(Y, T, this->denseBlockInfo.idxRow, this->denseBlockInfo.idxCol, 0)));
173 |
174 | // Update the trailing columns of the matrix block
175 | this->updateMat(this->denseBlockInfo.idxRow, mat.cols(), mat, this->m_blocksYT.size() - 1);
176 | }
177 |
178 | protected:
179 | };
180 |
181 | } // end namespace QRKit
182 |
183 | #endif
184 |
--------------------------------------------------------------------------------
/src/QRKit/BlockedThinQRBase.h:
--------------------------------------------------------------------------------
1 | // This file is part of Eigen, a lightweight C++ template library
2 | // for linear algebra.
3 | //
4 | // Copyright (C) 2017 Jan Svoboda
5 | // Copyright (C) 2016 Andrew Fitzgibbon
6 | //
7 | // This Source Code Form is subject to the terms of the Mozilla
8 | // Public License v. 2.0. If a copy of the MPL was not distributed
9 | // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 |
11 | #ifndef QRKIT_BLOCKED_THIN_QR_BASE_H
12 | #define QRKIT_BLOCKED_THIN_QR_BASE_H
13 |
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include "SparseBlockYTY.h"
19 | #include "SparseQRUtils.h"
20 | #include "SparseQROrdering.h"
21 |
22 | namespace QRKit{
23 | template class BlockedThinQRBase;
24 | template struct BlockedThinQRBaseMatrixQReturnType;
25 | template struct BlockedThinQRBaseMatrixQTransposeReturnType;
26 | template struct BlockedThinQRBase_QProduct;
27 | }
28 |
29 | namespace Eigen {
30 |
31 | namespace internal {
32 |
33 | // traits
34 | template struct traits >
35 | {
36 | typedef typename BlockedThinQRBaseType::MatrixType ReturnType;
37 | typedef typename ReturnType::StorageIndex StorageIndex;
38 | typedef typename ReturnType::StorageKind StorageKind;
39 | enum {
40 | RowsAtCompileTime = Dynamic,
41 | ColsAtCompileTime = Dynamic
42 | };
43 | };
44 |
45 | template struct traits >
46 | {
47 | typedef typename BlockedThinQRBaseType::MatrixType ReturnType;
48 | };
49 |
50 | template struct traits >
51 | {
52 | typedef typename Derived::PlainObject ReturnType;
53 | };
54 |
55 | // BlockedThinQRBase_traits
56 | template struct BlockedThinQRBase_traits { };
57 | template struct BlockedThinQRBase_traits> {
58 | typedef Matrix Vector;
59 | };
60 | template struct BlockedThinQRBase_traits> {
61 | typedef SparseVector Vector;
62 | };
63 | } // End namespace internal
64 | } // End namespace Eigen
65 |
66 | namespace QRKit {
67 | /**
68 | * \ingroup BlockedThinQRBase_Module
69 | * \class BlockedThinQRBase
70 | * \brief Sparse Householder QR Factorization for banded matrices
71 | * This implementation is not rank revealing and uses Eigen::HouseholderQR for solving the dense blocks.
72 | *
73 | * Q is the orthogonal matrix represented as products of Householder reflectors.
74 | * Use matrixQ() to get an expression and matrixQ().transpose() to get the transpose.
75 | * You can then apply it to a vector.
76 | *
77 | * R is the sparse triangular or trapezoidal matrix. The later occurs when A is rank-deficient.
78 | * matrixR().topLeftCorner(rank(), rank()) always returns a triangular factor of full rank.
79 | *
80 | * \tparam _MatrixType The type of the sparse matrix A, must be a column-major SparseMatrix<>
81 | * \implsparsesolverconcept
82 | *
83 | * \warning The input sparse matrix A must be in compressed mode (see SparseMatrix::makeCompressed()).
84 | *
85 | */
86 |
87 | template
88 | struct SparseQRUtils::HasRowsPermutation> {
89 | static const bool value = true;
90 | };
91 |
92 | template
93 | class BlockedThinQRBase : public SparseSolverBase >
94 | {
95 | protected:
96 | typedef SparseSolverBase > Base;
97 | using Base::m_isInitialized;
98 |
99 | public:
100 | using Base::_solve_impl;
101 | typedef _MatrixType MatrixType;
102 | typedef typename MatrixType::Scalar Scalar;
103 | typedef typename MatrixType::RealScalar RealScalar;
104 | typedef typename MatrixType::StorageIndex StorageIndex;
105 | typedef Matrix IndexVector;
106 | typedef Matrix DenseVectorType;
107 | typedef Matrix DenseMatrixType;
108 |
109 | typedef BlockedThinQRBaseMatrixQReturnType MatrixQType;
110 | typedef _MatrixRType MatrixRType;
111 | typedef PermutationMatrix PermutationType;
112 |
113 | typedef _DenseBlockQR DenseBlockQR;
114 |
115 | /*
116 | * Stores information about a dense block in a block sparse matrix.
117 | * Holds the position of the block (row index, column index) and its size (number of rows, number of columns).
118 | */
119 | typedef typename SparseQRUtils::BlockBandedMatrixInfo BlockBandedMatrixInfo;
120 |
121 | enum {
122 | ColsAtCompileTime = MatrixType::ColsAtCompileTime,
123 | MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime
124 | };
125 |
126 | public:
127 | BlockedThinQRBase() : m_analysisIsok(false)
128 | { }
129 |
130 | /** Construct a QR factorization of the matrix \a mat.
131 | *
132 | * \warning The matrix \a mat must be in compressed mode (see SparseMatrix::makeCompressed()).
133 | *
134 | * \sa compute()
135 | */
136 | explicit BlockedThinQRBase(const MatrixType& mat) : m_analysisIsok(false)
137 | {
138 | compute(mat);
139 | }
140 |
141 | /** Computes the QR factorization of the sparse matrix \a mat.
142 | *
143 | * \warning The matrix \a mat must be in compressed mode (see SparseMatrix::makeCompressed()).
144 | *
145 | * If input pattern analysis has been successfully performed before, it won't be run again by default.
146 | * forcePatternAnalysis - if true, forces reruning pattern analysis of the input matrix
147 | * \sa analyzePattern(), factorize()
148 | */
149 | virtual void compute(const MatrixType& mat) = 0;
150 | virtual void analyzePattern(const MatrixType& mat, bool rowPerm = true, bool colPerm = true) = 0;
151 | virtual void updateBlockInfo(const Index solvedCols, const MatrixType& mat, const Index newPivots, const Index blockCols = -1) = 0;
152 | virtual void factorize(DenseMatrixType& mat) = 0;
153 |
154 | /** \returns the number of rows of the represented matrix.
155 | */
156 | inline Index rows() const { return m_R.rows(); }
157 |
158 | /** \returns the number of columns of the represented matrix.
159 | */
160 | inline Index cols() const { return m_R.cols(); }
161 |
162 | /** \returns a const reference to the \b sparse upper triangular matrix R of the QR factorization.
163 | * \warning The entries of the returned matrix are not sorted. This means that using it in algorithms
164 | * expecting sorted entries will fail. This include random coefficient accesses (SpaseMatrix::coeff()),
165 | * and coefficient-wise operations. Matrix products and triangular solves are fine though.
166 | *
167 | * To sort the entries, you can assign it to a row-major matrix, and if a column-major matrix
168 | * is required, you can copy it again:
169 | * \code
170 | * SparseMatrix R = qr.matrixR(); // column-major, not sorted!
171 | * SparseMatrix Rr = qr.matrixR(); // row-major, sorted
172 | * SparseMatrix Rc = Rr; // column-major, sorted
173 | * \endcode
174 | */
175 | const MatrixRType& matrixR() const { return m_R; }
176 |
177 | /** \returns the number of non linearly dependent columns as determined by the pivoting threshold.
178 | *
179 | * \sa setPivotThreshold()
180 | */
181 | Index rank() const
182 | {
183 | eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
184 | return m_nonzeroPivots;
185 | }
186 |
187 | /** \returns an expression of the matrix Q as products of sparse Householder reflectors.
188 | * The common usage of this function is to apply it to a dense matrix or vector
189 | * \code
190 | * VectorXd B1, B2;
191 | * // Initialize B1
192 | * B2 = matrixQ() * B1;
193 | * \endcode
194 | *
195 | * To get a plain SparseMatrix representation of Q:
196 | * \code
197 | * SparseMatrix Q;
198 | * Q = BlockedThinQRBase >(A).matrixQ();
199 | * \endcode
200 | * Internally, this call simply performs a sparse product between the matrix Q
201 | * and a sparse identity matrix. However, due to the fact that the sparse
202 | * reflectors are stored unsorted, two transpositions are needed to sort
203 | * them before performing the product.
204 | */
205 | BlockedThinQRBaseMatrixQReturnType matrixQ() const
206 | {
207 | return BlockedThinQRBaseMatrixQReturnType(*this);
208 | }
209 |
210 | /** \returns a const reference to the column permutation P that was applied to A such that A*P = Q*R
211 | * It is the combination of the fill-in reducing permutation and numerical column pivoting.
212 | */
213 | const PermutationType& colsPermutation() const
214 | {
215 | eigen_assert(m_isInitialized && "Decomposition is not initialized.");
216 | return m_outputPerm_c;
217 | }
218 |
219 | const PermutationType& rowsPermutation() const {
220 | eigen_assert(m_isInitialized && "Decomposition is not initialized.");
221 | return this->m_rowPerm;
222 | }
223 |
224 | /** \internal */
225 | template
226 | bool _solve_impl(const MatrixBase& B, MatrixBase& dest) const
227 | {
228 | eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
229 | eigen_assert(this->rows() == B.rows() && "BlockedThinQRBase::solve() : invalid number of rows in the right hand side matrix");
230 |
231 | Index rank = this->rank();
232 |
233 | // Compute Q^T * b;
234 | typename Dest::PlainObject y = this->matrixQ().transpose() * B;
235 | typename Dest::PlainObject b = y;
236 |
237 | // Solve with the triangular matrix R
238 | y.resize((std::max)(cols(), y.rows()), y.cols());
239 | y.topRows(rank) = this->matrixR().topLeftCorner(rank, rank).template triangularView().solve(b.topRows(rank));
240 | y.bottomRows(y.rows() - rank).setZero();
241 |
242 | dest = y.topRows(cols());
243 |
244 | m_info = Success;
245 | return true;
246 | }
247 |
248 | /** \returns the solution X of \f$ A X = B \f$ using the current decomposition of A.
249 | *
250 | * \sa compute()
251 | */
252 | template
253 | inline const Solve solve(const MatrixBase& B) const
254 | {
255 | eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
256 | eigen_assert(this->rows() == B.rows() && "BlockedThinQRBase::solve() : invalid number of rows in the right hand side matrix");
257 | return Solve(*this, B.derived());
258 | }
259 | template
260 | inline const Solve solve(const SparseMatrixBase& B) const
261 | {
262 | eigen_assert(m_isInitialized && "The factorization should be called first, use compute()");
263 | eigen_assert(this->rows() == B.rows() && "BlockedThinQRBase::solve() : invalid number of rows in the right hand side matrix");
264 | return Solve(*this, B.derived());
265 | }
266 |
267 | /** \brief Reports whether previous computation was successful.
268 | *
269 | * \returns \c Success if computation was successful,
270 | * \c NumericalIssue if the QR factorization reports a numerical problem
271 | * \c InvalidInput if the input matrix is invalid
272 | *
273 | * \sa iparm()
274 | */
275 | ComputationInfo info() const
276 | {
277 | eigen_assert(m_isInitialized && "Decomposition is not initialized.");
278 | return m_info;
279 | }
280 |
281 | protected:
282 | typedef SparseBlockYTY SparseBlockYTYType;
283 |
284 | mutable ComputationInfo m_info;
285 |
286 | MatrixRType m_R; // The triangular factor matrix
287 | SparseBlockYTYType m_blocksYT; // Sparse block matrix storage holding the dense YTY blocks of the blocked representation of Householder reflectors.
288 |
289 | PermutationType m_outputPerm_c; // The final column permutation (for compatibility here, set to identity)
290 | PermutationType m_rowPerm;
291 |
292 | Index m_nonzeroPivots; // Number of non zero pivots found
293 |
294 | bool m_analysisIsok;
295 | bool m_factorizationIsok;
296 |
297 | /*
298 | * Structures filled during sparse matrix pattern analysis.
299 | */
300 | typename BlockBandedMatrixInfo::MatrixBlockInfo denseBlockInfo;
301 |
302 | template friend struct BlockedThinQRBase_QProduct;
303 |
304 | void updateMat(const Index& fromIdx, const Index& toIdx, DenseMatrixType& mat, const Index& blockK = -1);
305 | void computeBlockedRepresentation(const DenseBlockQR& slvr, DenseMatrixType& Y, DenseMatrixType& T);
306 | };
307 |
308 | template
309 | void BlockedThinQRBase::updateMat(const Index& fromIdx, const Index& toIdx, DenseMatrixType& mat, const Index& blockK) {
310 | // Now update the unsolved rest of m_pmat
311 | Index blockRows = this->m_blocksYT[blockK].value.rows();
312 |
313 | // loop over all items
314 | #pragma omp parallel for
315 | for (int j = fromIdx; j < toIdx; j++) {
316 | mat.middleRows(this->denseBlockInfo.idxRow, blockRows).col(j).noalias()
317 | += (this->m_blocksYT[blockK].value.Y() * (this->m_blocksYT[blockK].value.T().transpose() * (this->m_blocksYT[blockK].value.Y().transpose() * mat.middleRows(this->denseBlockInfo.idxRow, blockRows).col(j))));
318 | }
319 | }
320 |
321 | template
322 | void BlockedThinQRBase::computeBlockedRepresentation(const DenseBlockQR& slvr, DenseMatrixType& Y, DenseMatrixType& T) {
323 | Index numRows = this->denseBlockInfo.numRows;
324 | Index numCols = this->denseBlockInfo.numCols;
325 |
326 | T = DenseMatrixType::Zero(numCols, numCols);
327 | Y = DenseMatrixType::Identity(numRows, numCols);
328 | for (int bc = 0; bc < numCols; bc++) {
329 | Y.col(bc).segment(bc + 1, numRows - bc - 1) = slvr.householderQ().essentialVector(bc);
330 | }
331 | Eigen::internal::make_block_householder_triangular_factor(T, Y, slvr.hCoeffs());
332 | T = -T;
333 | }
334 |
335 | /*
336 | * General Householder product evaluation performing Q * A or Q.T * A.
337 | * The general version is assuming that A is sparse and that the output will be sparse as well.
338 | * Offers single-threaded and multi-threaded implementation.
339 | * The choice of implementation depends on a template parameter of the BlockedThinQRBase class.
340 | * The single-threaded implementation cannot work in-place. It is implemented this way for performance related reasons.
341 | */
342 | template