source: src/main/java/weka/classifiers/functions/pace/PaceMatrix.java @ 7

Last change on this file since 7 was 4, checked in by gnappo, 14 years ago

Import di weka.

File size: 35.3 KB
Line 
1/*
2 *    This program is free software; you can redistribute it and/or modify
3 *    it under the terms of the GNU General Public License as published by
4 *    the Free Software Foundation; either version 2 of the License, or (at
5 *    your option) any later version.
6 *
7 *    This program is distributed in the hope that it will be useful, but
8 *    WITHOUT ANY WARRANTY; without even the implied warranty of
9 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10 *    General Public License for more details.
11 *
12 *    You should have received a copy of the GNU General Public License
13 *    along with this program; if not, write to the Free Software
14 *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
15
16/*
17 *    PaceMatrix.java
18 *    Copyright (C) 2002 University of Waikato, Hamilton, New Zealand
19 *
20 */
21
22package weka.classifiers.functions.pace;
23
24import weka.core.RevisionUtils;
25import weka.core.matrix.DoubleVector;
26import weka.core.matrix.FlexibleDecimalFormat;
27import weka.core.matrix.IntVector;
28import weka.core.matrix.Matrix;
29import weka.core.matrix.Maths;
30
31import java.util.Random;
32import java.text.DecimalFormat;
33
34/**
35 * Class for matrix manipulation used for pace regression. <p>
36 *
37 * REFERENCES <p>
38 *
39 * Wang, Y. (2000). "A new approach to fitting linear models in high
40 * dimensional spaces." PhD Thesis. Department of Computer Science,
41 * University of Waikato, New Zealand. <p>
42 *
43 * Wang, Y. and Witten, I. H. (2002). "Modeling for optimal probability
44 * prediction." Proceedings of ICML'2002. Sydney. <p>
45 *
46 * @author Yong Wang (yongwang@cs.waikato.ac.nz)
47 * @version $Revision: 1.6 $
48 */
49public class PaceMatrix 
50  extends Matrix {
51 
52  /** for serialization */
53  static final long serialVersionUID = 2699925616857843973L;
54   
55  /* ------------------------
56     Constructors
57     * ------------------------ */
58 
59  /** Construct an m-by-n PACE matrix of zeros.
60      @param m    Number of rows.
61      @param n    Number of colums.
62  */
63  public PaceMatrix( int m, int n ) {
64    super( m, n );
65  }
66
67  /** Construct an m-by-n constant PACE matrix.
68      @param m    Number of rows.
69      @param n    Number of colums.
70      @param s    Fill the matrix with this scalar value.
71  */
72  public PaceMatrix( int m, int n, double s ) {
73    super( m, n, s );
74  }
75   
76  /** Construct a PACE matrix from a 2-D array.
77      @param A    Two-dimensional array of doubles.
78      @throws  IllegalArgumentException All rows must have the same length
79  */
80  public PaceMatrix( double[][] A ) {
81    super( A );
82  }
83
84  /** Construct a PACE matrix quickly without checking arguments.
85      @param A    Two-dimensional array of doubles.
86      @param m    Number of rows.
87      @param n    Number of colums.
88  */
89  public PaceMatrix( double[][] A, int m, int n ) {
90    super( A, m, n );
91  }
92   
93  /** Construct a PaceMatrix from a one-dimensional packed array
94      @param vals One-dimensional array of doubles, packed by columns (ala Fortran).
95      @param m    Number of rows.
96      @throws  IllegalArgumentException Array length must be a multiple of m.
97  */
98  public PaceMatrix( double vals[], int m ) {
99    super( vals, m );
100  }
101   
102  /** Construct a PaceMatrix with a single column from a DoubleVector
103      @param v    DoubleVector
104  */
105  public PaceMatrix( DoubleVector v ) {
106    this( v.size(), 1 );
107    setMatrix( 0, v.size()-1, 0, v );
108  }
109   
110  /** Construct a PaceMatrix from a Matrix
111      @param X    Matrix
112  */
113  public PaceMatrix( Matrix X ) {
114    super( X.getRowDimension(), X.getColumnDimension() );
115    A = X.getArray();
116  }
117   
118  /* ------------------------
119     Public Methods
120     * ------------------------ */
121
122  /** Set the row dimenion of the matrix
123   *  @param rowDimension the row dimension
124   */
125  public void setRowDimension( int rowDimension ) 
126  {
127    m = rowDimension;
128  }
129
130  /** Set the column dimenion of the matrix
131   *  @param columnDimension the column dimension
132   */
133  public void setColumnDimension( int columnDimension ) 
134  {
135    n = columnDimension;
136  }
137
138  /**
139   * Clone the PaceMatrix object.
140   *
141   * @return the clone
142   */
143  public Object clone () {
144    PaceMatrix X = new PaceMatrix(m,n);
145    double[][] C = X.getArray();
146    for (int i = 0; i < m; i++) {
147      for (int j = 0; j < n; j++) {
148        C[i][j] = A[i][j];
149      }
150    }
151    return (Object) X;
152  }
153   
154  /** Add a value to an element and reset the element
155   *  @param i    the row number of the element
156   *  @param j    the column number of the element
157   *  @param s    the double value to be added with
158   */
159  public void setPlus(int i, int j, double s) {
160    A[i][j] += s;
161  }
162
163  /** Multiply a value with an element and reset the element
164   *  @param i    the row number of the element
165   *  @param j    the column number of the element
166   *  @param s    the double value to be multiplied with
167   */
168  public void setTimes(int i, int j, double s) {
169    A[i][j] *= s;
170  }
171
172  /** Set the submatrix A[i0:i1][j0:j1] with a same value
173   *  @param i0 the index of the first element of the column
174   *  @param i1 the index of the last element of the column
175   *  @param j0 the index of the first column
176   *  @param j1 the index of the last column
177   *  @param s the value to be set to
178   */
179  public void setMatrix( int i0, int i1, int j0, int j1, double s ) {
180    try {
181      for( int i = i0; i <= i1; i++ ) {
182        for( int j = j0; j <= j1; j++ ) {
183          A[i][j] = s;
184        }
185      }
186    } catch( ArrayIndexOutOfBoundsException e ) {
187      throw new ArrayIndexOutOfBoundsException( "Index out of bounds" );
188    }
189  }
190 
191  /** Set the submatrix A[i0:i1][j] with the values stored in a
192   *  DoubleVector
193   *  @param i0 the index of the first element of the column
194   *  @param i1 the index of the last element of the column
195   *  @param j  the index of the column
196   *  @param v the vector that stores the values*/
197  public void setMatrix( int i0, int i1, int j, DoubleVector v ) {
198    for( int i = i0; i <= i1; i++ ) {
199      A[i][j] = v.get(i-i0);
200    }
201  }
202
203  /** Set the whole matrix from a 1-D array
204   *  @param v    1-D array of doubles
205   *  @param columnFirst   Whether to fill the column first or the row.
206   *  @throws  ArrayIndexOutOfBoundsException Submatrix indices
207   */
208  public void setMatrix ( double[] v, boolean columnFirst ) {
209    try {
210      if( v.length != m * n ) 
211        throw new IllegalArgumentException("sizes not match.");
212      int i, j, count = 0;
213      if( columnFirst ) {
214        for( i = 0; i < m; i++ ) {
215          for( j = 0; j < n; j++ ) {
216            A[i][j] = v[count];
217            count ++;
218          }
219        }
220      }
221      else {
222        for( j = 0; j < n; j++ ) {
223          for( i = 0; i < m; i++ ){
224            A[i][j] = v[count];
225            count ++;
226          }
227        }
228      }
229
230    } catch( ArrayIndexOutOfBoundsException e ) {
231      throw new ArrayIndexOutOfBoundsException( "Submatrix indices" );
232    }
233  }
234
235  /** Returns the maximum absolute value of all elements
236      @return the maximum value
237  */
238  public double maxAbs () {
239    double ma = Math.abs(A[0][0]);
240    for (int j = 0; j < n; j++) {
241      for (int i = 0; i < m; i++) {
242        ma = Math.max(ma, Math.abs(A[i][j]));
243      }
244    }
245    return ma;
246  }
247
248  /** Returns the maximum absolute value of some elements of a column,
249      that is, the elements of A[i0:i1][j].
250      @param i0 the index of the first element of the column
251      @param i1 the index of the last element of the column
252      @param j  the index of the column
253      @return the maximum value */
254  public double maxAbs ( int i0, int i1, int j ) {
255    double m = Math.abs(A[i0][j]);
256    for (int i = i0+1; i <= i1; i++) {
257      m = Math.max(m, Math.abs(A[i][j]));
258    }
259    return m;
260  }
261
262  /** Returns the minimum absolute value of some elements of a column,
263      that is, the elements of A[i0:i1][j].
264      @param i0 the index of the first element of the column
265      @param i1 the index of the last element of the column
266      @param column the index of the column
267      @return the minimum value
268  */
269  public double minAbs ( int i0, int i1, int column ) {
270    double m = Math.abs(A[i0][column]);
271    for (int i = i0+1; i <= i1; i++) {
272      m = Math.min(m, Math.abs(A[i][column]));
273    }
274    return m;
275  }
276   
277  /** Check if the matrix is empty
278   *   @return true if the matrix is empty
279   */
280  public boolean  isEmpty(){
281    if(m == 0 || n == 0) return true;
282    if(A == null) return true;
283    return false;
284  }
285   
286  /** Return a DoubleVector that stores a column of the matrix
287   *  @param j the index of the column
288   *  @return the column
289   */
290  public DoubleVector  getColumn( int j ) {
291    DoubleVector v = new DoubleVector( m );
292    double [] a = v.getArray();
293    for(int i = 0; i < m; i++)
294      a[i] = A[i][j];
295    return v;
296  }
297
298  /** Return a DoubleVector that stores some elements of a column of the
299   *  matrix
300   *  @param i0 the index of the first element of the column
301   *  @param i1 the index of the last element of the column
302   *  @param j  the index of the column
303   *  @return the DoubleVector
304   */
305  public DoubleVector  getColumn( int i0, int i1, int j ) {
306    DoubleVector v = new DoubleVector( i1-i0+1 );
307    double [] a = v.getArray();
308    int count = 0;
309    for( int i = i0; i <= i1; i++ ) {
310      a[count] = A[i][j];
311      count++;
312    }
313    return v;
314  }
315 
316 
317  /** Multiplication between a row (or part of a row) of the first matrix
318   *  and a column (or part or a column) of the second matrix.
319   *  @param i the index of the row in the first matrix
320   *  @param j0 the index of the first column in the first matrix
321   *  @param j1 the index of the last column in the first matrix
322   *  @param B the second matrix
323   *  @param l the index of the column in the second matrix
324   *  @return the result of the multiplication
325   */
326  public double  times( int i, int j0, int j1, PaceMatrix B, int l ) {
327    double s = 0.0;
328    for(int j = j0; j <= j1; j++ ) {
329      s += A[i][j] * B.A[j][l];
330    }
331    return s;
332  }
333 
334  /** Decimal format for converting a matrix into a string
335   *  @return the default decimal format
336   */
337  protected DecimalFormat []  format() {
338    return format(0, m-1, 0, n-1, 7, false );
339  }
340 
341  /** Decimal format for converting a matrix into a string
342   *  @param digits the number of digits
343   *  @return the decimal format
344   */
345  protected DecimalFormat []  format( int digits ) {
346    return format(0, m-1, 0, n-1, digits, false);
347  }
348
349  /** Decimal format for converting a matrix into a string
350   *  @param digits the number of digits
351   *  @param trailing
352   *  @return the decimal format
353   */
354  protected DecimalFormat []  format( int digits, boolean trailing ) {
355    return format(0, m-1, 0, n-1, digits, trailing);
356  }
357 
358  /** Decimal format for converting a matrix into a string
359   *  @param i0
360   *  @param i1
361   *  @param j
362   *  @param digits the number of digits
363   *  @param trailing
364   *  @return the decimal format
365   */
366  protected DecimalFormat  format(int i0, int i1, int j, int digits, 
367                                  boolean trailing) {
368    FlexibleDecimalFormat df = new FlexibleDecimalFormat(digits, trailing);
369    df.grouping( true );
370    for(int i = i0; i <= i1; i ++ )
371      df.update( A[i][j] );
372    return df;
373  }
374 
375  /** Decimal format for converting a matrix into a string
376   *  @param i0
377   *  @param i1
378   *  @param j0
379   *  @param j1
380   *  @param trailing
381   *  @param digits the number of digits
382   *  @return the decimal format
383   */
384  protected DecimalFormat []  format(int i0, int i1, int j0, int j1, 
385                                     int digits, boolean trailing) {
386    DecimalFormat [] f = new DecimalFormat[j1-j0+1];
387    for( int j = j0; j <= j1; j++ ) {
388      f[j] = format(i0, i1, j, digits, trailing);
389    }
390    return f;
391  }
392 
393  /**
394   * Converts matrix to string
395   *
396   * @return the matrix as string
397   */ 
398  public String  toString() {
399    return toString( 5, false );
400  }
401 
402  /**
403   * Converts matrix to string
404   *
405   * @param digits number of digits after decimal point
406   * @param trailing true if trailing zeros are padded
407   * @return the matrix as string
408   */ 
409  public String  toString( int digits, boolean trailing ) {
410   
411    if( isEmpty() ) return "null matrix";
412   
413    StringBuffer text = new StringBuffer();
414    DecimalFormat [] nf = format( digits, trailing );
415    int numCols = 0;
416    int count = 0;
417    int width = 80;
418    int lenNumber;
419   
420    int [] nCols = new int[n];
421    int nk=0;
422    for( int j = 0; j < n; j++ ) {
423      lenNumber = nf[j].format( A[0][j]).length(); 
424      if( count + 1 + lenNumber > width -1 ) {
425        nCols[nk++]  = numCols;
426        count = 0;
427        numCols = 0;
428      }
429      count += 1 + lenNumber;
430      ++numCols;
431    }
432    nCols[nk] = numCols;
433   
434    nk = 0;
435    for( int k = 0; k < n; ) {
436      for( int i = 0; i < m; i++ ) {
437        for( int j = k; j < k + nCols[nk]; j++)
438          text.append( " " + nf[j].format( A[i][j]) );
439        text.append("\n");
440      }
441      k += nCols[nk];
442      ++nk;
443      text.append("\n");
444    }
445   
446    return text.toString();
447  }
448 
449  /** Squared sum of a column or row in a matrix
450   * @param j the index of the column or row
451   * @param i0 the index of the first element
452   * @param i1 the index of the last element
453   * @param col if true, sum over a column; otherwise, over a row
454   * @return the squared sum
455   */
456  public double sum2( int j, int i0, int i1, boolean col ) {
457    double s2 = 0;
458    if( col ) {   // column
459      for( int i = i0; i <= i1; i++ ) 
460        s2 += A[i][j] * A[i][j];
461    }
462    else {
463      for( int i = i0; i <= i1; i++ ) 
464        s2 += A[j][i] * A[j][i];
465    }
466    return s2;
467  }
468 
469  /** Squared sum of columns or rows of a matrix
470   * @param col if true, sum over columns; otherwise, over rows
471   * @return the squared sum
472   */
473  public double[] sum2( boolean col ) {
474    int l = col ? n : m;
475    int p = col ? m : n;
476    double [] s2 = new double[l];
477    for( int i = 0; i < l; i++ ) 
478      s2[i] = sum2( i, 0, p-1, col );
479    return s2;
480  }
481
482  /** Constructs single Householder transformation for a column
483   *
484   @param j    the index of the column
485   @param k    the index of the row
486   @return     d and q
487  */
488  public double [] h1( int j, int k ) {
489    double dq[] = new double[2];
490    double s2 = sum2(j, k, m-1, true); 
491    dq[0] = A[k][j] >= 0 ? - Math.sqrt( s2 ) : Math.sqrt( s2 );
492    A[k][j] -= dq[0];
493    dq[1] = A[k][j] * dq[0];
494    return dq;
495  }
496 
497  /** Performs single Householder transformation on one column of a matrix
498   *
499   @param j    the index of the column
500   @param k    the index of the row
501   @param q    q = - u'u/2; must be negative
502   @param b    the matrix to be transformed
503   @param l    the column of the matrix b
504  */
505  public void h2( int j, int k, double q, PaceMatrix b, int l ) {
506    double s = 0, alpha;
507    for( int i = k; i < m; i++ )
508      s += A[i][j] * b.A[i][l];
509    alpha = s / q;
510    for( int i = k; i < m; i++ )
511      b.A[i][l] += alpha * A[i][j];
512  }
513 
514  /** Constructs the Givens rotation
515   *  @param a
516   *  @param b
517   *  @return a double array that stores the cosine and sine values
518   */
519  public double []  g1( double a, double b ) {
520    double cs[] = new double[2];
521    double r = Maths.hypot(a, b);
522    if( r == 0.0 ) {
523      cs[0] = 1;
524      cs[1] = 0;
525    }
526    else {
527      cs[0] = a / r;
528      cs[1] = b / r;
529    }
530    return cs;
531  }
532 
533  /** Performs the Givens rotation
534   * @param cs a array storing the cosine and sine values
535   * @param i0 the index of the row of the first element
536   * @param i1 the index of the row of the second element
537   * @param j the index of the column
538   */
539  public void  g2( double cs[], int i0, int i1, int j ){
540    double w =   cs[0] * A[i0][j] + cs[1] * A[i1][j];
541    A[i1][j] = - cs[1] * A[i0][j] + cs[0] * A[i1][j];
542    A[i0][j] = w;
543  }
544 
545  /** Forward ordering of columns in terms of response explanation.  On
546   *  input, matrices A and b are already QR-transformed. The indices of
547   *  transformed columns are stored in the pivoting vector.
548   * 
549   *@param b     the PaceMatrix b
550   *@param pvt   the pivoting vector
551   *@param k0    the first k0 columns (in pvt) of A are not to be changed
552   **/
553  public void forward( PaceMatrix b, IntVector pvt, int k0 ) {
554    for( int j = k0; j < Math.min(pvt.size(), m); j++ ) {
555      steplsqr( b, pvt, j, mostExplainingColumn(b, pvt, j), true );
556    }
557  }
558
559  /** Returns the index of the column that has the largest (squared)
560   *  response, when each of columns pvt[ks:] is moved to become the
561   *  ks-th column. On input, A and b are both QR-transformed.
562   * 
563   * @param b    response
564   * @param pvt  pivoting index of A
565   * @param ks   columns pvt[ks:] of A are to be tested
566   * @return the index of the column
567   */
568  public int  mostExplainingColumn( PaceMatrix b, IntVector pvt, int ks ) {
569    double val;
570    int [] p = pvt.getArray();
571    double ma = columnResponseExplanation( b, pvt, ks, ks );
572    int jma = ks;
573    for( int i = ks+1; i < pvt.size(); i++ ) {
574      val = columnResponseExplanation( b, pvt, i, ks );
575      if( val > ma ) {
576        ma = val;
577        jma = i;
578      }
579    }
580    return jma;
581  }
582 
583  /** Backward ordering of columns in terms of response explanation.  On
584   *  input, matrices A and b are already QR-transformed. The indices of
585   *  transformed columns are stored in the pivoting vector.
586   *
587   *  A and b must have the same number of rows, being the (pseudo-)rank.
588   * 
589   * @param b     PaceMatrix b
590   * @param pvt   pivoting vector
591   * @param ks    number of QR-transformed columns; psuedo-rank of A
592   * @param k0    first k0 columns in pvt[] are not to be ordered.
593   */
594  public void backward( PaceMatrix b, IntVector pvt, int ks, int k0 ) {
595    for( int j = ks; j > k0; j-- ) {
596      steplsqr( b, pvt, j, leastExplainingColumn(b, pvt, j, k0), false );
597    }
598  }
599
600  /** Returns the index of the column that has the smallest (squared)
601   *  response, when the column is moved to become the (ks-1)-th
602   *  column. On input, A and b are both QR-transformed.
603   * 
604   * @param b    response
605   * @param pvt  pivoting index of A
606   * @param ks   psudo-rank of A
607   * @param k0   A[][pvt[0:(k0-1)]] are excluded from the testing.
608   * @return the index of the column
609   */
610  public int  leastExplainingColumn( PaceMatrix b, IntVector pvt, int ks, 
611                                     int k0 ) {
612    double val;
613    int [] p = pvt.getArray();
614    double mi = columnResponseExplanation( b, pvt, ks-1, ks );
615    int jmi = ks-1;
616    for( int i = k0; i < ks - 1; i++ ) {
617      val = columnResponseExplanation( b, pvt, i, ks );
618      if( val <= mi ) {
619        mi = val;
620        jmi = i;
621      }
622    }
623    return jmi;
624  }
625 
626  /** Returns the squared ks-th response value if the j-th column becomes
627   *  the ks-th after orthogonal transformation.  A[][pvt[ks:j]] (or
628   *  A[][pvt[j:ks]], if ks > j) and b[] are already QR-transformed
629   *  on input and will remain unchanged on output.
630   *
631   *  More generally, it returns the inner product of the corresponding
632   *  row vector of the response PaceMatrix. (To be implemented.)
633   *
634   *@param b    PaceMatrix b
635   *@param pvt  pivoting vector
636   *@param j    the column A[pvt[j]][] is to be moved
637   *@param ks   the target column A[pvt[ks]][]
638   *@return     the squared response value */
639  public double  columnResponseExplanation( PaceMatrix b, IntVector pvt,
640                                            int j, int ks ) {
641    /*  Implementation:
642     *
643     *  If j == ks - 1, returns the squared ks-th response directly.
644     *
645     *  If j > ks -1, returns the ks-th response after
646     *  Householder-transforming the j-th column and the response.
647     *
648     *  If j < ks - 1, returns the ks-th response after a sequence of
649     *  Givens rotations starting from the j-th row. */
650
651    int k, l;
652    double [] xxx = new double[n];
653    int [] p = pvt.getArray();
654    double val;
655   
656    if( j == ks -1 ) val = b.A[j][0];
657    else if( j > ks - 1 ) {
658      int jm = Math.min(n-1, j);
659      DoubleVector u = getColumn(ks,jm,p[j]);
660      DoubleVector v = b.getColumn(ks,jm,0);
661      val = v.innerProduct(u) / u.norm2();
662    }
663    else {                 // ks > j
664      for( k = j+1; k < ks; k++ ) // make a copy of A[j][]
665        xxx[k] = A[j][p[k]];
666      val = b.A[j][0];
667      double [] cs;
668      for( k = j+1; k < ks; k++ ) {
669        cs = g1( xxx[k], A[k][p[k]] );
670        for( l = k+1; l < ks; l++ ) 
671          xxx[l] = - cs[1] * xxx[l] + cs[0] * A[k][p[l]];
672        val = - cs[1] * val + cs[0] * b.A[k][0];
673      }
674    }
675    return val * val;  // or inner product in later implementation???
676  }
677
678  /**
679   * QR transformation for a least squares problem<br/>
680   *            A x = b<br/>
681   * implicitly both A and b are transformed. pvt.size() is the psuedo-rank of
682   * A.
683   * 
684   * @param b    PaceMatrix b
685   * @param pvt  pivoting vector
686   * @param k0   the first k0 columns of A (indexed by pvt) are pre-chosen.
687   *            (But subject to rank examination.)
688   *
689   *            For example, the constant term may be reserved, in which
690   *            case k0 = 1.
691   **/
692  public void  lsqr( PaceMatrix b, IntVector pvt, int k0 ) {
693    final double TINY = 1e-15;
694    int [] p = pvt.getArray();
695    int ks = 0;  // psuedo-rank
696    for(int j = 0; j < k0; j++ )   // k0 pre-chosen columns
697      if( sum2(p[j],ks,m-1,true) > TINY ){ // large diagonal element
698        steplsqr(b, pvt, ks, j, true);
699        ks++;
700      }
701      else {                     // collinear column
702        pvt.shiftToEnd( j );
703        pvt.setSize(pvt.size()-1);
704        k0--;
705        j--;
706      }
707       
708    // initial QR transformation
709    for(int j = k0; j < Math.min( pvt.size(), m ); j++ ) {
710      if( sum2(p[j], ks, m-1, true) > TINY ) { 
711        steplsqr(b, pvt, ks, j, true);
712        ks++;
713      }
714      else {                     // collinear column
715        pvt.shiftToEnd( j );
716        pvt.setSize(pvt.size()-1);
717        j--;
718      }
719    }
720       
721    b.m = m = ks;           // reset number of rows
722    pvt.setSize( ks );
723  }
724   
725  /** QR transformation for a least squares problem <br/>
726   *            A x = b <br/>
727   * implicitly both A and b are transformed. pvt.size() is the psuedo-rank of A.
728   * 
729   * @param b    PaceMatrix b
730   * @param pvt  pivoting vector
731   * @param k0   the first k0 columns of A (indexed by pvt) are pre-chosen.
732   *            (But subject to rank examination.)
733   *
734   *            For example, the constant term may be reserved, in which
735   *            case k0 = 1.
736   **/
737  public void  lsqrSelection( PaceMatrix b, IntVector pvt, int k0 ) {
738    int numObs = m;         // number of instances
739    int numXs = pvt.size();
740
741    lsqr( b, pvt, k0 );
742
743    if( numXs > 200 || numXs > numObs ) { // too many columns. 
744      forward(b, pvt, k0);
745    }
746    backward(b, pvt, pvt.size(), k0);
747  }
748   
749  /**
750   * Sets all diagonal elements to be positive (or nonnegative) without
751   * changing the least squares solution
752   * @param Y the response
753   * @param pvt the pivoted column index
754   */
755  public void positiveDiagonal( PaceMatrix Y, IntVector pvt ) {
756     
757    int [] p = pvt.getArray();
758    for( int i = 0; i < pvt.size(); i++ ) {
759      if( A[i][p[i]] < 0.0 ) {
760        for( int j = i; j < pvt.size(); j++ ) 
761          A[i][p[j]] = - A[i][p[j]];
762        Y.A[i][0] = - Y.A[i][0];
763      }
764    }
765  }
766
767  /** Stepwise least squares QR-decomposition of the problem
768   *              A x = b
769   @param b    PaceMatrix b
770   @param pvt  pivoting vector
771   @param ks   number of transformed columns
772   @param j    pvt[j], the column to adjoin or delete
773   @param adjoin   to adjoin if true; otherwise, to delete */
774  public void  steplsqr( PaceMatrix b, IntVector pvt, int ks, int j, 
775                         boolean adjoin ) {
776    final int kp = pvt.size(); // number of columns under consideration
777    int [] p = pvt.getArray();
778       
779    if( adjoin ) {     // adjoining
780      int pj = p[j];
781      pvt.swap( ks, j );
782      double dq[] = h1( pj, ks );
783      int pk;
784      for( int k = ks+1; k < kp; k++ ){
785        pk = p[k];
786        h2( pj, ks, dq[1], this, pk);
787      }
788      h2( pj, ks, dq[1], b, 0 ); // for matrix. ???
789      A[ks][pj] = dq[0];
790      for( int k = ks+1; k < m; k++ )
791        A[k][pj] = 0;
792    }
793    else {          // removing
794      int pj = p[j];
795      for( int i = j; i < ks-1; i++ ) 
796        p[i] = p[i+1];
797      p[ks-1] = pj;
798      double [] cs;
799      for( int i = j; i < ks-1; i++ ){
800        cs = g1( A[i][p[i]], A[i+1][p[i]] );
801        for( int l = i; l < kp; l++ ) 
802          g2( cs, i, i+1, p[l] );
803        for( int l = 0; l < b.n; l++ )
804          b.g2( cs, i, i+1, l );
805      }
806    }
807  }
808   
809  /** Solves upper-triangular equation <br/>
810   *    R x = b <br/>
811   *  On output, the solution is stored in b
812   *  @param b the response
813   *  @param pvt the pivoting vector
814   *  @param kp the number of the first columns involved
815   */
816  public void  rsolve( PaceMatrix b, IntVector pvt, int kp) {
817    if(kp == 0) b.m = 0;
818    int i, j, k;
819    int [] p = pvt.getArray();
820    double s;
821    double [][] ba = b.getArray();
822    for( k = 0; k < b.n; k++ ) {
823      ba[kp-1][k] /= A[kp-1][p[kp-1]];
824      for( i = kp - 2; i >= 0; i-- ){
825        s = 0;
826        for( j = i + 1; j < kp; j++ )
827          s += A[i][p[j]] * ba[j][k];
828        ba[i][k] -= s;
829        ba[i][k] /= A[i][p[i]];
830      }
831    } 
832    b.m = kp;
833  }
834   
835  /** Returns a new matrix which binds two matrices together with rows.
836   *  @param b  the second matrix
837   *  @return the combined matrix
838   */
839  public PaceMatrix  rbind( PaceMatrix b ){
840    if( n != b.n ) 
841      throw new IllegalArgumentException("unequal numbers of rows.");
842    PaceMatrix c = new PaceMatrix( m + b.m, n );
843    c.setMatrix( 0, m - 1, 0, n - 1, this );
844    c.setMatrix( m, m + b.m - 1, 0, n - 1, b );
845    return c;
846  }
847
848  /** Returns a new matrix which binds two matrices with columns.
849   *  @param b the second matrix
850   *  @return the combined matrix
851   */
852  public PaceMatrix  cbind( PaceMatrix b ) {
853    if( m != b.m ) 
854      throw new IllegalArgumentException("unequal numbers of rows: " + 
855                                         m + " and " + b.m);
856    PaceMatrix c = new PaceMatrix(m, n + b.n);
857    c.setMatrix( 0, m - 1, 0, n - 1, this );
858    c.setMatrix( 0, m - 1, n, n + b.n - 1, b );
859    return c;
860  }
861
862  /** Solves the nonnegative linear squares problem. That is, <p>
863   *   <center>   min || A x - b||, subject to x >= 0.  </center> <p>
864   *
865   *  For algorithm, refer to P161, Chapter 23 of C. L. Lawson and
866   *  R. J. Hanson (1974).  "Solving Least Squares
867   *  Problems". Prentice-Hall.
868   *    @param b the response
869   *  @param pvt vector storing pivoting column indices
870   *    @return solution */
871  public DoubleVector nnls( PaceMatrix b, IntVector pvt ) {
872    int j, t, counter = 0, jm = -1, n = pvt.size();
873    double ma, max, alpha, wj;
874    int [] p = pvt.getArray();
875    DoubleVector x = new DoubleVector( n );
876    double [] xA = x.getArray();
877    PaceMatrix z = new PaceMatrix(n, 1);
878    PaceMatrix bt;
879       
880    // step 1
881    int kp = 0; // #variables in the positive set P
882    while ( true ) {         // step 2
883      if( ++counter > 3*n )  // should never happen
884        throw new RuntimeException("Does not converge");
885      t = -1;
886      max = 0.0;
887      bt = new PaceMatrix( b.transpose() );
888      for( j = kp; j <= n-1; j++ ) {   // W = A' (b - A x)
889        wj = bt.times( 0, kp, m-1, this, p[j] );
890        if( wj > max ) {        // step 4
891          max = wj;
892          t = j;
893        }
894      }
895           
896      // step 3
897      if ( t == -1) break; // optimum achieved
898           
899      // step 5
900      pvt.swap( kp, t );       // move variable from set Z to set P
901      kp++;
902      xA[kp-1] = 0;
903      steplsqr( b, pvt, kp-1, kp-1, true );
904      // step 6
905      ma = 0;
906      while ( ma < 1.5 ) {
907        for( j = 0; j <= kp-1; j++ ) z.A[j][0] = b.A[j][0];
908        rsolve(z, pvt, kp); 
909        ma = 2; jm = -1;
910        for( j = 0; j <= kp-1; j++ ) {  // step 7, 8 and 9
911          if( z.A[j][0] <= 0.0 ) { // alpha always between 0 and 1
912            alpha = xA[j] / ( xA[j] - z.A[j][0] ); 
913            if( alpha < ma ) {
914              ma = alpha; jm = j;
915            }
916          }
917        }
918        if( ma > 1.5 ) 
919          for( j = 0; j <= kp-1; j++ ) xA[j] = z.A[j][0];  // step 7
920        else { 
921          for( j = kp-1; j >= 0; j-- ) { // step 10
922            // Modified to avoid round-off error (which seemingly
923            // can cause infinite loop).
924            if( j == jm ) { // step 11
925              xA[j] = 0.0;
926              steplsqr( b, pvt, kp, j, false );
927              kp--;  // move variable from set P to set Z
928            }
929            else xA[j] += ma * ( z.A[j][0] - xA[j] );
930          }
931        }
932      }
933    }
934    x.setSize(kp);
935    pvt.setSize(kp);
936    return x;
937  }
938
939  /** Solves the nonnegative least squares problem with equality
940   *    constraint. That is, <p>
941   *  <center> min ||A x - b||, subject to x >= 0 and c x = d. </center> <p>
942   *
943   *  @param b the response
944   *  @param c coeficients of equality constraints
945   *  @param d constants of equality constraints
946   *  @param pvt vector storing pivoting column indices
947   *  @return the solution
948   */
949  public DoubleVector nnlse( PaceMatrix b, PaceMatrix c, PaceMatrix d, 
950                             IntVector pvt ) {
951    double eps = 1e-10 * Math.max( c.maxAbs(), d.maxAbs() ) /
952    Math.max( maxAbs(), b.maxAbs() );
953       
954    PaceMatrix e = c.rbind( new PaceMatrix( times(eps) ) );
955    PaceMatrix f = d.rbind( new PaceMatrix( b.times(eps) ) );
956
957    return e.nnls( f, pvt );
958  }
959
960  /** Solves the nonnegative least squares problem with equality
961   *    constraint. That is, <p>
962   *  <center> min ||A x - b||,  subject to x >= 0 and || x || = 1. </center>
963   *  <p>
964   *  @param b the response
965   *  @param pvt vector storing pivoting column indices
966   *  @return the solution
967   */
968  public DoubleVector nnlse1( PaceMatrix b, IntVector pvt ) {
969    PaceMatrix c = new PaceMatrix( 1, n, 1 );
970    PaceMatrix d = new PaceMatrix( 1, b.n, 1 );
971       
972    return nnlse(b, c, d, pvt);
973  }
974
975  /** Generate matrix with standard-normally distributed random elements
976      @param m    Number of rows.
977      @param n    Number of colums.
978      @return An m-by-n matrix with random elements.  */
979  public static Matrix randomNormal( int m, int n ) {
980    Random random = new Random();
981     
982    Matrix A = new Matrix(m,n);
983    double[][] X = A.getArray();
984    for (int i = 0; i < m; i++) {
985      for (int j = 0; j < n; j++) {
986        X[i][j] = random.nextGaussian();
987      }
988    }
989    return A;
990  }
991 
992  /**
993   * Returns the revision string.
994   *
995   * @return            the revision
996   */
997  public String getRevision() {
998    return RevisionUtils.extract("$Revision: 1.6 $");
999  }
1000
1001  /**
1002   * for testing only
1003   *
1004   * @param args the commandline arguments - ignored
1005   */
1006  public static void  main( String args[] ) {
1007    System.out.println("================================================" + 
1008                       "===========");
1009    System.out.println("To test the pace estimators of linear model\n" + 
1010                       "coefficients.\n");
1011
1012    double sd = 2;     // standard deviation of the random error term
1013    int n = 200;       // total number of observations
1014    double beta0 = 100;   // intercept
1015    int k1 = 20;       // number of coefficients of the first cluster
1016    double beta1 = 0;  // coefficient value of the first cluster
1017    int k2 = 20;      // number of coefficients of the second cluster
1018    double beta2 = 5; // coefficient value of the second cluster
1019    int k = 1 + k1 + k2;
1020
1021    DoubleVector beta = new DoubleVector( 1 + k1 + k2 );
1022    beta.set( 0, beta0 );
1023    beta.set( 1, k1, beta1 );
1024    beta.set( k1+1, k1+k2, beta2 );
1025
1026    System.out.println("The data set contains " + n + 
1027                       " observations plus " + (k1 + k2) + 
1028                       " variables.\n\nThe coefficients of the true model"
1029                       + " are:\n\n" + beta );
1030       
1031    System.out.println("\nThe standard deviation of the error term is " + 
1032                       sd );
1033       
1034    System.out.println("===============================================" 
1035                       + "============");
1036               
1037    PaceMatrix X = new PaceMatrix( n, k1+k2+1 );
1038    X.setMatrix( 0, n-1, 0, 0, 1 );
1039    X.setMatrix( 0, n-1, 1, k1+k2, random(n, k1+k2) );
1040       
1041    PaceMatrix Y = new 
1042      PaceMatrix( X.times( new PaceMatrix(beta) ).
1043                  plusEquals( randomNormal(n,1).times(sd) ) );
1044
1045    IntVector pvt = (IntVector) IntVector.seq(0, k1+k2);
1046
1047    /*System.out.println( "The OLS estimate (by jama.Matrix.solve()) is:\n\n" +
1048      (new PaceMatrix(X.solve(Y))).getColumn(0) );*/
1049       
1050    X.lsqrSelection( Y, pvt, 1 );
1051    X.positiveDiagonal( Y, pvt );
1052
1053    PaceMatrix sol = (PaceMatrix) Y.clone();
1054    X.rsolve( sol, pvt, pvt.size() );
1055    DoubleVector betaHat = sol.getColumn(0).unpivoting( pvt, k );
1056    System.out.println( "\nThe OLS estimate (through lsqr()) is: \n\n" + 
1057                        betaHat );
1058
1059    System.out.println( "\nQuadratic loss of the OLS estimate (||X b - X bHat||^2) = " + 
1060                        ( new PaceMatrix( X.times( new 
1061                          PaceMatrix(beta.minus(betaHat)) )))
1062                        .getColumn(0).sum2() );
1063
1064    System.out.println("=============================================" + 
1065                       "==============");
1066    System.out.println("             *** Pace estimation *** \n");
1067    DoubleVector r = Y.getColumn( pvt.size(), n-1, 0);
1068    double sde = Math.sqrt(r.sum2() / r.size());
1069       
1070    System.out.println( "Estimated standard deviation = " + sde );
1071
1072    DoubleVector aHat = Y.getColumn( 0, pvt.size()-1, 0).times( 1./sde );
1073    System.out.println("\naHat = \n" + aHat );
1074       
1075    System.out.println("\n========= Based on chi-square mixture ============");
1076
1077    ChisqMixture d2 = new ChisqMixture();
1078    int method = MixtureDistribution.NNMMethod;
1079    DoubleVector AHat = aHat.square();
1080    d2.fit( AHat, method ); 
1081    System.out.println( "\nEstimated mixing distribution is:\n" + d2 );
1082       
1083    DoubleVector ATilde = d2.pace2( AHat );
1084    DoubleVector aTilde = ATilde.sqrt().times(aHat.sign());
1085    PaceMatrix YTilde = new 
1086      PaceMatrix((new PaceMatrix(aTilde)).times( sde ));
1087    X.rsolve( YTilde, pvt, pvt.size() );
1088    DoubleVector betaTilde = 
1089    YTilde.getColumn(0).unpivoting( pvt, k );
1090    System.out.println( "\nThe pace2 estimate of coefficients = \n" + 
1091                        betaTilde );
1092    System.out.println( "Quadratic loss = " + 
1093                        ( new PaceMatrix( X.times( new 
1094                          PaceMatrix(beta.minus(betaTilde)) )))
1095                        .getColumn(0).sum2() );
1096       
1097    ATilde = d2.pace4( AHat );
1098    aTilde = ATilde.sqrt().times(aHat.sign());
1099    YTilde = new PaceMatrix((new PaceMatrix(aTilde)).times( sde ));
1100    X.rsolve( YTilde, pvt, pvt.size() );
1101    betaTilde = YTilde.getColumn(0).unpivoting( pvt, k );
1102    System.out.println( "\nThe pace4 estimate of coefficients = \n" + 
1103                        betaTilde );
1104    System.out.println( "Quadratic loss = " + 
1105                        ( new PaceMatrix( X.times( new 
1106                          PaceMatrix(beta.minus(betaTilde)) )))
1107                        .getColumn(0).sum2() );
1108       
1109    ATilde = d2.pace6( AHat );
1110    aTilde = ATilde.sqrt().times(aHat.sign());
1111    YTilde = new PaceMatrix((new PaceMatrix(aTilde)).times( sde ));
1112    X.rsolve( YTilde, pvt, pvt.size() );
1113    betaTilde = YTilde.getColumn(0).unpivoting( pvt, k );
1114    System.out.println( "\nThe pace6 estimate of coefficients = \n" + 
1115                        betaTilde );
1116    System.out.println( "Quadratic loss = " + 
1117                        ( new PaceMatrix( X.times( new 
1118                          PaceMatrix(beta.minus(betaTilde)) )))
1119                        .getColumn(0).sum2() );
1120       
1121    System.out.println("\n========= Based on normal mixture ============");
1122       
1123    NormalMixture d = new NormalMixture();
1124    d.fit( aHat, method ); 
1125    System.out.println( "\nEstimated mixing distribution is:\n" + d );
1126       
1127    aTilde = d.nestedEstimate( aHat );
1128    YTilde = new PaceMatrix((new PaceMatrix(aTilde)).times( sde ));
1129    X.rsolve( YTilde, pvt, pvt.size() );
1130    betaTilde = YTilde.getColumn(0).unpivoting( pvt, k );
1131    System.out.println( "The nested estimate of coefficients = \n" + 
1132                        betaTilde );
1133    System.out.println( "Quadratic loss = " + 
1134                        ( new PaceMatrix( X.times( new 
1135                          PaceMatrix(beta.minus(betaTilde)) )))
1136                        .getColumn(0).sum2() );
1137       
1138       
1139    aTilde = d.subsetEstimate( aHat );
1140    YTilde = new PaceMatrix((new PaceMatrix(aTilde)).times( sde ));
1141    X.rsolve( YTilde, pvt, pvt.size() );
1142    betaTilde = 
1143    YTilde.getColumn(0).unpivoting( pvt, k );
1144    System.out.println( "\nThe subset estimate of coefficients = \n" + 
1145                        betaTilde );
1146    System.out.println( "Quadratic loss = " + 
1147                        ( new PaceMatrix( X.times( new 
1148                          PaceMatrix(beta.minus(betaTilde)) )))
1149                        .getColumn(0).sum2() );
1150       
1151    aTilde = d.empiricalBayesEstimate( aHat );
1152    YTilde = new PaceMatrix((new PaceMatrix(aTilde)).times( sde ));
1153    X.rsolve( YTilde, pvt, pvt.size() );
1154    betaTilde = YTilde.getColumn(0).unpivoting( pvt, k );
1155    System.out.println( "\nThe empirical Bayes estimate of coefficients = \n"+
1156                        betaTilde );
1157       
1158    System.out.println( "Quadratic loss = " + 
1159                        ( new PaceMatrix( X.times( new 
1160                          PaceMatrix(beta.minus(betaTilde)) )))
1161                        .getColumn(0).sum2() );
1162       
1163  }
1164}
1165
1166
1167
Note: See TracBrowser for help on using the repository browser.