1"""

2 Wrapper functions to more user-friendly calling of certain math functions

3 whose output data-type is different than the input data-type in certain

4 domains of the input.

5

6 For example, for functions like `log` with branch cuts, the versions in this

7 module 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

13 Similarly, `sqrt`, other base logarithms, `power` and trig functions are

14 correctly handled. See their respective docstrings for specific examples.

15

16 Functions

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"""

33 import numpy.core.numeric as nx

34 import numpy.core.numerictypes as nt

35 from numpy.core.numeric import asarray, any

36 from numpy.core.overrides import array_function_dispatch

37 from 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

49 def _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

112 def _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

140 def _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

167 def _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

194 def _unary_dispatcher(x):

195 return (x,)

196

197

198@array_function_dispatch(_unary_dispatcher)

199 def 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)

252 def 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)

300 def 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

349 def _logn_dispatcher(n, x):

350 return (n, x,)

351

352

353@array_function_dispatch(_logn_dispatcher)

354 def 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)

390 def 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

437 def _power_dispatcher(x, p):

438 return (x, p)

439

440

441@array_function_dispatch(_power_dispatcher)

442 def 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)

487 def 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)

532 def 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)

578 def 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)