irlba
A C++ library for IRLBA
Loading...
Searching...
No Matches
wrappers.hpp
Go to the documentation of this file.
1#ifndef IRLBA_WRAPPERS_HPP
2#define IRLBA_WRAPPERS_HPP
3
4#include "utils.hpp"
5#include "Eigen/Dense"
6#include <type_traits>
7
13namespace irlba {
14
15// This painful setup for the workspaces is because std::conditional requires
16// both options to be valid types, so we can't just make a conditional to
17// extract Matrix_::(Adjoint)Workspace for non-Eigen matrices (as this won't be
18// valid for Eigen matrices that lack the typedef).
19
24template<class Matrix_, typename = int>
29 typedef typename Matrix_::Workspace type;
30};
31
37template<class Matrix_>
38struct get_workspace<Matrix_, decltype((void) std::declval<typename Matrix_::Index>(), 0)> {
42 typedef bool type;
43};
44
52template<class Matrix_>
54
60template<class Matrix_>
62 if constexpr(std::is_same<WrappedWorkspace<Matrix_>, bool>::value) {
63 return false;
64 } else {
65 return matrix.workspace();
66 }
67}
68
73template<class Matrix_, typename = int>
78 typedef typename Matrix_::AdjointWorkspace type;
79};
80
86template<class Matrix_>
87struct get_adjoint_workspace<Matrix_, decltype((void) std::declval<typename Matrix_::Index>(), 0)> {
91 typedef bool type;
92};
93
101template<class Matrix_>
103
109template<class Matrix_>
111 if constexpr(std::is_same<WrappedAdjointWorkspace<Matrix_>, bool>::value) {
112 return false;
113 } else {
114 return matrix.adjoint_workspace();
115 }
116}
117
121namespace internal {
122
123template<class Matrix_, typename = int>
124struct is_eigen {
125 static constexpr bool value = false;
126};
127
128template<class Matrix_>
129struct is_eigen<Matrix_, decltype((void) std::declval<typename Matrix_::Index>(), 0)> {
130 static constexpr bool value = true;
131};
132
133}
149template<class Matrix_, class Right_, class EigenVector_>
151 if constexpr(internal::is_eigen<Matrix_>::value) {
152 out.noalias() = matrix * rhs;
153 } else {
154 matrix.multiply(rhs, work, out);
155 }
156}
157
169template<class Matrix_, class Right_, class EigenVector_>
171 if constexpr(internal::is_eigen<Matrix_>::value) {
172 out.noalias() = matrix.adjoint() * rhs;
173 } else {
174 matrix.adjoint_multiply(rhs, work, out);
175 }
176}
177
184template<class EigenMatrix_, class Matrix_>
186 if constexpr(internal::is_eigen<Matrix_>::value) {
187 return EigenMatrix_(matrix);
188 } else {
189 return matrix.template realize<EigenMatrix_>();
190 }
191}
192
204template<class Matrix_, class EigenVector_>
205class Centered {
206public:
212 Centered(const Matrix_& matrix, const EigenVector_& center) : my_matrix(matrix), my_center(center) {}
213
217public:
218 Eigen::Index rows() const { return my_matrix.rows(); }
219
220 Eigen::Index cols() const { return my_matrix.cols(); }
221
222public:
223 struct Workspace {
224 Workspace(WrappedWorkspace<Matrix_> i) : inner(std::move(i)) {}
225 WrappedWorkspace<Matrix_> inner;
226 EigenVector_ buffer;
227 };
228
229 Workspace workspace() const {
230 return Workspace(wrapped_workspace(my_matrix));
231 }
232
233 struct AdjointWorkspace {
234 AdjointWorkspace(WrappedAdjointWorkspace<Matrix_> i) : inner(std::move(i)) {}
235 WrappedAdjointWorkspace<Matrix_> inner;
236 EigenVector_ buffer;
237 };
238
239 AdjointWorkspace adjoint_workspace() const {
240 return AdjointWorkspace(wrapped_adjoint_workspace(my_matrix));
241 }
242
243public:
244 template<class Right_>
245 void multiply(const Right_& rhs, Workspace& work, EigenVector_& out) const {
246 const auto& realized_rhs = internal::realize_rhs(rhs, work.buffer);
247 wrapped_multiply(my_matrix, realized_rhs, work.inner, out);
248 auto beta = realized_rhs.dot(my_center);
249 for (auto& o : out) {
250 o -= beta;
251 }
252 return;
253 }
254
255 template<class Right_>
256 void adjoint_multiply(const Right_& rhs, AdjointWorkspace& work, EigenVector_& out) const {
257 const auto& realized_rhs = internal::realize_rhs(rhs, work.buffer);
258 wrapped_adjoint_multiply(my_matrix, realized_rhs, work.inner, out);
259 auto beta = realized_rhs.sum();
260 out -= beta * (my_center);
261 return;
262 }
263
264 template<class EigenMatrix_>
265 Eigen::MatrixXd realize() const {
266 auto output = wrapped_realize<EigenMatrix_>(my_matrix);
267 output.array().rowwise() -= my_center.adjoint().array();
268 return output;
269 }
274private:
275 const Matrix_& my_matrix;
276 const EigenVector_& my_center;
277};
278
293template<bool column_, class Matrix_, class EigenVector_>
294class Scaled {
295public:
302 Scaled(const Matrix_& matrix, const EigenVector_& scale, bool divide) :
303 my_matrix(matrix), my_scale(scale), my_divide(divide) {}
304
308public:
309 Eigen::Index rows() const { return my_matrix.rows(); }
310
311 Eigen::Index cols() const { return my_matrix.cols(); }
312
313public:
314 template<template<class> class Wrapper_>
315 struct BufferedWorkspace {
316 BufferedWorkspace(size_t n, Wrapper_<Matrix_> c) : buffer(n), child(std::move(c)) {}
317 EigenVector_ buffer;
318 Wrapper_<Matrix_> child;
319 };
320
321 typedef typename std::conditional<column_, BufferedWorkspace<WrappedWorkspace>, WrappedWorkspace<Matrix_> >::type Workspace;
322
323 Workspace workspace() const {
324 if constexpr(column_) {
325 return BufferedWorkspace<WrappedWorkspace>(my_matrix.cols(), wrapped_workspace(my_matrix));
326 } else {
327 return wrapped_workspace(my_matrix);
328 }
329 }
330
331 typedef typename std::conditional<column_, WrappedAdjointWorkspace<Matrix_>, BufferedWorkspace<WrappedAdjointWorkspace> >::type AdjointWorkspace;
332
333 AdjointWorkspace adjoint_workspace() const {
334 if constexpr(column_) {
335 return wrapped_adjoint_workspace(my_matrix);
336 } else {
338 }
339 }
340
341public:
342 template<class Right_>
343 void multiply(const Right_& rhs, Workspace& work, EigenVector_& out) const {
344 if constexpr(column_) {
345 if (my_divide) {
346 // We store the result here, because the underlying matrix's multiply()
347 // might need to access rhs/scale multiple times, especially if it's
348 // parallelized. Better to pay the cost of accessing a separate memory
349 // space than computing the quotient repeatedly.
350 work.buffer = rhs.cwiseQuotient(my_scale);
351 } else {
352 work.buffer = rhs.cwiseProduct(my_scale);
353 }
354 wrapped_multiply(my_matrix, work.buffer, work.child, out);
355
356 } else {
357 wrapped_multiply(my_matrix, rhs, work, out);
358 if (my_divide) {
359 out.array() /= my_scale.array();
360 } else {
361 out.array() *= my_scale.array();
362 }
363 }
364 }
365
366 template<class Right_>
367 void adjoint_multiply(const Right_& rhs, AdjointWorkspace& work, EigenVector_& out) const {
368 if constexpr(column_) {
369 wrapped_adjoint_multiply(my_matrix, rhs, work, out);
370 if (my_divide) {
371 out.array() /= my_scale.array();
372 } else {
373 out.array() *= my_scale.array();
374 }
375
376 } else {
377 if (my_divide) {
378 work.buffer = rhs.cwiseQuotient(my_scale);
379 } else {
380 work.buffer = rhs.cwiseProduct(my_scale);
381 }
382 wrapped_adjoint_multiply(my_matrix, work.buffer, work.child, out);
383 }
384 }
385
386 template<class EigenMatrix_>
387 EigenMatrix_ realize() const {
388 auto output = wrapped_realize<EigenMatrix_>(my_matrix);
389
390 if constexpr(column_) {
391 if (my_divide) {
392 output.array().rowwise() /= my_scale.adjoint().array();
393 } else {
394 output.array().rowwise() *= my_scale.adjoint().array();
395 }
396 } else {
397 if (my_divide) {
398 output.array().colwise() /= my_scale.array();
399 } else {
400 output.array().colwise() *= my_scale.array();
401 }
402 }
403
404 return output;
405 }
410private:
411 const Matrix_& my_matrix;
412 const EigenVector_& my_scale;
413 bool my_divide;
414};
415
431template<bool column_, class Matrix_, class EigenVector_>
435
436}
437
438#endif
Wrapper for a centered matrix.
Definition wrappers.hpp:205
Centered(const Matrix_ &matrix, const EigenVector_ &center)
Definition wrappers.hpp:212
Wrapper for a scaled matrix.
Definition wrappers.hpp:294
Scaled(const Matrix_ &matrix, const EigenVector_ &scale, bool divide)
Definition wrappers.hpp:302
Implements IRLBA for approximate SVD.
Definition compute.hpp:18
Scaled< column_, Matrix_, EigenVector_ > make_Scaled(const Matrix_ &matrix, const EigenVector_ &scale, bool divide)
Definition wrappers.hpp:432
void wrapped_adjoint_multiply(const Matrix_ &matrix, const Right_ &rhs, WrappedAdjointWorkspace< Matrix_ > &work, EigenVector_ &out)
Definition wrappers.hpp:170
WrappedWorkspace< Matrix_ > wrapped_workspace(const Matrix_ &matrix)
Definition wrappers.hpp:61
void wrapped_multiply(const Matrix_ &matrix, const Right_ &rhs, WrappedWorkspace< Matrix_ > &work, EigenVector_ &out)
Definition wrappers.hpp:150
WrappedAdjointWorkspace< Matrix_ > wrapped_adjoint_workspace(const Matrix_ &matrix)
Definition wrappers.hpp:110
typename get_workspace< Matrix_ >::type WrappedWorkspace
Definition wrappers.hpp:53
typename get_adjoint_workspace< Matrix_ >::type WrappedAdjointWorkspace
Definition wrappers.hpp:102
EigenMatrix_ wrapped_realize(const Matrix_ &matrix)
Definition wrappers.hpp:185
Get the type of workspace for wrapped_adjoint_multiply().
Definition wrappers.hpp:74
Matrix_::AdjointWorkspace type
Definition wrappers.hpp:78
Get the type of workspace for wrapped_multiply().
Definition wrappers.hpp:25
Matrix_::Workspace type
Definition wrappers.hpp:29