123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617 |
- """
- Wrapper functions to more user-friendly calling of certain math functions
- whose output data-type is different than the input data-type in certain
- domains of the input.
- For example, for functions like `log` with branch cuts, the versions in this
- module provide the mathematically valid answers in the complex plane::
- >>> import math
- >>> from numpy.lib import scimath
- >>> scimath.log(-math.exp(1)) == (1+1j*math.pi)
- True
- Similarly, `sqrt`, other base logarithms, `power` and trig functions are
- correctly handled. See their respective docstrings for specific examples.
- Functions
- ---------
- .. autosummary::
- :toctree: generated/
- sqrt
- log
- log2
- logn
- log10
- power
- arccos
- arcsin
- arctanh
- """
- import numpy.core.numeric as nx
- import numpy.core.numerictypes as nt
- from numpy.core.numeric import asarray, any
- from numpy.core.overrides import array_function_dispatch
- from numpy.lib.type_check import isreal
- __all__ = [
- 'sqrt', 'log', 'log2', 'logn', 'log10', 'power', 'arccos', 'arcsin',
- 'arctanh'
- ]
- _ln2 = nx.log(2.0)
- def _tocomplex(arr):
- """Convert its input `arr` to a complex array.
- The input is returned as a complex array of the smallest type that will fit
- the original data: types like single, byte, short, etc. become csingle,
- while others become cdouble.
- A copy of the input is always made.
- Parameters
- ----------
- arr : array
- Returns
- -------
- array
- An array with the same input data as the input but in complex form.
- Examples
- --------
- First, consider an input of type short:
- >>> a = np.array([1,2,3],np.short)
- >>> ac = np.lib.scimath._tocomplex(a); ac
- array([1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64)
- >>> ac.dtype
- dtype('complex64')
- If the input is of type double, the output is correspondingly of the
- complex double type as well:
- >>> b = np.array([1,2,3],np.double)
- >>> bc = np.lib.scimath._tocomplex(b); bc
- array([1.+0.j, 2.+0.j, 3.+0.j])
- >>> bc.dtype
- dtype('complex128')
- Note that even if the input was complex to begin with, a copy is still
- made, since the astype() method always copies:
- >>> c = np.array([1,2,3],np.csingle)
- >>> cc = np.lib.scimath._tocomplex(c); cc
- array([1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64)
- >>> c *= 2; c
- array([2.+0.j, 4.+0.j, 6.+0.j], dtype=complex64)
- >>> cc
- array([1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64)
- """
- if issubclass(arr.dtype.type, (nt.single, nt.byte, nt.short, nt.ubyte,
- nt.ushort, nt.csingle)):
- return arr.astype(nt.csingle)
- else:
- return arr.astype(nt.cdouble)
- def _fix_real_lt_zero(x):
- """Convert `x` to complex if it has real, negative components.
- Otherwise, output is just the array version of the input (via asarray).
- Parameters
- ----------
- x : array_like
- Returns
- -------
- array
- Examples
- --------
- >>> np.lib.scimath._fix_real_lt_zero([1,2])
- array([1, 2])
- >>> np.lib.scimath._fix_real_lt_zero([-1,2])
- array([-1.+0.j, 2.+0.j])
- """
- x = asarray(x)
- if any(isreal(x) & (x < 0)):
- x = _tocomplex(x)
- return x
- def _fix_int_lt_zero(x):
- """Convert `x` to double if it has real, negative components.
- Otherwise, output is just the array version of the input (via asarray).
- Parameters
- ----------
- x : array_like
- Returns
- -------
- array
- Examples
- --------
- >>> np.lib.scimath._fix_int_lt_zero([1,2])
- array([1, 2])
- >>> np.lib.scimath._fix_int_lt_zero([-1,2])
- array([-1., 2.])
- """
- x = asarray(x)
- if any(isreal(x) & (x < 0)):
- x = x * 1.0
- return x
- def _fix_real_abs_gt_1(x):
- """Convert `x` to complex if it has real components x_i with abs(x_i)>1.
- Otherwise, output is just the array version of the input (via asarray).
- Parameters
- ----------
- x : array_like
- Returns
- -------
- array
- Examples
- --------
- >>> np.lib.scimath._fix_real_abs_gt_1([0,1])
- array([0, 1])
- >>> np.lib.scimath._fix_real_abs_gt_1([0,2])
- array([0.+0.j, 2.+0.j])
- """
- x = asarray(x)
- if any(isreal(x) & (abs(x) > 1)):
- x = _tocomplex(x)
- return x
- def _unary_dispatcher(x):
- return (x,)
- @array_function_dispatch(_unary_dispatcher)
- def sqrt(x):
- """
- Compute the square root of x.
- For negative input elements, a complex value is returned
- (unlike `numpy.sqrt` which returns NaN).
- Parameters
- ----------
- x : array_like
- The input value(s).
- Returns
- -------
- out : ndarray or scalar
- The square root of `x`. If `x` was a scalar, so is `out`,
- otherwise an array is returned.
- See Also
- --------
- numpy.sqrt
- Examples
- --------
- For real, non-negative inputs this works just like `numpy.sqrt`:
- >>> np.lib.scimath.sqrt(1)
- 1.0
- >>> np.lib.scimath.sqrt([1, 4])
- array([1., 2.])
- But it automatically handles negative inputs:
- >>> np.lib.scimath.sqrt(-1)
- 1j
- >>> np.lib.scimath.sqrt([-1,4])
- array([0.+1.j, 2.+0.j])
- """
- x = _fix_real_lt_zero(x)
- return nx.sqrt(x)
- @array_function_dispatch(_unary_dispatcher)
- def log(x):
- """
- Compute the natural logarithm of `x`.
- Return the "principal value" (for a description of this, see `numpy.log`)
- of :math:`log_e(x)`. For real `x > 0`, this is a real number (``log(0)``
- returns ``-inf`` and ``log(np.inf)`` returns ``inf``). Otherwise, the
- complex principle value is returned.
- Parameters
- ----------
- x : array_like
- The value(s) whose log is (are) required.
- Returns
- -------
- out : ndarray or scalar
- The log of the `x` value(s). If `x` was a scalar, so is `out`,
- otherwise an array is returned.
- See Also
- --------
- numpy.log
- Notes
- -----
- For a log() that returns ``NAN`` when real `x < 0`, use `numpy.log`
- (note, however, that otherwise `numpy.log` and this `log` are identical,
- i.e., both return ``-inf`` for `x = 0`, ``inf`` for `x = inf`, and,
- notably, the complex principle value if ``x.imag != 0``).
- Examples
- --------
- >>> np.emath.log(np.exp(1))
- 1.0
- Negative arguments are handled "correctly" (recall that
- ``exp(log(x)) == x`` does *not* hold for real ``x < 0``):
- >>> np.emath.log(-np.exp(1)) == (1 + np.pi * 1j)
- True
- """
- x = _fix_real_lt_zero(x)
- return nx.log(x)
- @array_function_dispatch(_unary_dispatcher)
- def log10(x):
- """
- Compute the logarithm base 10 of `x`.
- Return the "principal value" (for a description of this, see
- `numpy.log10`) of :math:`log_{10}(x)`. For real `x > 0`, this
- is a real number (``log10(0)`` returns ``-inf`` and ``log10(np.inf)``
- returns ``inf``). Otherwise, the complex principle value is returned.
- Parameters
- ----------
- x : array_like or scalar
- The value(s) whose log base 10 is (are) required.
- Returns
- -------
- out : ndarray or scalar
- The log base 10 of the `x` value(s). If `x` was a scalar, so is `out`,
- otherwise an array object is returned.
- See Also
- --------
- numpy.log10
- Notes
- -----
- For a log10() that returns ``NAN`` when real `x < 0`, use `numpy.log10`
- (note, however, that otherwise `numpy.log10` and this `log10` are
- identical, i.e., both return ``-inf`` for `x = 0`, ``inf`` for `x = inf`,
- and, notably, the complex principle value if ``x.imag != 0``).
- Examples
- --------
- (We set the printing precision so the example can be auto-tested)
- >>> np.set_printoptions(precision=4)
- >>> np.emath.log10(10**1)
- 1.0
- >>> np.emath.log10([-10**1, -10**2, 10**2])
- array([1.+1.3644j, 2.+1.3644j, 2.+0.j ])
- """
- x = _fix_real_lt_zero(x)
- return nx.log10(x)
- def _logn_dispatcher(n, x):
- return (n, x,)
- @array_function_dispatch(_logn_dispatcher)
- def logn(n, x):
- """
- Take log base n of x.
- If `x` contains negative inputs, the answer is computed and returned in the
- complex domain.
- Parameters
- ----------
- n : array_like
- The integer base(s) in which the log is taken.
- x : array_like
- The value(s) whose log base `n` is (are) required.
- Returns
- -------
- out : ndarray or scalar
- The log base `n` of the `x` value(s). If `x` was a scalar, so is
- `out`, otherwise an array is returned.
- Examples
- --------
- >>> np.set_printoptions(precision=4)
- >>> np.lib.scimath.logn(2, [4, 8])
- array([2., 3.])
- >>> np.lib.scimath.logn(2, [-4, -8, 8])
- array([2.+4.5324j, 3.+4.5324j, 3.+0.j ])
- """
- x = _fix_real_lt_zero(x)
- n = _fix_real_lt_zero(n)
- return nx.log(x)/nx.log(n)
- @array_function_dispatch(_unary_dispatcher)
- def log2(x):
- """
- Compute the logarithm base 2 of `x`.
- Return the "principal value" (for a description of this, see
- `numpy.log2`) of :math:`log_2(x)`. For real `x > 0`, this is
- a real number (``log2(0)`` returns ``-inf`` and ``log2(np.inf)`` returns
- ``inf``). Otherwise, the complex principle value is returned.
- Parameters
- ----------
- x : array_like
- The value(s) whose log base 2 is (are) required.
- Returns
- -------
- out : ndarray or scalar
- The log base 2 of the `x` value(s). If `x` was a scalar, so is `out`,
- otherwise an array is returned.
- See Also
- --------
- numpy.log2
- Notes
- -----
- For a log2() that returns ``NAN`` when real `x < 0`, use `numpy.log2`
- (note, however, that otherwise `numpy.log2` and this `log2` are
- identical, i.e., both return ``-inf`` for `x = 0`, ``inf`` for `x = inf`,
- and, notably, the complex principle value if ``x.imag != 0``).
- Examples
- --------
- We set the printing precision so the example can be auto-tested:
- >>> np.set_printoptions(precision=4)
- >>> np.emath.log2(8)
- 3.0
- >>> np.emath.log2([-4, -8, 8])
- array([2.+4.5324j, 3.+4.5324j, 3.+0.j ])
- """
- x = _fix_real_lt_zero(x)
- return nx.log2(x)
- def _power_dispatcher(x, p):
- return (x, p)
- @array_function_dispatch(_power_dispatcher)
- def power(x, p):
- """
- Return x to the power p, (x**p).
- If `x` contains negative values, the output is converted to the
- complex domain.
- Parameters
- ----------
- x : array_like
- The input value(s).
- p : array_like of ints
- The power(s) to which `x` is raised. If `x` contains multiple values,
- `p` has to either be a scalar, or contain the same number of values
- as `x`. In the latter case, the result is
- ``x[0]**p[0], x[1]**p[1], ...``.
- Returns
- -------
- out : ndarray or scalar
- The result of ``x**p``. If `x` and `p` are scalars, so is `out`,
- otherwise an array is returned.
- See Also
- --------
- numpy.power
- Examples
- --------
- >>> np.set_printoptions(precision=4)
- >>> np.lib.scimath.power([2, 4], 2)
- array([ 4, 16])
- >>> np.lib.scimath.power([2, 4], -2)
- array([0.25 , 0.0625])
- >>> np.lib.scimath.power([-2, 4], 2)
- array([ 4.-0.j, 16.+0.j])
- """
- x = _fix_real_lt_zero(x)
- p = _fix_int_lt_zero(p)
- return nx.power(x, p)
- @array_function_dispatch(_unary_dispatcher)
- def arccos(x):
- """
- Compute the inverse cosine of x.
- Return the "principal value" (for a description of this, see
- `numpy.arccos`) of the inverse cosine of `x`. For real `x` such that
- `abs(x) <= 1`, this is a real number in the closed interval
- :math:`[0, \\pi]`. Otherwise, the complex principle value is returned.
- Parameters
- ----------
- x : array_like or scalar
- The value(s) whose arccos is (are) required.
- Returns
- -------
- out : ndarray or scalar
- The inverse cosine(s) of the `x` value(s). If `x` was a scalar, so
- is `out`, otherwise an array object is returned.
- See Also
- --------
- numpy.arccos
- Notes
- -----
- For an arccos() that returns ``NAN`` when real `x` is not in the
- interval ``[-1,1]``, use `numpy.arccos`.
- Examples
- --------
- >>> np.set_printoptions(precision=4)
- >>> np.emath.arccos(1) # a scalar is returned
- 0.0
- >>> np.emath.arccos([1,2])
- array([0.-0.j , 0.-1.317j])
- """
- x = _fix_real_abs_gt_1(x)
- return nx.arccos(x)
- @array_function_dispatch(_unary_dispatcher)
- def arcsin(x):
- """
- Compute the inverse sine of x.
- Return the "principal value" (for a description of this, see
- `numpy.arcsin`) of the inverse sine of `x`. For real `x` such that
- `abs(x) <= 1`, this is a real number in the closed interval
- :math:`[-\\pi/2, \\pi/2]`. Otherwise, the complex principle value is
- returned.
- Parameters
- ----------
- x : array_like or scalar
- The value(s) whose arcsin is (are) required.
- Returns
- -------
- out : ndarray or scalar
- The inverse sine(s) of the `x` value(s). If `x` was a scalar, so
- is `out`, otherwise an array object is returned.
- See Also
- --------
- numpy.arcsin
- Notes
- -----
- For an arcsin() that returns ``NAN`` when real `x` is not in the
- interval ``[-1,1]``, use `numpy.arcsin`.
- Examples
- --------
- >>> np.set_printoptions(precision=4)
- >>> np.emath.arcsin(0)
- 0.0
- >>> np.emath.arcsin([0,1])
- array([0. , 1.5708])
- """
- x = _fix_real_abs_gt_1(x)
- return nx.arcsin(x)
- @array_function_dispatch(_unary_dispatcher)
- def arctanh(x):
- """
- Compute the inverse hyperbolic tangent of `x`.
- Return the "principal value" (for a description of this, see
- `numpy.arctanh`) of ``arctanh(x)``. For real `x` such that
- ``abs(x) < 1``, this is a real number. If `abs(x) > 1`, or if `x` is
- complex, the result is complex. Finally, `x = 1` returns``inf`` and
- ``x=-1`` returns ``-inf``.
- Parameters
- ----------
- x : array_like
- The value(s) whose arctanh is (are) required.
- Returns
- -------
- out : ndarray or scalar
- The inverse hyperbolic tangent(s) of the `x` value(s). If `x` was
- a scalar so is `out`, otherwise an array is returned.
- See Also
- --------
- numpy.arctanh
- Notes
- -----
- For an arctanh() that returns ``NAN`` when real `x` is not in the
- interval ``(-1,1)``, use `numpy.arctanh` (this latter, however, does
- return +/-inf for ``x = +/-1``).
- Examples
- --------
- >>> np.set_printoptions(precision=4)
- >>> from numpy.testing import suppress_warnings
- >>> with suppress_warnings() as sup:
- ... sup.filter(RuntimeWarning)
- ... np.emath.arctanh(np.eye(2))
- array([[inf, 0.],
- [ 0., inf]])
- >>> np.emath.arctanh([1j])
- array([0.+0.7854j])
- """
- x = _fix_real_abs_gt_1(x)
- return nx.arctanh(x)
|