-
Notifications
You must be signed in to change notification settings - Fork 3
/
integral-preprocessor.rkt
44 lines (41 loc) · 2.86 KB
/
integral-preprocessor.rkt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#lang s-exp rosette
(require "reactive-constants.rkt")
(provide integral-preprocessor symbolic-integral)
; The integral-preprocessor function takes the expression being integrated, e.g. (integral expr)
; plus additional arguments, representing keyword arguments in the source.
; var is the variable of integration - this has a default value of (milliseconds), provided by the caller though
; numeric-kw is #t if the keyword #:numeric was provided
; symbolic-kw is #t if the keyword #:symbolic was provided
; dt is the time step if the keyword #:dt and an argument was provided
; Only one of #:numeric or #:symbolic can be given -- a Racket macro ninja would check this in the macro, but
; here this function checks it. If neither the code first tries to find a symbolic integral, and if that doesn't
; work, it returns code to compute it numerically. #:dt can only be provided if #:numeric is specified. Again,
; a macro ninja would check this in the macro rather than here.
; It returns 4 values:
; symbolic? #t if the result is a symbolic integration, #f if numeric
; var the variable of integration (the supplied one if present, otherwise the default var of integration)
; expr if symbolic is #t, the symbolic integral; and otherwise null
; dt if symbolic is #f, the time step for numeric integration; and otherwise null
(define (integral-preprocessor expr var numeric-kw symbolic-kw dt)
(when (and numeric-kw symbolic-kw) (error "can only specify one of #:numeric and #:symbolic"))
(when (and (not numeric-kw) dt) (error "#:dt can only be specified in conjunction with #:numeric"))
; try to compute the symbolic integral and bind it to s, unless #:numeric is specified
(let* ([v (if var var default-variable-of-integration)]
[d (if dt dt default-integration-dt)]
[s (if numeric-kw #f (symbolic-integral expr v))])
(cond [(and symbolic-kw (not s))
(error "#:symbolic was specified but unable to find symbolic integral")])
(if s (values #t v s null) (values #f v null d))))
; Function to do symbolic integration at compile time -- super simple to start with.
; This doesn't do any simplification of the result -- which seems fine, since it is for evaluation
; rather than human consumption, and the code is going to be really fast with or without simplication.
(define (symbolic-integral expr var)
(match expr
[v #:when (equal? v var) (list '* 1/2 (list 'expt var 2))]
[(? number? n) (list '* n var)]
[(list '+ x y) (list '+ (symbolic-integral x var) (symbolic-integral y var))]
[(cons '* (list-no-order (? number? n) x)) (list '* n (symbolic-integral x var))]
[(list '/ x (? number? n)) (list '/ (symbolic-integral x var) n)]
[(list 'expt v (? number? n)) #:when (equal? v var) (list '/ (list 'expt v (+ n 1)) (+ n 1))]
[(list 'integral e) (symbolic-integral (symbolic-integral e var) var)]
[_ #f]))