Metadata-Version: 2.4
Name: nullbic
Version: 0.1.0
Summary: Symbolic regression with automatic null-baseline ΔBIC. Find formulas AND prove they're not noise.
Author: nullbic contributors
License: MIT
Project-URL: Homepage, https://github.com/glogwa68/nullbic
Project-URL: Repository, https://github.com/glogwa68/nullbic
Project-URL: Issues, https://github.com/glogwa68/nullbic/issues
Keywords: symbolic-regression,machine-learning,interpretability,BIC,auto-falsification
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Rust
Classifier: License :: OSI Approved :: MIT License
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Development Status :: 4 - Beta
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: pandas
Requires-Dist: pandas>=1.3; extra == "pandas"
Provides-Extra: test
Requires-Dist: pytest>=7; extra == "test"
Provides-Extra: dev
Requires-Dist: pandas>=1.3; extra == "dev"
Requires-Dist: pytest>=7; extra == "dev"
Requires-Dist: build; extra == "dev"
Requires-Dist: twine; extra == "dev"
Dynamic: license-file

# nullbic

**Symbolic regression with automatic null-baseline ΔBIC.**
Find formulas AND prove they're not noise.

```python
from nullbic import Dataset, discover

ds = Dataset.from_csv("data.csv", target="y")
report = discover(ds, n_generations=40)

print(report.summary())
print("real signal?", report.is_real_signal())
```

## Why this exists

Existing symbolic-regression tools (PySR, Eureqa, `gplearn`,
`SymbolicRegression.jl`) all give you a formula. **None of them, by default,
tell you whether that formula meaningfully beats a constant model or a
trivial linear fit.** Most "discoveries" published with these tools are
indistinguishable from noise, and the user has no easy way to tell.

`nullbic` ships those baselines as a first-class output:

- **ΔBIC vs F = const** (the mean predictor)
- **ΔBIC vs F = linear** (best OLS on all features)
- **z-score vs shuffled-target distribution** (30 shuffles by default)
- **`Verdict.STRONG` / `WEAK` / `NOISE`** assigned automatically

If the discovered formula doesn't beat all three baselines, the verdict
downgrades. No silent overfit.

## Install

```bash
git clone https://github.com/yourname/nullbic
cd nullbic
pip install -e .
# optional: pandas integration
pip install -e ".[pandas]"
```

The Rust core ships as a pre-built shared library inside the package
(`nullbic/_dll/nullbic.dll` on Windows; `.so` / `.dylib` on Linux / macOS).
No Rust toolchain needed at install time.

## Usage

### From a pandas DataFrame

```python
import pandas as pd
from nullbic import Dataset, discover

df = pd.read_csv("merge_results.csv")
ds = Dataset.from_pandas(df, target="cosine_sim")
rep = discover(ds, n_generations=40, pop_size=200, max_depth=4)

if rep.is_real_signal():
    print("Formula:", rep.formula)
    print(f"ΔBIC vs const : {rep.delta_bic_const:+.1f}")
    print(f"ΔBIC vs linear: {rep.delta_bic_linear:+.1f}")
    print(f"z vs shuffled : {rep.z_vs_shuffled:+.2f}")
```

### From a CSV

```python
from nullbic import Dataset, discover

ds = Dataset.from_csv("data.csv", target="y")
print(discover(ds).summary())
```

### From a CLI

```bash
nullbic data.csv y --gens=40 --pop=200 --depth=4
```

Exit code is `0` if the verdict is `STRONG` or `WEAK`, `1` if `NOISE` —
handy in CI/CD signal-validation pipelines.

## Three real use cases

### 1. Kaggle / tabular insight

Point at any cleaned dataset. Get a formula. **Get the proof it's not in
the noise.**

### 2. Black-box surrogate

Approximate an XGBoost / NN model with a symbolic surrogate; the verdict
tells you when the surrogate is meaningful vs cosmetic.

### 3. Empirical-law audit

Feed in a paper's claimed empirical relationship. The verdict says whether
the relationship really beats a linear baseline on the given data.

## How the verdict is assigned

| Verdict | Criteria |
|---------|----------|
| `STRONG`  | ΔBIC vs const < −10  **AND**  ΔBIC vs linear < −10  **AND**  z vs shuffled < −2 |
| `WEAK`    | Beats const but not all three thresholds |
| `NOISE`   | Doesn't beat const → formula is not extracting any signal |

These thresholds match standard model-selection conventions (Kass &
Raftery 1995 / Schwarz 1978).

## Architecture

```
nullbic (Python package)
  └── core.py             → public API (Dataset, discover, …)
  └── _bindings.py        → ctypes layer (private)
  └── _dll/nullbic.dll    → Rust shared library (pre-built)

nullbic-core (Rust crate, ~600 LOC)
  └── dataset             → tabular rows + train/test split + shuffled
  └── expr                → expression trees over named features
  └── optimizer           → single-level GA, niching, hyper-mutation
  └── baselines           → F=const + F=linear (OLS) + shuffled-target
  └── c_api               → extern "C" entry points
```

## Performance

Typical run on 500 rows × 10 features, 40 generations, pop 200:

- ~30–80 ms wall-clock on one core
- ~150 MB peak RAM
- Deterministic for a given seed

The GA is parallel via Rayon; throughput scales near-linearly with cores.

## Limitations and honest caveats

- The GA is intentionally simple. PySR is more sophisticated when raw
  accuracy is the only goal. **`nullbic` trades a few percent of accuracy
  for the auto-falsification report.**
- The "linear baseline" is plain OLS with a tiny ridge for numerical
  safety; it's not feature-engineered. If you're comparing to a serious
  linear model, replace `delta_bic_linear` with your own ΔBIC.
- 580 rows is comfortable. Below ~50 rows, results are unreliable — the
  shuffled-target distribution is too noisy to anchor the z-score.

## License

MIT. See `LICENSE`.

## Citation

If `nullbic` contributes to a paper, please cite it as:

> nullbic: symbolic regression with auto-falsification ΔBIC. 2026.

## Related

- [PySR](https://github.com/MilesCranmer/PySR) — sophisticated symbolic regression
- [gplearn](https://gplearn.readthedocs.io/) — sklearn-compatible GP
- [SymbolicRegression.jl](https://github.com/MilesCranmer/SymbolicRegression.jl) — Julia
