Skip to content

Power Exp Log

NumPy provides element-wise power, exponential, and logarithmic functions.

Mental Model

np.exp, np.log, and ** are element-wise ufuncs that apply the corresponding math function to every element independently. They are the vectorized equivalents of math.exp and math.log -- same formulas, but operating on entire arrays at once. Use np.log for natural log, np.log10 for base-10, and np.log2 for base-2.

Conceptually, exp and log are transformations of scale: exp maps additive differences to multiplicative ratios, and log does the reverse. This is why they appear everywhere — decibels (log scale of power), softmax (exp for probabilities), compound growth (exp of rate × time), and information theory (log of probabilities).

These Are Just Ufuncs

Every function on this page — np.exp, np.log, np.sqrt, np.power — is a ufunc. They follow the same execution pattern as arithmetic: broadcast inputs, apply a compiled C scalar function element-wise, produce an output array. They support out=, where=, .reduce(), and every other ufunc feature. The math is domain-specific; the mechanism is identical.

Power Operations

1. Using ** Operator

```python import numpy as np

def main(): a = np.array([[1, 2], [3, 4]])

print("a =")
print(a)
print()
print("a ** 2 =")
print(a ** 2)

if name == "main": main() ```

2. Using np.power

```python import numpy as np

def main(): a = np.array([[1, 2], [3, 4]])

# Equivalent to a ** 2
b = np.power(a, 2)

print("np.power(a, 2) =")
print(b)

if name == "main": main() ```

3. Base as Array

```python import numpy as np

def main(): a = np.array([[1, 2], [3, 4]])

# 2 raised to each element
b = 2 ** a
c = np.power(2, a)

print("2 ** a =")
print(b)
print()
print("np.power(2, a) =")
print(c)

if name == "main": main() ```

Square Root

1. np.sqrt

```python import numpy as np

def main(): a = np.array([1, 4, 9, 16, 25])

print(f"a = {a}")
print(f"np.sqrt(a) = {np.sqrt(a)}")
print(f"a ** 0.5 = {a ** 0.5}")

if name == "main": main() ```

2. Cube Root

```python import numpy as np

def main(): a = np.array([1, 8, 27, 64])

print(f"a = {a}")
print(f"np.cbrt(a) = {np.cbrt(a)}")
print(f"a ** (1/3) = {a ** (1/3)}")

if name == "main": main() ```

3. Negative Values

```python import numpy as np

def main(): a = np.array([-1, -4, -9])

# sqrt of negative returns nan
print(f"np.sqrt({a}) = {np.sqrt(a)}")

# Use complex dtype
a_complex = a.astype(complex)
print(f"np.sqrt({a_complex}) = {np.sqrt(a_complex)}")

if name == "main": main() ```

Exponential

1. np.exp

```python import numpy as np

def main(): x = np.array([0, 1, 2, 3])

print(f"x = {x}")
print(f"np.exp(x) = {np.exp(x)}")
print(f"e^0 = {np.exp(0):.4f}")
print(f"e^1 = {np.exp(1):.4f}")

if name == "main": main() ```

2. np.exp2

```python import numpy as np

def main(): x = np.array([0, 1, 2, 3, 4])

print(f"x = {x}")
print(f"np.exp2(x) = {np.exp2(x)}")  # 2^x
print(f"2 ** x = {2 ** x}")

if name == "main": main() ```

3. np.expm1

```python import numpy as np

def main(): # exp(x) - 1, more accurate for small x x = np.array([1e-10, 1e-5, 0.1, 1.0])

print("x          exp(x)-1          expm1(x)")
for val in x:
    print(f"{val:.0e}      {np.exp(val)-1:.10f}    {np.expm1(val):.10f}")

if name == "main": main() ```

Logarithm

1. Natural Log (ln)

```python import numpy as np

def main(): x = np.array([1, np.e, np.e2, np.e3])

print(f"x = {x}")
print(f"np.log(x) = {np.log(x)}")

if name == "main": main() ```

2. Log Base 10

```python import numpy as np

def main(): x = np.array([1, 10, 100, 1000])

print(f"x = {x}")
print(f"np.log10(x) = {np.log10(x)}")

if name == "main": main() ```

3. Log Base 2

```python import numpy as np

def main(): x = np.array([1, 2, 4, 8, 16])

print(f"x = {x}")
print(f"np.log2(x) = {np.log2(x)}")

if name == "main": main() ```

np.log1p

1. Log of 1+x

```python import numpy as np

def main(): # log(1+x), more accurate for small x x = np.array([1e-10, 1e-5, 0.1, 1.0])

print("x          log(1+x)          log1p(x)")
for val in x:
    print(f"{val:.0e}      {np.log(1+val):.10f}    {np.log1p(val):.10f}")

if name == "main": main() ```

2. Why Use log1p

For very small x, log(1+x) loses precision due to floating point.

3. Inverse Relationship

```python import numpy as np

def main(): x = np.array([0.1, 0.5, 1.0])

# expm1 and log1p are inverses
print(f"x = {x}")
print(f"log1p(expm1(x)) = {np.log1p(np.expm1(x))}")
print(f"expm1(log1p(x)) = {np.expm1(np.log1p(x))}")

if name == "main": main() ```

Visualization

1. Exp and Log Curves

```python import numpy as np import matplotlib.pyplot as plt

def main(): x_log = np.linspace(0.1, 10, 100) y_log = np.log(x_log)

x_exp = np.linspace(-2, 2, 100)
y_exp = np.exp(x_exp)

fig, (ax0, ax1) = plt.subplots(1, 2, figsize=(12, 4))

ax0.plot(x_log, y_log, 'r-', linewidth=2)
ax0.axhline(0, linestyle='--', alpha=0.3, color='blue')
ax0.axvline(0, linestyle='--', alpha=0.3, color='blue')
ax0.set_title('y = ln(x)')
ax0.set_xlabel('x')
ax0.set_ylabel('y')
ax0.grid(True, alpha=0.3)

ax1.plot(x_exp, y_exp, 'r-', linewidth=2)
ax1.axhline(0, linestyle='--', alpha=0.3, color='blue')
ax1.axvline(0, linestyle='--', alpha=0.3, color='blue')
ax1.set_title('y = exp(x)')
ax1.set_xlabel('x')
ax1.set_ylabel('y')
ax1.grid(True, alpha=0.3)

plt.tight_layout()
plt.show()

if name == "main": main() ```

2. Power Functions

```python import numpy as np import matplotlib.pyplot as plt

def main(): x = np.linspace(0, 3, 100)

fig, ax = plt.subplots(figsize=(8, 5))

for n in [0.5, 1, 2, 3]:
    ax.plot(x, x ** n, label=f'x^{n}')

ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('Power Functions')
ax.legend()
ax.grid(True, alpha=0.3)
ax.set_ylim(0, 10)

plt.show()

if name == "main": main() ```

Applications

1. Compound Interest

```python import numpy as np

def main(): principal = 1000 rate = 0.05 years = np.arange(1, 11)

# A = P * e^(rt) for continuous compounding
amount = principal * np.exp(rate * years)

print("Continuous Compounding at 5%")
print("Year | Amount")
print("-" * 20)
for y, a in zip(years, amount):
    print(f"  {y:2} | ${a:.2f}")

if name == "main": main() ```

2. Decibels

```python import numpy as np

def main(): # Power ratios ratios = np.array([0.1, 0.5, 1, 2, 10, 100])

# Convert to decibels
db = 10 * np.log10(ratios)

print("Ratio | dB")
print("-" * 20)
for r, d in zip(ratios, db):
    print(f"{r:5.1f} | {d:+.1f} dB")

if name == "main": main() ```

3. Softmax Function

```python import numpy as np

def softmax(x): # Subtract max for numerical stability exp_x = np.exp(x - np.max(x)) return exp_x / np.sum(exp_x)

def main(): logits = np.array([2.0, 1.0, 0.1])

probs = softmax(logits)

print(f"Logits: {logits}")
print(f"Softmax: {probs}")
print(f"Sum: {probs.sum():.4f}")

if name == "main": main() ```

Summary Table

1. Power Functions

Function Description
a ** b Power operator
np.power(a, b) Element-wise power
np.sqrt(a) Square root
np.cbrt(a) Cube root
np.square(a) Square (a²)

2. Exponential Functions

Function Description
np.exp(x) e^x
np.exp2(x) 2^x
np.expm1(x) e^x - 1 (accurate for small x)

3. Logarithm Functions

Function Description
np.log(x) Natural log (ln)
np.log10(x) Log base 10
np.log2(x) Log base 2
np.log1p(x) ln(1+x) (accurate for small x)

Exercises

Exercise 1. Compute the element-wise square, cube, and square root of np.array([1, 4, 9, 16, 25]) using np.power and np.sqrt.

Solution to Exercise 1

```python import numpy as np

arr = np.array([1, 4, 9, 16, 25]) print("Square:", np.power(arr, 2)) print("Cube:", np.power(arr, 3)) print("Sqrt:", np.sqrt(arr)) ```


Exercise 2. Compute \(e^x\) for \(x = [0, 1, 2, 3]\) using np.exp. Verify by comparing with np.e ** x.

Solution to Exercise 2

```python import numpy as np

x = np.array([0, 1, 2, 3]) print(np.exp(x)) print(np.e ** x)

Both produce [1.0, 2.718..., 7.389..., 20.086...]

```


Exercise 3. Given an array of positive values, compute the natural log, log base 10, and log base 2. Verify that np.exp(np.log(x)) returns the original values.

Solution to Exercise 3

```python import numpy as np

x = np.array([1.0, 10.0, 100.0]) print("ln:", np.log(x)) print("log10:", np.log10(x)) print("log2:", np.log2(x)) print("Roundtrip:", np.exp(np.log(x))) # [1. 10. 100.] ```


Exercise 4. Implement the softmax function \(\text{softmax}(x_i) = e^{x_i} / \sum_j e^{x_j}\) using NumPy. Apply it to [1.0, 2.0, 3.0] and verify the outputs sum to 1.

Solution to Exercise 4

```python import numpy as np

def softmax(x): e_x = np.exp(x - np.max(x)) # subtract max for numerical stability return e_x / e_x.sum()

result = softmax(np.array([1.0, 2.0, 3.0])) print(result) print(f"Sum: {result.sum():.10f}") # 1.0 ```