Metadata-Version: 2.5
Name: findakin
Version: 0.1.1
Summary: Population-level questions on collections of astronomical spectra: similarity search, clustering, anomalies, trajectories and encoder benchmarks on a local LanceDB store, with embeddings from spectro-kernel.
Project-URL: Homepage, https://findakin.lelain.net
Project-URL: Repository, https://github.com/matthieulel/findakin
Project-URL: Documentation, https://github.com/matthieulel/findakin/tree/main/docs
Project-URL: Changelog, https://github.com/matthieulel/findakin/blob/main/CHANGELOG.md
Author: matthieulel
License: MIT
License-File: LICENSE
Keywords: anomaly-detection,astronomy,clustering,embeddings,lancedb,mcp,similarity-search,spectra,spectroscopy
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Astronomy
Classifier: Topic :: Scientific/Engineering :: Physics
Requires-Python: >=3.11
Requires-Dist: astropy>=6.0
Requires-Dist: httpx>=0.27
Requires-Dist: lancedb>=0.15
Requires-Dist: numpy>=1.26
Requires-Dist: pandas>=2.0
Requires-Dist: pyarrow>=15
Requires-Dist: pydantic>=2.6
Requires-Dist: scikit-learn>=1.4
Requires-Dist: scipy>=1.12
Requires-Dist: spectro-kernel>=0.8
Requires-Dist: typer>=0.12
Provides-Extra: all
Requires-Dist: hdbscan>=0.8.38; extra == 'all'
Requires-Dist: plotly>=5.20; extra == 'all'
Requires-Dist: pyvo>=1.5; extra == 'all'
Requires-Dist: umap-learn>=0.5.6; extra == 'all'
Provides-Extra: analysis
Requires-Dist: hdbscan>=0.8.38; extra == 'analysis'
Requires-Dist: umap-learn>=0.5.6; extra == 'analysis'
Provides-Extra: dev
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest>=8.3; extra == 'dev'
Requires-Dist: ruff>=0.8; extra == 'dev'
Provides-Extra: encoders
Requires-Dist: torch>=2.2; extra == 'encoders'
Provides-Extra: mcp
Requires-Dist: fastmcp>=2.0; extra == 'mcp'
Provides-Extra: viz
Requires-Dist: plotly>=5.20; extra == 'viz'
Provides-Extra: vo
Requires-Dist: pyvo>=1.5; extra == 'vo'
Description-Content-Type: text/markdown

# Findakin

**Find a kin for every spectrum.**

Findakin answers *population* questions about collections of astronomical spectra:
which look alike, what structure they form, which stand out, which changed in time, and
which representation (encoder) is the most physical. It is a local Python library built on
[spectro-kernel](https://github.com/matthieulel/spectro-kernel) (one spectrum → one vector)
and [LanceDB](https://lancedb.com) (an embedded, versioned, columnar store with a vector
index). No server, no account: your spectra stay on your disk.

> **Status: 0.1 alpha, API may still change.** Findakin started as a hosted web application
> (FastAPI + MongoDB + Qdrant + React, retired in October 2026) and was rebuilt as a
> local-first library. Landing page: [findakin.lelain.net](https://findakin.lelain.net).

```
spectro-kernel  = algorithms on ONE spectrum   (embeddings, I/O, lines, RV, reduction…)
Findakin        = questions on MANY spectra    (index, neighbours, clusters, anomalies,
                                                trajectories, encoder benchmarks)
```

## Install

```bash
pip install findakin                 # core: store, ingestion, kernel recipes, k-NN
pip install "findakin[analysis]"     # + UMAP projection, HDBSCAN clustering
pip install "findakin[vo]"           # + SSAP / Virtual Observatory connectors
pip install "findakin[encoders]"     # + local PyTorch encoders (spectro-kernel embed_pretrained)
```

Python ≥ 3.11.

## Quick start

```python
from findakin import Store

st = Store.open("~/findakin/store")                 # a directory; created if missing

# 1. ingest — FITS (BeSS / ISIS / specINTI / Demetra dialects), CSV, JSON, TXT.
#    Spectra are deduplicated by content; metadata (object, time, instrument,
#    observer, site, resolution, coverage) is extracted from the headers.
report = st.ingest_folder("~/spectra/alphadra", source="staros", tags=["campaign:alphadra"])
print(report)                                       # 1012 files → 987 added, 25 duplicates, 0 errors

# 2. embed — any spectro-kernel strategy; several encoders coexist for the same spectra
enc = st.embed(strategy="dct", dim=256, norm_method="z_score")   # → 'dct:v1:256:nogrid:z_score'
enc_ll = st.embed(strategy="log_lambda", dim=256)                 # Doppler-robust recipe
# tip: with min_max the continuum term dominates the vector and every pair scores ≈ 0.99;
# z_score (zero mean) or continuum_subtracted let the lines drive the similarity.

# 3. ask
st.neighbours("~/spectra/new_alfdra.fits", k=10, encoder=enc)          # k-NN by file…
st.neighbours(some_id, k=10, encoder=enc, where="observer = 'mlelain'")  # …or by id, filtered
ids, X = st.vectors(enc, where="series_id = 'alf dra'")                 # numpy matrix for analysis

from findakin.analysis import cluster_embeddings, detect_anomalies, project_embeddings
labels = cluster_embeddings(X, method="hdbscan")
outliers = detect_anomalies(X, method="lof", contamination=0.02)
xy = project_embeddings(X, method="umap")
```

Same verbs on the command line:

```bash
findakin ingest ~/spectra/alphadra --source staros --tag campaign:alphadra
findakin embed --strategy dct --dim 256
findakin search ~/spectra/new_alfdra.fits -k 10
findakin info
```

## What is in the store

A LanceDB directory, versioned, rebuildable from your files:

| Table | Content |
|---|---|
| `spectra` | one row per spectrum: `id` (content hash), `source`, `object`, `series_id`, `t_mjd`, `instrument`, `observer`, `site`, `resolution`, `wl_min/wl_max`, `file_uri`, `tags`, raw header (JSON) |
| `flux_native` | the native wavelength / flux arrays (re-embed with a new encoder without re-reading files) |
| `emb__<encoder>` | one table per encoder: `(id, vector, provenance)` — `encoder_id = strategy:model_version:dim:grid:norm` |
| `encoders` | the encoder registry (config, dimension) |

`series_id` groups spectra that form a trajectory in time: an object followed over epochs
(default), a night of sky spectra (`series_from="night"`, e.g. aurorae), or a folder.

## Roadmap (short)

Phase 0 — store, ingestion, kernel encoders, k-NN, CLI ✅ · Phase 1 — common λ grids
(inter-instrument alignment), `flux__<grid>` tables, vector indexes · Phase 2 — named
populations, clusters / anomalies / projections stored as results, **trajectory anomalies**,
Plotly notebook helpers · Phase 3 — learned encoders registry, encoder benchmark with an
*instrument-leakage* score · Phase 4 — `findakin-mcp` over the store · Phase 5 — thin HTTP
façade for STAROS / SpecRelay.

## Development

```bash
uv venv .venv --python 3.12 && source .venv/bin/activate
uv pip install -e ".[dev]"
pytest -q
ruff check src tests
```

The landing page lives in [website/](website/) (Vite + React, deployed separately).

## License

MIT — © 2026 Matthieu Le Lain.
