1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one or more
3
+ * contributor license agreements. See the NOTICE file distributed with
4
+ * this work for additional information regarding copyright ownership.
5
+ * The ASF licenses this file to You under the Apache License, Version 2.0
6
+ * (the "License"); you may not use this file except in compliance with
7
+ * the License. You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ package org .apache .spark .mllib .linalg
19
+
20
+ import org .apache .spark .annotation .Experimental
21
+ import breeze .linalg .{DenseMatrix => BDM , DenseVector => BDV }
22
+ import org .netlib .util .{intW , doubleW }
23
+ import com .github .fommil .netlib .ARPACK
24
+
25
+ /**
26
+ * :: Experimental ::
27
+ * Represents eigenvalue decomposition factors.
28
+ */
29
+ @ Experimental
30
+ case class EigenValueDecomposition [VType ](s : Vector , V : VType )
31
+
32
+ object EigenValueDecomposition {
33
+ /**
34
+ * Compute the leading k eigenvalues and eigenvectors on a symmetric square matrix using ARPACK.
35
+ * The caller needs to ensure that the input matrix is real symmetric. This function requires
36
+ * memory for `n*(4*k+4)` doubles.
37
+ *
38
+ * @param mul a function that multiplies the symmetric matrix with a Vector.
39
+ * @param n dimension of the square matrix (maximum Int.MaxValue).
40
+ * @param k number of leading eigenvalues required.
41
+ * @param tol tolerance of the eigs computation.
42
+ * @return a dense vector of eigenvalues in descending order and a dense matrix of eigenvectors
43
+ * (columns of the matrix). The number of computed eigenvalues might be smaller than k.
44
+ */
45
+ private [mllib] def symmetricEigs (mul : Vector => Vector , n : Int , k : Int , tol : Double )
46
+ : (BDV [Double ], BDM [Double ]) = {
47
+ require(n > k, s " Number of required eigenvalues $k must be smaller than matrix dimension $n" )
48
+
49
+ val arpack = ARPACK .getInstance()
50
+
51
+ val tolW = new doubleW(tol)
52
+ val nev = new intW(k)
53
+ val ncv = scala.math.min(2 * k,n)
54
+
55
+ val bmat = " I"
56
+ val which = " LM"
57
+
58
+ var iparam = new Array [Int ](11 )
59
+ iparam(0 ) = 1
60
+ iparam(2 ) = 300
61
+ iparam(6 ) = 1
62
+
63
+ var ido = new intW(0 )
64
+ var info = new intW(0 )
65
+ var resid : Array [Double ] = new Array [Double ](n)
66
+ var v = new Array [Double ](n* ncv)
67
+ var workd = new Array [Double ](3 * n)
68
+ var workl = new Array [Double ](ncv* (ncv+ 8 ))
69
+ var ipntr = new Array [Int ](11 )
70
+
71
+ // first call to ARPACK
72
+ arpack.dsaupd(ido, bmat, n, which, nev.`val`, tolW, resid, ncv, v, n, iparam, ipntr, workd,
73
+ workl, workl.length, info)
74
+
75
+ val w = BDV (workd)
76
+
77
+ while (ido.`val` != 99 ) {
78
+ if (ido.`val` != - 1 && ido.`val` != 1 )
79
+ throw new IllegalStateException (" ARPACK returns ido = " + ido.`val`)
80
+ // multiply working vector with the matrix
81
+ val inputOffset = ipntr(0 ) - 1
82
+ val outputOffset = ipntr(1 ) - 1
83
+ val x = w(inputOffset until inputOffset + n)
84
+ val y = w(outputOffset until outputOffset + n)
85
+ y := BDV (mul(Vectors .fromBreeze(x)).toArray)
86
+ // call ARPACK
87
+ arpack.dsaupd(ido, bmat, n, which, nev.`val`, tolW, resid, ncv, v, n, iparam, ipntr,
88
+ workd, workl, workl.length, info)
89
+ }
90
+
91
+ if (info.`val` != 0 )
92
+ throw new IllegalStateException (" ARPACK returns non-zero info = " + info.`val`)
93
+
94
+ val d = new Array [Double ](nev.`val`)
95
+ val select = new Array [Boolean ](ncv)
96
+ val z = java.util.Arrays .copyOfRange(v, 0 , nev.`val` * n)
97
+
98
+ arpack.dseupd(true , " A" , select, d, z, n, 0.0 , bmat, n, which, nev, tol, resid, ncv, v, n,
99
+ iparam, ipntr, workd, workl, workl.length, info)
100
+
101
+ val computed = iparam(4 )
102
+
103
+ val s = BDV (d)(0 until computed)
104
+ val U = new BDM (n, computed, z)
105
+
106
+ val sortedEigenValuesWithIndex = s.toArray.zipWithIndex.sortBy(- 1 * _._1).zipWithIndex
107
+
108
+ val sorteds = BDV (sortedEigenValuesWithIndex.map(_._1._1))
109
+ val sortedU = BDM .zeros[Double ](n, computed)
110
+
111
+ // copy eigenvectors in descending order of eigenvalues
112
+ sortedEigenValuesWithIndex.map{
113
+ r => {
114
+ sortedU(:: , r._2) := U (:: , r._1._2)
115
+ }
116
+ }
117
+
118
+ (sorteds, sortedU)
119
+ }
120
+ }
0 commit comments