86 lines
2.8 KiB
Racket
86 lines
2.8 KiB
Racket
#lang racket/base
|
|
|
|
(require racket/math racket/flonum racket/contract racket/match
|
|
"math.rkt"
|
|
"contract.rkt" "contract-doc.rkt")
|
|
|
|
(provide (struct-out invertible-function)
|
|
make-axis-transform
|
|
id-transform
|
|
log-transform
|
|
cbrt-transform
|
|
hand-drawn-transform)
|
|
|
|
(define-struct/contract invertible-function ([f (real? . -> . real?)] [finv (real? . -> . real?)])
|
|
#:transparent)
|
|
|
|
;; Turns any total, surjective, monotone flonum op and its inverse into an axis transform
|
|
(define ((make-axis-transform flop flinv) x-min x-max)
|
|
(let ([x-min (exact->inexact x-min)]
|
|
[x-max (exact->inexact x-max)])
|
|
(define fx-min (flop x-min))
|
|
(define fx-scale (fl/ (fl- x-max x-min)
|
|
(fl- (flop x-max) fx-min)))
|
|
(define (f x)
|
|
(fl+ x-min (fl* (fl- (flop (exact->inexact x)) fx-min)
|
|
fx-scale)))
|
|
(define (finv y)
|
|
(flinv (fl+ fx-min (fl/ (fl- (exact->inexact y) x-min)
|
|
fx-scale))))
|
|
(invertible-function f finv)))
|
|
|
|
;; ===================================================================================================
|
|
;; Specific axis transforms
|
|
|
|
(define ((flnewton-invert f f-diff f-inv-guess n) y)
|
|
(let ([y (exact->inexact y)])
|
|
(let loop ([x (f-inv-guess y)] [n n])
|
|
(let/ec return
|
|
(when (zero? n) (return x))
|
|
|
|
(define dx (fl/ (fl- y (f x)) (f-diff x)))
|
|
(when (zero? dx) (return x))
|
|
|
|
(loop (fl- x dx) (sub1 n))))))
|
|
|
|
(define (sine-diag d)
|
|
(let ([d (exact->inexact d)])
|
|
(λ (x) (let ([x (exact->inexact x)])
|
|
(fl+ x (fl* (fl/ 1.0 (fl* 4.0 d)) (flsin (fl* d x))))))))
|
|
|
|
(define (sine-diag-diff d)
|
|
(let ([d (exact->inexact d)])
|
|
(λ (x) (let ([x (exact->inexact x)])
|
|
(fl- (fl/ (flcos (fl* d x)) 4.0) 1.0)))))
|
|
|
|
(define (sine-diag-inv d)
|
|
(flnewton-invert (sine-diag d) (sine-diag-diff d) values 10))
|
|
|
|
(define cbrt
|
|
(let ([e (exact->inexact 1/3)])
|
|
(λ (x) (let ([x (exact->inexact x)])
|
|
(fl* (sgn x) (expt (flabs x) e))))))
|
|
|
|
(define (cube x)
|
|
(let ([x (exact->inexact x)])
|
|
(fl* x (fl* x x))))
|
|
|
|
|
|
(defproc (id-transform [x-min real?] [x-max real?]) invertible-function?
|
|
(invertible-function values values))
|
|
|
|
(defproc (log-transform [x-min real?] [x-max real?]) invertible-function?
|
|
(when ((exact->inexact x-min) . <= . 0)
|
|
(raise-type-error 'log-transform "positive real" 0 x-min x-max))
|
|
((make-axis-transform fllog flexp) x-min x-max))
|
|
|
|
(define cbrt-trans (make-axis-transform cbrt cube))
|
|
|
|
(defproc (cbrt-transform [x-min real?] [x-max real?]) invertible-function?
|
|
(cbrt-trans x-min x-max))
|
|
|
|
(defproc (hand-drawn-transform [freq (>/c 0)]) (real? real? . -> . invertible-function?)
|
|
(λ (mn mx)
|
|
(define d (/ freq (- mx mn)))
|
|
((make-axis-transform (sine-diag d) (sine-diag-inv d)) mn mx)))
|