Source code for hpfracc.algorithms.advanced_methods

"""
Advanced Fractional Calculus Methods

This module implements advanced fractional calculus methods with optimizations:
- Weyl derivative via FFT Convolution with parallelization
- Marchaud derivative with Difference Quotient convolution and memory optimization
- Hadamard derivative
- Reiz/Reiz-Feller derivative via spectral method
- Adomian Decomposition method

All methods include parallel processing and memory optimizations.

**Naming:** parallel-enhanced variants of Weyl / Marchaud / … are exposed as
``Parallel*`` classes. The legacy names ``OptimizedWeylDerivative``, etc., remain
as **aliases** of those classes (they are unrelated to ``OptimizedCaputo`` in
``hpfracc.algorithms.optimized_methods``, which aliases the unified RL/Caputo/GL engines).
"""

import warnings

import numpy as np
from typing import Union, Optional, Tuple, Callable, Dict, List
from concurrent.futures import ThreadPoolExecutor

from ..core.definitions import FractionalOrder
from ..special import gamma
from .optimized_methods import ParallelConfig


[docs] class WeylDerivative: """ Weyl fractional derivative via FFT Convolution with parallelization. The Weyl derivative is defined as: D^α f(x) = (1/Γ(n-α)) (d/dx)^n ∫_x^∞ (τ-x)^(n-α-1) f(τ) dτ This implementation uses FFT convolution for efficiency and parallel processing. """
[docs] def __init__( self, alpha: Union[float, FractionalOrder], parallel_config: Optional[ParallelConfig] = None, optimized: bool = True, **kwargs, ): """Initialize Weyl derivative calculator.""" if isinstance(alpha, (int, float)): self.alpha = FractionalOrder(alpha) else: self.alpha = alpha self.n = int(np.ceil(self.alpha.alpha)) self.alpha_val = self.alpha.alpha # accept alias 'config' if provided if parallel_config is None and 'config' in kwargs: parallel_config = kwargs.get('config') self.parallel_config = parallel_config or ParallelConfig() if not hasattr(self.parallel_config, 'n_jobs'): self.parallel_config.n_jobs = 1 self.optimized = optimized # expose for tests self.fractional_order = self.alpha
[docs] def compute( self, f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], h: Optional[float] = None, use_parallel: bool = True, ) -> Union[float, np.ndarray]: """Compute Weyl derivative using optimized FFT convolution.""" if isinstance(f, str): raise TypeError("f must be a callable or ndarray, not str") if callable(f) and isinstance(x, str): raise TypeError("x must be a numeric grid or scalar bound, not str") if callable(f): if hasattr(x, "__len__") and not isinstance(x, (str, bytes)): x_array = np.asarray(x, dtype=float) else: x_max = x if h is None: h = x_max / 1000 x_array = np.arange(0, x_max + h, h) f_array = np.array([f(xi) for xi in x_array], dtype=float) else: f_array = np.asarray(f, dtype=float) if isinstance(x, str): raise TypeError("x must be a numeric grid or scalar, not str") if hasattr(x, "__len__") and not isinstance(x, (str, bytes)): x_array = np.asarray(x, dtype=float) else: x_array = np.arange(len(f_array), dtype=float) * (h or 1.0) # Ensure arrays have the same length min_len = min(len(f_array), len(x_array)) f_array = f_array[:min_len] x_array = x_array[:min_len] if len(f_array) == 0: return np.array([]) if use_parallel and self.parallel_config.enabled: return self._compute_parallel(f_array, x_array, h or 1.0) else: return self._compute_serial(f_array, x_array, h or 1.0)
[docs] def _compute_serial( self, f: np.ndarray, x: np.ndarray, h: float) -> np.ndarray: """Serial computation using optimized FFT convolution.""" N = len(f) n = self.n alpha = self.alpha_val # Create Weyl kernel: (τ-x)^(n-α-1) / Γ(n-α) kernel = np.zeros(N) for i in range(N): if x[i] > 0: kernel[i] = (x[i] ** (n - alpha - 1)) / gamma(n - alpha) # Pad arrays for circular convolution f_padded = np.pad(f, (0, N), mode="constant") kernel_padded = np.pad(kernel, (0, N), mode="constant") # FFT convolution f_fft = np.fft.fft(f_padded) kernel_fft = np.fft.fft(kernel_padded) conv_fft = f_fft * kernel_fft conv = np.real(np.fft.ifft(conv_fft)) # Apply nth derivative using finite differences result = np.zeros(N) result[:n] = 0.0 for i in range(n, N): if n == 1: if i < N - 1: result[i] = (conv[i + 1] - conv[i - 1]) / (2 * h) else: result[i] = (conv[i] - conv[i - 1]) / h else: if i < N - 1: result[i] = (conv[i + 1] - 2 * conv[i] + conv[i - 1]) / (h**2) else: result[i] = (conv[i] - conv[i - 1]) / h return result * h
[docs] def _compute_parallel( self, f: np.ndarray, x: np.ndarray, h: float) -> np.ndarray: """Parallel computation using chunked processing.""" N = len(f) # Handle empty arrays if N == 0: return np.array([]) chunk_size = max(1, N // self.parallel_config.n_jobs) chunks = [ (f[i: i + chunk_size], x[i: i + chunk_size], h) for i in range(0, N, chunk_size) ] with ThreadPoolExecutor(max_workers=self.parallel_config.n_jobs) as executor: results = list(executor.map(self._process_chunk, chunks)) return np.concatenate(results)
[docs] def _process_chunk( self, chunk_data: Tuple[np.ndarray, np.ndarray, float] ) -> np.ndarray: """Process a chunk of data for parallel computation.""" f_chunk, x_chunk, h = chunk_data return self._compute_serial(f_chunk, x_chunk, h)
[docs] class MarchaudDerivative: """ Marchaud fractional derivative with Difference Quotient convolution and memory optimization. The Marchaud derivative is defined as: D^α f(x) = α/Γ(1-α) ∫_0^∞ [f(x) - f(x-τ)] / τ^(α+1) dτ This implementation uses difference quotient convolution with memory optimization. """
[docs] def __init__( self, alpha: Union[float, FractionalOrder], parallel_config: Optional[ParallelConfig] = None, **kwargs, ): """Initialize Marchaud derivative calculator.""" if isinstance(alpha, (int, float)): self.alpha = FractionalOrder(alpha) else: self.alpha = alpha self.alpha_val = self.alpha.alpha if parallel_config is None and 'config' in kwargs: parallel_config = kwargs.get('config') self.parallel_config = parallel_config or ParallelConfig() if not hasattr(self.parallel_config, 'n_jobs'): self.parallel_config.n_jobs = 1 self.fractional_order = self.alpha # Precompute constants self.coeff = self.alpha_val / gamma(1 - self.alpha_val)
[docs] def compute( self, f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], h: Optional[float] = None, use_parallel: bool = True, memory_optimized: bool = True, ) -> Union[float, np.ndarray]: """Compute Marchaud derivative with memory optimization.""" if callable(f): if hasattr(x, "__len__"): x_array = x else: x_max = x if h is None: h = x_max / 1000 x_array = np.arange(0, x_max + h, h) f_array = np.array([f(xi) for xi in x_array]) else: f_array = f if hasattr(x, "__len__"): x_array = x else: x_array = np.arange(len(f)) * (h or 1.0) # Ensure arrays have the same length min_len = min(len(f_array), len(x_array)) f_array = f_array[:min_len] x_array = x_array[:min_len] if memory_optimized: return self._compute_memory_optimized( f_array, x_array, h or 1.0, use_parallel ) else: return self._compute_standard( f_array, x_array, h or 1.0, use_parallel)
[docs] def _compute_memory_optimized( self, f: np.ndarray, x: np.ndarray, h: float, use_parallel: bool ) -> np.ndarray: """Memory-optimized computation using streaming approach.""" N = len(f) # Handle empty arrays if N == 0: return np.array([]) result = np.zeros(N) # Use smaller chunks to reduce memory usage chunk_size = max(1, min(1000, N // 4)) if use_parallel and self.parallel_config.enabled: chunks = [(f, x, h, i, min(i + chunk_size, N)) for i in range(0, N, chunk_size)] with ThreadPoolExecutor( max_workers=self.parallel_config.n_jobs ) as executor: chunk_results = list(executor.map( self._process_marchaud_chunk, chunks)) for i, chunk_result in enumerate(chunk_results): start_idx = i * chunk_size end_idx = min(start_idx + chunk_size, N) result[start_idx:end_idx] = chunk_result else: for i in range(0, N, chunk_size): end_idx = min(i + chunk_size, N) result[i:end_idx] = self._compute_marchaud_segment( f, x, h, i, end_idx) return result
[docs] def _process_marchaud_chunk( self, chunk_data: Tuple[np.ndarray, np.ndarray, float, int, int] ) -> np.ndarray: """Process a chunk for Marchaud derivative.""" f, x, h, start_idx, end_idx = chunk_data return self._compute_marchaud_segment(f, x, h, start_idx, end_idx)
[docs] def _compute_marchaud_segment( self, f: np.ndarray, x: np.ndarray, h: float, start_idx: int, end_idx: int) -> np.ndarray: """Compute Marchaud derivative for a segment.""" result = np.zeros(end_idx - start_idx) for i in range(start_idx, end_idx): if i == 0: result[i - start_idx] = 0.0 continue # Compute difference quotient integral integral = 0.0 max_tau = min(i, 1000) # Limit integration range for efficiency for j in range(1, max_tau + 1): tau = j * h if i - j >= 0: diff = f[i] - f[i - j] integral += diff / (tau ** (self.alpha_val + 1)) result[i - start_idx] = self.coeff * integral * h return result
[docs] def _compute_standard( self, f: np.ndarray, x: np.ndarray, h: float, use_parallel: bool ) -> np.ndarray: """Standard computation without memory optimization.""" N = len(f) result = np.zeros(N) for i in range(N): if i == 0: result[i] = 0.0 continue integral = 0.0 for j in range(1, i + 1): tau = j * h diff = f[i] - f[i - j] integral += diff / (tau ** (self.alpha_val + 1)) result[i] = self.coeff * integral * h return result
[docs] class HadamardDerivative: """ Hadamard fractional derivative. The Hadamard derivative is defined as: D^α f(x) = (1/Γ(n-α)) (x d/dx)^n ∫_1^x (log(x/t))^(n-α-1) f(t) dt/t This implementation uses logarithmic transformation and efficient quadrature. """
[docs] def __init__( self, alpha: Union[float, FractionalOrder, None] = None, order: Union[float, FractionalOrder, None] = None, **kwargs, ): """Initialize Hadamard derivative calculator. Accepts both `alpha` and `order` for backward compatibility. """ # Prefer explicit alpha if provided; otherwise fall back to order provided = alpha if alpha is not None else order if provided is None: raise TypeError( "HadamardDerivative requires `alpha` or `order` parameter") if isinstance(provided, (int, float)): self.alpha = FractionalOrder(provided) else: self.alpha = provided self.n = int(np.ceil(self.alpha.alpha)) self.alpha_val = self.alpha.alpha self.fractional_order = self.alpha
[docs] def compute( self, f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], h: Optional[float] = None, ) -> Union[float, np.ndarray]: """Compute Hadamard derivative using logarithmic transformation.""" if callable(f): if hasattr(x, "__len__"): x_array = x else: x_max = x if h is None: h = x_max / 1000 # Start from 1 for Hadamard x_array = np.arange(1, x_max + h, h) f_array = np.array([f(xi) for xi in x_array]) else: f_array = f if hasattr(x, "__len__"): x_array = x else: x_array = np.arange(1, len(f) + 1) * (h or 1.0) # Ensure arrays have the same length min_len = min(len(f_array), len(x_array)) f_array = f_array[:min_len] x_array = x_array[:min_len] return self._compute_hadamard(f_array, x_array, h or 1.0)
[docs] def _compute_hadamard( self, f: np.ndarray, x: np.ndarray, h: float) -> np.ndarray: """Compute Hadamard derivative using logarithmic transformation.""" N = len(f) result = np.zeros(N) for i in range(N): if i < self.n: result[i] = 0.0 continue # Transform to logarithmic coordinates log_x = np.log(x[i]) # Compute integral part integral = 0.0 for j in range(i): if x[j] <= 0: continue # Skip zero or negative values log_t = np.log(x[j]) log_kernel = (log_x - log_t) ** (self.n - self.alpha_val - 1) integral += f[j] * log_kernel / x[j] # Apply differential operator (x d/dx)^n if self.n == 1: result[i] = x[i] * integral / gamma(self.n - self.alpha_val) else: # Higher derivatives using recursive application temp = integral / gamma(self.n - self.alpha_val) for k in range(self.n): temp = x[i] * np.gradient(temp, x[i]) result[i] = temp return result * h
[docs] class ReizFellerDerivative: """ Reiz-Feller fractional derivative via spectral method. The Reiz-Feller derivative is defined as: :math:`D^{\\alpha} f(x) = \\frac{1}{2\\pi} \\int_{-\\infty}^{\\infty} |\\xi|^{\\alpha} \\, \\mathcal{F}[f](\\xi) \\, e^{i\\xi x} \\, d\\xi` This implementation uses FFT for spectral computation. """
[docs] def __init__( self, alpha: Union[float, FractionalOrder], parallel_config: Optional[ParallelConfig] = None, **kwargs, ): """Initialize Reiz-Feller derivative calculator.""" if isinstance(alpha, (int, float)): self.alpha = FractionalOrder(alpha) else: self.alpha = alpha self.alpha_val = self.alpha.alpha if parallel_config is None and 'config' in kwargs: parallel_config = kwargs.get('config') self.parallel_config = parallel_config or ParallelConfig() if not hasattr(self.parallel_config, 'n_jobs'): self.parallel_config.n_jobs = 1 self.fractional_order = self.alpha
[docs] def compute( self, f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], h: Optional[float] = None, use_parallel: bool = True, ) -> Union[float, np.ndarray]: """Compute Reiz-Feller derivative using spectral method.""" if callable(f): if hasattr(x, "__len__"): x_array = x else: x_max = x if h is None: h = x_max / 1000 x_array = np.arange(-x_max, x_max + h, h) f_array = np.array([f(xi) for xi in x_array]) else: f_array = f if hasattr(x, "__len__"): x_array = x else: x_array = np.arange(len(f)) * (h or 1.0) # Ensure arrays have the same length min_len = min(len(f_array), len(x_array)) f_array = f_array[:min_len] x_array = x_array[:min_len] return self._compute_spectral(f_array, x_array, h or 1.0, use_parallel)
[docs] def _compute_spectral( self, f: np.ndarray, x: np.ndarray, h: float, use_parallel: bool ) -> np.ndarray: """Compute using spectral method with FFT.""" N = len(f) original_len = N # Ensure N is even for FFT if N % 2 == 1: N += 1 f = np.pad(f, (0, 1), mode="edge") x = np.pad(x, (0, 1), mode="edge") # Compute FFT f_fft = np.fft.fft(f) # Create frequency array freq = np.fft.fftfreq(N, h) # Apply spectral filter |ξ|^α spectral_filter = np.abs(freq) ** self.alpha_val spectral_filter[0] = 0 # Handle zero frequency # Apply filter in frequency domain filtered_fft = f_fft * spectral_filter # Inverse FFT result = np.real(np.fft.ifft(filtered_fft)) return result[:original_len] # Return original length
[docs] class AdomianDecomposition: """ Adomian Decomposition Method for solving fractional differential equations. This method decomposes the solution into a series and computes each term using the Adomian polynomials. """
[docs] def __init__( self, alpha: Union[float, FractionalOrder], parallel_config: Optional[ParallelConfig] = None, **kwargs, ): """Initialize Adomian Decomposition solver.""" if isinstance(alpha, (int, float)): self.alpha = FractionalOrder(alpha) else: self.alpha = alpha self.alpha_val = self.alpha.alpha if parallel_config is None and 'config' in kwargs: parallel_config = kwargs.get('config') self.parallel_config = parallel_config or ParallelConfig() if not hasattr(self.parallel_config, 'n_jobs'): self.parallel_config.n_jobs = 1 self.fractional_order = self.alpha
[docs] def solve( self, equation: Callable, initial_conditions: Dict, t_span: Tuple[float, float], n_steps: int = 1000, n_terms: int = 10, use_parallel: bool = True, ) -> Tuple[np.ndarray, np.ndarray]: """ Solve fractional differential equation using Adomian decomposition. Args: equation: Function representing the FDE initial_conditions: Dictionary of initial conditions t_span: Time span (t0, tf) n_steps: Number of time steps n_terms: Number of terms in the decomposition use_parallel: Whether to use parallel processing Returns: Tuple of (time_points, solution) """ t0, tf = t_span t = np.linspace(t0, tf, n_steps) h = (tf - t0) / (n_steps - 1) # Initialize solution series solution = np.zeros(n_steps) # Add initial condition if 0 in initial_conditions: solution += initial_conditions[0] # Compute decomposition terms if use_parallel and self.parallel_config.enabled: terms = self._compute_terms_parallel(equation, t, h, n_terms) else: terms = self._compute_terms_serial(equation, t, h, n_terms) # Sum all terms for term in terms: solution += term return t, solution
[docs] def _compute_terms_serial( self, equation: Callable, t: np.ndarray, h: float, n_terms: int ) -> List[np.ndarray]: """Compute decomposition terms serially.""" terms = [] for n in range(1, n_terms + 1): # Compute Adomian polynomial using the same time array adomian = self._compute_adomian_polynomial(equation, terms, n, t) # Compute integral term integral_term = self._compute_integral_term(adomian, t, h) terms.append(integral_term) return terms
[docs] def _compute_terms_parallel( self, equation: Callable, t: np.ndarray, h: float, n_terms: int ) -> List[np.ndarray]: """Compute decomposition terms in parallel.""" term_indices = list(range(1, n_terms + 1)) with ThreadPoolExecutor(max_workers=self.parallel_config.n_jobs) as executor: futures = [] for n in term_indices: future = executor.submit( self._compute_single_term, equation, t, h, n) futures.append(future) terms = [future.result() for future in futures] return terms
[docs] def _compute_adomian_polynomial(self, equation: Callable, previous_terms: List[np.ndarray], n: int, t: np.ndarray) -> np.ndarray: """Compute the nth Adomian polynomial.""" N = len(t) adomian = np.zeros(N) for i in range(N): # Simplified polynomial computation using the provided time array adomian[i] = equation(t[i], 0) * (t[i] ** n) / gamma(n + 1) return adomian
[docs] def _compute_integral_term( self, adomian: np.ndarray, t: np.ndarray, h: float ) -> np.ndarray: """Compute integral term using fractional integration.""" N = len(adomian) result = np.zeros(N) for i in range(N): integral = 0.0 for j in range(i + 1): # Avoid division by zero and negative powers if t[i] > t[j] and self.alpha_val > 0: kernel = ((t[i] - t[j]) ** (self.alpha_val - 1) ) / gamma(self.alpha_val) integral += adomian[j] * kernel result[i] = integral * h return result
[docs] def _compute_single_term( self, equation: Callable, t: np.ndarray, h: float, n: int ) -> np.ndarray: """Compute a single decomposition term.""" # This is a simplified implementation # In practice, you would need the previous terms to compute the Adomian # polynomial adomian = np.zeros_like(t) # For demonstration, use a simple approximation for i in range(len(t)): adomian[i] = equation(t[i], 0) * (t[i] ** n) / gamma(n + 1) return self._compute_integral_term(adomian, t, h)
# Convenience functions for easy access
[docs] def weyl_derivative( f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], alpha: float, h: Optional[float] = None, **kwargs, ) -> Union[float, np.ndarray]: """Convenience function for Weyl derivative.""" calculator = WeylDerivative(alpha, **kwargs) return calculator.compute(f, x, h)
[docs] def marchaud_derivative( f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], alpha: float, h: Optional[float] = None, **kwargs, ) -> Union[float, np.ndarray]: """Convenience function for Marchaud derivative.""" calculator = MarchaudDerivative(alpha, **kwargs) return calculator.compute(f, x, h)
[docs] def hadamard_derivative( f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], alpha: float, h: Optional[float] = None, **kwargs, ) -> Union[float, np.ndarray]: """Convenience function for Hadamard derivative.""" calculator = HadamardDerivative(alpha, **kwargs) return calculator.compute(f, x, h)
[docs] def reiz_feller_derivative( f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], alpha: float, h: Optional[float] = None, **kwargs, ) -> Union[float, np.ndarray]: """Convenience function for Reiz-Feller derivative.""" calculator = ReizFellerDerivative(alpha, **kwargs) return calculator.compute(f, x, h)
# ============================================================================= # Parallel-enhanced variants (preferred names) # =============================================================================
[docs] class ParallelWeylDerivative(WeylDerivative): """Weyl derivative with ``optimized=True`` defaults (parallel / FFT path)."""
[docs] def __init__(self, order: Union[float, FractionalOrder], **kwargs): super().__init__(order, optimized=True, **kwargs)
[docs] class ParallelMarchaudDerivative(MarchaudDerivative): """Marchaud derivative exposed under the parallel-enhanced naming scheme."""
[docs] def __init__(self, order: Union[float, FractionalOrder], **kwargs): super().__init__(order, **kwargs)
[docs] class ParallelHadamardDerivative(HadamardDerivative): """Hadamard derivative under the parallel-enhanced naming scheme."""
[docs] def __init__(self, order: Union[float, FractionalOrder], **kwargs): super().__init__(order, **kwargs)
[docs] class ParallelReizFellerDerivative(ReizFellerDerivative): """Reiz–Feller derivative under the parallel-enhanced naming scheme."""
[docs] def __init__(self, order: Union[float, FractionalOrder], **kwargs): super().__init__(order, **kwargs)
[docs] class ParallelAdomianDecomposition(AdomianDecomposition): """Adomian decomposition under the parallel-enhanced naming scheme."""
[docs] def __init__(self, order: Union[float, FractionalOrder], **kwargs): super().__init__(order, **kwargs)
# Deprecated aliases (avoid clashing semantically with ``optimized_methods.Optimized*``) OptimizedWeylDerivative = ParallelWeylDerivative OptimizedMarchaudDerivative = ParallelMarchaudDerivative OptimizedHadamardDerivative = ParallelHadamardDerivative OptimizedReizFellerDerivative = ParallelReizFellerDerivative OptimizedAdomianDecomposition = ParallelAdomianDecomposition # ============================================================================= # Convenience functions (prefer ``parallel_*``) # =============================================================================
[docs] def parallel_weyl_derivative( f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], alpha: Union[float, FractionalOrder], h: Optional[float] = None, **kwargs, ) -> Union[float, np.ndarray]: calculator = ParallelWeylDerivative(alpha, **kwargs) return calculator.compute(f, x, h)
[docs] def parallel_marchaud_derivative( f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], alpha: Union[float, FractionalOrder], h: Optional[float] = None, **kwargs, ) -> Union[float, np.ndarray]: calculator = ParallelMarchaudDerivative(alpha, **kwargs) return calculator.compute(f, x, h)
[docs] def parallel_hadamard_derivative( f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], alpha: Union[float, FractionalOrder], h: Optional[float] = None, **kwargs, ) -> Union[float, np.ndarray]: calculator = ParallelHadamardDerivative(alpha, **kwargs) return calculator.compute(f, x, h)
[docs] def parallel_reiz_feller_derivative( f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], alpha: Union[float, FractionalOrder], h: Optional[float] = None, **kwargs, ) -> Union[float, np.ndarray]: calculator = ParallelReizFellerDerivative(alpha, **kwargs) return calculator.compute(f, x, h)
[docs] def optimized_weyl_derivative( f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], alpha: Union[float, FractionalOrder], h: Optional[float] = None, **kwargs, ) -> Union[float, np.ndarray]: warnings.warn( "optimized_weyl_derivative is deprecated; use parallel_weyl_derivative", DeprecationWarning, stacklevel=2, ) return parallel_weyl_derivative(f, x, alpha, h, **kwargs)
[docs] def optimized_marchaud_derivative( f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], alpha: Union[float, FractionalOrder], h: Optional[float] = None, **kwargs, ) -> Union[float, np.ndarray]: warnings.warn( "optimized_marchaud_derivative is deprecated; use parallel_marchaud_derivative", DeprecationWarning, stacklevel=2, ) return parallel_marchaud_derivative(f, x, alpha, h, **kwargs)
[docs] def optimized_hadamard_derivative( f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], alpha: Union[float, FractionalOrder], h: Optional[float] = None, **kwargs, ) -> Union[float, np.ndarray]: warnings.warn( "optimized_hadamard_derivative is deprecated; use parallel_hadamard_derivative", DeprecationWarning, stacklevel=2, ) return parallel_hadamard_derivative(f, x, alpha, h, **kwargs)
[docs] def optimized_reiz_feller_derivative( f: Union[Callable, np.ndarray], x: Union[float, np.ndarray], alpha: Union[float, FractionalOrder], h: Optional[float] = None, **kwargs, ) -> Union[float, np.ndarray]: warnings.warn( "optimized_reiz_feller_derivative is deprecated; use parallel_reiz_feller_derivative", DeprecationWarning, stacklevel=2, ) return parallel_reiz_feller_derivative(f, x, alpha, h, **kwargs)