Coverage for chebpy/core/fun.py: 100%
2 statements
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-07 10:30 +0000
« prev ^ index » next coverage.py v7.10.2, created at 2025-08-07 10:30 +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
16class Fun(ABC):
17 """Abstract base class for functions defined on arbitrary intervals.
19 This class defines the interface for all function representations on
20 arbitrary intervals. It serves as the base class for specific
21 implementations like Bndfun and Classicfun.
23 Concrete subclasses must implement all the abstract methods defined here,
24 which include constructors, algebraic operations, calculus operations,
25 and utility functions.
26 """
28 # --------------------------
29 # alternative constructors
30 # --------------------------
31 @classmethod
32 @abstractmethod
33 def initconst(cls): # pragma: no cover
34 """Initialize a constant function.
36 This constructor creates a function that represents a constant value
37 on the specified interval.
39 Args:
40 c: The constant value.
41 interval: The interval on which to define the function.
43 Returns:
44 Fun: A new instance representing the constant function f(x) = c.
45 """
46 raise NotImplementedError
48 @classmethod
49 @abstractmethod
50 def initempty(cls): # pragma: no cover
51 """Initialize an empty function.
53 This constructor creates an empty function representation, which is
54 useful as a placeholder or for special cases.
56 Returns:
57 Fun: A new empty instance.
58 """
59 raise NotImplementedError
61 @classmethod
62 @abstractmethod
63 def initfun_adaptive(cls): # pragma: no cover
64 """Initialize from a callable function using adaptive sampling.
66 This constructor determines the appropriate number of points needed to
67 represent the function to the specified tolerance using an adaptive algorithm.
69 Args:
70 f (callable): The function to be approximated.
71 interval: The interval on which to define the function.
73 Returns:
74 Fun: A new instance representing the function f.
75 """
76 raise NotImplementedError
78 @classmethod
79 @abstractmethod
80 def initfun_fixedlen(cls): # pragma: no cover
81 """Initialize from a callable function using a fixed number of points.
83 This constructor uses a specified number of points to represent the function,
84 rather than determining the number adaptively.
86 Args:
87 f (callable): The function to be approximated.
88 interval: The interval on which to define the function.
89 n (int): The number of points to use.
91 Returns:
92 Fun: A new instance representing the function f.
93 """
94 raise NotImplementedError
96 # -------------------
97 # "private" methods
98 # -------------------
99 @abstractmethod
100 def __add__(self, other): # pragma: no cover
101 """Add this function with another function or a scalar.
103 This method implements the addition operation between this function
104 and another function or a scalar.
106 Args:
107 other (Fun or scalar): The function or scalar to add to this function.
109 Returns:
110 Fun: A new function representing the sum.
111 """
112 raise NotImplementedError
114 @abstractmethod
115 def __call__(self, x): # pragma: no cover
116 """Evaluate the function at points x.
118 This method evaluates the function at the specified points.
120 Args:
121 x (float or array-like): Points at which to evaluate the function.
123 Returns:
124 float or array-like: The value(s) of the function at the specified point(s).
125 Returns a scalar if x is a scalar, otherwise an array of the same size as x.
126 """
127 raise NotImplementedError
129 @abstractmethod
130 def __init__(self): # pragma: no cover
131 """Initialize a new Fun instance.
133 This method initializes a new function representation on the specified interval.
134 The specific initialization depends on the concrete subclass implementation.
135 """
136 raise NotImplementedError
138 @abstractmethod
139 def __mul__(self, other): # pragma: no cover
140 """Multiply this function with another function or a scalar.
142 This method implements the multiplication operation between this function
143 and another function or a scalar.
145 Args:
146 other (Fun or scalar): The function or scalar to multiply with this function.
148 Returns:
149 Fun: A new function representing the product.
150 """
151 raise NotImplementedError
153 @abstractmethod
154 def __neg__(self): # pragma: no cover
155 """Return the negative of this function.
157 This method implements the unary negation operation for this function.
159 Returns:
160 Fun: A new function representing -f(x).
161 """
162 raise NotImplementedError
164 @abstractmethod
165 def __pos__(self): # pragma: no cover
166 """Return the positive of this function (which is the function itself).
168 This method implements the unary plus operation for this function.
170 Returns:
171 Fun: This function object (unchanged).
172 """
173 raise NotImplementedError
175 @abstractmethod
176 def __pow__(self, power): # pragma: no cover
177 """Raise this function to a power.
179 This method implements the power operation for this function.
181 Args:
182 power (int, float, or Fun): The exponent to which this function is raised.
184 Returns:
185 Fun: A new function representing f(x)^power.
186 """
187 raise NotImplementedError
189 @abstractmethod
190 def __radd__(self, other): # pragma: no cover
191 """Add a scalar or another function to this function (from the right).
193 This method is called when a scalar or another function is added to this function,
194 i.e., other + self.
196 Args:
197 other (scalar or Fun): The scalar or function to add to this function.
199 Returns:
200 Fun: A new function representing the sum.
201 """
202 raise NotImplementedError
204 @abstractmethod
205 def __repr__(self): # pragma: no cover
206 """Return a string representation of the function.
208 This method returns a string representation of the function that includes
209 relevant information about its representation and interval.
211 Returns:
212 str: A string representation of the function.
213 """
214 raise NotImplementedError
216 @abstractmethod
217 def __rmul__(self, other): # pragma: no cover
218 """Multiply a scalar or another function with this function (from the right).
220 This method is called when a scalar or another function is multiplied with this function,
221 i.e., other * self.
223 Args:
224 other (scalar or Fun): The scalar or function to multiply with this function.
226 Returns:
227 Fun: A new function representing the product.
228 """
229 raise NotImplementedError
231 @abstractmethod
232 def __rsub__(self, other): # pragma: no cover
233 """Subtract this function from a scalar or another function.
235 This method is called when this function is subtracted from a scalar or another function,
236 i.e., other - self.
238 Args:
239 other (scalar or Fun): The scalar or function from which to subtract this function.
241 Returns:
242 Fun: A new function representing the difference.
243 """
244 raise NotImplementedError
246 @abstractmethod
247 def __sub__(self, other): # pragma: no cover
248 """Subtract another function or a scalar from this function.
250 This method implements the subtraction operation between this function
251 and another function or a scalar.
253 Args:
254 other (Fun or scalar): The function or scalar to subtract from this function.
256 Returns:
257 Fun: A new function representing the difference.
258 """
259 raise NotImplementedError
261 # ------------
262 # properties
263 # ------------
264 @property
265 @abstractmethod
266 def coeffs(self): # pragma: no cover
267 """Get the coefficients of the function representation.
269 This property returns the coefficients used in the function representation,
270 such as Chebyshev coefficients for a Chebyshev series.
272 Returns:
273 array-like: The coefficients of the function representation.
274 """
275 raise NotImplementedError
277 @property
278 @abstractmethod
279 def interval(self): # pragma: no cover
280 """Get the interval on which this function is defined.
282 This property returns the interval object representing the domain
283 of definition for this function.
285 Returns:
286 Interval: The interval on which this function is defined.
287 """
288 raise NotImplementedError
290 @property
291 @abstractmethod
292 def isconst(self): # pragma: no cover
293 """Check if this function represents a constant.
295 This property determines whether the function is constant (i.e., f(x) = c
296 for some constant c) over its interval of definition.
298 Returns:
299 bool: True if the function is constant, False otherwise.
300 """
301 raise NotImplementedError
303 @property
304 @abstractmethod
305 def isempty(self): # pragma: no cover
306 """Check if this function is empty.
308 This property determines whether the function is empty, which is a special
309 state used as a placeholder or for special cases.
311 Returns:
312 bool: True if the function is empty, False otherwise.
313 """
314 raise NotImplementedError
316 @property
317 @abstractmethod
318 def iscomplex(self): # pragma: no cover
319 """Check if this function has complex values.
321 This property determines whether the function has complex values or is
322 purely real-valued.
324 Returns:
325 bool: True if the function has complex values, False otherwise.
326 """
327 raise NotImplementedError
329 @property
330 @abstractmethod
331 def size(self): # pragma: no cover
332 """Get the size of the function representation.
334 This property returns the number of coefficients or other measure of the
335 complexity of the function representation.
337 Returns:
338 int: The size of the function representation.
339 """
340 raise NotImplementedError
342 @property
343 @abstractmethod
344 def support(self): # pragma: no cover
345 """Get the support interval of this function.
347 This property returns the interval on which this function is defined,
348 represented as a numpy array with two elements [a, b].
350 Returns:
351 numpy.ndarray: Array containing the endpoints of the interval.
352 """
353 raise NotImplementedError
355 @property
356 @abstractmethod
357 def vscale(self): # pragma: no cover
358 """Get the vertical scale of the function.
360 This property returns a measure of the range of function values, typically
361 the maximum absolute value of the function on its interval of definition.
363 Returns:
364 float: The vertical scale of the function.
365 """
366 raise NotImplementedError
368 # -----------
369 # utilities
370 # -----------
371 @abstractmethod
372 def copy(self): # pragma: no cover
373 """Create a deep copy of this function.
375 This method creates a new function that is a deep copy of this function,
376 ensuring that modifications to the copy do not affect the original.
378 Returns:
379 Fun: A new function that is a deep copy of this function.
380 """
381 raise NotImplementedError
383 @abstractmethod
384 def imag(self): # pragma: no cover
385 """Get the imaginary part of this function.
387 This method returns a new function representing the imaginary part of this function.
388 If this function is real-valued, returns a zero function.
390 Returns:
391 Fun: A new function representing the imaginary part of this function.
392 """
393 raise NotImplementedError
395 @abstractmethod
396 def real(self): # pragma: no cover
397 """Get the real part of this function.
399 This method returns a new function representing the real part of this function.
400 If this function is already real-valued, returns this function.
402 Returns:
403 Fun: A new function representing the real part of this function.
404 """
405 raise NotImplementedError
407 @abstractmethod
408 def restrict(self, subinterval): # pragma: no cover
409 """Restrict this function to a subinterval.
411 This method creates a new function that is the restriction of this function
412 to the specified subinterval.
414 Args:
415 subinterval (array-like): The subinterval to which this function should be restricted.
416 Must be contained within the original interval of definition.
418 Returns:
419 Fun: A new function representing the restriction of this function to the subinterval.
420 """
421 raise NotImplementedError
423 @abstractmethod
424 def simplify(self): # pragma: no cover
425 """Simplify the function representation.
427 This method simplifies the function representation by removing unnecessary
428 coefficients or reducing the degree, while maintaining the specified accuracy.
430 Returns:
431 Fun: A new function with a simplified representation.
432 """
433 raise NotImplementedError
435 @abstractmethod
436 def values(self): # pragma: no cover
437 """Get the values of the function at the points used for its representation.
439 This method returns the values of the function at the points used for its
440 representation, such as Chebyshev points.
442 Returns:
443 array-like: The values of the function at the representation points.
444 """
445 raise NotImplementedError
447 # -------------
448 # rootfinding
449 # -------------
450 @abstractmethod
451 def roots(self): # pragma: no cover
452 """Find the roots (zeros) of the function on its interval of definition.
454 This method computes the points where the function equals zero
455 within its interval of definition.
457 Returns:
458 array-like: An array of the roots of the function in its interval of definition,
459 sorted in ascending order.
460 """
461 raise NotImplementedError
463 # ----------
464 # calculus
465 # ----------
466 @abstractmethod
467 def cumsum(self): # pragma: no cover
468 """Compute the indefinite integral of the function.
470 This method calculates the indefinite integral (antiderivative) of the function,
471 with the constant of integration chosen so that the indefinite integral
472 evaluates to 0 at the left endpoint of the interval.
474 Returns:
475 Fun: A new function representing the indefinite integral of this function.
476 """
477 raise NotImplementedError
479 @abstractmethod
480 def diff(self): # pragma: no cover
481 """Compute the derivative of the function.
483 This method calculates the derivative of the function with respect to x.
485 Returns:
486 Fun: A new function representing the derivative of this function.
487 """
488 raise NotImplementedError
490 @abstractmethod
491 def sum(self): # pragma: no cover
492 """Compute the definite integral of the function over its interval of definition.
494 This method calculates the definite integral of the function
495 over its interval of definition.
497 Returns:
498 float or complex: The definite integral of the function over its interval of definition.
499 """
500 raise NotImplementedError