1 #ifndef SEEN_SYSEQ_H
2 #define SEEN_SYSEQ_H
4 /*
5 * Auxiliary routines to solve systems of linear equations in several variants and sizes.
6 *
7 * Authors:
8 * Maximilian Albert <Anhalter42@gmx.de>
9 *
10 * Copyright (C) 2007 Authors
11 *
12 * Released under GNU GPL, read the file 'COPYING' for more information
13 */
15 #include <iostream>
16 #include <iomanip>
17 #include <vector>
18 #include "math.h"
20 namespace SysEq {
22 enum SolutionKind {
23 unique = 0,
24 ambiguous,
25 no_solution,
26 solution_exists // FIXME: remove this; does not yield enough information
27 };
29 inline void explain(SolutionKind sol) {
30 switch (sol) {
31 case SysEq::unique:
32 std::cout << "unique" << std::endl;
33 break;
34 case SysEq::ambiguous:
35 std::cout << "ambiguous" << std::endl;
36 break;
37 case SysEq::no_solution:
38 std::cout << "no solution" << std::endl;
39 break;
40 case SysEq::solution_exists:
41 std::cout << "solution exists" << std::endl;
42 break;
43 }
44 }
46 inline double
47 determinant3x3 (double A[3][3]) {
48 return (A[0][0]*A[1][1]*A[2][2] +
49 A[0][1]*A[1][2]*A[2][0] +
50 A[0][2]*A[1][0]*A[2][1] -
51 A[0][0]*A[1][2]*A[2][1] -
52 A[0][1]*A[1][0]*A[2][2] -
53 A[0][2]*A[1][1]*A[2][0]);
54 }
56 /* Determinant of the 3x3 matrix having a, b, and c as columns */
57 inline double
58 determinant3v (const double a[3], const double b[3], const double c[3]) {
59 return (a[0]*b[1]*c[2] +
60 a[1]*b[2]*c[0] +
61 a[2]*b[0]*c[1] -
62 a[0]*b[2]*c[1] -
63 a[1]*b[0]*c[2] -
64 a[2]*b[1]*c[0]);
65 }
67 /* Copy the elements of A into B */
68 template <int S, int T>
69 inline void copy_mat(double A[S][T], double B[S][T]) {
70 for (int i = 0; i < S; ++i) {
71 for (int j = 0; j < T; ++j) {
72 B[i][j] = A[i][j];
73 }
74 }
75 }
77 template <int S, int T>
78 inline void print_mat (const double A[S][T]) {
79 std::cout.setf(std::ios::left, std::ios::internal);
80 for (int i = 0; i < S; ++i) {
81 for (int j = 0; j < T; ++j) {
82 printf ("%8.2f ", A[i][j]);
83 }
84 std::cout << std::endl;;
85 }
86 }
88 /* Multiplication of two matrices */
89 template <int S, int U, int T>
90 inline void multiply(double A[S][U], double B[U][T], double res[S][T]) {
91 for (int i = 0; i < S; ++i) {
92 for (int j = 0; j < T; ++j) {
93 double sum = 0;
94 for (int k = 0; k < U; ++k) {
95 sum += A[i][k] * B[k][j];
96 }
97 res[i][j] = sum;
98 }
99 }
100 }
102 /*
103 * Multiplication of a matrix with a vector (for convenience, because with the previous
104 * multiplication function we would always have to write v[i][0] for elements of the vector.
105 */
106 template <int S, int T>
107 inline void multiply(double A[S][T], double v[T], double res[S]) {
108 for (int i = 0; i < S; ++i) {
109 double sum = 0;
110 for (int k = 0; k < T; ++k) {
111 sum += A[i][k] * v[k];
112 }
113 res[i] = sum;
114 }
115 }
117 // Remark: Since we are using templates, we cannot separate declarations from definitions (which would
118 // result in linker errors but have to include the definitions here for the following functions.
119 // FIXME: Maybe we should rework all this by using vector<vector<double> > structures for matrices
120 // instead of double[S][T]. This would allow us to avoid templates. Would the performance degrade?
122 /*
123 * Find the element of maximal absolute value in row i that
124 * does not lie in one of the columns given in avoid_cols.
125 */
126 template <int S, int T>
127 static int find_pivot(const double A[S][T], unsigned int i, std::vector<int> const &avoid_cols) {
128 if (i >= S) {
129 return -1;
130 }
131 int pos = -1;
132 double max = 0;
133 for (int j = 0; j < T; ++j) {
134 if (std::find(avoid_cols.begin(), avoid_cols.end(), j) != avoid_cols.end()) {
135 continue; // skip "forbidden" columns
136 }
137 if (fabs(A[i][j]) > max) {
138 pos = j;
139 max = fabs(A[i][j]);
140 }
141 }
142 return pos;
143 }
145 /*
146 * Performs a single 'exchange step' in the Gauss-Jordan algorithm (i.e., swapping variables in the
147 * two vectors).
148 */
149 template <int S, int T>
150 static void gauss_jordan_step (double A[S][T], int row, int col) {
151 double piv = A[row][col]; // pivot element
152 /* adapt the entries of the matrix, first outside the pivot row/column */
153 for (int k = 0; k < S; ++k) {
154 if (k == row) continue;
155 for (int l = 0; l < T; ++l) {
156 if (l == col) continue;
157 A[k][l] -= A[k][col] * A[row][l] / piv;
158 }
159 }
160 /* now adapt the pivot column ... */
161 for (int k = 0; k < S; ++k) {
162 if (k == row) continue;
163 A[k][col] /= piv;
164 }
165 /* and the pivot row */
166 for (int l = 0; l < T; ++l) {
167 if (l == col) continue;
168 A[row][l] /= -piv;
169 }
170 /* finally, set the element at the pivot position itself */
171 A[row][col] = 1/piv;
172 }
174 /*
175 * Perform Gauss-Jordan elimination on the matrix A, optionally avoiding a given column during pivot search
176 */
177 template <int S, int T>
178 static std::vector<int> gauss_jordan (double A[S][T], int avoid_col = -1) {
179 std::vector<int> cols_used;
180 if (avoid_col != -1) {
181 cols_used.push_back (avoid_col);
182 }
183 int col;
184 for (int i = 0; i < S; ++i) {
185 /* for each row find a pivot element of maximal absolute value, skipping the columns that were used before */
186 col = find_pivot<S,T>(A, i, cols_used);
187 cols_used.push_back(col);
188 if (col == -1) {
189 // no non-zero elements in the row
190 return cols_used;
191 }
193 /* if pivot search was successful we can perform a Gauss-Jordan step */
194 gauss_jordan_step<S,T> (A, i, col);
195 }
196 if (avoid_col != -1) {
197 // since the columns that were used will be needed later on, we need to clean up the column vector
198 cols_used.erase(cols_used.begin());
199 }
200 return cols_used;
201 }
203 /* compute the modified value that x[index] needs to assume so that in the end we have x[index]/x[T-1] = val */
204 template <int S, int T>
205 static double projectify (std::vector<int> const &cols, const double B[S][T], const double x[T],
206 const int index, const double val) {
207 double val_proj = 0.0;
208 if (index != -1) {
209 int c = -1;
210 for (int i = 0; i < S; ++i) {
211 if (cols[i] == T-1) {
212 c = i;
213 break;
214 }
215 }
216 if (c == -1) {
217 std::cout << "Something is wrong. Rethink!!" << std::endl;
218 return SysEq::no_solution;
219 }
221 double sp = 0;
222 for (int j = 0; j < T; ++j) {
223 if (j == index) continue;
224 sp += B[c][j] * x[j];
225 }
226 double mu = 1 - val * B[c][index];
227 if (fabs(mu) < 1E-6) {
228 std::cout << "No solution since adapted value is too close to zero" << std::endl;
229 return SysEq::no_solution;
230 }
231 val_proj = sp*val/mu;
232 } else {
233 val_proj = val; // FIXME: Is this correct?
234 }
235 return val_proj;
236 }
238 /**
239 * Solve the linear system of equations \a A * \a x = \a v where we additionally stipulate
240 * \a x[\a index] = \a val if \a index is not -1. The system is solved using Gauss-Jordan
241 * elimination so that we can gracefully handle the case that zero or infinitely many
242 * solutions exist.
243 *
244 * Since our application will be to finding preimages of projective mappings, we provide
245 * an additional argument \a proj. If this is true, we find a solution of
246 * \a x[\a index]/\a x[\T - 1] = \a val insted (i.e., we want the corresponding coordinate
247 * of the _affine image_ of the point with homogeneous coordinate vector \a x to be equal
248 * to \a val.
249 *
250 * Remark: We don't need this but it would be relatively simple to let the calling function
251 * prescripe the value of _multiple_ components of the solution vector instead of only a single one.
252 */
253 template <int S, int T> SolutionKind gaussjord_solve (double A[S][T], double x[T], double v[S],
254 int index = -1, double val = 0.0, bool proj = false) {
255 double B[S][T];
256 //copy_mat<S,T>(A,B);
257 SysEq::copy_mat<S,T>(A,B);
258 std::vector<int> cols = gauss_jordan<S,T>(B, index);
259 if (std::find(cols.begin(), cols.end(), -1) != cols.end()) {
260 // pivot search failed for some row so the system is not solvable
261 return SysEq::no_solution;
262 }
264 /* the vector x is filled with the coefficients of the desired solution vector at appropriate places;
265 * the other components are set to zero, and we additionally set x[index] = val if applicable
266 */
267 std::vector<int>::iterator k;
268 for (int j = 0; j < S; ++j) {
269 x[cols[j]] = v[j];
270 }
271 for (int j = 0; j < T; ++j) {
272 k = std::find(cols.begin(), cols.end(), j);
273 if (k == cols.end()) {
274 x[j] = 0;
275 }
276 }
278 // we need to adapt the value if we we are in the "projective case" (see above)
279 double val_new = (proj ? projectify<S,T>(cols, B, x, index, val) : val);
281 if (index != -1 && index >= 0 && index < T) {
282 // we want the specified coefficient of the solution vector to have a given value
283 x[index] = val_new;
284 }
286 /* the final solution vector is now obtained as the product B*x, where B is the matrix
287 * obtained by Gauss-Jordan manipulation of A; we use w as an auxiliary vector and
288 * afterwards copy the result back to x
289 */
290 double w[S];
291 SysEq::multiply<S,T>(B,x,w);
292 for (int j = 0; j < S; ++j) {
293 x[cols[j]] = w[j];
294 }
296 if (S + (index == -1 ? 0 : 1) == T) {
297 return SysEq::unique;
298 } else {
299 return SysEq::ambiguous;
300 }
301 }
303 } // namespace SysEq
305 #endif /* __SYSEQ_H__ */
307 /*
308 Local Variables:
309 mode:c++
310 c-file-style:"stroustrup"
311 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
312 indent-tabs-mode:nil
313 fill-column:99
314 End:
315 */
316 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :