source: git/src/matrix.c @ 8a7804fb

main
Last change on this file since 8a7804fb 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
RevLine 
[421b7d2]1/* matrix.c
[d1b1380]2 * Matrix building and solving routines
[2d8d46d]3 * Copyright (C) 1993-2003,2010,2013,2024 Olly Betts
[846746e]4 *
[89231c4]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.
[846746e]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
[89231c4]12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
[846746e]14 *
[89231c4]15 * You should have received a copy of the GNU General Public License
[0b99107]16 * along with this program; if not, see
17 * <https://www.gnu.org/licenses/>.
[d1b1380]18 */
19
[2164fa4]20/*#define SOR 1*/
[702f518]21
[032ed06]22#if 0
23# define DEBUG_INVALID 1
24#endif
25
[4c83f84]26#include <config.h>
[d1b1380]27
28#include "debug.h"
[a420b49]29#include "cavern.h"
[c082b69]30#include "filename.h"
31#include "message.h"
[d1b1380]32#include "netbits.h"
33#include "matrix.h"
[a49a80c0]34#include "osalloc.h"
[d1b1380]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
[9965b2b]47static void print_matrix(real *M, real *B, long n);
[d1b1380]48#endif
49
[9965b2b]50static void choleski(real *M, real *B, long n);
[3fde384f]51
[d1b1380]52#ifdef SOR
[9965b2b]53static void sor(real *M, real *B, long n);
[d1b1380]54#endif
55
[a420b49]56/* for M(row, col) col must be <= row, so Y <= X */
[ae917b96]57# define M(X, Y) ((real *)M)[((((size_t)(X)) * ((X) + 1)) >> 1) + (Y)]
[421b7d2]58              /* +(Y>X?0*printf("row<col (line %d)\n",__LINE__):0) */
[ae917b96]59/*#define M_(X, Y) ((real *)M)[((((size_t)(Y)) * ((Y) + 1)) >> 1) + (X)]*/
[d1b1380]60
[9814fb7]61static void set_row(node *stn, int row_number) {
[2d8d46d]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;
[55cd7d6]69        if (to->colour < 0 && stn->name->pos == to->name->pos) {
[9814fb7]70            set_row(to, row_number);
[2d8d46d]71        }
72    }
73}
[d1b1380]74
[2d8d46d]75#ifdef NO_COVARIANCES
76# define FACTOR 1
77#else
78# define FACTOR 3
79#endif
[d1b1380]80
[55cd7d6]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 */
[032ed06]90extern void
[d9b5db53]91solve_matrix(node *list)
[032ed06]92{
[2d8d46d]93   // Assign a matrix row/column index to each group of stations with the same
94   // pos.
[55cd7d6]95   //
96   // We also set listend to the last station in the list while doing so, which
[bf9faf6]97   // we use after solving to splice list into fixedlist.
[55cd7d6]98   node *listend = NULL;
[ae917b96]99   size_t n = 0;
[55cd7d6]100   for (node *stn = list; stn; stn = stn->next) {
101      listend = stn;
102      if (stn->colour < 0) {
[9814fb7]103          set_row(stn, n++);
[2d8d46d]104      }
[2164fa4]105   }
[2d8d46d]106   SVX_ASSERT(n > 0);
[d1b1380]107
[2d8d46d]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.
[ae917b96]111   pos **stn_tab = osmalloc(n * sizeof(pos*));
[3fde384f]112
[ae917b96]113   real *M = osmalloc((((n * FACTOR * (n * FACTOR + 1)) >> 1)) * sizeof(real));
114   real *B = osmalloc(n * FACTOR * sizeof(real));
[dbd68203]115
[7811ed7]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);
[dbd68203]120
[3fde384f]121#ifdef NO_COVARIANCES
[5bb3dc4]122   int dim = 2;
[3fde384f]123#else
[2d8d46d]124   int dim = 0; /* Collapse loop to a single iteration. */
[3fde384f]125#endif
[a420b49]126   for ( ; dim >= 0; dim--) {
[907fe10]127      /* Initialise M and B to zero - zeroing "linearly" will minimise
[421b7d2]128       * paging when the matrix is large */
[66de220]129      {
[2d8d46d]130         int end = n * FACTOR;
131         for (int row = 0; row < end; row++) B[row] = (real)0.0;
[ae917b96]132         end = ((size_t)n * FACTOR * (n * FACTOR + 1)) >> 1;
[2d8d46d]133         for (int row = 0; row < end; row++) M[row] = (real)0.0;
[66de220]134      }
[dbd68203]135
[3c7ab9a]136      /* Construct matrix by going through the stn list.
[421b7d2]137       *
[907fe10]138       * All legs between two fixed stations can be ignored here.
[421b7d2]139       *
[3c7ab9a]140       * Other legs we want to add exactly once to M.  To achieve this we
[07ff034]141       * want to:
[3c7ab9a]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       */
[55cd7d6]154      for (node *stn = list; stn; stn = stn->next) {
[2d8d46d]155         if (dim == 0) {
[55cd7d6]156             stn_tab[stn->colour] = stn->name->pos;
[2d8d46d]157         }
158
[2164fa4]159#ifdef NO_COVARIANCES
160         real e;
161#else
[dac18d8]162         svar e;
[eb18f4d]163         delta a;
[2164fa4]164#endif
[b5d3988]165#if DEBUG_MATRIX_BUILD
[dbd68203]166         print_prefix(stn->name);
[b5d3988]167         printf(" used: %d colour %ld\n",
[a420b49]168                (!!stn->leg[2]) << 2 | (!!stn -> leg[1]) << 1 | (!!stn->leg[0]),
[b5d3988]169                stn->colour);
[3fde384f]170
[5bb3dc4]171         for (int dirn = 0; dirn <= 2 && stn->leg[dirn]; dirn++) {
[907fe10]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         }
[dbd68203]177         putnl();
[d1b1380]178#endif /* DEBUG_MATRIX_BUILD */
[b5d3988]179
[2d8d46d]180         int f = stn->colour;
[55cd7d6]181         SVX_ASSERT(f >= 0);
182         {
[5bb3dc4]183            for (int dirn = 0; dirn <= 2 && stn->leg[dirn]; dirn++) {
[907fe10]184               linkfor *leg = stn->leg[dirn];
185               node *to = leg->l.to;
[55cd7d6]186               if (fixed(to)) {
[907fe10]187                  bool fRev = !data_here(leg);
188                  if (fRev) leg = reverse_leg(leg);
189                  /* Ignore equated nodes */
[3fde384f]190#ifdef NO_COVARIANCES
[907fe10]191                  e = leg->v[dim];
192                  if (e != (real)0.0) {
193                     e = ((real)1.0) / e;
194                     M(f,f) += e;
[f52dcc7]195                     B[f] += e * POS(to, dim);
[907fe10]196                     if (fRev) {
[f52dcc7]197                        B[f] += leg->d[dim];
[907fe10]198                     } else {
[f52dcc7]199                        B[f] -= leg->d[dim];
[564f471]200                     }
[907fe10]201                  }
[3fde384f]202#else
[907fe10]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                     }
[5bb3dc4]209                     delta b;
[907fe10]210                     mulsd(&b, &e, &a);
[5bb3dc4]211                     for (int i = 0; i < 3; i++) {
[907fe10]212                        M(f * FACTOR + i, f * FACTOR + i) += e[i];
213                        B[f * FACTOR + i] += b[i];
[564f471]214                     }
[907fe10]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                  }
[3fde384f]219#endif
[55cd7d6]220               } else if (data_here(leg) &&
221                          (leg->l.reverse & FLAG_ARTICULATION) == 0) {
[907fe10]222                  /* forward leg, unfixed -> unfixed */
[55cd7d6]223                  int t = to->colour;
224                  SVX_ASSERT(t >= 0);
[d1b1380]225#if DEBUG_MATRIX
[16a78e0]226# ifdef NO_COVARIANCES
[907fe10]227                  printf("Leg %d to %d, var %f, delta %f\n", f, t, e,
228                         leg->d[dim]);
[16a78e0]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
[d1b1380]234#endif
[907fe10]235                  /* Ignore equated nodes & lollipops */
[3fde384f]236#ifdef NO_COVARIANCES
[907fe10]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;
[5bb3dc4]243                     real a = e * leg->d[dim];
[907fe10]244                     B[f] -= a;
245                     B[t] += a;
246                  }
[3fde384f]247#else
[907fe10]248                  if (t != f && invert_svar(&e, &leg->v)) {
249                     mulsd(&a, &e, &leg->d);
[5bb3dc4]250                     for (int i = 0; i < 3; i++) {
[907fe10]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];
[dbd68203]280                     }
281                  }
[907fe10]282#endif
[564f471]283               }
[907fe10]284            }
[dbd68203]285         }
[d1b1380]286      }
287
288#if PRINT_MATRICES
[2d8d46d]289      print_matrix(M, B, n * FACTOR); /* 'ave a look! */
[d1b1380]290#endif
291
292#ifdef SOR
[032ed06]293      /* defined in network.c, may be altered by -z<letters> on command line */
[a420b49]294      if (optimize & BITA('i'))
[2d8d46d]295         sor(M, B, n * FACTOR);
[dbd68203]296      else
[d1b1380]297#endif
[2d8d46d]298         choleski(M, B, n * FACTOR);
[d1b1380]299
[dbd68203]300      {
[2d8d46d]301         for (int m = (int)(n - 1); m >= 0; m--) {
[3fde384f]302#ifdef NO_COVARIANCES
[c19f129]303            stn_tab[m]->p[dim] = B[m];
[032ed06]304            if (dim == 0) {
[4c07c51]305               SVX_ASSERT2(pos_fixed(stn_tab[m]),
[032ed06]306                       "setting station coordinates didn't mark pos as fixed");
307            }
[3fde384f]308#else
[5bb3dc4]309            for (int i = 0; i < 3; i++) {
[c19f129]310               stn_tab[m]->p[i] = B[m * FACTOR + i];
[702f518]311            }
[4c07c51]312            SVX_ASSERT2(pos_fixed(stn_tab[m]),
[032ed06]313                    "setting station coordinates didn't mark pos as fixed");
[d1b1380]314#endif
[4a59b4f]315         }
[dbd68203]316      }
317   }
[55cd7d6]318
[bf9faf6]319   // Put the solved stations back on fixedlist.
320   listend->next = fixedlist;
321   if (fixedlist) fixedlist->prev = listend;
322   fixedlist = list;
[55cd7d6]323
[ae917b96]324   free(B);
325   free(M);
326   free(stn_tab);
[2d8d46d]327
328#if DEBUG_MATRIX
[55cd7d6]329   for (node *stn = list; stn; stn = stn->next) {
[2d8d46d]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
[d1b1380]335}
336
[4e7fb5e]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.
[702f518]341 */
[d1b1380]342/* Note M must be symmetric positive definite */
343/* routine is entitled to scribble on M and B if it wishes */
[a420b49]344static void
[9965b2b]345choleski(real *M, real *B, long n)
[a420b49]346{
[5bb3dc4]347   for (int j = 1; j < n; j++) {
[3fde384f]348      real V;
[5bb3dc4]349      for (int i = 0; i < j; i++) {
[421b7d2]350         V = (real)0.0;
[5bb3dc4]351         for (int k = 0; k < i; k++) V += M(i,k) * M(j,k) * M(k,k);
[a420b49]352         M(j,i) = (M(j,i) - V) / M(i,i);
[dbd68203]353      }
354      V = (real)0.0;
[5bb3dc4]355      for (int k = 0; k < j; k++) V += M(j,k) * M(j,k) * M(k,k);
[3fde384f]356      M(j,j) -= V; /* may be best to add M() last for numerical reasons too */
[dbd68203]357   }
[d1b1380]358
[dbd68203]359   /* Multiply x by L inverse */
[5bb3dc4]360   for (int i = 0; i < n - 1; i++) {
361      for (int j = i + 1; j < n; j++) {
[dbd68203]362         B[j] -= M(j,i) * B[i];
[3fde384f]363      }
[dbd68203]364   }
[d1b1380]365
[dbd68203]366   /* Multiply x by D inverse */
[5bb3dc4]367   for (int i = 0; i < n; i++) {
[dbd68203]368      B[i] /= M(i,i);
[3fde384f]369   }
370
371   /* Multiply x by (L transpose) inverse */
[5bb3dc4]372   for (int i = (int)(n - 1); i > 0; i--) {
373      for (int j = i - 1; j >= 0; j--) {
[421b7d2]374         B[j] -= M(i,j) * B[i];
[3fde384f]375      }
[dbd68203]376   }
[d1b1380]377
[dbd68203]378   /* printf("\n%ld/%ld\n\n",flops,flopsTot); */
[d1b1380]379}
380
381#ifdef SOR
382/* factor to use for SOR (must have 1 <= SOR_factor < 2) */
[702f518]383#define SOR_factor 1.93 /* 1.95 */
[d1b1380]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 */
[a420b49]387static void
[9965b2b]388sor(real *M, real *B, long n)
[a420b49]389{
[dbd68203]390   long it = 0;
[d1b1380]391
[ae917b96]392   real *X = osmalloc(n * sizeof(real));
[d1b1380]393
[5bb3dc4]394   const real threshold = 0.00001;
[d1b1380]395
[647407d]396   printf("reciprocating diagonal\n"); /* TRANSLATE */
[d1b1380]397
[3fde384f]398   /* munge diagonal so we can multiply rather than divide */
[5bb3dc4]399   for (int row = n - 1; row >= 0; row--) {
[dbd68203]400      M(row,row) = 1 / M(row,row);
[702f518]401      X[row] = 0;
[dbd68203]402   }
[d1b1380]403
[647407d]404   printf("starting iteration\n"); /* TRANSLATE */
[d1b1380]405
[5bb3dc4]406   real t;
[dbd68203]407   do {
408      /*printf("*");*/
409      it++;
410      t = 0.0;
[5bb3dc4]411      for (int row = 0; row < n; row++) {
412         real x = B[row];
413         int col;
[a420b49]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];
[dbd68203]416         x *= M(row,row);
[3b8b342]417         real sor_delta = (x - X[row]) * SOR_factor;
418         X[row] += sor_delta;
419         real t2 = fabs(sor_delta);
[dbd68203]420         if (t2 > t) t = t2;
421      }
[3b8b342]422      printf("% 6ld: %8.6f\n", it, t);
[dbd68203]423   } while (t >= threshold && it < 100000);
[d1b1380]424
[dbd68203]425   if (t >= threshold) {
426      fprintf(stderr, "*not* converged after %ld iterations\n", it);
427      BUG("iteration stinks");
428   }
[d1b1380]429
[647407d]430   printf("%ld iterations\n", it); /* TRANSLATE */
[d1b1380]431
432#if 0
[dbd68203]433   putnl();
[5bb3dc4]434   for (int row = n - 1; row >= 0; row--) {
[dbd68203]435      t = 0.0;
[5bb3dc4]436      for (int col = 0; col < row; col++) t += M(row, col) * X[col];
[a420b49]437      t += X[row] / M(row, row);
438      for (col = row + 1; col < n; col++)
439         t += M(col, row) * X[col];
[b5d3988]440      printf("[ %f %f ]\n", t, B[row]);
[dbd68203]441   }
[d1b1380]442#endif
443
[5bb3dc4]444   for (int row = n - 1; row >= 0; row--) B[row] = X[row];
[d1b1380]445
[ae917b96]446   free(X);
[647407d]447   printf("\ndone\n"); /* TRANSLATE */
[dbd68203]448}
[d1b1380]449#endif
450
451#if PRINT_MATRICES
[a420b49]452static void
[9965b2b]453print_matrix(real *M, real *B, long n)
[a420b49]454{
[dbd68203]455   printf("Matrix, M and vector, B:\n");
[5bb3dc4]456   for (long row = 0; row < n; row++) {
457      long col;
[a420b49]458      for (col = 0; col <= row; col++) printf("%6.2f\t", M(row, col));
459      for (; col <= n; col++) printf(" \t");
[dbd68203]460      printf("\t%6.2f\n", B[row]);
461   }
462   putnl();
463   return;
[d1b1380]464}
465#endif
Note: See TracBrowser for help on using the repository browser.