Package weka.classifiers.functions.pace

Source Code of weka.classifiers.functions.pace.PaceMatrix

/*
*    This program is free software; you can redistribute it and/or modify
*    it under the terms of the GNU General Public License as published by
*    the Free Software Foundation; either version 2 of the License, or (at
*    your option) any later version.
*
*    This program is distributed in the hope that it will be useful, but
*    WITHOUT ANY WARRANTY; without even the implied warranty of
*    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
*    General Public License for more details.
*
*    You should have received a copy of the GNU General Public License
*    along with this program; if not, write to the Free Software
*    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */

/*
*    PaceMatrix.java
*    Copyright (C) 2002 University of Waikato, Hamilton, New Zealand
*
*/

package weka.classifiers.functions.pace;

import weka.core.RevisionUtils;
import weka.core.matrix.DoubleVector;
import weka.core.matrix.FlexibleDecimalFormat;
import weka.core.matrix.IntVector;
import weka.core.matrix.Matrix;
import weka.core.matrix.Maths;

import java.util.Random;
import java.text.DecimalFormat;

/**
* Class for matrix manipulation used for pace regression. <p>
*
* REFERENCES <p>
*
* Wang, Y. (2000). "A new approach to fitting linear models in high
* dimensional spaces." PhD Thesis. Department of Computer Science,
* University of Waikato, New Zealand. <p>
*
* Wang, Y. and Witten, I. H. (2002). "Modeling for optimal probability
* prediction." Proceedings of ICML'2002. Sydney. <p>
*
* @author Yong Wang (yongwang@cs.waikato.ac.nz)
* @version $Revision: 1.6 $
*/
public class PaceMatrix
  extends Matrix {
 
  /** for serialization */
  static final long serialVersionUID = 2699925616857843973L;
   
  /* ------------------------
     Constructors
     * ------------------------ */
 
  /** Construct an m-by-n PACE matrix of zeros.
      @param m    Number of rows.
      @param n    Number of colums.
  */
  public PaceMatrix( int m, int n ) {
    super( m, n );
  }

  /** Construct an m-by-n constant PACE matrix.
      @param m    Number of rows.
      @param n    Number of colums.
      @param s    Fill the matrix with this scalar value.
  */
  public PaceMatrix( int m, int n, double s ) {
    super( m, n, s );
  }
   
  /** Construct a PACE matrix from a 2-D array.
      @param A    Two-dimensional array of doubles.
      @throws  IllegalArgumentException All rows must have the same length
  */
  public PaceMatrix( double[][] A ) {
    super( A );
  }

  /** Construct a PACE matrix quickly without checking arguments.
      @param A    Two-dimensional array of doubles.
      @param m    Number of rows.
      @param n    Number of colums.
  */
  public PaceMatrix( double[][] A, int m, int n ) {
    super( A, m, n );
  }
   
  /** Construct a PaceMatrix from a one-dimensional packed array
      @param vals One-dimensional array of doubles, packed by columns (ala Fortran).
      @param m    Number of rows.
      @throws  IllegalArgumentException Array length must be a multiple of m.
  */
  public PaceMatrix( double vals[], int m ) {
    super( vals, m );
  }
   
  /** Construct a PaceMatrix with a single column from a DoubleVector
      @param v    DoubleVector
  */
  public PaceMatrix( DoubleVector v ) {
    this( v.size(), 1 );
    setMatrix( 0, v.size()-1, 0, v );
  }
   
  /** Construct a PaceMatrix from a Matrix
      @param X    Matrix
  */
  public PaceMatrix( Matrix X ) {
    super( X.getRowDimension(), X.getColumnDimension() );
    A = X.getArray();
  }
   
  /* ------------------------
     Public Methods
     * ------------------------ */

  /** Set the row dimenion of the matrix
   *  @param rowDimension the row dimension
   */
  public void setRowDimension( int rowDimension )
  {
    m = rowDimension;
  }

  /** Set the column dimenion of the matrix
   *  @param columnDimension the column dimension
   */
  public void setColumnDimension( int columnDimension )
  {
    n = columnDimension;
  }

  /**
   * Clone the PaceMatrix object.
   *
   * @return the clone
   */
  public Object clone () {
    PaceMatrix X = new PaceMatrix(m,n);
    double[][] C = X.getArray();
    for (int i = 0; i < m; i++) {
      for (int j = 0; j < n; j++) {
  C[i][j] = A[i][j];
      }
    }
    return (Object) X;
  }
   
  /** Add a value to an element and reset the element
   *  @param i    the row number of the element
   *  @param j    the column number of the element
   *  @param s    the double value to be added with
   */
  public void setPlus(int i, int j, double s) {
    A[i][j] += s;
  }

  /** Multiply a value with an element and reset the element
   *  @param i    the row number of the element
   *  @param j    the column number of the element
   *  @param s    the double value to be multiplied with
   */
  public void setTimes(int i, int j, double s) {
    A[i][j] *= s;
  }

  /** Set the submatrix A[i0:i1][j0:j1] with a same value
   *  @param i0 the index of the first element of the column
   *  @param i1 the index of the last element of the column
   *  @param j0 the index of the first column
   *  @param j1 the index of the last column
   *  @param s the value to be set to
   */
  public void setMatrix( int i0, int i1, int j0, int j1, double s ) {
    try {
      for( int i = i0; i <= i1; i++ ) {
  for( int j = j0; j <= j1; j++ ) {
    A[i][j] = s;
  }
      }
    } catch( ArrayIndexOutOfBoundsException e ) {
      throw new ArrayIndexOutOfBoundsException( "Index out of bounds" );
    }
  }
 
  /** Set the submatrix A[i0:i1][j] with the values stored in a
   *  DoubleVector
   *  @param i0 the index of the first element of the column
   *  @param i1 the index of the last element of the column
   *  @param j  the index of the column
   *  @param v the vector that stores the values*/
  public void setMatrix( int i0, int i1, int j, DoubleVector v ) {
    for( int i = i0; i <= i1; i++ ) {
      A[i][j] = v.get(i-i0);
    }
  }

  /** Set the whole matrix from a 1-D array
   *  @param v    1-D array of doubles
   *  @param columnFirst   Whether to fill the column first or the row.
   *  @throws  ArrayIndexOutOfBoundsException Submatrix indices
   */
  public void setMatrix ( double[] v, boolean columnFirst ) {
    try {
      if( v.length != m * n )
  throw new IllegalArgumentException("sizes not match.");
      int i, j, count = 0;
      if( columnFirst ) {
  for( i = 0; i < m; i++ ) {
    for( j = 0; j < n; j++ ) {
      A[i][j] = v[count];
      count ++;
    }
  }
      }
      else {
  for( j = 0; j < n; j++ ) {
    for( i = 0; i < m; i++ ){
      A[i][j] = v[count];
      count ++;
    }
  }
      }

    } catch( ArrayIndexOutOfBoundsException e ) {
      throw new ArrayIndexOutOfBoundsException( "Submatrix indices" );
    }
  }

  /** Returns the maximum absolute value of all elements
      @return the maximum value
  */
  public double maxAbs () {
    double ma = Math.abs(A[0][0]);
    for (int j = 0; j < n; j++) {
      for (int i = 0; i < m; i++) {
  ma = Math.max(ma, Math.abs(A[i][j]));
      }
    }
    return ma;
  }

  /** Returns the maximum absolute value of some elements of a column,
      that is, the elements of A[i0:i1][j].
      @param i0 the index of the first element of the column
      @param i1 the index of the last element of the column
      @param j  the index of the column
      @return the maximum value */
  public double maxAbs ( int i0, int i1, int j ) {
    double m = Math.abs(A[i0][j]);
    for (int i = i0+1; i <= i1; i++) {
      m = Math.max(m, Math.abs(A[i][j]));
    }
    return m;
  }

  /** Returns the minimum absolute value of some elements of a column,
      that is, the elements of A[i0:i1][j].
      @param i0 the index of the first element of the column
      @param i1 the index of the last element of the column
      @param column the index of the column
      @return the minimum value
  */
  public double minAbs ( int i0, int i1, int column ) {
    double m = Math.abs(A[i0][column]);
    for (int i = i0+1; i <= i1; i++) {
      m = Math.min(m, Math.abs(A[i][column]));
    }
    return m;
  }
   
  /** Check if the matrix is empty
   *   @return true if the matrix is empty
   */
  public boolean  isEmpty(){
    if(m == 0 || n == 0) return true;
    if(A == null) return true;
    return false;
  }
   
  /** Return a DoubleVector that stores a column of the matrix
   *  @param j the index of the column
   *  @return the column
   */
  public DoubleVector  getColumn( int j ) {
    DoubleVector v = new DoubleVector( m );
    double [] a = v.getArray();
    for(int i = 0; i < m; i++)
      a[i] = A[i][j];
    return v;
  }

  /** Return a DoubleVector that stores some elements of a column of the
   *  matrix
   *  @param i0 the index of the first element of the column
   *  @param i1 the index of the last element of the column
   *  @param j  the index of the column
   *  @return the DoubleVector
   */
  public DoubleVector  getColumn( int i0, int i1, int j ) {
    DoubleVector v = new DoubleVector( i1-i0+1 );
    double [] a = v.getArray();
    int count = 0;
    for( int i = i0; i <= i1; i++ ) {
      a[count] = A[i][j];
      count++;
    }
    return v;
  }
 
 
  /** Multiplication between a row (or part of a row) of the first matrix
   *  and a column (or part or a column) of the second matrix.
   *  @param i the index of the row in the first matrix
   *  @param j0 the index of the first column in the first matrix
   *  @param j1 the index of the last column in the first matrix
   *  @param B the second matrix
   *  @param l the index of the column in the second matrix
   *  @return the result of the multiplication
   */
  public double  times( int i, int j0, int j1, PaceMatrix B, int l ) {
    double s = 0.0;
    for(int j = j0; j <= j1; j++ ) {
      s += A[i][j] * B.A[j][l];
    }
    return s;
  }
 
  /** Decimal format for converting a matrix into a string
   *  @return the default decimal format
   */
  protected DecimalFormat []  format() {
    return format(0, m-1, 0, n-1, 7, false );
  }
 
  /** Decimal format for converting a matrix into a string
   *  @param digits the number of digits
   *  @return the decimal format
   */
  protected DecimalFormat []  format( int digits ) {
    return format(0, m-1, 0, n-1, digits, false);
  }

  /** Decimal format for converting a matrix into a string
   *  @param digits the number of digits
   *  @param trailing
   *  @return the decimal format
   */
  protected DecimalFormat []  format( int digits, boolean trailing ) {
    return format(0, m-1, 0, n-1, digits, trailing);
  }
 
  /** Decimal format for converting a matrix into a string
   *  @param i0
   *  @param i1
   *  @param j
   *  @param digits the number of digits
   *  @param trailing
   *  @return the decimal format
   */
  protected DecimalFormat  format(int i0, int i1, int j, int digits,
          boolean trailing) {
    FlexibleDecimalFormat df = new FlexibleDecimalFormat(digits, trailing);
    df.grouping( true );
    for(int i = i0; i <= i1; i ++ )
      df.update( A[i][j] );
    return df;
  }
 
  /** Decimal format for converting a matrix into a string
   *  @param i0
   *  @param i1
   *  @param j0
   *  @param j1
   *  @param trailing
   *  @param digits the number of digits
   *  @return the decimal format
   */
  protected DecimalFormat []  format(int i0, int i1, int j0, int j1,
             int digits, boolean trailing) {
    DecimalFormat [] f = new DecimalFormat[j1-j0+1];
    for( int j = j0; j <= j1; j++ ) {
      f[j] = format(i0, i1, j, digits, trailing);
    }
    return f;
  }
 
  /**
   * Converts matrix to string
   *
   * @return the matrix as string
   */
  public String  toString() {
    return toString( 5, false );
  }
 
  /**
   * Converts matrix to string
   *
   * @param digits number of digits after decimal point
   * @param trailing true if trailing zeros are padded
   * @return the matrix as string
   */
  public String  toString( int digits, boolean trailing ) {
   
    if( isEmpty() ) return "null matrix";
   
    StringBuffer text = new StringBuffer();
    DecimalFormat [] nf = format( digits, trailing );
    int numCols = 0;
    int count = 0;
    int width = 80;
    int lenNumber;
   
    int [] nCols = new int[n];
    int nk=0;
    for( int j = 0; j < n; j++ ) {
      lenNumber = nf[j].format( A[0][j]).length();
      if( count + 1 + lenNumber > width -1 ) {
  nCols[nk++= numCols;
  count = 0;
  numCols = 0;
      }
      count += 1 + lenNumber;
      ++numCols;
    }
    nCols[nk] = numCols;
   
    nk = 0;
    for( int k = 0; k < n; ) {
      for( int i = 0; i < m; i++ ) {
  for( int j = k; j < k + nCols[nk]; j++)
    text.append( " " + nf[j].format( A[i][j]) );
  text.append("\n");
      }
      k += nCols[nk];
      ++nk;
      text.append("\n");
    }
   
    return text.toString();
  }
 
  /** Squared sum of a column or row in a matrix
   * @param j the index of the column or row
   * @param i0 the index of the first element
   * @param i1 the index of the last element
   * @param col if true, sum over a column; otherwise, over a row
   * @return the squared sum
   */
  public double sum2( int j, int i0, int i1, boolean col ) {
    double s2 = 0;
    if( col ) {   // column
      for( int i = i0; i <= i1; i++ )
  s2 += A[i][j] * A[i][j];
    }
    else {
      for( int i = i0; i <= i1; i++ )
  s2 += A[j][i] * A[j][i];
    }
    return s2;
  }
 
  /** Squared sum of columns or rows of a matrix
   * @param col if true, sum over columns; otherwise, over rows
   * @return the squared sum
   */
  public double[] sum2( boolean col ) {
    int l = col ? n : m;
    int p = col ? m : n;
    double [] s2 = new double[l];
    for( int i = 0; i < l; i++ )
      s2[i] = sum2( i, 0, p-1, col );
    return s2;
  }

  /** Constructs single Householder transformation for a column
   *
   @param j    the index of the column
   @param k    the index of the row
   @return     d and q
  */
  public double [] h1( int j, int k ) {
    double dq[] = new double[2];
    double s2 = sum2(j, k, m-1, true)
    dq[0] = A[k][j] >= 0 ? - Math.sqrt( s2 ) : Math.sqrt( s2 );
    A[k][j] -= dq[0];
    dq[1] = A[k][j] * dq[0];
    return dq;
  }
 
  /** Performs single Householder transformation on one column of a matrix
   *
   @param j    the index of the column
   @param k    the index of the row
   @param q    q = - u'u/2; must be negative
   @param b    the matrix to be transformed
   @param l    the column of the matrix b
  */
  public void h2( int j, int k, double q, PaceMatrix b, int l ) {
    double s = 0, alpha;
    for( int i = k; i < m; i++ )
      s += A[i][j] * b.A[i][l];
    alpha = s / q;
    for( int i = k; i < m; i++ )
      b.A[i][l] += alpha * A[i][j];
  }
 
  /** Constructs the Givens rotation
   *  @param a
   *  @param b
   *  @return a double array that stores the cosine and sine values
   */
  public double []  g1( double a, double b ) {
    double cs[] = new double[2];
    double r = Maths.hypot(a, b);
    if( r == 0.0 ) {
      cs[0] = 1;
      cs[1] = 0;
    }
    else {
      cs[0] = a / r;
      cs[1] = b / r;
    }
    return cs;
  }
 
  /** Performs the Givens rotation
   * @param cs a array storing the cosine and sine values
   * @param i0 the index of the row of the first element
   * @param i1 the index of the row of the second element
   * @param j the index of the column
   */
  public void  g2( double cs[], int i0, int i1, int j ){
    double w =   cs[0] * A[i0][j] + cs[1] * A[i1][j];
    A[i1][j] = - cs[1] * A[i0][j] + cs[0] * A[i1][j];
    A[i0][j] = w;
  }
 
  /** Forward ordering of columns in terms of response explanation.  On
   *  input, matrices A and b are already QR-transformed. The indices of
   *  transformed columns are stored in the pivoting vector.
   * 
   *@param b     the PaceMatrix b
   *@param pvt   the pivoting vector
   *@param k0    the first k0 columns (in pvt) of A are not to be changed
   **/
  public void forward( PaceMatrix b, IntVector pvt, int k0 ) {
    for( int j = k0; j < Math.min(pvt.size(), m); j++ ) {
      steplsqr( b, pvt, j, mostExplainingColumn(b, pvt, j), true );
    }
  }

  /** Returns the index of the column that has the largest (squared)
   *  response, when each of columns pvt[ks:] is moved to become the
   *  ks-th column. On input, A and b are both QR-transformed.
   * 
   * @param b    response
   * @param pvt  pivoting index of A
   * @param ks   columns pvt[ks:] of A are to be tested
   * @return the index of the column
   */
  public int  mostExplainingColumn( PaceMatrix b, IntVector pvt, int ks ) {
    double val;
    int [] p = pvt.getArray();
    double ma = columnResponseExplanation( b, pvt, ks, ks );
    int jma = ks;
    for( int i = ks+1; i < pvt.size(); i++ ) {
      val = columnResponseExplanation( b, pvt, i, ks );
      if( val > ma ) {
  ma = val;
  jma = i;
      }
    }
    return jma;
  }
 
  /** Backward ordering of columns in terms of response explanation.  On
   *  input, matrices A and b are already QR-transformed. The indices of
   *  transformed columns are stored in the pivoting vector.
   *
   *  A and b must have the same number of rows, being the (pseudo-)rank.
   * 
   * @param b     PaceMatrix b
   * @param pvt   pivoting vector
   * @param ks    number of QR-transformed columns; psuedo-rank of A
   * @param k0    first k0 columns in pvt[] are not to be ordered.
   */
  public void backward( PaceMatrix b, IntVector pvt, int ks, int k0 ) {
    for( int j = ks; j > k0; j-- ) {
      steplsqr( b, pvt, j, leastExplainingColumn(b, pvt, j, k0), false );
    }
  }

  /** Returns the index of the column that has the smallest (squared)
   *  response, when the column is moved to become the (ks-1)-th
   *  column. On input, A and b are both QR-transformed.
   * 
   * @param b    response
   * @param pvt  pivoting index of A
   * @param ks   psudo-rank of A
   * @param k0   A[][pvt[0:(k0-1)]] are excluded from the testing.
   * @return the index of the column
   */
  public int  leastExplainingColumn( PaceMatrix b, IntVector pvt, int ks,
             int k0 ) {
    double val;
    int [] p = pvt.getArray();
    double mi = columnResponseExplanation( b, pvt, ks-1, ks );
    int jmi = ks-1;
    for( int i = k0; i < ks - 1; i++ ) {
      val = columnResponseExplanation( b, pvt, i, ks );
      if( val <= mi ) {
  mi = val;
  jmi = i;
      }
    }
    return jmi;
  }
 
  /** Returns the squared ks-th response value if the j-th column becomes
   *  the ks-th after orthogonal transformation.  A[][pvt[ks:j]] (or
   *  A[][pvt[j:ks]], if ks > j) and b[] are already QR-transformed
   *  on input and will remain unchanged on output.
   *
   *  More generally, it returns the inner product of the corresponding
   *  row vector of the response PaceMatrix. (To be implemented.)
   *
   *@param b    PaceMatrix b
   *@param pvt  pivoting vector
   *@param j    the column A[pvt[j]][] is to be moved
   *@param ks   the target column A[pvt[ks]][]
   *@return     the squared response value */
  public double  columnResponseExplanation( PaceMatrix b, IntVector pvt,
              int j, int ks ) {
    /*  Implementation:
     *
     *  If j == ks - 1, returns the squared ks-th response directly.
     *
     *  If j > ks -1, returns the ks-th response after
     *  Householder-transforming the j-th column and the response.
     *
     *  If j < ks - 1, returns the ks-th response after a sequence of
     *  Givens rotations starting from the j-th row. */

    int k, l;
    double [] xxx = new double[n];
    int [] p = pvt.getArray();
    double val;
   
    if( j == ks -1 ) val = b.A[j][0];
    else if( j > ks - 1 ) {
      int jm = Math.min(n-1, j);
      DoubleVector u = getColumn(ks,jm,p[j]);
      DoubleVector v = b.getColumn(ks,jm,0);
      val = v.innerProduct(u) / u.norm2();
    }
    else {                 // ks > j
      for( k = j+1; k < ks; k++ ) // make a copy of A[j][]
  xxx[k] = A[j][p[k]];
      val = b.A[j][0];
      double [] cs;
      for( k = j+1; k < ks; k++ ) {
  cs = g1( xxx[k], A[k][p[k]] );
  for( l = k+1; l < ks; l++ )
    xxx[l] = - cs[1] * xxx[l] + cs[0] * A[k][p[l]];
  val = - cs[1] * val + cs[0] * b.A[k][0];
      }
    }
    return val * val;  // or inner product in later implementation???
  }

  /**
   * QR transformation for a least squares problem<br/>
   *            A x = b<br/>
   * implicitly both A and b are transformed. pvt.size() is the psuedo-rank of
   * A.
   * 
   * @param b    PaceMatrix b
   * @param pvt  pivoting vector
   * @param k0   the first k0 columns of A (indexed by pvt) are pre-chosen.
   *            (But subject to rank examination.)
   *
   *            For example, the constant term may be reserved, in which
   *            case k0 = 1.
   **/
  public void  lsqr( PaceMatrix b, IntVector pvt, int k0 ) {
    final double TINY = 1e-15;
    int [] p = pvt.getArray();
    int ks = 0// psuedo-rank
    for(int j = 0; j < k0; j++ )   // k0 pre-chosen columns
      if( sum2(p[j],ks,m-1,true) > TINY ){ // large diagonal element
  steplsqr(b, pvt, ks, j, true);
  ks++;
      }
      else {                     // collinear column
  pvt.shiftToEnd( j );
  pvt.setSize(pvt.size()-1);
  k0--;
  j--;
      }
 
    // initial QR transformation
    for(int j = k0; j < Math.min( pvt.size(), m ); j++ ) {
      if( sum2(p[j], ks, m-1, true) > TINY ) {
  steplsqr(b, pvt, ks, j, true);
  ks++;
      }
      else {                     // collinear column
  pvt.shiftToEnd( j );
  pvt.setSize(pvt.size()-1);
  j--;
      }
    }
 
    b.m = m = ks;           // reset number of rows
    pvt.setSize( ks );
  }
   
  /** QR transformation for a least squares problem <br/>
   *            A x = b <br/>
   * implicitly both A and b are transformed. pvt.size() is the psuedo-rank of A.
   * 
   * @param b    PaceMatrix b
   * @param pvt  pivoting vector
   * @param k0   the first k0 columns of A (indexed by pvt) are pre-chosen.
   *            (But subject to rank examination.)
   *
   *            For example, the constant term may be reserved, in which
   *            case k0 = 1.
   **/
  public void  lsqrSelection( PaceMatrix b, IntVector pvt, int k0 ) {
    int numObs = m;         // number of instances
    int numXs = pvt.size();

    lsqr( b, pvt, k0 );

    if( numXs > 200 || numXs > numObs ) { // too many columns. 
      forward(b, pvt, k0);
    }
    backward(b, pvt, pvt.size(), k0);
  }
   
  /**
   * Sets all diagonal elements to be positive (or nonnegative) without
   * changing the least squares solution
   * @param Y the response
   * @param pvt the pivoted column index
   */
  public void positiveDiagonal( PaceMatrix Y, IntVector pvt ) {
    
    int [] p = pvt.getArray();
    for( int i = 0; i < pvt.size(); i++ ) {
      if( A[i][p[i]] < 0.0 ) {
  for( int j = i; j < pvt.size(); j++ )
    A[i][p[j]] = - A[i][p[j]];
  Y.A[i][0] = - Y.A[i][0];
      }
    }
  }

  /** Stepwise least squares QR-decomposition of the problem
   *            A x = b
   @param b    PaceMatrix b
   @param pvt  pivoting vector
   @param ks   number of transformed columns
   @param j    pvt[j], the column to adjoin or delete
   @param adjoin   to adjoin if true; otherwise, to delete */
  public void  steplsqr( PaceMatrix b, IntVector pvt, int ks, int j,
       boolean adjoin ) {
    final int kp = pvt.size(); // number of columns under consideration
    int [] p = pvt.getArray();
 
    if( adjoin ) {     // adjoining
      int pj = p[j];
      pvt.swap( ks, j );
      double dq[] = h1( pj, ks );
      int pk;
      for( int k = ks+1; k < kp; k++ ){
  pk = p[k];
  h2( pj, ks, dq[1], this, pk);
      }
      h2( pj, ks, dq[1], b, 0 ); // for matrix. ???
      A[ks][pj] = dq[0];
      for( int k = ks+1; k < m; k++ )
  A[k][pj] = 0;
    }
    else {          // removing
      int pj = p[j];
      for( int i = j; i < ks-1; i++ )
  p[i] = p[i+1];
      p[ks-1] = pj;
      double [] cs;
      for( int i = j; i < ks-1; i++ ){
  cs = g1( A[i][p[i]], A[i+1][p[i]] );
  for( int l = i; l < kp; l++ )
    g2( cs, i, i+1, p[l] );
  for( int l = 0; l < b.n; l++ )
    b.g2( cs, i, i+1, l );
      }
    }
  }
   
  /** Solves upper-triangular equation <br/>
   *     R x = b <br/>
   *  On output, the solution is stored in b
   *  @param b the response
   *  @param pvt the pivoting vector
   *  @param kp the number of the first columns involved
   */
  public void  rsolve( PaceMatrix b, IntVector pvt, int kp) {
    if(kp == 0) b.m = 0;
    int i, j, k;
    int [] p = pvt.getArray();
    double s;
    double [][] ba = b.getArray();
    for( k = 0; k < b.n; k++ ) {
      ba[kp-1][k] /= A[kp-1][p[kp-1]];
      for( i = kp - 2; i >= 0; i-- ){
  s = 0;
  for( j = i + 1; j < kp; j++ )
    s += A[i][p[j]] * ba[j][k];
  ba[i][k] -= s;
  ba[i][k] /= A[i][p[i]];
      }
    }
    b.m = kp;
  }
   
  /** Returns a new matrix which binds two matrices together with rows.
   *  @param b  the second matrix
   *  @return the combined matrix
   */
  public PaceMatrix  rbind( PaceMatrix b ){
    if( n != b.n )
      throw new IllegalArgumentException("unequal numbers of rows.");
    PaceMatrix c = new PaceMatrix( m + b.m, n );
    c.setMatrix( 0, m - 1, 0, n - 1, this );
    c.setMatrix( m, m + b.m - 1, 0, n - 1, b );
    return c;
  }

  /** Returns a new matrix which binds two matrices with columns.
   *  @param b the second matrix
   *  @return the combined matrix
   */
  public PaceMatrix  cbind( PaceMatrix b ) {
    if( m != b.m )
      throw new IllegalArgumentException("unequal numbers of rows: " +
           m + " and " + b.m);
    PaceMatrix c = new PaceMatrix(m, n + b.n);
    c.setMatrix( 0, m - 1, 0, n - 1, this );
    c.setMatrix( 0, m - 1, n, n + b.n - 1, b );
    return c;
  }

  /** Solves the nonnegative linear squares problem. That is, <p>
   *   <center>   min || A x - b||, subject to x >= 0.  </center> <p>
   *
   *  For algorithm, refer to P161, Chapter 23 of C. L. Lawson and
   *  R. J. Hanson (1974).  "Solving Least Squares
   *  Problems". Prentice-Hall.
   *   @param b the response
   *  @param pvt vector storing pivoting column indices
   *  @return solution */
  public DoubleVector nnls( PaceMatrix b, IntVector pvt ) {
    int j, t, counter = 0, jm = -1, n = pvt.size();
    double ma, max, alpha, wj;
    int [] p = pvt.getArray();
    DoubleVector x = new DoubleVector( n );
    double [] xA = x.getArray();
    PaceMatrix z = new PaceMatrix(n, 1);
    PaceMatrix bt;
 
    // step 1
    int kp = 0; // #variables in the positive set P
    while ( true ) {         // step 2
      if( ++counter > 3*n // should never happen
  throw new RuntimeException("Does not converge");
      t = -1;
      max = 0.0;
      bt = new PaceMatrix( b.transpose() );
      for( j = kp; j <= n-1; j++ ) {   // W = A' (b - A x)
  wj = bt.times( 0, kp, m-1, this, p[j] );
  if( wj > max ) {        // step 4
    max = wj;
    t = j;
  }
      }
     
      // step 3
      if ( t == -1) break; // optimum achieved
     
      // step 5
      pvt.swap( kp, t );       // move variable from set Z to set P
      kp++;
      xA[kp-1] = 0;
      steplsqr( b, pvt, kp-1, kp-1, true );
      // step 6
      ma = 0;
      while ( ma < 1.5 ) {
  for( j = 0; j <= kp-1; j++ ) z.A[j][0] = b.A[j][0];
  rsolve(z, pvt, kp);
  ma = 2; jm = -1;
  for( j = 0; j <= kp-1; j++ ) {  // step 7, 8 and 9
    if( z.A[j][0] <= 0.0 ) { // alpha always between 0 and 1
      alpha = xA[j] / ( xA[j] - z.A[j][0] );
      if( alpha < ma ) {
        ma = alpha; jm = j;
      }
    }
  }
  if( ma > 1.5 )
    for( j = 0; j <= kp-1; j++ ) xA[j] = z.A[j][0]// step 7
  else {
    for( j = kp-1; j >= 0; j-- ) { // step 10
      // Modified to avoid round-off error (which seemingly
      // can cause infinite loop).
      if( j == jm ) { // step 11
        xA[j] = 0.0;
        steplsqr( b, pvt, kp, j, false );
        kp--;  // move variable from set P to set Z
      }
      else xA[j] += ma * ( z.A[j][0] - xA[j] );
    }
  }
      }
    }
    x.setSize(kp);
    pvt.setSize(kp);
    return x;
  }

  /** Solves the nonnegative least squares problem with equality
   *  constraint. That is, <p>
   <center> min ||A x - b||, subject to x >= 0 and c x = d. </center> <p>
   *
   *  @param b the response
   *  @param c coeficients of equality constraints
   *  @param d constants of equality constraints
   *  @param pvt vector storing pivoting column indices
   *  @return the solution
   */
  public DoubleVector nnlse( PaceMatrix b, PaceMatrix c, PaceMatrix d,
           IntVector pvt ) {
    double eps = 1e-10 * Math.max( c.maxAbs(), d.maxAbs() ) /
    Math.max( maxAbs(), b.maxAbs() );
 
    PaceMatrix e = c.rbind( new PaceMatrix( times(eps) ) );
    PaceMatrix f = d.rbind( new PaceMatrix( b.times(eps) ) );

    return e.nnls( f, pvt );
  }

  /** Solves the nonnegative least squares problem with equality
   *  constraint. That is, <p>
   <center> min ||A x - b||,  subject to x >= 0 and || x || = 1. </center>
   <p>
   @param b the response
   *  @param pvt vector storing pivoting column indices
   *  @return the solution
   */
  public DoubleVector nnlse1( PaceMatrix b, IntVector pvt ) {
    PaceMatrix c = new PaceMatrix( 1, n, 1 );
    PaceMatrix d = new PaceMatrix( 1, b.n, 1 );
 
    return nnlse(b, c, d, pvt);
  }

  /** Generate matrix with standard-normally distributed random elements
      @param m    Number of rows.
      @param n    Number of colums.
      @return An m-by-n matrix with random elements.  */
  public static Matrix randomNormal( int m, int n ) {
    Random random = new Random();
    
    Matrix A = new Matrix(m,n);
    double[][] X = A.getArray();
    for (int i = 0; i < m; i++) {
      for (int j = 0; j < n; j++) {
  X[i][j] = random.nextGaussian();
      }
    }
    return A;
  }
 
  /**
   * Returns the revision string.
   *
   * @return    the revision
   */
  public String getRevision() {
    return RevisionUtils.extract("$Revision: 1.6 $");
  }

  /**
   * for testing only
   *
   * @param args the commandline arguments - ignored
   */
  public static void  main( String args[] ) {
    System.out.println("================================================" +
           "===========");
    System.out.println("To test the pace estimators of linear model\n" +
           "coefficients.\n");

    double sd = 2;     // standard deviation of the random error term
    int n = 200;       // total number of observations
    double beta0 = 100;   // intercept
    int k1 = 20;       // number of coefficients of the first cluster
    double beta1 = 0// coefficient value of the first cluster
    int k2 = 20;      // number of coefficients of the second cluster
    double beta2 = 5; // coefficient value of the second cluster
    int k = 1 + k1 + k2;

    DoubleVector beta = new DoubleVector( 1 + k1 + k2 );
    beta.set( 0, beta0 );
    beta.set( 1, k1, beta1 );
    beta.set( k1+1, k1+k2, beta2 );

    System.out.println("The data set contains " + n +
           " observations plus " + (k1 + k2) +
           " variables.\n\nThe coefficients of the true model"
           + " are:\n\n" + beta );
 
    System.out.println("\nThe standard deviation of the error term is " +
           sd );
 
    System.out.println("==============================================="
           + "============");
   
    PaceMatrix X = new PaceMatrix( n, k1+k2+1 );
    X.setMatrix( 0, n-1, 0, 0, 1 );
    X.setMatrix( 0, n-1, 1, k1+k2, random(n, k1+k2) );
 
    PaceMatrix Y = new
      PaceMatrix( X.times( new PaceMatrix(beta) ).
      plusEquals( randomNormal(n,1).times(sd) ) );

    IntVector pvt = (IntVector) IntVector.seq(0, k1+k2);

    /*System.out.println( "The OLS estimate (by jama.Matrix.solve()) is:\n\n" +
      (new PaceMatrix(X.solve(Y))).getColumn(0) );*/
 
    X.lsqrSelection( Y, pvt, 1 );
    X.positiveDiagonal( Y, pvt );

    PaceMatrix sol = (PaceMatrix) Y.clone();
    X.rsolve( sol, pvt, pvt.size() );
    DoubleVector betaHat = sol.getColumn(0).unpivoting( pvt, k );
    System.out.println( "\nThe OLS estimate (through lsqr()) is: \n\n" +
      betaHat );

    System.out.println( "\nQuadratic loss of the OLS estimate (||X b - X bHat||^2) = " +
      ( new PaceMatrix( X.times( new
        PaceMatrix(beta.minus(betaHat)) )))
      .getColumn(0).sum2() );

    System.out.println("=============================================" +
           "==============");
    System.out.println("             *** Pace estimation *** \n");
    DoubleVector r = Y.getColumn( pvt.size(), n-1, 0);
    double sde = Math.sqrt(r.sum2() / r.size());
 
    System.out.println( "Estimated standard deviation = " + sde );

    DoubleVector aHat = Y.getColumn( 0, pvt.size()-1, 0).times( 1./sde );
    System.out.println("\naHat = \n" + aHat );
 
    System.out.println("\n========= Based on chi-square mixture ============");

    ChisqMixture d2 = new ChisqMixture();
    int method = MixtureDistribution.NNMMethod;
    DoubleVector AHat = aHat.square();
    d2.fit( AHat, method );
    System.out.println( "\nEstimated mixing distribution is:\n" + d2 );
 
    DoubleVector ATilde = d2.pace2( AHat );
    DoubleVector aTilde = ATilde.sqrt().times(aHat.sign());
    PaceMatrix YTilde = new
      PaceMatrix((new PaceMatrix(aTilde)).times( sde ));
    X.rsolve( YTilde, pvt, pvt.size() );
    DoubleVector betaTilde =
    YTilde.getColumn(0).unpivoting( pvt, k );
    System.out.println( "\nThe pace2 estimate of coefficients = \n" +
      betaTilde );
    System.out.println( "Quadratic loss = " +
      ( new PaceMatrix( X.times( new
        PaceMatrix(beta.minus(betaTilde)) )))
      .getColumn(0).sum2() );
 
    ATilde = d2.pace4( AHat );
    aTilde = ATilde.sqrt().times(aHat.sign());
    YTilde = new PaceMatrix((new PaceMatrix(aTilde)).times( sde ));
    X.rsolve( YTilde, pvt, pvt.size() );
    betaTilde = YTilde.getColumn(0).unpivoting( pvt, k );
    System.out.println( "\nThe pace4 estimate of coefficients = \n" +
      betaTilde );
    System.out.println( "Quadratic loss = " +
      ( new PaceMatrix( X.times( new
        PaceMatrix(beta.minus(betaTilde)) )))
      .getColumn(0).sum2() );
 
    ATilde = d2.pace6( AHat );
    aTilde = ATilde.sqrt().times(aHat.sign());
    YTilde = new PaceMatrix((new PaceMatrix(aTilde)).times( sde ));
    X.rsolve( YTilde, pvt, pvt.size() );
    betaTilde = YTilde.getColumn(0).unpivoting( pvt, k );
    System.out.println( "\nThe pace6 estimate of coefficients = \n" +
      betaTilde );
    System.out.println( "Quadratic loss = " +
      ( new PaceMatrix( X.times( new
        PaceMatrix(beta.minus(betaTilde)) )))
      .getColumn(0).sum2() );
 
    System.out.println("\n========= Based on normal mixture ============");
 
    NormalMixture d = new NormalMixture();
    d.fit( aHat, method );
    System.out.println( "\nEstimated mixing distribution is:\n" + d );
 
    aTilde = d.nestedEstimate( aHat );
    YTilde = new PaceMatrix((new PaceMatrix(aTilde)).times( sde ));
    X.rsolve( YTilde, pvt, pvt.size() );
    betaTilde = YTilde.getColumn(0).unpivoting( pvt, k );
    System.out.println( "The nested estimate of coefficients = \n" +
      betaTilde );
    System.out.println( "Quadratic loss = " +
      ( new PaceMatrix( X.times( new
        PaceMatrix(beta.minus(betaTilde)) )))
      .getColumn(0).sum2() );
 
 
    aTilde = d.subsetEstimate( aHat );
    YTilde = new PaceMatrix((new PaceMatrix(aTilde)).times( sde ));
    X.rsolve( YTilde, pvt, pvt.size() );
    betaTilde =
    YTilde.getColumn(0).unpivoting( pvt, k );
    System.out.println( "\nThe subset estimate of coefficients = \n" +
      betaTilde );
    System.out.println( "Quadratic loss = " +
      ( new PaceMatrix( X.times( new
        PaceMatrix(beta.minus(betaTilde)) )))
      .getColumn(0).sum2() );
 
    aTilde = d.empiricalBayesEstimate( aHat );
    YTilde = new PaceMatrix((new PaceMatrix(aTilde)).times( sde ));
    X.rsolve( YTilde, pvt, pvt.size() );
    betaTilde = YTilde.getColumn(0).unpivoting( pvt, k );
    System.out.println( "\nThe empirical Bayes estimate of coefficients = \n"+
      betaTilde );
 
    System.out.println( "Quadratic loss = " +
      ( new PaceMatrix( X.times( new
        PaceMatrix(beta.minus(betaTilde)) )))
      .getColumn(0).sum2() );
 
  }
}


TOP

Related Classes of weka.classifiers.functions.pace.PaceMatrix

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.