Feature Selection Toolbox FST4
UTIA · Czech Academy of Sciences
Loading...
Searching...
No Matches
indexed_matrix.hpp
Go to the documentation of this file.
1#ifndef FSTINDEXEDMATRIX_H
2#define FSTINDEXEDMATRIX_H
3 /*
27=========================================================================
28Copyright and license:
29 * FST4 software (with the exception of any bundled or externally linked
30 third-party components) is Copyright (C) 2000-present Institute of
31 Information Theory and Automation of the Czech Academy of Sciences (UTIA).
32 * FST4 is free software: you can redistribute it and/or modify it under
33 the terms of the GNU Affero General Public License as published by the
34 Free Software Foundation, version 3. The full text of the license is in
35 the file LICENSE in the distribution root and at
36 https://www.gnu.org/licenses/agpl-3.0.html.
37 * FST4 is distributed in the hope that it will be useful, but WITHOUT ANY
38 WARRANTY; without even the implied warranty of MERCHANTABILITY or
39 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
40 License for more details.
41 * Closed-source use without the AGPL's copyleft obligations requires a
42 commercial license from UTIA; in case of interest write to
43 utia@utia.cas.cz. LICENSING.md in the distribution root explains both
44 options, the contributor license agreement that every contribution
45 requires, and the contacts (technical questions and pull requests:
46 somol@utia.cas.cz, with "FST" in the subject line).
47========================================================================== */
48
49#include <memory>
50#include <cassert>
51#include <iostream>
52#include <limits>
53#include <cmath>
54#include <cstring> // memcpy
55#include <cstddef> // std::size_t (linearized-index arithmetic)
56#ifdef FST_USE_EIGEN
57#include <Eigen/Dense> // optional Eigen-backed LU/inverse/determinant (CMake option FST4_USE_EIGEN, implies EIGEN_MPL2_ONLY)
58#endif
59#include "error.hpp"
60#include "global.hpp"
61#include "indexed_vector.hpp"
62
63/*============== Template parameter type naming conventions ==============
64--------- Numeric types: -------------------------------------------------
65DATATYPE - data sample values - usually real numbers (but may be integers
66 in text processing etc.)
67REALTYPE - must be real numbers - for representing intermediate results of
68 calculations like mean, covariance etc.
69IDXTYPE - index values for enumeration of data samples - (nonnegative) integers,
70 extent depends on numbers of samples in data
71DIMTYPE - index values for enumeration of features (dimensions), or classes (not
72 class sizes) - (nonnegative) integers, usually lower extent than IDXTYPE,
73 but be aware of expressions like _classes*_features*_features !
74 in linearized representations of feature matrices for all classes
75BINTYPE - feature selection marker type - represents ca. <10 different feature
76 states (selected, deselected, sel./desel. temporarily 1st nested loop, 2nd...)
77RETURNTYPE - criterion value: real value, but may be extended in future to support
78 multiple values
79--------- Class types: ---------------------------------------------------
80SUBSET - class of class type Subset
81CLASSIFIER - class implementing interface defined in abstract class Classifier
82EVALUATOR - class implementing interface defined in abstract class Sequential_Step
83DISTANCE - class implementing interface defined in abstract class Distance
84DATAACCESSOR - class implementing interface defined in abstract class Data_Accessor
85INTERVALCONTAINER - class of class type Data_Intervaller
86CONTAINER - STL container of class type Data_Interval
87========================================================================== */
88
89namespace FST {
90
91template<typename DATATYPE, typename DIMTYPE, class SUBSET>
92class Indexed_Vector;
93
94template<typename DATATYPE, typename DIMTYPE, class SUBSET>
95class Indexed_Matrix;
96
97template<typename DATATYPE, typename DIMTYPE, class SUBSET>
98std::ostream& operator<<(std::ostream& os, const Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>& mr);
99
101template<typename DATATYPE, typename DIMTYPE, class SUBSET>
102class Indexed_Matrix {
103public:
104 typedef std::shared_ptr<SUBSET> PSubset;
105 Indexed_Matrix() : _n_max(0), _n(0), _sign(0), _d(0) {notify("Indexed_Matrix (empty) constructor");}
106 Indexed_Matrix(const DIMTYPE n, bool LUstorage=false) : _n_max(0), _n(0), _sign(0), _d(0) {notify("Indexed_Matrix constructor"); reset(n,LUstorage);}
107 Indexed_Matrix(const Indexed_Matrix& im);
109 ~Indexed_Matrix() {notify("Indexed_Matrix destructor");}
110 void reset(const DIMTYPE n_max, bool LUstorage=false);
111 void redim(const DIMTYPE n=0);
112
113 DATATYPE& at_raw(const DIMTYPE row, const DIMTYPE col) {assert(row<_n); assert(col<_n); assert(_data); return _data[(std::size_t)row*_n+col];}
114 DATATYPE& at(const DIMTYPE row, const DIMTYPE col) {assert(row<_d); assert(col<_d); assert(_index); assert(_data); return _data[(std::size_t)_index[row]*_n+_index[col]];}
115 DATATYPE* get_row_raw(const DIMTYPE row) {assert(row<_n); assert(_data); return &(_data[(std::size_t)row*_n]);}
116 DATATYPE* get_row(const DIMTYPE row) {assert(row<_d); assert(_index); assert(_data); return &(_data[(std::size_t)_index[row]*_n]);}
117
118 void narrow_to(const PSubset sub);
119 void denarrow();
120 DIMTYPE get_n_max() const {return _n_max;}
121 DIMTYPE get_n() const {return _n;}
122 DIMTYPE get_d() const {return _d;}
123
124 // "raw" operations, ignoring index[]
125 void set_all_raw_to(const DATATYPE val);
126 void add_raw(const Indexed_Matrix& mr);
127 void add_constmul_raw(const Indexed_Matrix& mr, const DATATYPE mul);
128 void copy_constmul_raw(const Indexed_Matrix& mr, const DATATYPE mul);
129 void copy_raw(const Indexed_Matrix& mr);
130
131 // "soft" methods - the source (this) is read through the narrow_to() index map,
132 // the result objects are written raw (their own _index is ignored). They share
133 // one contract:
134 // * no method allocates for the caller -- every result object must be pre-allocated
135 // (Indexed_Matrix results by reset()/redim() to exactly this->get_d(),
136 // Indexed_Vector results to at least this->get_d());
137 // * the LU buffer must have been created with LUstorage==true (the second
138 // constructor argument, or reset()'s) -- the permutation buffers live there;
139 // * LUdecompose() must be called first; solve_equations(), invert(), determinant()
140 // and log_determinant() then only read that buffer. Call them on the SAME object,
141 // with the SAME narrow_to() subset in force, that produced the decomposition:
142 // the LU buffer does not carry the identity of its source matrix, and all four
143 // take their dimension get_d() from *this*, not from the buffer.
144
152 void LUdecompose(Indexed_Matrix &result);
163 void invert(Indexed_Matrix &result, const Indexed_Matrix &LU_matrix);
168 DATATYPE determinant(const Indexed_Matrix &LU_matrix);
173 DATATYPE log_determinant(const Indexed_Matrix &LU_matrix);
174
175 friend std::ostream& operator<< <>(std::ostream& os, const Indexed_Matrix& mr);
176protected:
177 // NOTE: matrix size is defined by:
178 // _n_max - hard constant representing maximum allocated space of size _n_max*_n_max
179 // _n - "full feature set" dimensionality, _n<=_n_max. If Indexed_Matrix servers as output buffer
180 // to store matrices of various (reduced) dimensionalites, this is needed for correct functionality
181 // of at(), at_raw() etc. (both "raw" and "non-raw" matrix item access methods)
182 // _d - "virtual subset" dimensionality, _d<=_n. Assuming the matrix holds _n*_n values (_n consecutively stored
183 // rows of _n values each), a virtual sub-matrix can be defined and accessed using at() ("non-raw" methods)
184 DIMTYPE _n_max;
185 DIMTYPE _n;
186 std::unique_ptr<DATATYPE[]> _data;
187 // if used as LU decomposition target
188 std::unique_ptr<DIMTYPE[]> _permut;
189 int _sign;
190
191 // sub-matrix access tools
192 DIMTYPE _d;
193 std::unique_ptr<DIMTYPE[]> _index;
194
195 // temporary needed inside LUdecompose()
196 std::unique_ptr<DATATYPE[]> _tmp_rowscale;
197 std::unique_ptr<DIMTYPE[]> _tmp_whereis;
198 // temporaries needed inside invert()
199 std::unique_ptr<Indexed_Vector<DATATYPE,DIMTYPE,SUBSET> > _tmp_eq_coefs;
200 std::unique_ptr<Indexed_Vector<DATATYPE,DIMTYPE,SUBSET> > _tmp_eq_roots;
201};
202
203template<typename DATATYPE, typename DIMTYPE, class SUBSET>
204Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>::Indexed_Matrix(const Indexed_Matrix& im) : // copy-constructor
205 _n_max(im._n_max),
206 _n(im._n),
207 _sign(im._sign),
208 _d(im._d)
209{
210 notify("Indexed_Matrix copy-constructor.");
211 if(_n_max>0)
212 {
213 _data.reset(new DATATYPE[(std::size_t)_n_max*_n_max]); memcpy((void *)_data.get(),(void *)(im._data).get(),sizeof(DATATYPE)*_n_max*_n_max);
214 _index.reset(new DIMTYPE[_n_max]); memcpy((void *)_index.get(),(void *)(im._index).get(),sizeof(DIMTYPE)*_n_max); // selected features (subspace) - index into _data
215 if(im._permut) {_permut.reset(new DIMTYPE[_n_max]); memcpy((void *)_permut.get(),(void *)(im._permut).get(),sizeof(DIMTYPE)*_n_max);}
216 if(im._tmp_whereis) {_tmp_whereis.reset(new DIMTYPE[_n_max]); memcpy((void *)_tmp_whereis.get(),(void *)(im._tmp_whereis).get(),sizeof(DIMTYPE)*_n_max);}
217 if(im._tmp_rowscale) {_tmp_rowscale.reset(new DATATYPE[_n_max]); memcpy((void *)_tmp_rowscale.get(),(void *)(im._tmp_rowscale).get(),sizeof(DATATYPE)*_n_max);}
218 }
219 if(im._tmp_eq_roots) _tmp_eq_roots.reset(new Indexed_Vector<DATATYPE,DIMTYPE,SUBSET>(*(im._tmp_eq_roots))); // solve_equations result
220 if(im._tmp_eq_coefs) _tmp_eq_coefs.reset(new Indexed_Vector<DATATYPE,DIMTYPE,SUBSET>(*(im._tmp_eq_coefs))); // equations' right hand side coefficients
221}
222
223template<typename DATATYPE, typename DIMTYPE, class SUBSET>
224Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>& Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>::operator=(Indexed_Matrix const &im)
225{
226 notify("Indexed_Matrix operator=.");
227 assert( (im._n_max>0 && im._data && im._index) || (im._n_max==0 && !im._data && !im._index) );
228 assert( !im._permut || (im._permut && im._n_max > 0) );
229 assert( !im._tmp_whereis || (im._tmp_whereis && im._n_max > 0) );
230 assert( !im._tmp_rowscale || (im._tmp_rowscale && im._n_max > 0) );
231 if (this != &im)
232 {
233 _n=im._n;
234 _d=im._d;
235 _sign=im._sign;
236
237 if(im._n_max>0)
238 {
239 if(_n_max!=im._n_max)
240 {
241 _data.reset(new DATATYPE[(std::size_t)im._n_max*im._n_max]);
242 _index.reset(new DIMTYPE[im._n_max]);
243 }
244 memcpy((void *)_data.get(),(void *)(im._data).get(),sizeof(DATATYPE)*im._n_max*im._n_max);
245 memcpy((void *)_index.get(),(void *)(im._index).get(),sizeof(DIMTYPE)*im._n_max); // selected features (subspace) - index into _data
246
247 // allocate also when the capacities match but this side is not yet an LUstorage
248 // matrix - a capacity-only test would leave the buffer null and memcpy into it
249 if(im._permut) {if(!_permut || _n_max!=im._n_max) _permut.reset(new DIMTYPE[im._n_max]); memcpy((void *)_permut.get(),(void *)(im._permut).get(),sizeof(DIMTYPE)*im._n_max);} else _permut.reset();
250 if(im._tmp_whereis) {if(!_tmp_whereis || _n_max!=im._n_max) _tmp_whereis.reset(new DIMTYPE[im._n_max]); memcpy((void *)_tmp_whereis.get(),(void *)(im._tmp_whereis).get(),sizeof(DIMTYPE)*im._n_max);} else _tmp_whereis.reset();
251 if(im._tmp_rowscale) {if(!_tmp_rowscale || _n_max!=im._n_max) _tmp_rowscale.reset(new DATATYPE[im._n_max]); memcpy((void *)_tmp_rowscale.get(),(void *)(im._tmp_rowscale).get(),sizeof(DATATYPE)*im._n_max);} else _tmp_rowscale.reset();
252
253 _n_max=im._n_max;
254 } else {
255 _n_max=0;
256 _data.reset();
257 _index.reset();
258 _permut.reset();
259 _tmp_whereis.reset();
260 _tmp_rowscale.reset();
261 }
262 // use assignment operator to accelerate...
263 if(im._tmp_eq_roots) _tmp_eq_roots.reset(new Indexed_Vector<DATATYPE,DIMTYPE,SUBSET>(*(im._tmp_eq_roots))); // solve_equations result
264 else _tmp_eq_roots.reset();
265 if(im._tmp_eq_coefs) _tmp_eq_coefs.reset(new Indexed_Vector<DATATYPE,DIMTYPE,SUBSET>(*(im._tmp_eq_coefs))); // equations' right hand side coefficients
266 else _tmp_eq_coefs.reset();
267 }
268 return *this;
269}
270
271template<typename DATATYPE, typename DIMTYPE, class SUBSET>
272void Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>::reset(const DIMTYPE n_max, bool LUstorage)
273{
274 notify("Indexed_Matrix reset.");
275 assert(n_max>0);
276 if(n_max!=_n_max) {
277 _n=_d=_n_max=n_max;
278 _data.reset(new DATATYPE[(std::size_t)_n_max*_n_max]);
279 _index.reset(new DIMTYPE[_n_max]); // selected features (subspace) - index into _data
280 if(LUstorage) {
281 _permut.reset(new DIMTYPE[_n_max]);
282 _tmp_whereis.reset(new DIMTYPE[_n_max]);
283 } else {
284 _permut.reset();
285 _tmp_whereis.reset();
286 }
287 _tmp_rowscale.reset();
288 _tmp_eq_coefs.reset();
289 _tmp_eq_roots.reset();
290 }
291 else if(LUstorage && !_permut) { // same capacity, but LU storage newly requested: a
292 _permut.reset(new DIMTYPE[_n_max]); // capacity-only test would leave these null
293 _tmp_whereis.reset(new DIMTYPE[_n_max]); // and the init loop below dereference them
294 }
295 _d=_n=_n_max;
296 for(DIMTYPE i=0;i<_n_max;i++) _index[i]=i;
297 if(LUstorage) {
298 for(DIMTYPE i=0;i<_n_max;i++) _permut[i]=i;
299 for(DIMTYPE i=0;i<_n_max;i++) _tmp_whereis[i]=i;
300 }
301 _sign=0;
302}
303
304template<typename DATATYPE, typename DIMTYPE, class SUBSET>
306{
307 notify("Indexed_Matrix redim.");
308 assert(n<=_n_max);
309 assert(_data);
310 assert(_index);
311 if(n==0) _d=_n=_n_max; else _d=_n=n;
312 for(DIMTYPE i=0;i<_n;i++) _index[i]=i;
313 if(_permut) for(DIMTYPE i=0;i<_n;i++) _permut[i]=i;
314 if(_tmp_whereis) for(DIMTYPE i=0;i<_n;i++) _tmp_whereis[i]=i;
315 _sign=0;
316}
317
318template<typename DATATYPE, typename DIMTYPE, class SUBSET>
319void Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>::narrow_to(const PSubset sub) // fill _index[] -> access to data is then mapped to sub-matrix
320{
321 assert(_n>=sub->get_d_raw()); // precondition: enough space to store the subset
322 assert(_index);
323 _d=0;
324 for(DIMTYPE f : sub->selected_features()) _index[_d++]=f;
325 assert(_d>0);
326}
327
328template<typename DATATYPE, typename DIMTYPE, class SUBSET>
329void Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>::denarrow() // reset _index[] -> access is reset to the full matrix
330{
331 assert(_index);
332 for(DIMTYPE i=0;i<_n;i++) _index[i]=i;
333 _d=_n;
334}
335
336template<typename DATATYPE, typename DIMTYPE, class SUBSET>
338{
339 assert(_data);
340 for(std::size_t i=0;i<(std::size_t)_n*_n;i++) _data[i]=val;
341}
342
343template<typename DATATYPE, typename DIMTYPE, class SUBSET>
345{
346 assert(_data);
347 assert(mr._data);
348 assert(mr.get_n()==_n);
349 for(std::size_t i=0;i<(std::size_t)_n*_n;i++) _data[i]+=mr._data[i];
350}
351
352template<typename DATATYPE, typename DIMTYPE, class SUBSET>
353void Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>::add_constmul_raw(const Indexed_Matrix& mr, const DATATYPE mul)
354{
355 assert(_data);
356 assert(mr._data);
357 assert(mr.get_n()==_n);
358 for(std::size_t i=0;i<(std::size_t)_n*_n;i++) _data[i]+=mul * mr._data[i];
359}
360
361template<typename DATATYPE, typename DIMTYPE, class SUBSET>
362void Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>::copy_constmul_raw(const Indexed_Matrix& mr, const DATATYPE mul)
363{
364 assert(_data);
365 assert(mr._data);
366 assert(mr.get_n()==_n);
367 for(std::size_t i=0;i<(std::size_t)_n*_n;i++) _data[i]=mul * mr._data[i];
368}
369
370template<typename DATATYPE, typename DIMTYPE, class SUBSET>
372{
373 assert(_data);
374 assert(mr._data);
375 assert(mr.get_n()==_n);
376 for(std::size_t i=0;i<(std::size_t)_n*_n;i++) _data[i]=mr._data[i];
377}
378
379template<typename DATATYPE, typename DIMTYPE, class SUBSET>
380void Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>::LUdecompose(Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET> &result)
381{
382 // NOTE: 'result' raw data access assumed (result._index ignored)
383 // while this->_index is used when accessing this->_data
384 //
385 // Crout's method with implicit row scaling and partial pivoting, with one twist:
386 // rows are NEVER physically moved inside result._data. Instead two index arrays
387 // carried by 'result' record where each row went --
388 // result._tmp_whereis[l] = physical row of _data holding logical row l
389 // result._permut[p] = logical row stored in physical row p (its inverse)
390 // -- so a "row interchange" is a swap of two entries in each array (plus the swap of
391 // the two implicit scale factors and a flip of result._sign). Every access to the
392 // working matrix therefore goes through result._tmp_whereis[.]*_d rather than the
393 // plain row offset, and so must every consumer: solve_equations(), determinant() and
394 // log_determinant() all read the diagonal and the triangles through _tmp_whereis.
395 // On return the L (unit-diagonal, below) and U (on and above the diagonal) factors of
396 // the PERMUTED matrix are packed in result._data in that logical ordering.
397 notify("Indexed_Matrix::LUdecompose");
398 assert(result._n==_d); // enough pre-allocated space required
399 assert(result._data);
400 assert(result._permut);
401 assert(result._tmp_whereis);
402 assert(_index);
403 result.denarrow(); // no indexes needed in result, will be written in raw ordering
404
405#ifdef FST_USE_EIGEN
406 // Eigen-backed variant: 'result' holds a PLAIN copy of the narrowed source
407 // (identity permutation, sign 1); the consumers - solve_equations()/invert()/
408 // determinant()/log_determinant() - factor it with Eigen::PartialPivLU on demand.
409 // All five methods swap together under FST_USE_EIGEN, so the internal LU
410 // representation never mixes with the hand-rolled Crout layout below.
411 result._d=_d;
412 {
413 std::size_t pROW=0;
414 for(DIMTYPE i1=0;i1<_d;i1++) {
415 for(DIMTYPE i2=0;i2<_d;i2++) {
416 assert(pROW+i2<(std::size_t)_d*_d);
417 assert((std::size_t)_index[i1]*_n+_index[i2]<(std::size_t)_n*_n);
418 result._data[pROW+i2]=_data[(std::size_t)_index[i1]*_n+_index[i2]];
419 }
420 pROW+=_d;
421 }
422 }
423 for(DIMTYPE j=0;j<_d;j++) {result._permut[j]=j; result._tmp_whereis[j]=j;}
424 result._sign=1;
425#else
426 if(!_tmp_rowscale) _tmp_rowscale.reset(new DATATYPE[_n_max]); // create 'implicit scaling of rows' buffer once for the rest of Indexed_Matrix life
427
428 DATATYPE max;
429 DIMTYPE idxmax=0; // the meaningful init is per-column below; DIMTYPE is unsigned, so a -1 "none yet" sentinel is not available here
430 DATATYPE pom,sum;
431 DIMTYPE j,i1,i2,tmpi;
432 std::size_t pROW,pROWK,pCOL;
433
434 result._d=_d;
435 pROW=0;
436 for(i1=0;i1<_d;i1++) { // copy source sub-matrix to result buffer for further manipulations
437 for(i2=0;i2<_d;i2++) {
438 assert(pROW+i2<(std::size_t)_d*_d);
439 assert((std::size_t)_index[i1]*_n+_index[i2]<(std::size_t)_n*_n);
440 result._data[pROW+i2]=_data[(std::size_t)_index[i1]*_n+_index[i2]];
441 }
442 pROW+=_d;
443 }
444
445 result._sign=1;
446 /* initialize permutation vector */
447 for(j=0;j<_d;j++) {result._permut[j]=j; result._tmp_whereis[j]=j;}
448
449 /* find scaling of rows */
450 // implicit row scaling: each row's pivot candidate is later weighted by 1/(its largest
451 // magnitude), so pivoting compares rows on equal terms regardless of their units
452 pROW=0;
453 for(i1=0;i1<_d;i1++)
454 {
455 max=0.0;
456 for(j=0;j<_d;j++) {assert(pROW+j<(std::size_t)_d*_d); if((pom=fabs(result._data[pROW+j]))>max) max=pom;}
457
458 // an all-(near-)zero row makes the matrix singular and the scale factor infinite:
459 // the only condition this method reports by throwing (fst_error code 23), as
460 // opposed to the pivot clamping further below
461 if(max<std::numeric_limits<DATATYPE>::min()) throw fst_error("Indexed_Matrix::LUdecompose() value too small/matrix singular or near-singular",23);
462
463 _tmp_rowscale[i1]=1.0/max;
464 pROW+=_d;
465 }
466
467 /* Crout's method */
468 for(j=0;j<_d;j++)
469 {
470 /* column j above the diagonal: the U entries */
471 for(i1=0;i1<j;i1++)
472 {
473 pROW=(std::size_t)result._tmp_whereis[i1]*_d;
474 assert(pROW+j<(std::size_t)_d*_d);
475 sum=result._data[pROW+j];
476
477 for(i2=0;i2<i1;i2++)
478 {
479 pROWK=(std::size_t)result._tmp_whereis[i2]*_d;
480 assert(pROW+i2<(std::size_t)_d*_d);
481 assert(pROWK+j<(std::size_t)_d*_d);
482 sum-=result._data[pROW+i2]*result._data[pROWK+j];
483 }
484 result._data[pROW+j]=sum;
485 }
486 /* search for largest pivot */
487 max=0.0;
488 idxmax=j; // "no interchange" -- also the outcome for a column that is zero after
489 // scaling, where no candidate ever beats max; leaving idxmax at its value
490 // from the previous column would swap rows on no evidence, and the
491 // too-small-pivot clamp below is what handles the degenerate value
492 for(i1=j;i1<_d;i1++)
493 {
494 pROW=(std::size_t)result._tmp_whereis[i1]*_d;
495 assert(pROW+j<(std::size_t)_d*_d);
496 sum=result._data[pROW+j];
497
498 for(i2=0;i2<j;i2++)
499 {
500 pROWK=(std::size_t)result._tmp_whereis[i2]*_d;
501 assert(pROW+i2<(std::size_t)_d*_d);
502 assert(pROWK+j<(std::size_t)_d*_d);
503 sum-=result._data[pROW+i2]*result._data[pROWK+j];
504 }
505 result._data[pROW+j]=sum;
506 pom=_tmp_rowscale[i1]*fabs(sum); // implicit scaling applies to the comparison only
507 if(pom>max) {max=pom; idxmax=i1;}
508 }
509
510 /* interchange rows ? */
511 if(j!=idxmax)
512 {
513 // NOTE: no data is moved -- the interchange is recorded in _tmp_whereis
514 // and its inverse _permut (see the block comment at the top)
515
516 assert(idxmax<_d);
517 assert(j<_d);
518
519 pom=_tmp_rowscale[idxmax];
520 _tmp_rowscale[idxmax]=_tmp_rowscale[j];
521 _tmp_rowscale[j]=pom;
522
523 i1=result._tmp_whereis[j];
524 i2=result._tmp_whereis[idxmax];
525
526 assert(i1<_d);
527 assert(i2<_d);
528
529 tmpi=result._permut[i1];
530 result._permut[i1]=result._permut[i2];
531 result._permut[i2]=tmpi;
532
533 tmpi=result._tmp_whereis[result._permut[i1]];
534 result._tmp_whereis[result._permut[i1]]=result._tmp_whereis[result._permut[i2]];
535 result._tmp_whereis[result._permut[i2]]=tmpi;
536
537 result._sign=-result._sign;
538 }
539
540 pCOL=(std::size_t)result._tmp_whereis[j]*_d;
541
542 /* divide the column below the diagonal by the pivot: the L entries */
543 if(j<(_d-1))
544 {
545 assert(pCOL+j<(std::size_t)_d*_d);
546 // A pivot at or below the smallest representable magnitude is NOT an error here:
547 // its reciprocal is replaced by the largest representable number of the pivot's
548 // sign and only a notify() message is issued. The decomposition then completes,
549 // but it describes a (numerically) singular matrix -- expect huge or infinite
550 // entries downstream, a determinant() that over/underflows, and an invert()
551 // result that is meaningless. Callers that cannot tolerate that (the normal-model
552 // criteria) treat such a subset as unusable.
553 if(fabs(result._data[pCOL+j])<=std::numeric_limits<DATATYPE>::min()) {
554 if(result._data[pCOL+j]<0.0) pom=-std::numeric_limits<DATATYPE>::max();
555 else pom=std::numeric_limits<DATATYPE>::max();
556 notify("NOTE: division by too small a number - result set to maximum representable number.");
557 } else
558 pom=1.0/result._data[pCOL+j];
559
560 for(i1=j+1;i1<_d;i1++)
561 {
562 pROW=(std::size_t)result._tmp_whereis[i1]*_d;
563 assert(pROW+j<(std::size_t)_d*_d);
564 result._data[pROW+j] *= pom;
565 }
566 }
567 }
568#endif // FST_USE_EIGEN
569}
570
571template<typename DATATYPE, typename DIMTYPE, class SUBSET>
573{
574 // NOTE: result_vec, coef_vec, and LU_matrix raw data access assumed (*->_index ignored)
575 // while this->_index is used when accessing this->_data
576 notify("Indexed_Matrix::solve_equations");
577 assert(result_vec._data);
578 assert(result_vec._n>=_d); // enough pre-allocated space required
579 assert(coef_vec._data);
580 assert(coef_vec._n>=_d); // enough pre-allocated space required
581 assert(LU_matrix._n==_d);
582 assert(LU_matrix._permut);
583 assert(LU_matrix._tmp_whereis);
584
585#ifdef FST_USE_EIGEN
586 // Eigen-backed variant: LU_matrix holds the plain narrowed copy (see LUdecompose)
587 typedef Eigen::Matrix<DATATYPE,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> FST_EMat;
588 typedef Eigen::Matrix<DATATYPE,Eigen::Dynamic,1> FST_EVec;
589 Eigen::Map<const FST_EMat> A_e(LU_matrix._data.get(),(Eigen::Index)_d,(Eigen::Index)_d);
590 Eigen::Map<const FST_EVec> b_e(coef_vec._data.get(),(Eigen::Index)_d);
591 Eigen::Map<FST_EVec> x_e(result_vec._data.get(),(Eigen::Index)_d);
592 x_e=Eigen::PartialPivLU<FST_EMat>(A_e).solve(b_e);
593#else
594 DIMTYPE i,j;
595 DATATYPE sum;
596 std::size_t pROW;
597
598 /* apply the decomposition's row permutation to the right-hand side b */
599 for(i=0;i<_d;i++) {assert(LU_matrix._tmp_whereis[i]<_d); result_vec[i]=coef_vec[LU_matrix._tmp_whereis[i]];}
600
601 /* forward substitution through L (unit diagonal, so result_vec[0] stays as it is) */
602 for(i=1;i<_d;i++)
603 {
604 pROW=(std::size_t)LU_matrix._tmp_whereis[i]*_d;
605 sum=0.0;
606 for(j=0;j<i;j++) {
607 assert(pROW+j<(std::size_t)_d*_d);
608 sum+=LU_matrix._data[pROW+j]*result_vec[j];
609 }
610 result_vec[i]-=sum;
611 }
612
613 /* back substitution through U (counting down with a 1-based i, since DIMTYPE is unsigned) */
614 for(i=_d;i>0;i--)
615 {
616 pROW=(std::size_t)LU_matrix._tmp_whereis[i-1]*_d;
617 assert(pROW+i-1<(std::size_t)_d*_d);
618 sum=0.0;
619 for(j=i;j<_d;j++) sum+=LU_matrix._data[pROW+j]*result_vec[j];
620 if(fabs(LU_matrix._data[pROW+i-1])<=std::numeric_limits<DATATYPE>::min()) {
621 if(LU_matrix._data[pROW+i-1]*(result_vec[i-1]-sum)<0.0) result_vec[i-1]=-std::numeric_limits<DATATYPE>::max();
622 else result_vec[i-1]=std::numeric_limits<DATATYPE>::max();
623 notify("NOTE: division by too small number - result set to maximum representable number.");
624 } else {
625 result_vec[i-1]=(result_vec[i-1]-sum)/LU_matrix._data[pROW+i-1];
626 }
627 }
628#endif // FST_USE_EIGEN
629}
630
631template<typename DATATYPE, typename DIMTYPE, class SUBSET>
632void Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>::invert(Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET> &result, const Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET> &LU_Matrix)
633{
634 // NOTE: 'result' and 'LU_matrix' raw data access assumed (*->_index ignored)
635 // while this->_index is used when accessing this->_data
636 notify("Indexed_Matrix::invert");
637 assert(result._data);
638 assert(result._n==_d); // enough pre-allocated space required
639 assert(LU_Matrix._data);
640 assert(LU_Matrix._permut);
641 assert(LU_Matrix._index);
642 assert(LU_Matrix._d==_d);
643
644#ifdef FST_USE_EIGEN
645 // Eigen-backed variant: LU_Matrix holds the plain narrowed copy (see LUdecompose)
646 typedef Eigen::Matrix<DATATYPE,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> FST_EMat;
647 Eigen::Map<const FST_EMat> A_e(LU_Matrix._data.get(),(Eigen::Index)_d,(Eigen::Index)_d);
648 Eigen::Map<FST_EMat> R_e(result._data.get(),(Eigen::Index)_d,(Eigen::Index)_d);
649 R_e=Eigen::PartialPivLU<FST_EMat>(A_e).inverse();
650#else
651 if(_tmp_eq_roots) assert(_tmp_eq_roots->_n>=_d);
652 else _tmp_eq_roots.reset(new Indexed_Vector<DATATYPE,DIMTYPE,SUBSET>(_n_max)); // solve_equations result
653 if(_tmp_eq_coefs) assert(_tmp_eq_coefs->_n>=_d);
654 else _tmp_eq_coefs.reset(new Indexed_Vector<DATATYPE,DIMTYPE,SUBSET>(_n_max)); // equations' right hand side coefficients
655
656 std::size_t pROW;
657 DIMTYPE i,j;
658
659 // column j of the inverse is the solution of A*x = e_j
660 for(j=0;j<_d;j++)
661 {
662 for(i=0;i<_d;i++) (*_tmp_eq_coefs)[i]=0.0;
663 (*_tmp_eq_coefs)[j]=1.0;
664
666
667 pROW=0;
668 for(i=0;i<_d;i++)
669 {
670 result._data[pROW+j]=(*_tmp_eq_roots)[i];
671 pROW+=_d;
672 }
673 }
674#endif // FST_USE_EIGEN
675}
676
677template<typename DATATYPE, typename DIMTYPE, class SUBSET>
678DATATYPE Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>::determinant(const Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET> &LU_matrix)
679{
680 // NOTE: 'LU_matrix' raw data access assumed (*->_index ignored)
681 assert(LU_matrix._data);
682 assert(LU_matrix._tmp_whereis);
683 assert(LU_matrix._d==_d);
684
685 notify("Indexed_Matrix::determinant");
686#ifdef FST_USE_EIGEN
687 // Eigen-backed variant: LU_matrix holds the plain narrowed copy (see LUdecompose)
688 typedef Eigen::Matrix<DATATYPE,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> FST_EMat;
689 Eigen::Map<const FST_EMat> A_e(LU_matrix._data.get(),(Eigen::Index)_d,(Eigen::Index)_d);
690 return (DATATYPE)Eigen::PartialPivLU<FST_EMat>(A_e).determinant();
691#else
692 DATATYPE det;
693 std::size_t pROW;
694 DIMTYPE j;
695
696 // permutation sign times the product of the U diagonal, each element reached through
697 // the logical-to-physical row map (rows were never physically swapped)
698 det=LU_matrix._sign;
699 for(j=0;j<_d;j++)
700 {
701 pROW=(std::size_t)LU_matrix._tmp_whereis[j]*_d+j;
702 assert(pROW<(std::size_t)_d*_d);
703 det*=LU_matrix._data[pROW];
704 }
705 return det;
706#endif // FST_USE_EIGEN
707}
708
709template<typename DATATYPE, typename DIMTYPE, class SUBSET>
710DATATYPE Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>::log_determinant(const Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET> &LU_matrix)
711{
712 // ln|det| as the sum of ln|LU diagonal| - does not over/underflow where the plain
713 // diagonal product in determinant() would (high dimension); sign is discarded, which
714 // is safe for the covariance-matrix use (positive determinants) it exists for
715 // NOTE: 'LU_matrix' raw data access assumed (*->_index ignored)
716 assert(LU_matrix._data);
717 assert(LU_matrix._tmp_whereis);
718 assert(LU_matrix._d==_d);
719
720 notify("Indexed_Matrix::log_determinant");
721#ifdef FST_USE_EIGEN
722 // Eigen-backed variant: factor the plain narrowed copy, sum ln|U diagonal|
723 typedef Eigen::Matrix<DATATYPE,Eigen::Dynamic,Eigen::Dynamic,Eigen::RowMajor> FST_EMat;
724 Eigen::Map<const FST_EMat> A_e(LU_matrix._data.get(),(Eigen::Index)_d,(Eigen::Index)_d);
725 Eigen::PartialPivLU<FST_EMat> lu_e(A_e);
726 DATATYPE logdet=0.0;
727 for(DIMTYPE j=0;j<_d;j++) logdet+=std::log(std::fabs(lu_e.matrixLU()((Eigen::Index)j,(Eigen::Index)j)));
728 return logdet;
729#else
730 DATATYPE logdet=0.0;
731 std::size_t pROW;
732 for(DIMTYPE j=0;j<_d;j++)
733 {
734 pROW=(std::size_t)LU_matrix._tmp_whereis[j]*_d+j;
735 assert(pROW<(std::size_t)_d*_d);
736 logdet+=std::log(std::fabs(LU_matrix._data[pROW]));
737 }
738 return logdet;
739#endif // FST_USE_EIGEN
740}
741
745template<typename DATATYPE, typename DIMTYPE, class SUBSET>
746std::ostream& operator<<(std::ostream& os, const Indexed_Matrix<DATATYPE,DIMTYPE,SUBSET>& mr)
747{
748 assert(mr._n>0);
749 assert(mr._d>0);
750 assert(mr._data);
751 assert(mr._index);
752 os << "----------------"<<std::endl;
753 os << "Indexed_Matrix::";
754 os << "_index: "; for(DIMTYPE j=0;j<mr._d;j++) {os << mr._index[j] << " ";} os<<std::endl;
755 os << "_data (indexed):"<<std::endl;
756 for(DIMTYPE i=0;i<mr._d;i++) {for(DIMTYPE j=0;j<mr._d;j++) {os << mr._data[(std::size_t)mr._index[i]*mr._n+mr._index[j]] << " ";} os << std::endl;}
757 os << "----------------"<<std::endl;
758 return os;
759}
760
761} // namespace
762#endif // FSTINDEXEDMATRIX_H ///:~
Matrix representation and operations, allows operation in selected subspace.
Definition indexed_matrix.hpp:102
void add_constmul_raw(const Indexed_Matrix &mr, const DATATYPE mul)
this += mul * mr, elementwise over the raw get_n()*get_n() entries (requires mr.get_n()==get_n())
Definition indexed_matrix.hpp:353
std::shared_ptr< SUBSET > PSubset
shared pointer to the feature subset consumed by narrow_to()
Definition indexed_matrix.hpp:104
void invert(Indexed_Matrix &result, const Indexed_Matrix &LU_matrix)
Definition indexed_matrix.hpp:632
DIMTYPE _n_max
allocated space can hold _n_max*_n_max matrix
Definition indexed_matrix.hpp:184
void redim(const DIMTYPE n=0)
keeps main allocations, just resets index arrays and adjusts _n; n==0 here equals _n_max
Definition indexed_matrix.hpp:305
void denarrow()
reset _index[] -> access is reset to the full matrix
Definition indexed_matrix.hpp:329
std::unique_ptr< DIMTYPE[]> _index
maps virtual sub-matrix _d dimensions to original _n dimensions
Definition indexed_matrix.hpp:193
std::unique_ptr< Indexed_Vector< DATATYPE, DIMTYPE, SUBSET > > _tmp_eq_coefs
right-hand side handed to solve_equations() by invert() (allocated on first use)
Definition indexed_matrix.hpp:199
void reset(const DIMTYPE n_max, bool LUstorage=false)
reallocates
Definition indexed_matrix.hpp:272
DATATYPE * get_row(const DIMTYPE row)
pointer to the start of matrix row _index[row] (only the row is subset-mapped – the returned row stil...
Definition indexed_matrix.hpp:116
std::unique_ptr< DIMTYPE[]> _permut
LU row permutation, physical row -> logical row (the inverse of _tmp_whereis); allocated only for an ...
Definition indexed_matrix.hpp:188
std::unique_ptr< DIMTYPE[]> _tmp_whereis
LU row permutation, logical row -> physical row in _data (the map that replaces physical row swapping...
Definition indexed_matrix.hpp:197
DATATYPE & at_raw(const DIMTYPE row, const DIMTYPE col)
raw (unindexed) element access, row and col in [0,get_n())
Definition indexed_matrix.hpp:113
int _sign
sign of the LU row permutation (+1/-1; 0 before any decomposition), the factor determinant() needs
Definition indexed_matrix.hpp:189
std::unique_ptr< DATATYPE[]> _data
raw matrix data, _n_max*_n_max entries, row by row
Definition indexed_matrix.hpp:186
void narrow_to(const PSubset sub)
fill _index[] -> access to data is then mapped to sub-matrix
Definition indexed_matrix.hpp:319
void LUdecompose(Indexed_Matrix &result)
Definition indexed_matrix.hpp:380
std::unique_ptr< Indexed_Vector< DATATYPE, DIMTYPE, SUBSET > > _tmp_eq_roots
solution vector returned by solve_equations() to invert() (allocated on first use)
Definition indexed_matrix.hpp:200
DIMTYPE get_n_max() const
the allocated capacity (max n the matrix can reset()/redim() to without reallocating)
Definition indexed_matrix.hpp:120
void copy_constmul_raw(const Indexed_Matrix &mr, const DATATYPE mul)
this = mul * mr, elementwise over the raw get_n()*get_n() entries (an assignment, not an accumulation...
Definition indexed_matrix.hpp:362
Indexed_Matrix & operator=(Indexed_Matrix const &im)
deep-copy assignment (reallocates only where capacity or LU-storage presence differs from im's)
Definition indexed_matrix.hpp:224
DIMTYPE _d
virtual sub-matrix (as represented by _index) dimensionality
Definition indexed_matrix.hpp:192
DIMTYPE get_d() const
the current virtual (narrow_to()'d) dimensionality, get_d()<=get_n()
Definition indexed_matrix.hpp:122
DATATYPE * get_row_raw(const DIMTYPE row)
pointer to the start of raw row row (unindexed, get_n() entries), row in [0,get_n())
Definition indexed_matrix.hpp:115
std::unique_ptr< DATATYPE[]> _tmp_rowscale
reciprocals of the row maxima (implicit row scaling used for pivot choice); allocated on the first LU...
Definition indexed_matrix.hpp:196
DIMTYPE get_n() const
the full ("raw") dimensionality currently in use, get_n()<=get_n_max()
Definition indexed_matrix.hpp:121
DIMTYPE _n
full feature set dimensionality
Definition indexed_matrix.hpp:185
void add_raw(const Indexed_Matrix &mr)
this += mr, elementwise over the raw get_n()*get_n() entries (requires mr.get_n()==get_n())
Definition indexed_matrix.hpp:344
void solve_equations(Indexed_Vector< DATATYPE, DIMTYPE, SUBSET > &result_vec, const Indexed_Matrix &LU_matrix, Indexed_Vector< DATATYPE, DIMTYPE, SUBSET > &coef_vec)
Definition indexed_matrix.hpp:572
DATATYPE & at(const DIMTYPE row, const DIMTYPE col)
indexed access through the narrow_to() subset map – both row and col are mapped, row/col in [0,...
Definition indexed_matrix.hpp:114
void set_all_raw_to(const DATATYPE val)
sets all get_n()*get_n() raw entries to val (ignores the index map)
Definition indexed_matrix.hpp:337
void copy_raw(const Indexed_Matrix &mr)
this = mr, elementwise over the raw get_n()*get_n() entries (requires mr.get_n()==get_n())
Definition indexed_matrix.hpp:371
DATATYPE determinant(const Indexed_Matrix &LU_matrix)
Definition indexed_matrix.hpp:678
DATATYPE log_determinant(const Indexed_Matrix &LU_matrix)
Definition indexed_matrix.hpp:710
Indexed_Matrix(const DIMTYPE n, bool LUstorage=false)
allocates for an n x n matrix; LUstorage true also allocates the permutation/whereis buffers LUdecomp...
Definition indexed_matrix.hpp:106
Vector representation and operations, allows operation in selected subspace. Also serves as the helpe...
Definition indexed_vector.hpp:100
DIMTYPE _n
full ("raw") dimensionality currently in use, _n<=_n_max; see get_n()
Definition indexed_vector.hpp:143
std::unique_ptr< DATATYPE[]> _data
raw vector data buffer (_n_max entries allocated)
Definition indexed_vector.hpp:144
FST4 specific error passing class.
Definition error.hpp:61
FST4 specific error passing class.
Global definitions.
Vector representation and operations, allows operation in selected subspace.
The Feature Selection Toolbox 4 library: every library component – data access, criteria and classifi...
Definition data_accessor.hpp:80
void notify(const std::string msg)
Prints msg followed by a newline via syncout::print(); a no-op unless the preprocessor macro DEBUG is...
Definition global.hpp:112
std::ostream & operator<<(std::ostream &os, const Data_Intervaller< CONTAINER, IDXTYPE > &ia)
Streams every contained interval as "si:<startidx>, c:<count>" lines to os.
Definition data_intervaller.hpp:152
Copyright © 2000–present ÚTIA, Institute of Information Theory and Automation of the Czech Academy of Sciences · Contacts · This site sets no cookies and uses no analytics.