Metadata-Version: 2.4
Name: pytyche
Version: 0.2.0
Summary: Bayesian causal inference for zero-inflated outcomes — GPU-accelerated joint hurdle BCF with SBC calibration
Author-email: Tim Radcliffe <tradcliffe2@gmail.com>
License: MIT License
        
        Copyright (c) 2026 Tim Radcliffe
        
        Permission is hereby granted, free of charge, to any person obtaining a copy
        of this software and associated documentation files (the "Software"), to deal
        in the Software without restriction, including without limitation the rights
        to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
        copies of the Software, and to permit persons to whom the Software is
        furnished to do so, subject to the following conditions:
        
        The above copyright notice and this permission notice shall be included in all
        copies or substantial portions of the Software.
        
        THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
        IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
        FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
        AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
        LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
        OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
        SOFTWARE.
        
Project-URL: Homepage, https://gitlab.com/tradcliffe2/tyche
Project-URL: Repository, https://gitlab.com/tradcliffe2/tyche
Project-URL: Issues, https://gitlab.com/tradcliffe2/tyche/-/issues
Keywords: causal-inference,bayesian,BCF,BART,JAX,calibration,experimentation,treatment-effects,hurdle-model
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy
Requires-Dist: scipy
Requires-Dist: pandas
Requires-Dist: scikit-learn
Requires-Dist: pyyaml
Requires-Dist: arviz
Requires-Dist: matplotlib>=3.7
Requires-Dist: pillow>=10.0
Requires-Dist: tqdm>=4.60
Requires-Dist: jax
Requires-Dist: bartz>=0.9.0
Requires-Dist: numpyro
Provides-Extra: gpu
Requires-Dist: jax[cuda12]; extra == "gpu"
Provides-Extra: cpu-bcf
Requires-Dist: stochtree; extra == "cpu-bcf"
Provides-Extra: tracking
Requires-Dist: dvc>=3.0; extra == "tracking"
Requires-Dist: dvclive>=3.0; extra == "tracking"
Requires-Dist: matplotlib>=3.7; extra == "tracking"
Requires-Dist: plotext>=5; extra == "tracking"
Provides-Extra: dev
Requires-Dist: pytest; extra == "dev"
Requires-Dist: pytest-cov; extra == "dev"
Requires-Dist: pytest-xdist; extra == "dev"
Requires-Dist: ruff; extra == "dev"
Requires-Dist: pyright; extra == "dev"
Provides-Extra: docs
Requires-Dist: sphinx>=7.3; extra == "docs"
Requires-Dist: myst-parser>=3.0; extra == "docs"
Requires-Dist: myst-nb>=1.1; extra == "docs"
Requires-Dist: jupytext>=1.16; extra == "docs"
Requires-Dist: furo>=2024.5; extra == "docs"
Requires-Dist: sphinx-autodoc-typehints; extra == "docs"
Requires-Dist: sphinx-autobuild>=2024.4; extra == "docs"
Requires-Dist: sphinx-copybutton; extra == "docs"
Requires-Dist: sphinx-design; extra == "docs"
Requires-Dist: sphinx-sitemap; extra == "docs"
Requires-Dist: jsonschema>=4.0; extra == "docs"
Requires-Dist: linkify-it-py>=2.0; extra == "docs"
Dynamic: license-file

# Pytyche

**Faster, calibrated heterogeneous-treatment-effect (HTE) discovery in a sequential / adaptive targeting loop — built for real-world online experiments where most users don't convert and the conversion signal is noisy.**

Pytyche fits a Bayesian Causal Forest (BCF) over hurdle-distributed outcomes (revenue, conversion, click-through, churn), recalibrates its posterior intervals against simulation-based ground truth, and feeds the calibrated posterior into a sequential allocator that drives the next round of a multi-round experiment. The end-to-end loop runs on a single GPU. The same primitives also work for a single-shot RCT or observational analysis, but the API is shaped for multi-round growth experiments where you narrow allocation toward responder segments over time while keeping controls in every segment so measurement stays honest.

**Documentation:** <https://tradcliffe2.gitlab.io/tyche/> · **First fit:** [tutorial](https://tradcliffe2.gitlab.io/tyche/tutorials/first-hurdle-bcf-fit.html) · **Overview:** [concept doc](https://tradcliffe2.gitlab.io/tyche/concepts/overview.html) · **Status:** alpha (0.1.0.dev) — MIT

---

## What's inside

| | Why this is here |
|---|---|
| **GPU joint hurdle BCF** on top of [bartz](https://github.com/Gattocrucco/bartz) | **Roughly 5× to 60× faster than the StochTree CPU backend at production scales on real workloads** — the canonical joint hurdle fit lands 4.5–8.6× at n=750k (the heaviest case: two coupled forests with shared topology), while the single-channel continuous and binary paths hit 17–63× from n=250k through n=2M. Full grid in the [bench publication snapshot](ROADMAP.md#empirical-performance-snapshot-2026-05-08--bench-publication-grid). What used to be a CPU-week of simulation-based calibration (SBC) sweeps now runs overnight. |
| **Shared-tree topology** with dual leaves | A single tree structure jointly fits both channels of a hurdle outcome (probit conversion + log-severity), instead of training two independent forests — following Linero et al.'s shared Bayesian forests. The structural prior carries information across channels and stabilizes per-segment group-average treatment effect (GATE) estimates at low conversion rates — exactly the regime online experiments live in. |
| **Perturb + rotation MCMC proposals** (research / alpha) | Augments bartz's grow/prune chain with structural tree moves following Pratola et al., adapted to the GPU kernels. Aimed at reducing autocorrelation in deep-tree posteriors. |
| **Programmatic DGP generators** built for tailored SBC recalibration | A small typed grammar (`pytyche.generators.scenarios`) parameterizes the data-generating process (DGP); sweeps over it produce the SBC tables that recalibrate the posterior at your operating regime. Agentic DGP generation is in development. |
| **Sequential targeting as a first-class API citizen** | The sequential experiment loop (`pt.sequential_experiment`) runs Thompson allocation with controls retention; built-in power simulations sweep over the same DGP classes used for calibration. See [sequential-targeting](docs/concepts/sequential-targeting.md). |
| **Honest-uncertainty contracts** | `pytyche.contracts` separates observed data from ground truth at the type level, so analysis code structurally cannot peek at what it shouldn't see. See [statistical-honesty](docs/concepts/statistical-honesty.md). |
| **Interpretable policy segmentations alongside the posterior** | Each round of sequential targeting compresses the BCF CATE posterior into a shallow policy tree (cf. Hitsch / Misra / Zhang 2024) — a stakeholder-facing decision surface, not just a model object, that drives the next round's allocation and gives reviewers an auditable handle on the model's segment-level decisions. First-class public API + custom-criterion + robustness layer on the [roadmap](ROADMAP.md). |

## Why it exists

BCF posteriors at production scale are structurally miscalibrated — the regularization prior of Bayesian Additive Regression Trees (BART, the model family BCF builds on) narrows credible intervals for better point estimation at the cost of honest uncertainty. The standard fixes (more chains, better sampler) don't help. The actual fix is empirical: SBC across realistic DGPs, then isotonic recalibration. That requires hundreds of full posterior fits, which on CPU is a multi-week research project per design surface.

GPU BCF makes that loop cheap enough to run routinely. **Speed isn't the contribution — speed is what unlocks the contribution.** With the loop cheap, calibration becomes something you do per-deployment instead of per-publication; with calibrated posteriors, sequential targeting becomes safe enough to actually deploy.

## Install

```bash
# Recommended — GPU JAX (CUDA 12)
uv add 'pytyche[gpu]'

# CPU-only (fully functional; the first fit warns once when no CUDA device is found)
uv add pytyche
```

Verify the runtime with `python -c "import pytyche as pt; pt.check_setup()"`.

For a development checkout (editable, all extras, bartz fork as a submodule):

```bash
git clone --recurse-submodules https://gitlab.com/tradcliffe2/tyche
cd tyche
uv sync --all-extras
```

## Quick start

Fit the canonical model on an 800-visitor synthetic dataset in ~20 seconds on JAX-CPU:

```python
import os; os.environ["JAX_PLATFORMS"] = "cpu"  # omit for GPU
import pytyche as pt

bundle = pt.generate(n_visitors=800, segments={
    "responders":     {"pct": 0.4, "base_conv": 0.08, "treatment_effect": 0.10,
                       "aov_mu": 3.5, "aov_sigma": 0.5, "treatment_aov_mu_shift": 0.15},
    "non_responders": {"pct": 0.6, "base_conv": 0.06, "treatment_effect": 0.0,
                       "aov_mu": 3.3, "aov_sigma": 0.5, "treatment_aov_mu_shift": 0.0},
}, metric="revenue_per_visitor", seed=0)

result = pt.fit(bundle.observed, num_burnin=40, num_mcmc=80, num_trees_mu=30,
                num_trees_tau=15, max_depth=4, num_gfr_sweeps=2,
                diagnostic_interval=20, seed=0)
result.analyze()  # comparisons, discovered segments, recommendation
# result.rpv_cate_samples → (n_visitors, 80) posterior draws of the per-visitor CATE
```

The full product is the adaptive loop on top of that fit — `pt.sequential_experiment(...)` runs a realistically-sized multi-round experiment (350,000 visitors) in about fifteen minutes on a consumer GPU; the **[adaptive-experiment tutorial](https://tradcliffe2.gitlab.io/tyche/tutorials/first-adaptive-experiment.html)** walks it end to end. For the single-fit on-ramp (posterior interpretation at segment level, ground-truth comparison) see the **[first-fit tutorial](https://tradcliffe2.gitlab.io/tyche/tutorials/first-hurdle-bcf-fit.html)**.

## What pytyche is and isn't

Pytyche is a **designed-experiment library** for round-based online experiments with a handful of treatments: assignment rules are explicit, propensities are recorded exactly, and the loop pauses between rounds. Out of scope: marketplaces and anything else with cross-visitor interference (SUTVA violations), regulated decision contexts needing preregistration-grade governance, large-catalog per-item recommendation, real-time/streaming adaptation, and heavily-confounded observational inference (use econml / DoubleML). Extremely heavy-tailed revenue and calibration applied far from the scale it was fitted at both warrant a domain-specific re-check. The full enumeration lives in the [overview's scope section](https://tradcliffe2.gitlab.io/tyche/concepts/overview.html).

## Documentation

The published site follows the [Diátaxis](https://diataxis.fr/) information architecture:

- **[Tutorials](https://tradcliffe2.gitlab.io/tyche/tutorials/index.html)** — hands-on walkthroughs. Start with [your first hurdle BCF fit](https://tradcliffe2.gitlab.io/tyche/tutorials/first-hurdle-bcf-fit.html).
- **[How-to guides](https://tradcliffe2.gitlab.io/tyche/how-to/index.html)** — recipes for specific tasks.
- **[Concepts](https://tradcliffe2.gitlab.io/tyche/concepts/index.html)** — design rationale. Start with [overview](https://tradcliffe2.gitlab.io/tyche/concepts/overview.html).
- **[Reference](https://tradcliffe2.gitlab.io/tyche/reference/index.html)** — curated **Public** API for users, full **Internal** tree for extenders.

Build locally:

```bash
uv sync --all-extras
uv run sphinx-build -W -b html docs/ docs/_build/html
```

## Documentation coverage

**Public-API docstring coverage: 98%** (72/73 symbols on the curated public surface — baseline in [`docs/coverage-baseline.json`](docs/coverage-baseline.json)).

CI enforces *stable-or-increasing* coverage on every MR via `tests/test_docs/test_public_api_coverage.py`. When an MR raises coverage, bump the baseline in the same MR so the new floor sticks. The **[doc-health dashboard](https://tradcliffe2.gitlab.io/tyche/meta/health.html)** renders the current polish-state breakdown (drafting / reviewed / polished) and the drift list — docs whose declared `depends-on` source files have moved since the doc's `last-human-review`. Polished-tier drift fails CI; lower-tier drift is reported but doesn't block.

## Development

See [`CLAUDE.md`](CLAUDE.md) for project principles (development discipline, the SDLC-citizen contract for docs, the OpenSpec workflow), [`CLAUDE-AGENT.md`](CLAUDE-AGENT.md) for the implementation-subagent guide, and [`docs/concepts/testing-philosophy.md`](docs/concepts/testing-philosophy.md) for the rationale behind pytyche's testing practices. Spec-driven changes live under [`openspec/`](openspec/).

## License

MIT — see [`LICENSE`](LICENSE). Built on [bartz](https://github.com/Gattocrucco/bartz) (MIT) by Giacomo Petrillo; the GPU BART kernels pytyche fits on top of are bartz's. Hurdle / shared-tree extensions and the calibration / targeting / generator stack are pytyche's.

**Source:** <https://gitlab.com/tradcliffe2/tyche> (PyPI package name: `pytyche`; GitLab repo name: `tyche` for URL brevity).

## Citation

*Methodology paper in preparation. Cite as `pytyche, v0.1.0.dev (alpha), https://gitlab.com/tradcliffe2/tyche` until a citable DOI is up.*
