Skip to content

Commit 716be3d

Browse files
committed
Add Matrix module.
1 parent c949a70 commit 716be3d

File tree

2 files changed

+240
-0
lines changed

2 files changed

+240
-0
lines changed

index.html

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
<link rel="stylesheet" type="text/css" href="https://code.getmdl.io/1.3.0/material.indigo-blue.min.css"/>
88
<link rel="stylesheet" type="text/css" href="css/style.css"/>
99
<script defer src="https://code.getmdl.io/1.3.0/material.min.js"></script>
10+
<script async src="js/matrix.js"></script>
1011
</head>
1112
<body>
1213
<!-- Always shows a header, even in smaller screens. -->

js/matrix.js

+239
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
/**
2+
* Matrix -- represents a matrix of numerical values.
3+
*/
4+
function Matrix (h, w, zero) {
5+
h = h === undefined ? 1 : h;
6+
w = w === undefined ? 1 : w;
7+
zero = zero === undefined ? false : zero;
8+
9+
this.rows = h;
10+
this.cols = w;
11+
this.data = new Array();
12+
13+
for (var i = 0; i < h; ++i) {
14+
this.data.push(new Array(w));
15+
16+
for (var j = 0; (zero && j < this.data[i].length); ++j)
17+
this.data[i][j] = 0;
18+
}
19+
20+
this.set = function (row, col, value) {
21+
this._checkIndex(row,col);
22+
if (typeof value !== "number")
23+
throw new TypeError ("`value' must be a number");
24+
25+
this.data[row][col] = value;
26+
};
27+
this.setRow = function (row, rowValues) {
28+
this._checkRow(row);
29+
30+
if (rowValues.constructor !== Array)
31+
throw new TypeError ("`rowValues' must be an array of numbers");
32+
33+
if (rowValues.length !== this.cols)
34+
throw new RangeError("Row must be of length " + this.cols.toString());
35+
36+
this.data[row] = rowValues.slice(0);
37+
};
38+
this.setCol = function (col, colValues) {
39+
this._checkCol(col);
40+
41+
if (colValues.constructor !== Array)
42+
throw new TypeError("`colValues' must be an array of numbers.");
43+
44+
if (colValues.length !== this.rows)
45+
throw new RangeError("Column must be of length " + this.rows.toString());
46+
47+
for (var row = 0; row < this.rows; ++row)
48+
this.data[row][col] = colValues[row];
49+
};
50+
this.get = function (row, col) {
51+
this._checkIndex(row,col);
52+
return this.data[row][col];
53+
};
54+
this.getRow = function (row) {
55+
this._checkRow(row);
56+
return this.data[row];
57+
};
58+
this.getCol = function (col) {
59+
this._checkCol(col);
60+
var column = new Array ();
61+
for (var row = 0; row < this.rows; ++row) column.push(this.data[row][col]);
62+
return column;
63+
};
64+
this.print = function () {
65+
console.table(this.data);
66+
};
67+
this.equals = function (other) {
68+
if (other.rows !== this.rows || other.cols !== this.cols) return false;
69+
70+
for (var row = 0; row < this.rows; ++row) {
71+
for (var col = 0; col < this.cols; ++col) {
72+
if (this.get(row,col) !== other.get(row,col)) return false;
73+
}
74+
}
75+
76+
return true;
77+
};
78+
this.rref = function (augment) {
79+
if (augment && augment.rows !== this.rows)
80+
throw new RangeError ("Augment dimension mismatch");
81+
82+
var result = {
83+
"rref":Matrix.copy(this),
84+
"augment":(augment !== undefined) ? Matrix.copy(augment) : augment
85+
};
86+
87+
var diagLength = (result.rref.rows < result.rref.cols) ?
88+
result.rref.rows : result.rref.cols;
89+
var scaleFactor;
90+
91+
//Loop over diagonal entries
92+
for (var i = 0; i < diagLength; ++i) {
93+
//Find the leading one for this diagonal index
94+
var leadingOne = i;
95+
96+
while (leadingOne < diagLength && result.rref.get(leadingOne,i) === 0)
97+
++leadingOne;
98+
99+
//A leading one was found, continue with elimination
100+
if (leadingOne < result.rref.rows) {
101+
if (leadingOne !== i && leadingOne < result.rref.rows) {
102+
result.rref._swapRows(i,leadingOne);
103+
104+
if (augment) result.augment._swapRows(i,leadingOne);
105+
}
106+
107+
scaleFactor = (1./result.rref.get(i,i));
108+
result.rref._scaleRow(i, scaleFactor);
109+
if (augment) result.augment._scaleRow(i, scaleFactor);
110+
111+
for (var row = 0; row < result.rref.rows; ++row) {
112+
if (row === i) continue;
113+
114+
scaleFactor = -1*(result.rref.get(row,i));
115+
116+
if (scaleFactor !== 0) {
117+
result.rref._addRow(row, result.rref.getRow(i), scaleFactor);
118+
if (augment)
119+
result.augment._addRow(row, result.augment.getRow(i), scaleFactor);
120+
}
121+
}
122+
}
123+
}
124+
125+
return result;
126+
};
127+
this.transpose = function () {
128+
var transMatrx = new Matrix (this.cols, this.rows);
129+
130+
for (var row = 0; row < this.rows; ++row) {
131+
transMatrx.setCol(row, this.getRow());
132+
}
133+
134+
return transMatrx;
135+
};
136+
this.inverse = function () {
137+
if (this.rows !== this.cols) throw new RangeError("Matrix not square");
138+
139+
if (this.rows === 1 && this.cols === 1) return (1./this.get(0,0));
140+
else {
141+
var eye = Matrix.identity(this.rows);
142+
var rrefData = this.rref(eye);
143+
144+
if (rrefData.rref.equals(eye)) return rrefData.augment;
145+
else throw new Error ("Matrix not invertible");
146+
}
147+
};
148+
this.scale = function (scaleFactor) {
149+
var scaled = new Matrix (this.rows, this.cols);
150+
151+
for (var row = 0; row < this.rows; ++row) {
152+
for (var col = 0; col < this.cols; ++col)
153+
scaled.set(row, col, this.get(row, col) * scaleFactor);
154+
}
155+
};
156+
this.multiply = function (other) {
157+
if (other.cols === 1 && other.rows === 1) return this.scale(other.get(0,0));
158+
else if (this.cols !== other.rows)
159+
throw new RangeError ("Dimension mismatch");
160+
161+
var multiplied = new Matrix (this.rows, other.cols);
162+
163+
for (var row = 0; row < this.rows; ++row) {
164+
for (var col = 0; col < other.cols; ++col) {
165+
multiplied.set(row, col, Matrix.dot(this.getRow(row), other.getCol(col)));
166+
}
167+
}
168+
169+
return multiplied;
170+
};
171+
this._checkRow = function (row) {
172+
if (row < 0 || row >= this.rows)
173+
throw new RangeError("Row index out of range");
174+
};
175+
this._checkCol = function (col) {
176+
if (col < 0 || col >= this.cols)
177+
throw new RangeError("Column index out of range");
178+
};
179+
this._checkIndex = function (row, col) {
180+
this._checkRow(row);
181+
this._checkCol(col);
182+
}
183+
this._scaleRow = function (row, scaleFactor) {
184+
this._checkRow(row);
185+
for (var col = 0; col < this.cols; ++col)
186+
this.data[row][col] *= scaleFactor;
187+
};
188+
this._addRow = function (row, added, scaleFactor) {
189+
this._checkRow(row);
190+
if (added.length !== this.cols)
191+
throw new RangeError ("Added row must be of length " + cols.toString());
192+
193+
scaleFactor = scaleFactor === undefined ? 1 : scaleFactor;
194+
for (var col = 0; col < this.cols; ++col)
195+
this.data[row][col] += added[col] * scaleFactor;
196+
};
197+
this._swapRows = function (row1, row2) {
198+
this._checkRow(row1);
199+
this._checkRow(row2);
200+
201+
var tmp = this.getRow(row1).slice();
202+
this.setRow(row1,this.getRow(row2));
203+
this.setRow(row2,tmp);
204+
};
205+
};
206+
207+
Matrix.copy = function (matrix) {
208+
var clone = new Matrix (matrix.rows, matrix.cols);
209+
for (var row = 0; row < clone.rows; ++row)
210+
clone.setRow(row, matrix.getRow(row));
211+
return clone;
212+
};
213+
Matrix.colFromArray = function (vec) {
214+
colVec = new Matrix (vec.length, 1);
215+
colVec.setCol(0, vec);
216+
return colVec;
217+
};
218+
Matrix.rowFromArray = function (vec) {
219+
rowVec = new Matrix (1, vec.length);
220+
rowVec.setRow(0, vec);
221+
return rowVec;
222+
};
223+
Matrix.dot = function (vec1, vec2) {
224+
if (vec1.length !== vec2.length)
225+
throw new RangeError ("Input vectors of unequal lengths");
226+
227+
var result;
228+
229+
for (var i = 0; i < vec1.length; ++i) result += vec1[i] * vec2[i];
230+
231+
return result;
232+
};
233+
Matrix.identity = function (w) {
234+
var eye = new Matrix(w,w,true);
235+
236+
for (var i = 0; i < w; ++i) eye.set(i,i,1);
237+
238+
return eye;
239+
};

0 commit comments

Comments
 (0)