Blender V5.0
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
10
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
23using EigenSparseMatrix = Eigen::SparseMatrix<double, Eigen::ColMajor>;
24using EigenSparseLU = Eigen::SparseLU<EigenSparseMatrix>;
25using EigenVectorX = Eigen::VectorXd;
26using EigenTriplet = Eigen::Triplet<double>;
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 = nullptr;
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_right_hand_sides)
103{
104 return new LinearSolver(num_rows, num_columns, num_right_hand_sides, false);
105}
106
108 int num_columns,
109 int num_right_hand_sides)
110{
111 return new LinearSolver(num_rows, num_columns, num_right_hand_sides, true);
112}
113
115{
116 delete solver;
117}
118
119/* Variables */
120
121void EIG_linear_solver_variable_set(LinearSolver *solver, int rhs, int index, double value)
122{
123 solver->variable[index].value[rhs] = value;
124}
125
126double EIG_linear_solver_variable_get(LinearSolver *solver, int rhs, int index)
127{
128 return solver->variable[index].value[rhs];
129}
130
132{
133 if (!solver->variable[index].locked) {
135 solver->variable[index].locked = true;
136 }
137}
138
140{
141 if (solver->variable[index].locked) {
143 solver->variable[index].locked = false;
144 }
145}
146
148{
149 int num_rhs = solver->num_rhs;
150
151 for (int i = 0; i < solver->num_variables; i++) {
152 LinearSolver::Variable *v = &solver->variable[i];
153 if (!v->locked) {
154 for (int j = 0; j < num_rhs; j++) {
155 solver->x[j][v->index] = v->value[j];
156 }
157 }
158 }
159}
160
162{
163 int num_rhs = solver->num_rhs;
164
165 for (int i = 0; i < solver->num_variables; i++) {
166 LinearSolver::Variable *v = &solver->variable[i];
167 if (!v->locked) {
168 for (int j = 0; j < num_rhs; j++) {
169 v->value[j] = solver->x[j][v->index];
170 }
171 }
172 }
173}
174
175/* Matrix */
176
178{
179 /* transition to matrix construction if necessary */
181 int n = 0;
182
183 for (int i = 0; i < solver->num_variables; i++) {
184 if (solver->variable[i].locked) {
185 solver->variable[i].index = ~0;
186 }
187 else {
188 solver->variable[i].index = n++;
189 }
190 }
191
192 int m = (solver->num_rows == 0) ? n : solver->num_rows;
193
194 solver->m = m;
195 solver->n = n;
196
197 assert(solver->least_squares || m == n);
198
199 /* reserve reasonable estimate */
200 solver->Mtriplets.clear();
201 solver->Mtriplets.reserve(std::max(m, n) * 3);
202
203 solver->b.resize(solver->num_rhs);
204 solver->x.resize(solver->num_rhs);
205
206 for (int i = 0; i < solver->num_rhs; i++) {
207 solver->b[i].setZero(m);
208 solver->x[i].setZero(n);
209 }
210
212
214 }
215}
216
217void EIG_linear_solver_matrix_add(LinearSolver *solver, int row, int col, double value)
218{
220 return;
221 }
222
224
225 if (!solver->least_squares && solver->variable[row].locked) {
226 ;
227 }
228 else if (solver->variable[col].locked) {
229 if (!solver->least_squares) {
230 row = solver->variable[row].index;
231 }
232
234 coeff.index = row;
235 coeff.value = value;
236 solver->variable[col].a.push_back(coeff);
237 }
238 else {
239 if (!solver->least_squares) {
240 row = solver->variable[row].index;
241 }
242 col = solver->variable[col].index;
243
244 /* direct insert into matrix is too slow, so use triplets */
245 EigenTriplet triplet(row, col, value);
246 solver->Mtriplets.push_back(triplet);
247 }
248}
249
250/* Right hand side */
251
252void EIG_linear_solver_right_hand_side_add(LinearSolver *solver, int rhs, int index, double value)
253{
255
256 if (solver->least_squares) {
257 solver->b[rhs][index] += value;
258 }
259 else if (!solver->variable[index].locked) {
260 index = solver->variable[index].index;
261 solver->b[rhs][index] += value;
262 }
263}
264
265/* Solve */
266
268{
269 /* nothing to solve, perhaps all variables were locked */
270 if (solver->m == 0 || solver->n == 0) {
271 return true;
272 }
273
274 bool result = true;
275
277
279 /* create matrix from triplets */
280 solver->M.resize(solver->m, solver->n);
281 solver->M.setFromTriplets(solver->Mtriplets.begin(), solver->Mtriplets.end());
282 solver->Mtriplets.clear();
283
284 /* create least squares matrix */
285 if (solver->least_squares) {
286 solver->MtM = solver->M.transpose() * solver->M;
287 }
288
289 /* convert M to compressed column format */
290 EigenSparseMatrix &M = (solver->least_squares) ? solver->MtM : solver->M;
291 M.makeCompressed();
292
293 /* perform sparse LU factorization */
294 EigenSparseLU *sparseLU = new EigenSparseLU();
295 solver->sparseLU = sparseLU;
296
297 sparseLU->compute(M);
298 result = (sparseLU->info() == Eigen::Success);
299
301 }
302
303 if (result) {
304 /* solve for each right hand side */
305 for (int rhs = 0; rhs < solver->num_rhs; rhs++) {
306 /* modify for locked variables */
307 EigenVectorX &b = solver->b[rhs];
308
309 for (int i = 0; i < solver->num_variables; i++) {
310 LinearSolver::Variable *variable = &solver->variable[i];
311
312 if (variable->locked) {
313 std::vector<LinearSolver::Coeff> &a = variable->a;
314
315 for (int j = 0; j < a.size(); j++) {
316 b[a[j].index] -= a[j].value * variable->value[rhs];
317 }
318 }
319 }
320
321 /* solve */
322 if (solver->least_squares) {
323 EigenVectorX Mtb = solver->M.transpose() * b;
324 solver->x[rhs] = solver->sparseLU->solve(Mtb);
325 }
326 else {
327 EigenVectorX &b = solver->b[rhs];
328 solver->x[rhs] = solver->sparseLU->solve(b);
329 }
330
331 if (solver->sparseLU->info() != Eigen::Success) {
332 result = false;
333 }
334 }
335
336 if (result) {
338 }
339 }
340
341 /* clear for next solve */
342 for (int rhs = 0; rhs < solver->num_rhs; rhs++) {
343 solver->b[rhs].setZero(solver->m);
344 }
345
346 return result;
347}
348
349/* Debugging */
350
352{
353 std::cout << "A:" << solver->M << std::endl;
354
355 for (int rhs = 0; rhs < solver->num_rhs; rhs++) {
356 std::cout << "b " << rhs << ":" << solver->b[rhs] << std::endl;
357 }
358
359 if (solver->MtM.rows() && solver->MtM.cols()) {
360 std::cout << "AtA:" << solver->MtM << std::endl;
361 }
362}
ATTR_WARN_UNUSED_RESULT const BMVert * v
uint col
#define assert(assertion)
Eigen::SparseMatrix< double, Eigen::ColMajor > EigenSparseMatrix
void EIG_linear_solver_print_matrix(LinearSolver *solver)
LinearSolver * EIG_linear_solver_new(int num_rows, int num_columns, int num_right_hand_sides)
void EIG_linear_solver_variable_set(LinearSolver *solver, int rhs, int index, double value)
Eigen::SparseLU< EigenSparseMatrix > EigenSparseLU
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)
LinearSolver * EIG_linear_least_squares_solver_new(int num_rows, int num_columns, int num_right_hand_sides)
void EIG_linear_solver_delete(LinearSolver *solver)
Eigen::Triplet< double > EigenTriplet
Eigen::VectorXd EigenVectorX
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)
#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
i
Definition text_draw.cc:230