-
Notifications
You must be signed in to change notification settings - Fork 4
/
README
288 lines (209 loc) · 10 KB
/
README
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
Note: I am not actively maintaining this library anymore. (But I
will accept pull requests.) If you are considering this library you
should also consider writing your code in C/C++ and compile it to
JavaScript with Emscripten. That's what I do now.
- * - * - * - * - * - * - * - * - * - * - * - * - * - * - * -
NumJS -- A JavaScript library for numerical computing
=====================================================
This library provides:
- classes and functions for doing computations with
* Real and Complex Numbers
* Real and Complex Matrices
* Permutation Matrices
- linear decompositons and linear solvers:
* LU and PLU decomposition
* QR decomposition [TBD]
* singular value decomposition [TBD]
* linear least-squares fitter [TBD]
- special functions:
* B-spline basis functions [TBD]
* Gauss error function [TBD]
- polynomials:
* evaluating polynomials [TBD]
* numerical polynomial solver [TBD]
The items marked as [TBD] aren't implemented yet. Feel free to implement
them (and/or other stuff) and send me your pull requests.
A warning about numerical computing with JavaScript
---------------------------------------------------
Some JavaScript implementations store numbers as single precision (32bit)
floating point values. So the possible applications for JavaScript in the world
of numerical computing is in fact very limited and you should always make sure
that you understand how to estimate the numerical errors in the calculation you
want to perform.
Some of the more advanced routines in this library use the value NumJS.eps to
determine when a value is small enough to be treated as zero. The value of
NumJS.eps is initialized to the square root of the machine epsilon when NumJS
is loaded.
Real and Complex Numbers
------------------------
JavaScript does not support operator overloading. So in all arithmetric that
might involve objects other then real numbers must be performed via the
following NumJS function:
NumJS.ADD(x, y) .... x + y
NumJS.SUB(x, y) .... x - y
NumJS.MUL(x, y) .... x * y
NumJS.DOT(x, y) .... x . y
NumJS.DIV(x, y) .... x / y
NumJS.SOLVE(x, y) .... x \ y
NumJS.POW(x, y) .... x ^ y
NumJS.INV(x) .... 1/x
NumJS.NEG(x) .... -x
NumJS.ABS(x) .... abs(x)
NumJS.NORM(x) .... norm(x) (euclidean norm)
NumJS.ARG(x) .... arg(x)
NumJS.CONJ(x) .... conjugate(x)
NumJS.TRANSP(x) .... transpose(x)
NumJS.EXP(x) .... e^x
NumJS.LOG(x) .... log(x)
NumJS.DET(x) .... det(x)
NumJS.RE(x) .... real_part(x)
NumJS.IM(x) .... imaginary_part(x)
NumJS.ROUND(x, N) .... round(x) (to N decimals)
NumJS.EQ(x, y) .... x == y
NumJS.EQ_ABS(x, y, T) .... x == y (with max abs error T)
NumJS.EQ_REL(x, y, T) .... x == y (with max rel error T)
Note that not all objects support all operations. Whenever an operation
is performed on objects that do not support it, a "NumJS.* type error"
exception is thrown.
The script "opmap.html" generates a table showing the allowed operand
types for each operation.
For real numbers the native JavaScript number type is used:
var x = 1.2345;
Complex numbers can be created using the NumJS.C(re, im) helper function:
var z = NumJS.C(1, 1);
or in polar coordinates using the NumJS.P(abs, arg) helper function:
var z = NumJS.P(Math.PI/4, Math.sqrt(2));
Complex numbers have a .toString() method that is automatically used by
the interpreter to convert a complex number to a string when needed. They
also have .toFixed() and a .toPrecision() methods that are compatible with
the generic JavaScript .toFixed() and .toPrecision() number methods.
Expression Parser
-----------------
It can be hard to write complex expressions using the NumJS.* operator functions.
So NumJS comes with en expression parser for infix expressions that can be used
to automatically create functions that use the NumJS.* operators from string
expressions:
var func = eval(NumJS.Parse(
"a + b * -(c / pow(-2i, a)) + b",
"a, b, c", { pow: "NumJS.POW" }));
var result = func(1, 2, 3);
The 1st argument to NumJS.Parse() is the expression to generate a function for.
The 2nd argument to NumJS.Parse() is the argument list for the generated function
and the 3rd argument is a hash with all local defines (like short forms for
function calls) to be included in the generated function. The JavaScript code
that is returned by NumJS.Parse() looks like this:
(function(a, b, c){
var pow = NumJS.POW;
var $0 = NumJS.C(0, 2);
var $1 = NumJS.NEG($0);
var $2 = pow($1, a);
var $3 = NumJS.DIV(c, $2);
var $4 = NumJS.NEG($3);
var $5 = NumJS.MUL(b, $4);
var $6 = NumJS.ADD(a, $5);
var $7 = NumJS.ADD($6, b);
return $7;
})
The parser accepts the following constructs in expressions:
- number literals and variable names
- imagenary literals (a number followed by 'i')
- matrices using [ a, b, c; d, e, f ] notation
- ( .. ) blocks and function calls
- prefix '+' (ignored) and '-' (NumJS.NEG)
- infix '*' (NumJS.MUL)
- infix '/' (NumJS.DIV)
- infix '\' (NumJS.SOLVE)
- infix '+' (NumJS.ADD)
- infix '-' (NumJS.SUB)
It is generally recommended to parse and evaluate an expression only once and
then reuse the resulting function handler for subsequent calculations.
Matrices
--------
Matrices can be created using one of the following two functions:
var matrix3x3 = NumJS.MAT(3, 3);
var permut3x3 = NumJS.PMAT(3);
Normal matrices are initialized to all-zeros and can be filled using the
.set(row, col, value) method. Note that row and column indexes start with 0.
Permutation matrices are initialized to the identity matrix and can be
modified using the .pivot_col(c1, c2) and .pivot_row(r1, r2) methods,
that pivot the two specified rows or columns.
Alternatively matrices can be initialized using their constructors by passing
and additional array with the initialization data in row-major order:
var matrixFlip3x3 = NumJS.MAT(3, 3, [
0, 0, 1,
0, 1, 0,
1, 0, 0
]);
Permutation matrices can also be created with initalization data. In this
case the array must contain the row-number of each '1' per column:
var permutFlip3x3 = NumJS.PMAT(3, [2, 1, 0]);
There is no special vector class. So vectors must simply be created as
Nx1 matrices.
Matrix-matrix and matrix-scalar operations can be performed using the
uppercase NumJS.* metioned above and work as one would expect it. E.g.:
var A = NumJS.MAT(2, 2, [1, 2, 3, 4]);
var B_re = NumJS.MAT(2, 2, [5, 6, 7, 8]);
var B_im = NumJS.MAT(2, 2, [9, 0, 1, 2]);
var B = NumJS.ADD(B_re, NumJS.MUL(B_im, NumJS.C(0,1)));
var Z = NumJS.MUL(A, B);
Individal matrix elements can be accessed using the .get(row, col) method.
Note that also permutation matrices provide a .get(row, col) method, but
lack support of a .set(row, col, value) method.
A copy of a matrix can be created using the .copy() method. This method
converts permutation matrices into normal matrices. If this is not the desired
behavior, use the .clone() method instead.
A row-major array of matrix block can be generated using the matrix
.cut(row, col, nRows, nCols) method. A block of a matrix can be written
using the matrix .paste(row, col, nRows, nCols) mehthod. The methods
.cut_cm() and .paste_cm() do the same for column-major arrays. This
functions can be used to easily rearange matrices or initialize them
row- or column-wise:
var A = NumJS.MAT(3, 3);
A.paste(0, 0, 1, 3, [ 1, 2, 3 ]);
A.paste(1, 0, 1, 3, [ 4, 5, 6 ]);
A.paste(2, 0, 1, 3, [ 7, 8, 9 ]);
The dimension of a matrix is stored in its .rows and .cols properties.
A permutation matrix has an additional .sign property that holds the
sign of the permutation.
Matrices also have a .toString() method that is automatically used by the
interpreter to convert a Matrix to a string when needed. They also have
.toFixed() and a .toPrecision() methods that are compatible with the generic
JavaScript .toFixed() and .toPrecision() number methods.
Additionally matrices also have a .toTextBlock() method that creates
a nice multi-line text representation of the matrix.
Reduced row echelon form of a matrix and matrix rank
----------------------------------------------------
Each matrix object provides the method .rref() that calculates the
reduced row echelon form of the matrix. The matrix object returned
by .rref() has an additional property .pivcols that is an array with
the indices of all pivot columns.
The number of elements in .rref().pivcols is also the rank of the
matrix. For convinience each matrix object also has a .rank() method
that simply returns the value of .rref().pivcols.length.
PA = LU factorization and solver
--------------------------------
Each matrix object provides the methods .LU() and .PLU() that perform
an LU factorization and return a PLU object when the factorization was
successful and null if the matrix can't be factorized.
The result of a factorization is cached. So successive callc to .LU()
or .PLU() do not introduce any significant performance problem.
The .LU() method performs LU factorization without pivoting and .PLU()
performs factorization with pivoting. In most cases factorization with
pivoting is prefered over factorization without pivoting.
The PLU object stores the factorization in the properties .P, .L and .U
with .P beeing a permutation matrix object and .L and .U beeing normal
matrices.
Per definition .L is a lower triangular matrix with all ones on the main
diagonal and .U is an upper triangular matrix.
The PLU.solve(Y) method solves a linear system using the factorization:
// solve A*X = Y for X:
var A = NumJS.MAT(n, n, [ ... ]);
var Y = NumJS.MAT(n, k, [ ... ]);
var X = A.PLU().solve(Y);
This is actually identical to using the NumJS.SOLVE() operator:
// solve A*X = Y for X:
var X = NumJS.SOLVE(A, Y);
The PLU.det() method calculates the matrix determinant using the
factorization (product along the diagonal of U times sign of P). The
NumJS.DET(M) operator is using PLU.det() behind the scenes to
calculate a matrix determinant.