groupoid 0.1.0.dev1__tar.gz → 0.1.0.dev4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. groupoid-0.1.0.dev4/.github/dependabot.yml +78 -0
  2. groupoid-0.1.0.dev4/.github/scripts/install_torch_cpu.sh +25 -0
  3. groupoid-0.1.0.dev4/.github/workflows/auto-tag-release.yml +72 -0
  4. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/.github/workflows/ci.yml +18 -18
  5. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/.github/workflows/docs-deploy.yml +8 -7
  6. groupoid-0.1.0.dev4/.github/workflows/release.yml +107 -0
  7. groupoid-0.1.0.dev4/.zenodo.json +39 -0
  8. groupoid-0.1.0.dev4/CHANGELOG.md +162 -0
  9. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/CITATION.cff +2 -2
  10. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/CONTRIBUTING.md +39 -0
  11. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/LIMITATIONS.md +14 -5
  12. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/PKG-INFO +40 -26
  13. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/PROBLEMS.md +19 -6
  14. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/README.md +38 -24
  15. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/STATUS.md +28 -14
  16. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/codemeta.json +1 -1
  17. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/docs/index.md +12 -10
  18. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/docs/quickstart.md +15 -6
  19. groupoid-0.1.0.dev4/experiments/PREREGISTRATION.md +154 -0
  20. groupoid-0.1.0.dev4/experiments/README.md +64 -0
  21. groupoid-0.1.0.dev4/experiments/RESULTS.md +163 -0
  22. groupoid-0.1.0.dev4/experiments/analyze.py +261 -0
  23. groupoid-0.1.0.dev4/experiments/results.json +12615 -0
  24. groupoid-0.1.0.dev4/experiments/run_benchmark.py +213 -0
  25. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid/__init__.py +1 -1
  26. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid/aggregation.py +66 -1
  27. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid/groupoid.py +5 -0
  28. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid/optimizer.py +70 -10
  29. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid.egg-info/PKG-INFO +40 -26
  30. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid.egg-info/SOURCES.txt +11 -0
  31. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid.egg-info/requires.txt +1 -1
  32. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/pyproject.toml +2 -2
  33. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/test_coverage_gaps.py +4 -2
  34. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/test_defensive_paths.py +12 -0
  35. groupoid-0.1.0.dev4/tests/test_optimizer_validation.py +141 -0
  36. groupoid-0.1.0.dev4/tests/test_pipeline_integration.py +130 -0
  37. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/test_smoke.py +21 -0
  38. groupoid-0.1.0.dev1/.github/dependabot.yml +0 -29
  39. groupoid-0.1.0.dev1/.zenodo.json +0 -39
  40. groupoid-0.1.0.dev1/CHANGELOG.md +0 -77
  41. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  42. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  43. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/.github/ISSUE_TEMPLATE/mathematical_correction.md +0 -0
  44. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/.gitignore +0 -0
  45. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/.pre-commit-config.yaml +0 -0
  46. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/LICENSE +0 -0
  47. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/REPRODUCE.md +0 -0
  48. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/SECURITY.md +0 -0
  49. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/benchmarks/__init__.py +0 -0
  50. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/benchmarks/conftest.py +0 -0
  51. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/benchmarks/test_karcher_benchmark.py +0 -0
  52. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/docs/api.md +0 -0
  53. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/docs/overrides/404.html +0 -0
  54. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/docs/theory.md +0 -0
  55. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/examples/README.md +0 -0
  56. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/examples/__init__.py +0 -0
  57. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/examples/synthetic_curve_demo.py +0 -0
  58. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid/cohomology.py +0 -0
  59. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid/laplacian.py +0 -0
  60. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid/manifold.py +0 -0
  61. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid/persistence.py +0 -0
  62. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid/sheaf.py +0 -0
  63. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid/transport.py +0 -0
  64. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid.egg-info/dependency_links.txt +0 -0
  65. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/groupoid.egg-info/top_level.txt +0 -0
  66. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/mkdocs.yml +0 -0
  67. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/scripts/reproduce_sheaf_laplacian.py +0 -0
  68. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/setup.cfg +0 -0
  69. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/__init__.py +0 -0
  70. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/test_aggregation_disconnected.py +0 -0
  71. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/test_cohomology_cocycle.py +0 -0
  72. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/test_imports.py +0 -0
  73. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/test_integration.py +0 -0
  74. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/test_persistence.py +0 -0
  75. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/test_persistence_dimension_labels.py +0 -0
  76. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/test_sheaf_laplacian.py +0 -0
  77. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/test_transport_validation.py +0 -0
  78. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/theory/__init__.py +0 -0
  79. {groupoid-0.1.0.dev1 → groupoid-0.1.0.dev4}/tests/theory/test_mathematical_invariants.py +0 -0
@@ -0,0 +1,78 @@
1
+ # Dependabot configuration.
2
+ # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
3
+ #
4
+ # Opens reviewable update PRs (never auto-merged) so security and version bumps
5
+ # are surfaced proactively, rather than relying on a manual read of the CI
6
+ # dependency-audit output. Complements the CI dependency-vulnerability scan.
7
+ version: 2
8
+ updates:
9
+ # Python dependencies declared in pyproject.toml.
10
+ - package-ecosystem: "pip"
11
+ directory: "/"
12
+ schedule:
13
+ interval: "weekly"
14
+ day: "monday"
15
+ # Pin the run to a predictable local hour. With no time/timezone,
16
+ # Dependabot fires at its default slot in UTC, which lands in the middle
17
+ # of the night for the maintainer. Adjust `timezone` to your actual IANA
18
+ # zone if America/New_York is not yours.
19
+ time: "09:00"
20
+ timezone: "America/New_York"
21
+ open-pull-requests-limit: 5
22
+ # Manage DIRECT dependencies only — the ones declared in pyproject.toml.
23
+ # GROUPOID ships no committed/compiled lockfile, so this is currently a
24
+ # no-op (Dependabot already only edits the pyproject declarations). It is
25
+ # kept for consistency and as a guard: should a compiled lock ever be added,
26
+ # its transitive pins must advance by recompiling the lock, not by standalone
27
+ # Dependabot line-edits. Transitive CVEs are NOT lost: Dependabot *security*
28
+ # updates patch vulnerable dependencies regardless of this allow filter.
29
+ allow:
30
+ - dependency-type: "direct"
31
+ groups:
32
+ # Batch updates by area into a single PR per group to limit noise instead
33
+ # of one PR per package.
34
+ runtime:
35
+ patterns:
36
+ - "torch"
37
+ - "geomstats"
38
+ - "pymanopt"
39
+ - "ripser"
40
+ - "persim"
41
+ - "POT"
42
+ - "networkx"
43
+ - "einops"
44
+ - "numpy"
45
+ - "scipy"
46
+ - "pydantic"
47
+ - "loguru"
48
+ dev:
49
+ patterns:
50
+ - "pytest*"
51
+ - "ruff"
52
+ - "black"
53
+ - "mypy"
54
+ - "hypothesis"
55
+ - "bandit"
56
+ - "pip-audit"
57
+ - "pre-commit"
58
+ docs:
59
+ patterns: ["mkdocs*", "mkdocstrings*"]
60
+ # Fallback batcher. With `allow: direct` and the groups above, this should
61
+ # stay empty; if a future declared dep is not matched above, this keeps it
62
+ # to a single PR instead of one-per-package.
63
+ other:
64
+ patterns: ["*"]
65
+
66
+ # GitHub Actions used in the workflows (keeps action versions current).
67
+ - package-ecosystem: "github-actions"
68
+ directory: "/"
69
+ schedule:
70
+ interval: "weekly"
71
+ day: "monday"
72
+ time: "09:00"
73
+ timezone: "America/New_York"
74
+ open-pull-requests-limit: 5
75
+ groups:
76
+ # Batch all action bumps into a single weekly PR instead of one each.
77
+ actions:
78
+ patterns: ["*"]
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env bash
2
+ # Installs CPU-only torch from the PyTorch index with retries.
3
+ #
4
+ # That index (download.pytorch.org/whl/cpu, Cloudflare R2-backed as
5
+ # download-r2.pytorch.org) intermittently drops the TLS handshake
6
+ # (SSLV3_ALERT_HANDSHAKE_FAILURE) from GitHub-hosted runners. pip's own
7
+ # built-in retries reuse the same connection pool within one process and
8
+ # do not help here; a fresh `pip install` invocation gets a fresh TLS
9
+ # connection and has in practice succeeded where the retries within a
10
+ # single invocation did not. Five attempts with linear backoff (10s, 20s,
11
+ # 30s, 40s) before giving up.
12
+ set -euo pipefail
13
+
14
+ max_attempts=5
15
+ attempt=1
16
+ until pip install torch --index-url https://download.pytorch.org/whl/cpu; do
17
+ if [ "${attempt}" -ge "${max_attempts}" ]; then
18
+ echo "::error::torch install failed after ${max_attempts} attempts" >&2
19
+ exit 1
20
+ fi
21
+ sleep_seconds=$((attempt * 10))
22
+ echo "torch install attempt ${attempt}/${max_attempts} failed; retrying in ${sleep_seconds}s" >&2
23
+ sleep "${sleep_seconds}"
24
+ attempt=$((attempt + 1))
25
+ done
@@ -0,0 +1,72 @@
1
+ name: Auto Tag Release
2
+
3
+ # Creates the tag for a release-cut merge to main; it does nothing else.
4
+ # release.yml (build, attestations, PyPI publish, signing, GitHub Release)
5
+ # is triggered solely by that tag push, exactly as it would be from a
6
+ # manual `git tag vX.Y.Z && git push origin vX.Y.Z`. Keeping tag creation
7
+ # and publishing in two separate, single-purpose workflows means the
8
+ # actual publish path has exactly one trigger (a tag push) to reason
9
+ # about, instead of depending on GitHub's anti-recursion behavior for
10
+ # GITHUB_TOKEN-authored pushes not re-triggering the same push event.
11
+ #
12
+ # If a version needs a tag this workflow's format check rejects (see
13
+ # below), tag it manually -- this automation only ever creates a tag
14
+ # when one is missing, so a pre-existing manual tag always takes
15
+ # precedence and this workflow becomes a no-op for that version.
16
+
17
+ on:
18
+ push:
19
+ branches: [main]
20
+ paths:
21
+ - pyproject.toml
22
+
23
+ permissions:
24
+ contents: read
25
+
26
+ jobs:
27
+ tag:
28
+ runs-on: ubuntu-latest
29
+ permissions:
30
+ contents: write # push the tag; nothing else in this workflow needs it
31
+ steps:
32
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
33
+ with:
34
+ fetch-depth: 0 # need full tag history to check for an existing tag
35
+
36
+ - name: Create the release tag if this version doesn't have one yet
37
+ run: |
38
+ set -euo pipefail
39
+
40
+ version=$(grep -m1 '^version = "' pyproject.toml | sed -E 's/version = "(.*)"/\1/')
41
+ if [ -z "${version}" ]; then
42
+ echo "::error::could not extract a version from pyproject.toml"
43
+ exit 1
44
+ fi
45
+
46
+ # PEP 440-ish release/pre-release only: X.Y.Z, optionally with
47
+ # .devN / aN / bN / rcN. No local version segment (+...), no
48
+ # anything else. A version that doesn't match this is presumably
49
+ # intentional and unusual enough to want a human tagging it by
50
+ # hand rather than this automation guessing.
51
+ if ! echo "${version}" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(\.dev[0-9]+|a[0-9]+|b[0-9]+|rc[0-9]+)?$'; then
52
+ echo "::error::version '${version}' is not a recognized release format (expected X.Y.Z, optionally with .devN/aN/bN/rcN, no local metadata); refusing to tag automatically. Tag it manually if this is intentional."
53
+ exit 1
54
+ fi
55
+
56
+ tag="v${version}"
57
+
58
+ if git rev-parse "refs/tags/${tag}" >/dev/null 2>&1; then
59
+ echo "Tag ${tag} already exists; nothing to do."
60
+ exit 0
61
+ fi
62
+
63
+ if ! grep -qF "[${version}]" CHANGELOG.md; then
64
+ echo "::error::CHANGELOG.md has no [${version}] heading; refusing to tag an undocumented release. Add the changelog section as part of the version-bump PR."
65
+ exit 1
66
+ fi
67
+
68
+ echo "Version ${version} has no matching tag; creating ${tag}."
69
+ git config user.name "github-actions[bot]"
70
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
71
+ git tag -a "${tag}" -m "Release ${tag}"
72
+ git push origin "refs/tags/${tag}"
@@ -13,8 +13,8 @@ jobs:
13
13
  lint:
14
14
  runs-on: ubuntu-latest
15
15
  steps:
16
- - uses: actions/checkout@v4
17
- - uses: actions/setup-python@v5
16
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
17
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
18
18
  with:
19
19
  python-version: "3.11"
20
20
  - name: Install dependencies
@@ -25,7 +25,7 @@ jobs:
25
25
  # Linting only against `ruff black mypy` left numpy/pydantic uninstalled,
26
26
  # so --strict raised disallow_subclassing_any on the BaseModel subclass.
27
27
  # Consistent with the test / security / docs jobs. CPU torch keeps it lean.
28
- pip install torch --index-url https://download.pytorch.org/whl/cpu
28
+ .github/scripts/install_torch_cpu.sh
29
29
  pip install -e ".[dev]"
30
30
  - name: Run ruff
31
31
  run: ruff check .
@@ -40,14 +40,14 @@ jobs:
40
40
  matrix:
41
41
  python-version: ["3.10", "3.11", "3.12"]
42
42
  steps:
43
- - uses: actions/checkout@v4
44
- - uses: actions/setup-python@v5
43
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
44
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
45
45
  with:
46
46
  python-version: ${{ matrix.python-version }}
47
47
  - name: Install dependencies
48
48
  run: |
49
49
  python -m pip install --upgrade pip
50
- pip install torch --index-url https://download.pytorch.org/whl/cpu
50
+ .github/scripts/install_torch_cpu.sh
51
51
  pip install -e ".[dev]"
52
52
  - name: Run tests with coverage gate
53
53
  run: >-
@@ -58,8 +58,8 @@ jobs:
58
58
  security:
59
59
  runs-on: ubuntu-latest
60
60
  steps:
61
- - uses: actions/checkout@v4
62
- - uses: actions/setup-python@v5
61
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
62
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
63
63
  with:
64
64
  python-version: "3.11"
65
65
  - name: Install dependencies
@@ -69,7 +69,7 @@ jobs:
69
69
  # packages GROUPOID actually depends on. The previous `safety` step
70
70
  # installed only the tools and never the project, so it audited the
71
71
  # wrong environment. CPU torch keeps the install lean.
72
- pip install torch --index-url https://download.pytorch.org/whl/cpu
72
+ .github/scripts/install_torch_cpu.sh
73
73
  pip install -e .
74
74
  pip install bandit pip-audit
75
75
  # Pin setuptools LAST. torch requires setuptools<82 and pulls in a
@@ -95,8 +95,8 @@ jobs:
95
95
  pre-commit:
96
96
  runs-on: ubuntu-latest
97
97
  steps:
98
- - uses: actions/checkout@v4
99
- - uses: actions/setup-python@v5
98
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
99
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
100
100
  with:
101
101
  python-version: "3.11"
102
102
  - name: Install pre-commit
@@ -107,14 +107,14 @@ jobs:
107
107
  benchmark:
108
108
  runs-on: ubuntu-latest
109
109
  steps:
110
- - uses: actions/checkout@v4
111
- - uses: actions/setup-python@v5
110
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
111
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
112
112
  with:
113
113
  python-version: "3.11"
114
114
  - name: Install dependencies
115
115
  run: |
116
116
  python -m pip install --upgrade pip
117
- pip install torch --index-url https://download.pytorch.org/whl/cpu
117
+ .github/scripts/install_torch_cpu.sh
118
118
  pip install -e ".[dev]"
119
119
  # Benchmarks run for information only. There is no regression gate:
120
120
  # timing baselines recorded on one machine are not comparable to a
@@ -128,7 +128,7 @@ jobs:
128
128
  pytest benchmarks/ -v --benchmark-json=benchmark_results.json || true
129
129
  - name: Upload benchmark results
130
130
  if: always()
131
- uses: actions/upload-artifact@v4
131
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
132
132
  with:
133
133
  name: benchmark-results
134
134
  path: benchmark_results.json
@@ -137,14 +137,14 @@ jobs:
137
137
  docs:
138
138
  runs-on: ubuntu-latest
139
139
  steps:
140
- - uses: actions/checkout@v4
141
- - uses: actions/setup-python@v5
140
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
141
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
142
142
  with:
143
143
  python-version: "3.11"
144
144
  - name: Install dependencies
145
145
  run: |
146
146
  python -m pip install --upgrade pip
147
- pip install torch --index-url https://download.pytorch.org/whl/cpu
147
+ .github/scripts/install_torch_cpu.sh
148
148
  pip install -e ".[docs]"
149
149
  - name: Build docs
150
150
  run: mkdocs build --strict
@@ -7,8 +7,6 @@ on:
7
7
 
8
8
  permissions:
9
9
  contents: read
10
- pages: write
11
- id-token: write
12
10
 
13
11
  concurrency:
14
12
  group: pages
@@ -18,27 +16,30 @@ jobs:
18
16
  build:
19
17
  runs-on: ubuntu-latest
20
18
  steps:
21
- - uses: actions/checkout@v4
22
- - uses: actions/setup-python@v5
19
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
20
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
23
21
  with:
24
22
  python-version: "3.11"
25
23
  - name: Install dependencies
26
24
  run: |
27
25
  python -m pip install --upgrade pip
28
- pip install torch --index-url https://download.pytorch.org/whl/cpu
26
+ .github/scripts/install_torch_cpu.sh
29
27
  pip install -e ".[docs]"
30
28
  - name: Build docs
31
29
  run: mkdocs build --strict
32
- - uses: actions/upload-pages-artifact@v3
30
+ - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5
33
31
  with:
34
32
  path: site/
35
33
 
36
34
  deploy:
37
35
  needs: build
38
36
  runs-on: ubuntu-latest
37
+ permissions:
38
+ pages: write
39
+ id-token: write
39
40
  environment:
40
41
  name: github-pages
41
42
  url: ${{ steps.deployment.outputs.page_url }}
42
43
  steps:
43
44
  - id: deployment
44
- uses: actions/deploy-pages@v4
45
+ uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5
@@ -0,0 +1,107 @@
1
+ name: Release
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v[0-9]*"
7
+ workflow_dispatch:
8
+ inputs:
9
+ ref:
10
+ description: "Existing tag/ref to build and publish to PyPI (e.g. v0.1.0.dev1). PyPI only — does NOT create a GitHub Release or Zenodo deposit. Leave blank for a normal tag-triggered release."
11
+ required: false
12
+ default: ""
13
+
14
+ permissions:
15
+ contents: read
16
+
17
+ jobs:
18
+ build:
19
+ runs-on: ubuntu-latest
20
+ permissions:
21
+ contents: read
22
+ id-token: write # provenance OIDC
23
+ attestations: write # actions/attest-build-provenance + attest-sbom
24
+ steps:
25
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
26
+ with:
27
+ ref: ${{ github.event.inputs.ref || github.ref }}
28
+ fetch-depth: 0
29
+ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
30
+ with:
31
+ python-version: "3.12"
32
+ cache: pip
33
+ - name: Install build tooling
34
+ run: |
35
+ python -m pip install --upgrade pip build
36
+ pip install cyclonedx-bom
37
+ - name: Build sdist + wheel
38
+ run: python -m build
39
+ - name: Generate CycloneDX SBOM
40
+ run: cyclonedx-py requirements -o sbom.cdx.json <(pip freeze)
41
+ - name: Provenance attestation (dist)
42
+ uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
43
+ with:
44
+ subject-path: "dist/*"
45
+ - name: SBOM attestation (CycloneDX)
46
+ uses: actions/attest-sbom@c604332985a26aa8cf1bdc465b92731239ec6b9e # v4.1.0
47
+ with:
48
+ subject-path: "dist/*"
49
+ sbom-path: "sbom.cdx.json"
50
+ - name: Upload artifacts
51
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
52
+ with:
53
+ name: release-artifacts
54
+ path: |
55
+ dist/*
56
+ sbom.cdx.json
57
+
58
+ publish-pypi:
59
+ needs: build
60
+ runs-on: ubuntu-latest
61
+ permissions:
62
+ contents: read
63
+ id-token: write # PyPI Trusted Publishing OIDC
64
+ environment:
65
+ name: pypi
66
+ url: https://pypi.org/project/groupoid/
67
+ steps:
68
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
69
+ with:
70
+ name: release-artifacts
71
+ path: .
72
+ - name: Publish to PyPI (Trusted Publishing, OIDC)
73
+ uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # release/v1
74
+ with:
75
+ packages-dir: dist
76
+ print-hash: true
77
+
78
+ sign-and-release:
79
+ needs: build
80
+ # Only on a real tag push — a manual workflow_dispatch (used to publish an
81
+ # already-released tag to PyPI) must NOT create a duplicate GitHub Release
82
+ # or trigger a second Zenodo deposit.
83
+ if: github.event_name == 'push'
84
+ runs-on: ubuntu-latest
85
+ permissions:
86
+ contents: write # create GitHub Release
87
+ id-token: write # Sigstore signing OIDC
88
+ steps:
89
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
90
+ - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
91
+ with:
92
+ name: release-artifacts
93
+ path: .
94
+ - name: Sign artifacts with Sigstore
95
+ uses: sigstore/gh-action-sigstore-python@5b79a39c381910c090341a2c9b0bf022c8b387e1 # v3.4.0
96
+ with:
97
+ inputs: |
98
+ dist/*.tar.gz
99
+ dist/*.whl
100
+ - name: Create GitHub Release
101
+ uses: softprops/action-gh-release@718ea10b132b3b2eba29c1007bb80653f286566b # v3.0.1
102
+ with:
103
+ generate_release_notes: true
104
+ files: |
105
+ dist/*
106
+ sbom.cdx.json
107
+ *.sigstore.json
@@ -0,0 +1,39 @@
1
+ {
2
+ "upload_type": "software",
3
+ "title": "GROUPOID: Groupoid-based aggregation for federated learning on Riemannian manifolds",
4
+ "version": "0.1.0.dev4",
5
+ "description": "<p>Pre-alpha research prototype exploring groupoid-based aggregation for federated learning on Riemannian manifolds. Implements transport groupoid morphisms, first cohomology (H^1) for consistency detection, the cellular sheaf Laplacian for spectral analysis, parallel transport (Schild's and pole ladders), Riemannian optimizers, and Karcher mean aggregation via geomstats.</p><p><strong>Erratum / supersession notice:</strong> this version (v0.1.0.dev4) carries the mathematical corrections introduced in v0.1.0.dev1 over the earlier v0.1.0.dev0 snapshot (version DOI 10.5281/zenodo.20563975): the sheaf (connection) Laplacian is a verified positive-semidefinite operator with a transport-consistent kernel; persistence diagrams retain the homology-dimension label so H0 and H1 are no longer conflated; and H^1 cohomology raises on incomplete cocycles instead of forming partial holonomy. It also carries the v0.1.0.dev2 correction of a first-step magnitude inflation in the RiemannianAdam optimizer's bias initialization, and the v0.1.0.dev3 Morphism str()/format() display fix and documentation-accuracy corrections (v0.1.0.dev3 was cut in source control but never separately tagged or deposited, so this is the first deposit carrying its changes). New in v0.1.0.dev4: the Riemannian SGD and Adam optimizers now parallel-transport their accumulated moments between iterates instead of projecting them (projection could annihilate a geodesic-aligned moment); the parallel-transport and persistent-homology modules are wired into the aggregation pipeline; and a preregistered synthetic benchmark (see the repository's experiments/ directory) tests the central transport-aggregation hypothesis, finding it supported on synthetic frame-misaligned data and still unvalidated on real federated learning tasks. The v0.1.0.dev0 version DOI must not be cited for results. Cite the concept DOI (10.5281/zenodo.20563974), which always resolves to the latest corrected version, or this version or later.</p>",
6
+ "creators": [
7
+ {
8
+ "name": "Maniches, Santiago",
9
+ "affiliation": "TOPOLOGICA LLC",
10
+ "orcid": "0009-0005-6480-1987"
11
+ }
12
+ ],
13
+ "license": "Apache-2.0",
14
+ "keywords": [
15
+ "federated learning",
16
+ "Riemannian geometry",
17
+ "groupoid",
18
+ "sheaf theory",
19
+ "cohomology",
20
+ "topological data analysis"
21
+ ],
22
+ "related_identifiers": [
23
+ {
24
+ "identifier": "10.5281/zenodo.20563974",
25
+ "relation": "isVersionOf",
26
+ "scheme": "doi"
27
+ },
28
+ {
29
+ "identifier": "10.5281/zenodo.20787126",
30
+ "relation": "isNewVersionOf",
31
+ "scheme": "doi"
32
+ },
33
+ {
34
+ "identifier": "https://github.com/smaniches/GROUPOID",
35
+ "relation": "isSupplementTo",
36
+ "scheme": "url"
37
+ }
38
+ ]
39
+ }
@@ -0,0 +1,162 @@
1
+ # Changelog
2
+
3
+ All notable changes to GROUPOID are documented in this file.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and
6
+ this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html)
7
+ (with PEP 440 identifiers for Python pre-releases).
8
+
9
+ ## [Unreleased]
10
+
11
+ Nothing yet.
12
+
13
+ ## [0.1.0.dev4] - 2026-07-06
14
+
15
+ `v0.1.0.dev3` was cut in source control (below) but never tagged or
16
+ released: no `v0.1.0.dev3` tag was ever pushed, so PyPI, the GitHub
17
+ Release, and the Zenodo deposit all skip directly from `0.1.0.dev2` to
18
+ this version, which carries both the `0.1.0.dev3` changes and the ones
19
+ below. See the "Releasing" section of `CONTRIBUTING.md` for the
20
+ automation added in this version that prevents this gap from recurring.
21
+
22
+ ### Fixed
23
+ - **Optimizer moment accumulators are now parallel-transported between
24
+ iterates**: Adam's first moment was previously carried across steps by
25
+ tangent projection (which can annihilate a geodesic-aligned moment) and
26
+ the SGD momentum velocity was not moved at all (leaving it non-tangent
27
+ at the new iterate). Both now use the metric's parallel transport (the
28
+ Becigneul-Ganea construction), with projection kept as the fallback for
29
+ metrics without parallel transport. Regression tests discriminate
30
+ transport from projection by exact norm preservation (#49).
31
+
32
+ ### Added
33
+ - `TransportGroupoidAggregator.register_transport_from_points`: computes
34
+ and registers the transport matrix from two client base points via the
35
+ pole or Schild ladder, wiring `groupoid.transport` into the pipeline
36
+ (#49).
37
+ - Opt-in `track_divergence` flag on the aggregator: each round computes
38
+ the persistent-homology summary of the transported parameters with the
39
+ H0-vs-H0 bottleneck distance to the previous round, exposed as
40
+ `FederatedRound.divergence` (default off; existing behavior unchanged),
41
+ wiring `groupoid.persistence` into the pipeline (#49).
42
+ - Descent validation for the Riemannian optimizers: SGD, momentum SGD,
43
+ and Adam descend to a known target on S^2; the optimizer status labels
44
+ in STATUS.md and the docs are upgraded accordingly (general
45
+ convergence-rate analysis still does not exist and is not claimed)
46
+ (#49).
47
+ - A preregistered synthetic benchmark (`experiments/`): preregistration
48
+ pushed before execution, 600 seeded runs, 2x2 transport/mean ablation
49
+ against an oracle estimand, corrupted-cocycle conditions, and a
50
+ committed preregistered analysis (`experiments/RESULTS.md`). Supports
51
+ the transport benefit under frame misalignment (largely by
52
+ construction) and the pooled H^1-error correlation under corruption,
53
+ with the within-level caveat, a failed null check reported as failed,
54
+ and one documented seeding deviation (#49).
55
+ - Release automation: pushing a version bump to `main` now tags and
56
+ releases automatically, removing the manual tag-push step.
57
+
58
+ ## [0.1.0.dev3] - 2026-07-06
59
+
60
+ Cut in source control but never tagged or released; see `0.1.0.dev4` above.
61
+
62
+ ### Fixed
63
+ - **`Morphism` `str()`/`format()` output**: pydantic's `BaseModel` defines
64
+ `__str__` separately from `__repr__`, so `print()`, f-strings, and loguru's
65
+ `{}` formatting dumped the full transport matrix instead of the compact
66
+ `Morphism(A -> B)` form the `__repr__` override implements. `__str__` now
67
+ aliases `__repr__`, with a regression test (#47).
68
+
69
+ ### Changed
70
+ - Docs-site installation pages corrected to state the package is published on
71
+ PyPI as a development pre-release, aligning `docs/index.md` and
72
+ `docs/quickstart.md` with the README and `STATUS.md` corrections from #37
73
+ and #39 (#47).
74
+ - `curvature_adaptive_lr` docstring corrected to describe the implemented
75
+ behavior: the rate is damped in positive curvature and returned unchanged
76
+ otherwise; no enlargement is applied in negative curvature (#47).
77
+ - `LIMITATIONS.md` now records that `torch`, `pymanopt`, `POT`, and `einops`
78
+ are declared runtime dependencies with no current code path (the suite
79
+ passes with all four uninstalled) (#47).
80
+
81
+ ## [0.1.0.dev2] - 2026-06-21
82
+
83
+ ### Fixed
84
+ - **RiemannianAdam first-moment bias initialization**: the first moment was
85
+ seeded as `grad` while the zero-init bias correction `m / (1 - beta1**t)` was
86
+ still applied, leaving `1/(1 - beta1)` uncancelled and inflating the first
87
+ optimization step ~10x (beta1 = 0.9). Now seeded as `(1 - beta1) * grad` so the
88
+ bias correction recovers `grad`, matching standard Adam. The optimizer is not
89
+ integrated into the aggregation pipeline, so no published result changes (#39).
90
+
91
+ ### Changed
92
+ - `STATUS.md` corrected to state the package is published on PyPI as a development
93
+ pre-release (`groupoid 0.1.0.dev2`) rather than "not published on PyPI" (#39).
94
+
95
+ ## [0.1.0.dev1] - 2026-06-16
96
+
97
+ First tag cut on mathematically corrected code. The earlier `v0.1.0.dev0` tag and
98
+ its Zenodo version DOI (`10.5281/zenodo.20563975`) predate the correctness fixes
99
+ below and are **superseded**; that snapshot must not be cited for results. The
100
+ concept DOI (`10.5281/zenodo.20563974`) resolves to this version.
101
+
102
+ ### Fixed
103
+ - **Sheaf (connection) Laplacian** corrected to a positive-semidefinite operator
104
+ `L = delta^T delta` with a transport-consistent kernel, verified against an
105
+ independently constructed coboundary (#13, closes #8).
106
+ - **Persistence diagrams** now retain the homology-dimension label, so H0 and H1
107
+ bars are no longer conflated; `track_divergence` compares H0 against H0 only,
108
+ verified against an independent scipy minimum-spanning-tree reconstruction
109
+ (#21, closes #10).
110
+ - **Cohomology**: `compute_h1` raises `IncompleteCocycleError` naming the missing
111
+ edge instead of forming a meaningless partial holonomy, and disconnected-graph
112
+ transport is re-raised as an explicit domain error (#20).
113
+ - Benchmark CI regression check soft-skips on a missing JSON baseline instead of
114
+ failing the job (#15).
115
+
116
+ ### Added
117
+ - 100% line + branch test coverage of the `groupoid` package on Python 3.10-3.12,
118
+ enforced in CI via `--cov-branch --cov-fail-under=100` (#18).
119
+ - Cold-clone reproducibility: a seeded determinism receipt and reproduction guide
120
+ (`REPRODUCE.md`), install cap, stub clarity, and a real CI gate (#16).
121
+ - Aggregation tests covering forward-edge traversal, weighted mean, and the
122
+ inconsistency flag (#17).
123
+ - Zenodo concept-DOI badge and `CITATION.cff` DOI metadata (#11).
124
+ - A `Documentation` project URL and keywords aligned across `pyproject.toml`,
125
+ `codemeta.json`, and `CITATION.cff` (#19).
126
+ - A consolidated `PROBLEMS.md` self-audit recording corrected bugs, known
127
+ limitations, and deferred work (#23).
128
+ - A `CHANGELOG.md` linked from package metadata (#24).
129
+ - A `.zenodo.json` so archival deposits carry curated metadata, a supersession
130
+ note for `v0.1.0.dev0`, and version-chain links to the concept and prior DOIs.
131
+ - Dependabot configuration for weekly `pip` and `github-actions` updates (#25).
132
+
133
+ ### Changed
134
+ - Numpy array annotations parameterized to `NDArray[np.float64]`, with
135
+ `mypy --strict` enforced as a hard, gating CI step (#26).
136
+
137
+ ### Security
138
+ - Replaced the deprecated soft `safety check` with a hard, gating `pip-audit`
139
+ step; the fix-less torch advisory CVE-2025-3000 is documented and explicitly
140
+ ignored with its rationale in `SECURITY.md` (#22).
141
+
142
+ ## [0.1.0.dev0] - 2026-06-05
143
+
144
+ Initial pre-alpha research prototype. Core implementation of groupoid-based
145
+ aggregation for federated learning on Riemannian manifolds: the transport groupoid
146
+ (morphism composition and inverse), the Karcher mean via geomstats, H^1
147
+ cohomological consistency, the sheaf Laplacian, parallel transport (Schild's and
148
+ pole ladders), and persistence-based divergence tracking. Ships machine-readable
149
+ citation metadata (`CITATION.cff`, `codemeta.json` with ORCID). Pre-alpha: APIs
150
+ may change and the central hypothesis is not yet validated (see `STATUS.md` and
151
+ `LIMITATIONS.md`).
152
+
153
+ Date shown is the tag commit (`v0.1.0.dev0` -> 2a02954). `CITATION.cff` records
154
+ `date-released: 2026-05-25`, when the core implementation landed; the tag was
155
+ later placed on the metadata commit.
156
+
157
+ [Unreleased]: https://github.com/smaniches/GROUPOID/compare/v0.1.0.dev4...HEAD
158
+ [0.1.0.dev4]: https://github.com/smaniches/GROUPOID/compare/v0.1.0.dev2...v0.1.0.dev4
159
+ [0.1.0.dev3]: https://github.com/smaniches/GROUPOID/compare/v0.1.0.dev2...af42cb4
160
+ [0.1.0.dev2]: https://github.com/smaniches/GROUPOID/compare/v0.1.0.dev1...v0.1.0.dev2
161
+ [0.1.0.dev1]: https://github.com/smaniches/GROUPOID/compare/v0.1.0.dev0...v0.1.0.dev1
162
+ [0.1.0.dev0]: https://github.com/smaniches/GROUPOID/releases/tag/v0.1.0.dev0
@@ -2,8 +2,8 @@ cff-version: 1.2.0
2
2
  message: "If you use this software, please cite it as below."
3
3
  type: software
4
4
  title: "GROUPOID: Groupoid-based aggregation for federated learning on Riemannian manifolds"
5
- version: 0.1.0.dev1
6
- date-released: 2026-06-16
5
+ version: 0.1.0.dev4
6
+ date-released: 2026-07-06
7
7
  license: Apache-2.0
8
8
  repository-code: "https://github.com/smaniches/GROUPOID"
9
9
  doi: 10.5281/zenodo.20563974