Modernize PyRCN for scikit-learn 1.6+ (Python 3.10+)#62
Merged
Conversation
The nn/ PyTorch subpackage was an early draft and is out of scope for the scikit-learn compatibility modernization. Remove it along with the torch, torchvision and torchaudio dependencies so that `import pyrcn` no longer requires PyTorch. - Delete src/pyrcn/nn/ - Drop nn from the top-level package imports and __all__ - Strip torch from util.seed_everything (keep random/numpy seeding) - Remove torch/torchvision/torchaudio from pyproject dependencies Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Prepare for scikit-learn >= 1.6 by removing reliance on private or deprecated symbols. No behavioral change. - base/_activations.py: define the inplace activation table locally instead of importing and mutating the private sklearn.neural_network._base.ACTIVATIONS global. - Drop the deprecated @_deprecate_positional_args decorator throughout; __init__/metric signatures are already keyword-only via `*`. - echo_state_network, extreme_learning_machine: use public sklearn.base.RegressorMixin in type hints instead of the private sklearn.linear_model._base.LinearModel. - node_to_node: import csr_matrix from scipy.sparse, not the removed scipy.sparse.csr submodule. - Replace the typing_extensions / sys.version_info shims with plain typing (Python >= 3.10) and drop now-dead `import sys`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Custom estimators relied on APIs removed or deprecated in scikit-learn 1.6: - BaseEstimator._validate_data / _check_n_features were removed. Replace them with the public sklearn.utils.validation.validate_data function. - The estimator type is now derived from tags and _estimator_type is deprecated. Per the official developer guide, mixins must precede BaseEstimator in the MRO so __sklearn_tags__ resolves the estimator type correctly; reorder the bases of every estimator accordingly. This restores is_regressor / is_classifier detection. - Coates: replace the getattr(clusterer, "_estimator_type") check with the public sklearn.base.is_clusterer helper. No behavioral change intended. The suite goes from 50 to 21 failing; the remaining failures are all in the sequence-aware metrics wrappers and are addressed separately. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The sequence-aware metric wrappers called scikit-learn's private _check_targets / _check_reg_targets helpers, whose signatures changed in 1.6 (sample_weight became positional), causing unpacking and validation errors. Replace those calls: flatten the per-sequence arrays with the public check_consistent_length + np.concatenate and derive the target type via the public sklearn.utils.multiclass.type_of_target. The public metric performs its own target validation, so the private helpers are no longer needed. Also route mean_squared_error(squared=False) to the public root_mean_squared_error, since the `squared` parameter was removed from sklearn.metrics.mean_squared_error in 1.6. PyRCN's own `squared` argument is preserved. No behavioral change intended. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The repository moved from TUD-STKS/PyRCN to PlasmaControl/PyRCN. Update all GitHub and mybinder links in the README, docs and example notebook. Links to other TUD-STKS repositories (Automatic-Music-Transcription, gci_estimation) are left unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Bump requires-python to >=3.10 (the floor for scikit-learn >= 1.6) and add a scikit-learn>=1.6 dependency floor; list numpy/scipy/joblib/pandas explicitly. - Fix the license classifier (was MIT; the LICENSE file and all source headers are BSD-3-Clause). - Single source of truth for the version: pyproject reads it dynamically from pyrcn._version, and _version.py is set to 0.0.18 (was 0.0.17post1). - Add test and examples optional-dependency extras. - Remove the redundant setup.cfg (pyproject is now canonical). - Fix requirements.txt: the malformed `requests[...]` lines were meant to be matplotlib/seaborn/ipywidgets/ipympl/tqdm; drop obsolete typing-extensions. - mypy: ignore_missing_imports for third-party packages without type stubs. - Read the Docs: build with Python 3.12 (>=3.10 is now required). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- CI matrix: Python 3.10-3.13 (drop 3.9); bump actions/checkout to v4 and actions/setup-python to v5 in both workflows. - Install the package via the `test` extra instead of an ad-hoc plugin list plus requirements.txt; run flake8 (blocking) and mypy (informational, the remaining strict-typing findings are deferred to the code-unification pass). - Remove pytest.ini, whose addopts required cov/mypy plugins just to collect tests; pyproject's [tool.pytest.ini_options] keeps a bare `pytest` working, and coverage is requested explicitly in CI. - Fix a flake8 E231 in tests so linting can cover the test suite too. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Apply modern Python 3.10 idioms (the new requires-python floor) consistently: - Type hints: PEP 604 unions (Union[A, B] -> A | B, Optional[A] -> A | None) and PEP 585 builtin generics (Dict/List/Tuple -> dict/list/tuple); move Callable/Iterable to collections.abc. Applied with pyupgrade --py310-plus. - Replace str.format(...) calls with f-strings. - Sort and group imports consistently (isort) and add `from __future__ import annotations` to every module. - Reflow the multi-line signatures whose alignment shifted. Purely mechanical; no behavioral change. flake8 clean, suite unchanged (85 passed / 2 skipped). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Remove the dead, commented-out NonlinearVectorAutoregression class that had been left as a module-level string literal in blocks/_input_to_node.py. - Replace `raise BaseException(...)` with `raise TypeError(...)` in the ESN and ELM partial_fit guards; BaseException should never be raised directly. - Fix a stray `%s` left in a NodeToNode hidden_layer_size error message. No behavioral change (aside from the more specific exception type on an invalid-regressor error path). flake8 clean, suite unchanged (85 passed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
_make_sparse takes a random_state argument but drew the retained-weight indices from the global numpy RNG (np.random.choice), so the sparsity mask was not reproducible from a fixed seed. Use the passed random_state. This makes the sparse weight masks deterministic w.r.t. random_state (the exact mask for a given seed changes); the test suite is unaffected. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Coates no longer calls check_random_state in __init__; it stores the raw random_state and resolves it in _extract_random_patches, so get_params / clone round-trip correctly. Numerically equivalent for a single fit. - Replace the mutable default arguments Coates(clusterer=KMeans()) and lorenz(x_0=[1.0, 1.0, 1.0]) with None sentinels, constructing the default inside the method/function to avoid a shared mutable instance. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Resolve the remaining strict-mypy findings so `mypy src/pyrcn` is clean, and drop the informational `continue-on-error` from the CI type-check step. - util.batched: annotate the generator return as Iterator[tuple]. - util.value_to_tuple: correct the signature (value may be a tuple; size is an int) and always return. - BatchIntrinsicPlasticity: initialize self._m/_c as floats (they are assigned float distribution parameters). - Coates: type the pooling axis as `int | None`, and assert self.clusterer is not None at the post-fit use sites (it is resolved to KMeans() in fit). No behavioral change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Apply the same modernization used for the package to tests/: PEP 604 / PEP 585 type syntax and f-strings (pyupgrade --py310-plus), consistent import sorting with `from __future__ import annotations` (isort), and removal of unused imports (autoflake). No test logic changed; suite unchanged (85 passed). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Modernize PyRCN to run on the current scientific-Python stack
(scikit-learn 1.9 / NumPy 2.x / SciPy 1.18 / Python 3.10–3.13) and to
clean up the codebase, without changing the public API. The full test suite
passes (85 passed / 2 skipped);
flake8andmypyare clean.The PyTorch
nnsubpackage (an early draft) is removed and torch is droppedas a dependency — a redesigned backend for sequential data is planned
separately.
Phase 1 — scikit-learn 1.6+ compatibility (no behavioral change)
nnsubpackage and the torch/torchvision/torchaudio deps.sklearn/scipyimports with public APIs(own
ACTIVATIONStable,RegressorMixintype hints,scipy.sparse,drop
_deprecate_positional_args, plaintyping).validate_data,mixins-before-
BaseEstimatorMRO (restoresis_regressor/is_classifier),is_clustererin Coates.(drop private
_check_targets/_check_reg_targets; routemean_squared_error(squared=False)toroot_mean_squared_error).requires-python >=3.10,scikit-learn>=1.6, dynamic version(0.0.18), fix the BSD license classifier, drop
setup.cfg/pytest.ini,CI matrix 3.10–3.13, update repo URLs to the PlasmaControl org.
Phase 2 — code unification + bug fixes
f-strings, sorted imports +
from __future__ import annotations,BaseException→TypeError, dead-code removal, strictmypy(now enforcedin CI), and the same modernization applied to
tests/._make_sparsenow uses the passedrandom_stateinstead of the globalNumPy RNG, so sparse weights are reproducible from a seed (the exact mask
for a given seed changes).
random_state(resolved infit) and no longer uses a shared mutableKMeans()default, soclone/get_paramsround-trip correctly. Verifiednumerically identical for a single fit with an explicit clusterer; the
differing cases (clone/
get_params, default clusterer across multipleinstances, repeated
fitwith an int seed) are all where the previousbehavior was a bug.
🤖 Generated with Claude Code