Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
gaussian_elimination.h
Go to the documentation of this file.
1/*
2 This file is part of darktable,
3 Copyright (C) 2017-2020 darktable developers.
4
5 darktable is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 darktable is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with darktable. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/*
20 These routines can be used to solve full-rank linear systems of
21 equations by Gaussian elimination with partial pivoting. The
22 functions gauss_make_triangular and gauss_solve have been adopted
23 from Fortran routines as presented in the book "Numerik" by
24 Helmuth Späth, Vieweg Verlag, 1994, see also
25 http://dx.doi.org/10.1007/978-3-322-89220-1
26
27*/
28
29
30#pragma once
31
32#include <math.h>
33#include <stdlib.h>
34
35// Gaussian elimination with partial vivoting
36// after function call the square matrix A is triangular
37// vector p keeps track of row swaps
38// returns 0 if matrix A is singular
39// matrix elements are stored in row-major order
40static int gauss_make_triangular(double *A, int *p, int n)
41{
42 p[n - 1] = n - 1; // we never swap from the last row
43 for(int k = 0; k < n; ++k)
44 {
45 // find pivot element for row swap
46 int m = k;
47 for(int i = k + 1; i < n; ++i)
48 if(fabs(A[k + n * i]) > fabs(A[k + n * m])) m = i;
49 p[k] = m; // rows k and m are swapped
50 // eliminate elements and swap rows
51 double t1 = A[k + n * m];
52 A[k + n * m] = A[k + n * k];
53 A[k + n * k] = t1; // new diagonal elements are (implicitly) one, store scaling factors on diagonal
54 if(t1 != 0)
55 {
56 for(int i = k + 1; i < n; ++i) A[k + n * i] /= -t1;
57 // swap rows
58 if(k != m)
59 for(int i = k + 1; i < n; ++i)
60 {
61 double t2 = A[i + n * m];
62 A[i + n * m] = A[i + n * k];
63 A[i + n * k] = t2;
64 }
65 for(int j = k + 1; j < n; ++j)
66 for(int i = k + 1; i < n; ++i) A[i + n * j] += A[k + j * n] * A[i + k * n];
67 }
68 else
69 // the matrix is singular
70 return 0;
71 }
72 return 1;
73}
74
75// backward substritution after Gaussian elimination
76static void gauss_solve_triangular(const double *A, const int *p, double *b, int n)
77{
78 // permute and rescale elements of right-hand-side
79 for(int k = 0; k < n - 1; ++k)
80 {
81 int m = p[k];
82 double t = b[m];
83 b[m] = b[k];
84 b[k] = t;
85 for(int i = k + 1; i < n; ++i) b[i] += A[k + n * i] * t;
86 }
87 // perform backward substritution
88 for(int k = n - 1; k > 0; --k)
89 {
90 b[k] /= A[k + n * k];
91 double t = b[k];
92 for(int i = 0; i < k; ++i) b[i] -= A[k + n * i] * t;
93 }
94 b[0] /= A[0 + 0 * n];
95}
96
97static int gauss_solve(double *A, double *b, int n)
98{
99 int *p = malloc(n * sizeof(*p));
100 int err_code = 1;
101 if((err_code = gauss_make_triangular(A, p, n))) gauss_solve_triangular(A, p, b, n);
102 free(p);
103 return err_code;
104}
105
106
108static inline int transpose_dot_matrix(double *const restrict A, // input
109 double *const restrict A_square, // output
110 const size_t m, const size_t n)
111{
112 // Construct the square symmetrical definite positive matrix A' A,
113
114 for(size_t i = 0; i < n; ++i)
115 for(size_t j = 0; j < n; ++j)
116 {
117 double sum = 0.0;
118 for(size_t k = 0; k < m; ++k)
119 sum += A[k * n + i] * A[k * n + j];
120
121 A_square[i * n + j] = sum;
122 }
123
124 return 1;
125}
126
127
129static inline int transpose_dot_vector(double *const restrict A, // input
130 double *const restrict y, // input
131 double *const restrict y_square, // output
132 const size_t m, const size_t n)
133{
134 // Construct the vector A' y
135 for(size_t i = 0; i < n; ++i)
136 {
137 double sum = 0.0;
138 for(size_t k = 0; k < m; ++k)
139 sum += A[k * n + i] * y[k];
140
141 y_square[i] = sum;
142 }
143
144 return 1;
145}
146
147
148static inline int pseudo_solve_gaussian(double *const restrict A,
149 double *const restrict y,
150 const size_t m, const size_t n, const int checks)
151{
152 // Solve the weighted linear problem w A'A x = w A' y with the over-constrained rectanguler matrix A
153 // of dimension m x n (m >= n) and w a vector of weights, by the least squares method
154 int valid = 1;
155
156 if(m < n)
157 {
158 fprintf(stderr, "pseudo solve: cannot cast %zu \303\227 %zu matrix\n", m, n);
159 return 0;
160 }
161
162 double *const restrict A_square = dt_alloc_align(n * n * sizeof(double));
163 double *const restrict y_square = dt_alloc_align(n * sizeof(double));
164
165 if(y_square == NULL || A_square == NULL) goto error;
166
167 #ifdef _OPENMP
168 #pragma omp parallel sections
169 #endif
170 {
171 #ifdef _OPENMP
172 #pragma omp section
173 #endif
174 {
175 // Prepare the least squares matrix = A' A
176 transpose_dot_matrix(A, A_square, m, n);
177 }
178
179 #ifdef _OPENMP
180 #pragma omp section
181 #endif
182 {
183 // Prepare the y square vector = A' y
184 transpose_dot_vector(A, y, y_square, m, n);
185 }
186 }
187
188 // Solve A' A x = A' y for x
189 valid = gauss_solve(A_square, y_square, n);
190 for(size_t k = 0; k < n; k++) y[k] = y_square[k];
191
192error:;
193 if(y_square) dt_free_align(y_square);
194 if(A_square) dt_free_align(A_square);
195
196 return valid;
197}
198// clang-format off
199// modelines: These editor modelines have been set for all relevant files by tools/update_modelines.py
200// vim: shiftwidth=2 expandtab tabstop=2 cindent
201// kate: tab-indents: off; indent-width 2; replace-tabs on; indent-mode cstyle; remove-trailing-spaces modified;
202// clang-format on
static void error(char *msg)
Definition ashift_lsd.c:191
#define m
Definition basecurve.c:231
#define A(y, x)
Definition colorspaces.c:148
#define __DT_CLONE_TARGETS__
Definition darktable.h:249
#define dt_free_align(A)
Definition darktable.h:334
static int pseudo_solve_gaussian(double *const restrict A, double *const restrict y, const size_t m, const size_t n, const int checks)
Definition gaussian_elimination.h:148
static __DT_CLONE_TARGETS__ int transpose_dot_vector(double *const restrict A, double *const restrict y, double *const restrict y_square, const size_t m, const size_t n)
Definition gaussian_elimination.h:129
static int gauss_make_triangular(double *A, int *p, int n)
Definition gaussian_elimination.h:40
static void gauss_solve_triangular(const double *A, const int *p, double *b, int n)
Definition gaussian_elimination.h:76
static int gauss_solve(double *A, double *b, int n)
Definition gaussian_elimination.h:97
static __DT_CLONE_TARGETS__ int transpose_dot_matrix(double *const restrict A, double *const restrict A_square, const size_t m, const size_t n)
Definition gaussian_elimination.h:108
#define dt_alloc_align(B)
Definition tests/cache.c:22