diff --git a/src/zutil/math/Matrix.java b/src/zutil/math/Matrix.java index d7a3f8a..9ec3e74 100755 --- a/src/zutil/math/Matrix.java +++ b/src/zutil/math/Matrix.java @@ -172,6 +172,40 @@ public class Matrix { * Vector **********************************************************************/ + /** + * Vector addition, every element in the first vector will be added + * with the corresponding element in the second vector. + * + * @return a new vector with subtracted elements + */ + public static double[] add(double[] vector1, double[] vector2){ + vectorPreCheck(vector1, vector2); + double[] result = new double[vector1.length]; + + for (int i=0; i < result.length; ++i) { + result[i] = vector1[i] + vector2[i]; + } + return result; + } + + /** + * Matrix Vector addition, every column in the matrix will be added + * with the vector. + * + * @return a new matrix with subtracted elements + */ + public static double[][] add(double[][] matrix, double[] vector){ + vectorPreCheck(matrix, vector); + double[][] result = new double[matrix.length][matrix[0].length]; + + for (int y=0; y < result.length; ++y) { + for (int x=0; x < result.length; ++x) { + result[y][x] = matrix[y][x] + vector[x]; + } + } + return result; + } + /** * Vector subtraction, every element in the first vector will be subtracted * with the corresponding element in the second vector. @@ -188,19 +222,40 @@ public class Matrix { return result; } + /** + * Matrix Vector subtraction, each column in the matrix will be subtracted + * with the vector. + * + * @return a new vector with subtracted elements + */ + public static double[][] subtract(double[][] matrix, double[] vector){ + vectorPreCheck(matrix, vector); + double[][] result = new double[matrix.length][matrix[0].length]; + + for (int y=0; y < result.length; ++y) { + for (int x=0; x < result.length; ++x) { + result[y][x] = matrix[y][x] - vector[x]; + } + } + + return result; + } + /** * Matrix Vector multiplication, each element column in the matrix will be * multiplied with the corresponding element row in the vector. * * @return a new vector with the result */ - public static double[] multiply(double[][] matrix, double[] vector){ + public static double[][] multiply(double[][] matrix, double[] vector){ vectorPreCheck(matrix, vector); - double[] result = new double[matrix.length]; + double[][] result = new double[matrix.length][1]; - for (int y=0; y < matrix.length; ++y) { - for (int x=0; x < matrix[y].length; ++x){ - result[y] += matrix[y][x] * vector[x]; + for (int y=0; y < result.length; ++y) { + for (int x=0; x transpose(theta) * x * */ - protected static double[] calculateHypotesis(double[][] x, double[] theta){ + protected static double[][] calculateHypotesis(double[][] x, double[] theta){ return Matrix.multiply(x, theta); } @@ -24,15 +24,36 @@ public class LinearRegression { * Linear Regresion cost method. *

* - * J(O) = 1 / (2 * m) * Σ { ( h(xi) - yi )^2 } + * J(O) = 1 / (2 * m) * Σ { ( h(Xi) - Yi )^2 } *
* m = learning data size (rows) * @return a number indicating the error rate */ protected static double calculateCost(double[][] x, double[] y, double[] theta){ - return 1 / (2 * x.length) * Matrix.sum( + return 1.0 / (2.0 * x.length) * Matrix.sum( Matrix.Elemental.pow( Matrix.subtract(calculateHypotesis(x, theta), y), 2)); } + + /** + * Gradient Descent algorithm + *

+ * + * Oj = Oj - α * (1 / m) * Σ { ( h(Xi) - Yi ) * Xij } + *
+ * + * @return the theta that was found to minimize the cost function + */ + public static double[] gradientAscent(double[][] x, double[] y, double[] theta, double alpha){ + double[] newTheta = new double[theta.length]; + double m = y.length; + double[][] hypotesisCache = Matrix.subtract(calculateHypotesis(x, theta), y); + + for (int j= 0; j < theta.length; j++) { + newTheta[j] = theta[j] - alpha * (1.0/m) * Matrix.sum(Matrix.add(hypotesisCache, Matrix.getColumn(x, j))); + } + + return newTheta; + } } diff --git a/test/zutil/math/MatrixTest.java b/test/zutil/math/MatrixTest.java index e979a7e..bea2330 100755 --- a/test/zutil/math/MatrixTest.java +++ b/test/zutil/math/MatrixTest.java @@ -70,14 +70,46 @@ public class MatrixTest { @Test - public void vectorMultiply(){ + public void vectorAddition(){ assertArrayEquals( - new double[]{8,14}, - Matrix.multiply(new double[][]{{2,3},{-4,9}}, new double[]{1,2}), + new double[]{3,5,-1,13}, + Matrix.add(new double[]{2,3,-4,9}, new double[]{1,2,3,4}), 0.0 ); } + @Test + public void vectorMatrixAddition(){ + assertArrayEquals( + new double[][]{{2,3,4,5},{2,3,4,5},{2,3,4,5},{2,3,4,5}}, + Matrix.add(new double[][]{{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4}}, new double[]{1,1,1,1}) + ); + } + + @Test + public void vectorSubtraction(){ + assertArrayEquals( + new double[]{1,1,-7,5}, + Matrix.subtract(new double[]{2,3,-4,9}, new double[]{1,2,3,4}), + 0.0 + ); + } + + @Test + public void vectorMatrixSubtraction(){ + assertArrayEquals( + new double[][]{{0,1,2,3},{0,1,2,3},{0,1,2,3},{0,1,2,3}}, + Matrix.subtract(new double[][]{{1,2,3,4},{1,2,3,4},{1,2,3,4},{1,2,3,4}}, new double[]{1,1,1,1}) + ); + } + + @Test + public void vectorMultiply(){ + assertArrayEquals( + new double[][]{{8},{14}}, + Matrix.multiply(new double[][]{{2,3},{-4,9}}, new double[]{1,2})); + } + @Test public void vectorDivision(){ assertArrayEquals( @@ -138,4 +170,13 @@ public class MatrixTest { new double[][]{{1,0,0,0},{0,1,0,0},{0,0,1,0},{0,0,0,1}}, Matrix.identity(4)); } + + @Test + public void getColumn(){ + assertArrayEquals( + new double[]{2,3,4,1}, + Matrix.getColumn(new double[][]{{1,2,3,4},{2,3,4,1},{3,4,1,2},{4,1,2,3}}, 1), + 0.0 + ); + } } \ No newline at end of file diff --git a/test/zutil/ml/LinearRegressionTest.java b/test/zutil/ml/LinearRegressionTest.java new file mode 100755 index 0000000..4ecb21f --- /dev/null +++ b/test/zutil/ml/LinearRegressionTest.java @@ -0,0 +1,44 @@ +package zutil.ml; + +import org.junit.Test; + +import static org.junit.Assert.*; + +/** + * Test cases are from the Machine Learning course on coursera. + * https://www.coursera.org/learn/machine-learning/discussions/all/threads/0SxufTSrEeWPACIACw4G5w + */ +public class LinearRegressionTest { + + @Test + public void calculateHypotesis() { + double[][] hypotesis = LinearRegression.calculateHypotesis( + /* x */ new double[][]{{1, 2}, {1, 3}, {1, 4}, {1, 5}}, + /* theta */ new double[]{0.1, 0.2} + ); + + assertArrayEquals(new double[][]{{0.5}, {0.7}, {0.9}, {1.1}}, hypotesis); + } + + @Test + public void calculateCost() { + double cost = LinearRegression.calculateCost( + /* x */ new double[][]{{1, 2}, {1, 3}, {1, 4}, {1, 5}}, + /* y */ new double[]{7, 6, 5, 4}, + /* theta */ new double[]{0.1, 0.2} + ); + + assertEquals(11.9450, cost, 0.0001); + } + + @Test + public void gradientAscent() { + double[] theta = LinearRegression.gradientAscent( + /* x */ new double[][]{{1, 5},{1, 2},{1, 4},{1, 5}}, + /* y */ new double[]{1, 6, 4, 2}, + /* theta */ new double[]{0, 0}, + /* alpha */0.01); + + assertArrayEquals(new double[]{0.032500, 0.107500}, theta, 0.000001); + } +} \ No newline at end of file