/* gausol_triang_reduce.h - Gaussian triangulation with full pivoting. */ /* Last edited on 2024-11-30 12:32:08 by stolfi */ #ifndef gausol_triang_reduce_H #define gausol_triang_reduce_H #include #include void gausol_triang_reduce ( uint32_t m, uint32_t prow[], uint32_t n, uint32_t pcol[], double A[], uint32_t p, double B[], double tiny, double *det_P, uint32_t *rank_P ); /* Modifies the {m×n} matrix {A} and the {m×p} matrix {B} of a linear system {A X = B} according the Gaussian elimination method, leaving {A} upper triangular modulo a permutation of the rows and columns. Specifically, the matrices {A} abd {B} should be stored by rows. That is, {A} and {B} should have {m*n} and {m*p} elements, respectively, {A[i,j]} should be actually {A[i*n + j]}, and {B[i,k]} should be {B[i*p + k]}. Effect of the procedure: The procedure modifies {A} and {B} by a sequence of row operations {A <- R A} and {B <- R B} where {R} is an {m×m} matrix with unit determinant. During these row operations, any element whose absolute value was or becomes smaller than {tiny} in absolute value gets set to zero. If {tiny} is zero or negative, this cleanup is supressed. The procedure also returns in {*rank_P} an integer {rank} in {0..min(m,n)} that the max number of linearly independent rows (or columns) of {A}. The procedure also fills tables {prow[0..m-1]} and {pcol[0..n-1]} with permutations of the indices {0..m-1} and {0..n-1}, respectively. The tables {prow} and {pcol} define permuted versions {P} of {A} and {Q} of {B}, as explained in {gausol_solve.h}. On output of the procedure, (1) {P[i,i]} is nonzero for {i} in {0..rank-1}. (2) {P[k,j]} is zero for {j} in {0..rank-1} and {k} in {j+1..m-1}. These conditions say that {P00} is upper triangular with nonzero diagonal, and that {P10} is all zeros. Moreover, condition (3) must hold, which is, either {rank == min(m,n)}, in which case {P11} is empty, or one of the following is (3.00) The first element {P11[0,0] = P[rank,rank]} is zero. (3.10) If {prow} is not {NULL}, the first column of {P11} (namely {P[i,rank]} for {i} in {rank..m-1}) is zero. (3.01) If {pcol} is not {NULL}, the first row of {P11} (namely {P[rank,j]} for {j} in {rank..n-1}) is zero.. (3.11) if both {pcol} and {prow} are non-{NULL}, then the whole of {P11} (namely {P[i,j]} for {i} in {rank..m-1} and {j} in {rank..n-1}) is zero. Note that (3.10) and (3.01) imply (3.00), and (3.11) implies all three. In cases (3.10), (3.01), and (3.11), the rank of {A} was {rank} (apart from roundoff errors). If only (3.00) holds (that is, {prow} and {pcol} were both {NULL}), the rank of {A} was at least {rank}. Also, if {*det_P} is not {NULL}, the procedure stores into {*det_P} the determinant of the submatrix {P00} after triangulation; that is, the product of its diagonal elements, with sign reversed if the two permutations imply an odd total number of pair swaps. This {det} will always be a nonzero number. If the {A} matrix is square and {rank} is equal to {m}, that is the determinant of the original {A}, apart from roundoff. If {A} is square but the returned {rank} is less than {m}, then the determinant of the original {A} is apparently zero; if the matrix is not square, its determinant is undefined. IN both of tehse cases {det} will not be the determinant of the original {A}. Uses for {prow=pcol=NULL}: This combination can be used when it is known that the coefficient matrix {A} has a strongly dominating diagonal, so that pivoting will not be needed, not even the simple version. It could be used, for example, to refine the inverse of a matrix {A} after computing a first approximation {M0}. For that one may compute the inverse {D1} of {C1 = A M0} and set the new inverse as {M1 = M0 D1}. The matrix {C1} should be very close to the identity so no pivoting should be necessary. Uses when exactly one of {prow} and {pcol} is {NULL}: A typical application of this case is finding the rank of a matrix {A}. This is the same as that of {P}, hence it is the returned result {rank}. Another typical application for this case is to compute the determinant of a square {m×m} matrix {A}. If the returned {rank} is less than {m} then the determinant is zero, otherwise it is {sgn} times the determinant of {P00}, and the latter is the product of {P[i,i]} for {i} in {0..rank-1}. Another typical application is solving the {p} linear equation systems {A X = B} where {X} is {n×p} {p}. This equation is equivalent to {P Y = Q} where {Y[j,k] = X[pcol[j],k]} for {j} in {0..n-1} and {k} in {0..p-1}. This in turn is equivalent to {P00 Y0 + P01 Y1 = Q0} and {P11 Y1 = Q1} where {Y0} and {Y1} are the first {rank} and last {n-rank} rows of {Y}, respectively. But if {rank