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