symbolic mathematics engine in OCaml with differentiation, integration, simplification, and numerical methods
17

Configure Feed

Select the types of activity you want to include in your feed.

initial commit

author vm.fail date (Jan 19, 2026, 1:43 AM UTC) commit 39f239fb
+544
+4
.gitignore
··· 1 + _build/ 2 + *.install 3 + *.merlin 4 + .DS_Store
+19
Makefile
··· 1 + .PHONY: build test clean install repl 2 + 3 + build: 4 + dune build 5 + 6 + test: 7 + dune test 8 + 9 + clean: 10 + dune clean 11 + 12 + install: 13 + dune install 14 + 15 + repl: 16 + dune exec leibniz-repl 17 + 18 + example: 19 + dune exec examples/basic.exe
+31
README.md
··· 1 + # leibniz 2 + 3 + symbolic differentiation engine in OCaml with simplification 4 + 5 + ## building 6 + 7 + ```bash 8 + dune build 9 + ``` 10 + 11 + ## repl 12 + 13 + ```bash 14 + dune exec leibniz-repl 15 + ``` 16 + 17 + ## usage 18 + 19 + ```ocaml 20 + open Leibniz 21 + 22 + let f = Parser.parse "x^3 * sin(x)" 23 + let df = Expr.diff "x" f 24 + let () = print_endline (Expr.to_string df) 25 + 26 + let ddf = Expr.diff "x" df 27 + let () = print_endline (Expr.to_string ddf) 28 + 29 + let value = Expr.eval [("x", 2.0)] df 30 + let () = print_endline (string_of_float value) 31 + ```
+4
bin/dune
··· 1 + (executable 2 + (name repl) 3 + (public_name leibniz-repl) 4 + (libraries leibniz))
+77
bin/repl.ml
··· 1 + open Leibniz 2 + 3 + let print_help () = 4 + print_endline "leibniz - symbolic differentiation engine"; 5 + print_endline ""; 6 + print_endline "commands:"; 7 + print_endline " diff <var> <expr> - differentiate expression"; 8 + print_endline " partial <var1,var2> <expr> - partial derivative"; 9 + print_endline " simplify <expr> - simplify expression"; 10 + print_endline " eval <var=val,...> <expr> - evaluate numerically"; 11 + print_endline " latex <expr> - output latex format"; 12 + print_endline " help - show this help"; 13 + print_endline " quit - exit"; 14 + print_endline "" 15 + 16 + let parse_env str = 17 + let pairs = String.split_on_char ',' str in 18 + List.map (fun p -> 19 + match String.split_on_char '=' p with 20 + | [var; value] -> (String.trim var, float_of_string (String.trim value)) 21 + | _ -> failwith "invalid environment format" 22 + ) pairs 23 + 24 + let () = 25 + print_help (); 26 + 27 + let rec loop () = 28 + print_string "> "; 29 + flush stdout; 30 + try 31 + let line = read_line () in 32 + let parts = String.split_on_char ' ' line in 33 + match parts with 34 + | [] -> loop () 35 + | "quit" :: _ -> () 36 + | "help" :: _ -> print_help (); loop () 37 + | "diff" :: var :: rest -> 38 + let expr_str = String.concat " " rest in 39 + let expr = Parser.parse expr_str in 40 + let result = Expr.diff var expr in 41 + print_endline (Expr.to_string result); 42 + loop () 43 + | "partial" :: vars :: rest -> 44 + let var_list = String.split_on_char ',' vars in 45 + let expr_str = String.concat " " rest in 46 + let expr = Parser.parse expr_str in 47 + let result = List.fold_left (fun e v -> Expr.diff (String.trim v) e) expr var_list in 48 + print_endline (Expr.to_string result); 49 + loop () 50 + | "simplify" :: rest -> 51 + let expr_str = String.concat " " rest in 52 + let expr = Parser.parse expr_str in 53 + let result = Expr.simplify expr in 54 + print_endline (Expr.to_string result); 55 + loop () 56 + | "eval" :: env_str :: rest -> 57 + let env = parse_env env_str in 58 + let expr_str = String.concat " " rest in 59 + let expr = Parser.parse expr_str in 60 + let result = Expr.eval env expr in 61 + print_endline (string_of_float result); 62 + loop () 63 + | "latex" :: rest -> 64 + let expr_str = String.concat " " rest in 65 + let expr = Parser.parse expr_str in 66 + print_endline (Expr.to_latex expr); 67 + loop () 68 + | _ -> 69 + print_endline "unknown command (type 'help' for commands)"; 70 + loop () 71 + with 72 + | End_of_file -> () 73 + | e -> 74 + print_endline ("error: " ^ Printexc.to_string e); 75 + loop () 76 + in 77 + loop ()
+13
dune-project
··· 1 + (lang dune 3.0) 2 + (name leibniz) 3 + (version 0.1.0) 4 + 5 + (generate_opam_files true) 6 + 7 + (package 8 + (name leibniz) 9 + (synopsis "Symbolic differentiation engine with intelligent simplification") 10 + (depends 11 + (ocaml (>= 4.14)) 12 + dune 13 + (ounit2 :with-test)))
+24
leibniz.opam
··· 1 + # This file is generated by dune, edit dune-project instead 2 + opam-version: "2.0" 3 + version: "0.1.0" 4 + synopsis: "Symbolic differentiation engine with intelligent simplification" 5 + depends: [ 6 + "ocaml" {>= "4.14"} 7 + "dune" {>= "3.0"} 8 + "ounit2" {with-test} 9 + "odoc" {with-doc} 10 + ] 11 + build: [ 12 + ["dune" "subst"] {dev} 13 + [ 14 + "dune" 15 + "build" 16 + "-p" 17 + name 18 + "-j" 19 + jobs 20 + "@install" 21 + "@runtest" {with-test} 22 + "@doc" {with-doc} 23 + ] 24 + ]
+3
lib/dune
··· 1 + (library 2 + (name leibniz) 3 + (public_name leibniz))
+207
lib/expr.ml
··· 1 + type 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 + 16 + let 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 + 35 + and to_string_atom = function 36 + | (Const _ | Var _ | Sin _ | Cos _ | Tan _ | Exp _ | Ln _) as e -> to_string e 37 + | e -> "(" ^ to_string e ^ ")" 38 + 39 + and 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 + 43 + and 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 + 47 + and to_string_add = function 48 + | (Add _ | Sub _) as e -> to_string e 49 + | e -> to_string e 50 + 51 + and to_string_pow_exp = function 52 + | (Const _ | Var _ | Pow _) as e -> to_string e 53 + | e -> "(" ^ to_string e ^ ")" 54 + 55 + and to_string_paren = function 56 + | (Add _ | Sub _) as e -> "(" ^ to_string e ^ ")" 57 + | e -> to_string e 58 + 59 + let 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 + 74 + and 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 + 80 + and 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 + 86 + and 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 + 101 + and 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 + 109 + and 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 + 118 + and simplify_neg = function 119 + | Const c -> Const (-.c) 120 + | Neg e -> e 121 + | e -> Neg e 122 + 123 + and simplify_exp = function 124 + | Const 0.0 -> Const 1.0 125 + | Ln e -> e 126 + | e -> Exp e 127 + 128 + let 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 + 154 + let rec diff_n var n expr = 155 + if n <= 0 then expr 156 + else diff_n var (n - 1) (diff var expr) 157 + 158 + let partial vars expr = 159 + List.fold_left (fun e v -> diff v e) expr vars 160 + 161 + let 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 + 180 + and to_latex_atom_latex = function 181 + | (Const _ | Var _) as e -> to_latex e 182 + | e -> "(" ^ to_latex e ^ ")" 183 + 184 + and 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 + 188 + and to_latex_paren_latex = function 189 + | (Add _ | Sub _) as e -> "(" ^ to_latex e ^ ")" 190 + | e -> to_latex e 191 + 192 + let 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)
+44
lib/lexer.ml
··· 1 + type token = 2 + | Num of float 3 + | Var of string 4 + | Ident of string 5 + | Plus | Minus | Star | Slash | Caret 6 + | LParen | RParen 7 + | EOF 8 + 9 + let is_digit c = c >= '0' && c <= '9' 10 + let is_alpha c = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') 11 + let is_alphanum c = is_alpha c || is_digit c 12 + 13 + let tokenize str = 14 + let len = String.length str in 15 + let rec aux i acc = 16 + if i >= len then List.rev (EOF :: acc) 17 + else 18 + match str.[i] with 19 + | ' ' | '\t' | '\n' -> aux (i + 1) acc 20 + | '+' -> aux (i + 1) (Plus :: acc) 21 + | '-' -> aux (i + 1) (Minus :: acc) 22 + | '*' -> aux (i + 1) (Star :: acc) 23 + | '/' -> aux (i + 1) (Slash :: acc) 24 + | '^' -> aux (i + 1) (Caret :: acc) 25 + | '(' -> aux (i + 1) (LParen :: acc) 26 + | ')' -> aux (i + 1) (RParen :: acc) 27 + | c when is_digit c || c = '.' -> 28 + let j = ref (i + 1) in 29 + while !j < len && (is_digit str.[!j] || str.[!j] = '.') do 30 + incr j 31 + done; 32 + let num_str = String.sub str i (!j - i) in 33 + aux !j (Num (float_of_string num_str) :: acc) 34 + | c when is_alpha c -> 35 + let j = ref (i + 1) in 36 + while !j < len && is_alphanum str.[!j] do 37 + incr j 38 + done; 39 + let id = String.sub str i (!j - i) in 40 + let tok = if !j < len && str.[!j] = '(' then Ident id else Var id in 41 + aux !j (tok :: acc) 42 + | c -> failwith (Printf.sprintf "unexpected character: %c" c) 43 + in 44 + aux 0 []
+111
lib/parser.ml
··· 1 + open Lexer 2 + open Expr 3 + 4 + type parse_state = { tokens : token list; mutable pos : int } 5 + 6 + let peek state = 7 + if state.pos < List.length state.tokens then 8 + List.nth state.tokens state.pos 9 + else EOF 10 + 11 + let advance state = 12 + state.pos <- state.pos + 1 13 + 14 + let expect state tok = 15 + if peek state = tok then advance state 16 + else failwith (Printf.sprintf "expected token") 17 + 18 + let rec parse_expr state = 19 + parse_additive state 20 + 21 + and parse_additive state = 22 + let left = ref (parse_multiplicative state) in 23 + while peek state = Plus || peek state = Minus do 24 + let op = peek state in 25 + advance state; 26 + let right = parse_multiplicative state in 27 + left := if op = Plus then Add (!left, right) else Sub (!left, right) 28 + done; 29 + !left 30 + 31 + and parse_multiplicative state = 32 + let left = ref (parse_power state) in 33 + while peek state = Star || peek state = Slash do 34 + let op = peek state in 35 + advance state; 36 + let right = parse_power state in 37 + left := if op = Star then Mul (!left, right) else Div (!left, right) 38 + done; 39 + !left 40 + 41 + and parse_power state = 42 + let left = parse_unary state in 43 + if peek state = Caret then begin 44 + advance state; 45 + let right = parse_power state in 46 + match left with 47 + | Var "e" -> Exp right 48 + | _ -> Pow (left, right) 49 + end else left 50 + 51 + and parse_unary state = 52 + match peek state with 53 + | Minus -> 54 + advance state; 55 + Neg (parse_unary state) 56 + | _ -> parse_primary state 57 + 58 + and parse_primary state = 59 + match peek state with 60 + | Num n -> 61 + advance state; 62 + Const n 63 + | Var v -> 64 + advance state; 65 + Var v 66 + | Ident "sin" -> 67 + advance state; 68 + expect state LParen; 69 + let arg = parse_expr state in 70 + expect state RParen; 71 + Sin arg 72 + | Ident "cos" -> 73 + advance state; 74 + expect state LParen; 75 + let arg = parse_expr state in 76 + expect state RParen; 77 + Cos arg 78 + | Ident "tan" -> 79 + advance state; 80 + expect state LParen; 81 + let arg = parse_expr state in 82 + expect state RParen; 83 + Tan arg 84 + | Ident "exp" -> 85 + advance state; 86 + expect state LParen; 87 + let arg = parse_expr state in 88 + expect state RParen; 89 + Exp arg 90 + | Ident "ln" -> 91 + advance state; 92 + expect state LParen; 93 + let arg = parse_expr state in 94 + expect state RParen; 95 + Ln arg 96 + | Ident "e" -> 97 + advance state; 98 + Const (exp 1.0) 99 + | LParen -> 100 + advance state; 101 + let expr = parse_expr state in 102 + expect state RParen; 103 + expr 104 + | _ -> failwith "unexpected token in primary" 105 + 106 + let parse str = 107 + let tokens = tokenize str in 108 + let state = { tokens; pos = 0 } in 109 + let expr = parse_expr state in 110 + if peek state = EOF then expr 111 + else failwith "unexpected tokens after expression"
+7
test_expr_output.ml
··· 1 + open Leibniz.Parser 2 + open Leibniz.Expr 3 + 4 + let () = 5 + let expr = parse "e^(x^2)" in 6 + let result = diff "x" expr in 7 + print_endline (to_string result)