-
-
Notifications
You must be signed in to change notification settings - Fork 1
Implement QR decomposition with LAPACK backend #55
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
beec2dc
200b66f
1e774d3
f400935
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,21 @@ | ||||||
| package vecxt | ||||||
|
|
||||||
| import vecxt.matrix.Matrix | ||||||
|
|
||||||
| object QR: | ||||||
| /** Computes the QR decomposition of a matrix. | ||||||
| * | ||||||
| * The QR decomposition factorizes a matrix A into the product Q * R, where: | ||||||
| * - Q is an orthogonal matrix (Q^T * Q = I) | ||||||
| * - R is an upper triangular matrix | ||||||
| * | ||||||
| * @param matrix | ||||||
| * The input matrix to decompose. Must have positive dimensions. | ||||||
| * @return | ||||||
| * A named tuple containing: | ||||||
| * - Q: The orthogonal matrix | ||||||
| * - R: The upper triangular matrix | ||||||
| */ | ||||||
| inline def qr(matrix: Matrix[Double]): (Q: Matrix[Double], R: Matrix[Double]) = ??? | ||||||
|
||||||
| inline def qr(matrix: Matrix[Double]): (Q: Matrix[Double], R: Matrix[Double]) = ??? | |
| inline def qr(matrix: Matrix[Double])(using inline bc: BoundsCheck): (Q: Matrix[Double], R: Matrix[Double]) = ??? |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,166 @@ | ||||||||||||||||||||||
| package vecxt | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| import dev.ludovic.netlib.lapack.JavaLAPACK | ||||||||||||||||||||||
| import org.netlib.util.intW | ||||||||||||||||||||||
| import vecxt.matrix.Matrix | ||||||||||||||||||||||
| import vecxt.MatrixInstance.* | ||||||||||||||||||||||
| import vecxt.MatrixHelper.zeros | ||||||||||||||||||||||
| import vecxt.BoundsCheck.BoundsCheck | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| object QR: | ||||||||||||||||||||||
| private lazy final val lapack = JavaLAPACK.getInstance() | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| /** Computes the QR decomposition of a matrix using LAPACK's dgeqrf and dorgqr routines. | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * The QR decomposition factorizes a matrix A into the product Q * R, where: | ||||||||||||||||||||||
| * - Q is an orthogonal matrix (Q^T * Q = I) | ||||||||||||||||||||||
| * - R is an upper triangular matrix | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * For an m×n matrix A: | ||||||||||||||||||||||
| * - Q is m×m (orthogonal) | ||||||||||||||||||||||
| * - R is m×n (upper triangular) | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * This is useful for solving least squares problems, computing matrix rank, and other numerical linear algebra | ||||||||||||||||||||||
| * tasks. | ||||||||||||||||||||||
| * | ||||||||||||||||||||||
| * @param matrix | ||||||||||||||||||||||
| * The input matrix to decompose. Must have positive dimensions. | ||||||||||||||||||||||
| * @return | ||||||||||||||||||||||
| * A named tuple containing: | ||||||||||||||||||||||
| * - Q: The orthogonal matrix (m×m) | ||||||||||||||||||||||
| * - R: The upper triangular matrix (m×n) | ||||||||||||||||||||||
| * @throws IllegalArgumentException | ||||||||||||||||||||||
| * if matrix dimensions are not positive or if an argument to LAPACK is invalid | ||||||||||||||||||||||
| * @throws ArithmeticException | ||||||||||||||||||||||
| * if the QR decomposition fails to compute | ||||||||||||||||||||||
| */ | ||||||||||||||||||||||
| inline def qr(matrix: Matrix[Double])(using | ||||||||||||||||||||||
| inline bc: BoundsCheck | ||||||||||||||||||||||
| ): (Q: Matrix[Double], R: Matrix[Double]) = | ||||||||||||||||||||||
| val (m, n) = matrix.shape | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| nonEmptyMatCheck(matrix) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| val minmn = math.min(m, n) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Make a copy of the input matrix (LAPACK overwrites it) | ||||||||||||||||||||||
| val aCopy = matrix.deepCopy | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Array to store the scalar factors of the elementary reflectors | ||||||||||||||||||||||
| val tau = Array.ofDim[Double](minmn) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Query for optimal workspace size | ||||||||||||||||||||||
| val workQuery = Array.ofDim[Double](1) | ||||||||||||||||||||||
| val info = new intW(0) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| lapack.dgeqrf( | ||||||||||||||||||||||
| m, | ||||||||||||||||||||||
| n, | ||||||||||||||||||||||
| Array.empty[Double], | ||||||||||||||||||||||
| scala.math.max(1, m), | ||||||||||||||||||||||
| Array.empty[Double], | ||||||||||||||||||||||
| workQuery, | ||||||||||||||||||||||
| -1, | ||||||||||||||||||||||
| info | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| if info.`val` != 0 then throw IllegalStateException(s"QR workspace query failed. INFO=${info.`val`}") | ||||||||||||||||||||||
| end if | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| // Allocate workspace | ||||||||||||||||||||||
| val lwork = math.max(1, workQuery(0).toInt) | ||||||||||||||||||||||
|
Comment on lines
+67
to
+71
|
||||||||||||||||||||||
| if info.`val` != 0 then throw IllegalStateException(s"QR workspace query failed. INFO=${info.`val`}") | |
| end if | |
| // Allocate workspace | |
| val lwork = math.max(1, workQuery(0).toInt) | |
| // If workspace query fails, fallback to a reasonable default (as in eig.scala) | |
| val lwork = | |
| if info.`val` == 0 then math.max(1, workQuery(0).toInt) | |
| else math.max(1, m * n) // fallback: use m*n as workspace size | |
| // Optionally, you could log or comment here: s"QR workspace query failed (INFO=${info.`val`}), using fallback workspace size: $lwork" |
Copilot
AI
Nov 26, 2025
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nitpick] The workspace query failure handling differs from eig.scala which uses a fallback approach. Consider following the pattern from eig.scala (lines 53-55) which provides a fallback workspace size if the query fails, rather than throwing an exception. This would make the implementation more robust.
| if info.`val` != 0 then throw IllegalStateException(s"Q generation workspace query failed. INFO=${info.`val`}") | |
| end if | |
| val lworkQ = math.max(1, workQuery(0).toInt) | |
| val lworkQ = | |
| if info.`val` == 0 then math.max(1, workQuery(0).toInt) | |
| else | |
| // Fallback: use a reasonable default if workspace query fails (pattern from eig.scala) | |
| math.max(1, m * m) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing import for
BoundsChecktype. Add the following import to match the JVM implementation and support the corrected signature: