(**

How to formally differentiate a function

This small program shows how differentiation can be seen as a purely formal and automatic procedure.

Program in OCaml v4+ by Lilian Besson (ENS Cachan) for MA 101 at Mahindra École Centrale, (C) 2014.

GPLv3 Licenced.

@date Monday 14 September 2014 @author Lilian BESSON *)



print_endline "# How to (simply) differentiate a function thanks to OCaml formal programming.\n";;

(**

Define the data type.

*)


(** A function is an expression depending on some variables. We choose to work only with sin, cos, exp and ln as basic functions. *)

type funct = Id of string | Lambda of float
    | Sin of funct | Cos of funct | Exp of funct | Ln of funct
    | Prod of funct * funct | Inv of funct | Sum of funct * funct
    | Power of funct * int
    | Comp of funct * funct
    | Unknown of string
;;

(**

Examples.

*)


let x = Id("x");;
let fsin = Sin(x)
and fcos = Cos(x)
and fexp = Exp(x)
and fln = Ln(x);;

let y = Id("y");;
let fpi = Lambda(3.1415)
and fpoly1 = Sum(Lambda(1.), Power(y, 7))
and fcossin = Comp((Cos(y)), (Sin(y)))
and fexpln = Comp((Exp(y)), (Ln(y)))
and finv = Inv(y)
and fsqsq = Power(y, 4)
and fh = Unknown("h");;

(**

Simplify as much as possible.

*)


(** Expression returned if the function is not valid (some basic check, not sophiscated ones). *)

exception Not_defined;;

(** Recursively put in canonic form. *)

let rec canonic = function
    | Sin(Lambda(0.)) -> Lambda(0.)
    | Cos(Lambda(0.)) -> Lambda(1.)
    | Ln(Lambda(x)) when x<=0. -> raise Not_defined
    | Ln(Lambda(1.)) -> Lambda(0.)
    | Prod(Lambda(0.), g) -> Lambda(0.)
    | Prod(Lambda(1.), g) -> (canonic g)
    | Prod(Lambda(-1.), Prod(Lambda(-1.), g)) -> g
    | Prod(g, Lambda(x)) -> canonic (Prod(Lambda(x), (canonic g)))
    | Prod(Lambda(l), g) -> Prod(Lambda(l), (canonic g))
    | Prod(f,g) -> Prod((canonic f), (canonic g))
    | Inv(Inv(f)) -> f
    | Inv(Lambda(0.)) -> raise Not_defined
    | Inv(Lambda(x)) -> Lambda(1. /. x)
    | Inv(f) -> Inv((canonic f))
    | Power(f,0) -> Lambda(1.)
    | Power(f,1) -> canonic f
    | Power(f,-1) -> canonic (Inv(f))
    | Power(f,n) when n<0 -> canonic (Inv(Power(f, -n)))
    | Power(f,n) -> Power((canonic f), n)
    | Sum(f, Prod(Lambda(-1.), g) ) when f=g -> Lambda(0.)
    | Sum(f,g) -> Sum((canonic f), (canonic g))
    | Comp(Exp(Id(_)),Ln(f)) -> f
    | Comp(Ln(f),Exp(Id(_))) -> f
    | Comp(f,g) -> Comp((canonic f), (canonic g))
    | u -> u
;;


(**

Pretty printing a function.

*)


let print = Format.printf;;
let sprint = Format.sprintf;;
let fprint = Printf.fprintf;;

(** To a string without the variable name. *)

let rec string_of_funct_novar = function
    | Id(x) -> ""
    | Lambda(l) -> sprint "%g" l
    | Sin(Id(_)) -> "sin"
    | Cos(Id(_)) -> "cos"
    | Exp(Id(_)) -> "exp"
    | Ln(Id(_)) -> "ln"
    | Sin(f) -> sprint "sin(%s)" (string_of_funct_novar f)
    | Cos(f) -> sprint "cos(%s)" (string_of_funct_novar f)
    | Exp(f) -> sprint "exp(%s)" (string_of_funct_novar f)
    | Ln(f) -> sprint "ln(%s)" (string_of_funct_novar f)
    | Prod(Lambda(0.), g) -> "0"
    | Prod(Lambda(1.), g) -> (string_of_funct_novar g)
    | Prod(Lambda(-1.), g) -> sprint "-(%s)" (string_of_funct_novar g)
    | Prod(Lambda(l), g) -> sprint "%g(%s)" l (string_of_funct_novar g)
    | Prod(f,g) -> sprint "(%s).(%s)" (string_of_funct_novar f) (string_of_funct_novar g)
    | Inv(f) -> sprint "(1/(%s))" (string_of_funct_novar f)
    | Sum(f,g) -> sprint "(%s) + (%s)" (string_of_funct_novar f) (string_of_funct_novar g)
    | Power(f,0) -> "1"
    | Power(f,1) -> (string_of_funct_novar f)
    | Power(f,-1) -> sprint "(1/(%s))" (string_of_funct_novar f)
    | Power(f,n) when n<0 -> sprint "(%s)^(%i)" (string_of_funct_novar f) n
    | Power(f,n) -> sprint "(%s)^%i" (string_of_funct_novar f) n
    | Comp(f,g) -> sprint "%s(%s)" (string_of_funct_novar f) (string_of_funct_novar g)
    | Unknown(s) -> s
;;

(** To a string with the variable name. *)

let rec string_of_funct = function
    | Id(x) -> x
    | Lambda(l) -> sprint  "%g" l
    | Sin(f) -> sprint "sin(%s)" (string_of_funct f)
    | Cos(f) -> sprint "cos(%s)" (string_of_funct f)
    | Exp(f) -> sprint "exp(%s)" (string_of_funct f)
    | Ln(f) -> sprint "ln(%s)" (string_of_funct f)
    | Prod(Lambda(0.), g) -> "0"
    | Prod(Lambda(1.), g) -> (string_of_funct g)
    | Prod(Lambda(-1.), g) -> sprint "-(%s)" (string_of_funct g)
    | Prod(Lambda(l), g) -> sprint "%g(%s)" l (string_of_funct g)
    | Prod(f,g) -> sprint "(%s).(%s)" (string_of_funct f) (string_of_funct g)
    | Inv(Inv(f)) -> (string_of_funct f)
    | Inv(Id(x)) -> sprint "1/%s" x
    | Inv(f) -> sprint "(1/(%s))" (string_of_funct f)
    | Sum(Lambda(l),g) -> sprint "%g + (%s)" l (string_of_funct g)
    | Sum(g,Lambda(l)) -> sprint "(%s) + %g" (string_of_funct g) l
    | Sum(f,g) -> sprint "(%s) + (%s)" (string_of_funct f) (string_of_funct g)
    | Power(f,0) -> "1"
    | Power(f,1) -> (string_of_funct f)
    | Power(f,-1) -> sprint "(1/(%s))" (string_of_funct f)
    | Power(f,n) when n<0 -> sprint "(%s)^(%i)" (string_of_funct f) n
    | Power(f,n) -> sprint "(%s)^%i" (string_of_funct f) n
    | Comp(f,g) -> sprint "%s(%s)" (string_of_funct_novar f) (string_of_funct g)
    | Unknown(s) -> s
;;

(** Printing. *)

let print_funct_novar f = print_endline (string_of_funct_novar (canonic f));;

(** Printing. *)

let print_funct f = print_endline (string_of_funct (canonic f));;

let print_funct_fancy name x f = print " %s : %s --> %s\n" name x (string_of_funct (canonic f));;


(**

Examples.

*)


print "## First five functions :\n";;
print_funct_fancy "Identity" "x" x;;
print_funct_fancy "Sine" "x" fsin;;
print_funct_fancy "Cosine" "x" fcos;;
print_funct_fancy "Exponential" "x" fexp;;
print_funct_fancy "Logarithm" "x" fln;;

print "\n\n## Some other examples of functions :\n";;
print_funct_fancy "Identity" "y" y;;
print_funct_fancy "Constant" "y" fpi;;
print_funct_fancy "Poly1" "y" fpoly1;;
print_funct_fancy "cos o sin" "y" fcossin;;
print_funct_fancy "exp o ln" "y" fexpln;;
print_funct_fancy "Inverse" "y" finv;;
print_funct_fancy "Power 4" "y" fsqsq;;
print_funct_fancy "Unknown h" "y" fh;;


(**

Differentiate with formal rules.

*)


let rec differentiate_aux = function
    | Lambda(l) -> Lambda(0.)
    | Id(x) -> Lambda(1.)
    | Sin(f) -> Prod( (differentiate_aux f), Cos(f) )
    | Cos(f) -> Prod( (differentiate_aux f), ( Prod(Lambda(-1.), (Sin(f)))) )
    | Exp(f) -> Prod( (differentiate_aux f), Exp(f) )
    | Ln(f) -> Prod( (differentiate_aux f), Inv(f) )
    | Prod(f,g) -> Sum( (Prod((differentiate_aux f), g)), (Prod(f, (differentiate_aux g))) )
    | Inv(f) -> Prod( ( Prod(Lambda(-1.), (differentiate_aux f)) ), (Power(f, -2)) )
    | Sum(f,g) -> Sum((differentiate_aux f), (differentiate_aux g))
    | Power(f,0) -> Lambda(0.)
    | Power(f,1) -> differentiate_aux f
    | Power(f,n) -> Prod( (Prod( (Lambda(float_of_int n)), (differentiate_aux f) )), (Power( f, n-1 )) )
    | Comp(f,g) -> Prod( (differentiate_aux g), (Comp( (differentiate_aux f), g )) )
    | Unknown(s) -> Unknown( (sprint "%s'" s) )
;;

(** Final value, when we put in simplify f as much as possible before differentiating. *)

let differentiate f = (canonic (differentiate_aux (canonic f)));;

(**

Examples.

*)


print "\n\n## Differential of the first five functions :\n";;

print_funct_fancy "Differential of Identity" "x" (differentiate x);;
print_funct_fancy "Differential of Sine" "x" (differentiate fsin);;
print_funct_fancy "Differential of Cosine" "x" (differentiate fcos);;
print_funct_fancy "Differential of Exponential" "x" (differentiate fexp);;
print_funct_fancy "Differential of Logarithm" "x" (differentiate fln);;


print "\n\n## Differential of some other examples of functions :\n";;

print_funct_fancy "Differential of Identity" "y" (differentiate y);;
print_funct_fancy "Differential of Constant" "y" (differentiate fpi);;
print_funct_fancy "Differential of Poly1" "y" (differentiate fpoly1);;
print_funct_fancy "Differential of cos o sin" "y" (differentiate fcossin);;
print_funct_fancy "Differential of exp o ln" "y" (differentiate fexpln);;
print_funct_fancy "Differential of Inverse" "y" (differentiate finv);;
print_funct_fancy "Differential of Power 4" "y" (differentiate fsqsq);;
print_funct_fancy "Differential of Unknown h" "y" (differentiate fh);;


(** Fin *)

print "\n\nEnd of the tests, program in OCaml v4+ by Lilian Besson (ENS Cachan), for MA 101 at Mahindra École Centrale, (C) 2014.";;
print "\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n";;
GA|Analytics