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.
Files changed (90) hide show
  1. hmm_craft-0.4.0/.github/ISSUE_TEMPLATE/bug_report.yml +43 -0
  2. hmm_craft-0.4.0/.github/ISSUE_TEMPLATE/config.yml +1 -0
  3. hmm_craft-0.4.0/.github/ISSUE_TEMPLATE/feature_request.yml +22 -0
  4. hmm_craft-0.4.0/.github/PULL_REQUEST_TEMPLATE.md +19 -0
  5. hmm_craft-0.4.0/.github/dependabot.yml +14 -0
  6. hmm_craft-0.4.0/.github/workflows/ci.yml +80 -0
  7. hmm_craft-0.4.0/.github/workflows/release.yml +55 -0
  8. hmm_craft-0.4.0/.github/workflows/wheels.yml +129 -0
  9. hmm_craft-0.4.0/.gitignore +20 -0
  10. hmm_craft-0.4.0/ARCHITECTURE.md +170 -0
  11. hmm_craft-0.4.0/AUTHORS.rst +32 -0
  12. hmm_craft-0.4.0/CHANGES.rst +215 -0
  13. hmm_craft-0.4.0/CITATION.cff +34 -0
  14. hmm_craft-0.4.0/CODE_OF_CONDUCT.md +83 -0
  15. hmm_craft-0.4.0/CONTRIBUTING.md +74 -0
  16. hmm_craft-0.4.0/DIFFERENCES.md +121 -0
  17. hmm_craft-0.4.0/LICENSE.txt +28 -0
  18. hmm_craft-0.4.0/PKG-INFO +164 -0
  19. hmm_craft-0.4.0/README.rst +123 -0
  20. hmm_craft-0.4.0/RELEASING.md +99 -0
  21. hmm_craft-0.4.0/SECURITY.md +12 -0
  22. hmm_craft-0.4.0/ci/build_requirements.in +3 -0
  23. hmm_craft-0.4.0/ci/build_requirements.txt +18 -0
  24. hmm_craft-0.4.0/ci/floor_constraints.txt +14 -0
  25. hmm_craft-0.4.0/ci/tool_requirements.in +1 -0
  26. hmm_craft-0.4.0/ci/tool_requirements.txt +15 -0
  27. hmm_craft-0.4.0/doc/.gitignore +2 -0
  28. hmm_craft-0.4.0/doc/Makefile +19 -0
  29. hmm_craft-0.4.0/doc/make.bat +35 -0
  30. hmm_craft-0.4.0/doc/source/GMMHMM.rst +296 -0
  31. hmm_craft-0.4.0/doc/source/_static/hide_some_gallery_elements.css +3 -0
  32. hmm_craft-0.4.0/doc/source/api.rst +90 -0
  33. hmm_craft-0.4.0/doc/source/changelog.rst +1 -0
  34. hmm_craft-0.4.0/doc/source/conf.py +53 -0
  35. hmm_craft-0.4.0/doc/source/index.rst +22 -0
  36. hmm_craft-0.4.0/doc/source/tutorial.rst +243 -0
  37. hmm_craft-0.4.0/examples/README.txt +4 -0
  38. hmm_craft-0.4.0/examples/plot_casino.py +145 -0
  39. hmm_craft-0.4.0/examples/plot_gaussian_model_selection.py +78 -0
  40. hmm_craft-0.4.0/examples/plot_hmm_sampling_and_decoding.py +117 -0
  41. hmm_craft-0.4.0/examples/plot_multinomial_hmm.py +129 -0
  42. hmm_craft-0.4.0/examples/plot_poisson_hmm.py +111 -0
  43. hmm_craft-0.4.0/examples/plot_variational_inference.py +185 -0
  44. hmm_craft-0.4.0/ext/_hmmc.cpp +356 -0
  45. hmm_craft-0.4.0/pyproject.toml +92 -0
  46. hmm_craft-0.4.0/scripts/benchmark.py +298 -0
  47. hmm_craft-0.4.0/scripts/bit_equality.py +1156 -0
  48. hmm_craft-0.4.0/scripts/check_signoff.py +90 -0
  49. hmm_craft-0.4.0/scripts/ci_bit_equality.sh +109 -0
  50. hmm_craft-0.4.0/scripts/probe_covariance_only_fit.py +198 -0
  51. hmm_craft-0.4.0/scripts/test_bit_equality.py +547 -0
  52. hmm_craft-0.4.0/scripts/test_check_signoff.py +219 -0
  53. hmm_craft-0.4.0/scripts/test_floor_constraints.py +86 -0
  54. hmm_craft-0.4.0/setup.cfg +4 -0
  55. hmm_craft-0.4.0/setup.py +9 -0
  56. hmm_craft-0.4.0/src/hmm_craft.egg-info/PKG-INFO +164 -0
  57. hmm_craft-0.4.0/src/hmm_craft.egg-info/SOURCES.txt +88 -0
  58. hmm_craft-0.4.0/src/hmm_craft.egg-info/dependency_links.txt +1 -0
  59. hmm_craft-0.4.0/src/hmm_craft.egg-info/requires.txt +12 -0
  60. hmm_craft-0.4.0/src/hmm_craft.egg-info/scm_file_list.json +85 -0
  61. hmm_craft-0.4.0/src/hmm_craft.egg-info/scm_version.json +8 -0
  62. hmm_craft-0.4.0/src/hmm_craft.egg-info/top_level.txt +1 -0
  63. hmm_craft-0.4.0/src/hmmcraft/__init__.py +15 -0
  64. hmm_craft-0.4.0/src/hmmcraft/_emissions.py +411 -0
  65. hmm_craft-0.4.0/src/hmmcraft/_kl_divergence.py +117 -0
  66. hmm_craft-0.4.0/src/hmmcraft/_utils.py +90 -0
  67. hmm_craft-0.4.0/src/hmmcraft/base.py +1283 -0
  68. hmm_craft-0.4.0/src/hmmcraft/hmm.py +1170 -0
  69. hmm_craft-0.4.0/src/hmmcraft/stats.py +98 -0
  70. hmm_craft-0.4.0/src/hmmcraft/tests/__init__.py +96 -0
  71. hmm_craft-0.4.0/src/hmmcraft/tests/conftest.py +12 -0
  72. hmm_craft-0.4.0/src/hmmcraft/tests/test_base.py +395 -0
  73. hmm_craft-0.4.0/src/hmmcraft/tests/test_categorical_hmm.py +167 -0
  74. hmm_craft-0.4.0/src/hmmcraft/tests/test_deprecated_numpy_use.py +129 -0
  75. hmm_craft-0.4.0/src/hmmcraft/tests/test_gaussian_hmm.py +419 -0
  76. hmm_craft-0.4.0/src/hmmcraft/tests/test_gaussian_hmm_min_covar.py +185 -0
  77. hmm_craft-0.4.0/src/hmmcraft/tests/test_gaussian_hmm_spherical_shape.py +47 -0
  78. hmm_craft-0.4.0/src/hmmcraft/tests/test_gmm_hmm.py +111 -0
  79. hmm_craft-0.4.0/src/hmmcraft/tests/test_gmm_hmm_multisequence.py +286 -0
  80. hmm_craft-0.4.0/src/hmmcraft/tests/test_gmm_hmm_new.py +265 -0
  81. hmm_craft-0.4.0/src/hmmcraft/tests/test_hmmc.py +76 -0
  82. hmm_craft-0.4.0/src/hmmcraft/tests/test_kl_divergence.py +55 -0
  83. hmm_craft-0.4.0/src/hmmcraft/tests/test_multinomial_hmm.py +181 -0
  84. hmm_craft-0.4.0/src/hmmcraft/tests/test_poisson_hmm.py +101 -0
  85. hmm_craft-0.4.0/src/hmmcraft/tests/test_sequence_lengths.py +96 -0
  86. hmm_craft-0.4.0/src/hmmcraft/tests/test_utils.py +110 -0
  87. hmm_craft-0.4.0/src/hmmcraft/tests/test_variational_categorical.py +255 -0
  88. hmm_craft-0.4.0/src/hmmcraft/tests/test_variational_gaussian.py +507 -0
  89. hmm_craft-0.4.0/src/hmmcraft/utils.py +83 -0
  90. 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)