Coverage for src / chebpy / fun.py: 100%
4 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-22 21:33 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-22 21:33 +0000
1"""Abstract base class for functions defined on arbitrary intervals.
3This module provides the Fun abstract base class, which defines the interface for
4all function representations on arbitrary intervals in ChebPy. It specifies the
5methods and properties that concrete function classes must implement.
7The Fun class serves as the foundation for the function class hierarchy in ChebPy,
8with concrete implementations like Bndfun and Classicfun inheriting from it.
9It defines a comprehensive interface for working with function representations,
10including algebraic operations, calculus operations, and utility functions.
11"""
13from abc import ABC, abstractmethod
14from typing import Any
16import numpy as np
19class Fun(ABC):
20 """Abstract base class for functions defined on arbitrary intervals.
22 This class defines the interface for all function representations on
23 arbitrary intervals. It serves as the base class for specific
24 implementations like Bndfun and Classicfun.
26 Concrete subclasses must implement all the abstract methods defined here,
27 which include constructors, algebraic operations, calculus operations,
28 and utility functions.
29 """
31 # --------------------------
32 # alternative constructors
33 # --------------------------
34 @classmethod
35 @abstractmethod
36 def initconst(cls, c: float, interval: Any) -> "Fun": # pragma: no cover
37 """Initialize a constant function.
39 This constructor creates a function that represents a constant value
40 on the specified interval.
42 Args:
43 c: The constant value.
44 interval: The interval on which to define the function.
46 Returns:
47 Fun: A new instance representing the constant function f(x) = c.
48 """
49 raise NotImplementedError
51 @classmethod
52 @abstractmethod
53 def initempty(cls) -> "Fun": # pragma: no cover
54 """Initialize an empty function.
56 This constructor creates an empty function representation, which is
57 useful as a placeholder or for special cases.
59 Returns:
60 Fun: A new empty instance.
61 """
62 raise NotImplementedError
64 @classmethod
65 @abstractmethod
66 def initfun_adaptive(cls, f: Any, interval: Any) -> "Fun": # pragma: no cover
67 """Initialize from a callable function using adaptive sampling.
69 This constructor determines the appropriate number of points needed to
70 represent the function to the specified tolerance using an adaptive algorithm.
72 Args:
73 f (callable): The function to be approximated.
74 interval: The interval on which to define the function.
76 Returns:
77 Fun: A new instance representing the function f.
78 """
79 raise NotImplementedError
81 @classmethod
82 @abstractmethod
83 def initfun_fixedlen(cls, f: Any, interval: Any, n: int) -> "Fun": # pragma: no cover
84 """Initialize from a callable function using a fixed number of points.
86 This constructor uses a specified number of points to represent the function,
87 rather than determining the number adaptively.
89 Args:
90 f (callable): The function to be approximated.
91 interval: The interval on which to define the function.
92 n (int): The number of points to use.
94 Returns:
95 Fun: A new instance representing the function f.
96 """
97 raise NotImplementedError
99 # -------------------
100 # "private" methods
101 # -------------------
102 @abstractmethod
103 def __add__(self, other: Any) -> "Fun": # pragma: no cover
104 """Add this function with another function or a scalar.
106 This method implements the addition operation between this function
107 and another function or a scalar.
109 Args:
110 other (Fun or scalar): The function or scalar to add to this function.
112 Returns:
113 Fun: A new function representing the sum.
114 """
115 raise NotImplementedError
117 @abstractmethod
118 def __call__(self, x: Any) -> Any: # pragma: no cover
119 """Evaluate the function at points x.
121 This method evaluates the function at the specified points.
123 Args:
124 x (float or array-like): Points at which to evaluate the function.
126 Returns:
127 float or array-like: The value(s) of the function at the specified point(s).
128 Returns a scalar if x is a scalar, otherwise an array of the same size as x.
129 """
130 raise NotImplementedError
132 @abstractmethod
133 def __init__(self) -> None: # pragma: no cover
134 """Initialize a new Fun instance.
136 This method initializes a new function representation on the specified interval.
137 The specific initialization depends on the concrete subclass implementation.
138 """
139 raise NotImplementedError
141 @abstractmethod
142 def __mul__(self, other: Any) -> "Fun": # pragma: no cover
143 """Multiply this function with another function or a scalar.
145 This method implements the multiplication operation between this function
146 and another function or a scalar.
148 Args:
149 other (Fun or scalar): The function or scalar to multiply with this function.
151 Returns:
152 Fun: A new function representing the product.
153 """
154 raise NotImplementedError
156 @abstractmethod
157 def __neg__(self) -> "Fun": # pragma: no cover
158 """Return the negative of this function.
160 This method implements the unary negation operation for this function.
162 Returns:
163 Fun: A new function representing -f(x).
164 """
165 raise NotImplementedError
167 @abstractmethod
168 def __pos__(self) -> "Fun": # pragma: no cover
169 """Return the positive of this function (which is the function itself).
171 This method implements the unary plus operation for this function.
173 Returns:
174 Fun: This function object (unchanged).
175 """
176 raise NotImplementedError
178 @abstractmethod
179 def __pow__(self, power: Any) -> "Fun": # pragma: no cover
180 """Raise this function to a power.
182 This method implements the power operation for this function.
184 Args:
185 power (int, float, or Fun): The exponent to which this function is raised.
187 Returns:
188 Fun: A new function representing f(x)^power.
189 """
190 raise NotImplementedError
192 @abstractmethod
193 def __radd__(self, other: Any) -> "Fun": # pragma: no cover
194 """Add a scalar or another function to this function (from the right).
196 This method is called when a scalar or another function is added to this function,
197 i.e., other + self.
199 Args:
200 other (scalar or Fun): The scalar or function to add to this function.
202 Returns:
203 Fun: A new function representing the sum.
204 """
205 raise NotImplementedError
207 @abstractmethod
208 def __repr__(self) -> str: # pragma: no cover
209 """Return a string representation of the function.
211 This method returns a string representation of the function that includes
212 relevant information about its representation and interval.
214 Returns:
215 str: A string representation of the function.
216 """
217 raise NotImplementedError
219 @abstractmethod
220 def __rmul__(self, other: Any) -> "Fun": # pragma: no cover
221 """Multiply a scalar or another function with this function (from the right).
223 This method is called when a scalar or another function is multiplied with this function,
224 i.e., other * self.
226 Args:
227 other (scalar or Fun): The scalar or function to multiply with this function.
229 Returns:
230 Fun: A new function representing the product.
231 """
232 raise NotImplementedError
234 @abstractmethod
235 def __rsub__(self, other: Any) -> "Fun": # pragma: no cover
236 """Subtract this function from a scalar or another function.
238 This method is called when this function is subtracted from a scalar or another function,
239 i.e., other - self.
241 Args:
242 other (scalar or Fun): The scalar or function from which to subtract this function.
244 Returns:
245 Fun: A new function representing the difference.
246 """
247 raise NotImplementedError
249 @abstractmethod
250 def __sub__(self, other: Any) -> "Fun": # pragma: no cover
251 """Subtract another function or a scalar from this function.
253 This method implements the subtraction operation between this function
254 and another function or a scalar.
256 Args:
257 other (Fun or scalar): The function or scalar to subtract from this function.
259 Returns:
260 Fun: A new function representing the difference.
261 """
262 raise NotImplementedError
264 # ------------
265 # properties
266 # ------------
267 @property
268 @abstractmethod
269 def coeffs(self) -> np.ndarray: # pragma: no cover
270 """Get the coefficients of the function representation.
272 This property returns the coefficients used in the function representation,
273 such as Chebyshev coefficients for a Chebyshev series.
275 Returns:
276 array-like: The coefficients of the function representation.
277 """
278 raise NotImplementedError
280 @property
281 @abstractmethod
282 def interval(self) -> Any: # pragma: no cover
283 """Get the interval on which this function is defined.
285 This property returns the interval object representing the domain
286 of definition for this function.
288 Returns:
289 Interval: The interval on which this function is defined.
290 """
291 raise NotImplementedError
293 @property
294 @abstractmethod
295 def isconst(self) -> bool: # pragma: no cover
296 """Check if this function represents a constant.
298 This property determines whether the function is constant (i.e., f(x) = c
299 for some constant c) over its interval of definition.
301 Returns:
302 bool: True if the function is constant, False otherwise.
303 """
304 raise NotImplementedError
306 @property
307 @abstractmethod
308 def isempty(self) -> bool: # pragma: no cover
309 """Check if this function is empty.
311 This property determines whether the function is empty, which is a special
312 state used as a placeholder or for special cases.
314 Returns:
315 bool: True if the function is empty, False otherwise.
316 """
317 raise NotImplementedError
319 @property
320 @abstractmethod
321 def iscomplex(self) -> bool: # pragma: no cover
322 """Check if this function has complex values.
324 This property determines whether the function has complex values or is
325 purely real-valued.
327 Returns:
328 bool: True if the function has complex values, False otherwise.
329 """
330 raise NotImplementedError
332 @property
333 @abstractmethod
334 def size(self) -> int: # pragma: no cover
335 """Get the size of the function representation.
337 This property returns the number of coefficients or other measure of the
338 complexity of the function representation.
340 Returns:
341 int: The size of the function representation.
342 """
343 raise NotImplementedError
345 @property
346 @abstractmethod
347 def support(self) -> np.ndarray: # pragma: no cover
348 """Get the support interval of this function.
350 This property returns the interval on which this function is defined,
351 represented as a numpy array with two elements [a, b].
353 Returns:
354 numpy.ndarray: Array containing the endpoints of the interval.
355 """
356 raise NotImplementedError
358 @property
359 @abstractmethod
360 def vscale(self) -> float: # pragma: no cover
361 """Get the vertical scale of the function.
363 This property returns a measure of the range of function values, typically
364 the maximum absolute value of the function on its interval of definition.
366 Returns:
367 float: The vertical scale of the function.
368 """
369 raise NotImplementedError
371 # -----------
372 # utilities
373 # -----------
374 @abstractmethod
375 def copy(self) -> "Fun": # pragma: no cover
376 """Create a deep copy of this function.
378 This method creates a new function that is a deep copy of this function,
379 ensuring that modifications to the copy do not affect the original.
381 Returns:
382 Fun: A new function that is a deep copy of this function.
383 """
384 raise NotImplementedError
386 @abstractmethod
387 def imag(self) -> "Fun": # pragma: no cover
388 """Get the imaginary part of this function.
390 This method returns a new function representing the imaginary part of this function.
391 If this function is real-valued, returns a zero function.
393 Returns:
394 Fun: A new function representing the imaginary part of this function.
395 """
396 raise NotImplementedError
398 @abstractmethod
399 def real(self) -> "Fun": # pragma: no cover
400 """Get the real part of this function.
402 This method returns a new function representing the real part of this function.
403 If this function is already real-valued, returns this function.
405 Returns:
406 Fun: A new function representing the real part of this function.
407 """
408 raise NotImplementedError
410 @abstractmethod
411 def restrict(self, subinterval: Any) -> "Fun": # pragma: no cover
412 """Restrict this function to a subinterval.
414 This method creates a new function that is the restriction of this function
415 to the specified subinterval.
417 Args:
418 subinterval (array-like): The subinterval to which this function should be restricted.
419 Must be contained within the original interval of definition.
421 Returns:
422 Fun: A new function representing the restriction of this function to the subinterval.
423 """
424 raise NotImplementedError
426 @abstractmethod
427 def simplify(self) -> "Fun": # pragma: no cover
428 """Simplify the function representation.
430 This method simplifies the function representation by removing unnecessary
431 coefficients or reducing the degree, while maintaining the specified accuracy.
433 Returns:
434 Fun: A new function with a simplified representation.
435 """
436 raise NotImplementedError
438 @abstractmethod
439 def values(self) -> np.ndarray: # pragma: no cover
440 """Get the values of the function at the points used for its representation.
442 This method returns the values of the function at the points used for its
443 representation, such as Chebyshev points.
445 Returns:
446 array-like: The values of the function at the representation points.
447 """
448 raise NotImplementedError
450 # -------------
451 # rootfinding
452 # -------------
453 @abstractmethod
454 def roots(self) -> np.ndarray: # pragma: no cover
455 """Find the roots (zeros) of the function on its interval of definition.
457 This method computes the points where the function equals zero
458 within its interval of definition.
460 Returns:
461 array-like: An array of the roots of the function in its interval of definition,
462 sorted in ascending order.
463 """
464 raise NotImplementedError
466 # ----------
467 # calculus
468 # ----------
469 @abstractmethod
470 def cumsum(self) -> "Fun": # pragma: no cover
471 """Compute the indefinite integral of the function.
473 This method calculates the indefinite integral (antiderivative) of the function,
474 with the constant of integration chosen so that the indefinite integral
475 evaluates to 0 at the left endpoint of the interval.
477 Returns:
478 Fun: A new function representing the indefinite integral of this function.
479 """
480 raise NotImplementedError
482 @abstractmethod
483 def diff(self) -> "Fun": # pragma: no cover
484 """Compute the derivative of the function.
486 This method calculates the derivative of the function with respect to x.
488 Returns:
489 Fun: A new function representing the derivative of this function.
490 """
491 raise NotImplementedError
493 @abstractmethod
494 def sum(self) -> float: # pragma: no cover
495 """Compute the definite integral of the function over its interval of definition.
497 This method calculates the definite integral of the function
498 over its interval of definition.
500 Returns:
501 float or complex: The definite integral of the function over its interval of definition.
502 """
503 raise NotImplementedError