sonic-spatial 1.0.0rc1__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.
- sonic_spatial-1.0.0rc1/.github/workflows/release.yml +80 -0
- sonic_spatial-1.0.0rc1/.gitignore +80 -0
- sonic_spatial-1.0.0rc1/.readthedocs.yaml +20 -0
- sonic_spatial-1.0.0rc1/CHANGELOG.md +292 -0
- sonic_spatial-1.0.0rc1/LICENSE +29 -0
- sonic_spatial-1.0.0rc1/MANIFEST.in +9 -0
- sonic_spatial-1.0.0rc1/PKG-INFO +321 -0
- sonic_spatial-1.0.0rc1/README.md +264 -0
- sonic_spatial-1.0.0rc1/compat/quadsv/README.md +14 -0
- sonic_spatial-1.0.0rc1/compat/quadsv/pyproject.toml +24 -0
- sonic_spatial-1.0.0rc1/compat/quadsv/src/quadsv/__init__.py +50 -0
- sonic_spatial-1.0.0rc1/docs/Makefile +25 -0
- sonic_spatial-1.0.0rc1/docs/changelog.rst +5 -0
- sonic_spatial-1.0.0rc1/docs/conf.py +158 -0
- sonic_spatial-1.0.0rc1/docs/guides/faq.rst +113 -0
- sonic_spatial-1.0.0rc1/docs/guides/installation.rst +69 -0
- sonic_spatial-1.0.0rc1/docs/guides/kernels.rst +401 -0
- sonic_spatial-1.0.0rc1/docs/guides/multisample.rst +324 -0
- sonic_spatial-1.0.0rc1/docs/guides/quickstart.rst +352 -0
- sonic_spatial-1.0.0rc1/docs/guides/scaling.rst +319 -0
- sonic_spatial-1.0.0rc1/docs/guides/theory.rst +211 -0
- sonic_spatial-1.0.0rc1/docs/index.rst +119 -0
- sonic_spatial-1.0.0rc1/pyproject.toml +182 -0
- sonic_spatial-1.0.0rc1/setup.cfg +4 -0
- sonic_spatial-1.0.0rc1/setup.py +8 -0
- sonic_spatial-1.0.0rc1/src/sonic/__init__.py +73 -0
- sonic_spatial-1.0.0rc1/src/sonic/_rasterize.py +95 -0
- sonic_spatial-1.0.0rc1/src/sonic/_version.py +24 -0
- sonic_spatial-1.0.0rc1/src/sonic/api.py +182 -0
- sonic_spatial-1.0.0rc1/src/sonic/comparators/__init__.py +33 -0
- sonic_spatial-1.0.0rc1/src/sonic/comparators/base.py +1323 -0
- sonic_spatial-1.0.0rc1/src/sonic/comparators/features.py +1250 -0
- sonic_spatial-1.0.0rc1/src/sonic/comparators/grid.py +588 -0
- sonic_spatial-1.0.0rc1/src/sonic/comparators/irregular.py +600 -0
- sonic_spatial-1.0.0rc1/src/sonic/comparators/multisample.py +1669 -0
- sonic_spatial-1.0.0rc1/src/sonic/comparators/normalization.py +375 -0
- sonic_spatial-1.0.0rc1/src/sonic/detectors/__init__.py +18 -0
- sonic_spatial-1.0.0rc1/src/sonic/detectors/base.py +116 -0
- sonic_spatial-1.0.0rc1/src/sonic/detectors/grid.py +680 -0
- sonic_spatial-1.0.0rc1/src/sonic/detectors/irregular.py +1415 -0
- sonic_spatial-1.0.0rc1/src/sonic/kernels/__init__.py +35 -0
- sonic_spatial-1.0.0rc1/src/sonic/kernels/base.py +902 -0
- sonic_spatial-1.0.0rc1/src/sonic/kernels/fft.py +1067 -0
- sonic_spatial-1.0.0rc1/src/sonic/kernels/matrix.py +450 -0
- sonic_spatial-1.0.0rc1/src/sonic/kernels/nufft.py +1482 -0
- sonic_spatial-1.0.0rc1/src/sonic/statistics.py +1369 -0
- sonic_spatial-1.0.0rc1/src/sonic/utils.py +491 -0
- sonic_spatial-1.0.0rc1/src/sonic_spatial.egg-info/PKG-INFO +321 -0
- sonic_spatial-1.0.0rc1/src/sonic_spatial.egg-info/SOURCES.txt +52 -0
- sonic_spatial-1.0.0rc1/src/sonic_spatial.egg-info/dependency_links.txt +1 -0
- sonic_spatial-1.0.0rc1/src/sonic_spatial.egg-info/requires.txt +29 -0
- sonic_spatial-1.0.0rc1/src/sonic_spatial.egg-info/scm_file_list.json +66 -0
- sonic_spatial-1.0.0rc1/src/sonic_spatial.egg-info/scm_version.json +8 -0
- sonic_spatial-1.0.0rc1/src/sonic_spatial.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
release:
|
|
5
|
+
types: [published]
|
|
6
|
+
|
|
7
|
+
permissions:
|
|
8
|
+
contents: read
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
build:
|
|
12
|
+
runs-on: ubuntu-latest
|
|
13
|
+
steps:
|
|
14
|
+
- uses: actions/checkout@v4
|
|
15
|
+
with:
|
|
16
|
+
fetch-depth: 0
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: "3.12"
|
|
20
|
+
- run: python -m pip install build twine
|
|
21
|
+
- run: python -m build
|
|
22
|
+
- run: python -m build compat/quadsv --outdir compat-dist
|
|
23
|
+
- run: python -m twine check dist/* compat-dist/*
|
|
24
|
+
- name: Verify release version and wheel ownership
|
|
25
|
+
env:
|
|
26
|
+
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
|
27
|
+
run: |
|
|
28
|
+
case "$RELEASE_TAG" in
|
|
29
|
+
v*) version="${RELEASE_TAG#v}" ;;
|
|
30
|
+
*) echo "Release tag must start with v"; exit 1 ;;
|
|
31
|
+
esac
|
|
32
|
+
sonic_wheel="dist/sonic_spatial-${version}-py3-none-any.whl"
|
|
33
|
+
quadsv_wheel="compat-dist/quadsv-${version}-py3-none-any.whl"
|
|
34
|
+
test -f "$sonic_wheel"
|
|
35
|
+
test -f "$quadsv_wheel"
|
|
36
|
+
unzip -Z1 "$sonic_wheel" | grep -qx 'sonic/__init__.py'
|
|
37
|
+
! unzip -Z1 "$sonic_wheel" | grep -q '^quadsv/'
|
|
38
|
+
unzip -Z1 "$quadsv_wheel" | grep -qx 'quadsv/__init__.py'
|
|
39
|
+
unzip -p "$quadsv_wheel" 'quadsv-*.dist-info/METADATA' |
|
|
40
|
+
grep -Eq '^Requires-Dist: sonic-spatial(<2,>=1\.0\.0rc1|>=1\.0\.0rc1,<2)$'
|
|
41
|
+
- uses: actions/upload-artifact@v4
|
|
42
|
+
with:
|
|
43
|
+
name: sonic-dist
|
|
44
|
+
path: dist/
|
|
45
|
+
- uses: actions/upload-artifact@v4
|
|
46
|
+
with:
|
|
47
|
+
name: quadsv-dist
|
|
48
|
+
path: compat-dist/
|
|
49
|
+
|
|
50
|
+
publish-sonic:
|
|
51
|
+
needs: build
|
|
52
|
+
runs-on: ubuntu-latest
|
|
53
|
+
environment: pypi
|
|
54
|
+
permissions:
|
|
55
|
+
id-token: write
|
|
56
|
+
steps:
|
|
57
|
+
- uses: actions/download-artifact@v4
|
|
58
|
+
with:
|
|
59
|
+
name: sonic-dist
|
|
60
|
+
path: dist/
|
|
61
|
+
- name: Publish SONIC
|
|
62
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
63
|
+
with:
|
|
64
|
+
packages-dir: dist
|
|
65
|
+
|
|
66
|
+
publish-quadsv:
|
|
67
|
+
needs: [build, publish-sonic]
|
|
68
|
+
runs-on: ubuntu-latest
|
|
69
|
+
environment: pypi
|
|
70
|
+
permissions:
|
|
71
|
+
id-token: write
|
|
72
|
+
steps:
|
|
73
|
+
- uses: actions/download-artifact@v4
|
|
74
|
+
with:
|
|
75
|
+
name: quadsv-dist
|
|
76
|
+
path: compat-dist/
|
|
77
|
+
- name: Publish QuadSV compatibility package
|
|
78
|
+
uses: pypa/gh-action-pypi-publish@release/v1
|
|
79
|
+
with:
|
|
80
|
+
packages-dir: compat-dist
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
lib/
|
|
14
|
+
lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
pip-wheel-metadata/
|
|
20
|
+
share/python-wheels/
|
|
21
|
+
*.egg-info/
|
|
22
|
+
.installed.cfg
|
|
23
|
+
*.egg
|
|
24
|
+
MANIFEST
|
|
25
|
+
|
|
26
|
+
# Virtual environments
|
|
27
|
+
venv/
|
|
28
|
+
ENV/
|
|
29
|
+
env/
|
|
30
|
+
.venv
|
|
31
|
+
|
|
32
|
+
# Jupyter Notebook
|
|
33
|
+
.ipynb_checkpoints
|
|
34
|
+
|
|
35
|
+
# PyCharm
|
|
36
|
+
.idea/
|
|
37
|
+
|
|
38
|
+
# VSCode
|
|
39
|
+
.vscode/
|
|
40
|
+
|
|
41
|
+
# Testing
|
|
42
|
+
.pytest_cache/
|
|
43
|
+
.coverage
|
|
44
|
+
htmlcov/
|
|
45
|
+
.tox/
|
|
46
|
+
.nox/
|
|
47
|
+
.hypothesis/
|
|
48
|
+
|
|
49
|
+
# Data + analysis outputs (project-root-anchored so nested packages aren't ignored)
|
|
50
|
+
/data/
|
|
51
|
+
/results/
|
|
52
|
+
*.h5ad
|
|
53
|
+
*.csv
|
|
54
|
+
figures/
|
|
55
|
+
*.png
|
|
56
|
+
*.jpg
|
|
57
|
+
*.pdf
|
|
58
|
+
|
|
59
|
+
# OS
|
|
60
|
+
.DS_Store
|
|
61
|
+
Thumbs.db
|
|
62
|
+
|
|
63
|
+
# Misc
|
|
64
|
+
*.log
|
|
65
|
+
.cache/
|
|
66
|
+
|
|
67
|
+
# copilot
|
|
68
|
+
.github/copilot-instructions.md
|
|
69
|
+
|
|
70
|
+
# Sphinx documentation build
|
|
71
|
+
docs/_build/
|
|
72
|
+
docs/_static/
|
|
73
|
+
docs/_templates/
|
|
74
|
+
|
|
75
|
+
# setuptools-scm generated version file (see [tool.setuptools_scm] in pyproject.toml)
|
|
76
|
+
src/sonic/_version.py
|
|
77
|
+
|
|
78
|
+
# claude code
|
|
79
|
+
CLAUDE.md
|
|
80
|
+
.claude/
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Read the Docs configuration file.
|
|
2
|
+
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details.
|
|
3
|
+
|
|
4
|
+
version: 2
|
|
5
|
+
|
|
6
|
+
build:
|
|
7
|
+
os: ubuntu-24.04
|
|
8
|
+
tools:
|
|
9
|
+
python: "3.12"
|
|
10
|
+
|
|
11
|
+
sphinx:
|
|
12
|
+
configuration: docs/conf.py
|
|
13
|
+
fail_on_warning: true
|
|
14
|
+
|
|
15
|
+
python:
|
|
16
|
+
install:
|
|
17
|
+
- method: pip
|
|
18
|
+
path: .
|
|
19
|
+
extra_requirements:
|
|
20
|
+
- docs
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [1.0.0rc1] - 2026-08-29
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
- **QuadSV is now SONIC** (Spatial Organization through Nonrandom-pattern
|
|
14
|
+
Inference and Comparison). The canonical distribution is `sonic-spatial`
|
|
15
|
+
and the import package is `sonic`; the `quadsv` distribution and import
|
|
16
|
+
namespace remain as a compatibility bridge for existing users.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- **GLM design API for cross-sample pattern comparison.** New public
|
|
20
|
+
`compare_glm(spectra, design, contrast, …)` generalises the
|
|
21
|
+
two-group test to arbitrary OLS designs (binary, continuous,
|
|
22
|
+
multi-factor) with an analytic Wald test. The two-group case is
|
|
23
|
+
recovered exactly. `Comparator.test_diff_freq(...)` gains a
|
|
24
|
+
`contrast=` argument (column name, dict, or contrast vector).
|
|
25
|
+
- **Analytic null for `log_l2`** (`null="analytic"`) on
|
|
26
|
+
`compare_two_groups`, `compare_two_groups_masked`, and
|
|
27
|
+
`compare_glm`. Per-gene statistic is integrated via Liu's
|
|
28
|
+
approximation against a pooled-across-genes **full** within-group
|
|
29
|
+
Σ (a single 30×30 eigendecomposition before each Liu integration);
|
|
30
|
+
bypasses the small-n permutation BH-floor while keeping mean
|
|
31
|
+
within-group null FPR at ~0.012 across the three benchmark panels.
|
|
32
|
+
Emits a `UserWarning` at residual df < 3. The masked variant uses
|
|
33
|
+
a mask-aware pooled estimator with per-gene noncentrality scaling
|
|
34
|
+
so genes with different observed cohorts get correctly-scaled
|
|
35
|
+
eigenvalues.
|
|
36
|
+
- **Analytic Welch t tests for `compare_two_groups_scalar`**.
|
|
37
|
+
Computes per-gene two-sided p-values from the
|
|
38
|
+
Welch-Satterthwaite t-distribution; the scalar DE companion uses
|
|
39
|
+
this fixed analytic null rather than exposing a `null=` selector.
|
|
40
|
+
- **`normalize_shape: bool = False` keyword** on every spectrum-input
|
|
41
|
+
comparison test (`compare_two_groups`, `compare_two_groups_masked`,
|
|
42
|
+
`compare_glm`). When True, divides each per-(sample, gene)
|
|
43
|
+
spectrum by its sum along the frequency axis before the statistic
|
|
44
|
+
is computed, so the test fires only on shape-only redistribution
|
|
45
|
+
of power across radial frequencies. Statistic-agnostic; default
|
|
46
|
+
False preserves prior behaviour.
|
|
47
|
+
- **Comparator null-covariance and effective-rank diagnostics**:
|
|
48
|
+
`Comparator.effective_rank(weights=None)` for per-sample heterogeneity
|
|
49
|
+
in the gene spectrum cross-frequency covariance;
|
|
50
|
+
`Comparator.estimate_null_covariance(design, contrast=...)`for the
|
|
51
|
+
pooled log-spectrum covariance, weighted covariance, scaled Liu
|
|
52
|
+
eigenvalues, effective rank, residual df, and masked-path eligibility
|
|
53
|
+
metadata used by `test_diff_freq(..., statistic="log_l2", null="analytic")`.
|
|
54
|
+
- **Top-level convenience exports**:
|
|
55
|
+
`sonic.Detector(data, …)` and `sonic.Comparator(data_list, …)`
|
|
56
|
+
factories that dispatch on `AnnData` vs `SpatialData`;
|
|
57
|
+
`sonic.compute_null_params`, `sonic.auto_chunk_size`,
|
|
58
|
+
`sonic.liu_sf` promoted to top level (canonical
|
|
59
|
+
`sonic.statistics` paths still work).
|
|
60
|
+
- **Public-API freeze test** (`tests/test_public_api.py`) snapshots
|
|
61
|
+
`__all__`, docstring presence, canonical-path identity, and
|
|
62
|
+
asserts removed legacy paths raise `ModuleNotFoundError`.
|
|
63
|
+
- **Convenience input modes for `Comparator.normalize_covariates`.**
|
|
64
|
+
In addition to the existing per-sample
|
|
65
|
+
`Sequence[np.ndarray]` of pre-rasterized
|
|
66
|
+
`(n_covariates, ny, nx)` images, the method now accepts a shared
|
|
67
|
+
`Sequence[str]` of column names; the subclass interprets it
|
|
68
|
+
natively:
|
|
69
|
+
|
|
70
|
+
* `ComparatorIrregular` looks each key up in `adata.obs.columns`
|
|
71
|
+
first, then `adata.var_names` (preferring obs on collision);
|
|
72
|
+
the resolved per-spot vector is NUFFTed directly onto the
|
|
73
|
+
sample's k-grid — so the same call accepts deconvolved
|
|
74
|
+
cell-type proportion columns *and* per-gene expression columns
|
|
75
|
+
(e.g., a housekeeping or marker gene) interchangeably.
|
|
76
|
+
* `ComparatorGrid` forwards the keys as `value_key=` to
|
|
77
|
+
`spatialdata.rasterize_bins`, so any combination of `.obs`
|
|
78
|
+
columns and `var_names` in the comparator's table works.
|
|
79
|
+
|
|
80
|
+
Mode is detected from the first element's type. Both paths reduce
|
|
81
|
+
to the same `(n_covariates, K)` per-sample features fed into the
|
|
82
|
+
log-space residualization, so the math is identical — only the
|
|
83
|
+
input boilerplate is different.
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
- **Breaking: package layout migrated to `src/sonic/`** with the
|
|
87
|
+
four conceptual layers as physical subpackages —
|
|
88
|
+
`sonic.kernels.{fft,nufft}`,
|
|
89
|
+
`sonic.detectors.{base,irregular,grid}`,
|
|
90
|
+
`sonic.comparators.{__init__,multisample}`. `import sonic` and
|
|
91
|
+
`from sonic import …` keep working; editable installs must be
|
|
92
|
+
reissued (`pip install -e ".[dev]"`). Lint / format commands now
|
|
93
|
+
target `src/ tests/`.
|
|
94
|
+
- **Breaking: unified `normalize_*` surface API in
|
|
95
|
+
`sonic.comparators.multisample`** (no aliases):
|
|
96
|
+
* `normalize_by_background` → `normalize_background`
|
|
97
|
+
* `residualize_against_covariates` → `normalize_covariates`
|
|
98
|
+
* `shape_normalize` → `normalize_shape`
|
|
99
|
+
Consistent first-arg `spectra`, keyword-only after, `eps=1e-12`
|
|
100
|
+
default on every helper, and NumPy-style docstrings with LaTeX
|
|
101
|
+
math. `normalize_covariates`'s first positional arg is renamed
|
|
102
|
+
`gene_spectra` → `spectra`, and its implementation now operates in
|
|
103
|
+
**log-space**: it residualises `log(spectra + ε)` against
|
|
104
|
+
`[1, log(C^T + ε)]` and exponentiates, so the output stays strictly
|
|
105
|
+
positive and composes cleanly with downstream `log_l2` tests.
|
|
106
|
+
Log-space `normalize_covariates` also **commutes exactly** with
|
|
107
|
+
`normalize_background` (left- vs right-multiplication of the
|
|
108
|
+
log-spectrum matrix by orthogonal-projection matrices on disjoint
|
|
109
|
+
axes), so the two can be applied in either order. The remaining
|
|
110
|
+
chainable comparator instance method follows the rename:
|
|
111
|
+
* `.residualize()` → `.normalize_covariates()`
|
|
112
|
+
- **Breaking: `Comparator.fit()` renamed to `Comparator.compute_spectra()`**.
|
|
113
|
+
The method computes per-sample radial-binned power spectra rather than
|
|
114
|
+
fitting model parameters; the new name describes the operation
|
|
115
|
+
directly and matches the codebase's verb-first method convention. All
|
|
116
|
+
three keyword arguments (`n_jobs`, `landmark_genes`, `progress`) and
|
|
117
|
+
the chainable `return self` behaviour are unchanged.
|
|
118
|
+
- **Breaking: `design` moved from Comparator constructor to test
|
|
119
|
+
time.** The cross-sample contrast is no longer a construction
|
|
120
|
+
argument — it is supplied directly to `.test_diff_freq(design, ...)`
|
|
121
|
+
/ `.test_diff_expr(design, ...)` (positional first arg). A single fitted comparator can now
|
|
122
|
+
serve any number of unrelated contrasts on the same `spectra_`
|
|
123
|
+
without recomputing per-sample spectra. `min_samples_per_group`
|
|
124
|
+
follows `design` to `test_diff_freq` (kwarg) since it's a property
|
|
125
|
+
of the design's group sizes, not of the spectra. `design` accepts
|
|
126
|
+
the same three forms as before:
|
|
127
|
+
* 1-D array / Series of binary labels → two-sample dispatch
|
|
128
|
+
(`compare_two_groups` / `compare_two_groups_masked`);
|
|
129
|
+
* 2-D `np.ndarray` of shape `(n_samples, p)` → GLM design matrix,
|
|
130
|
+
used verbatim by `compare_glm`;
|
|
131
|
+
* `pandas.DataFrame` → GLM design, patsy-encoded by
|
|
132
|
+
`compare_glm`.
|
|
133
|
+
- **Breaking: default `null` switched from `"permutation"` to
|
|
134
|
+
`"analytic"` across the spectral comparison surface** —
|
|
135
|
+
`Comparator.test_diff_freq`,
|
|
136
|
+
`sonic.comparators.multisample.compare_two_groups`, and
|
|
137
|
+
`sonic.comparators.multisample.compare_two_groups_masked`. The
|
|
138
|
+
analytic Wald test (Liu mixture-χ² null) bypasses the small-n permutation
|
|
139
|
+
BH-floor and is the only path that works on every dispatch target
|
|
140
|
+
(binary permutation/analytic + GLM analytic), so it makes a single sensible
|
|
141
|
+
package-wide default. Callers who want the permutation null must
|
|
142
|
+
now pass `null="permutation"` explicitly. As a related
|
|
143
|
+
ergonomic fix, `compare_two_groups{,_masked}(statistic="welch_t_cauchy",
|
|
144
|
+
null="analytic")` no longer raises — `welch_t_cauchy` carries its own
|
|
145
|
+
analytic null (documented as ignoring the `null` kwarg) so the
|
|
146
|
+
package default `null="analytic"` is treated as a no-op for that
|
|
147
|
+
statistic.
|
|
148
|
+
- **Breaking: statistical-test naming cleanup** in
|
|
149
|
+
`sonic.comparators.multisample` and the corresponding
|
|
150
|
+
`Comparator.test_diff_*` methods:
|
|
151
|
+
* **`compare_designs` → `compare_glm`.** The plural form was
|
|
152
|
+
awkward (one design per call); `compare_glm` names the test
|
|
153
|
+
family at the call site and parallels the binary
|
|
154
|
+
`compare_two_groups` cleanly.
|
|
155
|
+
* **Statistic `"cauchy_welch"` → `"welch_t_cauchy"`.** The new
|
|
156
|
+
token reads in pipeline order (per-bin Welch t first, gene-level
|
|
157
|
+
Cauchy combination second) and disambiguates from naming the
|
|
158
|
+
gene-level aggregator alone.
|
|
159
|
+
* **Scalar DE `null=` selector retired** on
|
|
160
|
+
`compare_two_groups_scalar` / `Comparator.test_diff_expr`.
|
|
161
|
+
Scalar DE now always uses the analytic Welch-Satterthwaite
|
|
162
|
+
t-distribution null.
|
|
163
|
+
* **`null="liu"` alias retired.** The `liu` token referred to
|
|
164
|
+
the numerical algorithm used to integrate the analytic χ² mixture
|
|
165
|
+
tail (see `sonic.statistics.liu_sf`), not a separate
|
|
166
|
+
statistical concept. Single canonical token: `analytic`.
|
|
167
|
+
- **Breaking: Comparator attribute surface narrowed** (sklearn-style
|
|
168
|
+
moderate-privacy convention). The public surface is now `samples`,
|
|
169
|
+
`gene_names`, `feature_mode`, `freq_edges`, plus the
|
|
170
|
+
trailing-underscore fitted attributes (`spectra_`, `dc_`,
|
|
171
|
+
`presence_`, `rotation_angles_`). `design`/`groups_` are no longer
|
|
172
|
+
carried as instance state — the comparator is design-agnostic.
|
|
173
|
+
Internal config knobs that were inadvertently public are now
|
|
174
|
+
single-underscore-prefixed: `_n_radial_bins`, `_fft_solver`,
|
|
175
|
+
`_workers`, `_presence_threshold`, `_spacings`, `_grid_shapes`,
|
|
176
|
+
`_spectrum_fft_solver`, `_fft_chunk_size`, `_spacing_override`,
|
|
177
|
+
`_bins`, `_table_name`, `_col_key`, `_row_key`, `_value_key`.
|
|
178
|
+
- **Breaking: Comparator test methods renamed and aligned with the
|
|
179
|
+
standalone `compare_*` API** in `sonic.comparators.multisample`:
|
|
180
|
+
* `.test_pattern()` → `.test_diff_freq()` — gains a new
|
|
181
|
+
`normalize_shape: bool = False` keyword, forwarded to its
|
|
182
|
+
dispatch target (`compare_two_groups`,
|
|
183
|
+
`compare_two_groups_masked`, or `compare_glm`) so users get
|
|
184
|
+
the shape-only DF path without mutating `cmp.spectra_`.
|
|
185
|
+
* `.test_expression()` → `.test_diff_expr()` — uses analytic
|
|
186
|
+
t-distribution tests for scalar DE and supports both binary
|
|
187
|
+
two-group and GLM contrast designs.
|
|
188
|
+
|
|
189
|
+
### Removed
|
|
190
|
+
- **Breaking: `groups=` / `design=` constructor kwargs on
|
|
191
|
+
`ComparatorIrregular` and `ComparatorGrid` are gone.** Supply the
|
|
192
|
+
1-D labels or design matrix to the test method instead
|
|
193
|
+
(`cmp.test_diff_freq(design, ...)`, `cmp.test_diff_expr(design,
|
|
194
|
+
...)`). The comparator no longer carries design state; one fitted
|
|
195
|
+
comparator can serve any number of contrasts on the same spectra.
|
|
196
|
+
- **Breaking: `Comparator.shape_normalize()` chainable method
|
|
197
|
+
retired.** Use the equivalent
|
|
198
|
+
`cmp.test_diff_freq(..., normalize_shape=True)` keyword path for the
|
|
199
|
+
one-shot non-destructive test, or call
|
|
200
|
+
`sonic.comparators.multisample.normalize_shape(cmp.spectra_)`
|
|
201
|
+
directly to obtain the standalone transform. The previous in-place
|
|
202
|
+
method silently mutated `cmp.spectra_` and surprised subsequent
|
|
203
|
+
`.test_diff_freq()`/`.test_diff_expr()` calls on the same comparator.
|
|
204
|
+
- **Breaking: the `test = test_pattern` alias retired.** Use the
|
|
205
|
+
explicit `cmp.test_diff_freq(...)` (or `cmp.test_diff_expr(...)` for
|
|
206
|
+
the DE companion); the unqualified `cmp.test()` was ambiguous once
|
|
207
|
+
the API exposed two complementary tests.
|
|
208
|
+
- **Breaking: `center` argument retired** across the comparator API.
|
|
209
|
+
`ComparatorIrregular`, `ComparatorGrid`, and
|
|
210
|
+
`compute_sample_spectrum` no longer accept `center`. Per-gene
|
|
211
|
+
mean centring (the previous default) is now the only spectrum
|
|
212
|
+
normalisation path. The `_ZSCORE_CLIP` constant, the
|
|
213
|
+
`zscore_clip` parameter, and the per-bin clamp in the NUFFT loop
|
|
214
|
+
are deleted (~50 LOC).
|
|
215
|
+
- **Breaking: `benchmark_statistics` function and the matching
|
|
216
|
+
`Comparator.benchmark()` method retired.** Invoke
|
|
217
|
+
`compare_two_groups` directly with each `statistic=` value to A/B
|
|
218
|
+
compare on the same fitted spectra (~95 LOC).
|
|
219
|
+
- **Breaking: `statistic="hotelling_lw"` and `statistic="mmd_rbf"`
|
|
220
|
+
paths retired** from every comparison function. Both were
|
|
221
|
+
impractically slow and consistently dominated on sensitivity by
|
|
222
|
+
`log_l2 + null='analytic'` or `welch_t_cauchy`. `_AVAILABLE_STATISTICS`
|
|
223
|
+
now reads `("log_l2", "welch_t_cauchy")`.
|
|
224
|
+
- **Breaking: six legacy-path shim modules removed** —
|
|
225
|
+
`sonic.fft`, `sonic.nufft`, `sonic.detector`,
|
|
226
|
+
`sonic.detector_grid`, `sonic._detector_base`,
|
|
227
|
+
`sonic.multisample`. Use the canonical subpackage paths.
|
|
228
|
+
- **Breaking: backend ABCs `Kernel` and `MatrixKernelBase` no
|
|
229
|
+
longer re-exported from top-level `sonic`**. They live at
|
|
230
|
+
`sonic.kernels` and are intended for backend authors.
|
|
231
|
+
|
|
232
|
+
### Fixed
|
|
233
|
+
- CI workflow install step referenced non-existent extras
|
|
234
|
+
(`[dev,test,spatial]` and `[docs,spatial]`); narrowed to the
|
|
235
|
+
actual `[dev]` / `[docs]` extras in `pyproject.toml`.
|
|
236
|
+
|
|
237
|
+
## Release Process
|
|
238
|
+
|
|
239
|
+
The immediate release is the real prerelease `1.0.0rc1`. The thorough
|
|
240
|
+
documentation rewrite remains deferred to the final `1.0.0`; until the RC is
|
|
241
|
+
tagged, keep its changes under `Unreleased`.
|
|
242
|
+
|
|
243
|
+
- [x] Review the current installation, migration, and release notes for
|
|
244
|
+
accuracy; the full documentation rewrite is not an RC blocker.
|
|
245
|
+
- [x] Run the full test suite: `pytest tests/ --cov=sonic`.
|
|
246
|
+
- [x] Run lint checks: `ruff check src tests compat/quadsv/src`.
|
|
247
|
+
- [x] Build the documentation without warnings:
|
|
248
|
+
`sphinx-build -W -b html docs/ docs/_build/`.
|
|
249
|
+
- [x] Add the pending `sonic-spatial` publisher with owner `JiayuSuPKU`,
|
|
250
|
+
repository `sonic`, workflow `release.yml`, and environment `pypi`.
|
|
251
|
+
- [x] Add the additional `quadsv` publisher with the same identity. Keep the
|
|
252
|
+
existing QuadSV credential until this release succeeds.
|
|
253
|
+
- [x] Update the compatibility requirement to
|
|
254
|
+
`sonic-spatial>=1.0.0rc1,<2` and make
|
|
255
|
+
the compatibility wheel, rather than the SONIC wheel, own the deprecated
|
|
256
|
+
`quadsv` import namespace.
|
|
257
|
+
- [x] Confirm the new Read the Docs project builds successfully and legacy
|
|
258
|
+
documentation links remain available or redirect.
|
|
259
|
+
- [x] Add a dated `1.0.0rc1` section below `Unreleased`, leaving a new empty
|
|
260
|
+
`Unreleased` section for future changes.
|
|
261
|
+
- [ ] Commit the release preparation and create an annotated tag:
|
|
262
|
+
`git tag -a v1.0.0rc1 -m "SONIC 1.0.0rc1"`.
|
|
263
|
+
- [x] Build both distributions: `python -m build` and
|
|
264
|
+
`python -m build compat/quadsv --outdir compat-dist`.
|
|
265
|
+
- [x] Verify their metadata:
|
|
266
|
+
`python -m twine check dist/* compat-dist/*`.
|
|
267
|
+
- [ ] Push `v1.0.0rc1`, then publish its GitHub prerelease to trigger trusted
|
|
268
|
+
publishing of SONIC followed by the QuadSV compatibility package.
|
|
269
|
+
- [ ] Verify clean installations of `sonic-spatial==1.0.0rc1` and
|
|
270
|
+
`quadsv==1.0.0rc1` from PyPI, then remove obsolete QuadSV credentials.
|
|
271
|
+
|
|
272
|
+
## [0.1.0] - 2026-02-02
|
|
273
|
+
|
|
274
|
+
### Added
|
|
275
|
+
- Initial public release
|
|
276
|
+
- Q-test framework for univariate spatial pattern detection
|
|
277
|
+
- R-test framework for bivariate spatial co-expression
|
|
278
|
+
- Core kernel methods: Gaussian, Matérn, CAR, Graph Laplacian, Moran's I
|
|
279
|
+
- Implicit mode for scalable large-N computation (N > 5000)
|
|
280
|
+
- FFT acceleration for regular grid data (Visium HD)
|
|
281
|
+
- `DetectorIrregular` for AnnData integration (genome-wide SVG detection)
|
|
282
|
+
- `DetectorGrid` for large-scale Visium HD analysis
|
|
283
|
+
- Null approximation methods: CLT, Welch/Satterthwaite, Liu
|
|
284
|
+
- Comprehensive test suite (unit + integration tests)
|
|
285
|
+
- Tutorial test cases demonstrating all major workflows
|
|
286
|
+
- Complete documentation with quickstart and theory sections
|
|
287
|
+
- Support for Python 3.10, 3.11, 3.12
|
|
288
|
+
|
|
289
|
+
## [0.1.1]
|
|
290
|
+
|
|
291
|
+
### Fixed
|
|
292
|
+
- Fix type hinting issues in `quadsv.kernels` module
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Jiayu Su
|
|
4
|
+
All rights reserved.
|
|
5
|
+
|
|
6
|
+
Redistribution and use in source and binary forms, with or without
|
|
7
|
+
modification, are permitted provided that the following conditions are met:
|
|
8
|
+
|
|
9
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
10
|
+
list of conditions and the following disclaimer.
|
|
11
|
+
|
|
12
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
13
|
+
this list of conditions and the following disclaimer in the documentation
|
|
14
|
+
and/or other materials provided with the distribution.
|
|
15
|
+
|
|
16
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
17
|
+
contributors may be used to endorse or promote products derived from
|
|
18
|
+
this software without specific prior written permission.
|
|
19
|
+
|
|
20
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
21
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
22
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
23
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
24
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
25
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
26
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
27
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
28
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
29
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|