symbolic mathematics engine in OCaml with differentiation, integration, simplification, and numerical methods
1open Expr
2
3let rec to_latex ?(implicit_mult=true) = function
4 | Const f ->
5 if Float.is_integer f then
6 string_of_int (int_of_float f)
7 else
8 string_of_float f
9 | SymConst Pi -> "\\pi"
10 | SymConst E -> "e"
11 | Var s -> s
12 | Add (e1, e2) -> to_latex ~implicit_mult e1 ^ " + " ^ to_latex ~implicit_mult e2
13 | Sub (e1, e2) -> to_latex ~implicit_mult e1 ^ " - " ^ to_latex_paren ~implicit_mult e2
14 | Mul (e1, e2) ->
15 let sep = if implicit_mult && should_implicit e1 e2 then "\\," else "\\cdot" in
16 to_latex_mul ~implicit_mult e1 ^ sep ^ to_latex_mul ~implicit_mult e2
17 | Div (e1, e2) -> "\\frac{" ^ to_latex ~implicit_mult e1 ^ "}{" ^ to_latex ~implicit_mult e2 ^ "}"
18 | Pow (e1, e2) -> to_latex_atom ~implicit_mult e1 ^ "^{" ^ to_latex ~implicit_mult e2 ^ "}"
19 | Neg e -> "-" ^ to_latex_atom ~implicit_mult e
20 | Sin e -> "\\sin\\left(" ^ to_latex ~implicit_mult e ^ "\\right)"
21 | Cos e -> "\\cos\\left(" ^ to_latex ~implicit_mult e ^ "\\right)"
22 | Tan e -> "\\tan\\left(" ^ to_latex ~implicit_mult e ^ "\\right)"
23 | Sinh e -> "\\sinh\\left(" ^ to_latex ~implicit_mult e ^ "\\right)"
24 | Cosh e -> "\\cosh\\left(" ^ to_latex ~implicit_mult e ^ "\\right)"
25 | Tanh e -> "\\tanh\\left(" ^ to_latex ~implicit_mult e ^ "\\right)"
26 | Asin e -> "\\arcsin\\left(" ^ to_latex ~implicit_mult e ^ "\\right)"
27 | Acos e -> "\\arccos\\left(" ^ to_latex ~implicit_mult e ^ "\\right)"
28 | Atan e -> "\\arctan\\left(" ^ to_latex ~implicit_mult e ^ "\\right)"
29 | Atan2 (e1, e2) -> "\\text{atan2}\\left(" ^ to_latex ~implicit_mult e1 ^ ", " ^ to_latex ~implicit_mult e2 ^ "\\right)"
30 | Exp e -> "e^{" ^ to_latex ~implicit_mult e ^ "}"
31 | Ln e -> "\\ln\\left(" ^ to_latex ~implicit_mult e ^ "\\right)"
32 | Log (base_e, arg) -> "\\log_{" ^ to_latex ~implicit_mult base_e ^ "}\\left(" ^ to_latex ~implicit_mult arg ^ "\\right)"
33 | Sqrt e -> "\\sqrt{" ^ to_latex ~implicit_mult e ^ "}"
34 | Abs e -> "\\left|" ^ to_latex ~implicit_mult e ^ "\\right|"
35
36and should_implicit e1 e2 =
37 match (e1, e2) with
38 | (Const _ | SymConst _), (Var _ | Sin _ | Cos _ | Tan _ | Sinh _ | Cosh _ | Tanh _ |
39 Asin _ | Acos _ | Atan _ | Exp _ | Ln _ | Log _ | Sqrt _ | Abs _ | Pow _) -> true
40 | _ -> false
41
42and to_latex_atom ?(implicit_mult=true) = function
43 | (Const _ | SymConst _ | Var _) as e -> to_latex ~implicit_mult e
44 | e -> "\\left(" ^ to_latex ~implicit_mult e ^ "\\right)"
45
46and to_latex_mul ?(implicit_mult=true) = function
47 | (Const _ | SymConst _ | Var _ | Pow _ | Sin _ | Cos _ | Tan _ | Sinh _ | Cosh _ | Tanh _
48 | Asin _ | Acos _ | Atan _ | Atan2 _ | Exp _ | Ln _ | Log _ | Sqrt _ | Abs _) as e -> to_latex ~implicit_mult e
49 | e -> "\\left(" ^ to_latex ~implicit_mult e ^ "\\right)"
50
51and to_latex_paren ?(implicit_mult=true) = function
52 | (Add _ | Sub _) as e -> "\\left(" ^ to_latex ~implicit_mult e ^ "\\right)"
53 | e -> to_latex ~implicit_mult e
54
55let to_graphviz expr =
56 let counter = ref 0 in
57 let next_id () =
58 incr counter;
59 !counter
60 in
61
62 let rec build_graph e =
63 let id = next_id () in
64 match e with
65 | Const f ->
66 let label = if Float.is_integer f then string_of_int (int_of_float f) else string_of_float f in
67 (id, Printf.sprintf " n%d [label=\"%s\" shape=oval];\n" id label, "")
68 | SymConst Pi -> (id, Printf.sprintf " n%d [label=\"π\" shape=oval];\n" id, "")
69 | SymConst E -> (id, Printf.sprintf " n%d [label=\"e\" shape=oval];\n" id, "")
70 | Var v -> (id, Printf.sprintf " n%d [label=\"%s\" shape=oval];\n" id v, "")
71 | Add (e1, e2) -> build_binary id "+" e1 e2
72 | Sub (e1, e2) -> build_binary id "-" e1 e2
73 | Mul (e1, e2) -> build_binary id "*" e1 e2
74 | Div (e1, e2) -> build_binary id "/" e1 e2
75 | Pow (e1, e2) -> build_binary id "^" e1 e2
76 | Neg e -> build_unary id "-" e
77 | Sin e -> build_unary id "sin" e
78 | Cos e -> build_unary id "cos" e
79 | Tan e -> build_unary id "tan" e
80 | Sinh e -> build_unary id "sinh" e
81 | Cosh e -> build_unary id "cosh" e
82 | Tanh e -> build_unary id "tanh" e
83 | Asin e -> build_unary id "asin" e
84 | Acos e -> build_unary id "acos" e
85 | Atan e -> build_unary id "atan" e
86 | Atan2 (e1, e2) -> build_binary id "atan2" e1 e2
87 | Exp e -> build_unary id "exp" e
88 | Ln e -> build_unary id "ln" e
89 | Log (e1, e2) -> build_binary id "log" e1 e2
90 | Sqrt e -> build_unary id "sqrt" e
91 | Abs e -> build_unary id "abs" e
92
93 and build_unary id op e =
94 let (eid, enodes, eedges) = build_graph e in
95 let node = Printf.sprintf " n%d [label=\"%s\" shape=diamond];\n" id op in
96 let edge = Printf.sprintf " n%d -> n%d;\n" id eid in
97 (id, node ^ enodes, edge ^ eedges)
98
99 and build_binary id op e1 e2 =
100 let (id1, nodes1, edges1) = build_graph e1 in
101 let (id2, nodes2, edges2) = build_graph e2 in
102 let node = Printf.sprintf " n%d [label=\"%s\" shape=box];\n" id op in
103 let edge1 = Printf.sprintf " n%d -> n%d [label=\"L\"];\n" id id1 in
104 let edge2 = Printf.sprintf " n%d -> n%d [label=\"R\"];\n" id id2 in
105 (id, node ^ nodes1 ^ nodes2, edge1 ^ edge2 ^ edges1 ^ edges2)
106 in
107
108 let (_, nodes, edges) = build_graph expr in
109 "digraph expr {\n" ^ nodes ^ edges ^ "}\n"