source: git/src/matrix.c @ 86907b0

stereo-2025
Last change on this file since 86907b0 was 86907b0, checked in by Olly Betts <olly@…>, 12 months ago

Stop trying to reallocate stn_tab smaller

This was implemented back in 2001 with the intention of freeing up as
much memory as possible before allocating memory for the matrix.

However modern realloc() implementations are unlikely to actually
free up any memory - e.g. glibc's never shrinks, tcmalloc's
only shrinks if the size is reduced by >= 50%, which will never
happen for real-world survey data. Also, unless the implementation
can shrink a block in place we're unlikely to free up memory that
can be use for a large matrix allocation anyway.

Meanwhile even the oldest machines still in use have enough memory
that we probably don't need to worry about memory here anyway.

  • Property mode set to 100644
File size: 12.7 KB
Line 
1/* matrix.c
2 * Matrix building and solving routines
3 * Copyright (C) 1993-2003,2010,2013 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, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
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 "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
46static void print_matrix(real *M, real *B, long n);
47#endif
48
49static void choleski(real *M, real *B, long n);
50
51#ifdef SOR
52static void sor(real *M, real *B, long n);
53#endif
54
55/* for M(row, col) col must be <= row, so Y <= X */
56# define M(X, Y) ((real *)M)[((((OSSIZE_T)(X)) * ((X) + 1)) >> 1) + (Y)]
57              /* +(Y>X?0*printf("row<col (line %d)\n",__LINE__):0) */
58/*#define M_(X, Y) ((real *)M)[((((OSSIZE_T)(Y)) * ((Y) + 1)) >> 1) + (X)]*/
59
60static int find_stn_in_tab(node *stn);
61static int add_stn_to_tab(node *stn);
62static void build_matrix(node *list);
63
64static long n_stn_tab;
65
66static pos **stn_tab;
67
68extern void
69solve_matrix(node *list)
70{
71   node *stn;
72   long n = 0;
73   FOR_EACH_STN(stn, list) {
74      if (!fixed(stn)) n++;
75   }
76   if (n == 0) return;
77
78   /* We need to allocate stn_tab with one entry per unfixed cluster of equated
79    * stations, but it's much simpler to count the number of unfixed nodes.
80    * This will over-count stations with more than 3 legs and equated stations
81    * but in a typical survey that's a small minority of stations.
82    */
83   stn_tab = osmalloc((OSSIZE_T)(n * ossizeof(pos*)));
84   n_stn_tab = 0;
85
86   FOR_EACH_STN(stn, list) {
87      if (!fixed(stn)) add_stn_to_tab(stn);
88   }
89
90   build_matrix(list);
91#if DEBUG_MATRIX
92   FOR_EACH_STN(stn, list) {
93      printf("(%8.2f, %8.2f, %8.2f ) ", POS(stn, 0), POS(stn, 1), POS(stn, 2));
94      print_prefix(stn->name);
95      putnl();
96   }
97#endif
98
99   osfree(stn_tab);
100}
101
102#ifdef NO_COVARIANCES
103# define FACTOR 1
104#else
105# define FACTOR 3
106#endif
107
108static void
109build_matrix(node *list)
110{
111   if (n_stn_tab == 0) {
112      if (!fQuiet)
113         puts(msg(/*Network solved by reduction - no simultaneous equations to solve.*/74));
114      return;
115   }
116   /* (OSSIZE_T) cast may be needed if n_stn_tab>=181 */
117   real *M = osmalloc((OSSIZE_T)((((OSSIZE_T)n_stn_tab * FACTOR * (n_stn_tab * FACTOR + 1)) >> 1)) * ossizeof(real));
118   real *B = osmalloc((OSSIZE_T)(n_stn_tab * FACTOR * ossizeof(real)));
119
120   if (!fQuiet) {
121      if (n_stn_tab == 1)
122         out_current_action(msg(/*Solving one equation*/78));
123      else
124         out_current_action1(msg(/*Solving %d simultaneous equations*/75), n_stn_tab);
125   }
126
127#ifdef NO_COVARIANCES
128   int dim = 2;
129#else
130   int dim = 0; /* fudge next loop for now */
131#endif
132   for ( ; dim >= 0; dim--) {
133      node *stn;
134      int row;
135
136      /* Initialise M and B to zero - zeroing "linearly" will minimise
137       * paging when the matrix is large */
138      {
139         int end = n_stn_tab * FACTOR;
140         for (row = 0; row < end; row++) B[row] = (real)0.0;
141         end = ((OSSIZE_T)n_stn_tab * FACTOR * (n_stn_tab * FACTOR + 1)) >> 1;
142         for (row = 0; row < end; row++) M[row] = (real)0.0;
143      }
144
145      /* Construct matrix by going through the stn list.
146       *
147       * All legs between two fixed stations can be ignored here.
148       *
149       * Other legs we want to add exactly once to M.  To achieve this we
150       * wan to:
151       *
152       * - add forward legs between two unfixed stations,
153       *
154       * - add legs from unfixed stations to fixed stations (we do them from
155       *   the unfixed end so we don't need to detect when we're at a fixed
156       *   point cut line and determine which side we're currently dealing
157       *   with).
158       *
159       * To implement this, we only look at legs from unfixed stations and add
160       * a leg if to a fixed station, or to an unfixed station and it's a
161       * forward leg.
162       */
163      FOR_EACH_STN(stn, list) {
164#ifdef NO_COVARIANCES
165         real e;
166#else
167         svar e;
168         delta a;
169#endif
170#if DEBUG_MATRIX_BUILD
171         print_prefix(stn->name);
172         printf(" used: %d colour %ld\n",
173                (!!stn->leg[2]) << 2 | (!!stn -> leg[1]) << 1 | (!!stn->leg[0]),
174                stn->colour);
175
176         for (int dirn = 0; dirn <= 2 && stn->leg[dirn]; dirn++) {
177#ifdef NO_COVARIANCES
178            printf("Leg %d, vx=%f, reverse=%d, to ", dirn,
179                   stn->leg[dirn]->v[0], stn->leg[dirn]->l.reverse);
180#else
181            printf("Leg %d, vx=%f, reverse=%d, to ", dirn,
182                   stn->leg[dirn]->v[0][0], stn->leg[dirn]->l.reverse);
183#endif
184            print_prefix(stn->leg[dirn]->l.to->name);
185            putnl();
186         }
187         putnl();
188#endif /* DEBUG_MATRIX_BUILD */
189
190         if (!fixed(stn)) {
191            int f = find_stn_in_tab(stn);
192            for (int dirn = 0; dirn <= 2 && stn->leg[dirn]; dirn++) {
193               linkfor *leg = stn->leg[dirn];
194               node *to = leg->l.to;
195               if (fixed(to)) {
196                  bool fRev = !data_here(leg);
197                  if (fRev) leg = reverse_leg(leg);
198                  /* Ignore equated nodes */
199#ifdef NO_COVARIANCES
200                  e = leg->v[dim];
201                  if (e != (real)0.0) {
202                     e = ((real)1.0) / e;
203                     M(f,f) += e;
204                     B[f] += e * POS(to, dim);
205                     if (fRev) {
206                        B[f] += leg->d[dim];
207                     } else {
208                        B[f] -= leg->d[dim];
209                     }
210                  }
211#else
212                  if (invert_svar(&e, &leg->v)) {
213                     if (fRev) {
214                        adddd(&a, &POSD(to), &leg->d);
215                     } else {
216                        subdd(&a, &POSD(to), &leg->d);
217                     }
218                     delta b;
219                     mulsd(&b, &e, &a);
220                     for (int i = 0; i < 3; i++) {
221                        M(f * FACTOR + i, f * FACTOR + i) += e[i];
222                        B[f * FACTOR + i] += b[i];
223                     }
224                     M(f * FACTOR + 1, f * FACTOR) += e[3];
225                     M(f * FACTOR + 2, f * FACTOR) += e[4];
226                     M(f * FACTOR + 2, f * FACTOR + 1) += e[5];
227                  }
228#endif
229               } else if (data_here(leg)) {
230                  /* forward leg, unfixed -> unfixed */
231                  int t = find_stn_in_tab(to);
232#if DEBUG_MATRIX
233                  printf("Leg %d to %d, var %f, delta %f\n", f, t, e,
234                         leg->d[dim]);
235#endif
236                  /* Ignore equated nodes & lollipops */
237#ifdef NO_COVARIANCES
238                  e = leg->v[dim];
239                  if (t != f && e != (real)0.0) {
240                     e = ((real)1.0) / e;
241                     M(f,f) += e;
242                     M(t,t) += e;
243                     if (f < t) M(t,f) -= e; else M(f,t) -= e;
244                     real a = e * leg->d[dim];
245                     B[f] -= a;
246                     B[t] += a;
247                  }
248#else
249                  if (t != f && invert_svar(&e, &leg->v)) {
250                     mulsd(&a, &e, &leg->d);
251                     for (int i = 0; i < 3; i++) {
252                        M(f * FACTOR + i, f * FACTOR + i) += e[i];
253                        M(t * FACTOR + i, t * FACTOR + i) += e[i];
254                        if (f < t)
255                           M(t * FACTOR + i, f * FACTOR + i) -= e[i];
256                        else
257                           M(f * FACTOR + i, t * FACTOR + i) -= e[i];
258                        B[f * FACTOR + i] -= a[i];
259                        B[t * FACTOR + i] += a[i];
260                     }
261                     M(f * FACTOR + 1, f * FACTOR) += e[3];
262                     M(t * FACTOR + 1, t * FACTOR) += e[3];
263                     M(f * FACTOR + 2, f * FACTOR) += e[4];
264                     M(t * FACTOR + 2, t * FACTOR) += e[4];
265                     M(f * FACTOR + 2, f * FACTOR + 1) += e[5];
266                     M(t * FACTOR + 2, t * FACTOR + 1) += e[5];
267                     if (f < t) {
268                        M(t * FACTOR + 1, f * FACTOR) -= e[3];
269                        M(t * FACTOR, f * FACTOR + 1) -= e[3];
270                        M(t * FACTOR + 2, f * FACTOR) -= e[4];
271                        M(t * FACTOR, f * FACTOR + 2) -= e[4];
272                        M(t * FACTOR + 2, f * FACTOR + 1) -= e[5];
273                        M(t * FACTOR + 1, f * FACTOR + 2) -= e[5];
274                     } else {
275                        M(f * FACTOR + 1, t * FACTOR) -= e[3];
276                        M(f * FACTOR, t * FACTOR + 1) -= e[3];
277                        M(f * FACTOR + 2, t * FACTOR) -= e[4];
278                        M(f * FACTOR, t * FACTOR + 2) -= e[4];
279                        M(f * FACTOR + 2, t * FACTOR + 1) -= e[5];
280                        M(f * FACTOR + 1, t * FACTOR + 2) -= e[5];
281                     }
282                  }
283#endif
284               }
285            }
286         }
287      }
288
289#if PRINT_MATRICES
290      print_matrix(M, B, n_stn_tab * FACTOR); /* 'ave a look! */
291#endif
292
293#ifdef SOR
294      /* defined in network.c, may be altered by -z<letters> on command line */
295      if (optimize & BITA('i'))
296         sor(M, B, n_stn_tab * FACTOR);
297      else
298#endif
299         choleski(M, B, n_stn_tab * FACTOR);
300
301      {
302         for (int m = (int)(n_stn_tab - 1); m >= 0; m--) {
303#ifdef NO_COVARIANCES
304            stn_tab[m]->p[dim] = B[m];
305            if (dim == 0) {
306               SVX_ASSERT2(pos_fixed(stn_tab[m]),
307                       "setting station coordinates didn't mark pos as fixed");
308            }
309#else
310            for (int i = 0; i < 3; i++) {
311               stn_tab[m]->p[i] = B[m * FACTOR + i];
312            }
313            SVX_ASSERT2(pos_fixed(stn_tab[m]),
314                    "setting station coordinates didn't mark pos as fixed");
315#endif
316         }
317#if EXPLICIT_FIXED_FLAG
318         for (int m = n_stn_tab - 1; m >= 0; m--) fixpos(stn_tab[m]);
319#endif
320      }
321   }
322   osfree(B);
323   osfree(M);
324}
325
326static int
327find_stn_in_tab(node *stn)
328{
329   int i = 0;
330   pos *p = stn->name->pos;
331   while (stn_tab[i] != p)
332      if (++i == n_stn_tab) {
333#if DEBUG_INVALID
334         fputs("Station ", stderr);
335         fprint_prefix(stderr, stn->name);
336         fputs(" not in table\n\n", stderr);
337#endif
338#if 0
339         print_prefix(stn->name);
340         printf(" used: %d colour %d\n",
341                (!!stn->leg[2])<<2 | (!!stn->leg[1])<<1 | (!!stn->leg[0]),
342                stn->colour);
343#endif
344         fatalerror(/*Bug in program detected! Please report this to the authors*/11);
345      }
346   return i;
347}
348
349static int
350add_stn_to_tab(node *stn)
351{
352   int i;
353   pos *p = stn->name->pos;
354   for (i = 0; i < n_stn_tab; i++) {
355      if (stn_tab[i] == p) return i;
356   }
357   stn_tab[n_stn_tab++] = p;
358   return i;
359}
360
361/* Solve MX=B for X by Choleski factorisation - modified Choleski actually
362 * since we factor into LDL' while Choleski is just LL'
363 */
364/* Note M must be symmetric positive definite */
365/* routine is entitled to scribble on M and B if it wishes */
366static void
367choleski(real *M, real *B, long n)
368{
369   for (int j = 1; j < n; j++) {
370      real V;
371      for (int i = 0; i < j; i++) {
372         V = (real)0.0;
373         for (int k = 0; k < i; k++) V += M(i,k) * M(j,k) * M(k,k);
374         M(j,i) = (M(j,i) - V) / M(i,i);
375      }
376      V = (real)0.0;
377      for (int k = 0; k < j; k++) V += M(j,k) * M(j,k) * M(k,k);
378      M(j,j) -= V; /* may be best to add M() last for numerical reasons too */
379   }
380
381   /* Multiply x by L inverse */
382   for (int i = 0; i < n - 1; i++) {
383      for (int j = i + 1; j < n; j++) {
384         B[j] -= M(j,i) * B[i];
385      }
386   }
387
388   /* Multiply x by D inverse */
389   for (int i = 0; i < n; i++) {
390      B[i] /= M(i,i);
391   }
392
393   /* Multiply x by (L transpose) inverse */
394   for (int i = (int)(n - 1); i > 0; i--) {
395      for (int j = i - 1; j >= 0; j--) {
396         B[j] -= M(i,j) * B[i];
397      }
398   }
399
400   /* printf("\n%ld/%ld\n\n",flops,flopsTot); */
401}
402
403#ifdef SOR
404/* factor to use for SOR (must have 1 <= SOR_factor < 2) */
405#define SOR_factor 1.93 /* 1.95 */
406
407/* Solve MX=B for X by SOR of Gauss-Siedel */
408/* routine is entitled to scribble on M and B if it wishes */
409static void
410sor(real *M, real *B, long n)
411{
412   long it = 0;
413
414   real *X = osmalloc(n * ossizeof(real));
415
416   const real threshold = 0.00001;
417
418   printf("reciprocating diagonal\n"); /* TRANSLATE */
419
420   /* munge diagonal so we can multiply rather than divide */
421   for (int row = n - 1; row >= 0; row--) {
422      M(row,row) = 1 / M(row,row);
423      X[row] = 0;
424   }
425
426   printf("starting iteration\n"); /* TRANSLATE */
427
428   real t;
429   do {
430      /*printf("*");*/
431      it++;
432      t = 0.0;
433      for (int row = 0; row < n; row++) {
434         real x = B[row];
435         int col;
436         for (col = 0; col < row; col++) x -= M(row,col) * X[col];
437         for (col++; col < n; col++) x -= M(col,row) * X[col];
438         x *= M(row,row);
439         real delta = (x - X[row]) * SOR_factor;
440         X[row] += delta;
441         real t2 = fabs(delta);
442         if (t2 > t) t = t2;
443      }
444      printf("% 6d: %8.6f\n", it, t);
445   } while (t >= threshold && it < 100000);
446
447   if (t >= threshold) {
448      fprintf(stderr, "*not* converged after %ld iterations\n", it);
449      BUG("iteration stinks");
450   }
451
452   printf("%ld iterations\n", it); /* TRANSLATE */
453
454#if 0
455   putnl();
456   for (int row = n - 1; row >= 0; row--) {
457      t = 0.0;
458      for (int col = 0; col < row; col++) t += M(row, col) * X[col];
459      t += X[row] / M(row, row);
460      for (col = row + 1; col < n; col++)
461         t += M(col, row) * X[col];
462      printf("[ %f %f ]\n", t, B[row]);
463   }
464#endif
465
466   for (int row = n - 1; row >= 0; row--) B[row] = X[row];
467
468   osfree(X);
469   printf("\ndone\n"); /* TRANSLATE */
470}
471#endif
472
473#if PRINT_MATRICES
474static void
475print_matrix(real *M, real *B, long n)
476{
477   printf("Matrix, M and vector, B:\n");
478   for (long row = 0; row < n; row++) {
479      long col;
480      for (col = 0; col <= row; col++) printf("%6.2f\t", M(row, col));
481      for (; col <= n; col++) printf(" \t");
482      printf("\t%6.2f\n", B[row]);
483   }
484   putnl();
485   return;
486}
487#endif
Note: See TracBrowser for help on using the repository browser.