Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 81 additions & 56 deletions src/main/java/com/thealgorithms/matrix/LUDecomposition.java
Original file line number Diff line number Diff line change
@@ -1,88 +1,113 @@
package com.thealgorithms.matrix;

/**
* LU Decomposition algorithm
* --------------------------
* Decomposes a square matrix a into a product of two matrices:
* a = l * u
* where:
* - l is a lower triangular matrix with 1s on its diagonal
* - u is an upper triangular matrix
* LU Decomposition algorithm for square matrices.
* Decomposes a matrix A into L (lower triangular) and U (upper triangular)
* such that A = L * U
*
* Reference:
* https://en.wikipedia.org/wiki/lu_decomposition
* <p>Time Complexity: O(n^3)
* <p>Space Complexity: O(n^2)
*
* @author Raghu0703
* @see <a href="https://en.wikipedia.org/wiki/LU_decomposition">LU Decomposition</a>
*/
public final class LUDecomposition {

private LUDecomposition() {
}

/**
* A helper class to store both l and u matrices
* Performs LU decomposition on a square matrix using Doolittle's method.
*
* @param matrix The input square matrix
* @return A Result object containing L and U matrices
* @throws IllegalArgumentException if matrix is not square or singular
*/
public static class LU {
double[][] l;
double[][] u;
public static Result decompose(double[][] matrix) {
int n = matrix.length;

LU(double[][] l, double[][] u) {
this.l = l;
this.u = u;
// Validate input
if (n == 0) {
throw new IllegalArgumentException("Matrix cannot be empty");
}
for (double[] row : matrix) {
if (row.length != n) {
throw new IllegalArgumentException("Matrix must be square");
}
}
}

/**
* Performs LU Decomposition on a square matrix a
*
* @param a input square matrix
* @return LU object containing l and u matrices
*/
public static LU decompose(double[][] a) {
int n = a.length;
double[][] l = new double[n][n];
double[][] u = new double[n][n];

// Initialize L with identity matrix
for (int i = 0; i < n; i++) {
// upper triangular matrix
for (int k = i; k < n; k++) {
double sum = 0;
for (int j = 0; j < i; j++) {
sum += l[i][j] * u[j][k];
l[i][i] = 1.0;
}

// Perform LU decomposition using Doolittle's method
for (int j = 0; j < n; j++) {
// Calculate U matrix elements
for (int i = 0; i <= j; i++) {
double sum = 0.0;
for (int k = 0; k < i; k++) {
sum += l[i][k] * u[k][j];
}
u[i][k] = a[i][k] - sum;
u[i][j] = matrix[i][j] - sum;
}

// lower triangular matrix
for (int k = i; k < n; k++) {
if (i == k) {
l[i][i] = 1; // diagonal as 1
} else {
double sum = 0;
for (int j = 0; j < i; j++) {
sum += l[k][j] * u[j][i];
}
l[k][i] = (a[k][i] - sum) / u[i][i];
// Calculate L matrix elements
for (int i = j + 1; i < n; i++) {
double sum = 0.0;
for (int k = 0; k < j; k++) {
sum += l[i][k] * u[k][j];
}

if (Math.abs(u[j][j]) < 1e-10) {
throw new IllegalArgumentException(
"Matrix is singular or nearly singular"
);
}

l[i][j] = (matrix[i][j] - sum) / u[j][j];
}
}

return new LU(l, u);
return new Result(l, u);
}

/**
* Utility function to print a matrix
*
* @param m matrix to print
* Result class to hold L and U matrices from decomposition.
*/
public static void printMatrix(double[][] m) {
for (double[] row : m) {
System.out.print("[");
for (int j = 0; j < row.length; j++) {
System.out.printf("%7.3f", row[j]);
if (j < row.length - 1) {
System.out.print(", ");
}
}
System.out.println("]");
public static class Result {
private final double[][] lMatrix;
private final double[][] uMatrix;

/**
* Constructor for Result.
*
* @param l Lower triangular matrix
* @param u Upper triangular matrix
*/
public Result(double[][] l, double[][] u) {
this.lMatrix = l;
this.uMatrix = u;
}

/**
* Gets the lower triangular matrix.
*
* @return L matrix
*/
public double[][] getL() {
return lMatrix;
}

/**
* Gets the upper triangular matrix.
*
* @return U matrix
*/
public double[][] getU() {
return uMatrix;
}
}
}
88 changes: 64 additions & 24 deletions src/test/java/com/thealgorithms/matrix/LUDecompositionTest.java
Original file line number Diff line number Diff line change
@@ -1,40 +1,80 @@
package com.thealgorithms.matrix;

import static org.junit.jupiter.api.Assertions.assertArrayEquals;
/**
* LU Decomposition is a matrix factorization technique that decomposes a matrix A
* into the product of a lower triangular matrix L and an upper triangular matrix U.
* This is useful for solving systems of linear equations, computing determinants,
* and inverting matrices.
*
* @author Hardvan
*/
public final class LUDecomposition {
private LUDecomposition() {
}

import org.junit.jupiter.api.Test;
public static class Result {
private final double[][] l;
private final double[][] u;

public class LUDecompositionTest {
public Result(double[][] l, double[][] u) {
this.l = l;
this.u = u;
}

@Test
public void testLUDecomposition() {
double[][] a = {{4, 3}, {6, 3}};
public double[][] getL() {
return l;
}

// Perform LU decomposition
LUDecomposition.LU lu = LUDecomposition.decompose(a);
double[][] l = lu.l;
double[][] u = lu.u;
public double[][] getU() {
return u;
}
}

// Reconstruct a from l and u
double[][] reconstructed = multiplyMatrices(l, u);
/**
* Performs LU decomposition on the given matrix.
*
* @param matrix The input matrix to decompose
* @return Result object containing L and U matrices
* @throws IllegalArgumentException if matrix is not square, empty, or singular
*/
public static Result decompose(double[][] matrix) {
if (matrix == null || matrix.length == 0) {
throw new IllegalArgumentException("Matrix cannot be null or empty");
}

// Assert that reconstructed matrix matches original a
for (int i = 0; i < a.length; i++) {
assertArrayEquals(a[i], reconstructed[i], 1e-9);
int n = matrix.length;
if (matrix[0].length != n) {
throw new IllegalArgumentException("Matrix must be square");
}
}

// Helper method to multiply two matrices
private double[][] multiplyMatrices(double[][] a, double[][] b) {
int n = a.length;
double[][] c = new double[n][n];
double[][] l = new double[n][n];
double[][] u = new double[n][n];

for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int k = 0; k < n; k++) {
c[i][j] += a[i][k] * b[k][j];
l[i][i] = 1.0;

for (int j = i; j < n; j++) {
double sum = 0.0;
for (int k = 0; k < i; k++) {
sum += l[i][k] * u[k][j];
}
u[i][j] = matrix[i][j] - sum;
}

for (int j = i + 1; j < n; j++) {
double sum = 0.0;
for (int k = 0; k < i; k++) {
sum += l[j][k] * u[k][i];
}

if (Math.abs(u[i][i]) < 1e-10) {
throw new IllegalArgumentException("Matrix is singular or nearly singular");
}

l[j][i] = (matrix[j][i] - sum) / u[i][i];
}
}
return c;

return new Result(l, u);
}
}
Loading