Skip to content

Instantly share code, notes, and snippets.

@ericnormand
Last active August 30, 2019 19:48
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save ericnormand/7935bebce9fbbfbb15f36063063172b3 to your computer and use it in GitHub Desktop.

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