Test the rank of the Jacobian by Gram-Schmidt, instead of by a

questionable tolerance on the pivot while row reducing.

[git-p4: depot-paths = "//depot/solvespace/": change = 1818]
This commit is contained in:
Jonathan Westhues 2008-07-02 00:18:25 -08:00
parent 48c5018613
commit 1bc68779d9
2 changed files with 39 additions and 58 deletions

View File

@ -195,8 +195,8 @@ public:
} B; } B;
} mat; } mat;
bool Tol(double v); static const double RANK_MAG_TOLERANCE;
int GaussJordan(void); int CalculateRank(void);
static bool SolveLinearSystem(double X[], double A[][MAX_UNKNOWNS], static bool SolveLinearSystem(double X[], double A[][MAX_UNKNOWNS],
double B[], int N); double B[], int N);
bool SolveLeastSquares(void); bool SolveLeastSquares(void);

View File

@ -1,5 +1,7 @@
#include "solvespace.h" #include "solvespace.h"
const double System::RANK_MAG_TOLERANCE = 1e-4;
void System::WriteJacobian(int tag) { void System::WriteJacobian(int tag) {
int a, i, j; int a, i, j;
@ -145,67 +147,46 @@ void System::SolveBySubstitution(void) {
} }
} }
bool System::Tol(double v) { //-----------------------------------------------------------------------------
return (fabs(v) < 0.0001); // Calculate the rank of the Jacobian matrix, by Gram-Schimdt orthogonalization
} // in place. A row (~equation) is considered to be all zeros if its magnitude
// is less than the tolerance RANK_MAG_TOLERANCE.
//-----------------------------------------------------------------------------
int System::CalculateRank(void) {
// Actually work with magnitudes squared, not the magnitudes
double rowMag[MAX_UNKNOWNS];
ZERO(&rowMag);
double tol = RANK_MAG_TOLERANCE*RANK_MAG_TOLERANCE;
int System::GaussJordan(void) { int i, iprev, j;
int i, j;
// Now eliminate.
i = 0;
int rank = 0; int rank = 0;
for(j = 0; j < mat.n; j++) {
// First, seek a pivot in our column. for(i = 0; i < mat.m; i++) {
int ip, imax; // Subtract off this row's component in the direction of any
double max = 0; // previous rows
for(ip = i; ip < mat.m; ip++) { for(iprev = 0; iprev < i; iprev++) {
double v = fabs(mat.A.num[ip][j]); if(rowMag[iprev] <= tol) continue; // ignore zero rows
if(v > max) {
imax = ip; double dot = 0;
max = v; for(j = 0; j < mat.n; j++) {
dot += (mat.A.num[iprev][j]) * (mat.A.num[i][j]);
}
for(j = 0; j < mat.n; j++) {
mat.A.num[i][j] -= (dot/rowMag[iprev])*mat.A.num[iprev][j];
} }
} }
if(!Tol(max)) { // Our row is now normal to all previous rows; calculate the
// There's a usable pivot in this column. Swap it in: // magnitude of what's left
int js; double mag = 0;
for(js = j; js < mat.n; js++) { for(j = 0; j < mat.n; j++) {
double temp; mag += (mat.A.num[i][j]) * (mat.A.num[i][j]);
temp = mat.A.num[imax][js]; }
mat.A.num[imax][js] = mat.A.num[i][js]; if(mag > tol) {
mat.A.num[i][js] = temp;
}
// Get a 1 as the leading entry in the row we're working on.
double v = mat.A.num[i][j];
for(js = 0; js < mat.n; js++) {
mat.A.num[i][js] /= v;
}
// Eliminate this column from rows except this one.
int is;
for(is = 0; is < mat.m; is++) {
if(is == i) continue;
// We're trying to drive A[is][j] to zero. We know
// that A[i][j] is 1, so we want to subtract off
// A[is][j] times our present row.
double v = mat.A.num[is][j];
for(js = 0; js < mat.n; js++) {
mat.A.num[is][js] -= v*mat.A.num[i][js];
}
mat.A.num[is][j] = 0;
}
// And this is a bound variable, so rank goes up by one.
rank++; rank++;
// Move on to the next row, since we just used this one to
// eliminate from column j.
i++;
if(i >= mat.m) break;
} }
rowMag[i] = mag;
} }
return rank; return rank;
} }
@ -394,7 +375,7 @@ void System::FindWhichToRemoveToFixJacobian(Group *g) {
WriteJacobian(0); WriteJacobian(0);
EvalJacobian(); EvalJacobian();
int rank = GaussJordan(); int rank = CalculateRank();
if(rank == mat.m) { if(rank == mat.m) {
// We fixed it by removing this constraint // We fixed it by removing this constraint
(g->solved.remove).Add(&(c->h)); (g->solved.remove).Add(&(c->h));
@ -455,7 +436,7 @@ void System::Solve(Group *g) {
EvalJacobian(); EvalJacobian();
int rank = GaussJordan(); int rank = CalculateRank();
if(rank != mat.m) { if(rank != mat.m) {
FindWhichToRemoveToFixJacobian(g); FindWhichToRemoveToFixJacobian(g);
g->solved.how = Group::SINGULAR_JACOBIAN; g->solved.how = Group::SINGULAR_JACOBIAN;