mathslate 0.1.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.
- mathslate-0.1.0/.github/workflows/ci.yml +48 -0
- mathslate-0.1.0/.github/workflows/publish.yml +45 -0
- mathslate-0.1.0/.gitignore +12 -0
- mathslate-0.1.0/LICENSE +21 -0
- mathslate-0.1.0/PKG-INFO +338 -0
- mathslate-0.1.0/README.md +301 -0
- mathslate-0.1.0/README_ko.md +248 -0
- mathslate-0.1.0/docs/manual.md +2136 -0
- mathslate-0.1.0/docs/manual_ko.md +1734 -0
- mathslate-0.1.0/docs/tutorial.md +637 -0
- mathslate-0.1.0/docs/tutorial_ko.md +511 -0
- mathslate-0.1.0/examples/__init__.py +0 -0
- mathslate-0.1.0/examples/build_3d_tour.py +301 -0
- mathslate-0.1.0/examples/build_tour.py +804 -0
- mathslate-0.1.0/examples/marimo_notebook.py +113 -0
- mathslate-0.1.0/examples/mathslate_3d_tour.ipynb +494 -0
- mathslate-0.1.0/examples/mathslate_3d_tour.py +219 -0
- mathslate-0.1.0/examples/mathslate_tour.ipynb +1799 -0
- mathslate-0.1.0/examples/mathslate_tour.py +1079 -0
- mathslate-0.1.0/examples/quickstart.py +81 -0
- mathslate-0.1.0/mathslate/__init__.py +140 -0
- mathslate-0.1.0/mathslate/_text.py +64 -0
- mathslate-0.1.0/mathslate/ai/__init__.py +51 -0
- mathslate-0.1.0/mathslate/ai/providers.py +231 -0
- mathslate-0.1.0/mathslate/ai/suggest.py +453 -0
- mathslate-0.1.0/mathslate/api.py +626 -0
- mathslate-0.1.0/mathslate/classroom.py +229 -0
- mathslate-0.1.0/mathslate/codegen.py +843 -0
- mathslate-0.1.0/mathslate/core/__init__.py +24 -0
- mathslate-0.1.0/mathslate/core/_budget.py +334 -0
- mathslate-0.1.0/mathslate/core/_failure.py +85 -0
- mathslate-0.1.0/mathslate/core/_sets.py +170 -0
- mathslate-0.1.0/mathslate/core/_source.py +48 -0
- mathslate-0.1.0/mathslate/core/analysis.py +1277 -0
- mathslate-0.1.0/mathslate/core/binding.py +182 -0
- mathslate-0.1.0/mathslate/core/data.py +775 -0
- mathslate-0.1.0/mathslate/core/dispatch.py +1106 -0
- mathslate-0.1.0/mathslate/core/sampling.py +947 -0
- mathslate-0.1.0/mathslate/core/surfaces.py +305 -0
- mathslate-0.1.0/mathslate/core/tables.py +202 -0
- mathslate-0.1.0/mathslate/errors.py +43 -0
- mathslate-0.1.0/mathslate/render/__init__.py +7 -0
- mathslate-0.1.0/mathslate/render/axes.py +126 -0
- mathslate-0.1.0/mathslate/render/options.py +199 -0
- mathslate-0.1.0/mathslate/render/plotly_backend.py +580 -0
- mathslate-0.1.0/mathslate/result.py +842 -0
- mathslate-0.1.0/mathslate/ui/__init__.py +16 -0
- mathslate-0.1.0/mathslate/ui/adapters.py +104 -0
- mathslate-0.1.0/mathslate/ui/interact.py +302 -0
- mathslate-0.1.0/mathslate_prd_0.3.md +2193 -0
- mathslate-0.1.0/pyproject.toml +52 -0
- mathslate-0.1.0/tests/conftest.py +73 -0
- mathslate-0.1.0/tests/corpus.py +224 -0
- mathslate-0.1.0/tests/helpers.py +44 -0
- mathslate-0.1.0/tests/test_acceptance.py +222 -0
- mathslate-0.1.0/tests/test_ai_credentials.py +182 -0
- mathslate-0.1.0/tests/test_ai_safety.py +115 -0
- mathslate-0.1.0/tests/test_analysis.py +394 -0
- mathslate-0.1.0/tests/test_api_surface.py +220 -0
- mathslate-0.1.0/tests/test_binding.py +84 -0
- mathslate-0.1.0/tests/test_codegen.py +105 -0
- mathslate-0.1.0/tests/test_codegen_fidelity.py +514 -0
- mathslate-0.1.0/tests/test_corpus.py +95 -0
- mathslate-0.1.0/tests/test_discontinuity_review.py +117 -0
- mathslate-0.1.0/tests/test_dispatch.py +151 -0
- mathslate-0.1.0/tests/test_docs.py +108 -0
- mathslate-0.1.0/tests/test_environments.py +119 -0
- mathslate-0.1.0/tests/test_example_notebook.py +123 -0
- mathslate-0.1.0/tests/test_exclusions.py +168 -0
- mathslate-0.1.0/tests/test_extreme_values.py +35 -0
- mathslate-0.1.0/tests/test_failure_taxonomy.py +176 -0
- mathslate-0.1.0/tests/test_fitting_robustness.py +181 -0
- mathslate-0.1.0/tests/test_inequalities.py +258 -0
- mathslate-0.1.0/tests/test_input_validation.py +207 -0
- mathslate-0.1.0/tests/test_interaction.py +260 -0
- mathslate-0.1.0/tests/test_marimo_imports.py +130 -0
- mathslate-0.1.0/tests/test_notebook_display.py +165 -0
- mathslate-0.1.0/tests/test_parametric_sampling.py +156 -0
- mathslate-0.1.0/tests/test_sampling.py +149 -0
- mathslate-0.1.0/tests/test_session_state.py +180 -0
- mathslate-0.1.0/tests/test_symbolic_budget.py +434 -0
- mathslate-0.1.0/tests/test_tables_and_export.py +181 -0
- mathslate-0.1.0/tests/test_text.py +68 -0
- mathslate-0.1.0/tests/test_three_dimensions.py +310 -0
- mathslate-0.1.0/tests/test_tour_notebooks.py +255 -0
- mathslate-0.1.0/tests/test_v1_data_and_ai.py +615 -0
- mathslate-0.1.0/tests/test_view_and_mesh.py +926 -0
- mathslate-0.1.0/tests/test_warning_policy.py +89 -0
- mathslate-0.1.0/tests/test_worksheet_display.py +119 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
name: tests (py${{ matrix.python }}, ${{ matrix.os }})
|
|
10
|
+
runs-on: ${{ matrix.os }}
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
os: [ubuntu-latest, windows-latest]
|
|
15
|
+
python: ["3.10", "3.12"]
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
- uses: actions/setup-python@v5
|
|
19
|
+
with:
|
|
20
|
+
python-version: ${{ matrix.python }}
|
|
21
|
+
# All three notebook hosts, so acceptance criterion 5 really runs.
|
|
22
|
+
- run: pip install -e ".[dev,jupyter,marimo]" nbclient nbformat ipykernel
|
|
23
|
+
# The marimo tour is maintained separately by its author. Its expensive
|
|
24
|
+
# rendering and generator-synchronisation checks remain available for
|
|
25
|
+
# manual verification, but must not make the library CI fail.
|
|
26
|
+
- run: python -m pytest -q --ignore=tests/test_tour_notebooks.py
|
|
27
|
+
|
|
28
|
+
no_frontend_dependency:
|
|
29
|
+
name: acceptance criterion 6 - no frontend dependency
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
- uses: actions/setup-python@v5
|
|
34
|
+
with:
|
|
35
|
+
python-version: "3.12"
|
|
36
|
+
- name: install the package exactly as a user would
|
|
37
|
+
run: pip install .
|
|
38
|
+
- name: assert no frontend package came with it
|
|
39
|
+
run: |
|
|
40
|
+
python - <<'PY'
|
|
41
|
+
import importlib.util, sys
|
|
42
|
+
for frontend in ("marimo", "ipywidgets", "notebook", "jupyter_core"):
|
|
43
|
+
if importlib.util.find_spec(frontend) is not None:
|
|
44
|
+
sys.exit(f"{frontend} was installed by `pip install mathslate`")
|
|
45
|
+
import mathslate
|
|
46
|
+
mathslate.plot(mathslate.sin(mathslate.x), verbose=False)
|
|
47
|
+
print("clean install, plot works:", mathslate.frontend_report())
|
|
48
|
+
PY
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
name: Publish distribution to PyPI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- "v*"
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
|
|
12
|
+
jobs:
|
|
13
|
+
build:
|
|
14
|
+
name: Build distribution
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v6
|
|
18
|
+
with:
|
|
19
|
+
persist-credentials: false
|
|
20
|
+
- uses: actions/setup-python@v6
|
|
21
|
+
with:
|
|
22
|
+
python-version: "3.12"
|
|
23
|
+
- name: Build wheel and source distribution
|
|
24
|
+
run: python -m pip install --upgrade build twine && python -m build && python -m twine check dist/*
|
|
25
|
+
- uses: actions/upload-artifact@v5
|
|
26
|
+
with:
|
|
27
|
+
name: python-package-distributions
|
|
28
|
+
path: dist/
|
|
29
|
+
|
|
30
|
+
publish:
|
|
31
|
+
name: Publish to PyPI
|
|
32
|
+
if: startsWith(github.ref, 'refs/tags/v')
|
|
33
|
+
needs: build
|
|
34
|
+
runs-on: ubuntu-latest
|
|
35
|
+
environment:
|
|
36
|
+
name: pypi
|
|
37
|
+
url: https://pypi.org/p/mathslate
|
|
38
|
+
permissions:
|
|
39
|
+
id-token: write
|
|
40
|
+
steps:
|
|
41
|
+
- uses: actions/download-artifact@v6
|
|
42
|
+
with:
|
|
43
|
+
name: python-package-distributions
|
|
44
|
+
path: dist/
|
|
45
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
mathslate-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MathSlate contributors
|
|
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.
|
mathslate-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mathslate
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A mathematical workspace that grows with you.
|
|
5
|
+
Project-URL: Homepage, https://github.com/berd2/mathslate
|
|
6
|
+
Project-URL: Documentation, https://github.com/berd2/mathslate/blob/master/docs/manual.md
|
|
7
|
+
Project-URL: Repository, https://github.com/berd2/mathslate
|
|
8
|
+
Project-URL: Issues, https://github.com/berd2/mathslate/issues
|
|
9
|
+
Author: MathSlate contributors
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: education,mathematics,plotly,plotting,sympy
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Education
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
18
|
+
Requires-Python: >=3.10
|
|
19
|
+
Requires-Dist: numpy>=1.24
|
|
20
|
+
Requires-Dist: plotly>=5.18
|
|
21
|
+
Requires-Dist: sympy>=1.12
|
|
22
|
+
Provides-Extra: ai
|
|
23
|
+
Requires-Dist: anthropic>=0.40; extra == 'ai'
|
|
24
|
+
Provides-Extra: ai-gemini
|
|
25
|
+
Requires-Dist: google-genai>=1.0; extra == 'ai-gemini'
|
|
26
|
+
Provides-Extra: ai-openai
|
|
27
|
+
Requires-Dist: openai>=1.50; extra == 'ai-openai'
|
|
28
|
+
Provides-Extra: dev
|
|
29
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
30
|
+
Requires-Dist: tomli>=2.0; (python_version < '3.11') and extra == 'dev'
|
|
31
|
+
Provides-Extra: jupyter
|
|
32
|
+
Requires-Dist: anywidget>=0.9; extra == 'jupyter'
|
|
33
|
+
Requires-Dist: ipywidgets>=8.0; extra == 'jupyter'
|
|
34
|
+
Provides-Extra: marimo
|
|
35
|
+
Requires-Dist: marimo>=0.9; extra == 'marimo'
|
|
36
|
+
Description-Content-Type: text/markdown
|
|
37
|
+
|
|
38
|
+
# MathSlate
|
|
39
|
+
|
|
40
|
+
**A mathematical workspace that grows with you.**
|
|
41
|
+
|
|
42
|
+
A mathematical workspace that orchestrates SymPy, NumPy and Plotly so that a
|
|
43
|
+
learner can go from their first graph to real scientific computing without ever
|
|
44
|
+
changing tools.
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from mathslate import *
|
|
48
|
+
|
|
49
|
+
plot(sin(x)/x)
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
curve | x ∈ [-10, 10] | 411 samples | 1 discontinuity handled
|
|
54
|
+
· singularities at x = 0
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
That is the whole first lesson. No `symbols`, no `lambdify`, no `linspace`, no
|
|
58
|
+
`figure`, no `show`. When you are ready for those, ask:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
plot(sin(x)/x).show_python()
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
and MathSlate prints the plain NumPy + SymPy + Plotly program that would have
|
|
65
|
+
produced the same picture — including the parts it did quietly on your behalf.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## Install
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pip install mathslate
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Nothing frontend-specific comes with it. Add what your notebook needs:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
pip install "mathslate[jupyter]"
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
pip install "mathslate[marimo]"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
> **marimo users:** marimo rejects `import *` at parse time — it needs to know
|
|
86
|
+
> statically which names each cell defines in order to build its reactive
|
|
87
|
+
> graph. Import explicitly instead; everything else is identical. See the
|
|
88
|
+
> [manual](https://github.com/berd2/mathslate/blob/master/docs/manual.md#marimo-forbids-import-).
|
|
89
|
+
>
|
|
90
|
+
> ```python
|
|
91
|
+
> from mathslate import plot, polar, sin, cos, tan, exp, sqrt, x, y, t
|
|
92
|
+
> ```
|
|
93
|
+
|
|
94
|
+
## The one rule
|
|
95
|
+
|
|
96
|
+
`plot()` infers what you meant. There is exactly one thing to memorise:
|
|
97
|
+
|
|
98
|
+
- a **list** means *several things together*
|
|
99
|
+
- a **tuple** means *one vector-valued object*
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
plot([sin(x), cos(x)]) # two curves, overlaid
|
|
103
|
+
plot((sin(t), cos(t))) # one parametric curve
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
Everything else follows the dispatch contract:
|
|
107
|
+
|
|
108
|
+
| Input | Free symbols | Result |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| `Expr` | 1 | 2D curve |
|
|
111
|
+
| `Expr` | 0 | horizontal line + message |
|
|
112
|
+
| `list[Expr]` | 1 shared | curves overlaid |
|
|
113
|
+
| `tuple[Expr, Expr]` | 1 shared | 2D parametric curve |
|
|
114
|
+
| `callable` | — | numeric sampling |
|
|
115
|
+
| array-like | — | data series |
|
|
116
|
+
| `(xdata, ydata)` | — | scatter |
|
|
117
|
+
| `Expr` | 2 | surface, `kind="contour"` to flatten |
|
|
118
|
+
| `Eq(lhs, rhs)` | 2 | implicit curve |
|
|
119
|
+
| `tuple[Expr × 3]` | 1 or 2 | space curve / parametric surface |
|
|
120
|
+
|
|
121
|
+
Two cases inference cannot decide in principle, so you say them out loud:
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
polar(1 + cos(t)) # r = f(θ) is indistinguishable from y = f(x)
|
|
125
|
+
plot(x*y, kind="contour") # surface vs. contour
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Every call reports what it inferred, in one line. That line is not logging: it
|
|
129
|
+
is where you first learn that parameters you never wrote exist.
|
|
130
|
+
|
|
131
|
+
## Which symbol becomes the axis
|
|
132
|
+
|
|
133
|
+
1. an explicit range wins — `plot(a*sin(x), (a, -1, 1))`
|
|
134
|
+
2. bound parameters are never axes — `plot(a*sin(x), parameters={a: 3})`
|
|
135
|
+
3. otherwise the conventional order: `x, y, z` → `t, u, v` → `r, θ` → alphabetical
|
|
136
|
+
4. if it is still ambiguous, MathSlate asks instead of guessing
|
|
137
|
+
|
|
138
|
+
## The hard part: discontinuities
|
|
139
|
+
|
|
140
|
+
`plot(tan(x))` drawing no spurious vertical lines is the feature that separates
|
|
141
|
+
MathSlate from a plotting wrapper. Everything else here is convenience.
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
plot(tan(x)) # broken at every pole, y-window from the 2nd–98th percentile
|
|
145
|
+
plot(1/x) # broken at 0
|
|
146
|
+
plot(floor(x)) # broken at every integer
|
|
147
|
+
plot(sqrt(x)) # never sampled outside its real domain
|
|
148
|
+
plot(x/abs(x)) # broken at 0
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
How, in order:
|
|
152
|
+
|
|
153
|
+
1. **Symbolic singularities** from `sympy.calculus.singularities` — never guessed.
|
|
154
|
+
2. **Real domain** from `sympy.calculus.util.continuous_domain` — never sampled outside it.
|
|
155
|
+
3. **Adaptive subdivision**: 200 uniform points, then midpoints wherever three
|
|
156
|
+
adjacent points bend, to depth 8 and at most 5000 points.
|
|
157
|
+
4. **Line breaking**: NaN at every discontinuity. Jumps SymPy cannot see
|
|
158
|
+
(`floor`, `sign`, `Piecewise`) are found by bisection — a genuine jump keeps
|
|
159
|
+
its size as the interval shrinks, a steep slope does not.
|
|
160
|
+
5. **Y-clipping**: the visible window comes from the 2nd–98th percentile with
|
|
161
|
+
near-pole samples excluded.
|
|
162
|
+
6. **Vectorised evaluation** via `lambdify(modules="numpy")`, falling back to
|
|
163
|
+
element-wise evaluation loudly — never silently. The last element-wise tier
|
|
164
|
+
is SymPy's own `evalf`, which is what makes `zeta`, `Si` and `besselj`
|
|
165
|
+
plottable at all: no numeric backend carries them.
|
|
166
|
+
|
|
167
|
+
All six apply to parametric and polar curves as well. There the continuous
|
|
168
|
+
pieces are the intersection of both components' domains, the jump probe watches
|
|
169
|
+
`x(t)` as well as `y(t)`, and the window is clipped horizontally too — `x(t)`
|
|
170
|
+
can reach a pole in a way `x` never can when it is the axis.
|
|
171
|
+
|
|
172
|
+
```python
|
|
173
|
+
plot((tan(t), t)) # broken at t = π/2 and 3π/2, not drawn out to 10¹⁶
|
|
174
|
+
polar(tan(t)) # the same, through the polar reduction
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## What a function *is*: `analyze()`
|
|
178
|
+
|
|
179
|
+
Explicit, never automatic — property detection is too slow and too noisy to run
|
|
180
|
+
on every plot.
|
|
181
|
+
|
|
182
|
+
```python
|
|
183
|
+
analyze(x**3 - 3*x) # roots -sqrt(3), 0, sqrt(3); max at -1; min at 1
|
|
184
|
+
analyze(1/x) # x = 0 vertical; y = 0 at both ends; odd
|
|
185
|
+
plot(tan(x)).analyze() # analyses the window you are looking at
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
Roots, extrema, inflection points, symmetry, periodicity, asymptotes,
|
|
189
|
+
discontinuities and monotonic intervals. Exact where SymPy can solve it,
|
|
190
|
+
sampled where it cannot — and the sampled lines are **labelled approximate**,
|
|
191
|
+
because an approximation dressed as a proof is worse than no answer.
|
|
192
|
+
|
|
193
|
+
It reports what a function *is*. It never narrates how the answer was reached.
|
|
194
|
+
|
|
195
|
+
## Interactive: `slider()`
|
|
196
|
+
|
|
197
|
+
Bind a parameter and it becomes something the reader can drag. **You never
|
|
198
|
+
write a callback.**
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
a = slider(-3, 3, default=1, name="a")
|
|
202
|
+
plot(a*sin(x)) # x is the axis, a is the parameter — inferred
|
|
203
|
+
animate(a*sin(x)) # the same, with a play button
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
The control is Plotly's own, carried inside the figure, so it needs no frontend
|
|
207
|
+
package and survives export to one self-contained HTML file — which is the
|
|
208
|
+
point if you are handing it to a class. `Slider.widget()` gives you
|
|
209
|
+
`mo.ui.slider` or `ipywidgets` instead when you want live recomputation.
|
|
210
|
+
|
|
211
|
+
## Your own numbers: `dataset()`
|
|
212
|
+
|
|
213
|
+
The bridge from symbolic to data. Write the model as you would on paper; get
|
|
214
|
+
**the same expression back with its parameters filled in**, still SymPy.
|
|
215
|
+
|
|
216
|
+
```python
|
|
217
|
+
readings = dataset({"x": [0, 1, 2, 3], "y": [1.0, 3.1, 4.9, 7.2]})
|
|
218
|
+
found = readings.fit(a*x + b) # a = 2.05, b = 0.98, R² = 0.999
|
|
219
|
+
diff(found.expr, x) # still an expression — everything works on it
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
Linear in the parameters is solved exactly; anything else is refined
|
|
223
|
+
iteratively. `.residuals` and `.r_squared` come back so the fit can be judged
|
|
224
|
+
rather than trusted.
|
|
225
|
+
|
|
226
|
+
```python
|
|
227
|
+
plot(readings) # the columns
|
|
228
|
+
plot(readings, kind="hist") # their shape
|
|
229
|
+
plot(Matrix([[2, 1], [1, 3]])) # a matrix as what it does, with eigenvectors
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Numbers, and one file to hand out
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
table(sin(x), (x, 0, 1)) # the same function, read as values
|
|
236
|
+
plot(a*sin(x)).to_html("lesson.html")
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
`to_html()` embeds Plotly itself, so the page opens with no network and nothing
|
|
240
|
+
installed — sliders included. That is why the slider is built from frames
|
|
241
|
+
carried inside the figure rather than a notebook widget: a widget needs a live
|
|
242
|
+
kernel, and a file handed to a class does not have one.
|
|
243
|
+
|
|
244
|
+
## Escape hatches
|
|
245
|
+
|
|
246
|
+
Peeling the wrapper off costs nothing:
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
f = plot(sin(x)/x)
|
|
250
|
+
f.plotly # the Plotly Figure — yours to mutate
|
|
251
|
+
f.sympy # the expression
|
|
252
|
+
f.numpy # the sampled (x, y) arrays
|
|
253
|
+
f.python() # the equivalent code, as a string
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
## Optional: the assistant, and worksheets
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
from mathslate.ai import ask
|
|
260
|
+
print(ask("plot the tangent over one period").code) # you read it, then run it
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Claude, OpenAI or Gemini — install one and set its key, either as the provider's
|
|
264
|
+
environment variable or with `api_key=` on `ask()`/`configure()`. **The core never
|
|
265
|
+
imports any of it** and works fully offline, which is the point on a school
|
|
266
|
+
network; the test suite asserts that in a subprocess. `ask()` returns code, it
|
|
267
|
+
does not execute it. `Suggestion.run()` validates a restricted MathSlate subset
|
|
268
|
+
by default; unrestricted Python requires the explicit `unsafe=True` escape hatch.
|
|
269
|
+
|
|
270
|
+
```python
|
|
271
|
+
from mathslate.classroom import worksheet
|
|
272
|
+
worksheet([
|
|
273
|
+
"Where does sin(x)/x go at zero?",
|
|
274
|
+
("The graph", plot(sin(x)/x)),
|
|
275
|
+
("The numbers", table(sin(x)/x, (x, -1, 1))),
|
|
276
|
+
], title="Limits", path="handout.html")
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
Plotly is embedded once however many figures the page holds.
|
|
280
|
+
Pass `standalone=False` to `worksheet(...)`, `.html()`, `.save()` or `.preview()`
|
|
281
|
+
to link the versioned Plotly CDN and keep networked outputs small.
|
|
282
|
+
|
|
283
|
+
## What MathSlate is not
|
|
284
|
+
|
|
285
|
+
- Not a CAS. All symbolic computation is SymPy's.
|
|
286
|
+
- **Not a step-by-step derivation engine.** There is no `explain()`, no "show
|
|
287
|
+
steps", no generated worked solutions. SymPy provides no general
|
|
288
|
+
step-by-step engine, and a partial one would be worse than none.
|
|
289
|
+
- Not a notebook or editor. It runs inside marimo and Jupyter.
|
|
290
|
+
- Not Wolfram Language compatible.
|
|
291
|
+
- Not a grading or LMS tool.
|
|
292
|
+
- Not high-performance numerics — educational scale (≤10⁶ points).
|
|
293
|
+
- Not a GUI equation editor. Input is Python code.
|
|
294
|
+
|
|
295
|
+
## Development
|
|
296
|
+
|
|
297
|
+
```bash
|
|
298
|
+
python -m venv .venv && .venv/Scripts/pip install -e ".[dev,jupyter,marimo]"
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
```bash
|
|
302
|
+
python -m pytest -q
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
The suite covers each PRD acceptance criterion in its own class, plus the
|
|
306
|
+
200-function corpus, a 30-case discontinuity review, a three-environment parity
|
|
307
|
+
check, and every example in `docs/`.
|
|
308
|
+
|
|
309
|
+
## Try it
|
|
310
|
+
|
|
311
|
+
A guided tour of everything in v1.0, as a notebook you run yourself — 95 cells
|
|
312
|
+
of curves, discontinuities, 3D, sliders, `analyze()`, tables, data fitting,
|
|
313
|
+
worksheets and refusals. Nothing in it needs a network.
|
|
314
|
+
|
|
315
|
+
```bash
|
|
316
|
+
marimo edit examples/mathslate_tour.py
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
```bash
|
|
320
|
+
jupyter lab examples/mathslate_tour.ipynb
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
The two are the same notebook: both are generated from
|
|
324
|
+
[`examples/build_tour.py`](https://github.com/berd2/mathslate/blob/master/examples/build_tour.py), and the test suite executes
|
|
325
|
+
the Jupyter one end to end on every run, so a tour cell that stops working
|
|
326
|
+
fails the build.
|
|
327
|
+
|
|
328
|
+
## Documentation
|
|
329
|
+
|
|
330
|
+
- **[Tutorial](https://github.com/berd2/mathslate/blob/master/docs/tutorial.md)** — a one-sitting walkthrough, from your first
|
|
331
|
+
graph to reading real Python. Start here.
|
|
332
|
+
- **[Reference manual](https://github.com/berd2/mathslate/blob/master/docs/manual.md)** — every option, the full dispatch
|
|
333
|
+
contract, the sampling algorithm, and the exact guarantees.
|
|
334
|
+
- [`mathslate_prd_0.3.md`](https://github.com/berd2/mathslate/blob/master/mathslate_prd_0.3.md) — the product requirements
|
|
335
|
+
document, with an implementation-status section kept up to date.
|
|
336
|
+
|
|
337
|
+
Every `python` example in both documents is executed by the test suite in
|
|
338
|
+
document order, so the manuals cannot drift from the code.
|