Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
syseq.h
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2#ifndef SEEN_SYSEQ_H
3#define SEEN_SYSEQ_H
4
5/*
6 * Auxiliary routines to solve systems of linear equations in several variants and sizes.
7 *
8 * Authors:
9 * Maximilian Albert <Anhalter42@gmx.de>
10 *
11 * Copyright (C) 2007 Authors
12 *
13 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
14 */
15
16#include <algorithm>
17#include <iostream>
18#include <iomanip>
19#include <vector>
20#include <cmath>
21
22namespace SysEq {
23
25 unique = 0,
28 solution_exists // FIXME: remove this; does not yield enough information
29};
30
31inline void explain(SolutionKind sol) {
32 switch (sol) {
33 case SysEq::unique:
34 std::cout << "unique" << std::endl;
35 break;
37 std::cout << "ambiguous" << std::endl;
38 break;
40 std::cout << "no solution" << std::endl;
41 break;
43 std::cout << "solution exists" << std::endl;
44 break;
45 }
46}
47
48inline double
49determinant3x3 (double A[3][3]) {
50 return (A[0][0]*A[1][1]*A[2][2] +
51 A[0][1]*A[1][2]*A[2][0] +
52 A[0][2]*A[1][0]*A[2][1] -
53 A[0][0]*A[1][2]*A[2][1] -
54 A[0][1]*A[1][0]*A[2][2] -
55 A[0][2]*A[1][1]*A[2][0]);
56}
57
58/* Determinant of the 3x3 matrix having a, b, and c as columns */
59inline double
60determinant3v (const double a[3], const double b[3], const double c[3]) {
61 return (a[0]*b[1]*c[2] +
62 a[1]*b[2]*c[0] +
63 a[2]*b[0]*c[1] -
64 a[0]*b[2]*c[1] -
65 a[1]*b[0]*c[2] -
66 a[2]*b[1]*c[0]);
67}
68
69/* Copy the elements of A into B */
70template <int S, int T>
71inline void copy_mat(double A[S][T], double B[S][T]) {
72 for (int i = 0; i < S; ++i) {
73 for (int j = 0; j < T; ++j) {
74 B[i][j] = A[i][j];
75 }
76 }
77}
78
79template <int S, int T>
80inline void print_mat (const double A[S][T]) {
81 std::cout.setf(std::ios::left, std::ios::internal);
82 for (int i = 0; i < S; ++i) {
83 for (int j = 0; j < T; ++j) {
84 printf ("%8.2f ", A[i][j]);
85 }
86 std::cout << std::endl;;
87 }
88}
89
90/* Multiplication of two matrices */
91template <int S, int U, int T>
92inline void multiply(double A[S][U], double B[U][T], double res[S][T]) {
93 for (int i = 0; i < S; ++i) {
94 for (int j = 0; j < T; ++j) {
95 double sum = 0;
96 for (int k = 0; k < U; ++k) {
97 sum += A[i][k] * B[k][j];
98 }
99 res[i][j] = sum;
100 }
101 }
102}
103
104/*
105 * Multiplication of a matrix with a vector (for convenience, because with the previous
106 * multiplication function we would always have to write v[i][0] for elements of the vector.
107 */
108template <int S, int T>
109inline void multiply(double A[S][T], double v[T], double res[S]) {
110 for (int i = 0; i < S; ++i) {
111 double sum = 0;
112 for (int k = 0; k < T; ++k) {
113 sum += A[i][k] * v[k];
114 }
115 res[i] = sum;
116 }
117}
118
119// Remark: Since we are using templates, we cannot separate declarations from definitions (which would
120// result in linker errors but have to include the definitions here for the following functions.
121// FIXME: Maybe we should rework all this by using vector<vector<double> > structures for matrices
122// instead of double[S][T]. This would allow us to avoid templates. Would the performance degrade?
123
124/*
125 * Find the element of maximal absolute value in row i that
126 * does not lie in one of the columns given in avoid_cols.
127 */
128template <int S, int T>
129static int find_pivot(const double A[S][T], unsigned int i, std::vector<int> const &avoid_cols) {
130 if (i >= S) {
131 return -1;
132 }
133 int pos = -1;
134 double max = 0;
135 for (int j = 0; j < T; ++j) {
136 if (std::find(avoid_cols.begin(), avoid_cols.end(), j) != avoid_cols.end()) {
137 continue; // skip "forbidden" columns
138 }
139 if (fabs(A[i][j]) > max) {
140 pos = j;
141 max = fabs(A[i][j]);
142 }
143 }
144 return pos;
145}
146
147/*
148 * Performs a single 'exchange step' in the Gauss-Jordan algorithm (i.e., swapping variables in the
149 * two vectors).
150 */
151template <int S, int T>
152static void gauss_jordan_step (double A[S][T], int row, int col) {
153 double piv = A[row][col]; // pivot element
154 /* adapt the entries of the matrix, first outside the pivot row/column */
155 for (int k = 0; k < S; ++k) {
156 if (k == row) continue;
157 for (int l = 0; l < T; ++l) {
158 if (l == col) continue;
159 A[k][l] -= A[k][col] * A[row][l] / piv;
160 }
161 }
162 /* now adapt the pivot column ... */
163 for (int k = 0; k < S; ++k) {
164 if (k == row) continue;
165 A[k][col] /= piv;
166 }
167 /* and the pivot row */
168 for (int l = 0; l < T; ++l) {
169 if (l == col) continue;
170 A[row][l] /= -piv;
171 }
172 /* finally, set the element at the pivot position itself */
173 A[row][col] = 1/piv;
174}
175
176/*
177 * Perform Gauss-Jordan elimination on the matrix A, optionally avoiding a given column during pivot search
178 */
179template <int S, int T>
180static std::vector<int> gauss_jordan (double A[S][T], int avoid_col = -1) {
181 std::vector<int> cols_used;
182 if (avoid_col != -1) {
183 cols_used.push_back (avoid_col);
184 }
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 int col = find_pivot<S,T>(A, i, cols_used);
188 cols_used.push_back(col);
189 if (col == -1) {
190 // no non-zero elements in the row
191 return cols_used;
192 }
193
194 /* if pivot search was successful we can perform a Gauss-Jordan step */
195 gauss_jordan_step<S,T> (A, i, col);
196 }
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());
200 }
201 return cols_used;
202}
203
204/* compute the modified value that x[index] needs to assume so that in the end we have x[index]/x[T-1] = val */
205template <int S, int T>
206static 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;
209 if (index != -1) {
210 int c = -1;
211 for (int i = 0; i < S; ++i) {
212 if (cols[i] == T-1) {
213 c = i;
214 break;
215 }
216 }
217 if (c == -1) {
218 std::cerr << "Something is wrong. Rethink!!" << std::endl;
219 return SysEq::no_solution;
220 }
221
222 double sp = 0;
223 for (int j = 0; j < T; ++j) {
224 if (j == index) continue;
225 sp += B[c][j] * x[j];
226 }
227 double mu = 1 - val * B[c][index];
228 if (fabs(mu) < 1E-6) {
229 std::cerr << "No solution since adapted value is too close to zero" << std::endl;
230 return SysEq::no_solution;
231 }
232 val_proj = sp*val/mu;
233 } else {
234 val_proj = val; // FIXME: Is this correct?
235 }
236 return val_proj;
237}
238
254template <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) {
256 double B[S][T];
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;
263 }
264
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
267 */
268 std::vector<int>::iterator k;
269 for (int j = 0; j < S; ++j) {
270 x[cols[j]] = v[j];
271 }
272 for (int j = 0; j < T; ++j) {
273 k = std::find(cols.begin(), cols.end(), j);
274 if (k == cols.end()) {
275 x[j] = 0;
276 }
277 }
278
279 // we need to adapt the value if we are in the "projective case" (see above)
280 double val_new = (proj ? projectify<S,T>(cols, B, x, index, val) : val);
281
282 if (index >= 0 && index < T) {
283 // we want the specified coefficient of the solution vector to have a given value
284 x[index] = val_new;
285 }
286
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
290 */
291 double w[S];
292 SysEq::multiply<S,T>(B,x,w); // initializes w
293 for (int j = 0; j < S; ++j) {
294 x[cols[j]] = w[j];
295 }
296
297 if (S + (index == -1 ? 0 : 1) == T) {
298 return SysEq::unique;
299 } else {
300 return SysEq::ambiguous;
301 }
302}
303
304} // namespace SysEq
305
306#endif // SEEN_SYSEQ_H
307
308/*
309 Local Variables:
310 mode:c++
311 c-file-style:"stroustrup"
312 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
313 indent-tabs-mode:nil
314 fill-column:99
315 End:
316*/
317// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
const double w
Definition conic-4.cpp:19
double c[8][4]
static auto proj(Geom::Point const &p, int dim)
Definition syseq.h:22
static double projectify(std::vector< int > const &cols, const double B[S][T], const double x[T], const int index, const double val)
Definition syseq.h:206
double determinant3x3(double A[3][3])
Definition syseq.h:49
static void gauss_jordan_step(double A[S][T], int row, int col)
Definition syseq.h:152
static std::vector< int > gauss_jordan(double A[S][T], int avoid_col=-1)
Definition syseq.h:180
void copy_mat(double A[S][T], double B[S][T])
Definition syseq.h:71
SolutionKind gaussjord_solve(double A[S][T], double x[T], double v[S], int index=-1, double val=0.0, bool proj=false)
Solve the linear system of equations A * x = v where we additionally stipulate x[index] = val if inde...
Definition syseq.h:254
void multiply(double A[S][U], double B[U][T], double res[S][T])
Definition syseq.h:92
static int find_pivot(const double A[S][T], unsigned int i, std::vector< int > const &avoid_cols)
Definition syseq.h:129
SolutionKind
Definition syseq.h:24
@ unique
Definition syseq.h:25
@ ambiguous
Definition syseq.h:26
@ no_solution
Definition syseq.h:27
@ solution_exists
Definition syseq.h:28
void print_mat(const double A[S][T])
Definition syseq.h:80
void explain(SolutionKind sol)
Definition syseq.h:31
double determinant3v(const double a[3], const double b[3], const double c[3])
Definition syseq.h:60
double sum(const double alpha[16], const double &x, const double &y)
int index