225 lines
6.0 KiB
D
225 lines
6.0 KiB
D
import std.algorithm;
|
|
import std.array;
|
|
import std.exception;
|
|
import std.range;
|
|
import std.stdio;
|
|
|
|
public class Matrix {
|
|
private double[][] data;
|
|
private size_t rowCount;
|
|
private size_t colCount;
|
|
|
|
public this(size_t size)
|
|
in(size > 0, "Must have at least one element")
|
|
{
|
|
this(size, size);
|
|
}
|
|
|
|
public this(size_t rows, size_t cols)
|
|
in(rows > 0, "Must have at least one row")
|
|
in(cols > 0, "Must have at least one column")
|
|
{
|
|
rowCount = rows;
|
|
colCount = cols;
|
|
|
|
data = uninitializedArray!(double[][])(rows, cols);
|
|
foreach (ref row; data) {
|
|
row[] = 0.0;
|
|
}
|
|
}
|
|
|
|
public this(const double[][] source) {
|
|
enforce(source.length > 0, "Must have at least one row");
|
|
rowCount = source.length;
|
|
|
|
enforce(source[0].length > 0, "Must have at least one column");
|
|
colCount = source[0].length;
|
|
|
|
data = uninitializedArray!(double[][])(rowCount, colCount);
|
|
foreach (i; 0 .. rowCount) {
|
|
enforce(source[i].length == colCount, "All rows must have equal columns");
|
|
data[i] = source[i].dup;
|
|
}
|
|
}
|
|
|
|
public auto opIndex(size_t r, size_t c) const {
|
|
return data[r][c];
|
|
}
|
|
|
|
public auto opIndex(size_t r) const {
|
|
return data[r];
|
|
}
|
|
|
|
public auto opBinary(string op)(const Matrix rhs) const {
|
|
static if (op == "*") {
|
|
auto rc1 = rowCount;
|
|
auto cc1 = colCount;
|
|
auto rc2 = rhs.rowCount;
|
|
auto cc2 = rhs.colCount;
|
|
enforce(cc1 == rc2, "Cannot multiply if the first columns does not equal the second rows");
|
|
auto result = new Matrix(rc1, cc2);
|
|
foreach (i; 0 .. rc1) {
|
|
foreach (j; 0 .. cc2) {
|
|
foreach (k; 0 .. rc2) {
|
|
result[i, j] += this[i, k] * rhs[k, j];
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
} else {
|
|
assert(false, "Not implemented");
|
|
}
|
|
}
|
|
|
|
public void opIndexAssign(double value, size_t r, size_t c) {
|
|
data[r][c] = value;
|
|
}
|
|
|
|
public void opIndexAssign(const double[] value, size_t r) {
|
|
enforce(colCount == value.length, "Slice size must match column size");
|
|
data[r] = value.dup;
|
|
}
|
|
|
|
public void opIndexOpAssign(string op)(double value, size_t r, size_t c) {
|
|
mixin("data[r][c] " ~ op ~ "= value;");
|
|
}
|
|
|
|
public auto transpose() const {
|
|
auto rc = rowCount;
|
|
auto cc = colCount;
|
|
auto t = new Matrix(cc, rc);
|
|
foreach (i; 0 .. cc) {
|
|
foreach (j; 0 .. rc) {
|
|
t[i, j] = this[j, i];
|
|
}
|
|
}
|
|
return t;
|
|
}
|
|
|
|
public void toReducedRowEchelonForm() {
|
|
auto lead = 0;
|
|
auto rc = rowCount;
|
|
auto cc = colCount;
|
|
foreach (r; 0 .. rc) {
|
|
if (cc <= lead) {
|
|
return;
|
|
}
|
|
auto i = r;
|
|
|
|
while (this[i, lead] == 0.0) {
|
|
i++;
|
|
if (rc == i) {
|
|
i = r;
|
|
lead++;
|
|
if (cc == lead) {
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
auto temp = this[i];
|
|
this[i] = this[r];
|
|
this[r] = temp;
|
|
|
|
if (this[r, lead] != 0.0) {
|
|
auto div = this[r, lead];
|
|
foreach (j; 0 .. cc) {
|
|
this[r, j] = this[r, j] / div;
|
|
}
|
|
}
|
|
|
|
foreach (k; 0 .. rc) {
|
|
if (k != r) {
|
|
auto mult = this[k, lead];
|
|
foreach (j; 0 .. cc) {
|
|
this[k, j] -= this[r, j] * mult;
|
|
}
|
|
}
|
|
}
|
|
|
|
lead++;
|
|
}
|
|
}
|
|
|
|
public auto inverse() const {
|
|
enforce(rowCount == colCount, "Not a square matrix");
|
|
auto len = rowCount;
|
|
auto aug = new Matrix(len, 2 * len);
|
|
foreach (i; 0 .. len) {
|
|
foreach (j; 0 .. len) {
|
|
aug[i, j] = this[i, j];
|
|
}
|
|
// augment identity matrix to right
|
|
aug[i, i + len] = 1.0;
|
|
}
|
|
aug.toReducedRowEchelonForm;
|
|
auto inv = new Matrix(len);
|
|
// remove identify matrix to left
|
|
foreach (i; 0 .. len) {
|
|
foreach (j; len .. 2 * len) {
|
|
inv[i, j - len] = aug[i, j];
|
|
}
|
|
}
|
|
return inv;
|
|
}
|
|
|
|
void toString(scope void delegate(const(char)[]) sink) const {
|
|
import std.format;
|
|
auto fmt = FormatSpec!char("%s");
|
|
|
|
put(sink, "[");
|
|
foreach (i; 0 .. rowCount) {
|
|
if (i > 0) {
|
|
put(sink, " [");
|
|
} else {
|
|
put(sink, "[");
|
|
}
|
|
|
|
formatValue(sink, this[i, 0], fmt);
|
|
foreach (j; 1 .. colCount) {
|
|
put(sink, ", ");
|
|
formatValue(sink, this[i, j], fmt);
|
|
}
|
|
|
|
if (i + 1 < rowCount) {
|
|
put(sink, "]\n");
|
|
} else {
|
|
put(sink, "]");
|
|
}
|
|
}
|
|
put(sink, "]");
|
|
}
|
|
}
|
|
|
|
auto multipleRegression(double[] y, Matrix x) {
|
|
auto tm = new Matrix([y]);
|
|
auto cy = tm.transpose;
|
|
auto cx = x.transpose;
|
|
return ((x * cx).inverse * x * cy).transpose[0].dup;
|
|
}
|
|
|
|
void main() {
|
|
auto y = [1.0, 2.0, 3.0, 4.0, 5.0];
|
|
auto x = new Matrix([[2.0, 1.0, 3.0, 4.0, 5.0]]);
|
|
auto v = multipleRegression(y, x);
|
|
v.writeln;
|
|
|
|
y = [3.0, 4.0, 5.0];
|
|
x = new Matrix([
|
|
[1.0, 2.0, 1.0],
|
|
[1.0, 1.0, 2.0]
|
|
]);
|
|
v = multipleRegression(y, x);
|
|
v.writeln;
|
|
|
|
y = [52.21, 53.12, 54.48, 55.84, 57.20, 58.57, 59.93, 61.29, 63.11, 64.47, 66.28, 68.10, 69.92, 72.19, 74.46];
|
|
auto a = [1.47, 1.50, 1.52, 1.55, 1.57, 1.60, 1.63, 1.65, 1.68, 1.70, 1.73, 1.75, 1.78, 1.80, 1.83];
|
|
x = new Matrix([
|
|
repeat(1.0, a.length).array,
|
|
a,
|
|
a.map!"a * a".array
|
|
]);
|
|
v = multipleRegression(y, x);
|
|
v.writeln;
|
|
}
|