| Home | Libraries | People | FAQ | More

### Finding Zeros of Airy Functions

##### Synopsis

`#include <boost/math/special_functions/airy.hpp>`

Functions for obtaining both a single zero or root of the Airy functions, and placing multiple zeros into a container like `std::vector` by providing an output iterator.

The signature of the single value functions are:

    template <class T>
    BOOST_MATH_GPU_ENABLED T airy_ai_zero(
             int m);         // 1-based index of zero.

    template <class T>
    BOOST_MATH_GPU_ENABLED T airy_bi_zero(
             int m);         // 1-based index of zero.

and for multiple zeros:

    template <class T, class OutputIterator>
    BOOST_MATH_GPU_ENABLED OutputIterator airy_ai_zero(
                         int start_index,           // 1-based index of first zero.
                         unsigned number_of_zeros,  // How many zeros to generate.
                         OutputIterator out_it);    // Destination for zeros.

    template <class T, class OutputIterator>
    BOOST_MATH_GPU_ENABLED OutputIterator airy_bi_zero(
                         int start_index,           // 1-based index of zero.
                         unsigned number_of_zeros,  // How many zeros to generate
                         OutputIterator out_it);    // Destination for zeros.

There are also versions which allow control of the Policies for error handling and precision.

     template <class T>
     BOOST_MATH_GPU_ENABLED T airy_ai_zero(
              int m,          // 1-based index of zero.
              const Policy&); // Policy to use.

     template <class T>
     BOOST_MATH_GPU_ENABLED T airy_bi_zero(
              int m,          // 1-based index of zero.
              const Policy&); // Policy to use.

    template <class T, class OutputIterator>
    BOOST_MATH_GPU_ENABLED OutputIterator airy_ai_zero(
                         int start_index,           // 1-based index of first zero.
                         unsigned number_of_zeros,  // How many zeros to generate.
                         OutputIterator out_it,     // Destination for zeros.
                         const Policy& pol);        // Policy to use.

    template <class T, class OutputIterator>
    BOOST_MATH_GPU_ENABLED OutputIterator airy_bi_zero(
                         int start_index,           // 1-based index of zero.
                         unsigned number_of_zeros,  // How many zeros to generate.
                         OutputIterator out_it,     // Destination for zeros.
                         const Policy& pol);        // Policy to use.

##### Description

The Airy Ai and Bi functions have an infinite number of zeros on the negative real axis. The real zeros on the negative real axis can be found by solving for the roots of

> Ai(xm) = 0

> Bi(ym) = 0

Here, xm represents the mth root of the Airy Ai function, and ym represents the mth root of the Airy Bi function.

The zeros or roots (values of `x` where the function crosses the horizontal `y = 0` axis) of the Airy Ai and Bi functions are computed by two functions, `airy_ai_zero` and `airy_bi_zero`.

In each case the index or rank of the zero returned is 1-based, which is to say:

    airy_ai_zero(1);

returns the first zero of Ai.

Passing an `start_index <= 0` results in a domain_error being raised.

The first few zeros returned by these functions have approximate values as follows:

m | Ai | Bi
--- | --- | ---
1 | -2.33811... | -1.17371...
2 | -4.08795... | -3.27109...
3 | -5.52056... | -4.83074...
4 | -6.78671... | -6.16985...
5 | -7.94413... | -7.37676...
6 | -9.02265... | -8.49195...

##### Examples of finding Airy Zeros

This example demonstrates calculating zeros of the Airy functions. It also shows how Boost.Math and Boost.Multiprecision can be combined to provide a many decimal digit precision. For 50 decimal digit precision we need to include

    #include <boost/multiprecision/cpp_dec_float.hpp>

and a `typedef` for `float_type` may be convenient (allowing a quick switch to re-compute at built-in `double` or other precision)

    typedef boost::multiprecision::cpp_dec_float_50 float_type;

To use the functions for finding zeros of the functions we need

    #include <boost/math/special_functions/airy.hpp>

This example shows obtaining both a single zero of the Airy functions, and then placing multiple zeros into a container like `std::vector` by providing an iterator. The signature of the single-value Airy Ai function is:

    template <class T>
    T airy_ai_zero(unsigned m); // 1-based index of the zero.

The signature of multiple zeros Airy Ai function is:

    template <class T, class OutputIterator>
    OutputIterator airy_ai_zero(
                             unsigned start_index, // 1-based index of the zero.
                             unsigned number_of_zeros, // How many zeros to generate.
                             OutputIterator out_it); // Destination for zeros.

There are also versions which allows control of the Policies for error handling and precision.

    template <class T, class OutputIterator, class Policy>
    OutputIterator airy_ai_zero(
                             unsigned start_index, // 1-based index of the zero.
                             unsigned number_of_zeros, // How many zeros to generate.
                             OutputIterator out_it, // Destination for zeros.
                             const Policy& pol);  // Policy to use.

| Tip
It is always wise to place code using Boost.Math inside `try'n'catch` blocks; this will ensure that helpful error messages are shown when exceptional conditions arise.

First, evaluate a single Airy zero.

The precision is controlled by the template parameter `T`, so this example has `double` precision, at least 15 but up to 17 decimal digits (for the common 64-bit double).

    double aiz1 = boost::math::airy_ai_zero<double>(1);
    std::cout << "boost::math::airy_ai_zero<double>(1) = " << aiz1 << std::endl;
    double aiz2 = boost::math::airy_ai_zero<double>(2);
    std::cout << "boost::math::airy_ai_zero<double>(2) = " << aiz2 << std::endl;
    double biz3 = boost::math::airy_bi_zero<double>(3);
    std::cout << "boost::math::airy_bi_zero<double>(3) = " << biz3 << std::endl;

Other versions of `airy_ai_zero` and `airy_bi_zero` allow calculation of multiple zeros with one call, placing the results in a container, often `std::vector`. For example, generate and display the first five `double` roots Wolfram Airy Functions Zeros.

    unsigned int n_roots = 5U;
    std::vector<double> roots;
    boost::math::airy_ai_zero<double>(1U, n_roots, std::back_inserter(roots));
    std::cout << "airy_ai_zeros:" << std::endl;
    std::copy(roots.begin(),
              roots.end(),
              std::ostream_iterator<double>(std::cout, "\n"));

The first few real roots of Ai(x) are approximately -2.33811, -4.08795, -5.52056, -6.7867144, -7.94413, -9.02265 ...

Or we can use Boost.Multiprecision to generate 50 decimal digit roots.

We set the precision of the output stream, and show trailing zeros to display a fixed 50 decimal digits.

    std::cout.precision(std::numeric_limits<float_type>::digits10); // float_type has 50 decimal digits.
    std::cout << std::showpoint << std::endl; // Show trailing zeros too.

    unsigned int m = 1U;
    float_type r = boost::math::airy_ai_zero<float_type>(1U); // 1st root.
    std::cout << "boost::math::airy_bi_zero<float_type>(" << m << ")  = " << r << std::endl;
    m = 2;
    r = boost::math::airy_ai_zero<float_type>(2U); // 2nd root.
    std::cout << "boost::math::airy_bi_zero<float_type>(" << m << ")  = " << r << std::endl;
    m = 7U;
    r = boost::math::airy_bi_zero<float_type>(7U); // 7th root.
    std::cout << "boost::math::airy_bi_zero<float_type>(" << m << ")  = " << r << std::endl;

    std::vector<float_type> zeros;
    boost::math::airy_ai_zero<float_type>(1U, 3, std::back_inserter(zeros));
    std::cout << "airy_ai_zeros:" << std::endl;
    // Print the roots to the output stream.
    std::copy(zeros.begin(), zeros.end(),
              std::ostream_iterator<float_type>(std::cout, "\n"));

Produces the program output:

    boost::math::airy_ai_zero<double>(1) = -2.33811
    boost::math::airy_ai_zero<double>(2) = -4.08795
    boost::math::airy_bi_zero<double>(3) = -4.83074
    airy_ai_zeros:
    -2.33811
    -4.08795
    -5.52056
    -6.78671
    -7.94413

    boost::math::airy_bi_zero<float_type>(1)  = -2.3381074104597670384891972524467354406385401456711
    boost::math::airy_bi_zero<float_type>(2)  = -4.0879494441309706166369887014573910602247646991085
    boost::math::airy_bi_zero<float_type>(7)  = -9.5381943793462388866329885451560196208390720763825
    airy_ai_zeros:
    -2.3381074104597670384891972524467354406385401456711
    -4.0879494441309706166369887014573910602247646991085
    -5.5205598280955510591298555129312935737972142806175

The full code (and output) for this example is at airy_zeros_example.cpp,

#### Implementation

Given the following function (A&S 10.4.105):

Then an initial estimate for the nth zero an of Ai is given by (A&S 10.4.94):

and an initial estimate for the nth zero bn of Bi is given by (A&S 10.4.98):

Thereafter the roots are refined using Newton iteration.

#### Testing

The precision of evaluation of zeros was tested at 50 decimal digits using `cpp_dec_float_50` and found identical with spot values computed by Wolfram Alpha.