I have coded up the adaptive Simpson integration algorithm below, including a 
contract. The algorithm has been mentioned recently on 'users'. Any comments 
from the numerically inclined? -- Matthias




#lang racket

(require rackunit)

;; -----------------------------------------------------------------------------
;; interface, with contract 

;; Simpson's rule for approximating an integral
(define (simpson f L R)
  (* (/ (- R L) 6) (+ (f L) (* 4 (f (mid L R))) (f R))))

(define (mid L R) (/ (+ L R) 2.))

;; adaptive Simpson limits approximate error to 15 ε
(define (simpson-error-estimate f L R ε)
  [define M (mid L R)]
  (< (abs (+ (simpson f L M) (+ simpson f M R) (- (simpson f L R)))) (* 15 ε)))
  
(provide/contract
 [adaptive-simpson 
  (->i ((f (-> real? real?))) (#:epsilon (ε real?)) 
       (r (f ε)
          (->i ((L real?) (R  (L) (and/c real? (>/c L)))) () (r real?)
               ;; adds six function calls to f, which seems tolerable 
               #:post (L R) (simpson-error-estimate f L R ε))))])

;; -----------------------------------------------------------------------------
;; implementation 

(define (adaptive-simpson f #:epsilon [ε .000000001])
  (lambda (L R)
    (define f@L (f L))
    (define f@R (f R))
    (define-values (M f@M whole) (simpson-1call-to-f f L f@L R f@R))
    (asr f L f@L R f@R ε whole M f@M)))

;; computationally efficient: 2 function calls per step 
(define (asr f L f@L R f@R ε whole M f@M)
  (define-values (leftM  f@leftM  left*)  (simpson-1call-to-f f L f@L M f@M))
  (define-values (rightM f@rightM right*) (simpson-1call-to-f f M f@M R f@R))
  (define delta* (- (+ left* right*) whole))
  (cond
    [(<= (abs delta*) (* 15 ε)) (+ left* right* (/ delta* whole 15))]
    [else (define epsilon1 (/ ε 2))
          (+ (asr f L f@L M f@M epsilon1 left*  leftM  f@leftM) 
             (asr f M f@M R f@R epsilon1 right* rightM f@rightM))]))

(define (simpson-1call-to-f f L f@L R f@R)
  (define M (mid L R))
  (define f@M (f M))
  (values M f@M (* (/ (abs (- R L)) 6) (+ f@L (* 4 f@M) f@R))))

;; simplistic prototype: many calls to f per step 
(define (asr.v0 f L R ε whole)
  (define M  (mid L R))
  (define left*  (simpson f L M))
  (define right* (simpson f M R))
  (define delta* (- (+ left* right*) whole))
  (cond
    [(<= (abs delta*) (* 15 ε)) (+ left* right* (/ delta* whole 15))]
    [else (define epsilon1 (/ ε 2))
          (+ (asr.v0 f L M epsilon1 left*) (asr.v0 f M R epsilon1 right*))]))

;; -----------------------------------------------------------------------------
;; examples
(define int-const=5  (adaptive-simpson (lambda (x) 5)))
(check-equal? (int-const=5 0 1) 5.0)
(check-equal? (int-const=5 0 10) 50.0)

(define int-identity (adaptive-simpson values))
(check-equal? (int-identity 0 1) .5)

(define step (lambda (x) (if (< x 1) (sin x) 1)))
(define int-step (adaptive-simpson step))

(int-step 0 2)


____________________
  Racket Users list:
  http://lists.racket-lang.org/users

Reply via email to