symbolic mathematics engine in OCaml with differentiation, integration, simplification, and numerical methods
1type expr =
2 | Const of float
3 | Var of string
4 | Add of expr * expr
5 | Sub of expr * expr
6 | Mul of expr * expr
7 | Div of expr * expr
8 | Pow of expr * expr
9 | Neg of expr
10 | Sin of expr
11 | Cos of expr
12 | Tan of expr
13 | Exp of expr
14 | Ln of expr
15
16let rec to_string = function
17 | Const f ->
18 if Float.is_integer f then
19 string_of_int (int_of_float f)
20 else
21 string_of_float f
22 | Var s -> s
23 | Add (e1, e2) -> to_string_add e1 ^ " + " ^ to_string_add e2
24 | Sub (e1, e2) -> to_string e1 ^ " - " ^ to_string_paren e2
25 | Mul (e1, e2) -> to_string_mul e1 ^ "*" ^ to_string_mul e2
26 | Div (e1, e2) -> to_string_div e1 ^ "/" ^ to_string_paren e2
27 | Pow (e1, e2) -> to_string_atom e1 ^ "^" ^ to_string_pow_exp e2
28 | Neg e -> "-" ^ to_string_atom e
29 | Sin e -> "sin(" ^ to_string e ^ ")"
30 | Cos e -> "cos(" ^ to_string e ^ ")"
31 | Tan e -> "tan(" ^ to_string e ^ ")"
32 | Exp e -> "e^" ^ to_string_pow_exp e
33 | Ln e -> "ln(" ^ to_string e ^ ")"
34
35and to_string_atom = function
36 | (Const _ | Var _ | Sin _ | Cos _ | Tan _ | Exp _ | Ln _) as e -> to_string e
37 | e -> "(" ^ to_string e ^ ")"
38
39and to_string_mul = function
40 | (Const _ | Var _ | Pow _ | Sin _ | Cos _ | Tan _ | Exp _ | Ln _ | Mul _) as e -> to_string e
41 | e -> "(" ^ to_string e ^ ")"
42
43and to_string_div = function
44 | (Const _ | Var _ | Pow _ | Sin _ | Cos _ | Tan _ | Exp _ | Ln _ | Mul _) as e -> to_string e
45 | e -> "(" ^ to_string e ^ ")"
46
47and to_string_add = function
48 | (Add _ | Sub _) as e -> to_string e
49 | e -> to_string e
50
51and to_string_pow_exp = function
52 | (Const _ | Var _ | Pow _) as e -> to_string e
53 | e -> "(" ^ to_string e ^ ")"
54
55and to_string_paren = function
56 | (Add _ | Sub _) as e -> "(" ^ to_string e ^ ")"
57 | e -> to_string e
58
59let rec simplify = function
60 | Const _ as c -> c
61 | Var _ as v -> v
62 | Add (e1, e2) -> simplify_add (simplify e1) (simplify e2)
63 | Sub (e1, e2) -> simplify_sub (simplify e1) (simplify e2)
64 | Mul (e1, e2) -> simplify_mul (simplify e1) (simplify e2)
65 | Div (e1, e2) -> simplify_div (simplify e1) (simplify e2)
66 | Pow (e1, e2) -> simplify_pow (simplify e1) (simplify e2)
67 | Neg e -> simplify_neg (simplify e)
68 | Sin e -> Sin (simplify e)
69 | Cos e -> Cos (simplify e)
70 | Tan e -> Tan (simplify e)
71 | Exp e -> simplify_exp (simplify e)
72 | Ln e -> Ln (simplify e)
73
74and simplify_add e1 e2 =
75 match (e1, e2) with
76 | Const 0.0, e | e, Const 0.0 -> e
77 | Const a, Const b -> Const (a +. b)
78 | _ -> Add (e1, e2)
79
80and simplify_sub e1 e2 =
81 match (e1, e2) with
82 | e, Const 0.0 -> e
83 | Const a, Const b -> Const (a -. b)
84 | _ -> Sub (e1, e2)
85
86and simplify_mul e1 e2 =
87 match (e1, e2) with
88 | Const 0.0, _ | _, Const 0.0 -> Const 0.0
89 | Const 1.0, e | e, Const 1.0 -> e
90 | Const a, Const b -> Const (a *. b)
91 | Const a, Mul (Const b, e) -> simplify_mul (Const (a *. b)) e
92 | Mul (Const a, e), Const b -> simplify_mul (Const (a *. b)) e
93 | Const a, Mul (e1, Mul (Const b, e2)) ->
94 simplify_mul (Const (a *. b)) (Mul (e1, e2))
95 | Mul (Const a, e1), Mul (Const b, e2) ->
96 simplify_mul (Const (a *. b)) (Mul (e1, e2))
97 | (Sin _ | Cos _ | Tan _ | Exp _ | Ln _ | Pow _), Var _ ->
98 Mul (e2, e1)
99 | _ -> Mul (e1, e2)
100
101and simplify_div e1 e2 =
102 match (e1, e2) with
103 | Const 0.0, _ -> Const 0.0
104 | e, Const 1.0 -> e
105 | Const a, Const b -> Const (a /. b)
106 | e1, e2 when e1 = e2 -> Const 1.0
107 | _ -> Div (e1, e2)
108
109and simplify_pow e1 e2 =
110 match (e1, e2) with
111 | _, Const 0.0 -> Const 1.0
112 | e, Const 1.0 -> e
113 | Const 0.0, _ -> Const 0.0
114 | Const 1.0, _ -> Const 1.0
115 | Const a, Const b -> Const (a ** b)
116 | _ -> Pow (e1, e2)
117
118and simplify_neg = function
119 | Const c -> Const (-.c)
120 | Neg e -> e
121 | e -> Neg e
122
123and simplify_exp = function
124 | Const 0.0 -> Const 1.0
125 | Ln e -> e
126 | e -> Exp e
127
128let rec diff var = function
129 | Const _ -> Const 0.0
130 | Var v -> if v = var then Const 1.0 else Const 0.0
131 | Add (e1, e2) -> simplify (Add (diff var e1, diff var e2))
132 | Sub (e1, e2) -> simplify (Sub (diff var e1, diff var e2))
133 | Mul (e1, e2) ->
134 simplify (Add (Mul (diff var e1, e2), Mul (e1, diff var e2)))
135 | Div (e1, e2) ->
136 let num = Sub (Mul (diff var e1, e2), Mul (e1, diff var e2)) in
137 let den = Pow (e2, Const 2.0) in
138 simplify (Div (num, den))
139 | Pow (e, Const n) ->
140 simplify (Mul (Mul (Const n, Pow (e, Const (n -. 1.0))), diff var e))
141 | Pow (e1, e2) ->
142 let term1 = Mul (e2, Mul (Pow (e1, Sub (e2, Const 1.0)), diff var e1)) in
143 let term2 = Mul (Pow (e1, e2), Mul (Ln e1, diff var e2)) in
144 simplify (Add (term1, term2))
145 | Neg e -> simplify (Neg (diff var e))
146 | Sin e -> simplify (Mul (Cos e, diff var e))
147 | Cos e -> simplify (Neg (Mul (Sin e, diff var e)))
148 | Tan e ->
149 let sec2 = Div (Const 1.0, Pow (Cos e, Const 2.0)) in
150 simplify (Mul (sec2, diff var e))
151 | Exp e -> simplify (Mul (Exp e, diff var e))
152 | Ln e -> simplify (Div (diff var e, e))
153
154let rec diff_n var n expr =
155 if n <= 0 then expr
156 else diff_n var (n - 1) (diff var expr)
157
158let partial vars expr =
159 List.fold_left (fun e v -> diff v e) expr vars
160
161let rec to_latex = function
162 | Const f ->
163 if Float.is_integer f then
164 string_of_int (int_of_float f)
165 else
166 string_of_float f
167 | Var s -> s
168 | Add (e1, e2) -> to_latex e1 ^ " + " ^ to_latex e2
169 | Sub (e1, e2) -> to_latex e1 ^ " - " ^ to_latex_paren_latex e2
170 | Mul (e1, e2) -> to_latex_mul_latex e1 ^ to_latex_mul_latex e2
171 | Div (e1, e2) -> "\\frac{" ^ to_latex e1 ^ "}{" ^ to_latex e2 ^ "}"
172 | Pow (e1, e2) -> to_latex_atom_latex e1 ^ "^{" ^ to_latex e2 ^ "}"
173 | Neg e -> "-" ^ to_latex_atom_latex e
174 | Sin e -> "\\sin(" ^ to_latex e ^ ")"
175 | Cos e -> "\\cos(" ^ to_latex e ^ ")"
176 | Tan e -> "\\tan(" ^ to_latex e ^ ")"
177 | Exp e -> "e^{" ^ to_latex e ^ "}"
178 | Ln e -> "\\ln(" ^ to_latex e ^ ")"
179
180and to_latex_atom_latex = function
181 | (Const _ | Var _) as e -> to_latex e
182 | e -> "(" ^ to_latex e ^ ")"
183
184and to_latex_mul_latex = function
185 | (Const _ | Var _ | Pow _ | Sin _ | Cos _ | Tan _ | Exp _ | Ln _) as e -> to_latex e
186 | e -> "(" ^ to_latex e ^ ")"
187
188and to_latex_paren_latex = function
189 | (Add _ | Sub _) as e -> "(" ^ to_latex e ^ ")"
190 | e -> to_latex e
191
192let rec eval env = function
193 | Const f -> f
194 | Var v ->
195 (try List.assoc v env
196 with Not_found -> failwith ("unbound variable: " ^ v))
197 | Add (e1, e2) -> eval env e1 +. eval env e2
198 | Sub (e1, e2) -> eval env e1 -. eval env e2
199 | Mul (e1, e2) -> eval env e1 *. eval env e2
200 | Div (e1, e2) -> eval env e1 /. eval env e2
201 | Pow (e1, e2) -> eval env e1 ** eval env e2
202 | Neg e -> -.(eval env e)
203 | Sin e -> sin (eval env e)
204 | Cos e -> cos (eval env e)
205 | Tan e -> tan (eval env e)
206 | Exp e -> exp (eval env e)
207 | Ln e -> log (eval env e)