Skip to content

Commit

Permalink
remove string constructor
Browse files Browse the repository at this point in the history
  • Loading branch information
breandan committed Nov 5, 2021
1 parent 758e7a0 commit c047113
Show file tree
Hide file tree
Showing 9 changed files with 56 additions and 42 deletions.
10 changes: 6 additions & 4 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ version = "0.1.9"

repositories {
mavenCentral()
maven("https://clojars.org/repo")
maven("https://jitpack.io")
}

java {
Expand All @@ -61,6 +61,11 @@ dependencies {
implementation(platform(kotlin("bom")))
implementation(kotlin("stdlib"))
implementation(kotlin("reflect"))
// Used to cache graph lookups
implementation("com.github.ben-manes.caffeine:caffeine:3.0.4")
// implementation("io.github.reactivecircus.cache4k:cache4k:0.3.0")
// implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2")
// implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.5.2")

// Property-based testing
val kotestVersion = "5.0.0.M3"
Expand All @@ -75,9 +80,6 @@ dependencies {
api("org.graalvm.js:js:21.3.0")
api("guru.nidi:graphviz-kotlin:0.18.1")

// Used to cache graph lookups
implementation("com.github.ben-manes.caffeine:caffeine:3.0.4")

testImplementation("junit", "junit", "4.13.2")
testCompileOnly("org.jetbrains:annotations:22.0.0")
testImplementation("org.slf4j:slf4j-simple:1.7.32")
Expand Down
17 changes: 13 additions & 4 deletions src/main/kotlin/ai/hypergraph/kaliningraph/LabeledGraph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import ai.hypergraph.kaliningraph.matrix.BooleanMatrix
import ai.hypergraph.kaliningraph.typefamily.*
import guru.nidi.graphviz.attribute.Color.*
import guru.nidi.graphviz.attribute.Style.FILLED
import kotlin.reflect.KProperty

/**
* DSL for labeled graphs - just enumerate paths. Duplicates will be merged.
Expand All @@ -24,7 +25,7 @@ class LGBuilder {
operator fun String.minus(v: String): LGVertex = LGVertex(this) - LGVertex(v)

operator fun LGVertex.plus(edge: LabeledEdge) =
V(id) { outgoing + edge }.also { mutGraph += it.graph }
V(this) { outgoing + edge }.also { mutGraph += it.graph }

operator fun LGVertex.plus(vertex: LGVertex) =
(graph + vertex.graph).also { mutGraph += it }
Expand All @@ -44,11 +45,16 @@ open class LabeledGraph(override val vertices: Set<LGVertex> = setOf()):
this(LGBuilder().also { it.builder() }.mutGraph)
constructor(graph: String): this(
graph.split(" ").fold(LabeledGraph()) { acc, it ->
acc + acc.G(*it.toList().zipWithNext().toTypedArray())
acc + P(*it.toList().zipWithNext().map { (a, b) -> a.toString() to b.toString() }.toTypedArray())
}
)

companion object: LabeledGraph()
companion object: LabeledGraph() {
fun P(
vararg adjList: Pair<String, String>,
p2v: (Pair<String, String>) -> LGVertex = { (s, t) -> LGVertex(s, setOf(LGVertex(t))) }
) = LabeledGraph(adjList.map { p2v(it) }
.fold(LabeledGraph()) { acc, v -> acc + v.graph })
}

var accumuator = mutableSetOf<String>()
var description = ""
Expand Down Expand Up @@ -81,8 +87,11 @@ class LGVertex constructor(
this(randomString(), edgeMap = { s -> out.map { t -> LabeledEdge(s, t) }.toSet() })
constructor(label: String, out: Set<LGVertex> = emptySet()) :
this(label = label, edgeMap = { s -> out.map { t -> LabeledEdge(s, t) }.toSet() })
constructor(lgv: LGVertex, edgeMap: (LGVertex) -> Set<LabeledEdge>) :
this(label = lgv.label, edgeMap = edgeMap)

override fun encode() = label.vectorize()
operator fun getValue(a: Any?, prop: KProperty<*>): LGVertex = LGVertex(prop.name)
override fun render() = super.render().also { if (occupied) it.add(FILLED, RED.fill()) else it.add(BLACK) }
// override fun toString(): String = label
}
Expand Down
7 changes: 4 additions & 3 deletions src/main/kotlin/ai/hypergraph/kaliningraph/TypedGraph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import guru.nidi.graphviz.attribute.Color.*
import guru.nidi.graphviz.attribute.Style

// TODO: convert to/from other graph types
// TODO: should we lift TypedGraph and inherit other concrete graphs or introduce a Sheaf type?
// https://www.jakobhansen.org/publications/gentleintroduction.pdf
open class TypedGraph<T: Any>
constructor(override val vertices: Set<TypedVertex<T>> = setOf()):
Graph<TypedGraph<T>, TypedEdge<T>, TypedVertex<T>>(vertices) {
Expand All @@ -24,14 +26,13 @@ class TypedVertex<T: Any> constructor(
this(t = t, edgeMap = { s -> out.map { t -> TypedEdge<T>(s, t) }.toSet() })
constructor(t: T, edgeMap: (TypedVertex<T>) -> Set<TypedEdge<T>>) :
this(t = t, occupied = false, edgeMap = edgeMap)

constructor(tv: TypedVertex<T>, edgeMap: (TypedVertex<T>) -> Set<TypedEdge<T>>):
this(tv.t, tv.occupied, edgeMap)
override fun encode() = (t?.toString() ?: "").vectorize()
override fun render() = super.render().also {
if (occupied) it.add(Style.FILLED, RED.fill()) else it.add(BLACK)
}

override fun V(newId: String, edgeMap: (TypedVertex<T>) -> Set<TypedEdge<T>>) =
TypedVertex(t, occupied, edgeMap)
}

open class TypedEdge<T: Any>(override val source: TypedVertex<T>, override val target: TypedVertex<T>, val v: String? = null) :
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package ai.hypergraph.kaliningraph.automata

import ai.hypergraph.kaliningraph.*
import ai.hypergraph.kaliningraph.typefamily.*
import kotlin.reflect.KProperty

open class Automaton(override val vertices: Set<State> = setOf(State()))
: Graph<Automaton, Transition, State>(vertices)
Expand All @@ -16,7 +17,9 @@ open class State(
constructor(id: String? = null, out: Set<State> = setOf()) : this(id = id ?: randomString(),
edgeMap = { s -> out.map { t -> Transition(s, t) }.toSet() })

override fun V(newId: String, edgeMap: (State) -> Set<Transition>): State = State(id, edgeMap)
constructor(state: State, edgeMap: (State) -> Set<Transition>): this(state.id, edgeMap)

operator fun getValue(a: Any?, prop: KProperty<*>): State = State(prop.name)
}

class AutomatonBuilder {
Expand All @@ -27,10 +30,10 @@ class AutomatonBuilder {
val i by State(); val j by State(); val k by State(); val l by State()

operator fun State.minus(v: State) =
V(id) { v.outgoing + Transition(v, this) }.also { automaton += it.graph }
V(this) { v.outgoing + Transition(v, this) }.also { automaton += it.graph }

operator fun State.plus(edge: Transition) =
V(id) { outgoing + edge }.also { automaton += it.graph }
V(this) { outgoing + edge }.also { automaton += it.graph }

operator fun State.plus(vertex: State) =
(graph + vertex.graph).also { automaton += it }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ open class Gate(
constructor(id: String = randomString(), op: Op = Ops.id, vararg gates: Gate) :
this(id, op, { s -> gates.toSet().map { t -> UnlabeledEdge(s, t) }.toSet() })
constructor(id: String = randomString(), edgeMap: (Gate) -> Set<UnlabeledEdge>): this(id, Ops.id, edgeMap)
constructor(gate: Gate, edgeMap: (Gate) -> Set<UnlabeledEdge>) :
this(id = gate.id, edgeMap = edgeMap)

companion object {
fun wrap(value: Any): Gate = if (value is Gate) value else Gate(value.toString())
Expand All @@ -111,10 +113,8 @@ open class Gate(

override fun G(vertices: Set<Gate>) = ComputationGraph(vertices)
override fun E(s: Gate, t: Gate) = UnlabeledEdge(s, t)
override fun V(newId: String, edgeMap: (Gate) -> Set<UnlabeledEdge>) =
Gate(newId, Ops.id, edgeMap)

override operator fun getValue(a: Any?, prop: KProperty<*>): Gate = Gate(prop.name)
operator fun getValue(a: Any?, prop: KProperty<*>): Gate = Gate(prop.name)
open operator fun setValue(builder: CircuitBuilder, prop: KProperty<*>, value: Gate) {
builder.graph += Gate(prop.name, Gate(Ops.eql, value)).let {
ComputationGraph(vertices=it.graph/* TODO: Is this double-boxing a problem? */, root = it)
Expand Down
39 changes: 19 additions & 20 deletions src/main/kotlin/ai/hypergraph/kaliningraph/typefamily/IGraph.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,30 +20,22 @@ interface IGF<G: IGraph<G, E, V>, E: IEdge<G, E, V>, V: IVertex<G, E, V>> {

fun E(s: V, t: V): E = E.newInstance(s, t)

fun V(newId: String = "", edgeMap: (V) -> Set<E>): V = V.newInstance(newId, edgeMap)

fun V(old: V, edgeMap: (V) -> Set<E>): V =
// If no default constructor is provided, implementors must override V
try { V(old.id, edgeMap) } catch (e: Exception) { old.V(old, edgeMap) }

fun V(newId: String = "", out: Set<V> = emptySet()): V =
V(newId) { s -> out.map { t -> E(s, t) }.toSet() }
try {
V.newInstance(old, edgeMap)
} catch (e: Exception) {
TODO("IGF subtypes must provide a copy constructor or override this method.")
}

fun G(vararg graphs: G): G = G(graphs.toList())
fun G(vararg vertices: V): G = G(vertices.map { it.graph })

fun <T: Any> G(
vararg adjList: Pair<T, T>,
p2v: (Pair<T, T>) -> V = { (s, t) -> V("$s", setOf(V("$t"))) }
): G = adjList.map { p2v(it) }.fold(G()) { acc, v -> acc + v.graph }

fun V() = gev[2]
fun <T: Any> G(list: List<T> = emptyList()): G = when {
list.isEmpty() -> setOf()
list allAre G() -> list.fold(G()) { it, acc -> it + acc as G }
list allAre V() -> list.map { it as V }.toSet()
list anyAre IGF::class -> list.first { it is IGF<*, *, *> }
.let { throw Exception("Unsupported: Graph(${it::class.java})") }
else -> G(*list.zipWithNext().toTypedArray())
else -> throw Exception("Unsupported constructor: G(${list.joinToString(",") { it.javaClass.simpleName }})")
}.let { G(it) }

// Gafter's gadget! http://gafter.blogspot.com/2006/12/super-type-tokens.html
Expand All @@ -58,7 +50,7 @@ interface IGF<G: IGraph<G, E, V>, E: IEdge<G, E, V>, V: IVertex<G, E, V>> {
val G: Constructor<G> get() = gev[0].getConstructor(Set::class.java) as Constructor<G>
val E: Constructor<E> get() = gev.let { it[1].getConstructor(it[2], it[2]) } as Constructor<E>
/** TODO: Generify first argument to support [TypedVertex] */
val V: Constructor<V> get() = gev[2].getConstructor(String::class.java, Function1::class.java) as Constructor<V>
val V: Constructor<V> get() = gev[2].getConstructor(V(), Function1::class.java) as Constructor<V>

/**
* Memoizes the result of evaluating a pure function, indexed by:
Expand All @@ -73,9 +65,17 @@ interface IGF<G: IGraph<G, E, V>, E: IEdge<G, E, V>, V: IVertex<G, E, V>> {
methodRef: Int = Throwable().stackTrace[1].hashCode(),
args: Array<*>? = null,
computation: () -> T
): T = memo.get(Triple(classRef, methodRef, args)) { computation() } as T
): T =
memo.get(Triple(classRef, methodRef, args)) { computation() } as T
// computation().also {
// GlobalScope.async {
// memo.get(Triple(classRef, methodRef, args)) { it as Any } as T
// }
// }

companion object {
// val memo = Cache.Builder().build<Triple<*, *, *>, Any>()

// https://github.com/ben-manes/caffeine/issues/160#issuecomment-305681211
val memo = Caffeine.newBuilder().buildAsync<Triple<*, *, *>, Any>().synchronous()
}
Expand Down Expand Up @@ -180,7 +180,7 @@ interface IGraph<G, E, V>: IGF<G, E, V>, Set<V>, (V) -> Set<V>
vertices.flatMap { src ->
src.outgoing.map { edge -> edge.target to E(edge.target, src) }
}.groupBy({ it.first }, { it.second }).mapValues { (_, v) -> v.toSet() })
.map { (k, v) -> V(k.id) { v } }.toSet().let { G(it) }
.map { (k, v) -> V(k) { v } }.toSet().let { G(it) }

fun isomorphicTo(that: G): Boolean =
this.size == that.size &&
Expand Down Expand Up @@ -253,14 +253,13 @@ interface IVertex<G, E, V>: IGF<G, E, V>, Encodable

// Removes all edges pointing outside the set
private fun Set<V>.closure(): Set<V> =
map { v -> V(id) { v.outgoing.filter { it.target in this }.toSet() } }.toSet()
map { v -> V(this@IVertex as V) { v.outgoing.filter { it.target in this }.toSet() } }.toSet()

private fun Set<V>.neighbors(): Set<V> = flatMap { it.neighbors() }.toSet()

fun neighborhood(): G = G(neighbors(0).closure())

override fun encode(): DoubleArray = id.vectorize()
operator fun getValue(a: Any?, prop: KProperty<*>): V = V(prop.name)
fun render(): MutableNode = Factory.mutNode(id).add(Label.of(toString()))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class LabeledGraphTest {

@Test
fun testAdjListConstructor() = Assertions.assertEquals(
graph, LabeledGraph.G(
graph, LabeledGraph.P(
"a" to "b",
"b" to "c",
"c" to "d",
Expand Down
4 changes: 2 additions & 2 deletions src/test/kotlin/ai/hypergraph/kaliningraph/PrefAttach.kt
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ fun main() =
}
"Up" in it.key -> if (graphs.size > 1) graphs.removeLastOrNull()
"Down" in it.key -> graphs.add(graphs.last().prefAttach { degree ->
this + V(
newId = size.toString(),
this + LGVertex(
label = size.toString(),
out = if (vertices.isEmpty()) emptySet()
else degMap.sample().take(degree.coerceAtMost(size)).toSet()
).graph
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ fun animate(initial: LabeledGraph, transition: (Document, KeyboardEvent, Mutable

el.innerHTML(graphs.last().html())
mat.render(graphs.last()) { it.A }
vec.render(graphs.last()) { it.S() }
nex.render(graphs.last()) { it.A.transpose() * it.S() }
vec.render(graphs.last()) { it.S().also { println(it) } }
nex.render(graphs.last()) { (it.A.transpose() * it.S()).also { println(it) } }
desc.innerHTML("<p style=\"font-size:40px\">${graphs.last().description}</p>")
}
}
Expand Down

0 comments on commit c047113

Please sign in to comment.