Skip to content

Instantly share code, notes, and snippets.

@ericnormand
Last active August 23, 2019 15:52
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ericnormand/397337f77faa6b68815c29f60f532db0 to your computer and use it in GitHub Desktop.
Save ericnormand/397337f77faa6b68815c29f60f532db0 to your computer and use it in GitHub Desktop.

symbolic differentiation

I got excited last week by the numeric differentiator challenge. When I originally read that section of SICP so many years ago, I was elated. Do you know how hard that would be to do in Java?

Now for something that's even harder to do in Java: symbolic differentiation.

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

The challenge this week is to use four differentiation formulas to make a symbolic differentiator. It has been a while since I've done any calculus, so I had to look these up:

  1. dc/dx = 0 (for a constant c, or variable c different from x)
  2. dx/dx = 1
  3. d(u+v)/dx = du/dx + dv/dx (addition rule)
  4. d(uv)/dx = u(dv/dx) + v(du/dx) (product rule)

This won't differentiate every formula, but it will differentiate sums and products of variables and numbers. You should make a function that takes an expression and a variable to differentiate by, like so:

(deriv '(+ x 5) 'x) ;=> (+ 1 0)

Don't worry about simplifying the final expression. Just make sure it's right.

(ns symderiv.main)
(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}))))
(ns symderiv.main
(:require [meander.strategy.delta :as r]))
(defn deriv [expr var]
(let [operations (r/rewrite
; addition
(+ ?u ?v)
(+ ~(deriv ?u var) ~(deriv ?v var))
; multiplication
(* ?u ?v)
(+ (* ?u ~(deriv ?v var)) (* ?v ~(deriv ?u var))))
var' (r/pipe (r/pred #(= var %)) (r/build 1))
constant' (r/pipe (r/pred number?) (r/build 0))
other-var' (r/pipe (r/pred symbol?) (r/build 0))
choice (r/choice operations var' other-var' constant')]
(choice expr)))
(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]
(list '+ u v))
(defn prod? [expr]
(and (seq? expr)
(= '* (first expr))))
(defn ->prod [u v]
(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))))))
(defn sum? [exp]
(= (first exp) '+))
(defn product? [exp]
(= (first exp) '*))
(defn pred* [exp clauses]
(when clauses
(list 'if (list (first clauses) exp)
(second clauses)
(pred* exp (-> clauses next next)))))
(defmacro pred [exp & clauses]
(pred* exp clauses))
(defn deriv [exp v]
(pred exp
number? 0
symbol? (if (= exp v) 1 0)
sum? (list '+
(deriv (second exp) v)
(deriv (last exp) v))
product? (list '+
(list '*
(second exp)
(deriv (last exp) v))
(list '*
(deriv (second exp) v)
(last exp)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment