hmm-craft 0.4.0__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.
- hmm_craft-0.4.0/.github/ISSUE_TEMPLATE/bug_report.yml +43 -0
- hmm_craft-0.4.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
- hmm_craft-0.4.0/.github/ISSUE_TEMPLATE/feature_request.yml +22 -0
- hmm_craft-0.4.0/.github/PULL_REQUEST_TEMPLATE.md +19 -0
- hmm_craft-0.4.0/.github/dependabot.yml +14 -0
- hmm_craft-0.4.0/.github/workflows/ci.yml +80 -0
- hmm_craft-0.4.0/.github/workflows/release.yml +55 -0
- hmm_craft-0.4.0/.github/workflows/wheels.yml +129 -0
- hmm_craft-0.4.0/.gitignore +20 -0
- hmm_craft-0.4.0/ARCHITECTURE.md +170 -0
- hmm_craft-0.4.0/AUTHORS.rst +32 -0
- hmm_craft-0.4.0/CHANGES.rst +215 -0
- hmm_craft-0.4.0/CITATION.cff +34 -0
- hmm_craft-0.4.0/CODE_OF_CONDUCT.md +83 -0
- hmm_craft-0.4.0/CONTRIBUTING.md +74 -0
- hmm_craft-0.4.0/DIFFERENCES.md +121 -0
- hmm_craft-0.4.0/LICENSE.txt +28 -0
- hmm_craft-0.4.0/PKG-INFO +164 -0
- hmm_craft-0.4.0/README.rst +123 -0
- hmm_craft-0.4.0/RELEASING.md +99 -0
- hmm_craft-0.4.0/SECURITY.md +12 -0
- hmm_craft-0.4.0/ci/build_requirements.in +3 -0
- hmm_craft-0.4.0/ci/build_requirements.txt +18 -0
- hmm_craft-0.4.0/ci/floor_constraints.txt +14 -0
- hmm_craft-0.4.0/ci/tool_requirements.in +1 -0
- hmm_craft-0.4.0/ci/tool_requirements.txt +15 -0
- hmm_craft-0.4.0/doc/.gitignore +2 -0
- hmm_craft-0.4.0/doc/Makefile +19 -0
- hmm_craft-0.4.0/doc/make.bat +35 -0
- hmm_craft-0.4.0/doc/source/GMMHMM.rst +296 -0
- hmm_craft-0.4.0/doc/source/_static/hide_some_gallery_elements.css +3 -0
- hmm_craft-0.4.0/doc/source/api.rst +90 -0
- hmm_craft-0.4.0/doc/source/changelog.rst +1 -0
- hmm_craft-0.4.0/doc/source/conf.py +53 -0
- hmm_craft-0.4.0/doc/source/index.rst +22 -0
- hmm_craft-0.4.0/doc/source/tutorial.rst +243 -0
- hmm_craft-0.4.0/examples/README.txt +4 -0
- hmm_craft-0.4.0/examples/plot_casino.py +145 -0
- hmm_craft-0.4.0/examples/plot_gaussian_model_selection.py +78 -0
- hmm_craft-0.4.0/examples/plot_hmm_sampling_and_decoding.py +117 -0
- hmm_craft-0.4.0/examples/plot_multinomial_hmm.py +129 -0
- hmm_craft-0.4.0/examples/plot_poisson_hmm.py +111 -0
- hmm_craft-0.4.0/examples/plot_variational_inference.py +185 -0
- hmm_craft-0.4.0/ext/_hmmc.cpp +356 -0
- hmm_craft-0.4.0/pyproject.toml +92 -0
- hmm_craft-0.4.0/scripts/benchmark.py +298 -0
- hmm_craft-0.4.0/scripts/bit_equality.py +1156 -0
- hmm_craft-0.4.0/scripts/check_signoff.py +90 -0
- hmm_craft-0.4.0/scripts/ci_bit_equality.sh +109 -0
- hmm_craft-0.4.0/scripts/probe_covariance_only_fit.py +198 -0
- hmm_craft-0.4.0/scripts/test_bit_equality.py +547 -0
- hmm_craft-0.4.0/scripts/test_check_signoff.py +219 -0
- hmm_craft-0.4.0/scripts/test_floor_constraints.py +86 -0
- hmm_craft-0.4.0/setup.cfg +4 -0
- hmm_craft-0.4.0/setup.py +9 -0
- hmm_craft-0.4.0/src/hmm_craft.egg-info/PKG-INFO +164 -0
- hmm_craft-0.4.0/src/hmm_craft.egg-info/SOURCES.txt +88 -0
- hmm_craft-0.4.0/src/hmm_craft.egg-info/dependency_links.txt +1 -0
- hmm_craft-0.4.0/src/hmm_craft.egg-info/requires.txt +12 -0
- hmm_craft-0.4.0/src/hmm_craft.egg-info/scm_file_list.json +85 -0
- hmm_craft-0.4.0/src/hmm_craft.egg-info/scm_version.json +8 -0
- hmm_craft-0.4.0/src/hmm_craft.egg-info/top_level.txt +1 -0
- hmm_craft-0.4.0/src/hmmcraft/__init__.py +15 -0
- hmm_craft-0.4.0/src/hmmcraft/_emissions.py +411 -0
- hmm_craft-0.4.0/src/hmmcraft/_kl_divergence.py +117 -0
- hmm_craft-0.4.0/src/hmmcraft/_utils.py +90 -0
- hmm_craft-0.4.0/src/hmmcraft/base.py +1283 -0
- hmm_craft-0.4.0/src/hmmcraft/hmm.py +1170 -0
- hmm_craft-0.4.0/src/hmmcraft/stats.py +98 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/__init__.py +96 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/conftest.py +12 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_base.py +395 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_categorical_hmm.py +167 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_deprecated_numpy_use.py +129 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_gaussian_hmm.py +419 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_gaussian_hmm_min_covar.py +185 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_gaussian_hmm_spherical_shape.py +47 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_gmm_hmm.py +111 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_gmm_hmm_multisequence.py +286 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_gmm_hmm_new.py +265 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_hmmc.py +76 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_kl_divergence.py +55 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_multinomial_hmm.py +181 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_poisson_hmm.py +101 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_sequence_lengths.py +96 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_utils.py +110 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_variational_categorical.py +255 -0
- hmm_craft-0.4.0/src/hmmcraft/tests/test_variational_gaussian.py +507 -0
- hmm_craft-0.4.0/src/hmmcraft/utils.py +83 -0
- hmm_craft-0.4.0/src/hmmcraft/vhmm.py +852 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something gives a wrong result, raises, or crashes.
|
|
3
|
+
body:
|
|
4
|
+
- type: markdown
|
|
5
|
+
attributes:
|
|
6
|
+
value: |
|
|
7
|
+
This fork has one maintainer and no response time. A report with a
|
|
8
|
+
short example that runs as posted is looked at first. Questions about
|
|
9
|
+
using hidden Markov models are not answered here.
|
|
10
|
+
- type: textarea
|
|
11
|
+
id: example
|
|
12
|
+
attributes:
|
|
13
|
+
label: Example
|
|
14
|
+
description: >
|
|
15
|
+
A short, self-contained example that fails when pasted into a new empty
|
|
16
|
+
file. Generate the data with a seeded random number generator inside
|
|
17
|
+
the example; do not attach data files.
|
|
18
|
+
render: python
|
|
19
|
+
validations:
|
|
20
|
+
required: true
|
|
21
|
+
- type: textarea
|
|
22
|
+
id: observed
|
|
23
|
+
attributes:
|
|
24
|
+
label: What happens, and what you expected
|
|
25
|
+
validations:
|
|
26
|
+
required: true
|
|
27
|
+
- type: textarea
|
|
28
|
+
id: versions
|
|
29
|
+
attributes:
|
|
30
|
+
label: Versions
|
|
31
|
+
description: hmm-craft, Python, NumPy, SciPy, scikit-learn, and the operating system.
|
|
32
|
+
validations:
|
|
33
|
+
required: true
|
|
34
|
+
- type: dropdown
|
|
35
|
+
id: upstream
|
|
36
|
+
attributes:
|
|
37
|
+
label: Does the same example also fail on hmmlearn 0.3.3?
|
|
38
|
+
options:
|
|
39
|
+
- "Yes"
|
|
40
|
+
- "No"
|
|
41
|
+
- Not tried
|
|
42
|
+
validations:
|
|
43
|
+
required: true
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
blank_issues_enabled: false
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Ask for something hmm-craft does not do.
|
|
3
|
+
body:
|
|
4
|
+
- type: markdown
|
|
5
|
+
attributes:
|
|
6
|
+
value: |
|
|
7
|
+
A feature request without a pull request may be closed.
|
|
8
|
+
- type: textarea
|
|
9
|
+
id: request
|
|
10
|
+
attributes:
|
|
11
|
+
label: What you want, and why
|
|
12
|
+
validations:
|
|
13
|
+
required: true
|
|
14
|
+
- type: dropdown
|
|
15
|
+
id: pr
|
|
16
|
+
attributes:
|
|
17
|
+
label: Will you send a pull request?
|
|
18
|
+
options:
|
|
19
|
+
- "Yes"
|
|
20
|
+
- "No"
|
|
21
|
+
validations:
|
|
22
|
+
required: true
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
## What changes
|
|
2
|
+
|
|
3
|
+
## Does it change the result of a fit?
|
|
4
|
+
|
|
5
|
+
- [ ] No.
|
|
6
|
+
- [ ] Yes. It is behind an option whose default is hmmlearn 0.3.3's behaviour.
|
|
7
|
+
|
|
8
|
+
## Use of AI tools
|
|
9
|
+
|
|
10
|
+
- [ ] No AI tool helped write this change.
|
|
11
|
+
- [ ] An AI tool helped: name it here. Each commit it helped write ends with
|
|
12
|
+
`Assisted-by: <tool>` (see `CONTRIBUTING.md`).
|
|
13
|
+
|
|
14
|
+
## Checklist
|
|
15
|
+
|
|
16
|
+
- [ ] A test that fails without the change.
|
|
17
|
+
- [ ] `python -m pytest --pyargs hmmcraft.tests doc` passes.
|
|
18
|
+
- [ ] No Python line is 80 characters or longer.
|
|
19
|
+
- [ ] Every commit is signed off (`git commit -s`).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Proposes newer releases of the pinned GitHub Actions once a month, in one
|
|
2
|
+
# pull request. Build requirements (ci/*.txt) are updated by hand.
|
|
3
|
+
version: 2
|
|
4
|
+
updates:
|
|
5
|
+
- package-ecosystem: github-actions
|
|
6
|
+
directory: /
|
|
7
|
+
schedule:
|
|
8
|
+
interval: monthly
|
|
9
|
+
groups:
|
|
10
|
+
actions:
|
|
11
|
+
patterns: ["*"]
|
|
12
|
+
commit-message:
|
|
13
|
+
prefix: chore
|
|
14
|
+
labels: []
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
name: ci
|
|
2
|
+
|
|
3
|
+
# The suite and the bit-equality check against hmmlearn 0.3.3, in two cells:
|
|
4
|
+
# the declared minimums ("floor") and the newest wheels ("latest"). Wheels and
|
|
5
|
+
# the sdist are built and tested by wheels.yml. The weekly run shows within a
|
|
6
|
+
# week a new numpy, scipy or scikit-learn release that breaks the suite, or
|
|
7
|
+
# under which this package's fits differ from hmmlearn 0.3.3's (both are
|
|
8
|
+
# installed at the same versions). On a pull request, every commit must be
|
|
9
|
+
# signed off.
|
|
10
|
+
|
|
11
|
+
on:
|
|
12
|
+
pull_request:
|
|
13
|
+
push:
|
|
14
|
+
branches: [main]
|
|
15
|
+
workflow_dispatch:
|
|
16
|
+
schedule:
|
|
17
|
+
- cron: "47 3 * * 1"
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
concurrency:
|
|
23
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
24
|
+
cancel-in-progress: true
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
style:
|
|
28
|
+
runs-on: ubuntu-24.04
|
|
29
|
+
timeout-minutes: 5
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
32
|
+
with:
|
|
33
|
+
persist-credentials: false
|
|
34
|
+
- name: No Python line of 80 characters or more
|
|
35
|
+
run: |
|
|
36
|
+
shopt -s globstar &&
|
|
37
|
+
! grep -E '.{80}' -- **/*.py
|
|
38
|
+
|
|
39
|
+
sign-off:
|
|
40
|
+
if: github.event_name == 'pull_request'
|
|
41
|
+
runs-on: ubuntu-24.04
|
|
42
|
+
timeout-minutes: 5
|
|
43
|
+
steps:
|
|
44
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
45
|
+
with:
|
|
46
|
+
fetch-depth: 0
|
|
47
|
+
persist-credentials: false
|
|
48
|
+
- name: Every commit signed off (DCO)
|
|
49
|
+
env:
|
|
50
|
+
BASE: ${{ github.event.pull_request.base.sha }}
|
|
51
|
+
HEAD: ${{ github.event.pull_request.head.sha }}
|
|
52
|
+
# The base branch's copy of the script, so a pull request that edits
|
|
53
|
+
# the script is checked by the copy it does not change.
|
|
54
|
+
run: |
|
|
55
|
+
git show "$BASE:scripts/check_signoff.py" > "$RUNNER_TEMP/check_signoff.py"
|
|
56
|
+
python3 -I "$RUNNER_TEMP/check_signoff.py" "$BASE" "$HEAD"
|
|
57
|
+
|
|
58
|
+
bit-equality:
|
|
59
|
+
name: bit equality (${{ matrix.cell }})
|
|
60
|
+
runs-on: ubuntu-24.04
|
|
61
|
+
timeout-minutes: 40
|
|
62
|
+
strategy:
|
|
63
|
+
fail-fast: false
|
|
64
|
+
matrix:
|
|
65
|
+
cell: [floor, latest]
|
|
66
|
+
env:
|
|
67
|
+
HMMCRAFT_SEED: "0"
|
|
68
|
+
OMP_NUM_THREADS: "1"
|
|
69
|
+
OPENBLAS_NUM_THREADS: "1"
|
|
70
|
+
MKL_NUM_THREADS: "1"
|
|
71
|
+
steps:
|
|
72
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
73
|
+
with:
|
|
74
|
+
fetch-depth: 0
|
|
75
|
+
persist-credentials: false
|
|
76
|
+
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
|
|
77
|
+
with:
|
|
78
|
+
python-version: "3.12"
|
|
79
|
+
- name: Suite and bit equality against hmmlearn 0.3.3
|
|
80
|
+
run: scripts/ci_bit_equality.sh "${{ matrix.cell }}" "$RUNNER_TEMP/check"
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
name: release
|
|
2
|
+
|
|
3
|
+
# Publishes to PyPI by trusted publishing: no stored credential exists in this
|
|
4
|
+
# repository. A tag "v<version>" builds the 20 wheels and the sdist through
|
|
5
|
+
# wheels.yml; the publish job then waits for the maintainer's approval of the
|
|
6
|
+
# "pypi" environment before it uploads. setuptools-scm takes the version from
|
|
7
|
+
# the tag, and the publish job checks that every file carries it.
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
tags:
|
|
12
|
+
- "v[0-9]*"
|
|
13
|
+
|
|
14
|
+
permissions: {}
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
build:
|
|
18
|
+
permissions:
|
|
19
|
+
contents: read
|
|
20
|
+
uses: ./.github/workflows/wheels.yml
|
|
21
|
+
with:
|
|
22
|
+
full: true
|
|
23
|
+
|
|
24
|
+
publish:
|
|
25
|
+
needs: build
|
|
26
|
+
runs-on: ubuntu-24.04
|
|
27
|
+
timeout-minutes: 15
|
|
28
|
+
environment:
|
|
29
|
+
name: pypi
|
|
30
|
+
url: https://pypi.org/project/hmm-craft/
|
|
31
|
+
permissions:
|
|
32
|
+
id-token: write
|
|
33
|
+
steps:
|
|
34
|
+
- uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
|
|
35
|
+
with:
|
|
36
|
+
pattern: dist-*
|
|
37
|
+
path: dist
|
|
38
|
+
merge-multiple: true
|
|
39
|
+
- name: Check what is about to be uploaded
|
|
40
|
+
env:
|
|
41
|
+
TAG: ${{ github.ref_name }}
|
|
42
|
+
run: |
|
|
43
|
+
version="${TAG#v}"
|
|
44
|
+
find dist -type f -print
|
|
45
|
+
test "$(find dist -type f -name '*.whl' | wc -l)" -eq 20
|
|
46
|
+
test "$(find dist -type f -name '*.tar.gz' | wc -l)" -eq 1
|
|
47
|
+
for f in dist/*; do
|
|
48
|
+
case "$(basename "$f")" in
|
|
49
|
+
"hmm_craft-$version-"*.whl | "hmm_craft-$version.tar.gz") ;;
|
|
50
|
+
*) echo "$f does not carry version $version" >&2; exit 1 ;;
|
|
51
|
+
esac
|
|
52
|
+
done
|
|
53
|
+
- uses: pypa/gh-action-pypi-publish@7245906fb61fa33a13554f9d5fb856fe61758086 # v1.14.3
|
|
54
|
+
with:
|
|
55
|
+
packages-dir: dist/
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
name: wheels
|
|
2
|
+
|
|
3
|
+
# 20 wheels and the sdist. A pull request builds and tests the Linux x86_64
|
|
4
|
+
# cells only; a manual run, the weekly schedule and a release build everything.
|
|
5
|
+
# Every wheel is tested after it is built, from the installed wheel, so a cell
|
|
6
|
+
# that cannot run the tests ships no wheel. Build requirements are pinned with
|
|
7
|
+
# hashes in ci/build_requirements.txt and installed before each build; nothing
|
|
8
|
+
# fetches them at whatever version is current.
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
pull_request:
|
|
12
|
+
workflow_dispatch:
|
|
13
|
+
schedule:
|
|
14
|
+
- cron: "17 3 * * 1"
|
|
15
|
+
workflow_call:
|
|
16
|
+
inputs:
|
|
17
|
+
full:
|
|
18
|
+
description: Build every platform, not only Linux x86_64.
|
|
19
|
+
type: boolean
|
|
20
|
+
default: true
|
|
21
|
+
|
|
22
|
+
permissions:
|
|
23
|
+
contents: read
|
|
24
|
+
|
|
25
|
+
concurrency:
|
|
26
|
+
group: wheels-${{ github.workflow }}-${{ github.ref }}
|
|
27
|
+
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
|
28
|
+
|
|
29
|
+
jobs:
|
|
30
|
+
plan:
|
|
31
|
+
runs-on: ubuntu-24.04
|
|
32
|
+
timeout-minutes: 5
|
|
33
|
+
outputs:
|
|
34
|
+
matrix: ${{ steps.matrix.outputs.matrix }}
|
|
35
|
+
steps:
|
|
36
|
+
- id: matrix
|
|
37
|
+
env:
|
|
38
|
+
FULL: ${{ inputs.full || github.event_name == 'workflow_dispatch' || github.event_name == 'schedule' }}
|
|
39
|
+
run: |
|
|
40
|
+
cells='{"name":"linux-x86_64","os":"ubuntu-24.04","archs":"x86_64"}'
|
|
41
|
+
if [ "$FULL" = true ]; then
|
|
42
|
+
cells="$cells"',{"name":"linux-aarch64","os":"ubuntu-24.04-arm","archs":"aarch64"}'
|
|
43
|
+
cells="$cells"',{"name":"macos-arm64","os":"macos-15","archs":"arm64"}'
|
|
44
|
+
cells="$cells"',{"name":"macos-x86_64","os":"macos-15-intel","archs":"x86_64"}'
|
|
45
|
+
cells="$cells"',{"name":"windows-amd64","os":"windows-2025","archs":"AMD64"}'
|
|
46
|
+
fi
|
|
47
|
+
echo "matrix={\"include\":[$cells]}" >> "$GITHUB_OUTPUT"
|
|
48
|
+
|
|
49
|
+
wheels:
|
|
50
|
+
name: wheels (${{ matrix.name }})
|
|
51
|
+
needs: plan
|
|
52
|
+
runs-on: ${{ matrix.os }}
|
|
53
|
+
timeout-minutes: 60
|
|
54
|
+
strategy:
|
|
55
|
+
fail-fast: false
|
|
56
|
+
matrix: ${{ fromJSON(needs.plan.outputs.matrix) }}
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
59
|
+
with:
|
|
60
|
+
fetch-depth: 0
|
|
61
|
+
persist-credentials: false
|
|
62
|
+
- uses: pypa/cibuildwheel@f9b966cfadf4109556cadc5bb39ddb72d0ce664e # v4.3.5
|
|
63
|
+
env:
|
|
64
|
+
# CPython 3.12 to 3.15 with the GIL; no free-threaded, musllinux or
|
|
65
|
+
# 32-bit builds. Linux wheels are manylinux_2_28 (cibuildwheel's
|
|
66
|
+
# default image).
|
|
67
|
+
CIBW_BUILD: "cp312-* cp313-* cp314-* cp315-*"
|
|
68
|
+
CIBW_SKIP: "*-musllinux_*"
|
|
69
|
+
CIBW_ARCHS: ${{ matrix.archs }}
|
|
70
|
+
CIBW_BUILD_FRONTEND: "pip; args: --no-build-isolation"
|
|
71
|
+
CIBW_BEFORE_BUILD: >-
|
|
72
|
+
python -m pip install --only-binary :all: --require-hashes
|
|
73
|
+
-r {project}/ci/build_requirements.txt
|
|
74
|
+
CIBW_TEST_REQUIRES: pytest
|
|
75
|
+
CIBW_TEST_ENVIRONMENT: HMMCRAFT_SEED=0
|
|
76
|
+
# The project's pytest configuration turns a DeprecationWarning from
|
|
77
|
+
# the package into an error. The installed wheel is tested, not the
|
|
78
|
+
# tree.
|
|
79
|
+
CIBW_TEST_COMMAND: >-
|
|
80
|
+
python -I -m pytest -c {project}/pyproject.toml -p no:cacheprovider
|
|
81
|
+
--pyargs hmmcraft.tests {project}/doc
|
|
82
|
+
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
83
|
+
with:
|
|
84
|
+
name: dist-wheels-${{ matrix.name }}
|
|
85
|
+
path: wheelhouse/*.whl
|
|
86
|
+
if-no-files-found: error
|
|
87
|
+
|
|
88
|
+
sdist:
|
|
89
|
+
runs-on: ubuntu-24.04
|
|
90
|
+
timeout-minutes: 30
|
|
91
|
+
env:
|
|
92
|
+
HMMCRAFT_SEED: "0"
|
|
93
|
+
steps:
|
|
94
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
95
|
+
with:
|
|
96
|
+
fetch-depth: 0
|
|
97
|
+
persist-credentials: false
|
|
98
|
+
- uses: actions/setup-python@6d4fd22b20465ccc0f7354476588e8df7c9ca0e0 # v7.1.0
|
|
99
|
+
with:
|
|
100
|
+
python-version: "3.12"
|
|
101
|
+
- name: Build the sdist
|
|
102
|
+
run: |
|
|
103
|
+
python -m pip install --only-binary :all: --require-hashes \
|
|
104
|
+
-r ci/tool_requirements.txt
|
|
105
|
+
python -m pip install --only-binary :all: --require-hashes \
|
|
106
|
+
-r ci/build_requirements.txt
|
|
107
|
+
python -m build --sdist --no-isolation
|
|
108
|
+
ls -l dist
|
|
109
|
+
tar tzf dist/*.tar.gz > sdist_files.txt
|
|
110
|
+
for f in ext/_hmmc.cpp pyproject.toml LICENSE.txt AUTHORS.rst; do
|
|
111
|
+
grep -q "/$f\$" sdist_files.txt || { echo "missing $f" >&2; exit 1; }
|
|
112
|
+
done
|
|
113
|
+
- name: Install the sdist and run the suite
|
|
114
|
+
run: |
|
|
115
|
+
python -m venv "$RUNNER_TEMP/sdist-env"
|
|
116
|
+
env_python="$RUNNER_TEMP/sdist-env/bin/python"
|
|
117
|
+
"$env_python" -m pip install --only-binary :all: --require-hashes \
|
|
118
|
+
-r ci/build_requirements.txt
|
|
119
|
+
"$env_python" -m pip install --only-binary :all: \
|
|
120
|
+
numpy scipy scikit-learn pytest
|
|
121
|
+
"$env_python" -m pip install --no-build-isolation --no-deps \
|
|
122
|
+
dist/*.tar.gz
|
|
123
|
+
cd "$RUNNER_TEMP"
|
|
124
|
+
"$env_python" -I -m pytest -p no:cacheprovider --pyargs hmmcraft.tests
|
|
125
|
+
- uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
|
|
126
|
+
with:
|
|
127
|
+
name: dist-sdist
|
|
128
|
+
path: dist/*.tar.gz
|
|
129
|
+
if-no-files-found: error
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
*.egg-info/
|
|
2
|
+
.*.swp
|
|
3
|
+
*.o
|
|
4
|
+
*.pyc
|
|
5
|
+
*.pyd
|
|
6
|
+
*.so
|
|
7
|
+
.cache/
|
|
8
|
+
.eggs/
|
|
9
|
+
.ipynb_checkpoints/
|
|
10
|
+
.pytest_cache/
|
|
11
|
+
build/
|
|
12
|
+
wheelhouse/
|
|
13
|
+
dist/
|
|
14
|
+
htmlcov/
|
|
15
|
+
oprofile_data/
|
|
16
|
+
.coverage
|
|
17
|
+
.coverage.*
|
|
18
|
+
.gdb_history
|
|
19
|
+
# generated by doctest:
|
|
20
|
+
filename.pkl
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
# How hmm-craft is built
|
|
2
|
+
|
|
3
|
+
This file describes the package from the Python API down to the C++ kernel,
|
|
4
|
+
what the package promises about its fits, and how a change to a fit is made.
|
|
5
|
+
It is written from the code in this repository; the code wins where the two
|
|
6
|
+
disagree.
|
|
7
|
+
|
|
8
|
+
## Layout
|
|
9
|
+
|
|
10
|
+
| Path | Holds |
|
|
11
|
+
|---|---|
|
|
12
|
+
| `src/hmmcraft/base.py` | `ConvergenceMonitor`, `_AbstractHMM` (the public methods and the fit loop), `BaseHMM` (expectation-maximization), `VariationalBaseHMM` (variational inference) |
|
|
13
|
+
| `src/hmmcraft/_emissions.py` | one base class per emission family: how to compute the emission probabilities, collect the statistics an M-step needs, and draw a sample |
|
|
14
|
+
| `src/hmmcraft/hmm.py` | the five EM models: `CategoricalHMM`, `GaussianHMM`, `GMMHMM`, `MultinomialHMM`, `PoissonHMM` |
|
|
15
|
+
| `src/hmmcraft/vhmm.py` | the two variational models: `VariationalCategoricalHMM`, `VariationalGaussianHMM` |
|
|
16
|
+
| `src/hmmcraft/utils.py` | public helpers: `normalize`, `log_normalize`, `fill_covars` |
|
|
17
|
+
| `src/hmmcraft/stats.py` | `log_multivariate_normal_density` for the four covariance types |
|
|
18
|
+
| `src/hmmcraft/_utils.py`, `_kl_divergence.py` | private helpers; the KL divergences of the variational lower bound |
|
|
19
|
+
| `ext/_hmmc.cpp` | the C++ kernel, built by `setup.py` with pybind11 as `hmmcraft._hmmc` |
|
|
20
|
+
| `src/hmmcraft/tests/` | the suite, installed with the package |
|
|
21
|
+
| `scripts/` | the bit-equality check and the CI helpers; not installed |
|
|
22
|
+
|
|
23
|
+
## Classes
|
|
24
|
+
|
|
25
|
+
```mermaid
|
|
26
|
+
classDiagram
|
|
27
|
+
BaseEstimator <|-- _AbstractHMM
|
|
28
|
+
_AbstractHMM <|-- BaseHMM
|
|
29
|
+
_AbstractHMM <|-- VariationalBaseHMM
|
|
30
|
+
_AbstractHMM <|-- BaseCategoricalHMM
|
|
31
|
+
_AbstractHMM <|-- BaseGaussianHMM
|
|
32
|
+
BaseHMM <|-- BaseGMMHMM
|
|
33
|
+
BaseHMM <|-- BaseMultinomialHMM
|
|
34
|
+
BaseHMM <|-- BasePoissonHMM
|
|
35
|
+
BaseCategoricalHMM <|-- CategoricalHMM
|
|
36
|
+
BaseHMM <|-- CategoricalHMM
|
|
37
|
+
BaseGaussianHMM <|-- GaussianHMM
|
|
38
|
+
BaseHMM <|-- GaussianHMM
|
|
39
|
+
BaseGMMHMM <|-- GMMHMM
|
|
40
|
+
BaseMultinomialHMM <|-- MultinomialHMM
|
|
41
|
+
BasePoissonHMM <|-- PoissonHMM
|
|
42
|
+
BaseCategoricalHMM <|-- VariationalCategoricalHMM
|
|
43
|
+
VariationalBaseHMM <|-- VariationalCategoricalHMM
|
|
44
|
+
BaseGaussianHMM <|-- VariationalGaussianHMM
|
|
45
|
+
VariationalBaseHMM <|-- VariationalGaussianHMM
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`BaseEstimator` is scikit-learn's: it gives `get_params`, `set_params` and
|
|
49
|
+
cloning. A public model combines two halves. The emission half (a class of
|
|
50
|
+
`_emissions.py`) knows the distribution of one observation in one state. The
|
|
51
|
+
learning half (`BaseHMM` or `VariationalBaseHMM`) knows how the start
|
|
52
|
+
probabilities and the transition matrix are estimated. Categorical and
|
|
53
|
+
Gaussian emissions exist in both halves, so their emission classes derive
|
|
54
|
+
from `_AbstractHMM` and are combined with a learning class; the other three
|
|
55
|
+
emission classes derive from `BaseHMM` directly.
|
|
56
|
+
|
|
57
|
+
## The fit loop
|
|
58
|
+
|
|
59
|
+
`_AbstractHMM.fit(X, lengths)` is the only fit loop. `X` holds every
|
|
60
|
+
sequence, one after another; `lengths` gives the length of each, a positive
|
|
61
|
+
integer. `fit` first passes both to `_utils.split_X_lengths`, which raises
|
|
62
|
+
`ValueError` for any other entry and for a total that is not the number of
|
|
63
|
+
samples, so no start is computed from invalid `lengths`.
|
|
64
|
+
|
|
65
|
+
```mermaid
|
|
66
|
+
flowchart TD
|
|
67
|
+
A["fit(X, lengths)"] --> B["_init: starting parameters<br/>(named in init_params, or not yet set)"]
|
|
68
|
+
B --> C["_check: shapes and sums"]
|
|
69
|
+
C --> D["_do_estep"]
|
|
70
|
+
D --> E["_compute_lower_bound"]
|
|
71
|
+
E --> F["_do_mstep: new parameters<br/>(those named in params)"]
|
|
72
|
+
F --> G{"ConvergenceMonitor:<br/>n_iter reached, or gain below tol?"}
|
|
73
|
+
G -- no --> D
|
|
74
|
+
G -- yes --> H["return self"]
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
`_do_estep` splits `X` by `lengths` and, for each sequence:
|
|
78
|
+
|
|
79
|
+
1. computes the emission probability of every sample in every state
|
|
80
|
+
(`_compute_likelihood`, or `_compute_log_likelihood` when
|
|
81
|
+
`implementation="log"`, the default);
|
|
82
|
+
2. calls the kernel's forward and backward passes on that matrix with
|
|
83
|
+
`startprob_` and `transmat_`, and turns the two lattices into the
|
|
84
|
+
posterior probability of each state at each sample;
|
|
85
|
+
3. adds what the M-step needs to a dictionary of statistics
|
|
86
|
+
(`_accumulate_sufficient_statistics`); for the transitions, the kernel
|
|
87
|
+
computes the expected transition counts (`compute_log_xi_sum` or
|
|
88
|
+
`compute_scaling_xi_sum`).
|
|
89
|
+
|
|
90
|
+
It also sums the posteriors into `state_occupancy_`, the expected number of
|
|
91
|
+
samples in each state. `BaseHMM` returns the summed log-likelihood as the
|
|
92
|
+
lower bound; `VariationalBaseHMM` subtracts the KL divergences of its
|
|
93
|
+
posteriors from their priors, and runs the passes on subnormalized
|
|
94
|
+
probabilities (`_estep_begin`).
|
|
95
|
+
|
|
96
|
+
A model class supplies these hooks; the loop never changes per class:
|
|
97
|
+
|
|
98
|
+
| Hook | Does |
|
|
99
|
+
|---|---|
|
|
100
|
+
| `_init` | sets the starting parameters named in `init_params`, and any that is not yet set (k-means for Gaussian means, for instance) |
|
|
101
|
+
| `_check` | validates shapes and that probabilities sum to one |
|
|
102
|
+
| `_compute_likelihood` / `_compute_log_likelihood` | at least one implemented; the base class derives a missing one from the other |
|
|
103
|
+
| `_initialize_sufficient_statistics` | the empty dictionary of statistics |
|
|
104
|
+
| `_accumulate_sufficient_statistics` | adds one sequence's statistics |
|
|
105
|
+
| `_do_mstep` | sets the parameters named in `params` from the statistics |
|
|
106
|
+
| `_generate_sample_from_state` | draws one observation, for `sample` |
|
|
107
|
+
| `_get_n_fit_scalars_per_param` | the free parameters, for the warning on too little data and for `aic` and `bic` |
|
|
108
|
+
|
|
109
|
+
`score` runs the forward pass alone; `score_samples`, `predict_proba` and
|
|
110
|
+
`decode(algorithm="map")` run the forward and backward passes; none of them
|
|
111
|
+
fits. `decode` with
|
|
112
|
+
`algorithm="viterbi"` calls the kernel's `viterbi`. `sample` is a Python loop
|
|
113
|
+
over the transition matrix and `_generate_sample_from_state`.
|
|
114
|
+
|
|
115
|
+
## The C++ kernel
|
|
116
|
+
|
|
117
|
+
`ext/_hmmc.cpp` holds the recursions that loop over every sample and every
|
|
118
|
+
pair of states. It knows nothing about emissions: it receives the emission
|
|
119
|
+
probabilities (or log-probabilities) of one sequence as a matrix of shape
|
|
120
|
+
`(n_samples, n_components)`, already computed in Python. Every argument is a
|
|
121
|
+
float64 array.
|
|
122
|
+
|
|
123
|
+
| Function | Takes | Returns |
|
|
124
|
+
|---|---|---|
|
|
125
|
+
| `forward_log` | start probabilities, transition matrix, emission log-probabilities | the log-likelihood and the forward lattice, in log space |
|
|
126
|
+
| `backward_log` | the same | the backward lattice, in log space |
|
|
127
|
+
| `forward_scaling` | start probabilities, transition matrix, emission probabilities | the log-likelihood, the forward lattice and the scaling factor of each sample |
|
|
128
|
+
| `backward_scaling` | the same, and the scaling factors | the backward lattice, scaled with those factors |
|
|
129
|
+
| `compute_log_xi_sum` | forward lattice, transition matrix, backward lattice, emission log-probabilities | the expected transition counts, in log space |
|
|
130
|
+
| `compute_scaling_xi_sum` | forward lattice, transition matrix, backward lattice, emission probabilities | the expected transition counts |
|
|
131
|
+
| `viterbi` | start probabilities, transition matrix, emission log-probabilities | the log-probability of the best path and the path |
|
|
132
|
+
|
|
133
|
+
A shape that does not match raises `ValueError`, and so does a matrix with no
|
|
134
|
+
rows (a sequence without samples) or no columns (a model without states): the
|
|
135
|
+
recursions use unchecked accessors, and hmmlearn 0.3.3's kernel indexed
|
|
136
|
+
outside its arrays there. Each function releases the GIL while it loops. The
|
|
137
|
+
kernel's source is hmmlearn 0.3.3's apart from that check and the module's
|
|
138
|
+
package name.
|
|
139
|
+
|
|
140
|
+
## What the package promises
|
|
141
|
+
|
|
142
|
+
**Fits are checked to equal hmmlearn 0.3.3's, bit for bit, apart from the
|
|
143
|
+
differences listed in `DIFFERENCES.md`.** `scripts/bit_equality.py` fits every
|
|
144
|
+
model class, covariance type and implementation with both packages, in two
|
|
145
|
+
environments with the same versions, on synthetic data generated inside the
|
|
146
|
+
script from a fixed seed, with one thread, and compares every fitted array,
|
|
147
|
+
the log-likelihood, both decodes and a drawn sample as bytes, not within a
|
|
148
|
+
tolerance. `DIFFERENCES.md` names the data, the versions, the platform and the
|
|
149
|
+
result; the claim covers that check and nothing beyond it. Both CI cells
|
|
150
|
+
(`ci.yml`) run it on every pull request and every week, each with the
|
|
151
|
+
reference installed at the same versions as the package.
|
|
152
|
+
|
|
153
|
+
### Step 1 and step 2
|
|
154
|
+
|
|
155
|
+
The fork changes fits in two steps.
|
|
156
|
+
|
|
157
|
+
**Step 1** is the fork as first published: hmmlearn 0.3.3 under a new name,
|
|
158
|
+
with newer minimum versions, wheels, CI, and the changes listed in
|
|
159
|
+
`DIFFERENCES.md`. Only one of them changes a fit with every option at its
|
|
160
|
+
default, and it replaces a result that hmmlearn 0.3.3 computed wrongly or
|
|
161
|
+
failed to compute (the Gaussian fit of covariances without means).
|
|
162
|
+
`GaussianHMM(covars_floor=...)` changes fits only when it is set. In the check
|
|
163
|
+
above, every other fit equals 0.3.3's.
|
|
164
|
+
|
|
165
|
+
**Step 2** holds the later changes that move a fit hmmlearn 0.3.3 computes
|
|
166
|
+
correctly. Each one comes behind an option whose default keeps 0.3.3's result,
|
|
167
|
+
is listed in `DIFFERENCES.md` with an exception of its own in the check, is
|
|
168
|
+
named in `CHANGES.rst`, and ships in a minor release (`RELEASING.md`).
|
|
169
|
+
Equality is always checked against hmmlearn 0.3.3, with every option at its
|
|
170
|
+
default.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
Authors and contributors, in no particular order:
|
|
2
|
+
|
|
3
|
+
* Ron Weiss
|
|
4
|
+
* Shiqiao Du
|
|
5
|
+
* Jaques Grobler
|
|
6
|
+
* David Cournapeau
|
|
7
|
+
* Fabian Pedregosa
|
|
8
|
+
* Gael Varoquaux
|
|
9
|
+
* Andreas Mueller
|
|
10
|
+
* Bertrand Thirion
|
|
11
|
+
* Daniel Nouri
|
|
12
|
+
* Gilles Louppe
|
|
13
|
+
* Jake Vanderplas
|
|
14
|
+
* John Benediktsson
|
|
15
|
+
* Lars Buitinck
|
|
16
|
+
* Mikhail Korobov
|
|
17
|
+
* Robert McGibbon
|
|
18
|
+
* Stefano Lattarini
|
|
19
|
+
* Vlad Niculae
|
|
20
|
+
* csytracy
|
|
21
|
+
* Alexandre Gramfort
|
|
22
|
+
* Sergei Lebedev
|
|
23
|
+
* Daniela Huppenkothen
|
|
24
|
+
* Christopher Farrow
|
|
25
|
+
* Alexandr Yanenko
|
|
26
|
+
* Antony Lee
|
|
27
|
+
* Matthew Danielson
|
|
28
|
+
* Alex Rockhill
|
|
29
|
+
|
|
30
|
+
Authors and contributors of the hmm-craft fork, from 2026:
|
|
31
|
+
|
|
32
|
+
* rudolfmleziva (maintainer)
|