Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?

symbolic differentiation, pt 2

Now that we've got a basic symbolic differentiator (and the numeric one), we could simplify the expressions. SICP gives some basic simplifications.

  1. (+ 0 x) => x or additive identity
  2. (* 1 x) => x or multiplicative identity
  3. (+ 1 2) => 3 or constant simplification
  4. (* 3 2) => 6 or constant simplification

Here's the relevant link into SICP, for reference.

The challenge this week is to integrate these easy simplifications into the differentiator. If you didn't do last week's challenge, grab someone else's code and work from that.

(defn =number?
""
[expr num]
(and (number? expr)
(= expr num)))
(defn make-sum
[expr1 expr2]
(cond
(=number? expr1 0) expr2
(=number? expr2 0) expr1
(and (number? expr1)
(number? expr2)) (+ expr1 expr2)
:else (list '+ expr1 expr2)))
(defn make-product
[expr1 expr2]
(cond
(or (=number? expr1 0)
(=number? expr2 0)) 0
(=number? expr1 1) expr2
(=number? expr2 1) expr1
(and (number? expr1)
(number? expr2)) (* expr1 expr2)
:else (list '* expr1 expr2)))
(defn deriv
"symbolic differentiation.
issue: 339
question: https://gist.github.com/ericnormand/397337f77faa6b68815c29f60f532db0"
[expr v]
(cond
(list? expr) (let [[op a b] expr]
(case op
+ (make-sum (deriv a v)
(deriv b v))
* (make-sum (make-product a
(deriv b v))
(make-product b
(deriv a v)))
(throw
(ex-info
"unrecognized error"
{:op op}))))
(= expr v) 1
(or (number? expr)
(symbol? expr)) 0
:else (throw
(ex-info
"unrecognized expr"
{:expr expr}))))
(comment
(deriv '(+ x 5) 'x)
(deriv '(+ (* 7 x) (* x 5)) 'x)
(deriv '(+ (* 12 x) (* x 5)) 'x)
)
(ns symderiv.main
(:require [clojure.walk :as w]))
(defn deriv [expr v]
(cond
(list? expr) (let [[op a b] expr]
(case op
+ (list '+ (deriv a v) (deriv b v))
* (list '+ (list '* a (deriv b v)) (list '* b (deriv a v)))
(throw (ex-info "Unrecognized operator" {:op op}))))
(= expr v) 1
(or (number? expr) (symbol? expr)) 0
:else (throw (ex-info "Unrecognized expression" {:expr expr}))))
(defmulti simplify-op #(and (list? %) (= 3 (count %)) (first %)))
(defmethod simplify-op '+
[[_ a b :as expr]]
(cond
(and (number? a) (number? b)) (+ a b)
(= 0 a) b
(= 0 b) a
:else expr))
(defmethod simplify-op '*
[[_ a b :as expr]]
(cond
(and (number? a) (number? b)) (* a b)
(or (= 0 a) (= 0 b)) 0
(= 1 a) b
(= 1 b) a
:else expr))
(defmethod simplify-op :default
[expr]
expr)
(def simplify (partial w/postwalk simplify-op))
(defn variable? [v]
(symbol? v))
(defn same-variable? [v1 v2]
(and (variable? v1) (variable? v2) (= v1 v2)))
(defn sum? [expr]
(and (seq? expr)
(= '+ (first expr))))
(defn ->sum [u v]
(cond
(= 0 u)
v
(= 0 v)
u
(and (number? u) (number? v))
(+ u v)
:else
(list '+ u v)))
(defn prod? [expr]
(and (seq? expr)
(= '* (first expr))))
(defn ->prod [u v]
(cond
(= 0 u)
0
(= 0 v)
0
(= 1 u)
v
(= 1 v)
u
(and (number? u) (number? v))
(* u v)
:else
(list '* u v)))
(defn deriv [expr var]
(cond
(same-variable? expr var)
1
(or (variable? expr) (number? expr))
0
(sum? expr)
(let [[_ u v] expr]
(->sum (deriv u var) (deriv v var)))
(prod? expr)
(let [[_ u v] expr]
(->sum
(->prod u (deriv v var))
(->prod v (deriv u var))))))
(comment
(deriv '(+ (* x x) x 5) 'x)
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment