/// <summary> Create a transposed matrix </summary> public MyMatrix Transpose() { MyMatrix result = new MyMatrix(this.cols, this.rows); for (int row = 0; row < this.rows; row++) { for (int col = 0; col < this.cols; col++) { result[col, row] = this.data[row, col]; } } return(result); }
/// <summary> Iteratively solve the coefficients using Gauss-Newton method </summary> /// <remarks> http://en.wikipedia.org/wiki/Gauss%E2%80%93Newton_algorithm </remarks> public double[] Solve() { if (this.ys == null) { throw new InvalidOperationException("Not yet initialized"); } double lastSSE = double.MaxValue; double[] errors = new double[this.ys.Length]; // let C0 be the current coefficient guess, C1 be the better answer we are after // let E0 be the error using current guess // we have: // JacT * Jac * (C1 - C0) = JacT * E0 // MyMatrix jacobian = Jacobian(); MyMatrix jacobianT = jacobian.Transpose(); MyMatrix product = jacobianT * jacobian; SquareMatrix inverse = SquareMatrix.FromMatrix(product).Inverse(); for (int iteration = 0; iteration < GaussNewton.MaxIteration; iteration++) { this.sse = 0; for (int i = 0; i < this.ys.Length; i++) { double y = function(this.coefficients, this.xs[i]); errors[i] = this.ys[i] - y; sse += errors[i] * errors[i]; } if (lastSSE - sse < GaussNewton.ConvergeThreshold) { this.solved = true; return(this.coefficients); } double[] shift = inverse * (jacobianT * errors); for (int i = 0; i < this.coefficientCount; i++) { this.coefficients[i] += shift[i]; } lastSSE = sse; } throw new InvalidOperationException("No answer can be found"); }
/// <summary> Calculate a Jacobian matrix. </summary> /// <remarks> http://en.wikipedia.org/wiki/Jacobian_matrix_and_determinant </remarks> private MyMatrix Jacobian() { double[][] p = new double[this.coefficientCount][]; for (int i = 0; i < p.Length; i++) { p[i] = new double[this.coefficientCount]; p[i][i] = 1; } MyMatrix jacobian = new MyMatrix(this.ys.Length, this.coefficientCount); for (int i = 0; i < this.ys.Length; i++) { for (int j = 0; j < this.coefficientCount; j++) { jacobian[i, j] = function(p[j], this.xs[i]); } } return(jacobian); }
/// <summary> Multiply by another matrix. The width of left matrix should equal the height of right matrix </summary> public MyMatrix Multiply(MyMatrix m) { if (m == null || m.rows != this.cols) { throw new ArgumentException("Invalid matrix to multiply"); } MyMatrix result = new MyMatrix(this.rows, m.cols); int inner = this.cols; for (int row = 0; row < result.rows; row++) { for (int col = 0; col < result.cols; col++) { double sum = 0; for (int i = 0; i < inner; i++) { sum += this[row, i] * m[i, col]; } result[row, col] = sum; } } return(result); }