Blender  V2.93
linear_solver.cc
Go to the documentation of this file.
1 /*
2  * Sparse linear solver.
3  * Copyright (C) 2004 Bruno Levy
4  * Copyright (C) 2005-2015 Blender Foundation
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  * If you modify this software, you should include a notice giving the
21  * name of the person performing the modification, the date of modification,
22  * and the reason for such modification.
23  */
24 
25 #include "linear_solver.h"
26 
27 #include <Eigen/Sparse>
28 
29 #include <algorithm>
30 #include <cassert>
31 #include <cstdlib>
32 #include <iostream>
33 #include <vector>
34 
35 /* Eigen data structures */
36 
37 typedef Eigen::SparseMatrix<double, Eigen::ColMajor> EigenSparseMatrix;
38 typedef Eigen::SparseLU<EigenSparseMatrix> EigenSparseLU;
39 typedef Eigen::VectorXd EigenVectorX;
40 typedef Eigen::Triplet<double> EigenTriplet;
41 
42 /* Linear Solver data structure */
43 
44 struct LinearSolver {
45  struct Coeff {
47  {
48  index = 0;
49  value = 0.0;
50  }
51 
52  int index;
53  double value;
54  };
55 
56  struct Variable {
58  {
59  memset(value, 0, sizeof(value));
60  locked = false;
61  index = 0;
62  }
63 
64  double value[4];
65  bool locked;
66  int index;
67  std::vector<Coeff> a;
68  };
69 
71 
72  LinearSolver(int num_rows_, int num_variables_, int num_rhs_, bool lsq_)
73  {
74  assert(num_variables_ > 0);
75  assert(num_rhs_ <= 4);
76 
78  m = 0;
79  n = 0;
80  sparseLU = NULL;
81  num_variables = num_variables_;
82  num_rhs = num_rhs_;
83  num_rows = num_rows_;
84  least_squares = lsq_;
85 
86  variable.resize(num_variables);
87  }
88 
90  {
91  delete sparseLU;
92  }
93 
95 
96  int n;
97  int m;
98 
99  std::vector<EigenTriplet> Mtriplets;
102  std::vector<EigenVectorX> b;
103  std::vector<EigenVectorX> x;
104 
106 
108  std::vector<Variable> variable;
109 
110  int num_rows;
111  int num_rhs;
112 
114 };
115 
116 LinearSolver *EIG_linear_solver_new(int num_rows, int num_columns, int num_rhs)
117 {
118  return new LinearSolver(num_rows, num_columns, num_rhs, false);
119 }
120 
121 LinearSolver *EIG_linear_least_squares_solver_new(int num_rows, int num_columns, int num_rhs)
122 {
123  return new LinearSolver(num_rows, num_columns, num_rhs, true);
124 }
125 
127 {
128  delete solver;
129 }
130 
131 /* Variables */
132 
133 void EIG_linear_solver_variable_set(LinearSolver *solver, int rhs, int index, double value)
134 {
135  solver->variable[index].value[rhs] = value;
136 }
137 
138 double EIG_linear_solver_variable_get(LinearSolver *solver, int rhs, int index)
139 {
140  return solver->variable[index].value[rhs];
141 }
142 
144 {
145  if (!solver->variable[index].locked) {
147  solver->variable[index].locked = true;
148  }
149 }
150 
152 {
153  if (solver->variable[index].locked) {
155  solver->variable[index].locked = false;
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  solver->x[j][v->index] = v->value[j];
168  }
169  }
170 }
171 
173 {
174  int num_rhs = solver->num_rhs;
175 
176  for (int i = 0; i < solver->num_variables; i++) {
177  LinearSolver::Variable *v = &solver->variable[i];
178  if (!v->locked) {
179  for (int j = 0; j < num_rhs; j++)
180  v->value[j] = solver->x[j][v->index];
181  }
182  }
183 }
184 
185 /* Matrix */
186 
188 {
189  /* transition to matrix construction if necessary */
191  int n = 0;
192 
193  for (int i = 0; i < solver->num_variables; i++) {
194  if (solver->variable[i].locked)
195  solver->variable[i].index = ~0;
196  else
197  solver->variable[i].index = n++;
198  }
199 
200  int m = (solver->num_rows == 0) ? n : solver->num_rows;
201 
202  solver->m = m;
203  solver->n = n;
204 
205  assert(solver->least_squares || m == n);
206 
207  /* reserve reasonable estimate */
208  solver->Mtriplets.clear();
209  solver->Mtriplets.reserve(std::max(m, n) * 3);
210 
211  solver->b.resize(solver->num_rhs);
212  solver->x.resize(solver->num_rhs);
213 
214  for (int i = 0; i < solver->num_rhs; i++) {
215  solver->b[i].setZero(m);
216  solver->x[i].setZero(n);
217  }
218 
220 
222  }
223 }
224 
225 void EIG_linear_solver_matrix_add(LinearSolver *solver, int row, int col, double value)
226 {
228  return;
229 
231 
232  if (!solver->least_squares && solver->variable[row].locked)
233  ;
234  else if (solver->variable[col].locked) {
235  if (!solver->least_squares)
236  row = solver->variable[row].index;
237 
238  LinearSolver::Coeff coeff;
239  coeff.index = row;
240  coeff.value = value;
241  solver->variable[col].a.push_back(coeff);
242  }
243  else {
244  if (!solver->least_squares)
245  row = solver->variable[row].index;
246  col = solver->variable[col].index;
247 
248  /* direct insert into matrix is too slow, so use triplets */
249  EigenTriplet triplet(row, col, value);
250  solver->Mtriplets.push_back(triplet);
251  }
252 }
253 
254 /* Right hand side */
255 
256 void EIG_linear_solver_right_hand_side_add(LinearSolver *solver, int rhs, int index, double value)
257 {
259 
260  if (solver->least_squares) {
261  solver->b[rhs][index] += value;
262  }
263  else if (!solver->variable[index].locked) {
264  index = solver->variable[index].index;
265  solver->b[rhs][index] += value;
266  }
267 }
268 
269 /* Solve */
270 
272 {
273  /* nothing to solve, perhaps all variables were locked */
274  if (solver->m == 0 || solver->n == 0)
275  return true;
276 
277  bool result = true;
278 
280 
282  /* create matrix from triplets */
283  solver->M.resize(solver->m, solver->n);
284  solver->M.setFromTriplets(solver->Mtriplets.begin(), solver->Mtriplets.end());
285  solver->Mtriplets.clear();
286 
287  /* create least squares matrix */
288  if (solver->least_squares)
289  solver->MtM = solver->M.transpose() * solver->M;
290 
291  /* convert M to compressed column format */
292  EigenSparseMatrix &M = (solver->least_squares) ? solver->MtM : solver->M;
293  M.makeCompressed();
294 
295  /* perform sparse LU factorization */
296  EigenSparseLU *sparseLU = new EigenSparseLU();
297  solver->sparseLU = sparseLU;
298 
299  sparseLU->compute(M);
300  result = (sparseLU->info() == Eigen::Success);
301 
303  }
304 
305  if (result) {
306  /* solve for each right hand side */
307  for (int rhs = 0; rhs < solver->num_rhs; rhs++) {
308  /* modify for locked variables */
309  EigenVectorX &b = solver->b[rhs];
310 
311  for (int i = 0; i < solver->num_variables; i++) {
312  LinearSolver::Variable *variable = &solver->variable[i];
313 
314  if (variable->locked) {
315  std::vector<LinearSolver::Coeff> &a = variable->a;
316 
317  for (int j = 0; j < a.size(); j++)
318  b[a[j].index] -= a[j].value * variable->value[rhs];
319  }
320  }
321 
322  /* solve */
323  if (solver->least_squares) {
324  EigenVectorX Mtb = solver->M.transpose() * b;
325  solver->x[rhs] = solver->sparseLU->solve(Mtb);
326  }
327  else {
328  EigenVectorX &b = solver->b[rhs];
329  solver->x[rhs] = solver->sparseLU->solve(b);
330  }
331 
332  if (solver->sparseLU->info() != Eigen::Success)
333  result = false;
334  }
335 
336  if (result)
338  }
339 
340  /* clear for next solve */
341  for (int rhs = 0; rhs < solver->num_rhs; rhs++)
342  solver->b[rhs].setZero(solver->m);
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  if (solver->MtM.rows() && solver->MtM.cols())
357  std::cout << "AtA:" << solver->MtM << std::endl;
358 }
ATTR_WARN_UNUSED_RESULT const BMVert * v
uint col
void EIG_linear_solver_print_matrix(LinearSolver *solver)
Eigen::VectorXd EigenVectorX
void EIG_linear_solver_variable_set(LinearSolver *solver, int rhs, int index, double value)
LinearSolver * EIG_linear_solver_new(int num_rows, int num_columns, int num_rhs)
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
LinearSolver * EIG_linear_least_squares_solver_new(int num_rows, int num_columns, int num_rhs)
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
Definition: linear_solver.h:35
#define M
static unsigned a[3]
Definition: RandGen.cpp:92
std::vector< Coeff > a
EigenSparseMatrix MtM
EigenSparseLU * sparseLU
@ STATE_VARIABLES_CONSTRUCT
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
float max