From f8cf31796bf5665ebff08ef8b48964ad619a9e98 Mon Sep 17 00:00:00 2001 From: Anup Omkar Date: Mon, 2 Oct 2023 22:42:17 +0530 Subject: [PATCH 1/8] feat: adding matrix rank algorithm --- .../com/thealgorithms/maths/MatrixRank.java | 62 +++++++++++++++++++ .../thealgorithms/maths/MatrixRankTest.java | 35 +++++++++++ 2 files changed, 97 insertions(+) create mode 100644 src/main/java/com/thealgorithms/maths/MatrixRank.java create mode 100644 src/test/java/com/thealgorithms/maths/MatrixRankTest.java diff --git a/src/main/java/com/thealgorithms/maths/MatrixRank.java b/src/main/java/com/thealgorithms/maths/MatrixRank.java new file mode 100644 index 000000000000..bb7294cea1fe --- /dev/null +++ b/src/main/java/com/thealgorithms/maths/MatrixRank.java @@ -0,0 +1,62 @@ +package com.thealgorithms.maths; + +/** + * + * + *

Rank of Matrix

+ * + *

+ * The rank of a matrix is the largest number of linearly independent rows/columns of the matrix. + * The rank is not only defined for square matrices. + *

+ * + *

+ * The rank of a matrix can also be defined as the largest order of any non-zero minor in the matrix. + *

+ * + *

Read more about rank of matrix here

+ * + * @author Anup Omkar + */ +public class MatrixRank { + + // Small value to handle precision issues with floating point numbers + private final static double EPSILON = 1e-10; + + /** + * Returns rank of matrix + * + * @param matrix matrix whose rank is to be found + * @return rank of matrix + */ + public static int computeRank(double[][] matrix) { + int numRows = matrix.length; + int numColumns = matrix[0].length; + int rank = 0; + + boolean[] rowMarked = new boolean[numRows]; + for(int colIndex = 0; colIndex < numColumns; ++colIndex) { + int pivotRow; + for(pivotRow = 0; pivotRow < numRows; ++pivotRow) { + if(!rowMarked[pivotRow] && Math.abs(matrix[pivotRow][colIndex]) > EPSILON) { + break; + } + } + if(pivotRow != numRows) { + ++rank; + rowMarked[pivotRow] = true; + for(int nextCol = colIndex + 1; nextCol < numColumns; ++nextCol) { + matrix[pivotRow][nextCol] /= matrix[pivotRow][colIndex]; + } + for(int otherRow = 0; otherRow < numRows; ++otherRow) { + if(otherRow != pivotRow && Math.abs(matrix[otherRow][colIndex]) > EPSILON) { + for(int col2 = colIndex + 1; col2 < numColumns; ++col2) { + matrix[otherRow][col2] -= matrix[pivotRow][col2] * matrix[otherRow][colIndex]; + } + } + } + } + } + return rank; + } +} diff --git a/src/test/java/com/thealgorithms/maths/MatrixRankTest.java b/src/test/java/com/thealgorithms/maths/MatrixRankTest.java new file mode 100644 index 000000000000..4a7f1bea4e69 --- /dev/null +++ b/src/test/java/com/thealgorithms/maths/MatrixRankTest.java @@ -0,0 +1,35 @@ +package com.thealgorithms.maths; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import org.junit.jupiter.api.Test; + +class MatrixRankTest { + + @Test + void computeRank() { + + double[][] identityMatrix = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; + assertEquals(3, MatrixRank.computeRank(identityMatrix)); + + double[][] zeroMatrix = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; + assertEquals(0, MatrixRank.computeRank(zeroMatrix)); + + double[][] square1x1Matrix = {{1}}; + assertEquals(1, MatrixRank.computeRank(square1x1Matrix)); + + double[][] square2x2Matrix = {{1, 2}, {3, 4}}; + assertEquals(2, MatrixRank.computeRank(square2x2Matrix)); + + double[][] square3x3Matrix = {{3, -1, 2}, {-3, 1, 2}, {-6, 2, 4}}; + assertEquals(2, MatrixRank.computeRank(square3x3Matrix)); + + double[][] square4x4Matrix = {{2, 3, 0, 1}, {1, 0, 1, 2}, {-1, 1, 1, -2}, {1, 5, 3, -1}}; + assertEquals(3, MatrixRank.computeRank(square4x4Matrix)); + + double[][] rectangular2X3Matrix = {{1, 2, 3}, {3, 6, 9}}; + assertEquals(1, MatrixRank.computeRank(rectangular2X3Matrix)); + + double[][] rectangular3X4Matrix = {{0.25, 0.5, 0.75, 2}, {1.5, 3, 4.5, 6}, {1, 2, 3, 4}}; + assertEquals(2, MatrixRank.computeRank(rectangular3X4Matrix)); + } +} From 0f17227b964c61140bd5557c0f12ffcd6e44473e Mon Sep 17 00:00:00 2001 From: Anup Omkar Date: Mon, 2 Oct 2023 23:03:04 +0530 Subject: [PATCH 2/8] fix: formatting --- .../java/com/thealgorithms/maths/MatrixRank.java | 16 ++++++++-------- .../com/thealgorithms/maths/MatrixRankTest.java | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/thealgorithms/maths/MatrixRank.java b/src/main/java/com/thealgorithms/maths/MatrixRank.java index bb7294cea1fe..5208ef0dd10d 100644 --- a/src/main/java/com/thealgorithms/maths/MatrixRank.java +++ b/src/main/java/com/thealgorithms/maths/MatrixRank.java @@ -35,22 +35,22 @@ public static int computeRank(double[][] matrix) { int rank = 0; boolean[] rowMarked = new boolean[numRows]; - for(int colIndex = 0; colIndex < numColumns; ++colIndex) { + for (int colIndex = 0; colIndex < numColumns; ++colIndex) { int pivotRow; - for(pivotRow = 0; pivotRow < numRows; ++pivotRow) { - if(!rowMarked[pivotRow] && Math.abs(matrix[pivotRow][colIndex]) > EPSILON) { + for (pivotRow = 0; pivotRow < numRows; ++pivotRow) { + if (!rowMarked[pivotRow] && Math.abs(matrix[pivotRow][colIndex]) > EPSILON) { break; } } - if(pivotRow != numRows) { + if (pivotRow != numRows) { ++rank; rowMarked[pivotRow] = true; - for(int nextCol = colIndex + 1; nextCol < numColumns; ++nextCol) { + for (int nextCol = colIndex + 1; nextCol < numColumns; ++nextCol) { matrix[pivotRow][nextCol] /= matrix[pivotRow][colIndex]; } - for(int otherRow = 0; otherRow < numRows; ++otherRow) { - if(otherRow != pivotRow && Math.abs(matrix[otherRow][colIndex]) > EPSILON) { - for(int col2 = colIndex + 1; col2 < numColumns; ++col2) { + for (int otherRow = 0; otherRow < numRows; ++otherRow) { + if (otherRow != pivotRow && Math.abs(matrix[otherRow][colIndex]) > EPSILON) { + for (int col2 = colIndex + 1; col2 < numColumns; ++col2) { matrix[otherRow][col2] -= matrix[pivotRow][col2] * matrix[otherRow][colIndex]; } } diff --git a/src/test/java/com/thealgorithms/maths/MatrixRankTest.java b/src/test/java/com/thealgorithms/maths/MatrixRankTest.java index 4a7f1bea4e69..d02966ba370c 100644 --- a/src/test/java/com/thealgorithms/maths/MatrixRankTest.java +++ b/src/test/java/com/thealgorithms/maths/MatrixRankTest.java @@ -1,6 +1,7 @@ package com.thealgorithms.maths; import static org.junit.jupiter.api.Assertions.assertEquals; + import org.junit.jupiter.api.Test; class MatrixRankTest { From 32608b0e320271cf1bef36dce1f4ead983ece58b Mon Sep 17 00:00:00 2001 From: Anup Omkar Date: Thu, 12 Oct 2023 21:43:09 +0530 Subject: [PATCH 3/8] fix: adding comments, refactor and handling edge cases --- .../com/thealgorithms/maths/MatrixRank.java | 127 +++++++++++++----- .../thealgorithms/maths/MatrixRankTest.java | 43 +++--- 2 files changed, 112 insertions(+), 58 deletions(-) diff --git a/src/main/java/com/thealgorithms/maths/MatrixRank.java b/src/main/java/com/thealgorithms/maths/MatrixRank.java index 5208ef0dd10d..988941ff7191 100644 --- a/src/main/java/com/thealgorithms/maths/MatrixRank.java +++ b/src/main/java/com/thealgorithms/maths/MatrixRank.java @@ -1,33 +1,27 @@ package com.thealgorithms.maths; /** - * - * - *

Rank of Matrix

- * - *

- * The rank of a matrix is the largest number of linearly independent rows/columns of the matrix. - * The rank is not only defined for square matrices. - *

- * - *

- * The rank of a matrix can also be defined as the largest order of any non-zero minor in the matrix. - *

- * - *

Read more about rank of matrix here

+ * This class provides a method to compute the rank of a matrix. + * In linear algebra, the rank of a matrix is the maximum number of linearly independent rows or columns in the matrix. + * For example, consider the following 3x3 matrix: + * 1 2 3 + * 2 4 6 + * 3 6 9 + * Despite having 3 rows and 3 columns, this matrix only has a rank of 1 because all rows (and columns) are multiples of each other. + * It's a fundamental concept that gives key insights into the structure of the matrix. + * It's important to note that the rank is not only defined for square matrices but for any m x n matrix. * * @author Anup Omkar */ public class MatrixRank { - // Small value to handle precision issues with floating point numbers private final static double EPSILON = 1e-10; /** - * Returns rank of matrix + * @brief Computes the rank of the input matrix * - * @param matrix matrix whose rank is to be found - * @return rank of matrix + * @param matrix The input matrix + * @return The rank of the input matrix */ public static int computeRank(double[][] matrix) { int numRows = matrix.length; @@ -35,28 +29,93 @@ public static int computeRank(double[][] matrix) { int rank = 0; boolean[] rowMarked = new boolean[numRows]; + if (isJaggedMatrix(matrix)) { + throw new IllegalArgumentException("The input matrix is a jagged matrix"); + } for (int colIndex = 0; colIndex < numColumns; ++colIndex) { - int pivotRow; - for (pivotRow = 0; pivotRow < numRows; ++pivotRow) { - if (!rowMarked[pivotRow] && Math.abs(matrix[pivotRow][colIndex]) > EPSILON) { - break; - } - } + int pivotRow = findPivotRow(matrix, rowMarked, colIndex); if (pivotRow != numRows) { ++rank; rowMarked[pivotRow] = true; - for (int nextCol = colIndex + 1; nextCol < numColumns; ++nextCol) { - matrix[pivotRow][nextCol] /= matrix[pivotRow][colIndex]; - } - for (int otherRow = 0; otherRow < numRows; ++otherRow) { - if (otherRow != pivotRow && Math.abs(matrix[otherRow][colIndex]) > EPSILON) { - for (int col2 = colIndex + 1; col2 < numColumns; ++col2) { - matrix[otherRow][col2] -= matrix[pivotRow][col2] * matrix[otherRow][colIndex]; - } - } - } + normalizePivotRow(matrix, pivotRow, colIndex); + eliminateRows(matrix, pivotRow, colIndex); } } return rank; } + + /** + * @brief Checks if the input matrix is a jagged matrix. + * Jaggged matrix is a matrix where the number of columns in each row is not the same. + * + * @param matrix The input matrix + * @return True if the input matrix is a jagged matrix, false otherwise + */ + private static boolean isJaggedMatrix(double[][] matrix) { + int numColumns = matrix[0].length; + for (double[] row : matrix) { + if (row.length != numColumns) { + return true; + } + } + return false; + } + + /** + * @brief The pivot row is the row in the matrix that is used to eliminate other rows and reduce the matrix to its row echelon form. + * The pivot row is selected as the first row (from top to bottom) where the value in the current column (the pivot column) is not zero. + * This row is then used to "eliminate" other rows, by subtracting multiples of the pivot row from them, so that all other entries in the pivot column become zero. + * This process is repeated for each column, each time selecting a new pivot row, until the matrix is in row echelon form. + * The number of pivot rows (rows with a leading entry, or pivot) then gives the rank of the matrix. + * + * @param matrix The input matrix + * @param rowMarked An array indicating which rows have been marked + * @param colIndex The column index + * @return The pivot row index, or the number of rows if no suitable pivot row was found + */ + private static int findPivotRow(double[][] matrix, boolean[] rowMarked, int colIndex) { + int numRows = matrix.length; + for (int pivotRow = 0; pivotRow < numRows; ++pivotRow) { + if (!rowMarked[pivotRow] && Math.abs(matrix[pivotRow][colIndex]) > EPSILON) { + return pivotRow; + } + } + return numRows; + } + + /** + * @brief This method divides all values in the pivot row by the value in the given column. + * This ensures that the pivot value itself will be 1, which simplifies further calculations. + * + * @param matrix The input matrix + * @param pivotRow The pivot row index + * @param colIndex The column index + */ + private static void normalizePivotRow(double[][] matrix, int pivotRow, int colIndex) { + int numColumns = matrix[0].length; + for (int nextCol = colIndex + 1; nextCol < numColumns; ++nextCol) { + matrix[pivotRow][nextCol] /= matrix[pivotRow][colIndex]; + } + } + + /** + * @brief This method subtracts multiples of the pivot row from all other rows, + * so that all values in the given column of other rows will be zero. + * This is a key step in reducing the matrix to row echelon form. + * + * @param matrix The input matrix + * @param pivotRow The pivot row index + * @param colIndex The column index + */ + private static void eliminateRows(double[][] matrix, int pivotRow, int colIndex) { + int numRows = matrix.length; + int numColumns = matrix[0].length; + for (int otherRow = 0; otherRow < numRows; ++otherRow) { + if (otherRow != pivotRow && Math.abs(matrix[otherRow][colIndex]) > EPSILON) { + for (int col2 = colIndex + 1; col2 < numColumns; ++col2) { + matrix[otherRow][col2] -= matrix[pivotRow][col2] * matrix[otherRow][colIndex]; + } + } + } + } } diff --git a/src/test/java/com/thealgorithms/maths/MatrixRankTest.java b/src/test/java/com/thealgorithms/maths/MatrixRankTest.java index d02966ba370c..9ee5ac5e3899 100644 --- a/src/test/java/com/thealgorithms/maths/MatrixRankTest.java +++ b/src/test/java/com/thealgorithms/maths/MatrixRankTest.java @@ -1,36 +1,31 @@ package com.thealgorithms.maths; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import java.util.stream.Stream; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; class MatrixRankTest { - @Test - void computeRank() { - - double[][] identityMatrix = {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}; - assertEquals(3, MatrixRank.computeRank(identityMatrix)); - - double[][] zeroMatrix = {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}; - assertEquals(0, MatrixRank.computeRank(zeroMatrix)); - - double[][] square1x1Matrix = {{1}}; - assertEquals(1, MatrixRank.computeRank(square1x1Matrix)); - - double[][] square2x2Matrix = {{1, 2}, {3, 4}}; - assertEquals(2, MatrixRank.computeRank(square2x2Matrix)); - - double[][] square3x3Matrix = {{3, -1, 2}, {-3, 1, 2}, {-6, 2, 4}}; - assertEquals(2, MatrixRank.computeRank(square3x3Matrix)); - - double[][] square4x4Matrix = {{2, 3, 0, 1}, {1, 0, 1, 2}, {-1, 1, 1, -2}, {1, 5, 3, -1}}; - assertEquals(3, MatrixRank.computeRank(square4x4Matrix)); + private static Stream inputStream() { + return Stream.of(Arguments.of(3, new double[][] {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}), Arguments.of(0, new double[][] {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}), Arguments.of(1, new double[][] {{1}}), Arguments.of(2, new double[][] {{1, 2}, {3, 4}}), + Arguments.of(2, new double[][] {{3, -1, 2}, {-3, 1, 2}, {-6, 2, 4}}), Arguments.of(3, new double[][] {{2, 3, 0, 1}, {1, 0, 1, 2}, {-1, 1, 1, -2}, {1, 5, 3, -1}}), Arguments.of(1, new double[][] {{1, 2, 3}, {3, 6, 9}}), + Arguments.of(2, new double[][] {{0.25, 0.5, 0.75, 2}, {1.5, 3, 4.5, 6}, {1, 2, 3, 4}})); + } - double[][] rectangular2X3Matrix = {{1, 2, 3}, {3, 6, 9}}; - assertEquals(1, MatrixRank.computeRank(rectangular2X3Matrix)); + @ParameterizedTest + @MethodSource("inputStream") + void computeRankTests(int expectedRank, double[][] matrix) { + assertEquals(expectedRank, MatrixRank.computeRank(matrix)); + } - double[][] rectangular3X4Matrix = {{0.25, 0.5, 0.75, 2}, {1.5, 3, 4.5, 6}, {1, 2, 3, 4}}; - assertEquals(2, MatrixRank.computeRank(rectangular3X4Matrix)); + @Test + void computeRankWithJaggedArray() { + double[][] jaggedArray = {{1, 2}, {10}, {100, 200, 300}}; + assertThrows(IllegalArgumentException.class, () -> MatrixRank.computeRank(jaggedArray)); } } From 8e67dc3f000ccc774f8ed683874e357b725d1c30 Mon Sep 17 00:00:00 2001 From: Anup Omkar Date: Thu, 12 Oct 2023 22:45:30 +0530 Subject: [PATCH 4/8] refactor: minor refactor --- src/main/java/com/thealgorithms/maths/MatrixRank.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/thealgorithms/maths/MatrixRank.java b/src/main/java/com/thealgorithms/maths/MatrixRank.java index 988941ff7191..48dac5955601 100644 --- a/src/main/java/com/thealgorithms/maths/MatrixRank.java +++ b/src/main/java/com/thealgorithms/maths/MatrixRank.java @@ -44,6 +44,10 @@ public static int computeRank(double[][] matrix) { return rank; } + private static boolean isZero(double value) { + return Math.abs(value) < EPSILON; + } + /** * @brief Checks if the input matrix is a jagged matrix. * Jaggged matrix is a matrix where the number of columns in each row is not the same. @@ -76,7 +80,7 @@ private static boolean isJaggedMatrix(double[][] matrix) { private static int findPivotRow(double[][] matrix, boolean[] rowMarked, int colIndex) { int numRows = matrix.length; for (int pivotRow = 0; pivotRow < numRows; ++pivotRow) { - if (!rowMarked[pivotRow] && Math.abs(matrix[pivotRow][colIndex]) > EPSILON) { + if (!rowMarked[pivotRow] && !isZero(matrix[pivotRow][colIndex])) { return pivotRow; } } @@ -111,7 +115,7 @@ private static void eliminateRows(double[][] matrix, int pivotRow, int colIndex) int numRows = matrix.length; int numColumns = matrix[0].length; for (int otherRow = 0; otherRow < numRows; ++otherRow) { - if (otherRow != pivotRow && Math.abs(matrix[otherRow][colIndex]) > EPSILON) { + if (otherRow != pivotRow && !isZero(matrix[otherRow][colIndex])) { for (int col2 = colIndex + 1; col2 < numColumns; ++col2) { matrix[otherRow][col2] -= matrix[pivotRow][col2] * matrix[otherRow][colIndex]; } From 269f7b64cbd2577359059fa412294a9d926380eb Mon Sep 17 00:00:00 2001 From: Anup Omkar Date: Tue, 17 Oct 2023 22:53:39 +0530 Subject: [PATCH 5/8] enhancement: check matrix validity --- .../com/thealgorithms/maths/MatrixRank.java | 41 ++++++++++++++++--- .../thealgorithms/maths/MatrixRankTest.java | 22 ++++++---- 2 files changed, 50 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/thealgorithms/maths/MatrixRank.java b/src/main/java/com/thealgorithms/maths/MatrixRank.java index 48dac5955601..3d0c12807ce4 100644 --- a/src/main/java/com/thealgorithms/maths/MatrixRank.java +++ b/src/main/java/com/thealgorithms/maths/MatrixRank.java @@ -13,7 +13,7 @@ * * @author Anup Omkar */ -public class MatrixRank { +public final class MatrixRank { private final static double EPSILON = 1e-10; @@ -24,21 +24,26 @@ public class MatrixRank { * @return The rank of the input matrix */ public static int computeRank(double[][] matrix) { + validateInputMatrix(matrix); + int numRows = matrix.length; int numColumns = matrix[0].length; int rank = 0; boolean[] rowMarked = new boolean[numRows]; - if (isJaggedMatrix(matrix)) { - throw new IllegalArgumentException("The input matrix is a jagged matrix"); + + double[][] matrixCopy = new double[numRows][numColumns]; + for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) { + System.arraycopy(matrix[rowIndex], 0, matrixCopy[rowIndex], 0, numColumns); } + for (int colIndex = 0; colIndex < numColumns; ++colIndex) { - int pivotRow = findPivotRow(matrix, rowMarked, colIndex); + int pivotRow = findPivotRow(matrixCopy, rowMarked, colIndex); if (pivotRow != numRows) { ++rank; rowMarked[pivotRow] = true; - normalizePivotRow(matrix, pivotRow, colIndex); - eliminateRows(matrix, pivotRow, colIndex); + normalizePivotRow(matrixCopy, pivotRow, colIndex); + eliminateRows(matrixCopy, pivotRow, colIndex); } } return rank; @@ -48,6 +53,30 @@ private static boolean isZero(double value) { return Math.abs(value) < EPSILON; } + private static void validateInputMatrix(double[][] matrix) { + if (matrix == null) { + throw new IllegalArgumentException("The input matrix cannot be null"); + } + if (matrix.length == 0) { + throw new IllegalArgumentException("The input matrix cannot be empty"); + } + if (!hasValidRows(matrix)) { + throw new IllegalArgumentException("The input matrix cannot have null or empty rows"); + } + if (isJaggedMatrix(matrix)) { + throw new IllegalArgumentException("The input matrix cannot be jagged"); + } + } + + private static boolean hasValidRows(double[][] matrix) { + for (double[] row : matrix) { + if (row == null || row.length == 0) { + return false; + } + } + return true; + } + /** * @brief Checks if the input matrix is a jagged matrix. * Jaggged matrix is a matrix where the number of columns in each row is not the same. diff --git a/src/test/java/com/thealgorithms/maths/MatrixRankTest.java b/src/test/java/com/thealgorithms/maths/MatrixRankTest.java index 9ee5ac5e3899..bf4d075fe819 100644 --- a/src/test/java/com/thealgorithms/maths/MatrixRankTest.java +++ b/src/test/java/com/thealgorithms/maths/MatrixRankTest.java @@ -4,28 +4,36 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import java.util.stream.Stream; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; class MatrixRankTest { - private static Stream inputStream() { + private static Stream validInputStream() { return Stream.of(Arguments.of(3, new double[][] {{1, 0, 0}, {0, 1, 0}, {0, 0, 1}}), Arguments.of(0, new double[][] {{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}), Arguments.of(1, new double[][] {{1}}), Arguments.of(2, new double[][] {{1, 2}, {3, 4}}), Arguments.of(2, new double[][] {{3, -1, 2}, {-3, 1, 2}, {-6, 2, 4}}), Arguments.of(3, new double[][] {{2, 3, 0, 1}, {1, 0, 1, 2}, {-1, 1, 1, -2}, {1, 5, 3, -1}}), Arguments.of(1, new double[][] {{1, 2, 3}, {3, 6, 9}}), Arguments.of(2, new double[][] {{0.25, 0.5, 0.75, 2}, {1.5, 3, 4.5, 6}, {1, 2, 3, 4}})); } + private static Stream invalidInputStream() { + return Stream.of(Arguments.of((Object) new double[][] {{1, 2}, {10}, {100, 200, 300}}), // jagged array + Arguments.of((Object) new double[][] {}), // empty matrix + Arguments.of((Object) new double[][] {{}, {}}), // empty row + Arguments.of((Object) null), // null matrix + Arguments.of((Object) new double[][] {{1, 2}, null}) // null row + ); + } + @ParameterizedTest - @MethodSource("inputStream") + @MethodSource("validInputStream") void computeRankTests(int expectedRank, double[][] matrix) { assertEquals(expectedRank, MatrixRank.computeRank(matrix)); } - @Test - void computeRankWithJaggedArray() { - double[][] jaggedArray = {{1, 2}, {10}, {100, 200, 300}}; - assertThrows(IllegalArgumentException.class, () -> MatrixRank.computeRank(jaggedArray)); + @ParameterizedTest + @MethodSource("invalidInputStream") + void computeRankWithInvalidMatrix(double[][] matrix) { + assertThrows(IllegalArgumentException.class, () -> MatrixRank.computeRank(matrix)); } } From b1b6f6dc0ddcd92c4e309b7fa2cb254817d60024 Mon Sep 17 00:00:00 2001 From: Anup Omkar Date: Wed, 18 Oct 2023 22:49:49 +0530 Subject: [PATCH 6/8] refactor: minor refactor and fixes --- .../com/thealgorithms/maths/MatrixRank.java | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/thealgorithms/maths/MatrixRank.java b/src/main/java/com/thealgorithms/maths/MatrixRank.java index 3d0c12807ce4..3249e80e6134 100644 --- a/src/main/java/com/thealgorithms/maths/MatrixRank.java +++ b/src/main/java/com/thealgorithms/maths/MatrixRank.java @@ -15,7 +15,10 @@ */ public final class MatrixRank { - private final static double EPSILON = 1e-10; + private MatrixRank() { + } + + private static final double EPSILON = 1e-10; /** * @brief Computes the rank of the input matrix @@ -32,10 +35,7 @@ public static int computeRank(double[][] matrix) { boolean[] rowMarked = new boolean[numRows]; - double[][] matrixCopy = new double[numRows][numColumns]; - for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) { - System.arraycopy(matrix[rowIndex], 0, matrixCopy[rowIndex], 0, numColumns); - } + double[][] matrixCopy = deepCopy(matrix); for (int colIndex = 0; colIndex < numColumns; ++colIndex) { int pivotRow = findPivotRow(matrixCopy, rowMarked, colIndex); @@ -53,6 +53,16 @@ private static boolean isZero(double value) { return Math.abs(value) < EPSILON; } + private static double[][] deepCopy(double[][] matrix) { + int numRows = matrix.length; + int numColumns = matrix[0].length; + double[][] matrixCopy = new double[numRows][numColumns]; + for (int rowIndex = 0; rowIndex < numRows; ++rowIndex) { + System.arraycopy(matrix[rowIndex], 0, matrixCopy[rowIndex], 0, numColumns); + } + return matrixCopy; + } + private static void validateInputMatrix(double[][] matrix) { if (matrix == null) { throw new IllegalArgumentException("The input matrix cannot be null"); From 4ed756e57ef5bc39b391f675f6357adcb76ecfc9 Mon Sep 17 00:00:00 2001 From: Andrii Siriak Date: Wed, 25 Oct 2023 10:16:59 +0300 Subject: [PATCH 7/8] Update src/main/java/com/thealgorithms/maths/MatrixRank.java --- src/main/java/com/thealgorithms/maths/MatrixRank.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/thealgorithms/maths/MatrixRank.java b/src/main/java/com/thealgorithms/maths/MatrixRank.java index 3249e80e6134..7a628b92dccb 100644 --- a/src/main/java/com/thealgorithms/maths/MatrixRank.java +++ b/src/main/java/com/thealgorithms/maths/MatrixRank.java @@ -89,7 +89,7 @@ private static boolean hasValidRows(double[][] matrix) { /** * @brief Checks if the input matrix is a jagged matrix. - * Jaggged matrix is a matrix where the number of columns in each row is not the same. + * Jagged matrix is a matrix where the number of columns in each row is not the same. * * @param matrix The input matrix * @return True if the input matrix is a jagged matrix, false otherwise From 34a185336b657e08d1784f2a86cb953c13301449 Mon Sep 17 00:00:00 2001 From: Anup Omkar Date: Wed, 25 Oct 2023 13:13:31 +0530 Subject: [PATCH 8/8] feat: add unit test to check if input matrix is not modified while calculating the rank --- src/test/java/com/thealgorithms/maths/MatrixRankTest.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/thealgorithms/maths/MatrixRankTest.java b/src/test/java/com/thealgorithms/maths/MatrixRankTest.java index bf4d075fe819..415b84ec43f8 100644 --- a/src/test/java/com/thealgorithms/maths/MatrixRankTest.java +++ b/src/test/java/com/thealgorithms/maths/MatrixRankTest.java @@ -3,6 +3,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import java.util.Arrays; import java.util.stream.Stream; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; @@ -28,7 +29,12 @@ private static Stream invalidInputStream() { @ParameterizedTest @MethodSource("validInputStream") void computeRankTests(int expectedRank, double[][] matrix) { - assertEquals(expectedRank, MatrixRank.computeRank(matrix)); + int originalHashCode = Arrays.deepHashCode(matrix); + int rank = MatrixRank.computeRank(matrix); + int newHashCode = Arrays.deepHashCode(matrix); + + assertEquals(expectedRank, rank); + assertEquals(originalHashCode, newHashCode); } @ParameterizedTest