Metadata-Version: 2.4
Name: cume
Version: 0.1.0
Summary: cume: GPU-accelerated PMF for Source Apportionment
License-Expression: Apache-2.0
Requires-Python: >=3.8
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: torch>=1.10.0
Requires-Dist: numpy>=1.20.0
Requires-Dist: scipy>=1.7.0
Requires-Dist: pandas>=1.3.0
Requires-Dist: matplotlib>=3.4.0
Requires-Dist: seaborn>=0.11.0
Requires-Dist: PyQt6>=6.4.0
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pyinstaller>=6.0; extra == "dev"
Dynamic: license-file

# cume

GPU-accelerated **Positive Matrix Factorization (PMF)** for source apportionment, with a
native desktop interface. cume is a general receptor-model tool — it works on any
sample × variable data matrix, not just mass spectra — and its workflow and diagnostics
follow EPA PMF 5.0 and SoFi.

The PMF engine runs on Apple Silicon (MPS), CUDA, or CPU via PyTorch; the UI is native
PyQt6 (no web layer), so plots are interactive and the interface stays responsive while a
run is in progress.

## Install (macOS)

Open `dist/cume-0.1.0.dmg` and drag **cume** onto Applications. First launch on an
unsigned-developer build: right-click the app → **Open**.

Windows and Linux installers are produced by CI (see **Building** below).

## Input data

A CSV where rows are samples and columns are variables (species/channels/features):

- **First column** is used as the time/sample index **only** if it's a date or ID column
  (header `date`/`time`/`datetime`/`timestamp`/`sample`/`id`, or values that parse as
  dates). Otherwise every column is treated as a variable and samples are numbered 0…N-1.
- Missing values are handled automatically (see preprocessing below); they should be blank
  or `NaN`, not zeros.
- Optionally supply a separate uncertainty CSV, or let cume compute uncertainties.

## Workflow

1. **Load data** — opens the **Data Quality** tab first.
2. **Data Quality** — review each variable's signal-to-noise (S/N) classification
   (strong / weak / bad) and edit `keep` / `×unc` before running. Below-detection and
   missing values are substituted per the EPA Polissar scheme.
3. **Set parameters** — device, number of factors (or scan a range), runs, init
   (random default, so multiple runs explore the solution space), robust mode,
   uncertainty formula.
4. *(Optional)* **Constraints** — reference source profiles (a-value constrained PMF),
   ratio constraints, and G pull-down (force a factor's contribution to ~0 over a window).
5. **Run PMF (base)** — solves the base model (best of N runs); results appear in the tabs.
   With multiple runs the **Base Runs** tab lets you pick the active run. **Rolling PMF**
   mode (windowed re-fits) is a separate solve with a Profile Evolution heatmap.
6. *(Optional)* **Run error estimation (reuse base)** — Bootstrap, DISP, and/or BS-DISP on
   the existing base solution, **without recomputing it**.

### Result tabs

Tabs appear as the analysis warrants; every plot has a zoom/pan/save toolbar.

- **Time Series**, **Profiles** (factor identification vs reference profiles, when supplied),
  **G-Space** (post-hoc fpeak rotation slider), **Diagnostics** (Q metrics + convergence).
- **Base Runs** (multi-run): per-run Q-convergence curves + diagnostics table; the lowest-Q
  run is recommended, but you can switch the active solution to any run.
- **Seed Stability**: factor-profile spread across runs (box-per-variable) + run-to-run cosine.
- **Factor Scan**: Q/Qexp and Q(true)/Q(robust) vs factor count, plus a %ΔQ "elbow" chart.
- **Bootstrap / DISP / BS-DISP**: error estimation. Profile uncertainty is box-per-variable;
  DISP/BS-DISP report the EPA dQmax swap table (a swap at dQmax=4 ⇒ not interpretable);
  Bootstrap reports the factor mapping rate.
- **a-value Sensitivity**: each factor's similarity to its reference vs the a-value.
- **Profile Evolution** (rolling): heatmap of each factor's profile across windows.

### Constraint input formats

- **Ratio** — `factor, num, den, target, weight` per line (0-based indices). Constrains
  `F[num]/F[den] ≈ target` within a factor.
- **G pull-down** — `factor, start-end, weight` per line. Pulls a factor's contribution to
  ~0 over the sample range (use a large weight). Multiple lines = multiple constraints.
- **G pull-to-value** — `factor, start-end, value, weight` per line. Pulls a factor's
  contribution toward a target value over the window (generalizes pull-up/down).

### Save / load / export

- **Save / Load results** (`.npz`) — archive a run and reopen it later for comparison.
- **Save / Load config** (`.json`) — reuse a parameter set across datasets.
- **History** — every base run's parameters + Q are logged (`~/.cume/run_history.jsonl`).
- **Name factors** — label factors (auto-suggested from the reference match); names flow
  into plots and CSV exports.
- **Export** — G / F / diagnostics CSV, a self-contained HTML report (with a reproducibility
  provenance block), and per-method error-estimation data (bootstrap mean/SD/mapping,
  DISP/BS-DISP swap counts and profile ranges).

## Run from source

```bash
python3 -m venv .venv
.venv/bin/pip install torch numpy scipy pandas matplotlib seaborn PyQt6
PYTHONPATH=src .venv/bin/python -m cume_qt
```

## Scripting / batch (CLI & API)

For automated or reproducible pipelines, use the command line or the Python API (no GUI):

```bash
pip install -e .
cume-pmf data.csv --factors 4 --runs 5 -o out   # → out_G.csv, out_F.csv, out_diagnostics.csv, out_manifest.json
cume-pmf data.csv --scan 2 6 -o out             # scan factor counts → out_scan.csv
```

```python
from cume import run_pmf, scan_factors
res = run_pmf("data.csv", n_factors=4, n_runs=5)
print(res["diagnostics"]["Q_ratio"], res["provenance"]["data_sha256_16"])
```

Every run records a provenance manifest (cume version, timestamp, data fingerprint,
parameters), so results are reproducible. A recovery benchmark (`tests/test_recovery.py`)
checks that known synthetic factors are recovered.

## Building

The code is cross-platform, but PyInstaller cannot cross-compile — each OS's installer is
built on that OS.

**macOS (local):**

```bash
python3 -m venv .venv-build
.venv-build/bin/pip install torch numpy scipy pandas matplotlib seaborn PyQt6 pyinstaller
.venv-build/bin/python tools/build_qt_dmg.py     # → dist/cume.app + dist/cume-0.1.0.dmg
```

**Signed / notarized distribution (optional):** with an Apple Developer ID, run
`tools/sign_notarize.sh` (see its header) to sign + notarize the dmg so it opens by
double-click on any Mac instead of right-click → Open.

**All three platforms (GitHub Actions):** `.github/workflows/build.yml` builds macOS
`.dmg`, Windows Inno Setup `.exe`, and Linux `.AppImage` on each push. See `BUILDING.md`
for enabling it. The repo is not yet under git; `git init` + push to GitHub to trigger CI.

## Project layout

```
src/cume/       PMF engine (solver, bootstrap, disp, bsdisp, rotate, preprocess,
                profile_match, report, plotting, constraints, rolling) + api, cli
src/cume_qt/    native PyQt6 app (main window, worker, plots)
tests/          test_recovery.py — synthetic factor-recovery benchmark
cume_qt.spec    cross-platform PyInstaller spec
tools/          build scripts          packaging/  Windows/Linux installer configs
```

See `DESIGN.md` for the feature mapping against EPA PMF 5.0 / SoFi, and `ROADMAP.md` for
the gap analysis.
