Python implementation of Scalable Column Subset Selection via Boolean Relaxation and Frank-Wolfe Method (KIM, 2026).
The Column Subset Selection Problem (CSSP) asks: given a data matrix
This is NP-hard. Standard approaches are exact-but-exponential (branch-and-bound) or fast-but-suboptimal (greedy forward selection). This library takes a third path.
Financial interpretation: selecting the
We adapt the Boolean relaxation framework of Moka et al. (2025) — originally developed for minimum-variance portfolio selection — and show it applies directly to CSSP. The key observation is that the CSSP inner subproblem has the same algebraic form as a minimum-variance portfolio problem, giving a continuous relaxation with provable properties.
The relaxed objective
-
Strictly convex when
$\delta \geq \eta_1$ (largest eigenvalue of$A = X^T X / n$ ) -
Agrees with the original at every binary corner
$s \in {0,1}^p$
FW-Homotopy exploits this with a geometric schedule
As
Benchmarked on 8 datasets (
| Regime | Objective vs Greedy | Speedup |
|---|---|---|
| Dense ( |
Within 3% | Up to 3.5× faster |
| Sparse ( |
Higher variance | Greedy preferred |
Recommended parameters:
pip install git+https://github.com/SnowHana/gridfw.gitOr in editable mode:
git clone https://github.com/SnowHana/gridfw.git
cd gridfw
pip install -e .import numpy as np
from grad_fw import FWHomotopySolver
# Build covariance matrix from return data
X = np.random.randn(252, 100) # 252 trading days, 100 assets
A = X.T @ X / len(X)
# Select k=20 assets that best span the covariance structure
solver = FWHomotopySolver(A, k=20, alpha=0.1, n_steps=500, n_mc_samples=50)
s = solver.solve()
selected = np.where(s > 0.5)[0]
print(f"Selected assets: {selected}") # exactly 20 indicesFor comparison against the greedy baseline:
from grad_fw.benchmarks.GreedySolver import GreedySolver
from grad_fw.benchmarks.benchmarks import run_experiment
result = run_experiment(A, k=20, experiment_name="my_experiment")
print(f"FW/Greedy ratio: {result['ratio']:.3f} | Speedup: {result['speedupx']:.2f}x")src/grad_fw/
├── __init__.py # Public API: FWHomotopySolver, DatasetLoader
├── fw_homotomy.py # FW-Homotopy solver (main algorithm)
├── data_loader.py # Dataset loading & preprocessing
├── benchmarks/
│ ├── GreedySolver.py # Greedy forward-selection baseline O(pk³)
│ ├── BruteForceSolver.py # Exact brute-force (small p only)
│ └── benchmarks.py # run_experiment / find_critical_k
└── verif/
├── core.py # BooleanRelaxation math & gradient formulas
└── verifiers.py # Numerical gradient checkers
examples/market/
├── sp500_load_data.py # yfinance data pipeline with caching
├── sp500.py # Solver wrappers for financial data
├── sp500_plots.py # Correlation heatmaps and selection visualisations
└── backtest.py # Walk-forward backtest engine (5 strategies, 8 diagnostics)
# Correctness tests (fast, no external data)
pytest tests/sanity_check/ tests/grad_check/
# Full numerical experiments from the paper (slow)
pytest tests/performance/ -m slowResults log to logs/ (created automatically).
| Dataset | Source | Required action |
|---|---|---|
| Synthetic, Toeplitz | Generated in code | None |
| MNIST, Madelon | OpenML (auto-download) | None |
| Myocardial | UCI Repo (auto-download) | None |
| SECOM | UCI ML Repository | secom.data → data/secom.data |
| Residential Building | UCI ML Repository | Residential-Building-Data-Sets.xlsx → data/residential.xlsx |
| Arrhythmia | UCI ML Repository | arrhythmia.data → data/arrhythmia.data |
Tests that cannot find their data file are automatically skipped.
| Condition | Recommendation |
|---|---|
|
|
FW-Homotopy (faster, comparable quality) |
|
|
Greedy (more stable) |
| Brute-force or Greedy |
CSSP applied to the full S&P 500 universe (
pip install -e ".[examples]"
python examples/market/sp500.py # static selection (FW vs Greedy)
python examples/market/backtest.py # walk-forward backtestPrice data downloads automatically from yfinance on first run (~2 min). Company metadata is committed to data/market/.
The full S&P 500 return correlation matrix reordered by hierarchical clustering. CSSP selects
The most robust result across all 74 walk-forward windows:
| Universe | Mean condition number | Relative to full |
|---|---|---|
| Full S&P 500 ( |
2,021,160 | 1× |
| Market-cap top-50 | 8,587 | 235× better |
| CSSP-selected 50 | 527 | 3,836× better |
CSSP consistently produces the best-conditioned
Methodology — strict no-lookahead protocol:
- 74 monthly windows: 2-year rolling training → 1-month out-of-sample test
- CSSP selection re-estimated from scratch each training window
- 5 strategies × 8 diagnostics including statistical significance tests, sector attribution, and rolling Sharpe
Momentum strategies (12-1 signal, top-20 stocks within each universe):
| Strategy | Ann. Return | Sharpe | Volatility | Max Drawdown |
|---|---|---|---|---|
| CSSP-Momentum | 16.3% | 0.545 | 29.9% | −51.1% |
| Market-Cap-Filtered | 14.5% | 0.622 | 23.4% | −39.1% |
| Random-k Momentum | 11.4% | 0.547 | 20.8% | −42.2% |
| Full-Universe Momentum | 12.6% | 0.401 | 31.4% | −51.4% |
| Equal-Weight (benchmark) | 7.3% | 0.340 | 21.4% | −47.7% |
Rolling Sharpe (252-day window):
Honest interpretation: All momentum strategies co-move closely on a rolling basis — market regime dominates strategy differences. The CSSP-Momentum cumulative outperformance is driven by a sector tilt (Energy +8.4%, Consumer Cyclical +9.8% relative to market-cap filter) that worked pre-2022 and reversed after. Monthly t-tests show no strategy difference is significant at the 5% level (n = 74 months), consistent with a small sample and regime dependency rather than persistent alpha.
CSSP's 3,836× conditioning advantage should matter most for mean-variance optimisation (MVO), which directly inverts the covariance matrix. We tested minimum-variance portfolios built on each universe.
| Strategy | Ann. Return | Sharpe | Volatility | Max Drawdown |
|---|---|---|---|---|
| MarketCap-MVO | 11.6% | 0.71 | 17.7% | −31.3% |
| Equal-Weight | 7.6% | 0.45 | 21.3% | −37.9% |
| CSSP-MVO | 1.2% | 0.18 | 26.4% | −50.5% |
Why CSSP-MVO underperformed: CSSP maximises
This reveals an important boundary: the conditioning advantage is necessary but not sufficient for MVO to outperform. The universe composition (what stocks you select) matters more than the numerical quality of their covariance matrix.
Rolling Sharpe for MVO strategies:
On a rolling basis, all MVO strategies co-move tightly — consistent with market regime dominating individual strategy differences, and with Tikhonov regularisation adequately compensating for ill-conditioning even in the full-universe case.
CSSP selections are stable across consecutive monthly windows:
| Metric | Value |
|---|---|
| Mean Jaccard similarity (consecutive windows) | 0.595 |
| Random-selection baseline Jaccard | 0.054 |
| Mean monthly selection turnover | 40.5% |
Selections are 11× more stable than random, confirming CSSP consistently identifies the same representative stocks rather than churning. The 40.5% monthly turnover is non-trivial and should be factored into any transaction cost model.
CSSP is best understood as a sparse covariance representation tool rather than a signal or return predictor:
| Use case | CSSP helps? | Why |
|---|---|---|
| Sparse ETF basket construction | ✓ Strong | Selects minimum stocks that span index covariance |
| Index replication with transaction cost constraint | ✓ Strong | k stocks cover the covariance structure efficiently |
| Risk factor identification | ✓ Strong | 3,836× better-conditioned submatrix |
| Momentum universe pre-filter | ~ Regime-dependent | Sector tilt dominates, not conditioning |
| Minimum-variance portfolio (MVO) | ✗ | Objective mismatch: CSSP selects high-coverage stocks, MVO needs low-variance stocks |
KIM, Wujin (Daniel). Scalable Column Subset Selection via Boolean Relaxation and Frank-Wolfe Method. 2026.
Based on the Boolean relaxation framework of Moka et al. (2025), originally developed for minimum-variance portfolio optimisation.
MIT License






