source: git/kernel/linearAlgebra.cc @ f86c48

spielwiese
Last change on this file since f86c48 was f86c48, checked in by Hans Schoenemann <hannes@…>, 14 years ago
format git-svn-id: file:///usr/local/Singular/svn/trunk@13460 2c84dea3-7e68-4137-9b89-c4e89433aadc
  • Property mode set to 100644
File size: 13.8 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 <kernel/mod2.h>
20#include <kernel/structs.h>
21#include <kernel/polys.h>
22#include <kernel/ideals.h>
23#include <kernel/numbers.h>
24#include <kernel/matpol.h>
25#include <kernel/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          nNormalize(n);
151
152          /* filling lMat;
153             old entry was zero, so no need to call pDelete(.) */
154          MATELEM(lMat, rGauss, r) = pNSet(nCopy(n));
155
156          /* adjusting uMat: */
157          MATELEM(uMat, rGauss, r + cOffset) = NULL; pDelete(&p);
158          n = nNeg(n);
159          for (int cGauss = r + cOffset + 1; cGauss <= cc; cGauss++)
160          {
161            MATELEM(uMat, rGauss, cGauss)
162              = pAdd(MATELEM(uMat, rGauss, cGauss),
163                     ppMult_nn(MATELEM(uMat, r, cGauss), n));
164            pNormalize(MATELEM(uMat, rGauss, cGauss));
165          }
166
167          nDelete(&n); /* clean up n */
168        }
169      }
170    }
171  }
172
173  /* building the permutation matrix from 'permut' */
174  for (int r = 1; r <= rr; r++)
175    MATELEM(pMat, r, permut[r]) = pOne();
176  delete[] permut;
177
178  return;
179}
180
181/**
182 * This code first computes the LU-decomposition of aMat,
183 * and then calls the method for inverting a matrix which
184 * is given by its LU-decomposition.
185 **/
186bool luInverse(const matrix aMat, matrix &iMat)
187{ /* aMat is guaranteed to be an (n x n)-matrix */
188  matrix pMat;
189  matrix lMat;
190  matrix uMat;
191  luDecomp(aMat, pMat, lMat, uMat);
192  bool result = luInverseFromLUDecomp(pMat, lMat, uMat, iMat);
193
194  /* clean-up */
195  idDelete((ideal*)&pMat);
196  idDelete((ideal*)&lMat);
197  idDelete((ideal*)&uMat);
198
199  return result;
200}
201
202/* Assumes that aMat is already in row echelon form */
203int rankFromRowEchelonForm(const matrix aMat)
204{
205  int rank = 0;
206  int rr = aMat->rows(); int cc = aMat->cols();
207  int r = 1; int c = 1;
208  while ((r <= rr) && (c <= cc))
209  {
210    if (MATELEM(aMat, r, c) == NULL) c++;
211    else { rank++; r++; }
212  }
213  return rank;
214}
215
216int luRank(const matrix aMat, const bool isRowEchelon)
217{
218  if (isRowEchelon) return rankFromRowEchelonForm(aMat);
219  else
220  { /* compute the LU-decomposition and read off the rank from
221       the upper triangular matrix of that decomposition */
222    matrix pMat;
223    matrix lMat;
224    matrix uMat;
225    luDecomp(aMat, pMat, lMat, uMat);
226    int result = rankFromRowEchelonForm(uMat);
227 
228    /* clean-up */
229    idDelete((ideal*)&pMat);
230    idDelete((ideal*)&lMat);
231    idDelete((ideal*)&uMat);
232 
233    return result;
234  }
235}
236
237bool upperRightTriangleInverse(const matrix uMat, matrix &iMat,
238                               bool diagonalIsOne)
239{
240  int d = uMat->rows();
241  poly p; poly q;
242
243  /* check whether uMat is invertible */
244  bool invertible = diagonalIsOne;
245  if (!invertible)
246  {
247    invertible = true;
248    for (int r = 1; r <= d; r++)
249    {
250      if (MATELEM(uMat, r, r) == NULL)
251      {
252        invertible = false;
253        break;
254      }
255    }
256  }
257
258  if (invertible)
259  {
260    iMat = mpNew(d, d);
261    for (int r = d; r >= 1; r--)
262    {
263      if (diagonalIsOne)
264        MATELEM(iMat, r, r) = pOne();
265      else
266        MATELEM(iMat, r, r) = pNSet(nInvers(pGetCoeff(MATELEM(uMat, r, r))));
267      for (int c = r + 1; c <= d; c++)
268      {
269        p = NULL;
270        for (int k = r + 1; k <= c; k++)
271        {
272          q = ppMult_qq(MATELEM(uMat, r, k), MATELEM(iMat, k, c));
273          p = pAdd(p, q);
274        }
275        p = pNeg(p);
276        p = pMult(p, pCopy(MATELEM(iMat, r, r)));
277        pNormalize(p);
278        MATELEM(iMat, r, c) = p;
279      }
280    }
281  }
282
283  return invertible;
284}
285
286bool lowerLeftTriangleInverse(const matrix lMat, matrix &iMat,
287                              bool diagonalIsOne)
288{
289  int d = lMat->rows(); poly p; poly q;
290
291  /* check whether lMat is invertible */
292  bool invertible = diagonalIsOne;
293  if (!invertible)
294  {
295    invertible = true;
296    for (int r = 1; r <= d; r++)
297    {
298      if (MATELEM(lMat, r, r) == NULL)
299      {
300        invertible = false;
301        break;
302      }
303    }
304  }
305
306  if (invertible)
307  {
308    iMat = mpNew(d, d);
309    for (int c = d; c >= 1; c--)
310    {
311      if (diagonalIsOne)
312        MATELEM(iMat, c, c) = pOne();
313      else
314        MATELEM(iMat, c, c) = pNSet(nInvers(pGetCoeff(MATELEM(lMat, c, c))));
315      for (int r = c + 1; r <= d; r++)
316      {
317        p = NULL;
318        for (int k = c; k <= r - 1; k++)
319        {
320          q = ppMult_qq(MATELEM(lMat, r, k), MATELEM(iMat, k, c));
321          p = pAdd(p, q);
322        }
323        p = pNeg(p);
324        p = pMult(p, pCopy(MATELEM(iMat, c, c)));
325        pNormalize(p);
326        MATELEM(iMat, r, c) = p;
327      }
328    }
329  }
330
331  return invertible;
332}
333
334/**
335 * This code computes the inverse by inverting lMat and uMat, and
336 * then performing two matrix multiplications.
337 **/
338bool luInverseFromLUDecomp(const matrix pMat, const matrix lMat,
339                           const matrix uMat, matrix &iMat)
340{ /* uMat is guaranteed to be quadratic */
341  int d = uMat->rows();
342
343  matrix lMatInverse; /* for storing the inverse of lMat;
344                         this inversion will always work                */
345  matrix uMatInverse; /* for storing the inverse of uMat, if invertible */
346
347  bool result = upperRightTriangleInverse(uMat, uMatInverse, false);
348  if (result)
349  {
350    /* next will always work, since lMat is known to have all diagonal
351       entries equal to 1 */
352    lowerLeftTriangleInverse(lMat, lMatInverse, true);
353    iMat = mpMult(mpMult(uMatInverse, lMatInverse), pMat);
354   
355    /* clean-up */
356    idDelete((ideal*)&lMatInverse);
357    idDelete((ideal*)&uMatInverse);
358  }
359
360  return result;
361}
362
363bool luSolveViaLUDecomp(const matrix pMat, const matrix lMat,
364                        const matrix uMat, const matrix bVec,
365                        matrix &xVec, matrix &H)
366{
367  int m = uMat->rows(); int n = uMat->cols();
368  matrix cVec = mpNew(m, 1);  /* for storing pMat * bVec */
369  matrix yVec = mpNew(m, 1);  /* for storing the unique solution of
370                                 lMat * yVec = cVec */
371
372  /* compute cVec = pMat * bVec but without actual multiplications */
373  for (int r = 1; r <= m; r++)
374  {
375    for (int c = 1; c <= m; c++)
376    {
377      if (MATELEM(pMat, r, c) != NULL)
378        { MATELEM(cVec, r, 1) = pCopy(MATELEM(bVec, c, 1)); break; }
379    }
380  }
381
382  /* solve lMat * yVec = cVec; this will always work as lMat is invertible;
383     moreover, no divisions are needed, since lMat[i, i] = 1, for all i */
384  for (int r = 1; r <= m; r++)
385  {
386    poly p = pNeg(pCopy(MATELEM(cVec, r, 1)));
387    for (int c = 1; c < r; c++)
388      p = pAdd(p, ppMult_qq(MATELEM(lMat, r, c), MATELEM(yVec, c, 1) ));
389    MATELEM(yVec, r, 1) = pNeg(p);
390    pNormalize(MATELEM(yVec, r, 1));
391  }
392 
393  /* determine whether uMat * xVec = yVec is solvable */
394  bool isSolvable = true;
395  bool isZeroRow; int nonZeroRowIndex;
396  for (int r = m; r >= 1; r--)
397  {
398    isZeroRow = true;
399    for (int c = 1; c <= n; c++)
400      if (MATELEM(uMat, r, c) != NULL) { isZeroRow = false; break; }
401    if (isZeroRow)
402    {
403      if (MATELEM(yVec, r, 1) != NULL) { isSolvable = false; break; }
404    }
405    else { nonZeroRowIndex = r; break; }
406  }
407
408  if (isSolvable)
409  {
410    xVec = mpNew(n, 1);
411    matrix N = mpNew(n, n); int dim = 0;
412    poly p; poly q;
413    /* solve uMat * xVec = yVec and determine a basis of the solution
414       space of the homogeneous system uMat * xVec = 0;
415       We do not know in advance what the dimension (dim) of the latter
416       solution space will be. Thus, we start with the possibly too wide
417       matrix N and later copy the relevant columns of N into H. */
418    int nonZeroC; int lastNonZeroC = n + 1;
419    for (int r = nonZeroRowIndex; r >= 1; r--)
420    {
421      for (nonZeroC = 1; nonZeroC <= n; nonZeroC++)
422        if (MATELEM(uMat, r, nonZeroC) != NULL) break;
423      for (int w = lastNonZeroC - 1; w >= nonZeroC + 1; w--)
424      {
425        /* this loop will only be done when the given linear system has
426           more than one, i.e., infinitely many solutions */
427        dim++;
428        /* now we fill two entries of the dim-th column of N */
429        MATELEM(N, w, dim) = pNeg(pCopy(MATELEM(uMat, r, nonZeroC)));
430        MATELEM(N, nonZeroC, dim) = pCopy(MATELEM(uMat, r, w));
431      }
432      for (int d = 1; d <= dim; d++)
433      {
434        /* here we fill the entry of N at [r, d], for each valid vector
435           that we already have in N;
436           again, this will only be done when the given linear system has
437           more than one, i.e., infinitely many solutions */
438        p = NULL;
439        for (int c = nonZeroC + 1; c <= n; c++)
440          if (MATELEM(N, c, d) != NULL)
441            p = pAdd(p, ppMult_qq(MATELEM(uMat, r, c), MATELEM(N, c, d)));
442        q = pNSet(nInvers(pGetCoeff(MATELEM(uMat, r, nonZeroC))));
443        MATELEM(N, nonZeroC, d) = pMult(pNeg(p), q);;
444      }
445      p = pNeg(pCopy(MATELEM(yVec, r, 1)));
446      for (int c = nonZeroC + 1; c <= n; c++)
447        if (MATELEM(xVec, c, 1) != NULL)
448          p = pAdd(p, ppMult_qq(MATELEM(uMat, r, c), MATELEM(xVec, c, 1)));
449      q = pNSet(nInvers(pGetCoeff(MATELEM(uMat, r, nonZeroC))));
450      MATELEM(xVec, nonZeroC, 1) = pMult(pNeg(p), q);
451      pNormalize(MATELEM(xVec, nonZeroC, 1));
452      lastNonZeroC = nonZeroC;
453    }
454    if (dim == 0)
455    {
456      /* that means the given linear system has exactly one solution;
457         we return as H the 1x1 matrix with entry zero */
458      H = mpNew(1, 1);
459    }
460    else
461    {
462      /* copy the first 'dim' columns of N into H */
463      H = mpNew(n, dim);
464      for (int r = 1; r <= n; r++)
465        for (int c = 1; c <= dim; c++)
466          MATELEM(H, r, c) = pCopy(MATELEM(N, r, c));
467    }
468    /* clean up N */
469    idDelete((ideal*)&N);
470  }
471
472  idDelete((ideal*)&cVec);
473  idDelete((ideal*)&yVec);
474
475  return isSolvable;
476}
Note: See TracBrowser for help on using the repository browser.