ultimatepp/uppsrc/Geom/linsolv.cpp
mdelfede d2b54f7989 changed svn layout
git-svn-id: svn://ultimatepp.org/upp/trunk@281 f0d560ea-af0d-0410-9eb7-867de7ffcac7
2008-06-07 22:31:27 +00:00

129 lines
2.7 KiB
C++

#include "Geom.h"
NAMESPACE_UPP
LinearSolver::LinearSolver(int count, double tolerance)
: rows(count)
, col1(count + 1)
, tolerance(tolerance)
{
ASSERT(tolerance >= 0);
matrix.SetCount(count * (count + 1), 0);
left_rows.SetCount(rows);
left_cols.SetCount(rows);
for(int i = 0; i < rows; i++)
left_rows[i] = left_cols[i] = i;
}
void LinearSolver::AddLSI(const double *bases, double value)
{
double *row = matrix.Begin();
const double *bi = bases;
for(int i = 0; i < rows; i++) {
const double *bj = bases;
for(int j = 0; j < rows; j++)
*row++ += *bi * *bj++;
*row++ += *bi++ * value;
}
}
Vector<double> LinearSolver::Solve()
{
while(!left_rows.IsEmpty())
{
int er = -1, ec = -1;
double best = tolerance;
for(int pr = 0; pr < left_rows.GetCount(); pr++)
{
const double *p = Row(left_rows[pr]);
for(int pc = 0; pc < left_cols.GetCount(); pc++)
{
double v = fabs(p[left_cols[pc]]);
if(v > best)
{
best = v;
ec = pc;
er = pr;
}
}
}
if(er < 0 || best <= 0) // just to be sure
{ // no more fixed variables
for(int i = 0; i < left_rows.GetCount(); i++)
if(fabs(Right(left_rows[i])) > tolerance)
return Vector<double>(); // error
break;
}
int cr = left_rows[er], cc = left_cols[ec];
int p = left_rows.Pop();
if(er < left_rows.GetCount()) left_rows[er] = p;
p = left_cols.Pop();
if(ec < left_cols.GetCount()) left_cols[ec] = p;
const double *src = Row(cr);
const int *xb = left_cols.Begin(), *xe = left_cols.End();
for(int i = 0; i < left_rows.GetCount(); i++)
{
double *dest = Row(left_rows[i]);
double r = -dest[cc] / src[cc];
for(const int *xp = xb; xp < xe; xp++)
dest[*xp] += src[*xp] * r;
dest[rows] += src[rows] * r;
}
pivots.Add(Point(cc, cr));
}
Vector<double> result;
result.SetCount(rows, Null);
for(int r = pivots.GetCount(); --r >= 0;)
{
Point pivot = pivots[r];
const double *row = Row(pivot.y);
int px = pivot.x;
double out = row[rows]; // right side
for(int s = r; ++s < pivots.GetCount();)
{
int c = pivots[s].x;
out -= row[c] * result[c];
}
result[pivot.x] = out / row[pivot.x];
}
return result;
}
void LinearSolver::SelfTest()
{
for(int i = 0; i < 1000; i++)
{
int ord = rand() % 10 + 1;
Vector<double> res;
while(res.GetCount() < ord)
res.Add(rand());
LinearSolver ls(ord);
for(int r = 0; r < ord; r++)
{
double rs = 0;
for(int c = 0; c < ord; c++)
rs += res[c] * (ls(r, c) = rand());
ls(r) = rs;
}
Vector<double> out = ls.Solve();
if(!out.IsEmpty())
{
ASSERT(out.GetCount() == ord);
for(int c = 0; c < out.GetCount(); c++)
if(!IsNull(out[c]))
{
double d = out[c] - res[c];
ASSERT(fabs(d) <= 1e-3);
}
}
}
}
END_UPP_NAMESPACE