A sparse MPC solver for walking motion generation.
as_chol_solve.cpp
Go to the documentation of this file.
1 
9 /****************************************
10  * INCLUDES
11  ****************************************/
12 
13 #include "as_chol_solve.h"
14 
15 #include <cmath> // sqrt
16 #include <cstring> // memset, memmove
17 
18 
19 /****************************************
20  * FUNCTIONS
21  ****************************************/
22 namespace AS
23 {
24  //==============================================
25  // constructors / destructors
26 
32  chol_solve::chol_solve (const int N) : ecL(N)
33  {
34  nu = new double[SMPC_NUM_VAR*N];
35  z = new double[SMPC_NUM_VAR*N];
36 
37  icL = new double*[N*2];
38  icL_mem = new double[SMPC_NUM_VAR*N*N*2];
39  for(int i = 0; i < N*2; ++i)
40  {
41  icL[i] = &icL_mem[i * SMPC_NUM_VAR*N];
42  }
43  }
44 
45 
50  {
51  if (icL != NULL)
52  {
53  delete icL_mem;
54  delete icL;
55  }
56  if (z != NULL)
57  {
58  delete z;
59  }
60  if (nu != NULL)
61  {
62  delete nu;
63  }
64  }
65  //==============================================
66 
67 
77  const problem_parameters& ppar,
78  const constraint& c,
79  const int ic_len,
80  double *row)
81  {
82  double i2H = ppar.i2Q[0]; // a'*inv(H) = a'*inv(H)*a
83  int first_num = c.ind; // first !=0 element
84 
85 
86  // reset memory
87  memset(row, 0, ic_len * sizeof(double));
88 
89 
90  // a * iH * -I
91  row[first_num] = -i2H * c.coef_x;
92  row[first_num + 3] = -i2H * c.coef_y;
93 
94  if (first_num/SMPC_NUM_STATE_VAR != ppar.N-1)
95  {
96  // a * iH * A'
97  row[first_num + 6] = i2H * c.coef_x;
98  row[first_num + 9] = i2H * c.coef_y;
99  }
100 
101  // initialize the last element in the row
102  // coef_x^2 + coef_y^2 = 1, since they are row in a rotation matrix
103  row[ic_len] = i2H;
104  }
105 
106 
107 
116  const problem_parameters& ppar,
117  const double *x,
118  double *dx)
119  {
120  double *s_nu = nu;
121  int i;
122 
123 
124  // generate L
125  ecL.form (ppar);
126 
127  // obtain s = E * x;
128  E.form_Ex (ppar, x, s_nu);
129 
130  // obtain nu
131  ecL.solve_forward(ppar.N, s_nu);
132  // make copy of z - it is constant
133  for (i = 0; i < ppar.N * SMPC_NUM_STATE_VAR; i += SMPC_NUM_STATE_VAR)
134  {
135  s_nu[i] = -s_nu[i];
136  s_nu[i+1] = -s_nu[i+1];
137  s_nu[i+2] = -s_nu[i+2];
138  s_nu[i+3] = -s_nu[i+3];
139  s_nu[i+4] = -s_nu[i+4];
140  s_nu[i+5] = -s_nu[i+5];
141  }
142  memmove(z, s_nu, sizeof(double) * ppar.N * SMPC_NUM_STATE_VAR);
143  ecL.solve_backward(ppar.N, s_nu);
144 
145  // - i2H * E' * nu
146  E.form_i2HETx (ppar, s_nu, dx);
147 
148 
149  // dx = -(x + inv(H) * E' * nu)
150  // ~~~~~~~~~~~~~~~~
151  // dx = -(x + dx )
152  for (i = 0; i < ppar.N*SMPC_NUM_VAR; i += SMPC_NUM_VAR)
153  {
154  // dx for state variables
155  dx[i] -= x[i] ;
156  dx[i+1] -= x[i+1];
157  dx[i+2] -= x[i+2];
158  dx[i+3] -= x[i+3];
159  dx[i+4] -= x[i+4];
160  dx[i+5] -= x[i+5];
161  dx[i+6] -= x[i+6];
162  dx[i+7] -= x[i+7];
163  }
164  }
165 
166 
177  const AS::problem_parameters& ppar,
178  const vector <AS::constraint>& active_set,
179  const double *x,
180  double *dx)
181  {
182  int ic_num = active_set.size()-1;
183  constraint c = active_set.back();
184 
185  update (ppar, c, ic_num);
186  update_z (ppar, c, ic_num, x);
187  resolve (ppar, active_set, x, dx);
188  }
189 
190 
200  const problem_parameters& ppar,
201  const constraint& c,
202  const int ic_num)
203  {
204  int i, j, k;
205 
206  double *new_row = icL[ic_num]; // current row in icL
207  // trailing elements of new_row corresponding to active constraints
208  double *new_row_end = &new_row[ppar.N*SMPC_NUM_STATE_VAR];
209 
210  int last_num = ic_num + ppar.N*SMPC_NUM_STATE_VAR; // the last !=0 element
211 
212 
213  // form row 'a' in the current row of icL
214  form_sa_row(ppar, c, last_num, new_row);
215 
216  // Forward substitution using L for equality constraints
217  ecL.solve_forward(ppar.N, new_row, c.cind/2);
218 
219 
220  // update the trailing elements of new_row using the
221  // elements computed using forward substitution above
222  for(i = c.cind/2 * SMPC_NUM_STATE_VAR;
223  i < ppar.N * SMPC_NUM_STATE_VAR;
224  i += SMPC_NUM_STATE_VAR)
225  {
226  // make a copy for faster computations
227  double tmp_copy_el[6] = {new_row[i],
228  new_row[i+1],
229  new_row[i+2],
230  new_row[i+3],
231  new_row[i+4],
232  new_row[i+5]};
233 
234  // update the last (diagonal) number in the row
235  new_row[last_num] -= tmp_copy_el[0] * tmp_copy_el[0]
236  + tmp_copy_el[1] * tmp_copy_el[1]
237  + tmp_copy_el[2] * tmp_copy_el[2]
238  + tmp_copy_el[3] * tmp_copy_el[3]
239  + tmp_copy_el[4] * tmp_copy_el[4]
240  + tmp_copy_el[5] * tmp_copy_el[5];
241 
242  // update elements after N*SMPC_NUM_STATE_VAR using the previously added rows
243  // in icL
244  for (j = 0; j < ic_num; ++j)
245  {
246  new_row_end[j] -= tmp_copy_el[0] * icL[j][i]
247  + tmp_copy_el[1] * icL[j][i+1]
248  + tmp_copy_el[2] * icL[j][i+2]
249  + tmp_copy_el[3] * icL[j][i+3]
250  + tmp_copy_el[4] * icL[j][i+4]
251  + tmp_copy_el[5] * icL[j][i+5];
252  }
253  }
254 
255 
256  // update elements in the end of icL
257  for(i = SMPC_NUM_STATE_VAR * ppar.N, k = 0; i < last_num; ++i, ++k)
258  {
259  new_row[i] /= icL[k][i];
260  double tmp_copy_el = new_row[i];
261 
262  // determine number in the row of L
263 
264  // update the last (diagonal) number in the row
265  new_row[last_num] -= tmp_copy_el * tmp_copy_el;
266 
267  for (j = k+1; j < ic_num; ++j)
268  {
269  new_row_end[j] -= tmp_copy_el * icL[j][i];
270  }
271  }
272 
273  // square root of the diagonal element
274  new_row[last_num] = sqrt(new_row[last_num]);
275  }
276 
277 
278 
288  const problem_parameters& ppar,
289  const constraint& c,
290  const int ic_num,
291  const double *x)
292  {
293  // update lagrange multipliers
294  const int zind = ppar.N*SMPC_NUM_STATE_VAR + ic_num;
295  // sn
296  const int first_num = c.ind; // first !=0 element
297 
298  double zn = -x[first_num] * c.coef_x
299  -x[first_num+3] * c.coef_y;
300 
301  // zn
302  memmove (nu, z, zind * sizeof(double));
303  for (int i = first_num; i < zind; ++i)
304  {
305  zn -= z[i] * icL[ic_num][i];
306  }
307  nu[zind] = z[zind] = zn/icL[ic_num][zind];
308  return;
309  }
310 
311 
312 
323  const AS::problem_parameters& ppar,
324  const vector <AS::constraint>& active_set,
325  const double *x,
326  double *dx)
327  {
328  int i;
329  const int nW = active_set.size();
330 
331  // backward substitution for icL
332  for (i = nW-1; i >= 0; --i)
333  {
334  const int last_el_num = i + ppar.N*SMPC_NUM_STATE_VAR;
335  nu[last_el_num] /= icL[i][last_el_num];
336 
337  for (int j = active_set[i].ind; j < last_el_num; ++j)
338  {
339  nu[j] -= nu[last_el_num] * icL[i][j];
340  }
341  }
342  // backward substitution for ecL
343  ecL.solve_backward(ppar.N, nu);
344 
345 
346  // - i2H * E' * nu
347  E.form_i2HETx (ppar, nu, dx);
348 
349 
350  // dx = -(x + inv(H) * E' * nu)
351  // ~~~~~~~~~~~~~~~~
352  // dx = -(x + dx )
353  for (i = 0; i < ppar.N*SMPC_NUM_VAR; i += SMPC_NUM_VAR)
354  {
355  // dx for state variables
356  dx[i] -= x[i] ;
357  dx[i+1] -= x[i+1];
358  dx[i+2] -= x[i+2];
359  dx[i+3] -= x[i+3];
360  dx[i+4] -= x[i+4];
361  dx[i+5] -= x[i+5];
362  dx[i+6] -= x[i+6];
363  dx[i+7] -= x[i+7];
364  }
365 
366  // -iH * A(W,:)' * lambda
367  const double *lambda = get_lambda(ppar);
368  const double i2Q0 = ppar.i2Q[0];
369  for (i = 0; i < nW; ++i)
370  {
371  constraint c = active_set[i];
372  dx[c.ind] -= i2Q0 * c.coef_x * lambda[i];
373  dx[c.ind+3] -= i2Q0 * c.coef_y * lambda[i];
374  }
375  }
376 
377 
378 
392  const AS::problem_parameters& ppar,
393  const vector <AS::constraint>& active_set,
394  const int ind_exclude,
395  const double *x,
396  double *dx)
397  {
398  const int nW = active_set.size();
399  const int last_el_ind = ppar.N*SMPC_NUM_STATE_VAR + ind_exclude;
400 
401  // for each element of z affected by removed constraint
402  // find a base that stays the same
403  double z_tmp = 0;
404  for (int i = nW; i > ind_exclude; --i)
405  {
406  const int zind = ppar.N*SMPC_NUM_STATE_VAR + i;
407  double zn = z[zind] * icL[i][zind];
408  z[zind] = z_tmp;
409 
410  for (int j = last_el_ind; j < zind; ++j)
411  {
412  zn += z[j] * icL[i][j];
413  }
414  z_tmp = zn;
415  }
416  z[last_el_ind] = z_tmp;
417 
418 
419  // downdate L
420  downdate (ppar, nW, ind_exclude, x);
421 
422 
423  // recompute elements of z
424  for (int i = ind_exclude; i < nW; i++)
425  {
426  int zind = ppar.N*SMPC_NUM_STATE_VAR + i;
427  double zn = z[zind];
428 
429  // zn
430  // start from the first !=0 element
431  for (int j = last_el_ind; j < zind; j++)
432  {
433  zn -= z[j] * icL[i][j];
434  }
435  z[zind] = zn/icL[i][zind];
436  }
437 
438  // copy z to nu
439  memmove(nu, z, sizeof(double) * (ppar.N*SMPC_NUM_STATE_VAR + nW));
440 
441  resolve (ppar, active_set, x, dx);
442  }
443 
444 
445 
451  {
452  return(&nu[SMPC_NUM_STATE_VAR*ppar.N]);
453  }
454 
455 
456 
457 
467  const problem_parameters& ppar,
468  const int nW,
469  const int ind_exclude,
470  const double *x)
471  {
472  // Shuffle memory pointers to avoid copying of the data.
473  double * downdate_row = icL[ind_exclude];
474  for (int i = ind_exclude + 1; i < nW + 1; i++)
475  {
476  icL[i-1] = icL[i];
477  }
478  icL[nW] = downdate_row;
479 
480 
481  for (int i = ind_exclude; i < nW; i++)
482  {
483  int el_index = SMPC_NUM_STATE_VAR*ppar.N + i;
484  double *cur_el = &icL[i][el_index];
485  double x1 = cur_el[0];
486  double x2 = cur_el[1];
487  double cosT, sinT;
488 
489 
490  // Givens rotation matrix
491  if (abs(x2) >= abs(x1))
492  {
493  double t = x1/x2;
494  sinT = 1/sqrt(1 + t*t);
495  cosT = sinT*t;
496  }
497  else
498  {
499  double t = x2/x1;
500  cosT = 1/sqrt(1 + t*t);
501  sinT = cosT*t;
502  }
503 
504 
505  // update elements in the current line
506  cur_el[0] = cosT*x1 + sinT*x2;
507  cur_el[1] = 0;
508 
509  // change sign if needed (diagonal elements of Cholesky
510  // decomposition must be positive)
511  double sign = copysign(1, cur_el[0]);
512  cur_el[0] = fabs(cur_el[0]);
513 
514  // update the lines below the current one.
515  for (int j = i + 1; j < nW; j++)
516  {
517  x1 = icL[j][el_index];
518  x2 = icL[j][el_index + 1];
519 
520  icL[j][el_index] = sign * (cosT*x1 + sinT*x2);
521  icL[j][el_index + 1] = -sinT*x1 + cosT*x2;
522  }
523  }
524  }
525 }
#define SMPC_NUM_STATE_VAR
Number of state variables.
Definition: smpc_solver.h:24
void form_i2HETx(const problem_parameters &, const double *, double *)
Forms i2H * E' * x.
Definition: as_matrix_E.cpp:78
void up_resolve(const AS::problem_parameters &, const vector< AS::constraint > &, const double *, double *)
A wrapper around private functions, which update Cholesky factor and resolve the system.
void update(const AS::problem_parameters &, const AS::constraint &, const int)
Adds a row corresponding to some inequality constraint to L, see 'pCholUpAlg'.
double coef_y
Coefficients.
Definition: as_constraint.h:67
int cind
The sequential number of the constraint.
Definition: as_constraint.h:58
void resolve(const AS::problem_parameters &, const vector< AS::constraint > &, const double *, double *)
Determines feasible descent direction with respect to added inequality constraints.
#define SMPC_NUM_VAR
Total number of variables.
Definition: smpc_solver.h:28
void form_Ex(const problem_parameters &, const double *, double *)
Forms E*x.
Definition: as_matrix_E.cpp:29
double coef_x
Coefficients.
Definition: as_constraint.h:66
AS::matrix_E E
matrix of equality AS::constraints
Definition: as_chol_solve.h:68
void form_sa_row(const AS::problem_parameters &, const AS::constraint &, const int, double *)
Forms row vector 's_a' (pCholUp).
A set of problem parameters.
Defines constraints associated with states of the system.
Definition: as_constraint.h:26
double ** icL
L for inequality AS::constraints.
Definition: as_chol_solve.h:74
void form(const problem_parameters &)
Builds matrix L.
void solve(const AS::problem_parameters &, const double *, double *)
Determines feasible descent direction.
void update_z(const AS::problem_parameters &, const AS::constraint &, const int, const double *)
Adjust vector 'z' after update.
int ind
Index of the first element of a state in the vector of states.
Definition: as_constraint.h:61
double * icL_mem
All lines of icL are stored in one chunk of memory.
Definition: as_chol_solve.h:77
double * nu
Vector of Lagrange multipliers.
Definition: as_chol_solve.h:65
AS::matrix_ecL ecL
L for equality AS::constraints.
Definition: as_chol_solve.h:71
double * z
Vector z.
Definition: as_chol_solve.h:80
void solve_forward(const int, double *, const int start_ind=0) const
Solve system ecL * x = b using forward substitution.
~chol_solve()
Destructor.
void downdate(const AS::problem_parameters &, const int, const int, const double *)
Delete a line from icL, see page 'pCholDown'.
double * get_lambda(const AS::problem_parameters &)
void solve_backward(const int, double *) const
Solve system ecL' * x = b using backward substitution.
chol_solve(const int)
Constructor.
void down_resolve(const AS::problem_parameters &, const vector< AS::constraint > &, const int, const double *, double *)
A wrapper around private functions, which downdate Cholesky factor and resolve the system.