Skip to content

Commit 03656cb

Browse files
committed
Add dgesv_
1 parent e274a0e commit 03656cb

File tree

8 files changed

+312
-82
lines changed

8 files changed

+312
-82
lines changed

.github/workflows/ci.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@ jobs:
1616
run: swift-format lint --recursive --strict --parallel .
1717
- name: Install BLAS & LAPACK
1818
run: apt-get update && apt-get install -y libblas-dev liblapack-dev
19-
- name: Build
19+
- name: Build (CLPK)
2020
run: swift build
21-
- name: Test
22-
run: swift test --enable-code-coverage
21+
- name: Test (CLPK)
22+
run: swift test -Xswiftc -DACCELERATE_NEW_LAPACK
23+
- name: Build (LAPACK)
24+
run: swift build -Xswiftc -DACCELERATE_NEW_LAPACK
25+

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ The package is structured as follows:
3737
- [x] `vDSP_mtransD` - Transpose a matrix
3838
- [ ] `vDSP_mmulD` - Matrix multiplication
3939
- `LAPACK` - LAPACK functions:
40-
- [ ] `dgesv_` - Solve a system of linear equations
40+
- [x] `dgesv_` - Solve a system of linear equations
4141
- [x] `dgesvd_` - Singular Value Decomposition
4242
- [ ] `dgetrf_` - LU Decomposition
4343
- [ ] `dgetri_` - Inverse of a matrix

Sources/AccelerateLinux/MatrixOps/BasicOps.swift

Lines changed: 0 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,6 @@
11
#if canImport(Accelerate)
22
@_exported import Accelerate
33
#else
4-
import CBLAS
5-
import CLAPACK
6-
7-
/// Calculates the double-precision maximum value of a vector.
8-
/// - Parameters:
9-
/// - __A: The vector to be examined.
10-
/// - __I: The stride of the vector.
11-
/// - __C: The maximum value.
12-
/// - __N: The number of elements in the vector.
13-
public func vDSP_maxvD(
14-
_ __A: UnsafePointer<Double>,
15-
_ __I: vDSP_Stride,
16-
_ __C: UnsafeMutablePointer<Double>,
17-
_ __N: vDSP_Length
18-
) {
19-
__C.pointee = -Double.infinity
20-
guard __N > 0 else { return }
21-
for i in 1..<__N {
22-
__C.pointee = max(__C.pointee, __A[Int(i) * __I])
23-
}
24-
}
25-
26-
/// Calculates the double-precision minimum value of a vector.
27-
/// - Parameters:
28-
/// - __A: The vector to be examined.
29-
/// - __I: The stride of the vector.
30-
/// - __C: The minimum value.
31-
/// - __N: The number of elements in the vector.
32-
public func vDSP_minvD(
33-
_ __A: UnsafePointer<Double>,
34-
_ __I: vDSP_Stride,
35-
_ __C: UnsafeMutablePointer<Double>,
36-
_ __N: vDSP_Length
37-
) {
38-
__C.pointee = Double.infinity
39-
guard __N > 0 else { return }
40-
for i in 1..<__N {
41-
__C.pointee = min(__C.pointee, __A[Int(i) * __I])
42-
}
43-
}
444

455
/// Transposes a double-precision matrix.
466
/// - Parameters:
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#if canImport(Accelerate)
2+
@_exported import Accelerate
3+
#else
4+
import CLAPACK
5+
6+
#if !ACCELERATE_NEW_LAPACK
7+
/// DGESV computes the solution to a real system of linear equations
8+
/// A * X = B,
9+
/// where A is an N-by-N matrix and X and B are N-by-NRHS matrices.
10+
/// The LU decomposition with partial pivoting and row interchanges is
11+
/// used to factor A as
12+
/// A = P * L * U,
13+
/// where P is a permutation matrix, L is unit lower triangular, and U is
14+
/// upper triangular. The factored form of A is then used to solve the
15+
/// system of equations A * X = B.
16+
@_silgen_name("dgesv_")
17+
public func dgesv_(
18+
_ __n: UnsafeMutablePointer<__CLPK_integer>!,
19+
_ __nrhs: UnsafeMutablePointer<__CLPK_integer>!,
20+
_ __a: UnsafeMutablePointer<__CLPK_doublereal>!,
21+
_ __lda: UnsafeMutablePointer<__CLPK_integer>!,
22+
_ __ipiv: UnsafeMutablePointer<__CLPK_integer>!,
23+
_ __b: UnsafeMutablePointer<__CLPK_doublereal>!,
24+
_ __ldb: UnsafeMutablePointer<__CLPK_integer>!,
25+
_ __info: UnsafeMutablePointer<__CLPK_integer>!
26+
)
27+
28+
/// DGESVD computes the singular value decomposition (SVD) of a real
29+
/// M-by-N matrix A, optionally computing the left and/or right singular
30+
/// vectors. The SVD is written
31+
///
32+
/// `A = U * SIGMA * transpose(V)`
33+
///
34+
/// where SIGMA is an M-by-N matrix which is zero except for its
35+
/// min(m,n) diagonal elements, U is an M-by-M orthogonal matrix, and
36+
/// V is an N-by-N orthogonal matrix. The diagonal elements of SIGMA
37+
/// are the singular values of A; they are real and non-negative, and
38+
/// are returned in descending order. The first min(m,n) columns of
39+
/// U and V are the left and right singular vectors of A.
40+
///
41+
/// Note: that the routine returns V**T, not V.
42+
@_silgen_name("dgesvd_")
43+
public func dgesvd_(
44+
_ __jobu: UnsafeMutablePointer<CChar>!,
45+
_ __jobvt: UnsafeMutablePointer<CChar>!,
46+
_ __m: UnsafeMutablePointer<__CLPK_integer>!,
47+
_ __n: UnsafeMutablePointer<__CLPK_integer>!,
48+
_ __a: UnsafeMutablePointer<__CLPK_doublereal>!,
49+
_ __lda: UnsafeMutablePointer<__CLPK_integer>!,
50+
_ __s: UnsafeMutablePointer<__CLPK_doublereal>!,
51+
_ __u: UnsafeMutablePointer<__CLPK_doublereal>!,
52+
_ __ldu: UnsafeMutablePointer<__CLPK_integer>!,
53+
_ __vt: UnsafeMutablePointer<__CLPK_doublereal>!,
54+
_ __ldvt: UnsafeMutablePointer<__CLPK_integer>!,
55+
_ __work: UnsafeMutablePointer<__CLPK_doublereal>!,
56+
_ __lwork: UnsafeMutablePointer<__CLPK_integer>!,
57+
_ __info: UnsafeMutablePointer<__CLPK_integer>!
58+
)
59+
#endif // ACCELERATE_NEW_LAPACK
60+
#endif // canImport(Accelerate)
Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,30 @@
11
#if canImport(Accelerate)
22
@_exported import Accelerate
33
#else
4-
import CBLAS
54
import CLAPACK
65

6+
#if ACCELERATE_NEW_LAPACK
7+
/// DGESV computes the solution to a real system of linear equations
8+
/// A * X = B,
9+
/// where A is an N-by-N matrix and X and B are N-by-NRHS matrices.
10+
/// The LU decomposition with partial pivoting and row interchanges is
11+
/// used to factor A as
12+
/// A = P * L * U,
13+
/// where P is a permutation matrix, L is unit lower triangular, and U is
14+
/// upper triangular. The factored form of A is then used to solve the
15+
/// system of equations A * X = B.
16+
@_silgen_name("dgesv_")
17+
public func dgesv_(
18+
_ __n: UnsafeMutablePointer<__LAPACK_int>!,
19+
_ __nrhs: UnsafeMutablePointer<__LAPACK_int>!,
20+
_ __a: UnsafeMutablePointer<__LAPACK_doublereal>!,
21+
_ __lda: UnsafeMutablePointer<__LAPACK_int>!,
22+
_ __ipiv: UnsafeMutablePointer<__LAPACK_int>!,
23+
_ __b: UnsafeMutablePointer<__LAPACK_doublereal>!,
24+
_ __ldb: UnsafeMutablePointer<__LAPACK_int>!,
25+
_ __info: UnsafeMutablePointer<__LAPACK_int>!
26+
)
27+
728
/// DGESVD computes the singular value decomposition (SVD) of a real
829
/// M-by-N matrix A, optionally computing the left and/or right singular
930
/// vectors. The SVD is written
@@ -18,7 +39,6 @@ import CLAPACK
1839
/// U and V are the left and right singular vectors of A.
1940
///
2041
/// Note: that the routine returns V**T, not V.
21-
#if ACCELERATE_NEW_LAPACK
2242
@_silgen_name("dgesvd_")
2343
public func dgesvd_(
2444
_ __jobu: UnsafeMutablePointer<CChar>!,
@@ -36,23 +56,5 @@ public func dgesvd_(
3656
_ __lwork: UnsafeMutablePointer<__LAPACK_int>!,
3757
_ __info: UnsafeMutablePointer<__LAPACK_int>!
3858
)
39-
#else
40-
@_silgen_name("dgesvd_")
41-
public func dgesvd_(
42-
_ __jobu: UnsafeMutablePointer<CChar>!,
43-
_ __jobvt: UnsafeMutablePointer<CChar>!,
44-
_ __m: UnsafeMutablePointer<__CLPK_integer>!,
45-
_ __n: UnsafeMutablePointer<__CLPK_integer>!,
46-
_ __a: UnsafeMutablePointer<__CLPK_doublereal>!,
47-
_ __lda: UnsafeMutablePointer<__CLPK_integer>!,
48-
_ __s: UnsafeMutablePointer<__CLPK_doublereal>!,
49-
_ __u: UnsafeMutablePointer<__CLPK_doublereal>!,
50-
_ __ldu: UnsafeMutablePointer<__CLPK_integer>!,
51-
_ __vt: UnsafeMutablePointer<__CLPK_doublereal>!,
52-
_ __ldvt: UnsafeMutablePointer<__CLPK_integer>!,
53-
_ __work: UnsafeMutablePointer<__CLPK_doublereal>!,
54-
_ __lwork: UnsafeMutablePointer<__CLPK_integer>!,
55-
_ __info: UnsafeMutablePointer<__CLPK_integer>!
56-
)
5759
#endif // ACCELERATE_NEW_LAPACK
5860
#endif // canImport(Accelerate)
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#if canImport(Accelerate)
2+
@_exported import Accelerate
3+
#else
4+
5+
/// Calculates the double-precision maximum value of a vector.
6+
/// - Parameters:
7+
/// - __A: The vector to be examined.
8+
/// - __I: The stride of the vector.
9+
/// - __C: The maximum value.
10+
/// - __N: The number of elements in the vector.
11+
public func vDSP_maxvD(
12+
_ __A: UnsafePointer<Double>,
13+
_ __I: vDSP_Stride,
14+
_ __C: UnsafeMutablePointer<Double>,
15+
_ __N: vDSP_Length
16+
) {
17+
__C.pointee = -Double.infinity
18+
guard __N > 0 else { return }
19+
for i in 1..<__N {
20+
__C.pointee = max(__C.pointee, __A[Int(i) * __I])
21+
}
22+
}
23+
24+
/// Calculates the double-precision minimum value of a vector.
25+
/// - Parameters:
26+
/// - __A: The vector to be examined.
27+
/// - __I: The stride of the vector.
28+
/// - __C: The minimum value.
29+
/// - __N: The number of elements in the vector.
30+
public func vDSP_minvD(
31+
_ __A: UnsafePointer<Double>,
32+
_ __I: vDSP_Stride,
33+
_ __C: UnsafeMutablePointer<Double>,
34+
_ __N: vDSP_Length
35+
) {
36+
__C.pointee = Double.infinity
37+
guard __N > 0 else { return }
38+
for i in 1..<__N {
39+
__C.pointee = min(__C.pointee, __A[Int(i) * __I])
40+
}
41+
}
42+
#endif
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
#if !ACCELERATE_NEW_LAPACK
2+
import AccelerateLinux
3+
import Foundation
4+
import Testing
5+
6+
@Suite("CPLK Tests")
7+
struct CPLKTests {
8+
// https://www.intel.com/content/www/us/en/docs/onemkl/code-samples-lapack/2022-1/dgesv-example-c.html
9+
@Test("dgesv_")
10+
func test_dgesv_() {
11+
let N = 5
12+
let NRHS = 3
13+
let LDA = N
14+
let LDB = N
15+
16+
var n = __CLPK_integer(N)
17+
var nrhs = __CLPK_integer(NRHS)
18+
var lda = __CLPK_integer(LDA)
19+
var ldb = __CLPK_integer(LDB)
20+
var info = __CLPK_integer(0)
21+
22+
var ipiv = [__CLPK_integer](repeating: 0, count: Int(N))
23+
var a: [__CLPK_doublereal] = [
24+
6.80, -2.11, 5.66, 5.97, 8.23, -6.05, -3.30,
25+
5.36, -4.44, 1.08, -0.45, 2.58, -2.70, 0.27,
26+
9.04, 8.32, 2.71, 4.35, -7.17, 2.14, -9.67,
27+
-5.14, -7.26, 6.08, -6.87,
28+
]
29+
30+
var b: [__CLPK_doublereal] = [
31+
4.02, 6.19, -8.22, -7.57, -3.03, -1.56, 4.00, -8.67,
32+
1.75, 2.86, 9.81, -4.09, -4.57, -8.61, 8.99,
33+
]
34+
35+
dgesv_(&n, &nrhs, &a, &lda, &ipiv, &b, &ldb, &info)
36+
37+
if info > 0 {
38+
Issue.record(
39+
"The diagonal element of the triangular factor of A, U, is zero. The factorization has been completed, but the factor U is exactly singular, so the solution could not be computed."
40+
)
41+
return
42+
}
43+
44+
#expect(
45+
b.map {
46+
($0 * pow(10, 2)).rounded() / pow(10, 2)
47+
} == [-0.80, -0.70, 0.59, 1.32, 0.57, -0.39, -0.55, 0.84, -0.10, 0.11, 0.96, 0.22, 1.90, 5.36, 4.04]
48+
)
49+
}
50+
51+
// https://www.intel.com/content/www/us/en/docs/onemkl/code-samples-lapack/2022-1/dgesvd-example-c.html
52+
@Test("dgesvd_")
53+
func test_dgesvd_() {
54+
let M = 6
55+
let N = 5
56+
let LDA = M
57+
let LDU = M
58+
let LDVT = N
59+
60+
var m = __CLPK_integer(M)
61+
var n = __CLPK_integer(N)
62+
var lda = __CLPK_integer(LDA)
63+
var ldu = __CLPK_integer(LDU)
64+
var ldvt = __CLPK_integer(LDVT)
65+
var info = __CLPK_integer(0)
66+
var lwork = __CLPK_integer(-1)
67+
var wkopt = __CLPK_doublereal(0)
68+
69+
var a: [__CLPK_doublereal] = [
70+
8.79, 6.11, -9.15, 9.57, -3.49, 9.84, 9.93, 6.91,
71+
-7.93, 1.64, 4.02, 0.15, 9.83, 5.04, 4.86, 8.83,
72+
9.80, -8.99, 5.45, -0.27, 4.85, 0.74, 10.00, -6.02,
73+
3.16, 7.98, 3.01, 5.80, 4.27, -5.31,
74+
]
75+
76+
var s = [__CLPK_doublereal](repeating: 0, count: Int(min(m, n)))
77+
var u = [__CLPK_doublereal](repeating: 0, count: Int(ldu * m))
78+
var vt = [__CLPK_doublereal](repeating: 0, count: Int(ldvt * n))
79+
80+
let jobu = "A"
81+
let jobvt = "A"
82+
83+
jobu.withCString { jobuPtr in
84+
jobvt.withCString { jobvtPtr in
85+
let mutableJobuPtr = UnsafeMutablePointer(mutating: jobuPtr)
86+
let mutableJobvtPtr = UnsafeMutablePointer(mutating: jobvtPtr)
87+
88+
dgesvd_(mutableJobuPtr, mutableJobvtPtr, &m, &n, &a, &lda, &s, &u, &ldu, &vt, &ldvt, &wkopt, &lwork, &info)
89+
}
90+
}
91+
92+
lwork = __CLPK_integer(wkopt)
93+
var work = [__CLPK_doublereal](repeating: 0, count: Int(lwork))
94+
95+
jobu.withCString { jobuPtr in
96+
jobvt.withCString { jobvtPtr in
97+
let mutableJobuPtr = UnsafeMutablePointer(mutating: jobuPtr)
98+
let mutableJobvtPtr = UnsafeMutablePointer(mutating: jobvtPtr)
99+
100+
dgesvd_(mutableJobuPtr, mutableJobvtPtr, &m, &n, &a, &lda, &s, &u, &ldu, &vt, &ldvt, &work, &lwork, &info)
101+
}
102+
}
103+
104+
if info > 0 {
105+
Issue.record("The algorithm computing SVD failed to converge.")
106+
return
107+
}
108+
109+
#expect(
110+
s.map {
111+
($0 * pow(10, 2)).rounded() / pow(10, 2)
112+
} == [27.47, 22.64, 8.56, 5.99, 2.01]
113+
)
114+
115+
// Note: if we care about orthogonality of U and V we should check those too
116+
}
117+
}
118+
#endif

0 commit comments

Comments
 (0)