Metadata-Version: 2.4
Name: pyblackwell
Version: 0.1.2
Summary: Blackwell approachability primitives for finite vector-payoff games.
Author: Zsolt Döme
License-Expression: MIT
Project-URL: Homepage, https://gitlab.com/domezsolt/pyblackwell
Project-URL: Documentation, https://gitlab.com/domezsolt/pyblackwell/-/blob/main/docs/index.md
Project-URL: Repository, https://gitlab.com/domezsolt/pyblackwell
Project-URL: Issues, https://gitlab.com/domezsolt/pyblackwell/-/issues
Project-URL: Changelog, https://gitlab.com/domezsolt/pyblackwell/-/blob/main/CHANGELOG.md
Project-URL: Security, https://gitlab.com/domezsolt/pyblackwell/-/blob/main/SECURITY.md
Keywords: blackwell-approachability,online-learning,game-theory,vector-payoffs,calibration
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Mathematics
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: numpy>=1.24
Requires-Dist: scipy>=1.10
Requires-Dist: typing-extensions>=4.8
Provides-Extra: cvx
Requires-Dist: cvxpy>=1.4; extra == "cvx"
Provides-Extra: torch
Requires-Dist: torch>=2.2; extra == "torch"
Provides-Extra: jax
Requires-Dist: jax>=0.4; extra == "jax"
Requires-Dist: jaxlib>=0.4; extra == "jax"
Provides-Extra: diffopt
Requires-Dist: cvxpylayers<1.0,>=0.1.9; python_version < "3.11" and extra == "diffopt"
Requires-Dist: cvxpylayers>=1.0; python_version >= "3.11" and extra == "diffopt"
Requires-Dist: jaxopt>=0.8; extra == "diffopt"
Provides-Extra: rl
Requires-Dist: gymnasium>=0.29; extra == "rl"
Provides-Extra: sklearn
Requires-Dist: scikit-learn>=1.3; extra == "sklearn"
Provides-Extra: plot
Requires-Dist: matplotlib>=3.7; extra == "plot"
Provides-Extra: pandas
Requires-Dist: pandas>=2.0; extra == "pandas"
Provides-Extra: docs
Requires-Dist: mkdocs>=1.5; extra == "docs"
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
Requires-Dist: mkdocstrings[python]>=0.24; extra == "docs"
Provides-Extra: test
Requires-Dist: pytest>=7.4; extra == "test"
Requires-Dist: pytest-cov>=4.1; extra == "test"
Requires-Dist: hypothesis>=6.88; extra == "test"
Provides-Extra: benchmarks
Requires-Dist: pytest-benchmark>=4.0; extra == "benchmarks"
Provides-Extra: release
Requires-Dist: build>=1.2; extra == "release"
Requires-Dist: twine>=5.0; extra == "release"
Provides-Extra: dev
Requires-Dist: pytest>=7.4; extra == "dev"
Requires-Dist: pytest-cov>=4.1; extra == "dev"
Requires-Dist: hypothesis>=6.88; extra == "dev"
Requires-Dist: mkdocs>=1.5; extra == "dev"
Requires-Dist: mkdocs-material>=9.5; extra == "dev"
Requires-Dist: mkdocstrings[python]>=0.24; extra == "dev"
Requires-Dist: pytest-benchmark>=4.0; extra == "dev"
Requires-Dist: build>=1.2; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"
Requires-Dist: mypy>=1.8; extra == "dev"
Requires-Dist: pre-commit>=3.6; extra == "dev"
Provides-Extra: all
Requires-Dist: cvxpy>=1.4; extra == "all"
Requires-Dist: torch>=2.2; extra == "all"
Requires-Dist: jax>=0.4; extra == "all"
Requires-Dist: jaxlib>=0.4; extra == "all"
Requires-Dist: cvxpylayers<1.0,>=0.1.9; python_version < "3.11" and extra == "all"
Requires-Dist: cvxpylayers>=1.0; python_version >= "3.11" and extra == "all"
Requires-Dist: jaxopt>=0.8; extra == "all"
Requires-Dist: gymnasium>=0.29; extra == "all"
Requires-Dist: scikit-learn>=1.3; extra == "all"
Requires-Dist: matplotlib>=3.7; extra == "all"
Requires-Dist: pandas>=2.0; extra == "all"
Requires-Dist: mkdocs>=1.5; extra == "all"
Requires-Dist: mkdocs-material>=9.5; extra == "all"
Requires-Dist: mkdocstrings[python]>=0.24; extra == "all"
Requires-Dist: pytest>=7.4; extra == "all"
Requires-Dist: pytest-cov>=4.1; extra == "all"
Requires-Dist: hypothesis>=6.88; extra == "all"
Requires-Dist: pytest-benchmark>=4.0; extra == "all"
Requires-Dist: build>=1.2; extra == "all"
Requires-Dist: twine>=5.0; extra == "all"
Requires-Dist: ruff>=0.4; extra == "all"
Requires-Dist: mypy>=1.8; extra == "all"
Requires-Dist: pre-commit>=3.6; extra == "all"
Dynamic: license-file

# PyBlackwell

> Blackwell approachability primitives for finite vector-payoff games, with
> explicit numerical certificates and reproducible experiment tooling.

[![PyPI version](https://img.shields.io/pypi/v/pyblackwell.svg)](https://pypi.org/project/pyblackwell/)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/pyblackwell.svg)](https://pypi.org/project/pyblackwell/)
[![GitLab pipeline](https://gitlab.com/domezsolt/pyblackwell/badges/main/pipeline.svg)](https://gitlab.com/domezsolt/pyblackwell/-/pipelines)
[![MIT license](https://img.shields.io/pypi/l/pyblackwell.svg)](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/LICENSE)

[Documentation](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/docs/index.md)
· [PyPI](https://pypi.org/project/pyblackwell/)
· [Examples](https://gitlab.com/domezsolt/pyblackwell/-/tree/main/examples)
· [Changelog](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/CHANGELOG.md)
· [Security](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/SECURITY.md)

PyBlackwell connects target-set geometry, finite games, minimax action
selection, repeated-game solvers, and diagnostics in one NumPy-first API. The
classical path covers bounded finite vector-payoff games, closed convex targets,
full-information or expected-payoff updates, and exact or tolerance-controlled
projection and minimax oracles. Extensions for calibration, partial feedback,
online learning, and RL expose their additional assumptions instead of
presenting numerical diagnostics as stronger guarantees.

## Install

Install the base package, which depends only on NumPy, SciPy, and
`typing-extensions`:

```bash
python -m pip install pyblackwell
```

PyBlackwell requires Python 3.10 or newer. The project CI currently tests
CPython 3.10, 3.11, and 3.12 on Linux.

## Quick start

This matching game has scalar payoffs `-1` and `1`; mixing both learner actions
equally keeps the running payoff average at the point target `{0}`.

```python
import numpy as np

from pyblackwell import BlackwellApproachability, MatrixGame, PointTarget

payoffs = np.array(
    [
        [[-1.0], [1.0]],
        [[1.0], [-1.0]],
    ]
)

game = MatrixGame(payoffs)
solver = BlackwellApproachability(game, PointTarget([0.0]))

for env_action in [0, 1] * 25:
    strategy = solver.strategy()
    solver.update(game.payoff(strategy, env_action), env_action=env_action)

certificate = solver.certificate()
print(
    {
        "iterations": certificate.iteration,
        "average": certificate.average.tolist(),
        "distance": certificate.distance,
    }
)
```

```text
{'iterations': 50, 'average': [0.0], 'distance': 0.0}
```

The certificate records the running average, target projection, distance,
separating normal, numerical status, and decision-time oracle result. Use
`solver.trace()` for per-iteration histories or `summary_only=True` for bounded
memory when only aggregate diagnostics are needed.

## Why PyBlackwell?

- Closed-form and optimization-backed targets expose projection, containment,
  support, and separation through consistent result objects.
- Classical, OCO, regret-reduction, predictive, delayed, and adaptive solver
  variants share explicit state, certificate, and trace conventions.
- Full-information, bandit, and partial-monitoring helpers keep observation
  assumptions visible in diagnostics.
- Optional integrations stay behind extras and lazy imports, so the base
  package remains lightweight.
- Seeded examples, trace archives, benchmark metadata, and experiment manifests
  support reproducible numerical work.
- Stable, experimental, and private import paths are documented explicitly.

## Compatibility and optional extras

| Extra | Capability |
| --- | --- |
| `cvx` | CVXPY-backed convex and semidefinite targets |
| `diffopt` | CVXPYLayers and JAXopt differentiable projections |
| `torch`, `jax`, `pandas` | Result and trace adapters |
| `rl` | Gymnasium vector-reward integration |
| `sklearn` | scikit-learn calibration adapters |
| `plot` | Matplotlib convergence and calibration plots |

Install only what a workflow needs, for example:

```bash
python -m pip install "pyblackwell[cvx,plot]"
```

PyBlackwell is currently an alpha `0.x` project. The
[API policy](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/docs/api.md)
identifies supported and experimental namespaces; pin a version when relying on
experimental modules or numerical backend behavior.

## Scope and limitations

PyBlackwell is for target-set approachability and related online-learning
reductions. It is not a general convex optimization toolkit, an extensive-form
game solver, or a proof that an arbitrary bandit or RL training loop satisfies
the classical Blackwell assumptions. External game packages should own trees,
information sets, CFR, sequence-form optimization, and equilibrium claims, with
PyBlackwell consuming validated payoff or regret records through adapters.

Certificates describe the numerical computation that ran. Exact finite-game,
approximate-oracle, bandit, sampled-calibration, and RL claims have different
assumptions. Read the
[guarantee boundaries](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/docs/theory_limitations.md)
before interpreting positive residuals, approximation statuses, or realized
sample paths.

## Documentation

- [Installation and extras](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/docs/installation.md)
- [Guided quickstart](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/docs/quickstart.md)
- [Application tutorials](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/docs/index.md#tutorial-paths)
- [Concepts and theory](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/docs/theory.md)
- [API reference and stability policy](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/docs/api.md)
- [Executable examples](https://gitlab.com/domezsolt/pyblackwell/-/tree/main/examples)
- [Architecture](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/ARCHITECTURE.md)

Packaged example configurations and saved traces also have command-line entry
points:

```bash
pyblackwell-example --list
pyblackwell-example matching-pennies-point
pyblackwell-trace-summary path/to/trace.npz
pyblackwell-benchmark run --mode smoke --iterations 20
```

## Reproducible research

Library examples teach supported APIs; `benchmarks/` tracks stable performance
workloads; `experiments/` evaluates scientific claims. Generated runs are not
source files and belong under the ignored `runs/` directory.

The corrected article experiment uses a frozen protocol, disjoint validation
and test decisions, explicit seed streams, environment and source provenance,
and checksummed result bundles:

```bash
OMP_NUM_THREADS=1 MKL_NUM_THREADS=1 OPENBLAS_NUM_THREADS=1 \
PYTHONPATH=src python experiments/article_contract_monitor_v2.py \
  --profile smoke --output-dir runs/article_v2_smoke
```

Use the immutable `article` profile only from a clean checkout. See the
[experiment policy](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/experiments/README.md),
[frozen protocol](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/experiments/article_v2/PROTOCOL.md),
and [correction note](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/experiments/article_v2/CORRECTION_NOTE.md).
The comparison does not claim that Blackwell eliminates scalarization, and it
keeps expected-update calibration separate from realized forecast error.

## Support and development

Use the [issue tracker](https://gitlab.com/domezsolt/pyblackwell/-/issues) for
reproducible bugs and scoped feature requests. Read
[SUPPORT.md](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/SUPPORT.md)
for the information to include and
[SECURITY.md](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/SECURITY.md)
for private vulnerability reporting.

Contributors can create the locked environment and run the same command
contract used by CI:

```bash
uv sync --locked
make check
make package-check
```

Read the [contribution guide](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/CONTRIBUTING.md)
and [code of conduct](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/CODE_OF_CONDUCT.md)
before opening a merge request.

## License and citation

PyBlackwell is released under the [MIT License](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/LICENSE).
For research use, cite the software using
[`CITATION.cff`](https://gitlab.com/domezsolt/pyblackwell/-/blob/main/CITATION.cff)
and pin the release version used by the experiment.
