Skip to content

Commit

Permalink
Started to write code for deferring coefficient evaluation until cont…
Browse files Browse the repository at this point in the history
…ext provides a field to execute in.

This might just be recreating homology with coefficients in Q from first principles.
  • Loading branch information
Mikael Vejdemo-Johansson committed May 15, 2024
1 parent 5d7a5ae commit 31ef41f
Show file tree
Hide file tree
Showing 3 changed files with 147 additions and 0 deletions.
4 changes: 4 additions & 0 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ libraryDependencies +=
libraryDependencies += "org.scalacheck" %% "scalacheck" % "1.17.0" % "test"
libraryDependencies += "org.scalaz" %% "scalaz-core" % "7.3.6"
libraryDependencies += "org.scalaz" %% "scalaz-scalacheck-binding" % "7.4.0-M14"
libraryDependencies += "org.bitbucket.inkytonik.kiama" %% "kiama" % "2.5.1"
libraryDependencies += "org.bitbucket.inkytonik.kiama" %% "kiama-extras" % "2.5.1"



lazy val root = (project in file("."))
.enablePlugins(
Expand Down
68 changes: 68 additions & 0 deletions src/main/scala/org/appliedtopology/tda4j/Deferred.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package org.appliedtopology.tda4j


import scala.math.Numeric.DoubleIsFractional

/** This functionality is an experiment in using algebraic effect structures for flexible coefficient choices. The idea
* is that each actual computation that is needed is built up as an AST, and simplified; and a coefficient choice is an
* effect handler that evaluates the remainder in the field of coefficients to be used.
*/

trait FractionalHandler {
type T
val fractional: Fractional[T]
def eval(fractionalExpr: FractionalExpr): T
}

object doubleHandler extends FractionalHandler {
import FractionalExpr.*
override type T = Double

override val fractional: Fractional[Double] = DoubleIsFractional

override def eval(fractionalExpr: FractionalExpr): Double = fractionalExpr match {
case Zero => 0.0
case One => 1.0
case Negate(x) => -eval(x)
case Inv(x) => 1 / eval(x)
case Plus(x, y) => eval(x) + eval(y)
case Minus(x, y) => eval(x) - eval(y)
case Times(x, y) => eval(x) * eval(y)
case Div(x, y) => eval(x) / eval(y)
}
}

sealed trait FractionalExpr
object FractionalExpr {
case object Zero extends FractionalExpr
case object One extends FractionalExpr
case class Plus(x: FractionalExpr, y: FractionalExpr) extends FractionalExpr
case class Minus(x: FractionalExpr, y: FractionalExpr) extends FractionalExpr
case class Negate(x: FractionalExpr) extends FractionalExpr
case class Times(x: FractionalExpr, y: FractionalExpr) extends FractionalExpr
case class Div(x: FractionalExpr, y: FractionalExpr) extends FractionalExpr
case class Inv(x: FractionalExpr) extends FractionalExpr
case class Sum(xs: List[FractionalExpr]) extends FractionalExpr
case class Prod(xs: List[FractionalExpr]) extends FractionalExpr
case class Succ(x: FractionalExpr) extends FractionalExpr
}

given Fractional[FractionalExpr] = new Fractional[FractionalExpr] {
import FractionalExpr.*

override def div(x: FractionalExpr, y: FractionalExpr): FractionalExpr = Div(x, y)
override def plus(x: FractionalExpr, y: FractionalExpr): FractionalExpr = Plus(x, y)
override def minus(x: FractionalExpr, y: FractionalExpr): FractionalExpr = Minus(x, y)
override def times(x: FractionalExpr, y: FractionalExpr): FractionalExpr = Times(x, y)
override def negate(x: FractionalExpr): FractionalExpr = Negate(x)
override def fromInt(x: Int): FractionalExpr =
if (x < 0) Negate(fromInt(-x))
else Plus(One, fromInt(x - 1))
override def parseString(str: String): Option[FractionalExpr] = None
override def toInt(x: FractionalExpr): Int = doubleHandler.eval(x).round.toInt
override def toLong(x: FractionalExpr): Long = doubleHandler.eval(x).round
override def toFloat(x: FractionalExpr): Float = doubleHandler.eval(x).toFloat
override def toDouble(x: FractionalExpr): Double = doubleHandler.eval(x)
override def compare(x: FractionalExpr, y: FractionalExpr): Int =
doubleHandler.fractional.compare(doubleHandler.eval(x), doubleHandler.eval(y))
}
75 changes: 75 additions & 0 deletions src/test/scala/org/appliedtopology/tda4j/DeferredSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package org.appliedtopology.tda4j

import org.specs2.mutable

class DeferredSpec extends mutable.Specification {
val fr = FractionalExpr
val fractional : Fractional[FractionalExpr] = summon[Fractional[FractionalExpr]]
import fractional.*
import org.bitbucket.inkytonik.kiama.*
import org.bitbucket.inkytonik.kiama.rewriting.*
import org.bitbucket.inkytonik.kiama.rewriting.Rewriter.*
import org.bitbucket.inkytonik.kiama.util.Trampolines.*

"For deferred evaluation" >> {
"Create expressions" >> {
val expr: FractionalExpr =
fr.One + fr.One - fr.One - fr.One + fr.Zero/fr.One - fr.One/fr.One + (fr.One+fr.One)/(fr.One+fr.One)
doubleHandler.eval(expr) must be_==(0.0)

// ruleset from https://apps.dtic.mil/sti/pdfs/ADA087641.pdf ch. 8 Rings
val rs : List[Strategy] = List(
rule { case fr.Minus(x,y) => fr.Plus(x, fr.Negate(y)) },
rule { case fr.Div(x,y) => fr.Times(x, fr.Inv(y)) },
// ch 7. Abelian Groups, precompiled set
rule { case fr.Plus(x, fr.Zero) => x },
rule { case fr.Plus(x, fr.Negate(y)) if(x == y) => fr.Zero },
rule { case fr.Negate(fr.Zero) => fr.Zero },
rule { case fr.Negate(fr.Negate(x)) => x },
rule { case fr.Negate(fr.Plus(x, y)) => fr.Plus(fr.Negate(x), fr.Negate(y)) },
// ch 8. Rings - associative + commutative
rule { case fr.Times(x, fr.Plus(y,z)) => fr.Plus(fr.Times(x,y),fr.Times(x,z)) },
rule { case fr.Times(fr.Plus(x,y), z) => fr.Plus(fr.Times(x,z),fr.Times(y,z)) },
rule { case fr.Times(x, fr.Zero) => fr.Zero },
rule { case fr.Times(x, fr.Negate(y)) => fr.Negate(fr.Times(x,y)) },
rule { case fr.Times(fr.Zero, x) => fr.Zero },
rule { case fr.Times(fr.Negate(x), y) => fr.Negate(fr.Times(x,y)) },
// add handling of inverses
rule { case fr.Inv(fr.Inv(x)) => x },
rule { case fr.Inv(fr.Times(x,y)) => fr.Times(fr.Inv(y), fr.Inv(x)) },
rule { case fr.Inv(fr.One) => fr.One },
// add associativity with collections?
//// construct sums
rule { case fr.Plus(fr.Plus(x,y),z) => fr.Sum(List(x,y,z)) },
rule { case fr.Plus(x,fr.Plus(y,z)) => fr.Sum(List(x,y,z)) },
//// extend sums
rule { case fr.Plus(fr.Sum(xs), y) => fr.Sum(y :: xs) },
rule { case fr.Plus(x, fr.Sum(ys)) => fr.Sum(x :: ys) },
rule { case fr.Sum(fr.Plus(x,y) :: rest) => fr.Sum(x :: y :: rest) },
rule { case fr.Sum(fr.Sum(xs) :: rest) => fr.Sum(xs ++ rest) },
//// construct products
rule { case fr.Times(fr.Times(x,y),z) => fr.Prod(List(x,y,z)) },
rule { case fr.Times(x,fr.Times(y,z)) => fr.Prod(List(x,y,z)) },
//// extend products
rule { case fr.Times(fr.Prod(xs), y) => fr.Prod(y :: xs) },
rule { case fr.Times(x, fr.Prod(ys)) => fr.Prod(x :: ys) },
rule { case fr.Prod(fr.Times(x,y) :: rest) => fr.Prod(x :: y :: rest) },
rule { case fr.Prod(fr.Prod(xs) :: rest) => fr.Prod(xs ++ rest) },
)

val rsRules = rs.foldRight(id)(_ <+ _)

// ruleset from Degano and Sirovich,
// described in https://apps.dtic.mil/sti/pdfs/ADA087641.pdf ch. 18 Arithmetic Theories
val ds : List[Strategy] = List(
rule { case fr.One => fr.Succ(fr.Zero) },
rule { case fr.Plus(x, fr.One) => fr.Succ(x) },
rule { case fr.Plus(fr.Zero, x) => x },
rule { case fr.Times(fr.Zero, x) => fr.Zero },
rule { case fr.Plus(fr.Succ(x), y) => fr.Succ(fr.Plus(x,y)) },
rule { case fr.Times(fr.Succ(x), y) => fr.Plus(fr.Times(x,y), y) },
rule { case fr.Times(x, fr.Plus(y,z)) => fr.Plus(fr.Times(x,y), fr.Times(x,z)) },
)
}
}
}

0 comments on commit 31ef41f

Please sign in to comment.