A clojure library for symbolic manipulation of Algebraic Expressions.
(solve 'blue
(ex (= pencils (+ green white blue red)))
(ex (= (/ pencils 10) green))
(ex (= (/ pencils 2) white))
(ex (= (/ pencils 4) blue))
(ex (= red 45))) ;=> #{{blue 75N}}
expresso aims to be a general library for manipulating mathematical expressions. This are the key objectives:
- Enable mathematical expressions to be encoded
- Provide a powerful facility for general expression manipulation (aka term rewriting)
- Provide a range of very useful manipulations, which include
- simplifying mathematical expressions
- solving a (set of) equations in regard to unknowns
- differentiating expressions
- optimizing of an expression for performance
- compiling an expression to a clojure function
- Be extensible through adding domain knowledge
- Full compatibility with core.matrix
Add the following line to your leiningen dependencies:
[expresso "0.2.4"]
For an in-depth tutorial and showcase of expresso, see the expresso tutorial
expresso's expressions are just normal clojure s-expressions. expresso has various convenience functions/macros for creating expressions:
;;the ex macro constructs an expression with automatic quoting of variables
(let [x 3]
(ex (+ x ~x))) ;=> (+ x 3)
;;the ex' macro constructs an expression with explicit quoting of variables
(let [x 3]
(ex' (+ x 'x))) ;=> (+ 3 x)
;;all functions in the core namespace also support pure s-exp as parameters
(solve '[x] '(= (+ 1 x) 3)) ;=> #{2}
(use 'numeric.expresso.core)
(simplify (ex (+ (* 4 a) (* 3 a) (* -1 (* 7 a)))))
=> 0
(def F1 (ex (= Y (+ X Z)))
(def F2 (ex (= X [1 2 3]))
(def F3 (ex (= Z (* 2.0 X)))
(solve [Y] F1 F2 F3)
=> #{[3.0 6.0 9.0]}
(def opt (optimize (ex (+ b (* (+ 5 b) (** y (+ a b)) (** z (+ b a)))))))
opt
=> (let [local478813 (+ a b)] (+ (* b 6) (** y local478813) (** z local478813)))
(def f (compile-expr [a b y z] opt))
(f 1 2 3 4)
=> 103.0
The public API is in numeric.expresso.core - go test it out!
Expresso supports a powerful way to manipulate expressions with rewrite rules, which are built ontop of core.logic. Here are a few example rules
(use 'numeric.expresso.rules)
;;?&* matches zero or more items
(def r [(rule (ex (* 0 ?&*)) :=> 0)
(rule (ex (* 1 ?&*)) :=> (ex (* ?&*)))
;;supports optional guard with :if
(rule (ex (/ ?x ?x)) :=> 1 :if (guard (not= ?x 0)))])
;;rules match semantically. because * is commutative the rules match regardless of the order of arguments
(transform-expression r (ex (+ (* 2 (/ 4 4) 3) a (* 4 0))))
=> (+ (* 2 3) a 0)
;;The right hand side and guard of a rule can be 'arbitrary core.logic relations'.
;;The trans and guard macro create suitable core.logic relations out of normal clojure code.
;;==> is a shorthand for trans
(apply-rule (rule (ex (map ?f ?coll)) :==> (map ?f ?coll) :if (guard (coll? ?coll)))
(ex (map ~inc [1 2 3]))) ;=> (2 3 4)
This library is ready to use, but still in an early state so you might find some bugs. Any bug reports/feature recommendations are very welcome.
Currently, expresso is well suited for manipulations of symbolic expressions in clojure, it also has some support for solving equations, etc what you excpect from a computer algebra system. However it is still far from a full featured CAS System like Maxima. Any contributions to help it become a real clojure Computer Algebra System are very welcome!
In the short term I am hoping to get symbolic matrices working soon. That means having an expression as a core.matrix implementation. This will be possible with the generic core.matrix api which is currently in development.
Also I am planning to replace the current rule based engine with a more faster one and to have proper compilation from rules to fast clojure functions. Kovas Boguta has made some very interesting experiments in this direction with his combinator project.