Feel-Good Thompson Sampling for Contextual Bandits: a Markov Chain Monte Carlo Showdown
This repository implements various MCMC-based contextual bandit algorithms.
Features
-
Algorithms:
- Langevin Monte Carlo (LMC)
- Underdamped Langevin Monte Carlo (ULMC)
- Metropolis-Adjusted Langevin Algorithm (MALA)
- Hamiltonian Monte Carlo (HMC)
- Epsilon-Greedy
- Upper-Confidence-Bound (UCB)
- Neural Thompson Sampling (NTS)
- Linear Thompson Sampling (LTS)
- Neural Upper-Confidence-Bound (NUCB)
- Neural Greedy (NG)
- And numerous variants with Feel-Good and smoothed Feel-Good exploration terms
-
Environments:
- Linear bandits
- Logistic bandits
- Wheel bandit problem
- Neural bandits
Installation
Option 1: Install from PyPI (Recommended)
pip install ctx-bandits-mcmc
With optional dependencies:
# For development tools
pip install ctx-bandits-mcmc[dev]
# For neural bandit experiments
pip install ctx-bandits-mcmc[neural]
# All optional dependencies
pip install ctx-bandits-mcmc[dev,neural]
Option 2: Install from Source
# Clone the repository
git clone https://github.com/SarahLiaw/ctx-bandits-mcmc-showdown.git
cd ctx-bandits-mcmc-showdown
# Install the package
pip install .
# Or install in editable mode for development
pip install -e .[dev]
Option 3: Install from GitHub
pip install git+https://github.com/SarahLiaw/ctx-bandits-mcmc-showdown.git
For detailed installation instructions, platform-specific notes, and troubleshooting, see INSTALL.md.
Quick Start
Running Linear Bandit Experiments
To run a linear bandit experiment with the LMC-TS agent:
python3 run.py --config_path config/linear/lmcts.json
Running Wheel Bandit Experiments
To run the wheel bandit experiment with the ULMC agent:
python3 run_all_wheel_agents.py --agents ulmc --num_trials 1
Batch Running Multiple Experiments
To run multiple experiments with different seeds:
python3 run_linear_batch.py --n_seeds 5
Configuration
Configuration files are stored in the config/ directory, organized by environment type (linear, logistic, wheel, neural). Each agent has its own configuration file with hyperparameters.
Results
Results are saved in the results/ directory by default. The directory structure is:
results/
├── linear/
├── logistic/
└── wheel/
└── neural/
Posterior Distribution Quality Analysis
Overview
The posterior_analysis.py script provides a controlled comparison of MCMC algorithm posterior approximations against the true analytical Bayesian posterior. This analysis isolates approximation quality from exploration strategy by running all algorithms on identical data.
Key insight: Thompson Sampling theory assumes sampling from the true posterior π*_t. This tool quantifies how well MCMC approximations π̃_t match π*_t.
Quick Start
Run posterior analysis with default algorithms:
python posterior_analysis.py
This will:
- Generate fixed synthetic data (6 arms, 20 dimensions, 2000 timesteps)
- Run LinTS, LMCTS, FGLMCTS, MALATS, and PLMCTS on identical data
- Compute true analytical posteriors for each arm
- Create 2D scatter plots comparing true (green) vs. algorithm (red) posteriors
- Calculate Wasserstein distances quantifying approximation quality
Customization
Select specific algorithms:
python posterior_analysis.py --algorithms LinTS LMCTS MALATS
Change random seed:
python posterior_analysis.py --seed 42
Available algorithms:
LinTS- Analytical Thompson Sampling (baseline)LMCTS- Langevin Monte Carlo TSFGLMCTS- Feel-Good LMC-TSSFGLMCTS- Smoothed Feel-Good LMC-TSMALATS- Metropolis-Adjusted LangevinFGMALATS- Feel-Good MALA-TSSFGMALATS- Smoothed Feel-Good MALA-TSPLMCTS- Preconditioned LMC-TSPFGLMCTS- Preconditioned Feel-Good LMC-TSPSFGLMCTS- Preconditioned Smoothed Feel-Good LMC-TSHMCTS- Hamiltonian Monte Carlo TSFGHMCTS,SFGHMCTS,PHMCTS,PFGHMCTS,PSFGHMCTS- HMC variants
Configuration
Edit parameters in posterior_analysis.py (lines 31-38):
K_ARMS = 6 # Number of arms
D_DIM = 20 # Context dimension
LAMBDA_PRIOR = 1.0 # Prior precision
SIGMA_REWARD = 0.5 # Reward noise
T_HORIZON = 2000 # Time horizon
N_POSTERIOR_SAMPLES = 1500 # Samples for visualization
ETA = 1.0 # Inverse temperature
CORRELATED_CONTEXTS = True # True: elliptical, False: circular posteriors
Output Structure
Results are saved in posterior_analysis_YYYYMMDD_HHMMSS/:
posterior_analysis_20251023_100530/
├── synthetic_data.pt # Fixed data used by all algorithms
├── results.json # Wasserstein distances and play counts
├── LinTS_posterior_comparison.png # 1×6 grid: true (green) vs. alg (red)
├── LMCTS_posterior_comparison.png
├── MALATS_posterior_comparison.png
└── ...
Interpreting Results
Visualization (PNG files):
- 1×6 grid: One subplot per arm showing β₁ vs β₂ projection
- Green scatter: 1500 samples from true analytical posterior
- Red scatter: 1500 samples from algorithm's posterior
- Good approximation: Red and green overlap
- Poor approximation: Red shifted or wrong shape
- Under-exploration: Sparse or missing red samples
Metrics (results.json):
{
"LMCTS": {
"wasserstein_distances": [0.23, 0.34, 0.18, 0.56, 0.29, 1.23],
"mean_wasserstein": 0.468,
"num_plays_per_arm": [423, 312, 589, 245, 389, 42]
}
}
- Wasserstein distance < 0.3: Excellent approximation
- 0.3 < W < 0.7: Good approximation
- W > 1.0: Poor approximation
- W = NaN: Arm never played
Testing
Run unit tests to verify core functionality:
python -m pytest test_posterior_analysis.py -v
Or with unittest:
python test_posterior_analysis.py
Tests cover:
- Data generation (correlated/uncorrelated)
- True posterior computation
- Feature map correctness
- Sampling procedures
- Wasserstein distance calculation
Use Cases
- Algorithm Development: Verify new MCMC variants maintain accurate posteriors
- Hyperparameter Tuning: Check if step sizes / burn-in periods affect approximation quality
- Failure Mode Diagnosis: Distinguish under-exploration from poor MCMC convergence
- Computational Trade-offs: Evaluate if preconditioned methods justify extra cost
Mathematical Background
For linear bandits with reward model r_t = X_t^T β_i + ε_t, the posterior is:
Prior: β_i ~ N(0, λ^-1 I)
Posterior: β_i | D_t ~ N(μ_post, Σ_post)
Σ_post^-1 = λI + (η/σ²) Σ X_s X_s^T
μ_post = Σ_post · (η/σ²) · Σ X_s r_s
This closed-form solution serves as ground truth. MCMC algorithms approximate this through sampling procedures (Langevin, MALA, HMC).
Weights & Biases Integration
The code is integrated with Weights & Biases for experiment tracking. To use it:
- Install wandb:
pip install wandb - Log in:
wandb login - Run your experiments - results will be logged to your W&B account
Testing
Running Tests
We provide comprehensive unit tests for the posterior analysis functionality:
# Quick test run
make test
# Verbose output
make test-verbose
# Skip slow tests
make test-quick
Or directly with pytest:
pytest test_posterior_analysis.py -v
Test Coverage
Tests validate:
- ✅ Data generation (correlated/uncorrelated contexts)
- ✅ True posterior computation (Bayesian linear regression)
- ✅ Block diagonal feature maps
- ✅ Posterior sampling procedures
- ✅ Wasserstein distance calculation
- ✅ Complete pipeline integration
See TESTING.md for detailed testing documentation.
Development Workflow
Using Make Commands
make install # Install dependencies
make test # Run tests
make posterior-analysis # Run posterior analysis
make build # Build distribution packages
make upload-test # Upload to TestPyPI
make update-version # Update package version (interactive)
make clean # Clean generated files
Updating Package Version
To release a new version:
# Automated (recommended)
make update-version
# Or manual: update version in setup.py, pyproject.toml, src/__init__.py
# Then: make clean-all && make build && make upload
See VERSION_UPDATE_GUIDE.md for complete instructions.
Adding New Agents
To add a new agent:
- Create a new class in
src/MCMC.pyinheriting from the base agent class - Implement the required methods (
choose_arm,update, etc.) - Add the agent to the
format_agentfunction inrun.py - Create a configuration file in the appropriate
config/subdirectory - Add tests if implementing new sampling mechanisms
Citation
If you use this code in your research, please consider citing our paper:
@article{anand2025feelgoodthompsonsamplingcontextual, title={Feel-Good Thompson Sampling for Contextual Bandits: a Markov Chain Monte Carlo Showdown}, author={Emile Anand and Sarah Liaw}, year={2025}, eprint={2507.15290}, archivePrefix={arXiv}, primaryClass={cs.LG}, url={https://arxiv.org/abs/2507.15290}, }
Metadata
Release files for ctx-bandits-mcmc 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| ctx_bandits_mcmc-1.0.1.tar.gz | 97.7 kB | Without provenance | Details for ctx_bandits_mcmc-1.0.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| ctx_bandits_mcmc-1.0.1-py3-none-any.whl | Python 3 | none | any | Details for ctx_bandits_mcmc-1.0.1-py3-none-any.whl |
Total release size: 118.1 kB
Release files / ctx_bandits_mcmc-1.0.1.tar.gz
| Download URL | ctx_bandits_mcmc-1.0.1.tar.gz |
|---|---|
| Size | 97.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f2a8ec5a258636ac77ef73d1d46d251d4f5eada238c93466cf4420fbb9015feb
|
|
BLAKE2b-256 checksum How to use checksums |
3718d323f300806ec0f065af536b26a18b04d357b4a0031a7d97f06598186b58
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / ctx_bandits_mcmc-1.0.1-py3-none-any.whl
| Download URL | ctx_bandits_mcmc-1.0.1-py3-none-any.whl |
|---|---|
| Size | 20.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
55ed7c3bd2e1217abbbf0006c38e06c4acb8fa0c9bba193fde0349d7e5ee822d
|
|
BLAKE2b-256 checksum How to use checksums |
c212e9e7aa4e2f60f747ab23ba004be9bd148be2ae888118fc6a3708760ef4de
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Security for ctx-bandits-mcmc 1.0.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.