Skip to content

Commit 79903eb

Browse files
Release 1.1.0: changelog, version bump, and a publish workflow (#75)
precise had no publish workflow, so a GitHub release did nothing. This mirrors humpday: build, check, test-install the wheel, then upload, with trusted publishing over OIDC rather than a stored token. PyPI needs a publisher configured for this repository, the publish.yml workflow and the pypi environment before an upload will be accepted. 1.1.0 rather than 1.0.1: three new estimators is a feature release. No public API was removed, though two behaviours change -- suggest() recommends differently now that the frozen model is not inert, and LedoitWolfCovariance no longer collapses under heavy tails. Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent d3e8d2b commit 79903eb

3 files changed

Lines changed: 103 additions & 2 deletions

File tree

.github/workflows/publish.yml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
name: Publish to PyPI
2+
3+
# Fires when a GitHub release is published. Build and test-install happen before anything is
4+
# uploaded, so a wheel that cannot be installed never reaches PyPI. Authentication is trusted
5+
# publishing (OIDC) rather than a stored token, which is why the publish job needs id-token: write
6+
# and an environment; PyPI must have a publisher configured for this repository, workflow and
7+
# environment name for the upload to be accepted.
8+
on:
9+
release:
10+
types: [published]
11+
workflow_dispatch:
12+
inputs:
13+
test_pypi:
14+
description: 'Deploy to Test PyPI instead of PyPI'
15+
type: boolean
16+
default: false
17+
18+
jobs:
19+
build:
20+
runs-on: ubuntu-latest
21+
steps:
22+
- uses: actions/checkout@v4
23+
- uses: actions/setup-python@v5
24+
with:
25+
python-version: "3.11"
26+
- name: Install build dependencies
27+
run: |
28+
python -m pip install --upgrade pip
29+
pip install build twine
30+
- name: Build package
31+
run: python -m build
32+
- name: Check package
33+
run: twine check dist/*
34+
- uses: actions/upload-artifact@v4
35+
with:
36+
name: dist
37+
path: dist/
38+
39+
test-install:
40+
needs: build
41+
runs-on: ubuntu-latest
42+
steps:
43+
- uses: actions/setup-python@v5
44+
with:
45+
python-version: "3.11"
46+
- uses: actions/download-artifact@v4
47+
with:
48+
name: dist
49+
path: dist/
50+
- name: Test installation from wheel
51+
run: |
52+
pip install dist/*.whl
53+
python -c "import precise; print(f'precise {precise.__version__} installed')"
54+
python -c "from precise import all_estimators; print(f'{len(all_estimators())} estimators')"
55+
56+
publish:
57+
needs: [build, test-install]
58+
runs-on: ubuntu-latest
59+
environment:
60+
name: ${{ github.event.inputs.test_pypi == 'true' && 'test-pypi' || 'pypi' }}
61+
url: ${{ github.event.inputs.test_pypi == 'true' && 'https://test.pypi.org/p/precise' || 'https://pypi.org/p/precise' }}
62+
permissions:
63+
id-token: write # mandatory for trusted publishing
64+
steps:
65+
- uses: actions/download-artifact@v4
66+
with:
67+
name: dist
68+
path: dist/
69+
- name: Publish to Test PyPI
70+
if: github.event.inputs.test_pypi == 'true'
71+
uses: pypa/gh-action-pypi-publish@release/v1
72+
with:
73+
repository-url: https://test.pypi.org/legacy/
74+
- name: Publish to PyPI
75+
if: github.event.inputs.test_pypi != 'true'
76+
uses: pypa/gh-action-pypi-publish@release/v1

CHANGELOG.md

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ All notable changes to `precise` are documented here. The format follows
44
[Keep a Changelog](https://keepachangelog.com/), and the project aims to follow
55
[Semantic Versioning](https://semver.org/).
66

7-
## [Unreleased]
7+
## [1.1.0] — 2026-09-13
88

99
### Added
1010
- `NonlinearShrinkageCovariance`: online analytical **nonlinear** shrinkage of the covariance
@@ -25,6 +25,31 @@ All notable changes to `precise` are documented here. The format follows
2525
distribution is not an equivalence, and Oriol (arXiv:2410.14420) derives the weighted formulas
2626
properly — but it has no window boundary, so it does not pay for a shock twice.
2727
`research/turnover.py` measures the difference.
28+
- A JOSS paper under `papers/joss/`, and the note *Spectral Calibration Without a Split* under
29+
`papers/online_spectral_calibration/`.
30+
31+
### Fixed
32+
- `LedoitWolfCovariance` collapsed to a scaled identity under heavy tails. `pi_bar` averages a
33+
quantity growing like the fourth power of the observation, so one fat-tailed draw could pin the
34+
shrinkage intensity at 1 and leave the estimate with no off-diagonals: at t(3) innovations it
35+
retained 0.088 of the covariance structure where OAS retained 0.543. Each observation's
36+
contribution is now winsorized at ten times the running mean, which leaves Gaussian behaviour
37+
unchanged to three decimals and takes t(3) retention to 0.409.
38+
- The frozen recommender was inert. `sklearn`'s `tree_.value` holds class proportions, and the
39+
exporter cast them with `int()`, flooring every value under 1.0 to zero — 46 of 47 nodes carried
40+
no weight, so `suggest()` had been ranking on the heuristic ruleset alone. The model now covers
41+
19 of 20 estimators (it was 9), and two training runs produce a byte-identical artifact.
42+
- Training was irreproducible whenever `randomcov` was installed: three generative ensembles in
43+
`research/oos.py` ignored the `rng` they were passed and drew from global state.
44+
- The training grid stopped at `n/p = 3`, so every data-rich low-dimensional problem was
45+
extrapolation. It now spans `p` from 5 to 60 and `n/p` from 0.5 to 25.
46+
- A malformed generated model can no longer break `import precise`.
47+
48+
### Changed
49+
- `suggest()`'s safe default moves from `LedoitWolfCovariance` to `NonlinearShrinkageCovariance`,
50+
which has the best mean rank of any single fixed choice over eleven ensembles and seven `(p, n)`
51+
regimes. It is not uniformly best: at `p` close to `n` it ranks 9.41 and the trained model is
52+
worth far more there, which is why the model still leads and this only breaks ties.
2853

2954
## [1.0.0] — 2026-06-05
3055

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
44

55
[project]
66
name = "precise"
7-
version = "1.0.0"
7+
version = "1.1.0"
88
description = "Online (incremental) covariance and correlation estimation — the online complement to sklearn.covariance"
99
readme = "README.md"
1010
requires-python = ">=3.9"

0 commit comments

Comments
 (0)