xbe.at

Python / Python for ML and AI

Bounded Analytic Functions in Python

9 min read, 15 sections

Contents
  1. Introduction to Bounded Analytic Functions
  2. Definition and Properties
  3. The Unit Disk and Hardy Space
  4. Maximum Modulus Principle
  5. Schwarz Lemma
  6. Blaschke Products
  7. Inner and Outer Functions
  8. Nevanlinna-Pick Interpolation
  9. Hardy Spaces and Hp Norms
  10. Conformal Mapping
  11. Boundary Behavior
  12. Applications in Signal Processing
  13. Real-life Example: Image Compression
  14. Real-life Example: Control Systems
  15. Additional Resources

Introduction to Bounded Analytic Functions

Bounded analytic functions are complex-valued functions that are both analytic and bounded on a given domain. They play a crucial role in complex analysis and have applications in various fields of mathematics and engineering.

import numpy as np
import matplotlib.pyplot as plt

def plot_bounded_function(f, domain):
    x = np.linspace(domain[0], domain[1], 1000)
    y = np.linspace(domain[0], domain[1], 1000)
    X, Y = np.meshgrid(x, y)
    Z = X + 1j*Y
    
    plt.figure(figsize=(10, 8))
    plt.contourf(X, Y, np.abs(f(Z)), levels=20, cmap='viridis')
    plt.colorbar(label='Magnitude')
    plt.title('Magnitude of a Bounded Analytic Function')
    plt.xlabel('Re(z)')
    plt.ylabel('Im(z)')
    plt.show()

# Example: f(z) = (z - 1) / (z + 1)
f = lambda z: (z - 1) / (z + 1)
plot_bounded_function(f, [-2, 2])

Definition and Properties

A function f(z) is bounded and analytic on a domain D if:

  1. f(z) is analytic (complex differentiable) at every point in D
  2. There exists a constant M > 0 such that |f(z)| ≤ M for all z in D
def is_bounded_analytic(f, domain, M, epsilon=1e-6):
    def complex_derivative(f, z, h=1e-8):
        return (f(z + h) - f(z)) / h
    
    z = np.random.uniform(domain[0], domain[1], size=(1000, 2)).view(np.complex128)
    
    analytic = np.allclose(f(z), complex_derivative(f, z), atol=epsilon)
    bounded = np.all(np.abs(f(z)) <= M)
    
    return analytic and bounded

# Example: f(z) = sin(z) / z
f = lambda z: np.sin(z) / z
domain = [-10, 10]
M = 1

print(f"Is f(z) = sin(z)/z bounded analytic? {is_bounded_analytic(f, domain, M)}")

The Unit Disk and Hardy Space

The unit disk D = {z : |z| < 1} is a fundamental domain for studying bounded analytic functions. The Hardy space H∞(D) consists of all bounded analytic functions on D.

def plot_unit_disk():
    theta = np.linspace(0, 2*np.pi, 100)
    x = np.cos(theta)
    y = np.sin(theta)
    
    plt.figure(figsize=(8, 8))
    plt.plot(x, y, 'b-')
    plt.fill(x, y, 'lightblue', alpha=0.3)
    plt.title('Unit Disk')
    plt.xlabel('Re(z)')
    plt.ylabel('Im(z)')
    plt.axis('equal')
    plt.grid(True)
    plt.show()

plot_unit_disk()

Maximum Modulus Principle

For a non-constant analytic function f(z) on a domain D, the maximum of |f(z)| occurs on the boundary of D, not in its interior.

def maximum_modulus_principle(f, domain, num_points=1000):
    boundary_x = np.concatenate([
        np.linspace(domain[0], domain[1], num_points),
        np.full(num_points, domain[1]),
        np.linspace(domain[1], domain[0], num_points),
        np.full(num_points, domain[0])
    ])
    
    boundary_y = np.concatenate([
        np.full(num_points, domain[2]),
        np.linspace(domain[2], domain[3], num_points),
        np.full(num_points, domain[3]),
        np.linspace(domain[3], domain[2], num_points)
    ])
    
    boundary_z = boundary_x + 1j*boundary_y
    max_modulus = np.max(np.abs(f(boundary_z)))
    
    return max_modulus

# Example: f(z) = z^2
f = lambda z: z**2
domain = [-1, 1, -1, 1]  # [x_min, x_max, y_min, y_max]

max_value = maximum_modulus_principle(f, domain)
print(f"Maximum modulus of f(z) = z^2 on the boundary: {max_value}")

Schwarz Lemma

The Schwarz Lemma is a powerful tool for studying bounded analytic functions on the unit disk. It states that if f(z) is analytic on D, |f(z)| ≤ 1 for all z in D, and f(0) = 0, then |f(z)| ≤ |z| for all z in D.

def schwarz_lemma(f, z):
    if np.abs(z) >= 1:
        raise ValueError("z must be inside the unit disk")
    
    return np.abs(f(z)) <= np.abs(z)

# Example: f(z) = z^2
f = lambda z: z**2
z = 0.5 + 0.3j

print(f"For f(z) = z^2 and z = {z}:")
print(f"|f(z)| = {np.abs(f(z)):.4f}")
print(f"|z| = {np.abs(z):.4f}")
print(f"Schwarz Lemma holds: {schwarz_lemma(f, z)}")

Blaschke Products

Blaschke products are an important class of bounded analytic functions on the unit disk. They are used to construct other bounded analytic functions and study their properties.

def blaschke_product(z, zeros):
    product = 1
    for a in zeros:
        if np.abs(a) >= 1:
            raise ValueError("All zeros must be inside the unit disk")
        product *= (z - a) / (1 - np.conj(a) * z)
    return product

def plot_blaschke_product(zeros):
    theta = np.linspace(0, 2*np.pi, 1000)
    z = np.exp(1j * theta)
    
    values = blaschke_product(z, zeros)
    
    plt.figure(figsize=(10, 8))
    plt.polar(theta, np.abs(values))
    plt.title('Magnitude of Blaschke Product on the Unit Circle')
    plt.show()

# Example: Blaschke product with zeros at 0.5 and -0.3+0.4j
zeros = [0.5, -0.3+0.4j]
plot_blaschke_product(zeros)

Inner and Outer Functions

Bounded analytic functions can be factored into inner and outer functions. Inner functions have modulus 1 on the unit circle, while outer functions have no zeros in the unit disk.

def inner_function(z, a):
    return (z - a) / (1 - np.conj(a) * z)

def outer_function(z, f):
    return np.exp((1 / (2*np.pi)) * np.log(np.abs(f(np.exp(1j*z)))))

def plot_inner_outer(a, f):
    theta = np.linspace(0, 2*np.pi, 1000)
    z = np.exp(1j * theta)
    
    inner_values = inner_function(z, a)
    outer_values = outer_function(theta, f)
    
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
    
    ax1.plot(theta, np.abs(inner_values))
    ax1.set_title('Magnitude of Inner Function')
    ax1.set_xlabel('θ')
    ax1.set_ylabel('|f(e^(iθ))|')
    
    ax2.plot(theta, outer_values)
    ax2.set_title('Outer Function')
    ax2.set_xlabel('θ')
    ax2.set_ylabel('f(e^(iθ))')
    
    plt.tight_layout()
    plt.show()

# Example: Inner function with a = 0.5, Outer function f(z) = 1 + z
a = 0.5
f = lambda z: 1 + z
plot_inner_outer(a, f)

Nevanlinna-Pick Interpolation

The Nevanlinna-Pick interpolation problem involves finding a bounded analytic function that takes specified values at given points in the unit disk.

import numpy as np
from scipy.linalg import solve

def nevanlinna_pick(points, values):
    n = len(points)
    A = np.zeros((n, n), dtype=complex)
    
    for i in range(n):
        for j in range(n):
            A[i, j] = (1 - values[i] * np.conj(values[j])) / (1 - points[i] * np.conj(points[j]))
    
    return np.all(np.linalg.eigvals(A) >= 0)

# Example: Interpolation problem
points = [0, 0.5, -0.5j]
values = [0, 0.5, 0.25]

solvable = nevanlinna_pick(points, values)
print(f"Is the interpolation problem solvable? {solvable}")

Hardy Spaces and Hp Norms

Hardy spaces Hp are spaces of analytic functions on the unit disk with bounded p-norms. H∞ is the space of bounded analytic functions.

def hp_norm(f, p, num_points=1000):
    theta = np.linspace(0, 2*np.pi, num_points)
    z = np.exp(1j * theta)
    
    if p == float('inf'):
        return np.max(np.abs(f(z)))
    else:
        return (np.mean(np.abs(f(z))**p)**(1/p))

# Example: Calculate H2 and H∞ norms for f(z) = z / (1 - z/2)
f = lambda z: z / (1 - z/2)

h2_norm = hp_norm(f, 2)
hinf_norm = hp_norm(f, float('inf'))

print(f"H2 norm: {h2_norm:.4f}")
print(f"H∞ norm: {hinf_norm:.4f}")

Conformal Mapping

Conformal mapping preserves angles and local shapes. It's a powerful tool for transforming bounded analytic functions between different domains.

def mobius_transform(z, a, b, c, d):
    return (a*z + b) / (c*z + d)

def plot_conformal_mapping(f, domain):
    x = np.linspace(domain[0], domain[1], 100)
    y = np.linspace(domain[2], domain[3], 100)
    X, Y = np.meshgrid(x, y)
    Z = X + 1j*Y
    
    W = f(Z)
    
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
    
    ax1.contour(X, Y, X, colors='blue', alpha=0.5)
    ax1.contour(X, Y, Y, colors='red', alpha=0.5)
    ax1.set_title('Original Domain')
    ax1.set_xlabel('Re(z)')
    ax1.set_ylabel('Im(z)')
    
    ax2.contour(W.real, W.imag, X, colors='blue', alpha=0.5)
    ax2.contour(W.real, W.imag, Y, colors='red', alpha=0.5)
    ax2.set_title('Mapped Domain')
    ax2.set_xlabel('Re(w)')
    ax2.set_ylabel('Im(w)')
    
    plt.tight_layout()
    plt.show()

# Example: Möbius transform mapping the unit disk to the upper half-plane
f = lambda z: 1j * (1 + z) / (1 - z)
domain = [-1, 1, -1, 1]

plot_conformal_mapping(f, domain)

Boundary Behavior

The boundary behavior of bounded analytic functions is crucial for understanding their properties and applications.

def plot_boundary_behavior(f, num_points=1000):
    theta = np.linspace(0, 2*np.pi, num_points)
    z = np.exp(1j * theta)
    
    values = f(z)
    
    plt.figure(figsize=(10, 8))
    plt.plot(values.real, values.imag)
    plt.title('Boundary Behavior of a Bounded Analytic Function')
    plt.xlabel('Re(f(e^(iθ)))')
    plt.ylabel('Im(f(e^(iθ)))')
    plt.axis('equal')
    plt.grid(True)
    plt.show()

# Example: f(z) = (z + 1) / (z - 1)
f = lambda z: (z + 1) / (z - 1)
plot_boundary_behavior(f)

Applications in Signal Processing

Bounded analytic functions have applications in signal processing, particularly in the design of digital filters and spectral factorization.

import numpy as np
import matplotlib.pyplot as plt
from scipy import signal

def design_lowpass_filter(cutoff, order):
    b, a = signal.butter(order, cutoff, btype='low', analog=False)
    w, h = signal.freqz(b, a)
    return w, h

def plot_filter_response(w, h):
    plt.figure(figsize=(10, 6))
    plt.plot(w / np.pi, np.abs(h))
    plt.title('Lowpass Filter Frequency Response')
    plt.xlabel('Normalized Frequency')
    plt.ylabel('Magnitude')
    plt.grid(True)
    plt.ylim(0, 1.1)
    plt.show()

# Example: Design a lowpass filter
cutoff = 0.3
order = 5
w, h = design_lowpass_filter(cutoff, order)
plot_filter_response(w, h)

Real-life Example: Image Compression

Bounded analytic functions play a role in image compression algorithms, particularly in techniques like singular value decomposition (SVD) for image approximation.

import numpy as np
import matplotlib.pyplot as plt

def compress_image(image, k):
    U, S, Vt = np.linalg.svd(image)
    compressed = np.dot(U[:, :k], np.dot(np.diag(S[:k]), Vt[:k, :]))
    return compressed

def plot_compressed_image(original, compressed):
    fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(15, 6))
    ax1.imshow(original, cmap='gray')
    ax1.set_title('Original Image')
    ax1.axis('off')
    ax2.imshow(compressed, cmap='gray')
    ax2.set_title('Compressed Image')
    ax2.axis('off')
    plt.show()

# Example usage (assuming 'image' is a 2D numpy array):
# compressed = compress_image(image, k=50)
# plot_compressed_image(image, compressed)

Real-life Example: Control Systems

Bounded analytic functions are crucial in control system theory, particularly in the design of stabilizing controllers for linear systems.

import control
import numpy as np
import matplotlib.pyplot as plt

def design_controller(system, desired_poles):
    K = control.place(system.A, system.B, desired_poles)
    return K

def plot_step_response(system, title):
    t, y = control.step_response(system)
    plt.figure(figsize=(10, 6))
    plt.plot(t, y)
    plt.title(title)
    plt.xlabel('Time')
    plt.ylabel('Output')
    plt.grid(True)
    plt.show()

# Example usage:
# A = np.array([[0, 1], [-2, -3]])
# B = np.array([[0], [1]])
# C = np.array([[1, 0]])
# D = np.array([[0]])
# 
# system = control.ss(A, B, C, D)
# desired_poles = [-1, -2]
# 
# K = design_controller(system, desired_poles)
# controlled_system = control.feedback(system, K)
# 
# plot_step_response(system, 'Original System')
# plot_step_response(controlled_system, 'Controlled System')

Additional Resources

For further exploration of bounded analytic functions and their applications, consider the following resources:

  1. "Bounded Analytic Functions" by John B. Garnett (Springer) ArXiv link: https://arxiv.org/abs/math/0007181
  2. "Theory of H^p Spaces" by Peter Duren (Academic Press) ArXiv link: https://arxiv.org/abs/1508.03491
  3. "Complex Analysis" by Elias M. Stein and Rami Shakarchi (Princeton University Press) ArXiv link: https://arxiv.org/abs/math/0104031

These resources provide in-depth coverage of bounded analytic functions and related topics in complex analysis. Remember to verify the availability and relevance of these resources, as ArXiv links may change over time.

More in Python for ML and AI

  1. Building a Powerful Spam Detector with Python6 min

    Spam detection is the process of identifying and filtering out unwanted and unsolicited messages, also known as spam. In this presentation, we will explore...

  2. Binary Quantization with Python8 min

    Binary quantization is a process of converting continuous or multi-level data into binary (0 or 1) representations. It's widely used in digital signal...

  3. Building a Python Language Translator9 min

    Language translation is a complex task that involves converting text from one language to another while preserving meaning and context. In Python, we can...

  4. Basic Knowledge Graph Tutorial with Python11 min

    Knowledge graphs are powerful tools for representing and organizing complex information. They consist of entities (nodes) and relationships (edges) that...

  5. Building a RMSprop Optimizer from Scratch in Python13 min

    RMSprop (Root Mean Square Propagation) is an adaptive learning rate optimization algorithm designed to address the diminishing learning rates in AdaGrad. It...

  6. An End-to-End Guide to Model Explainability in Python6 min

    Model explainability is crucial in machine learning, allowing us to understand and interpret the decisions made by complex models. This guide will explore...