source: git/gfanlib/gfanlib_matrix.h @ 5ff68b

spielwiese
Last change on this file since 5ff68b was 5ff68b, checked in by Yue Ren <ren@…>, 10 years ago
chg: new gfanlib version, fixed multiplicities of cones inside fans
  • Property mode set to 100644
File size: 18.0 KB
Line 
1/*
2 * lib_zmatrix.h
3 *
4 *  Created on: Sep 28, 2010
5 *      Author: anders
6 */
7
8#ifndef LIB_ZMATRIX_H_
9#define LIB_ZMATRIX_H_
10
11#include <vector>
12#include <algorithm>
13#include "gfanlib_vector.h"
14
15namespace gfan{
16
17template <class typ> class Matrix{
18  int width,height;
19  std::vector<Vector<typ> > rows;
20public:
21  // rowIterator;
22 // std::vector<Vector<typ> >::iterator rowsBegin(){return rows.begin();}
23//  std::vector<Vector<typ> >::iterator rowsEnd(){return rows.end();}
24  inline int getHeight()const{return height;};
25  inline int getWidth()const{return width;};
26  Matrix(const Matrix &a):width(a.getWidth()),height(a.getHeight()),rows(a.rows){
27  }
28  Matrix(int height_, int width_):width(width_),height(height_),rows(height_){
29    assert(height>=0);
30    assert(width>=0);
31    for(int i=0;i<getHeight();i++)rows[i]=Vector<typ>(width);
32  };
33  ~Matrix(){
34  };
35  Matrix():width(0),height(0){
36  };
37  static Matrix rowVectorMatrix(Vector<typ> const &v)
38  {
39    Matrix ret(1,v.size());
40    for(int i=0;i<v.size();i++)ret[0][i]=v[i];
41    return ret;
42  }
43  Vector<typ> column(int i)const
44    {
45      assert(i>=0);
46      assert(i<getWidth());
47      Vector<typ> ret(getHeight());
48      for(int j=0;j<getHeight();j++)ret[j]=rows[j][i];
49      return ret;
50    }
51  Matrix transposed()const
52    {
53      Matrix ret(getWidth(),getHeight());
54      for(int i=0;i<getWidth();i++)
55        ret.rows[i]=column(i);
56      return ret;
57    }
58  static Matrix identity(int n)
59    {
60      Matrix m(n,n);
61      for(int i=0;i<n;i++)m.rows[i]=Vector<typ>::standardVector(n,i);
62      return m;
63    }
64  void append(Matrix const &m)
65    {
66      for(int i=0;i<m.height;i++)
67        {
68          rows.push_back(m[i]);
69        }
70      height+=m.height;
71    }
72  void appendRow(Vector<typ> const &v)
73    {
74      assert((int)v.size()==width);
75      rows.push_back(v);
76      height++;
77    }
78  void eraseLastRow()
79  {
80    assert(rows.size()>0);
81    rows.pop_back();
82    height--;
83  }
84  /*IntegerVector vectormultiply(IntegerVector const &v)const
85    {
86      assert(v.size()==width);
87      IntegerVector ret(height);
88      for(int i=0;i<height;i++)
89        ret[i]=dot(rows[i],v);
90      return ret;
91    }*/
92  /**
93   * Decides if v is in the kernel of the matrix.
94   */
95/*  bool inKernel(IntegerVector const &v)const
96    {
97      assert(v.size()==width);
98      for(int i=0;i<height;i++)
99          if(dotLong(rows[i],v)!=0)return false;
100      return true;
101    }
102*/
103  friend Matrix operator*(const typ &s, const Matrix& q)
104    {
105      Matrix p=q;
106      for(int i=0;i<q.height;i++)p[i]=s*(q[i]);
107      return p;
108    }
109  friend Matrix operator*(const Matrix& a, const Matrix& b)
110    {
111      assert(a.width==b.height);
112      Matrix ret(b.width,a.height);
113      for(int i=0;i<b.width;i++)
114        ret[i]=a.vectormultiply(b.column(i));
115      return ret.transposed();
116    }
117  /*  template<class T>
118    Matrix<T>(const Matrix<T>& c):v(c.size()){
119    for(int i=0;i<size();i++)v[i]=typ(c[i]);}
120  */
121  friend Matrix operator-(const Matrix &b)
122  {
123    Matrix ret(b.height,b.width);
124    for(int i=0;i<b.height;i++)ret[i]=-b[i];
125    return ret;
126  }
127
128  /**
129     Returns the specified submatrix. The endRow and endColumn are not included.
130   */
131  Matrix submatrix(int startRow, int startColumn, int endRow, int endColumn)const
132  {
133    assert(startRow>=0);
134    assert(startColumn>=0);
135    assert(endRow>=startRow);
136    assert(endColumn>=startColumn);
137    assert(endRow<=height);
138    assert(endColumn<=width);
139    Matrix ret(endRow-startRow,endColumn-startColumn);
140    for(int i=startRow;i<endRow;i++)
141      for(int j=startColumn;j<endColumn;j++)
142        ret[i-startRow][j-startColumn]=rows[i][j];
143    return ret;
144  }
145  const Vector<typ>& operator[](int n)const{assert(n>=0 && n<getHeight());return (rows[n]);}
146// Bugfix for gcc4.5 (passing assertion to the above operator):
147  Vector<typ>& operator[](int n){if(!(n>=0 && n<getHeight())){(*(const Matrix<typ>*)(this))[n];}return (rows[n]);}
148
149
150  bool operator<(const Matrix & b)const
151  {
152    if(getWidth()<b.getWidth())return true;
153    if(b.getWidth()<getWidth())return false;
154    if(getHeight()<b.getHeight())return true;
155    if(b.getHeight()<getHeight())return false;
156
157    for(int i=0;i<getHeight();i++)
158      {
159        if((*this)[i]<b[i])return true;
160        if(b[i]<(*this)[i])return false;
161      }
162    return false;
163  }
164  /**
165     Adds a times the i th row to the j th row.
166  */
167  void madd(int i, typ a, int j)
168  {
169    assert(i!=j);
170    assert(i>=0 && i<height);
171    assert(j>=0 && j<height);
172
173    if(!a.isZero())
174    for(int k=0;k<width;k++)
175      if(!rows[i][k].isZero())
176        rows[j][k].madd(rows[i][k],a);
177  }
178
179  friend std::ostream &operator<<(std::ostream &f, Matrix const &a){
180    f<<"{";
181    for(int i=0;i<a.getHeight();i++)
182      {
183        if(i)f<<","<<std::endl;
184        f<<a.rows[i];
185      }
186    f<<"}"<<std::endl;
187    return f;
188  }
189
190  std::string toString()const
191  {
192          std::stringstream f;
193          f<<*this;
194          return f.str();
195  }
196
197  /**
198     Swaps the i th and the j th row.
199   */
200  void swapRows(int i, int j)
201  {
202    std::swap(rows[i],rows[j]);
203  }
204  /**
205     This method is used for iterating through the pivots in a matrix
206     in row echelon form. To find the first pivot put i=-1 and
207     j=-1 and call this routine. The (i,j) th entry of the matrix is a
208     pivot. Call the routine again to get the next pivot. When no more
209     pivots are found the routine returns false.
210  */
211  bool nextPivot(int &i, int &j)const
212  {
213    i++;
214    if(i>=height)return false;
215    while(++j<width)
216      {
217        if(!rows[i][j].isZero()) return true;
218      }
219    return false;
220  }
221  /**
222     Returns the indices of the columns containing a pivot.
223     The returned list is sorted.
224     The matrix must be in row echelon form.
225   */
226  std::vector<int> pivotColumns()const
227  {
228    std::vector<int> ret;
229    int pivotI=-1;
230    int pivotJ=-1;
231    while(nextPivot(pivotI,pivotJ))ret.push_back(pivotJ);
232    return ret;
233  }
234  /**
235     Returns the indices of the columns not containing a pivot.
236     The returned list is sorted.
237     The matrix must be in row echelon form.
238   */
239  std::vector<int> nonPivotColumns()const
240  {
241    std::vector<int> ret;
242    int pivotI=-1;
243    int pivotJ=-1;
244    int firstPossiblePivot=0;
245    while(nextPivot(pivotI,pivotJ))
246      {
247        for(int j=firstPossiblePivot;j<pivotJ;j++)
248          ret.push_back(j);
249        firstPossiblePivot=pivotJ+1;
250      }
251    for(int j=firstPossiblePivot;j<getWidth();j++)
252      ret.push_back(j);
253
254    return ret;
255  }
256  /**
257     This routine removes the zero rows of the matrix.
258   */
259  void removeZeroRows()
260  {
261    int nonZeros=0;
262    for(int i=0;i<height;i++)if(!(*this)[i].isZero())nonZeros++;
263    if(nonZeros==height)return;
264
265    Matrix b(nonZeros,width);
266
267    int j=0;
268    for(int i=0;i<height;i++)
269      {
270        if(!(*this)[i].isZero())
271          {
272            b[j]=(*this)[i];
273            j++;
274          }
275      }
276    *this=b;
277  }
278  /**
279     Returns the index of a row whose index is at least currentRow and
280     which has a non-zero element on the column th entry. If no such
281     row exists then -1 is returned. This routine is used in the Gauss
282     reduction. To make the reduction more efficient the routine
283     chooses its row with as few non-zero entries as possible.
284   */
285  int findRowIndex(int column, int currentRow)const
286  {
287    int best=-1;
288    int bestNumberOfNonZero=0;
289    for(int i=currentRow;i<height;i++)
290      if(!rows[i][column].isZero())
291        {
292          int nz=0;
293          for(int k=column+1;k<width;k++)
294            if(!rows[i][k].isZero())nz++;
295          if(best==-1)
296            {
297              best=i;
298              bestNumberOfNonZero=nz;
299            }
300          else if(nz<bestNumberOfNonZero)
301            {
302              best=i;
303              bestNumberOfNonZero=nz;
304            }
305        }
306    return best;
307  }
308  /**
309     Performs a Gauss reduction and returns the number of row swaps (and negative scalings)
310     done. The result is a matrix in row echelon form. The pivots may
311     not be all 1.  In terms of Groebner bases, what is computed is a
312     minimal (not necessarily reduced) Groebner basis of the linear
313     ideal generated by the rows.  The number of swaps is need if one
314     wants to compute the determinant afterwards. In this case it is
315     also a good idea to set the flag returnIfZeroDeterminant which
316     make the routine terminate before completion if it discovers that
317     the determinant is zero.
318  */
319  int reduce(bool returnIfZeroDeterminant=false, bool integral=false, bool makePivotsOne=false)
320  {
321    assert(integral || typ::isField());
322    assert(!makePivotsOne || !integral);
323
324    int retSwaps=0;
325    int currentRow=0;
326
327    for(int i=0;i<width;i++)
328      {
329        int s=findRowIndex(i,currentRow);
330
331        if(s!=-1)
332          {
333            if(s!=currentRow)
334              {
335                swapRows(currentRow,s);
336                retSwaps++;
337              }
338            if(makePivotsOne)
339              {//THE PIVOT SHOULD BE SET TO ONE IF INTEGRAL IS FALSE
340                if(rows[currentRow][i].sign()>=0)retSwaps++;
341                typ inverse=typ(1)/rows[currentRow][i];
342                //                if(!rows[currentRow][i].isOne())
343                  {
344                    for(int k=0;k<width;k++)
345                      if(!rows[currentRow][k].isZero())
346                        rows[currentRow][k]*=inverse;
347                  }
348              }
349            for(int j=currentRow+1;j<height;j++)
350              if(integral)
351                {
352                  if(!rows[j][i].isZero())
353                    {
354                      typ s;
355                      typ t;
356
357                      typ g=typ::gcd(rows[currentRow][i],rows[j][i],s,t);
358                      typ u=-rows[j][i]/g;
359                      typ v=rows[currentRow][i]/g;
360                        /* We want the (s,t) vector to be as small as possible.
361                         * We are allowed to adjust by multiples of (u,v).
362                         * The following computes the correct multiplier (in most cases).
363                         */
364/*                        {
365                          FieldElement multiplier=(s*u+t*v)*((u*u+v*v).inverse());
366                          double d=mpq_get_d(*(multiplier.getGmpRationalTemporaryPointer()));
367                          multiplier=multiplier.getField()->zHomomorphism(-(((int)(d+10000.5))-10000));
368                          s.madd(multiplier,u);
369                          t.madd(multiplier,v);
370                        }*/
371                        for(int k=0;k<width;k++)
372                          {
373                            typ A=rows[currentRow][k];
374                            typ B=rows[j][k];
375
376                            rows[currentRow][k]=s*A+t*B;
377                            rows[j][k]=u*A+v*B;
378                          }
379                      }
380                  }
381                else
382                  {
383                    if(!rows[j][i].isZero())
384                      madd(currentRow,-rows[j][i]/rows[currentRow][i],j);
385                  }
386              currentRow++;
387            }
388          else
389            if(returnIfZeroDeterminant)return -1;
390        }
391
392      return retSwaps;
393    }
394  /**
395     Computes a reduced row echelon form from a row echelon form. In
396     Groebner basis terms this is the same as tranforming a minimal
397     Groebner basis to a reduced one except that we do not force
398     pivots to be 1 unless the scalePivotsToOne parameter is set.
399   */
400  int REformToRREform(bool scalePivotsToOne=false)
401  {
402    int ret=0;
403    int pivotI=-1;
404    int pivotJ=-1;
405    while(nextPivot(pivotI,pivotJ))
406      {
407        if(scalePivotsToOne)
408          rows[pivotI]=rows[pivotI]/rows[pivotI][pivotJ];
409        for(int i=0;i<pivotI;i++)
410          if(!rows[i][pivotJ].isZero())
411            madd(pivotI,-rows[i][pivotJ]/rows[pivotI][pivotJ],i);
412      }
413    return ret;
414  }
415  /**
416     This function may be called if the FieldMatrix is in Row Echelon
417     Form. The input is a FieldVector which is rewritten modulo the
418     rows of the matrix. The result is unique and is the same as the
419     normal form of the input vector modulo the Groebner basis of the
420     linear ideal generated by the rows of the matrix.
421  */
422  Vector<typ> canonicalize(Vector<typ> v)const
423  {
424    assert(typ::isField());
425    assert((int)v.size()==getWidth());
426
427    int pivotI=-1;
428    int pivotJ=-1;
429
430    while(nextPivot(pivotI,pivotJ))
431      if(!v[pivotJ].isZero())
432      {
433        typ s=-v[pivotJ]/rows[pivotI][pivotJ];
434
435        for(int k=0;k<width;k++)
436          if(!rows[pivotI][k].isZero())
437            v[k].madd(rows[pivotI][k],s);
438      }
439    return v;
440  }
441  /**
442     Calls reduce() and constructs matrix whose rows forms a basis of
443     the kernel of the linear map defined by the original matrix. The
444     return value is the new matrix.
445   */
446  Matrix reduceAndComputeKernel()
447  {
448    Matrix ret(width-reduceAndComputeRank(),width);
449
450    REformToRREform();
451
452    int k=0;
453    int pivotI=-1;
454    int pivotJ=-1;
455    bool pivotExists=nextPivot(pivotI,pivotJ);
456    for(int j=0;j<width;j++)
457      {
458        if(pivotExists && (pivotJ==j))
459          {
460            pivotExists=nextPivot(pivotI,pivotJ);
461            continue;
462          }
463        int pivot2I=-1;
464        int pivot2J=-1;
465        while(nextPivot(pivot2I,pivot2J))
466          {
467            ret[k][pivot2J]=rows[pivot2I][j]/rows[pivot2I][pivot2J];
468          }
469        ret[k][j]=typ(-1);
470        k++;
471      }
472    return ret;
473  }
474  /**
475     Assumes that the matrix has a kernel of dimension 1.
476     Calls reduce() and returns a non-zero vector in the kernel.
477     If the matrix is an (n-1)x(n) matrix then the returned vector has
478     the property that if it was appended as a row to the original matrix
479     then the determinant of that matrix would be positive. Of course
480     this property, as described here, only makes sense for ordered fields.
481     Only allowed for fields at the moment.
482   */
483  Vector<typ> reduceAndComputeVectorInKernel()
484  {
485    assert(typ::isField());
486    // TODO: (optimization) if the field is ordered, then it is better to just keep track of signs rather than
487    // multiplying by sign*diagonalProduct*lastEntry at the end.
488    int nswaps=this->reduce();
489    typ sign=typ(1-2*(nswaps&1));
490    int rank=reduceAndComputeRank();
491    assert(rank+1==width);
492
493    REformToRREform();
494
495    Vector<typ> ret(width);
496
497    typ diagonalProduct(1);
498    {
499      int pivot2I=-1;
500      int pivot2J=-1;
501      while(nextPivot(pivot2I,pivot2J))
502        {
503          diagonalProduct*=rows[pivot2I][pivot2J];
504        }
505    }
506    {
507      int j=nonPivotColumns().front();
508      int pivot2I=-1;
509      int pivot2J=-1;
510      ret[j]=typ(-1);
511      // Pretend that we are appending ret to the matrix, and reducing this
512      // new row by the previous ones. The last entry of the resulting matrix
513      // is lastEntry.
514      typ lastEntry=ret[j];
515      while(nextPivot(pivot2I,pivot2J))
516        {
517          ret[pivot2J]=rows[pivot2I][j]/rows[pivot2I][pivot2J];
518          lastEntry-=ret[pivot2J]*ret[pivot2J];
519        }
520      ret=(sign*(diagonalProduct*lastEntry))*ret;
521    }
522
523    return ret;
524  }
525
526  /**
527     Calls reduce() and returns the rank of the matrix.
528   */
529  int reduceAndComputeRank()
530  {
531    reduce(false,!typ::isField(),false);
532    int ret=0;
533    int pivotI=-1;
534    int pivotJ=-1;
535    while(nextPivot(pivotI,pivotJ))ret++;
536    return ret;
537  }
538  /**
539   * Sort the rows of the matrix.
540   */
541  void sortRows()
542  {
543    std::sort(rows.begin(),rows.end());
544  }
545  /**
546   * Sort the rows of the matrix and remove duplicate rows.
547   */
548  void sortAndRemoveDuplicateRows()
549  {
550    sortRows();
551    if(getHeight()==0)return;
552    Matrix B(0,getWidth());
553    B.appendRow((*this)[0]);
554    for(int i=1;i<getHeight();i++)
555      if(rows[i]!=rows[i-1])B.appendRow((*this)[i]);
556    *this=B;
557  }
558  /**
559     Takes two matrices with the same number of columns and construct
560     a new matrix which has the rows of the matrix top on the top and
561     the rows of the matrix bottom at the bottom. The return value is
562     the constructed matrix.
563   */
564  friend Matrix combineOnTop(Matrix const &top, Matrix const &bottom)
565  {
566    assert(bottom.getWidth()==top.getWidth());
567    Matrix ret(top.getHeight()+bottom.getHeight(),top.getWidth());
568    for(int i=0;i<top.getHeight();i++)ret.rows[i]=top.rows[i];
569    for(int i=0;i<bottom.getHeight();i++)ret.rows[i+top.getHeight()]=bottom.rows[i];
570
571    return ret;
572  }
573  /**
574     Takes two matrices with the same number of rows and construct
575     a new matrix which has the columns of the matrix left on the left and
576     the columns of the matrix right on the right. The return value is
577     the constructed matrix.
578   */
579  friend Matrix combineLeftRight(Matrix const &left, Matrix const &right)
580  {
581    assert(left.getHeight()==right.getHeight());
582    Matrix ret(left.getHeight(),left.getWidth()+right.getWidth());
583    for(int i=0;i<left.getHeight();i++)
584      {
585        for(int j=0;j<left.getWidth();j++)ret.rows[i][j]=left.rows[i][j];
586        for(int j=0;j<right.getWidth();j++)ret.rows[i][j+left.getWidth()]=right.rows[i][j];
587      }
588    return ret;
589  }
590};
591
592typedef Matrix<Integer> ZMatrix;
593typedef Matrix<Rational> QMatrix;
594typedef Matrix<int> IntMatrix;
595
596inline QMatrix ZToQMatrix(ZMatrix const &m)
597{
598  QMatrix ret(m.getHeight(),m.getWidth());
599  for(int i=0;i<m.getHeight();i++)ret[i]=ZToQVector(m[i]);
600  return ret;
601}
602
603inline ZMatrix QToZMatrixPrimitive(QMatrix const &m)
604{
605  ZMatrix ret(m.getHeight(),m.getWidth());
606  for(int i=0;i<m.getHeight();i++)ret[i]=QToZVectorPrimitive(m[i]);
607  return ret;
608}
609
610
611inline IntMatrix ZToIntMatrix(ZMatrix const &m)
612{
613  IntMatrix ret(m.getHeight(),m.getWidth());
614  for(int i=0;i<m.getHeight();i++)ret[i]=ZToIntVector(m[i]);
615  return ret;
616}
617
618
619inline ZMatrix IntToZMatrix(IntMatrix const &m)
620{
621  ZMatrix ret(m.getHeight(),m.getWidth());
622  for(int i=0;i<m.getHeight();i++)ret[i]=IntToZVector(m[i]);
623  return ret;
624}
625
626inline QMatrix canonicalizeSubspace(QMatrix const &m)
627{
628  QMatrix temp=m;
629  temp.reduce();
630  temp.REformToRREform();
631  temp.removeZeroRows();
632  return temp;
633}
634
635inline ZMatrix canonicalizeSubspace(ZMatrix const &m)
636{
637  return QToZMatrixPrimitive(canonicalizeSubspace(ZToQMatrix(m)));
638}
639
640
641inline QMatrix kernel(QMatrix const &m)
642{
643  QMatrix temp=m;
644  return temp.reduceAndComputeKernel();
645}
646
647inline ZMatrix kernel(ZMatrix const &m)
648{
649  return QToZMatrixPrimitive(kernel(ZToQMatrix(m)));
650}
651
652}
653
654
655#endif /* LIB_ZMATRIX_H_ */
Note: See TracBrowser for help on using the repository browser.