Skip to content

Commit 69ce09d

Browse files
committed
Complex erf and Fresnel functions
1 parent 067b183 commit 69ce09d

File tree

4 files changed

+142
-40
lines changed

4 files changed

+142
-40
lines changed

math-doc/math/scribblings/math-special-functions.scrbl

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
The term ``special function'' has no formal definition. However, for the purposes of the
2020
@racketmodname[math] library, a @deftech{special function} is one that is not @tech{elementary}.
2121

22-
The special functions are split into three groups: @secref{complex-functions}, @secref{real-functions} and
22+
The special functions are split into two groups: @secref{complex-real-functions} and
2323
@secref{flonum-functions}. Functions that accept real arguments are usually defined
2424
in terms of their flonum counterparts, but are different in two crucial ways:
2525
@itemlist[
@@ -56,7 +56,7 @@ The most general type @racket[Real -> (U Zero Flonum)] is used to generate
5656
@racket[lambert]'s contract when it is used in untyped code. Except for this discussion,
5757
this the only type documented for @racket[lambert].
5858

59-
@section[#:tag "complex-functions"]{Complex Functions}
59+
@section[#:tag "complex-real-functions"]{Complex and real Functions}
6060

6161
@defproc[(gamma [x Number]) Number]{
6262
Computes the @hyperlink["http://en.wikipedia.org/wiki/Gamma_function"]{gamma function},
@@ -132,8 +132,6 @@ error.
132132
If you need low relative error near negative roots, use @racket[bfpsi0].
133133
}
134134

135-
@section[#:tag "real-functions"]{Real Functions}
136-
137135
@defproc[(psi [m Integer] [x Real]) Flonum]{
138136
Computes a @hyperlink["http://en.wikipedia.org/wiki/Polygamma_function"]{polygamma function},
139137
or the @racket[m]th logarithmic derivative of the gamma function. The order @racket[m] must be
@@ -154,7 +152,7 @@ near negative roots. Near negative roots, relative error is apparently unbounded
154152
is low.
155153
}
156154

157-
@deftogether[(@defproc[(erf [x Real]) Real]
155+
@deftogether[(@defproc[(erf [x Number]) Number]
158156
@defproc[(erfc [x Real]) Real])]{
159157
Compute the @hyperlink["http://en.wikipedia.org/wiki/Error_function"]{error function and
160158
complementary error function}, respectively. The only exact cases are @racket[(erf 0) = 0]
@@ -167,7 +165,8 @@ and @racket[(erfc 0) = 1].
167165
(erf 1)
168166
(- 1 (erfc 1))
169167
(erf -1)
170-
(- (erfc 1) 1)]
168+
(- (erfc 1) 1)
169+
(erf 1+i)]
171170

172171
Mathematically, erfc(@italic{x}) = 1 - erf(@italic{x}), but having separate implementations
173172
can help maintain accuracy. To compute an expression containing erf, use @racket[erf] for
@@ -185,8 +184,8 @@ manipulate @racket[(- 1.0 (erfc x))] and its surrounding expressions to avoid th
185184
(flulp-error (fllog1p (- (erfc x))) log-erf-x)]
186185
For negative @racket[x] away from @racket[0.0], do the same with @racket[(- (erfc (- x)) 1.0)].
187186

188-
For @racket[erf], error is no greater than 2 @tech{ulps} everywhere that has been tested, and
189-
is almost always no greater than 1. For @racket[erfc], observed error is no greater than 4 ulps,
187+
For @racket[erf], on the real line the error is no greater than 2 @tech{ulps} everywhere that has been tested, and
188+
is almost always no greater than 1. In the complex plane the relative error is smaller 1e-12. For @racket[erfc], observed error is no greater than 4 ulps,
190189
and is usually no greater than 2.
191190
}
192191

@@ -394,10 +393,10 @@ have very dissimilar magnitudes (e.g. @racket[1e-16] and @racket[1e16]), it exhi
394393
@tech{catastrophic cancellation}. We are working on it.
395394
}
396395

397-
@deftogether[(@defproc[(Fresnel-S [x Real]) Real]
398-
@defproc[(Fresnel-C [x Real]) Real]
399-
@defproc[(Fresnel-RS [x Real]) Real]
400-
@defproc[(Fresnel-RC [x Real]) Real])]{
396+
@deftogether[(@defproc[(Fresnel-S [x Number]) Number]
397+
@defproc[(Fresnel-C [x Number]) Number]
398+
@defproc[(Fresnel-RS [x Number]) Number]
399+
@defproc[(Fresnel-RC [x Number]) Number])]{
401400
Compute the @hyperlink["https://en.wikipedia.org/wiki/Fresnel_integral"]{Fresnel integrals}. Where
402401
@itemlist[
403402
@item{@racket[(Fresnel-S x)] calculates ∫sin(πt²/2) |0->x}
@@ -413,7 +412,9 @@ The first two are sometimes also referred to as the natural Fresnel integrals.
413412
(Fresnel-RS 1)
414413
(* (sqrt (/ pi 2)) (Fresnel-S (* (sqrt (/ 2 pi)) 1)))]
415414

416-
Spot-checks within the region 0<=x<=150 sugest that the error is no greater than 1e-14 everywhere that has been tested, and usually is lower than 2e-15.
415+
Spot-checks on the real line within the region 0<=x<=150 sugest that the error is no greater than 1e-14
416+
everywhere that has been tested, and usually is lower than 2e-15. In the complex plane the relative error
417+
is smaller than 1e-12.
417418
}
418419

419420

math-lib/math/private/functions/erf.rkt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ IEEE Transactions on Communications, 2000, vol 48, pp 529--532
1414
"continued-fraction.rkt")
1515

1616
(provide flerf flerfc*expsqr flerfc
17-
erf erfc)
17+
erf erfc complex-erf)
1818

1919
;; ===================================================================================================
2020
;; erf
@@ -242,11 +242,14 @@ IEEE Transactions on Communications, 2000, vol 48, pp 529--532
242242

243243
(: erf (case-> (Zero -> Zero)
244244
(Flonum -> Flonum)
245-
(Real -> (U Zero Flonum))))
246-
(define (erf x)
247-
(cond [(flonum? x) (flerf x)]
245+
(Real -> (U Zero Flonum))
246+
(Number -> Number)))
247+
(define (erf z)
248+
(define x (if (= (imag-part z) 0) (real-part z) z))
249+
(cond [(flonum? x) (flerf x)]
248250
[(eqv? x 0) x]
249-
[else (flerf (fl x))]))
251+
[(real? x) (flerf (fl x))]
252+
[else (complex-erf z)]))
250253

251254
(: erfc (case-> (Zero -> One)
252255
(Flonum -> Flonum)

math-lib/math/private/functions/fresnel.rkt

Lines changed: 102 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,11 @@ would like to extend this together with erf to the complex plane
2020
|#
2121

2222
(require "../../base.rkt"
23-
"../../flonum.rkt")
23+
"../../flonum.rkt"
24+
"erf.rkt")
2425

25-
(provide flFresnel-S Fresnel-S Fresnel-RS
26-
flFresnel-C Fresnel-C Fresnel-RC)
26+
(provide flFresnel-S complex-Fresnel-S Fresnel-S Fresnel-RS
27+
flFresnel-C complex-Fresnel-C Fresnel-C Fresnel-RC)
2728

2829
;------------------------------
2930
;polinomials for the rational assymptotic aproximation for S & C
@@ -76,13 +77,15 @@ would like to extend this together with erf to the complex plane
7677

7778
;------------------------------
7879
;polinomials for the rational powerseries aproximation for S
79-
(define sn (make-flpolyfun ( 3.18016297876567817986E11
80+
(define sn/d
81+
(make-quotient-flpolyfun ( 3.18016297876567817986E11
8082
-4.42979518059697779103E10
8183
2.54890880573376359104E9
8284
-6.29741486205862506537E7
8385
7.08840045257738576863E5
84-
-2.99181919401019853726E3)))
85-
(define sd (make-flpolyfun ( 6.07366389490084639049E11
86+
-2.99181919401019853726E3
87+
0.0)
88+
( 6.07366389490084639049E11
8689
2.24411795645340920940E10
8790
4.19320245898111231129E8
8891
5.17343888770096400730E6
@@ -97,7 +100,7 @@ would like to extend this together with erf to the complex plane
97100
[(fl< x 1.5625)
98101
(define X2 (fl* x x))
99102
(define X4 (fl* X2 X2))
100-
(fl* (fl* X2 x) (fl/ (sn X4)(sd X4)))]
103+
(fl* (fl* X2 x) (sn/d X4))]
101104
[else
102105
(define t (fl* pi (fl* x x)))
103106
(define t/2 (fl/ t 2.0))
@@ -107,15 +110,23 @@ would like to extend this together with erf to the complex plane
107110
(fl- 0.5 (fl/ (fl+ (fl* f (flcos t/2))(fl* g (flsin t/2)))
108111
(fl* pi x)))]))
109112

113+
(define (complex-Fresnel-S [z : Number]) : Number
114+
(define z* (* z (sqrt (/ pi 4))))
115+
(* (/ 1+i 4)
116+
(- (complex-erf (* z* 1+i))
117+
(* +i (complex-erf (* z* 1-i))))))
118+
110119
;------------------------------
111120
;polinomials for the rational powerseries aproximation for C
112-
(define cn (make-flpolyfun ( 9.99999999999999998822E-1
121+
(define cn/d
122+
(make-quotient-flpolyfun ( 9.99999999999999998822E-1
113123
-2.05525900955013891793E-1
114124
1.88843319396703850064E-2
115125
-6.45191435683965050962E-4
116126
9.50428062829859605134E-6
117-
-4.98843114573573548651E-8)))
118-
(define cd (make-flpolyfun ( 1.00000000000000000118E0
127+
-4.98843114573573548651E-8
128+
0.0)
129+
( 1.00000000000000000118E0
119130
4.12142090722199792936E-2
120131
8.68029542941784300606E-4
121132
1.22262789024179030997E-5
@@ -130,7 +141,7 @@ would like to extend this together with erf to the complex plane
130141
[(fl< x 1.5625)
131142
(define X2 (fl* x x))
132143
(define X4 (fl* X2 X2))
133-
(fl* x (fl/ (cn X4)(cd X4)))]
144+
(fl* x (cn/d X4))]
134145
[else
135146
(define t (fl* pi (fl* x x)))
136147
(define t/2 (fl/ t 2.0))
@@ -140,13 +151,57 @@ would like to extend this together with erf to the complex plane
140151
(fl+ 0.5 (fl/ (fl- (fl* f (flsin t/2))(fl* g (flcos t/2)))
141152
(fl* pi x)))]))
142153

154+
(define (complex-Fresnel-C [z : Number]) : Number
155+
(define z* (* z (sqrt (/ pi 4))))
156+
(* (/ 1-i 4)
157+
(+ (complex-erf (* z* 1+i))
158+
(* +i (complex-erf (* z* 1-i))))))
159+
143160
;------------------------------
144-
(define (Fresnel-S [x : Real]) : Real (flFresnel-S (fl x)))
145-
(define (Fresnel-C [x : Real]) : Real (flFresnel-C (fl x)))
146-
(define (Fresnel-RS [x : Real]) : Real
147-
(* (flsqrt (fl/ pi 2.0)) (flFresnel-S (* (flsqrt (fl/ 2.0 pi)) (fl x)))))
148-
(define (Fresnel-RC [x : Real]) : Real
149-
(* (flsqrt (fl/ pi 2.0)) (flFresnel-C (* (flsqrt (fl/ 2.0 pi)) (fl x)))))
161+
(: Fresnel-S (case-> (Zero -> Zero)
162+
(Flonum -> Flonum)
163+
(Real -> (U Zero Flonum))
164+
(Number -> Number)))
165+
(define (Fresnel-S z)
166+
(define x (if (= (imag-part z) 0) (real-part z) z))
167+
(cond
168+
[(eqv? z 0) 0]
169+
[(flonum? x)(flFresnel-S x)]
170+
[(real? x)(flFresnel-S (fl x))]
171+
[else (complex-Fresnel-S z)]))
172+
(: Fresnel-C (case-> (Zero -> Zero)
173+
(Flonum -> Flonum)
174+
(Real -> (U Zero Flonum))
175+
(Number -> Number)))
176+
(define (Fresnel-C z)
177+
(define x (if (= (imag-part z) 0) (real-part z) z))
178+
(cond
179+
[(eqv? z 0) 0]
180+
[(flonum? x)(flFresnel-C x)]
181+
[(real? x)(flFresnel-C (fl x))]
182+
[else (complex-Fresnel-C z)]))
183+
(: Fresnel-RS (case-> (Zero -> Zero)
184+
(Flonum -> Flonum)
185+
(Real -> (U Zero Flonum))
186+
(Number -> Number)))
187+
(define (Fresnel-RS z)
188+
(define x (if (= (imag-part z) 0) (real-part z) z))
189+
(cond
190+
[(eqv? z 0) 0]
191+
[(flonum? x)(* (flsqrt (fl/ pi 2.0))(flFresnel-S (* (flsqrt (fl/ 2.0 pi)) x)))]
192+
[(real? x) (* (flsqrt (fl/ pi 2.0))(flFresnel-S (* (flsqrt (fl/ 2.0 pi)) (fl x))))]
193+
[else (* (sqrt (/ pi 2))(complex-Fresnel-S (* (sqrt (/ 2 pi)) z)))]))
194+
(: Fresnel-RC (case-> (Zero -> Zero)
195+
(Flonum -> Flonum)
196+
(Real -> (U Zero Flonum))
197+
(Number -> Number)))
198+
(define (Fresnel-RC z)
199+
(define x (if (= (imag-part z) 0) (real-part z) z))
200+
(cond
201+
[(eqv? z 0) 0]
202+
[(flonum? x)(* (flsqrt (fl/ pi 2.0))(flFresnel-C (* (flsqrt (fl/ 2.0 pi)) x)))]
203+
[(real? x) (* (flsqrt (fl/ pi 2.0))(flFresnel-C (* (flsqrt (fl/ 2.0 pi)) (fl x))))]
204+
[else (* (sqrt (/ pi 2))(complex-Fresnel-C (* (sqrt (/ 2 pi)) z)))]))
150205

151206
;------------------------------
152207
(module* bfFresnel #f
@@ -209,18 +264,18 @@ would like to extend this together with erf to the complex plane
209264
(bfFresnel-RC (bf* (bfsqrt (bf/ pi.bf (bf 2))) x) maxp)))
210265
)
211266

212-
(module* test racket/base
267+
#;(module* test #f
213268
;some functions to check the function.
214269
;commented out, because (partly) put in function-tests from math-test
215270
(require math/bigfloat
216-
rackunit)
271+
typed/rackunit)
217272
(require (submod "..")
218273
(submod ".." bfFresnel))
219274

220-
(define (test a #:ε1e-15])
221-
(check-= (Fresnel-S a) (bigfloat->flonum (bfFresnel-S (bf a))) ε (format "(Fresnel-S ~a)" a))
275+
(define (test [a : Flonum] #:ε : Flonum 1e-15])
276+
(check-= (Fresnel-S a) (bigfloat->flonum (bfFresnel-S (bf a))) ε (format "(Fresnel-S ~a)" a))
222277
(check-= (Fresnel-RS a) (bigfloat->flonum (bfFresnel-RS (bf a))) ε (format "(Fresnel-RS ~a)" a))
223-
(check-= (Fresnel-C a) (bigfloat->flonum (bfFresnel-C (bf a))) ε (format "(Fresnel-C ~a)" a))
278+
(check-= (Fresnel-C a) (bigfloat->flonum (bfFresnel-C (bf a))) ε (format "(Fresnel-C ~a)" a))
224279
(check-= (Fresnel-RC a) (bigfloat->flonum (bfFresnel-RC (bf a))) ε (format "(Fresnel-RC ~a)" a)))
225280

226281
(test 0.1)
@@ -242,6 +297,31 @@ would like to extend this together with erf to the complex plane
242297
(check-equal? (Fresnel-S -5)(-(Fresnel-S 5)))
243298
(check-equal? (Fresnel-C -5)(-(Fresnel-C 5)))
244299

300+
(check-= (magnitude
301+
(/ (complex-Fresnel-S 1+i)
302+
-2.0618882191948404680807165366857086008159083237378680520+2.0618882191948404680807165366857086008159083237378680520i))
303+
1 1e-12)
304+
(check-= (magnitude
305+
(/ (complex-Fresnel-S 5+0.2i)
306+
0.47365635370953447150430003290670950910437504109567910708+0.73462461062246762668741695076291749315532498862606992695i))
307+
1 1e-12)
308+
(check-= (magnitude
309+
(/ (complex-Fresnel-S -8-25i)
310+
4.33491319138289340482459027250885195089165476877024e270+1.38537242126661103439768955547584123133806956947632e270i))
311+
1 1e-12)
312+
(check-= (magnitude
313+
(/ (complex-Fresnel-C 1+i)
314+
2.55579377810243902463452238835219584215662360420358429635+2.55579377810243902463452238835219584215662360420358429635i))
315+
1 1e-12)
316+
(check-= (magnitude
317+
(/ (complex-Fresnel-C 5+0.2i)
318+
1.237351377588089955209810162536250644901272375752411527+0.02602758966318992966794130566838646516498797340046208293i))
319+
1 1e-12)
320+
(check-= (magnitude
321+
(/ (complex-Fresnel-C -8-25i)
322+
1.38537242126661103439768955547584123133806956947632e270-4.33491319138289340482459027250885195089165476877024e270i))
323+
1 1e-12)
324+
245325
#;(let ()
246326
(local-require plot)
247327
(define (mk)(* 100 (random)))

math-test/math/tests/function-tests.rkt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,21 @@
121121
(check-= (Fresnel-C 20) 0.4999873349723443881870062136976602164476 (ε))
122122
(check-= (Fresnel-C 50) 0.4999991894307279679558101639817919070024 (ε))
123123

124+
(let ([z 1+i]
125+
[ans -2.0618882191948404680807165366857086008159083237378680520+2.0618882191948404680807165366857086008159083237378680520i])
126+
(check-= (/ (Fresnel-S z) ans) 1 1e-12))
127+
(let ([z 5+0.2i]
128+
[ans 0.47365635370953447150430003290670950910437504109567910708+0.73462461062246762668741695076291749315532498862606992695i])
129+
(check-= (/ (Fresnel-S z) ans) 1 1e-12))
130+
(let ([z -8-25i]
131+
[ans 4.33491319138289340482459027250885195089165476877024e270+1.38537242126661103439768955547584123133806956947632e270i])
132+
(check-= (/ (Fresnel-S z) ans) 1 1e-12))
133+
(let ([z 1+i]
134+
[ans 2.55579377810243902463452238835219584215662360420358429635+2.55579377810243902463452238835219584215662360420358429635i])
135+
(check-= (/ (Fresnel-C z) ans) 1 1e-12))
136+
(let ([z 5+0.2i]
137+
[ans 1.237351377588089955209810162536250644901272375752411527+0.02602758966318992966794130566838646516498797340046208293i])
138+
(check-= (/ (Fresnel-C z) ans) 1 1e-12))
139+
(let ([z -8-25i]
140+
[ans 1.38537242126661103439768955547584123133806956947632e270-4.33491319138289340482459027250885195089165476877024e270i])
141+
(check-= (/ (Fresnel-C z) ans) 1 1e-12))

0 commit comments

Comments
 (0)