morseframes 0.1.0a1__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.
- morseframes-0.1.0a1/.github/workflows/ci.yml +71 -0
- morseframes-0.1.0a1/.github/workflows/publish.yml +115 -0
- morseframes-0.1.0a1/.gitignore +34 -0
- morseframes-0.1.0a1/.readthedocs.yaml +13 -0
- morseframes-0.1.0a1/CHANGELOG.md +31 -0
- morseframes-0.1.0a1/CITATION.cff +21 -0
- morseframes-0.1.0a1/CMakeLists.txt +188 -0
- morseframes-0.1.0a1/CONTRIBUTING.md +81 -0
- morseframes-0.1.0a1/LICENSE +22 -0
- morseframes-0.1.0a1/PKG-INFO +213 -0
- morseframes-0.1.0a1/README.md +160 -0
- morseframes-0.1.0a1/benchmarks/benchmark_gudhi_view.cpp +938 -0
- morseframes-0.1.0a1/docs/api.rst +11 -0
- morseframes-0.1.0a1/docs/benchmark_reproduction.md +321 -0
- morseframes-0.1.0a1/docs/benchmark_summary.md +135 -0
- morseframes-0.1.0a1/docs/conf.py +54 -0
- morseframes-0.1.0a1/docs/cpp_complex_view_api.md +91 -0
- morseframes-0.1.0a1/docs/gudhi_contribution_design_note.md +328 -0
- morseframes-0.1.0a1/docs/gudhi_upstream_patch_map.md +416 -0
- morseframes-0.1.0a1/docs/index.rst +45 -0
- morseframes-0.1.0a1/docs/inspecting_morse_data.rst +199 -0
- morseframes-0.1.0a1/docs/minimal_examples.rst +125 -0
- morseframes-0.1.0a1/docs/native_gudhi_large_lean_r30_table.tex +35 -0
- morseframes-0.1.0a1/docs/native_gudhi_overhead_summary_table.tex +20 -0
- morseframes-0.1.0a1/docs/native_gudhi_stage_profile_quick_table.tex +24 -0
- morseframes-0.1.0a1/docs/native_gudhi_view_default_r30_table.tex +35 -0
- morseframes-0.1.0a1/docs/native_gudhi_view_quick_table.tex +25 -0
- morseframes-0.1.0a1/docs/phase0_conventions.md +79 -0
- morseframes-0.1.0a1/docs/plotly_morse_square_demo.html +18 -0
- morseframes-0.1.0a1/docs/plotting_demo.rst +19 -0
- morseframes-0.1.0a1/docs/profile_metric_fair_validation_table.tex +36 -0
- morseframes-0.1.0a1/docs/profile_selector_decision_summary_table.tex +28 -0
- morseframes-0.1.0a1/docs/project_overview.rst +89 -0
- morseframes-0.1.0a1/docs/python_api.md +295 -0
- morseframes-0.1.0a1/docs/python_prime_field_tutorial.md +167 -0
- morseframes-0.1.0a1/docs/quickstart.rst +66 -0
- morseframes-0.1.0a1/docs/requirements.txt +3 -0
- morseframes-0.1.0a1/docs/selector_feature_diagnostic_table.tex +19 -0
- morseframes-0.1.0a1/docs/strategies.md +251 -0
- morseframes-0.1.0a1/docs/synthetic_scale_table.tex +30 -0
- morseframes-0.1.0a1/examples/example_barcode.cpp +137 -0
- morseframes-0.1.0a1/examples/example_morse_persistence_from_simplex_tree.cpp +86 -0
- morseframes-0.1.0a1/examples/gudhi_simplex_tree_morse.cpp +82 -0
- morseframes-0.1.0a1/include/gudhi/Morse_persistence/complex_view.h +53 -0
- morseframes-0.1.0a1/include/gudhi/Morse_persistence/diagram.h +57 -0
- morseframes-0.1.0a1/include/gudhi/Morse_persistence/morse_sequence.h +34 -0
- morseframes-0.1.0a1/include/gudhi/Morse_persistence/persistence_reducer.h +177 -0
- morseframes-0.1.0a1/include/gudhi/Morse_persistence/reference_map.h +125 -0
- morseframes-0.1.0a1/include/gudhi/Morse_persistence/strategy.h +100 -0
- morseframes-0.1.0a1/include/gudhi/Morse_persistence.h +29 -0
- morseframes-0.1.0a1/include/morseframes/annotation.hpp +366 -0
- morseframes-0.1.0a1/include/morseframes/complex_view.hpp +101 -0
- morseframes-0.1.0a1/include/morseframes/coreference_persistence.hpp +690 -0
- morseframes-0.1.0a1/include/morseframes/debug_checks.hpp +124 -0
- morseframes-0.1.0a1/include/morseframes/field_annotation_store.hpp +349 -0
- morseframes-0.1.0a1/include/morseframes/field_arithmetic.hpp +70 -0
- morseframes-0.1.0a1/include/morseframes/filtered_complex.hpp +244 -0
- morseframes-0.1.0a1/include/morseframes/instrumentation.hpp +173 -0
- morseframes-0.1.0a1/include/morseframes/inverse_annotation_store.hpp +354 -0
- morseframes-0.1.0a1/include/morseframes/morse_reference_api.hpp +171 -0
- morseframes-0.1.0a1/include/morseframes/morse_sequence.hpp +1400 -0
- morseframes-0.1.0a1/include/morseframes/reference_persistence.hpp +1601 -0
- morseframes-0.1.0a1/include/morseframes/simplex_tree_builder.hpp +1250 -0
- morseframes-0.1.0a1/include/morseframes/simplex_tree_morse.hpp +58 -0
- morseframes-0.1.0a1/include/morseframes/standard_persistence.hpp +271 -0
- morseframes-0.1.0a1/include/morseframes/working_sets.hpp +50 -0
- morseframes-0.1.0a1/pyproject.toml +51 -0
- morseframes-0.1.0a1/python/examples/plotly_morse_square_demo.py +68 -0
- morseframes-0.1.0a1/python/examples/prime_field_tutorial.py +134 -0
- morseframes-0.1.0a1/python/examples/quickstart.py +35 -0
- morseframes-0.1.0a1/python/morseframes/__init__.py +5172 -0
- morseframes-0.1.0a1/python/morseframes/_core_bindings.cpp +1437 -0
- morseframes-0.1.0a1/python/morseframes/plotting.py +1330 -0
- morseframes-0.1.0a1/python/tests/test_benchmark_tool.py +1466 -0
- morseframes-0.1.0a1/python/tests/test_package_metadata.py +15 -0
- morseframes-0.1.0a1/python/tests/test_plotting.py +70 -0
- morseframes-0.1.0a1/python/tests/test_python_api.py +1143 -0
- morseframes-0.1.0a1/tests/test_gudhi_simplex_tree_view.cpp +574 -0
- morseframes-0.1.0a1/tests/test_morse_reference.cpp +1004 -0
- morseframes-0.1.0a1/tools/analyze_selector_features.py +534 -0
- morseframes-0.1.0a1/tools/benchmark_persistence.py +2458 -0
- morseframes-0.1.0a1/tools/benchmark_prime_field_overhead.py +354 -0
- morseframes-0.1.0a1/tools/calibrate_profile_gate.py +275 -0
- morseframes-0.1.0a1/tools/compare_profile_gates.py +307 -0
- morseframes-0.1.0a1/tools/render_benchmark_summary.py +268 -0
- morseframes-0.1.0a1/tools/render_native_gudhi_stage_profile.py +334 -0
- morseframes-0.1.0a1/tools/render_native_gudhi_view_table.py +284 -0
- morseframes-0.1.0a1/tools/render_synthetic_scale_table.py +274 -0
- morseframes-0.1.0a1/tools/run_fair_profile_validation.py +1086 -0
- morseframes-0.1.0a1/tools/summarize_selector_decisions.py +516 -0
- morseframes-0.1.0a1/tools/validate_with_gudhi.py +90 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
pull_request:
|
|
6
|
+
|
|
7
|
+
env:
|
|
8
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
9
|
+
|
|
10
|
+
jobs:
|
|
11
|
+
test:
|
|
12
|
+
name: C++ and Python (${{ matrix.os }}, Python ${{ matrix.python-version }})
|
|
13
|
+
runs-on: ${{ matrix.os }}
|
|
14
|
+
timeout-minutes: 20
|
|
15
|
+
strategy:
|
|
16
|
+
fail-fast: false
|
|
17
|
+
matrix:
|
|
18
|
+
os: [ubuntu-latest, macos-latest]
|
|
19
|
+
python-version: ["3.11"]
|
|
20
|
+
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
|
|
24
|
+
- uses: actions/setup-python@v5
|
|
25
|
+
with:
|
|
26
|
+
python-version: ${{ matrix.python-version }}
|
|
27
|
+
|
|
28
|
+
- name: Upgrade pip
|
|
29
|
+
run: python -m pip install --upgrade pip
|
|
30
|
+
|
|
31
|
+
- name: Configure C++
|
|
32
|
+
run: cmake -S . -B build -DMORSEFRAMES_BUILD_GUDHI_TOOLS=OFF
|
|
33
|
+
|
|
34
|
+
- name: Build C++
|
|
35
|
+
run: cmake --build build --parallel
|
|
36
|
+
|
|
37
|
+
- name: Test C++
|
|
38
|
+
run: ctest --test-dir build --output-on-failure --timeout 120
|
|
39
|
+
|
|
40
|
+
- name: Install Python package
|
|
41
|
+
run: python -m pip install -e .
|
|
42
|
+
|
|
43
|
+
- name: Smoke-test native Python package
|
|
44
|
+
run: python -c "import morseframes as mf; print(mf.__version__, mf.cpp_backend_available()); assert mf.cpp_backend_available()"
|
|
45
|
+
|
|
46
|
+
- name: Test pure-Python fallback
|
|
47
|
+
run: MORSEFRAMES_DISABLE_CPP_BACKEND=1 python -m unittest discover -s python/tests -p "test_*.py"
|
|
48
|
+
|
|
49
|
+
- name: Run Python quickstart
|
|
50
|
+
run: python python/examples/quickstart.py
|
|
51
|
+
|
|
52
|
+
docs:
|
|
53
|
+
name: Documentation
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
timeout-minutes: 10
|
|
56
|
+
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v4
|
|
59
|
+
|
|
60
|
+
- uses: actions/setup-python@v5
|
|
61
|
+
with:
|
|
62
|
+
python-version: "3.12"
|
|
63
|
+
|
|
64
|
+
- name: Install documentation dependencies
|
|
65
|
+
run: python -m pip install -r docs/requirements.txt
|
|
66
|
+
|
|
67
|
+
- name: Check generated benchmark summary
|
|
68
|
+
run: python tools/render_benchmark_summary.py --check
|
|
69
|
+
|
|
70
|
+
- name: Build documentation
|
|
71
|
+
run: MORSEFRAMES_DISABLE_CPP_BACKEND=1 python -m sphinx -W --keep-going -b html docs docs/_build/html
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
name: Publish Python Package
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
repository:
|
|
7
|
+
description: "Package registry to publish to"
|
|
8
|
+
required: true
|
|
9
|
+
default: testpypi
|
|
10
|
+
type: choice
|
|
11
|
+
options:
|
|
12
|
+
- testpypi
|
|
13
|
+
- pypi
|
|
14
|
+
release:
|
|
15
|
+
types: [published]
|
|
16
|
+
|
|
17
|
+
permissions:
|
|
18
|
+
contents: read
|
|
19
|
+
|
|
20
|
+
env:
|
|
21
|
+
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
|
|
22
|
+
CIBW_BUILD: "cp310-* cp311-* cp312-*"
|
|
23
|
+
CIBW_SKIP: "*-musllinux_*"
|
|
24
|
+
CIBW_TEST_COMMAND: >-
|
|
25
|
+
python -c "import morseframes as mf;
|
|
26
|
+
assert mf.__version__ == '0.1.0a1';
|
|
27
|
+
assert mf.cpp_backend_available();
|
|
28
|
+
print(mf.__version__, mf.cpp_backend_available())"
|
|
29
|
+
|
|
30
|
+
jobs:
|
|
31
|
+
sdist:
|
|
32
|
+
name: Source distribution
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
|
|
35
|
+
steps:
|
|
36
|
+
- uses: actions/checkout@v4
|
|
37
|
+
|
|
38
|
+
- uses: actions/setup-python@v5
|
|
39
|
+
with:
|
|
40
|
+
python-version: "3.12"
|
|
41
|
+
|
|
42
|
+
- name: Build source distribution
|
|
43
|
+
run: |
|
|
44
|
+
python -m pip install --upgrade pip build
|
|
45
|
+
python -m build --sdist --outdir dist
|
|
46
|
+
|
|
47
|
+
- uses: actions/upload-artifact@v4
|
|
48
|
+
with:
|
|
49
|
+
name: sdist
|
|
50
|
+
path: dist/*.tar.gz
|
|
51
|
+
|
|
52
|
+
wheels:
|
|
53
|
+
name: Wheels (${{ matrix.os }})
|
|
54
|
+
runs-on: ${{ matrix.os }}
|
|
55
|
+
strategy:
|
|
56
|
+
fail-fast: false
|
|
57
|
+
matrix:
|
|
58
|
+
os: [ubuntu-latest, macos-latest]
|
|
59
|
+
|
|
60
|
+
steps:
|
|
61
|
+
- uses: actions/checkout@v4
|
|
62
|
+
|
|
63
|
+
- uses: actions/setup-python@v5
|
|
64
|
+
with:
|
|
65
|
+
python-version: "3.12"
|
|
66
|
+
|
|
67
|
+
- uses: pypa/cibuildwheel@v4.1.0
|
|
68
|
+
with:
|
|
69
|
+
output-dir: wheelhouse
|
|
70
|
+
|
|
71
|
+
- uses: actions/upload-artifact@v4
|
|
72
|
+
with:
|
|
73
|
+
name: wheels-${{ matrix.os }}
|
|
74
|
+
path: wheelhouse/*.whl
|
|
75
|
+
|
|
76
|
+
publish-testpypi:
|
|
77
|
+
name: Publish to TestPyPI
|
|
78
|
+
if: github.event_name == 'workflow_dispatch' && inputs.repository == 'testpypi'
|
|
79
|
+
needs: [sdist, wheels]
|
|
80
|
+
runs-on: ubuntu-latest
|
|
81
|
+
environment: testpypi
|
|
82
|
+
permissions:
|
|
83
|
+
contents: read
|
|
84
|
+
id-token: write
|
|
85
|
+
|
|
86
|
+
steps:
|
|
87
|
+
- uses: actions/download-artifact@v4
|
|
88
|
+
with:
|
|
89
|
+
path: dist
|
|
90
|
+
merge-multiple: true
|
|
91
|
+
|
|
92
|
+
- uses: pypa/gh-action-pypi-publish@v1.14.0
|
|
93
|
+
with:
|
|
94
|
+
packages-dir: dist
|
|
95
|
+
repository-url: https://test.pypi.org/legacy/
|
|
96
|
+
|
|
97
|
+
publish-pypi:
|
|
98
|
+
name: Publish to PyPI
|
|
99
|
+
if: github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.repository == 'pypi')
|
|
100
|
+
needs: [sdist, wheels]
|
|
101
|
+
runs-on: ubuntu-latest
|
|
102
|
+
environment: pypi
|
|
103
|
+
permissions:
|
|
104
|
+
contents: read
|
|
105
|
+
id-token: write
|
|
106
|
+
|
|
107
|
+
steps:
|
|
108
|
+
- uses: actions/download-artifact@v4
|
|
109
|
+
with:
|
|
110
|
+
path: dist
|
|
111
|
+
merge-multiple: true
|
|
112
|
+
|
|
113
|
+
- uses: pypa/gh-action-pypi-publish@v1.14.0
|
|
114
|
+
with:
|
|
115
|
+
packages-dir: dist
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
build/
|
|
2
|
+
build-*/
|
|
3
|
+
cmake-build-*/
|
|
4
|
+
CMakeFiles/
|
|
5
|
+
CMakeCache.txt
|
|
6
|
+
CTestTestfile.cmake
|
|
7
|
+
CMakeInit.txt
|
|
8
|
+
Makefile
|
|
9
|
+
build.ninja
|
|
10
|
+
.ninja_deps
|
|
11
|
+
.ninja_log
|
|
12
|
+
.cmake/
|
|
13
|
+
.skbuild-info.json
|
|
14
|
+
cmake_install.cmake
|
|
15
|
+
|
|
16
|
+
__pycache__/
|
|
17
|
+
*.py[cod]
|
|
18
|
+
*.so
|
|
19
|
+
*.dylib
|
|
20
|
+
*.a
|
|
21
|
+
*.o
|
|
22
|
+
*.egg-info/
|
|
23
|
+
dist/
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
|
|
26
|
+
*.aux
|
|
27
|
+
*.log
|
|
28
|
+
*.out
|
|
29
|
+
*.toc
|
|
30
|
+
docs/*_prose.tex
|
|
31
|
+
docs/experiments_morse_persistence*.pdf
|
|
32
|
+
docs/_build/
|
|
33
|
+
|
|
34
|
+
.DS_Store
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to MorseFrames are recorded here.
|
|
4
|
+
|
|
5
|
+
## 0.1.0a1 - 2026-06-20
|
|
6
|
+
|
|
7
|
+
Initial public alpha release.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Header-only C++ core for filtered simplicial complexes.
|
|
12
|
+
- Python interface with pure-Python fallback and optional nanobind backend.
|
|
13
|
+
- Morse sequence strategies: saturated, F-Min, F-Max, same-level reduction,
|
|
14
|
+
plateau-greedy, and flooding variants.
|
|
15
|
+
- Reference and coreference map construction.
|
|
16
|
+
- Morse-reference and Morse-coreference persistence over `Z_2` and prime
|
|
17
|
+
fields `F_p`.
|
|
18
|
+
- Simplex-tree-like builder for constructing complexes.
|
|
19
|
+
- Experimental GUDHI-facing adapter for `Simplex_tree`.
|
|
20
|
+
- Plotly demonstration for inspecting Morse critical data on a triangulated
|
|
21
|
+
square.
|
|
22
|
+
- Reproducible benchmark scripts and generated public benchmark table
|
|
23
|
+
fragments.
|
|
24
|
+
- Read the Docs documentation, including overview, quickstart, minimal
|
|
25
|
+
examples, and Morse-data inspection guide.
|
|
26
|
+
- GitHub Actions CI for C++, Python, and documentation builds.
|
|
27
|
+
|
|
28
|
+
### Notes
|
|
29
|
+
|
|
30
|
+
- This is research software. Public APIs are useful for experiments, but may
|
|
31
|
+
still evolve while the paper and GUDHI integration mature.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
cff-version: 1.2.0
|
|
2
|
+
message: "If you use MorseFrames in academic work, please cite it using this metadata."
|
|
3
|
+
type: software
|
|
4
|
+
title: "MorseFrames"
|
|
5
|
+
authors:
|
|
6
|
+
- family-names: Najman
|
|
7
|
+
given-names: Laurent
|
|
8
|
+
abstract: >-
|
|
9
|
+
Experimental C++ and Python software for Morse sequences, reference and
|
|
10
|
+
coreference maps, Morse complexes, and Morse-based persistent homology.
|
|
11
|
+
version: "0.1.0a1"
|
|
12
|
+
date-released: "2026-06-20"
|
|
13
|
+
license: MIT
|
|
14
|
+
repository-code: "https://github.com/lnajman/morseframes"
|
|
15
|
+
url: "https://morseframes.readthedocs.io/"
|
|
16
|
+
keywords:
|
|
17
|
+
- persistent homology
|
|
18
|
+
- discrete Morse theory
|
|
19
|
+
- Morse sequence
|
|
20
|
+
- computational topology
|
|
21
|
+
- simplicial complex
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
cmake_minimum_required(VERSION 3.16)
|
|
2
|
+
|
|
3
|
+
if(CMAKE_HOST_SYSTEM_NAME STREQUAL "Darwin" AND NOT CMAKE_OSX_ARCHITECTURES)
|
|
4
|
+
set(_MORSEFRAMES_PYTHON_FOR_ARCH "")
|
|
5
|
+
if(DEFINED Python_EXECUTABLE)
|
|
6
|
+
set(_MORSEFRAMES_PYTHON_FOR_ARCH "${Python_EXECUTABLE}")
|
|
7
|
+
elseif(DEFINED PYTHON_EXECUTABLE)
|
|
8
|
+
set(_MORSEFRAMES_PYTHON_FOR_ARCH "${PYTHON_EXECUTABLE}")
|
|
9
|
+
endif()
|
|
10
|
+
|
|
11
|
+
if(_MORSEFRAMES_PYTHON_FOR_ARCH)
|
|
12
|
+
execute_process(
|
|
13
|
+
COMMAND "${_MORSEFRAMES_PYTHON_FOR_ARCH}" -c
|
|
14
|
+
"import platform; print(platform.machine())"
|
|
15
|
+
OUTPUT_VARIABLE _MORSEFRAMES_PYTHON_ARCH
|
|
16
|
+
RESULT_VARIABLE _MORSEFRAMES_PYTHON_ARCH_RESULT
|
|
17
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
18
|
+
)
|
|
19
|
+
if(_MORSEFRAMES_PYTHON_ARCH_RESULT EQUAL 0
|
|
20
|
+
AND (_MORSEFRAMES_PYTHON_ARCH STREQUAL "arm64"
|
|
21
|
+
OR _MORSEFRAMES_PYTHON_ARCH STREQUAL "x86_64"))
|
|
22
|
+
set(CMAKE_OSX_ARCHITECTURES "${_MORSEFRAMES_PYTHON_ARCH}"
|
|
23
|
+
CACHE STRING "Build architectures for macOS" FORCE)
|
|
24
|
+
endif()
|
|
25
|
+
endif()
|
|
26
|
+
endif()
|
|
27
|
+
|
|
28
|
+
project(morseframes LANGUAGES CXX)
|
|
29
|
+
|
|
30
|
+
set(CMAKE_CXX_STANDARD 17)
|
|
31
|
+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
32
|
+
set(CMAKE_CXX_EXTENSIONS OFF)
|
|
33
|
+
|
|
34
|
+
if(DEFINED SKBUILD)
|
|
35
|
+
set(MORSEFRAMES_DEFAULT_BUILD_PYTHON ON)
|
|
36
|
+
set(MORSEFRAMES_DEFAULT_BUILD_TESTS OFF)
|
|
37
|
+
set(MORSEFRAMES_DEFAULT_BUILD_EXAMPLES OFF)
|
|
38
|
+
set(MORSEFRAMES_DEFAULT_BUILD_GUDHI_TOOLS OFF)
|
|
39
|
+
else()
|
|
40
|
+
set(MORSEFRAMES_DEFAULT_BUILD_PYTHON OFF)
|
|
41
|
+
set(MORSEFRAMES_DEFAULT_BUILD_TESTS ON)
|
|
42
|
+
set(MORSEFRAMES_DEFAULT_BUILD_EXAMPLES ON)
|
|
43
|
+
set(MORSEFRAMES_DEFAULT_BUILD_GUDHI_TOOLS ON)
|
|
44
|
+
endif()
|
|
45
|
+
|
|
46
|
+
option(MORSEFRAMES_BUILD_PYTHON
|
|
47
|
+
"Build nanobind Python extension"
|
|
48
|
+
${MORSEFRAMES_DEFAULT_BUILD_PYTHON})
|
|
49
|
+
option(MORSEFRAMES_BUILD_TESTS
|
|
50
|
+
"Build MorseFrames C++ tests"
|
|
51
|
+
${MORSEFRAMES_DEFAULT_BUILD_TESTS})
|
|
52
|
+
option(MORSEFRAMES_BUILD_EXAMPLES
|
|
53
|
+
"Build MorseFrames C++ examples"
|
|
54
|
+
${MORSEFRAMES_DEFAULT_BUILD_EXAMPLES})
|
|
55
|
+
option(MORSEFRAMES_BUILD_GUDHI_TOOLS
|
|
56
|
+
"Build optional GUDHI Simplex_tree integration tests and benchmark when headers are available"
|
|
57
|
+
${MORSEFRAMES_DEFAULT_BUILD_GUDHI_TOOLS})
|
|
58
|
+
set(MORSEFRAMES_GUDHI_INCLUDE_DIR "" CACHE PATH
|
|
59
|
+
"Optional path to a GUDHI include directory containing gudhi/Simplex_tree.h")
|
|
60
|
+
set(MORSEFRAMES_BOOST_INCLUDE_DIR "" CACHE PATH
|
|
61
|
+
"Optional path to a Boost include directory needed by GUDHI headers")
|
|
62
|
+
|
|
63
|
+
if(MORSEFRAMES_BUILD_GUDHI_TOOLS)
|
|
64
|
+
if(NOT MORSEFRAMES_GUDHI_INCLUDE_DIR)
|
|
65
|
+
find_path(MORSEFRAMES_GUDHI_INCLUDE_DIR
|
|
66
|
+
NAMES gudhi/Simplex_tree.h
|
|
67
|
+
HINTS
|
|
68
|
+
ENV GUDHI_ROOT
|
|
69
|
+
ENV GUDHI_DIR
|
|
70
|
+
PATH_SUFFIXES include
|
|
71
|
+
DOC "Path to a GUDHI include directory containing gudhi/Simplex_tree.h")
|
|
72
|
+
endif()
|
|
73
|
+
|
|
74
|
+
if(NOT MORSEFRAMES_BOOST_INCLUDE_DIR)
|
|
75
|
+
find_path(MORSEFRAMES_BOOST_INCLUDE_DIR
|
|
76
|
+
NAMES boost/core/empty_value.hpp
|
|
77
|
+
HINTS
|
|
78
|
+
ENV BOOST_ROOT
|
|
79
|
+
ENV Boost_ROOT
|
|
80
|
+
ENV CONDA_PREFIX
|
|
81
|
+
PATH_SUFFIXES include
|
|
82
|
+
DOC "Path to a Boost include directory needed by GUDHI headers")
|
|
83
|
+
endif()
|
|
84
|
+
endif()
|
|
85
|
+
|
|
86
|
+
add_library(morseframes INTERFACE)
|
|
87
|
+
target_include_directories(morseframes INTERFACE
|
|
88
|
+
${CMAKE_CURRENT_SOURCE_DIR}/include
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
if(MORSEFRAMES_BUILD_TESTS)
|
|
92
|
+
add_executable(morseframes_tests
|
|
93
|
+
tests/test_morse_reference.cpp
|
|
94
|
+
)
|
|
95
|
+
target_link_libraries(morseframes_tests PRIVATE morseframes)
|
|
96
|
+
endif()
|
|
97
|
+
|
|
98
|
+
if(MORSEFRAMES_BUILD_EXAMPLES)
|
|
99
|
+
add_executable(morseframes_example_barcode
|
|
100
|
+
examples/example_barcode.cpp
|
|
101
|
+
)
|
|
102
|
+
target_link_libraries(morseframes_example_barcode PRIVATE morseframes)
|
|
103
|
+
endif()
|
|
104
|
+
|
|
105
|
+
if(MORSEFRAMES_BUILD_GUDHI_TOOLS
|
|
106
|
+
AND MORSEFRAMES_GUDHI_INCLUDE_DIR
|
|
107
|
+
AND EXISTS "${MORSEFRAMES_GUDHI_INCLUDE_DIR}/gudhi/Simplex_tree.h"
|
|
108
|
+
AND MORSEFRAMES_BOOST_INCLUDE_DIR
|
|
109
|
+
AND EXISTS "${MORSEFRAMES_BOOST_INCLUDE_DIR}/boost/core/empty_value.hpp")
|
|
110
|
+
add_executable(morseframes_gudhi_simplex_tree_view_tests
|
|
111
|
+
tests/test_gudhi_simplex_tree_view.cpp
|
|
112
|
+
)
|
|
113
|
+
target_link_libraries(morseframes_gudhi_simplex_tree_view_tests PRIVATE morseframes)
|
|
114
|
+
target_include_directories(morseframes_gudhi_simplex_tree_view_tests PRIVATE
|
|
115
|
+
"${MORSEFRAMES_GUDHI_INCLUDE_DIR}"
|
|
116
|
+
"${MORSEFRAMES_BOOST_INCLUDE_DIR}"
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
add_executable(morseframes_benchmark_gudhi_view
|
|
120
|
+
benchmarks/benchmark_gudhi_view.cpp
|
|
121
|
+
)
|
|
122
|
+
target_link_libraries(morseframes_benchmark_gudhi_view PRIVATE morseframes)
|
|
123
|
+
target_include_directories(morseframes_benchmark_gudhi_view PRIVATE
|
|
124
|
+
"${MORSEFRAMES_GUDHI_INCLUDE_DIR}"
|
|
125
|
+
"${MORSEFRAMES_BOOST_INCLUDE_DIR}"
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
add_executable(morseframes_gudhi_simplex_tree_example
|
|
129
|
+
examples/gudhi_simplex_tree_morse.cpp
|
|
130
|
+
)
|
|
131
|
+
target_link_libraries(morseframes_gudhi_simplex_tree_example PRIVATE morseframes)
|
|
132
|
+
target_include_directories(morseframes_gudhi_simplex_tree_example PRIVATE
|
|
133
|
+
"${MORSEFRAMES_GUDHI_INCLUDE_DIR}"
|
|
134
|
+
"${MORSEFRAMES_BOOST_INCLUDE_DIR}"
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
add_executable(morseframes_gudhi_style_simplex_tree_example
|
|
138
|
+
examples/example_morse_persistence_from_simplex_tree.cpp
|
|
139
|
+
)
|
|
140
|
+
target_link_libraries(morseframes_gudhi_style_simplex_tree_example PRIVATE morseframes)
|
|
141
|
+
target_include_directories(morseframes_gudhi_style_simplex_tree_example PRIVATE
|
|
142
|
+
"${MORSEFRAMES_GUDHI_INCLUDE_DIR}"
|
|
143
|
+
"${MORSEFRAMES_BOOST_INCLUDE_DIR}"
|
|
144
|
+
)
|
|
145
|
+
elseif(MORSEFRAMES_BUILD_GUDHI_TOOLS
|
|
146
|
+
AND MORSEFRAMES_GUDHI_INCLUDE_DIR
|
|
147
|
+
AND EXISTS "${MORSEFRAMES_GUDHI_INCLUDE_DIR}/gudhi/Simplex_tree.h")
|
|
148
|
+
message(STATUS
|
|
149
|
+
"Skipping GUDHI Simplex_tree tools because Boost headers were not found. "
|
|
150
|
+
"Set MORSEFRAMES_BOOST_INCLUDE_DIR or BOOST_ROOT to enable them.")
|
|
151
|
+
elseif(MORSEFRAMES_BUILD_GUDHI_TOOLS)
|
|
152
|
+
message(STATUS
|
|
153
|
+
"Skipping GUDHI Simplex_tree tools because GUDHI headers were not found. "
|
|
154
|
+
"Set MORSEFRAMES_GUDHI_INCLUDE_DIR or GUDHI_ROOT to enable them.")
|
|
155
|
+
endif()
|
|
156
|
+
|
|
157
|
+
if(MORSEFRAMES_BUILD_PYTHON)
|
|
158
|
+
find_package(Python COMPONENTS Interpreter Development.Module REQUIRED)
|
|
159
|
+
execute_process(
|
|
160
|
+
COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
|
|
161
|
+
OUTPUT_STRIP_TRAILING_WHITESPACE
|
|
162
|
+
OUTPUT_VARIABLE nanobind_ROOT
|
|
163
|
+
)
|
|
164
|
+
find_package(nanobind CONFIG REQUIRED)
|
|
165
|
+
|
|
166
|
+
nanobind_add_module(_morse_core
|
|
167
|
+
python/morseframes/_core_bindings.cpp
|
|
168
|
+
)
|
|
169
|
+
target_link_libraries(_morse_core PRIVATE morseframes)
|
|
170
|
+
set_target_properties(_morse_core PROPERTIES
|
|
171
|
+
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/python/morseframes"
|
|
172
|
+
)
|
|
173
|
+
if(DEFINED SKBUILD)
|
|
174
|
+
install(TARGETS _morse_core
|
|
175
|
+
LIBRARY DESTINATION morseframes
|
|
176
|
+
RUNTIME DESTINATION morseframes
|
|
177
|
+
)
|
|
178
|
+
endif()
|
|
179
|
+
endif()
|
|
180
|
+
|
|
181
|
+
enable_testing()
|
|
182
|
+
if(TARGET morseframes_tests)
|
|
183
|
+
add_test(NAME morseframes_tests COMMAND morseframes_tests)
|
|
184
|
+
endif()
|
|
185
|
+
if(TARGET morseframes_gudhi_simplex_tree_view_tests)
|
|
186
|
+
add_test(NAME morseframes_gudhi_simplex_tree_view_tests
|
|
187
|
+
COMMAND morseframes_gudhi_simplex_tree_view_tests)
|
|
188
|
+
endif()
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
MorseFrames is research software. Contributions are welcome, especially small
|
|
4
|
+
tests, documentation fixes, reproducibility improvements, and focused bug
|
|
5
|
+
reports.
|
|
6
|
+
|
|
7
|
+
## Development Setup
|
|
8
|
+
|
|
9
|
+
From the repository root, build and test the C++ core:
|
|
10
|
+
|
|
11
|
+
```sh
|
|
12
|
+
cmake -S . -B build -DMORSEFRAMES_BUILD_GUDHI_TOOLS=OFF
|
|
13
|
+
cmake --build build --parallel
|
|
14
|
+
ctest --test-dir build --output-on-failure
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Install the Python package in editable mode:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
python3 -m pip install -e ".[dev]"
|
|
21
|
+
python3 -c "import morseframes as mf; print(mf.__version__, mf.cpp_backend_available())"
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Run the pure-Python fallback tests:
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
MORSEFRAMES_DISABLE_CPP_BACKEND=1 python3 -m unittest discover -s python/tests -p "test_*.py"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Run the quickstart example:
|
|
31
|
+
|
|
32
|
+
```sh
|
|
33
|
+
python3 python/examples/quickstart.py
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Documentation
|
|
37
|
+
|
|
38
|
+
Install the documentation dependencies and build the Sphinx site:
|
|
39
|
+
|
|
40
|
+
```sh
|
|
41
|
+
python3 -m pip install -r docs/requirements.txt
|
|
42
|
+
MORSEFRAMES_DISABLE_CPP_BACKEND=1 python3 -m sphinx -W --keep-going -b html docs docs/_build/html
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
The public documentation is hosted at:
|
|
46
|
+
|
|
47
|
+
<https://morseframes.readthedocs.io/>
|
|
48
|
+
|
|
49
|
+
## Optional GUDHI Tools
|
|
50
|
+
|
|
51
|
+
The GUDHI-facing examples, tests, and benchmarks are enabled when GUDHI and
|
|
52
|
+
Boost headers are available:
|
|
53
|
+
|
|
54
|
+
```sh
|
|
55
|
+
cmake -S . -B build \
|
|
56
|
+
-DMORSEFRAMES_GUDHI_INCLUDE_DIR=/path/to/gudhi/include \
|
|
57
|
+
-DMORSEFRAMES_BOOST_INCLUDE_DIR=/path/to/boost/include
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Benchmarks
|
|
61
|
+
|
|
62
|
+
Benchmark and table-generation scripts live in `tools/`. The public
|
|
63
|
+
reproduction instructions are in:
|
|
64
|
+
|
|
65
|
+
```text
|
|
66
|
+
docs/benchmark_reproduction.md
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Use benchmark results as comparative signals rather than absolute machine
|
|
70
|
+
independent timings. When changing algorithms or reducers, prefer reporting
|
|
71
|
+
ratios, dataset metadata, and repeated-run summaries.
|
|
72
|
+
|
|
73
|
+
## Pull Request Checklist
|
|
74
|
+
|
|
75
|
+
- Keep changes focused.
|
|
76
|
+
- Add or update tests when behavior changes.
|
|
77
|
+
- Run the C++ tests, Python fallback tests, and documentation build when
|
|
78
|
+
touching the corresponding surface.
|
|
79
|
+
- Keep generated scratch files out of commits.
|
|
80
|
+
- Do not commit private manuscript drafts or private discussion notes to this
|
|
81
|
+
public repository.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Laurent Najman
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|