import numpy as np
# =========================
# Input Handling for Matrix A Block
# =========================
# This block defines a function to handle user input for the number of variables and the coefficient matrix A.
# It prompts the user for n, reads the matrix rows, validates the input length, and returns A as a NumPy array.
# Exceptions are raised for invalid inputs to be handled in the calling code.
def take_A_input():
try:
n = int(input("dimension of matrix: "))
except ValueError:
raise ValueError("Invalid input: Number of variables must be an integer.")
print("\nEnter the coefficient matrix (A):")
A = []
for i in range(n):
try:
row = list(map(float, input(f"Row {i+1}: ").split()))
if len(row) != n:
raise ValueError(f"Row {i+1} must have exactly {n} elements.")
A.append(row)
except ValueError as e:
raise ValueError(f"Invalid input in row {i+1}: {e}")
return np.array(A, dtype=float), n # Return A and n for later use in dimensions.
# =========================
# Input Handling for Vector B Block
# =========================
# This block defines a function to handle user input for the constants vector B, given the dimension n.
# It prompts for each element of B, validates them as floats, and returns B as a NumPy array.
# Exceptions are raised for invalid inputs.
def take_B_input(n):
print("\nEnter the constants vector (B):")
B = []
for i in range(n):
try:
val = float(input(f"B[{i+1}]: "))
B.append(val)
except ValueError:
raise ValueError(f"Invalid input for B[{i+1}]: Must be a float.")
return np.array(B, dtype=float)
# =========================
# LU Decomposition Block
# =========================
# This block performs LU decomposition on matrix A without pivoting.
# It decomposes A into a lower triangular matrix L (with 1s on diagonal) and an upper triangular matrix U such that A = L * U.
# Raises an error if a zero pivot is encountered during the process.
def lu_decomposition(A):
n = len(A)
L = np.zeros((n, n)) # Initialize L as zero matrix; diagonals will be set to 1.
U = A.copy().astype(float) # Copy A to U to modify it in place without affecting original A.
try:
for i in range(n):
L[i, i] = 1 # Set diagonal of L to 1, as per standard LU decomposition.
for j in range(i+1, n):
if U[i, i] == 0:
raise ZeroDivisionError("Zero pivot encountered during LU decomposition") # Check for zero pivot to avoid division by zero.
factor = U[j, i] / U[i, i] # Compute the multiplier factor for elimination.
L[j, i] = factor # Store the factor in L below the diagonal.
U[j, i:] -= factor * U[i, i:] # Eliminate entries below pivot in U and update the row.
except Exception as e:
raise RuntimeError(f"Error during LU decomposition: {e}")
return L, U
# =========================
# LU Solve Block
# =========================
# This block solves the linear system Ax = B using LU decomposition.
# First, it performs forward substitution to solve Ly = B for y (assuming L diagonal is 1).
# Then, backward substitution to solve Ux = y for x.
# Returns the solution vector x.
def lu_solve(A, B):
try:
L, U = lu_decomposition(A) # Decompose A into L and U.
except Exception as e:
raise RuntimeError(f"Error in decomposition during solve: {e}")
n = len(B)
# Forward substitution: Solve Ly = B
y = np.zeros_like(B, dtype=float) # Initialize y vector.
try:
for i in range(n):
y[i] = B[i] - np.dot(L[i, :i], y[:i]) # Compute y[i] using previously solved y values.
except Exception as e:
raise RuntimeError(f"Error during forward substitution: {e}")
# Backward substitution: Solve Ux = y
x = np.zeros_like(y, dtype=float) # Initialize x vector.
try:
for i in range(n-1, -1, -1):
x[i] = (y[i] - np.dot(U[i, i+1:], x[i+1:])) / U[i, i] # Compute x[i] using previously solved x values; divide by pivot.
except ZeroDivisionError:
raise ZeroDivisionError("Division by zero during backward substitution")
except Exception as e:
raise RuntimeError(f"Error during backward substitution: {e}")
return x
# =========================
# Matrix Inverse Block
# =========================
# This block computes the inverse of matrix A using LU decomposition.
# It solves Ax = e_i for each standard basis vector e_i to find each column of the inverse.
# Returns the inverse matrix.
def inverse(A):
try:
n = len(A)
inv_A = np.zeros_like(A, dtype=float) # Initialize inverse matrix with zeros.
for i in range(n):
e = np.zeros(n) # Create standard basis vector e_i with 1 at position i.
e[i] = 1
inv_A[:, i] = lu_solve(A, e) # Solve for the i-th column of the inverse.
except Exception as e:
raise RuntimeError(f"Error during inverse calculation: {e}")
return inv_A
# =========================
# Determinant Calculation Block
# =========================
# This block computes the determinant of matrix A using LU decomposition.
# The determinant is the product of the diagonal elements of U (since det(A) = det(L) * det(U) and det(L) = 1).
def determinant(A):
try:
_, U = lu_decomposition(A) # Only need U for determinant.
det = np.prod(np.diag(U)) # Product of diagonal elements of U gives the determinant.
except Exception as e:
raise RuntimeError(f"Error during determinant calculation: {e}")
return det
# =========================
# Main Execution Block
# =========================
# This is the entry point of the program.
# It first handles input for A, performs LU decomposition, computes determinant and inverse, and prints them.
# Then, it prompts the user if they want to solve for roots; if yes, takes B input and solves Ax = B.
# All operations are wrapped in try-except to catch and display errors gracefully.
def main():
print("=== LU Decomposition Solver ===\n")
try:
A, n = take_A_input()
except Exception as e:
print("Input Error for A:", e)
return
print("\n--- LU Decomposition ---")
try:
L, U = lu_decomposition(A)
print("Lower Triangular Matrix L:\n", L)
print("Upper Triangular Matrix U:\n", U)
except Exception as e:
print("Error:", e)
print("\n--- Determinant of A ---")
try:
detA = determinant(A)
print(detA)
except Exception as e:
print("Error:", e)
print("\n--- Inverse of A ---")
try:
invA = inverse(A)
print(invA)
except Exception as e:
print("Error:", e)
# Prompt user for optional solving of roots
solve_roots = input("\nDo you want to know the roots (yes/no): ").strip().lower()
if solve_roots == 'yes':
try:
B = take_B_input(n)
except Exception as e:
print("Input Error for B:", e)
return
print("\n--- Solve using LU Decomposition ---")
try:
x_lu = lu_solve(A, B)
print("Solution:", x_lu)
except Exception as e:
print("Error:", e)
if __name__ == "__main__":
main()