CS 201: Derivatives

See https://courses.cs.washington.edu/courses/cse341/15wi/racket/deriv.rkt

A classic example of Scheme/Racket programs as data: symbolic differentiation. Adapted from "Structure and Interpretation of Computer Programs" Chapter 2. Somewhat modified: doesn't use constructor functions, and it separates out finding the derivative from simplifying expressions.

Includes unit tests.

The top-level function deriv takes an expression and a variable, and returns the derivative of that expression with respect to the variable, in simplified form

In [22]:
(require racket)
(require racket/base)
In [2]:
(define (deriv exp var)
  (simplify (basic-deriv exp var)))

basic-deriv takes the derivative of an expression exp with respect to a variable and returns the result without simplification

In [3]:
(define (basic-deriv exp var)
  (cond ((number? exp) 0)
        ((symbol? exp)
         (if (eq? exp var) 1 0))
        ((sum? exp)
         (list '+ (basic-deriv (left exp) var) (basic-deriv (right exp) var)))
        ((product? exp)
         (list '+
          (list '* (left exp) (basic-deriv (right exp) var))
          (list '* (basic-deriv (left exp) var) (right exp))))
        (else (error "unknown expression type -- basic-deriv" exp))))

Predicates and access functions

Test whether a list structure is a sum

In [4]:
(define (sum? x)
  (and (pair? x) (eq? (car x) '+)))

Test whether a list structure is a product.

In [5]:
(define (product? x)
  (and (pair? x) (eq? (car x) '*)))

Get the left hand part of a sum or product

In [6]:
(define (left exp)
  (cadr exp))

Get the right hand part of a sum or product

In [7]:
(define (right exp)
  (caddr exp))

Basic simplification function (nothing too fancy ... doesn't know about commutativity or associativity)

In [8]:
(define (simplify exp)
  (cond ((sum? exp) (simplify-sum exp))
        ((product? exp) (simplify-product exp))
        ;; if we get here, we can't simplify exp
        (else exp)))
In [9]:
(define (simplify-sum exp)
  ;; to simplify a sum, we need to first recursively simplify the left and right parts
  (let ((a (simplify (left exp)))
        (b (simplify (right exp))))
    (cond ((equal? 0 a) b)
          ((equal? 0 b) a)
          ((and (number? a) (number? b)) (+ a b))
          (else (list '+ a b)))))
In [10]:
(define (simplify-product exp) 
  (let ((a (simplify (left exp)))
        (b (simplify (right exp))))
    (cond ((or (equal? 0 a) (equal? 0 b)) 0)
          ((equal? 1 a) b)
          ((equal? 1 b) a)
          ((and (number? a) (number? b)) (* a b))
          (else (list '* a b)))))

Unit tests

See http://docs.racket-lang.org/rackunit/quick-start.html for documentation on Racket's unit testing library.

In [11]:
(require rackunit)
In [12]:
(define deriv-tests 
  (test-suite 
   "tests for deriv program"
   (check-equal? (deriv 'x 'x) 1 "deriv of x wrt x")
   (check-equal? (deriv 'y 'x) 0 "deriv of y wrt x")
   (check-equal? (deriv '(+ x 3) 'x) 1 "deriv of (+ x 3) wrt x")
   (check-equal? (deriv '(* (+ 2 3) x) 'x) 5 "deriv of unsimplified expression")
   (check-equal? (deriv '(+ x y) 'x) 1 "deriv of (+ x y) wrt x")
   ;; simplification is not as clever as it could be in the following case:
   (check-equal? (deriv '(* (+ x 1) (+ x -1)) 'x) '(+ (+ x 1) (+ x -1)) "deriv of (* (+ x 1) (+ x -1)) wrt x")
   (check-equal? (deriv '(* (* x y) (+ x 3)) 'x) '(+ (* x y) (* y (+ x 3))) "complex deriv")
   ))
In [13]:
(require rackunit/text-ui)
;; this line runs the tests ....
(run-tests deriv-tests)
7 success(es) 0 failure(s) 0 error(s) 7 test(s) run
Out[13]:
0
$$ \frac{\mathrm{d}}{\mathrm{d}x}x = 0 $$

|

In [14]:
(deriv 'x 'x)
Out[14]:
1
$$ \frac{\mathrm{d}}{\mathrm{d}x}y = 1 $$
In [17]:
(deriv 'y 'x)
Out[17]:
0
$$ \frac{\mathrm{d}}{\mathrm{d}x} (x + 3) = 1 $$
In [18]:
(deriv '(+ x 3) 'x)
Out[18]:
1
$$ \frac{\mathrm{d}}{\mathrm{d}x} (x * (2 + 3)) = 5 $$
In [15]:
(deriv '(* x (+ 2 3)) 'x)
Out[15]:
5
$$ \frac{\mathrm{d}}{\mathrm{d}x} (x + y) = 1 $$
In [17]:
(deriv '(+ x y) 'x)
Out[17]:
1
In [16]:
(basic-deriv '(+ x y) 'x)
Out[16]:
'(+ 1 0)
$$ \frac{\mathrm{d}}{\mathrm{d}x} (x + 1)(x + 3) = (x + 1)(x - 1) $$
In [31]:
(deriv '(* (+ x 1) (+ x -1)) 'x)
Out[31]:
'(+ x x)
$$ \frac{\mathrm{d}}{\mathrm{d}x} (xy)(x + 3) = (xy(y(x + 3)) $$
In [20]:
(deriv '(* (* x y) (+ x 3)) 'x)
Out[20]:
'(+ (* x y) (* y (+ x 3)))

More Robust Derivatives

See calculus.rkt"> and fundamental.rkt

In [24]:
(require "calculus.rkt")
In [27]:
(deriv '(* (* x y) (+ x 3)) 'x)
Out[27]:
'(+ (* x y) (* y (+ x 3)))
In [28]:
(deriv '(sin x) 'x)
Out[28]:
'(cos x)
In [29]:
(deriv '(* x 2 x x 3) 'x)
Out[29]:
'(+ (* 6 x x) (* 6 x x) (* 6 x x))
In [30]:
(deriv '(+ (* 2 (** x 5)) (* 4 (** x 3))) 'x)
Out[30]:
'(+ (* 10 (** x 4)) (* 12 (** x 2)))
In [34]:
(deriv '(* (+ x 1) (+ x -1)) 'x)
Out[34]:
'(+ x x)
In [ ]: