Blender V4.3
linear_solver.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2004 Bruno Levy
2 * SPDX-FileCopyrightText: 2005-2015 Blender Authors
3 *
4 * SPDX-License-Identifier: GPL-2.0-or-later */
5
11#include "linear_solver.h"
12
13#include <Eigen/Sparse>
14
15#include <algorithm>
16#include <cassert>
17#include <cstdlib>
18#include <iostream>
19#include <vector>
20
21/* Eigen data structures */
22
23typedef Eigen::SparseMatrix<double, Eigen::ColMajor> EigenSparseMatrix;
24typedef Eigen::SparseLU<EigenSparseMatrix> EigenSparseLU;
25typedef Eigen::VectorXd EigenVectorX;
26typedef Eigen::Triplet<double> EigenTriplet;
27
28/* Linear Solver data structure */
29
31 struct Coeff {
33 {
34 index = 0;
35 value = 0.0;
36 }
37
38 int index;
39 double value;
40 };
41
42 struct Variable {
44 {
45 memset(value, 0, sizeof(value));
46 locked = false;
47 index = 0;
48 }
49
50 double value[4];
51 bool locked;
52 int index;
53 std::vector<Coeff> a;
54 };
55
57
58 LinearSolver(int num_rows_, int num_variables_, int num_rhs_, bool lsq_)
59 {
60 assert(num_variables_ > 0);
61 assert(num_rhs_ <= 4);
62
64 m = 0;
65 n = 0;
66 sparseLU = NULL;
67 num_variables = num_variables_;
68 num_rhs = num_rhs_;
69 num_rows = num_rows_;
70 least_squares = lsq_;
71
72 variable.resize(num_variables);
73 }
74
76 {
77 delete sparseLU;
78 }
79
81
82 int n;
83 int m;
84
85 std::vector<EigenTriplet> Mtriplets;
88 std::vector<EigenVectorX> b;
89 std::vector<EigenVectorX> x;
90
92
94 std::vector<Variable> variable;
95
98
100};
101
102LinearSolver *EIG_linear_solver_new(int num_rows, int num_columns, int num_rhs)
103{
104 return new LinearSolver(num_rows, num_columns, num_rhs, false);
105}
106
107LinearSolver *EIG_linear_least_squares_solver_new(int num_rows, int num_columns, int num_rhs)
108{
109 return new LinearSolver(num_rows, num_columns, num_rhs, true);
110}
111
113{
114 delete solver;
115}
116
117/* Variables */
118
119void EIG_linear_solver_variable_set(LinearSolver *solver, int rhs, int index, double value)
120{
121 solver->variable[index].value[rhs] = value;
122}
123
124double EIG_linear_solver_variable_get(LinearSolver *solver, int rhs, int index)
125{
126 return solver->variable[index].value[rhs];
127}
128
130{
131 if (!solver->variable[index].locked) {
133 solver->variable[index].locked = true;
134 }
135}
136
138{
139 if (solver->variable[index].locked) {
141 solver->variable[index].locked = false;
142 }
143}
144
146{
147 int num_rhs = solver->num_rhs;
148
149 for (int i = 0; i < solver->num_variables; i++) {
150 LinearSolver::Variable *v = &solver->variable[i];
151 if (!v->locked) {
152 for (int j = 0; j < num_rhs; j++) {
153 solver->x[j][v->index] = v->value[j];
154 }
155 }
156 }
157}
158
160{
161 int num_rhs = solver->num_rhs;
162
163 for (int i = 0; i < solver->num_variables; i++) {
164 LinearSolver::Variable *v = &solver->variable[i];
165 if (!v->locked) {
166 for (int j = 0; j < num_rhs; j++) {
167 v->value[j] = solver->x[j][v->index];
168 }
169 }
170 }
171}
172
173/* Matrix */
174
176{
177 /* transition to matrix construction if necessary */
179 int n = 0;
180
181 for (int i = 0; i < solver->num_variables; i++) {
182 if (solver->variable[i].locked) {
183 solver->variable[i].index = ~0;
184 }
185 else {
186 solver->variable[i].index = n++;
187 }
188 }
189
190 int m = (solver->num_rows == 0) ? n : solver->num_rows;
191
192 solver->m = m;
193 solver->n = n;
194
195 assert(solver->least_squares || m == n);
196
197 /* reserve reasonable estimate */
198 solver->Mtriplets.clear();
199 solver->Mtriplets.reserve(std::max(m, n) * 3);
200
201 solver->b.resize(solver->num_rhs);
202 solver->x.resize(solver->num_rhs);
203
204 for (int i = 0; i < solver->num_rhs; i++) {
205 solver->b[i].setZero(m);
206 solver->x[i].setZero(n);
207 }
208
210
212 }
213}
214
215void EIG_linear_solver_matrix_add(LinearSolver *solver, int row, int col, double value)
216{
218 return;
219 }
220
222
223 if (!solver->least_squares && solver->variable[row].locked) {
224 ;
225 }
226 else if (solver->variable[col].locked) {
227 if (!solver->least_squares) {
228 row = solver->variable[row].index;
229 }
230
232 coeff.index = row;
233 coeff.value = value;
234 solver->variable[col].a.push_back(coeff);
235 }
236 else {
237 if (!solver->least_squares) {
238 row = solver->variable[row].index;
239 }
240 col = solver->variable[col].index;
241
242 /* direct insert into matrix is too slow, so use triplets */
243 EigenTriplet triplet(row, col, value);
244 solver->Mtriplets.push_back(triplet);
245 }
246}
247
248/* Right hand side */
249
250void EIG_linear_solver_right_hand_side_add(LinearSolver *solver, int rhs, int index, double value)
251{
253
254 if (solver->least_squares) {
255 solver->b[rhs][index] += value;
256 }
257 else if (!solver->variable[index].locked) {
258 index = solver->variable[index].index;
259 solver->b[rhs][index] += value;
260 }
261}
262
263/* Solve */
264
266{
267 /* nothing to solve, perhaps all variables were locked */
268 if (solver->m == 0 || solver->n == 0) {
269 return true;
270 }
271
272 bool result = true;
273
275
277 /* create matrix from triplets */
278 solver->M.resize(solver->m, solver->n);
279 solver->M.setFromTriplets(solver->Mtriplets.begin(), solver->Mtriplets.end());
280 solver->Mtriplets.clear();
281
282 /* create least squares matrix */
283 if (solver->least_squares) {
284 solver->MtM = solver->M.transpose() * solver->M;
285 }
286
287 /* convert M to compressed column format */
288 EigenSparseMatrix &M = (solver->least_squares) ? solver->MtM : solver->M;
289 M.makeCompressed();
290
291 /* perform sparse LU factorization */
292 EigenSparseLU *sparseLU = new EigenSparseLU();
293 solver->sparseLU = sparseLU;
294
295 sparseLU->compute(M);
296 result = (sparseLU->info() == Eigen::Success);
297
299 }
300
301 if (result) {
302 /* solve for each right hand side */
303 for (int rhs = 0; rhs < solver->num_rhs; rhs++) {
304 /* modify for locked variables */
305 EigenVectorX &b = solver->b[rhs];
306
307 for (int i = 0; i < solver->num_variables; i++) {
308 LinearSolver::Variable *variable = &solver->variable[i];
309
310 if (variable->locked) {
311 std::vector<LinearSolver::Coeff> &a = variable->a;
312
313 for (int j = 0; j < a.size(); j++) {
314 b[a[j].index] -= a[j].value * variable->value[rhs];
315 }
316 }
317 }
318
319 /* solve */
320 if (solver->least_squares) {
321 EigenVectorX Mtb = solver->M.transpose() * b;
322 solver->x[rhs] = solver->sparseLU->solve(Mtb);
323 }
324 else {
325 EigenVectorX &b = solver->b[rhs];
326 solver->x[rhs] = solver->sparseLU->solve(b);
327 }
328
329 if (solver->sparseLU->info() != Eigen::Success) {
330 result = false;
331 }
332 }
333
334 if (result) {
336 }
337 }
338
339 /* clear for next solve */
340 for (int rhs = 0; rhs < solver->num_rhs; rhs++) {
341 solver->b[rhs].setZero(solver->m);
342 }
343
344 return result;
345}
346
347/* Debugging */
348
350{
351 std::cout << "A:" << solver->M << std::endl;
352
353 for (int rhs = 0; rhs < solver->num_rhs; rhs++) {
354 std::cout << "b " << rhs << ":" << solver->b[rhs] << std::endl;
355 }
356
357 if (solver->MtM.rows() && solver->MtM.cols()) {
358 std::cout << "AtA:" << solver->MtM << std::endl;
359 }
360}
ATTR_WARN_UNUSED_RESULT const BMVert * v
local_group_size(16, 16) .push_constant(Type b
local_group_size(16, 16) .push_constant(Type rhs
#define NULL
uint col
LinearSolver * EIG_linear_solver_new(int num_rows, int num_columns, int num_rhs)
void EIG_linear_solver_print_matrix(LinearSolver *solver)
LinearSolver * EIG_linear_least_squares_solver_new(int num_rows, int num_columns, int num_rhs)
Eigen::VectorXd EigenVectorX
void EIG_linear_solver_variable_set(LinearSolver *solver, int rhs, int index, double value)
void EIG_linear_solver_right_hand_side_add(LinearSolver *solver, int rhs, int index, double value)
void EIG_linear_solver_variable_unlock(LinearSolver *solver, int index)
Eigen::SparseMatrix< double, Eigen::ColMajor > EigenSparseMatrix
Eigen::SparseLU< EigenSparseMatrix > EigenSparseLU
void EIG_linear_solver_delete(LinearSolver *solver)
Eigen::Triplet< double > EigenTriplet
static void linear_solver_variables_to_vector(LinearSolver *solver)
double EIG_linear_solver_variable_get(LinearSolver *solver, int rhs, int index)
static void linear_solver_vector_to_variables(LinearSolver *solver)
void EIG_linear_solver_matrix_add(LinearSolver *solver, int row, int col, double value)
bool EIG_linear_solver_solve(LinearSolver *solver)
static void linear_solver_ensure_matrix_construct(LinearSolver *solver)
void EIG_linear_solver_variable_lock(LinearSolver *solver, int index)
struct LinearSolver LinearSolver
#define M
std::vector< Coeff > a
EigenSparseMatrix MtM
EigenSparseLU * sparseLU
std::vector< Variable > variable
EigenSparseMatrix M
LinearSolver(int num_rows_, int num_variables_, int num_rhs_, bool lsq_)
std::vector< EigenVectorX > b
std::vector< EigenVectorX > x
std::vector< EigenTriplet > Mtriplets