PySINDy Compatibility Policy (v0.31b3)#
Status: IN_PROGRESS (v0.31b3)
Decision label: downstream_pysindy_compatibility_policy_and_wheel_hardening
Outcome: C_temporary_1x_policy — the pdelie[downstream] extra pins
pysindy>=1.7.5,<2 as a declared temporary policy. PySINDy 2.x is
not supported in v0.31. A dedicated migration release
(v0.31.1 / v0.32) will widen the pin once the four independent
2.x-API deltas are absorbed under a version-dispatch shim and once the
transitive numpy>=2 floor bump is coordinated.
This document is the single source of truth for the supported PySINDy
range, the exact public pdelie surfaces covered under that range, the
known 1.x-vs-2.x API differences, and the explicit non-claims the
downstream compatibility surface makes. The machine-readable form of
this policy lives at
configs/pysindy_compatibility_matrix.json and is emitted verbatim
under summary_type = "pdelie_pysindy_compatibility_matrix".
Supported PySINDy versions#
1.7.5(primary tested — pinned bypdelie[downstream]/pdelie[test]).
The pyproject constraint is:
pysindy>=1.7.5,<2
Primary tested version#
1.7.5— verified againstpdelie 0.30.0on Python 3.11.7 withnumpy 1.26.4,scipy 1.17.1, andscikit-learn 1.2.2. All 61 targetedpdelie.tasks.discovery/pdelie.tasks.weak_pde_librarytests pass (b0/b1/b2 test files) with no test-visible failures.
Secondary tested version#
None. The declared range admits exactly one PySINDy release
(1.7.5). Later 1.7.x point releases (if any) are covered by the
<2 upper bound but are not separately exercised.
Unsupported versions#
<1.7.5— not exercised; thepdelie.discovery.pysindy_adaptercode paths depend onSINDy.coefficients()returning the(n_state, n_library_terms)matrix shape and onlibrary.get_feature_names()being available on the fitted library. Earlier1.7.xsub-minors are excluded to keep the supported version set to exactly one line-item.>=2.0.0— HARD-broken againstpdelie 0.30.0on three independent axes:pysindy.SINDy(feature_names=..., discrete_time=..., t_default=...)constructor kwargs removed.pdelie.discovery.pysindy_adapter._build_pysindy_modelraisesTypeErroron any 2.x install.pysindy.WeakPDELibrary(library_functions=..., function_names=..., interaction_only=...)kwargs removed.pdelie.tasks.weak_pde_library._construct_weak_pde_libraryraisesScopeValidationErroron any 2.x install (the existing runtime guard catches theTypeErrorand rethrows).pysindy.SINDy.differentiate(...)removed.pdelie.tasks.discovery._compute_residuals_from_predictionsraisesAttributeErroron the residual path.
Additionally,
pysindy 2.xrequiresnumpy>=2.0, whilepdelie 0.30.0pinsnumpy<2.pipresolvesResolutionImpossibleon any joint install. Admittingpysindy>=2is therefore an atomic release-note question coupled with anumpyfloor bump; it is deferred to thev0.31.1/v0.32migration release.
Public pdelie surfaces covered by this policy#
The following runtime public surfaces are the exact call-sites that
this compatibility policy guards. They are the surfaces a
pdelie[downstream] user actually exercises through PySINDy under the
supported range:
pdelie.tasks.discovery.run_pysindy_pde_taskpdelie.tasks.discovery.summarize_discovery_task_resultpdelie.tasks.discovery.PySINDyDiscoveryUnsupportedBoundaryErrorpdelie.tasks.weak_pde_library.inspect_pysindy_weak_pde_librarypdelie.tasks.weak_pde_library.summarize_pysindy_weak_pde_library_diagnosticpdelie.tasks.weak_pde_library.WeakPDELibraryDiagnostic
Re-exports on pdelie.tasks (v0.31b2) are covered by the same policy.
No root pdelie export is added, and no submodule outside
pdelie.tasks.* re-exports any of the six names above.
Internal helpers that the future v0.31.1 / v0.32 shim will live
under are explicitly private:
pdelie.discovery.pysindy_adapter(existing, private).pdelie.discovery._pysindy_defaults(existing, private).pdelie.discovery._pysindy_compat(deferred; will be introduced when the pin is widened to admit PySINDy 2.x; no such module exists inv0.31b3).
The private-helper convention is enforced by the
configs/release_gate_manifest.json 0.31 row: _pysindy_compat and
any accompanying constants such as SUPPORTED_PYSINDY_VERSIONS are
recorded as forbidden_root_attributes so they cannot leak to the
pdelie root once introduced.
Explicit non-claims#
The v0.31b3 downstream compatibility surface makes none of the following claims. Each item below is a non-claim of the covered surfaces above, not a hidden guarantee.
No WSINDy benchmark. The wrapper produces a diagnostic-only strict-JSON summary (
summary_type = "pdelie_weak_pde_library_diagnostic",diagnostic_only = True). No sparse-recovery correctness claim, no design-matrix conditioning claim, and no recovery-rate benchmark are made.No noise robustness claim. No clean-vs-noisy gate is added. Callers are responsible for controlling input noise; the wrapper does not add or remove noise from any
FieldBatch.No nonperiodic discovery. The task-runner and the WeakPDELibrary wrapper reject nonperiodic-x
FieldBatchinputs withPySINDyDiscoveryUnsupportedBoundaryErrorbefore any PySINDy call. FD-nonperiodic PySINDy discovery is av0.32.5target.No PDEBench / The Well support claim. No external-dataset benchmark is claimed. The v0.28
pdelie.data.from_xarray_datasetreadiness path is orthogonal to this policy.No
pdelie.residuals.weak_1dretirement.weak_1dis retained throughv0.32close. This policy does not claim numerical equivalence between the WeakPDELibrary wrapper andweak_1d.No PySINDy 2.x support.
>=2.0.0is explicitly unsupported.
Known API differences (1.x vs 2.x)#
The following diffs are recorded here so that the future v0.31.1 /
v0.32 shim has a single reference point. They are not shimmed in
v0.31b3 — the pin excludes 2.x entirely. Recording them here is a
planning artifact.
Each row cites the pdelie call-site that would break under 2.x and the compact required change.
PySINDy API surface |
1.x behavior |
2.x behavior |
pdelie call-site |
Required change (deferred) |
|---|---|---|---|---|
|
accepts |
accepts only |
|
drop |
|
accepts |
drops all of the above kwargs; |
|
rewrite default |
|
present; |
REMOVED ( |
|
replace with a call through |
|
|
|
|
doc-only for the current single-traj call site; multi-traj callers must switch idiom |
|
present on fitted |
absent |
none in pdelie today |
doc-only; gate any future introspection on version |
|
accepts |
drops those; requires |
not built directly in pdelie today (docstring reference only); downstream examples that build a |
rewrite docs/examples; optionally add a |
|
accepts |
drops those; requires |
|
refactor to build a |
|
|
IDENTICAL shape and semantics |
|
none — this is the anchor of stability that makes the future shim feasible |
numpy floor (transitive) |
|
|
|
any release that admits |
|
|
|
none at code level; |
document the |
Install footgun: setuptools<81 with PySINDy 1.x#
pysindy 1.7.5 imports pkg_resources at package init, which was
removed from setuptools>=81. Downstream users installing
pdelie[downstream] into a fresh venv with pip 25.x may hit
ModuleNotFoundError: No module named 'pkg_resources' when importing
pysindy.
v0.31c1 adversarial install matrix — outcome B (setuptools cap added)#
The v0.31c self-sufficiency claim held only because python -m venv on
Python 3.11 pre-bundles setuptools 65.5.0 (which ships
pkg_resources). The v0.31c1 sub-release ran an adversarial follow-up
matrix on fresh Python 3.11 venvs with the ambient setuptools version
force-pinned to 81.0.0, 82.0.0, and 83.0.0 BEFORE installing
pdelie[downstream] — i.e. simulating a realistic environment where the
user (or the system, or a routine pip install --upgrade setuptools)
has already upgraded past the pkg_resources boundary.
Ambient setuptools |
pdelie[downstream] install |
|
Smoke |
|---|---|---|---|
|
pass |
pass (removal warning) |
pass |
|
pass |
fail — |
not run |
|
pass |
fail — same |
not run |
Outcome B_setuptools_82_boundary. The install itself never fails
(pip check reports clean); the failure surfaces only at
import pysindy because pysindy/__init__.py does from pkg_resources import DistributionNotFound, and setuptools 82 removed the
pkg_resources module entirely.
v0.31c1 amends the [downstream] extra with the narrow temporary
constraint:
setuptools<82; python_version < '3.12'
Post-fix verification (fresh venvs, rebuilt wheel): with ambient
setuptools 82.0.0 or 83.0.0 pre-installed, pip install "<wheel>[downstream]" auto-downgrades setuptools to 81.0.0 and both
smoke paths (run_pysindy_pde_task, inspect_pysindy_weak_pde_library)
plus the example CLI (python -m pdelie.examples.downstream_discovery_task_bridge) pass. Fresh venv
baseline: setuptools stays at the bundled 65.5.0, untouched.
The cap is scoped to python_version < '3.12' because the pysindy /
sklearn deps themselves are already scoped that way — Python 3.12+
does not receive pdelie[downstream] in v0.31. The constraint is
temporary: it retires when the pysindy pin widens to >=2 in
v0.31.1 / v0.32, since pysindy 2.x uses importlib.metadata
instead of pkg_resources.
v0.31c clean-install audit — outcome A (self-sufficient on Python 3.11)#
The v0.31c sub-release ran the mandatory clean-install audit with the
advertised command pip install "<wheel>[downstream]" into a fresh
Python 3.11 venv, without any manual setuptools<81 co-install.
The install succeeded end-to-end: CPython 3.11’s python -m venv
still bundles setuptools 65.5.0 (which ships pkg_resources), so
the standard install path is self-sufficient. Both v0.31 downstream
task paths (run_pysindy_pde_task and inspect_pysindy_weak_pde_library)
executed, and both emitted payloads passed
json.dumps(payload, allow_nan=False).
Therefore no setuptools<81 constraint is added to the
pdelie[downstream] extra in v0.31c. The declared python_version < '3.12' marker on the downstream deps already gates the footgun
population: on Python 3.12+, python -m venv no longer bundles
setuptools, and the pysindy pkg_resources import would fail without
an explicit co-install. That population is not supported by
pdelie[downstream] today.
Workaround for the 1.x line on Python 3.12+ (opt-in only)#
python -m pip install 'setuptools<81'
python -m pip install 'pdelie[downstream]'
Widening the pin to pysindy>=2 eliminates this footgun; that
migration is scheduled for v0.31.1 / v0.32.
CI matrix#
The CI matrix is machine-readable at
configs/pysindy_compatibility_matrix.json. Both currently-blocking
jobs exercise PySINDy 1.7.5 via the .[test] extra, which resolves
to the pinned pysindy>=1.7.5,<2 range:
Job |
PySINDy version |
Blocking |
|---|---|---|
|
|
yes |
|
|
yes |
No new CI job is required for v0.31b3. A dedicated matrix over
PySINDy versions is unnecessary while the pin admits exactly one
major and one minor line. When v0.31.1 / v0.32 widens the pin,
an advisory pysindy-2x job on pysindy==2.1.0 will be added.
Resource envelope#
The numbers below are diagnostic guidance only — a single point measurement from one manufactured Heat 1D fixture on one developer machine. They are recorded so downstream users have an order-of- magnitude sense of the WeakPDELibrary diagnostic’s cost. They are not a public runtime memory limit, not a performance contract, and not a supported benchmark. No pdelie API is gated on them.
Representative WeakPDELibrary run (v0.31b3):
Field |
Value |
|---|---|
field shape |
|
grid shape |
|
K (weak windows) |
60 |
feature count |
8 ( |
weak matrix |
|
runtime |
~0.75 s ( |
peak RSS |
~144 MiB ( |
PySINDy |
1.7.5 |
Method: the measurement wraps a single call to
pdelie.tasks.weak_pde_library.inspect_pysindy_weak_pde_library on a
manufactured periodic Heat 1D field batch generated by
pdelie.data.heat_1d.generate_heat_1d_field_batch. Peak RSS is the
resident set size of the whole Python process (including NumPy, SciPy,
PySINDy, and pdelie import overhead), reported via
resource.getrusage(RUSAGE_SELF).ru_maxrss and converted from bytes
to MiB on macOS. Note: the intended representative grid was
T=X=128, but the wrapper’s defensive lower bound max(8, 4*K) requires
at least 240 samples per axis for K=60, so the grid was bumped to
T=X=256 (the nearest power-of-two admissible above 240). Runs with
lower K may fit on the T=X=128 grid at proportionally lower runtime
and RSS.