diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 8d5748e..30c80a6 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,47 +1,45 @@ repos: -- repo: https://github.com/datarootsio/databooks - rev: 1.3.7 - hooks: - - id: databooks-meta -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.4.0 - hooks: - - id: check-added-large-files - args: ['--maxkb=300'] - - id: check-byte-order-marker - - id: check-case-conflict - - id: check-merge-conflict - - id: end-of-file-fixer - - id: forbid-new-submodules - - id: mixed-line-ending - - id: trailing-whitespace - - id: debug-statements - - id: check-yaml - exclude: .conda/meta.yaml - - id: requirements-txt-fixer -- repo: https://github.com/pycqa/flake8.git - rev: 6.1.0 - hooks: - - id: flake8 - additional_dependencies: [flake8-print, "importlib-metadata<5.0.0"] - args: [--config=setup.cfg] - exclude: ^examples/ -- repo: https://github.com/psf/black - rev: 23.11.0 - hooks: - - id: black - language_version: python3 - additional_dependencies: ['click==8.0.4'] -- repo: https://github.com/pycqa/isort - rev: 5.11.2 - hooks: - - id: isort - name: isort - entry: python -m isort - args: [--settings-path, ./pyproject.toml] - language: system - types: [python] -- repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.5.0 - hooks: - - id: mypy + - repo: https://github.com/datarootsio/databooks + rev: 1.3.7 + hooks: + - id: databooks-meta + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.4.0 + hooks: + - id: check-added-large-files + args: ["--maxkb=300"] + - id: check-byte-order-marker + - id: check-case-conflict + - id: check-merge-conflict + - id: end-of-file-fixer + - id: forbid-new-submodules + - id: mixed-line-ending + - id: trailing-whitespace + - id: debug-statements + - id: check-yaml + exclude: .conda/meta.yaml + - id: requirements-txt-fixer + - repo: https://github.com/pycqa/flake8.git + rev: 6.1.0 + hooks: + - id: flake8 + additional_dependencies: [flake8-print, "importlib-metadata<5.0.0"] + args: [--config=setup.cfg] + exclude: ^examples/ + - repo: https://github.com/psf/black + rev: 23.11.0 + hooks: + - id: black + language_version: python3 + additional_dependencies: ["click==8.0.4"] + - repo: https://github.com/pycqa/isort + rev: 5.13.2 + hooks: + - id: isort + name: isort + args: [--settings-path, ./pyproject.toml] + types: [python] + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.5.0 + hooks: + - id: mypy diff --git a/README.md b/README.md index cb87e08..f32c024 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,32 @@ -# Kale-Linear +

+kalelinear logo +

-Kale-Linear is a Python library for non-deep, knowledge-aware machine learning -from multiple sources, domains, or views. It provides NumPy-based methods for -leveraging related data distributions and structural assumptions, including -transfer learning, domain adaptation, manifold regularization, and group-aware -learning, through a scikit-learn style API. + + +[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/pykale/linear/blob/main/LICENSE) +[![Python](https://img.shields.io/badge/python-3.10%20%7C%203.11%20%7C%203.12-blue)](https://www.python.org) +[![PyPI version](https://img.shields.io/pypi/v/kalelinear?color=blue)](https://pypi.org/project/kalelinear/) +[![PyPI downloads](https://pepy.tech/badge/kalelinear)](https://pepy.tech/project/kalelinear) -The package is part of the PyKale ecosystem and focuses on classical linear and -kernel methods that are useful when data are structured by domain labels, -covariates, side information, or unlabeled target samples. -## Features +`kalelinear` is a Python library for learning harmonized or individualized models from multi-source/multi-view data in linear or reproducing kernel Hilbert spaces (RKHS). It provides NumPy-based methods for leveraging related data distributions and structural assumptions, including transfer learning, domain adaptation, manifold regularization, and group-aware learning, through a [`scikit-learn`](https://github.com/scikit-learn/scikit-learn) style API. + +The package is part of the [PyKale](https://github.com/pykale/pykale) ecosystem and focuses on linear and kernel methods for data characterized by covariates (e.g., domain labels, group labels, side information), unlabeled target samples, or tensor structures. + +## Methods and features - Transformer models for learning feature embeddings: - - Multilinear Principal Component Analysis (MPCA): Lu et al., 2008 - [[IEEE](https://ieeexplore.ieee.org/abstract/document/4359192)] - - Transfer Component Analysis (TCA): Pan et al., 2009 - [[paper](http://www.aaai.org/ocs/index.php/IJCAI/IJCAI-09/paper/download/294/962)] - - Joint Distribution Adaptation (JDA): Long et al., 2013 - [[paper](http://openaccess.thecvf.com/content_iccv_2013/papers/Long_Transfer_Feature_Learning_2013_ICCV_paper.pdf)] - - Balanced Distribution Adaptation (BDA): Wang et al., 2017 - [[paper](http://jd92.wang/assets/files/a08_icdm17.pdf)] - - Maximum Independence Domain Adaptation (MIDA): Yan et al., 2017 - [[paper](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=7815350)] -- Estimator models for classification and adaptation: - - Manifold Regularization Learning Framework (LapSVM, LapRLS): Belkin et al., - 2006 [[paper](http://www.jmlr.org/papers/v7/belkin06a.html)] - - Adaptation Regularization Learning Framework (ARSVM, ARRLS): Long et al., - 2014 [[paper](https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=6550016)] - - Covariate Independence Regularized Learning Framework (CoIRSVM, CoIRLS): - Zhou et al., 2020 - [[paper](https://aaai.org/ojs/index.php/AAAI/article/view/6179)], - Zhou, 2022 [[thesis](https://etheses.whiterose.ac.uk/id/eprint/31044/)] - - Group-specific Discriminant Analysis (GSDA): Zhou et al., 2025 - [[paper](https://academic.oup.com/gigascience/article/doi/10.1093/gigascience/giaf082/8244707)], - Zhou, 2022 [[thesis](https://etheses.whiterose.ac.uk/id/eprint/31044/)] + - Multilinear Principal Component Analysis (MPCA) [[1](#references)] + - Transfer Component Analysis (TCA) [[2](#references)] + - Joint Distribution Adaptation (JDA) [[3](#references)] + - Balanced Distribution Adaptation (BDA) [[4](#references)] + - Maximum Independence Domain Adaptation (MIDA) [[5](#references)] +- Estimator models for classification: + - Manifold Regularization Learning Framework (LapSVM, LapRLS) [[6](#references)] + - Adaptation Regularization Learning Framework (ARSVM, ARRLS) [[7](#references)] + - Covariate Independence Regularized Learning Framework (CoIRSVM, CoIRLS) [[8](#references)][[9](#references)] + - Group-specific Discriminant Analysis (GSDA) [[9](#references)][[10](#references)] - NumPy-compatible inputs and outputs. - scikit-learn style `fit`, `transform`, `predict`, `fit_transform`, and `fit_predict` workflows where applicable. @@ -54,7 +46,7 @@ Install from a local checkout for development: pip install -e ".[dev]" ``` -Kale-Linear requires Python 3.10 or later. Core dependencies include: +`kalelinear` requires Python 3.10 or later. Core dependencies include: - [NumPy](http://www.numpy.org/) - [SciPy](https://www.scipy.org/) @@ -198,8 +190,29 @@ From the root of the repository, run the following commands in your terminal: sphinx-build -b html docs/source docs/build/html ``` +# References + +[1] Lu, H., Plataniotis, K.N. and Venetsanopoulos, A.N., 2008. [MPCA: Multilinear principal component analysis of tensor objects](https://ieeexplore.ieee.org/abstract/document/4359192/). _IEEE Transactions on Neural Networks_, 19(1), pp.18-39. + +[2] Pan, S.J., Tsang, I.W., Kwok, J.T. and Yang, Q., 2011. [Domain adaptation via transfer component analysis](https://dl.acm.org/doi/abs/10.1109/TNN.2010.2091281). _IEEE Transactions on Neural Networks_, 22(2), p.199-210. + +[3] Long, M., Wang, J., Ding, G., Sun, J. and Yu, P.S., 2013. [Transfer feature learning with joint distribution adaptation.](https://openaccess.thecvf.com/content_iccv_2013/papers/Long_Transfer_Feature_Learning_2013_ICCV_paper.pdf) In _Proceedings of the IEEE International Conference on Computer Vision_ (pp. 2200-2207). + +[4] Wang, J., Chen, Y., Hao, S., Feng, W. and Shen, Z., 2017, November. [Balanced distribution adaptation for transfer learning](https://ieeexplore.ieee.org/document/8215613). In _2017 IEEE International Conference on Data Mining (ICDM)_ (pp. 1129-1134). IEEE. + +[5] Yan, K., Kou, L. and Zhang, D., 2017. [Learning domain-invariant subspace using domain features and independence maximization](https://ieeexplore.ieee.org/document/7815350). _IEEE Transactions on Cybernetics_, 48(1), pp.288-299. + +[6] Belkin, M., Niyogi, P. and Sindhwani, V., 2006. Manifold regularization: [A geometric framework for learning from labeled and unlabeled examples](https://www.jmlr.org/papers/v7/belkin06a.html). _Journal of Machine Learning Research_, 7(11). + +[7] Long, M., Wang, J., Ding, G., Pan, S.J. and Yu, P.S., 2013. [Adaptation regularization: A general framework for transfer learning](https://ieeexplore.ieee.org/abstract/document/6550016/). _IEEE Transactions on Knowledge and Data Engineering_, 26(5), pp.1076-1089. + +[8] Zhou, S., Li, W., Cox, C. and Lu, H., 2020, April. [Side information dependence as a regularizer for analyzing human brain conditions across cognitive experiments](https://ojs.aaai.org/index.php/AAAI/article/view/6179). In _Proceedings of the AAAI Conference on Artificial Intelligence_ (Vol. 34, No. 04, pp. 6957-6964). + +[9] Zhou, S., 2022. [Interpretable Domain-Aware Learning for Neuroimage Classification](https://etheses.whiterose.ac.uk/id/eprint/31044/) (Doctoral dissertation, University of Sheffield). + +[10] Zhou, S., Luo, J., Jiang, Y., Wang, H., Lu, H. and Gong, G., 2025. [Group-specific discriminant analysis enhances detection of sex differences in brain functional network lateralization](https://academic.oup.com/gigascience/article/doi/10.1093/gigascience/giaf082/8244707). _GigaScience_, 14, p.giaf082. -## Related Projects +## Other open domain adaptation repositories - [POT: Python Optimal Transport](https://github.com/rflamary/POT) - [Everything about Transfer Learning](https://github.com/jindongwang/transferlearning) @@ -211,4 +224,4 @@ From the root of the repository, run the following commands in your terminal: ## License -Kale-Linear is released under the MIT License. See [LICENSE](LICENSE) for details. +`kalelinear` is released under the MIT License. See [LICENSE](LICENSE) for details. diff --git a/docs/images/kalelinear.jpg b/docs/images/kalelinear.jpg new file mode 100644 index 0000000..ffb3519 Binary files /dev/null and b/docs/images/kalelinear.jpg differ diff --git a/docs/source/api.rst b/docs/source/api.rst index 44d0c82..29a5189 100644 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -6,6 +6,8 @@ API Reference This page is kept for existing links. The API reference is now split into the following pages: +* :doc:`api_embed` * :doc:`api_transformers` +* :doc:`api_predict` * :doc:`api_estimators` * :doc:`api_utilities` diff --git a/docs/source/api_embed.rst b/docs/source/api_embed.rst new file mode 100644 index 0000000..a3dd0dd --- /dev/null +++ b/docs/source/api_embed.rst @@ -0,0 +1,37 @@ +Embed +===== + +.. currentmodule:: kalelinear.embed + +This compatibility module mirrors the PyKale-style import path while exposing +the same transformer classes as :mod:`kalelinear.transformer`. + +.. autoclass:: MPCA + :members: + :undoc-members: + :show-inheritance: + :no-index: + +.. autoclass:: TCA + :members: + :undoc-members: + :show-inheritance: + :no-index: + +.. autoclass:: JDA + :members: + :undoc-members: + :show-inheritance: + :no-index: + +.. autoclass:: BDA + :members: + :undoc-members: + :show-inheritance: + :no-index: + +.. autoclass:: MIDA + :members: + :undoc-members: + :show-inheritance: + :no-index: diff --git a/docs/source/api_predict.rst b/docs/source/api_predict.rst new file mode 100644 index 0000000..8483b17 --- /dev/null +++ b/docs/source/api_predict.rst @@ -0,0 +1,49 @@ +Predict +======= + +.. currentmodule:: kalelinear.predict + +This compatibility module mirrors the PyKale-style import path while exposing +the same estimator classes as :mod:`kalelinear.estimator`. + +.. autoclass:: LapSVM + :members: + :undoc-members: + :show-inheritance: + :no-index: + +.. autoclass:: LapRLS + :members: + :undoc-members: + :show-inheritance: + :no-index: + +.. autoclass:: ARSVM + :members: + :undoc-members: + :show-inheritance: + :no-index: + +.. autoclass:: ARRLS + :members: + :undoc-members: + :show-inheritance: + :no-index: + +.. autoclass:: CoIRSVM + :members: + :undoc-members: + :show-inheritance: + :no-index: + +.. autoclass:: CoIRLS + :members: + :undoc-members: + :show-inheritance: + :no-index: + +.. autoclass:: GSDA + :members: + :undoc-members: + :show-inheritance: + :no-index: diff --git a/docs/source/index.rst b/docs/source/index.rst index 20b5b87..90ee5fd 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -17,7 +17,9 @@ Kale-Linear API .. toctree:: :maxdepth: 2 + api_embed api_transformers + api_predict api_estimators api_utilities @@ -31,7 +33,8 @@ Project .. toctree:: :maxdepth: 1 - GitHub repository + Read the Docs (PyKale subproject) + GitHub repository Indices and Tables ------------------ diff --git a/kalelinear/__init__.py b/kalelinear/__init__.py index b3408be..a5bc0f5 100644 --- a/kalelinear/__init__.py +++ b/kalelinear/__init__.py @@ -1,9 +1,19 @@ """ kalelinear. -A Transfer Learning Python package +Learning harmonized or individualized models from multi-source/multi-view data in linear or reproducing kernel Hilbert spaces (RKHS). """ -__version__ = "0.0.1" -__author__ = "Shuo Zhou" -__credits__ = "Machine Learning Group, Department of Computer Science, the University of Sheffield" +from importlib import import_module + +__version__ = "0.1.0a1" + +__all__ = ["transformer", "estimator", "embed", "predict"] + + +def __getattr__(name): + if name in __all__: + module = import_module(f"{__name__}.{name}") + globals()[name] = module + return module + raise AttributeError(f"module '{__name__}' has no attribute '{name}'") diff --git a/kalelinear/embed.py b/kalelinear/embed.py new file mode 100644 index 0000000..a9f59b3 --- /dev/null +++ b/kalelinear/embed.py @@ -0,0 +1,5 @@ +"""Embedding models exposed with a PyKale-style API.""" + +from kalelinear.transformer import BDA, JDA, MIDA, MPCA, TCA + +__all__ = ["TCA", "JDA", "BDA", "MIDA", "MPCA"] diff --git a/kalelinear/predict.py b/kalelinear/predict.py new file mode 100644 index 0000000..316142a --- /dev/null +++ b/kalelinear/predict.py @@ -0,0 +1,5 @@ +"""Predictive models exposed with a PyKale-style API.""" + +from kalelinear.estimator import ARRLS, ARSVM, CoIRLS, CoIRSVM, GSDA, LapRLS, LapSVM + +__all__ = ["ARSVM", "ARRLS", "CoIRSVM", "CoIRLS", "GSDA", "LapSVM", "LapRLS"] diff --git a/setup.py b/setup.py index ea3008b..1c42d1b 100644 --- a/setup.py +++ b/setup.py @@ -70,12 +70,14 @@ def read(*names, **kwargs): name="kalelinear", version="0.1.0a1", description="Non-deep knowledge-aware machine learning from multiple sources/views in Python", - url="https://github.com/pykale/kale-linear", + long_description=read("README.md"), + long_description_content_type="text/markdown", + url="https://github.com/pykale/linear", author="The PyKale team", author_email="pykale-group@sheffield.ac.uk", project_urls={ - "Bug Tracker": "https://github.com/pykale/kale-linear/issues", - "Source": "https://github.com/pykale/kale-linear", + "Bug Tracker": "https://github.com/pykale/linear/issues", + "Source": "https://github.com/pykale/linear", }, license="MIT License", packages=find_packages(exclude=("tests*", "examples*", "docs*")), diff --git a/tests/__init__.py b/tests/__init__.py index a8e8108..b3ab380 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -1 +1 @@ -"""Test package for kale-linear.""" +"""Test package for kalelinear.""" diff --git a/tests/test_public_api.py b/tests/test_public_api.py new file mode 100644 index 0000000..b431f3b --- /dev/null +++ b/tests/test_public_api.py @@ -0,0 +1,48 @@ +import sys + +import kalelinear +from kalelinear import embed, estimator, predict, transformer + + +def test_embed_module_exposes_transformers(): + assert embed.TCA is transformer.TCA + assert embed.JDA is transformer.JDA + assert embed.BDA is transformer.BDA + assert embed.MIDA is transformer.MIDA + assert embed.MPCA is transformer.MPCA + + +def test_predict_module_exposes_estimators(): + assert predict.ARSVM is estimator.ARSVM + assert predict.ARRLS is estimator.ARRLS + assert predict.CoIRSVM is estimator.CoIRSVM + assert predict.CoIRLS is estimator.CoIRLS + assert predict.GSDA is estimator.GSDA + assert predict.LapSVM is estimator.LapSVM + assert predict.LapRLS is estimator.LapRLS + + +def test_lazy_modules_are_cached_on_package(): + assert kalelinear.transformer is transformer + assert kalelinear.estimator is estimator + assert kalelinear.embed is embed + assert kalelinear.predict is predict + + +def test_lazy_attribute_load_triggers_getattr(): + # Remove cached entries to simulate a cold attribute access through __getattr__. + # A plain `from kalelinear import embed` bypasses __getattr__ via submodule + # fallback, so this is the only way to actually exercise the lazy-load path. + for name in ("embed", "predict"): + kalelinear.__dict__.pop(name, None) + sys.modules.pop(f"kalelinear.{name}", None) + + loaded_embed = kalelinear.embed + assert loaded_embed is sys.modules["kalelinear.embed"] + + loaded_predict = kalelinear.predict + assert loaded_predict is sys.modules["kalelinear.predict"] + + # Second access must return the same cached object without re-importing. + assert kalelinear.embed is loaded_embed + assert kalelinear.predict is loaded_predict