SLIDE 1
1/31 Categories as type classes in the Scala Algebra System Raphal - - PowerPoint PPT Presentation
1/31 Categories as type classes in the Scala Algebra System Raphal - - PowerPoint PPT Presentation
1/31 Categories as type classes in the Scala Algebra System Raphal Jolly Databeans CASC 2013 Berlin Outline 2/31 * The Scala Algebra System * Categorical view of computer algebra * Bounded
SLIDE 2
SLIDE 3
The Scala Algebra System (ScAS) 3/31 * polynomial arithmetic over various base rings (integer, rational, complex, modular) * rational and algebraic functions * ring modules/algebras * polynomial GCD * Gröbner bases
SLIDE 4
Trade-off 4/31 * type-safe * generic * mathematical syntax * efficient ?
SLIDE 5
Milestones 5/31 * 2003 : jscl-meditor * 2004 : jscl was chosen as symbolic engine of GeoGebra * 2007 : encounter with JAS and parametric polymorphism (Java 2.5) * 2008 : port to Scala -> ScAS * 2012 : ScAS 2.0 (type classes) * 2013 : scripting support in Scala 2.11
SLIDE 6
Categorical view of computer algebra 6/31 1.arithmetic operations between elements are restricted based on their 2.domains must support abstraction by means of 3.categories must support multiple inheritance 4.(optional) categories may support default definition of
- perations
SLIDE 7
Categorical view of computer algebra 7/31 * Axiom [Davenport:1992] * Gauss [Gruntz:1994] * JAS [Kredel:2006] * DoCon [Mechveliani:2001] * Mathemagix [VanDerHoeven:2002]
SLIDE 8
Bounded polymorphism 8/31 trait Ring[T <: Ring.Element[T]] { def zero: T ... }
- bject Ring {
trait Element[T <: Element[T]] { val factory: Ring[T] def +(that: T): T def unary_-: T ... } }
SLIDE 9
9/31
- bject BigInteger extends Ring[BigInteger] {
def apply(i: Int): BigInteger = apply(java.math.BigInteger.valueOf(i)) def apply(value: java.math.BigInteger) = new BigInteger(value) def zero = apply(0) } class BigInteger(val value: java.math.BigInteger) extends Ring.Element[BigInteger] { val factory = BigInteger def +(that: BigInteger) = factory( this.value.add(that.value)) def unary_- = factory(value.negate())
- verride def toString = value.toString
} BigInteger(1) + BigInteger(1) // 2
SLIDE 10
10/31 class Polynomial[C <: Ring.Element[C]](val ring: Ring[C], val variable: String) extends Ring[ Polynomial.Element[C]] { def generator = new Polynomial.Element(...)(this) }
- bject Polynomial {
def apply[C <: Ring.Element[C]](ring: Ring[C], variable: String) = new Polynomial(ring, variable) class Element[C <: Ring.Element[C]](val value: Repr[C])( val factory: Polynomial[C]) extends Ring.Element[ Element[C]] } val r = Polynomial(BigInteger, "x") val x = r.generator x + x // 2*x
SLIDE 11
Basic Types : BigInteger 11/31
Ring.Ele m e nt[T < : Ring.Ele m e nt[T]] < < inte rfa ce > > Ring[T < : Ring.Ele m e nt[T]] < < inte rfa ce > > BigInte ge r < < re a lize > > BigInte ge r < < re a lize > >
SLIDE 12
Basic Types : BigInteger, Polynomial 12/31
Polynom ia l.Ele m e nt[C < : Ring.Ele m e nt[C]] Polynom ia l[C < : Ring.Ele m e nt[C]] Ring.Ele m e nt[T < : Ring.Ele m e nt[T]] < < inte rfa ce > > Ring[T < : Ring.Ele m e nt[T]] < < inte rfa ce > > < < re a lize > > < < re a lize > > BigInte ge r < < re a lize > > BigInte ge r < < re a lize > >
SLIDE 13
Type classes 13/31
trait Ring[T] { outer => def zero: T def plus(x: T, y: T): T implicit def mkOps(value: T): Ring.Ops[T] = new Ring.Ops[T] { val lhs = value val factory = outer } }
- bject Ring {
trait ExtraImplicits { implicit def infixRingOps[T: Ring](lhs: T) = implicitly[Ring[T]].mkOps(lhs) } trait Ops[T] { val lhs: T val factory: Ring[T] def +(rhs: T) = factory.plus(lhs, rhs) } }
SLIDE 14
14/31 type BigInteger = java.math.BigInteger
- bject BigInteger extends Ring[BigInteger] {
def apply(i: Int) = java.math.BigInteger.valueOf(i) def zero = apply(0) def plus(x: BigInteger, y: BigInteger) = x.add(y) } trait ExtraImplicits { implicit val ZZ = BigInteger }
- bject Implicits extends ExtraImplicits
with Ring.ExtraImplicits import Implicits.{ZZ, infixRingOps} BigInteger(1) + BigInteger(1) // 2
SLIDE 15
15/31 class Polynomial[C: Ring](val variable: String) extends Ring[Repr[C]] { val ring = implicitly[Ring[C]] def generator: Repr[C] = ... }
- bject Polynomial {
def apply[C: Ring](variable: String) = new Polynomial(variable) } import Implicits.{ZZ, infixRingOps} implicit val r = Polynomial[BigInteger]("x") val x = r.generator x + x // 2*x
SLIDE 16
Basic Types : BigInteger 16/31
Ring[T] < < inte rfa ce > > BigInte ge r < < re a lize > > ja va .m a th::BigInte ge r
SLIDE 17
Basic Types : BigInteger, Polynomial 17/31
BigInte ge r Ring[T] < < inte rfa ce > > < < re a lize > > ja va .m a th::BigInte ge r Polynom ia l[C: Ring] < < re a lize > > Re pr[C]
SLIDE 18
Category hierarchy of ScAS 18/31
Monoid Ring Se m iGroup Se t UFD Abe lia nGroup NotQuite Group Euclide a nDom a in Fie ld Group Module Equiv / Orde ring Alge bra Ove rRing Alge bra Ve ctorSpa ce Boole a nAlge bra < < vie w> > Sta rRing
SLIDE 19
Type classes : pros 19/31 * avoid the dependent type problem : type system makes no distinction between types of elements from e.g. ModInteger(2) and ModInteger(3) but only one implicit value is allowed for the associated domain * reuse of existing types : allows unboxed primitive types => generic numeric-symbolic implementations * makes domain subclassing easier : SolvablePolynomial <: Polynomial RealAlgebraicNumber <: AlgebraicNumber PolynomialWithSubresGCD <: Polynomial
SLIDE 20
Type classes : cons 20/31 * does not play well with coercions BigInteger(1) + 1 => BigInteger(1) + BigInteger(1) x + 1 => x + r(1) * Need an idea to solve this
SLIDE 21
Hybrid abstraction scheme 21/31
- bject Ring {
trait ExtraImplicits { implicit def infixRingOps[T: Ring](lhs: T) = implicitly[Ring[T]].mkOps(lhs) } trait Ops[T] { val lhs: T val factory: Ring[T] def +(rhs: T) = factory.plus(lhs, rhs) } }
SLIDE 22
Hybrid abstraction scheme 21/31
- bject Ring {
trait ExtraImplicits { implicit def infixRingOps[T: Ring](lhs: T) = implicitly[Ring[T]].mkOps(lhs) } trait Ops[T] { val lhs: T val factory: Ring[T] def +(rhs: T) = factory.plus(lhs, rhs) } trait Element[T <: Element[T]] extends Ops[T] { this: T => val lhs = this } }
SLIDE 23
Hybrid abstraction scheme 22/31
BigInte ge r Ring[T] < < inte rfa ce > > < < re a lize > > ja va .m a th::BigInte ge r Polynom ia l[C: Ring] < < re a lize > > Re pr[C]
SLIDE 24
Hybrid abstraction scheme 22/31
Ring.Ele m e nt[T] < < inte rfa ce > > Ring[T] < < inte rfa ce > > Polynom ia l.Ele m e nt[C] Polynom ia l[C: Ring] < < re a lize > > < < re a lize > > BigInte ge r < < re a lize > > ja va .m a th::BigInte ge r
SLIDE 25
Type classes in computer algebra 23/31 * introduced in Haskell [Oliveira:2010] * first mentionned as possible abstractions for computer algebra structures [Weber:1993] [Santas:1995] * DoCon : actually uses them * Scala : a code example in the language documentation explicitly involves abstract algebraic constructs * Mathemagix uses a concept of categories that is completely equivalent to type classes
SLIDE 26
DoCon : computer algebra in Haskell 24/31 class CommutativeRing a => GCDRing a where gcd : a -> a -> a canAssoc : a -> a instance GCDRing Integer where canAssoc n = if n < 0 then -n else n gcd n 0 = n gcd n m = gcd m (mod n m) data Fraction a = a :/ a ... instance GCDRing a => AdditiveSemigroup (Fraction a) where (x :/ y) + (x' :/ y') = ... usual way to sum fractions instance GCDRing a => Field (Fraction a) where ...
SLIDE 27
25/31
abstract class SemiGroup[A] { def add(x: A, y: A): A } abstract class Monoid[A] extends SemiGroup[A] { def unit: A }
- bject ImplicitTest extends App {
implicit object StringMonoid extends Monoid[String] { def add(x: String, y: String): String = x concat y def unit: String = "" } implicit object IntMonoid extends Monoid[Int] { def add(x: Int, y: Int): Int = x + y def unit: Int = 0 } def sum[A](xs: List[A])(implicit m: Monoid[A]): A = if (xs.isEmpty) m.unit else m.add(xs.head, sum(xs.tail)) println(sum(List(1, 2, 3))) // 6 println(sum(List("a", "b", "c"))) // abc }
http://docs.scala-lang.org/tutorials/tour/implicit- parameters.html
SLIDE 28
Categories in the Scala standard library 26/31
Ordering Numeric PartialOrdering Equiv Integral Fractional
SLIDE 29
Categories in Spire 27/31
AdditiveMonoid AdditiveGroup AdditiveAbGroup Monoid Group AbGroup SemiGroup MultiplicativeSemiGroup MultiplicativeMonoid MultiplicativeGroup MultiplicativeAbGroup Rig Ring Module RightModule VectorSpace NormedVectorSpace InnerProductSpace EuclideanRing Field RingAlgebra ZAlgebra FieldAlgebra Numeric Eq Order Integral Fractional AdditiveSemiGroup SemiRing Rng
SLIDE 30
Operation 28/31
x + y ZZ infixRingOps( )( ).+( )
- ptimized away by Spire macro
ZZ.mkOps( ) .+ ( ) ZZ.plus ( , ) specialized ; inlined by JIT x + y
SLIDE 31
Spire is Fast 29/31
spire direct 38.9 36.9 30.1 29.9 19.3 13.4 10.6 10.5 scala spire nspec gcd mean
gcd mean 5 10 15 20 25 30 35 40 45 s cala s pire ns pec s pire direct
http://typelevel.org/blog/2013/07/07/generic-numeric- programming.html
SLIDE 32
ScAS : polypower benchmark 30/31
n 20 4.4 4.2 24 11.6 10.9 28 27.6 26.4 32 59.1 54.6 ModInteger ModInt
20 24 28 32 10 20 30 40 50 60 70 ModInteger ModInt
SLIDE 33