spire.example.kleene.scala Maven / Gradle / Ivy
The newest version!
package spire.example
import Predef.{any2stringadd => _, intWrapper => _, _}
import spire.algebra._
import spire.std.BooleanIsRig
import spire.implicits._
import spire.math._
import scala.reflect.ClassTag
import scala.annotation.tailrec
/**
* These examples are taken from http://r6.ca/blog/20110808T035622Z.html.
*
* The goal is to try to do as direct a translation as possible from the
* Haskell, to see how well we can do with Spire.
*
* The original example is in literate Haskell with good comments, so consult
* the link for more information.
*/
object KleeneDemo {
/**
* Show is a type class we'll use to control how types should display.
*/
trait Show[A] {
def show(a: A): String
}
object Show {
def apply[A](implicit ev: Show[A]) = ev
}
implicit class ShowOps[A: Show](a: A) {
def show: String = Show[A].show(a)
}
// Show[A] instances for built-in types
implicit object IntHasShow extends Show[Int] {
def show(a: Int) = a.toString
}
implicit object DoubleHasShow extends Show[Double] {
def show(a: Double) = a.toString
}
implicit object BooleanHasShow extends Show[Boolean] {
def show(a: Boolean) = if (a) "x" else "."
}
implicit def optionHasShow[A](implicit ev: Show[A]) = new Show[Option[A]] {
def show(a: Option[A]) = a.map(ev.show).getOrElse("-")
}
implicit def listHasShow[A](implicit ev: Show[A]) = new Show[List[A]] {
def show(a: List[A]) = a.map(ev.show).mkString("[", ",", "]")
}
implicit def streamHasShow[A](implicit ev: Show[A]) = new Show[Stream[A]] {
def show(s: Stream[A]) =
if (s.isEmpty) "[]" else "[%s,...]" format ev.show(s.head)
}
/**
* StarRig[A] is a Rig[A] that also has an asteration operator: kstar.
*
* Laws:
* 1. a.star = 1 + a * a.star = 1 + a.star * a
*/
trait StarRig[A] extends Rig[A] {
// one of these must be overridden in any type class instance
def kstar(a: A): A = plus(one, kplus(a))
def kplus(a: A): A = times(a, kstar(a))
}
object StarRig {
def apply[A](implicit ev: StarRig[A]) = ev
}
implicit def starRigHasRig[A](implicit ev: StarRig[A]): Rig[A] = ev
implicit class StarRigOps[A: StarRig](a: A) {
def kstar: A = StarRig[A].kstar(a)
def kplus: A = StarRig[A].kplus(a)
}
implicit def matrixHasStarRig[A](implicit dim: Dim, sr: StarRig[A], ct: ClassTag[A]) =
new StarRig[Matrix[A]] {
def zero: Matrix[A] = Matrix.zero
def one: Matrix[A] = Matrix.one
def plus(x: Matrix[A], y: Matrix[A]) = x + y
def times(x: Matrix[A], y: Matrix[A]) = x * y
override def kplus(m: Matrix[A]) = {
def f(k: Int, m: Matrix[A]) = Matrix[A] { (x, y) =>
m(x, y) + m(k, y) * m(k, k).kstar * m(x, k)
}
@tailrec def loop(m: Matrix[A], i: Int): Matrix[A] =
if (i >= 0) loop(f(i, m), i - 1) else m
loop(m, dim.n - 1)
}
}
/**
* A Kleene is a StarRig which obeys some additional laws.
*
* Laws:
* 1. a + a = a
* 2. a * x + x = x ==> a.kstar * x + x = x
* 3. x * a + x = x ==> x * a.kstar + x = x
*/
trait Kleene[A] extends StarRig[A]
object Kleene {
def apply[A](implicit ev: Kleene[A]) = ev
}
implicit def xyz[A](implicit ev: Kleene[A]): StarRig[A] = ev
// Kleene[A] instances for built-in types
implicit object BooleanHasKleene extends Kleene[Boolean] with BooleanIsRig {
override def kstar(x: Boolean) = true
}
/**
* Dim is a cute little class that let's us have implicit size information.
*
* This is to work around the fact that we don't currently have
* implementations of Bounded[A] or Ix[A] like Haskell does.
*
* Dim is probably not robust enough for real world use.
*/
case class Dim(n: Int)
/**
* Naive matrix trait.
*/
trait Matrix[A] { lhs =>
def dim: Dim
def apply(x: Int, y: Int): A
def map[B: ClassTag](f: A => B): Matrix[B]
def +(rhs: Matrix[A])(implicit rig: Rig[A]): Matrix[A]
def *(rhs: Matrix[A])(implicit rig: Rig[A]): Matrix[A]
}
object Matrix {
/**
* Builds a Matrix[A] given a function (Int, Int) => A and an implicit Dim
* to provide the dimensions over which to run the function.
*/
def apply[A: ClassTag](f: (Int, Int) => A)(implicit dim: Dim): Matrix[A] = {
val n = dim.n
val arr = new Array[A](n * n)
cfor(0)(_ < n, _ + 1) { y =>
cfor(0)(_ < n, _ + 1) { x =>
arr(y * n + x) = f(x, y)
}
}
new ArrayMatrix(arr)
}
/**
* Given an implicit Dim, builds the zero matrix (all zeros).
*/
def zero[A: Rig: ClassTag](implicit dim: Dim): Matrix[A] =
apply((x, y) => Rig[A].zero)
/**
* Given an implicit Dim, builds the identity matrix (diagonal ones).
*/
def one[A: Rig: ClassTag](implicit dim: Dim): Matrix[A] =
apply((x, y) => if (x == y) Rig[A].one else Rig[A].zero)
}
/**
* Mutable ArrayMatrix implementation.
*
* The mutability should only be used to initialize a matrix. Once it's built
* it will be typed as Matrix[A] with no interface for further mutation.
*
* The matrix also has naive implementations of addition and multiplication.
* These are not optimized--do not use this class in the wild!
*/
case class ArrayMatrix[A](arr: Array[A])(implicit val dim: Dim, ct: ClassTag[A]) extends Matrix[A] { lhs =>
def apply(x: Int, y: Int): A = arr(y * dim.n + x)
def update(x: Int, y: Int, a: A): Unit = arr(y * dim.n + x) = a
def map[B: ClassTag](f: A => B): Matrix[B] =
ArrayMatrix(arr.map(f))
def +(rhs: Matrix[A])(implicit rig: Rig[A]): Matrix[A] =
Matrix((x, y) => lhs(x, y) + rhs(x, y))
def *(rhs: Matrix[A])(implicit rig: Rig[A]): Matrix[A] =
Matrix { (x, y) =>
var total = rig.zero
cfor(0)(_ < dim.n, _ + 1)(j => total += lhs(j, y) * rhs(x, j))
total
}
}
// type class instance for Show[Matrix[A]]
implicit def matrixHasShow[A](implicit ev: Show[A]) = new Show[Matrix[A]] {
def show(m: Matrix[A]): String = {
val s = Show[A]
val n = m.dim.n
val lines = Array.fill(n)("")
cfor(0)(_ < n, _ + 1) { x =>
cfor(0)(_ < n, _ + 1)(y => lines(y) += s.show(m(x, y)) + " ")
val len = lines.foldLeft(0)(_ max _.length)
cfor(0)(_ < n, _ + 1)(y => lines(y) += " " * (len - lines(y).length))
}
lines.mkString("\n") + "\n"
}
}
// type class instance for Kleene[Matrix[A]]
implicit def matrixHasKleene[A](implicit dim: Dim, ka: Kleene[A], ct: ClassTag[A]) =
new Kleene[Matrix[A]] {
def zero: Matrix[A] = Matrix.zero
def one: Matrix[A] = Matrix.one
def plus(x: Matrix[A], y: Matrix[A]) = x + y
def times(x: Matrix[A], y: Matrix[A]) = x * y
override def kplus(m: Matrix[A]) = {
def f(k: Int, m: Matrix[A]) = Matrix[A] { (x, y) =>
m(x, y) + m(k, y) * m(k, k).kstar * m(x, k)
}
@tailrec def loop(m: Matrix[A], i: Int): Matrix[A] =
if (i >= 0) loop(f(i, m), i - 1) else m
loop(m, dim.n - 1)
}
}
/**
* Edge is a simple class used to construct adjacency matrices.
*
* It's important to remember that edges go: y -> x.
*
* Thus from is the y-coordinate and to is the x-coordinate.
*/
case class Edge(from: Int, to: Int)
// type class instance for Show[Edge]
implicit object EdgeHasShow extends Show[Edge] {
def show(e: Edge) = "(%c%c)" format ('A' + e.from, 'A' + e.to)
}
/**
* Graph provides functions for constructing an adjacency matrices.
*/
object Graph {
def apply(edges: Edge*)(implicit dim: Dim): Matrix[Boolean] = {
val m = ArrayMatrix(Array.fill[Boolean](dim.n * dim.n)(false))
edges.foreach { case Edge(from, to) => m(to, from) = true }
m
}
}
object LabeledGraph {
def apply(m: Matrix[Boolean])(implicit dim: Dim) = Matrix[Option[Edge]] { (x, y) =>
if (m(x, y)) Some(Edge(y, x)) else None
}
}
/**
* Expr[A] implements an AST for regular expressions.
*
* Basic regular consist of the following:
* 1. the empty set (Nul) -- a set with no strings
* 2. the empty string (Empty) -- set containing the empty string
* 3. literal strings (Var(a)) -- set containing a
* 4. concatenation (Then(a, b)) -- set of all xy, for x in a, y in b
* 5. alternation (Or(a, b)) -- union set of a and b
* 6. kleene star (Star(a)) -- set produced by 0+ concatenations from a
*
* For example, (a|bc)* includes "", "a", "bc", "abcaaaabc" but not "bc".
*/
sealed trait Expr[A]
case class Var[A](a: A) extends Expr[A]
case class Or[A](lhs: Expr[A], rhs: Expr[A]) extends Expr[A]
case class Then[A](lhs: Expr[A], rhs: Expr[A]) extends Expr[A]
case class Star[A](lhs: Expr[A]) extends Expr[A]
case class Empty[A]() extends Expr[A]
case class Nul[A]() extends Expr[A]
object Expr {
def apply[A](a: A): Expr[A] = Var(a)
}
// type class instance for Show[Expr[A]]
implicit def exprHasShow[A](implicit ev: Show[A]) = new Show[Expr[A]] {
def show(e: Expr[A]) = e match {
case Var(a) => ev.show(a)
case Empty() => "ε"
case Nul() => "∅"
case Star(x) => "(" + show(x) + ")*"
case Or(x, y) => "(" + show(x) + "|" + show(y) + ")"
case Then(x, y) => show(x) + show(y)
}
}
// type class instance for Kleene[Expr[A]]
implicit def exprHasKleene[A] = new Kleene[Expr[A]] {
def zero: Expr[A] = Nul()
def one: Expr[A] = Empty()
def plus(x: Expr[A], y: Expr[A]): Expr[A] = (x, y) match {
case (Nul(), e) => e
case (e, Nul()) => e
case (Empty(), Empty()) => Empty()
case (Empty(), Star(e)) => Star(e)
case (Star(e), Empty()) => Star(e)
case (e1, e2) => Or(e1, e2)
}
def times(x: Expr[A], y: Expr[A]): Expr[A] = (x, y) match {
case (Nul(), _) => Nul()
case (_, Nul()) => Nul()
case (Empty(), e) => e
case (e, Empty()) => e
case (e1, e2) => Then(e1, e2)
}
override def kstar(x: Expr[A]): Expr[A] = x match {
case Nul() => Empty()
case Empty() => Empty()
case Star(e) => kstar(e)
case _ => Star(x)
}
}
/**
* Tropical represents a finite quantity between zero and infinity.
*/
sealed trait Tropical[A]
case class Finite[A](a: A) extends Tropical[A]
case class Infinity[A]() extends Tropical[A]
object Tropical {
def apply[A](a: A): Tropical[A] = Finite(a)
def inf[A]: Tropical[A] = Infinity()
}
implicit def tropicalHasShow[A: Show] = new Show[Tropical[A]] {
def show(t: Tropical[A]) = t match {
case Finite(a) => Show[A].show(a)
case Infinity() => "∞"
}
}
implicit def tropicalHasOrder[A](implicit ord: Order[A]) = new Order[Tropical[A]] {
def compare(x: Tropical[A], y: Tropical[A]) = (x, y) match {
case (Infinity(), Infinity()) => 0
case (Infinity(), _) => 1
case (_, Infinity()) => -1
case (Finite(a1), Finite(a2)) => ord.compare(a1, a2)
}
}
implicit def TropicalHasKleene[A: Order: Rig] = new Kleene[Tropical[A]] {
def zero: Tropical[A] = Infinity()
def one: Tropical[A] = Tropical(Rig[A].zero)
def plus(x: Tropical[A], y: Tropical[A]): Tropical[A] = (x, y) match {
case (Infinity(), t) => t
case (t, Infinity()) => t
case (Finite(a1), Finite(a2)) => Tropical(a1 min a2)
}
def times(x: Tropical[A], y: Tropical[A]): Tropical[A] = (x, y) match {
case (Infinity(), _) => Infinity()
case (_, Infinity()) => Infinity()
case (Finite(a1), Finite(a2)) => Tropical(a1 + a2)
}
override def kstar(x: Tropical[A]): Tropical[A] = one
}
/**
* ShortestPath is a data structure which will track two things:
* 1. the path's cost, as Tropical[A]
* 2. the path itself, as B
* Any impossible path will have Infinity() as its cost.
*/
case class ShortestPath[A, B](a: Tropical[A], b: B) {
def map[C](f: B => C) = ShortestPath[A, C](a, f(b))
}
// type class instance for Show[ShortestPath[A, B]]
implicit def spHasShow[A: Show, B: Show] = new Show[ShortestPath[A, B]] {
def show(p: ShortestPath[A, B]) = "%s[%s]" format (p.b.show, p.a.show)
}
// type class instance for Kleene[ShortestPath[A, B]]
implicit def shortestPathHasKleene[A, B](implicit rig: Rig[Tropical[A]], ord: Order[Tropical[A]], kb: Kleene[B]) =
new Kleene[ShortestPath[A, B]] {
def zero = ShortestPath(rig.zero, kb.zero)
def one = ShortestPath(rig.one, kb.one)
def plus(x: ShortestPath[A, B], y: ShortestPath[A, B]) = (x.a compare y.a) match {
case -1 => x
case 0 => ShortestPath(x.a + y.a, x.b + y.b)
case 1 => y
}
def times(x: ShortestPath[A, B], y: ShortestPath[A, B]) =
ShortestPath(x.a * y.a, x.b * y.b)
override def kstar(x: ShortestPath[A, B]) =
ShortestPath(rig.one, if (x.a === rig.one) x.b.kstar else kb.one)
}
/**
* Language represents the set of every valid string in a regular
* language. Each W is a valid character, each Stream[W] is a (lazy)
* string, and SS[W] (e.g. Stream[Stream[W]]) is the complete set of
* all strings.
*/
case class Language[W](wss: SS[W]) {
def someWord: Option[List[W]] = wss.headOption.map(_.toList)
}
object Language {
def letter[W](w: W): Language[W] = Language(Stream(Stream(w)))
}
// handy type alias
type SS[W] = Stream[Stream[W]]
// type class instance for Show[Language[W]]
implicit def languageHasShow[W: Show] = new Show[Language[W]] {
def show(l: Language[W]) = Show[SS[W]].show(l.wss)
}
// type class instance for Kleene[Language[W]]
implicit def languageHasKleene[W] = new Kleene[Language[W]] {
def zero: Language[W] = Language(Stream.empty[Stream[W]])
def one: Language[W] = Language(Stream(Stream.empty[W]))
def plus(x: Language[W], y: Language[W]): Language[W] = {
def interleave(ws1: SS[W], ws2: SS[W]): SS[W] =
if (ws1.isEmpty) ws2 else ws1.head #:: interleave(ws2, ws1)
Language(interleave(x.wss, y.wss))
}
def times(x: Language[W], y: Language[W]): Language[W] =
Language(x.wss.flatMap(ws1 => y.wss.map(ws2 => ws1 #::: ws2)))
override def kstar(x: Language[W]): Language[W] = {
def plusList(z: Language[W]): Language[W] =
if (z.wss.isEmpty) zero else Language(z.wss #::: kstar(z).wss)
plus(one, plusList(x))
}
}
/**
*
*/
trait Compact[A] {
def map[B: Field](f: A => B): Compact[B] = this match {
case CompactReal(a) => CompactReal(f(a))
case _ => CompactInf()
}
}
case class CompactInf[A]() extends Compact[A]
case class CompactReal[A: Field](a: A) extends Compact[A]
object Compact {
def apply[A: Field](a: A): Compact[A] = CompactReal(a)
}
implicit def compactHasShow[A: Show] = new Show[Compact[A]] {
def show(c: Compact[A]) = c match {
case CompactReal(a) => a.show
case _ => "∞"
}
}
implicit def compactIsStarRig[A: Field] = new StarRig[Compact[A]] {
val zero: Compact[A] = Compact(Field[A].zero)
val one: Compact[A] = Compact(Field[A].one)
def plus(x: Compact[A], y: Compact[A]): Compact[A] = (x, y) match {
case (CompactInf(), _) => CompactInf()
case (_, CompactInf()) => CompactInf()
case (CompactReal(a), CompactReal(b)) => Compact(a + b)
}
def times(x: Compact[A], y: Compact[A]): Compact[A] = (x, y) match {
case (`zero`, _) => zero
case (_, `zero`) => zero
case (CompactInf(), _) => CompactInf()
case (_, CompactInf()) => CompactInf()
case (CompactReal(a), CompactReal(b)) => Compact(a * b)
}
override def kstar(x: Compact[A]): Compact[A] = x match {
case `one` => CompactInf()
case CompactInf() => CompactInf()
case CompactReal(a) => CompactReal((Field[A].one - a).reciprocal)
}
}
/**
*
*/
def graphExample() {
// our example graph will be 5x5
implicit val dim = Dim(5)
// edges for this example
val edges = List(
Edge(0, 1),
Edge(1, 2),
Edge(2, 3), Edge(2, 4),
Edge(3, 1),
Edge(4, 3)
)
// build the example graph
val example: Matrix[Boolean] = Graph(edges:_*)
// examine the graph
println("adjacency matrix:\n%s" format example.show)
println("reflexive-transitive closure:\n%s" format example.kstar.show)
println("transitive closure:\n%s" format example.kplus.show)
val labeled = LabeledGraph(example)
println("labels:\n%s" format labeled.show)
val expred = labeled.map(_.map(Expr.apply).getOrElse(Nul()))
println("exprs:\n%s" format expred.show)
println("path exprs:\n%s" format expred.kstar.show)
}
def pathExample() {
// our example graph will be 5x5
implicit val dim = Dim(6)
val edges = List(
(Edge(0, 1), 7), (Edge(0, 2), 9), (Edge(0, 5), 14),
(Edge(1, 2), 10), (Edge(1, 3), 15),
(Edge(2, 3), 11), (Edge(2, 5), 2),
(Edge(3, 4), 6),
(Edge(4, 5), 9)
)
val weighted: Matrix[Tropical[Int]] = {
val m = ArrayMatrix(Array.fill(dim.n * dim.n)(Tropical.inf[Int]))
edges.foreach { case (Edge(y, x), n) =>
m(x, y) = Tropical(n)
m(y, x) = Tropical(n)
}
m
}
println("weights:\n%s" format weighted.show)
println("least-cost:\n%s" format weighted.kstar.show)
val annotated = Matrix[ShortestPath[Int, Expr[Edge]]] { (x, y) =>
weighted(x, y) match {
case Infinity() => ShortestPath(Infinity(), Kleene[Expr[Edge]].zero)
case Finite(n) => ShortestPath(Finite(n), Var(Edge(y, x)))
}
}
println("annotated-re:\n" + annotated.show)
println("shortest-path-re:\n" + annotated.kstar.show)
val langed = Matrix[ShortestPath[Int, Language[Edge]]] { (x, y) =>
weighted(x, y) match {
case Infinity() => ShortestPath(Infinity(), Kleene[Language[Edge]].zero)
case Finite(n) => ShortestPath(Finite(n), Language.letter(Edge(y, x)))
}
}
println("l-annotated:\n" + langed.show)
println("l-shortest-path:\n" + langed.kstar.map(_.b.someWord).show)
def evalExpr[A, B: Kleene](expr: Expr[A])(f: A => B): B = expr match {
case Nul() => Kleene[B].zero
case Empty() => Kleene[B].one
case Var(a) => f(a)
case Star(x) => evalExpr(x)(f).kstar
case Or(x, y) => evalExpr(x)(f) + evalExpr(y)(f)
case Then(x, y) => evalExpr(x)(f) * evalExpr(y)(f)
}
val costExprs: Matrix[Expr[Int]] = annotated.map {
case ShortestPath(Infinity(), _) => Nul()
case ShortestPath(Finite(n), _) => Expr(n)
}
val leastCostExprs: Matrix[Tropical[Int]] =
costExprs.kstar.map(a => evalExpr(a)(Tropical.apply))
println("least-cost via evalExpr:\n" + leastCostExprs.show)
}
def solvingExample() {
// our example matrix is 2x2
implicit val dim = Dim(2)
val m: Matrix[Compact[Double]] = ArrayMatrix(Array(2.0, 1.0, 0.0, 2.0)).map(n => Compact(n))
println("2x2 matrix:\n" + m.show)
println("2x2 asteration:\n" + m.kstar.show)
def negate(m: Matrix[Compact[Double]]) = m.map(_.map(-_))
val one = Matrix.one[Compact[Double]]
def inverse(m: Matrix[Compact[Double]]) = (one + negate(m)).kstar
println("2x2 inverse:\n" + inverse(m).show)
}
def main(args: Array[String]) {
graphExample()
pathExample()
solvingExample()
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy