-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Started to write code for deferring coefficient evaluation until cont…
…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
Showing
3 changed files
with
147 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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
75
src/test/scala/org/appliedtopology/tda4j/DeferredSpec.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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)) }, | ||
) | ||
} | ||
} | ||
} |