Coverage for src/chebpy/api.py: 97%

70 statements  

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

1"""User-facing functions for creating and manipulating Chebfun objects. 

2 

3This module provides the main interface for users to create Chebfun objects, 

4which are the core data structure in ChebPy for representing functions. 

5""" 

6 

7from collections.abc import Callable 

8from typing import Any 

9 

10import numpy as np 

11 

12from .algorithms import barywts2, chebpts2, funqui 

13from .bndfun import Bndfun 

14from .chebfun import Chebfun 

15from .settings import _preferences as prefs 

16from .utilities import Domain, Interval 

17 

18 

19def chebfun( 

20 f: Callable[..., Any] | str | float | None = None, 

21 domain: np.ndarray | list[float] | None = None, 

22 n: int | None = None, 

23 *, 

24 sing: str | None = None, 

25 params: Any = None, 

26) -> "Chebfun": 

27 """Create a Chebfun object representing a function. 

28 

29 A Chebfun object represents a function using Chebyshev polynomials. This constructor 

30 can create Chebfun objects from various inputs including callable functions, 

31 constants, and special strings. 

32 

33 Args: 

34 f: The function to represent. Can be: 

35 - None: Creates an empty Chebfun 

36 - callable: A function handle like lambda x: x**2 

37 - str: A single alphabetic character (e.g., 'x') for the identity function 

38 - numeric: A constant value 

39 domain: The domain on which to define the function. Defaults to the domain 

40 specified in preferences. 

41 n: Optional number of points to use in the discretization. If None, adaptive 

42 construction is used. 

43 sing: Optional endpoint-singularity hint, one of ``"left"``, ``"right"``, 

44 or ``"both"``. When set, the appropriate boundary pieces are built 

45 as :class:`~chebpy.singfun.Singfun` instances using the 

46 Adcock-Richardson exponential clustering map; interior pieces remain 

47 :class:`~chebpy.bndfun.Bndfun`. Only supported with ``n=None``. 

48 params: Slit-strip map parameters (a :class:`~chebpy.maps.MapParams` 

49 carrying ``L`` and ``alpha``). Default ``None`` uses 

50 :class:`~chebpy.maps.MapParams` defaults. 

51 

52 Returns: 

53 Chebfun: A Chebfun object representing the function. 

54 

55 Raises: 

56 ValueError: If unable to construct a constant function from the input. 

57 

58 Examples: 

59 >>> # Empty Chebfun 

60 >>> f = chebfun() 

61 >>> 

62 >>> # Function from a lambda 

63 >>> import numpy as np 

64 >>> f = chebfun(lambda x: np.sin(x), domain=[-np.pi, np.pi]) 

65 >>> 

66 >>> # Identity function 

67 >>> x = chebfun('x') 

68 >>> 

69 >>> # Constant function 

70 >>> c = chebfun(3.14) 

71 >>> 

72 >>> # Function with an endpoint singularity 

73 >>> g = chebfun(np.sqrt, domain=[0.0, 1.0], sing="left") 

74 """ 

75 # Empty via chebfun() 

76 if f is None: 

77 return Chebfun.initempty() 

78 

79 domain = domain if domain is not None else prefs.domain 

80 

81 # Callable fct in chebfun(lambda x: f(x), ... ) 

82 if callable(f): 

83 return Chebfun.initfun(f, domain, n, sing=sing, params=params) 

84 

85 # Identity via chebfun('x', ... ) 

86 if isinstance(f, str) and len(f) == 1 and f.isalpha(): 

87 if n: 

88 return Chebfun.initfun(lambda x: x, domain, n) 

89 else: 

90 return Chebfun.initidentity(domain) 

91 

92 try: 

93 # Constant fct via chebfun(3.14, ... ), chebfun('3.14', ... ) 

94 return Chebfun.initconst(float(f), domain) 

95 except (OverflowError, ValueError) as err: 

96 raise ValueError(f) from err 

97 

98 

99def equifun(values: np.ndarray | list[float | complex], domain: np.ndarray | list[float] | None = None) -> "Chebfun": 

100 """Create a Chebfun from equispaced samples including both endpoints. 

101 

102 Args: 

103 values: Non-empty one-dimensional sample values. 

104 domain: Two finite endpoints for the sample interval. Defaults to 

105 the configured preference domain. 

106 

107 Returns: 

108 Chebfun: A scalar-valued Chebfun approximating the Floater-Hormann 

109 rational interpolant through the equispaced samples. 

110 

111 Raises: 

112 ValueError: If values are empty, non-numeric, not one-dimensional, or 

113 if domain is not exactly two finite endpoints. 

114 

115 Examples: 

116 >>> import numpy as np 

117 >>> from chebpy import equifun 

118 >>> x = np.linspace(-1, 1, 25) 

119 >>> f = equifun(np.sin(x)) 

120 >>> bool(abs(f(0.0)) < 1e-12) 

121 True 

122 """ 

123 vals = np.asarray(values) 

124 if vals.size == 0: 

125 msg = "values must be non-empty" 

126 raise ValueError(msg) 

127 if vals.ndim != 1: 

128 msg = "values must be one-dimensional" 

129 raise ValueError(msg) 

130 if not np.issubdtype(vals.dtype, np.number): 

131 msg = "values must be numeric" 

132 raise ValueError(msg) 

133 

134 dom = np.asarray(prefs.domain if domain is None else domain, dtype=float) 

135 if dom.ndim != 1 or dom.size != 2: 

136 msg = "domain must contain exactly two endpoints" 

137 raise ValueError(msg) 

138 if not np.all(np.isfinite(dom)): 

139 msg = "domain endpoints must be finite" 

140 raise ValueError(msg) 

141 if dom[0] >= dom[1]: 

142 msg = "domain endpoints must be strictly increasing" 

143 raise ValueError(msg) 

144 dom = Domain(dom) 

145 

146 if vals.size == 1: 

147 value = complex(vals[0]) if np.iscomplexobj(vals) else vals[0] 

148 return Chebfun.initconst(value, dom) 

149 return Chebfun.initfun(funqui(vals, dom), dom) 

150 

151 

152def pwc(domain: list[float] | None = None, values: list[float] | None = None) -> "Chebfun": 

153 """Initialize a piecewise-constant Chebfun. 

154 

155 Creates a piecewise-constant function represented as a Chebfun object. 

156 The function takes constant values on each interval defined by the domain. 

157 

158 Args: 

159 domain (list): A list of breakpoints defining the intervals. Must have 

160 length equal to len(values) + 1. Default is [-1, 0, 1]. 

161 values (list): A list of constant values for each interval. Default is [0, 1]. 

162 

163 Returns: 

164 Chebfun: A piecewise-constant Chebfun object. 

165 

166 Examples: 

167 >>> # Create a step function with value 0 on [-1,0] and 1 on [0,1] 

168 >>> f = pwc() 

169 >>> 

170 >>> # Create a custom piecewise-constant function 

171 >>> f = pwc(domain=[-2, -1, 0, 1, 2], values=[-1, 0, 1, 2]) 

172 """ 

173 if values is None: 

174 values = [0, 1] 

175 if domain is None: 

176 domain = [-1, 0, 1] 

177 funs: list[Any] = [] 

178 intervals = list(Domain(domain).intervals) 

179 for interval, value in zip(intervals, values, strict=False): 

180 funs.append(Bndfun.initconst(value, interval)) 

181 return Chebfun(funs) 

182 

183 

184def chebpts( 

185 n: int, 

186 domain: list[float] | None = None, 

187) -> tuple[np.ndarray, np.ndarray]: 

188 """Return *n* Chebyshev points and barycentric weights on *domain*. 

189 

190 This provides the same functionality as MATLAB's ``chebpts(n, [a, b])``. 

191 The points are Chebyshev points of the second kind (i.e. the extrema of 

192 the Chebyshev polynomial T_{n-1} plus the endpoints). 

193 

194 Args: 

195 n: Number of Chebyshev points. 

196 domain: Two-element list ``[a, b]`` specifying the interval. 

197 Defaults to ``[-1, 1]``. 

198 

199 Returns: 

200 A ``(points, weights)`` tuple where *points* is an array of *n* 

201 Chebyshev points on the given domain and *weights* is the 

202 corresponding array of barycentric interpolation weights. 

203 

204 Examples: 

205 >>> pts, wts = chebpts(4) 

206 >>> pts, wts = chebpts(4, [0, 3]) 

207 """ 

208 if domain is None: 

209 domain = [-1, 1] 

210 pts = chebpts2(n) 

211 wts = barywts2(n) 

212 interval = Interval(*domain) 

213 pts = interval(pts) 

214 return pts, wts 

215 

216 

217def trigfun( 

218 f: Callable[..., Any] | str | float | None = None, 

219 domain: np.ndarray | list[float] | None = None, 

220 n: int | None = None, 

221) -> "Chebfun": 

222 """Create a Chebfun backed by Fourier (trigonometric) technology. 

223 

224 This is the explicit entry point for constructing periodic functions. 

225 Unlike ``chebfun``, which always uses Chebyshev polynomial technology, 

226 ``trigfun`` always uses :class:`~chebpy.trigtech.Trigtech` as the 

227 underlying approximation technology. The user is responsible for 

228 ensuring that *f* is smooth and periodic on *domain*. 

229 

230 The API mirrors :func:`chebfun` exactly: 

231 

232 * ``trigfun()`` → empty Chebfun 

233 * ``trigfun(lambda x: np.sin(np.pi*x), [-1, 1])`` → from callable 

234 * ``trigfun('x')`` → identity (not truly periodic; provided for 

235 interface compatibility) 

236 * ``trigfun(3.14)`` → constant function 

237 

238 Args: 

239 f: The function to represent. Same semantics as :func:`chebfun`. 

240 domain: Domain ``[a, b]``. Defaults to ``prefs.domain``. 

241 n: Fixed number of Fourier modes. If None, adaptive construction 

242 is used. 

243 

244 Returns: 

245 Chebfun: A Chebfun object whose pieces are backed by Trigtech. 

246 

247 Examples: 

248 >>> import numpy as np 

249 >>> from chebpy import trigfun 

250 >>> f = trigfun(lambda x: np.cos(np.pi * x), [-1, 1]) 

251 >>> float(f(0.0)) 

252 1.0 

253 >>> g = trigfun(lambda x: np.sin(2 * np.pi * x)) 

254 >>> bool(abs(g.sum()) < 1e-12) 

255 True 

256 """ 

257 with prefs: 

258 prefs.tech = "Trigtech" 

259 return chebfun(f, domain, n)