All Downloads are FREE. Search and download functionalities are using the official Maven repository.

breeze.linalg.functions.qr.scala Maven / Gradle / Ivy

There is a newer version: 1.0
Show newest version
package breeze.linalg

import breeze.generic.UFunc
import org.netlib.util.intW
import com.github.fommil.netlib.LAPACK.{getInstance=>lapack}
import spire.implicits.{cforRange, cforRange2}


sealed private[this] trait QRMode
private[this] case object CompleteQR extends QRMode  // Q and R have dimensions (m, m), (m, n)
private[this] case object ReducedQR extends QRMode   // Q and R have dimensions (m, k), (k, n) with k = min(m, n)


/**
 * QR Factorization
 *
 * Previous versions of Breeze had qr(m, skipQ), where we could
 * skip the computation in making Q if we didn't want it. That is now supplanted by qr.justR(m)
 *
 * Supports complete and reduced mode of factorization of matrix A with dimensions (m, n).
 * If mode is complete matrices Q and R have dimensions (m, m), (m, n).
 * If mode is reduced matrices Q and R have dimensions (m, k), (k, n) with k = min(m, n).
 *
 * Complete QR factorization can be called by qr(A).
 *
 * Reduced QR factorization can be called by qr.reduced(A). If computation of Q is unnecessary, it
 * can be skipped by qr.reduced.justR(A)
 *
 * @return (Q, R)
 *         Q - A matrix with orthonormal columns
 *         R - The upper-triangular matrix
 *
 */
object qr extends UFunc {

  case class QR[M](q: M, r: M)

  type DenseQR = QR[DenseMatrix[Double]]
  type SDenseQR = QR[DenseMatrix[Float]]

  implicit object impl_DM_Double extends Impl[DenseMatrix[Double], DenseQR] {
    def apply(v: DenseMatrix[Double]): DenseQR = {
      val (q, r) = doQr(v, skipQ = false)(mode = CompleteQR)
      QR(q, r)
    }
  }

  implicit object impl_DM_Float extends Impl[DenseMatrix[Float], SDenseQR] {
    def apply(v: DenseMatrix[Float]): SDenseQR = {
      val (q, r) = doQr_Float(v, skipQ = false)(mode = CompleteQR)
      QR(q, r)
    }
  }

  /**
   * QR that just returns R.
   */
  object justR extends UFunc {

    implicit object impl_DM_Double extends Impl[DenseMatrix[Double], DenseMatrix[Double]] {
      def apply(v: DenseMatrix[Double]): DenseMatrix[Double] = {
        doQr(v, skipQ = true)(mode = CompleteQR)._2
      }
    }

    implicit object impl_DM_Float extends Impl[DenseMatrix[Float], DenseMatrix[Float]] {
      def apply(v: DenseMatrix[Float]): DenseMatrix[Float] = {
        doQr_Float(v, skipQ = true)(mode = CompleteQR)._2
      }
    }

    implicit def canJustQIfWeCanQR[T, M](implicit qrImpl: qr.Impl[T, QR[M]]): Impl[T, M] = {
      new Impl[T, M] {
        def apply(v: T): M = qrImpl(v).r
      }

    }
  }

  /**
   * QR that just returns Q.
   */
  object justQ extends UFunc {

    implicit def canJustQIfWeCanQR[T, M](implicit qrImpl: qr.Impl[T, QR[M]]): Impl[T, M] = {
      new Impl[T, M] {
        def apply(v: T): M = qrImpl(v).q
      }

    }
  }


  /**
   * QR Factorization that returns Q and R with dimensions (m, k), (k, n) where k = min(m, n)
   */
  object reduced extends UFunc {

    implicit object impl_reduced_DM_Double extends Impl[DenseMatrix[Double], DenseQR] {
      def apply(v: DenseMatrix[Double]): DenseQR = {
        val (q, r) = doQr(v, skipQ = false)(mode = ReducedQR)
        QR(q, r)
      }
    }

    implicit object impl_reduced_DM_Float extends Impl[DenseMatrix[Float], SDenseQR] {
      def apply(v: DenseMatrix[Float]): SDenseQR = {
        val (q, r) = doQr_Float(v, skipQ = false)(mode = ReducedQR)
        QR(q, r)
      }
    }

    /**
     * QR that just returns R with reduced size.
     */
    object justR extends UFunc {

      implicit object impl_reduced_DM_Double extends Impl[DenseMatrix[Double], DenseMatrix[Double]] {
        def apply(v: DenseMatrix[Double]): DenseMatrix[Double] = {
          doQr(v, skipQ = true)(mode = ReducedQR)._2
        }
      }

      implicit object impl_reduced_DM_Float extends Impl[DenseMatrix[Float], DenseMatrix[Float]] {
        def apply(v: DenseMatrix[Float]): DenseMatrix[Float] = {
          doQr_Float(v, skipQ = true)(mode = ReducedQR)._2
        }
      }

      implicit def canJustQIfWeCanQR[T, M](implicit qrImpl: qr.reduced.Impl[T, QR[M]]): Impl[T, M] = {
        new Impl[T, M] {
          def apply(v: T): M = qrImpl(v).r
        }
      }
    }

    /**
     * QR that just returns Q with reduced size.
     */
    object justQ extends UFunc {
      implicit def canJustQIfWeCanQR[T, M](implicit qrImpl: qr.reduced.Impl[T, QR[M]]): Impl[T, M] = {
        new Impl[T, M] {
          def apply(v: T): M = qrImpl(v).q
        }
      }
    }

  }

  private def doQr(M: DenseMatrix[Double], skipQ: Boolean)
                  (mode: QRMode): (DenseMatrix[Double], DenseMatrix[Double]) = {

    val A = M.copy

    val m = A.rows
    val n = A.cols

    val mn = scala.math.min(m, n)
    val tau = new Array[Double](mn)

    // Calculate optimal size of work data 'work'
    val work = new Array[Double](1)
    val info = new intW(0)
    lapack.dgeqrf(m, n, A.data, m, tau, work, -1, info)

    // do QR
    val lwork = if (info.`val` != 0) n else work(0).toInt
    var workspace = new Array[Double](lwork)

    lapack.dgeqrf(m, n, A.data, m, tau, workspace, lwork, info)

    //Error check
    if (info.`val` > 0)
      throw new NotConvergedException(NotConvergedException.Iterations)
    else if (info.`val` < 0)
      throw new IllegalArgumentException()

    // Handle mode that don't return Q
    if (skipQ) (null, upperTriangular(A(0 until mn, ::)))
    else {
      val Q = if (mode == CompleteQR && m > n) DenseMatrix.zeros[Double](m, m)
      else DenseMatrix.zeros[Double](m, n)

      val mc = if (mode == CompleteQR && m > n) m else mn

      Q(::, 0 until n) := A

      // Calculate optimal size of workspace
      lapack.dorgqr(m, mc, mn, Q.data, m, tau, work, -1, info)
      val lwork1 = if (info.`val` != 0) n else work(0).toInt
      workspace = new Array[Double](lwork1)
      // Compute Q
      lapack.dorgqr(m, mc, mn, Q.data, m, tau, workspace, lwork1, info)

      //Error check
      if (info.`val` > 0)
        throw new NotConvergedException(NotConvergedException.Iterations)
      else if (info.`val` < 0)
        throw new IllegalArgumentException()

      // Upper triangle
      cforRange(0 until mc){ i =>
        cforRange(0 until min(i, A.cols)){ j =>
          A(i, j) = 0.0
        }
      }

      (Q(::, 0 until mc), A(0 until mc, ::))
    }
  }

  private def doQr_Float(M: DenseMatrix[Float], skipQ: Boolean)
                        (mode: QRMode): (DenseMatrix[Float], DenseMatrix[Float]) = {

    val A = M.copy

    val m = A.rows
    val n = A.cols

    val mn = scala.math.min(m, n)
    val tau = new Array[Float](mn)

    // Calculate optimal size of work data 'work'
    val work = new Array[Float](1)
    val info = new intW(0)
    lapack.sgeqrf(m, n, A.data, m, tau, work, -1, info)

    // do QR
    val lwork = if (info.`val` != 0) n else work(0).toInt
    var workspace = new Array[Float](lwork)

    lapack.sgeqrf(m, n, A.data, m, tau, workspace, lwork, info)

    //Error check
    if (info.`val` > 0)
      throw new NotConvergedException(NotConvergedException.Iterations)
    else if (info.`val` < 0)
      throw new IllegalArgumentException()

    // Handle mode that don't return Q
    if (skipQ) (null, upperTriangular(A(0 until mn, ::)))
    else {
      val Q = if (mode == CompleteQR && m > n) DenseMatrix.zeros[Float](m, m)
      else DenseMatrix.zeros[Float](m, n)

      val mc = if (mode == CompleteQR && m > n) m else mn

      Q(::, 0 until n) := A

      // Calculate optimal size of workspace
      lapack.sorgqr(m, mc, mn, Q.data, m, tau, work, -1, info)
      val lwork1 = if (info.`val` != 0) n else work(0).toInt
      workspace = new Array[Float](lwork1)
      // Compute Q
      lapack.sorgqr(m, mc, mn, Q.data, m, tau, workspace, lwork1, info)

      //Error check
      if (info.`val` > 0)
        throw new NotConvergedException(NotConvergedException.Iterations)
      else if (info.`val` < 0)
        throw new IllegalArgumentException()

      // Upper triangle
      cforRange(0 until mc){ i =>
        cforRange(0 until min(i, A.cols)){ j =>
          A(i, j) = 0.0f
        }
      }

      (Q(::, 0 until mc), A(0 until mc, ::))
    }
  }
}


/**
 * QR Factorization with pivoting
 *
 * input: A m x n matrix
 * output: (Q,R,P,pvt) where AP = QR
 *   Q: m x m
 *   R: m x n
 *   P: n x n : permutation matrix (P(pvt(i),i) = 1)
 *   pvt : pivot indices
 */
object qrp extends UFunc {
  case class QRP[M, PivotMatrix](q: M, r: M, pivotMatrix: PivotMatrix, pivotIndices: Array[Int])

  type DenseQRP = QRP[DenseMatrix[Double], DenseMatrix[Int]]

  implicit object impl_DM_Double extends Impl[DenseMatrix[Double], DenseQRP] {
    def apply(A: DenseMatrix[Double]): DenseQRP = {
      val m = A.rows
      val n = A.cols

      //Get optimal workspace size
      // we do this by sending -1 as lwork to the lapack function
      val scratch, work = new Array[Double](1)
      var info = new intW(0)
      lapack.dgeqrf(m, n, scratch, m, scratch, work, -1, info)
      val lwork1 = if(info.`val` != 0) n else work(0).toInt
      lapack.dorgqr(m, m, scala.math.min(m,n), scratch, m, scratch, work, -1, info)
      val lwork2 = if(info.`val` != 0) n else work(0).toInt
      //allocate workspace mem. as max of lwork1 and lwork3
      val workspace = new Array[Double](scala.math.max(lwork1, lwork2))

      //Perform the QR factorization with dgep3
      val maxd = scala.math.max(m,n)
      val AFact = DenseMatrix.zeros[Double](m,maxd)
      val pvt = new Array[Int](n)
      val tau = new Array[Double](scala.math.min(m,n))

      cforRange2(0 until m, 0 until n){
        (r, c) => AFact(r,c) = A(r,c)
      }

      lapack.dgeqp3(m, n, AFact.data, m, pvt, tau, workspace, workspace.length, info)

      //Error check
      if (info.`val` > 0)
        throw new NotConvergedException(NotConvergedException.Iterations)
      else if (info.`val` < 0)
        throw new IllegalArgumentException()

      //Get R
      val R = DenseMatrix.zeros[Double](m,n)

      cforRange(0 until min(n, maxd)){ c =>
        cforRange(0 to min(m, c)){ r =>
          R(r,c) = AFact(r,c)
        }
      }

      //Get Q from the matrix returned by dgep3
      val Q = DenseMatrix.zeros[Double](m,m)
      lapack.dorgqr(m, m, scala.math.min(m,n), AFact.data, m, tau, workspace, workspace.length, info)

      cforRange2(0 until m, 0 until min(m, maxd)){
        (r, c) => Q(r,c) = AFact(r,c)
      }

      //Error check
      if (info.`val` > 0)
        throw new NotConvergedException(NotConvergedException.Iterations)
      else if (info.`val` < 0)
        throw new IllegalArgumentException()

      //Get P
      import NumericOps.Arrays._
      pvt -= 1
      val P = DenseMatrix.zeros[Int](n,n)

      cforRange(0 until n){ i =>
        P(pvt(i), i) = 1
      }

      QRP(Q,R,P,pvt)
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy