Coverage for src/chebpy/onefun.py: 100%

4 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-22 21:50 +0000

1"""Abstract base class for functions defined on the standard interval [-1, 1]. 

2 

3This module provides the Onefun abstract base class, which defines the interface for 

4all function representations on the standard interval [-1, 1] in ChebPy. It specifies 

5the methods and properties that concrete function classes must implement. 

6 

7The Onefun class serves as the foundation for representing functions on the standard 

8interval, with concrete implementations like Chebtech inheriting from it. Functions 

9defined on arbitrary intervals are typically mapped to this standard interval for 

10internal representation and manipulation. 

11""" 

12 

13from abc import ABC, abstractmethod 

14from typing import Any 

15 

16import numpy as np 

17 

18 

19class Onefun(ABC): 

20 """Abstract base class for functions defined on the interval [-1, 1]. 

21 

22 This class defines the interface for all function representations on the 

23 standard interval [-1, 1]. It serves as the base class for specific 

24 implementations like Chebtech. 

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 = None) -> "Onefun": # pragma: no cover 

37 """Initialize a constant function. 

38 

39 This constructor creates a function that represents a constant value 

40 on the interval [-1, 1]. 

41 

42 Args: 

43 c: The constant value. 

44 interval: Optional interval associated with the function (used by mapped subclasses). 

45 

46 Returns: 

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

48 """ 

49 raise NotImplementedError 

50 

51 @classmethod 

52 @abstractmethod 

53 def initempty(cls, *, interval: Any = None) -> "Onefun": # 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 Args: 

60 interval: Optional interval associated with the function (used by mapped subclasses). 

61 

62 Returns: 

63 Onefun: A new empty instance. 

64 """ 

65 raise NotImplementedError 

66 

67 @classmethod 

68 @abstractmethod 

69 def initidentity(cls) -> "Onefun": # pragma: no cover 

70 """Initialize the identity function f(x) = x. 

71 

72 This constructor creates a function that represents f(x) = x 

73 on the interval [-1, 1]. 

74 

75 Returns: 

76 Onefun: A new instance representing the identity function. 

77 """ 

78 raise NotImplementedError 

79 

80 @classmethod 

81 @abstractmethod 

82 def initfun(cls, f: Any, n: Any = None, *, interval: Any = None) -> "Onefun": # pragma: no cover 

83 """Initialize from a callable function. 

84 

85 This is a general constructor that delegates to either initfun_adaptive 

86 or initfun_fixedlen based on the provided parameters. 

87 

88 Args: 

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

90 n (int, optional): If provided, specifies the number of points to use. 

91 If None, determines the number adaptively. 

92 interval: Optional interval associated with the function (used by mapped subclasses). 

93 

94 Returns: 

95 Onefun: A new instance representing the function f. 

96 """ 

97 raise NotImplementedError 

98 

99 @classmethod 

100 @abstractmethod 

101 def initfun_adaptive(cls, f: Any) -> "Onefun": # pragma: no cover 

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

103 

104 This constructor determines the appropriate number of points needed to 

105 represent the function to a specified tolerance using an adaptive algorithm. 

106 

107 Args: 

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

109 

110 Returns: 

111 Onefun: A new instance representing the function f. 

112 """ 

113 raise NotImplementedError 

114 

115 @classmethod 

116 @abstractmethod 

117 def initfun_fixedlen(cls, f: Any, n: int) -> "Onefun": # pragma: no cover 

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

119 

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

121 rather than determining the number adaptively. 

122 

123 Args: 

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

125 n (int): The number of points to use. 

126 

127 Returns: 

128 Onefun: A new instance representing the function f. 

129 """ 

130 raise NotImplementedError 

131 

132 @classmethod 

133 @abstractmethod 

134 def initvalues(cls, values: Any, *, interval: Any = None) -> "Onefun": # pragma: no cover 

135 """Initialize from function values at Chebyshev points. 

136 

137 This constructor creates a function representation from values 

138 at Chebyshev points. 

139 

140 Args: 

141 values: Function values at Chebyshev points. 

142 interval: Optional interval associated with the function (used by mapped subclasses). 

143 

144 Returns: 

145 Onefun: A new instance representing the function with the given values. 

146 """ 

147 raise NotImplementedError 

148 

149 # ------------------- 

150 # "private" methods 

151 # ------------------- 

152 @abstractmethod 

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

154 """Evaluate the function at points x. 

155 

156 This method evaluates the function at the specified points. 

157 

158 Args: 

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

160 These should be in the interval [-1, 1]. 

161 

162 Returns: 

163 float or array-like: The value(s) of the function at the specified point(s). 

164 Returns a scalar if x is a scalar, otherwise an array of the same size as x. 

165 """ 

166 raise NotImplementedError 

167 

168 @abstractmethod 

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

170 """Initialize a new Onefun instance. 

171 

172 This method initializes a new function representation on the interval [-1, 1]. 

173 The specific initialization depends on the concrete subclass implementation. 

174 """ 

175 raise NotImplementedError 

176 

177 @abstractmethod 

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

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

180 

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

182 relevant information about its representation. 

183 

184 Returns: 

185 str: A string representation of the function. 

186 """ 

187 raise NotImplementedError 

188 

189 # ---------------- 

190 # algebra 

191 # ---------------- 

192 @abstractmethod 

193 def __add__(self, other: Any) -> "Onefun": # pragma: no cover 

194 """Add this function with another function or a scalar. 

195 

196 This method implements the addition operation between this function 

197 and another function or a scalar. 

198 

199 Args: 

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

201 

202 Returns: 

203 Onefun: A new function representing the sum. 

204 """ 

205 raise NotImplementedError 

206 

207 @abstractmethod 

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

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

210 

211 This method implements the multiplication operation between this function 

212 and another function or a scalar. 

213 

214 Args: 

215 other (Onefun or scalar): The function or scalar to multiply with this function. 

216 

217 Returns: 

218 Onefun: A new function representing the product. 

219 """ 

220 raise NotImplementedError 

221 

222 @abstractmethod 

223 def __neg__(self) -> "Onefun": # pragma: no cover 

224 """Return the negative of this function. 

225 

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

227 

228 Returns: 

229 Onefun: A new function representing -f(x). 

230 """ 

231 raise NotImplementedError 

232 

233 @abstractmethod 

234 def __pos__(self) -> "Onefun": # pragma: no cover 

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

236 

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

238 

239 Returns: 

240 Onefun: This function object (unchanged). 

241 """ 

242 raise NotImplementedError 

243 

244 @abstractmethod 

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

246 """Raise this function to a power. 

247 

248 This method implements the power operation for this function. 

249 

250 Args: 

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

252 

253 Returns: 

254 Onefun: A new function representing f(x)^power. 

255 """ 

256 raise NotImplementedError 

257 

258 @abstractmethod 

259 def __radd__(self, other: Any) -> "Onefun": # pragma: no cover 

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

261 

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

263 i.e., other + self. 

264 

265 Args: 

266 other (scalar or Onefun): The scalar or function to add to this function. 

267 

268 Returns: 

269 Onefun: A new function representing the sum. 

270 """ 

271 raise NotImplementedError 

272 

273 @abstractmethod 

274 def __rmul__(self, other: Any) -> "Onefun": # pragma: no cover 

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

276 

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

278 i.e., other * self. 

279 

280 Args: 

281 other (scalar or Onefun): The scalar or function to multiply with this function. 

282 

283 Returns: 

284 Onefun: A new function representing the product. 

285 """ 

286 raise NotImplementedError 

287 

288 @abstractmethod 

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

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

291 

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

293 i.e., other - self. 

294 

295 Args: 

296 other (scalar or Onefun): The scalar or function from which to subtract this function. 

297 

298 Returns: 

299 Onefun: A new function representing the difference. 

300 """ 

301 raise NotImplementedError 

302 

303 @abstractmethod 

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

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

306 

307 This method implements the subtraction operation between this function 

308 and another function or a scalar. 

309 

310 Args: 

311 other (Onefun or scalar): The function or scalar to subtract from this function. 

312 

313 Returns: 

314 Onefun: A new function representing the difference. 

315 """ 

316 raise NotImplementedError 

317 

318 # --------------- 

319 # properties 

320 # --------------- 

321 @property 

322 @abstractmethod 

323 def coeffs(self) -> np.ndarray: # pragma: no cover 

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

325 

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

327 such as Chebyshev coefficients for a Chebyshev series. 

328 

329 Returns: 

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

331 """ 

332 raise NotImplementedError 

333 

334 @property 

335 @abstractmethod 

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

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

338 

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

340 for some constant c) over the interval [-1, 1]. 

341 

342 Returns: 

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

344 """ 

345 raise NotImplementedError 

346 

347 @property 

348 @abstractmethod 

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

350 """Check if this function is empty. 

351 

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

353 state used as a placeholder or for special cases. 

354 

355 Returns: 

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

357 """ 

358 raise NotImplementedError 

359 

360 @property 

361 @abstractmethod 

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

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

364 

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

366 complexity of the function representation. 

367 

368 Returns: 

369 int: The size of the function representation. 

370 """ 

371 raise NotImplementedError 

372 

373 @property 

374 @abstractmethod 

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

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

377 

378 This property returns a measure of the range of function values, typically 

379 the maximum absolute value of the function on the interval [-1, 1]. 

380 

381 Returns: 

382 float: The vertical scale of the function. 

383 """ 

384 raise NotImplementedError 

385 

386 # --------------- 

387 # utilities 

388 # --------------- 

389 @abstractmethod 

390 def copy(self) -> "Onefun": # pragma: no cover 

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

392 

393 This method creates a new function that is a deep copy of this function, 

394 ensuring that modifications to the copy do not affect the original. 

395 

396 Returns: 

397 Onefun: A new function that is a deep copy of this function. 

398 """ 

399 raise NotImplementedError 

400 

401 @abstractmethod 

402 def imag(self) -> "Onefun": # pragma: no cover 

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

404 

405 This method returns a new function representing the imaginary part of this function. 

406 If this function is real-valued, returns a zero function. 

407 

408 Returns: 

409 Onefun: A new function representing the imaginary part of this function. 

410 """ 

411 raise NotImplementedError 

412 

413 @abstractmethod 

414 def prolong(self, n: int) -> "Onefun": # pragma: no cover 

415 """Extend the function representation to a larger size. 

416 

417 This method extends the function representation to use more coefficients 

418 or a higher degree, which can be useful for certain operations. 

419 

420 Args: 

421 n (int): The new size for the function representation. 

422 

423 Returns: 

424 Onefun: A new function with an extended representation. 

425 """ 

426 raise NotImplementedError 

427 

428 @abstractmethod 

429 def real(self) -> "Onefun": # pragma: no cover 

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

431 

432 This method returns a new function representing the real part of this function. 

433 If this function is already real-valued, returns this function. 

434 

435 Returns: 

436 Onefun: A new function representing the real part of this function. 

437 """ 

438 raise NotImplementedError 

439 

440 @abstractmethod 

441 def simplify(self) -> "Onefun": # pragma: no cover 

442 """Simplify the function representation. 

443 

444 This method simplifies the function representation by removing unnecessary 

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

446 

447 Returns: 

448 Onefun: A new function with a simplified representation. 

449 """ 

450 raise NotImplementedError 

451 

452 @abstractmethod 

453 def values(self) -> np.ndarray: # pragma: no cover 

454 """Get the values of the function at Chebyshev points. 

455 

456 This method returns the values of the function at Chebyshev points, 

457 which can be useful for certain operations or for visualization. 

458 

459 Returns: 

460 array-like: The values of the function at Chebyshev points. 

461 """ 

462 raise NotImplementedError 

463 

464 # -------------- 

465 # rootfinding 

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

467 @abstractmethod 

468 def roots(self) -> np.ndarray: # pragma: no cover 

469 """Find the roots (zeros) of the function on [-1, 1]. 

470 

471 This method computes the points where the function equals zero 

472 within the interval [-1, 1]. 

473 

474 Returns: 

475 array-like: An array of the roots of the function in the interval [-1, 1], 

476 sorted in ascending order. 

477 """ 

478 raise NotImplementedError 

479 

480 # ------------- 

481 # calculus 

482 # ------------- 

483 @abstractmethod 

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

485 """Compute the definite integral of the function over [-1, 1]. 

486 

487 This method calculates the definite integral of the function 

488 over the interval [-1, 1]. 

489 

490 Returns: 

491 float or complex: The definite integral of the function over [-1, 1]. 

492 """ 

493 raise NotImplementedError 

494 

495 @abstractmethod 

496 def cumsum(self) -> "Onefun": # pragma: no cover 

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

498 

499 This method calculates the indefinite integral (antiderivative) of the function, 

500 with the constant of integration chosen so that the indefinite integral 

501 evaluates to 0 at x = -1. 

502 

503 Returns: 

504 Onefun: A new function representing the indefinite integral of this function. 

505 """ 

506 raise NotImplementedError 

507 

508 @abstractmethod 

509 def diff(self) -> "Onefun": # pragma: no cover 

510 """Compute the derivative of the function. 

511 

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

513 

514 Returns: 

515 Onefun: A new function representing the derivative of this function. 

516 """ 

517 raise NotImplementedError