source: git/kernel/linearAlgebra.cc @ 19bf86

spielwiese
Last change on this file since 19bf86 was 19bf86, checked in by Frank Seelisch <seelisch@…>, 14 years ago
implemented and doc'ed lusolve; available thru interpreter git-svn-id: file:///usr/local/Singular/svn/trunk@12885 2c84dea3-7e68-4137-9b89-c4e89433aadc
  • Property mode set to 100644
File size: 11.5 KB
Line 
1/*****************************************************************************\
2 * Computer Algebra System SINGULAR   
3\*****************************************************************************/
4/** @file lineareAlgebra.cc
5 *
6 * This file implements basic linear algebra functionality.
7 *
8 * For more general information, see the documentation in
9 * lineareAlgebra.h.
10 *
11 * @author Frank Seelisch
12 *
13 * @internal @version \$Id$
14 *
15 **/
16/*****************************************************************************/
17
18// include header files
19#include "mod2.h"
20#include "structs.h"
21#include "polys.h"
22#include "ideals.h"
23#include "numbers.h"
24#include "matpol.h"
25#include "linearAlgebra.h"
26
27/**
28 * The returned score is based on the implementation of 'nSize' for
29 * numbers (, see numbers.h): nSize(n) provides a measure for the
30 * complexity of n. Thus, less complex pivot elements will be
31 * prefered, and get therefore a smaller pivot score. Consequently,
32 * we simply return the value of nSize.
33 * An exception to this rule are the ground fields R, long R, and
34 * long C: In these, the result of nSize relates to |n|. And since
35 * a larger modulus of the pivot element ensures a numerically more
36 * stable Gauss elimination, we would like to have a smaller score
37 * for larger values of |n|. Therefore, in R, long R, and long C,
38 * the negative of nSize will be returned.
39 **/
40int pivotScore(number n)
41{
42  int s = nSize(n);
43  if (rField_is_long_C(currRing) ||
44      rField_is_long_R(currRing) ||
45      rField_is_R(currRing))
46    return -s;
47  else
48    return s;
49}
50
51/**
52 * This code computes a score for each non-zero matrix entry in
53 * aMat[r1..r2, c1..c2]. If all entries are zero, false is returned,
54 * otherwise true. In the latter case, the minimum of all scores
55 * is sought, and the row and column indices of the corresponding
56 * matrix entry are stored in bestR and bestC.
57 **/
58bool pivot(const matrix aMat, const int r1, const int r2, const int c1,
59           const int c2, int* bestR, int* bestC)
60{
61  int bestScore; int score; bool foundBestScore = false; poly matEntry;
62
63  for (int c = c1; c <= c2; c++)
64  {
65    for (int r = r1; r <= r2; r++)
66    {
67      matEntry = MATELEM(aMat, r, c);
68      if (matEntry != NULL)
69      {
70        score = pivotScore(pGetCoeff(matEntry));
71        if ((!foundBestScore) || (score < bestScore))
72        {
73          bestScore = score;
74          *bestR = r;
75          *bestC = c;
76        }
77        foundBestScore = true;
78      }
79    }
80  }
81
82  return foundBestScore;
83}
84
85void luDecomp(const matrix aMat, matrix &pMat, matrix &lMat, matrix &uMat)
86{
87  int rr = aMat->rows();
88  int cc = aMat->cols();
89  pMat = mpNew(rr, rr);
90  lMat = mpNew(rr, rr);
91  uMat = mpNew(rr, cc);
92  /* copy aMat into uMat: */
93  for (int r = 1; r <= rr; r++)
94    for (int c = 1; c <= cc; c++)
95      MATELEM(uMat, r, c) = pCopy(aMat->m[c - 1 + (r - 1) * cc]);
96
97  /* we use an int array to store all row permutations;
98     note that we only make use of the entries [1..rr] */
99  int* permut = new int[rr + 1];
100  for (int i = 1; i <= rr; i++) permut[i] = i;
101 
102  /* fill lMat with the (rr x rr) unit matrix */
103  for (int r = 1; r <= rr; r++) MATELEM(lMat, r, r) = pOne();
104
105  int bestR; int bestC; int intSwap; poly pSwap; int cOffset = 0;
106  for (int r = 1; r < rr; r++)
107  {
108    if (r > cc) break;
109    while ((r + cOffset <= cc) &&
110           (!pivot(uMat, r, rr, r + cOffset, r + cOffset, &bestR, &bestC)))
111      cOffset++;
112    if (r + cOffset <= cc)
113    {
114      /* swap rows with indices r and bestR in permut */
115      intSwap = permut[r];
116      permut[r] = permut[bestR];
117      permut[bestR] = intSwap;
118
119      /* swap rows with indices r and bestR in uMat;
120         it is sufficient to do this for columns >= r + cOffset*/
121      for (int c = r + cOffset; c <= cc; c++)
122      {
123        pSwap = MATELEM(uMat, r, c);
124        MATELEM(uMat, r, c) = MATELEM(uMat, bestR, c);
125        MATELEM(uMat, bestR, c) = pSwap;
126      }
127
128      /* swap rows with indices r and bestR in lMat;
129         we must do this only for columns < r */
130      for (int c = 1; c < r; c++)
131      {
132        pSwap = MATELEM(lMat, r, c);
133        MATELEM(lMat, r, c) = MATELEM(lMat, bestR, c);
134        MATELEM(lMat, bestR, c) = pSwap;
135      }
136
137      /* perform next Gauss elimination step, i.e., below the
138         row with index r;
139         we need to adjust lMat and uMat;
140         we are certain that the matrix entry at [r, r + cOffset]
141         is non-zero: */
142      number pivotElement = pGetCoeff(MATELEM(uMat, r, r + cOffset));
143      poly p; number n;
144      for (int rGauss = r + 1; rGauss <= rr; rGauss++)
145      {
146        p = MATELEM(uMat, rGauss, r + cOffset);
147        if (p != NULL)
148        {
149          n = nDiv(pGetCoeff(p), pivotElement);
150
151          /* filling lMat;
152             old entry was zero, so no need to call pDelete(.) */
153          MATELEM(lMat, rGauss, r) = pNSet(nCopy(n));
154
155          /* adjusting uMat: */
156          MATELEM(uMat, rGauss, r + cOffset) = NULL; pDelete(&p);
157          n = nNeg(n);
158          for (int cGauss = r + cOffset + 1; cGauss <= cc; cGauss++)
159            MATELEM(uMat, rGauss, cGauss)
160              = pAdd(MATELEM(uMat, rGauss, cGauss),
161                     ppMult_nn(MATELEM(uMat, r, cGauss), n));
162
163          nDelete(&n); /* clean up n */
164        }
165      }
166    }
167  }
168
169  /* building the permutation matrix from 'permut' */
170  for (int r = 1; r <= rr; r++)
171    MATELEM(pMat, r, permut[r]) = pOne();
172  delete[] permut;
173
174  return;
175}
176
177/**
178 * This code first computes the LU-decomposition of aMat,
179 * and then calls the method for inverting a matrix which
180 * is given by its LU-decomposition.
181 **/
182bool luInverse(const matrix aMat, matrix &iMat)
183{ /* aMat is guaranteed to be an (n x n)-matrix */
184  int d = aMat->rows();
185
186  matrix pMat;
187  matrix lMat;
188  matrix uMat;
189  luDecomp(aMat, pMat, lMat, uMat);
190  bool result = luInverseFromLUDecomp(pMat, lMat, uMat, iMat);
191
192  /* clean-up */
193  idDelete((ideal*)&pMat);
194  idDelete((ideal*)&lMat);
195  idDelete((ideal*)&uMat);
196
197  return result;
198}
199
200bool upperRightTriangleInverse(const matrix uMat, matrix &iMat,
201                               bool diagonalIsOne)
202{
203  int d = uMat->rows();
204  poly p; poly q;
205
206  /* check whether uMat is invertible */
207  bool invertible = diagonalIsOne;
208  if (!invertible)
209  {
210    invertible = true;
211    for (int r = 1; r <= d; r++)
212    {
213      if (MATELEM(uMat, r, r) == NULL)
214      {
215        invertible = false;
216        break;
217      }
218    }
219  }
220
221  if (invertible)
222  {
223    iMat = mpNew(d, d);
224    for (int r = d; r >= 1; r--)
225    {
226      if (diagonalIsOne)
227        MATELEM(iMat, r, r) = pOne();
228      else
229        MATELEM(iMat, r, r) = pNSet(nInvers(pGetCoeff(MATELEM(uMat, r, r))));
230      for (int c = r + 1; c <= d; c++)
231      {
232        p = NULL;
233        for (int k = r + 1; k <= c; k++)
234        {
235          q = ppMult_qq(MATELEM(uMat, r, k), MATELEM(iMat, k, c));
236          p = pAdd(p, q);
237        }
238        p = pNeg(p);
239        p = pMult(p, pCopy(MATELEM(iMat, r, r)));
240        MATELEM(iMat, r, c) = p;
241      }
242    }
243  }
244
245  return invertible;
246}
247
248bool lowerLeftTriangleInverse(const matrix lMat, matrix &iMat,
249                              bool diagonalIsOne)
250{
251  int d = lMat->rows(); poly p; poly q;
252
253  /* check whether lMat is invertible */
254  bool invertible = diagonalIsOne;
255  if (!invertible)
256  {
257    invertible = true;
258    for (int r = 1; r <= d; r++)
259    {
260      if (MATELEM(lMat, r, r) == NULL)
261      {
262        invertible = false;
263        break;
264      }
265    }
266  }
267
268  if (invertible)
269  {
270    iMat = mpNew(d, d);
271    for (int c = d; c >= 1; c--)
272    {
273      if (diagonalIsOne)
274        MATELEM(iMat, c, c) = pOne();
275      else
276        MATELEM(iMat, c, c) = pNSet(nInvers(pGetCoeff(MATELEM(lMat, c, c))));
277      for (int r = c + 1; r <= d; r++)
278      {
279        p = NULL;
280        for (int k = c; k <= r - 1; k++)
281        {
282          q = ppMult_qq(MATELEM(lMat, r, k), MATELEM(iMat, k, c));
283          p = pAdd(p, q);
284        }
285        p = pNeg(p);
286        p = pMult(p, pCopy(MATELEM(iMat, c, c)));
287        MATELEM(iMat, r, c) = p;
288      }
289    }
290  }
291
292  return invertible;
293}
294
295/**
296 * This code computes the inverse by inverting lMat and uMat, and
297 * then performing two matrix multiplications.
298 **/
299bool luInverseFromLUDecomp(const matrix pMat, const matrix lMat,
300                           const matrix uMat, matrix &iMat)
301{ /* uMat is guaranteed to be quadratic */
302  int d = uMat->rows();
303
304  matrix lMatInverse; /* for storing the inverse of lMat;
305                         this inversion will always work                */
306  matrix uMatInverse; /* for storing the inverse of uMat, if invertible */
307
308  bool result = upperRightTriangleInverse(uMat, uMatInverse, false);
309  if (result)
310  {
311    /* next will always work, since lMat is known to have all diagonal
312       entries equal to 1 */
313    lowerLeftTriangleInverse(lMat, lMatInverse, true);
314    iMat = mpMult(mpMult(uMatInverse, lMatInverse), pMat);
315   
316    /* clean-up */
317    idDelete((ideal*)&lMatInverse);
318    idDelete((ideal*)&uMatInverse);
319  }
320
321  return result;
322}
323
324bool luSolveViaLUDecomp(const matrix pMat, const matrix lMat,
325                        const matrix uMat, const matrix bVec,
326                        matrix &xVec, int* dim)
327{
328  int m = uMat->rows(); int n = uMat->cols();
329  matrix cVec = mpNew(m, 1);  /* for storing pMat * bVec */
330  matrix yVec = mpNew(m, 1);  /* for storing the unique solution of
331                                 lMat * yVec = cVec */
332
333  /* compute cVec = pMat * bVec but without actual multiplications */
334  for (int r = 1; r <= m; r++)
335  {
336    for (int c = 1; c <= m; c++)
337    {
338      if (MATELEM(pMat, r, c) != NULL)
339        { MATELEM(cVec, r, 1) = pCopy(MATELEM(bVec, c, 1)); break; }
340    }
341  }
342
343  /* solve lMat * yVec = cVec; this will always work as lMat is invertible;
344     moreover, no divisions are needed, since lMat[i, i] = 1, for all i */
345  for (int r = 1; r <= m; r++)
346  {
347    poly p = pNeg(pCopy(MATELEM(cVec, r, 1)));
348    for (int c = 1; c < r; c++)
349      p = pAdd(p, ppMult_qq(MATELEM(lMat, r, c), MATELEM(yVec, c, 1) ));
350    MATELEM(yVec, r, 1) = pNeg(p);
351  }
352 
353  /* determine whether uMat * xVec = yVec is solvable */
354  bool isSolvable = true;
355  bool isZeroRow; int nonZeroRowIndex;
356  for (int r = m; r >= 1; r--)
357  {
358    isZeroRow = true;
359    for (int c = 1; c <= n; c++)
360      if (MATELEM(uMat, r, c) != NULL) { isZeroRow = false; break; }
361    if (isZeroRow)
362    {
363      if (MATELEM(yVec, r, 1) != NULL) { isSolvable = false; break; }
364    }
365    else { nonZeroRowIndex = r; break; }
366  }
367 
368  if (isSolvable)
369  {
370    xVec = mpNew(n, 1); *dim = 0;
371    /* solve uMat * xVec = yVec and determine the dimension of the affine
372       solution space */
373    int nonZeroC; int lastNonZeroC = n + 1;
374    for (int r = nonZeroRowIndex; r >= 1; r--)
375    {
376      for (nonZeroC = 1; nonZeroC <= n; nonZeroC++)
377        if (MATELEM(uMat, r, nonZeroC) != NULL) break;
378      *dim = *dim + lastNonZeroC - nonZeroC - 1;
379      poly p = pNeg(pCopy(MATELEM(yVec, r, 1)));
380      for (int c = nonZeroC + 1; c <= n; c++)
381        if (MATELEM(xVec, c, 1) != NULL)
382          p = pAdd(p, ppMult_qq(MATELEM(uMat, r, c), MATELEM(xVec, c, 1)));
383      poly q = pNSet(nInvers(pGetCoeff(MATELEM(uMat, r, nonZeroC))));
384      MATELEM(xVec, nonZeroC, 1) = pMult(pNeg(p), q);
385      lastNonZeroC = nonZeroC;
386    }
387  }
388
389  idDelete((ideal*)&cVec);
390  idDelete((ideal*)&yVec);
391
392  return isSolvable;
393}
Note: See TracBrowser for help on using the repository browser.