Metadata-Version: 2.4
Name: jacscanomaly
Version: 0.3.1
Summary: JAX-based scan anomaly detection for time-series residuals
Author: Kansuke Nunota
License: MIT
Project-URL: Homepage, https://github.com/NunotaKansuke/jacscanomaly
Project-URL: Repository, https://github.com/NunotaKansuke/jacscanomaly
Project-URL: Issues, https://github.com/NunotaKansuke/jacscanomaly/issues
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: jax
Requires-Dist: jaxopt
Requires-Dist: matplotlib
Dynamic: license-file

# jacscanomaly

**jacscanomaly** is a JAX-based framework for anomaly detection
in time-series data.

The package is designed to detect **microlensing planetary anomalies** by
scanning residuals after fitting a single lens model (e.g., PSPL),
while remaining fast thanks to JAX.

---

## Features

* **JAX-powered**: fast, vectorized grid scans with JIT compilation
* **Scan-based anomaly detection** on residuals
* **Built-in visualization**: PSPL fit, residuals, and anomaly scan summary

---

## Installation

```bash
pip install jacscanomaly
```

---

## Quick Example

```python
import numpy as np
import matplotlib.pyplot as plt
from jacscanomaly import Finder, FinderConfig

# load data (time, flux, flux_err)
data = np.load("example_data.npy")
time, flux, ferr = data[:, 0], data[:, 1], data[:, 2]

# run anomaly finder
config = FinderConfig(fitter_kind="pspl")
finder = Finder(config)
result = finder.run(time, flux, ferr)

# You can still pass an explicit initial guess if desired:
# p0 = np.array([10000, 10, 0.3])
# result = finder.run(time, flux, ferr, p0)

print("=== PSPL fit ===")
t0_pspl, tE_pspl, u0_pspl = result.fit.params
print(f"  t0          = {float(t0_pspl):.3f}")
print(f"  tE          = {float(tE_pspl):.3f}")
print(f"  u0          = {float(u0_pspl):.3f}")
print(f"  chi2 / dof  = {result.chi2_dof:.3f}\n")

b = result.best
print("=== Anomaly candidate ===")
print(f"  t0          = {b.t0:.3f}")
print(f"  teff        = {b.teff:.3f}")
print(f"  dchi2       = {b.dchi2:.3e}")
print(f"  score       = {b.score:.2f}")
```

---

## Visualization

```python
finder.plot_result()
finder.plot_anomaly_window()
plt.show()
```

These commands produce two complementary visualizations:

1. **Three-panel summary plot (`finder.plot_result`)**

   * **Top:** Observed light curve with the best-fit baseline model (PSPL)
   * **Middle:** Residuals after baseline fitting
   * **Bottom:** Anomaly scan result (Δχ² vs. time), showing where localized
     deviations from the baseline model are detected

2. **Focused anomaly window plot (`finder.plot_anomaly_window`)**

   * A zoomed-in view around the best anomaly candidate
   * Residuals are shown together with the anomaly template and the flat model

---

## Method Overview

The workflow of `jacscanomaly` is:

1. **First fitting**
   Fit a single lens model (e.g. PSPL) to the full light curve.

2. **Residual analysis**
   Compute residuals:

   ```
   residual = data − single_lens_model
   ```

3. **Local anomaly scan**
   For each grid point `(t0, teff)`, compare:

   * a flat model
   * an anomaly template model
     within a local time window.

4. **Detection statistic**
   The improvement is measured by:

   ```
   Δχ² = χ²_flat − χ²_anomaly
   ```

---

## Anomaly Score

To quantify how significant the best anomaly candidate is relative to others,
we define a **score**:

```
score = (Δχ²_best − median(Δχ²_others)) / std(Δχ²_others)
```

In practice, `jacscanomaly` estimates `median(Δχ²_others)` and
`std(Δχ²_others)` from the bulk of the other cluster peaks, trimming values
above `best_score_trim_percentile` first when possible. This makes the score
less sensitive to a few strong secondary peaks.

This measures how strongly the best candidate stands out from the rest of the grid.

---

## Configuration

Key parameters are controlled via `FinderConfig`:

```python
from jacscanomaly import FinderConfig

config = FinderConfig(
    teff_init=0.03,      # initial anomaly timescale
    teff_grid_n=20,      # number of teff grid points
    sigma=3.0,           # threshold for outlier counting
    best_score_trim_percentile=95.0,  # trim upper tail for best-candidate score
)
```

See `FinderConfig` for the full list of options.

---

## Example Data

The light curves used as examples in this repository are drawn from an original set of
**2,371 simulated Roman light curves** generated by the **Roman Galactic Exoplanet Survey
Project Infrastructure Team (RGES PIT)**, **WG07 Survey Simulations and Pipeline Validation**
(Farzaneh Zohrabi, Matthew Penny, Macy Huston, Ali Crisp, et al).

This representative sample of 2,371 light curves was selected assuming the **Cassan exoplanet
mass function** and consists of simulated Roman light curves of **planetary microlensing events**,
including higher-order effects such as **parallax** and **orbital motion**.

---

## Algorithmic Background

The anomaly scan implemented in `jacscanomaly` is inspired by the
systematic anomaly search methodology developed for microlensing surveys
(e.g., the KMTNet AnomalyFinder series). In particular, the approach
of scanning residual light curves over a grid of anomaly times and
durations is based on key ideas presented in:

> Zang, W., Jung, Y., Yee, J., et al. (2021). *Systematic KMTNet Planetary
> Anomaly Search, Paper I: OGLE-2019-BLG-1053Lb, A Buried Terrestrial
> Planet*. The Astronomical Journal, **162**, 163.  
> DOI: 10.3847/1538-3881/ac12d4 :contentReference[oaicite:3]{index=3}

This work described a semi-automated search algorithm that iteratively
scans events for localized deviations relative to a baseline model and
quantifies the significance of detected signals — an idea that is central
to the grid-scan and Δχ² evaluation in `jacscanomaly`.

---

### Finite-source magnification (FSPL)

Finite-source magnifications are computed using an external JAX-based
implementation.

The original FFT-based extended-source algorithm is from:
https://github.com/git-sunao/fft-extended-source

This algorithm is provided in JAX form by:
https://github.com/ShotaMiyazaki94/microjax

Specifically, `jacscanomaly` uses the FFT disk-integration implementation
available through:

    from microjax.fastlens import fspl_disk

Note:
`jacscanomaly` currently requires the GitHub source version of `microjax`.
The PyPI package `microjaxx==0.1.1` may not expose
`microjax.fastlens.fspl_disk`.

Install `microjax` from source before using FSPL functionality:

    git clone https://github.com/ShotaMiyazaki94/microjax.git
    cd microjax
    python -m pip install -e .

You can verify the installation with:

    from microjax.fastlens import fspl_disk

## Citation

If you use **jacscanomaly** in academic work, including journal articles,
conference proceedings, or theses, please cite the software.

Citation metadata is provided in the `citation.cff` file in this repository,
which can be used directly by GitHub and reference managers.

---

## Requirements

* Python ≥ 3.9
* numpy
* jax
* jaxopt
* matplotlib
