If any equations are soluble in a single variable, then handle them

separately, even before doing the rank test. In some cases, that's
a huge speedup.

[git-p4: depot-paths = "//depot/solvespace/": change = 1811]
This commit is contained in:
Jonathan Westhues 2008-06-26 01:34:26 -08:00
parent a47a77c37c
commit a31782e1ea
5 changed files with 113 additions and 54 deletions

View File

@ -444,7 +444,48 @@ void Expr::Substitute(hParam oldh, hParam newh) {
if(c >= 2) b->Substitute(oldh, newh); if(c >= 2) b->Substitute(oldh, newh);
} }
//-----------------------------------------------------------------------------
// If the expression references only one parameter that appears in pl, then
// return that parameter. If no param is referenced, then return NO_PARAMS.
// If multiple params are referenced, then return MULTIPLE_PARAMS.
//-----------------------------------------------------------------------------
const hParam Expr::NO_PARAMS = { 0 };
const hParam Expr::MULTIPLE_PARAMS = { 1 };
hParam Expr::ReferencedParams(ParamList *pl) {
if(op == PARAM) {
if(pl->FindByIdNoOops(x.parh)) {
return x.parh;
} else {
return NO_PARAMS;
}
}
if(op == PARAM_PTR) oops();
int c = Children();
if(c == 0) {
return NO_PARAMS;
} else if(c == 1) {
return a->ReferencedParams(pl);
} else if(c == 2) {
hParam pa, pb;
pa = a->ReferencedParams(pl);
pb = b->ReferencedParams(pl);
if(pa.v == NO_PARAMS.v) {
return pb;
} else if(pb.v == NO_PARAMS.v) {
return pa;
} else if(pa.v == pb.v) {
return pa; // either, doesn't matter
} else {
return MULTIPLE_PARAMS;
}
} else oops();
}
//-----------------------------------------------------------------------------
// Routines to pretty-print an expression. Mostly for debugging.
//-----------------------------------------------------------------------------
static char StringBuffer[4096]; static char StringBuffer[4096];
void Expr::App(char *s, ...) { void Expr::App(char *s, ...) {
@ -490,6 +531,14 @@ p:
} }
} }
//-----------------------------------------------------------------------------
// A parser; convert a string to an expression. Infix notation, with the
// usual shift/reduce approach. I had great hopes for user-entered eq
// constraints, but those don't seem very useful, so right now this is just
// to provide calculator type functionality wherever numbers are entered.
//-----------------------------------------------------------------------------
#define MAX_UNPARSED 1024 #define MAX_UNPARSED 1024
static Expr *Unparsed[MAX_UNPARSED]; static Expr *Unparsed[MAX_UNPARSED];
static int UnparsedCnt, UnparsedP; static int UnparsedCnt, UnparsedP;

3
expr.h
View File

@ -78,6 +78,9 @@ public:
Expr *FoldConstants(void); Expr *FoldConstants(void);
void Substitute(hParam oldh, hParam newh); void Substitute(hParam oldh, hParam newh);
static const hParam NO_PARAMS, MULTIPLE_PARAMS;
hParam ReferencedParams(ParamList *pl);
void ParamsToPointers(void); void ParamsToPointers(void);
void App(char *str, ...); void App(char *str, ...);

View File

@ -168,8 +168,6 @@ public:
// The corresponding parameter for each column // The corresponding parameter for each column
hParam param[MAX_UNKNOWNS]; hParam param[MAX_UNKNOWNS];
bool bound[MAX_UNKNOWNS];
// We're solving AX = B // We're solving AX = B
int m, n; int m, n;
struct { struct {
@ -197,7 +195,7 @@ public:
double B[], int N); double B[], int N);
bool SolveLeastSquares(void); bool SolveLeastSquares(void);
void WriteJacobian(int eqTag, int paramTag); void WriteJacobian(int tag);
void EvalJacobian(void); void EvalJacobian(void);
void WriteEquationsExceptFor(hConstraint hc, hGroup hg); void WriteEquationsExceptFor(hConstraint hc, hGroup hg);

View File

@ -1,12 +1,12 @@
#include "solvespace.h" #include "solvespace.h"
void System::WriteJacobian(int eqTag, int paramTag) { void System::WriteJacobian(int tag) {
int a, i, j; int a, i, j;
j = 0; j = 0;
for(a = 0; a < param.n; a++) { for(a = 0; a < param.n; a++) {
Param *p = &(param.elem[a]); Param *p = &(param.elem[a]);
if(p->tag != paramTag) continue; if(p->tag != tag) continue;
mat.param[j] = p->h; mat.param[j] = p->h;
j++; j++;
} }
@ -15,7 +15,7 @@ void System::WriteJacobian(int eqTag, int paramTag) {
i = 0; i = 0;
for(a = 0; a < eq.n; a++) { for(a = 0; a < eq.n; a++) {
Equation *e = &(eq.elem[a]); Equation *e = &(eq.elem[a]);
if(e->tag != eqTag) continue; if(e->tag != tag) continue;
mat.eq[i] = e->h; mat.eq[i] = e->h;
Expr *f = e->e->DeepCopyWithParamsAsPointers(&param, &(SS.param)); Expr *f = e->e->DeepCopyWithParamsAsPointers(&param, &(SS.param));
@ -152,10 +152,6 @@ bool System::Tol(double v) {
int System::GaussJordan(void) { int System::GaussJordan(void) {
int i, j; int i, j;
for(j = 0; j < mat.n; j++) {
mat.bound[j] = false;
}
// Now eliminate. // Now eliminate.
i = 0; i = 0;
int rank = 0; int rank = 0;
@ -201,8 +197,7 @@ int System::GaussJordan(void) {
mat.A.num[is][j] = 0; mat.A.num[is][j] = 0;
} }
// And mark this as a bound variable. // And this is a bound variable, so rank goes up by one.
mat.bound[j] = true;
rank++; rank++;
// Move on to the next row, since we just used this one to // Move on to the next row, since we just used this one to
@ -314,7 +309,7 @@ bool System::SolveLeastSquares(void) {
} }
bool System::NewtonSolve(int tag) { bool System::NewtonSolve(int tag) {
WriteJacobian(tag, tag); WriteJacobian(tag);
if(mat.m > mat.n) return false; if(mat.m > mat.n) return false;
int iter = 0; int iter = 0;
@ -396,7 +391,7 @@ void System::FindWhichToRemoveToFixJacobian(Group *g) {
WriteEquationsExceptFor(c->h, g->h); WriteEquationsExceptFor(c->h, g->h);
eq.ClearTags(); eq.ClearTags();
WriteJacobian(0, 0); WriteJacobian(0);
EvalJacobian(); EvalJacobian();
int rank = GaussJordan(); int rank = GaussJordan();
@ -423,26 +418,43 @@ void System::Solve(Group *g) {
dbp(" param %08x at %.3f", param.elem[i].h.v, param.elem[i].val); dbp(" param %08x at %.3f", param.elem[i].h.v, param.elem[i].val);
} */ } */
// All params and equations are assigned to group zero.
param.ClearTags(); param.ClearTags();
eq.ClearTags(); eq.ClearTags();
SolveBySubstitution(); SolveBySubstitution();
WriteJacobian(0, 0); // Before solving the big system, see if we can find any equations that
// are soluble alone. This can be a huge speedup. We don't know whether
// the system is consistent yet, but if it isn't then we'll catch that
// later.
int alone = 1;
for(i = 0; i < eq.n; i++) {
Equation *e = &(eq.elem[i]);
if(e->tag != 0) continue;
hParam hp = e->e->ReferencedParams(&param);
if(hp.v == Expr::NO_PARAMS.v) continue;
if(hp.v == Expr::MULTIPLE_PARAMS.v) continue;
Param *p = param.FindById(hp);
if(p->tag != 0) continue; // let rank test catch inconsistency
e->tag = alone;
p->tag = alone;
if(!NewtonSolve(alone)) {
// Failed to converge, bail out early
goto didnt_converge;
}
alone++;
}
// Now write the Jacobian for what's left, and do a rank test; that
// tells us if the system is inconsistently constrained.
WriteJacobian(0);
EvalJacobian(); EvalJacobian();
/*
for(i = 0; i < mat.m; i++) {
dbp("function %d: %s", i, mat.B.sym[i]->Print());
}
dbp("m=%d", mat.m);
for(i = 0; i < mat.m; i++) {
for(j = 0; j < mat.n; j++) {
dbp("A(%d,%d) = %.10f;", i+1, j+1, mat.A.num[i][j]);
}
} */
int rank = GaussJordan(); int rank = GaussJordan();
if(rank != mat.m) { if(rank != mat.m) {
FindWhichToRemoveToFixJacobian(g); FindWhichToRemoveToFixJacobian(g);
@ -451,14 +463,11 @@ void System::Solve(Group *g) {
return; return;
} }
/* dbp("bound states:"); // And do the leftovers as one big system
for(j = 0; j < mat.n; j++) { if(!NewtonSolve(0)) {
dbp(" param %08x: %d", mat.param[j], mat.bound[j]); goto didnt_converge;
} */ }
bool ok = NewtonSolve(0);
if(ok) {
// System solved correctly, so write the new values back in to the // System solved correctly, so write the new values back in to the
// main parameter table. // main parameter table.
for(i = 0; i < param.n; i++) { for(i = 0; i < param.n; i++) {
@ -477,9 +486,10 @@ void System::Solve(Group *g) {
g->solved.how = Group::SOLVED_OKAY; g->solved.how = Group::SOLVED_OKAY;
TextWindow::ReportHowGroupSolved(g->h); TextWindow::ReportHowGroupSolved(g->h);
} }
} else { return;
didnt_converge:
g->solved.how = Group::DIDNT_CONVERGE; g->solved.how = Group::DIDNT_CONVERGE;
TextWindow::ReportHowGroupSolved(g->h); TextWindow::ReportHowGroupSolved(g->h);
}
} }

View File

@ -9,10 +9,9 @@ auto-generate circles and faces when lathing
copy the section geometry to other end when sweeping copy the section geometry to other end when sweeping
cylindrical faces cylindrical faces
partitioned subsystems in the solver
long term long term
----- -----
incremental regen of entities? incremental regen of entities?
my own plane poly triangulation code