Solve Linear System
Problem Statement
Solve a system of linear equations Ax = b.
Background
A linear system Ax = b can be solved using:
- np.linalg.solve(A, b) - direct solution
- np.linalg.inv(A) @ b - via inverse (less efficient)
Example: 2x + y = 5, x + 3y = 7 Matrix form: [[2,1],[1,3]] @ [x,y] = [5,7]
Your Task
Write a function solve_system(A, b) that solves the linear system.
Output Format
Return a dictionary with:
- "solution": Solution vector x (rounded to 2 decimals, as list)
- "verification": A @ x (should equal b, rounded to 2 decimals, as list)
- "is_correct": True if A @ x ≈ b
Example:
A = [[2, 1], [1, 3]], b = [5, 7]
{'solution': [1.6, 1.8], 'verification': [5.0, 7.0], 'is_correct': True}2(1.6) + 1(1.8) = 5, 1(1.6) + 3(1.8) = 7
Constraints:
- A is square and invertible
- Round to 2 decimal places
More from NumPy Foundations
Background Knowledge
A linear system Ax=b represents n equations with n unknowns, where A is an n×n coefficient matrix, x is the unknown solution vector, and b is the right-hand side vector. Matrix-vector multiplication A@x computes the linear combination of columns of A weighted by components of x. For the system to have a unique solution, A must be square and invertible (non-singular, with non-zero determinant and full rank).
NumPy provides efficient tools for solving these systems. The function np.linalg.solve(A, b) uses LU decomposition (or similar direct methods) to compute x=A−1b stably and efficiently, avoiding explicit matrix inversion which is numerically unstable and O(n3) more costly. In contrast, np.linalg.inv(A) @ b computes the full inverse first, which is less recommended for large or ill-conditioned matrices. Verification checks if ∥A@x−b∥ is small (e.g., via tolerance), accounting for floating-point precision.
Algorithm/Approach
Use direct solver np.linalg.solve(A, b) for the primary solution, as it's optimized for dense matrices and handles conditioning better than inversion. Compute verification by forward-substituting the solution back: A@x, then compare element-wise to b within a tolerance (e.g., 10−10). Return results in rounded lists for the specified format, ensuring numerical stability by avoiding unnecessary operations like inversion.
Step-by-Step Strategy
- Validate inputs: Ensure A is square (A.shape==A.shape) and compatible with b (A.shape==b.shape); raise error if not.
- Solve the system: Call x = np.linalg.solve(A, b) to get the exact solution vector.
- Verify solution: Compute verification = A @ x and check if np.allclose(verification, b, atol=1e-10) for is_correct.
- Format output: Round x and verification to 2 decimals using np.round(..., 2).tolist(), then pack into dictionary {'solution':..., 'verification':..., 'is_correct':...}.
Common Pitfalls
- Non-square or singular matrices: np.linalg.solve raises LinAlgError if A is singular (detA=0) or mismatched shapes—handle with try-except or checks.
- Ill-conditioned matrices: Near-singular A (high condition number κ(A)=∥A∥⋅∥A−1∥) amplifies errors; use np.linalg.cond(A) to diagnose, but solver is robust for most cases.
- Rounding precision: Use np.round after computation, not before, to avoid distorting verification; lists must be flat (not arrays).
- Floating-point tolerance: Hardcode small atol for is_correct; exact equality fails due to precision (e.g., 5.0000000001 != 5.0).
- Import oversight: Ensure import numpy as np; forget @ operator (use np.dot as fallback).
Time & Space Complexity
- Time: O(n3) dominant from np.linalg.solve (LU decomposition) and A@x (O(n2)); negligible for verification/formatting. Suitable for n≤103.
- Space: O(n2) for A, O(n) for vectors; temporary O(n2) in solver—efficient for dense matrices.