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

1"""Abstract base class for functions defined on arbitrary intervals. 

2 

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. 

6 

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""" 

12 

13from abc import ABC, abstractmethod 

14from typing import Any 

15 

16import numpy as np 

17 

18 

19class Fun(ABC): 

20 """Abstract base class for functions defined on arbitrary intervals. 

21 

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. 

25 

26 Concrete subclasses must implement all the abstract methods defined here, 

27 which include constructors, algebraic operations, calculus operations, 

28 and utility functions. 

29 """ 

30 

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. 

38 

39 This constructor creates a function that represents a constant value 

40 on the specified interval. 

41 

42 Args: 

43 c: The constant value. 

44 interval: The interval on which to define the function. 

45 

46 Returns: 

47 Fun: A new instance representing the constant function f(x) = c. 

48 """ 

49 raise NotImplementedError 

50 

51 @classmethod 

52 @abstractmethod 

53 def initempty(cls) -> "Fun": # pragma: no cover 

54 """Initialize an empty function. 

55 

56 This constructor creates an empty function representation, which is 

57 useful as a placeholder or for special cases. 

58 

59 Returns: 

60 Fun: A new empty instance. 

61 """ 

62 raise NotImplementedError 

63 

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. 

68 

69 This constructor determines the appropriate number of points needed to 

70 represent the function to the specified tolerance using an adaptive algorithm. 

71 

72 Args: 

73 f (callable): The function to be approximated. 

74 interval: The interval on which to define the function. 

75 

76 Returns: 

77 Fun: A new instance representing the function f. 

78 """ 

79 raise NotImplementedError 

80 

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. 

85 

86 This constructor uses a specified number of points to represent the function, 

87 rather than determining the number adaptively. 

88 

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. 

93 

94 Returns: 

95 Fun: A new instance representing the function f. 

96 """ 

97 raise NotImplementedError 

98 

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. 

105 

106 This method implements the addition operation between this function 

107 and another function or a scalar. 

108 

109 Args: 

110 other (Fun or scalar): The function or scalar to add to this function. 

111 

112 Returns: 

113 Fun: A new function representing the sum. 

114 """ 

115 raise NotImplementedError 

116 

117 @abstractmethod 

118 def __call__(self, x: Any) -> Any: # pragma: no cover 

119 """Evaluate the function at points x. 

120 

121 This method evaluates the function at the specified points. 

122 

123 Args: 

124 x (float or array-like): Points at which to evaluate the function. 

125 

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 

131 

132 @abstractmethod 

133 def __init__(self) -> None: # pragma: no cover 

134 """Initialize a new Fun instance. 

135 

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 

140 

141 @abstractmethod 

142 def __mul__(self, other: Any) -> "Fun": # pragma: no cover 

143 """Multiply this function with another function or a scalar. 

144 

145 This method implements the multiplication operation between this function 

146 and another function or a scalar. 

147 

148 Args: 

149 other (Fun or scalar): The function or scalar to multiply with this function. 

150 

151 Returns: 

152 Fun: A new function representing the product. 

153 """ 

154 raise NotImplementedError 

155 

156 @abstractmethod 

157 def __neg__(self) -> "Fun": # pragma: no cover 

158 """Return the negative of this function. 

159 

160 This method implements the unary negation operation for this function. 

161 

162 Returns: 

163 Fun: A new function representing -f(x). 

164 """ 

165 raise NotImplementedError 

166 

167 @abstractmethod 

168 def __pos__(self) -> "Fun": # pragma: no cover 

169 """Return the positive of this function (which is the function itself). 

170 

171 This method implements the unary plus operation for this function. 

172 

173 Returns: 

174 Fun: This function object (unchanged). 

175 """ 

176 raise NotImplementedError 

177 

178 @abstractmethod 

179 def __pow__(self, power: Any) -> "Fun": # pragma: no cover 

180 """Raise this function to a power. 

181 

182 This method implements the power operation for this function. 

183 

184 Args: 

185 power (int, float, or Fun): The exponent to which this function is raised. 

186 

187 Returns: 

188 Fun: A new function representing f(x)^power. 

189 """ 

190 raise NotImplementedError 

191 

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). 

195 

196 This method is called when a scalar or another function is added to this function, 

197 i.e., other + self. 

198 

199 Args: 

200 other (scalar or Fun): The scalar or function to add to this function. 

201 

202 Returns: 

203 Fun: A new function representing the sum. 

204 """ 

205 raise NotImplementedError 

206 

207 @abstractmethod 

208 def __repr__(self) -> str: # pragma: no cover 

209 """Return a string representation of the function. 

210 

211 This method returns a string representation of the function that includes 

212 relevant information about its representation and interval. 

213 

214 Returns: 

215 str: A string representation of the function. 

216 """ 

217 raise NotImplementedError 

218 

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). 

222 

223 This method is called when a scalar or another function is multiplied with this function, 

224 i.e., other * self. 

225 

226 Args: 

227 other (scalar or Fun): The scalar or function to multiply with this function. 

228 

229 Returns: 

230 Fun: A new function representing the product. 

231 """ 

232 raise NotImplementedError 

233 

234 @abstractmethod 

235 def __rsub__(self, other: Any) -> "Fun": # pragma: no cover 

236 """Subtract this function from a scalar or another function. 

237 

238 This method is called when this function is subtracted from a scalar or another function, 

239 i.e., other - self. 

240 

241 Args: 

242 other (scalar or Fun): The scalar or function from which to subtract this function. 

243 

244 Returns: 

245 Fun: A new function representing the difference. 

246 """ 

247 raise NotImplementedError 

248 

249 @abstractmethod 

250 def __sub__(self, other: Any) -> "Fun": # pragma: no cover 

251 """Subtract another function or a scalar from this function. 

252 

253 This method implements the subtraction operation between this function 

254 and another function or a scalar. 

255 

256 Args: 

257 other (Fun or scalar): The function or scalar to subtract from this function. 

258 

259 Returns: 

260 Fun: A new function representing the difference. 

261 """ 

262 raise NotImplementedError 

263 

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. 

271 

272 This property returns the coefficients used in the function representation, 

273 such as Chebyshev coefficients for a Chebyshev series. 

274 

275 Returns: 

276 array-like: The coefficients of the function representation. 

277 """ 

278 raise NotImplementedError 

279 

280 @property 

281 @abstractmethod 

282 def interval(self) -> Any: # pragma: no cover 

283 """Get the interval on which this function is defined. 

284 

285 This property returns the interval object representing the domain 

286 of definition for this function. 

287 

288 Returns: 

289 Interval: The interval on which this function is defined. 

290 """ 

291 raise NotImplementedError 

292 

293 @property 

294 @abstractmethod 

295 def isconst(self) -> bool: # pragma: no cover 

296 """Check if this function represents a constant. 

297 

298 This property determines whether the function is constant (i.e., f(x) = c 

299 for some constant c) over its interval of definition. 

300 

301 Returns: 

302 bool: True if the function is constant, False otherwise. 

303 """ 

304 raise NotImplementedError 

305 

306 @property 

307 @abstractmethod 

308 def isempty(self) -> bool: # pragma: no cover 

309 """Check if this function is empty. 

310 

311 This property determines whether the function is empty, which is a special 

312 state used as a placeholder or for special cases. 

313 

314 Returns: 

315 bool: True if the function is empty, False otherwise. 

316 """ 

317 raise NotImplementedError 

318 

319 @property 

320 @abstractmethod 

321 def iscomplex(self) -> bool: # pragma: no cover 

322 """Check if this function has complex values. 

323 

324 This property determines whether the function has complex values or is 

325 purely real-valued. 

326 

327 Returns: 

328 bool: True if the function has complex values, False otherwise. 

329 """ 

330 raise NotImplementedError 

331 

332 @property 

333 @abstractmethod 

334 def size(self) -> int: # pragma: no cover 

335 """Get the size of the function representation. 

336 

337 This property returns the number of coefficients or other measure of the 

338 complexity of the function representation. 

339 

340 Returns: 

341 int: The size of the function representation. 

342 """ 

343 raise NotImplementedError 

344 

345 @property 

346 @abstractmethod 

347 def support(self) -> np.ndarray: # pragma: no cover 

348 """Get the support interval of this function. 

349 

350 This property returns the interval on which this function is defined, 

351 represented as a numpy array with two elements [a, b]. 

352 

353 Returns: 

354 numpy.ndarray: Array containing the endpoints of the interval. 

355 """ 

356 raise NotImplementedError 

357 

358 @property 

359 @abstractmethod 

360 def vscale(self) -> float: # pragma: no cover 

361 """Get the vertical scale of the function. 

362 

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. 

365 

366 Returns: 

367 float: The vertical scale of the function. 

368 """ 

369 raise NotImplementedError 

370 

371 # ----------- 

372 # utilities 

373 # ----------- 

374 @abstractmethod 

375 def copy(self) -> "Fun": # pragma: no cover 

376 """Create a deep copy of this function. 

377 

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. 

380 

381 Returns: 

382 Fun: A new function that is a deep copy of this function. 

383 """ 

384 raise NotImplementedError 

385 

386 @abstractmethod 

387 def imag(self) -> "Fun": # pragma: no cover 

388 """Get the imaginary part of this function. 

389 

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. 

392 

393 Returns: 

394 Fun: A new function representing the imaginary part of this function. 

395 """ 

396 raise NotImplementedError 

397 

398 @abstractmethod 

399 def real(self) -> "Fun": # pragma: no cover 

400 """Get the real part of this function. 

401 

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. 

404 

405 Returns: 

406 Fun: A new function representing the real part of this function. 

407 """ 

408 raise NotImplementedError 

409 

410 @abstractmethod 

411 def restrict(self, subinterval: Any) -> "Fun": # pragma: no cover 

412 """Restrict this function to a subinterval. 

413 

414 This method creates a new function that is the restriction of this function 

415 to the specified subinterval. 

416 

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. 

420 

421 Returns: 

422 Fun: A new function representing the restriction of this function to the subinterval. 

423 """ 

424 raise NotImplementedError 

425 

426 @abstractmethod 

427 def simplify(self) -> "Fun": # pragma: no cover 

428 """Simplify the function representation. 

429 

430 This method simplifies the function representation by removing unnecessary 

431 coefficients or reducing the degree, while maintaining the specified accuracy. 

432 

433 Returns: 

434 Fun: A new function with a simplified representation. 

435 """ 

436 raise NotImplementedError 

437 

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. 

441 

442 This method returns the values of the function at the points used for its 

443 representation, such as Chebyshev points. 

444 

445 Returns: 

446 array-like: The values of the function at the representation points. 

447 """ 

448 raise NotImplementedError 

449 

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. 

456 

457 This method computes the points where the function equals zero 

458 within its interval of definition. 

459 

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 

465 

466 # ---------- 

467 # calculus 

468 # ---------- 

469 @abstractmethod 

470 def cumsum(self) -> "Fun": # pragma: no cover 

471 """Compute the indefinite integral of the function. 

472 

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. 

476 

477 Returns: 

478 Fun: A new function representing the indefinite integral of this function. 

479 """ 

480 raise NotImplementedError 

481 

482 @abstractmethod 

483 def diff(self) -> "Fun": # pragma: no cover 

484 """Compute the derivative of the function. 

485 

486 This method calculates the derivative of the function with respect to x. 

487 

488 Returns: 

489 Fun: A new function representing the derivative of this function. 

490 """ 

491 raise NotImplementedError 

492 

493 @abstractmethod 

494 def sum(self) -> float: # pragma: no cover 

495 """Compute the definite integral of the function over its interval of definition. 

496 

497 This method calculates the definite integral of the function 

498 over its interval of definition. 

499 

500 Returns: 

501 float or complex: The definite integral of the function over its interval of definition. 

502 """ 

503 raise NotImplementedError