From a1bec3d78fa5ab12740025d7c155e55a2645c9e1 Mon Sep 17 00:00:00 2001 From: ikeuchi-screen Date: Sun, 7 Dec 2025 05:43:47 +0000 Subject: [PATCH 01/10] Update --- .github/workflows/tests.yml | 53 ++++++++++++++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d2c2aa0..12686b7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,6 +1,8 @@ name: Tests -on: [push] +on: + push: + pull_request: jobs: build: @@ -11,22 +13,67 @@ jobs: python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + cache: 'pip' + - name: Install dependencies run: | python -m pip install --upgrade pip pip install flake8 pytest pytest-cov if [ -f requirements.txt ]; then pip install -r requirements.txt; fi + - name: Lint with flake8 run: | # stop the build if there are Python syntax errors or undefined names flake8 lingam --count --select=E9,F63,F7,F82 --show-source --statistics # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide flake8 lingam --count --exit-zero --ignore=E203,E501,E741,C901 --max-line-length=127 --statistics + - name: Test with pytest run: | pytest -v --cov=lingam --cov-report=term-missing + + build-and-install: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.9", "3.10", "3.11", "3.12"] + + steps: + - uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: 'pip' + + - name: Build sdist & wheel via setup.py + run: | + python -m pip install --upgrade pip setuptools wheel + python setup.py sdist bdist_wheel + ls -la dist/ + echo "List sdist contents:" + tar -tf dist/*.tar.gz | head -n 50 + + - name: Test install from wheel + run: | + python -m venv venv_w + source venv_w/bin/activate + pip install --no-cache-dir dist/*.whl + python -c "import lingam; print(lingam.__version__)" + pip check + + - name: Test install from sdist + run: | + python -m venv venv_s + source venv_s/bin/activate + pip install --no-cache-dir dist/*.tar.gz + python -c "import lingam; print(lingam.__version__)" + pip check From 7ad0cde6b7bae19eb14d8c40af3edafcab5cc48a Mon Sep 17 00:00:00 2001 From: ikeuchi-screen Date: Sun, 7 Dec 2025 05:55:58 +0000 Subject: [PATCH 02/10] Update --- .github/workflows/tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 12686b7..415a777 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -56,7 +56,10 @@ jobs: - name: Build sdist & wheel via setup.py run: | + python -m venv venv_b + source venv_b/bin/activate python -m pip install --upgrade pip setuptools wheel + if [ -f requirements.txt ]; then pip install -r requirements.txt; fi python setup.py sdist bdist_wheel ls -la dist/ echo "List sdist contents:" From 2faa775eb2f0f40ecba4a8971185fc9a76536a82 Mon Sep 17 00:00:00 2001 From: ikeuchi-screen Date: Sun, 7 Dec 2025 06:58:11 +0000 Subject: [PATCH 03/10] Update --- .github/workflows/tests.yml | 7 +++--- pyproject.toml | 49 +++++++++++++++++++++++++++++++++++++ setup.py | 39 ----------------------------- 3 files changed, 52 insertions(+), 43 deletions(-) create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 415a777..b954f22 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -54,13 +54,12 @@ jobs: python-version: ${{ matrix.python-version }} cache: 'pip' - - name: Build sdist & wheel via setup.py + - name: Build sdist & wheel via pyproject.toml run: | python -m venv venv_b source venv_b/bin/activate - python -m pip install --upgrade pip setuptools wheel - if [ -f requirements.txt ]; then pip install -r requirements.txt; fi - python setup.py sdist bdist_wheel + python -m pip install --upgrade pip setuptools wheel build + python -m build ls -la dist/ echo "List sdist contents:" tar -tf dist/*.tar.gz | head -n 50 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..8baaf16 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,49 @@ +[build-system] +requires = ["setuptools>=61", "wheel"] +build-backend = "setuptools.build_meta" + +[project] +name = "lingam" +description = "LiNGAM Python Package" +readme = "README.md" +authors = [ + { name = "Takashi Ikeuchi" }, + { name = "Genya Haraoka" }, + { name = "Mayumi Ide" }, + { name = "Kouhei Nishikawa" }, + { name = "Yan Zeng" }, + { name = "Takashi Nicholas Maeda" }, + { name = "Wataru Kurebayashi" }, + { name = "Shohei Shimizu" } +] +license = { file = "LICENSE" } +requires-python = ">=3.9" +dependencies = [ + "numpy", + "scipy", + "scikit-learn", + "graphviz", + "statsmodels", + "networkx", + "pandas", + "pygam", + "matplotlib", + "psy", + "semopy", + "autograd" +] +classifiers = [ + "Programming Language :: Python :: 3", + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent" +] +dynamic = ["version"] + +[project.urls] +Homepage = "https://github.com/cdt15/lingam" + +[tool.setuptools.packages.find] +exclude = ["tests"] + +[tool.setuptools.dynamic] +version = { attr = "lingam.__version__" } diff --git a/setup.py b/setup.py deleted file mode 100644 index e3b2f0b..0000000 --- a/setup.py +++ /dev/null @@ -1,39 +0,0 @@ -import setuptools - -with open('README.md', 'r', encoding='utf-8') as fh: - README = fh.read() - -import lingam - -VERSION = lingam.__version__ - -setuptools.setup( - name='lingam', - version=VERSION, - author='T.Ikeuchi, G.Haraoka, M.Ide, Y.Zeng, T.N.Maeda, W.Kurebayashi, S.Shimizu', - description='LiNGAM Python Package', - long_description=README, - long_description_content_type='text/markdown', - install_requires=[ - 'numpy', - 'scipy', - 'scikit-learn', - 'graphviz', - 'statsmodels', - 'networkx', - 'pandas', - 'pygam', - 'matplotlib', - 'psy', - 'semopy', - 'autograd', - ], - url='https://github.com/cdt15/lingam', - packages=setuptools.find_packages(exclude=['tests']), - classifiers=[ - 'Programming Language :: Python :: 3', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - ], - python_requires='>=3.9', -) From c82683834e93d607e23a67e3c86ede7b05df0c88 Mon Sep 17 00:00:00 2001 From: ikeuchi-screen Date: Sun, 7 Dec 2025 06:59:47 +0000 Subject: [PATCH 04/10] Update --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8baaf16..ac1ad15 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "lingam" -description = "LiNGAM Python Package" +description = "Python package for causal discovery based on LiNGAM." readme = "README.md" authors = [ { name = "Takashi Ikeuchi" }, From e45b74c57c7e95887f7cd67cc17070e912c757c1 Mon Sep 17 00:00:00 2001 From: ikeuchi-screen Date: Sun, 7 Dec 2025 07:07:59 +0000 Subject: [PATCH 05/10] Update --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b954f22..0d73a9d 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] steps: - uses: actions/checkout@v4 From 8b246d57ba90d1d5094c0cea6a2edc82316ce2e8 Mon Sep 17 00:00:00 2001 From: ikeuchi-screen Date: Sun, 7 Dec 2025 07:13:34 +0000 Subject: [PATCH 06/10] Update --- .github/workflows/tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0d73a9d..b954f22 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 @@ -43,7 +43,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.9", "3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 From a8a488211742b77da63c71a85c5f2c3b10cc99e6 Mon Sep 17 00:00:00 2001 From: ikeuchi-screen Date: Mon, 22 Dec 2025 03:55:05 +0000 Subject: [PATCH 07/10] Added prior knowledge support to DirectLiNGAM-GPU --- lingam/direct_lingam.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/lingam/direct_lingam.py b/lingam/direct_lingam.py index 7987bcb..ab72d83 100644 --- a/lingam/direct_lingam.py +++ b/lingam/direct_lingam.py @@ -252,13 +252,20 @@ def _search_causal_order_gpu(self, X, U): Returns the instance itself. mlist: causal ordering """ + Uc, _ = self._search_candidate(U) + if len(Uc) == 1: + return Uc[0] + cols = len(U) rows = len(X) arr = X[:, np.array(U)] from lingam_cuda import causal_order as causal_order_gpu mlist = causal_order_gpu(arr, rows, cols) - return U[np.argmax(mlist)] + M_list = np.asarray(mlist) + M_list = M_list[np.isin(U, Uc)] + + return Uc[int(np.argmax(M_list))] def _mutual_information(self, x1, x2, param): """Calculate the mutual informations.""" From fa5acbb9417c2ef47962b8ec90f42eb3bd1c5c04 Mon Sep 17 00:00:00 2001 From: ikeuchi-screen Date: Mon, 22 Dec 2025 06:35:16 +0000 Subject: [PATCH 08/10] Adapted to changes in scikit-learn v1.8 --- lingam/causal_based_simulator.py | 2 +- lingam/tools/__init__.py | 2 +- lingam/utils/__init__.py | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lingam/causal_based_simulator.py b/lingam/causal_based_simulator.py index eb0bb68..ebd0274 100644 --- a/lingam/causal_based_simulator.py +++ b/lingam/causal_based_simulator.py @@ -668,7 +668,7 @@ class CBSIUnobsCommonCauseLiNGAM(CBSILiNGAM): def _check_causal_graph(self, causal_graph, X): try: # fill nan with zeros - causal_graph = check_array(causal_graph, force_all_finite="allow-nan", copy=True) + causal_graph = check_array(causal_graph, ensure_all_finite="allow-nan", copy=True) n_features = X.shape[1] if causal_graph.shape != (n_features, n_features): diff --git a/lingam/tools/__init__.py b/lingam/tools/__init__.py index 0a0f825..7284e93 100644 --- a/lingam/tools/__init__.py +++ b/lingam/tools/__init__.py @@ -84,7 +84,7 @@ def bootstrap_with_imputation( Elements which are not NaN are the imputation values. """ # check args - X = check_array(X, force_all_finite="allow-nan") + X = check_array(X, ensure_all_finite="allow-nan") n_sampling = check_scalar(n_sampling, "n_sampling", (numbers.Integral, np.integer), min_val=1) diff --git a/lingam/utils/__init__.py b/lingam/utils/__init__.py index 6020810..80b3d10 100644 --- a/lingam/utils/__init__.py +++ b/lingam/utils/__init__.py @@ -957,7 +957,7 @@ def evaluate_model_fit(adjacency_matrix, X, is_ordinal=None): """ # check inputs - adj = check_array(adjacency_matrix, force_all_finite="allow-nan") + adj = check_array(adjacency_matrix, ensure_all_finite="allow-nan") if adj.ndim != 2 or (adj.shape[0] != adj.shape[1]): raise ValueError("adj must be an square matrix.") @@ -1037,7 +1037,7 @@ def calculate_distance_from_root_nodes(adjacency_matrix, max_distance=None): adjacency_matrix, ensure_min_samples=2, ensure_min_features=2, - force_all_finite="allow-nan", + ensure_all_finite="allow-nan", ) if adjacency_matrix.shape[0] != adjacency_matrix.shape[1]: raise ValueError("adjacency_matrix must be an square matrix.") @@ -1103,7 +1103,7 @@ def calculate_total_effect(adjacency_matrix, from_index, to_index, is_continuous adjacency_matrix, ensure_min_samples=2, ensure_min_features=2, - force_all_finite="allow-nan", + ensure_all_finite="allow-nan", ) if adjacency_matrix.shape[0] != adjacency_matrix.shape[1]: raise ValueError( From 17605df4469e57a4b91d7d1adf3bfa22d4429243 Mon Sep 17 00:00:00 2001 From: ikeuchi-screen Date: Wed, 28 Jan 2026 05:11:42 +0000 Subject: [PATCH 09/10] Modify to pass CI --- tests/test_bottom_up_parce_lingam.py | 2 +- tests/test_direct_lingam.py | 2 +- tests/test_high_dim_direct_lingam.py | 2 +- tests/test_rcd.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_bottom_up_parce_lingam.py b/tests/test_bottom_up_parce_lingam.py index 4a4a995..2db1f21 100644 --- a/tests/test_bottom_up_parce_lingam.py +++ b/tests/test_bottom_up_parce_lingam.py @@ -108,7 +108,7 @@ def test_fit_invalid_data(): try: model = BottomUpParceLiNGAM() model.fit(X) - except ValueError: + except TypeError: pass else: raise AssertionError diff --git a/tests/test_direct_lingam.py b/tests/test_direct_lingam.py index df5f2ae..8cb19bb 100644 --- a/tests/test_direct_lingam.py +++ b/tests/test_direct_lingam.py @@ -62,7 +62,7 @@ def test_fit_invalid_data(): try: model = DirectLiNGAM() model.fit(X) - except ValueError: + except TypeError: pass else: raise AssertionError diff --git a/tests/test_high_dim_direct_lingam.py b/tests/test_high_dim_direct_lingam.py index 8177308..db41c53 100644 --- a/tests/test_high_dim_direct_lingam.py +++ b/tests/test_high_dim_direct_lingam.py @@ -79,7 +79,7 @@ def test_fit_invalid_data(): try: model = HighDimDirectLiNGAM() model.fit(X) - except ValueError: + except TypeError: pass else: raise AssertionError diff --git a/tests/test_rcd.py b/tests/test_rcd.py index aca3423..389a2f3 100644 --- a/tests/test_rcd.py +++ b/tests/test_rcd.py @@ -113,7 +113,7 @@ def test_fit_invalid_data(): try: model = RCD() model.fit(X) - except ValueError: + except TypeError: pass else: raise AssertionError From 47d095b1cfccc4dd3d298edaa583d9a92ab14946 Mon Sep 17 00:00:00 2001 From: ikeuchi-screen Date: Wed, 28 Jan 2026 06:25:39 +0000 Subject: [PATCH 10/10] Modify to pass CI --- tests/test_bottom_up_parce_lingam.py | 7 +++---- tests/test_direct_lingam.py | 7 +++---- tests/test_high_dim_direct_lingam.py | 7 +++---- tests/test_rcd.py | 7 +++---- 4 files changed, 12 insertions(+), 16 deletions(-) diff --git a/tests/test_bottom_up_parce_lingam.py b/tests/test_bottom_up_parce_lingam.py index 2db1f21..dc1861d 100644 --- a/tests/test_bottom_up_parce_lingam.py +++ b/tests/test_bottom_up_parce_lingam.py @@ -102,13 +102,12 @@ def test_fit_invalid_data(): raise AssertionError # Include non-numeric data - x0 = np.random.uniform(size=5) - x1 = np.array(['X', 'Y', 'X', 'Y', 'X']) - X = pd.DataFrame(np.array([x0, x1]).T, columns=['x0', 'x1']) + X = np.array([[1.2, 'X'], + [3.4, 'Y']], dtype=object) try: model = BottomUpParceLiNGAM() model.fit(X) - except TypeError: + except ValueError: pass else: raise AssertionError diff --git a/tests/test_direct_lingam.py b/tests/test_direct_lingam.py index 8cb19bb..f8555e4 100644 --- a/tests/test_direct_lingam.py +++ b/tests/test_direct_lingam.py @@ -56,13 +56,12 @@ def test_fit_invalid_data(): raise AssertionError # Include non-numeric data - x0 = np.random.uniform(size=5) - x1 = np.array(['X', 'Y', 'X', 'Y', 'X']) - X = pd.DataFrame(np.array([x0, x1]).T, columns=['x0', 'x1']) + X = np.array([[1.2, 'X'], + [3.4, 'Y']], dtype=object) try: model = DirectLiNGAM() model.fit(X) - except TypeError: + except ValueError: pass else: raise AssertionError diff --git a/tests/test_high_dim_direct_lingam.py b/tests/test_high_dim_direct_lingam.py index db41c53..5313b5b 100644 --- a/tests/test_high_dim_direct_lingam.py +++ b/tests/test_high_dim_direct_lingam.py @@ -73,13 +73,12 @@ def test_fit_invalid_data(): raise AssertionError # Include non-numeric data - x0 = np.random.uniform(size=5) - x1 = np.array(['X', 'Y', 'X', 'Y', 'X']) - X = pd.DataFrame(np.array([x0, x1]).T, columns=['x0', 'x1']) + X = np.array([[1.2, 'X'], + [3.4, 'Y']], dtype=object) try: model = HighDimDirectLiNGAM() model.fit(X) - except TypeError: + except ValueError: pass else: raise AssertionError diff --git a/tests/test_rcd.py b/tests/test_rcd.py index 389a2f3..c123bf3 100644 --- a/tests/test_rcd.py +++ b/tests/test_rcd.py @@ -107,13 +107,12 @@ def test_fit_invalid_data(): raise AssertionError # Include non-numeric data - x0 = np.random.uniform(size=5) - x1 = np.array(['X', 'Y', 'X', 'Y', 'X']) - X = pd.DataFrame(np.array([x0, x1]).T, columns=['x0', 'x1']) + X = np.array([[1.2, 'X'], + [3.4, 'Y']], dtype=object) try: model = RCD() model.fit(X) - except TypeError: + except ValueError: pass else: raise AssertionError