Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

fixed bug in clpmn violating array bounds #482

Merged
merged 2 commits into from

2 participants

@gertingold

Hi,

it seems that there is a bug in the Fortran code of CLPMN where data are written Into the array CPM beyond its bounds provided that M=N. A corresponding bug originally present in the real version LPMN was fixed in 3d5294d3 about eight years ago. This bug concerns not only the associated Legendre polynomials but also the spherical harmonics for complex angles. In Python, the bug becomes apparent in the following minimal code:

'''
from scipy.special.specfun import clpmn
clpmn(1, 1, 5, 3)
'''

The third and fourth argument should not be very important as long as they do not correspond to an argument 1 or -1.

This bug was found by Michael Hartmann and analyzed together with him.

The change in line 275 in b73f9f9 should fix the problem.

Best regards,
Gert

@pv
Owner
pv commented

Thanks. Adding a test to test_basic.py should also be done.

@gertingold

ok, I will try to come up with a test. It seems that so far there are no tests for the complex Legendre polynomials but only for the real ones. Should I add a test to the existing class TestLegendreFunctions (so far only real case or rather add a new class for the complex case?

@pv
Owner
pv commented

Adding it to the existing class should be OK. Here just checking e.g. the result from clpmn(1, 1, 5, 3) is probably enough to verify this fix --- writing a comprehensive test suite would take some more effort.

@gertingold

I have added a test to test_basic.py but I must admit that I am not completely happy with it. First of all, the test itself runs ok even with the bug in CLPMN but segfaults at the end if the bug is not fixed. This clearly indicates the presence of the bug but it is not the way how the problem should appear in a test. Secondly, it would be nicer to calculate the expected array from the known formulae but this requires importing sqrt from cmath. I am not sure whether it would be acceptable to do such an import in test_basic.py.

In the test, I have chosen z=0.5+0.3j instead of 5+3j as an argument because then |z|<1 and everything is fine. For |z|>1, there seems to be an extra factor of 1j but I would have to look more deeply into the associated Legendre polynomials for complex arguments to be sure about it. This relates to your remark about writing more comprehensive tests for CLPMN.

@pv pv merged commit 5b75d9c into from
@pv
Owner
pv commented

Thanks, looks good.

And you're absolutely correct, factors of i and -1 (in the real-valued code) apparently go wrong for |z| > 1, need to investigate -> http://projects.scipy.org/scipy/ticket/1877

@gertingold

The following picture displaying the behavior of the result of CLPNM for N=M=1 on the imaginary axis gives further evidence that the behavior at |z|=1 is strange. After all, the associated Legendre polynomials should be analytic at z=i.

clpnm_on_imagaxis

The problem arises from LS in the code of CLPNM.

The above picture was generated with the following code:

import sys, os
from cmath import sqrt
import numpy as np
from scipy import special
from pyx import canvas, color, graph, style, text

"""This script shows that the evaluation of the associated Legendre polynomials
   in scipy by means of the function CLPNM has a problem when |z|>1. The graph
   shows the behavior of the real and imaginary part for n=m=1 on the imaginary
   axis. The result should be analytic at z=i, but a jump is observed. This
   behavior is due to the variable LS in the code of CLPNM. For comparison, the
   analytical continuation of the known result for real arguments is shown.
"""

xvals = np.linspace(0, 2, 500)
yvals_real = [special.specfun.clpmn(1, 1, 0, x)[0][1, 1].real for x in xvals]
yvals_imag = [special.specfun.clpmn(1, 1, 0, x)[0][1, 1].imag for x in xvals]
correct_real = [-sqrt(1+x*x).real for x in xvals]

c = canvas.canvas()
text.set(mode="latex")

g = graph.graphxy(width=8,
                  key=graph.key.key(pos="tl", dist=0.05),
                  x=graph.axis.linear(title="$y$"))
g.plot([graph.data.points(zip(xvals, yvals_real), x=1, y=2, title="CLPMN, real part"),
        graph.data.points(zip(xvals, yvals_imag), x=1, y=2, title="CLPMN, imaginary part"),
        graph.data.points(zip(xvals, correct_real), x=1, y=2, title="expected (is real)")],
        styles=[graph.style.line([style.linestyle.solid, color.gradient.Rainbow])])

c.insert(g)
c.text(g.xpos+0.5*g.width, g.ypos+g.height+1,
       u"CLPMN test on imaginary axis, purely imaginary argument $z=\mathrm{i}y$",
       [text.halign.center])
c.writeGSfile("%s.png" % os.path.splitext(sys.argv[0])[0],
              device="png256", resolution=100)
@ClemensFMN ClemensFMN referenced this pull request from a commit
Commit has since been removed from the repository and is no longer available.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Mar 26, 2013
  1. @gertingold
  2. @gertingold

    test for bug in clpmn

    gertingold authored
This page is out of date. Refresh to see the latest.
View
2  scipy/special/specfun/specfun.f
@@ -272,7 +272,7 @@ SUBROUTINE CLPMN(MM,M,N,X,Y,CPM,CPD)
ZS=LS*(1.0D0-Z*Z)
DO 25 I=1,M
25 CPM(I,I)=-LS*(2.0D0*I-1.0D0)*ZQ*CPM(I-1,I-1)
- DO 30 I=0,M
+ DO 30 I=0,MIN(M,N-1)
30 CPM(I,I+1)=(2.0D0*I+1.0D0)*Z*CPM(I,I)
DO 35 I=0,M
DO 35 J=I+2,N
View
11 scipy/special/tests/test_basic.py
@@ -2186,6 +2186,17 @@ def test_log1pmore(self):
assert_array_almost_equal(l1pm,l1pmrl,8)
class TestLegendreFunctions(TestCase):
+ def test_clpmn(self):
+ clp = special.specfun.clpmn(1, 1, 0.5, 0.3)
+ assert_array_almost_equal(clp,(array([[ 1.0000,
+ 0.5+0.3j ],
+ [ 0.0000,
+ -0.9305815721+0.1611895232j ]]),
+ array([[ 0.0000,
+ 1.0000 ],
+ [ 0.0000,
+ 0.4674335183+0.4033449589j ]])),7)
+
def test_lpmn(self):
lp = special.lpmn(0,2,.5)
assert_array_almost_equal(lp,(array([ [ 1.00000 ,
Something went wrong with that request. Please try again.