-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmatrix.h
3503 lines (3068 loc) · 110 KB
/
matrix.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#pragma once
#ifndef MATRIX_H
#define MATRIX_H
#include<iostream>
#include<cstdlib>
#include<omp.h>
#include<stdexcept>
#include<fstream>
#include<random>
#include<limits>
#include<cmath>
#include<sstream>
#include<iomanip>
#include<complex>
#include<type_traits>
#include<x86intrin.h>
#include<cassert>
#include<string>
//testing
#include<algorithm>
namespace linear{
// deAlloc macro for deallocating an array pointer
#define deAlloc(x) delete[] x; x = NULL;
// Macros for precision handling
#define MATRIX_PRECISION 4 // precision of matrix values in console display
#define MATRIX_PRECISION_TOL 10
#define PRECISION_TOL(type) std::numeric_limits<type>::epsilon() * std::pow(10, MATRIX_PRECISION_TOL)
// Macros for IO
#define SAVEPATH "saves" //default path
#define F_EXT ".linmat" //default linear::matrix format
// Stringify Macro
#define CHANGE_ID_TO_STRING(x) (#x)
// linear::range is specially made for slicing operation to generate submatrices
struct range {
int start;
int end;
int length;
range(int x) : start(0), end(x), length(end-start) {}
range(int x, int y) : start(x), end(y), length(end-start) {}
const int size() {return end - start;}
};
// Defining type trait to check if a type is std::complex
template<typename DATA>
struct is_complex : std::false_type {};
template<typename DATA>
struct is_complex<std::complex<DATA>> : std::true_type {};
template<typename DATA> //whether it is qualified to be a matrix value
constexpr bool is_numeric_v = std::is_arithmetic_v<DATA> || is_complex<DATA>::value;
// This has been added to be able to print the data type of the matrix
template <typename T>
struct TypeName { //defaulting case if i missed any numerical type
static constexpr const char* value = "unknown";
};
#define DEFINE_TYPENAME_FOR_TYPE(TYPE)\
template <>\
struct TypeName<TYPE> {\
static constexpr const char* value = #TYPE;\
};
DEFINE_TYPENAME_FOR_TYPE(bool)
DEFINE_TYPENAME_FOR_TYPE(char)
DEFINE_TYPENAME_FOR_TYPE(signed char)
DEFINE_TYPENAME_FOR_TYPE(short)
DEFINE_TYPENAME_FOR_TYPE(int)
DEFINE_TYPENAME_FOR_TYPE(long)
DEFINE_TYPENAME_FOR_TYPE(long long)
DEFINE_TYPENAME_FOR_TYPE(float)
DEFINE_TYPENAME_FOR_TYPE(double)
DEFINE_TYPENAME_FOR_TYPE(long double)
DEFINE_TYPENAME_FOR_TYPE(std::complex<double>)
DEFINE_TYPENAME_FOR_TYPE(std::complex<int>)
DEFINE_TYPENAME_FOR_TYPE(std::complex<float>)
DEFINE_TYPENAME_FOR_TYPE(std::complex<long double>)
DEFINE_TYPENAME_FOR_TYPE(wchar_t)
template<typename DATA=double>
class matrix {
/*
DATA MEMBERS:- (- private; + public)
- val = flattened 2d array of type DATA in row major form
- row = number of rows of the matrix
- col = number of columns of the matrix
- *first = points to the first location of the *val
- *last = points to the last value in *val (*last <- *val + (row * col))
*/
static_assert(is_numeric_v<DATA>, "`linear::matrix` class only supports numerical types.");
DATA *val;
int row, col;
DATA *first = NULL;
DATA *last = NULL;
// itrs for chained-initialization (check operator<<)
int cur_row=0, cur_col=0;
//deallocate memory for Val
void delMemoryforVal() {
if (this->rows() > 0 && this->cols() > 0) {
delete[] this->val;
this->val = NULL;
}
}
// memory allocation for internal data structure holding the values
void getMemoryforVal(int r, int c) {
try {
if(r<1 || c<1)
throw std::invalid_argument("linear::getMemoryforVal() - Invalid dimension values.");
val = new DATA[r*c];
} catch (const std::exception& e) {
std::cerr << "linear::getMemoryforVal() - Heap memory allocation failed. " << e.what() << "\n";
exit(0);
}
this->row = r;
this->col = c;
//experimental
first = this->val;
last = this->val + (r*c);
}
// validate the Param values
bool validateParams(int x, int y, int dim) {
// explicitly used for `slice` operation
bool validatex = (x > -1 && x < dim+1);
bool validatey = (y > -1 && y < dim+1);
bool validatexlty = (x < y);
if( validatex && validatey && validatexlty)
return true;
else
return false;
}
// validate the index
bool isValidIndex(int r, int c) const {
int nRows = this->rows();
int nCols = this->cols();
return r >= 0 && r < nRows && c >= 0 && c < nCols;
}
/// Gaussian Elimination private method
void gaussianElimination(matrix<DATA>&, int, int);
/// Full Pivoting private method
void pickPivotFullPivoting(int, int&, int&);
// declaring class of different types as friend
template<typename ATAD>
friend class matrix;
/* enable private member access within the set
of matrix class template*/
public:
//// EXPERIMENTAL FUNCTIONS ////
//print the type of matrix in console
void print_type() {
std::cout<<'\n'<<TypeName<DATA>::value<<'\n';
}
//returns string of the type of the matrix
static constexpr const char* type_s() {
return TypeName<DATA>::value;
}
//First and Last accessors
DATA* begin() const {
return this->first;
}
DATA* end() const {
return this->last;
}
// get total memory of the data
size_t getTotalMemory() const {
return sizeof(DATA) * static_cast<size_t>(last - first);
}
//swap matrices contents
void swapValues(matrix<DATA>& other) {
try {
if(other.rows() != this->rows() || other.cols() != this->cols()) {
throw std::domain_error("linear::swapValues() - matrices have different dimensions.\n");
}
} catch (const std::exception& e) {
std::cerr<<e.what();
exit(0);
}
std::swap_ranges(this->first, this->last, other.first);
}
void fillUpperTriangle(DATA value)
{
int Rows = this->rows();
int Cols = this->cols();
for(DATA *i = this->first; i!= this->last; ++i) {
int rowIdx = (i - first) / Cols;
int colIdx = (i - first) % Cols;
if(colIdx > rowIdx)
*i = value;
}
}
void fillTriu(DATA value) {
this->fillUpperTriangle(value);
}
void fillLowerTriangle(DATA value) {
int Rows = this->rows();
int Cols = this->cols();
for(DATA *i = this->first; i!= this->last; ++i) {
int rowIdx = (i - first) / Cols;
int colIdx = (i - first) % Cols;
if(colIdx < rowIdx)
*i = value;
}
}
void fillTril(DATA value) {
this->fillLowerTriangle(value);
}
//// EXPERIMENTAL ENDS ////
// Getting matrix dimensions
matrix<int> getDims() const {
/*
Returns a 1x2 integer matrix (say Dims) with row in Dims(0,0) and col in Dims(0,1)
*/
matrix<int> Dims(1,2);
Dims(0,0) = this->row;
Dims(0,1) = this->col;
return Dims;
}
int rows() const {return this->row;}
int cols() const {return this->col;}
//change dimensions
void changeDims(int r, int c) {
/*
This function will reset memory.
It will reshape the matrix and
remove old data and reallocate
memory for new data.
This is different than the reshape function.
Reshape function does not delete the values
of original matrix. Infact it creates a new
matrix which it returns.
*/
this->delMemoryforVal();
this->getMemoryforVal(r, c);
}
// initialize empty matrix, trivial
matrix() {
this->row = this->col = 0;
this->first = this->last = NULL;
}
// initialize a square matrix, semi-init (lifetime hasn't started yet)
matrix(int n) {
getMemoryforVal(n,n);
}
// initialize a rectangular matrix, semi-init (lifetime hasn't started yet)
matrix(int row, int col) {
getMemoryforVal(row,col);
}
// initialize a square matrix using a flattened 2d array input
matrix(DATA *data, int n) {
getMemoryforVal(n,n);
std::copy(data, data + n*n, this->first);
}
// initialize a rectangle matrix using a flattened 2d array input
matrix(DATA *data, int row, int col) {
getMemoryforVal(row,col);
std::copy(data, data + row*col, this->first);
}
// initialize a row x col matrix with `value`
matrix(int row, int col, DATA value) {
getMemoryforVal(row,col);
std::fill(this->first, this->last, value);
}
matrix(int row, int col, std::complex<DATA> value) {
getMemoryforVal(row,col);
std::fill(this->first, this->last, value);
}
// initialize using a 2d std::vector
matrix(std::vector<std::vector<DATA>> data) {
getMemoryforVal(data.size(), data[0].size());
int stride = 0;
#pragma omp parallel for if(this->rows() >= 100)
for(int i=0; i<this->rows(); ++i) {
std::copy(data[i].begin(), data[i].end(), val + stride);
stride += this->cols();
}
}
//initialize a row vector 1xn using 1d std::vector
matrix(std::vector<DATA> data) {
getMemoryforVal(1, data.size());
std::copy(data.begin(), data.end(), this->first);
}
// initialize char matrix for cipher stuff
template <typename U = DATA, typename std::enable_if<std::is_same<U, char>::value, int>::type = 0>
matrix(std::string cipher) {
int size = cipher.size();
int dim = std::ceil(std::sqrt(size));
getMemoryforVal(dim,dim);
std::copy(cipher.begin(), cipher.end(), this->first);
}
//copy constructor
matrix(const matrix<DATA> &m) {
this->getMemoryforVal( m.rows(), m.cols());
std::copy(m.begin(), m.end(), this->val);
}
//copy constructor for handling different type matrices
template<typename ATAD>
matrix(const matrix<ATAD>& m) {
this->getMemoryforVal(m.rows(), m.cols());
#pragma omp parallel for collapse(2) if(m.rows() >= 64 || m.cols() >= 64)
for (int i = 0; i < this->row; ++i) {
for (int j = 0; j < this->col; ++j) {
if constexpr(std::is_class_v<ATAD>) {
if constexpr (std::is_same_v<ATAD, std::complex<typename ATAD::value_type>>) {
*(val + this->col * i + j) = static_cast<DATA>(std::real(m(i, j)));
}
} else if constexpr (!std::is_same_v<DATA, ATAD>) {
*(val + this->col * i + j) = static_cast<DATA>(m(i, j));
}
}
}
}
// Move constructor
matrix(matrix&& other) noexcept {
// Transfer ownership of resources from source to destination
this->val = std::move(other.val);
this->row = other.row;
this->col = other.col;
this->first = other.first;
this->last = other.last;
// Reset source to a valid state
other.val = NULL;
other.row = 0;
other.col = 0;
other.first = NULL;
other.last = NULL;
}
// Move constructor for handling different type matrices
template<typename ATAD>
matrix(matrix<ATAD>&& other) noexcept {
this->getMemoryforVal(other.rows(), other.cols());
#pragma omp parallel for collapse(2) if(other.rows() >= 100 || other.cols() >= 100)
for (int i = 0; i < this->row; ++i) {
for (int j = 0; j < this->col; ++j) {
if constexpr(std::is_class_v<ATAD>) {
if constexpr (std::is_same_v<ATAD, std::complex<typename ATAD::value_type>>) {
*(val + this->col * i + j) = static_cast<DATA>(std::real(std::move(other(i, j))));
} else if constexpr(std::is_same_v<ATAD,std::complex<DATA>>) {
*(val + this->col * i + j) = std::real(std::move(other(i, j)));
}
} else if constexpr (!std::is_same_v<DATA, ATAD>) {
*(val + this->col * i + j) = static_cast<DATA>(std::move(other(i, j)));
}
}
}
// Reset source to a valid state
other.delMemoryforVal();
}
//initializer list
matrix(std::initializer_list<std::initializer_list<DATA>> list) {
if(list.size() == 0 || list.begin()->size() == 0)
matrix();
else {
this->getMemoryforVal(list.size(), list.begin()->size());
int i=0, j=0;
for(const auto& ROW : list) {
for(const auto& elem : ROW) {
*(val + (this->col)*i + j) = static_cast<DATA>(elem);
++j;
}
j=0;
++i;
}
}
}
// Operator overloading for operator<< for chaining values into the matrix
matrix<DATA>& operator<<(const DATA& value);
template<typename ATAD, typename std::enable_if_t<is_numeric_v<ATAD>>>
matrix<DATA>& operator<<(const ATAD& value);
// insert/update all the elements in row major form into the internal data structure
void insertAll(int r=-1, int c=-1);
// insert value at rth row and cth column
void insertAt(DATA, int, int);
// update using flattened array
void updateWithArray(DATA*, int, int);
// display contents in a 2d grid form
void display(const std::string msg="Matrix:-") const;
//set `subMatrix` values
void setSubMatrix(int,int,int,int, const matrix&);
void setSubMatrix(range,range, const matrix&);
void iota(int start=int());
~matrix() noexcept {
this->delMemoryforVal();
}
/////// MATRIX OPERATIONS
matrix<DATA> &operator+=(matrix const& );
matrix<DATA> &operator+=(const DATA);
matrix<DATA> &operator-=(matrix const& );
matrix<DATA> &operator-=(const DATA);
template<typename ATAD>
matrix<DATA> &operator*=(const ATAD);
template<typename ATAD>
matrix<DATA> &operator/=(const ATAD);
// Index operator
inline DATA& operator()(const int, const int);
inline const DATA& operator()(const int, const int) const;
matrix<DATA> operator()(const matrix<bool>&);
//Assignment operator
matrix<DATA> &operator=(const matrix<DATA>& m1) {
this->changeDims(m1.rows(), m1.cols());
this->updateWithArray(m1.val, m1.rows(), m1.cols());
return *this;
}
template<typename ATAD>
matrix<DATA> &operator=(const matrix<ATAD>& m1) {
this->changeDims(m1.rows(), m1.cols());
if constexpr ( std::is_same_v<ATAD,std::complex<DATA>>) {
#pragma omp parallel for collapse(2) if(m1.rows() >= 100 || m1.cols() >= 100)
for(int i=0; i<m1.rows(); ++i)
for(int j=0; j<m1.cols(); ++j)
*(val + i*(this->cols()) + j) = std::real(m1(i,j));
} else if constexpr(!std::is_same_v<DATA, ATAD>) {
#pragma omp parallel for collapse(2) if(m1.rows() >= 100 || m1.cols() >= 100)
for(int i=0; i<m1.rows(); ++i)
for(int j=0; j<m1.cols(); ++j)
*(val + i*(this->cols()) + j) = static_cast<DATA>(m1(i,j));
}
return *this;
}
// Reshape
matrix<DATA> reshape(int newRow, int newCol);
// Transpose operation
matrix<DATA> operator~() const;
matrix<DATA> transpose();
matrix<DATA> T(){ return this->transpose();}
// Adjoint operation
matrix<DATA> adjoint();
/// Slice operation
matrix<DATA> slice(int, int, int, int);
matrix<DATA> operator()(range, range);
// Element-wise exponent operation
matrix<DATA> operator^(int);
matrix<DATA> operator^(double);
/// Swap Operations
void swapRows(int,int);
void swapCols(int, int);
/// Generate Augmented matrix (vertical stack or horizontal stack)
matrix<DATA> hStack(matrix const& ); // horizontal stack - hStack
matrix<DATA> vStack(matrix const& ); // vertical stack - vStack
matrix<DATA> stack(matrix const& obj, bool vert=false); //generalized stack - stack
/// aggregate functions (more to come)
matrix<DATA> max(int dim=-1);
matrix<DATA> argmax(int dim=-1);
matrix<DATA> min(int dim=-1);
matrix<DATA> argmin(int dim=-1);
/// matrix inverse operation
matrix<DATA> inv(); //experimental
// solve Ax = b
matrix<DATA> solve(const matrix<DATA>&); //experimental
// get determinant //gaussian elimination
double det(bool fullPivot=false);
double determinant(bool fullPivot=false) {
return this->det(fullPivot);
}
/// QUERY methods
bool isSquare() const { if(this->col == this->row) return true; else return false;}
bool isSymmetric() const noexcept;
DATA item() const;
bool isComparable(const matrix<DATA>&) const noexcept;
bool isMatMulDefined(const matrix<DATA>&) const noexcept;
bool all(bool value) const;
bool isany(bool value) const;
std::vector<std::vector<DATA>> toVector();
/// FILE OPERATIONS I/O
bool saveMatrix(const std::string&, const std::string& folderpath=SAVEPATH);
bool loadMatrix(const std::string&, const std::string& folderpath=SAVEPATH);
};
//// NON-MEMBER OPERATIONS DECLARATIONS ///
matrix<bool> operator!(const matrix<bool>&);
template<typename DATA>
matrix<DATA> operator+(const matrix<DATA>&, const matrix<DATA>&);
template<typename DATA>
matrix<DATA> operator+(const matrix<DATA>&, const double);
template<typename DATA>
matrix<DATA> operator+(const double, const matrix<DATA>&);
template<typename DATA>
matrix<DATA> operator-(const matrix<DATA>&, const matrix<DATA>&);
template<typename DATA>
matrix<DATA> operator-(const matrix<DATA>&, const double);
template<typename DATA>
matrix<DATA> operator-(const double, const matrix<DATA>&);
template<typename DATA, typename ATAD>
matrix<DATA> operator*(const matrix<DATA>&, const ATAD);
template<typename DATA, typename ATAD>
matrix<DATA> operator*(const ATAD, const matrix<DATA>&);
template<typename DATA>
matrix<DATA> operator*(const matrix<DATA>&, const matrix<DATA>&);
template<typename DATA, typename ATAD>
matrix<DATA> operator%(const matrix<DATA>&, const ATAD);
template<typename DATA>
matrix<DATA> operator%(const matrix<DATA>&, const matrix<DATA>&);
template<typename DATA>
matrix<DATA> operator&(const matrix<DATA>&, const matrix<DATA>&);
template<typename DATA>
matrix<bool> operator==(const matrix<DATA>&, const matrix<DATA>&);
template<typename DATA, typename ATAD,\
typename std::enable_if_t<std::is_arithmetic_v<ATAD>>>
matrix<bool> operator==(const matrix<DATA>&, const ATAD);
template<typename DATA, typename ATAD,\
typename std::enable_if_t<std::is_arithmetic_v<ATAD>>>
matrix<bool> operator==(const ATAD,const matrix<DATA>&);
template<typename DATA>
matrix<bool> operator<(const matrix<DATA>&, const matrix<DATA>&);
template<typename DATA, typename ATAD,\
typename std::enable_if_t<std::is_arithmetic_v<ATAD>>>
matrix<bool> operator<(const matrix<DATA>&, const ATAD);
template<typename DATA, typename ATAD,\
typename std::enable_if_t<std::is_arithmetic_v<ATAD>>>
matrix<bool> operator<(const ATAD,const matrix<DATA>&);
template<typename DATA>
matrix<bool> operator>(const matrix<DATA>&, const matrix<DATA>&);
template<typename DATA, typename ATAD,\
typename std::enable_if_t<std::is_arithmetic_v<ATAD>>>
matrix<bool> operator>(const matrix<DATA>&, const ATAD);
template<typename DATA, typename ATAD,\
typename std::enable_if_t<std::is_arithmetic_v<ATAD>>>
matrix<bool> operator>(const ATAD,const matrix<DATA>&);
template<typename DATA>
matrix<bool> operator<=(const matrix<DATA>&, const matrix<DATA>&);
template<typename DATA, typename ATAD,\
typename std::enable_if_t<std::is_arithmetic_v<ATAD>>>
matrix<bool> operator<=(const matrix<DATA>&, const ATAD);
template<typename DATA, typename ATAD,\
typename std::enable_if_t<std::is_arithmetic_v<ATAD>>>
matrix<bool> operator<=(const ATAD,const matrix<DATA>&);
template<typename DATA>
matrix<bool> operator>=(const matrix<DATA>&, const matrix<DATA>&);
template<typename DATA, typename ATAD,\
typename std::enable_if_t<std::is_arithmetic_v<ATAD>>>
matrix<bool> operator>=(const matrix<DATA>&, const ATAD);
template<typename DATA, typename ATAD,\
typename std::enable_if_t<std::is_arithmetic_v<ATAD>>>
matrix<bool> operator>=(const ATAD,const matrix<DATA>&);
template<typename DATA, typename ATAD>
matrix<DATA> operator/(const matrix<DATA>&, const ATAD);
template<typename DATA>
matrix<DATA> eye(int);
template<typename DATA>
matrix<DATA> diagonal(int, DATA);
template<typename DATA>
matrix<DATA> diag(const matrix<DATA>&, int shift=0);
matrix<double> upper_triangle_matrix(int, double mean=0., double std=1.);
matrix<double> lower_triangle_matrix(int, double mean=0., double std=1.);
matrix<double> utm(int, double mean=0., double std=1.);
matrix<double> ltm(int, double mean=0., double std=1.);
matrix<double> triu(int, double mean=0., double std=1.);
matrix<double> tril(int, double mean=0., double std=1.);
template<typename DATA>
bool is_triangular(matrix<DATA>&);
matrix<double> zeros(int);
matrix<double> zeros(int,int);
template<typename DATA>
matrix<DATA> zeros_like(const matrix<DATA>&);
template<typename DATA>
matrix<DATA> matrix_like(const matrix<DATA>&);
template<typename DATA>
matrix<DATA> ones(int);
template<typename DATA>
matrix<DATA> ones(int,int);
matrix<double> ones(int);
matrix<double> ones(int,int);
matrix<double> randomUniform(int, double minVal=0., double maxVal=1.);
matrix<double> randomUniform(int, int, double minVal=0., double maxVal=1.);
matrix<int> randomUniformInt(int, int, int);
matrix<int> randomUniformInt(int, int, int, int);
template<typename DATA, typename std::enable_if_t<std::is_floating_point_v<DATA>, int> = 0>
matrix<DATA> randomNormal(int, DATA mean=0., DATA std=1.);
template<typename DATA, typename std::enable_if_t<std::is_floating_point_v<DATA>, int> = 0>
matrix<DATA> randomNormal(int, int, DATA mean=0., DATA std=1.);
matrix<double> randomNormal(int, int, double mean=0., double std=1.);
matrix<double> randomNormal(int, double mean=0., double std=1.);
/// Non-member operations declarations end ///
/// Get 2D Vector
template<typename DATA>
std::vector<std::vector<DATA>> matrix<DATA>::toVector() {
std::vector<std::vector<DATA>> result(this->rows(), std::vector<DATA>(this->cols()));
int Cols = this->cols();
#pragma omp parallel for if(this->rows() >= 100 || this->cols() >= 100)
for(DATA *itr = this->begin(); itr != this->end(); ++itr)
{
int rowIdx = (itr - this->begin())/Cols;
int colIdx = (itr - this->begin())%Cols;
result[rowIdx][colIdx] = *itr;
}
return result;
}
/// IOTA
template<typename DATA>
void matrix<DATA>::iota(int start) {
try {
if(this->rows() < 1 || this->cols() < 1)
throw std::out_of_range("linear::matrix::iota - Empty matrix. Inflate it with memory first.\n");
} catch(const std::exception& e) {
std::cerr<<e.what();
exit(0);
}
#pragma omp parallel for if(this->rows() >= 100 || this->cols() >= 100)
for(DATA* itr = this->first; itr != this->last; ++itr)
*itr = start++;
}
/// RESHAPE METHOD DEFINITION
template<typename DATA>
matrix<DATA> matrix<DATA>::reshape(int newRow, int newCol) {
try {
if(newRow * newCol != (this->cols())*(this->rows())){
throw std::invalid_argument("linear::matrix::reshape - The product of dimensions do not match the product of dimensions of the given matrix.\n");
}
} catch(const std::exception& e) {
std::cerr<< e.what();
exit(0);
}
matrix<DATA> reshapedMatrix(newRow, newCol);
#pragma omp parallel for if(this->cols() >= 100 || this->rows() >= 100)
for(int i=0; i< ((this->cols()) * (this->rows())); ++i ) {
reshapedMatrix(i/newCol, i%newCol) = val[i];
}
return reshapedMatrix;
}
/// Picking pivot using FULL PIVOTING
template<typename DATA>
void matrix<DATA>::pickPivotFullPivoting(int startRow, int& pivotRow, int& pivotCol) {
pivotRow = startRow;
pivotCol = startRow;
for(int i=startRow; i<this->rows(); ++i) {
for(int j=startRow; j<this->cols(); ++j) {
if( abs(val[i*(this->cols()) + j]) > abs(val[pivotRow * (this->cols()) + pivotCol]) ) {
pivotRow = i;
pivotCol = j;
}
}
}
}
/// SWAP OPERATIONS
template<typename DATA>
void matrix<DATA>::swapRows(int row1, int row2) {
try {
if( !isValidIndex(row1, 0) || !isValidIndex(row2, 0)) {
throw std::out_of_range("linear::matrix::swapRows - row indices are out of range.\n");
}
} catch(const std::exception& e) {
std::cerr<<e.what();
exit(0);
}
#pragma omp parallel for if(this->cols() >= 100)
for(int j=0; j<col; ++j) {
DATA temp = *(val + row1*col + j);
*(val + row1*col + j) = *(val + row2*col + j);
*(val + row2*col + j) = temp;
}
}
template<typename DATA>
void matrix<DATA>::swapCols(int col1, int col2) {
try {
if( !isValidIndex(0, col1) || !isValidIndex(0, col2)) {
throw std::out_of_range("linear::matrix::swapCols - column indices are out of range.\n");
}
} catch(const std::exception& e) {
std::cerr<<e.what();
exit(0);
}
#pragma omp parallel for if(this->rows() >= 100)
for (int i = 0; i<row; ++i) {
DATA temp = *(val + i*col + col1);
*(val + i*col + col1) = *(val + i*col + col2);
*(val + i*col + col2) = temp;
}
}
/// GAUSSIAN ELIMINATION DEFINITION
template<typename DATA>
void matrix<DATA>::gaussianElimination(matrix<DATA>& augMat, int n, int m) {
//test
int minDim = ((n < m) ? n : m);
for(int i=0; i<minDim; ++i) { //test : minDim replaced n here
//finding the pivot
int pivotRow = i;
for(int k=i+1; k<n; ++k) {
if(abs(augMat(k,i)) > abs(augMat(pivotRow, i))) {
pivotRow = k;
}
}
try {
if(augMat(pivotRow,i) == 0) {
throw std::domain_error("linear::gaussianElimination - Matrix is singular. Cannot find inverse.");
}
} catch(std::exception &e) {
std::cerr<<e.what()<<'\n';
exit(0);
}
//swapping rows in augMat
augMat.swapRows(i, pivotRow);
//Applying gaussian elimination
DATA pivot = augMat(i, i);
// row scaling
for(int j=0; j< m; ++j) {
augMat(i, j) /= pivot;
}
for(int k=0; k<n; ++k) {
if( k != i) {
DATA factor = augMat(k, i);
// row combine for augMat
for(int j=0; j<m; ++j) {
augMat(k, j) -= factor * augMat(i, j);
}
} // if k!= i condn end
}
}
}
//// SOLVE AX = B /////
template<typename DATA>
matrix<DATA> matrix<DATA>::solve(const matrix<DATA>& b) {
int n = this->row;
int m = this->col; // n x m dims
try {
if( n != m) {
throw std::length_error("linear::matrix::solve is only applicable to square matrices.\n");
}
// validate the shape of b
if( b.rows() != n || b.cols() != 1) {
throw std::invalid_argument("linear::matrix::solve - b has invalid dimensions.\n");
}
} catch(const std::exception& e) {
std::cerr<<e.what();
exit(0);
}
matrix<DATA> augMat = this->hStack(b); // [A | b]
gaussianElimination(augMat, augMat.rows(), augMat.cols());
// get the solution from the rightmost colimns of augMat
matrix<DATA> sol = augMat.slice(0, n, n, n+1);
return sol;
}
/////// AGGREGATE FUNCTIONS ///////
///// Min
template<typename DATA>
matrix<DATA> matrix<DATA>::min(int dim) {
assert(dim>=-1 && dim<=1);
DATA minElem;
if(dim == -1) {
/* Returns a 1x1 matrix of max value */
matrix<DATA> m0(1,1);
// might subroutine the below repetitive code
minElem = *val;
for(int i=1; i<this->row*this->col; ++i)
if( minElem > *(val + i)) {
minElem = *(val + i);
}
m0.insertAt(minElem, 0, 0);
return m0;
} //dim == -1
else {
if(dim == 0) {
/* Returns a 1xcol matrix of max value in each jth column
this operation is performed along the 0th axis (row axis)
*/
matrix<DATA> m1(1, this->col);
for(int j=0; j<this->col; ++j) {
minElem = *(val + j);
for(int i=1; i<this->row; ++i)
if( minElem > *(val + i*(this->col) + j) )
minElem = *(val + i*(this->col) + j);
m1.insertAt(minElem, 0,j);
}
return m1;
} // dim == 0
else {
if(dim == 1) {
/*
Returns a rowx1 matrix of max value in each ith row
this operation is performed along the 1th axis (col axis)
*/
matrix<DATA> m2(this->row, 1);
for(int i=0; i<this->row; ++i) {
minElem = *(val + i*(this->col));
for(int j=1; j<this->col; ++j) {
if( minElem > *(val + i*(this->col) + j) )
minElem = *(val + i*(this->col) +j);
}
m2.insertAt(minElem, i, 0);
}
return m2;
}
else {
return matrix();
}
}
}
}
//// Indices of Min element
template<typename DATA>
matrix<DATA> matrix<DATA>::argmin(int dim) {
assert(dim>=-1 && dim<=1);
int minIdx_i, minIdx_j;
if(dim == -1) {
/*
Calculate the index of max element in entire matrix
Returns a 1x2 matrix with ith index at (0,0) and jth index at (0,1)
*/
matrix<DATA> m0(1,2);
// might subroutine the below repetitive code
minIdx_i = 0;
minIdx_j = 0;
DATA minElem = *(val + minIdx_i*(this->col) + minIdx_j);
for(int i=0; i<this->row; ++i)
for(int j=0; j<this->col; ++j)
{
if(minElem > *(val + i*(this->col) + j))
{
minElem = *(val + i*(this->col) + j);
minIdx_i = i;
minIdx_j = j;
}
}
// insert the indices at (0,0) and (0,1)
m0.insertAt(minIdx_i, 0, 0);
m0.insertAt(minIdx_j, 0, 1);
return m0;
} else {
if(dim == 0) {
/* Returns a 1xcol matrix of index of max value in each jth column
this operation is performed along the 0th axis (row axis)
*/
matrix<DATA> m1(1, this->col);
for(int j=0; j<this->col; ++j) {
int minIdx_i = 0;
DATA minElem = *(val + minIdx_i*(this->col) + j);
for(int i=1; i<this->row; ++i)
if( minElem > *(val + i*(this->col) + j) ) {
minElem = *(val + i*(this->col) + j);
minIdx_i = i;
}
m1.insertAt(minIdx_i, 0,j);
}
return m1;
} // dim == 0 condn end
else {
if(dim == 1) {
/*
Returns a rowx1 matrix with index of max value in each ith row
this operation is performed along the 1th axis (col axis)
*/
matrix<DATA> m2(this->row, 1);
for(int i=0; i<this->row; ++i) {
int minIdx_j = 0;
DATA minElem = *(val + i*(this->col) + minIdx_j);
for(int j=1; j<this->col; ++j) {
if( minElem > *(val + i*(this->col) + j) ) {
minElem = *(val + i*(this->col) + j);
minIdx_j = j;
}
}
m2.insertAt(minIdx_j, i, 0);
}
return m2;
} //dim == 1 condn end
else {
throw std::invalid_argument("The axis value is not correct.");
}
}
}
}
//// Max
template<typename DATA>
matrix<DATA> matrix<DATA>::max(int dim) {
assert(dim>=-1 && dim<=1);
DATA maxElem;
if(dim == -1) {
/* Returns a 1x1 matrix of max value */
matrix<DATA> m0(1,1);
// might subroutine the below repetitive code
maxElem = *val;
for(int i=1; i<this->row*this->col; ++i)
if( maxElem < *(val + i)) {
maxElem = *(val + i);
}