@@ -48,29 +48,33 @@ constructor(override val vertices: Set<V> = setOf())
48
48
val edges: Set <E > by lazy { edgMap.values.flatten().toSet() }
49
49
50
50
// Degree matrix
51
- val D : SpsMat by lazy { vwise { v, _ -> v .neighbors.size.toDouble() } }
51
+ val D : SpsMat by lazy { elwise(size) { i -> this [i] .neighbors.size.toDouble() } }
52
52
53
53
// Adjacency matrix
54
54
val A : SpsMat by lazy { vwise { v, n -> 1.0 } }
55
55
val A_AUG : SpsMat by lazy { A + A .transpose() + I }
56
56
57
- // Normalized adjacency
58
- val ANORM : SpsMat by lazy {
57
+ // Symmetric normalized adjacency
58
+ val ASYMNORM : SpsMat by lazy {
59
59
vwise { v, n -> 1.0 / (sqrt(v.degree.toDouble()) * sqrt(n.degree.toDouble())) }
60
60
}
61
61
62
62
// Laplacian matrix
63
63
val L : SpsMat by lazy { D - A }
64
- val I : SpsMat by lazy { elwise(size) { v, n -> if (v == n) 1.0 else null } }
65
- val LSYMNORM : SpsMat by lazy { I - ANORM }
64
+ val I : SpsMat by lazy { elwise(size) { i -> 1.0 } }
65
+ // Symmetric normalized Laplacian
66
+ val LSYMNORM : SpsMat by lazy { I - ASYMNORM }
67
+
68
+ val ENCODED : SpsMat by lazy { vertices.map { it.encode() }.toTypedArray().toEJMLSparse() }
69
+
70
+ // TODO: Implement APSP distance matrix using algebraic Floyd-Warshall
71
+ // https://doi.org/10.1137/1.9780898719918.ch5
66
72
67
73
inline fun vwise (crossinline lf : Graph <G , E , V >.(V , V ) -> Double? ): SpsMat =
68
74
elwise(size) { i, j ->
69
75
(this [i] to this [j]).let { (v, n) -> if (n in v.neighbors) lf(v, n) else null }
70
76
}
71
77
72
- fun encode (): SpsMat = vertices.map { it.encode() }.toTypedArray().toEJMLSparse()
73
-
74
78
val degMap: Map <V , Int > by lazy { vertices.map { it to it.neighbors.size }.toMap() }
75
79
operator fun SpsMat.get (n0 : V , n1 : V ) = this [index[n0]!! , index[n1]!! ]
76
80
operator fun SpsMat.set (n0 : V , n1 : V , value : Double ) {
@@ -88,6 +92,7 @@ constructor(override val vertices: Set<V> = setOf())
88
92
89
93
operator fun minus (graph : G ): G = new(vertices - graph.vertices)
90
94
95
+ // TODO: Reimplement using matrix transpose
91
96
fun reversed (): G = new(
92
97
vertices.map { it to setOf<E >() }.toMap() +
93
98
vertices.flatMap { src ->
@@ -96,14 +101,13 @@ constructor(override val vertices: Set<V> = setOf())
96
101
)
97
102
98
103
val histogram: Map <V , Int > by lazy { aggregateBy { it.size } }
99
- val labelFunc: (V ) -> Int = { v: V -> histogram[v]!! }
100
104
101
105
/* (𝟙 + A)ⁿ[a, b] counts the number of walks between vertices a, b of length n
102
106
* Let i be the smallest natural number such that (𝟙 + A)ⁱ has no zeros.
103
107
* Fact: i is the length of the longest shortest path in G.
104
108
*
105
109
* TODO: implement O(M(n)log(n)) version based on Booth & Lipton (1981)
106
- * https://link.springer.com/content/pdf/ 10.1007/BF00264532.pdf#page=5
110
+ * https://doi.org/ 10.1007/BF00264532
107
111
*/
108
112
109
113
tailrec fun diameter (d : Int = 1, walks : SpsMat = A_AUG ): Int =
@@ -134,16 +138,16 @@ constructor(override val vertices: Set<V> = setOf())
134
138
// Message passing rounds
135
139
t : Int = diameter() * 10,
136
140
// Matrix of node representations ℝ^{|V|xd}
137
- H : SpsMat = encode() ,
138
- // (Trainable) weight matrix
141
+ H : SpsMat = ENCODED ,
142
+ // (Trainable) weight matrix ℝ^{dxd}
139
143
W : SpsMat = randomMatrix(H .numCols),
140
- // Bias term
144
+ // Bias term ℝ^{dxd}
141
145
b : SpsMat = randomMatrix(size, H .numCols),
142
- // Nonlinearity
146
+ // Nonlinearity ℝ^{*} -> ℝ^{*}
143
147
σ: (SpsMat ) -> SpsMat = ACT_TANH ,
144
- // Layer normalization
148
+ // Layer normalization ℝ^{*} -> ℝ^{*}
145
149
z : (SpsMat ) -> SpsMat = NORM_AVG ,
146
- // Message
150
+ // Message ℝ^{*} -> ℝ^{*}
147
151
m : Graph <G , E , V >.(SpsMat ) -> SpsMat = { σ(z(A * it * W + it * W + b)) }
148
152
): SpsMat = if (t == 0 ) H else gnn(t = t - 1 , H = m(H ), W = W , b = b)
149
153
@@ -180,7 +184,7 @@ constructor(override val vertices: Set<V> = setOf())
180
184
// https://web.engr.oregonstate.edu/~erwig/papers/InductiveGraphs_JFP01.pdf#page=6
181
185
override fun toString () =
182
186
" (" + vertices.joinToString(" , " , " {" , " }" ) + " , " +
183
- edgList.map { (v, e) -> " ${v.id} →${e.target.id} " }.joinToString( " , " , " { " , " } " ) + " )"
187
+ edgList.joinToString( " , " , " { " , " } " ) { (v, e) -> " ${v.id} →${e.target.id} " } + " )"
184
188
185
189
open fun render (): MutableGraph = graph(directed = true , strict = true ) {
186
190
val color = if (DARKMODE ) WHITE else BLACK
@@ -220,7 +224,7 @@ constructor(val id: String) : IVertex<G, E, V> {
220
224
override val graph: G by lazy { Graph (neighbors(- 1 )) }
221
225
abstract val edgeMap: (V ) -> Collection <E > // Allows self-loops by passing this
222
226
override val outgoing by lazy { edgeMap(this as V ).toSet() }
223
- override val incoming by lazy { graph.reversed().edgMap.toMap() [this ]!! }
227
+ override val incoming by lazy { graph.reversed().edgMap[this ] ? : emptySet() }
224
228
open val neighbors by lazy { outgoing.map { it.target }.toSet() }
225
229
open val degree by lazy { neighbors.size }
226
230
0 commit comments