Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Kotlinš›: Differentiable Functional Programming with Algebraic Data Types

Breandan Considine
January 15, 2019
260

Kotlinš›: Differentiable Functional Programming with Algebraic DataĀ Types

Kotlin is a multi-platform programming language with compiler support for JVM, JS and native targets. The language emphasizes static typing, null-safety and interoperability with Java and JavaScript. In this work, we present an algebraically grounded implementation of forward and reverse mode automatic differentiation written in pure Kotlin and a property-based test suite for soundness checking. Our approach enables users to target multiple platforms through a single codebase and receive compile-time static analysis. A working prototype is provided at: https://github.com/breandan/kotlingrad

Breandan Considine

January 15, 2019
Tweet

Transcript

  1. Kotlināˆ‡ Diļ¬€erentiable functional programming with algebraic data types Breandan Considine

    UniversitĀ“ e de MontrĀ“ eal [email protected] January 15, 2019 Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 1 / 16
  2. Overview 1 Introduction and motivation 2 Architectural Overview 3 Usage

    4 Future Plans Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 2 / 16
  3. Type checking automatic diļ¬€erentiation Suppose we have a program P

    : R ā†’ R where: P(x) = pn ā—¦ pnāˆ’1 ā—¦ pnāˆ’2 ā—¦ ... ā—¦ p1 ā—¦ p0 (1) From the chain rule of calculus, we know that: dP dp0 = n i=1 dpi dpiāˆ’1 (2) In order for P to type check, what is the type of p0<i<n? pi : Tout(piāˆ’1) ā†’ Tin(pi+1) (3) What happens if we let P : Rc ā†’ R, P : Rc ā†’ Cd or P : ĪØp ā†’ ā„¦q? Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 3 / 16
  4. Why Kotlin? Goal: To implement automatic diļ¬€erentiation in Kotlin Kotlin

    is a language with strong static typing and null safety Supports ļ¬rst-class functions, higher order functions and lambdas Has support for algebraic data types, via tuples sealed classes Extension functions, operator overloading other syntax sugar Oļ¬€ers features for embedding domain speciļ¬c languages (DSLs) Access to all libraries and frameworks in the JVM ecosystem Multi-platform and cross-platform (JVM, Android, iOS, JS, native) Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 4 / 16
  5. Kotlināˆ‡ Priorities Type system Strong type system based on algebraic

    principles Leverage the compiler for static analysis No implicit broadcasting or shape coercion Parameterized numerical types and arbitary-precision Design principles Functional programming and lazy numerical evaluation Eager algebraic simpliļ¬cation of expression trees Operator overloading and tapeless reverse mode AD Usage desiderata Generalized AD with imperative array programming Automatic diļ¬€erentiation with inļ¬x and Polish notation Partials and higher order derivatives and gradients Testing and validation Numerical gradient checking and property-based testing Performance benchmarks and thorough regression testing Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 5 / 16
  6. Algebraic types Abstract algebra can be useful when generalizing to

    new structures Helps us to easily translate between mathematics and source code Most of the time in numerical computing, we are dealing with Fields A ļ¬eld is a set F with two operations + and Ɨ, with the properties: Associativity: āˆ€a, b, c āˆˆ F, a + (b + c) = (a + b) + c Commutivity: āˆ€a, b āˆˆ F, a + b = b + a and a Ɨ b = b Ɨ a Distributivity: āˆ€a, b, c āˆˆ F, a Ɨ (b Ɨ c) = (a Ɨ b) Ɨ c Identity: āˆ€a āˆˆ F, āˆƒ0, 1 āˆˆ F s.t. a + 0 = a and a Ɨ 1 = a + inverse: āˆ€a āˆˆ F, āˆƒ āˆ’ a s.t. a + (āˆ’a) = 0 Ɨ inverse: āˆ€a = 0 āˆˆ F, āˆƒaāˆ’1 s.t. a Ɨ aāˆ’1 = 1 Readily extensible to complex numbers, quaternions, dual numbers Field arithmetic can be implemented using parametric polymorphism What is a program, but a series of arithmetic operations? Sajovic & Vuk, Operational Calculus for Diļ¬€erentiable Programming Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 6 / 16
  7. How do we deļ¬ne algebraic types in Kotlināˆ‡? // T:

    Group<T> is effectively a self type interface Group<T: Group<T>> { operator fun plus ( f : T) : T operator fun times ( f : T) : T } // Inherits from Group, default methods interface Field <T: Field <T>>: Group<T> { operator fun unaryMinus ( ) : T operator fun minus ( f : T) : T = t h i s + āˆ’f fun i n v e r s e ( ) : T operator fun div ( f : T) : T = t h i s āˆ— f . i n v e r s e () } Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 7 / 16
  8. Algebraic Data Types class Var : Expr () class Const

    ( val num : Number ) : Expr () class Sum( val e1 : Expr , val e2 : Expr ) : Expr () class Prod ( val e1 : Expr , val e2 : Expr ) : Expr () sealed class Expr : Group { fun d i f f () = when( expr ) { i s Const āˆ’> Zero i s Sum āˆ’> e1 . d i f f () + e2 . d i f f () i s Prod āˆ’> e1 . d i f f () āˆ— e2 + e1 āˆ— e2 . d i f f () i s Var āˆ’> One } operator fun plus ( e : Expr ) = Sum( this , e ) operator fun times ( e : Expr ) = Prod ( this , e ) } Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 8 / 16
  9. Expression simpliļ¬cation operator fun Expr . times ( exp :

    Expr ) = when { t h i s i s Const && num == 0.0 āˆ’> Const ( 0 . 0 ) t h i s i s Const && num == 1.0 āˆ’> exp exp i s Const && exp . num == 0.0 āˆ’> exp exp i s Const && exp . num == 1.0 āˆ’> t h i s t h i s i s Const && exp i s Const āˆ’> Const (numāˆ—exp . num) else āˆ’> Prod ( this , e ) } // Sum(Prod(Const(2.0), Var()), Const(6.0)) val q = Const ( 2 . 0 ) āˆ— Sum( Var () , Const ( 3 . 0 ) ) Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 9 / 16
  10. Extension functions and contexts class Expr<T: Group<T>>: Group<Expr<T>> { //...

    operator fun plus ( exp : Expr<T>) = Sum( this , exp ) operator fun times ( exp : Expr<T>) = Prod ( this , exp ) } object DoubleContext { operator fun Number . times ( exp : Expr<DoubleReal >) = Const ( toDouble ( ) ) āˆ— exp } // Uses ā€˜*ā€˜ operator in DoubleContext fun Expr<DoubleReal >. multiplyByTwo () = with ( DoubleContext ) { 2 āˆ— t h i s } Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 10 / 16
  11. Automatic test case generation val x = v a r

    i a b l e ("x") val y = v a r i a b l e ("y") val z = y āˆ— ( s i n ( x āˆ— y ) āˆ’ x ) // Function under test val dz dx = d( z ) / d( x ) // Automatic derivative val manualDx = y āˆ— ( cos ( x āˆ— y ) āˆ— y āˆ’ 1) "dz/dx should be y * (cos(x * y) * y - 1)" { a s s e r t A l l (NumGen, NumGen) { cx , cy āˆ’> // Evaluate the results at a given seed val autoEval = dz dx ( x to cx , y to cy ) val manualEval = manualDx ( x to cx , y to cy ) // Should pass if |adEval - manualEval| < eps autoEval shouldBeApproximately manualEval } } Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 11 / 16
  12. Usage: plotting higher derivatives of nested functions with ( DoubleFunctor

    ) {// Use double -precision numerics val x = v a r i a b l e () // Declare an immutable variable val y = s i n ( s i n ( s i n ( x )))/ x + s i n ( x ) āˆ— x + cos ( x ) + x // Lazily compute reverse -mode automatic derivatives val dy dx = d( y ) / d( x ) val d2y dx = d( dy dx ) / d( x ) val d3y dx = d( d2y dx2 ) / d( x ) val d4y dx = d( d3y dx3 ) / d( x ) val d5y dx = d( d4y dx4 ) / d( x ) p l o t ( āˆ’10..10 , dy dx , dy2 dx , d3y dx , d4y dx , d5y dx ) } Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 12 / 16
  13. y = sin sin sin x x + x sin

    x + cos x + x, dy dx , d2y dx2 , d3y dx3 , d4y dx4 , d5y dx5 Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 13 / 16
  14. Further directions to explore Theory Generalization of types to higher

    order functions, vector spaces Dependent types via code generation to type-check tensor dimensions General programming operators and data structures Imperative deļ¬ne-by-run array programming syntax Parallelization and asynchrony (cf. HogWild, YellowFin) Implementation Details Closer integration with Kotlin/Java standard library Encode additional structure, i.e. function arity into type system Vectorized optimizations for matrices with certain properties Conļ¬gurable forward and backward AD modes based on dimension Automatic expression refactoring for numerical stability Primitive type specialization, i.e. FloatVector <: Vector<T>? Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 14 / 16
  15. Special thanks Liam Paull Michalis Famelis Alexander Nozik Hanneli Tavante

    Breandan Considine (UdeM) Kotlināˆ‡ January 15, 2019 16 / 16