source: git/src/matrix.c @ c82e0a8

main
Last change on this file since c82e0a8 was 0b99107, checked in by Olly Betts <olly@…>, 5 weeks ago

Eliminate old FSF addresses

Update GPL/LGPL licence files and boilerplate to direct people who
didn't receive the licence text to the FSF website, as the current
versions of the FSF licence texts now do, rather than giving a postal
address.

  • Property mode set to 100644
File size: 12.6 KB
Line 
1/* matrix.c
2 * Matrix building and solving routines
3 * Copyright (C) 1993-2003,2010,2013,2024 Olly Betts
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see
17 * <https://www.gnu.org/licenses/>.
18 */
19
20/*#define SOR 1*/
21
22#if 0
23# define DEBUG_INVALID 1
24#endif
25
26#include <config.h>
27
28#include "debug.h"
29#include "cavern.h"
30#include "filename.h"
31#include "message.h"
32#include "netbits.h"
33#include "matrix.h"
34#include "osalloc.h"
35#include "out.h"
36
37#undef PRINT_MATRICES
38#define PRINT_MATRICES 0
39
40#undef DEBUG_MATRIX_BUILD
41#define DEBUG_MATRIX_BUILD 0
42
43#undef DEBUG_MATRIX
44#define DEBUG_MATRIX 0
45
46#if PRINT_MATRICES
47static void print_matrix(real *M, real *B, long n);
48#endif
49
50static void choleski(real *M, real *B, long n);
51
52#ifdef SOR
53static void sor(real *M, real *B, long n);
54#endif
55
56/* for M(row, col) col must be <= row, so Y <= X */
57# define M(X, Y) ((real *)M)[((((size_t)(X)) * ((X) + 1)) >> 1) + (Y)]
58              /* +(Y>X?0*printf("row<col (line %d)\n",__LINE__):0) */
59/*#define M_(X, Y) ((real *)M)[((((size_t)(Y)) * ((Y) + 1)) >> 1) + (X)]*/
60
61static void set_row(node *stn, int row_number) {
62    // We store the matrix row/column index in stn->colour for quick and easy
63    // lookup when copying out the solved station coordinates.
64    stn->colour = row_number;
65    for (int d = 0; d < 3; d++) {
66        linkfor *leg = stn->leg[d];
67        if (!leg) break;
68        node *to = leg->l.to;
69        if (to->colour < 0 && stn->name->pos == to->name->pos) {
70            set_row(to, row_number);
71        }
72    }
73}
74
75#ifdef NO_COVARIANCES
76# define FACTOR 1
77#else
78# define FACTOR 3
79#endif
80
81/* Find positions for a subset of the reduced network by solving a matrix
82 * equation.
83 *
84 * list is a non-empty linked list of unfixed stations to solve for.
85 *
86 * As a pre-condition, all stations in list must have a negative value for
87 * stn->colour.  This can be ensured by the caller (which avoids having to
88 * make an extra pass over the list just to set the colours suitably).
89 */
90extern void
91solve_matrix(node *list)
92{
93   // Assign a matrix row/column index to each group of stations with the same
94   // pos.
95   //
96   // We also set listend to the last station in the list while doing so, which
97   // we use after solving to splice list into fixedlist.
98   node *listend = NULL;
99   size_t n = 0;
100   for (node *stn = list; stn; stn = stn->next) {
101      listend = stn;
102      if (stn->colour < 0) {
103          set_row(stn, n++);
104      }
105   }
106   SVX_ASSERT(n > 0);
107
108   // Array to map from row/column index to pos.  We fill this in as we build
109   // the matrix, and use it to know where to copy the solved station
110   // coordinates to.
111   pos **stn_tab = osmalloc(n * sizeof(pos*));
112
113   real *M = osmalloc((((n * FACTOR * (n * FACTOR + 1)) >> 1)) * sizeof(real));
114   real *B = osmalloc(n * FACTOR * sizeof(real));
115
116   if (n == 1)
117      out_current_action(msg(/*Solving one equation*/78));
118   else
119      out_current_action1(msg(/*Solving %d simultaneous equations*/75), (int)n);
120
121#ifdef NO_COVARIANCES
122   int dim = 2;
123#else
124   int dim = 0; /* Collapse loop to a single iteration. */
125#endif
126   for ( ; dim >= 0; dim--) {
127      /* Initialise M and B to zero - zeroing "linearly" will minimise
128       * paging when the matrix is large */
129      {
130         int end = n * FACTOR;
131         for (int row = 0; row < end; row++) B[row] = (real)0.0;
132         end = ((size_t)n * FACTOR * (n * FACTOR + 1)) >> 1;
133         for (int row = 0; row < end; row++) M[row] = (real)0.0;
134      }
135
136      /* Construct matrix by going through the stn list.
137       *
138       * All legs between two fixed stations can be ignored here.
139       *
140       * Other legs we want to add exactly once to M.  To achieve this we
141       * want to:
142       *
143       * - add forward legs between two unfixed stations,
144       *
145       * - add legs from unfixed stations to fixed stations (we do them from
146       *   the unfixed end so we don't need to detect when we're at a fixed
147       *   point cut line and determine which side we're currently dealing
148       *   with).
149       *
150       * To implement this, we only look at legs from unfixed stations and add
151       * a leg if to a fixed station, or to an unfixed station and it's a
152       * forward leg.
153       */
154      for (node *stn = list; stn; stn = stn->next) {
155         if (dim == 0) {
156             stn_tab[stn->colour] = stn->name->pos;
157         }
158
159#ifdef NO_COVARIANCES
160         real e;
161#else
162         svar e;
163         delta a;
164#endif
165#if DEBUG_MATRIX_BUILD
166         print_prefix(stn->name);
167         printf(" used: %d colour %ld\n",
168                (!!stn->leg[2]) << 2 | (!!stn -> leg[1]) << 1 | (!!stn->leg[0]),
169                stn->colour);
170
171         for (int dirn = 0; dirn <= 2 && stn->leg[dirn]; dirn++) {
172            printf("Leg %d, vx=%f, reverse=%d, to ", dirn,
173                   stn->leg[dirn]->v[0], stn->leg[dirn]->l.reverse);
174            print_prefix(stn->leg[dirn]->l.to->name);
175            putnl();
176         }
177         putnl();
178#endif /* DEBUG_MATRIX_BUILD */
179
180         int f = stn->colour;
181         SVX_ASSERT(f >= 0);
182         {
183            for (int dirn = 0; dirn <= 2 && stn->leg[dirn]; dirn++) {
184               linkfor *leg = stn->leg[dirn];
185               node *to = leg->l.to;
186               if (fixed(to)) {
187                  bool fRev = !data_here(leg);
188                  if (fRev) leg = reverse_leg(leg);
189                  /* Ignore equated nodes */
190#ifdef NO_COVARIANCES
191                  e = leg->v[dim];
192                  if (e != (real)0.0) {
193                     e = ((real)1.0) / e;
194                     M(f,f) += e;
195                     B[f] += e * POS(to, dim);
196                     if (fRev) {
197                        B[f] += leg->d[dim];
198                     } else {
199                        B[f] -= leg->d[dim];
200                     }
201                  }
202#else
203                  if (invert_svar(&e, &leg->v)) {
204                     if (fRev) {
205                        adddd(&a, &POSD(to), &leg->d);
206                     } else {
207                        subdd(&a, &POSD(to), &leg->d);
208                     }
209                     delta b;
210                     mulsd(&b, &e, &a);
211                     for (int i = 0; i < 3; i++) {
212                        M(f * FACTOR + i, f * FACTOR + i) += e[i];
213                        B[f * FACTOR + i] += b[i];
214                     }
215                     M(f * FACTOR + 1, f * FACTOR) += e[3];
216                     M(f * FACTOR + 2, f * FACTOR) += e[4];
217                     M(f * FACTOR + 2, f * FACTOR + 1) += e[5];
218                  }
219#endif
220               } else if (data_here(leg) &&
221                          (leg->l.reverse & FLAG_ARTICULATION) == 0) {
222                  /* forward leg, unfixed -> unfixed */
223                  int t = to->colour;
224                  SVX_ASSERT(t >= 0);
225#if DEBUG_MATRIX
226# ifdef NO_COVARIANCES
227                  printf("Leg %d to %d, var %f, delta %f\n", f, t, e,
228                         leg->d[dim]);
229# else
230                  printf("Leg %d to %d, var (%f, %f, %f; %f, %f, %f), "
231                         "delta %f\n", f, t, e[0], e[1], e[2], e[3], e[4], e[5],
232                         leg->d[dim]);
233# endif
234#endif
235                  /* Ignore equated nodes & lollipops */
236#ifdef NO_COVARIANCES
237                  e = leg->v[dim];
238                  if (t != f && e != (real)0.0) {
239                     e = ((real)1.0) / e;
240                     M(f,f) += e;
241                     M(t,t) += e;
242                     if (f < t) M(t,f) -= e; else M(f,t) -= e;
243                     real a = e * leg->d[dim];
244                     B[f] -= a;
245                     B[t] += a;
246                  }
247#else
248                  if (t != f && invert_svar(&e, &leg->v)) {
249                     mulsd(&a, &e, &leg->d);
250                     for (int i = 0; i < 3; i++) {
251                        M(f * FACTOR + i, f * FACTOR + i) += e[i];
252                        M(t * FACTOR + i, t * FACTOR + i) += e[i];
253                        if (f < t)
254                           M(t * FACTOR + i, f * FACTOR + i) -= e[i];
255                        else
256                           M(f * FACTOR + i, t * FACTOR + i) -= e[i];
257                        B[f * FACTOR + i] -= a[i];
258                        B[t * FACTOR + i] += a[i];
259                     }
260                     M(f * FACTOR + 1, f * FACTOR) += e[3];
261                     M(t * FACTOR + 1, t * FACTOR) += e[3];
262                     M(f * FACTOR + 2, f * FACTOR) += e[4];
263                     M(t * FACTOR + 2, t * FACTOR) += e[4];
264                     M(f * FACTOR + 2, f * FACTOR + 1) += e[5];
265                     M(t * FACTOR + 2, t * FACTOR + 1) += e[5];
266                     if (f < t) {
267                        M(t * FACTOR + 1, f * FACTOR) -= e[3];
268                        M(t * FACTOR, f * FACTOR + 1) -= e[3];
269                        M(t * FACTOR + 2, f * FACTOR) -= e[4];
270                        M(t * FACTOR, f * FACTOR + 2) -= e[4];
271                        M(t * FACTOR + 2, f * FACTOR + 1) -= e[5];
272                        M(t * FACTOR + 1, f * FACTOR + 2) -= e[5];
273                     } else {
274                        M(f * FACTOR + 1, t * FACTOR) -= e[3];
275                        M(f * FACTOR, t * FACTOR + 1) -= e[3];
276                        M(f * FACTOR + 2, t * FACTOR) -= e[4];
277                        M(f * FACTOR, t * FACTOR + 2) -= e[4];
278                        M(f * FACTOR + 2, t * FACTOR + 1) -= e[5];
279                        M(f * FACTOR + 1, t * FACTOR + 2) -= e[5];
280                     }
281                  }
282#endif
283               }
284            }
285         }
286      }
287
288#if PRINT_MATRICES
289      print_matrix(M, B, n * FACTOR); /* 'ave a look! */
290#endif
291
292#ifdef SOR
293      /* defined in network.c, may be altered by -z<letters> on command line */
294      if (optimize & BITA('i'))
295         sor(M, B, n * FACTOR);
296      else
297#endif
298         choleski(M, B, n * FACTOR);
299
300      {
301         for (int m = (int)(n - 1); m >= 0; m--) {
302#ifdef NO_COVARIANCES
303            stn_tab[m]->p[dim] = B[m];
304            if (dim == 0) {
305               SVX_ASSERT2(pos_fixed(stn_tab[m]),
306                       "setting station coordinates didn't mark pos as fixed");
307            }
308#else
309            for (int i = 0; i < 3; i++) {
310               stn_tab[m]->p[i] = B[m * FACTOR + i];
311            }
312            SVX_ASSERT2(pos_fixed(stn_tab[m]),
313                    "setting station coordinates didn't mark pos as fixed");
314#endif
315         }
316      }
317   }
318
319   // Put the solved stations back on fixedlist.
320   listend->next = fixedlist;
321   if (fixedlist) fixedlist->prev = listend;
322   fixedlist = list;
323
324   free(B);
325   free(M);
326   free(stn_tab);
327
328#if DEBUG_MATRIX
329   for (node *stn = list; stn; stn = stn->next) {
330      printf("(%8.2f, %8.2f, %8.2f ) ", POS(stn, 0), POS(stn, 1), POS(stn, 2));
331      print_prefix(stn->name);
332      putnl();
333   }
334#endif
335}
336
337/* Solve MX=B for X by first factoring M into LDL'.  This is a modified form
338 * of Choleski factorisation - the original Choleski factorisation is LL',
339 * but this modified version has the advantage of avoiding O(n) square root
340 * calculations.
341 */
342/* Note M must be symmetric positive definite */
343/* routine is entitled to scribble on M and B if it wishes */
344static void
345choleski(real *M, real *B, long n)
346{
347   for (int j = 1; j < n; j++) {
348      real V;
349      for (int i = 0; i < j; i++) {
350         V = (real)0.0;
351         for (int k = 0; k < i; k++) V += M(i,k) * M(j,k) * M(k,k);
352         M(j,i) = (M(j,i) - V) / M(i,i);
353      }
354      V = (real)0.0;
355      for (int k = 0; k < j; k++) V += M(j,k) * M(j,k) * M(k,k);
356      M(j,j) -= V; /* may be best to add M() last for numerical reasons too */
357   }
358
359   /* Multiply x by L inverse */
360   for (int i = 0; i < n - 1; i++) {
361      for (int j = i + 1; j < n; j++) {
362         B[j] -= M(j,i) * B[i];
363      }
364   }
365
366   /* Multiply x by D inverse */
367   for (int i = 0; i < n; i++) {
368      B[i] /= M(i,i);
369   }
370
371   /* Multiply x by (L transpose) inverse */
372   for (int i = (int)(n - 1); i > 0; i--) {
373      for (int j = i - 1; j >= 0; j--) {
374         B[j] -= M(i,j) * B[i];
375      }
376   }
377
378   /* printf("\n%ld/%ld\n\n",flops,flopsTot); */
379}
380
381#ifdef SOR
382/* factor to use for SOR (must have 1 <= SOR_factor < 2) */
383#define SOR_factor 1.93 /* 1.95 */
384
385/* Solve MX=B for X by SOR of Gauss-Siedel */
386/* routine is entitled to scribble on M and B if it wishes */
387static void
388sor(real *M, real *B, long n)
389{
390   long it = 0;
391
392   real *X = osmalloc(n * sizeof(real));
393
394   const real threshold = 0.00001;
395
396   printf("reciprocating diagonal\n"); /* TRANSLATE */
397
398   /* munge diagonal so we can multiply rather than divide */
399   for (int row = n - 1; row >= 0; row--) {
400      M(row,row) = 1 / M(row,row);
401      X[row] = 0;
402   }
403
404   printf("starting iteration\n"); /* TRANSLATE */
405
406   real t;
407   do {
408      /*printf("*");*/
409      it++;
410      t = 0.0;
411      for (int row = 0; row < n; row++) {
412         real x = B[row];
413         int col;
414         for (col = 0; col < row; col++) x -= M(row,col) * X[col];
415         for (col++; col < n; col++) x -= M(col,row) * X[col];
416         x *= M(row,row);
417         real sor_delta = (x - X[row]) * SOR_factor;
418         X[row] += sor_delta;
419         real t2 = fabs(sor_delta);
420         if (t2 > t) t = t2;
421      }
422      printf("% 6ld: %8.6f\n", it, t);
423   } while (t >= threshold && it < 100000);
424
425   if (t >= threshold) {
426      fprintf(stderr, "*not* converged after %ld iterations\n", it);
427      BUG("iteration stinks");
428   }
429
430   printf("%ld iterations\n", it); /* TRANSLATE */
431
432#if 0
433   putnl();
434   for (int row = n - 1; row >= 0; row--) {
435      t = 0.0;
436      for (int col = 0; col < row; col++) t += M(row, col) * X[col];
437      t += X[row] / M(row, row);
438      for (col = row + 1; col < n; col++)
439         t += M(col, row) * X[col];
440      printf("[ %f %f ]\n", t, B[row]);
441   }
442#endif
443
444   for (int row = n - 1; row >= 0; row--) B[row] = X[row];
445
446   free(X);
447   printf("\ndone\n"); /* TRANSLATE */
448}
449#endif
450
451#if PRINT_MATRICES
452static void
453print_matrix(real *M, real *B, long n)
454{
455   printf("Matrix, M and vector, B:\n");
456   for (long row = 0; row < n; row++) {
457      long col;
458      for (col = 0; col <= row; col++) printf("%6.2f\t", M(row, col));
459      for (; col <= n; col++) printf(" \t");
460      printf("\t%6.2f\n", B[row]);
461   }
462   putnl();
463   return;
464}
465#endif
Note: See TracBrowser for help on using the repository browser.