{
  "__type": "IngestedDoc",
  "__tag": 4010,
  "_content": {},
  "_ordered_sections": [],
  "item_file": null,
  "item_line": null,
  "item_type": null,
  "aliases": [],
  "example_section_data": {
    "__type": "Section",
    "__tag": 4015,
    "children": [],
    "title": [],
    "level": 0,
    "target": null
  },
  "see_also": [],
  "signature": null,
  "references": null,
  "qa": "tutorial:special",
  "arbitrary": [
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The main feature of the "
            },
            {
              "__type": "CrossRef",
              "__tag": 4002,
              "value": "scipy.special",
              "reference": {
                "__type": "RefInfo",
                "__tag": 4000,
                "module": "scipy",
                "version": "*",
                "kind": "api",
                "path": "scipy.special"
              },
              "kind": "module"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " package is the definition of numerous special functions of mathematical physics. Available functions include airy, elliptic, bessel, gamma, beta, hypergeometric, parabolic cylinder, mathieu, spheroidal wave, struve, and kelvin. There are also some low-level stats functions that are not intended for general use as an easier interface to these functions is provided by the "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "stats"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " module. Most of these functions can take array arguments and return array results following the same broadcasting rules as other math functions in Numerical Python. Many of these functions also accept complex numbers as input. For a complete list of the available functions with a one-line description type "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": ">>> help(special)."
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " Each function also has its own documentation accessible using help.  If you don't see a function you need, consider writing it and contributing it to the library. You can write the function in either C, Fortran, or Python. Look in the source code of the library for examples of each of these kinds of functions."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Special Functions ("
        },
        {
          "__type": "InlineRole",
          "__tag": 4003,
          "value": "scipy.special",
          "domain": null,
          "role": "mod",
          "inventory": null
        },
        {
          "__type": "Text",
          "__tag": 4046,
          "value": ")"
        }
      ],
      "level": 0,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Bessel functions are a family of solutions to Bessel's differential equation with real or complex order alpha:"
            }
          ]
        },
        {
          "__type": "Math",
          "__tag": 4058,
          "value": "x^2 \\frac{d^2 y}{dx^2} + x \\frac{dy}{dx} + (x^2 - \\alpha^2)y = 0"
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Among other uses, these functions arise in wave propagation problems, such as the vibrational modes of a thin drum head.  Here is an example of a circular drum head anchored at the edge:"
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": ">>> from scipy import special\n>>> import numpy as np\n>>> def drumhead_height(n, k, distance, angle, t):\n...    kth_zero = special.jn_zeros(n, k)[-1]\n...    return np.cos(t) * np.cos(n*angle) * special.jn(n, distance*kth_zero)\n>>> theta = np.r_[0:2*np.pi:50j]\n>>> radius = np.r_[0:1:50j]\n>>> x = np.array([r * np.cos(theta) for r in radius])\n>>> y = np.array([r * np.sin(theta) for r in radius])\n>>> z = np.array([drumhead_height(1, 1, r, theta, 0.5) for r in radius])\n\n>>> import matplotlib.pyplot as plt\n>>> fig = plt.figure()\n>>> ax = fig.add_axes(rect=(0, 0.05, 0.95, 0.95), projection='3d')\n>>> ax.plot_surface(x, y, z, rstride=1, cstride=1, cmap='RdBu_r', vmin=-0.5, vmax=0.5)\n>>> ax.set_xlabel('X')\n>>> ax.set_ylabel('Y')\n>>> ax.set_xticks(np.arange(-1, 1.1, 0.5))\n>>> ax.set_yticks(np.arange(-1, 1.1, 0.5))\n>>> ax.set_zlabel('Z')\n>>> plt.show()",
          "execution_status": null
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Bessel functions of real order("
        },
        {
          "__type": "InlineRole",
          "__tag": 4003,
          "value": "jv",
          "domain": null,
          "role": "func",
          "inventory": null
        },
        {
          "__type": "Text",
          "__tag": 4046,
          "value": ", "
        },
        {
          "__type": "InlineRole",
          "__tag": 4003,
          "value": "jn_zeros",
          "domain": null,
          "role": "func",
          "inventory": null
        },
        {
          "__type": "Text",
          "__tag": 4046,
          "value": ")"
        }
      ],
      "level": 1,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "SciPy also offers Cython bindings for scalar, typed versions of many of the functions in special. The following Cython code gives a simple example of how to use these functions    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "cimport scipy.special.cython_special as csc\n\ncdef:\n    double x = 1\n    double complex z = 1 + 1j\n    double si, ci, rgam\n    double complex cgam\n\nrgam = csc.gamma(x)\nprint(rgam)\ncgam = csc.gamma(z)\nprint(cgam)\ncsc.sici(x, &si, &ci)\nprint(si, ci)",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "(See the "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "Cython documentation"
                }
              ],
              "url": "http://docs.cython.org/en/latest/",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " for help with compiling Cython.) In the example the function "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "csc.gamma"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " works essentially like its ufunc counterpart "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "gamma",
              "domain": null,
              "role": null,
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", though it takes C types as arguments instead of NumPy arrays. Note, in particular, that the function is overloaded to support real and complex arguments; the correct variant is selected at compile time. The function "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "csc.sici"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " works slightly differently from "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "sici",
              "domain": null,
              "role": null,
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "; for the ufunc we could write "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "ai, bi = sici(x)"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", whereas in the Cython version multiple return values are passed as pointers. It might help to think of this as analogous to calling a ufunc with an output array: "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "sici(x, out=(si, ci))"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "There are two potential advantages to using the Cython bindings:"
            }
          ]
        },
        {
          "__type": "BulletList",
          "__tag": 4053,
          "ordered": false,
          "start": 1,
          "children": [
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "they avoid Python function overhead"
                    }
                  ]
                }
              ]
            },
            {
              "__type": "ListItem",
              "__tag": 4054,
              "children": [
                {
                  "__type": "Paragraph",
                  "__tag": 4045,
                  "children": [
                    {
                      "__type": "Text",
                      "__tag": 4046,
                      "value": "they do not require the Python Global Interpreter Lock (GIL)"
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The following sections discuss how to use these advantages to potentially speed up your code, though, of course, one should always profile the code first to make sure putting in the extra effort will be worth it."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Cython Bindings for Special Functions ("
        },
        {
          "__type": "InlineRole",
          "__tag": 4003,
          "value": "scipy.special.cython_special",
          "domain": null,
          "role": "mod",
          "inventory": null
        },
        {
          "__type": "Text",
          "__tag": 4046,
          "value": ")"
        }
      ],
      "level": 1,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "For the ufuncs in special, Python function overhead is avoided by vectorizing, that is, by passing an array to the function. Typically, this approach works quite well, but sometimes it is more convenient to call a special function on scalar inputs inside a loop, for example, when implementing your own ufunc. In this case, the Python function overhead can become significant. Consider the following example    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "import scipy.special as sc\ncimport scipy.special.cython_special as csc\n\ndef python_tight_loop():\n    cdef:\n        int n\n        double x = 1\n\n    for n in range(100):\n        sc.jv(n, x)\n\ndef cython_tight_loop():\n    cdef:\n        int n\n        double x = 1\n\n    for n in range(100):\n        csc.jv(n, x)",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "On one computer "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "python_tight_loop"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " took about 131 microseconds to run and "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cython_tight_loop"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " took about 18.2 microseconds to run. Obviously this example is contrived: one could just call "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "special.jv(np.arange(100), 1)"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and get results just as fast as in "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "cython_tight_loop"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". The point is that if Python function overhead becomes significant in your code, then the Cython bindings might be useful."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Avoiding Python Function Overhead"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "One often needs to evaluate a special function at many points, and typically the evaluations are trivially parallelizable. Since the Cython bindings do not require the GIL, it is easy to run them in parallel using Cython's "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "prange"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " function. For example, suppose that we wanted to compute the fundamental solution to the Helmholtz equation:"
            }
          ]
        },
        {
          "__type": "Math",
          "__tag": 4058,
          "value": "\\Delta_x G(x, y) + k^2G(x, y) = \\delta(x - y),"
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "where "
            },
            {
              "__type": "InlineMath",
              "__tag": 4057,
              "value": "k"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is the wavenumber and "
            },
            {
              "__type": "InlineMath",
              "__tag": 4057,
              "value": "\\delta"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is the Dirac delta function. It is known that in two dimensions the unique (radiating) solution is"
            }
          ]
        },
        {
          "__type": "Math",
          "__tag": 4058,
          "value": "G(x, y) = \\frac{i}{4}H_0^{(1)}(k|x - y|),"
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "where "
            },
            {
              "__type": "InlineMath",
              "__tag": 4057,
              "value": "H_0^{(1)}"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " is the Hankel function of the first kind, i.e., the function "
            },
            {
              "__type": "InlineRole",
              "__tag": 4003,
              "value": "hankel1",
              "domain": null,
              "role": null,
              "inventory": null
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ". The following example shows how we could compute this function in parallel    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "from libc.math cimport fabs\ncimport cython\nfrom cython.parallel cimport prange\n\nimport numpy as np\nimport scipy.special as sc\ncimport scipy.special.cython_special as csc\n\ndef serial_G(k, x, y):\n    return 0.25j*sc.hankel1(0, k*np.abs(x - y))\n\n@cython.boundscheck(False)\n@cython.wraparound(False)\ncdef void _parallel_G(double k, double[:,:] x, double[:,:] y,\n                      double complex[:,:] out) nogil:\n    cdef int i, j\n\n    for i in prange(x.shape[0]):\n        for j in range(y.shape[0]):\n            out[i,j] = 0.25j*csc.hankel1(0, k*fabs(x[i,j] - y[i,j]))\n\ndef parallel_G(k, x, y):\n    out = np.empty_like(x, dtype='complex128')\n    _parallel_G(k, x, y, out)\n    return out",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "(For help with compiling parallel code in Cython see "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "here"
                }
              ],
              "url": "http://docs.cython.org/en/latest/src/userguide/parallelism.html#compiling",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ".) If the above Cython code is in a file "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "test.pyx"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ", then we can write an informal benchmark which compares the parallel and serial versions of the function    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "import timeit\n\nimport numpy as np\n\nfrom test import serial_G, parallel_G\n\ndef main():\n    k = 1\n    x, y = np.linspace(-100, 100, 1000), np.linspace(-100, 100, 1000)\n    x, y = np.meshgrid(x, y)\n\n    def serial():\n        serial_G(k, x, y)\n\n    def parallel():\n        parallel_G(k, x, y)\n\n    time_serial = timeit.timeit(serial, number=3)\n    time_parallel = timeit.timeit(parallel, number=3)\n    print(\"Serial method took {:.3} seconds\".format(time_serial))\n    print(\"Parallel method took {:.3} seconds\".format(time_parallel))\n\nif __name__ == \"__main__\":\n    main()",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "On one quad-core computer the serial method took 1.29 seconds and the parallel method took 0.29 seconds."
            }
          ]
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Releasing the GIL"
        }
      ],
      "level": 2,
      "target": null
    },
    {
      "__type": "Section",
      "__tag": 4015,
      "children": [
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Some functions are not included in special because they are straightforward to implement with existing functions in NumPy and SciPy. To prevent reinventing the wheel, this section provides implementations of several such functions, which hopefully illustrate how to handle similar functions. In all examples NumPy is imported as "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "np"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": " and special is imported as "
            },
            {
              "__type": "InlineCode",
              "__tag": 4051,
              "value": "sc"
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "binary entropy function"
                }
              ],
              "url": "https://en.wikipedia.org/wiki/Binary_entropy_function",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ":    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "def binary_entropy(x):\n    return -(sc.xlogy(x, x) + sc.xlog1py(1 - x, -x))/np.log(2)",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "A rectangular step function on [0, 1]    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "def step(x):\n    return 0.5*(np.sign(x) + np.sign(1 - x))",
          "execution_status": null
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "Translating and scaling can be used to get an arbitrary step function."
            }
          ]
        },
        {
          "__type": "Paragraph",
          "__tag": 4045,
          "children": [
            {
              "__type": "Text",
              "__tag": 4046,
              "value": "The "
            },
            {
              "__type": "Link",
              "__tag": 4049,
              "children": [
                {
                  "__type": "Text",
                  "__tag": 4046,
                  "value": "ramp function"
                }
              ],
              "url": "https://en.wikipedia.org/wiki/Ramp_function",
              "title": ""
            },
            {
              "__type": "Text",
              "__tag": 4046,
              "value": ":    "
            }
          ]
        },
        {
          "__type": "Code",
          "__tag": 4050,
          "value": "def ramp(x):\n    return np.maximum(0, x)",
          "execution_status": null
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "Cython documentation"
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "here"
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "binary entropy function"
        },
        {
          "__type": "Target",
          "__tag": 4061,
          "label": "ramp function"
        }
      ],
      "title": [
        {
          "__type": "Text",
          "__tag": 4046,
          "value": "Functions not in "
        },
        {
          "__type": "InlineRole",
          "__tag": 4003,
          "value": "scipy.special",
          "domain": null,
          "role": "mod",
          "inventory": null
        }
      ],
      "level": 1,
      "target": null
    }
  ],
  "local_refs": []
}