Skip to content
This repository

Fresnel Integral Functions #938

Merged
merged 37 commits into from about 2 years ago

6 participants

raoulb Aaron Meurer Tom Bachmann Julien Rioux Vladimir Perić Miha Marolt
raoulb
Collaborator

This is a first implementation of the Fresnel integral functions S(x) and C(x).
It's the first part of Issue 2959: "Implement fresnel integrals".

The tests are still missing but I would like to get some feedback
before I proceed. (For example, are the function names ok?)

Aaron Meurer
Owner

Does Sage implement these? If so, what does it call them?

raoulb
Collaborator

In other CAS the names are:

Maxima: fresnel_s, fresnel_c
Maple: FresnelS, FresnelC
MMA: FresnelS, FresnelC
mpmath: fresnels, fresnelc

Sage probably calls maxima but I don't know.

Maybe we should use lower case only names too.

raoulb
Collaborator

Just added the testcases. Please review the branch thoroughly.

raoulb
Collaborator

Hmm, still having trouble with the limit function

In [2]: z = Symbol("z")

In [3]: limit(erf(z), z, oo)
Out[3]: 1

In [4]: limit(fresnels(z), z, oo)
Out[4]: 0

Why does it work for erf but not for fresnels?

Tom Bachmann
Collaborator

(reviewing this ASAP)

Tom Bachmann ness01 commented on the diff
sympy/functions/special/error_functions.py
((85 lines not shown))
  166 + def _eval_expand_complex(self, deep=True, **hints):
  167 + re_part, im_part = self.as_real_imag(deep=deep, **hints)
  168 + return re_part + im_part*S.ImaginaryUnit
  169 +
  170 +
  171 +class fresnels(FresnelIntegral):
  172 + r"""
  173 + Fresnel integral S.
  174 +
  175 + This function is defined by
  176 +
  177 + .. math:: \operatorname{S}(z) = \int_0^z \sin{\frac{\pi}{2} t^2} \mathrm{d}t.
  178 +
  179 + It is an entire function.
  180 +
  181 + Examples
7
Tom Bachmann Collaborator
ness01 added a note
Aaron Meurer Owner
asmeurer added a note

Actually, I like the order used here. Maybe I'm just used to Wikipedia, but to me, See Also and References should go last.

Tom Bachmann Collaborator
ness01 added a note
raoulb Collaborator
raoulb added a note

I would also expect that the references are the last point. This is
the case for any research paper too. The referred material is probably
used less that the examples. But if you like I can exchange the order.

Tom Bachmann Collaborator
ness01 added a note
Vladimir Perić
vperic added a note

+1 on Examples/See Also/References as the preferred ordering. Should we bring this up on the mailing list? @miham, you wrote most of the file originally, why is it so? I guess just to follow NumPy standards?

Miha Marolt
miham added a note

+1 on Examples/See Also/References as the preferred ordering. Should we bring this up on the mailing list? @miham, you wrote most of the file
originally, why is it so? I guess just to follow NumPy standards?

Yes, I just copied the order that is used in NumPy/SciPy. I already posted this question to the mailing list [1], but only Aaron and Ondrej voted. What do others think?

[1] http://groups.google.com/group/sympy/browse_thread/thread/7e958ed5edf61e86#

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/functions/special/error_functions.py
((250 lines not shown))
  331 + ==========
  332 +
  333 + .. [1] http://en.wikipedia.org/wiki/Fresnel_integral
  334 + .. [2] http://dlmf.nist.gov/7
  335 + .. [3] http://mathworld.wolfram.com/FresnelIntegrals.html
  336 + .. [4] http://functions.wolfram.com/GammaBetaErf/FresnelC
  337 + """
  338 +
  339 + _trigfunc = C.cos
  340 + _sign = S.One
  341 +
  342 + def _eval_rewrite_as_erf(self, z):
  343 + return (S.One-I)/4 * (erf((S.One+I)/2*sqrt(pi)*z) + I*erf((S.One-I)/2*sqrt(pi)*z))
  344 +
  345 + def _eval_nseries(self, x, n, logx):
  346 + return x*(-x**4)**n*(2**(-2*n)*pi**(2*n))/((4*n+1)*C.factorial(2*n))
6
Tom Bachmann Collaborator
ness01 added a note

This shouldn't be necessary (and is not right as it stands anyway - this really should return a series of several terms, and an order. It is usually not necessary if differentiation is implemented, unless there are poles.).

raoulb Collaborator
raoulb added a note

You are right, it is not necessary. However using diff is probably much slower, isn't it?
BTW: Maybe we should add symbolic order differentiation to sympy?

Which is the "best" example for this function where I can study how to do this right?

raoulb Collaborator
raoulb added a note

BTW: The order term is not tight, I get:

In [5]: fresnels(z).series(n=12)
Out[5]: pi*z**3/6 - pi**3*z**7/336 + pi**5*z**11/42240 + O(z**12)

but it is really O(z**15) which is much better than O(z**12).

Tom Bachmann Collaborator
ness01 added a note
Tom Bachmann Collaborator
ness01 added a note
raoulb Collaborator
raoulb added a note

Maybe this is a gsoc task to clean up this mess.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/functions/special/error_functions.py
((260 lines not shown))
  341 +
  342 + def _eval_rewrite_as_erf(self, z):
  343 + return (S.One-I)/4 * (erf((S.One+I)/2*sqrt(pi)*z) + I*erf((S.One-I)/2*sqrt(pi)*z))
  344 +
  345 + def _eval_nseries(self, x, n, logx):
  346 + return x*(-x**4)**n*(2**(-2*n)*pi**(2*n))/((4*n+1)*C.factorial(2*n))
  347 +
  348 + def _eval_aseries(self, n, args0, x, logx):
  349 + z = self.args[0]
  350 + #e = S.Half*I*pi*z**2
  351 + #h1 = C.hyper([S.One,S.Half],[],2*I/(pi*z**2))
  352 + #h2 = C.hyper([S.One,S.Half],[],-2*I/(pi*z**2))
  353 + #return (z**4)**C.Rational(3,4)/(2*z**3) + I/(2*pi*z)*(C.exp(-e)*h1 - C.exp(e)*h2)
  354 + return S.Half + 1/(pi*z)*C.sin(S.Half*pi*z**2)
  355 +
  356 + def _eval_as_leading_term(self, x):
3
Tom Bachmann Collaborator
ness01 added a note

I believe this function is not needed either. (In fact I think it should be removed all over sympy, but that might need some more investigation ^^.)

raoulb Collaborator
raoulb added a note

What is the sympy way to compute leading order behaviour of a function?
Just use the first time of series?

So you are right and we can remove this function too.

If we continue like that there will be not much left in the derived classes ^^

Tom Bachmann Collaborator
ness01 added a note
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/functions/special/error_functions.py
((252 lines not shown))
  333 + .. [1] http://en.wikipedia.org/wiki/Fresnel_integral
  334 + .. [2] http://dlmf.nist.gov/7
  335 + .. [3] http://mathworld.wolfram.com/FresnelIntegrals.html
  336 + .. [4] http://functions.wolfram.com/GammaBetaErf/FresnelC
  337 + """
  338 +
  339 + _trigfunc = C.cos
  340 + _sign = S.One
  341 +
  342 + def _eval_rewrite_as_erf(self, z):
  343 + return (S.One-I)/4 * (erf((S.One+I)/2*sqrt(pi)*z) + I*erf((S.One-I)/2*sqrt(pi)*z))
  344 +
  345 + def _eval_nseries(self, x, n, logx):
  346 + return x*(-x**4)**n*(2**(-2*n)*pi**(2*n))/((4*n+1)*C.factorial(2*n))
  347 +
  348 + def _eval_aseries(self, n, args0, x, logx):
6
Tom Bachmann Collaborator
ness01 added a note

Look at the gamma function implementation for what this function should do. (You need to return something oscillatory as you do, which probably means that this is not going to be much good for limits since gruntz cannot handle it.)

raoulb Collaborator
raoulb added a note

You mean I should look at polygamma or loggamma? Gamma itself does not have this function.
Let me see what I can figure out.

Tom Bachmann Collaborator
ness01 added a note
raoulb Collaborator
raoulb added a note

Hmm, for fresnel we can express the asymptotic expansion in 'closed' form, see:

http://functions.wolfram.com/GammaBetaErf/FresnelS/06/02/

raoulb Collaborator
raoulb added a note

Btw, trying something like:

        z = self.args[0]
        result = S.Half + 1/(pi*z)*C.sin(S.Half*pi*z**2)
        o = C.Order(1/z**3, x)
        return result._eval_nseries(x, n, logx) + o 

to mimic the code of loggamma does only return an O term.
I must admit that I do not fully understand what's going on on the
sympy side here :-/

Tom Bachmann Collaborator
ness01 added a note
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/utilities/lambdify.py
... ... @@ -44,7 +44,7 @@
44 44 #"uppergamma":"upper_gamma",
45 45 "LambertW":"lambertw",
46 46 "Matrix":"matrix",
47   - "conjugate":"conj",
2
Tom Bachmann Collaborator
ness01 added a note

I think you should avoid this sort of unrelated change.

raoulb Collaborator
raoulb added a note

Oops, this is a relict from an earlier commit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Tom Bachmann
Collaborator

Please add entries for fresnels and fresnelc to hyperexpand() table, so that e.g. integrate(sin(x**2), x, meijerg=True) returns a nice answer. There is an explanation on how to do this in the hyperexpand documentation (sphinx), if unclear ask here.

Regarding limits: there are two functions, "limit" and "gruntz". Limit is basically guesswork/heuristics whereas gruntz implements a "real" algorithm (guess which function I worked on a lot g). gruntz uses series expansions and cannot handle erf [simply because the necessary support code is not written. one needs to implement rewrite_as_tractable by defining a new helper function object, and then implement asymptotic series on the new object. ask if interested]. Making fresnel integrals work with gruntz would probably be non-trivial.

I'm not quite sure how limit works (except that it sometimes calls gruntz). Look there (simpy/series/limit.py) to figure out how to make the "standard" limits work without having to work with gruntz.

Tom Bachmann ness01 commented on the diff
sympy/functions/special/tests/test_error_functions.py
((43 lines not shown))
  77 +
  78 + assert fresnelc(z).diff(z) == cos(pi*z**2/2)
  79 +
  80 + assert fresnelc(z).as_leading_term(z) == z
  81 +
  82 + assert fresnelc(z).rewrite(erf) == (S.One-I)/4 * (erf((S.One+I)/2*sqrt(pi)*z) + I*erf((S.One-I)/2*sqrt(pi)*z))
  83 +
  84 + assert fresnelc(z)._eval_nseries(z, n, None) == z*(-z**4)**n*(2**(-2*n)*pi**(2*n))/((4*n+1)*factorial(2*n))
  85 +
  86 + assert fresnelc(z)._eval_aseries(z, oo, 0, 0) == S.Half + sin(pi*z**2/2)/(pi*z)
  87 +
  88 + assert fresnelc(w).is_real is True
  89 +
  90 + assert fresnelc(z).as_real_imag() == ((fresnelc(re(z) - I*re(z)*Abs(im(z))/Abs(re(z)))/2 + fresnelc(re(z) + I*re(z)*Abs(im(z))/Abs(re(z)))/2,
  91 + I*(fresnelc(re(z) - I*re(z)*Abs(im(z))/Abs(re(z))) - fresnelc(re(z) + I*re(z)*Abs(im(z))/Abs(re(z))))*
  92 + re(z)*Abs(im(z))/(2*im(z)*Abs(re(z)))))
8
Tom Bachmann Collaborator
ness01 added a note

Please add tests for numerical evaluation (e.g. test against the integral definition by numerically evaluating an Integral).

raoulb Collaborator
raoulb added a note

Shouldn't this go into the mpmath testsuite? Because the only thing I do is calling fresnel{s,c} from mpmath.

Tom Bachmann Collaborator
ness01 added a note
raoulb Collaborator
raoulb added a note

Ah ok so testing if we use mpmath correctly. But in our case here we rely on the methods in Function
and do not intoduce anything new. For this reason I think there is nothing to test.

Tom Bachmann Collaborator
ness01 added a note

I still think this should be tested numerically.

Tom Bachmann Collaborator
ness01 added a note

Please add

from sympy.utilities.randtest import test_numerically

test_numerically(re(fresnelc(z)), fresnelc(z).as_real_imag()[0], z)
test_numerically(im(fresnelc(z)), fresnelc(z).as_real_imag()[1], z)
test_numerically(fresnelc(z), fresnelc(z).rewrite(meijerg), z)

etc

raoulb Collaborator
raoulb added a note

Done. Do we want even more? (If yes, which ones)

Tom Bachmann Collaborator
ness01 added a note
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
raoulb
Collaborator

I think a good this to do is some documentation how to add new special functions and what to take care of.
Maybe I will write down some points once I understand this better.

Same applies for the extension of gruntz etc.

Tom Bachmann
Collaborator
raoulb
Collaborator

About the hyperexpand stuff, I tried but my problem is that I only know fresnels(z) in terms of a 1F2 with argument -pi**2*z**4/16 and not z only:
http://functions.wolfram.com/GammaBetaErf/FresnelS/26/01/ShowAll.html

Hence I tried to transform this to match the form required by add(...) and came up with the rule:

add((S(3)/4,), (S(3)/2,S(7)/4), fresnels(root(-16*z/pi**2, 4)) * 6/(pi*(root(-16*z/pi**2, 4))**3)

The result of the integration is something A*fresnels(B*z) which looks promising
but A and B are big messy constants.

raoulb
Collaborator

This is a first try. The results seem to be ok IFF these ploar factors can simplify to 1.

If we take principal roots then in the case of fresnels we get a prefactor of I and
the same for the argument. Hence they combine into I*fresnels(I*z) which is fresnels(z).
For the case of fresnelc we get factors of I and -I as -I*fresnelc(I*z) which is fresnelc(z).

So I think the rules are correct. But it would be nice if we can tell sympy to fully simplify.

raoulb
Collaborator

I started a wiki page (I hope this is the right place and title.):
https://github.com/sympy/sympy/wiki/About-implementing-special-functions

I plan to fill in the gap while I'm lerning how to do all the parts.

raoulb
Collaborator

gruntz uses series expansions and cannot handle erf [simply because the necessary support code is not written. one needs to
implement rewrite_as_tractable by defining a new helper function object, and then implement asymptotic series on the new
object. ask if interested].

I tried to make erf traktable by Gruntz, please see my "erf_tractable" branch.
It seems to work ... quite astonishing given my current knowledge about sympy.

But I'm not 100% sure if it really takes the correct codepath, even though I removed
everything from the erf class including direct evaluation at oo and -oo.

I'm not quite sure how limit works (except that it sometimes calls gruntz). Look there (simpy/series/limit.py) to figure out how to
make the "standard" limits work without having to work with gruntz.

Hmm, I don't like these heuristics. (Hardcoded tan/cot ... will cause work to extend when updating my trig branch.)

Tom Bachmann
Collaborator
Tom Bachmann
Collaborator
sympy/functions/special/error_functions.py
... ... @@ -82,3 +82,281 @@ def _eval_as_leading_term(self, x):
82 82
83 83 def _eval_is_real(self):
84 84 return self.args[0].is_real
  85 +
  86 +###############################################################################
  87 +#################### FRESNEL INTEGRALS ########################################
  88 +###############################################################################
  89 +
  90 +class FresnelIntegral(Function):
  91 + """ Base class for the Fresnel integrals."""
  92 +
  93 + nargs = 1
  94 +
  95 + _trigfunc = None
  96 + _sign = None
  97 +
1
Tom Bachmann Collaborator
ness01 added a note

Remove these two lines. We decided in the gsoc-3 review that it is more helpful to leave this undefined.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
raoulb
Collaborator

Making fresnel integrals work with gruntz would probably be non-trivial.

Maybe, but maybe not. We can rewrite the 'fresnel{s,c}' into 'erf'.
And if erf is tractable meybe we can do the fresnels this way.
It depends if we can do things like erf((1{+,-}I)/2*sqrt(pi)*z).
Currently we can not, Gruntz fails with an

NotImplementedError: Result depends on the sign of -sign((1/2 + I/2)**2)
raoulb
Collaborator

Your ideas are right, your implementation is poor ;).

I fully agree on the second part.

Comments: 1. Your _eval_aseries does not work. Try erfs(1/x).series(x)...
This is for formal reasons (typos, missing imports) etc
and I fixed this for you in my branch erf.

Some very stupid typos ... maybe it was a little bit too late last night.
This should be fixed now. The method was never called during limit
computation and I tested only erf(z), z->oo.

  1. Your _eval_aseries is wrong. Try (1-erf(1/x)).rewrite('tractable').replace(erfs, lambda t:erfs(t).series(x)). This will show you the asymptotic expansion of erfc which you should compare to e.g. wikipedia. A "realistic" way to see this is to run gruntz((1-erf(x))*exp(x**2)*x, x, oo) - this should return 1/sqrt(pi) but it does not, because your series starts only at 1/x**3. Wolfram alpha is a neat place to test limits.

I wanted to implement 5.17 of Gruntz, which is what I'm supposed to do
if I understand the text correctly. But obviously failed with the formula here too :-/

Fixed this (at least think so) and the realistic example now returns 1/sqrt(pi).
I'm still not sure about the big-O term.

  1. Why the limit still works with your broken code. I was somewhat befuddled by this, since your aseries cannot even be called. But all is well: rewrite_tractable must return a function with only algebraic singularities (except for explicit exp/log terms), and this enough knowledge to evaluate the limit.

Yes, the only function ever called was the rewrite tractable of erf.

  1. Cosmetic change. Implement erfs._eval_rewrite_as_intractable (converting back to erf). This is needed so that e.g. gruntz(erf(x), x, 0) does not return the "obscure" erfs. [This is not the most elegant solution to the tractable/intractable rewriting, but the way we currently do it.]

Done.

  1. Keep up the great work! If you have the time, please brush up your code and submit it for review.

Thanks! Yes I plan to do further work here, always as time permits.

Maybe I should merge this work into the erf improvement branch now.

And if you are bored, similar extensions are possible for zeta, Ei, and some bessel functions.

Yep :-)
Next to come after erf and fresnel. I plan to do the Airy functions too.

You may wish to consult Gruntz' thesis (see top of gruntz.py file) for extensive lists of examples.

I printed his thesis more than half a year ago but never finished reading it by now.
Maybe I should mention that I once had a lecture given by Dr. D. Gruntz (about software design).
It's a pitty that I never used this chance to talk about his algorithm.

Tom Bachmann
Collaborator
Aaron Meurer
Owner

The wiki is fine. When it gets fleshed out, we can migrate it to Sphinx.

raoulb
Collaborator

Rebased over master. Now we should review the series code and fix the remaining issues.

BTW: There ecists nice results for the Laplace transform. But sympy can not compute then yet.
What's missing?

Tom Bachmann
Collaborator

Oh yes, I almost forgot this. You should also extend the function _create_lookup_table at the top of sympy/integrals/meijerint.py. I'm afraid there is no documentation on how to do this, but I think it should be self-explanatory.

And then of course add tests for the transforms, and perhaps also some integrals involving fresenel functions (assuming they can be done, of course ^^).

sympy/functions/special/error_functions.py
((31 lines not shown))
  801 + changed = True
  802 +
  803 + nz = newarg.extract_multiplicatively(I)
  804 + if nz is not None:
  805 + prefact = cls._sign*I*prefact
  806 + newarg = nz
  807 + changed = True
  808 +
  809 + if changed:
  810 + return prefact*cls(newarg)
  811 +
  812 + # Values at infinities
  813 + if z is S.Infinity:
  814 + return S.Half
  815 + #elif z is S.NegativeInfinity:
  816 + # return -S.Half
3
Tom Bachmann Collaborator
ness01 added a note

Why is this commented out?

Tom Bachmann Collaborator
ness01 added a note

Ah I guess you extract signs automatically ... if so just delete this.

raoulb Collaborator
raoulb added a note

Ok. I left a comment explaining why these cases are missing.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/functions/special/error_functions.py
((152 lines not shown))
  922 + ==========
  923 +
  924 + .. [1] http://en.wikipedia.org/wiki/Fresnel_integral
  925 + .. [2] http://dlmf.nist.gov/7
  926 + .. [3] http://mathworld.wolfram.com/FresnelIntegrals.html
  927 + .. [4] http://functions.wolfram.com/GammaBetaErf/FresnelS
  928 + """
  929 +
  930 + _trigfunc = C.sin
  931 + _sign = -S.One
  932 +
  933 + def _eval_rewrite_as_erf(self, z):
  934 + return (S.One+I)/4 * (erf((S.One+I)/2*sqrt(pi)*z) - I*erf((S.One-I)/2*sqrt(pi)*z))
  935 +
  936 + def _eval_nseries(self, x, n, logx):
  937 + return x**3*(-x**4)**n*(2**(-2*n-1)*pi**(2*n+1))/((4*n+3)*C.factorial(2*n+1))
2
Tom Bachmann Collaborator
ness01 added a note

If you really want to keep this code, implement it using taylor_term. See e.g. the sin function.

raoulb Collaborator
raoulb added a note

Done.

I think is helpful for expressing the whole Taylor sum at once:

z = Symbol("z")
n = Symbol("n", integer=True)
Sum(fresnelc(z).taylor_term(n, z), (n,0,oo))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/functions/special/error_functions.py
((153 lines not shown))
  923 +
  924 + .. [1] http://en.wikipedia.org/wiki/Fresnel_integral
  925 + .. [2] http://dlmf.nist.gov/7
  926 + .. [3] http://mathworld.wolfram.com/FresnelIntegrals.html
  927 + .. [4] http://functions.wolfram.com/GammaBetaErf/FresnelS
  928 + """
  929 +
  930 + _trigfunc = C.sin
  931 + _sign = -S.One
  932 +
  933 + def _eval_rewrite_as_erf(self, z):
  934 + return (S.One+I)/4 * (erf((S.One+I)/2*sqrt(pi)*z) - I*erf((S.One-I)/2*sqrt(pi)*z))
  935 +
  936 + def _eval_nseries(self, x, n, logx):
  937 + return x**3*(-x**4)**n*(2**(-2*n-1)*pi**(2*n+1))/((4*n+3)*C.factorial(2*n+1))
  938 +
2
Tom Bachmann Collaborator
ness01 added a note

I think the remaining two functions should be deleted.

(fresnels is intractable at infinity, but not even introducing a helper function is going to work here since gruntz (afaik) just cannot work with oscillatory limits. It cannot even do gruntz(sin(x)/x,x,oo))

raoulb Collaborator
raoulb added a note

Done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Tom Bachmann ness01 commented on the diff
sympy/functions/special/tests/test_error_functions.py
((16 lines not shown))
  223 +
  224 + assert fresnels(z).diff(z) == sin(pi*z**2/2)
  225 +
  226 + assert fresnels(z).as_leading_term(z) == pi*z**3/6
  227 +
  228 + assert fresnels(z).rewrite(erf) == (S.One+I)/4 * (erf((S.One+I)/2*sqrt(pi)*z) - I*erf((S.One-I)/2*sqrt(pi)*z))
  229 +
  230 + assert fresnels(z)._eval_nseries(z, n, None) == z**3*(-z**4)**n*(2**(-2*n-1)*pi**(2*n+1))/((4*n+3)*factorial(2*n+1))
  231 +
  232 + assert fresnels(z)._eval_aseries(z, oo, 0, 0) == S.Half - cos(pi*z**2/2)/(pi*z)
  233 +
  234 + assert fresnels(w).is_real is True
  235 +
  236 + assert fresnels(z).as_real_imag() == ((fresnels(re(z) - I*re(z)*Abs(im(z))/Abs(re(z)))/2 + fresnels(re(z) + I*re(z)*Abs(im(z))/Abs(re(z)))/2,
  237 + I*(fresnels(re(z) - I*re(z)*Abs(im(z))/Abs(re(z))) - fresnels(re(z) + I*re(z)*Abs(im(z))/Abs(re(z))))*
  238 + re(z)*Abs(im(z))/(2*im(z)*Abs(re(z)))))
3
Tom Bachmann Collaborator
ness01 added a note

consider testing this numerically

raoulb Collaborator
raoulb added a note

Added a test with concrete numbers. Should I remove the symbolc expression test?

Tom Bachmann Collaborator
ness01 added a note

No, definitely keep the symbolic test.

What I meant was something like test_numerically(re(fresnels(z)), fresnels(z).as_real_imag()[0]). [Or, if you don't like random tests, test both sides on some specific value you choose.]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/functions/special/tests/test_error_functions.py
((8 lines not shown))
  215 + assert fresnels(-oo) == -S.Half
  216 +
  217 + assert fresnels(z) == fresnels(z)
  218 + assert fresnels(-z) == -fresnels(z)
  219 + assert fresnels(I*z) == -I*fresnels(z)
  220 + assert fresnels(-I*z) == I*fresnels(z)
  221 +
  222 + assert conjugate(fresnels(z)) == fresnels(conjugate(z))
  223 +
  224 + assert fresnels(z).diff(z) == sin(pi*z**2/2)
  225 +
  226 + assert fresnels(z).as_leading_term(z) == pi*z**3/6
  227 +
  228 + assert fresnels(z).rewrite(erf) == (S.One+I)/4 * (erf((S.One+I)/2*sqrt(pi)*z) - I*erf((S.One-I)/2*sqrt(pi)*z))
  229 +
  230 + assert fresnels(z)._eval_nseries(z, n, None) == z**3*(-z**4)**n*(2**(-2*n-1)*pi**(2*n+1))/((4*n+3)*factorial(2*n+1))
2
Tom Bachmann Collaborator
ness01 added a note

Test an actual series expansion, as in fresnels(z).series(z,n=5) or so.

raoulb Collaborator
raoulb added a note

Done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/simplify/hyperexpand.py
... ... @@ -246,6 +247,14 @@ def fm(a, z):
246 247 Matrix([[1, 0, 0]]),
247 248 Matrix([[-S.Half, S.Half, 0], [0, -S.Half, S.Half], [0, 2*z, 0]]))
248 249
  250 + # FresnelS
  251 + #add([S(3)/4], [S(3)/2,S(7)/4], fresnels(root(-16*z/pi**2, 4)) * 6/(pi*(root(-16*z/pi**2, 4))**3) )
  252 + add([S(3)/4], [S(3)/2,S(7)/4], fresnels(2/sqrt(pi)*root(-z,4)) * 6/(pi*8*(-z)**(S(3)/4)/pi**(S(3)/2) ) )
  253 +
  254 + # FresnelC
  255 + #add([S(1)/4], [S(1)/2,S(5)/4], fresnelc(root(-16*z/pi**2, 4)) / (pi*(root(-16*z/pi**2, 4))) )
  256 + add([S(1)/4], [S(1)/2,S(5)/4], fresnelc(2/sqrt(pi)*root(-z,4)) / (2/sqrt(pi)*root(-z,4)) )
1
Tom Bachmann Collaborator
ness01 added a note

Please change these as I explained in another comment; and compute bases (as also explained there).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
raoulb
Collaborator

Now the only things missing are related to the hyperexpand and meijerg stuff. I'll try to
do these soon.

@ness01 : Big Thanks for reviewing my branches!

sympy/functions/special/error_functions.py
((151 lines not shown))
  921 + .. [1] http://en.wikipedia.org/wiki/Fresnel_integral
  922 + .. [2] http://dlmf.nist.gov/7
  923 + .. [3] http://mathworld.wolfram.com/FresnelIntegrals.html
  924 + .. [4] http://functions.wolfram.com/GammaBetaErf/FresnelS
  925 + """
  926 +
  927 + _trigfunc = C.sin
  928 + _sign = -S.One
  929 +
  930 + @staticmethod
  931 + def taylor_term(n, x, *previous_terms):
  932 + if n < 0:
  933 + return S.Zero
  934 + else:
  935 + x = sympify(x)
  936 + return x**3*(-x**4)**n*(S(2)**(-2*n-1)*pi**(2*n+1))/((4*n+3)*C.factorial(2*n+1))
1
Tom Bachmann Collaborator
ness01 added a note

I think it is costumary to declare this function @cacheit. Moreover, you should really use previous_terms ... if you want very many terms, evaluating the factorials and powers is probably slowest.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Tom Bachmann
Collaborator
raoulb
Collaborator

Rebased on master

Tom Bachmann
Collaborator

Just as a reminder what I think has to be done before this can be pushed:

  • Fix the hyperexpand table entries (bases, coefficients); test that expressions for fresnel(cs) in terms of hypergeometric functions come out "nice". Also test contiguous functions (i.e. demonstrate that the basis is "nice").
  • As a culmination of this, make sure integral(sin(x**2)) etc work as expected.
  • Add a numerical test for fresnel(sc).as_real_imag.
  • Add tests for integration of fresnel(sc) (i.e. test your meijerg table entries).
raoulb
Collaborator

As a culmination of this, make sure integral(sin(x**2)) etc work as expected.

Done, but some nasty gamma remain. They should cancel to 1.

Add tests for integration of fresnel(sc) (i.e. test your meijerg table entries).

The command

integrate(fresnels(x), x)

gives me a strange error, not sure yet whats wrong here.

Tom Bachmann
Collaborator
sympy/functions/special/error_functions.py
((63 lines not shown))
  886 + if deep:
  887 + hints['complex'] = False
  888 + return (self.expand(deep, **hints), S.Zero)
  889 + else:
  890 + return (self, S.Zero)
  891 + if deep:
  892 + re, im = self.args[0].expand(deep, **hints).as_real_imag()
  893 + else:
  894 + re, im = self.args[0].as_real_imag()
  895 + return (re, im)
  896 +
  897 + def as_real_imag(self, deep=True, **hints):
  898 + x, y = self._as_real_imag(deep=deep, **hints)
  899 + sq = -y**2/x**2
  900 + re = S.Half*(self.func(x+x*sqrt(sq))+self.func(x-x*sqrt(sq)))
  901 + im = x/(2*y) * sqrt(sq) * (self.func(x-x*sqrt(sq)) - self.func(x+x*sqrt(sq)))
4
Tom Bachmann Collaborator
ness01 added a note

Can you add a comment where this comes from?

raoulb Collaborator
raoulb added a note

These are from here:

    # Fresnel S
    # http://functions.wolfram.com/06.32.19.0003.01
    # http://functions.wolfram.com/06.32.19.0006.01
    # Fresnel C
    # http://functions.wolfram.com/06.33.19.0003.01
    # http://functions.wolfram.com/06.33.19.0006.01 

Should I put this into the code?
Do we want links like these inside the code?

Tom Bachmann Collaborator
ness01 added a note

I'm not sure. @asmeurer ?

Aaron Meurer Owner
asmeurer added a note

I think it's fine. The Wolfram Functions site is a solid resource.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/functions/special/error_functions.py
((156 lines not shown))
  979 +
  980 + _trigfunc = C.sin
  981 + _sign = -S.One
  982 +
  983 + @staticmethod
  984 + @cacheit
  985 + def taylor_term(n, x, *previous_terms):
  986 + if n < 0:
  987 + return S.Zero
  988 + else:
  989 + x = sympify(x)
  990 + if len(previous_terms) > 1:
  991 + p = previous_terms[-1]
  992 + return (-pi**2*x**4*(4*n - 1)/(8*n*(2*n + 1)*(4*n + 3))) * p
  993 + else:
  994 + return x**3*(-x**4)**n*(S(2)**(-2*n-1)*pi**(2*n+1))/((4*n+3)*C.factorial(2*n+1))
1
Tom Bachmann Collaborator
ness01 added a note

PEP8 "requires" a number of spaces here. I suppose use your best judgement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/functions/special/tests/test_error_functions.py
((29 lines not shown))
  257 + assert fresnels(2+3*I).as_real_imag() == (fresnels(2 + 3*I)/2 + fresnels(2 - 3*I)/2, I*(fresnels(2 - 3*I) - fresnels(2 + 3*I))/2)
  258 +
  259 + assert fresnelc(0) == 0
  260 + assert fresnelc(oo) == S.Half
  261 + assert fresnelc(-oo) == -S.Half
  262 +
  263 + assert fresnelc(z) == fresnelc(z)
  264 + assert fresnelc(-z) == -fresnelc(z)
  265 + assert fresnelc(I*z) == I*fresnelc(z)
  266 + assert fresnelc(-I*z) == -I*fresnelc(z)
  267 +
  268 + assert conjugate(fresnelc(z)) == fresnelc(conjugate(z))
  269 +
  270 + assert fresnelc(z).diff(z) == cos(pi*z**2/2)
  271 +
  272 + assert fresnelc(z).rewrite(erf) == (S.One-I)/4 * (erf((S.One+I)/2*sqrt(pi)*z) + I*erf((S.One-I)/2*sqrt(pi)*z))
1
Tom Bachmann Collaborator
ness01 added a note

again, spaces

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/integrals/tests/test_meijerint.py
... ... @@ -586,3 +586,10 @@ def test_3153():
586 586 assert not expr.has(hyper)
587 587 # XXX the expression is a mess, but actually upon differentiation and
588 588 # putting in numerical values seems to work...
  589 +
  590 +
  591 +def test_fresnel():
  592 + from sympy import fresnels, fresnelc
  593 +
  594 + assert integrate(sin(pi*x**2/2),x) == 3*fresnels(x)*gamma(S(3)/4)/(4*gamma(S(7)/4))
1
Tom Bachmann Collaborator
ness01 added a note

apply expand_func to get rid of the gammas

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Tom Bachmann
Collaborator

Regarding the strange integration problem: this is because of the prefactors you put in. They are more complicated my code can handle, and moreover they are not right here. You will see that there is an expression like t**(9/4)*(t**2)**(3/4)*(-t)**(-3/4). Note the unnatural way in which the exponents are written, etc. This is because on the wolfram function site, the variable is an ordinary complex number, and all the exponentiation is ordinary complex exponentiation, with branch cuts and everything. We are using polar numbers, so you can most likely just replace -1 by polar_lift(-1) throughout and then simplify "naively".

I understand that it may not at all be clear what to do, neither about the meijerint nor the hyperexpand table. If you want me to, I can write these entries for you (it's not trivial and will probably take one or two hours) and perhaps an extended documentation at the same time. Of course, if you want to "figure it out yourself", even better :-).

Julien Rioux
Collaborator
jrioux commented

SymPy Bot Summary: :red_circle: There were test failures.

@raoulb: Please fix the test failures.

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sY-OEWDA

Interpreter: /usr/bin/python (2.7.2-final-0)
Architecture: Linux (64-bit)
Cache: yes
Test command: setup.py test
master hash: 8426fe8
branch hash: 3609cc6

Automatic review by SymPy Bot.

Julien Rioux
Collaborator
jrioux commented

SymPy Bot Summary: :red_circle: There were test failures.

@raoulb: Please fix the test failures.

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sYp6sWDA

Interpreter: /usr/bin/python (2.7.0-final-0)
Architecture: Linux (32-bit)
Cache: yes
Test command: setup.py test
master hash: 8426fe8
branch hash: 3609cc6

Automatic review by SymPy Bot.

raoulb
Collaborator
raoulb commented

SymPy Bot Summary: There were test failures.

@raoulb: Please fix the test failures.

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sYzpUYDA

Interpreter: /usr/bin/python (2.7.3-candidate-2)
Architecture: Linux (32-bit)
Cache: yes
Test command: setup.py test
master hash: 25904b8
branch hash: 0089534

Automatic review by SymPy Bot.

raoulb
Collaborator
raoulb commented

This PR should be ready for a new final review.

There are still some (or rather many) nice integrals we can not solve.
For example S(z)^2, C(z)^2, S(z^2), C(z^2), S(z)*C(z) ...
All have solutions in terms of Fresenl and trigonometric functions.

Tom Bachmann
Collaborator
ness01 commented

This needs to be rebased.

raoulb
Collaborator
raoulb commented

Rebased

sympy/core/expr.py
... ... @@ -554,7 +554,7 @@ def _eval_is_positive(self):
554 554 return False
555 555 try:
556 556 # check to see that we can get a value
557   - n2 = self._eval_evalf(1)
  557 + n2 = self.n(1)
1
Tom Bachmann Collaborator
ness01 added a note

@smichr @asmeurer

I'm somewhat unsure about this. The problem manifests as follows: gamma(3*exp_polar(I*pi)/2)._eval_evalf throws some mpmath internal exception. The problem is that _eval_evalf evaluates the argument to 1 binary digit accuracy, which apparently is -1, and then evaluates gamma at -1, causing the exception. I believe calling n instead alleviates the problem, since then a special routine for evaluating the gamma function is called, which knows about the exception, and tries evaluating at higher precision. [Which works, of course.]

I wonder if something else should be fixed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Tom Bachmann ness01 commented on the diff
sympy/functions/__init__.py
... ... @@ -23,9 +22,8 @@
23 22 asinh, acosh, atanh, acoth)
24 23 from sympy.functions.elementary.integers import floor, ceiling
25 24 from sympy.functions.elementary.piecewise import Piecewise, piecewise_fold
26   -
27   -from sympy.functions.special.error_functions import (erf, Ei, expint, E1,
28   - Si, Ci, Shi, Chi)
  25 +from sympy.functions.special.error_functions import (erf, Ei, expint,
2
Tom Bachmann Collaborator
ness01 added a note

Can you add the sympy. back in? I know it does not really matter, but I don't see why we should break the pattern either.

raoulb Collaborator
raoulb added a note

Should be ok with latest push

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Tom Bachmann ness01 commented on the diff
sympy/functions/special/error_functions.py
((123 lines not shown))
  950 + -fresnels(z)
  951 +
  952 + >>> fresnels(I*z)
  953 + -I*fresnels(z)
  954 +
  955 + The Fresnel S integral obeys the mirror symmetry:
  956 +
  957 + >>> from sympy import conjugate
  958 + >>> conjugate(fresnels(z))
  959 + fresnels(conjugate(z))
  960 +
  961 + Differentiation with respect to z is supported:
  962 +
  963 + >>> from sympy import diff
  964 + >>> diff(fresnels(z), z)
  965 + sin(pi*z**2/2)
2
Tom Bachmann Collaborator
ness01 added a note

Maybe add an integration example as well? Not too important.

raoulb Collaborator
raoulb added a note

Done, I just used the defining integral.
We could also show the Laplace transform.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/functions/special/tests/test_error_functions.py
... ... @@ -1,6 +1,6 @@
1   -from sympy import (symbols, expand, erf, nan, oo, Float, conjugate, sqrt, exp, pi, O, I, Ei,
2   - exp_polar, polar_lift, Symbol, I, exp, uppergamma, expint, log, loggamma, limit,
3   - meijerg, gamma, S, Shi, Chi, Si, Ci, E1, sin, cos, sinh, cosh)
  1 +from sympy import (symbols, expand, expand_func, erf, nan, oo, Float, conjugate, sqrt, sin, cos, pi, re, im, Abs, O,
  2 + factorial, exp_polar, polar_lift, Symbol, I, integrate, exp, uppergamma, expint, log, loggamma, limit,
  3 + hyper, meijerg, gamma, S, Shi, Chi, Si, Ci, E1, Ei, sin, cos, sinh, cosh, fresnels, fresnelc)
2
Tom Bachmann Collaborator
ness01 added a note

These lines need to be wrapped.

raoulb Collaborator
raoulb added a note

Done

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/integrals/tests/test_transforms.py
((12 lines not shown))
478 487 def test_inverse_laplace_transform():
479 488 from sympy import (expand, sinh, cosh, besselj, besseli, exp_polar,
480   - unpolarify, simplify)
  489 + unpolarify, simplify, fresnels, fresnelc)
2
Tom Bachmann Collaborator
ness01 added a note

This does not seem necessary? [presumably you meant to check inverse transforms and realised they did not work ^^]

raoulb Collaborator
raoulb added a note

No, just pasted into the wrong function and forgot about it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
sympy/simplify/hyperexpand.py
... ... @@ -162,6 +163,13 @@ def addb(ap, bq, B, C, M):
162 163 # This one is redundant.
163 164 add([-S.Half], [S.Half], exp(z) - sqrt(pi*z)*(-I)*erf(I*sqrt(z)))
164 165
  166 + # Added to get nice results for Laplace transform of Fresnel functions
  167 + # http://functions.wolfram.com/07.22.03.6437.01
  168 + add([1], [S(3)/4, S(5)/4],
  169 + sqrt(pi) * (cos(2*sqrt(polar_lift(-1)*z))*fresnelc(2*root(polar_lift(-1)*z,4)/sqrt(pi)) +
  170 + sin(2*sqrt(polar_lift(-1)*z))*fresnels(2*root(polar_lift(-1)*z,4)/sqrt(pi)))
  171 + / (2*root(polar_lift(-1)*z,4)))
4
Tom Bachmann Collaborator
ness01 added a note

I hate to say this, but you should convert this entry to addb format ...

raoulb Collaborator
raoulb added a note

I tried to do, but it was not so obvious to me what to put into the basis. I have only three slots in the basis
but a few more function-terms which could be put there. Maybe we should discuss this on irc. But I won't be
there tonight.

Tom Bachmann Collaborator
ness01 added a note

Let w = exp(I*pi/4). I believe

sqrt(pi)/(2*w) z**(-1/4) * (cosh(2*sqrt(z))*fresnelc(2*w*z**(1/4)/sqr(pi)) + I*sinh(2*sqrt(z))*fresnels(2*w/sqrt(pi)*z**(1/4)))

sqrt(pi)/(2*w) z**(1/4) * (sinh(2*sqrt(z))*fresnelc(2*w*z**(1/4)/sqr(pi)) + I*cosh(2*sqrt(z))*fresnels(2*w/sqrt(pi)*z**(1/4)))

1

(the constant function 1) should make a good basis. Then the first row of M would be something like [-1/4, 1, 1/4], the last row would be [0, 0, 0] and the middle row (as well as checking my first row) I leave to you :-).

raoulb Collaborator
raoulb added a note

Thanks, that was helpful! I didn't notice that grouping the terms this way leads to a good basis.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Tom Bachmann ness01 commented on the diff
sympy/utilities/lambdify.py
... ... @@ -50,7 +50,7 @@
50 50 "Shi":"shi",
51 51 "Chi":"chi",
52 52 "Si":"si",
53   - "Ci":"ci",
  53 + "Ci":"ci"
3
Tom Bachmann Collaborator
ness01 added a note

this looks like an unrelated and unnecessary change …?

raoulb Collaborator
raoulb added a note

Do I really have to undo this now?

Tom Bachmann Collaborator
ness01 added a note
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Tom Bachmann
Collaborator
ness01 commented

(I'm waiting for the test runner but to come round once more)

raoulb added some commits
raoulb raoulb First implementation of Fresnel integrals fb2b533
raoulb raoulb Complex expansion for Fresnel integrals 7d75819
raoulb raoulb Asymptotic series expansions for Fresnel integrals b5d6444
raoulb raoulb Leading term expansion for Fresnel integrals 9dc7c0f
raoulb raoulb Numerical evaluation for Fresnel integrals using mpmath ab67ab7
raoulb raoulb Series expansions for Fresnel integrals e5f83d9
raoulb raoulb Documentation and examples for Fresnel integrals bc3fb2d
raoulb raoulb Better asymptotic series expansions for Fresnel integrals e4953d0
raoulb raoulb Examples in an IPython notebook 75c3ed3
raoulb raoulb Fixed numerical evaluation of Fresnel integrals. 5f909a4
raoulb raoulb Updated examples in an IPython notebook 8c5a8e2
raoulb raoulb Renamed Fresnel integrals to 'fresnels' and 'fresnelc' 379873a
raoulb raoulb Add testcases for the Fresnel integrals 5080d5a
raoulb raoulb Adding Fresnel integrals to the hyperexpand Tables a4e7985
raoulb raoulb Declared the Fresnel integrals to be unbranched. 53c03b1
raoulb raoulb Apply comments from pull request review cd79acc
raoulb raoulb Added Meijer G representation of Fresnel Integrals e3b854a
raoulb raoulb Caching 'taylor_term' functions 19685ac
raoulb raoulb Improved Taylor terms of Fresnel integrals 86741fe
raoulb raoulb Test integrals defining Fresnel functions e002dd8
raoulb raoulb Addressing latest comments on code 0cf95a1
raoulb raoulb Rules to rewrite fresnel{s,c} functions in terms of 1F2 hypergeometri…
…c functions
b9d4933
raoulb raoulb Fixed hypergeometric formula for Fresnel functions 694d99d
raoulb raoulb Fix test_args test failures d26024f
raoulb raoulb Fix integration of Fresnel functions a44422f
raoulb raoulb Add tests for Fresnel integration 92f48ca
raoulb
Collaborator
raoulb commented

With this change, I hope the Fresnel stuff is finished now.

Tom Bachmann
Collaborator
ness01 commented

Actually, no. Something you did makes test_cg hang. I have no idea what, but you'll have to bisect this. [Note that the test runs in less then a few seconds in master.]

raoulb
Collaborator
raoulb commented

Ah, so I'm responsible that it hangs :-/

raoulb
Collaborator
raoulb commented

BTW: See also my new Fresnel examples notebook

Tom Bachmann
Collaborator
ness01 commented

Well it could just as easily be one of the patches I sent you, but it is confined to your branch, yes.

raoulb
Collaborator
raoulb commented

This one hangs:

cg_simp(2 * Sum(CG(1,alpha,0,0,1,alpha), (alpha,-1,1))) == 6

raoulb
Collaborator
raoulb commented

Actually, I think I found it to be your patch for the gamma poles.
At least without this change it runs fine.

Tom Bachmann
Collaborator
ness01 commented
Tom Bachmann
Collaborator
ness01 commented

Can you try this patch instead of the problematic commit:

diff --git a/sympy/core/function.py b/sympy/core/function.py
index 01c942d..22eb252 100644
--- a/sympy/core/function.py
+++ b/sympy/core/function.py
@@ -366,8 +366,12 @@ def _eval_evalf(self, prec):
                 return

         # Convert all args to mpf or mpc
+        # Convert the arguments to *higher* precision than requested for the
+        # final result.
+        # XXX + 5 is a guess, it is similar to what is used in evalf.py. Should
+        #     we be more intelligent about it?
         try:
-            args = [arg._to_mpmath(prec) for arg in self.args]
+            args = [arg._to_mpmath(prec + 5) for arg in self.args]
         except ValueError:
             return
raoulb added some commits
raoulb raoulb Tests for the gamma pole patch 2f579d2
raoulb raoulb A new rule in hyperexpand to get nice results for the Laplace transfo…
…rm of Fresnel functions

Examples without this formula:

  laplace_transform(fresnels(t), t, s, noconds=True)

   ⎛         ⎛         │    4 ⎞        ⎛  2⎞        ⎛  2⎞⎞
   ⎜     ┌─  ⎜   1     │  -s  ⎟        ⎜ s ⎟        ⎜ s ⎟⎟
  -⎜2⋅s⋅ ├─  ⎜         │ ─────⎟ - π⋅sin⎜───⎟ - π⋅cos⎜───⎟⎟
   ⎜    1╵ 2 ⎜3/4, 5/4 │     2⎟        ⎝2⋅π⎠        ⎝2⋅π⎠⎟
   ⎝         ⎝         │ 16⋅π ⎠                          ⎠
  ────────────────────────────────────────────────────────
                           2⋅π⋅s

  laplace_transform(fresnelc(t), t, s, noconds=True)

            ⎛         │    4 ⎞           ⎛  2⎞           ⎛  2⎞
     3  ┌─  ⎜   1     │  -s  ⎟      2    ⎜ s ⎟      2    ⎜ s ⎟
  2⋅s ⋅ ├─  ⎜         │ ─────⎟ - 3⋅π ⋅sin⎜───⎟ + 3⋅π ⋅cos⎜───⎟
       1╵ 2 ⎜5/4, 7/4 │     2⎟           ⎝2⋅π⎠           ⎝2⋅π⎠
            ⎝         │ 16⋅π ⎠
  ────────────────────────────────────────────────────────────
                                2
                             6⋅π ⋅s

And with this formula we can expand the 1F2 above:

  laplace_transform(fresnels(t), t, s, noconds=True)

       ⎛  2⎞                  ⎛  2⎞      ⎛  2⎞                  ⎛  2⎞
       ⎜ s ⎟         ⎛s⎞      ⎜ s ⎟      ⎜ s ⎟         ⎛s⎞      ⎜ s ⎟
    sin⎜───⎟⋅fresnels⎜─⎟   sin⎜───⎟   cos⎜───⎟⋅fresnelc⎜─⎟   cos⎜───⎟
       ⎝2⋅π⎠         ⎝π⎠      ⎝2⋅π⎠      ⎝2⋅π⎠         ⎝π⎠      ⎝2⋅π⎠
  - ──────────────────── + ──────── - ──────────────────── + ────────
             s               2⋅s               s               2⋅s

  laplace_transform(fresnelc(t), t, s, noconds=True)

     ⎛  2⎞                  ⎛  2⎞      ⎛  2⎞                  ⎛  2⎞
     ⎜ s ⎟         ⎛s⎞      ⎜ s ⎟      ⎜ s ⎟         ⎛s⎞      ⎜ s ⎟
  sin⎜───⎟⋅fresnelc⎜─⎟   sin⎜───⎟   cos⎜───⎟⋅fresnels⎜─⎟   cos⎜───⎟
     ⎝2⋅π⎠         ⎝π⎠      ⎝2⋅π⎠      ⎝2⋅π⎠         ⎝π⎠      ⎝2⋅π⎠
  ──────────────────── - ──────── - ──────────────────── + ────────
           s               2⋅s               s               2⋅s

This result is nice and correct.
14ddbc1
raoulb raoulb Adapt tests to new Laplace transformations of Fresnels 2a47c0b
raoulb raoulb Fix a remaining test failure 748d08b
raoulb raoulb Rewrite Fresnel functions in terms of Meijer G e370757
raoulb raoulb Tests for Fresnel rewrites 248365e
raoulb raoulb A different patch for the gamma pole issue (by ness) f3d3bc6
raoulb raoulb Fresnel function latex printing b3ba93f
raoulb raoulb Add integral example to Fresnel docstring 62bd77c
raoulb raoulb Addressed points from review discussion 008560c
raoulb raoulb Manually tuned rule for Laplace transform of Fresnels 56c6af0
raoulb
Collaborator
raoulb commented

SymPy Bot Summary: All tests have passed.

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sYh7AXDA

Interpreter: /usr/bin/python (2.7.3-candidate-2)
Architecture: Linux (32-bit)
Cache: yes
Test command: setup.py test
master hash: 4c213da
branch hash: 56c6af0

Automatic review by SymPy Bot.

raoulb
Collaborator
raoulb commented

Seems to work ok.

Tom Bachmann
Collaborator
ness01 commented

SymPy Bot Summary: :eight_spoked_asterisk: All tests have passed.

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sYyO4XDA

Interpreter: /usr/bin/python (2.7.3-candidate-2)
Architecture: Linux (64-bit)
Cache: yes
Test command: setup.py test
master hash: 4c213da
branch hash: 56c6af0

Automatic review by SymPy Bot.

Tom Bachmann ness01 merged commit 30cb220 into from
Tom Bachmann ness01 closed this
Tom Bachmann
Collaborator
ness01 commented

This is in. Good work!

raoulb
Collaborator
raoulb commented

Thanks :-)
And also thanks for your support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 37 unique commits by 1 author.

May 12, 2012
raoulb raoulb First implementation of Fresnel integrals fb2b533
raoulb raoulb Complex expansion for Fresnel integrals 7d75819
raoulb raoulb Asymptotic series expansions for Fresnel integrals b5d6444
raoulb raoulb Leading term expansion for Fresnel integrals 9dc7c0f
raoulb raoulb Numerical evaluation for Fresnel integrals using mpmath ab67ab7
raoulb raoulb Series expansions for Fresnel integrals e5f83d9
raoulb raoulb Documentation and examples for Fresnel integrals bc3fb2d
raoulb raoulb Better asymptotic series expansions for Fresnel integrals e4953d0
raoulb raoulb Examples in an IPython notebook 75c3ed3
raoulb raoulb Fixed numerical evaluation of Fresnel integrals. 5f909a4
raoulb raoulb Updated examples in an IPython notebook 8c5a8e2
raoulb raoulb Renamed Fresnel integrals to 'fresnels' and 'fresnelc' 379873a
raoulb raoulb Add testcases for the Fresnel integrals 5080d5a
raoulb raoulb Adding Fresnel integrals to the hyperexpand Tables a4e7985
raoulb raoulb Declared the Fresnel integrals to be unbranched. 53c03b1
raoulb raoulb Apply comments from pull request review cd79acc
raoulb raoulb Added Meijer G representation of Fresnel Integrals e3b854a
raoulb raoulb Caching 'taylor_term' functions 19685ac
raoulb raoulb Improved Taylor terms of Fresnel integrals 86741fe
raoulb raoulb Test integrals defining Fresnel functions e002dd8
raoulb raoulb Addressing latest comments on code 0cf95a1
raoulb raoulb Rules to rewrite fresnel{s,c} functions in terms of 1F2 hypergeometri…
…c functions
b9d4933
raoulb raoulb Fixed hypergeometric formula for Fresnel functions 694d99d
raoulb raoulb Fix test_args test failures d26024f
raoulb raoulb Fix integration of Fresnel functions a44422f
raoulb raoulb Add tests for Fresnel integration 92f48ca
raoulb raoulb Tests for the gamma pole patch 2f579d2
raoulb raoulb A new rule in hyperexpand to get nice results for the Laplace transfo…
…rm of Fresnel functions

Examples without this formula:

  laplace_transform(fresnels(t), t, s, noconds=True)

   ⎛         ⎛         │    4 ⎞        ⎛  2⎞        ⎛  2⎞⎞
   ⎜     ┌─  ⎜   1     │  -s  ⎟        ⎜ s ⎟        ⎜ s ⎟⎟
  -⎜2⋅s⋅ ├─  ⎜         │ ─────⎟ - π⋅sin⎜───⎟ - π⋅cos⎜───⎟⎟
   ⎜    1╵ 2 ⎜3/4, 5/4 │     2⎟        ⎝2⋅π⎠        ⎝2⋅π⎠⎟
   ⎝         ⎝         │ 16⋅π ⎠                          ⎠
  ────────────────────────────────────────────────────────
                           2⋅π⋅s

  laplace_transform(fresnelc(t), t, s, noconds=True)

            ⎛         │    4 ⎞           ⎛  2⎞           ⎛  2⎞
     3  ┌─  ⎜   1     │  -s  ⎟      2    ⎜ s ⎟      2    ⎜ s ⎟
  2⋅s ⋅ ├─  ⎜         │ ─────⎟ - 3⋅π ⋅sin⎜───⎟ + 3⋅π ⋅cos⎜───⎟
       1╵ 2 ⎜5/4, 7/4 │     2⎟           ⎝2⋅π⎠           ⎝2⋅π⎠
            ⎝         │ 16⋅π ⎠
  ────────────────────────────────────────────────────────────
                                2
                             6⋅π ⋅s

And with this formula we can expand the 1F2 above:

  laplace_transform(fresnels(t), t, s, noconds=True)

       ⎛  2⎞                  ⎛  2⎞      ⎛  2⎞                  ⎛  2⎞
       ⎜ s ⎟         ⎛s⎞      ⎜ s ⎟      ⎜ s ⎟         ⎛s⎞      ⎜ s ⎟
    sin⎜───⎟⋅fresnels⎜─⎟   sin⎜───⎟   cos⎜───⎟⋅fresnelc⎜─⎟   cos⎜───⎟
       ⎝2⋅π⎠         ⎝π⎠      ⎝2⋅π⎠      ⎝2⋅π⎠         ⎝π⎠      ⎝2⋅π⎠
  - ──────────────────── + ──────── - ──────────────────── + ────────
             s               2⋅s               s               2⋅s

  laplace_transform(fresnelc(t), t, s, noconds=True)

     ⎛  2⎞                  ⎛  2⎞      ⎛  2⎞                  ⎛  2⎞
     ⎜ s ⎟         ⎛s⎞      ⎜ s ⎟      ⎜ s ⎟         ⎛s⎞      ⎜ s ⎟
  sin⎜───⎟⋅fresnelc⎜─⎟   sin⎜───⎟   cos⎜───⎟⋅fresnels⎜─⎟   cos⎜───⎟
     ⎝2⋅π⎠         ⎝π⎠      ⎝2⋅π⎠      ⎝2⋅π⎠         ⎝π⎠      ⎝2⋅π⎠
  ──────────────────── - ──────── - ──────────────────── + ────────
           s               2⋅s               s               2⋅s

This result is nice and correct.
14ddbc1
raoulb raoulb Adapt tests to new Laplace transformations of Fresnels 2a47c0b
raoulb raoulb Fix a remaining test failure 748d08b
raoulb raoulb Rewrite Fresnel functions in terms of Meijer G e370757
raoulb raoulb Tests for Fresnel rewrites 248365e
raoulb raoulb A different patch for the gamma pole issue (by ness) f3d3bc6
raoulb raoulb Fresnel function latex printing b3ba93f
raoulb raoulb Add integral example to Fresnel docstring 62bd77c
raoulb raoulb Addressed points from review discussion 008560c
raoulb raoulb Manually tuned rule for Laplace transform of Fresnels 56c6af0
This page is out of date. Refresh to see the latest.
310 examples/notebooks/fresnel_integrals.ipynb
310 additions, 0 deletions not shown
6 sympy/core/function.py
@@ -366,8 +366,12 @@ def _eval_evalf(self, prec):
366 366 return
367 367
368 368 # Convert all args to mpf or mpc
  369 + # Convert the arguments to *higher* precision than requested for the
  370 + # final result.
  371 + # XXX + 5 is a guess, it is similar to what is used in evalf.py. Should
  372 + # we be more intelligent about it?
369 373 try:
370   - args = [arg._to_mpmath(prec) for arg in self.args]
  374 + args = [arg._to_mpmath(prec + 5) for arg in self.args]
371 375 except ValueError:
372 376 return
373 377
12 sympy/core/tests/test_args.py
@@ -904,6 +904,18 @@ def test_sympy__functions__special__error_functions__erf():
904 904 from sympy.functions.special.error_functions import erf
905 905 assert _test_args(erf(2))
906 906
  907 +@SKIP("abstract class")
  908 +def test_sympy__functions__special__error_functions__FresnelIntegral():
  909 + pass
  910 +
  911 +def test_sympy__functions__special__error_functions__fresnels():
  912 + from sympy.functions.special.error_functions import fresnels
  913 + assert _test_args(fresnels(2))
  914 +
  915 +def test_sympy__functions__special__error_functions__fresnelc():
  916 + from sympy.functions.special.error_functions import fresnelc
  917 + assert _test_args(fresnelc(2))
  918 +
907 919 def test_sympy__functions__special__error_functions__erfs():
908 920 from sympy.functions.special.error_functions import _erfs
909 921 assert _test_args(_erfs(2))
6 sympy/functions/__init__.py
@@ -9,7 +9,6 @@
9 9 rf, ff, binomial, RisingFactorial, FallingFactorial)
10 10 from sympy.functions.combinatorial.numbers import (fibonacci, lucas, harmonic,
11 11 bernoulli, bell, euler, catalan)
12   -
13 12 from sympy.functions.elementary.miscellaneous import (sqrt, root, Min, Max,
14 13 Id, real_root)
15 14 from sympy.functions.elementary.complexes import (re, im, sign, Abs,
@@ -23,9 +22,8 @@
23 22 asinh, acosh, atanh, acoth)
24 23 from sympy.functions.elementary.integers import floor, ceiling
25 24 from sympy.functions.elementary.piecewise import Piecewise, piecewise_fold
26   -
27   -from sympy.functions.special.error_functions import (erf, Ei, expint, E1,
28   - Si, Ci, Shi, Chi)
  25 +from sympy.functions.special.error_functions import (erf, Ei, expint,
  26 + E1, Si, Ci, Shi, Chi, fresnels, fresnelc)
29 27 from sympy.functions.special.gamma_functions import (gamma, lowergamma,
30 28 uppergamma, polygamma, loggamma, digamma, trigamma, beta)
31 29 from sympy.functions.special.zeta_functions import (dirichlet_eta, zeta,
302 sympy/functions/special/error_functions.py
@@ -3,11 +3,11 @@
3 3
4 4 from sympy.core import Add, S, C, sympify, cacheit, pi, I
5 5 from sympy.core.function import Function, ArgumentIndexError
6   -from sympy.functions.elementary.miscellaneous import sqrt
  6 +from sympy.functions.elementary.miscellaneous import sqrt, root
7 7 from sympy.functions.elementary.complexes import polar_lift
  8 +from sympy.functions.special.hyper import hyper, meijerg
8 9
9 10 # TODO series expansions
10   -# TODO fresnel integrals
11 11 # TODO see the "Note:" in Ei
12 12
13 13 ###############################################################################
@@ -829,10 +829,306 @@ def _eval_rewrite_as_expint(self, z):
829 829 from sympy import exp_polar
830 830 return -I*pi/2 - (E1(z) + E1(exp_polar(I*pi)*z))/2
831 831
  832 +
832 833 ###############################################################################
833   -#################### HELPER FUNCTIONS #########################################
  834 +#################### FRESNEL INTEGRALS ########################################
834 835 ###############################################################################
835 836
  837 +class FresnelIntegral(Function):
  838 + """ Base class for the Fresnel integrals."""
  839 +
  840 + nargs = 1
  841 + unbranched = True
  842 +
  843 + @classmethod
  844 + def eval(cls, z):
  845 + # Value at zero
  846 + if z is S.Zero:
  847 + return S(0)
  848 +
  849 + # Try to pull out factors of -1 and I
  850 + prefact = S.One
  851 + newarg = z
  852 + changed = False
  853 +
  854 + nz = newarg.extract_multiplicatively(-1)
  855 + if nz is not None:
  856 + prefact = -prefact
  857 + newarg = nz
  858 + changed = True
  859 +
  860 + nz = newarg.extract_multiplicatively(I)
  861 + if nz is not None:
  862 + prefact = cls._sign*I*prefact
  863 + newarg = nz
  864 + changed = True
  865 +
  866 + if changed:
  867 + return prefact*cls(newarg)
  868 +
  869 + # Values at positive infinities signs
  870 + # if any were extracted automatically
  871 + if z is S.Infinity:
  872 + return S.Half
  873 + elif z is I*S.Infinity:
  874 + return cls._sign*I*S.Half
  875 +
  876 + def fdiff(self, argindex=1):
  877 + if argindex == 1:
  878 + return self._trigfunc(S.Half*pi*self.args[0]**2)
  879 + else:
  880 + raise ArgumentIndexError(self, argindex)
  881 +
  882 + def _eval_is_real(self):
  883 + return self.args[0].is_real
  884 +
  885 + def _eval_conjugate(self):
  886 + return self.func(self.args[0].conjugate())
  887 +
  888 + def _as_real_imag(self, deep=True, **hints):
  889 + if self.args[0].is_real:
  890 + if deep:
  891 + hints['complex'] = False
  892 + return (self.expand(deep, **hints), S.Zero)
  893 + else:
  894 + return (self, S.Zero)
  895 + if deep:
  896 + re, im = self.args[0].expand(deep, **hints).as_real_imag()
  897 + else:
  898 + re, im = self.args[0].as_real_imag()
  899 + return (re, im)
  900 +
  901 + def as_real_imag(self, deep=True, **hints):
  902 + # Fresnel S
  903 + # http://functions.wolfram.com/06.32.19.0003.01
  904 + # http://functions.wolfram.com/06.32.19.0006.01
  905 + # Fresnel C
  906 + # http://functions.wolfram.com/06.33.19.0003.01
  907 + # http://functions.wolfram.com/06.33.19.0006.01
  908 + x, y = self._as_real_imag(deep=deep, **hints)
  909 + sq = -y**2/x**2
  910 + re = S.Half*(self.func(x+x*sqrt(sq))+self.func(x-x*sqrt(sq)))
  911 + im = x/(2*y) * sqrt(sq) * (self.func(x-x*sqrt(sq)) - self.func(x+x*sqrt(sq)))
  912 + return (re, im)
  913 +
  914 + def _eval_expand_complex(self, deep=True, **hints):
  915 + re_part, im_part = self.as_real_imag(deep=deep, **hints)
  916 + return re_part + im_part*S.ImaginaryUnit
  917 +
  918 +
  919 +class fresnels(FresnelIntegral):
  920 + r"""
  921 + Fresnel integral S.
  922 +
  923 + This function is defined by
  924 +
  925 + .. math:: \operatorname{S}(z) = \int_0^z \sin{\frac{\pi}{2} t^2} \mathrm{d}t.
  926 +
  927 + It is an entire function.
  928 +
  929 + Examples
  930 + ========
  931 +
  932 + >>> from sympy import I, oo, fresnels
  933 + >>> from sympy.abc import z
  934 +
  935 + Several special values are known:
  936 +
  937 + >>> fresnels(0)
  938 + 0
  939 + >>> fresnels(oo)
  940 + 1/2
  941 + >>> fresnels(-oo)
  942 + -1/2
  943 + >>> fresnels(I*oo)
  944 + -I/2
  945 + >>> fresnels(-I*oo)
  946 + I/2
  947 +
  948 + In general one can pull out factors of -1 and I from the argument:
  949 + >>> fresnels(-z)
  950 + -fresnels(z)
  951 +
  952 + >>> fresnels(I*z)
  953 + -I*fresnels(z)
  954 +
  955 + The Fresnel S integral obeys the mirror symmetry:
  956 +
  957 + >>> from sympy import conjugate
  958 + >>> conjugate(fresnels(z))
  959 + fresnels(conjugate(z))
  960 +
  961 + Differentiation with respect to z is supported:
  962 +
  963 + >>> from sympy import diff
  964 + >>> diff(fresnels(z), z)
  965 + sin(pi*z**2/2)
  966 +
  967 + Defining the Fresnel functions via an integral
  968 +
  969 + >>> from sympy import integrate, pi, sin, gamma, expand_func
  970 + >>> integrate(sin(pi*z**2/2), z)
  971 + 3*fresnels(z)*gamma(3/4)/(4*gamma(7/4))
  972 + >>> expand_func(integrate(sin(pi*z**2/2), z))
  973 + fresnels(z)
  974 +
  975 + We can numerically evaluate the Fresnel integral to arbitrary precision
  976 + on the whole complex plane:
  977 +
  978 + >>> fresnels(2).evalf(30)
  979 + 0.343415678363698242195300815958
  980 +
  981 + >>> fresnels(-2*I).evalf(30)
  982 + 0.343415678363698242195300815958*I
  983 +
  984 + See Also
  985 + ========
  986 +
  987 + fresnelc
  988 +
  989 + References
  990 + ==========
  991 +
  992 + .. [1] http://en.wikipedia.org/wiki/Fresnel_integral
  993 + .. [2] http://dlmf.nist.gov/7
  994 + .. [3] http://mathworld.wolfram.com/FresnelIntegrals.html
  995 + .. [4] http://functions.wolfram.com/GammaBetaErf/FresnelS
  996 + """
  997 +
  998 + _trigfunc = C.sin
  999 + _sign = -S.One
  1000 +
  1001 + @staticmethod
  1002 + @cacheit
  1003 + def taylor_term(n, x, *previous_terms):
  1004 + if n < 0:
  1005 + return S.Zero
  1006 + else:
  1007 + x = sympify(x)
  1008 + if len(previous_terms) > 1:
  1009 + p = previous_terms[-1]
  1010 + return (-pi**2*x**4*(4*n - 1)/(8*n*(2*n + 1)*(4*n + 3))) * p
  1011 + else:
  1012 + return x**3 * (-x**4)**n * (S(2)**(-2*n - 1)*pi**(2*n + 1)) / ((4*n + 3)*C.factorial(2*n + 1))
  1013 +
  1014 + def _eval_rewrite_as_erf(self, z):
  1015 + return (S.One + I)/4 * (erf((S.One + I)/2*sqrt(pi)*z) - I*erf((S.One - I)/2*sqrt(pi)*z))
  1016 +
  1017 + def _eval_rewrite_as_hyper(self, z):
  1018 + return pi*z**3/6 * hyper([S(3)/4], [S(3)/2, S(7)/4], -pi**2*z**4/16)
  1019 +
  1020 + def _eval_rewrite_as_meijerg(self, z):
  1021 + return (pi*z**(S(9)/4) / (sqrt(2)*(z**2)**(S(3)/4)*(-z)**(S(3)/4))
  1022 + * meijerg([],[1],[S(3)/4],[S(1)/4,0],-pi**2*z**4/16))
  1023 +
  1024 +class fresnelc(FresnelIntegral):
  1025 + r"""
  1026 + Fresnel integral C.
  1027 +
  1028 + This function is defined by
  1029 +
  1030 + .. math:: \operatorname{C}(z) = \int_0^z \cos{\frac{\pi}{2} t^2} \mathrm{d}t.
  1031 +
  1032 + It is an entire function.
  1033 +
  1034 + Examples
  1035 + ========
  1036 +
  1037 + >>> from sympy import I, oo, fresnelc
  1038 + >>> from sympy.abc import z
  1039 +
  1040 + Several special values are known:
  1041 +
  1042 + >>> fresnelc(0)
  1043 + 0
  1044 + >>> fresnelc(oo)
  1045 + 1/2
  1046 + >>> fresnelc(-oo)
  1047 + -1/2
  1048 + >>> fresnelc(I*oo)
  1049 + I/2
  1050 + >>> fresnelc(-I*oo)
  1051 + -I/2
  1052 +
  1053 + In general one can pull out factors of -1 and I from the argument:
  1054 + >>> fresnelc(-z)
  1055 + -fresnelc(z)
  1056 +
  1057 + >>> fresnelc(I*z)
  1058 + I*fresnelc(z)
  1059 +
  1060 + The Fresnel C integral obeys the mirror symmetry:
  1061 +
  1062 + >>> from sympy import conjugate
  1063 + >>> conjugate(fresnelc(z))
  1064 + fresnelc(conjugate(z))
  1065 +
  1066 + Differentiation with respect to z is supported:
  1067 +
  1068 + >>> from sympy import diff
  1069 + >>> diff(fresnelc(z), z)
  1070 + cos(pi*z**2/2)
  1071 +
  1072 + Defining the Fresnel functions via an integral
  1073 +
  1074 + >>> from sympy import integrate, pi, cos, gamma, expand_func
  1075 + >>> integrate(cos(pi*z**2/2), z)
  1076 + fresnelc(z)*gamma(1/4)/(4*gamma(5/4))
  1077 + >>> expand_func(integrate(cos(pi*z**2/2), z))
  1078 + fresnelc(z)
  1079 +
  1080 + We can numerically evaluate the Fresnel integral to arbitrary precision
  1081 + on the whole complex plane:
  1082 +
  1083 + >>> fresnelc(2).evalf(30)
  1084 + 0.488253406075340754500223503357
  1085 +
  1086 + >>> fresnelc(-2*I).evalf(30)
  1087 + -0.488253406075340754500223503357*I
  1088 +
  1089 + See Also
  1090 + ========
  1091 +
  1092 + fresnels
  1093 +
  1094 + References
  1095 + ==========
  1096 +
  1097 + .. [1] http://en.wikipedia.org/wiki/Fresnel_integral
  1098 + .. [2] http://dlmf.nist.gov/7
  1099 + .. [3] http://mathworld.wolfram.com/FresnelIntegrals.html
  1100 + .. [4] http://functions.wolfram.com/GammaBetaErf/FresnelC
  1101 + """
  1102 +
  1103 + _trigfunc = C.cos
  1104 + _sign = S.One
  1105 +
  1106 + @staticmethod
  1107 + @cacheit
  1108 + def taylor_term(n, x, *previous_terms):
  1109 + if n < 0:
  1110 + return S.Zero
  1111 + else:
  1112 + x = sympify(x)
  1113 + if len(previous_terms) > 1:
  1114 + p = previous_terms[-1]
  1115 + return (-pi**2*x**4*(4*n - 3)/(8*n*(2*n - 1)*(4*n + 1))) * p
  1116 + else:
  1117 + return x * (-x**4)**n * (S(2)**(-2*n)*pi**(2*n)) / ((4*n + 1)*C.factorial(2*n))
  1118 +
  1119 + def _eval_rewrite_as_erf(self, z):
  1120 + return (S.One - I)/4 * (erf((S.One + I)/2*sqrt(pi)*z) + I*erf((S.One - I)/2*sqrt(pi)*z))
  1121 +
  1122 + def _eval_rewrite_as_hyper(self, z):
  1123 + return z * hyper([S.One/4], [S.One/2, S(5)/4], -pi**2*z**4/16)
  1124 +
  1125 + def _eval_rewrite_as_meijerg(self, z):
  1126 + return (pi*z**(S(3)/4) / (sqrt(2)*root(z**2,4)*root(-z,4))
  1127 + * meijerg([],[1],[S(1)/4],[S(3)/4,0],-pi**2*z**4/16))
  1128 +
  1129 +###############################################################################
  1130 +#################### HELPER FUNCTIONS #########################################
  1131 +###############################################################################
836 1132
837 1133 class _erfs(Function):
838 1134 """
88 sympy/functions/special/tests/test_error_functions.py
... ... @@ -1,6 +1,8 @@
1   -from sympy import (symbols, expand, erf, nan, oo, Float, conjugate, sqrt, exp, pi, O, I, Ei,
2   - exp_polar, polar_lift, Symbol, I, exp, uppergamma, expint, log, loggamma, limit,
3   - meijerg, gamma, S, Shi, Chi, Si, Ci, E1, sin, cos, sinh, cosh)
  1 +from sympy import (symbols, expand, expand_func, erf, nan, oo, Float, conjugate,
  2 + sqrt, sin, cos, pi, re, im, Abs, O, factorial, exp_polar,
  3 + polar_lift, Symbol, I, integrate, exp, uppergamma, expint,
  4 + log, loggamma, limit, hyper, meijerg, gamma, S, Shi, Chi,
  5 + Si, Ci, E1, Ei, sin, cos, sinh, cosh, fresnels, fresnelc)
4 6
5 7 from sympy.functions.special.error_functions import _erfs
6 8
@@ -9,6 +11,8 @@
9 11 from sympy.utilities.pytest import raises
10 12
11 13 x, y, z = symbols('x,y,z')
  14 +w = Symbol("w", real=True)
  15 +n = Symbol("n", integer=True)
12 16
13 17 def test_erf():
14 18 assert erf(nan) == nan
@@ -227,3 +231,81 @@ def test_ci():
227 231 assert Ci(x).nseries(x, n=4) == EulerGamma + log(x) - x**2/4 + x**4/96 + O(x**5)
228 232 assert Chi(x).nseries(x, n=4) == EulerGamma + log(x) + x**2/4 + x**4/96 + O(x**5)
229 233 assert limit(log(x) - Ci(2*x), x, 0) == -log(2) - EulerGamma
  234 +
  235 +def test_fresnel():
  236 + assert fresnels(0) == 0
  237 + assert fresnels(oo) == S.Half
  238 + assert fresnels(-oo) == -S.Half
  239 +
  240 + assert fresnels(z) == fresnels(z)
  241 + assert fresnels(-z) == -fresnels(z)
  242 + assert fresnels(I*z) == -I*fresnels(z)
  243 + assert fresnels(-I*z) == I*fresnels(z)
  244 +
  245 + assert conjugate(fresnels(z)) == fresnels(conjugate(z))
  246 +