5
* Auxiliary routines to solve systems of linear equations in several variants and sizes.
8
* Maximilian Albert <Anhalter42@gmx.de>
10
* Copyright (C) 2007 Authors
12
* Released under GNU GPL, read the file 'COPYING' for more information
27
solution_exists // FIXME: remove this; does not yield enough information
30
inline void explain(SolutionKind sol) {
33
std::cout << "unique" << std::endl;
35
case SysEq::ambiguous:
36
std::cout << "ambiguous" << std::endl;
38
case SysEq::no_solution:
39
std::cout << "no solution" << std::endl;
41
case SysEq::solution_exists:
42
std::cout << "solution exists" << std::endl;
48
determinant3x3 (double A[3][3]) {
49
return (A[0][0]*A[1][1]*A[2][2] +
50
A[0][1]*A[1][2]*A[2][0] +
51
A[0][2]*A[1][0]*A[2][1] -
52
A[0][0]*A[1][2]*A[2][1] -
53
A[0][1]*A[1][0]*A[2][2] -
54
A[0][2]*A[1][1]*A[2][0]);
57
/* Determinant of the 3x3 matrix having a, b, and c as columns */
59
determinant3v (const double a[3], const double b[3], const double c[3]) {
60
return (a[0]*b[1]*c[2] +
68
/* Copy the elements of A into B */
69
template <int S, int T>
70
inline void copy_mat(double A[S][T], double B[S][T]) {
71
for (int i = 0; i < S; ++i) {
72
for (int j = 0; j < T; ++j) {
78
template <int S, int T>
79
inline void print_mat (const double A[S][T]) {
80
std::cout.setf(std::ios::left, std::ios::internal);
81
for (int i = 0; i < S; ++i) {
82
for (int j = 0; j < T; ++j) {
83
printf ("%8.2f ", A[i][j]);
85
std::cout << std::endl;;
89
/* Multiplication of two matrices */
90
template <int S, int U, int T>
91
inline void multiply(double A[S][U], double B[U][T], double res[S][T]) {
92
for (int i = 0; i < S; ++i) {
93
for (int j = 0; j < T; ++j) {
95
for (int k = 0; k < U; ++k) {
96
sum += A[i][k] * B[k][j];
104
* Multiplication of a matrix with a vector (for convenience, because with the previous
105
* multiplication function we would always have to write v[i][0] for elements of the vector.
107
template <int S, int T>
108
inline void multiply(double A[S][T], double v[T], double res[S]) {
109
for (int i = 0; i < S; ++i) {
111
for (int k = 0; k < T; ++k) {
112
sum += A[i][k] * v[k];
118
// Remark: Since we are using templates, we cannot separate declarations from definitions (which would
119
// result in linker errors but have to include the definitions here for the following functions.
120
// FIXME: Maybe we should rework all this by using vector<vector<double> > structures for matrices
121
// instead of double[S][T]. This would allow us to avoid templates. Would the performance degrade?
124
* Find the element of maximal absolute value in row i that
125
* does not lie in one of the columns given in avoid_cols.
127
template <int S, int T>
128
static int find_pivot(const double A[S][T], unsigned int i, std::vector<int> const &avoid_cols) {
134
for (int j = 0; j < T; ++j) {
135
if (std::find(avoid_cols.begin(), avoid_cols.end(), j) != avoid_cols.end()) {
136
continue; // skip "forbidden" columns
138
if (fabs(A[i][j]) > max) {
147
* Performs a single 'exchange step' in the Gauss-Jordan algorithm (i.e., swapping variables in the
150
template <int S, int T>
151
static void gauss_jordan_step (double A[S][T], int row, int col) {
152
double piv = A[row][col]; // pivot element
153
/* adapt the entries of the matrix, first outside the pivot row/column */
154
for (int k = 0; k < S; ++k) {
155
if (k == row) continue;
156
for (int l = 0; l < T; ++l) {
157
if (l == col) continue;
158
A[k][l] -= A[k][col] * A[row][l] / piv;
161
/* now adapt the pivot column ... */
162
for (int k = 0; k < S; ++k) {
163
if (k == row) continue;
166
/* and the pivot row */
167
for (int l = 0; l < T; ++l) {
168
if (l == col) continue;
171
/* finally, set the element at the pivot position itself */
176
* Perform Gauss-Jordan elimination on the matrix A, optionally avoiding a given column during pivot search
178
template <int S, int T>
179
static std::vector<int> gauss_jordan (double A[S][T], int avoid_col = -1) {
180
std::vector<int> cols_used;
181
if (avoid_col != -1) {
182
cols_used.push_back (avoid_col);
185
for (int i = 0; i < S; ++i) {
186
/* for each row find a pivot element of maximal absolute value, skipping the columns that were used before */
187
col = find_pivot<S,T>(A, i, cols_used);
188
cols_used.push_back(col);
190
// no non-zero elements in the row
194
/* if pivot search was successful we can perform a Gauss-Jordan step */
195
gauss_jordan_step<S,T> (A, i, col);
197
if (avoid_col != -1) {
198
// since the columns that were used will be needed later on, we need to clean up the column vector
199
cols_used.erase(cols_used.begin());
204
/* compute the modified value that x[index] needs to assume so that in the end we have x[index]/x[T-1] = val */
205
template <int S, int T>
206
static double projectify (std::vector<int> const &cols, const double B[S][T], const double x[T],
207
const int index, const double val) {
208
double val_proj = 0.0;
211
for (int i = 0; i < S; ++i) {
212
if (cols[i] == T-1) {
218
std::cout << "Something is wrong. Rethink!!" << std::endl;
219
return SysEq::no_solution;
223
for (int j = 0; j < T; ++j) {
224
if (j == index) continue;
225
sp += B[c][j] * x[j];
227
double mu = 1 - val * B[c][index];
228
if (fabs(mu) < 1E-6) {
229
std::cout << "No solution since adapted value is too close to zero" << std::endl;
230
return SysEq::no_solution;
232
val_proj = sp*val/mu;
234
val_proj = val; // FIXME: Is this correct?
240
* Solve the linear system of equations \a A * \a x = \a v where we additionally stipulate
241
* \a x[\a index] = \a val if \a index is not -1. The system is solved using Gauss-Jordan
242
* elimination so that we can gracefully handle the case that zero or infinitely many
245
* Since our application will be to finding preimages of projective mappings, we provide
246
* an additional argument \a proj. If this is true, we find a solution of
247
* \a x[\a index]/\a x[\T - 1] = \a val insted (i.e., we want the corresponding coordinate
248
* of the _affine image_ of the point with homogeneous coordinate vector \a x to be equal
251
* Remark: We don't need this but it would be relatively simple to let the calling function
252
* prescripe the value of _multiple_ components of the solution vector instead of only a single one.
254
template <int S, int T> SolutionKind gaussjord_solve (double A[S][T], double x[T], double v[S],
255
int index = -1, double val = 0.0, bool proj = false) {
257
//copy_mat<S,T>(A,B);
258
SysEq::copy_mat<S,T>(A,B);
259
std::vector<int> cols = gauss_jordan<S,T>(B, index);
260
if (std::find(cols.begin(), cols.end(), -1) != cols.end()) {
261
// pivot search failed for some row so the system is not solvable
262
return SysEq::no_solution;
265
/* the vector x is filled with the coefficients of the desired solution vector at appropriate places;
266
* the other components are set to zero, and we additionally set x[index] = val if applicable
268
std::vector<int>::iterator k;
269
for (int j = 0; j < S; ++j) {
272
for (int j = 0; j < T; ++j) {
273
k = std::find(cols.begin(), cols.end(), j);
274
if (k == cols.end()) {
279
// we need to adapt the value if we we are in the "projective case" (see above)
280
double val_new = (proj ? projectify<S,T>(cols, B, x, index, val) : val);
282
if (index != -1 && index >= 0 && index < T) {
283
// we want the specified coefficient of the solution vector to have a given value
287
/* the final solution vector is now obtained as the product B*x, where B is the matrix
288
* obtained by Gauss-Jordan manipulation of A; we use w as an auxiliary vector and
289
* afterwards copy the result back to x
292
SysEq::multiply<S,T>(B,x,w);
293
for (int j = 0; j < S; ++j) {
297
if (S + (index == -1 ? 0 : 1) == T) {
298
return SysEq::unique;
300
return SysEq::ambiguous;
306
#endif /* __SYSEQ_H__ */
311
c-file-style:"stroustrup"
312
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
317
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :