source: git/src/matrix.c @ 7ff9e18

stereo-2025
Last change on this file since 7ff9e18 was bf9faf6, checked in by Olly Betts <olly@…>, 8 months ago

Fix component counting bugs

The component count was wrong in some cases, and we calculate the number
of loops using this component count, so the loop count would be wrong by
the same amount in these cases.

  • 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
16 * along with this program; if not, write to the Free Software
[ecbc6c18]17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
[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"
34#include "out.h"
35
36#undef PRINT_MATRICES
37#define PRINT_MATRICES 0
38
39#undef DEBUG_MATRIX_BUILD
40#define DEBUG_MATRIX_BUILD 0
41
42#undef DEBUG_MATRIX
43#define DEBUG_MATRIX 0
44
45#if PRINT_MATRICES
[9965b2b]46static void print_matrix(real *M, real *B, long n);
[d1b1380]47#endif
48
[9965b2b]49static void choleski(real *M, real *B, long n);
[3fde384f]50
[d1b1380]51#ifdef SOR
[9965b2b]52static void sor(real *M, real *B, long n);
[d1b1380]53#endif
54
[a420b49]55/* for M(row, col) col must be <= row, so Y <= X */
[9965b2b]56# define M(X, Y) ((real *)M)[((((OSSIZE_T)(X)) * ((X) + 1)) >> 1) + (Y)]
[421b7d2]57              /* +(Y>X?0*printf("row<col (line %d)\n",__LINE__):0) */
[9965b2b]58/*#define M_(X, Y) ((real *)M)[((((OSSIZE_T)(Y)) * ((Y) + 1)) >> 1) + (X)]*/
[d1b1380]59
[9814fb7]60static void set_row(node *stn, int row_number) {
[2d8d46d]61    // We store the matrix row/column index in stn->colour for quick and easy
62    // lookup when copying out the solved station coordinates.
63    stn->colour = row_number;
64    for (int d = 0; d < 3; d++) {
65        linkfor *leg = stn->leg[d];
66        if (!leg) break;
67        node *to = leg->l.to;
[55cd7d6]68        if (to->colour < 0 && stn->name->pos == to->name->pos) {
[9814fb7]69            set_row(to, row_number);
[2d8d46d]70        }
71    }
72}
[d1b1380]73
[2d8d46d]74#ifdef NO_COVARIANCES
75# define FACTOR 1
76#else
77# define FACTOR 3
78#endif
[d1b1380]79
[55cd7d6]80/* Find positions for a subset of the reduced network by solving a matrix
81 * equation.
82 *
83 * list is a non-empty linked list of unfixed stations to solve for.
84 *
85 * As a pre-condition, all stations in list must have a negative value for
86 * stn->colour.  This can be ensured by the caller (which avoids having to
87 * make an extra pass over the list just to set the colours suitably).
88 */
[032ed06]89extern void
[d9b5db53]90solve_matrix(node *list)
[032ed06]91{
[2d8d46d]92   // Assign a matrix row/column index to each group of stations with the same
93   // pos.
[55cd7d6]94   //
95   // We also set listend to the last station in the list while doing so, which
[bf9faf6]96   // we use after solving to splice list into fixedlist.
[55cd7d6]97   node *listend = NULL;
[2d8d46d]98   long n = 0;
[55cd7d6]99   for (node *stn = list; stn; stn = stn->next) {
100      listend = stn;
101      if (stn->colour < 0) {
[9814fb7]102          set_row(stn, n++);
[2d8d46d]103      }
[2164fa4]104   }
[2d8d46d]105   SVX_ASSERT(n > 0);
[d1b1380]106
[2d8d46d]107   // Array to map from row/column index to pos.  We fill this in as we build
108   // the matrix, and use it to know where to copy the solved station
109   // coordinates to.
110   pos **stn_tab = osmalloc((OSSIZE_T)(n * ossizeof(pos*)));
[3fde384f]111
[2d8d46d]112   /* (OSSIZE_T) cast may be needed if n >= 181 */
113   real *M = osmalloc((OSSIZE_T)((((OSSIZE_T)n * FACTOR * (n * FACTOR + 1)) >> 1)) * ossizeof(real));
114   real *B = osmalloc((OSSIZE_T)(n * FACTOR * ossizeof(real)));
[dbd68203]115
[647407d]116   if (!fQuiet) {
[2d8d46d]117      if (n == 1)
[a4adf09]118         out_current_action(msg(/*Solving one equation*/78));
119      else
[2d8d46d]120         out_current_action1(msg(/*Solving %d simultaneous equations*/75), n);
[dbd68203]121   }
122
[3fde384f]123#ifdef NO_COVARIANCES
[5bb3dc4]124   int dim = 2;
[3fde384f]125#else
[2d8d46d]126   int dim = 0; /* Collapse loop to a single iteration. */
[3fde384f]127#endif
[a420b49]128   for ( ; dim >= 0; dim--) {
[907fe10]129      /* Initialise M and B to zero - zeroing "linearly" will minimise
[421b7d2]130       * paging when the matrix is large */
[66de220]131      {
[2d8d46d]132         int end = n * FACTOR;
133         for (int row = 0; row < end; row++) B[row] = (real)0.0;
134         end = ((OSSIZE_T)n * FACTOR * (n * FACTOR + 1)) >> 1;
135         for (int row = 0; row < end; row++) M[row] = (real)0.0;
[66de220]136      }
[dbd68203]137
[3c7ab9a]138      /* Construct matrix by going through the stn list.
[421b7d2]139       *
[907fe10]140       * All legs between two fixed stations can be ignored here.
[421b7d2]141       *
[3c7ab9a]142       * Other legs we want to add exactly once to M.  To achieve this we
[07ff034]143       * want to:
[3c7ab9a]144       *
145       * - add forward legs between two unfixed stations,
146       *
147       * - add legs from unfixed stations to fixed stations (we do them from
148       *   the unfixed end so we don't need to detect when we're at a fixed
149       *   point cut line and determine which side we're currently dealing
150       *   with).
151       *
152       * To implement this, we only look at legs from unfixed stations and add
153       * a leg if to a fixed station, or to an unfixed station and it's a
154       * forward leg.
155       */
[55cd7d6]156      for (node *stn = list; stn; stn = stn->next) {
[2d8d46d]157         if (dim == 0) {
[55cd7d6]158             stn_tab[stn->colour] = stn->name->pos;
[2d8d46d]159         }
160
[2164fa4]161#ifdef NO_COVARIANCES
162         real e;
163#else
[dac18d8]164         svar e;
[eb18f4d]165         delta a;
[2164fa4]166#endif
[b5d3988]167#if DEBUG_MATRIX_BUILD
[dbd68203]168         print_prefix(stn->name);
[b5d3988]169         printf(" used: %d colour %ld\n",
[a420b49]170                (!!stn->leg[2]) << 2 | (!!stn -> leg[1]) << 1 | (!!stn->leg[0]),
[b5d3988]171                stn->colour);
[3fde384f]172
[5bb3dc4]173         for (int dirn = 0; dirn <= 2 && stn->leg[dirn]; dirn++) {
[907fe10]174            printf("Leg %d, vx=%f, reverse=%d, to ", dirn,
175                   stn->leg[dirn]->v[0], stn->leg[dirn]->l.reverse);
176            print_prefix(stn->leg[dirn]->l.to->name);
177            putnl();
178         }
[dbd68203]179         putnl();
[d1b1380]180#endif /* DEBUG_MATRIX_BUILD */
[b5d3988]181
[2d8d46d]182         int f = stn->colour;
[55cd7d6]183         SVX_ASSERT(f >= 0);
184         {
[5bb3dc4]185            for (int dirn = 0; dirn <= 2 && stn->leg[dirn]; dirn++) {
[907fe10]186               linkfor *leg = stn->leg[dirn];
187               node *to = leg->l.to;
[55cd7d6]188               if (fixed(to)) {
[907fe10]189                  bool fRev = !data_here(leg);
190                  if (fRev) leg = reverse_leg(leg);
191                  /* Ignore equated nodes */
[3fde384f]192#ifdef NO_COVARIANCES
[907fe10]193                  e = leg->v[dim];
194                  if (e != (real)0.0) {
195                     e = ((real)1.0) / e;
196                     M(f,f) += e;
[f52dcc7]197                     B[f] += e * POS(to, dim);
[907fe10]198                     if (fRev) {
[f52dcc7]199                        B[f] += leg->d[dim];
[907fe10]200                     } else {
[f52dcc7]201                        B[f] -= leg->d[dim];
[564f471]202                     }
[907fe10]203                  }
[3fde384f]204#else
[907fe10]205                  if (invert_svar(&e, &leg->v)) {
206                     if (fRev) {
207                        adddd(&a, &POSD(to), &leg->d);
208                     } else {
209                        subdd(&a, &POSD(to), &leg->d);
210                     }
[5bb3dc4]211                     delta b;
[907fe10]212                     mulsd(&b, &e, &a);
[5bb3dc4]213                     for (int i = 0; i < 3; i++) {
[907fe10]214                        M(f * FACTOR + i, f * FACTOR + i) += e[i];
215                        B[f * FACTOR + i] += b[i];
[564f471]216                     }
[907fe10]217                     M(f * FACTOR + 1, f * FACTOR) += e[3];
218                     M(f * FACTOR + 2, f * FACTOR) += e[4];
219                     M(f * FACTOR + 2, f * FACTOR + 1) += e[5];
220                  }
[3fde384f]221#endif
[55cd7d6]222               } else if (data_here(leg) &&
223                          (leg->l.reverse & FLAG_ARTICULATION) == 0) {
[907fe10]224                  /* forward leg, unfixed -> unfixed */
[55cd7d6]225                  int t = to->colour;
226                  SVX_ASSERT(t >= 0);
[d1b1380]227#if DEBUG_MATRIX
[16a78e0]228# ifdef NO_COVARIANCES
[907fe10]229                  printf("Leg %d to %d, var %f, delta %f\n", f, t, e,
230                         leg->d[dim]);
[16a78e0]231# else
232                  printf("Leg %d to %d, var (%f, %f, %f; %f, %f, %f), "
233                         "delta %f\n", f, t, e[0], e[1], e[2], e[3], e[4], e[5],
234                         leg->d[dim]);
235# endif
[d1b1380]236#endif
[907fe10]237                  /* Ignore equated nodes & lollipops */
[3fde384f]238#ifdef NO_COVARIANCES
[907fe10]239                  e = leg->v[dim];
240                  if (t != f && e != (real)0.0) {
241                     e = ((real)1.0) / e;
242                     M(f,f) += e;
243                     M(t,t) += e;
244                     if (f < t) M(t,f) -= e; else M(f,t) -= e;
[5bb3dc4]245                     real a = e * leg->d[dim];
[907fe10]246                     B[f] -= a;
247                     B[t] += a;
248                  }
[3fde384f]249#else
[907fe10]250                  if (t != f && invert_svar(&e, &leg->v)) {
251                     mulsd(&a, &e, &leg->d);
[5bb3dc4]252                     for (int i = 0; i < 3; i++) {
[907fe10]253                        M(f * FACTOR + i, f * FACTOR + i) += e[i];
254                        M(t * FACTOR + i, t * FACTOR + i) += e[i];
255                        if (f < t)
256                           M(t * FACTOR + i, f * FACTOR + i) -= e[i];
257                        else
258                           M(f * FACTOR + i, t * FACTOR + i) -= e[i];
259                        B[f * FACTOR + i] -= a[i];
260                        B[t * FACTOR + i] += a[i];
261                     }
262                     M(f * FACTOR + 1, f * FACTOR) += e[3];
263                     M(t * FACTOR + 1, t * FACTOR) += e[3];
264                     M(f * FACTOR + 2, f * FACTOR) += e[4];
265                     M(t * FACTOR + 2, t * FACTOR) += e[4];
266                     M(f * FACTOR + 2, f * FACTOR + 1) += e[5];
267                     M(t * FACTOR + 2, t * FACTOR + 1) += e[5];
268                     if (f < t) {
269                        M(t * FACTOR + 1, f * FACTOR) -= e[3];
270                        M(t * FACTOR, f * FACTOR + 1) -= e[3];
271                        M(t * FACTOR + 2, f * FACTOR) -= e[4];
272                        M(t * FACTOR, f * FACTOR + 2) -= e[4];
273                        M(t * FACTOR + 2, f * FACTOR + 1) -= e[5];
274                        M(t * FACTOR + 1, f * FACTOR + 2) -= e[5];
275                     } else {
276                        M(f * FACTOR + 1, t * FACTOR) -= e[3];
277                        M(f * FACTOR, t * FACTOR + 1) -= e[3];
278                        M(f * FACTOR + 2, t * FACTOR) -= e[4];
279                        M(f * FACTOR, t * FACTOR + 2) -= e[4];
280                        M(f * FACTOR + 2, t * FACTOR + 1) -= e[5];
281                        M(f * FACTOR + 1, t * FACTOR + 2) -= e[5];
[dbd68203]282                     }
283                  }
[907fe10]284#endif
[564f471]285               }
[907fe10]286            }
[dbd68203]287         }
[d1b1380]288      }
289
290#if PRINT_MATRICES
[2d8d46d]291      print_matrix(M, B, n * FACTOR); /* 'ave a look! */
[d1b1380]292#endif
293
294#ifdef SOR
[032ed06]295      /* defined in network.c, may be altered by -z<letters> on command line */
[a420b49]296      if (optimize & BITA('i'))
[2d8d46d]297         sor(M, B, n * FACTOR);
[dbd68203]298      else
[d1b1380]299#endif
[2d8d46d]300         choleski(M, B, n * FACTOR);
[d1b1380]301
[dbd68203]302      {
[2d8d46d]303         for (int m = (int)(n - 1); m >= 0; m--) {
[3fde384f]304#ifdef NO_COVARIANCES
[c19f129]305            stn_tab[m]->p[dim] = B[m];
[032ed06]306            if (dim == 0) {
[4c07c51]307               SVX_ASSERT2(pos_fixed(stn_tab[m]),
[032ed06]308                       "setting station coordinates didn't mark pos as fixed");
309            }
[3fde384f]310#else
[5bb3dc4]311            for (int i = 0; i < 3; i++) {
[c19f129]312               stn_tab[m]->p[i] = B[m * FACTOR + i];
[702f518]313            }
[4c07c51]314            SVX_ASSERT2(pos_fixed(stn_tab[m]),
[032ed06]315                    "setting station coordinates didn't mark pos as fixed");
[d1b1380]316#endif
[4a59b4f]317         }
[dbd68203]318      }
319   }
[55cd7d6]320
[bf9faf6]321   // Put the solved stations back on fixedlist.
322   listend->next = fixedlist;
323   if (fixedlist) fixedlist->prev = listend;
324   fixedlist = list;
[55cd7d6]325
[dbd68203]326   osfree(B);
327   osfree(M);
[2d8d46d]328   osfree(stn_tab);
329
330#if DEBUG_MATRIX
[55cd7d6]331   for (node *stn = list; stn; stn = stn->next) {
[2d8d46d]332      printf("(%8.2f, %8.2f, %8.2f ) ", POS(stn, 0), POS(stn, 1), POS(stn, 2));
333      print_prefix(stn->name);
334      putnl();
335   }
336#endif
[d1b1380]337}
338
[702f518]339/* Solve MX=B for X by Choleski factorisation - modified Choleski actually
340 * since we factor into LDL' while Choleski is just LL'
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
[5bb3dc4]392   real *X = osmalloc(n * ossizeof(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
[dbd68203]446   osfree(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.