-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathgeneral_linear_solver.py
More file actions
213 lines (186 loc) · 10.6 KB
/
Copy pathgeneral_linear_solver.py
File metadata and controls
213 lines (186 loc) · 10.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
def paqlu_decomposition_in_place(A):
"""
Compute an in-place PAQ = LU decomposition of a rectangular matrix A with
row and column pivoting. This is a stub: the function body is not
implemented here, but the docstring fully specifies the interface,
conventions, and expected behavior so an implementation can be written.
Purpose and convention
- For a given input matrix A (m-by-n), produce integer permutation vectors
P (length m) and Q (length n) and overwrite A in place so that
if A_original denotes the input before modification, then
P_matrix @ A_original @ Q_matrix = L @ U,
where P_matrix and Q_matrix are the permutation matrices associated
with the vectors P and Q (see "Permutation vectors" below), L is an
m-by-r lower trapezoidal matrix with unit diagonal in its leading r
rows (where r = numerical rank), and U is an r-by-n upper trapezoidal
matrix. The factors L and U are stored in the single array A as
described in "Storage convention" below.
Parameters
----------
A : numpy.ndarray, shape (m, n)
The input matrix to be decomposed. It will be modified in place to
contain the L and U factors. A must be a two-dimensional contiguous
numeric array (float or complex). The function does not allocate a
separate output copy for the factorization results.
Returns
-------
P : numpy.ndarray, shape (m,), dtype=int
Row-permutation vector. The permutation vector uses zero-based
indexing and satisfies
A_original[P, :] == P_matrix @ A_original
where P_matrix is the corresponding m-by-m permutation matrix.
In words, row i of the permuted matrix is row P[i] of the original.
Q : numpy.ndarray, shape (n,), dtype=int
Column-permutation vector. The permutation vector uses zero-based
indexing and satisfies
A_original[:, Q] == A_original @ Q_matrix
where Q_matrix is the corresponding n-by-n permutation matrix.
In words, column j of the permuted matrix is column Q[j] of the original.
A : numpy.ndarray, shape (m, n)
The same array object that was passed in, modified in place so that
it contains the L and U factors:
- U is stored in the upper triangle (including the diagonal) of A
in the first r rows used by the elimination.
- The strict lower triangle of A contains the multipliers (entries
of L below the unit diagonal). The unit diagonal of L is implicit
and not stored (i.e., L[i,i] = 1 for i < r).
The exact layout for rectangular shapes:
- If m >= n (tall or square): U is n-by-n stored in rows 0..n-1,
L is m-by-n with unit diagonal in its first n rows.
- If m < n (wide): U is m-by-n stored in rows 0..m-1,
L is m-by-m with unit diagonal in its first m rows.
The numerical rank r is determined during elimination by pivot
thresholding (see "Rank and tolerance" below).
Notes on algorithm
- The decomposition is computed by Gaussian elimination with:
* row pivoting to avoid division by small numbers (partial pivoting),
* column pivoting simulated via permutation vector Q so that pivot
columns are moved before non-pivot columns (this is useful for
identifying pivot columns and free variables).
- Column pivoting should be implemented so that pivot columns are
selected with a criterion analogous to partial pivoting on rows:
choose a column that contains a sufficiently large pivot in the
current working submatrix. This places "pivot columns" before
"non-pivot (free) columns" in the order given by Q.
- The routine must not allocate full dense permutation matrices P_matrix
or Q_matrix; only the integer permutation vectors P and Q are returned.
Rank and tolerance
- The algorithm must decide when a pivot is numerically zero. An
appropriate default is to compare |pivot| to tol = max(m,n) * eps * max_abs,
where eps is machine precision and max_abs is the maximum absolute
value in the current working submatrix. The implementation may accept a
user-specified tolerance parameter; if not, it must document the
default used.
- The integer r (numerical rank) is the number of successful pivots
performed; after r pivots the remaining columns are considered free.
Complexity
- Time: O(min(m,n) * m * n) in the dense case (standard Gaussian
elimination complexity with pivoting).
- Memory: O(1) extra beyond the input A and the two permutation vectors.
Stability and usage
- This routine is intended for exact-solve and nullspace computations on
moderately sized dense matrices. For very large matrices or ill-conditioned
problems, use a robust SVD-based method to compute nullspaces and
least-squares solutions.
- After calling this function, use the returned P, Q, and in-place A
to form solutions, compute nullspace basis vectors, or to apply
forward/back substitution.
Exceptions
- The function should raise a ValueError if A is not two-dimensional.
- The function should raise TypeError if A's dtype is not a supported
numeric type.
Example (conceptual)
- Suppose A0 is the original m-by-n array. After calling
P, Q, A = paqlu_decomposition_in_place(A0)
the client can interpret the factorization as:
(permute rows by P) and (permute columns by Q) to get L and U
i.e. A0[P, :][:, Q] == L @ U (modulo rounding).
"""
raise NotImplementedError("paqlu_decomposition_in_place is a stub; implement factorization here.")
def solve(A, b):
"""
Compute a parametric solution of the linear system A x = b in the form
x = N @ xfree + c,
where xfree is an arbitrary vector of free-variable parameters. This is
a stub: the function body is not implemented here, but the docstring
precisely specifies the inputs, outputs, conventions, and error behavior.
Given
- A: an m-by-n matrix,
- b: a length-m vector (or m-by-k matrix for multiple right-hand sides),
this function returns
- N: an n-by(f) matrix whose columns form a basis for the nullspace of A,
i.e., A @ N == 0 (up to numerical tolerance). Here f = n - r is
the number of free variables and r is the numerical rank of A.
- c: a length-n vector (or n-by-k matrix matching b's second dimension)
that is a particular solution of A x = b, i.e., A @ c == b (within
tolerance), provided the system is consistent.
Parameters
----------
A : numpy.ndarray, shape (m, n)
Coefficient matrix of the linear system. A may be modified in place
by the routine that computes an LU-like factorization; if the user
wishes to preserve A, they should pass a copy.
b : numpy.ndarray, shape (m,) or (m, k)
Right-hand side vector (or multiple right-hand sides). Entries must
be numeric and compatible with the dtype of A.
Returns
-------
N : numpy.ndarray, shape (n, f)
Basis for the nullspace (homogeneous solutions) of A. If f == 0 (full
column rank), N is an array with shape (n, 0). Columns of N are
linearly independent and span {x : A @ x == 0}.
c : numpy.ndarray, shape (n,) or (n, k)
A particular solution of A x = b. If multiple right-hand sides are
provided (b shape (m,k)), c has shape (n,k). If the system is
inconsistent (no solution), the function should raise a ValueError.
Algorithm outline (implementation guidance)
1. Compute a PAQ = LU decomposition of A using paqlu_decomposition_in_place
(or an equivalent routine) to identify pivot columns and the numerical
rank r. The function paqlu_decomposition_in_place should return the row
and column permutation vectors P, Q and modify A in place to store L and U.
2. Apply the same row permutations to b: b_permuted = b[P].
3. Solve L y = b_permuted by forward substitution for the first r rows.
4. Solve U z = y_prefix by back substitution for the pivot variables.
If the system is inconsistent (e.g., a zero row in U corresponds to a
nonzero entry in y), raise ValueError("inconsistent system").
5. Construct a particular solution in the permuted variable ordering:
x_perm = [z (pivot variables); 0 (free variables)]
Because column permutation Q was used, place pivot and free variables
into their original positions by applying the inverse permutation:
c = inverse_permute_columns(x_perm, Q)
6. Build a nullspace basis N by setting each free variable to 1 (one at a
time) and solving the triangular system for pivot variables (similar to
computing the reduced-column-echelon homogeneous solutions). Then map
back through inverse column permutation Q so that each basis vector is
expressed in the original variable ordering.
Numerical tolerances
- All comparisons to zero should use a tolerance based on machine
precision, matrix norms, and the scale of the problem (see docstring
of paqlu_decomposition_in_place for a recommended default).
Edge cases and exceptions
- If A has shape (0, n) or (m, 0) handle appropriately:
* m == 0: any b must be empty; then any x is a solution -> choose c = 0,
N = identity (n-by-n).
* n == 0: only possible if b == 0; otherwise inconsistent.
- If the system is inconsistent (no x satisfies A x = b within tolerance),
raise ValueError("inconsistent system: A x = b has no solution").
- If b has multiple right-hand sides, compute corresponding columns of c
and return an N that is common for all right-hand sides.
Complexity
- Dominated by the cost of the decomposition: O(min(m,n) * m * n) time,
plus O(m*n) for forward/back substitution steps.
Examples (conceptual)
- For a full-column-rank tall matrix (m >= n, rank = n), f = 0 and
N has shape (n, 0). The routine computes the unique solution c = A^{-1} b
via the LU factors.
- For an underdetermined system (n > m, rank = r < n), f = n - r > 0,
and the returned N provides a basis for the affine family of solutions.
Returns
-------
N, c : numpy.ndarray, numpy.ndarray
Notes
- This stub assumes an implementation will rely on paqlu_decomposition_in_place.
- The function should preserve shapes and dtypes consistently; if b is
real and A is real, return real N and c; if complex, return complex types.
"""
raise NotImplementedError("solve is a stub; implement parametric solver here.")