From 359360bcebc4c0d7b323a50bf5be1a45b883cfe2 Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Thu, 8 Jan 2026 19:34:45 -0800 Subject: [PATCH 01/14] update action to the newer version from biocsetup, use the Linux-x86 runner --- .github/workflows/pypi-test.yml | 94 ++++++++++++++++++++++----------- 1 file changed, 64 insertions(+), 30 deletions(-) diff --git a/.github/workflows/pypi-test.yml b/.github/workflows/pypi-test.yml index f74ec8b0..fe6b241c 100644 --- a/.github/workflows/pypi-test.yml +++ b/.github/workflows/pypi-test.yml @@ -1,40 +1,74 @@ -# This workflow will install Python dependencies, run tests and lint with a single version of Python -# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions - name: Test the library on: push: - branches: [ main ] + branches: + - master # for legacy repos + - main pull_request: - branches: [ main ] + branches: + - master # for legacy repos + - main + workflow_dispatch: # Allow manually triggering the workflow + schedule: + # Run roughly every 15 days at 00:00 UTC + # (useful to check if updates on dependencies break the package) + - cron: "0 0 1,16 * *" -jobs: - build: +permissions: + contents: read + +concurrency: + group: >- + ${{ github.workflow }}-${{ github.ref_type }}- + ${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: true - runs-on: ubuntu-latest +jobs: + test: strategy: matrix: - python-version: [ '3.10' ] - - name: Python ${{ matrix.python-version }} + python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + platform: + - Linux-x86 + # - ubuntu-latest + # - macos-latest + # - windows-latest + runs-on: ${{ matrix.platform }} + name: Python ${{ matrix.python }}, ${{ matrix.platform }} steps: - - uses: actions/checkout@v3 - - name: Setup Python - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - cache: 'pip' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install flake8 pytest tox - # - name: Lint with flake8 - # run: | - # # stop the build if there are Python syntax errors or undefined names - # flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics - # # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide - # # flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - - name: Test with tox - run: | - tox + - uses: actions/checkout@v4 + + - uses: actions/setup-python@v5 + id: setup-python + with: + python-version: ${{ matrix.python }} + + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install tox coverage + + - name: Run tests + run: >- + pipx run --python '${{ steps.setup-python.outputs.python-path }}' + tox + -- -rFEx --durations 10 --color yes --cov --cov-branch --cov-report=xml # pytest args + + - name: Check for codecov token availability + id: codecov-check + shell: bash + run: | + if [ ${{ secrets.CODECOV_TOKEN }} != '' ]; then + echo "codecov=true" >> $GITHUB_OUTPUT; + else + echo "codecov=false" >> $GITHUB_OUTPUT; + fi + + - name: Upload coverage reports to Codecov with GitHub Action + uses: codecov/codecov-action@v5 + if: ${{ steps.codecov-check.outputs.codecov == 'true' }} + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} + slug: ${{ github.repository }} + flags: ${{ matrix.platform }} - py${{ matrix.python }} From 79f73d667be6332a4a5711d03ef9ba7565c1d0b3 Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Thu, 8 Jan 2026 19:36:13 -0800 Subject: [PATCH 02/14] only run tests on 3.10, same as before --- .github/workflows/pypi-test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pypi-test.yml b/.github/workflows/pypi-test.yml index fe6b241c..b87a3a76 100644 --- a/.github/workflows/pypi-test.yml +++ b/.github/workflows/pypi-test.yml @@ -28,7 +28,7 @@ jobs: test: strategy: matrix: - python: ["3.10", "3.11", "3.12", "3.13", "3.14"] + python: ["3.10"] # , "3.11", "3.12", "3.13", "3.14" platform: - Linux-x86 # - ubuntu-latest From 3cbd5e9a9aeb670a38ac2ea437298296943e1bfd Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Thu, 8 Jan 2026 20:09:01 -0800 Subject: [PATCH 03/14] update runner tag --- .github/workflows/pypi-publish.yml | 2 +- .github/workflows/pypi-test.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pypi-publish.yml b/.github/workflows/pypi-publish.yml index 544f751b..c6a1d1d7 100644 --- a/.github/workflows/pypi-publish.yml +++ b/.github/workflows/pypi-publish.yml @@ -11,7 +11,7 @@ on: jobs: docs: name: Push Sphinx Pages - runs-on: ubuntu-latest + runs-on: Linux-x64 steps: - uses: actions/checkout@v3 - uses: actions/setup-python@v3 diff --git a/.github/workflows/pypi-test.yml b/.github/workflows/pypi-test.yml index b87a3a76..14be0dae 100644 --- a/.github/workflows/pypi-test.yml +++ b/.github/workflows/pypi-test.yml @@ -30,7 +30,7 @@ jobs: matrix: python: ["3.10"] # , "3.11", "3.12", "3.13", "3.14" platform: - - Linux-x86 + - Linux-x64 # - ubuntu-latest # - macos-latest # - windows-latest From 4feb6cdc7093fcb82b49352a995ad4eddb1b5e9b Mon Sep 17 00:00:00 2001 From: Jayaram Kancherla Date: Thu, 8 Jan 2026 20:34:04 -0800 Subject: [PATCH 04/14] disable wandb logging for tests --- tox.ini | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tox.ini b/tox.ini index e3d04797..032f2c04 100644 --- a/tox.ini +++ b/tox.ini @@ -13,6 +13,8 @@ description = Invoke pytest to run automated tests setenv = TOXINIDIR = {toxinidir} CONDA_EXE = mamba + WANDB_MODE = disabled + WANDB_API_KEY = dummy passenv = HOME SETUPTOOLS_* From 3e013b69fa5f3722c34f2c384efe788c51567f67 Mon Sep 17 00:00:00 2001 From: avantikalal Date: Mon, 12 Jan 2026 22:57:32 +0000 Subject: [PATCH 05/14] updated tangermeme version --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index f5d65835..40b7c89a 100644 --- a/setup.cfg +++ b/setup.cfg @@ -70,7 +70,7 @@ install_requires = logomaker >= 0.8 pyBigWig ledidi - tangermeme >= 0.5.0 + tangermeme >= 1.0 memelite pygenomeviz <= 0.4.4 statsmodels >=0.11.1 From 25404fbaa7dfdb9215a78d4be853b5db74792eae Mon Sep 17 00:00:00 2001 From: avantikalal Date: Mon, 12 Jan 2026 23:55:16 +0000 Subject: [PATCH 06/14] add debug in test --- tests/test_interpret.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/test_interpret.py b/tests/test_interpret.py index 485caa27..c1ba6393 100644 --- a/tests/test_interpret.py +++ b/tests/test_interpret.py @@ -24,6 +24,33 @@ meme_file = os.path.join(cwd, "files", "test.meme") +def test_debug(): + arr = np.array([[[0., 0., 1., 0., 0., 0., 0., 1., 1.], + [0., 1., 0., 1., 0., 0., 0., 0., 0.], + [0., 0., 0., 0., 1., 0., 1., 0., 0.], + [1., 0., 0., 0., 0., 1., 0., 0., 0.]]], dtype=np.float32) + + import memelite + print(memelite.__version__) + + from memelite import fimo + print(help(fimo)) + + from grelu.io.motifs import read_meme_file + motifs = read_meme_file(meme_file, names=None) + pval = fimo( + motifs={k: Tensor(v) for k, v in motifs.items()}, + sequences=arr, + alphabet=["A", "C", "G", "T"], + bin_size=0.1, + eps=0.0001, + threshold=1e-3, + reverse_complement=False, + dim=1, + )[0]['p-value'].iloc[0] + assert np.equal(pval, 0.000244140625) + + def test_motifs_to_strings(motifs=meme_file): assert motifs_to_strings(motifs, sample=False) == ["CACGTG", "TGCGTG"] assert motifs_to_strings(motifs, names=["MA0004.1 Arnt"], sample=False) == [ From 1dc4736759aa18dec9bb1d7f11adcfa706fe361f Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 12 Jan 2026 23:55:36 +0000 Subject: [PATCH 07/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_interpret.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_interpret.py b/tests/test_interpret.py index c1ba6393..a9e60ec7 100644 --- a/tests/test_interpret.py +++ b/tests/test_interpret.py @@ -32,7 +32,7 @@ def test_debug(): import memelite print(memelite.__version__) - + from memelite import fimo print(help(fimo)) From 81d51ef651d6f03236e10d5abe08c0cc758f9cf1 Mon Sep 17 00:00:00 2001 From: avantikalal Date: Mon, 12 Jan 2026 23:59:22 +0000 Subject: [PATCH 08/14] add debug in test --- tests/test_interpret.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_interpret.py b/tests/test_interpret.py index c1ba6393..41eaf3bd 100644 --- a/tests/test_interpret.py +++ b/tests/test_interpret.py @@ -48,7 +48,8 @@ def test_debug(): reverse_complement=False, dim=1, )[0]['p-value'].iloc[0] - assert np.equal(pval, 0.000244140625) + assert np.equal(res['score'], 11.60498046875) + assert np.equal(res['p-value'], 0.000244140625) def test_motifs_to_strings(motifs=meme_file): From e66cabab205f4027b079c85a96b6ad383dfb85ca Mon Sep 17 00:00:00 2001 From: avantikalal Date: Tue, 13 Jan 2026 00:10:33 +0000 Subject: [PATCH 09/14] add debug in test --- tests/test_interpret.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_interpret.py b/tests/test_interpret.py index 67b0c223..5b5cfc71 100644 --- a/tests/test_interpret.py +++ b/tests/test_interpret.py @@ -32,13 +32,13 @@ def test_debug(): import memelite print(memelite.__version__) - + from memelite import fimo print(help(fimo)) from grelu.io.motifs import read_meme_file motifs = read_meme_file(meme_file, names=None) - pval = fimo( + res = fimo( motifs={k: Tensor(v) for k, v in motifs.items()}, sequences=arr, alphabet=["A", "C", "G", "T"], @@ -47,7 +47,7 @@ def test_debug(): threshold=1e-3, reverse_complement=False, dim=1, - )[0]['p-value'].iloc[0] + )[0].iloc[0] assert np.equal(res['score'], 11.60498046875) assert np.equal(res['p-value'], 0.000244140625) From ae691f235f6f6267213c60473bef45fbd93228c7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 00:11:38 +0000 Subject: [PATCH 10/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_interpret.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_interpret.py b/tests/test_interpret.py index 5b5cfc71..57fa57b2 100644 --- a/tests/test_interpret.py +++ b/tests/test_interpret.py @@ -32,7 +32,7 @@ def test_debug(): import memelite print(memelite.__version__) - + from memelite import fimo print(help(fimo)) From 245deb723441a20c0477c82bcbab98ab0e206395 Mon Sep 17 00:00:00 2001 From: avantikalal Date: Tue, 13 Jan 2026 04:56:17 +0000 Subject: [PATCH 11/14] lower level debug test --- tests/test_interpret.py | 65 ++++++++++++++++++++++++++--------------- 1 file changed, 42 insertions(+), 23 deletions(-) diff --git a/tests/test_interpret.py b/tests/test_interpret.py index 5b5cfc71..03e47c60 100644 --- a/tests/test_interpret.py +++ b/tests/test_interpret.py @@ -25,31 +25,50 @@ def test_debug(): - arr = np.array([[[0., 0., 1., 0., 0., 0., 0., 1., 1.], - [0., 1., 0., 1., 0., 0., 0., 0., 0.], - [0., 0., 0., 0., 1., 0., 1., 0., 0.], - [1., 0., 0., 0., 0., 1., 0., 0., 0.]]], dtype=np.float32) + import numpy as np + import math + from memelite.fimo import _all_pwm_to_mapping + alphabet=['A', 'C', 'G', 'T'] + bin_size=0.1 + eps=0.0001 + threshold=1e-3 + + log_threshold = math.log2(threshold) + pwm = np.array([[0.2 , 0.95, 0. , 0. , 0. , 0. ], + [0.8 , 0. , 1. , 0. , 0. , 0. ], + [0. , 0.05, 0. , 1. , 0. , 1. ], + [0. , 0. , 0. , 0. , 1. , 0. ]]) + pwm = np.log2(pwm + eps) - math.log2(0.25) + + smallest, score_to_pvals = _all_pwm_to_mapping(pwm, [0, 6], bin_size) + _score_to_pvals_lengths = [0] + [len(score_to_pvals[0])] + idx = np.where(score_to_pvals[0] < log_threshold)[0] + score_to_pvals = np.concatenate(score_to_pvals) + + score_threshold = np.empty(1, dtype=np.float32) + score_threshold[0] = (idx[0] + smallest[0]) * bin_size + + X = np.array([3, 1, 0, 1, 2, 3, 2, 0, 0], dtype=np.int8) + score = 0.0 + for j in range(6): + idx = X[1+j] + score += pwm[np.uint64(idx), np.uint64(j)] + s = score + + score_idx = int(score / bin_size) - smallest[0] + s_idx = score_idx + + pval_ = score_to_pvals[score_idx] + p = pval_ + + pval = 2.0 ** score_to_pvals[score_idx] + p2 = pval - import memelite - print(memelite.__version__) + assert s == 11.604980553086907 + assert s_idx == 794 + assert p == -12.0 + assert p2 == 0.000244140625 - from memelite import fimo - print(help(fimo)) - - from grelu.io.motifs import read_meme_file - motifs = read_meme_file(meme_file, names=None) - res = fimo( - motifs={k: Tensor(v) for k, v in motifs.items()}, - sequences=arr, - alphabet=["A", "C", "G", "T"], - bin_size=0.1, - eps=0.0001, - threshold=1e-3, - reverse_complement=False, - dim=1, - )[0].iloc[0] - assert np.equal(res['score'], 11.60498046875) - assert np.equal(res['p-value'], 0.000244140625) def test_motifs_to_strings(motifs=meme_file): From 89d98a68683ca727571ed4926f5bad597154850a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 04:58:02 +0000 Subject: [PATCH 12/14] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- tests/test_interpret.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/test_interpret.py b/tests/test_interpret.py index 5c1c0153..1f0c02e0 100644 --- a/tests/test_interpret.py +++ b/tests/test_interpret.py @@ -31,43 +31,43 @@ def test_debug(): bin_size=0.1 eps=0.0001 threshold=1e-3 - + log_threshold = math.log2(threshold) pwm = np.array([[0.2 , 0.95, 0. , 0. , 0. , 0. ], [0.8 , 0. , 1. , 0. , 0. , 0. ], [0. , 0.05, 0. , 1. , 0. , 1. ], [0. , 0. , 0. , 0. , 1. , 0. ]]) pwm = np.log2(pwm + eps) - math.log2(0.25) - + smallest, score_to_pvals = _all_pwm_to_mapping(pwm, [0, 6], bin_size) _score_to_pvals_lengths = [0] + [len(score_to_pvals[0])] idx = np.where(score_to_pvals[0] < log_threshold)[0] score_to_pvals = np.concatenate(score_to_pvals) - + score_threshold = np.empty(1, dtype=np.float32) score_threshold[0] = (idx[0] + smallest[0]) * bin_size - + X = np.array([3, 1, 0, 1, 2, 3, 2, 0, 0], dtype=np.int8) score = 0.0 for j in range(6): idx = X[1+j] score += pwm[np.uint64(idx), np.uint64(j)] s = score - - score_idx = int(score / bin_size) - smallest[0] + + score_idx = int(score / bin_size) - smallest[0] s_idx = score_idx - + pval_ = score_to_pvals[score_idx] p = pval_ - + pval = 2.0 ** score_to_pvals[score_idx] p2 = pval assert s == 11.604980553086907 - assert s_idx == 794 + assert s_idx == 794 assert p == -12.0 assert p2 == 0.000244140625 - + def test_motifs_to_strings(motifs=meme_file): From 541a71a520a8a503765b9f5b01327be948d27c74 Mon Sep 17 00:00:00 2001 From: avantikalal Date: Tue, 13 Jan 2026 05:17:04 +0000 Subject: [PATCH 13/14] add atol --- setup.cfg | 2 +- tests/test_interpret.py | 48 +---------------------------------------- 2 files changed, 2 insertions(+), 48 deletions(-) diff --git a/setup.cfg b/setup.cfg index 40b7c89a..f5d65835 100644 --- a/setup.cfg +++ b/setup.cfg @@ -70,7 +70,7 @@ install_requires = logomaker >= 0.8 pyBigWig ledidi - tangermeme >= 1.0 + tangermeme >= 0.5.0 memelite pygenomeviz <= 0.4.4 statsmodels >=0.11.1 diff --git a/tests/test_interpret.py b/tests/test_interpret.py index 5c1c0153..4ca8fc21 100644 --- a/tests/test_interpret.py +++ b/tests/test_interpret.py @@ -24,52 +24,6 @@ meme_file = os.path.join(cwd, "files", "test.meme") -def test_debug(): - import math - from memelite.fimo import _all_pwm_to_mapping - alphabet=['A', 'C', 'G', 'T'] - bin_size=0.1 - eps=0.0001 - threshold=1e-3 - - log_threshold = math.log2(threshold) - pwm = np.array([[0.2 , 0.95, 0. , 0. , 0. , 0. ], - [0.8 , 0. , 1. , 0. , 0. , 0. ], - [0. , 0.05, 0. , 1. , 0. , 1. ], - [0. , 0. , 0. , 0. , 1. , 0. ]]) - pwm = np.log2(pwm + eps) - math.log2(0.25) - - smallest, score_to_pvals = _all_pwm_to_mapping(pwm, [0, 6], bin_size) - _score_to_pvals_lengths = [0] + [len(score_to_pvals[0])] - idx = np.where(score_to_pvals[0] < log_threshold)[0] - score_to_pvals = np.concatenate(score_to_pvals) - - score_threshold = np.empty(1, dtype=np.float32) - score_threshold[0] = (idx[0] + smallest[0]) * bin_size - - X = np.array([3, 1, 0, 1, 2, 3, 2, 0, 0], dtype=np.int8) - score = 0.0 - for j in range(6): - idx = X[1+j] - score += pwm[np.uint64(idx), np.uint64(j)] - s = score - - score_idx = int(score / bin_size) - smallest[0] - s_idx = score_idx - - pval_ = score_to_pvals[score_idx] - p = pval_ - - pval = 2.0 ** score_to_pvals[score_idx] - p2 = pval - - assert s == 11.604980553086907 - assert s_idx == 794 - assert p == -12.0 - assert p2 == 0.000244140625 - - - def test_motifs_to_strings(motifs=meme_file): assert motifs_to_strings(motifs, sample=False) == ["CACGTG", "TGCGTG"] assert motifs_to_strings(motifs, names=["MA0004.1 Arnt"], sample=False) == [ @@ -277,7 +231,7 @@ def test_scan_sequences(): 'fimo_p-value': [0.000244140625, 0.000244140625], 'matched_seq': ['CACGTG', 'TGCGTG'] }) - assert out.equals(expected) + pd.testing.assert_frame_equal(out, expected, atol=1e-3) # Allow reverse complement out = scan_sequences(seqs, motifs=meme_file, rc=True, pthresh=1e-3) From 8a8106a621ede7d8b25726d2f03f2de4c3f11f84 Mon Sep 17 00:00:00 2001 From: avantikalal Date: Tue, 13 Jan 2026 05:38:51 +0000 Subject: [PATCH 14/14] use atol for all tests --- tests/test_interpret.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_interpret.py b/tests/test_interpret.py index 4ca8fc21..70fe0d6c 100644 --- a/tests/test_interpret.py +++ b/tests/test_interpret.py @@ -248,7 +248,7 @@ def test_scan_sequences(): 'matched_seq': ['CACGTG', 'CACGTG', 'CACGCA', 'TGCGTG'] }) - assert out.equals(expected) + pd.testing.assert_frame_equal(out, expected, atol=1e-3) # Reverse complement with attributions attrs = get_attributions(model, seqs, method="inputxgradient") @@ -266,7 +266,7 @@ def test_scan_sequences(): 'site_attr_score': np.float32([0.0, 0.0, 0.009259258396923542, -0.009259259328246117]), 'motif_attr_score': [0.003703703731298441, 0.0, 0.0, -0.03549381507926434] }) - assert out.equals(expected) + pd.testing.assert_frame_equal(out, expected, atol=1e-3) def test_compare_motifs(): @@ -288,7 +288,7 @@ def test_compare_motifs(): 'fimo_score_ref': [11.60498046875, -2.9944558143615723], 'fimo_score_diff': [-26.253820657730103, 13.22646164894104] }) - assert out.equals(expected) + pd.testing.assert_frame_equal(out, expected, atol=1e-3) def test_run_tomtom():