Skip to content

Commit

Permalink
incorporate astminer and normalized laplacian
Browse files Browse the repository at this point in the history
  • Loading branch information
breandan committed Aug 25, 2020
1 parent 590defa commit 5a4cd55
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 41 deletions.
4 changes: 3 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ repositories {
mavenCentral()
maven("https://jitpack.io")
jcenter()
// maven("https://dl.bintray.com/mipt-npm/dev")
maven("https://dl.bintray.com/egor-bogomolov/astminer/")
maven("https://dl.bintray.com/mipt-npm/dev")
}

dependencies {
Expand All @@ -27,6 +28,7 @@ dependencies {
implementation("com.github.kwebio:kweb-core:0.7.20")
implementation("org.slf4j:slf4j-simple:1.7.30")
implementation("com.github.breandan:tensor:master-SNAPSHOT")
implementation("io.github.vovak.astminer:astminer:0.5")

// val kmathVersion by extra { "0.1.4-dev-8" }
// implementation("scientifik:kmath-core:$kmathVersion")
Expand Down
56 changes: 37 additions & 19 deletions src/main/kotlin/edu/mcgill/kaliningraph/Graph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import edu.mcgill.kaliningraph.typefamily.*
import guru.nidi.graphviz.attribute.*
import guru.nidi.graphviz.attribute.Arrow.NORMAL
import guru.nidi.graphviz.attribute.Color.*
import guru.nidi.graphviz.attribute.GraphAttr.CONCENTRATE
import guru.nidi.graphviz.attribute.Style.lineWidth
import guru.nidi.graphviz.graph
import guru.nidi.graphviz.model.*
Expand All @@ -14,7 +15,7 @@ import org.apache.commons.rng.simple.RandomSource.JDK
import org.ejml.data.DMatrixSparseCSC
import org.ejml.data.DMatrixSparseTriplet
import org.ejml.kotlin.*
import kotlin.math.tanh
import kotlin.math.*
import kotlin.random.Random
import kotlin.reflect.KProperty

Expand Down Expand Up @@ -49,31 +50,45 @@ constructor(override val vertices: Set<V> = setOf())

// Degree matrix
val D: DMatrixSparseCSC by lazy {
DMatrixSparseTriplet(size, size, totalEdges).also { degMat ->
vertices.forEach { v -> degMat[v, v] = v.neighbors.size.toDouble() }
}.toCSC()
elwise { v, _ ->
this[v, v] = v.neighbors.size.toDouble()
}
}

// Adjacency matrix
val A: DMatrixSparseCSC by lazy {
val A: DMatrixSparseCSC by lazy { elwise { v, n -> this[v, n] = 1.0 } }

val ANORM: DMatrixSparseCSC by lazy {
elwise { v, n ->
this[v, n] = 1.0 / (sqrt(v.outgoing.size.toDouble()) * sqrt(n.outgoing.size.toDouble()))
}
}

inline fun elwise(crossinline lf: DMatrixSparseTriplet.(V, V) -> Unit) =
DMatrixSparseTriplet(size, size, totalEdges).also { adjMat ->
vertices.forEach { v -> v.neighbors.forEach { n -> adjMat[v, n] = 1.0 } }
forEach { v -> v.neighbors.forEach { n -> adjMat.lf(v, n) } }
}.toCSC()
}

// Laplacian matrix
val L by lazy { D - A }
val I by lazy { elwise { v, n -> if(v == n) this[v, n] = 1.0 } }

val LSYMNORM by lazy { I - ANORM }

val featureDim by lazy { size } //min(size, 10) }

val H0 by lazy {
DMatrixSparseTriplet(size, size, totalEdges).also { featMat ->
vertices.encode().forEachIndexed { i, row ->
row.forEachIndexed { j, it -> if(it != 0.0) featMat[i, j] = it }
}
}.toCSC()
// LSYMNORM.power(20) { a, b -> a * b }
L.power(20) { a, b -> a * b }
}

// TODO: implement one-hot encoder for finite alphabet
fun Set<V>.encode() = Array(size) { i -> Array(size) { j -> if(i == j) 1.0 else 0.0 } }
fun Set<V>.encode() = Array(size) { i ->
DoubleArray(featureDim) { j ->
// Random.nextDouble()
if(i == j) 1.0 else 0.0
}
}

val degMap by lazy { vertices.map { it to it.neighbors.size }.toMap() }
operator fun DMatrixSparseTriplet.get(n0: V, n1: V) = this[index[n0]!!, index[n1]!!]
Expand Down Expand Up @@ -118,18 +133,21 @@ constructor(override val vertices: Set<V> = setOf())
// H^t := σ(AH^(t-1)W^(t) + H^(t-1)W^t)
@Suppress("NonAsciiCharacters")
tailrec fun mpnn(
t: Int = 10,
t: Int = 20,
// Matrix of node representations ℝ^{|V|xd}
H: DMatrixSparseCSC = H0,
W: DMatrixSparseCSC = randomMatrix(size, size) { Random.nextDouble() },
b: DMatrixSparseCSC = randomMatrix(size, size) { Random.nextDouble() },
// (Trainable) weight matrix
W: DMatrixSparseCSC = randomMatrix(H0.numCols, H0.numCols) { Random.nextDouble() },
// Bias term
b: DMatrixSparseCSC = randomMatrix(size, H0.numCols) { Random.nextDouble() },
// Nonlinearity
σ: (DMatrixSparseCSC) -> DMatrixSparseCSC = { it.elwise { tanh(it) } }
): DMatrixSparseCSC =
if(t == 0) H
else
mpnn(
t = t - 1,
H = σ(A * H * W + H * W + b),
// H = A + A,
W = W,
b = b
)
Expand Down Expand Up @@ -168,10 +186,10 @@ constructor(override val vertices: Set<V> = setOf())
"(" + vertices.joinToString(", ", "{", "}") + ", " +
edgList.map { (v, e) -> "${v.id}${e.target.id}" }.joinToString(", ", "{", "}") + ")"

open fun render(): MutableGraph = graph(directed = true) {
open fun render(): MutableGraph = graph(directed = true, strict = true) {
val color = if (DARKMODE) WHITE else BLACK
edge[color, NORMAL, lineWidth(THICKNESS)]
graph[Rank.dir(Rank.RankDir.LEFT_TO_RIGHT), TRANSPARENT.background(), GraphAttr.margin(0.0), Attributes.attr("compound", "true"), Attributes.attr("nslimit", "20")]
graph[CONCENTRATE, Rank.dir(Rank.RankDir.LEFT_TO_RIGHT), TRANSPARENT.background(), GraphAttr.margin(0.0), Attributes.attr("compound", "true"), Attributes.attr("nslimit", "20")]
node[color, color.font(), Font.config("Helvetica", 20), lineWidth(THICKNESS), Attributes.attr("shape", "Mrecord")]

vertices.forEach { vertex ->
Expand Down
7 changes: 4 additions & 3 deletions src/main/kotlin/edu/mcgill/kaliningraph/HelloKaliningraph.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package edu.mcgill.kaliningraph

import edu.mcgill.kaliningraph.circuits.Notebook
import edu.mcgill.kaliningraph.circuits.CircuitBuilder

fun main() {
println("Hello Kaliningraph!")
Expand Down Expand Up @@ -28,10 +28,11 @@ fun main() {
println("WL3(efgh) = $efgh_wl3")
println("Isomorphic: ${abca.isomorphicTo(efgh)}")

Notebook {
CircuitBuilder {
a = b + c
e = a + d
f = b - h
b = g + 1
}.show()
}//.show()

}
7 changes: 6 additions & 1 deletion src/main/kotlin/edu/mcgill/kaliningraph/LabeledGraph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ class LabeledGraphBuilder {
}
}

// TODO: convert to/from other graph types
open class LabeledGraph(override val vertices: Set<LGVertex> = setOf()):
Graph<LabeledGraph, LabeledEdge, LGVertex>(vertices) {
constructor(vararg vertices: LGVertex): this(vertices.toSet())
Expand All @@ -60,17 +61,21 @@ open class LabeledGraph(override val vertices: Set<LGVertex> = setOf()):

class LGVertex constructor(
id: String = randomString(),
val label: String = "",
var occupied: Boolean = false,
override val edgeMap: (LGVertex) -> Set<LabeledEdge>,
) : Vertex<LabeledGraph, LabeledEdge, LGVertex>(id) {
constructor(id: String? = randomString(), label: String = "", out: Set<LGVertex> = emptySet()) :
this(id = id ?: randomString(), label = label, edgeMap = { s -> out.map { t -> LabeledEdge(s, t) }.toSet() })
constructor(id: String? = randomString(), out: Set<LGVertex> = emptySet()) :
this(id = id ?: randomString(), edgeMap = { s -> out.map { t -> LabeledEdge(s, t) }.toSet() })
constructor(out: Set<LGVertex> = setOf()) : this(randomString(), edgeMap = { s -> out.map { t -> LabeledEdge(s, t) }.toSet() })

override fun render() = super.render().also { if (occupied) it.add(FILLED, RED.fill()) else it.add(BLACK) }
// override fun toString(): String = label
override fun Graph(vertices: Set<LGVertex>) = LabeledGraph(vertices)
override fun Edge(s: LGVertex, t: LGVertex) = LabeledEdge(s, t)
override fun Vertex(newId: String, edgeMap: (LGVertex) -> Set<LabeledEdge>) = LGVertex(newId, occupied, edgeMap)
override fun Vertex(newId: String, edgeMap: (LGVertex) -> Set<LabeledEdge>) = LGVertex(newId, "", occupied, edgeMap)
}

open class LabeledEdge(override val source: LGVertex, override val target: LGVertex, val label: String? = null) :
Expand Down
57 changes: 45 additions & 12 deletions src/main/kotlin/edu/mcgill/kaliningraph/Utils.kt
Original file line number Diff line number Diff line change
@@ -1,23 +1,45 @@
package edu.mcgill.kaliningraph

//import org.hipparchus.distribution.EnumeratedDistribution
//import org.hipparchus.random.RandomDataGenerator
//import org.hipparchus.util.Pair
import guru.nidi.graphviz.engine.Engine
import astminer.common.model.Node
import guru.nidi.graphviz.engine.*
import guru.nidi.graphviz.engine.Engine.DOT
import guru.nidi.graphviz.engine.Format
import guru.nidi.graphviz.engine.Format.SVG
import guru.nidi.graphviz.model.*
import guru.nidi.graphviz.toGraphviz
import org.ejml.data.*
import org.ejml.dense.row.CommonOps_DDRM
import org.ejml.dense.row.DMatrixComponent
import org.ejml.dense.row.*
import org.ejml.ops.ConvertDMatrixStruct
import java.awt.image.BufferedImage
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.*
import java.math.*
import java.util.*
import javax.imageio.ImageIO
import kotlin.system.measureTimeMillis

fun Node.toKGraph() =
LabeledGraphBuilder {
closure(
toVisit = setOf(this@toKGraph),
successors = { flatMap { setOfNotNull(it.getParent()) + it.getChildren() }.toSet() }
).forEach { parent ->
getChildren().forEach { child ->
LGVertex(id = parent.toString(), label = parent.getToken()) - LGVertex(id = child.toString(), label = child.getToken())
LGVertex(id = child.toString(), label = child.getToken()) - LGVertex(id = parent.toString(), label = parent.getToken())
}
}
}

tailrec fun <T> closure(
toVisit: Set<T> = emptySet(),
visited: Set<T> = emptySet(),
successors: Set<T>.() -> Set<T>
): Set<T> =
if (toVisit.isEmpty()) visited
else closure(
toVisit = toVisit.successors() - visited,
visited = visited + toVisit,
successors = successors
)

val THICKNESS = 4
val DARKMODE = false
Expand Down Expand Up @@ -65,6 +87,17 @@ fun DMatrixSparseCSC.elwise(op: (Double) -> Double) =
fun randomMatrix(rows: Int, cols: Int, rand: () -> Double) =
Array(rows) { Array(cols) { rand() }.toDoubleArray() }.toEJMLSparse()

fun Array<DoubleArray>.toEJMLSparse() = DMatrixSparseCSC(size, size, sumBy { it.count { it == 0.0 } })
.also { s -> for (i in 0 until size) for (j in 0 until size) this[i][j].let { if (0 < it) s[i, j] = it } }
fun Array<DoubleArray>.toEJMLDense() = DMatrixRMaj(this)
fun Array<DoubleArray>.toEJMLSparse() = DMatrixSparseCSC(size, this[0].size, sumBy { it.count { it == 0.0 } })
.also { s -> for (i in indices) for (j in this[0].indices) this[i][j].let { if (0 < it) s[i, j] = it } }
fun Array<DoubleArray>.toEJMLDense() = DMatrixRMaj(this)

fun Double.round(precision: Int = 10) = BigDecimal(this, MathContext(precision)).toDouble()

fun Array<DoubleArray>.round(precision: Int = 3): Array<DoubleArray> =
map { it.map { it.round(precision) }.toDoubleArray() }.toTypedArray()

fun <T> powBench(constructor: T, matmul: (T, T) -> T): Long =
measureTimeMillis { constructor.power(100, matmul) }

fun <T> T.power(exp: Int, matmul: (T, T) -> T) =
(0..exp).fold(this) { acc, i -> matmul(acc, this) }
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import guru.nidi.graphviz.attribute.Label
import kotlin.reflect.KProperty

// Mutable environment with support for variable overwriting/reassignment
class Notebook {
class CircuitBuilder {
var graph = ComputationGraph()

var a by Var(); var b by Var(); var c by Var(); var d by Var()
Expand All @@ -25,8 +25,8 @@ class Notebook {
op(wrap(left), wrap(right))

companion object {
operator fun invoke(builder: Notebook.() -> Unit) =
Notebook().also { it.builder() }.graph
operator fun invoke(builder: CircuitBuilder.() -> Unit) =
CircuitBuilder().also { it.builder() }.graph
}
}

Expand Down Expand Up @@ -88,7 +88,7 @@ open class Gate constructor(
Gate(newId, Monad.id, edgeMap)

override operator fun getValue(a: Any?, prop: KProperty<*>): Gate = Gate(prop.name)
open operator fun setValue(builder: Notebook, prop: KProperty<*>, value: Gate) {
open operator fun setValue(builder: CircuitBuilder, prop: KProperty<*>, value: Gate) {
builder.graph += Gate(prop.name, Gate(`=`, value)).graph
}
}
Expand Down Expand Up @@ -121,7 +121,7 @@ open class UnlabeledEdge(override val source: Gate, override val target: Gate):
}

fun main() {
Notebook {
CircuitBuilder {
val funA by def(a, b, c) { a + b + c }
j = funA(3, 2, 1)
j = b * c
Expand Down

0 comments on commit 5a4cd55

Please sign in to comment.