packages feed

BesselJ 0.1.0.1 → 0.2.0.0

raw patch · 8 files changed

+301/−39 lines, 8 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Math.AngerJ: AngerResult :: Complex Double -> (Double, Double) -> (Int, Int) -> AngerResult
+ Math.AngerJ: [_codes] :: AngerResult -> (Int, Int)
+ Math.AngerJ: [_errors] :: AngerResult -> (Double, Double)
+ Math.AngerJ: [_result] :: AngerResult -> Complex Double
+ Math.AngerJ: angerJ :: Complex Double -> Complex Double -> Double -> Int -> IO AngerResult
+ Math.AngerJ: data AngerResult
+ Math.AngerJ: instance GHC.Show.Show Math.AngerJ.AngerResult
+ Math.AngerWeber: AngerWeberResult :: Complex Double -> BesselResult -> AngerResult -> AngerWeberResult
+ Math.AngerWeber: [_angerResult] :: AngerWeberResult -> AngerResult
+ Math.AngerWeber: [_besselResult] :: AngerWeberResult -> BesselResult
+ Math.AngerWeber: [_result] :: AngerWeberResult -> Complex Double
+ Math.AngerWeber: angerWeber :: Complex Double -> Complex Double -> Double -> Int -> IO AngerWeberResult
+ Math.AngerWeber: data AngerWeberResult
+ Math.AngerWeber: instance GHC.Show.Show Math.AngerWeber.AngerWeberResult
+ Math.WeberE: WeberResult :: Complex Double -> (Double, Double) -> (Int, Int) -> WeberResult
+ Math.WeberE: [_codes] :: WeberResult -> (Int, Int)
+ Math.WeberE: [_errors] :: WeberResult -> (Double, Double)
+ Math.WeberE: [_result] :: WeberResult -> Complex Double
+ Math.WeberE: data WeberResult
+ Math.WeberE: instance GHC.Show.Show Math.WeberE.WeberResult
+ Math.WeberE: weberE :: Complex Double -> Complex Double -> Double -> Int -> IO WeberResult

Files

BesselJ.cabal view
@@ -1,8 +1,8 @@ cabal-version:       2.2
 name:                BesselJ
-version:             0.1.0.1
-synopsis:            Bessel J-function
-description:         Computation of the Bessel J-function of a complex variable. 
+version:             0.2.0.0
+synopsis:            Bessel J-function, Anger J-function, Weber E-function, and Anger-Weber function.
+description:         Computation of Bessel J-function, Anger J-function, Weber E-function, Anger-Weber function, of a complex variable. 
 homepage:            https://github.com/stla/BesselJ#readme
 license:             BSD-3-Clause
 license-file:        LICENSE
@@ -17,6 +17,9 @@ library
   hs-source-dirs:      src
   exposed-modules:     Math.BesselJ
+                     , Math.AngerJ
+                     , Math.WeberE
+                     , Math.AngerWeber
   build-depends:       base >= 4.7 && < 5
                      , gamma >= 0.10.0.0
                      , numerical-integration >= 0.1.2.3
CHANGELOG.md view
@@ -9,3 +9,9 @@ ## 0.1.0.1 - 2023-09-22  More unit tests.+++## 0.2.0.0 - 2023-09-22++Added implementations of Anger J-function, Weber E-function, and Anger-Weber function.+
README.md view
@@ -1,9 +1,12 @@ # BesselJ
 
-*Computation of the Bessel J-function of a complex variable.*
+*Computation of the Bessel J-function, Anger J-function, Weber E-function, and Anger-Weber function, of a complex variable.*
 
 The order of the Bessel J-function implemented in this package can be a 
-complex number with real part larger than -0.5, or any integer.
+complex number with real part larger than -0.5, or any integer. 
+There is no restriction for the Anger J-function and the Weber E-function. 
+For the Anger-Weber function, the order must be a non-integer 
+complex number with real part larger than -0.5.
 
 ___
 
+ src/Math/AngerJ.hs view
@@ -0,0 +1,47 @@+module Math.AngerJ
+  ( AngerResult(..), angerJ )
+  where
+import Data.Complex          ( imagPart, realPart, Complex(..) )
+import Numerical.Integration ( integration, IntegralResult(..) )
+import Foreign.C             ( CDouble )
+
+
+-- | Data type to store the result of a computation of the Anger J-function.
+-- The fields are @_result@ for the value, @_errors@ for the error estimates 
+-- of the integrals used for the computation, and @_codes@ for the convergence 
+-- codes of these integrals (0 for success).
+data AngerResult = AngerResult {
+    _result :: Complex Double
+  , _errors :: (Double, Double)
+  , _codes  :: (Int, Int)
+} deriving Show
+
+
+cpxdbl2cpxcdbl :: Complex Double -> Complex CDouble
+cpxdbl2cpxcdbl z = realToFrac (realPart z) :+ realToFrac (imagPart z)
+
+
+-- | Anger-J function. It is computed with two integrals. The field @_errors@ 
+-- in the result provides the error estimates of the integrals. The field 
+-- @_codes@ provides the codes indicating success (0) or failure of each integral.
+angerJ :: Complex Double  -- ^ order, complex number 
+       -> Complex Double  -- ^ the variable, a complex number
+       -> Double          -- ^ target relative accuracy for the integrals, e.g. 1e-5
+       -> Int             -- ^ number of subdivisions for the integrals, e.g. 5000
+       -> IO AngerResult  -- ^ result
+angerJ nu z err subdiv = do
+  let z' = cpxdbl2cpxcdbl z
+      x' = realPart z'
+      y' = imagPart z'
+      nu' = cpxdbl2cpxcdbl nu
+      a' = realPart nu'
+      b' = imagPart nu'
+      reintegrand t = cosh(b' * t - y' * sin t) * cos(a' * t - x' * sin t)
+      imintegrand t = -sinh(b' * t - y' * sin t) * sin(a' * t - x' * sin t)
+  re <- integration reintegrand 0 pi 0.0 err subdiv
+  im <- integration imintegrand 0 pi 0.0 err subdiv
+  return AngerResult {
+      _result = (_value re :+ _value im) / pi
+    , _errors = (_error re, _error im)
+    , _codes  = (_code re, _code im)
+  }
+ src/Math/AngerWeber.hs view
@@ -0,0 +1,51 @@+module Math.AngerWeber
+  ( AngerWeberResult(..), angerWeber )
+  where
+import Data.Complex          ( realPart, imagPart, Complex(..) )
+import Math.BesselJ          ( besselJ, BesselResult(..) )
+import Math.AngerJ           ( angerJ, AngerResult(..))
+
+
+-- | Data type to store the result of a computation of the Anger-Weber function.
+-- It is based on a computation of the Bessel J-function and a computation of 
+-- the Anger J-function. 
+-- The fields are @_result@ for the value, @_besselResult@ for the result of 
+-- the computation of the Bessel J-function, and @_angerResult@ for the result 
+-- of the computation of the Anger J-function.
+data AngerWeberResult = AngerWeberResult {
+    _result       :: Complex Double
+  , _besselResult :: BesselResult
+  , _angerResult  :: AngerResult
+} deriving Show
+
+
+isInteger :: Complex Double -> Bool
+isInteger z = y == 0 && x == fromIntegral (floor x :: Int)
+  where
+    x = realPart z
+    y = imagPart z
+
+
+aResult :: AngerResult -> Complex Double
+aResult (AngerResult r  _  _) = r
+
+bResult :: BesselResult -> Complex Double
+bResult (BesselResult r _ _) = r
+
+
+-- | Anger-Weber function. 
+angerWeber :: Complex Double       -- ^ order, non-integer complex number with real part larger than -0.5
+           -> Complex Double       -- ^ the variable, a complex number
+           -> Double               -- ^ target relative accuracy for the integrals, e.g. 1e-5
+           -> Int                  -- ^ number of subdivisions for the integrals, e.g. 5000
+           -> IO AngerWeberResult  -- ^ result
+angerWeber nu z err subdiv 
+  | isInteger nu = error "The order `nu` cannot be an integer."
+  | realPart nu <= -0.5 = error "The real part of the order `nu` must be larger than -0.5."
+  | otherwise = do
+      bessel <- besselJ nu z err subdiv
+      anger <- angerJ nu z err subdiv
+      let bresult = bResult bessel
+          aresult = aResult anger 
+          result = (aresult - bresult) / sin(pi*nu)
+      return (AngerWeberResult result bessel anger)
src/Math/BesselJ.hs view
@@ -6,6 +6,7 @@ import Math.Gamma            ( Gamma(gamma) )
 import Foreign.C             ( CDouble )
 
+
 -- | Data type to store the result of a computation of the Bessel J-function.
 -- The fields are @_result@ for the value, @_errors@ for the error estimates 
 -- of the integrals used for the computation, and @_codes@ for the convergence 
@@ -16,12 +17,15 @@   , _codes  :: (Int, Int)
 } deriving Show
 
+
 cpxdbl2cpxcdbl :: Complex Double -> Complex CDouble
 cpxdbl2cpxcdbl z = realToFrac (realPart z) :+ realToFrac (imagPart z)
 
+
 dbl2cdbl :: Double -> CDouble
 dbl2cdbl = realToFrac
 
+
 -- | Bessel-J function. It is computed with two integrals. The field @_errors@ 
 -- in the result are the error estimates of the integrals. The field @_codes@ 
 -- is the code indicating success (0) or failure.
@@ -81,8 +85,8 @@ 
 
 -- | Bessel-J function. It is computed with two integrals. The field @_errors@ 
--- in the result are the error estimates of the integrals. The field @_codes@ 
--- provides the code indicating success (0) or failure of each integral.
+-- in the result provides the error estimates of the integrals. The field 
+-- @_codes@ provides the codes indicating success (0) or failure of each integral.
 besselJnu :: Complex Double  -- ^ order, complex number with real part > -0.5
           -> Complex Double  -- ^ the variable, a complex number
           -> Double          -- ^ target relative accuracy for the integrals, e.g. 1e-5
@@ -115,8 +119,8 @@ asInteger z = floor (realPart z) :: Int
 
 -- | Bessel-J function. It is computed with two integrals. The field @_errors@ 
--- in the result are the error estimates of the integrals. The field @_codes@ 
--- provides the code indicating success (0) or failure of each integral.
+-- in the result provides the error estimates of the integrals. The field 
+-- @_codes@ provides the codes indicating success (0) or failure of each integral.
 besselJ :: Complex Double  -- ^ order, integer or complex number with real part > -0.5
         -> Complex Double  -- ^ the variable, a complex number
         -> Double          -- ^ target relative accuracy for the integrals, e.g. 1e-5
@@ -125,4 +129,4 @@ besselJ nu z err subdiv
   | isInteger nu = besselJn (asInteger nu) z err subdiv
   | realPart nu > -0.5 = besselJnu nu z err subdiv
-  | otherwise = error "Invalid value of the order."+  | otherwise = error "Invalid value of the order."
+ src/Math/WeberE.hs view
@@ -0,0 +1,47 @@+module Math.WeberE
+  ( WeberResult(..), weberE )
+  where
+import Data.Complex          ( imagPart, realPart, Complex(..) )
+import Numerical.Integration ( integration, IntegralResult(..) )
+import Foreign.C             ( CDouble )
+
+
+-- | Data type to store the result of a computation of the Weber E-function.
+-- The fields are @_result@ for the value, @_errors@ for the error estimates 
+-- of the integrals used for the computation, and @_codes@ for the convergence 
+-- codes of these integrals (0 for success).
+data WeberResult = WeberResult {
+    _result :: Complex Double
+  , _errors :: (Double, Double)
+  , _codes  :: (Int, Int)
+} deriving Show
+
+
+cpxdbl2cpxcdbl :: Complex Double -> Complex CDouble
+cpxdbl2cpxcdbl z = realToFrac (realPart z) :+ realToFrac (imagPart z)
+
+
+-- | Weber-E function. It is computed with two integrals. The field @_errors@ 
+-- in the result provides the error estimates of the integrals. The field 
+-- @_codes@ provides the codes indicating success (0) or failure of each integral.
+weberE :: Complex Double  -- ^ order, complex number 
+       -> Complex Double  -- ^ the variable, a complex number
+       -> Double          -- ^ target relative accuracy for the integrals, e.g. 1e-5
+       -> Int             -- ^ number of subdivisions for the integrals, e.g. 5000
+       -> IO WeberResult  -- ^ result
+weberE nu z err subdiv = do
+  let z' = cpxdbl2cpxcdbl z
+      x' = realPart z'
+      y' = imagPart z'
+      nu' = cpxdbl2cpxcdbl nu
+      a' = realPart nu'
+      b' = imagPart nu'
+      reintegrand t = cosh(b' * t - y' * sin t) * sin(a' * t - x' * sin t)
+      imintegrand t = sinh(b' * t - y' * sin t) * cos(a' * t - x' * sin t)
+  re <- integration reintegrand 0 pi 0.0 err subdiv
+  im <- integration imintegrand 0 pi 0.0 err subdiv
+  return WeberResult {
+      _result = (_value re :+ _value im) / pi
+    , _errors = (_error re, _error im)
+    , _codes  = (_code re, _code im)
+  }
tests/Main.hs view
@@ -3,18 +3,30 @@ import           Data.Complex     ( Complex(..), conjugate )
 import           Test.Tasty       ( defaultMain, testGroup )
 import           Test.Tasty.HUnit ( testCase )
-import           Math.BesselJ     ( BesselResult(_result), besselJ )
+import           Math.BesselJ     ( BesselResult(..), besselJ )
+import           Math.AngerJ      ( AngerResult(..), angerJ )
+import           Math.WeberE      ( WeberResult(..), weberE )
+import           Math.AngerWeber  ( AngerWeberResult(..), angerWeber )
 
-i_ :: Complex Double
-i_ = 0.0 :+ 1.0
 
+aResult :: AngerResult -> Complex Double
+aResult (AngerResult r  _  _) = r
 
+bResult :: BesselResult -> Complex Double
+bResult (BesselResult r _ _) = r
+
+wResult :: WeberResult -> Complex Double
+wResult (WeberResult r  _  _) = r
+
+awResult :: AngerWeberResult -> Complex Double
+awResult (AngerWeberResult r  _  _) = r
+
 main :: IO ()
 main = defaultMain $
   testGroup "Tests"
   [ 
     testCase "nu = 1+2i -- z = 3+4i" $ do
-      my <- _result <$> besselJ (1 :+ 2) (3 :+ 4) 1e-5 5000
+      my <- bResult <$> besselJ (1 :+ 2) (3 :+ 4) 1e-5 5000
       let wolfram = 0.31925 :+ (-0.66956) 
       assertAreClose "" 1e-5 my wolfram,
 
@@ -22,60 +34,149 @@     --   let nu = 0.5 :+ 2
     --       z = 3 :+ 4
     --       y = 2 * sin (nu * pi) / (pi * z)
-    --   x1 <- _result <$> jnu (nu-1) z 
-    --   x2 <- _result <$> jnu (-nu) z
-    --   x3 <- _result <$> jnu (1-nu) z
-    --   x4 <- _result <$> jnu nu z 
+    --   x1 <- bResult <$> jnu (nu-1) z 
+    --   x2 <- bResult <$> jnu (-nu) z
+    --   x3 <- bResult <$> jnu (1-nu) z
+    --   x4 <- bResult <$> jnu nu z 
     --   assertAreClose "" 1e-3 (x1*x2 + x3*x4) y
 
     testCase "recurrence relation" $ do
       let nu = 0.5 :+ 2
           z = 3 :+ 4
-      x1 <- _result <$> besselJ nu z 1e-5 5000
-      x2 <- _result <$> besselJ (nu+1) z 1e-5 5000
-      x3 <- _result <$> besselJ (nu+2) z 1e-5 5000
+      x1 <- bResult <$> besselJ nu z 1e-8 10000
+      x2 <- bResult <$> besselJ (nu+1) z 1e-8 10000
+      x3 <- bResult <$> besselJ (nu+2) z 1e-8 10000
       let y = 2*(nu+1)/z * x2 - x3
-      assertAreClose "" 1e-7 x1 y,
+      assertAreClose "" 1e-8 x1 y,
 
     testCase "elementary equality" $ do
       let z = 3 :+ 4
           s = sqrt(2 / pi / z) * sin z
-      x <- _result <$> besselJ 0.5 z 1e-5 5000
-      assertAreClose "" 1e-9 x s,
+      x <- bResult <$> besselJ 0.5 z 1e-5 5000
+      assertAreClose "" 1e-10 x s,
 
     testCase "remove square root" $ do
       let z = 2 :+ 1
           nu = (-0.3) :+ 1
-      x <- _result <$> besselJ nu (sqrt (z*z)) 1e-5 5000
-      y <- _result <$> besselJ nu z 1e-5 5000
-      assertAreClose "" 1e-6 x (z**(-nu) * (z*z)**(nu/2) * y),
+      x <- bResult <$> besselJ nu (sqrt (z*z)) 1e-5 5000
+      y <- bResult <$> besselJ nu z 1e-5 5000
+      assertAreClose "" 1e-7 x (z**(-nu) * (z*z)**(nu/2) * y),
 
     testCase "remove square root --- integer nu" $ do
       let z = 2 :+ 1
           nu = -4
-      x <- _result <$> besselJ nu (sqrt (z*z)) 1e-5 5000
-      y <- _result <$> besselJ nu z 1e-5 5000
-      assertAreClose "" 1e-6 x (z**(-nu) * (z*z)**(nu/2) * y),
+      x <- bResult <$> besselJ nu (sqrt (z*z)) 1e-5 5000
+      y <- bResult <$> besselJ nu z 1e-5 5000
+      assertAreClose "" 1e-7 x (z**(-nu) * (z*z)**(nu/2) * y),
 
     testCase "remove minus sign" $ do
       let z = 2 :+ 1
           nu = (-0.3) :+ 1
-      x <- _result <$> besselJ nu (-z) 1e-5 5000
-      y <- _result <$> besselJ nu z 1e-5 5000
-      assertAreClose "" 1e-6 x ((-z)**nu * z**(-nu) * y), 
+      x <- bResult <$> besselJ nu (-z) 1e-5 5000
+      y <- bResult <$> besselJ nu z 1e-5 5000
+      assertAreClose "" 1e-7 x ((-z)**nu * z**(-nu) * y), 
 
     testCase "conjugate" $ do
       let z = 2 :+ 5
           nu = 0.3 :+ (-1)
-      x <- _result <$> besselJ (conjugate nu) (conjugate z) 1e-5 5000
-      y <- _result <$> besselJ nu z 1e-5 5000
-      assertAreClose "" 1e-6 x (conjugate y), 
+      x <- bResult <$> besselJ (conjugate nu) (conjugate z) 1e-5 5000
+      y <- bResult <$> besselJ nu z 1e-5 5000
+      assertAreClose "" 1e-7 x (conjugate y), 
 
     testCase "conjugate --- integer nu" $ do
       let z = 2 :+ 5
           nu = 7
-      x <- _result <$> besselJ nu (conjugate z) 1e-5 5000
-      y <- _result <$> besselJ nu z 1e-5 5000
-      assertAreClose "" 1e-6 x (conjugate y)
+      x <- bResult <$> besselJ nu (conjugate z) 1e-5 5000
+      y <- bResult <$> besselJ nu z 1e-5 5000
+      assertAreClose "" 1e-7 x (conjugate y),
+
+
+    -- Anger --------
+
+    testCase "Anger at z = 0" $ do
+      let nu = (-2.3) :+ 1
+          y = sin(pi*nu) / (pi*nu)
+      x <- aResult <$> angerJ nu 0 1e-5 5000
+      assertAreClose "" 1e-8 x y,
+
+    testCase "Anger is Bessel when nu is integer" $ do
+      let z = 2 :+ 6
+          nu = 10
+      x <- aResult <$> angerJ nu z 1e-5 5000
+      y <- bResult <$> besselJ nu z 1e-5 5000
+      assertAreClose "" 1e-8 x y,
+
+    testCase "Anger - remove minus sign" $ do
+      let z = 2 :+ 1
+          nu = (-0.3) :+ 1
+      x <- aResult <$> angerJ nu (-z) 1e-5 5000
+      y <- aResult <$> angerJ (-nu) z 1e-5 5000
+      assertAreClose "" 1e-8 x y,
+
+    testCase "Anger - recurrence relation" $ do
+      let z = (-2) :+ 1
+          nu = (-0.3) :+ 1
+      x1 <- aResult <$> angerJ (nu-1) z 1e-5 5000
+      x2 <- aResult <$> angerJ (nu+1) z 1e-5 5000
+      y <- aResult <$> angerJ nu z 1e-5 5000
+      let x = x1 + x2 
+          y' = 2*nu/z * y - 2*sin(pi*nu)/(pi*z)
+      assertAreClose "" 1e-8 x y',
+
+
+    -- Weber --------
+
+    testCase "Weber at z = 0" $ do
+      let nu = (-2.3) :+ 1
+          y = (1 - cos(pi*nu)) / (pi*nu)
+      x <- wResult <$> weberE nu 0 1e-5 5000
+      assertAreClose "" 1e-7 x y,
+
+    testCase "Weber - remove minus sign" $ do
+      let z = 2 :+ 1
+          nu = (-0.3) :+ 1
+      x <- wResult <$> weberE nu (-z) 1e-5 5000
+      y <- wResult <$> weberE (-nu) z 1e-5 5000
+      assertAreClose "" 1e-7 x (-y),
+
+    testCase "Weber - recurrence relation" $ do
+      let z = (-2) :+ 1
+          nu = (-0.3) :+ 1
+      x1 <- wResult <$> weberE (nu-1) z 1e-5 5000
+      x2 <- wResult <$> weberE (nu+1) z 1e-5 5000
+      y <- wResult <$> weberE nu z 1e-5 5000
+      let x = x1 + x2 
+          y' = 2*nu/z * y - 2*(1-cos(pi*nu))/(pi*z)
+      assertAreClose "" 1e-7 x y',
+    
+
+    -- Relations between Anger and Weber --------
+
+    testCase "Relation 1 between Anger and Weber" $ do
+      let z = (-7) :+ 6
+          nu = (-3.3) :+ 9
+      w1 <- wResult <$> weberE nu z 1e-5 5000
+      w2 <- wResult <$> weberE (-nu) z 1e-5 5000
+      a <- aResult <$> angerJ nu z 1e-5 5000
+      assertAreClose "" 1e-7 (sin(pi*nu)*a) (cos(pi*nu)*w1 - w2),
+
+    testCase "Relation 2 between Anger and Weber" $ do
+      let z = (-7) :+ 6
+          nu = (-3.3) :+ 9
+      a1 <- aResult <$> angerJ nu z 1e-5 5000
+      a2 <- aResult <$> angerJ (-nu) z 1e-5 5000
+      w <- wResult <$> weberE nu z 1e-5 5000
+      assertAreClose "" 1e-7 (sin(pi*nu)*w) (a2 - cos(pi*nu)*a1),
+    
+
+    -- Anger-Weber --------
+
+    testCase "A value of Anger-Weber" $ do
+      let z = 2.5 :+ 0.5
+          nu = 1.0 / 3.0
+          wolfram = 0.102015 :+ (-0.0162118)
+      aw <- awResult <$> angerWeber nu z 1e-6 5000
+      assertAreClose "" 1e-5 aw wolfram
+
 
   ]