pycodemath 0.2.0__tar.gz

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.
Files changed (40) hide show
  1. pycodemath-0.2.0/LICENSE +21 -0
  2. pycodemath-0.2.0/PKG-INFO +327 -0
  3. pycodemath-0.2.0/README.md +301 -0
  4. pycodemath-0.2.0/pycodemath/__init__.py +91 -0
  5. pycodemath-0.2.0/pycodemath/__main__.py +10 -0
  6. pycodemath-0.2.0/pycodemath/bench/__init__.py +0 -0
  7. pycodemath-0.2.0/pycodemath/bench/benchmark.py +61 -0
  8. pycodemath-0.2.0/pycodemath/cli/__init__.py +0 -0
  9. pycodemath-0.2.0/pycodemath/cli/mcp_server.py +82 -0
  10. pycodemath-0.2.0/pycodemath/cli/repl.py +620 -0
  11. pycodemath-0.2.0/pycodemath/codegen/__init__.py +0 -0
  12. pycodemath-0.2.0/pycodemath/codegen/emit.py +554 -0
  13. pycodemath-0.2.0/pycodemath/codegen/pipeline.py +139 -0
  14. pycodemath-0.2.0/pycodemath/core/__init__.py +0 -0
  15. pycodemath-0.2.0/pycodemath/core/errors.py +15 -0
  16. pycodemath-0.2.0/pycodemath/core/ir.py +307 -0
  17. pycodemath-0.2.0/pycodemath/engine/__init__.py +0 -0
  18. pycodemath-0.2.0/pycodemath/engine/linalg.py +197 -0
  19. pycodemath-0.2.0/pycodemath/engine/numerics.py +973 -0
  20. pycodemath-0.2.0/pycodemath/engine/ode.py +1279 -0
  21. pycodemath-0.2.0/pycodemath/engine/symbolic.py +204 -0
  22. pycodemath-0.2.0/pycodemath/frontend/__init__.py +0 -0
  23. pycodemath-0.2.0/pycodemath/frontend/parser.py +299 -0
  24. pycodemath-0.2.0/pycodemath.egg-info/PKG-INFO +327 -0
  25. pycodemath-0.2.0/pycodemath.egg-info/SOURCES.txt +38 -0
  26. pycodemath-0.2.0/pycodemath.egg-info/dependency_links.txt +1 -0
  27. pycodemath-0.2.0/pycodemath.egg-info/entry_points.txt +3 -0
  28. pycodemath-0.2.0/pycodemath.egg-info/requires.txt +10 -0
  29. pycodemath-0.2.0/pycodemath.egg-info/top_level.txt +1 -0
  30. pycodemath-0.2.0/pyproject.toml +46 -0
  31. pycodemath-0.2.0/setup.cfg +4 -0
  32. pycodemath-0.2.0/tests/test_cli.py +265 -0
  33. pycodemath-0.2.0/tests/test_codegen.py +280 -0
  34. pycodemath-0.2.0/tests/test_core.py +284 -0
  35. pycodemath-0.2.0/tests/test_coverage_r2.py +26 -0
  36. pycodemath-0.2.0/tests/test_linalg.py +53 -0
  37. pycodemath-0.2.0/tests/test_numerics.py +505 -0
  38. pycodemath-0.2.0/tests/test_ode.py +858 -0
  39. pycodemath-0.2.0/tests/test_parser.py +160 -0
  40. pycodemath-0.2.0/tests/test_symbolic.py +87 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 cybersora
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.
@@ -0,0 +1,327 @@
1
+ Metadata-Version: 2.4
2
+ Name: pycodemath
3
+ Version: 0.2.0
4
+ Summary: Token-efficient exact math for AI agents: symbolic engine, ODE suite, optimizer and an optimized-code generator (SymPy + NumPy).
5
+ Author: cybersora
6
+ License: MIT
7
+ Keywords: math,symbolic,ode,codegen,mcp,ai-agents,sympy,numpy
8
+ Classifier: License :: OSI Approved :: MIT License
9
+ Classifier: Programming Language :: Python :: 3.11
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
14
+ Requires-Python: >=3.11
15
+ Description-Content-Type: text/markdown
16
+ License-File: LICENSE
17
+ Requires-Dist: sympy>=1.12
18
+ Requires-Dist: numpy>=1.24
19
+ Requires-Dist: mpmath>=1.3
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest>=7; extra == "dev"
22
+ Requires-Dist: mypy>=1.8; extra == "dev"
23
+ Provides-Extra: mcp
24
+ Requires-Dist: mcp>=1.0; extra == "mcp"
25
+ Dynamic: license-file
26
+
27
+ <p align="center">
28
+ <picture>
29
+ <source media="(prefers-color-scheme: dark)" srcset="assets/pycodemath-lockup.svg">
30
+ <img alt="pycodemath" src="assets/pycodemath-lockup-light.svg" width="420">
31
+ </picture>
32
+ </p>
33
+
34
+ <p align="center">
35
+ <strong>Token-efficient exact math for AI agents.</strong>
36
+ </p>
37
+
38
+ <p align="center">
39
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-e11d33.svg"></a>
40
+ <img alt="Python 3.11+" src="https://img.shields.io/badge/python-3.11%2B-3776ab.svg">
41
+ <img alt="230 tests passing" src="https://img.shields.io/badge/tests-230%20passing-2ea043.svg">
42
+ <img alt="mypy: clean" src="https://img.shields.io/badge/mypy-clean-2ea043.svg">
43
+ <a href="https://github.com/cybersora9/pycodemath/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/cybersora9/pycodemath/actions/workflows/ci.yml/badge.svg"></a>
44
+ </p>
45
+
46
+ Write math in a few characters, get an exact answer or standalone, optimized
47
+ Python/NumPy code back — instead of asking a language model to "do arithmetic
48
+ in its head" or to hand-write numerical loops.
49
+
50
+ Built as a thin, disciplined layer over SymPy + NumPy:
51
+
52
+ 1. **Cut tokens** — one short command in, one exact result out. Perfect as an
53
+ agent tool (MCP server included).
54
+ 2. **Better code than naive Python** — the generator applies symbolic
55
+ simplification + common-subexpression elimination (CSE) before emitting
56
+ code (measured ~1.7x faster than naive expansion on repeated
57
+ subexpressions).
58
+
59
+ ```
60
+ text → [parser] → IR (expression tree) → [engine] evaluate / simplify / solve
61
+ → [generator] IR → CSE → Python/NumPy source
62
+ ```
63
+
64
+ ## Install
65
+
66
+ ```bash
67
+ pip install -e . # core: sympy + numpy
68
+ pip install -e .[mcp] # + MCP server for AI agents
69
+ pip install -e .[dev] # + pytest
70
+ ```
71
+
72
+ Requires Python ≥ 3.11.
73
+
74
+ ## Quick start
75
+
76
+ ### One-shot CLI (scriptable)
77
+
78
+ Every command below is a real invocation with its real output.
79
+
80
+ ```console
81
+ $ python -m pycodemath "diff sin(x)*x dx"
82
+ x*cos(x) + sin(x)
83
+
84
+ $ python -m pycodemath "integrate 2*x dx"
85
+ x**2
86
+
87
+ $ python -m pycodemath "solve x^2 - 4 for x"
88
+ -2, 2
89
+
90
+ $ python -m pycodemath "sin(x)^2 + cos(x)^2"
91
+ 1
92
+
93
+ $ python -m pycodemath "eig [[2,1],[1,2]]"
94
+ 3, 1
95
+
96
+ $ python -m pycodemath "solve_nd x^2+y^2-4; x-y for x,y at 1,1"
97
+ x = 1.4142135623746899, y = 1.4142135623746899
98
+ ```
99
+
100
+ Errors go to stderr with exit code 1, results to stdout with exit code 0 —
101
+ safe to call from scripts and agent tools. On Windows consoles set
102
+ `PYTHONUTF8=1`.
103
+
104
+ ### REPL
105
+
106
+ ```bash
107
+ python -m pycodemath # or just: pycodemath
108
+ ```
109
+
110
+ Type `help` for the full command table (derivatives, integrals, equation
111
+ solving, matrices, root finding, optimization, the whole ODE suite, and code
112
+ generation).
113
+
114
+ ### MCP server (math for any AI agent)
115
+
116
+ ```bash
117
+ pip install -e .[mcp]
118
+ claude mcp add pycodemath -- python -m pycodemath.cli.mcp_server
119
+ ```
120
+
121
+ Exposes one tool, `math_eval`, that accepts the same commands as the REPL —
122
+ an agent sends `diff sin(x)*x dx` and receives `x*cos(x) + sin(x)` exactly,
123
+ with zero mental arithmetic.
124
+
125
+ ### Use it as a Claude Code skill
126
+
127
+ The MCP server above is the portable path — it plugs into **any** MCP client.
128
+ If you specifically use [Claude Code](https://claude.com/claude-code), you can
129
+ also wire Pycodemath in as a **skill**, so Claude reaches for the engine on its
130
+ own whenever a prompt needs real math (no MCP process to keep running).
131
+
132
+ **What it changes:** instead of computing "in its head" — where a large model
133
+ can quietly get arithmetic, an integral, or an eigenvalue wrong — Claude shells
134
+ out to the engine and pastes back an exact SymPy/NumPy result. One short command
135
+ in, one exact line out: fewer tokens, no silent mistakes.
136
+
137
+ **Deploy (once):**
138
+
139
+ ```bash
140
+ pip install pycodemath # or: pip install -e . (from a clone)
141
+ mkdir -p ~/.claude/skills/pycodemath
142
+ ```
143
+
144
+ Save the following as `~/.claude/skills/pycodemath/SKILL.md`:
145
+
146
+ ```markdown
147
+ ---
148
+ name: pycodemath
149
+ description: Compute math with the local Pycodemath engine (SymPy+NumPy) instead
150
+ of in your head — derivatives, integrals, solving equations and systems
151
+ (linear and nonlinear), determinants/inverses/eigenvalues, gradients/Jacobians/
152
+ Hessians, function minima, ODEs, and optimized Python/NumPy code generation
153
+ (CSE). Use whenever the user asks to compute or verify symbolic/numerical math,
154
+ or to generate code from a formula.
155
+ ---
156
+
157
+ # Pycodemath — local math engine
158
+
159
+ One command = one call (the package is pip-installed, so any working directory):
160
+
161
+ python -m pycodemath "<command>"
162
+
163
+ Result goes to stdout (exit 0); errors to stderr (exit 1). Power notation: `^` or `**`.
164
+ On Windows consoles, set `PYTHONUTF8=1`.
165
+
166
+ ## Commands
167
+
168
+ | Command | Example |
169
+ |---|---|
170
+ | `<expression>` — simplify | `python -m pycodemath "sin(x)^2 + cos(x)^2"` → `1` |
171
+ | `diff <expr> d<var>` — derivative | `"diff sin(x)*x dx"` → `x*cos(x) + sin(x)` |
172
+ | `integrate <expr> d<var>` — symbolic integral | `"integrate 2*x dx"` → `x**2` |
173
+ | `solve <expr> for <var>` — solve = 0 | `"solve x^2-4 for x"` → `-2, 2` |
174
+ | `code <expr>` — CSE-optimized NumPy code | `"code (sin(x)+cos(x))^2"` |
175
+ | `det / inv / transpose / eig <A>` | `"eig [[2,1],[1,2]]"` → `3, 1` |
176
+ | `solve_system <A> = <b>` — linear system | `"solve_system [[2,1],[1,3]] = [3,5]"` |
177
+ | `root <expr> for <var> at <x0>` — numeric root | `"root x^2-2 for x at 1"` |
178
+ | `min <expr> for <var> at <x0> [method newton|bfgs]` — 1D minimum | `"min (x-3)^2 for x at 0"` |
179
+ | `grad <expr> for <x,y,...>` — symbolic gradient | `"grad x^2*y for x,y"` |
180
+ | `solve_nd <f1>; <f2> for <x,y> at <x0,y0>` — nonlinear system | `"solve_nd x^2+y^2-4; x-y for x,y at 1,1"` |
181
+ | `min_nd <expr> for <x,y> at <x0,y0> [method newton|bfgs]` — N-D minimum | `"min_nd (1-x)^2+100*(y-x^2)^2 for x,y at -1.2,1 method bfgs"` |
182
+ | limits / series / sums / ODEs | `"limit sin(x)/x for x to 0"`, `"sum 1/k^2 for k from 1 to oo"` |
183
+
184
+ Type `help` in the REPL (`python -m pycodemath`) for the full command table.
185
+
186
+ ## Rules
187
+
188
+ - **When to use:** the user wants a concrete math result (derivative, integral,
189
+ equation, matrix, minimum, ODE) or optimized code from a formula. The engine is
190
+ exact — trust its output over mental arithmetic.
191
+ - **When not to use:** trivial arithmetic or conceptual questions with no compute.
192
+ - A `error: ...` line on stderr (exit 1) usually means a typo in the command, a
193
+ numerical method that did not converge, or no real solution — read the message,
194
+ it is specific.
195
+ ```
196
+
197
+ Restart Claude Code (or open a new session) and it will invoke the skill
198
+ automatically when a task needs exact math. To confirm it registered, run `/help`
199
+ and look for `pycodemath` in the skills list.
200
+
201
+ ## Limits, series and symbolic sums
202
+
203
+ Beyond `diff`/`integrate`/`solve`, the engine handles limits (including
204
+ one-sided and at infinity), Taylor/Laurent expansions and symbolic summation
205
+ — finite or infinite. All real invocations with real outputs:
206
+
207
+ ```console
208
+ $ python -m pycodemath "limit (1+1/n)^n for n to oo"
209
+ E
210
+
211
+ $ python -m pycodemath "series exp(x) for x n 4"
212
+ x**3/6 + x**2/2 + x + 1
213
+
214
+ $ python -m pycodemath "sum k for k from 1 to n"
215
+ n**2/2 + n/2
216
+
217
+ $ python -m pycodemath "sum 1/k^2 for k from 1 to oo"
218
+ pi**2/6
219
+ ```
220
+
221
+ A divergent sum or a nonexistent limit refuses with a readable error instead
222
+ of returning a symbolic echo.
223
+
224
+ ## Optimization: Newton and BFGS where gradient descent stalls
225
+
226
+ `min` / `min_nd` default to plain gradient descent; `method newton|bfgs`
227
+ switches to second-order methods with Armijo backtracking line search.
228
+ On the Rosenbrock valley (start `(-1.2, 1)`) gradient descent refuses after
229
+ 10 000 iterations, while BFGS converges in 36:
230
+
231
+ ```console
232
+ $ python -m pycodemath "min_nd (1-x)^2 + 100*(y-x^2)^2 for x,y at -1.2,1 method bfgs"
233
+ x = 0.999999999999454, y = 0.9999999999989762
234
+ ```
235
+
236
+ ## ODE suite
237
+
238
+ Symbolic (`dsolve`) plus a full numerical toolbox — scalar and systems,
239
+ forward and backward in time (`t1 < t0`), all refusing to silently jump over
240
+ singularities (a readable error instead of garbage):
241
+
242
+ | solver | what it does |
243
+ | --- | --- |
244
+ | `solve_ode_num` | classic fixed-step RK4 |
245
+ | `solve_ode_adaptive` | Dormand–Prince 5(4), adaptive step (like `ode45`) |
246
+ | `solve_ode_dense` | adaptive + dense output: callable `sol(t)` anywhere |
247
+ | `solve_ode_events` | event detection `g(t,y)=0` (+ terminal events) |
248
+ | `solve_ode_stiff` | implicit BDF2 + Newton for stiff equations |
249
+ | `solve_ode_stiff_adaptive` | **variable-step BDF2** with local error control |
250
+ | `solve_ode_system_*` | vector variants of all of the above |
251
+
252
+ Adaptive integration of a smooth problem takes 41 steps at `rtol 1e-8`:
253
+
254
+ ```console
255
+ $ python -m pycodemath "ode_adaptive y*cos(t) for y(t) from 0 to 5 at 1 rtol 1e-8"
256
+ y(5) = 0.3833049965035854 (41 adaptive steps)
257
+ ```
258
+
259
+ On the stiff classic `y' = -1000(y - cos t)` the explicit pair is limited by
260
+ *stability* (378 steps at `rtol 1e-6`; with the same budget fixed-step RK4
261
+ returns astronomically wrong finite values). The adaptive BDF gets there in
262
+ 313 steps — and starting on the slow manifold, where stiffness is pure, in
263
+ **58 steps vs 357**:
264
+
265
+ ```console
266
+ $ python -m pycodemath "odestiff_adaptive -1000*(y-cos(t)) for y(t) from 0 to 1 at 0"
267
+ y(1) = 0.5411432587540607 (adaptive BDF, 313 implicit steps)
268
+ ```
269
+
270
+ The system variant handles the Van der Pol oscillator with μ=1000 over
271
+ `[0, 2000]` in 5 332 steps (~1 s) — an explicit method would need ≥ 2 000 000.
272
+
273
+ ## Code generation
274
+
275
+ `code <expr>` emits standalone, CSE-optimized NumPy source (real output):
276
+
277
+ ```console
278
+ $ python -m pycodemath "code (sin(x)+cos(x))^2 + (sin(x)+cos(x))^3"
279
+ import numpy as np
280
+
281
+
282
+ def f(x):
283
+ """Pycodemath: f(x) — NumPy code."""
284
+ _c0 = np.sin(x + (1/4)*np.pi)
285
+ return 2*_c0**2*(np.sqrt(2)*_c0 + 1)
286
+ ```
287
+
288
+ The Python API also generates standalone ODE integrators — including an
289
+ adaptive one and one with **dense output** whose emitted interpolant is
290
+ bit-for-bit identical to the engine (measured max difference: `0.0` on nodes
291
+ and off-node grids, forward and backward):
292
+
293
+ ```python
294
+ from pycodemath import parse, generate_ode_dense
295
+
296
+ art = generate_ode_dense(parse("y*cos(t)"), "t", "y")
297
+ sol = art(1.0, 0.0, 5.0, 1e-8) # standalone DOPRI5, returns an interpolant
298
+ sol(2.5) # -> 1.8193369962907706
299
+ len(sol.ts) # -> 42 accepted nodes
300
+ ```
301
+
302
+ Event detection from the API:
303
+
304
+ ```python
305
+ from pycodemath import parse, solve_ode_events
306
+
307
+ ev = solve_ode_events(parse("cos(t)"), "t", 0.0, (0.0, 10.0), parse("y"), rtol=1e-8)
308
+ ev.event_times # -> [3.141593, 6.283185, 9.424778] (π, 2π, 3π)
309
+ ```
310
+
311
+ ## Design contracts
312
+
313
+ - One IR (`Expr`/`Matrix`) shared by the engine and the generator.
314
+ - Numerical loops run on compiled functions (`lambdify` + LRU cache) —
315
+ zero SymPy calls per iteration.
316
+ - Divergence or a domain problem raises a readable `PycodemathError` —
317
+ never NaN/garbage in a result.
318
+ - The parser resolves only a whitelist of mathematical functions — unknown
319
+ names become symbols, not Python code; string literals and attribute
320
+ access are rejected outright, and evaluation cost is bounded so a single
321
+ expression (e.g. `9**9**9`) can't exhaust memory.
322
+ - Tests measure real numbers first, then assert them with a margin —
323
+ **230 tests**, all green, on Ubuntu and Windows (CI + mypy included).
324
+
325
+ ## License
326
+
327
+ MIT — see [LICENSE](LICENSE).
@@ -0,0 +1,301 @@
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="assets/pycodemath-lockup.svg">
4
+ <img alt="pycodemath" src="assets/pycodemath-lockup-light.svg" width="420">
5
+ </picture>
6
+ </p>
7
+
8
+ <p align="center">
9
+ <strong>Token-efficient exact math for AI agents.</strong>
10
+ </p>
11
+
12
+ <p align="center">
13
+ <a href="LICENSE"><img alt="License: MIT" src="https://img.shields.io/badge/license-MIT-e11d33.svg"></a>
14
+ <img alt="Python 3.11+" src="https://img.shields.io/badge/python-3.11%2B-3776ab.svg">
15
+ <img alt="230 tests passing" src="https://img.shields.io/badge/tests-230%20passing-2ea043.svg">
16
+ <img alt="mypy: clean" src="https://img.shields.io/badge/mypy-clean-2ea043.svg">
17
+ <a href="https://github.com/cybersora9/pycodemath/actions/workflows/ci.yml"><img alt="CI" src="https://github.com/cybersora9/pycodemath/actions/workflows/ci.yml/badge.svg"></a>
18
+ </p>
19
+
20
+ Write math in a few characters, get an exact answer or standalone, optimized
21
+ Python/NumPy code back — instead of asking a language model to "do arithmetic
22
+ in its head" or to hand-write numerical loops.
23
+
24
+ Built as a thin, disciplined layer over SymPy + NumPy:
25
+
26
+ 1. **Cut tokens** — one short command in, one exact result out. Perfect as an
27
+ agent tool (MCP server included).
28
+ 2. **Better code than naive Python** — the generator applies symbolic
29
+ simplification + common-subexpression elimination (CSE) before emitting
30
+ code (measured ~1.7x faster than naive expansion on repeated
31
+ subexpressions).
32
+
33
+ ```
34
+ text → [parser] → IR (expression tree) → [engine] evaluate / simplify / solve
35
+ → [generator] IR → CSE → Python/NumPy source
36
+ ```
37
+
38
+ ## Install
39
+
40
+ ```bash
41
+ pip install -e . # core: sympy + numpy
42
+ pip install -e .[mcp] # + MCP server for AI agents
43
+ pip install -e .[dev] # + pytest
44
+ ```
45
+
46
+ Requires Python ≥ 3.11.
47
+
48
+ ## Quick start
49
+
50
+ ### One-shot CLI (scriptable)
51
+
52
+ Every command below is a real invocation with its real output.
53
+
54
+ ```console
55
+ $ python -m pycodemath "diff sin(x)*x dx"
56
+ x*cos(x) + sin(x)
57
+
58
+ $ python -m pycodemath "integrate 2*x dx"
59
+ x**2
60
+
61
+ $ python -m pycodemath "solve x^2 - 4 for x"
62
+ -2, 2
63
+
64
+ $ python -m pycodemath "sin(x)^2 + cos(x)^2"
65
+ 1
66
+
67
+ $ python -m pycodemath "eig [[2,1],[1,2]]"
68
+ 3, 1
69
+
70
+ $ python -m pycodemath "solve_nd x^2+y^2-4; x-y for x,y at 1,1"
71
+ x = 1.4142135623746899, y = 1.4142135623746899
72
+ ```
73
+
74
+ Errors go to stderr with exit code 1, results to stdout with exit code 0 —
75
+ safe to call from scripts and agent tools. On Windows consoles set
76
+ `PYTHONUTF8=1`.
77
+
78
+ ### REPL
79
+
80
+ ```bash
81
+ python -m pycodemath # or just: pycodemath
82
+ ```
83
+
84
+ Type `help` for the full command table (derivatives, integrals, equation
85
+ solving, matrices, root finding, optimization, the whole ODE suite, and code
86
+ generation).
87
+
88
+ ### MCP server (math for any AI agent)
89
+
90
+ ```bash
91
+ pip install -e .[mcp]
92
+ claude mcp add pycodemath -- python -m pycodemath.cli.mcp_server
93
+ ```
94
+
95
+ Exposes one tool, `math_eval`, that accepts the same commands as the REPL —
96
+ an agent sends `diff sin(x)*x dx` and receives `x*cos(x) + sin(x)` exactly,
97
+ with zero mental arithmetic.
98
+
99
+ ### Use it as a Claude Code skill
100
+
101
+ The MCP server above is the portable path — it plugs into **any** MCP client.
102
+ If you specifically use [Claude Code](https://claude.com/claude-code), you can
103
+ also wire Pycodemath in as a **skill**, so Claude reaches for the engine on its
104
+ own whenever a prompt needs real math (no MCP process to keep running).
105
+
106
+ **What it changes:** instead of computing "in its head" — where a large model
107
+ can quietly get arithmetic, an integral, or an eigenvalue wrong — Claude shells
108
+ out to the engine and pastes back an exact SymPy/NumPy result. One short command
109
+ in, one exact line out: fewer tokens, no silent mistakes.
110
+
111
+ **Deploy (once):**
112
+
113
+ ```bash
114
+ pip install pycodemath # or: pip install -e . (from a clone)
115
+ mkdir -p ~/.claude/skills/pycodemath
116
+ ```
117
+
118
+ Save the following as `~/.claude/skills/pycodemath/SKILL.md`:
119
+
120
+ ```markdown
121
+ ---
122
+ name: pycodemath
123
+ description: Compute math with the local Pycodemath engine (SymPy+NumPy) instead
124
+ of in your head — derivatives, integrals, solving equations and systems
125
+ (linear and nonlinear), determinants/inverses/eigenvalues, gradients/Jacobians/
126
+ Hessians, function minima, ODEs, and optimized Python/NumPy code generation
127
+ (CSE). Use whenever the user asks to compute or verify symbolic/numerical math,
128
+ or to generate code from a formula.
129
+ ---
130
+
131
+ # Pycodemath — local math engine
132
+
133
+ One command = one call (the package is pip-installed, so any working directory):
134
+
135
+ python -m pycodemath "<command>"
136
+
137
+ Result goes to stdout (exit 0); errors to stderr (exit 1). Power notation: `^` or `**`.
138
+ On Windows consoles, set `PYTHONUTF8=1`.
139
+
140
+ ## Commands
141
+
142
+ | Command | Example |
143
+ |---|---|
144
+ | `<expression>` — simplify | `python -m pycodemath "sin(x)^2 + cos(x)^2"` → `1` |
145
+ | `diff <expr> d<var>` — derivative | `"diff sin(x)*x dx"` → `x*cos(x) + sin(x)` |
146
+ | `integrate <expr> d<var>` — symbolic integral | `"integrate 2*x dx"` → `x**2` |
147
+ | `solve <expr> for <var>` — solve = 0 | `"solve x^2-4 for x"` → `-2, 2` |
148
+ | `code <expr>` — CSE-optimized NumPy code | `"code (sin(x)+cos(x))^2"` |
149
+ | `det / inv / transpose / eig <A>` | `"eig [[2,1],[1,2]]"` → `3, 1` |
150
+ | `solve_system <A> = <b>` — linear system | `"solve_system [[2,1],[1,3]] = [3,5]"` |
151
+ | `root <expr> for <var> at <x0>` — numeric root | `"root x^2-2 for x at 1"` |
152
+ | `min <expr> for <var> at <x0> [method newton|bfgs]` — 1D minimum | `"min (x-3)^2 for x at 0"` |
153
+ | `grad <expr> for <x,y,...>` — symbolic gradient | `"grad x^2*y for x,y"` |
154
+ | `solve_nd <f1>; <f2> for <x,y> at <x0,y0>` — nonlinear system | `"solve_nd x^2+y^2-4; x-y for x,y at 1,1"` |
155
+ | `min_nd <expr> for <x,y> at <x0,y0> [method newton|bfgs]` — N-D minimum | `"min_nd (1-x)^2+100*(y-x^2)^2 for x,y at -1.2,1 method bfgs"` |
156
+ | limits / series / sums / ODEs | `"limit sin(x)/x for x to 0"`, `"sum 1/k^2 for k from 1 to oo"` |
157
+
158
+ Type `help` in the REPL (`python -m pycodemath`) for the full command table.
159
+
160
+ ## Rules
161
+
162
+ - **When to use:** the user wants a concrete math result (derivative, integral,
163
+ equation, matrix, minimum, ODE) or optimized code from a formula. The engine is
164
+ exact — trust its output over mental arithmetic.
165
+ - **When not to use:** trivial arithmetic or conceptual questions with no compute.
166
+ - A `error: ...` line on stderr (exit 1) usually means a typo in the command, a
167
+ numerical method that did not converge, or no real solution — read the message,
168
+ it is specific.
169
+ ```
170
+
171
+ Restart Claude Code (or open a new session) and it will invoke the skill
172
+ automatically when a task needs exact math. To confirm it registered, run `/help`
173
+ and look for `pycodemath` in the skills list.
174
+
175
+ ## Limits, series and symbolic sums
176
+
177
+ Beyond `diff`/`integrate`/`solve`, the engine handles limits (including
178
+ one-sided and at infinity), Taylor/Laurent expansions and symbolic summation
179
+ — finite or infinite. All real invocations with real outputs:
180
+
181
+ ```console
182
+ $ python -m pycodemath "limit (1+1/n)^n for n to oo"
183
+ E
184
+
185
+ $ python -m pycodemath "series exp(x) for x n 4"
186
+ x**3/6 + x**2/2 + x + 1
187
+
188
+ $ python -m pycodemath "sum k for k from 1 to n"
189
+ n**2/2 + n/2
190
+
191
+ $ python -m pycodemath "sum 1/k^2 for k from 1 to oo"
192
+ pi**2/6
193
+ ```
194
+
195
+ A divergent sum or a nonexistent limit refuses with a readable error instead
196
+ of returning a symbolic echo.
197
+
198
+ ## Optimization: Newton and BFGS where gradient descent stalls
199
+
200
+ `min` / `min_nd` default to plain gradient descent; `method newton|bfgs`
201
+ switches to second-order methods with Armijo backtracking line search.
202
+ On the Rosenbrock valley (start `(-1.2, 1)`) gradient descent refuses after
203
+ 10 000 iterations, while BFGS converges in 36:
204
+
205
+ ```console
206
+ $ python -m pycodemath "min_nd (1-x)^2 + 100*(y-x^2)^2 for x,y at -1.2,1 method bfgs"
207
+ x = 0.999999999999454, y = 0.9999999999989762
208
+ ```
209
+
210
+ ## ODE suite
211
+
212
+ Symbolic (`dsolve`) plus a full numerical toolbox — scalar and systems,
213
+ forward and backward in time (`t1 < t0`), all refusing to silently jump over
214
+ singularities (a readable error instead of garbage):
215
+
216
+ | solver | what it does |
217
+ | --- | --- |
218
+ | `solve_ode_num` | classic fixed-step RK4 |
219
+ | `solve_ode_adaptive` | Dormand–Prince 5(4), adaptive step (like `ode45`) |
220
+ | `solve_ode_dense` | adaptive + dense output: callable `sol(t)` anywhere |
221
+ | `solve_ode_events` | event detection `g(t,y)=0` (+ terminal events) |
222
+ | `solve_ode_stiff` | implicit BDF2 + Newton for stiff equations |
223
+ | `solve_ode_stiff_adaptive` | **variable-step BDF2** with local error control |
224
+ | `solve_ode_system_*` | vector variants of all of the above |
225
+
226
+ Adaptive integration of a smooth problem takes 41 steps at `rtol 1e-8`:
227
+
228
+ ```console
229
+ $ python -m pycodemath "ode_adaptive y*cos(t) for y(t) from 0 to 5 at 1 rtol 1e-8"
230
+ y(5) = 0.3833049965035854 (41 adaptive steps)
231
+ ```
232
+
233
+ On the stiff classic `y' = -1000(y - cos t)` the explicit pair is limited by
234
+ *stability* (378 steps at `rtol 1e-6`; with the same budget fixed-step RK4
235
+ returns astronomically wrong finite values). The adaptive BDF gets there in
236
+ 313 steps — and starting on the slow manifold, where stiffness is pure, in
237
+ **58 steps vs 357**:
238
+
239
+ ```console
240
+ $ python -m pycodemath "odestiff_adaptive -1000*(y-cos(t)) for y(t) from 0 to 1 at 0"
241
+ y(1) = 0.5411432587540607 (adaptive BDF, 313 implicit steps)
242
+ ```
243
+
244
+ The system variant handles the Van der Pol oscillator with μ=1000 over
245
+ `[0, 2000]` in 5 332 steps (~1 s) — an explicit method would need ≥ 2 000 000.
246
+
247
+ ## Code generation
248
+
249
+ `code <expr>` emits standalone, CSE-optimized NumPy source (real output):
250
+
251
+ ```console
252
+ $ python -m pycodemath "code (sin(x)+cos(x))^2 + (sin(x)+cos(x))^3"
253
+ import numpy as np
254
+
255
+
256
+ def f(x):
257
+ """Pycodemath: f(x) — NumPy code."""
258
+ _c0 = np.sin(x + (1/4)*np.pi)
259
+ return 2*_c0**2*(np.sqrt(2)*_c0 + 1)
260
+ ```
261
+
262
+ The Python API also generates standalone ODE integrators — including an
263
+ adaptive one and one with **dense output** whose emitted interpolant is
264
+ bit-for-bit identical to the engine (measured max difference: `0.0` on nodes
265
+ and off-node grids, forward and backward):
266
+
267
+ ```python
268
+ from pycodemath import parse, generate_ode_dense
269
+
270
+ art = generate_ode_dense(parse("y*cos(t)"), "t", "y")
271
+ sol = art(1.0, 0.0, 5.0, 1e-8) # standalone DOPRI5, returns an interpolant
272
+ sol(2.5) # -> 1.8193369962907706
273
+ len(sol.ts) # -> 42 accepted nodes
274
+ ```
275
+
276
+ Event detection from the API:
277
+
278
+ ```python
279
+ from pycodemath import parse, solve_ode_events
280
+
281
+ ev = solve_ode_events(parse("cos(t)"), "t", 0.0, (0.0, 10.0), parse("y"), rtol=1e-8)
282
+ ev.event_times # -> [3.141593, 6.283185, 9.424778] (π, 2π, 3π)
283
+ ```
284
+
285
+ ## Design contracts
286
+
287
+ - One IR (`Expr`/`Matrix`) shared by the engine and the generator.
288
+ - Numerical loops run on compiled functions (`lambdify` + LRU cache) —
289
+ zero SymPy calls per iteration.
290
+ - Divergence or a domain problem raises a readable `PycodemathError` —
291
+ never NaN/garbage in a result.
292
+ - The parser resolves only a whitelist of mathematical functions — unknown
293
+ names become symbols, not Python code; string literals and attribute
294
+ access are rejected outright, and evaluation cost is bounded so a single
295
+ expression (e.g. `9**9**9`) can't exhaust memory.
296
+ - Tests measure real numbers first, then assert them with a margin —
297
+ **230 tests**, all green, on Ubuntu and Windows (CI + mypy included).
298
+
299
+ ## License
300
+
301
+ MIT — see [LICENSE](LICENSE).