taylorize 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
taylorize/__init__.py ADDED
@@ -0,0 +1,42 @@
1
+ """taylorize — lazy truncated Taylor series for higher-order automatic
2
+ differentiation and Taylor-method ODE integration.
3
+
4
+ A :class:`Taylor` is the normalized Taylor polynomial of a smooth function
5
+ around a point. Higher-order coefficients are computed lazily, so arbitrary
6
+ orders can be requested *after* the expression has been built.
7
+
8
+ This is the minimal 0.0.1 seed of the project.
9
+ """
10
+ from .core import (
11
+ Taylor,
12
+ constant,
13
+ cos,
14
+ derivative,
15
+ exp,
16
+ log,
17
+ ode_step,
18
+ power,
19
+ sin,
20
+ sqrt,
21
+ tan,
22
+ taylor_series,
23
+ variable,
24
+ )
25
+
26
+ __version__ = "0.0.1"
27
+
28
+ __all__ = [
29
+ "Taylor",
30
+ "variable",
31
+ "constant",
32
+ "exp",
33
+ "log",
34
+ "sin",
35
+ "cos",
36
+ "tan",
37
+ "sqrt",
38
+ "power",
39
+ "derivative",
40
+ "taylor_series",
41
+ "ode_step",
42
+ ]
taylorize/core.py ADDED
@@ -0,0 +1,345 @@
1
+ """Core: a small, dependency-free truncated Taylor series type.
2
+
3
+ A :class:`Taylor` represents the normalized Taylor polynomial of a smooth
4
+ function ``x(t)`` around ``t = 0``::
5
+
6
+ x(t) = c[0] + c[1] t + c[2] t^2 + ...
7
+
8
+ with ``c[k] = x^(k)(0) / k!``. Higher-order coefficients are produced lazily
9
+ from a recurrence rule, so any order can be requested after the fact.
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import math
14
+ from typing import Callable, List, Optional
15
+
16
+ __all__ = [
17
+ "Taylor",
18
+ "variable",
19
+ "constant",
20
+ "exp",
21
+ "log",
22
+ "sin",
23
+ "cos",
24
+ "tan",
25
+ "sqrt",
26
+ "power",
27
+ "derivative",
28
+ "taylor_series",
29
+ "ode_step",
30
+ ]
31
+
32
+
33
+ class Taylor:
34
+ """A truncated Taylor polynomial with lazily computed coefficients."""
35
+
36
+ __slots__ = ("c", "_rule")
37
+
38
+ def __init__(self, coeffs, _rule: Optional[Callable[[int], object]] = None) -> None:
39
+ if isinstance(coeffs, Taylor):
40
+ self.c: List[object] = list(coeffs.c)
41
+ self._rule = coeffs._rule if _rule is None else _rule
42
+ elif isinstance(coeffs, (list, tuple)):
43
+ self.c = list(coeffs)
44
+ self._rule = _rule
45
+ else:
46
+ self.c = [coeffs]
47
+ self._rule = _rule
48
+ if not self.c:
49
+ raise ValueError("Taylor needs at least one coefficient.")
50
+
51
+ # -- coefficient access --------------------------------------------------
52
+ @property
53
+ def primal(self):
54
+ """The constant term ``c[0]``."""
55
+ return self.c[0]
56
+
57
+ @property
58
+ def order(self) -> int:
59
+ """Currently materialized truncation order."""
60
+ return len(self.c) - 1
61
+
62
+ def ensure(self, n: int) -> "Taylor":
63
+ """Materialize coefficients up to degree ``n`` (lazily if possible)."""
64
+ cur = len(self.c) - 1
65
+ if n > cur:
66
+ if self._rule is None:
67
+ self.c.extend([0] * (n - cur))
68
+ else:
69
+ for k in range(cur + 1, n + 1):
70
+ self.c.append(self._rule(k))
71
+ return self
72
+
73
+ def coeff(self, k: int):
74
+ if k < 0:
75
+ raise IndexError(k)
76
+ if k >= len(self.c):
77
+ if self._rule is None:
78
+ return 0
79
+ self.ensure(k)
80
+ return self.c[k]
81
+
82
+ def __getitem__(self, k):
83
+ if isinstance(k, slice):
84
+ if k.stop is not None and k.stop > len(self.c):
85
+ self.ensure(k.stop - 1)
86
+ return self.c[k]
87
+ return self.coeff(k)
88
+
89
+ def eval(self, h, order: Optional[int] = None):
90
+ """Evaluate the polynomial at ``h`` with Horner's scheme."""
91
+ if order is not None:
92
+ self.ensure(order)
93
+ coeffs = self.c[: order + 1]
94
+ else:
95
+ coeffs = self.c
96
+ acc = 0
97
+ for c in reversed(coeffs):
98
+ acc = acc * h + c
99
+ return acc
100
+
101
+ def derivative(self, k: int):
102
+ """Return the unnormalized ``k``-th derivative at 0: ``k! * c[k]``."""
103
+ return self.coeff(k) * math.factorial(k)
104
+
105
+ def diff(self) -> "Taylor":
106
+ """Formal derivative ``x'(t)`` as a new lazy Taylor."""
107
+ return Taylor([self.coeff(1)], _rule=lambda k: (k + 1) * self.coeff(k + 1))
108
+
109
+ def integrate(self, c0=0) -> "Taylor":
110
+ """Formal integral with constant term ``c0``."""
111
+ return Taylor([c0], _rule=lambda k: self.coeff(k - 1) / k)
112
+
113
+ def __repr__(self) -> str:
114
+ return "Taylor([%s])" % ", ".join(repr(v) for v in self.c)
115
+
116
+ # -- arithmetic ----------------------------------------------------------
117
+ def __add__(self, other):
118
+ if not isinstance(other, Taylor):
119
+ return Taylor([self.c[0] + other], _rule=lambda k: self.coeff(k))
120
+ out = Taylor(
121
+ [self.primal + other.primal],
122
+ _rule=lambda k: self.coeff(k) + other.coeff(k),
123
+ )
124
+ out.ensure(max(self.order, other.order))
125
+ return out
126
+
127
+ __radd__ = __add__
128
+
129
+ def __neg__(self):
130
+ return Taylor([-self.primal], _rule=lambda k: -self.coeff(k))
131
+
132
+ def __sub__(self, other):
133
+ return self + (-_as_taylor(other))
134
+
135
+ def __rsub__(self, other):
136
+ return _as_taylor(other) - self
137
+
138
+ def __mul__(self, other):
139
+ if not isinstance(other, Taylor):
140
+ return Taylor([self.primal * other], _rule=lambda k: self.coeff(k) * other)
141
+ out = Taylor(
142
+ [self.primal * other.primal],
143
+ _rule=lambda k: sum(
144
+ self.coeff(j) * other.coeff(k - j) for j in range(k + 1)
145
+ ),
146
+ )
147
+ out.ensure(max(self.order, other.order))
148
+ return out
149
+
150
+ __rmul__ = __mul__
151
+
152
+ def __truediv__(self, other):
153
+ if not isinstance(other, Taylor):
154
+ if other == 0:
155
+ raise ZeroDivisionError("division of Taylor by zero")
156
+ return Taylor([self.primal / other], _rule=lambda k: self.coeff(k) / other)
157
+ y0 = other.primal
158
+ if y0 == 0:
159
+ raise ZeroDivisionError("division by Taylor with zero constant term")
160
+ out = Taylor([self.primal / y0])
161
+ out._rule = lambda k: (
162
+ self.coeff(k)
163
+ - sum(other.coeff(j) * out.coeff(k - j) for j in range(1, k + 1))
164
+ ) / y0
165
+ out.ensure(max(self.order, other.order))
166
+ return out
167
+
168
+ def __rtruediv__(self, other):
169
+ return _as_taylor(other) / self
170
+
171
+ def __pow__(self, p):
172
+ return power(self, p)
173
+
174
+ # -- elementary functions ------------------------------------------------
175
+ def exp(self) -> "Taylor":
176
+ out = Taylor([math.exp(self.primal)])
177
+ out._rule = (
178
+ lambda k: sum(j * self.coeff(j) * out.coeff(k - j) for j in range(1, k + 1))
179
+ / k
180
+ )
181
+ out.ensure(self.order)
182
+ return out
183
+
184
+ def log(self) -> "Taylor":
185
+ x0 = self.primal
186
+ if x0 <= 0:
187
+ raise ValueError("log requires a positive constant term.")
188
+ out = Taylor([math.log(x0)])
189
+ out._rule = lambda k: (
190
+ self.coeff(k)
191
+ - sum(j * out.coeff(j) * self.coeff(k - j) for j in range(1, k)) / k
192
+ ) / x0
193
+ out.ensure(self.order)
194
+ return out
195
+
196
+ def sqrt(self) -> "Taylor":
197
+ return power(self, 0.5)
198
+
199
+ def sin_cos(self):
200
+ s = Taylor([math.sin(self.primal)])
201
+ c = Taylor([math.cos(self.primal)])
202
+ s._rule = (
203
+ lambda k: sum(j * self.coeff(j) * c.coeff(k - j) for j in range(1, k + 1))
204
+ / k
205
+ )
206
+ c._rule = (
207
+ lambda k: -sum(j * self.coeff(j) * s.coeff(k - j) for j in range(1, k + 1))
208
+ / k
209
+ )
210
+ s.ensure(self.order)
211
+ c.ensure(self.order)
212
+ return s, c
213
+
214
+ def sin(self) -> "Taylor":
215
+ return self.sin_cos()[0]
216
+
217
+ def cos(self) -> "Taylor":
218
+ return self.sin_cos()[1]
219
+
220
+ def tan(self) -> "Taylor":
221
+ return self.sin() / self.cos()
222
+
223
+
224
+ # -- helpers -----------------------------------------------------------------
225
+ def _as_taylor(x) -> Taylor:
226
+ return x if isinstance(x, Taylor) else Taylor([x])
227
+
228
+
229
+ def variable(x0, order: Optional[int] = None) -> Taylor:
230
+ """The independent variable, ``t = x0 + h``."""
231
+ t = Taylor([x0, 1] if order != 0 else [x0])
232
+ if order:
233
+ t.ensure(order)
234
+ return t
235
+
236
+
237
+ def constant(c, order: Optional[int] = None) -> Taylor:
238
+ t = Taylor([c])
239
+ if order:
240
+ t.ensure(order)
241
+ return t
242
+
243
+
244
+ def power(x, p) -> Taylor:
245
+ """``x ** p`` for a Taylor base and scalar (or Taylor) exponent."""
246
+ if isinstance(p, Taylor):
247
+ return (p * x.log()).exp()
248
+ if not isinstance(x, Taylor):
249
+ return x ** p
250
+ if isinstance(p, int):
251
+ if p == 0:
252
+ return constant(1, order=x.order)
253
+ if p < 0:
254
+ return constant(1) / power(x, -p)
255
+ result = constant(1)
256
+ base = x
257
+ e = p
258
+ while e:
259
+ if e & 1:
260
+ result = result * base
261
+ base = base * base
262
+ e >>= 1
263
+ return result
264
+ x0 = x.primal
265
+ p_plus_1 = p + 1
266
+ out = Taylor([x0 ** p])
267
+ out._rule = lambda k: (
268
+ sum((j * p_plus_1 - k) * x.coeff(j) * out.coeff(k - j) for j in range(1, k + 1))
269
+ / (k * x0)
270
+ )
271
+ out.ensure(x.order)
272
+ return out
273
+
274
+
275
+ def exp(x):
276
+ return _as_taylor(x).exp()
277
+
278
+
279
+ def log(x):
280
+ return _as_taylor(x).log()
281
+
282
+
283
+ def sqrt(x):
284
+ return _as_taylor(x).sqrt()
285
+
286
+
287
+ def sin(x):
288
+ return _as_taylor(x).sin()
289
+
290
+
291
+ def cos(x):
292
+ return _as_taylor(x).cos()
293
+
294
+
295
+ def tan(x):
296
+ return _as_taylor(x).tan()
297
+
298
+
299
+ # -- JAX-style transforms ----------------------------------------------------
300
+ def derivative(f: Callable, k: int = 1) -> Callable:
301
+ """Transform ``f`` into a function returning its ``k``-th derivative."""
302
+ if k < 0:
303
+ raise ValueError("k must be >= 0")
304
+
305
+ def grad_k(x0):
306
+ return f(variable(x0, order=k)).derivative(k)
307
+
308
+ grad_k.__name__ = getattr(f, "__name__", "grad_k")
309
+ return grad_k
310
+
311
+
312
+ def taylor_series(f: Callable, order: int) -> Callable:
313
+ """Transform ``f`` into a function returning ``[c_0, ..., c_order]``."""
314
+ if order < 0:
315
+ raise ValueError("order must be >= 0")
316
+
317
+ def series_fn(x0):
318
+ return list(f(variable(x0, order=order)).ensure(order).c)
319
+
320
+ series_fn.__name__ = getattr(f, "__name__", "series_fn")
321
+ return series_fn
322
+
323
+
324
+ def ode_step(f: Callable, order: int = 10) -> Callable:
325
+ """Transform a vector field ``f`` into an ``order``-th Taylor step.
326
+
327
+ The returned function ``step(x, dt, t=0.0)`` advances the state ``x``
328
+ (a scalar or a sequence) by a single Taylor-method step of size ``dt``.
329
+ The vector field is evaluated once per step on lazy Taylor jets.
330
+ """
331
+ if order < 1:
332
+ raise ValueError("order must be >= 1")
333
+
334
+ def step(x, dt, t=0.0):
335
+ scalar = not isinstance(x, (list, tuple))
336
+ xs = [x] if scalar else list(x)
337
+ jets = [Taylor([v]) for v in xs]
338
+ rhs = [f(jets[0])] if scalar else list(f(jets))
339
+ for jet, r in zip(jets, rhs):
340
+ jet._rule = (lambda k, r=r: r.coeff(k - 1) / k)
341
+ out = [jet.eval(dt, order=order) for jet in jets]
342
+ return out[0] if scalar else out
343
+
344
+ step.__name__ = "step"
345
+ return step
@@ -0,0 +1,85 @@
1
+ Metadata-Version: 2.5
2
+ Name: taylorize
3
+ Version: 0.0.1
4
+ Summary: Lazy truncated Taylor series: higher-order automatic differentiation and arbitrary-precision Taylor-method ODE integration.
5
+ Project-URL: Homepage, https://github.com/yonesuke/taylorize
6
+ Project-URL: Repository, https://github.com/yonesuke/taylorize
7
+ Project-URL: Issues, https://github.com/yonesuke/taylorize/issues
8
+ Author: Ryosuke Yoneda
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: arbitrary-precision,automatic-differentiation,higher-order,ode,taylor-series
12
+ Classifier: Development Status :: 2 - Pre-Alpha
13
+ Classifier: Intended Audience :: Science/Research
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
17
+ Requires-Python: >=3.9
18
+ Description-Content-Type: text/markdown
19
+
20
+ # taylorize
21
+
22
+ Lazy truncated Taylor series for **higher-order automatic differentiation** and
23
+ **arbitrary-precision Taylor-method ODE integration**.
24
+
25
+ `taylorize` represents a smooth function by its normalized Taylor coefficients
26
+ `c[k] = x^(k)(0) / k!` and computes them **lazily**, so you can build an
27
+ expression once and then ask for any order afterwards.
28
+
29
+ > Status: **0.0.1 (pre-alpha seed)**. The API is minimal and will change.
30
+
31
+ ## Install
32
+
33
+ ```bash
34
+ pip install taylorize
35
+ ```
36
+
37
+ ## Quickstart
38
+
39
+ ```python
40
+ from taylorize import derivative, taylor_series, exp, sin, ode_step
41
+
42
+ def f(x):
43
+ return exp(sin(x))
44
+
45
+ # 5th derivative of exp(sin(x)) at 0
46
+ print(derivative(f, k=5)(0.0)) # -8.0
47
+
48
+ # Taylor coefficients of sin(x)
49
+ print(taylor_series(sin, order=9)(0.0)) # [0, 1, 0, -1/6, ...]
50
+
51
+ # One 16th-order Taylor step of the simple pendulum
52
+ def pendulum(u):
53
+ theta, omega = u[0], u[1]
54
+ return [omega, -sin(theta)]
55
+
56
+ step = ode_step(pendulum, order=16)
57
+ print(step([1.0, 0.0], 0.1))
58
+ ```
59
+
60
+ See [`examples/basic.py`](examples/basic.py).
61
+
62
+ ## Idea
63
+
64
+ The core object is a truncated power series in a nilpotent direction `h`:
65
+
66
+ ```
67
+ x(h) = c[0] + c[1] h + c[2] h^2 + ... + c[N] h^N
68
+ ```
69
+
70
+ Every elementary function is just a recurrence on these coefficients
71
+ (e.g. `exp`: `c[k] = (1/k) * sum(j * x[j] * c[k-j])`), and the chain rule turns
72
+ any composition into ring operations on series. The same machinery integrates
73
+ ODEs one Taylor step at a time.
74
+
75
+ ## Roadmap
76
+
77
+ - Backend-agnostic coefficients (`math`, `numpy`, `mpmath`, `torch`, `jax`, `sympy`)
78
+ - Automatic interception of `math.*` / `np.*` / `torch.*` / `sp.*`
79
+ - Functions defined by ODEs (`from_ode`) and coupled systems
80
+ - Special functions (`erf`, Airy, Bessel, Jacobi, ...)
81
+ - Adaptive-step Taylor ODE solver
82
+
83
+ ## License
84
+
85
+ MIT
@@ -0,0 +1,6 @@
1
+ taylorize/__init__.py,sha256=tmDWM513hO_VI3YrCx82HdvPHDQHv9NkJpawtV17eWY,788
2
+ taylorize/core.py,sha256=__ZgTs0Tt9c1H5Ja51VrQkpy2RFBdanDw65MIilOvIw,10098
3
+ taylorize-0.0.1.dist-info/METADATA,sha256=23rV_JMQpgCi-a6ugx8KrIe2JIeFB1DEkl7j5kHcpZE,2622
4
+ taylorize-0.0.1.dist-info/WHEEL,sha256=2iZrJkDIkeDh0k9SikKMqFuRP-ua6mb4Ir1AsER4BEw,87
5
+ taylorize-0.0.1.dist-info/licenses/LICENSE,sha256=he91dD1MWO8087zdNhDvn4z-XVWS5joxWUBAFESRLS0,1071
6
+ taylorize-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.33.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Ryosuke Yoneda
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.