geom-nmf 0.1.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.
geom_nmf-0.1.0/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Bora Jin
4
+ Copyright (c) 2026 QuantAQ (initial project scaffolding)
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in all
14
+ copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ SOFTWARE.
@@ -0,0 +1,324 @@
1
+ Metadata-Version: 2.4
2
+ Name: geom-nmf
3
+ Version: 0.1.0
4
+ Summary: Geometric non-negative matrix factorization (GeomNMF) via maximum-volume simplex fitting.
5
+ License-Expression: MIT
6
+ License-File: LICENSE
7
+ Keywords: nmf,non-negative matrix factorization,source apportionment,endmember extraction,simplex,unmixing
8
+ Author: Bora Jin
9
+ Author-email: jinbora0720@gmail.com
10
+ Requires-Python: >=3.9
11
+ Classifier: Development Status :: 3 - Alpha
12
+ Classifier: Intended Audience :: Science/Research
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3 :: Only
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Scientific/Engineering
22
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
23
+ Classifier: Typing :: Typed
24
+ Provides-Extra: all
25
+ Provides-Extra: plot
26
+ Provides-Extra: progress
27
+ Requires-Dist: matplotlib (>=3.5) ; extra == "all"
28
+ Requires-Dist: matplotlib (>=3.5) ; extra == "plot"
29
+ Requires-Dist: numpy (>=1.21)
30
+ Requires-Dist: scikit-learn (>=1.0)
31
+ Requires-Dist: scipy (>=1.7)
32
+ Requires-Dist: tqdm (>=4.60) ; extra == "all"
33
+ Requires-Dist: tqdm (>=4.60) ; extra == "progress"
34
+ Project-URL: Bug Tracker, https://github.com/jinbora0720/geom-nmf/issues
35
+ Project-URL: Changelog, https://github.com/jinbora0720/geom-nmf/blob/main/CHANGELOG.md
36
+ Project-URL: Documentation, https://github.com/jinbora0720/geom-nmf/tree/main/docs
37
+ Project-URL: Homepage, https://github.com/jinbora0720/geom-nmf
38
+ Project-URL: Repository, https://github.com/jinbora0720/geom-nmf
39
+ Description-Content-Type: text/markdown
40
+
41
+ # geom-nmf
42
+
43
+ ![Tests](https://github.com/jinbora0720/geom-nmf/actions/workflows/tests.yml/badge.svg)
44
+ ![Python Versions](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)
45
+ ![License](https://img.shields.io/badge/license-MIT-green)
46
+
47
+ **Geometric non-negative matrix factorization (GeomNMF)** via maximum-volume simplex fitting.
48
+
49
+ GeomNMF assumes a nonnegative data matrix follows a simplex mixing model
50
+
51
+ ```
52
+ Y ≈ W H, W ≥ 0, H ≥ 0, H 1 = 1
53
+ ```
54
+
55
+ and estimates the source profiles `H` (endmembers) as the vertices of the maximum-volume
56
+ (K−1)-simplex inscribed in the convex hull of the row-normalized data. It then recovers the
57
+ mixing weights `W` and a source attribution matrix `Φ` giving the share of each feature
58
+ attributable to each source. Typical applications include source apportionment of
59
+ multi-pollutant air quality data and spectral unmixing.
60
+
61
+ # Getting Started
62
+
63
+ ## Installation
64
+
65
+ Install the latest version directly from GitHub:
66
+
67
+ ```sh
68
+ pip install git+https://github.com/jinbora0720/geom-nmf.git
69
+ ```
70
+
71
+ With optional plotting (matplotlib) and progress-bar (tqdm) support:
72
+
73
+ ```sh
74
+ pip install "geom-nmf[all] @ git+https://github.com/jinbora0720/geom-nmf.git"
75
+ ```
76
+
77
+ | Extra | Enables |
78
+ |------------|-------------------------------------------------------------|
79
+ | `plot` | `barplot_Phi` (installs matplotlib) |
80
+ | `progress` | tqdm progress bar for the max-volume search (`verbose=True`) |
81
+ | `all` | both |
82
+
83
+ Install from a specific branch or tag:
84
+
85
+ ```sh
86
+ pip install git+https://github.com/jinbora0720/geom-nmf.git@<branch-name-or-tag>
87
+ ```
88
+
89
+ ## Quick example
90
+
91
+ ```python
92
+ import numpy as np
93
+ from geom_nmf import GeomNMF, barplot_Phi, permute_to_reference
94
+
95
+ rng = np.random.default_rng(0)
96
+ K, J, N = 3, 6, 1000
97
+ H_true = rng.dirichlet(np.ones(J), size=K) # (K, J) source profiles
98
+ W_true = rng.dirichlet(0.5 * np.ones(K), size=N) # (N, K) mixing proportions
99
+ Y = rng.lognormal(size=(N, 1)) * (W_true @ H_true) # (N, J) nonnegative data
100
+
101
+ result = GeomNMF(Y, K=3)
102
+
103
+ result.H_star # (K, J) estimated source profiles, rows sum to 1
104
+ result.W_tilde # (N, K) source contributions on the original scale of Y
105
+ result.mu_tilde # (K,) mean contribution per source
106
+ result.Phi # (K, J) share of each feature attributable to each source
107
+ result.fit_diagnostics["intrinsic_rank"] # should be K - 1 for noise-free data
108
+
109
+ # sources are identified up to permutation; align with a reference if you have one
110
+ H_hat, mu_hat, Phi_hat, order = permute_to_reference(H_true, result.H_star, result.mu_tilde, result.Phi)
111
+
112
+ barplot_Phi(Phi_hat, feature_labels=["PM1", "PM2.5", "PM10", "CO", "NO2", "O3"])
113
+ ```
114
+
115
+ `Y` can be a NumPy array, a pandas DataFrame, or any 2-D array-like. A runnable version is in
116
+ [`examples/quickstart.py`](examples/quickstart.py).
117
+
118
+ ## Public API
119
+
120
+ | Function / class | Purpose |
121
+ |-------------------------|------------------------------------------------------------------|
122
+ | `GeomNMF` | Full pipeline; returns a `GeomNMFResult` named tuple |
123
+ | `nfindr` | Standalone N-FINDR endmember extraction |
124
+ | `log_simplex_volume` | Log-volume of the simplex spanned by K points |
125
+ | `estimate_weights` | Recover simplex weights `W` given `Y` and `H` |
126
+ | `compute_Phi` | Source attribution matrix from mean weights and `H` |
127
+ | `permute_to_reference` | Align estimated sources to a reference ordering (Hungarian) |
128
+ | `barplot_Phi` | Grouped bar plot of `Φ`, optionally with bootstrap error bars |
129
+
130
+ ## Choosing options
131
+
132
+ - **`candidate_method="exact"`** (default) uses convex-hull vertices and is globally optimal,
133
+ but the number of hull vertices grows quickly for noisy, high-dimensional data.
134
+ - **`candidate_method="random"`** uses extremes along random projections; cap the pool with
135
+ `n_candidates`.
136
+ - **`prune=True`** clusters candidates before the exhaustive search. It can discard the true
137
+ extremes, so pair it with **`refine_greedy=True`**, which runs N-FINDR swaps on the full data
138
+ and keeps the result only if it increases the simplex volume.
139
+ - A `RuntimeWarning` is raised if the exhaustive search would exceed 50 million subsets.
140
+
141
+ ## Contributing
142
+
143
+ We welcome contributions! If you'd like to fix a bug, add a feature, or improve documentation, follow the steps below. Don't worry if you're new to this — it's a straightforward process once you've done it a couple of times.
144
+
145
+ This project uses [Poetry](https://python-poetry.org/) to manage dependencies and virtual environments.
146
+
147
+ ### 1. Fork the repository
148
+
149
+ A "fork" is your own personal copy of the project on GitHub. To create one, click the **Fork** button in the top-right corner of the [geom-nmf repository](https://github.com/jinbora0720/geom-nmf) page.
150
+
151
+ ### 2. Clone your fork
152
+
153
+ Download your fork to your local machine:
154
+
155
+ ```sh
156
+ git clone https://github.com/<your-github-username>/geom-nmf.git
157
+ cd geom-nmf
158
+ ```
159
+
160
+ ### 3. Install Poetry
161
+
162
+ If you don't already have Poetry installed, choose the instructions for your operating system below.
163
+
164
+ **macOS**
165
+
166
+ The recommended way on macOS is via [Homebrew](https://brew.sh/). If you don't have Homebrew installed, install it first:
167
+
168
+ ```sh
169
+ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
170
+ ```
171
+
172
+ Then install Poetry:
173
+
174
+ ```sh
175
+ brew install poetry
176
+ ```
177
+
178
+ **Linux / Windows (WSL)**
179
+
180
+ ```sh
181
+ curl -sSL https://install.python-poetry.org | python3 -
182
+ ```
183
+
184
+ Then follow any instructions it prints to add Poetry to your `PATH`.
185
+
186
+ **Verify the installation**
187
+
188
+ ```sh
189
+ poetry --version
190
+ ```
191
+
192
+ ### 4. Install dependencies
193
+
194
+ This installs both the main dependencies and the development dependencies (e.g. pytest, sphinx) into an isolated virtual environment managed by Poetry:
195
+
196
+ ```sh
197
+ poetry install
198
+ ```
199
+
200
+ To also install the optional coverage or documentation tools:
201
+
202
+ ```sh
203
+ poetry install --with coverage
204
+ poetry install --with docs
205
+ ```
206
+
207
+ You only need to run this once (and again whenever dependencies change).
208
+
209
+ To activate the virtual environment (Poetry ≥ 2.0):
210
+
211
+ ```sh
212
+ eval $(poetry env activate)
213
+ ```
214
+
215
+ On Poetry 1.x, use `poetry shell` instead.
216
+
217
+ Your prompt will change to indicate you're inside the environment. To exit when you're done:
218
+
219
+ ```sh
220
+ exit
221
+ ```
222
+
223
+ Alternatively, prefix any command with `poetry run` to run it inside the environment without activating it:
224
+
225
+ ```sh
226
+ poetry run pytest tests/
227
+ ```
228
+
229
+ ### 5. Create a branch
230
+
231
+ Never make changes directly on `main`. Instead, create a new branch with a short descriptive name:
232
+
233
+ ```sh
234
+ git checkout -b my-feature-branch
235
+ ```
236
+
237
+ ### 6. Make your changes
238
+
239
+ Edit files, write code, fix bugs — whatever your contribution involves. Keep your changes focused on one thing per branch.
240
+
241
+ ### 7. Run the tests
242
+
243
+ Before submitting, make sure all tests pass:
244
+
245
+ ```sh
246
+ poetry run pytest tests/
247
+ ```
248
+
249
+ If you've added new functionality, add a corresponding test in the `tests/` directory.
250
+
251
+ Common flags:
252
+
253
+ | Flag | Description |
254
+ |------|-------------|
255
+ | `-v` | Verbose output — shows each test name and pass/fail status |
256
+ | `-q` | Quiet output — minimal output, just a summary |
257
+ | `-x` | Stop after the first failure |
258
+ | `-s` | Disable output capture — allows `print()` statements to show in terminal |
259
+ | `-k "expression"` | Run only tests whose names match the expression (e.g. `-k "test_fit"`) |
260
+ | `--tb=short` | Show a shortened traceback on failures (`short`, `long`, `no`, `line`) |
261
+ | `--cov=geom_nmf` | Report test coverage for the package (requires `pytest-cov`) |
262
+
263
+ Examples:
264
+
265
+ ```sh
266
+ # Run all tests verbosely
267
+ pytest -v tests/
268
+
269
+ # Stop on first failure with full traceback
270
+ pytest -x --tb=long tests/
271
+
272
+ # Run only tests matching a keyword
273
+ pytest -v -k "test_fit" tests/
274
+
275
+ # Run with coverage report
276
+ pytest --cov=geom_nmf --cov-report=term-missing tests/
277
+ ```
278
+
279
+ ### 8. Commit and push your changes
280
+
281
+ Stage and commit your changes with a clear message describing what you did:
282
+
283
+ ```sh
284
+ git add <file(s) you changed>
285
+ git commit -m "Brief description of your change"
286
+ git push origin my-feature-branch
287
+ ```
288
+
289
+ ### 9. Open a Pull Request
290
+
291
+ Go to your fork on GitHub. You should see a prompt to open a Pull Request — click it. Fill in a short description of what your changes do and why. A maintainer will review your PR and may leave comments or request changes before merging.
292
+
293
+ ### Tips
294
+
295
+ - Keep pull requests small and focused — one feature or fix per PR is much easier to review
296
+ - Check that your branch is up to date with `main` before opening a PR:
297
+ ```sh
298
+ git fetch origin
299
+ git rebase origin/main
300
+ ```
301
+ - To add a new dependency: `poetry add <package>` (or `poetry add --group dev <package>` for dev-only)
302
+ - If you're unsure about a change, open an issue first to discuss it before writing code
303
+
304
+ ## Building Documentation
305
+
306
+ The documentation is built with [Sphinx](https://www.sphinx-doc.org/) from the NumPy-style
307
+ docstrings in the source code.
308
+
309
+ ```sh
310
+ poetry install --with docs
311
+ poetry run make -C docs html
312
+ ```
313
+
314
+ Then open `docs/_build/html/index.html` in your browser. The build treats warnings as errors
315
+ (`-W`), so broken references or malformed docstrings will fail the build.
316
+
317
+ ## Building a distribution
318
+
319
+ ```sh
320
+ poetry build # writes dist/geom_nmf-<version>.tar.gz and .whl
321
+ ```
322
+
323
+ &copy; 2026 Bora Jin. Released under the [MIT License](LICENSE).
324
+
@@ -0,0 +1,283 @@
1
+ # geom-nmf
2
+
3
+ ![Tests](https://github.com/jinbora0720/geom-nmf/actions/workflows/tests.yml/badge.svg)
4
+ ![Python Versions](https://img.shields.io/badge/python-3.9%20%7C%203.10%20%7C%203.11%20%7C%203.12%20%7C%203.13-blue)
5
+ ![License](https://img.shields.io/badge/license-MIT-green)
6
+
7
+ **Geometric non-negative matrix factorization (GeomNMF)** via maximum-volume simplex fitting.
8
+
9
+ GeomNMF assumes a nonnegative data matrix follows a simplex mixing model
10
+
11
+ ```
12
+ Y ≈ W H, W ≥ 0, H ≥ 0, H 1 = 1
13
+ ```
14
+
15
+ and estimates the source profiles `H` (endmembers) as the vertices of the maximum-volume
16
+ (K−1)-simplex inscribed in the convex hull of the row-normalized data. It then recovers the
17
+ mixing weights `W` and a source attribution matrix `Φ` giving the share of each feature
18
+ attributable to each source. Typical applications include source apportionment of
19
+ multi-pollutant air quality data and spectral unmixing.
20
+
21
+ # Getting Started
22
+
23
+ ## Installation
24
+
25
+ Install the latest version directly from GitHub:
26
+
27
+ ```sh
28
+ pip install git+https://github.com/jinbora0720/geom-nmf.git
29
+ ```
30
+
31
+ With optional plotting (matplotlib) and progress-bar (tqdm) support:
32
+
33
+ ```sh
34
+ pip install "geom-nmf[all] @ git+https://github.com/jinbora0720/geom-nmf.git"
35
+ ```
36
+
37
+ | Extra | Enables |
38
+ |------------|-------------------------------------------------------------|
39
+ | `plot` | `barplot_Phi` (installs matplotlib) |
40
+ | `progress` | tqdm progress bar for the max-volume search (`verbose=True`) |
41
+ | `all` | both |
42
+
43
+ Install from a specific branch or tag:
44
+
45
+ ```sh
46
+ pip install git+https://github.com/jinbora0720/geom-nmf.git@<branch-name-or-tag>
47
+ ```
48
+
49
+ ## Quick example
50
+
51
+ ```python
52
+ import numpy as np
53
+ from geom_nmf import GeomNMF, barplot_Phi, permute_to_reference
54
+
55
+ rng = np.random.default_rng(0)
56
+ K, J, N = 3, 6, 1000
57
+ H_true = rng.dirichlet(np.ones(J), size=K) # (K, J) source profiles
58
+ W_true = rng.dirichlet(0.5 * np.ones(K), size=N) # (N, K) mixing proportions
59
+ Y = rng.lognormal(size=(N, 1)) * (W_true @ H_true) # (N, J) nonnegative data
60
+
61
+ result = GeomNMF(Y, K=3)
62
+
63
+ result.H_star # (K, J) estimated source profiles, rows sum to 1
64
+ result.W_tilde # (N, K) source contributions on the original scale of Y
65
+ result.mu_tilde # (K,) mean contribution per source
66
+ result.Phi # (K, J) share of each feature attributable to each source
67
+ result.fit_diagnostics["intrinsic_rank"] # should be K - 1 for noise-free data
68
+
69
+ # sources are identified up to permutation; align with a reference if you have one
70
+ H_hat, mu_hat, Phi_hat, order = permute_to_reference(H_true, result.H_star, result.mu_tilde, result.Phi)
71
+
72
+ barplot_Phi(Phi_hat, feature_labels=["PM1", "PM2.5", "PM10", "CO", "NO2", "O3"])
73
+ ```
74
+
75
+ `Y` can be a NumPy array, a pandas DataFrame, or any 2-D array-like. A runnable version is in
76
+ [`examples/quickstart.py`](examples/quickstart.py).
77
+
78
+ ## Public API
79
+
80
+ | Function / class | Purpose |
81
+ |-------------------------|------------------------------------------------------------------|
82
+ | `GeomNMF` | Full pipeline; returns a `GeomNMFResult` named tuple |
83
+ | `nfindr` | Standalone N-FINDR endmember extraction |
84
+ | `log_simplex_volume` | Log-volume of the simplex spanned by K points |
85
+ | `estimate_weights` | Recover simplex weights `W` given `Y` and `H` |
86
+ | `compute_Phi` | Source attribution matrix from mean weights and `H` |
87
+ | `permute_to_reference` | Align estimated sources to a reference ordering (Hungarian) |
88
+ | `barplot_Phi` | Grouped bar plot of `Φ`, optionally with bootstrap error bars |
89
+
90
+ ## Choosing options
91
+
92
+ - **`candidate_method="exact"`** (default) uses convex-hull vertices and is globally optimal,
93
+ but the number of hull vertices grows quickly for noisy, high-dimensional data.
94
+ - **`candidate_method="random"`** uses extremes along random projections; cap the pool with
95
+ `n_candidates`.
96
+ - **`prune=True`** clusters candidates before the exhaustive search. It can discard the true
97
+ extremes, so pair it with **`refine_greedy=True`**, which runs N-FINDR swaps on the full data
98
+ and keeps the result only if it increases the simplex volume.
99
+ - A `RuntimeWarning` is raised if the exhaustive search would exceed 50 million subsets.
100
+
101
+ ## Contributing
102
+
103
+ We welcome contributions! If you'd like to fix a bug, add a feature, or improve documentation, follow the steps below. Don't worry if you're new to this — it's a straightforward process once you've done it a couple of times.
104
+
105
+ This project uses [Poetry](https://python-poetry.org/) to manage dependencies and virtual environments.
106
+
107
+ ### 1. Fork the repository
108
+
109
+ A "fork" is your own personal copy of the project on GitHub. To create one, click the **Fork** button in the top-right corner of the [geom-nmf repository](https://github.com/jinbora0720/geom-nmf) page.
110
+
111
+ ### 2. Clone your fork
112
+
113
+ Download your fork to your local machine:
114
+
115
+ ```sh
116
+ git clone https://github.com/<your-github-username>/geom-nmf.git
117
+ cd geom-nmf
118
+ ```
119
+
120
+ ### 3. Install Poetry
121
+
122
+ If you don't already have Poetry installed, choose the instructions for your operating system below.
123
+
124
+ **macOS**
125
+
126
+ The recommended way on macOS is via [Homebrew](https://brew.sh/). If you don't have Homebrew installed, install it first:
127
+
128
+ ```sh
129
+ /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
130
+ ```
131
+
132
+ Then install Poetry:
133
+
134
+ ```sh
135
+ brew install poetry
136
+ ```
137
+
138
+ **Linux / Windows (WSL)**
139
+
140
+ ```sh
141
+ curl -sSL https://install.python-poetry.org | python3 -
142
+ ```
143
+
144
+ Then follow any instructions it prints to add Poetry to your `PATH`.
145
+
146
+ **Verify the installation**
147
+
148
+ ```sh
149
+ poetry --version
150
+ ```
151
+
152
+ ### 4. Install dependencies
153
+
154
+ This installs both the main dependencies and the development dependencies (e.g. pytest, sphinx) into an isolated virtual environment managed by Poetry:
155
+
156
+ ```sh
157
+ poetry install
158
+ ```
159
+
160
+ To also install the optional coverage or documentation tools:
161
+
162
+ ```sh
163
+ poetry install --with coverage
164
+ poetry install --with docs
165
+ ```
166
+
167
+ You only need to run this once (and again whenever dependencies change).
168
+
169
+ To activate the virtual environment (Poetry ≥ 2.0):
170
+
171
+ ```sh
172
+ eval $(poetry env activate)
173
+ ```
174
+
175
+ On Poetry 1.x, use `poetry shell` instead.
176
+
177
+ Your prompt will change to indicate you're inside the environment. To exit when you're done:
178
+
179
+ ```sh
180
+ exit
181
+ ```
182
+
183
+ Alternatively, prefix any command with `poetry run` to run it inside the environment without activating it:
184
+
185
+ ```sh
186
+ poetry run pytest tests/
187
+ ```
188
+
189
+ ### 5. Create a branch
190
+
191
+ Never make changes directly on `main`. Instead, create a new branch with a short descriptive name:
192
+
193
+ ```sh
194
+ git checkout -b my-feature-branch
195
+ ```
196
+
197
+ ### 6. Make your changes
198
+
199
+ Edit files, write code, fix bugs — whatever your contribution involves. Keep your changes focused on one thing per branch.
200
+
201
+ ### 7. Run the tests
202
+
203
+ Before submitting, make sure all tests pass:
204
+
205
+ ```sh
206
+ poetry run pytest tests/
207
+ ```
208
+
209
+ If you've added new functionality, add a corresponding test in the `tests/` directory.
210
+
211
+ Common flags:
212
+
213
+ | Flag | Description |
214
+ |------|-------------|
215
+ | `-v` | Verbose output — shows each test name and pass/fail status |
216
+ | `-q` | Quiet output — minimal output, just a summary |
217
+ | `-x` | Stop after the first failure |
218
+ | `-s` | Disable output capture — allows `print()` statements to show in terminal |
219
+ | `-k "expression"` | Run only tests whose names match the expression (e.g. `-k "test_fit"`) |
220
+ | `--tb=short` | Show a shortened traceback on failures (`short`, `long`, `no`, `line`) |
221
+ | `--cov=geom_nmf` | Report test coverage for the package (requires `pytest-cov`) |
222
+
223
+ Examples:
224
+
225
+ ```sh
226
+ # Run all tests verbosely
227
+ pytest -v tests/
228
+
229
+ # Stop on first failure with full traceback
230
+ pytest -x --tb=long tests/
231
+
232
+ # Run only tests matching a keyword
233
+ pytest -v -k "test_fit" tests/
234
+
235
+ # Run with coverage report
236
+ pytest --cov=geom_nmf --cov-report=term-missing tests/
237
+ ```
238
+
239
+ ### 8. Commit and push your changes
240
+
241
+ Stage and commit your changes with a clear message describing what you did:
242
+
243
+ ```sh
244
+ git add <file(s) you changed>
245
+ git commit -m "Brief description of your change"
246
+ git push origin my-feature-branch
247
+ ```
248
+
249
+ ### 9. Open a Pull Request
250
+
251
+ Go to your fork on GitHub. You should see a prompt to open a Pull Request — click it. Fill in a short description of what your changes do and why. A maintainer will review your PR and may leave comments or request changes before merging.
252
+
253
+ ### Tips
254
+
255
+ - Keep pull requests small and focused — one feature or fix per PR is much easier to review
256
+ - Check that your branch is up to date with `main` before opening a PR:
257
+ ```sh
258
+ git fetch origin
259
+ git rebase origin/main
260
+ ```
261
+ - To add a new dependency: `poetry add <package>` (or `poetry add --group dev <package>` for dev-only)
262
+ - If you're unsure about a change, open an issue first to discuss it before writing code
263
+
264
+ ## Building Documentation
265
+
266
+ The documentation is built with [Sphinx](https://www.sphinx-doc.org/) from the NumPy-style
267
+ docstrings in the source code.
268
+
269
+ ```sh
270
+ poetry install --with docs
271
+ poetry run make -C docs html
272
+ ```
273
+
274
+ Then open `docs/_build/html/index.html` in your browser. The build treats warnings as errors
275
+ (`-W`), so broken references or malformed docstrings will fail the build.
276
+
277
+ ## Building a distribution
278
+
279
+ ```sh
280
+ poetry build # writes dist/geom_nmf-<version>.tar.gz and .whl
281
+ ```
282
+
283
+ &copy; 2026 Bora Jin. Released under the [MIT License](LICENSE).