Skip to content

Commit 50a6ccd

Browse files
authored
Merge pull request #12 from 0xC000005/v0.16-polish
v0.16.0: Polish Bundle (final MoCaX 4.3.1 cosmetic mirror)
2 parents 43a2e30 + d238c97 commit 50a6ccd

15 files changed

Lines changed: 1769 additions & 9 deletions

CHANGELOG.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,20 @@ All notable changes to PyChebyshev will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [0.16.0] - 2026-04-25
9+
10+
### Added — Polish Bundle (final MoCaX 4.3.1 cosmetic mirror)
11+
12+
- `clone()` deep copy method on all four classes (`ChebyshevApproximation`, `ChebyshevSpline`, `ChebyshevSlider`, `ChebyshevTT`). Like save/load, the source `function` callable is not duplicated; clone has `function = None`.
13+
- Instance getters: `get_max_derivative_order()` (all four), `get_error_threshold()` (Approximation/Spline), `get_special_points()` (Approximation/Spline; Approximation now stores `special_points` with `__setstate__` backfill), `get_evaluation_points()` and `get_num_evaluation_points()` (all four; eval grid size semantics, matching `len(get_evaluation_points()) == get_num_evaluation_points()`).
14+
- `peek_format_version(filename)` static on `ChebyshevApproximation` — read `.pcb` major version without deserializing.
15+
- `is_dimensionality_allowed(num_dim)` static on all four classes (returns True for any positive int; hook for future per-class capability caps).
16+
- `defer_build=True` keyword-only ctor flag + `set_original_function_values(values)` instance mutator on `ChebyshevApproximation` and `ChebyshevSpline` — in-place deferred construction. Bit-identical to the `from_values()` factory. Spline path threads `additional_data` to pieces; atomic per-piece validation.
17+
- Optional typed helpers `Domain`, `Ns`, `SpecialPoints` (frozen dataclasses) exported from the package; constructors of all four classes accept raw lists or these dataclasses.
18+
- `ChebyshevTT` ctor now accepts `max_derivative_order: int = 2` keyword-only kwarg; `__setstate__` backfill for backward-compat pickles.
19+
20+
All additions are strictly additive — no breaking changes.
21+
822
## [0.15.0] - 2026-04-25
923

1024
### Added

CLAUDE.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ PyChebyshev is a pip-installable Python library for multi-dimensional Chebyshev
1212
# Setup
1313
uv sync
1414

15-
# Run tests (~733 tests, ~110s due to 5D Black-Scholes builds)
15+
# Run tests (~838 tests, ~110s due to 5D Black-Scholes builds)
1616
uv run pytest tests/ -v
1717

1818
# Run a single test
@@ -53,6 +53,12 @@ The installable package. Public classes: `ChebyshevApproximation`, `ChebyshevSpl
5353
`is_construction_finished`/`get_constructor_type`/`get_used_ns`, and the
5454
`get_derivative_id` integer registry + `eval(..., derivative_id=...)` (the
5555
last one excluding `ChebyshevTT`).
56+
- v0.16 adds `clone()`, instance getters (`get_max_derivative_order`,
57+
`get_error_threshold`, `get_special_points`, `get_evaluation_points`,
58+
`get_num_evaluation_points`), `peek_format_version()` static,
59+
`is_dimensionality_allowed()` static, `defer_build=True` +
60+
`set_original_function_values()`, and optional `Domain`/`Ns`/`SpecialPoints`
61+
typed helpers (constructors accept both forms).
5662

5763
### Benchmark Scripts (project root)
5864

@@ -70,6 +76,7 @@ Not part of the library. Compare Chebyshev barycentric against alternative metho
7076
- `compare_extrude_slice.py` — PyChebyshev extrusion/slicing vs MoCaX comparison (requires `mocaxextend_lib/`)
7177
- `compare_from_values.py` — PyChebyshev nodes()/from_values() vs MoCaX Extend comparison (requires `mocaxextend_lib/`)
7278
- `compare_special_points.py` — PyChebyshev special_points vs MoCaX MocaxSpecialPoints + MocaxNs comparison (requires `mocax_lib/`)
79+
- `compare_v016_polish.py` — PyChebyshev v0.16 polish surface vs MoCaX 4.3.1 cosmetic API (requires `mocaxpy`; gracefully skips MoCaX side if not installed)
7380

7481
### Tests (`tests/`)
7582

@@ -89,6 +96,11 @@ Not part of the library. Compare Chebyshev barycentric against alternative metho
8996
binary rejection, derivative_id registry on Approximation/Spline/Slider,
9097
introspection trio (`is_construction_finished`, `get_constructor_type`,
9198
`get_used_ns`).
99+
- `test_v016_polish.py`~74 tests: clone() on all four classes, instance getters
100+
(`get_max_derivative_order`, `get_error_threshold`, `get_special_points`,
101+
`get_evaluation_points`, `get_num_evaluation_points`), `peek_format_version`,
102+
`is_dimensionality_allowed`, `defer_build` + `set_original_function_values`,
103+
`Domain`/`Ns`/`SpecialPoints` typed helpers.
92104

93105
### CI/CD (`.github/workflows/`)
94106

compare_v016_polish.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
"""Side-by-side comparison: PyChebyshev v0.16 polish surface vs MoCaX 4.3.1.
2+
3+
Most v0.16 surface methods have direct MoCaX equivalents. This script
4+
demonstrates the calls side-by-side. Run from the repo root:
5+
6+
uv run python compare_v016_polish.py
7+
8+
Requires mocax_lib/ at the repo root (gitignored).
9+
"""
10+
11+
from __future__ import annotations
12+
13+
import math
14+
15+
from pychebyshev import (
16+
ChebyshevApproximation,
17+
Domain,
18+
Ns,
19+
)
20+
21+
22+
def f(x, _):
23+
return math.sin(x[0]) + math.cos(x[1])
24+
25+
26+
def main():
27+
# PyChebyshev: typed-helper construction
28+
py_cheb = ChebyshevApproximation(
29+
f, 2, Domain([(-1.0, 1.0), (-1.0, 1.0)]), Ns([10, 10]),
30+
)
31+
py_cheb.build(verbose=False)
32+
33+
print("PyChebyshev v0.16 polish surface:")
34+
print(f" clone() → {type(py_cheb.clone()).__name__}")
35+
print(f" get_max_derivative_order() → {py_cheb.get_max_derivative_order()}")
36+
print(f" get_error_threshold() → {py_cheb.get_error_threshold()}")
37+
print(f" get_special_points() → {py_cheb.get_special_points()}")
38+
print(f" get_num_evaluation_points() → {py_cheb.get_num_evaluation_points()}")
39+
print(f" get_evaluation_points().shape → {py_cheb.get_evaluation_points().shape}")
40+
print(f" is_dimensionality_allowed(2) → {ChebyshevApproximation.is_dimensionality_allowed(2)}")
41+
42+
try:
43+
from mocaxpy import Mocax, MocaxDomain, MocaxNs
44+
except ImportError:
45+
print("\n(mocaxpy not installed; skipping MoCaX side)")
46+
return
47+
48+
mc = Mocax(
49+
f,
50+
2,
51+
MocaxDomain([(-1.0, 1.0), (-1.0, 1.0)]),
52+
MocaxNs([10, 10]),
53+
None, # error_threshold
54+
)
55+
print("\nMoCaX 4.3.1 equivalent surface:")
56+
print(f" clone() → {type(mc.clone()).__name__}")
57+
print(f" get_max_derivative_order() → {mc.get_max_derivative_order()}")
58+
print(f" get_num_evaluation_points() → {mc.get_num_evaluation_points()}")
59+
60+
61+
if __name__ == "__main__":
62+
main()

docs/roadmap.md

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,109 @@ All additive, no breaking changes.
112112
**Closes MoCaX gaps:** `additional_data`, `get_derivative_id`, descriptor,
113113
introspection.
114114

115+
## v0.16 — Polish Bundle :material-check:
116+
117+
Final cosmetic mirror of the MoCaX 4.3.1 surface. Strictly additive, no
118+
breaking changes.
119+
120+
- `clone()` — deep copy on all four classes
121+
- `get_special_points()`, `get_max_derivative_order()`,
122+
`get_error_threshold()` — instance getters
123+
- `get_evaluation_points()` / `get_num_evaluation_points()` — flat
124+
`(N, num_dim)` grid post-construction (MoCaX-style)
125+
- `set_original_function_values(values)` — in-place deferred
126+
construction (alt to the `from_values()` factory)
127+
- `peek_format_version(filename)` static — read `.pcb` version without
128+
deserializing
129+
- `is_dimensionality_allowed(num_dim)` static — pre-flight capability check
130+
- Optional typed helpers `Domain`, `Ns`, `SpecialPoints` (constructors
131+
accept both raw lists and these dataclasses)
132+
133+
**Closes MoCaX gaps:** the last cosmetic surface methods.
134+
135+
## v0.17 — Integrate Everywhere :material-clock-outline:
136+
137+
Extend the v0.9 calculus toolkit so every class supports integration.
138+
139+
- `ChebyshevSlider.integrate(dims=None, bounds=None)` — full and partial
140+
integration via the sliding-decomposition closed form
141+
- `ChebyshevTT.integrate(dims=None, bounds=None)` — full and partial
142+
integration via Fejér-1 weight contraction into TT cores
143+
144+
After v0.17, `integrate()` is available on all four classes. Roots and
145+
min/max on Slider/TT remain deferred to v0.21.
146+
147+
**Beyond MoCaX:** MoCaX has no `integrate()` API on any class.
148+
149+
## v0.18 — TT Feature Parity :material-clock-outline:
150+
151+
Bring `ChebyshevTT` up to parity with `ChebyshevApproximation` for the
152+
non-calculus surface.
153+
154+
- TT `extrude()` — add a dim by appending a constant core
155+
- TT `slice()` — fix a dim by contracting a core via barycentric weights
156+
- TT `from_values()` classmethod — build from a precomputed full tensor
157+
(skip TT-Cross)
158+
- TT `nodes()` static — generate the Chebyshev grid without function eval
159+
- TT algebra `+`, `-`, `*` (scalar) — core stacking + rounding
160+
- `to_dense()` / `from_dense()` — convert between TT and Approximation
161+
162+
**Beyond MoCaX:** richer TT primitives than MoCaXExtend exposes.
163+
164+
## v0.19 — Build & Diagnostics :material-clock-outline:
165+
166+
Ergonomics for users with slow `f` or large grids.
167+
168+
- Parallel function evaluation at build time (`n_workers=` ctor kwarg via
169+
`concurrent.futures.ProcessPoolExecutor`)
170+
- `tqdm`-based progress bars during construction (opt-in, `verbose=2`)
171+
- `plot_convergence(target_error)` helper — builds at increasing N,
172+
plots error decay
173+
- Visualization helpers: `plot_1d`, `plot_2d_surface`, `plot_2d_contour`
174+
175+
New optional dependency group `pychebyshev[viz]` for matplotlib + tqdm.
176+
177+
**Beyond MoCaX:** MoCaX has neither parallel build nor visualization
178+
helpers.
179+
180+
## v0.20 — Adaptive Refinement + Interop :material-clock-outline:
181+
182+
Smart node placement plus delivery of the v0.14 `.pcb` portability promise.
183+
184+
- Auto-knot detection for `ChebyshevSpline` — scan for kinks via
185+
curvature/derivative discontinuity, place knots automatically
186+
- Sobol indices computed from spectral coefficients (cheap once the
187+
interpolant exists)
188+
- Auto dimension reordering by importance (helps TT rank growth)
189+
- Reference `.pcb` reader implementations in **Rust** and **Julia** as
190+
separate sub-repos (`0xC000005/pcb-readers`)
191+
192+
**Beyond MoCaX:** MoCaX is closed-source; `.pcb` ecosystem is
193+
PyChebyshev-unique.
194+
195+
## v0.21 — Advanced Calculus :material-clock-outline:
196+
197+
Research-grade extensions to close the remaining calculus surface.
198+
199+
- N-D rootfinding via Möller–Stetter colleague matrices
200+
(Trefethen 2017 ch. 24) on `ChebyshevApproximation` and
201+
`ChebyshevSpline`
202+
- `roots()`, `minimize()`, `maximize()` on `ChebyshevSlider` and
203+
`ChebyshevTT` (1-D-at-a-time + Brent's method bracket where no closed
204+
form exists)
205+
- Higher-order partial derivatives on demand (currently capped at
206+
construction-time `max_derivative_order`)
207+
208+
**Beyond MoCaX:** spectral N-D rootfinding has no MoCaX analog.
209+
115210
## v1.0.0 — Parity Announcement :material-clock-outline:
116211

117-
Feature-complete against MoCaX Intelligence 4.3.1. No new code — this release
118-
is:
212+
Feature-complete against MoCaX Intelligence 4.3.1, plus the beyond-MoCaX
213+
extensions from v0.16–v0.21. No new code — this release is:
119214

120215
- Status bump `Beta``Production/Stable`
121216
- API stability commitment going forward
122-
- Summary CHANGELOG entry covering the parity items
217+
- Summary CHANGELOG entry covering the parity arc + extensions
123218
- Refreshed README with the final performance comparison
124219

125220
---

docs/user-guide/ergonomics.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,3 +114,80 @@ Pickle `load()` is the exception — it preserves the saved object's metadata
114114
| `getConstructorType()` | `get_constructor_type()` |
115115
| `getUsedNs()` | `get_used_ns()` |
116116
| `isConstructionFinished()` | `is_construction_finished()` |
117+
118+
---
119+
120+
## v0.16 polish surface
121+
122+
The v0.16 release adds the final cosmetic mirror of the MoCaX 4.3.1 API.
123+
All additions are strictly additive.
124+
125+
### `clone()`
126+
127+
Return an independent deep copy of any interpolant:
128+
129+
```python
130+
cheb2 = cheb1.clone()
131+
cheb2.set_descriptor("variant")
132+
# cheb1 is untouched
133+
```
134+
135+
Available on `ChebyshevApproximation`, `ChebyshevSpline`, `ChebyshevSlider`,
136+
`ChebyshevTT`. Like `save()`/`load()`, the source `function` callable is not
137+
duplicated — the clone has `function = None`.
138+
139+
### Instance getters
140+
141+
| Method | Available on | Returns |
142+
|---|---|---|
143+
| `get_max_derivative_order()` | all four | `int` |
144+
| `get_error_threshold()` | Approximation, Spline | `float \| None` |
145+
| `get_special_points()` | Approximation, Spline | `list[list[float]] \| None` |
146+
| `get_evaluation_points()` | all four | `np.ndarray` shape `(N, num_dim)` |
147+
| `get_num_evaluation_points()` | all four | `int` |
148+
149+
`get_evaluation_points()` returns the flat `(N, num_dim)` grid (MoCaX style,
150+
matches `len(get_evaluation_points()) == get_num_evaluation_points()`).
151+
152+
### Static helpers
153+
154+
```python
155+
ChebyshevApproximation.peek_format_version("model.pcb") # → 1
156+
157+
ChebyshevTT.is_dimensionality_allowed(5) # → True
158+
```
159+
160+
### Deferred construction (Approximation, Spline)
161+
162+
Construct a grid-only interpolant, then fill its tensor in place:
163+
164+
```python
165+
cheb = ChebyshevApproximation(None, 2, [[-1,1],[-1,1]], [10,10],
166+
defer_build=True)
167+
168+
# Compute values externally — e.g. on a distributed cluster
169+
points = cheb.get_evaluation_points() # (100, 2)
170+
values = compute_function_on_cluster(points).reshape(10, 10)
171+
172+
cheb.set_original_function_values(values)
173+
# now evaluable
174+
result = cheb.eval([0.3, -0.4], [0, 0])
175+
```
176+
177+
This is the in-place analog of the `from_values()` factory. Bit-identical
178+
results in both paths.
179+
180+
### Optional typed helpers
181+
182+
```python
183+
from pychebyshev import Domain, Ns, SpecialPoints, ChebyshevApproximation
184+
185+
cheb = ChebyshevApproximation(
186+
f, 2,
187+
Domain([(0.0, 1.0), (0.0, 1.0)]), # equivalent to [[0,1],[0,1]]
188+
Ns([15, 15]), # equivalent to [15, 15]
189+
)
190+
```
191+
192+
Constructors of all four classes accept both raw lists and these frozen
193+
dataclasses.

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "pychebyshev"
7-
version = "0.15.0"
7+
version = "0.16.0"
88
description = "Fast multi-dimensional Chebyshev tensor interpolation with analytical derivatives"
99
readme = "README.md"
1010
license = {text = "MIT"}

src/pychebyshev/__init__.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,58 @@
2121
0.7764
2222
"""
2323

24+
from __future__ import annotations
25+
26+
from dataclasses import dataclass
27+
2428
from pychebyshev._version import __version__
2529
from pychebyshev.barycentric import ChebyshevApproximation
2630
from pychebyshev.slider import ChebyshevSlider
2731
from pychebyshev.spline import ChebyshevSpline
2832
from pychebyshev.tensor_train import ChebyshevTT
2933

30-
__all__ = ["ChebyshevApproximation", "ChebyshevSlider", "ChebyshevSpline", "ChebyshevTT", "__version__"]
34+
35+
@dataclass(frozen=True)
36+
class Domain:
37+
"""Typed container for an interpolant's per-dimension bounds.
38+
39+
Equivalent to a raw ``list[tuple[float, float]]``. Constructors of
40+
all four PyChebyshev classes accept either form.
41+
"""
42+
43+
bounds: list[tuple[float, float]]
44+
45+
46+
@dataclass(frozen=True)
47+
class Ns:
48+
"""Typed container for an interpolant's per-dimension node counts.
49+
50+
Equivalent to a raw ``list[int]``. Accepted by
51+
:class:`ChebyshevApproximation`, :class:`ChebyshevSpline`,
52+
:class:`ChebyshevSlider`, and :class:`ChebyshevTT`.
53+
"""
54+
55+
counts: list[int]
56+
57+
58+
@dataclass(frozen=True)
59+
class SpecialPoints:
60+
"""Typed container for per-dimension kink/knot locations.
61+
62+
Equivalent to a raw ``list[list[float]]``. Accepted by
63+
:class:`ChebyshevApproximation` and :class:`ChebyshevSpline`.
64+
"""
65+
66+
knots_per_dim: list[list[float]]
67+
68+
69+
__all__ = [
70+
"ChebyshevApproximation",
71+
"ChebyshevSlider",
72+
"ChebyshevSpline",
73+
"ChebyshevTT",
74+
"Domain",
75+
"Ns",
76+
"SpecialPoints",
77+
"__version__",
78+
]

0 commit comments

Comments
 (0)