GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: src/ell/eigen.c Lines: 0 296 0.0 %
Date: 2017-05-26 Branches: 0 127 0.0 %

Line Branch Exec Source
1
/*
2
  Teem: Tools to process and visualize scientific data and images             .
3
  Copyright (C) 2013, 2012, 2011, 2010, 2009  University of Chicago
4
  Copyright (C) 2008, 2007, 2006, 2005  Gordon Kindlmann
5
  Copyright (C) 2004, 2003, 2002, 2001, 2000, 1999, 1998  University of Utah
6
7
  This library is free software; you can redistribute it and/or
8
  modify it under the terms of the GNU Lesser General Public License
9
  (LGPL) as published by the Free Software Foundation; either
10
  version 2.1 of the License, or (at your option) any later version.
11
  The terms of redistributing and/or modifying this software also
12
  include exceptions to the LGPL that facilitate static linking.
13
14
  This library is distributed in the hope that it will be useful,
15
  but WITHOUT ANY WARRANTY; without even the implied warranty of
16
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17
  Lesser General Public License for more details.
18
19
  You should have received a copy of the GNU Lesser General Public License
20
  along with this library; if not, write to Free Software Foundation, Inc.,
21
  51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22
*/
23
24
25
#include "ell.h"
26
27
/* lop A
28
  fprintf(stderr, "_ellAlign3: ----------\n");
29
  fprintf(stderr, "_ellAlign3: v0 = %g %g %g\n", (v+0)[0], (v+0)[1], (v+0)[2]);
30
  fprintf(stderr, "_ellAlign3: v3 = %g %g %g\n", (v+3)[0], (v+3)[1], (v+3)[2]);
31
  fprintf(stderr, "_ellAlign3: v6 = %g %g %g\n", (v+6)[0], (v+6)[1], (v+6)[2]);
32
  fprintf(stderr, "_ellAlign3: d = %g %g %g -> %d %d %d\n",
33
          d0, d1, d2, Mi, ai, bi);
34
  fprintf(stderr, "_ellAlign3:  pre dot signs (03, 06, 36): %d %d %d\n",
35
          airSgn(ELL_3V_DOT(v+0, v+3)),
36
          airSgn(ELL_3V_DOT(v+0, v+6)),
37
          airSgn(ELL_3V_DOT(v+3, v+6)));
38
  */
39
40
/* lop B
41
  fprintf(stderr, "_ellAlign3: v0 = %g %g %g\n", (v+0)[0], (v+0)[1], (v+0)[2]);
42
  fprintf(stderr, "_ellAlign3: v3 = %g %g %g\n", (v+3)[0], (v+3)[1], (v+3)[2]);
43
  fprintf(stderr, "_ellAlign3: v6 = %g %g %g\n", (v+6)[0], (v+6)[1], (v+6)[2]);
44
  fprintf(stderr, "_ellAlign3:  post dot signs %d %d %d\n",
45
          airSgn(ELL_3V_DOT(v+0, v+3)),
46
          airSgn(ELL_3V_DOT(v+0, v+6)),
47
          airSgn(ELL_3V_DOT(v+3, v+6)));
48
  if (airSgn(ELL_3V_DOT(v+0, v+3)) < 0
49
      || airSgn(ELL_3V_DOT(v+0, v+6)) < 0
50
      || airSgn(ELL_3V_DOT(v+3, v+6)) < 0) {
51
    exit(1);
52
  }
53
  */
54
55
/*
56
******** ell_quadratic()
57
**
58
** finds real roots of A*x^2 + B*x + C.
59
**
60
** records the found roots in the given root array, and returns a
61
** value from the ell_quadratic_root* enum:
62
**
63
**   ell_quadratic_root_two:
64
**      two distinct roots root[0] > root[1]
65
**   ell_quadratic_root_complex:
66
**      two complex conjugate roots at root[0] +/- i*root[1]
67
**   ell_quadratic_root_double:
68
**      a repeated root root[0] == root[1]
69
**
70
** HEY simple as this code may seem, it has definitely numerical
71
** issues that have not been explored or fixed, such as what if A is
72
** near 0.  Also correctly handling the transition from double root to
73
** complex roots needs to be re-thought, as well as this issue:
74
** http://people.csail.mit.edu/bkph/articles/Quadratics.pdf Should
75
** also understand http://www.cs.berkeley.edu/~wkahan/Qdrtcs.pdf
76
**
77
** This does NOT use biff
78
*/
79
int
80
ell_quadratic(double root[2], double A, double B, double C) {
81
  /* static const char me[]="ell_quadratic"; */
82
  int ret;
83
  double disc, rd, tmp, eps=1.0E-12;
84
85
  disc = B*B - 4*A*C;
86
  if (disc > 0) {
87
    rd = sqrt(disc);
88
    root[0] = (-B + rd)/(2*A);
89
    root[1] = (-B - rd)/(2*A);
90
    if (root[0] < root[1]) {
91
      ELL_SWAP2(root[0], root[1], tmp);
92
    }
93
    ret = ell_quadratic_root_two;
94
  } else if (disc < -eps) {
95
    root[0] = -B/(2*A);
96
    root[1] = sqrt(-disc)/(2*A);
97
    ret = ell_quadratic_root_complex;
98
  } else {
99
    /* 0 == disc or only *very slightly* negative */
100
    root[0] = root[1] = -B/(2*A);
101
    ret = ell_quadratic_root_double;
102
  }
103
  return ret;
104
}
105
106
int
107
ell_2m_eigenvalues_d(double eval[2], const double m[4]) {
108
  double A, B, C;
109
  int ret;
110
111
  A = 1;
112
  B = -m[0] - m[3];
113
  C = m[0]*m[3] - m[1]*m[2];
114
  ret = ell_quadratic(eval, A, B, C);
115
  return ret;
116
}
117
118
void
119
ell_2m_1d_nullspace_d(double ans[2], const double _n[4]) {
120
  /* static const char me[]="ell_2m_1d_nullspace_d"; */
121
  double n[4], dot, len, rowv[2];
122
123
  ELL_4V_COPY(n, _n);
124
  dot = ELL_2V_DOT(n + 2*0, n + 2*1);
125
  /*
126
  fprintf(stderr, "!%s: n = {{%g,%g},{%g,%g}}\n", me,
127
          n[0], n[1], n[2], n[3]);
128
  fprintf(stderr, "!%s: dot = %g\n", me, dot);
129
  */
130
  if (dot > 0) {
131
    ELL_2V_ADD2(rowv, n + 2*0, n + 2*1);
132
  } else {
133
    ELL_2V_SUB(rowv, n + 2*0, n + 2*1);
134
  }
135
  /* fprintf(stderr, "!%s: rowv = %g %g\n", me, rowv[0], rowv[1]); */
136
  /* have found good description of what's perpendicular nullspace,
137
     so now perpendicularize it */
138
  ans[0] = rowv[1];
139
  ans[1] = -rowv[0];
140
  ELL_2V_NORM(ans, ans, len);
141
  /*
142
  if (!(AIR_EXISTS(ans[0]) && AIR_EXISTS(ans[1]))) {
143
    fprintf(stderr, "!%s: bad! %g %g\n", me, ans[0], ans[1]);
144
  }
145
  */
146
  return;
147
}
148
149
/*
150
******** ell_2m_eigensolve_d
151
**
152
** Eigensolve 2x2 matrix, which may be asymmetric
153
*/
154
int
155
ell_2m_eigensolve_d(double eval[2], double evec[4], const double m[4]) {
156
  /* static const char me[]="ell_2m_eigensolve_d"; */
157
  double nul[4], ident[4] = {1,0,0,1};
158
  int ret;
159
160
  ret = ell_2m_eigenvalues_d(eval, m);
161
  /*
162
  fprintf(stderr, "!%s: m = {{%.17g,%.17g},{%.17g,%.17g}} -> "
163
          "%s evals (%.17g,%.17g)\n", me, m[0], m[1], m[2], m[3],
164
          airEnumStr(ell_quadratic_root, ret), eval[0], eval[1]);
165
  */
166
  switch (ret) {
167
  case ell_quadratic_root_two:
168
    ELL_4V_SCALE_ADD2(nul, 1.0, m, -eval[0], ident);
169
    ell_2m_1d_nullspace_d(evec + 2*0, nul);
170
    /*
171
    fprintf(stderr, "!%s: eval=%.17g -> nul {{%.17g,%.17g},{%.17g,%.17g}} "
172
            "-> evec %.17g %.17g\n", me, eval[0],
173
            nul[0], nul[1], nul[2], nul[3],
174
            (evec + 2*0)[0], (evec + 2*0)[1]);
175
    */
176
    ELL_4V_SCALE_ADD2(nul, 1.0, m, -eval[1], ident);
177
    ell_2m_1d_nullspace_d(evec + 2*1, nul);
178
    /*
179
    fprintf(stderr, "!%s: eval=%.17g -> nul {{%.17g,%.17g},{%.17g,%.17g}} "
180
            "-> evec %.17g %.17g\n", me, eval[1],
181
            nul[0], nul[1], nul[2], nul[3],
182
            (evec + 2*1)[0], (evec + 2*1)[1]);
183
    */
184
    break;
185
  case ell_quadratic_root_double:
186
    /* fprintf(stderr, "!%s: double eval=%.17g\n", me, eval[0]); */
187
    ELL_4V_SCALE_ADD2(nul, 1.0, m, -eval[0], ident);
188
    /*
189
    fprintf(stderr, "!%s: nul = {{%.17g,%.17g},{%.17g,%.17g}} (len %.17g)\n",
190
            me, nul[0], nul[1], nul[2], nul[3], ELL_4V_LEN(nul));
191
    */
192
    if (ELL_4V_DOT(nul, nul)) {
193
      /* projecting out the nullspace produced non-zero matrix,
194
         (possibly from an asymmetric matrix) so there is real
195
         orientation to recover */
196
      ell_2m_1d_nullspace_d(evec + 2*0, nul);
197
      ELL_2V_COPY(evec + 2*1, evec + 2*0);
198
    } else {
199
      /* so this was isotropic symmetric; invent orientation */
200
      ELL_2V_SET(evec + 2*0, 1, 0);
201
      ELL_2V_SET(evec + 2*1, 0, 1);
202
    }
203
    break;
204
  case ell_quadratic_root_complex:
205
    /* HEY punting for now */
206
    ELL_2V_SET(evec + 2*0, 0.5, 0);
207
    ELL_2V_SET(evec + 2*1, 0, 0.5);
208
    break;
209
  default:
210
    /* fprintf(stderr, "%s: unexpected solution indicator %d\n", me, ret); */
211
    break;
212
  }
213
  return ret;
214
}
215
216
217
void
218
_ell_align3_d(double v[9]) {
219
  double d0, d1, d2;
220
  int Mi, ai, bi;
221
222
  d0 = ELL_3V_DOT(v+0, v+0);
223
  d1 = ELL_3V_DOT(v+3, v+3);
224
  d2 = ELL_3V_DOT(v+6, v+6);
225
  Mi = ELL_MAX3_IDX(d0, d1, d2);
226
  ai = (Mi + 1) % 3;
227
  bi = (Mi + 2) % 3;
228
  /* lop A */
229
  if (ELL_3V_DOT(v+3*Mi, v+3*ai) < 0) {
230
    ELL_3V_SCALE(v+3*ai, -1, v+3*ai);
231
  }
232
  if (ELL_3V_DOT(v+3*Mi, v+3*bi) < 0) {
233
    ELL_3V_SCALE(v+3*bi, -1, v+3*bi);
234
  }
235
  /* lob B */
236
  /* we can't guarantee that dot(v+3*ai,v+3*bi) > 0 . . . */
237
}
238
239
/*
240
** leaves v+3*0 untouched, but makes sure that v+3*0, v+3*1, and v+3*2
241
** are mutually orthogonal.  Also leaves the magnitudes of all
242
** vectors unchanged.
243
*/
244
void
245
_ell_3m_enforce_orthogonality(double v[9]) {
246
  double d00, d10, d11, d20, d21, d22, scl, tv[3];
247
248
  d00 = ELL_3V_DOT(v+3*0, v+3*0);
249
  d10 = ELL_3V_DOT(v+3*1, v+3*0);
250
  d11 = ELL_3V_DOT(v+3*1, v+3*1);
251
  ELL_3V_SCALE_ADD2(tv, 1, v+3*1, -d10/d00, v+3*0);
252
  scl = sqrt(d11/ELL_3V_DOT(tv, tv));
253
  ELL_3V_SCALE(v+3*1, scl, tv);
254
  d20 = ELL_3V_DOT(v+3*2, v+3*0);
255
  d21 = ELL_3V_DOT(v+3*2, v+3*1);
256
  d22 = ELL_3V_DOT(v+3*2, v+3*2);
257
  ELL_3V_SCALE_ADD3(tv, 1, v+3*2, -d20/d00, v+3*0, -d21/d00, v+3*1);
258
  scl = sqrt(d22/ELL_3V_DOT(tv, tv));
259
  ELL_3V_SCALE(v+3*2, scl, tv);
260
  return;
261
}
262
263
/*
264
** makes sure that v+3*2 has a positive dot product with
265
** cross product of v+3*0 and v+3*1
266
*/
267
void
268
_ell_3m_make_right_handed_d(double v[9]) {
269
  double x[3];
270
271
  ELL_3V_CROSS(x, v+3*0, v+3*1);
272
  if (0 > ELL_3V_DOT(x, v+3*2)) {
273
    ELL_3V_SCALE(v+3*2, -1, v+3*2);
274
  }
275
}
276
277
/* lop A
278
  fprintf(stderr, "===  pre ===\n");
279
  fprintf(stderr, "crosses:  %g %g %g\n", (t+0)[0], (t+0)[1], (t+0)[2]);
280
  fprintf(stderr, "          %g %g %g\n", (t+3)[0], (t+3)[1], (t+3)[2]);
281
  fprintf(stderr, "          %g %g %g\n", (t+6)[0], (t+6)[1], (t+6)[2]);
282
  fprintf(stderr, "cross dots:  %g %g %g\n",
283
          ELL_3V_DOT(t+0, t+3), ELL_3V_DOT(t+0, t+6), ELL_3V_DOT(t+3, t+6));
284
*/
285
286
/* lop B
287
  fprintf(stderr, "=== post ===\n");
288
  fprintf(stderr, "crosses:  %g %g %g\n", (t+0)[0], (t+0)[1], (t+0)[2]);
289
  fprintf(stderr, "          %g %g %g\n", (t+3)[0], (t+3)[1], (t+3)[2]);
290
  fprintf(stderr, "          %g %g %g\n", (t+6)[0], (t+6)[1], (t+6)[2]);
291
  fprintf(stderr, "cross dots:  %g %g %g\n",
292
          ELL_3V_DOT(t+0, t+3), ELL_3V_DOT(t+0, t+6), ELL_3V_DOT(t+3, t+6));
293
*/
294
295
/*
296
******** ell_3m_1d_nullspace_d()
297
**
298
** the given matrix is assumed to have a nullspace of dimension one.
299
** A normalized vector which spans the nullspace is put into ans.
300
**
301
** The given nullspace matrix is NOT modified.
302
**
303
** This does NOT use biff
304
*/
305
void
306
ell_3m_1d_nullspace_d(double ans[3], const double _n[9]) {
307
  double t[9], n[9], norm;
308
309
  ELL_3M_TRANSPOSE(n, _n);
310
  /* find the three cross-products of pairs of column vectors of n */
311
  ELL_3V_CROSS(t+0, n+0, n+3);
312
  ELL_3V_CROSS(t+3, n+0, n+6);
313
  ELL_3V_CROSS(t+6, n+3, n+6);
314
  /* lop A */
315
  _ell_align3_d(t);
316
  /* lop B */
317
  /* add them up (longer, hence more accurate, should dominate) */
318
  ELL_3V_ADD3(ans, t+0, t+3, t+6);
319
320
  /* normalize */
321
  ELL_3V_NORM(ans, ans, norm);
322
323
  return;
324
}
325
326
/*
327
******** ell_3m_2d_nullspace_d()
328
**
329
** the given matrix is assumed to have a nullspace of dimension two.
330
**
331
** The given nullspace matrix is NOT modified
332
**
333
** This does NOT use biff
334
*/
335
void
336
ell_3m_2d_nullspace_d(double ans0[3], double ans1[3], const double _n[9]) {
337
  double n[9], tmp[3], norm;
338
339
  ELL_3M_TRANSPOSE(n, _n);
340
  _ell_align3_d(n);
341
  ELL_3V_ADD3(tmp, n+0, n+3, n+6);
342
  ELL_3V_NORM(tmp, tmp, norm);
343
344
  /* any two vectors which are perpendicular to the (supposedly 1D)
345
     span of the column vectors span the nullspace */
346
  ell_3v_perp_d(ans0, tmp);
347
  ELL_3V_NORM(ans0, ans0, norm);
348
  ELL_3V_CROSS(ans1, tmp, ans0);
349
350
  return;
351
}
352
353
/*
354
******** ell_3m_eigenvalues_d()
355
**
356
** finds eigenvalues of given matrix.
357
**
358
** returns information about the roots according to ellCubeRoot enum,
359
** see header for ellCubic for details.
360
**
361
** given matrix is NOT modified
362
**
363
** This does NOT use biff
364
**
365
** Doing the frobenius normalization proved successfull in avoiding the
366
** the creating of NaN eigenvalues when the coefficients of the matrix
367
** were really large (> 50000).  Also, when the matrix norm was really
368
** small, the comparison to "epsilon" in ell_cubic mistook three separate
369
** roots for a single and a double, with this matrix in particular:
370
**  1.7421892  0.0137642  0.0152975
371
**  0.0137642  1.7565432 -0.0062296
372
**  0.0152975 -0.0062296  1.7700019
373
** (actually, this is prior to tenEigensolve's isotropic removal)
374
**
375
** HEY: tenEigensolve_d and tenEigensolve_f start by removing the
376
** isotropic part of the tensor.  It may be that that smarts should
377
** be migrated here, but GLK is uncertain how it would change the
378
** handling of non-symmetric matrices.
379
*/
380
int
381
ell_3m_eigenvalues_d(double _eval[3], const double _m[9], const int newton) {
382
  double A, B, C, scale, frob, m[9], eval[3];
383
  int roots;
384
385
  frob = ELL_3M_FROB(_m);
386
  scale = frob ? 1.0/frob : 1.0;
387
  ELL_3M_SCALE(m, scale, _m);
388
  /*
389
  printf("!%s: m = %g %g %g; %g %g %g; %g %g %g\n", "ell_3m_eigenvalues_d",
390
         m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8]);
391
  */
392
  /*
393
  ** from gordon with mathematica; these are the coefficients of the
394
  ** cubic polynomial in x: det(x*I - M).  The full cubic is
395
  ** x^3 + A*x^2 + B*x + C.
396
  */
397
  A = -m[0] - m[4] - m[8];
398
  B = m[0]*m[4] - m[3]*m[1]
399
    + m[0]*m[8] - m[6]*m[2]
400
    + m[4]*m[8] - m[7]*m[5];
401
  C = (m[6]*m[4] - m[3]*m[7])*m[2]
402
    + (m[0]*m[7] - m[6]*m[1])*m[5]
403
    + (m[3]*m[1] - m[0]*m[4])*m[8];
404
  /*
405
  printf("!%s: A B C = %g %g %g\n", "ell_3m_eigenvalues_d", A, B, C);
406
  */
407
  roots = ell_cubic(eval, A, B, C, newton);
408
  /* no longer need to sort here */
409
  ELL_3V_SCALE(_eval, 1.0/scale, eval);
410
  return roots;
411
}
412
413
void
414
_ell_3m_evecs_d(double evec[9], double eval[3], int roots,
415
                const double m[9]) {
416
  double n[9], e0=0, e1=0.0, e2=0.0, t /* , tmpv[3] */ ;
417
418
  ELL_3V_GET(e0, e1, e2, eval);
419
  /* if (ell_debug) {
420
    printf("ell_3m_evecs_d: numroots = %d\n", numroots);
421
    } */
422
423
  /* we form m - lambda*I by doing a memcpy from m, and then
424
     (repeatedly) over-writing the diagonal elements */
425
  ELL_3M_COPY(n, m);
426
  switch (roots) {
427
  case ell_cubic_root_three:
428
    /* if (ell_debug) {
429
      printf("ell_3m_evecs_d: evals: %20.15f %20.15f %20.15f\n",
430
             eval[0], eval[1], eval[2]);
431
             } */
432
    ELL_3M_DIAG_SET(n, m[0]-e0, m[4]-e0, m[8]-e0);
433
    ell_3m_1d_nullspace_d(evec+0, n);
434
    ELL_3M_DIAG_SET(n, m[0]-e1, m[4]-e1, m[8]-e1);
435
    ell_3m_1d_nullspace_d(evec+3, n);
436
    ELL_3M_DIAG_SET(n, m[0]-e2, m[4]-e2, m[8]-e2);
437
    ell_3m_1d_nullspace_d(evec+6, n);
438
    _ell_3m_enforce_orthogonality(evec);
439
    _ell_3m_make_right_handed_d(evec);
440
    ELL_3V_SET(eval, e0, e1, e2);
441
    break;
442
  case ell_cubic_root_single_double:
443
    ELL_SORT3(e0, e1, e2, t);
444
    if (e0 > e1) {
445
      /* one big (e0) , two small (e1, e2) : more like a cigar */
446
      ELL_3M_DIAG_SET(n, m[0]-e0, m[4]-e0, m[8]-e0);
447
      ell_3m_1d_nullspace_d(evec+0, n);
448
      ELL_3M_DIAG_SET(n, m[0]-e1, m[4]-e1, m[8]-e1);
449
      ell_3m_2d_nullspace_d(evec+3, evec+6, n);
450
    }
451
    else {
452
      /* two big (e0, e1), one small (e2): more like a pancake */
453
      ELL_3M_DIAG_SET(n, m[0]-e0, m[4]-e0, m[8]-e0);
454
      ell_3m_2d_nullspace_d(evec+0, evec+3, n);
455
      ELL_3M_DIAG_SET(n, m[0]-e2, m[4]-e2, m[8]-e2);
456
      ell_3m_1d_nullspace_d(evec+6, n);
457
    }
458
    _ell_3m_enforce_orthogonality(evec);
459
    _ell_3m_make_right_handed_d(evec);
460
    ELL_3V_SET(eval, e0, e1, e2);
461
    break;
462
  case ell_cubic_root_triple:
463
    /* one triple root; use any basis as the eigenvectors */
464
    ELL_3V_SET(evec+0, 1, 0, 0);
465
    ELL_3V_SET(evec+3, 0, 1, 0);
466
    ELL_3V_SET(evec+6, 0, 0, 1);
467
    ELL_3V_SET(eval, e0, e1, e2);
468
    break;
469
  case ell_cubic_root_single:
470
    /* only one real root */
471
    ELL_3M_DIAG_SET(n, m[0]-e0, m[4]-e0, m[8]-e0);
472
    ell_3m_1d_nullspace_d(evec+0, n);
473
    ELL_3V_SET(evec+3, AIR_NAN, AIR_NAN, AIR_NAN);
474
    ELL_3V_SET(evec+6, AIR_NAN, AIR_NAN, AIR_NAN);
475
    ELL_3V_SET(eval, e0, AIR_NAN, AIR_NAN);
476
    break;
477
  }
478
  /* if (ell_debug) {
479
    printf("ell_3m_evecs_d (numroots = %d): evecs: \n", numroots);
480
    ELL_3MV_MUL(tmpv, m, evec[0]);
481
    printf(" (%g:%g): %20.15f %20.15f %20.15f\n",
482
           eval[0], ELL_3V_DOT(evec[0], tmpv),
483
           evec[0][0], evec[0][1], evec[0][2]);
484
    ELL_3MV_MUL(tmpv, m, evec[1]);
485
    printf(" (%g:%g): %20.15f %20.15f %20.15f\n",
486
           eval[1], ELL_3V_DOT(evec[1], tmpv),
487
           evec[1][0], evec[1][1], evec[1][2]);
488
    ELL_3MV_MUL(tmpv, m, evec[2]);
489
    printf(" (%g:%g): %20.15f %20.15f %20.15f\n",
490
           eval[2], ELL_3V_DOT(evec[2], tmpv),
491
           evec[2][0], evec[2][1], evec[2][2]);
492
           } */
493
  return;
494
}
495
496
/*
497
******** ell_3m_eigensolve_d()
498
**
499
** finds eigenvalues and eigenvectors of given matrix m
500
**
501
** returns information about the roots according to ellCubeRoot enum,
502
** see header for ellCubic for details.  When eval[i] is set, evec+3*i
503
** is set to a corresponding eigenvector.  The eigenvectors are
504
** (evec+0)[], (evec+3)[], and (evec+6)[]
505
**
506
** NOTE: Even in the post-Teem-1.7 switch from column-major to
507
** row-major- its still the case that the eigenvectors are at
508
** evec+0, evec+3, evec+6: this means that they USED to be the
509
** "columns" of the matrix, and NOW they're the rows.
510
**
511
** The eigenvalues (and associated eigenvectors) are sorted in
512
** descending order.
513
**
514
** This does NOT use biff
515
*/
516
int
517
ell_3m_eigensolve_d(double eval[3], double evec[9],
518
                    const double m[9], const int newton) {
519
  int roots;
520
521
  /* if (ell_debug) {
522
    printf("ell_3m_eigensolve_d: input matrix:\n");
523
    printf("{{%20.15f,\t%20.15f,\t%20.15f},\n", m[0], m[1], m[2]);
524
    printf(" {%20.15f,\t%20.15f,\t%20.15f},\n", m[3], m[4], m[5]);
525
    printf(" {%20.15f,\t%20.15f,\t%20.15f}};\n",m[6], m[7], m[8]);
526
    } */
527
528
  roots = ell_3m_eigenvalues_d(eval, m, newton);
529
  _ell_3m_evecs_d(evec, eval, roots, m);
530
531
  return roots;
532
}
533
534
/* ____________________________ 3m2sub ____________________________ */
535
/*
536
******** ell_3m2sub_eigenvalues_d
537
**
538
** for doing eigensolve of the upper-left 2x2 matrix sub-matrix of a
539
** 3x3 matrix.  The other entries are assumed to be zero.  A 0 root is
540
** put last (in eval[2]), possibly in defiance of the usual eigenvalue
541
** ordering.
542
*/
543
int
544
ell_3m2sub_eigenvalues_d(double eval[3], const double _m[9]) {
545
  double A, B, m[4], D, Dsq, eps=1.0E-11;
546
  int roots;
547
  /* static const char me[]="ell_3m2sub_eigenvalues_d"; */
548
549
  m[0] = _m[0];
550
  m[1] = _m[1];
551
  m[2] = _m[3];
552
  m[3] = _m[4];
553
554
  /* cubic characteristic equation is L^3 + A*L^2 + B*L = 0 */
555
  A = -m[0] - m[3];
556
  B = m[0]*m[3] - m[1]*m[2];
557
  Dsq = A*A - 4*B;
558
  /*
559
  fprintf(stderr, "!%s: m = {{%f,%f},{%f,%f}} -> A=%f B=%f Dsq=%.17f %s 0 (%.17f)\n", me,
560
          m[0], m[1], m[2], m[3], A, B, Dsq,
561
          (Dsq > 0 ? ">" : (Dsq < 0 ? "<" : "==")), eps);
562
  fprintf(stderr, "!%s: Dsq = \n", me);
563
  airFPFprintf_d(stderr, Dsq);
564
  fprintf(stderr, "!%s: eps = \n", me);
565
  airFPFprintf_d(stderr, eps);
566
  ell_3m_print_d(stderr, _m);
567
  */
568
  if (Dsq > eps) {
569
    D = sqrt(Dsq);
570
    eval[0] = (-A + D)/2;
571
    eval[1] = (-A - D)/2;
572
    eval[2] = 0;
573
    roots = ell_cubic_root_three;
574
  } else if (Dsq < -eps) {
575
    /* no quadratic roots; only the implied zero */
576
    ELL_3V_SET(eval, AIR_NAN, AIR_NAN, 0);
577
    roots = ell_cubic_root_single;
578
  } else {
579
    /* a quadratic double root */
580
    ELL_3V_SET(eval, -A/2, -A/2, 0);
581
    roots = ell_cubic_root_single_double;
582
  }
583
  /*
584
  fprintf(stderr, "!%s: Dsq=%f, roots=%d (%f %f %f)\n",
585
          me, Dsq, roots, eval[0], eval[1], eval[2]);
586
  */
587
  return roots;
588
}
589
590
void
591
_ell_22v_enforce_orthogonality(double uu[2], double _vv[2]) {
592
  double dot, vv[2], len;
593
594
  dot = ELL_2V_DOT(uu, _vv);
595
  ELL_2V_SCALE_ADD2(vv, 1, _vv, -dot, uu);
596
  ELL_2V_NORM(_vv, vv, len);
597
  return;
598
}
599
600
/*
601
** NOTE: assumes that eval and roots have come from
602
** ell_3m2sub_eigenvalues_d(m)
603
*/
604
void
605
_ell_3m2sub_evecs_d(double evec[9], double eval[3], int roots,
606
                    const double m[9]) {
607
  double n[4];
608
  static const char me[]="_ell_3m2sub_evecs_d";
609
610
  if (ell_cubic_root_three == roots) {
611
    /* set off-diagonal entries once */
612
    n[1] = m[1];
613
    n[2] = m[3];
614
    /* find first evec */
615
    n[0] = m[0] - eval[0];
616
    n[3] = m[4] - eval[0];
617
    ell_2m_1d_nullspace_d(evec + 3*0, n);
618
    (evec + 3*0)[2] = 0;
619
    /* find second evec */
620
    n[0] = m[0] - eval[1];
621
    n[3] = m[4] - eval[1];
622
    ell_2m_1d_nullspace_d(evec + 3*1, n);
623
    (evec + 3*1)[2] = 0;
624
    _ell_22v_enforce_orthogonality(evec + 3*0, evec + 3*1);
625
    /* make right-handed */
626
    ELL_3V_CROSS(evec + 3*2, evec + 3*0, evec + 3*1);
627
  } else if (ell_cubic_root_single_double == roots) {
628
    /* can pick any 2D basis */
629
    ELL_3V_SET(evec + 3*0, 1, 0, 0);
630
    ELL_3V_SET(evec + 3*1, 0, 1, 0);
631
    ELL_3V_SET(evec + 3*2, 0, 0, 1);
632
  } else {
633
    /* ell_cubic_root_single == roots, if assumptions are met */
634
    ELL_3V_SET(evec + 3*0, AIR_NAN, AIR_NAN, 0);
635
    ELL_3V_SET(evec + 3*1, AIR_NAN, AIR_NAN, 0);
636
    ELL_3V_SET(evec + 3*2, 0, 0, 1);
637
  }
638
  if (!ELL_3M_EXISTS(evec)) {
639
    fprintf(stderr, "%s: given m = \n", me);
640
    ell_3m_print_d(stderr, m);
641
    fprintf(stderr, "%s: got roots = %s (%d) and evecs = \n", me,
642
            airEnumStr(ell_cubic_root, roots), roots);
643
    ell_3m_print_d(stderr, evec);
644
  }
645
  return;
646
}
647
648
int
649
ell_3m2sub_eigensolve_d(double eval[3], double evec[9],
650
                        const double m[9]) {
651
  int roots;
652
653
  roots = ell_3m2sub_eigenvalues_d(eval, m);
654
  _ell_3m2sub_evecs_d(evec, eval, roots, m);
655
656
  return roots;
657
}
658
659
/* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 3m2sub ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ */
660
661
/*
662
******** ell_3m_svd_d
663
**
664
** singular value decomposition:
665
** mat = uu * diag(sval) * vv
666
**
667
** singular values are square roots of eigenvalues of mat * mat^T
668
** columns of uu are eigenvectors of mat * mat^T
669
** rows of vv are eigenvectors of mat^T * mat
670
**
671
** returns info about singular values according to ellCubeRoot enum
672
**
673
** HEY: I think this does the wrong thing when given a symmetric
674
** matrix with negative eigenvalues . . .
675
*/
676
int
677
ell_3m_svd_d(double uu[9], double sval[3], double vv[9],
678
             const double mat[9], const int newton) {
679
  double trn[9], msqr[9], eval[3], evec[9];
680
  int roots;
681
682
  ELL_3M_TRANSPOSE(trn, mat);
683
  ELL_3M_MUL(msqr, mat, trn);
684
  roots = ell_3m_eigensolve_d(eval, evec, msqr, newton);
685
  sval[0] = sqrt(eval[0]);
686
  sval[1] = sqrt(eval[1]);
687
  sval[2] = sqrt(eval[2]);
688
  ELL_3M_TRANSPOSE(uu, evec);
689
  ELL_3M_MUL(msqr, trn, mat);
690
  _ell_3m_evecs_d(vv, eval, roots, msqr);
691
692
  return roots;
693
}
694
695
/*
696
** NOTE: profiling showed that about a quarter of the execution time of
697
** ell_6ms_eigensolve_d() is spent here; so reconsider its need and
698
** implementation . . . (fabs vs. AIR_ABS() made no difference)
699
*/
700
static void
701
_maxI_sum_find(unsigned int maxI[2], double *sumon, double *sumoff,
702
               double mat[6][6]) {
703
  double maxm, tmp;
704
  unsigned int rrI, ccI;
705
706
  /* we hope that all these loops are unrolled by the optimizer */
707
  *sumon = *sumoff = 0.0;
708
  for (rrI=0; rrI<6; rrI++) {
709
    *sumon += AIR_ABS(mat[rrI][rrI]);
710
  }
711
  maxm = -1;
712
  maxI[0] = maxI[1] = 0;
713
  for (rrI=0; rrI<5; rrI++) {
714
    for (ccI=rrI+1; ccI<6; ccI++) {
715
      tmp = AIR_ABS(mat[rrI][ccI]);
716
      *sumoff += tmp;
717
      if (tmp > maxm) {
718
        maxm = tmp;
719
        maxI[0] = rrI;
720
        maxI[1] = ccI;
721
      }
722
    }
723
  }
724
725
  /*
726
  if (1) {
727
    double nrm, trc;
728
    nrm = trc = 0;
729
    for (rrI=0; rrI<6; rrI++) {
730
      trc += mat[rrI][rrI];
731
      nrm += mat[rrI][rrI]*mat[rrI][rrI];
732
    }
733
    for (rrI=0; rrI<5; rrI++) {
734
      for (ccI=rrI+1; ccI<6; ccI++) {
735
        nrm += 2*mat[rrI][ccI]*mat[rrI][ccI];
736
      }
737
    }
738
    fprintf(stderr, "---------------- invars = %g %g\n", trc, nrm);
739
  }
740
  */
741
742
  return;
743
}
744
745
static int
746
_compar(const void *A_void, const void *B_void) {
747
  const double *A, *B;
748
  A = AIR_CAST(const double *, A_void);
749
  B = AIR_CAST(const double *, B_void);
750
  return (A[0] < B[0] ? 1 : (A[0] > B[0] ? -1 : 0));
751
}
752
753
/*
754
******* ell_6ms_eigensolve_d
755
**
756
** uses Jacobi iterations to find eigensystem of 6x6 symmetric matrix,
757
** given in sym[21], to within convergence threshold eps.  Puts
758
** eigenvalues, in descending order, in eval[6], and corresponding
759
** eigenvectors in _evec+0, _evec+6, . . ., _evec+30.  NOTE: you can
760
** pass a NULL _evec if eigenvectors aren't needed.
761
**
762
** does NOT use biff
763
*/
764
int
765
ell_6ms_eigensolve_d(double eval[6], double _evec[36],
766
                     const double sym[21], const double eps) {
767
  /* char me[]="ell_6ms_eigensolve_d"; */
768
  double mat[2][6][6], evec[2][6][6], sumon, sumoff, evtmp[12];
769
  unsigned int cur, rrI, ccI, maxI[2], iter;
770
771
  if (!( eval && sym && eps >= 0 )) {
772
    return 1;
773
  }
774
  /* copy symmetric matrix sym[] into upper tris of mat[0][][] & mat[1][][] */
775
  mat[0][0][0] = sym[ 0];
776
  mat[0][0][1] = sym[ 1];
777
  mat[0][0][2] = sym[ 2];
778
  mat[0][0][3] = sym[ 3];
779
  mat[0][0][4] = sym[ 4];
780
  mat[0][0][5] = sym[ 5];
781
  mat[0][1][1] = sym[ 6];
782
  mat[0][1][2] = sym[ 7];
783
  mat[0][1][3] = sym[ 8];
784
  mat[0][1][4] = sym[ 9];
785
  mat[0][1][5] = sym[10];
786
  mat[0][2][2] = sym[11];
787
  mat[0][2][3] = sym[12];
788
  mat[0][2][4] = sym[13];
789
  mat[0][2][5] = sym[14];
790
  mat[0][3][3] = sym[15];
791
  mat[0][3][4] = sym[16];
792
  mat[0][3][5] = sym[17];
793
  mat[0][4][4] = sym[18];
794
  mat[0][4][5] = sym[19];
795
  mat[0][5][5] = sym[20];
796
  if (_evec) {
797
    /* initialize evec[0]; */
798
    for (rrI=0; rrI<6; rrI++) {
799
      for (ccI=0; ccI<6; ccI++) {
800
        evec[0][ccI][rrI] = (rrI == ccI);
801
      }
802
    }
803
  }
804
  /*
805
  fprintf(stderr, "!%s(INIT): m = [", me);
806
  for (rrI=0; rrI<6; rrI++) {
807
    for (ccI=0; ccI<6; ccI++) {
808
      fprintf(stderr, "%f%s",
809
              (rrI <= ccI ? mat[0][rrI][ccI] : mat[0][ccI][rrI]),
810
              ccI<5 ? "," : (rrI<5 ? ";" : "]"));
811
    }
812
    fprintf(stderr, "\n");
813
  }
814
  */
815
  maxI[0] = maxI[1] = UINT_MAX; /* quiet warnings about using maxI unset */
816
  _maxI_sum_find(maxI, &sumon, &sumoff, mat[0]);
817
  cur = 1;         /* fake out anticipating first line of loop */
818
  iter = 0;
819
  while (sumoff/sumon > eps) {
820
    double th, tt, cc, ss;
821
    const unsigned int P = maxI[0];
822
    const unsigned int Q = maxI[1];
823
    //make sure that P and Q are within the bounds for mat[2][6][6]
824
    if(P >=6 || Q >= 6){
825
      break;
826
    }
827
    /*
828
    fprintf(stderr, "!%s(%u): sumoff/sumon = %g/%g = %g > %g\n", me, iter,
829
            sumoff, sumon, sumoff/sumon, eps);
830
    */
831
    cur = 1 - cur;
832
833
    th = (mat[cur][Q][Q] - mat[cur][P][P])/(2*mat[cur][P][Q]);
834
    tt = (th > 0 ? +1 : -1)/(AIR_ABS(th) + sqrt(th*th + 1));
835
    cc = 1/sqrt(tt*tt + 1);
836
    ss = cc*tt;
837
    /*
838
    fprintf(stderr, "!%s(%u): maxI = (P,Q) = (%u,%u) --> ss=%f, cc=%f\n",
839
            me, iter, P, Q, ss, cc);
840
    fprintf(stderr, "     r = [");
841
    for (rrI=0; rrI<6; rrI++) {
842
      for (ccI=0; ccI<6; ccI++) {
843
        fprintf(stderr, "%g%s",
844
                (rrI == ccI
845
                 ? (rrI == P || rrI == Q ? cc : 1.0)
846
                 : (rrI == P && ccI == Q
847
                    ? ss
848
                    : (rrI == Q && ccI == P
849
                       ? -ss
850
                       : 0))),
851
                ccI<5 ? "," : (rrI<5 ? ";" : "]"));
852
      }
853
      fprintf(stderr, "\n");
854
    }
855
    */
856
    /* initialize by copying whole matrix */
857
    for (rrI=0; rrI<6; rrI++) {
858
      for (ccI=rrI; ccI<6; ccI++) {
859
        mat[1-cur][rrI][ccI] = mat[cur][rrI][ccI];
860
      }
861
    }
862
    /* perform Jacobi rotation */
863
    for (rrI=0; rrI<P; rrI++) {
864
      mat[1-cur][rrI][P] = cc*mat[cur][rrI][P] - ss*mat[cur][rrI][Q];
865
    }
866
    for (ccI=P+1; ccI<6; ccI++) {
867
      mat[1-cur][P][ccI] = cc*mat[cur][P][ccI] - ss*(Q <= ccI
868
                                                     ? mat[cur][Q][ccI]
869
                                                     : mat[cur][ccI][Q]);
870
    }
871
    for (rrI=0; rrI<Q; rrI++) {
872
      mat[1-cur][rrI][Q] = ss*(rrI <= P
873
                               ? mat[cur][rrI][P]
874
                               : mat[cur][P][rrI]) + cc*mat[cur][rrI][Q];
875
    }
876
    for (ccI=Q+1; ccI<6; ccI++) {
877
      mat[1-cur][Q][ccI] = ss*mat[cur][P][ccI] + cc*mat[cur][Q][ccI];
878
    }
879
    /* set special entries */
880
    mat[1-cur][P][P] = mat[cur][P][P] - tt*mat[cur][P][Q];
881
    mat[1-cur][Q][Q] = mat[cur][Q][Q] + tt*mat[cur][P][Q];
882
    mat[1-cur][P][Q] = 0.0;
883
    if (_evec) {
884
      /* NOTE: the eigenvectors use transpose of indexing of mat */
885
      /* start by copying all */
886
      for (rrI=0; rrI<6; rrI++) {
887
        for (ccI=0; ccI<6; ccI++) {
888
          evec[1-cur][ccI][rrI] = evec[cur][ccI][rrI];
889
        }
890
      }
891
      for (rrI=0; rrI<6; rrI++) {
892
        evec[1-cur][P][rrI] = cc*evec[cur][P][rrI] - ss*evec[cur][Q][rrI];
893
        evec[1-cur][Q][rrI] = ss*evec[cur][P][rrI] + cc*evec[cur][Q][rrI];
894
      }
895
    }
896
897
    _maxI_sum_find(maxI, &sumon, &sumoff, mat[1-cur]);
898
899
    /*
900
    fprintf(stderr, "!%s(%u): m = [", me, iter);
901
    for (rrI=0; rrI<6; rrI++) {
902
      for (ccI=0; ccI<6; ccI++) {
903
        fprintf(stderr, "%f%s",
904
                (rrI <= ccI ? mat[1-cur][rrI][ccI] : mat[1-cur][ccI][rrI]),
905
                ccI<5 ? "," : (rrI<5 ? ";" : "]"));
906
      }
907
      fprintf(stderr, "\n");
908
    }
909
    */
910
    iter++;
911
  }
912
  /* 1-cur is index of final solution */
913
914
  /* sort evals */
915
  for (ccI=0; ccI<6; ccI++) {
916
    evtmp[0 + 2*ccI] = mat[1-cur][ccI][ccI];
917
    evtmp[1 + 2*ccI] = ccI;
918
  }
919
  qsort(evtmp, 6, 2*sizeof(double), _compar);
920
921
  /* copy out solution */
922
  for (ccI=0; ccI<6; ccI++) {
923
    eval[ccI] = evtmp[0 + 2*ccI];
924
    if (_evec) {
925
      unsigned eeI;
926
      for (rrI=0; rrI<6; rrI++) {
927
        eeI = AIR_CAST(unsigned int, evtmp[1 + 2*ccI]);
928
        _evec[rrI + 6*ccI] = evec[1-cur][eeI][rrI];
929
      }
930
    }
931
  }
932
933
  return 0;
934
}