Adaptive Conformal Inference Under Distribution Shift
Project description
adaptive-conformal-inference
Out-of-the-box Python library for Adaptive Conformal Inference (ACI).
It provides a clean, model-agnostic online object (ACI) that:
- takes your model prediction,
- issues a conformal prediction set,
- updates adaptively after observing the true outcome.
This repo also includes scripts that reproduce key paper figures and practical demos of ACI vs fixed baselines.
Based on:
Isaac Gibbs and Emmanuel J. Candès. Adaptive Conformal Inference Under Distribution Shift. NeurIPS 2021.
arXiv:2106.00170
Project links:
PyPI: https://pypi.org/project/adaptive-conformal-inference/GitHub: https://github.com/emrekuru/aci-python
Installation
pip install adaptive-conformal-inference
For running the example scripts (stock data + plots):
pip install adaptive-conformal-inference[examples]
Core Usage
Initialization Parameters
| Parameter | Default | What it controls and how to tune it |
|---|---|---|
alpha |
required | Target miss rate. alpha=0.1 means target coverage is about 90%. Lower alpha makes intervals more conservative (wider on average). |
gamma |
required | Adaptation speed for alpha_t. Increase if environment changes quickly and coverage lags. Decrease if alpha_t oscillates too much. |
lookback |
500 |
Calibration memory for score quantiles. Larger is more stable but slower to react. Smaller reacts faster but can be noisy. |
method |
"simple" |
Alpha update rule. "simple" reacts directly to each miss/hit. "momentum" smooths updates using recent history. |
momentum_bw |
0.95 |
Used only for method="momentum". Closer to 1.0 means smoother, longer memory. Lower values emphasize very recent rounds. |
score_fn |
None |
Conformity score function score_fn(y_true, y_pred) -> float. Default is absolute error abs(y_true - y_pred). Choose this to match your task's notion of error. |
set_fn |
None |
Prediction-set builder set_fn(y_pred, qhat). Default is symmetric interval [y_pred-qhat, y_pred+qhat]. Use this for CQR/relative/non-additive set construction. |
clip_alpha |
True |
Keeps alpha_t in [0, 1]. Recommended True for most users to avoid invalid quantile levels. |
Conformity Score (score_fn)
score_fn defines what "bad prediction" means for your problem. ACI adapts coverage for that score.
- Default (
None): absolute error, good for many regression tasks. - Relative/percentage error:
- useful when prediction scale changes a lot over time.
- often better for volatility-like tasks.
- Asymmetric score:
- useful if underprediction is more costly than overprediction (or vice versa).
score_fn must return a finite number. (Some valid scores, such as CQR score, can be negative inside the base quantile band.)
Built-in helpers (paper-style workflows):
- Scores:
absolute_error_scorerelative_error_scorecqr_score
- Set builders:
symmetric_interval_setrelative_interval_setcqr_interval_set
Example (CQR-style):
from aci import ACI, cqr_score, cqr_interval_set
aci = ACI(
alpha=0.1,
gamma=0.005,
lookback=500,
score_fn=cqr_score,
set_fn=cqr_interval_set,
)
# y_pred_t is (q_low_t, q_high_t)
prediction_set = aci.issue(y_pred_t)
out = aci.observe(y_true_t)
Tuning Guide
- Coverage below target for long periods:
- increase
gamma(faster correction), or decreaselookback(faster calibration updates).
- increase
- Coverage far above target (intervals too wide):
- decrease
gamma, or increaselookbackfor less reactive widening.
- decrease
alpha_tis very jumpy:- decrease
gamma, or usemethod="momentum"with highmomentum_bw(for example0.95to0.99).
- decrease
- Regime changes happen often:
- use smaller
lookbackand/or largergamma.
- use smaller
- Regime is mostly stable:
- use larger
lookbackand smallergammafor smoother intervals.
- use larger
API Reference
The package exposes one class: aci.ACI.
| Method / Property | What it does in practice |
|---|---|
issue(y_pred) |
Call this once per round after producing your model prediction. Returns set_fn(y_pred, qhat_t). With default set_fn, this is (lower, upper). |
observe(y_true) |
Call this once the true outcome is available. Computes conformity score and miscoverage event, updates alpha_t, and returns diagnostics (hit, err_t, score_t, alpha_used, alpha_next, qhat_t). |
reset() |
Restarts the object to its initial state (alpha_t=alpha, empty histories). Useful between datasets/episodes. |
alpha_t |
Current adaptive miss-rate level that will be used for the next issue(...). If it goes down, intervals usually widen; if it goes up, intervals usually narrow. |
alpha_history |
Historical alpha_t values. Use this to inspect adaptation behavior and tune gamma/lookback. |
err_history |
Historical miss indicators (1=miss, 0=hit). Use mean of this to estimate realized miss rate. |
score_history |
Historical conformity scores from your score_fn. Useful for debugging whether score design matches your task. |
has_pending_prediction |
True after issue(...) and before observe(...). Helps enforce correct online call order. |
err_t follows the paper directly: err_t = 1 when score_t > qhat_t, else 0.
Online Workflow
Use this order every round:
- Compute your point prediction
y_pred. - Call
issue(y_pred)to get interval bounds. - Observe the true value
y_true. - Call
observe(y_true)to update ACI state.
from aci import ACI
# One object handles interval issuance + online alpha adaptation
aci = ACI(alpha=0.1, gamma=0.005, lookback=500, method="simple")
for t in range(T):
y_pred_t = model_predict(x_t) # your forecaster
lower, upper = aci.issue(y_pred_t) # C_hat_t(alpha_t)
y_true_t = observe_truth()
out = aci.observe(y_true_t) # updates alpha_t online
hit = out["hit"] # per-round coverage signal
alpha_next = aci.alpha_t # alpha to be used on the NEXT round
Reproduce Paper Results
# Figure 1: Volatility prediction with normalized score (4 stocks)
python examples/figure1.py
# Figure 2: Volatility prediction with unnormalized score (4 stocks)
python examples/figure2.py
# Figure 4: Daily open prices (4 stocks)
python examples/figure4.py
# Figure 5: Stock alpha_t trajectories (simple update)
python examples/figure5.py
# Figure 6: Stock alpha_t trajectories (momentum update)
python examples/figure6.py
# Figure 9: Additional-stock local coverage (8 stocks)
python examples/figure9.py
Reproduction File Map
examples/figure1.pyRuns the full Figure 1 reproduction (normalized score), loads stock data, computes local coverage, and rendersfigures/figure1.png.examples/figure2.pyRuns the full Figure 2 reproduction (unnormalized score), loads stock data, computes local coverage, and rendersfigures/figure2.png.examples/figure4.pyPlots daily open prices for Nvidia, AMD, BlackBerry, and Fannie Mae, and rendersfigures/figure4.png.examples/figure5.pyPlots stock-marketalpha_ttrajectories using simple ACI update, and rendersfigures/figure5.png.examples/figure6.pyPlots stock-marketalpha_ttrajectories using momentum ACI update (bw=0.95), and rendersfigures/figure6.png.examples/figure9.pyRuns the additional-stocks local coverage experiment from Appendix A.4, and rendersfigures/figure9.png.examples/common/data.pyShared data utilities for stock examples: stock loading and local coverage computation.examples/common/garch_forecaster.pyShared forecasting logic: rolling GARCH(1,1) fitting and one-step variance predictions.examples/common/aci_workflow.pyShared ACI evaluation loop: runs adaptive ACI vs fixed-alpha baseline on aligned(y_pred, y_true)sequences.examples/common/volatility_pipeline.pyShared end-to-end pipeline that combines GARCH forecasts with the ACI workflow; used by stock figure scripts.
Figure 1 (Normalized Score)
| Our reproduction | Paper figure |
|---|---|
Figure 2 (Unnormalized Score)
| Our reproduction | Paper figure |
|---|---|
Figure 4 (Daily Open Prices)
| Our reproduction | Paper figure |
|---|---|
Figure 5 (Stock Alpha Trajectories, Simple Update)
| Our reproduction | Paper figure |
|---|---|
Figure 6 (Stock Alpha Trajectories, Momentum Update)
| Our reproduction | Paper figure |
|---|---|
Figure 9 (Additional Stocks Local Coverage)
| Our reproduction | Paper figure |
|---|---|
Additional Examples
1) Simple Example (Default ACI)
Run:
python examples/simple_example.py
What this example does:
- Uses default ACI score/set functions (absolute-error score + symmetric interval).
- Compares adaptive ACI against a fixed-alpha baseline.
- Visualizes per-round hits (green) and misses (red).
What to observe:
- ACI and fixed both recalibrate with recent scores, but only ACI updates
alpha_t. - When misses become frequent, ACI tends to lower
alpha_tand widen intervals. - When misses are rare, ACI tends to raise
alpha_tand narrow intervals.
2) CQR Asymmetric Example
Run:
python examples/cqr_asymmetric_example.py
What this example does:
- Demonstrates custom ACI functions using:
cqr_scorecqr_interval_set
- Compares:
- ACI with CQR functions (asymmetric interval behavior)
- ACI with default functions (symmetric interval behavior)
What to observe:
- With CQR functions, lower and upper sides can have different widths (asymmetric intervals).
- With default functions, intervals remain symmetric around a single center prediction.
- ACI still uses a single shared
alpha_tfor interval coverage in both cases.
Citation
@article{gibbs2021adaptive,
title={Adaptive conformal inference under distribution shift},
author={Gibbs, Isaac and Candes, Emmanuel},
journal={Advances in Neural Information Processing Systems},
volume={34},
pages={1660--1672},
year={2021}
}
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file adaptive_conformal_inference-1.0.1.tar.gz.
File metadata
- Download URL: adaptive_conformal_inference-1.0.1.tar.gz
- Upload date:
- Size: 13.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ce6dd963dc572f04a6a840218ea9e4955e82b0fb687faed4681fb8c000d156cb
|
|
| MD5 |
86295fa3f3de59245cfdf86edd4cdc8c
|
|
| BLAKE2b-256 |
8fc4cde96d9641d5b31139af27b032db7f0854e28e66b3e1fb7e6409a9e049ca
|
Provenance
The following attestation bundles were made for adaptive_conformal_inference-1.0.1.tar.gz:
Publisher:
python-publish.yml on emrekuruu/adaptive_conformal_inference
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
adaptive_conformal_inference-1.0.1.tar.gz -
Subject digest:
ce6dd963dc572f04a6a840218ea9e4955e82b0fb687faed4681fb8c000d156cb - Sigstore transparency entry: 937549148
- Sigstore integration time:
-
Permalink:
emrekuruu/adaptive_conformal_inference@a81c178ac6deebfab190f85ef007dda11893c3bb -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/emrekuruu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@a81c178ac6deebfab190f85ef007dda11893c3bb -
Trigger Event:
release
-
Statement type:
File details
Details for the file adaptive_conformal_inference-1.0.1-py3-none-any.whl.
File metadata
- Download URL: adaptive_conformal_inference-1.0.1-py3-none-any.whl
- Upload date:
- Size: 10.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5cdb97aa3ac0f04340a4d275762bec67ed422566a9cbaf3f9aa26d1063a0e7f
|
|
| MD5 |
22ffe2f219702f5b7fb180a3ffb303ac
|
|
| BLAKE2b-256 |
52aea44d91bf6c2e002f47e2de61861ff42224945ecac6393c057ddcc73bb803
|
Provenance
The following attestation bundles were made for adaptive_conformal_inference-1.0.1-py3-none-any.whl:
Publisher:
python-publish.yml on emrekuruu/adaptive_conformal_inference
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
adaptive_conformal_inference-1.0.1-py3-none-any.whl -
Subject digest:
f5cdb97aa3ac0f04340a4d275762bec67ed422566a9cbaf3f9aa26d1063a0e7f - Sigstore transparency entry: 937549162
- Sigstore integration time:
-
Permalink:
emrekuruu/adaptive_conformal_inference@a81c178ac6deebfab190f85ef007dda11893c3bb -
Branch / Tag:
refs/tags/v1.0.1 - Owner: https://github.com/emrekuruu
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@a81c178ac6deebfab190f85ef007dda11893c3bb -
Trigger Event:
release
-
Statement type: