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

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 

14 

15 

16class Fun(ABC): 

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

18 

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. 

22 

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

24 which include constructors, algebraic operations, calculus operations, 

25 and utility functions. 

26 """ 

27 

28 # -------------------------- 

29 # alternative constructors 

30 # -------------------------- 

31 @classmethod 

32 @abstractmethod 

33 def initconst(cls): # pragma: no cover 

34 """Initialize a constant function. 

35 

36 This constructor creates a function that represents a constant value 

37 on the specified interval. 

38 

39 Args: 

40 c: The constant value. 

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

42 

43 Returns: 

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

45 """ 

46 raise NotImplementedError 

47 

48 @classmethod 

49 @abstractmethod 

50 def initempty(cls): # pragma: no cover 

51 """Initialize an empty function. 

52 

53 This constructor creates an empty function representation, which is 

54 useful as a placeholder or for special cases. 

55 

56 Returns: 

57 Fun: A new empty instance. 

58 """ 

59 raise NotImplementedError 

60 

61 @classmethod 

62 @abstractmethod 

63 def initfun_adaptive(cls): # pragma: no cover 

64 """Initialize from a callable function using adaptive sampling. 

65 

66 This constructor determines the appropriate number of points needed to 

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

68 

69 Args: 

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

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

72 

73 Returns: 

74 Fun: A new instance representing the function f. 

75 """ 

76 raise NotImplementedError 

77 

78 @classmethod 

79 @abstractmethod 

80 def initfun_fixedlen(cls): # pragma: no cover 

81 """Initialize from a callable function using a fixed number of points. 

82 

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

84 rather than determining the number adaptively. 

85 

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. 

90 

91 Returns: 

92 Fun: A new instance representing the function f. 

93 """ 

94 raise NotImplementedError 

95 

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. 

102 

103 This method implements the addition operation between this function 

104 and another function or a scalar. 

105 

106 Args: 

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

108 

109 Returns: 

110 Fun: A new function representing the sum. 

111 """ 

112 raise NotImplementedError 

113 

114 @abstractmethod 

115 def __call__(self, x): # pragma: no cover 

116 """Evaluate the function at points x. 

117 

118 This method evaluates the function at the specified points. 

119 

120 Args: 

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

122 

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 

128 

129 @abstractmethod 

130 def __init__(self): # pragma: no cover 

131 """Initialize a new Fun instance. 

132 

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 

137 

138 @abstractmethod 

139 def __mul__(self, other): # pragma: no cover 

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

141 

142 This method implements the multiplication operation between this function 

143 and another function or a scalar. 

144 

145 Args: 

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

147 

148 Returns: 

149 Fun: A new function representing the product. 

150 """ 

151 raise NotImplementedError 

152 

153 @abstractmethod 

154 def __neg__(self): # pragma: no cover 

155 """Return the negative of this function. 

156 

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

158 

159 Returns: 

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

161 """ 

162 raise NotImplementedError 

163 

164 @abstractmethod 

165 def __pos__(self): # pragma: no cover 

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

167 

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

169 

170 Returns: 

171 Fun: This function object (unchanged). 

172 """ 

173 raise NotImplementedError 

174 

175 @abstractmethod 

176 def __pow__(self, power): # pragma: no cover 

177 """Raise this function to a power. 

178 

179 This method implements the power operation for this function. 

180 

181 Args: 

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

183 

184 Returns: 

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

186 """ 

187 raise NotImplementedError 

188 

189 @abstractmethod 

190 def __radd__(self, other): # pragma: no cover 

191 """Add a scalar or another function to this function (from the right). 

192 

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

194 i.e., other + self. 

195 

196 Args: 

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

198 

199 Returns: 

200 Fun: A new function representing the sum. 

201 """ 

202 raise NotImplementedError 

203 

204 @abstractmethod 

205 def __repr__(self): # pragma: no cover 

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

207 

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

209 relevant information about its representation and interval. 

210 

211 Returns: 

212 str: A string representation of the function. 

213 """ 

214 raise NotImplementedError 

215 

216 @abstractmethod 

217 def __rmul__(self, other): # pragma: no cover 

218 """Multiply a scalar or another function with this function (from the right). 

219 

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

221 i.e., other * self. 

222 

223 Args: 

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

225 

226 Returns: 

227 Fun: A new function representing the product. 

228 """ 

229 raise NotImplementedError 

230 

231 @abstractmethod 

232 def __rsub__(self, other): # pragma: no cover 

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

234 

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

236 i.e., other - self. 

237 

238 Args: 

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

240 

241 Returns: 

242 Fun: A new function representing the difference. 

243 """ 

244 raise NotImplementedError 

245 

246 @abstractmethod 

247 def __sub__(self, other): # pragma: no cover 

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

249 

250 This method implements the subtraction operation between this function 

251 and another function or a scalar. 

252 

253 Args: 

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

255 

256 Returns: 

257 Fun: A new function representing the difference. 

258 """ 

259 raise NotImplementedError 

260 

261 # ------------ 

262 # properties 

263 # ------------ 

264 @property 

265 @abstractmethod 

266 def coeffs(self): # pragma: no cover 

267 """Get the coefficients of the function representation. 

268 

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

270 such as Chebyshev coefficients for a Chebyshev series. 

271 

272 Returns: 

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

274 """ 

275 raise NotImplementedError 

276 

277 @property 

278 @abstractmethod 

279 def interval(self): # pragma: no cover 

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

281 

282 This property returns the interval object representing the domain 

283 of definition for this function. 

284 

285 Returns: 

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

287 """ 

288 raise NotImplementedError 

289 

290 @property 

291 @abstractmethod 

292 def isconst(self): # pragma: no cover 

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

294 

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

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

297 

298 Returns: 

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

300 """ 

301 raise NotImplementedError 

302 

303 @property 

304 @abstractmethod 

305 def isempty(self): # pragma: no cover 

306 """Check if this function is empty. 

307 

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

309 state used as a placeholder or for special cases. 

310 

311 Returns: 

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

313 """ 

314 raise NotImplementedError 

315 

316 @property 

317 @abstractmethod 

318 def iscomplex(self): # pragma: no cover 

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

320 

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

322 purely real-valued. 

323 

324 Returns: 

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

326 """ 

327 raise NotImplementedError 

328 

329 @property 

330 @abstractmethod 

331 def size(self): # pragma: no cover 

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

333 

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

335 complexity of the function representation. 

336 

337 Returns: 

338 int: The size of the function representation. 

339 """ 

340 raise NotImplementedError 

341 

342 @property 

343 @abstractmethod 

344 def support(self): # pragma: no cover 

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

346 

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

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

349 

350 Returns: 

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

352 """ 

353 raise NotImplementedError 

354 

355 @property 

356 @abstractmethod 

357 def vscale(self): # pragma: no cover 

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

359 

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. 

362 

363 Returns: 

364 float: The vertical scale of the function. 

365 """ 

366 raise NotImplementedError 

367 

368 # ----------- 

369 # utilities 

370 # ----------- 

371 @abstractmethod 

372 def copy(self): # pragma: no cover 

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

374 

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. 

377 

378 Returns: 

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

380 """ 

381 raise NotImplementedError 

382 

383 @abstractmethod 

384 def imag(self): # pragma: no cover 

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

386 

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. 

389 

390 Returns: 

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

392 """ 

393 raise NotImplementedError 

394 

395 @abstractmethod 

396 def real(self): # pragma: no cover 

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

398 

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. 

401 

402 Returns: 

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

404 """ 

405 raise NotImplementedError 

406 

407 @abstractmethod 

408 def restrict(self, subinterval): # pragma: no cover 

409 """Restrict this function to a subinterval. 

410 

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

412 to the specified subinterval. 

413 

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. 

417 

418 Returns: 

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

420 """ 

421 raise NotImplementedError 

422 

423 @abstractmethod 

424 def simplify(self): # pragma: no cover 

425 """Simplify the function representation. 

426 

427 This method simplifies the function representation by removing unnecessary 

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

429 

430 Returns: 

431 Fun: A new function with a simplified representation. 

432 """ 

433 raise NotImplementedError 

434 

435 @abstractmethod 

436 def values(self): # pragma: no cover 

437 """Get the values of the function at the points used for its representation. 

438 

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

440 representation, such as Chebyshev points. 

441 

442 Returns: 

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

444 """ 

445 raise NotImplementedError 

446 

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. 

453 

454 This method computes the points where the function equals zero 

455 within its interval of definition. 

456 

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 

462 

463 # ---------- 

464 # calculus 

465 # ---------- 

466 @abstractmethod 

467 def cumsum(self): # pragma: no cover 

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

469 

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. 

473 

474 Returns: 

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

476 """ 

477 raise NotImplementedError 

478 

479 @abstractmethod 

480 def diff(self): # pragma: no cover 

481 """Compute the derivative of the function. 

482 

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

484 

485 Returns: 

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

487 """ 

488 raise NotImplementedError 

489 

490 @abstractmethod 

491 def sum(self): # pragma: no cover 

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

493 

494 This method calculates the definite integral of the function 

495 over its interval of definition. 

496 

497 Returns: 

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

499 """ 

500 raise NotImplementedError