Skip to main content

Conformal prediction and robust decision-making toolkit (PyTorch + CVXPY)

Project description

Robbuffet logo robbuffet

PyPI Python CI Docs

Conformal prediction + robust decision making with PyTorch predictors and CVXPY optimizers.

Install

  • From PyPI (once published):
    pip install robbuffet
    
  • From source:
    git clone https://github.com/yashpatel5400/robbuffet
    cd robbuffet
    pip install .
    
  • Editable + dev extras:
    pip install -e .[dev]
    

Submodules

This repo uses the DCRNN_PyTorch submodule for the METR-LA shortest-path example. Clone with:

git clone --recurse-submodules https://github.com/yashpatel5400/robbuffet

or, if already cloned:

git submodule update --init --recursive

For the METR-LA example, generate predictions via:

cd examples/DCRNN_PyTorch
python run_demo_pytorch.py --config_filename=data/model/pretrained/METR-LA/config.yaml
cd ../..

This writes data/dcrnn_predictions_pytorch.npz that the example consumes.

What this package does

  • Calibrate PyTorch predictors with split conformal prediction and geometry-aware score functions.
  • Produce prediction regions (convex or unions) that can be sampled, visualized (1D/2D), or passed to downstream optimizers.
  • Build deterministic or scenario-based robust decision problems that respect conformal regions.

Supported scores/geometries with closed-form robustification:

  • L2 residual (L2Score) → L2 ball.
  • L1 residual (L1Score) → L1 ball.
  • Linf residual (LinfScore) → Linf ball (hypercube).
  • Mahalanobis residual (MahalanobisScore) → ellipsoid.

Quickstart (split conformal, L2 residual score)

import torch
from torch.utils.data import DataLoader, TensorDataset
from robbuffet import L2Score, SplitConformalCalibrator

# toy predictor
model = torch.nn.Linear(2, 2)

# calibration data loader
x_cal = torch.randn(200, 2)
y_cal = x_cal + 0.1 * torch.randn_like(x_cal)
cal_loader = DataLoader(TensorDataset(x_cal, y_cal), batch_size=32)

cal = SplitConformalCalibrator(model, L2Score(), cal_loader)
alpha = 0.1
cal.calibrate(alpha=alpha)

x_new = torch.randn(1, 2)
region = cal.predict_region(x_new)
print("center:", region.center, "radius:", region.radius)

Visualization

from robbuffet import vis
import matplotlib.pyplot as plt
vis.plot_region_2d(region, grid_limits=((-1, 1), (-1, 1)), resolution=200)
plt.show()

Affine Robust Solver

For linear/affine dependence on the uncertain parameter theta, build a predictor + score and conformal region, then use support functions:

import cvxpy as cp
import numpy as np
import torch
from torch.utils.data import TensorDataset, DataLoader
from robbuffet import L2Score, SplitConformalCalibrator, AffineRobustSolver

# toy predictor
model = torch.nn.Linear(2, 2)
x_cal = torch.randn(200, 2)
y_cal = x_cal + 0.1 * torch.randn_like(x_cal)
cal_loader = DataLoader(TensorDataset(x_cal, y_cal), batch_size=32)

cal = SplitConformalCalibrator(model, L2Score(), cal_loader)
q = cal.calibrate(alpha=0.1)
region = cal.predict_region(torch.zeros(1, 2))  # example point

def base_obj(w):
    return cp.norm(w, 2)

def theta_dir(w):
    return w

def robust_constraints(w):
    # Example affine constraint <w, theta> <= 0.5 for all theta in region
    return [(w, 0.5)]

solver = AffineRobustSolver(
    decision_shape=(2,),
    region=region,
    base_objective_fn=base_obj,
    theta_direction_fn=theta_dir,
    constraints_fn=lambda w: [],
    robust_constraints_fn=robust_constraints,
)
w_star, status = solver.solve()
print("status:", status, "w*:", w_star)

AffineRobustSolver assumes the uncertain parameter enters the problem affinely. The robustified optimization has the form:

$\min_{w} \quad g(w) + \sup_{\theta \in \mathcal{C}(x)} \langle d(w), \theta \rangle$
$\text{s.t. } h_i(w) \le 0, \quad \langle a_j(w), \theta \rangle \le b_j(w) \quad\quad \forall \theta \in \mathcal{C}(x).$

Here:

  • $g(w)$ is base_objective_fn(w); $h_i(w)$ and $b_j(w)$ come from constraints_fn.
  • The dependence on $\theta$ is affine: theta_direction_fn(w) corresponds to $d(w)$ in the objective, and each pair $(a_j(w), b_j(w))$ comes from robust_constraints_fn.
  • $\mathcal{C}(x)$ is the conformal region returned by cal.predict_region(...).

AffineRobustSolver replaces the affine $\theta$ terms with support functions $h_{\mathcal{C}}(\cdot)$; non-affine $\theta$ dependence is not supported. Use the Danskin or sampling-based approaches when the uncertainty enters non-affinely or the region is nonconvex/union and you prefer gradient-based optimization.

Gradient-Based (Danskin) Solver

For nonconvex or union regions, get a conformal region from a predictor/score, then use the Danskin optimizer:

import numpy as np
import torch
from robbuffet import DanskinRobustOptimizer, SplitConformalCalibrator
from robbuffet.scores import GPCPScore
from torch.utils.data import DataLoader, TensorDataset

# toy sampler predictor: returns K samples (K, batch, d)
def sampler(x):
    base = torch.randn(5, x.shape[0], 2)
    return base

score_fn = GPCPScore(sampler)
cal = SplitConformalCalibrator(sampler, score_fn, DataLoader(TensorDataset(torch.zeros(10, 1), torch.zeros(10, 1)), batch_size=2))
q = cal.calibrate(alpha=0.1)
region = cal.predict_region(torch.zeros(1, 1))

def inner(theta_var, w_np):
    return theta_var @ w_np

def value_and_grad(w_np, theta_np):
    return float(theta_np @ w_np), np.array(theta_np, dtype=float)

project = lambda w_vec: np.clip(w_vec, -1, 1)
opt = DanskinRobustOptimizer(region, nom_obj=inner, value_and_grad_fn=value_and_grad, project_fn=project)
w_star, _ = opt.solve(w0=np.zeros(2), step_size=0.1, max_iters=100)
print("Danskin w*:", w_star)

Examples

  • examples/robust_shortest_path_metrla.py — robust shortest path on METR-LA with conformalized DCRNN_PyTorch forecasts (needs examples/DCRNN_PyTorch submodule + predictions NPZ).
  • examples/robust_bike_newsvendor.py — conformal calibration on UCI Bike Sharing demand + robust newsvendor decisions.

Run with python examples/<script>.py. The METR-LA script assumes you have generated examples/DCRNN_PyTorch/data/dcrnn_predictions_pytorch.npz (see Submodules above).

Trial runner

Use scripts/run_trials.py to run an example multiple times, cache results, and compare robust vs nominal:

# absolute objectives (no normalization)
python scripts/run_trials.py --example robust_bike_newsvendor.py --trials 5 --alpha 0.1

# relative gaps (requires avg_cost_oracle from the example)
python scripts/run_trials.py --example robust_bike_newsvendor.py --trials 5 --alpha 0.1 --relative

Outputs mean/std and a paired t-test (robust < nominal) when scipy is available. Caches results in .cache/run_trials.json.

Extending

  • Add new ScoreFunction implementations that expose their induced region geometry via build_region.
  • For non-convex regions, return PredictionRegion.union([...]) so optimizers can decompose or sample.
  • Use the scenario optimizer as a default inner-approximation; for affine cases use the deterministic robustifiers above.

Contributing

Please open issues for bugs/feature requests and PRs for fixes/additions. See CONTRIBUTING.md for guidelines.

Citation

If you use Robbuffet in academic work, please cite:

@software{robbuffet,
  title = {Robbuffet: Conformal prediction and robust decision making},
  author = {Yash Patel},
  year = {2025},
  url = {https://github.com/yashpatel5400/robbuffet}
}

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

robbuffet-0.1.tar.gz (17.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

robbuffet-0.1-py3-none-any.whl (16.7 kB view details)

Uploaded Python 3

File details

Details for the file robbuffet-0.1.tar.gz.

File metadata

  • Download URL: robbuffet-0.1.tar.gz
  • Upload date:
  • Size: 17.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for robbuffet-0.1.tar.gz
Algorithm Hash digest
SHA256 32e67351850673d93c9128205bb9199d95c46087d0a98c4217d0e52fa07b7ff9
MD5 58f3e6c102ec8858aaef44508f66c4b9
BLAKE2b-256 cda3ce31fd2c3a903cc71bd090fc01fdd972ecd41246a4f33194755cb4c6fe84

See more details on using hashes here.

File details

Details for the file robbuffet-0.1-py3-none-any.whl.

File metadata

  • Download URL: robbuffet-0.1-py3-none-any.whl
  • Upload date:
  • Size: 16.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.9

File hashes

Hashes for robbuffet-0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 d4de70813e871560fc491b0b6144ba9eb070a78df5a616be960b2db5d035c749
MD5 b12cb6836388464469bc4901d3e13b6f
BLAKE2b-256 c1c3bada8d3a56f50c1eb4c1b404e5e6fb5d0834c049a9e136aba40a24c0899d

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page