1"""
2Wrapper functions to more user-friendly calling of certain math functions
3whose output data-type is different than the input data-type in certain
4domains of the input.
5
6For example, for functions like `log` with branch cuts, the versions in this
7module provide the mathematically valid answers in the complex plane::
8
9 >>> import math
10 >>> np.emath.log(-math.exp(1)) == (1+1j*math.pi)
11 True
12
13Similarly, `sqrt`, other base logarithms, `power` and trig functions are
14correctly handled. See their respective docstrings for specific examples.
15
16Functions
17---------
18
19.. autosummary::
20 :toctree: generated/
21
22 sqrt
23 log
24 log2
25 logn
26 log10
27 power
28 arccos
29 arcsin
30 arctanh
31
32"""
33import numpy.core.numeric as nx
34import numpy.core.numerictypes as nt
35from numpy.core.numeric import asarray, any
36from numpy.core.overrides import array_function_dispatch
37from numpy.lib.type_check import isreal
38
39
40__all__ = [
41 'sqrt', 'log', 'log2', 'logn', 'log10', 'power', 'arccos', 'arcsin',
42 'arctanh'
43 ]
44
45
46_ln2 = nx.log(2.0)
47
48
49def _tocomplex(arr):
50 """Convert its input `arr` to a complex array.
51
52 The input is returned as a complex array of the smallest type that will fit
53 the original data: types like single, byte, short, etc. become csingle,
54 while others become cdouble.
55
56 A copy of the input is always made.
57
58 Parameters
59 ----------
60 arr : array
61
62 Returns
63 -------
64 array
65 An array with the same input data as the input but in complex form.
66
67 Examples
68 --------
69
70 First, consider an input of type short:
71
72 >>> a = np.array([1,2,3],np.short)
73
74 >>> ac = np.lib.scimath._tocomplex(a); ac
75 array([1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64)
76
77 >>> ac.dtype
78 dtype('complex64')
79
80 If the input is of type double, the output is correspondingly of the
81 complex double type as well:
82
83 >>> b = np.array([1,2,3],np.double)
84
85 >>> bc = np.lib.scimath._tocomplex(b); bc
86 array([1.+0.j, 2.+0.j, 3.+0.j])
87
88 >>> bc.dtype
89 dtype('complex128')
90
91 Note that even if the input was complex to begin with, a copy is still
92 made, since the astype() method always copies:
93
94 >>> c = np.array([1,2,3],np.csingle)
95
96 >>> cc = np.lib.scimath._tocomplex(c); cc
97 array([1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64)
98
99 >>> c *= 2; c
100 array([2.+0.j, 4.+0.j, 6.+0.j], dtype=complex64)
101
102 >>> cc
103 array([1.+0.j, 2.+0.j, 3.+0.j], dtype=complex64)
104 """
105 if issubclass(arr.dtype.type, (nt.single, nt.byte, nt.short, nt.ubyte,
106 nt.ushort, nt.csingle)):
107 return arr.astype(nt.csingle)
108 else:
109 return arr.astype(nt.cdouble)
110
111
112def _fix_real_lt_zero(x):
113 """Convert `x` to complex if it has real, negative components.
114
115 Otherwise, output is just the array version of the input (via asarray).
116
117 Parameters
118 ----------
119 x : array_like
120
121 Returns
122 -------
123 array
124
125 Examples
126 --------
127 >>> np.lib.scimath._fix_real_lt_zero([1,2])
128 array([1, 2])
129
130 >>> np.lib.scimath._fix_real_lt_zero([-1,2])
131 array([-1.+0.j, 2.+0.j])
132
133 """
134 x = asarray(x)
135 if any(isreal(x) & (x < 0)):
136 x = _tocomplex(x)
137 return x
138
139
140def _fix_int_lt_zero(x):
141 """Convert `x` to double if it has real, negative components.
142
143 Otherwise, output is just the array version of the input (via asarray).
144
145 Parameters
146 ----------
147 x : array_like
148
149 Returns
150 -------
151 array
152
153 Examples
154 --------
155 >>> np.lib.scimath._fix_int_lt_zero([1,2])
156 array([1, 2])
157
158 >>> np.lib.scimath._fix_int_lt_zero([-1,2])
159 array([-1., 2.])
160 """
161 x = asarray(x)
162 if any(isreal(x) & (x < 0)):
163 x = x * 1.0
164 return x
165
166
167def _fix_real_abs_gt_1(x):
168 """Convert `x` to complex if it has real components x_i with abs(x_i)>1.
169
170 Otherwise, output is just the array version of the input (via asarray).
171
172 Parameters
173 ----------
174 x : array_like
175
176 Returns
177 -------
178 array
179
180 Examples
181 --------
182 >>> np.lib.scimath._fix_real_abs_gt_1([0,1])
183 array([0, 1])
184
185 >>> np.lib.scimath._fix_real_abs_gt_1([0,2])
186 array([0.+0.j, 2.+0.j])
187 """
188 x = asarray(x)
189 if any(isreal(x) & (abs(x) > 1)):
190 x = _tocomplex(x)
191 return x
192
193
194def _unary_dispatcher(x):
195 return (x,)
196
197
198@array_function_dispatch(_unary_dispatcher)
199def sqrt(x):
200 """
201 Compute the square root of x.
202
203 For negative input elements, a complex value is returned
204 (unlike `numpy.sqrt` which returns NaN).
205
206 Parameters
207 ----------
208 x : array_like
209 The input value(s).
210
211 Returns
212 -------
213 out : ndarray or scalar
214 The square root of `x`. If `x` was a scalar, so is `out`,
215 otherwise an array is returned.
216
217 See Also
218 --------
219 numpy.sqrt
220
221 Examples
222 --------
223 For real, non-negative inputs this works just like `numpy.sqrt`:
224
225 >>> np.emath.sqrt(1)
226 1.0
227 >>> np.emath.sqrt([1, 4])
228 array([1., 2.])
229
230 But it automatically handles negative inputs:
231
232 >>> np.emath.sqrt(-1)
233 1j
234 >>> np.emath.sqrt([-1,4])
235 array([0.+1.j, 2.+0.j])
236
237 Different results are expected because:
238 floating point 0.0 and -0.0 are distinct.
239
240 For more control, explicitly use complex() as follows:
241
242 >>> np.emath.sqrt(complex(-4.0, 0.0))
243 2j
244 >>> np.emath.sqrt(complex(-4.0, -0.0))
245 -2j
246 """
247 x = _fix_real_lt_zero(x)
248 return nx.sqrt(x)
249
250
251@array_function_dispatch(_unary_dispatcher)
252def log(x):
253 """
254 Compute the natural logarithm of `x`.
255
256 Return the "principal value" (for a description of this, see `numpy.log`)
257 of :math:`log_e(x)`. For real `x > 0`, this is a real number (``log(0)``
258 returns ``-inf`` and ``log(np.inf)`` returns ``inf``). Otherwise, the
259 complex principle value is returned.
260
261 Parameters
262 ----------
263 x : array_like
264 The value(s) whose log is (are) required.
265
266 Returns
267 -------
268 out : ndarray or scalar
269 The log of the `x` value(s). If `x` was a scalar, so is `out`,
270 otherwise an array is returned.
271
272 See Also
273 --------
274 numpy.log
275
276 Notes
277 -----
278 For a log() that returns ``NAN`` when real `x < 0`, use `numpy.log`
279 (note, however, that otherwise `numpy.log` and this `log` are identical,
280 i.e., both return ``-inf`` for `x = 0`, ``inf`` for `x = inf`, and,
281 notably, the complex principle value if ``x.imag != 0``).
282
283 Examples
284 --------
285 >>> np.emath.log(np.exp(1))
286 1.0
287
288 Negative arguments are handled "correctly" (recall that
289 ``exp(log(x)) == x`` does *not* hold for real ``x < 0``):
290
291 >>> np.emath.log(-np.exp(1)) == (1 + np.pi * 1j)
292 True
293
294 """
295 x = _fix_real_lt_zero(x)
296 return nx.log(x)
297
298
299@array_function_dispatch(_unary_dispatcher)
300def log10(x):
301 """
302 Compute the logarithm base 10 of `x`.
303
304 Return the "principal value" (for a description of this, see
305 `numpy.log10`) of :math:`log_{10}(x)`. For real `x > 0`, this
306 is a real number (``log10(0)`` returns ``-inf`` and ``log10(np.inf)``
307 returns ``inf``). Otherwise, the complex principle value is returned.
308
309 Parameters
310 ----------
311 x : array_like or scalar
312 The value(s) whose log base 10 is (are) required.
313
314 Returns
315 -------
316 out : ndarray or scalar
317 The log base 10 of the `x` value(s). If `x` was a scalar, so is `out`,
318 otherwise an array object is returned.
319
320 See Also
321 --------
322 numpy.log10
323
324 Notes
325 -----
326 For a log10() that returns ``NAN`` when real `x < 0`, use `numpy.log10`
327 (note, however, that otherwise `numpy.log10` and this `log10` are
328 identical, i.e., both return ``-inf`` for `x = 0`, ``inf`` for `x = inf`,
329 and, notably, the complex principle value if ``x.imag != 0``).
330
331 Examples
332 --------
333
334 (We set the printing precision so the example can be auto-tested)
335
336 >>> np.set_printoptions(precision=4)
337
338 >>> np.emath.log10(10**1)
339 1.0
340
341 >>> np.emath.log10([-10**1, -10**2, 10**2])
342 array([1.+1.3644j, 2.+1.3644j, 2.+0.j ])
343
344 """
345 x = _fix_real_lt_zero(x)
346 return nx.log10(x)
347
348
349def _logn_dispatcher(n, x):
350 return (n, x,)
351
352
353@array_function_dispatch(_logn_dispatcher)
354def logn(n, x):
355 """
356 Take log base n of x.
357
358 If `x` contains negative inputs, the answer is computed and returned in the
359 complex domain.
360
361 Parameters
362 ----------
363 n : array_like
364 The integer base(s) in which the log is taken.
365 x : array_like
366 The value(s) whose log base `n` is (are) required.
367
368 Returns
369 -------
370 out : ndarray or scalar
371 The log base `n` of the `x` value(s). If `x` was a scalar, so is
372 `out`, otherwise an array is returned.
373
374 Examples
375 --------
376 >>> np.set_printoptions(precision=4)
377
378 >>> np.emath.logn(2, [4, 8])
379 array([2., 3.])
380 >>> np.emath.logn(2, [-4, -8, 8])
381 array([2.+4.5324j, 3.+4.5324j, 3.+0.j ])
382
383 """
384 x = _fix_real_lt_zero(x)
385 n = _fix_real_lt_zero(n)
386 return nx.log(x)/nx.log(n)
387
388
389@array_function_dispatch(_unary_dispatcher)
390def log2(x):
391 """
392 Compute the logarithm base 2 of `x`.
393
394 Return the "principal value" (for a description of this, see
395 `numpy.log2`) of :math:`log_2(x)`. For real `x > 0`, this is
396 a real number (``log2(0)`` returns ``-inf`` and ``log2(np.inf)`` returns
397 ``inf``). Otherwise, the complex principle value is returned.
398
399 Parameters
400 ----------
401 x : array_like
402 The value(s) whose log base 2 is (are) required.
403
404 Returns
405 -------
406 out : ndarray or scalar
407 The log base 2 of the `x` value(s). If `x` was a scalar, so is `out`,
408 otherwise an array is returned.
409
410 See Also
411 --------
412 numpy.log2
413
414 Notes
415 -----
416 For a log2() that returns ``NAN`` when real `x < 0`, use `numpy.log2`
417 (note, however, that otherwise `numpy.log2` and this `log2` are
418 identical, i.e., both return ``-inf`` for `x = 0`, ``inf`` for `x = inf`,
419 and, notably, the complex principle value if ``x.imag != 0``).
420
421 Examples
422 --------
423 We set the printing precision so the example can be auto-tested:
424
425 >>> np.set_printoptions(precision=4)
426
427 >>> np.emath.log2(8)
428 3.0
429 >>> np.emath.log2([-4, -8, 8])
430 array([2.+4.5324j, 3.+4.5324j, 3.+0.j ])
431
432 """
433 x = _fix_real_lt_zero(x)
434 return nx.log2(x)
435
436
437def _power_dispatcher(x, p):
438 return (x, p)
439
440
441@array_function_dispatch(_power_dispatcher)
442def power(x, p):
443 """
444 Return x to the power p, (x**p).
445
446 If `x` contains negative values, the output is converted to the
447 complex domain.
448
449 Parameters
450 ----------
451 x : array_like
452 The input value(s).
453 p : array_like of ints
454 The power(s) to which `x` is raised. If `x` contains multiple values,
455 `p` has to either be a scalar, or contain the same number of values
456 as `x`. In the latter case, the result is
457 ``x[0]**p[0], x[1]**p[1], ...``.
458
459 Returns
460 -------
461 out : ndarray or scalar
462 The result of ``x**p``. If `x` and `p` are scalars, so is `out`,
463 otherwise an array is returned.
464
465 See Also
466 --------
467 numpy.power
468
469 Examples
470 --------
471 >>> np.set_printoptions(precision=4)
472
473 >>> np.emath.power([2, 4], 2)
474 array([ 4, 16])
475 >>> np.emath.power([2, 4], -2)
476 array([0.25 , 0.0625])
477 >>> np.emath.power([-2, 4], 2)
478 array([ 4.-0.j, 16.+0.j])
479
480 """
481 x = _fix_real_lt_zero(x)
482 p = _fix_int_lt_zero(p)
483 return nx.power(x, p)
484
485
486@array_function_dispatch(_unary_dispatcher)
487def arccos(x):
488 """
489 Compute the inverse cosine of x.
490
491 Return the "principal value" (for a description of this, see
492 `numpy.arccos`) of the inverse cosine of `x`. For real `x` such that
493 `abs(x) <= 1`, this is a real number in the closed interval
494 :math:`[0, \\pi]`. Otherwise, the complex principle value is returned.
495
496 Parameters
497 ----------
498 x : array_like or scalar
499 The value(s) whose arccos is (are) required.
500
501 Returns
502 -------
503 out : ndarray or scalar
504 The inverse cosine(s) of the `x` value(s). If `x` was a scalar, so
505 is `out`, otherwise an array object is returned.
506
507 See Also
508 --------
509 numpy.arccos
510
511 Notes
512 -----
513 For an arccos() that returns ``NAN`` when real `x` is not in the
514 interval ``[-1,1]``, use `numpy.arccos`.
515
516 Examples
517 --------
518 >>> np.set_printoptions(precision=4)
519
520 >>> np.emath.arccos(1) # a scalar is returned
521 0.0
522
523 >>> np.emath.arccos([1,2])
524 array([0.-0.j , 0.-1.317j])
525
526 """
527 x = _fix_real_abs_gt_1(x)
528 return nx.arccos(x)
529
530
531@array_function_dispatch(_unary_dispatcher)
532def arcsin(x):
533 """
534 Compute the inverse sine of x.
535
536 Return the "principal value" (for a description of this, see
537 `numpy.arcsin`) of the inverse sine of `x`. For real `x` such that
538 `abs(x) <= 1`, this is a real number in the closed interval
539 :math:`[-\\pi/2, \\pi/2]`. Otherwise, the complex principle value is
540 returned.
541
542 Parameters
543 ----------
544 x : array_like or scalar
545 The value(s) whose arcsin is (are) required.
546
547 Returns
548 -------
549 out : ndarray or scalar
550 The inverse sine(s) of the `x` value(s). If `x` was a scalar, so
551 is `out`, otherwise an array object is returned.
552
553 See Also
554 --------
555 numpy.arcsin
556
557 Notes
558 -----
559 For an arcsin() that returns ``NAN`` when real `x` is not in the
560 interval ``[-1,1]``, use `numpy.arcsin`.
561
562 Examples
563 --------
564 >>> np.set_printoptions(precision=4)
565
566 >>> np.emath.arcsin(0)
567 0.0
568
569 >>> np.emath.arcsin([0,1])
570 array([0. , 1.5708])
571
572 """
573 x = _fix_real_abs_gt_1(x)
574 return nx.arcsin(x)
575
576
577@array_function_dispatch(_unary_dispatcher)
578def arctanh(x):
579 """
580 Compute the inverse hyperbolic tangent of `x`.
581
582 Return the "principal value" (for a description of this, see
583 `numpy.arctanh`) of ``arctanh(x)``. For real `x` such that
584 ``abs(x) < 1``, this is a real number. If `abs(x) > 1`, or if `x` is
585 complex, the result is complex. Finally, `x = 1` returns``inf`` and
586 ``x=-1`` returns ``-inf``.
587
588 Parameters
589 ----------
590 x : array_like
591 The value(s) whose arctanh is (are) required.
592
593 Returns
594 -------
595 out : ndarray or scalar
596 The inverse hyperbolic tangent(s) of the `x` value(s). If `x` was
597 a scalar so is `out`, otherwise an array is returned.
598
599
600 See Also
601 --------
602 numpy.arctanh
603
604 Notes
605 -----
606 For an arctanh() that returns ``NAN`` when real `x` is not in the
607 interval ``(-1,1)``, use `numpy.arctanh` (this latter, however, does
608 return +/-inf for ``x = +/-1``).
609
610 Examples
611 --------
612 >>> np.set_printoptions(precision=4)
613
614 >>> from numpy.testing import suppress_warnings
615 >>> with suppress_warnings() as sup:
616 ... sup.filter(RuntimeWarning)
617 ... np.emath.arctanh(np.eye(2))
618 array([[inf, 0.],
619 [ 0., inf]])
620 >>> np.emath.arctanh([1j])
621 array([0.+0.7854j])
622
623 """
624 x = _fix_real_abs_gt_1(x)
625 return nx.arctanh(x)