node-red-contrib-prib-functions 0.17.0 → 0.19.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -18
- package/dataAnalysis/pca.js +399 -0
- package/documentation/loadInjector.png +0 -0
- package/matrix/icons/icons8-matrix-desktop-80.png +0 -0
- package/matrix/matrix.js +660 -0
- package/matrix/matrixNode.html +214 -0
- package/matrix/matrixNode.js +162 -0
- package/package.json +33 -6
- package/test/01-base.js +88 -0
- package/test/data/.flow.json.backup +3192 -0
- package/test/data/flow.json +3212 -0
- package/test/data/settings.js +544 -0
- package/test/dataAnalysis.js +1 -1
- package/test/matrix/01base.js +338 -0
- package/test/matrix/02base.js +36 -0
- package/test/matrix/10flowMatrices.js +68 -0
- package/testing/data/countries.csv +250 -0
- package/testing/hostAvailable.html +0 -2
- package/testing/load-injector.html +76 -21
- package/testing/load-injector.js +35 -54
- package/testing/test.js +1 -0
- package/transform/transform.html +61 -19
- package/transform/transform.js +110 -6
- package/documentation/LoadInjector.JPG +0 -0
package/matrix/matrix.js
ADDED
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
const logger = new (require("node-red-contrib-logger"))("Matrix");
|
|
2
|
+
logger.sendInfo("Copyright 2022 Jaroslav Peter Prib");
|
|
3
|
+
|
|
4
|
+
const zeroFloat32Value=1e-6;
|
|
5
|
+
function Matrix(rows,columns,fill) {
|
|
6
|
+
if(rows instanceof Array) {
|
|
7
|
+
this.rows=rows.length;
|
|
8
|
+
if(this.rows==0) throw Error("expected rows")
|
|
9
|
+
const row=rows[0];
|
|
10
|
+
if(!row instanceof Array) throw Error("expected row to be array of columns")
|
|
11
|
+
this.columns=row.length;
|
|
12
|
+
if(this.columns==0) throw Error("expected Columns")
|
|
13
|
+
this.createVector().fillArray(rows);
|
|
14
|
+
return this;
|
|
15
|
+
}
|
|
16
|
+
if(rows instanceof Object) {
|
|
17
|
+
Object.assign(this,rows);
|
|
18
|
+
} else {
|
|
19
|
+
this.rows=rows;
|
|
20
|
+
this.columns=columns;
|
|
21
|
+
}
|
|
22
|
+
this.createVector();
|
|
23
|
+
return this;
|
|
24
|
+
}
|
|
25
|
+
Matrix.prototype.add=function(matrix){
|
|
26
|
+
this.forEachCellPairSet(matrix,(cellA,cellB)=>cellA+cellB)
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
Matrix.prototype.addCell=function(row,column,value){
|
|
30
|
+
this.vector[this.getIndex(row,column)]+=value;
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
Matrix.prototype.addRow=function(row,vectorIn){
|
|
34
|
+
if(vectorIn & row>=0){
|
|
35
|
+
this.forRowCells(row,(value,column,offset,vector)=>vector[offset]+=vectorIn[column]);
|
|
36
|
+
return this;
|
|
37
|
+
}
|
|
38
|
+
const vector=vectorIn?vectorIn:row;
|
|
39
|
+
if(this.size==this.sizeMax){
|
|
40
|
+
this.vector.copyWithin(0,this.columns,this.sizeMax);
|
|
41
|
+
this.rows--;
|
|
42
|
+
this.size-=this.columns;
|
|
43
|
+
}
|
|
44
|
+
this.vector.set(vector, this.rows*this.columns);
|
|
45
|
+
this.rows++;
|
|
46
|
+
this.size+=this.columns;
|
|
47
|
+
return this;
|
|
48
|
+
}
|
|
49
|
+
Matrix.prototype.addRow2Row=function(rowA,rowB,factor=1,startColumn=0,endColumn=this.columns-1){
|
|
50
|
+
const diff=(rowA-rowB)*this.columns;
|
|
51
|
+
this.forRowCells(rowB,(value,column,offset,vector)=>vector[offset]+=vector[offset+diff]*factor,startColumn,endColumn);
|
|
52
|
+
return this;
|
|
53
|
+
}
|
|
54
|
+
Matrix.prototype.backwardSubstitution=function(){
|
|
55
|
+
const vector=new Float32Array(this.rows);
|
|
56
|
+
for(let row=this.rows-1; row>=0; row--) {
|
|
57
|
+
vector[row] = this.get(row,this.rows);
|
|
58
|
+
for(let column=row+1; column<this.rows; column++) {
|
|
59
|
+
vector[row] -= this.get(row,column)*vector[column];
|
|
60
|
+
}
|
|
61
|
+
vector[row] /= this.get(row,row);
|
|
62
|
+
}
|
|
63
|
+
return vector;
|
|
64
|
+
}
|
|
65
|
+
Matrix.prototype.clone=function(){
|
|
66
|
+
const matrix= new Matrix({rows:this.rows,columns:this.columns,size:this.size,sizeMax:this.sizeMax})
|
|
67
|
+
matrix.vector.set(this.vector,0);
|
|
68
|
+
return matrix
|
|
69
|
+
}
|
|
70
|
+
Matrix.prototype.consoleLog=function(label){
|
|
71
|
+
console.log({label:label,rows:this.rows,columns:this.columns,size:this.size,sizeMax:this.sizeMax})
|
|
72
|
+
return this;
|
|
73
|
+
}
|
|
74
|
+
Matrix.prototype.createLike=function(matrix){
|
|
75
|
+
return new Matrix({rows:this.rows,columns:this.columns,size:this.size,sizeMax:this.sizeMax})
|
|
76
|
+
}
|
|
77
|
+
Matrix.prototype.createForEachCellPairSet=function(matrix,call){
|
|
78
|
+
const result=this.createLike();
|
|
79
|
+
if(matrix instanceof Matrix){
|
|
80
|
+
if(this.rows!=matrix.rows) throw Error("number of rows different");
|
|
81
|
+
if(this.columns!=matrix.columns) throw Error("number of columns different");
|
|
82
|
+
for(let offset=0;offset<this.size;offset++)
|
|
83
|
+
result.vector[offset]=call.apply(this,[this.vector[offset],matrix.vector[offset]]);
|
|
84
|
+
return result;
|
|
85
|
+
}
|
|
86
|
+
if(this.rows!=matrix.length) throw Error("number of rows different");
|
|
87
|
+
if(this.columns!=matrix[0].length) throw Error("number of columns different");
|
|
88
|
+
for(let offset=0,row=0;row<this.rows;row++) {
|
|
89
|
+
for(let column=0;column<this.columns;column++) {
|
|
90
|
+
result.vector[offset]=call.apply(this,[this.vector[offset],matrix[row][column]]);
|
|
91
|
+
offset++
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
Matrix.prototype.createVector=function(){
|
|
97
|
+
if(this.size==null) {
|
|
98
|
+
if(this.rows==null){
|
|
99
|
+
if(this.rowsMax==null) throw Error("rows or rowsMax not specified")
|
|
100
|
+
this.rows=0;
|
|
101
|
+
this.sizeMax=this.rowsMax*this.columns;
|
|
102
|
+
}
|
|
103
|
+
if(this.columns==null) throw Error("columns not specified")
|
|
104
|
+
this.size=this.rows*this.columns
|
|
105
|
+
if(this.sizeMax==null) this.sizeMax=this.size
|
|
106
|
+
} else {
|
|
107
|
+
if(this.columns==null) throw Error("columns not specified")
|
|
108
|
+
if(this.rows==null){
|
|
109
|
+
this.rows=0;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
this.vector=new Float32Array(this.sizeMax);
|
|
113
|
+
return this;
|
|
114
|
+
}
|
|
115
|
+
Matrix.prototype.divideCell=function(row,column,value){
|
|
116
|
+
this.vector[this.getIndex(row,column)]/=value;
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
Matrix.prototype.divideRow=function(row,factor,startColumn=0,endColumn=this.columns-1){
|
|
120
|
+
this.determinant/=factor;
|
|
121
|
+
this.forRowCells(row,(value,column,offset,vector)=>vector[offset]/=factor,startColumn,endColumn);
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
Matrix.prototype.equalsNearly=function(matrix,precision=6){
|
|
125
|
+
const thisObject=this;
|
|
126
|
+
if(matrix instanceof Matrix){
|
|
127
|
+
if(this.rows!=matrix.rows) throw Error("rows counts not equal actual: "+this.rows+" expected: "+matrix.rows)
|
|
128
|
+
if(this.columns!=matrix.columns) throw Error("columns counts not equal actual: "+this.columns+" expected: "+matrix.columns)
|
|
129
|
+
this.forEachCell((value,row,column,vector,offset)=>{
|
|
130
|
+
try{
|
|
131
|
+
thisObject.equalsNearlyValues(value,matrix.vector[offset],precision)
|
|
132
|
+
} catch(ex) {
|
|
133
|
+
throw Error("row: "+row+" column: "+column+", cell values "+ex.message)
|
|
134
|
+
}
|
|
135
|
+
});
|
|
136
|
+
} else {
|
|
137
|
+
if(this.rows!=matrix.length) throw Error("rows counts not equal actual: "+this.rows+" expected: "+matrix.length)
|
|
138
|
+
if(this.columns!=matrix[0].length) throw Error("columns counts not equal actual: "+this.columns+" expected: "+matrix[0].length)
|
|
139
|
+
this.forEachCell((value,row,column)=>{
|
|
140
|
+
try{
|
|
141
|
+
thisObject.equalsNearlyValues(value,matrix[row][column],precision)
|
|
142
|
+
} catch(ex) {
|
|
143
|
+
throw Error("row: "+row+" column: "+column+", cell values "+ex.message)
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
Matrix.prototype.equalsNearlyValues=function(x,y,precision=6){
|
|
150
|
+
if(x==0){
|
|
151
|
+
if(y.toFixed(precision)==0) return this;
|
|
152
|
+
} else {
|
|
153
|
+
if((y/x).toFixed(precision)==1) return this;
|
|
154
|
+
}
|
|
155
|
+
throw Error(x+" != "+y);
|
|
156
|
+
return this;
|
|
157
|
+
}
|
|
158
|
+
Matrix.prototype.equalsNearlyVector=function(vector,precision=6,baseVector=this.vector){
|
|
159
|
+
baseVector.forEach((v,i,a)=>{
|
|
160
|
+
try{
|
|
161
|
+
this.equalsNearlyValues(v,vector[i],precision)
|
|
162
|
+
} catch(ex) {
|
|
163
|
+
throw Error('not equal, index '+i+' left: '+v+' right: '+vector[i]);
|
|
164
|
+
}
|
|
165
|
+
})
|
|
166
|
+
return this;
|
|
167
|
+
}
|
|
168
|
+
Matrix.prototype.fill=function(value, start, end){
|
|
169
|
+
this.vector.fill(value, start, end);
|
|
170
|
+
return this;
|
|
171
|
+
}
|
|
172
|
+
Matrix.prototype.fillArray=function(a){
|
|
173
|
+
const matrix=this;
|
|
174
|
+
a.forEach((columns)=>matrix.addRow(columns))
|
|
175
|
+
return this;
|
|
176
|
+
}
|
|
177
|
+
Matrix.prototype.findColumnRow=function(column,call,startRow=0,endRow=this.rows-1){
|
|
178
|
+
let offset=startRow*this.columns+column;
|
|
179
|
+
for(let row=startRow;row<=this.rows;row++){
|
|
180
|
+
if(call.apply(this,[this.vector[offset],row,column,offset,this.vector])) {
|
|
181
|
+
return row;
|
|
182
|
+
}
|
|
183
|
+
offset+=this.columns;
|
|
184
|
+
}
|
|
185
|
+
return -1;
|
|
186
|
+
}
|
|
187
|
+
Matrix.prototype.findRowColumn=function(row,call,startColumn=0,endColumn=this.columns-1){
|
|
188
|
+
let offset=row*this.columns+startColumn;
|
|
189
|
+
for(let column=startColumn;column<=endColumn;column++){
|
|
190
|
+
if(call.apply(this,[this.vector[offset],column,offset,this.vector]))
|
|
191
|
+
return column;
|
|
192
|
+
offset++;
|
|
193
|
+
}
|
|
194
|
+
return -1;
|
|
195
|
+
}
|
|
196
|
+
Matrix.prototype.forColumnCells=function(column,call,startRow=0,endRow=this.rows-1){
|
|
197
|
+
let offset=startRow*this.columns+column;
|
|
198
|
+
for(let row=startRow;row<=endRow;row++){
|
|
199
|
+
call.apply(this,[this.vector[offset],row,offset,this.vector]);
|
|
200
|
+
offset+=this.columns;
|
|
201
|
+
}
|
|
202
|
+
return this;
|
|
203
|
+
}
|
|
204
|
+
Matrix.prototype.forEachCell=function(call){
|
|
205
|
+
for(let offset=0,row=0;row<this.rows;row++) {
|
|
206
|
+
for(let column=0;column<this.columns;column++) {
|
|
207
|
+
call.apply(this,[this.vector[offset],row,column,this.vector,offset,this]);
|
|
208
|
+
offset++
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
return this;
|
|
212
|
+
}
|
|
213
|
+
Matrix.prototype.forEachCellLowerTriangle=function(call){
|
|
214
|
+
this.testIsSquare();
|
|
215
|
+
for(let row=0;row<this.rows;row++) {
|
|
216
|
+
let offset=row*this.columns;
|
|
217
|
+
for(let column=0;column<=row;column++) {
|
|
218
|
+
call.apply(this,[this.vector[offset],row,column,this.vector,offset,this]);
|
|
219
|
+
offset++
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
return this;
|
|
223
|
+
}
|
|
224
|
+
Matrix.prototype.forEachCellPairSet=function(matrix,call){
|
|
225
|
+
if(matrix instanceof Matrix){
|
|
226
|
+
if(this.rows!=matrix.rows) throw Error("number of rows different");
|
|
227
|
+
if(this.columns!=matrix.columns) throw Error("number of columns different");
|
|
228
|
+
for(let offset=0;offset<this.size;offset++)
|
|
229
|
+
this.vector[offset]=call.apply(this,[this.vector[offset],matrix.vector[offset]]);
|
|
230
|
+
return this;
|
|
231
|
+
}
|
|
232
|
+
if(this.rows!=matrix.length) throw Error("number of rows different");
|
|
233
|
+
if(this.columns!=matrix[0].length) throw Error("number of columns different");
|
|
234
|
+
for(let offset=0,row=0;row<this.rows;row++) {
|
|
235
|
+
for(let column=0;column<this.columns;column++) {
|
|
236
|
+
this.vector[offset]=call.apply(this,[this.vector[offset],matrix[row][column]]);
|
|
237
|
+
offset++
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return this;
|
|
241
|
+
}
|
|
242
|
+
Matrix.prototype.forEachCellUpperTriangle=function(call){
|
|
243
|
+
this.testIsSquare();
|
|
244
|
+
for(let row=0;row<this.rows;row++) {
|
|
245
|
+
let offset=row*this.columns;
|
|
246
|
+
for(let column=row;column<=this.columns;column++) {
|
|
247
|
+
call.apply(this,[this.vector[offset],row,column,this.vector,offset,this]);
|
|
248
|
+
offset++
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return this;
|
|
252
|
+
}
|
|
253
|
+
Matrix.prototype.forEachRow=function(call){
|
|
254
|
+
for(let row=0;row<this.rows;row++) {
|
|
255
|
+
const rowVector=this.getRow(row);
|
|
256
|
+
call.apply(this,[rowVector,row,this])
|
|
257
|
+
}
|
|
258
|
+
return this;
|
|
259
|
+
}
|
|
260
|
+
Matrix.prototype.forRowCells=function(row,call,startColumn=0,endColumn=this.columns-1){
|
|
261
|
+
let offset=row*this.columns+startColumn;
|
|
262
|
+
for(let column=startColumn;column<=endColumn;column++){
|
|
263
|
+
call.apply(this,[this.vector[offset],column,offset,this.vector]);
|
|
264
|
+
offset++;
|
|
265
|
+
}
|
|
266
|
+
return this;
|
|
267
|
+
}
|
|
268
|
+
Matrix.prototype.forwardElimination=function(){
|
|
269
|
+
let maxValue,maxRow;
|
|
270
|
+
for(let pivotRowColumn=0; pivotRowColumn<this.rows; pivotRowColumn++) {
|
|
271
|
+
maxRow=pivotRowColumn; // Initialize maximum value and index for pivot
|
|
272
|
+
maxValue=Math.abs(this.get(maxRow,pivotRowColumn));
|
|
273
|
+
for(let row=pivotRowColumn+1; row<this.rows; row++){
|
|
274
|
+
const value=Math.abs(this.get(row,pivotRowColumn))
|
|
275
|
+
if(value>maxValue) {
|
|
276
|
+
maxValue=value;
|
|
277
|
+
maxRow=row;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// if a principal diagonal element is zero then matrix is singular + division-by-zero later
|
|
281
|
+
if(this.get(pivotRowColumn,maxRow)==0) {
|
|
282
|
+
throw Error("Singular matrix, "+(this.get(pivotRowColumn,this.columns)==0?"may have infinitely many solutions":"inconsistent system"))
|
|
283
|
+
}
|
|
284
|
+
if(maxRow!=pivotRowColumn) this.swapRows(pivotRowColumn, maxRow);
|
|
285
|
+
pivotValue=this.get(pivotRowColumn,pivotRowColumn);
|
|
286
|
+
for(let row=pivotRowColumn+1; row<this.rows; row++){
|
|
287
|
+
const factor=this.get(row,pivotRowColumn)/pivotValue;
|
|
288
|
+
for(let column=pivotRowColumn+1; column<this.columns; column++){
|
|
289
|
+
this.subtractCell(row,column,this.get(pivotRowColumn,column)*factor);
|
|
290
|
+
}
|
|
291
|
+
this.set(row,pivotRowColumn,0);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
return this;
|
|
295
|
+
}
|
|
296
|
+
Matrix.prototype.gaussianElimination=function(){
|
|
297
|
+
return this.forwardElimination().backwardSubstitution();
|
|
298
|
+
}
|
|
299
|
+
Matrix.prototype.get=function(row, column){
|
|
300
|
+
return this.vector[row*this.columns+column];
|
|
301
|
+
}
|
|
302
|
+
Matrix.prototype.getAdjoint=function(){
|
|
303
|
+
if(this.columns==1) return new Matrix([[1]]);
|
|
304
|
+
const adjoint=this.createLike();
|
|
305
|
+
for(let offset=0,row=0;row<adjoint.rows;row++) {
|
|
306
|
+
for(let column=0;column<adjoint.columns;column++) {
|
|
307
|
+
const temp=this.getCofactor(column,row); // get reverse
|
|
308
|
+
adjoint.vector[offset]=((row+column)%2==0)?temp.getDeterminant():-temp.getDeterminant();
|
|
309
|
+
offset++
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
return adjoint;
|
|
313
|
+
}
|
|
314
|
+
Matrix.prototype.getCofactor=function(cellRow,cellColumn){
|
|
315
|
+
const matrixSize=this.rows-1;
|
|
316
|
+
const matrix=new Matrix(matrixSize,matrixSize)
|
|
317
|
+
const startLength=cellColumn;
|
|
318
|
+
const endLength=matrixSize-cellColumn;
|
|
319
|
+
const columnOffsetPart2=cellColumn+1;
|
|
320
|
+
let thisRowOffset=0;matrixRowOffset=0;
|
|
321
|
+
for(let row=0;row<this.rows;row++) {
|
|
322
|
+
if(row!==cellRow){
|
|
323
|
+
const vectorStart=this.vector.slice(thisRowOffset,thisRowOffset+startLength);
|
|
324
|
+
const thisRowOffsetPart2=thisRowOffset+columnOffsetPart2;
|
|
325
|
+
const vectorEnd=this.vector.slice(thisRowOffsetPart2,thisRowOffsetPart2+endLength);
|
|
326
|
+
matrix.vector.set(vectorStart,matrixRowOffset);
|
|
327
|
+
matrix.vector.set(vectorEnd,matrixRowOffset+cellColumn)
|
|
328
|
+
matrixRowOffset+=matrixSize; //next rows
|
|
329
|
+
}
|
|
330
|
+
thisRowOffset+=this.rows
|
|
331
|
+
}
|
|
332
|
+
return matrix;
|
|
333
|
+
}
|
|
334
|
+
Matrix.prototype.getComplementMinor=function(cellRow, cellColumn){
|
|
335
|
+
const matrix = new Matrix(this.rows-1,this.columns-1);
|
|
336
|
+
for(let row=0,column=0,sourceRow=0; sourceRow<this.rows; sourceRow++) {
|
|
337
|
+
if(sourceRow==cellRow) continue;
|
|
338
|
+
for(let sourceColumn=0; sourceColumn<this.columns; sourceColum++) {
|
|
339
|
+
if(sourceColum==cellColumn) continue; // In the first sourceRow Data ellipsis of column
|
|
340
|
+
matrix.set(row,column,this.get(sourceRow, sourceColumn));
|
|
341
|
+
column++;
|
|
342
|
+
if(column >= matrix.columns ) {
|
|
343
|
+
column=0;
|
|
344
|
+
row++;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return matrix;
|
|
349
|
+
}
|
|
350
|
+
Matrix.prototype.getDeterminant=function(){
|
|
351
|
+
if(this.determinant) return this.determinant
|
|
352
|
+
this.testIsSquare();
|
|
353
|
+
return this.setDeterminant();
|
|
354
|
+
}
|
|
355
|
+
Matrix.prototype.getDeterminantUsingCofactor=function(){
|
|
356
|
+
this.determinant=0;
|
|
357
|
+
if(this.rows>2) {
|
|
358
|
+
let sign=1;
|
|
359
|
+
for(let column=0; column<this.columns; column++) {
|
|
360
|
+
this.determinant += sign*this.vector[column]*this.getCofactor(0,column).getDeterminantUsingCofactor();
|
|
361
|
+
sign=-sign;
|
|
362
|
+
}
|
|
363
|
+
} else {
|
|
364
|
+
this.determinant=this.vector[0]*this.vector[3]- this.vector[1]*this.vector[2];
|
|
365
|
+
}
|
|
366
|
+
return this.determinant;
|
|
367
|
+
}
|
|
368
|
+
Matrix.prototype.getDeterminantUsingRowEchelonForm=function(){
|
|
369
|
+
if(this.rows==1) return this.vector[0];
|
|
370
|
+
const matrix=this.clone();
|
|
371
|
+
matrix.determinant=parseFloat(1); //force use of float
|
|
372
|
+
matrix.rowEchelonForm();
|
|
373
|
+
this.determinant=1/matrix.determinant;
|
|
374
|
+
return this.determinant;
|
|
375
|
+
}
|
|
376
|
+
Matrix.prototype.getIdentity=function(){
|
|
377
|
+
const identity=this.createLike();
|
|
378
|
+
for(let offset=0;offset<identity.size;offset+=identity.columns+1) identity.vector[offset]=1;
|
|
379
|
+
return identity;
|
|
380
|
+
}
|
|
381
|
+
Matrix.prototype.getIndex=function(row, column){
|
|
382
|
+
return row*this.columns+column;
|
|
383
|
+
}
|
|
384
|
+
Matrix.prototype.getInverse=Matrix.prototype.getInverseGaussJordan;
|
|
385
|
+
Matrix.prototype.getInverseAdjointMethod=function(){
|
|
386
|
+
const determinant=this.getDeterminant();
|
|
387
|
+
if(determinant==0) throw Error("Singular matrix, can't find its inverse");
|
|
388
|
+
const adjoint=this.getAdjoint();
|
|
389
|
+
for(let offset=0;offset<adjoint.size;offset++) {
|
|
390
|
+
adjoint.vector[offset]/=determinant;
|
|
391
|
+
}
|
|
392
|
+
return adjoint;
|
|
393
|
+
}
|
|
394
|
+
Matrix.prototype.getInverseGaussJordan=function(){
|
|
395
|
+
this.testIsSquare();
|
|
396
|
+
const matrix = new Matrix(this.rows, this.columns * 2);
|
|
397
|
+
for (let row=0,offset=this.columns; row<this.rows; ++row) {
|
|
398
|
+
matrix.setRow(this.getRow(row),row);
|
|
399
|
+
matrix.vector[offset+row]=1;
|
|
400
|
+
offset+=matrix.columns
|
|
401
|
+
}
|
|
402
|
+
matrix.reducedRowEchelonForm();
|
|
403
|
+
return matrix.getMatrix(0,this.columns,this.rows,this.columns);
|
|
404
|
+
}
|
|
405
|
+
Matrix.prototype.getMatrix=function(row,column,rows,columns){
|
|
406
|
+
const matrix = new Matrix(rows,columns);
|
|
407
|
+
for(let matrixOffset=0,thisOffset=row*this.columns+column; row<this.rows; ++row,thisOffset+=this.columns,matrixOffset+=rows) {
|
|
408
|
+
matrix.vector.set(this.vector.subarray(thisOffset,thisOffset+columns),matrixOffset)
|
|
409
|
+
}
|
|
410
|
+
return matrix;
|
|
411
|
+
}
|
|
412
|
+
Matrix.prototype.getRow=function(row){
|
|
413
|
+
const start=row*this.columns;
|
|
414
|
+
return this.vector.subarray(start,start+this.columns);
|
|
415
|
+
}
|
|
416
|
+
Matrix.prototype.getVandermonde=function(vectorIn,columns){
|
|
417
|
+
const matrix = new Matrix(vectorIn.length,columns);
|
|
418
|
+
matrix.forEachCell((cell,row,column,vector,offset,object)=>vector[offset]=vectorIn[row]**column)
|
|
419
|
+
return matrix;
|
|
420
|
+
}
|
|
421
|
+
Matrix.prototype.getZeroed=function(row, column){
|
|
422
|
+
const offset=row*this.columns+column;
|
|
423
|
+
const value=this.vector[offset];
|
|
424
|
+
if(value==0 || Math.abs(value)>zeroFloat32Value) return value;
|
|
425
|
+
this.vector[offset]=0;
|
|
426
|
+
return 0;
|
|
427
|
+
}
|
|
428
|
+
Matrix.prototype.maxAbsColumn=function(column,startRow=0){
|
|
429
|
+
let rowColumnOffset=startRow*this.columns+column;
|
|
430
|
+
let maxValue=Math.abs(this.vector[rowColumnOffset]);
|
|
431
|
+
rowColumnOffset+=this.columns;
|
|
432
|
+
while (rowColumnOffset<this.size) {
|
|
433
|
+
const value=Math.abs(this.vector[rowColumnOffset])
|
|
434
|
+
if(value>maxValue) {
|
|
435
|
+
maxValue=value;
|
|
436
|
+
maxRowColumnOffset=rowColumnOffset;
|
|
437
|
+
};
|
|
438
|
+
rowColumnOffset+=this.columns;
|
|
439
|
+
}
|
|
440
|
+
return {row:(maxRowColumnOffset-column)/this.columns,value:this.vector[maxRowColumnOffset]}
|
|
441
|
+
}
|
|
442
|
+
Matrix.prototype.maxColumn=function(column,startRow=0){
|
|
443
|
+
let rowColumnOffset=startRow*this.columns+column;
|
|
444
|
+
let maxValue=this.vector[rowColumnOffset];
|
|
445
|
+
rowColumnOffset+=this.columns;
|
|
446
|
+
while (rowColumnOffset<this.size) {
|
|
447
|
+
const value=this.vector[rowColumnOffset];
|
|
448
|
+
if(value>maxValue) {
|
|
449
|
+
maxValue=value;
|
|
450
|
+
maxRowColumnOffset=rowColumnOffset;
|
|
451
|
+
};
|
|
452
|
+
rowColumnOffset+=this.columns;
|
|
453
|
+
}
|
|
454
|
+
return {row:(maxRowColumnOffset-column)/this.columns,value:this.vector[maxRowColumnOffset]}
|
|
455
|
+
}
|
|
456
|
+
Matrix.prototype.multiply=function(rightMatrix){
|
|
457
|
+
const leftMatrix=this;
|
|
458
|
+
if(leftMatrix.columns!=rightMatrix.rows) throw Error(leftMatrix.columns+ "columns != "+rightMatrix.rows+" rows of argument");
|
|
459
|
+
const result=new Matrix({rows:leftMatrix.rows,columns:rightMatrix.columns})
|
|
460
|
+
result.forEachCell((cell,row,column)=>{
|
|
461
|
+
const value=leftMatrix.reduceRow(row,
|
|
462
|
+
(value,rowCell,leftRow,leftColumn)=>
|
|
463
|
+
value+rowCell*rightMatrix.get(leftColumn,column)
|
|
464
|
+
);
|
|
465
|
+
result.set(row,column,value);
|
|
466
|
+
})
|
|
467
|
+
return result;
|
|
468
|
+
}
|
|
469
|
+
Matrix.prototype.multiplyRow=function(row,factor){
|
|
470
|
+
this.determinant*=factor;
|
|
471
|
+
this.forRowCells(row,(cell,column,offset,vector)=>vector[offset]*=factor)
|
|
472
|
+
return this;
|
|
473
|
+
}
|
|
474
|
+
Matrix.prototype.norm=function(){
|
|
475
|
+
|
|
476
|
+
return Math.sqrt(this.reduce((aggregate,cell)=>aggregate+cell*cell))
|
|
477
|
+
}
|
|
478
|
+
Matrix.prototype.reduce=function(call,aggregate=0){
|
|
479
|
+
this.forEachCell((cell,row,column,vector,offset,object)=>
|
|
480
|
+
aggregate=call.apply(this,[aggregate,cell,row,column,vector,offset,object])
|
|
481
|
+
);
|
|
482
|
+
return aggregate;
|
|
483
|
+
}
|
|
484
|
+
Matrix.prototype.reducedRowEchelonForm=function(){
|
|
485
|
+
const lastColumn=this.columns-1;
|
|
486
|
+
for(let pivotColumn=0,pivotRow=0;pivotRow<this.rows;pivotRow++){
|
|
487
|
+
if(this.rows<=pivotRow) throw Error("logic error")
|
|
488
|
+
let row=pivotRow;
|
|
489
|
+
while(this.getZeroed(row,pivotColumn)==0){
|
|
490
|
+
row++
|
|
491
|
+
if(this.rows==row){
|
|
492
|
+
row=pivotRow;
|
|
493
|
+
pivotColumn++
|
|
494
|
+
if(pivotColumn>=this.columns) {
|
|
495
|
+
if(row==this.rows-1) return this;
|
|
496
|
+
throw Error("row all zeroes which is not last row")
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
if(row!==pivotRow) this.swapRows(row,pivotRow)
|
|
501
|
+
const factor=this.get(pivotRow,pivotColumn)
|
|
502
|
+
for(let column=pivotColumn;column<this.columns;column++)
|
|
503
|
+
this.divideCell(pivotRow,column,factor)
|
|
504
|
+
if(pivotColumn==lastColumn) break;
|
|
505
|
+
for(let row=0;row<this.rows;row++){
|
|
506
|
+
if(row==pivotRow) continue;
|
|
507
|
+
const factor=-this.getZeroed(row,pivotColumn);
|
|
508
|
+
if(factor==0) continue;
|
|
509
|
+
for(let column=pivotColumn;column<this.columns;column++){
|
|
510
|
+
const value=this.get(pivotRow,column);
|
|
511
|
+
if(value)
|
|
512
|
+
this.addCell(row,column,factor*value)
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if(++pivotColumn>this.columns) break;
|
|
516
|
+
}
|
|
517
|
+
return this;
|
|
518
|
+
}
|
|
519
|
+
Matrix.prototype.rowEchelonForm=function(){
|
|
520
|
+
const columns=this.columns;
|
|
521
|
+
let pivotRow=0,pivotColumn=0,lastRowAdj=this.rows-1;
|
|
522
|
+
nextPivot: while(pivotRow<=lastRowAdj & pivotColumn<columns){
|
|
523
|
+
let pivotValue=this.getZeroed(pivotRow, pivotColumn);
|
|
524
|
+
if(pivotValue==0) {
|
|
525
|
+
const i=this.findRowColumn(pivotRow,value=>Math.abs(value)>zeroFloat32Value,pivotColumn+1,lastRowAdj) //none zeroe?
|
|
526
|
+
if(i==-1){
|
|
527
|
+
if(pivotRow==lastRowAdj) {
|
|
528
|
+
pivotColumn++;
|
|
529
|
+
continue nextPivot;
|
|
530
|
+
}
|
|
531
|
+
this.swapRows(pivotRow,lastRowAdj--);
|
|
532
|
+
if(lastRowAdj<=1) return this;
|
|
533
|
+
continue nextPivot;
|
|
534
|
+
}
|
|
535
|
+
const row=this.findColumnRow(pivotColumn,value=>Math.abs(value)>zeroFloat32Value,pivotRow+1,lastRowAdj);
|
|
536
|
+
if(row==-1) {
|
|
537
|
+
pivotColumn++;
|
|
538
|
+
continue nextPivot;
|
|
539
|
+
}
|
|
540
|
+
this.swapRows(pivotRow,row);
|
|
541
|
+
pivotValue=this.get(pivotRow, pivotColumn);
|
|
542
|
+
}
|
|
543
|
+
this.divideRow(pivotRow,pivotValue,pivotColumn);
|
|
544
|
+
for(let row=pivotRow+1; row<=lastRowAdj; row++) {
|
|
545
|
+
const factor=-this.get(row, pivotColumn);
|
|
546
|
+
this.addRow2Row(pivotRow,row,factor,pivotColumn);
|
|
547
|
+
}
|
|
548
|
+
pivotRow++;
|
|
549
|
+
pivotColumn++;
|
|
550
|
+
}
|
|
551
|
+
return this;
|
|
552
|
+
}
|
|
553
|
+
Matrix.prototype.reduceRow=function(row,call,value=0){
|
|
554
|
+
this.forRowCells(row,(cell,column,offset,vector)=>{
|
|
555
|
+
value=call.apply(this,[value,cell,row,column]);
|
|
556
|
+
});
|
|
557
|
+
return value;
|
|
558
|
+
}
|
|
559
|
+
Matrix.prototype.set=function(row,column,value){
|
|
560
|
+
this.vector[this.getIndex(row,column)]=value;
|
|
561
|
+
return this;
|
|
562
|
+
}
|
|
563
|
+
Matrix.prototype.setDeterminant=function(){
|
|
564
|
+
this.determinant=this.getDeterminantUsingRowEchelonForm();
|
|
565
|
+
return this.determinant;
|
|
566
|
+
}
|
|
567
|
+
Matrix.prototype.setRow=function(vector,row){
|
|
568
|
+
this.vector.set(vector, row*this.columns);
|
|
569
|
+
return this;
|
|
570
|
+
}
|
|
571
|
+
Matrix.prototype.setRunningSum=function(){
|
|
572
|
+
this.forEachCellLowerTriangle((cell,row,column,vector,offset,object)=>vector[offset]=1);
|
|
573
|
+
return this;
|
|
574
|
+
}
|
|
575
|
+
Matrix.prototype.substract=function(matrix){
|
|
576
|
+
this.forEachCellPairSet(matrix,(cellA,cellB)=>cellA-cellB)
|
|
577
|
+
return this;
|
|
578
|
+
}
|
|
579
|
+
Matrix.prototype.subtractCell=function(row,column,value){
|
|
580
|
+
this.vector[this.getIndex(row,column)]-=value;
|
|
581
|
+
return this;
|
|
582
|
+
}
|
|
583
|
+
Matrix.prototype.sumRow=function(row){
|
|
584
|
+
return this.reduceRow(row,(value,cell)=>value+cell);
|
|
585
|
+
}
|
|
586
|
+
Matrix.prototype.swapRows=function(rowA,rowB){
|
|
587
|
+
this.determinant=-this.determinant;
|
|
588
|
+
const startRowA=rowA*this.columns;
|
|
589
|
+
const rowAVector=this.vector.slice(startRowA,startRowA+this.columns);
|
|
590
|
+
const startRowB=rowB*this.columns
|
|
591
|
+
this.vector.copyWithin(startRowA,startRowB,startRowB+this.columns);
|
|
592
|
+
this.vector.set(rowAVector,startRowB);
|
|
593
|
+
return this;
|
|
594
|
+
}
|
|
595
|
+
Matrix.prototype.testIsSquare=function(){
|
|
596
|
+
if(this.rows!=this.columns) throw Error("not square matrix");
|
|
597
|
+
return this;
|
|
598
|
+
}
|
|
599
|
+
Matrix.prototype.toArray=function(precision=6){
|
|
600
|
+
const result=[];
|
|
601
|
+
this.forEachRow((row,index)=>{
|
|
602
|
+
const columns=[];
|
|
603
|
+
row.forEach(value=>columns.push(value))
|
|
604
|
+
result.push(columns)
|
|
605
|
+
})
|
|
606
|
+
return result;
|
|
607
|
+
}
|
|
608
|
+
Matrix.prototype.transpose=function(){
|
|
609
|
+
const matrix=new Matrix({rows:this.columns,columns:this.rows})
|
|
610
|
+
this.forEachCell((cell,row,column)=>matrix.set(column,row,cell))
|
|
611
|
+
return matrix;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
module.exports=Matrix;
|
|
615
|
+
|
|
616
|
+
/*
|
|
617
|
+
function setDataPoint(value,term,node,dp) {
|
|
618
|
+
Object.assign(dp,{
|
|
619
|
+
avg:0,
|
|
620
|
+
count:0, =rows
|
|
621
|
+
movingSum:0,
|
|
622
|
+
movingSumSquared:0,
|
|
623
|
+
movingSumCubed:0,
|
|
624
|
+
outlier:false,
|
|
625
|
+
sum:0,
|
|
626
|
+
sumSquared:0,
|
|
627
|
+
sumCubed:0,
|
|
628
|
+
term:term,
|
|
629
|
+
weightedMovingSum:0,
|
|
630
|
+
exponentialWeightedMoving:[new EMA(0.25),new EMA(0.5),new EMA(0.75)]
|
|
631
|
+
});
|
|
632
|
+
};
|
|
633
|
+
dp.max=Math.max(dp.max||value,value);
|
|
634
|
+
dp.min=Math.min(dp.min||value,value);
|
|
635
|
+
dp.range=dp.max-dp.min;
|
|
636
|
+
dp.sum+=value;
|
|
637
|
+
dp.sumSquared+=Math.pow(value,2);
|
|
638
|
+
dp.sumCubed+=Math.pow(value,3);
|
|
639
|
+
dp.movingSum+=value-removedValue;
|
|
640
|
+
dp.movingSumSquared+=Math.pow(value,2)-Math.pow(removedValue,2);
|
|
641
|
+
dp.movingSumCubed+=Math.pow(value,3)-Math.pow(removedValue,3);
|
|
642
|
+
// dp.avg=dp.sum/this.rows;
|
|
643
|
+
const avg=dp.avg;
|
|
644
|
+
dp.normalised=dp.range ? (value-avg)/dp.range : 0;
|
|
645
|
+
// dp.movingAvg=dp.movingSum/values.length;
|
|
646
|
+
// dp.variance=dp.sumSquared/count - Math.pow(avg,2);
|
|
647
|
+
// dp.stdDev=Math.sqrt(dp.variance);
|
|
648
|
+
dp.movingVariance=dp.movingSumSquared/values.length - Math.pow(dp.movingAvg,2);
|
|
649
|
+
dp.movingStdDev=Math.sqrt(dp.movingVariance);
|
|
650
|
+
dp.median=functions.median(values);
|
|
651
|
+
dp.standardized=( (value-avg)/dp.stdDev )||0;
|
|
652
|
+
dp.movingStandardized=( (value-dp.movingAvg)/dp.movingStdDev )||0;
|
|
653
|
+
dp.skewness=(dp.sumCubed-3*avg*dp.variance-Math.pow(avg,3))/dp.variance*dp.stdDev;
|
|
654
|
+
dp.movingSkewness=(dp.movingSumCubed-3*dp.movingAvg*dp.movingVariance-Math.pow(dp.movingAvg,3))/dp.movingVariance*dp.stdDev;
|
|
655
|
+
dp.outlier=node.outliersFunction(node,dp,value);
|
|
656
|
+
dp.weightedMovingSum+=count*value;
|
|
657
|
+
dp.weightedMovingAvg=(dp.weightedMovingAvg*2/count)/(count+1);
|
|
658
|
+
dp.exponentialWeightedMoving.forEach(c=>c.sample(value));
|
|
659
|
+
}
|
|
660
|
+
*/
|