From a93fdf9161561a260456ed04c7a9df0561fccf77 Mon Sep 17 00:00:00 2001 From: mikessh Date: Wed, 4 Nov 2026 04:02:01 +0300 Subject: [PATCH 1/5] fix: bound native builds and align docs with the release branch Co-Authored-By: Codex --- .github/workflows/docs.yml | 13 +++++++------ CHANGELOG.md | 7 +++++++ README.md | 2 +- docs/requirements.txt | 2 -- pyproject.toml | 4 ++-- setup.sh | 19 ++++++++++--------- tests/python/test_setup.py | 4 ++++ 7 files changed, 31 insertions(+), 20 deletions(-) delete mode 100644 docs/requirements.txt diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 6126310..5223008 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -2,8 +2,10 @@ name: Docs on: push: - branches: [dev] # dev is the default branch; the github-pages environment - paths: [docs/**, python/**, src/**, "**/*.py", .github/workflows/docs.yml] + branches: [master, dev] # master is the default/release branch. + paths: [docs/**, python/**, src/**, "**/*.py", pyproject.toml, CMakeLists.txt, .github/workflows/docs.yml] + pull_request: + branches: [master, dev] workflow_dispatch: permissions: @@ -12,7 +14,7 @@ permissions: id-token: write concurrency: - group: pages + group: pages-${{ github.ref }} cancel-in-progress: true jobs: @@ -25,9 +27,7 @@ jobs: python-version: "3.12" - uses: astral-sh/setup-uv@v6 - name: Install package + docs deps - run: | - uv pip install --system -e . - uv pip install --system -r docs/requirements.txt + run: uv pip install --system -e ".[docs]" - name: Build HTML run: sphinx-build -b html -W --keep-going docs docs/_build/html - uses: actions/configure-pages@v5 @@ -36,6 +36,7 @@ jobs: path: docs/_build/html deploy: + if: github.ref == 'refs/heads/master' needs: build runs-on: ubuntu-latest environment: diff --git a/CHANGELOG.md b/CHANGELOG.md index afa687f..3da2983 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to `seqtree`. Dates are release dates. From 1.0.0 the project follows semantic versioning: breaking changes need a **major** bump. +## Unreleased + +- Reject unknown setup flags before creating an environment or installing packages. +- Keep native bindings in the reviewed nanobind 2.x range. +- Build documentation from declared extras on master/dev and pull requests; + deploy master only, without development builds cancelling release documentation. + ## [1.3.1] — 2026-10-19 ### Changed diff --git a/README.md b/README.md index ae42b89..aa627d0 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ [![Python](https://img.shields.io/pypi/pyversions/seqtree.svg)](https://pypi.org/project/seqtree/) [![License](https://img.shields.io/badge/license-GPLv3-green)](LICENSE) [![CI](https://github.com/antigenomics/seqtree/actions/workflows/ci.yml/badge.svg)](https://github.com/antigenomics/seqtree/actions/workflows/ci.yml) -[![Docs](https://github.com/antigenomics/seqtree/actions/workflows/docs.yml/badge.svg?branch=dev)](https://antigenomics.github.io/seqtree/) +[![Docs](https://github.com/antigenomics/seqtree/actions/workflows/docs.yml/badge.svg?branch=master)](https://antigenomics.github.io/seqtree/) Fast fuzzy search over biological sequences (amino-acid or nucleotide), as a C++ core with a minimal Python binding. Build an immutable index once, then search diff --git a/docs/requirements.txt b/docs/requirements.txt deleted file mode 100644 index 820da3b..0000000 --- a/docs/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -sphinx>=7 -pydata-sphinx-theme>=0.15 diff --git a/pyproject.toml b/pyproject.toml index 8616a7b..1026fdd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["scikit-build-core>=0.12", "nanobind>=2.5"] +requires = ["scikit-build-core>=0.12", "nanobind>=2.5,<3"] build-backend = "scikit_build_core.build" [project] @@ -26,7 +26,7 @@ dependencies = [] # seqtree itself has no runtime dependencies and must never import it. test = ["pytest>=7.4", "pytest-cov>=4.1", "biopython>=1.81"] bench = ["huggingface_hub"] -docs = ["sphinx", "pydata-sphinx-theme"] +docs = ["sphinx>=7", "pydata-sphinx-theme>=0.15"] control = ["huggingface_hub"] # only needed to download controls larger than the bundled subset pmhc = ["huggingface_hub"] # only needed to fetch the isalgo/pmhc_data epitope tables diff --git a/setup.sh b/setup.sh index ed91494..85554e9 100755 --- a/setup.sh +++ b/setup.sh @@ -1,12 +1,21 @@ #!/bin/sh # Repo-local venv + editable install, via uv. POSIX sh -- runs under bash and zsh. Flags: # --tests also install pytest extra -# --bench also install benchmark extras (huggingface_hub, psutil) +# --bench also install benchmark extras (huggingface_hub) # # Needs uv (https://docs.astral.sh/uv/); `brew install uv` or the standalone installer. set -eu cd "$(dirname "$0")" || exit 1 +extras="" +for arg in "$@"; do + case "$arg" in + --tests) extras="${extras}test," ;; + --bench) extras="${extras}bench," ;; + *) echo "Unknown flag: $arg" >&2; exit 2 ;; + esac +done + if ! command -v uv >/dev/null 2>&1; then echo "setup.sh needs uv -- install it with 'brew install uv' or see https://docs.astral.sh/uv/" exit 1 @@ -19,14 +28,6 @@ if [ ! -d .venv ]; then fi . .venv/bin/activate -extras="" -for arg in "$@"; do - case "$arg" in - --tests) extras="${extras}test," ;; - --bench) extras="${extras}bench," ;; - esac -done - if [ -n "$extras" ]; then extras="[${extras%,}]"; fi uv pip install -e ".$extras" diff --git a/tests/python/test_setup.py b/tests/python/test_setup.py index a925d28..2f8c1a8 100644 --- a/tests/python/test_setup.py +++ b/tests/python/test_setup.py @@ -15,6 +15,10 @@ def test_setup_extras_and_install_failure(tmp_path): root = tmp_path / "project" root.mkdir() (root / "setup.sh").write_text((Path(__file__).resolve().parents[2] / "setup.sh").read_text()) + unknown = subprocess.run(["sh", "setup.sh", "--unknown"], cwd=root, + env=env, capture_output=True, text=True, timeout=10) + assert unknown.returncode == 2 and "Unknown flag: --unknown" in unknown.stderr + assert not (root / ".venv").exists() activation = root / ".venv" / "bin" / "activate" activation.parent.mkdir(parents=True) activation.write_text("") From 8db83737423ad7892f6209739b84013c6ecbd52d Mon Sep 17 00:00:00 2001 From: mikessh Date: Wed, 4 Nov 2026 04:41:19 +0300 Subject: [PATCH 2/5] Preserve compiler and caller Release flags Co-Authored-By: Codex --- CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 718307a..f786fba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,9 +7,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON) if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() -if(NOT MSVC) - set(CMAKE_CXX_FLAGS_RELEASE "-O3") -endif() # MSVC Release already uses /O2; -O3 is not a valid MSVC flag +# Keep CMake/toolchain Release flags, including compiler-specific optimization. option(SEQTREE_TESTS "Build C++ tests" OFF) option(SEQTREE_BENCH "Build C++ benchmarks" OFF) From ac91116694b9f77c480538c67ef29bd48cdc9cd4 Mon Sep 17 00:00:00 2001 From: mikessh Date: Wed, 4 Nov 2026 06:47:59 +0300 Subject: [PATCH 3/5] Cap automatic native batches at sixteen allocated CPUs --- CHANGELOG.md | 3 +++ README.md | 6 +++++- include/seqtree/parallel.hpp | 4 ++-- skills/seqtree/SKILL.md | 3 +++ tests/cpp/test_parallel.cpp | 11 +++++++++++ 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3da2983..16595a9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ versioning: breaking changes need a **major** bump. ## Unreleased +- Cap automatic native CPU budgets at 16 allocated CPUs; preserve larger explicit + requests within the allocation and serial inner kernels in sample workers. + - Reject unknown setup flags before creating an environment or installing packages. - Keep native bindings in the reviewed nanobind 2.x range. - Build documentation from declared extras on master/dev and pull requests; diff --git a/README.md b/README.md index aa627d0..e228042 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ for hit in idx.search("CASSLAPGATNEKLFF", p): print(hit.ref_id, hit.score, hit.n_subs) # parallel batch (releases the GIL) -results = idx.search_batch(queries, p, threads=0) # 0 = all cores +results = idx.search_batch(queries, p, threads=0) # 0 = automatic, up to 16 allocated CPUs # matrix-weighted budget. Name seqtrie -- engine="auto" always means seqtm. # gap_open must follow the matrix: 2 * blosum62.scale() == 28, not the default 1. @@ -273,3 +273,7 @@ and tie, count, exclusion and unsupported-value semantics. The public [seqtree skill](skills/seqtree/SKILL.md) guides use of the C++20 library and Python bindings for sequence search, alignment, distances, and batch operations. + +CPU budgets use `threads=0` for automatic parallel work, capped at 16 allocated CPUs. +Use `1` for serial work or a positive +count to override the automatic cap within the scheduler/container allocation. diff --git a/include/seqtree/parallel.hpp b/include/seqtree/parallel.hpp index 6f9a92e..aa25677 100644 --- a/include/seqtree/parallel.hpp +++ b/include/seqtree/parallel.hpp @@ -67,7 +67,7 @@ inline unsigned available_cpus() { } // Runs body(i, local) for every i in [0, n), across nt workers pulling contiguous chunks off -// one atomic cursor. `threads <= 0` means the detected available CPU budget. +// one atomic cursor. `threads <= 0` selects up to 16 available CPUs; positive counts may exceed that cap. // // `make_local()` is called once per worker and its result handed to every body() that worker // runs -- that is how a Searcher or a scratch buffer gets allocated once per thread rather @@ -85,7 +85,7 @@ template void parallel_for(size_t n, int threads, MakeLocal make_local, Body body, size_t chunk_min = 1, size_t chunk_max = 1024) { const unsigned available = available_cpus(); - unsigned nt = threads > 0 ? unsigned(threads) : available; + unsigned nt = threads > 0 ? unsigned(threads) : std::min(available, 16u); if (nt > available || nt >= 1000) throw std::invalid_argument("worker count exceeds the available CPU budget; request fewer threads"); if (n == 0) return; diff --git a/skills/seqtree/SKILL.md b/skills/seqtree/SKILL.md index b482b08..f2c3daf 100644 --- a/skills/seqtree/SKILL.md +++ b/skills/seqtree/SKILL.md @@ -131,3 +131,6 @@ Keep exact hits for ordinary annotation. Exclusion filters hits without rebuildi indices, refitting models, or changing population sizes. The legacy `evalues` helper excludes zero-score hits, which can include nonidentical sequences under masked penalties; other reducers use encoded identity. Check the specific API. + +Automatic native batches use at most 16 available CPUs; positive thread counts +override this cap within the allocation. diff --git a/tests/cpp/test_parallel.cpp b/tests/cpp/test_parallel.cpp index 1b3609e..5e8ba11 100644 --- a/tests/cpp/test_parallel.cpp +++ b/tests/cpp/test_parallel.cpp @@ -67,3 +67,14 @@ TEST_CASE("parallel batches reject an excessive live worker budget") { [] { return 0; }, [](size_t, int&) {}), std::invalid_argument); } + + +TEST_CASE("automatic batches cap workers without capping the allocation") { + for (int requested : {0, int(seqtree::available_cpus())}) { + std::atomic factories{0}; + seqtree::parallel_for(2048, requested, [&] { ++factories; return 0; }, + [](size_t, int&) {}); + CHECK(factories == (requested ? unsigned(requested) : + std::min(seqtree::available_cpus(), 16u))); + } +} From 975d33362b647dccf4e7b86c680ae5c602dad531 Mon Sep 17 00:00:00 2001 From: mikessh Date: Wed, 4 Nov 2026 08:34:10 +0300 Subject: [PATCH 4/5] Keep retired scratch out of version control and release artifacts --- .gitignore | 3 +++ pyproject.toml | 1 + 2 files changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index ab7fe61..77eed0e 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ tmp/ !/skills/seqtree/ /add_bindings.py /add_native.py + +# Private retired branches, notes and scratch; never package or publish. +/attic/ diff --git a/pyproject.toml b/pyproject.toml index 1026fdd..93ddb89 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ wheel.packages = ["python/seqtree"] cmake.build-type = "Release" # Keep the sdist lean: ship only what builds the package. Anchor with a leading "/". sdist.exclude = [ + "/attic/", "/bench", "/tests", "/docs", "/.github", "/setup.sh", "/build", "/appendix", "/examples", # Local agent config: machine-specific absolute paths and unrelated repo names. "/.claude", "/CLAUDE.md", "/AGENTS.md", "/ROADMAP.md", "/SOURCES.md", "/skills", From 9b2d6760545c200b406f6ca26c168f81d9d0aef4 Mon Sep 17 00:00:00 2001 From: mikessh Date: Wed, 4 Nov 2026 11:55:33 +0300 Subject: [PATCH 5/5] Prepare seqtree 1.3.3 release Co-Authored-By: Codex --- CHANGELOG.md | 2 ++ pyproject.toml | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 16595a9..e55dac5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ versioning: breaking changes need a **major** bump. ## Unreleased +## 1.3.3 + - Cap automatic native CPU budgets at 16 allocated CPUs; preserve larger explicit requests within the allocation and serial inner kernels in sample workers. diff --git a/pyproject.toml b/pyproject.toml index 93ddb89..53f2b67 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "scikit_build_core.build" [project] name = "seqtree" -version = "1.3.2" +version = "1.3.3" description = "Fast fuzzy search over biological sequences (C++ core, Python bindings)" readme = "README.md" requires-python = ">=3.10"