Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed .DS_Store
Binary file not shown.
13 changes: 12 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,15 @@ bazel-*
data/factor_covariance.csv
data/factor_exposure.csv
data/specific_variance.csv
data/topic_exposure.csv
data/topic_exposure.csv

# Python bytecode / caches
__pycache__/
*.py[cod]
.pytest_cache/

# Packaging artifacts
*.egg-info/

# OS cruft
.DS_Store
1 change: 1 addition & 0 deletions .python-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
3.12
105 changes: 105 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Directory Map

This is a map of the directory for reference.

```
.
├── .DS_Store
├── .gitignore
├── .vscode
│ └── settings.json
├── AGENTS.md
├── README.md
├── data
│ ├── BFI228.csv
│ ├── blackrock_covariance.csv
│ └── eighthr.data
├── environment.yml
├── figures
│ ├── .DS_Store
│ ├── blackrock_results.pdf
│ ├── dist_comparison.pdf
│ ├── model_selec_results_n=100_gaussian.pdf
│ ├── model_selec_results_n=100_non_gaussian.pdf
│ ├── model_selec_results_n=200_gaussian.pdf
│ ├── model_selec_results_n=200_gaussian_fb.pdf
│ ├── model_selec_results_n=200_non_gaussian.pdf
│ ├── model_selec_results_n=200_non_gaussian_fb.pdf
│ ├── model_selec_results_n=300_gaussian.pdf
│ ├── model_selec_results_n=300_non_gaussian.pdf
│ ├── model_selec_results_n=400_gaussian.pdf
│ ├── model_selec_results_n=400_non_gaussian.pdf
│ ├── model_selec_results_n=500_gaussian.pdf
│ ├── model_selec_results_n=500_non_gaussian.pdf
│ ├── ozone_results.pdf
│ ├── reliability.pdf
│ └── temp.pdf
├── notebooks
│ ├── .DS_Store
│ ├── .ipynb_checkpoints
│ │ ├── Development Notebook-checkpoint.ipynb
│ │ ├── blackrock_css-checkpoint.ipynb
│ │ ├── missing_data-checkpoint.ipynb
│ │ ├── model_selection-checkpoint.ipynb
│ │ ├── testing_data_generation_and_null_distribution-checkpoint.ipynb
│ │ └── testing_subset_selection-checkpoint.ipynb
│ ├── blackrock_css.ipynb
│ ├── missing_data.ipynb
│ ├── model_selection.ipynb
│ ├── notebook_utils
│ │ ├── .DS_Store
│ │ ├── __pycache__
│ │ │ ├── data_generation.cpython-310.pyc
│ │ │ ├── missing_data.cpython-310.pyc
│ │ │ └── utils.cpython-310.pyc
│ │ ├── data_generation.py
│ │ ├── missing_data.py
│ │ └── utils.py
│ ├── results
│ │ ├── gaussian_results.csv
│ │ └── non_gaussian_results
│ ├── testing_data_generation_and_null_distribution.ipynb
│ └── testing_subset_selection.ipynb
├── pycss
│ ├── .DS_Store
│ ├── CSS.py
│ ├── __init__.py
│ ├── __pycache__
│ │ ├── PCSS.cpython-310.pyc
│ │ ├── __init__.cpython-310.pyc
│ │ ├── css.cpython-310.pyc
│ │ ├── hypothesis_testing.cpython-310.pyc
│ │ ├── model_selection.cpython-310.pyc
│ │ ├── subset_selection.cpython-310.pyc
│ │ └── utils.cpython-310.pyc
│ ├── model_selection.py
│ ├── subset_selection.py
│ └── utils.py
├── pycss.egg-info
│ ├── PKG-INFO
│ ├── SOURCES.txt
│ ├── dependency_links.txt
│ ├── not-zip-safe
│ ├── requires.txt
│ └── top_level.txt
├── requirements.txt
├── setup.py
└── tests
├── .DS_Store
├── __pycache__
│ ├── test_CSS.cpython-310-pytest-7.3.1.pyc
│ ├── test_PCSS.cpython-310-pytest-7.2.1.pyc
│ ├── test_PCSS.cpython-310-pytest-7.3.1.pyc
│ ├── test_css.cpython-310-pytest-7.2.1.pyc
│ ├── test_subset_selection.cpython-310-pytest-7.2.1.pyc
│ ├── test_subset_selection.cpython-310-pytest-7.3.1.pyc
│ ├── test_utils.cpython-310-pytest-7.2.1.pyc
│ └── test_utils.cpython-310-pytest-7.3.1.pyc
├── test_CSS.py
├── test_subset_selection.py
├── test_utils.py
└── utils
├── .DS_Store
└── __pycache__
└── test_general.cpython-310-pytest-7.2.1.pyc
```
20 changes: 10 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

## Installation

1. If you do not have conda installed already, please install it. There are many ways to get conda. We recommend installing Mambaforge which is a conda installation with mamba installed by default and set to use conda-forge as the default set of package repositories.
This fork uses [uv](https://docs.astral.sh/uv/). From the repo root:

2. Clone the git repo:
```
git clone git@github.com:AnavSood/CSS.git
uv sync
uv run pytest tests/
```

3. Set up your conda environment. The list of packages that will be installed inside your conda environment can be seen in `environment.yml`.
```
mamba update -y conda
# create a development virtual environment with useful tools
mamba env create
conda activate CSS
```
`pycss` itself is pure Python. The original Cython
[choldate](https://github.com/modusdatascience/choldate) dependency is
replaced by a pure-NumPy implementation in `pycss/chol.py`; the original
package is vendored under `vendor/choldate/` (with the build fixes from
[choldate PR #8](https://github.com/modusdatascience/choldate/pull/8)) as a
dev-only dependency so the replacement is tested against it
(`tests/test_chol.py`). Reference benchmarks live in `benchmarks/`.
25 changes: 25 additions & 0 deletions benchmarks/RESULTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Reference Benchmarks: cholupdate

**Date:** 2026-07-18

**Machine:**
- Architecture: arm64
- OS: macOS 26.5.2
- Python: 3.12.13

## Results

```
cholupdate micro-benchmark (mean per call)
p numpy (ms) choldate (ms) numpy/choldate
10 0.0278 0.0006 49.7
50 0.1346 0.0022 62.4
100 0.2716 0.0072 37.9
250 0.7090 0.0359 19.7
500 1.5132 0.1415 10.7
1000 3.4433 0.5547 6.2

swapping_css end-to-end (p=100, k=10, single init)
numpy backend: 0.005 s
choldate backend: 0.003 s
```
66 changes: 66 additions & 0 deletions benchmarks/bench_chol.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
"""Reference benchmark: pure-NumPy cholupdate vs vendored Cython choldate.

Run from the repo root: uv run python benchmarks/bench_chol.py
Numbers are for reference only; optimization is not a project goal.
"""
import time

import numpy as np

import pycss.subset_selection as ss
from pycss.chol import cholupdate as np_cholupdate

try:
from choldate import cholupdate as c_cholupdate
except ImportError:
c_cholupdate = None


def bench_update(fn, p, reps=200, seed=0):
rng = np.random.default_rng(seed)
M = rng.standard_normal((p, p))
A = M @ M.T + p * np.eye(p)
R0 = np.ascontiguousarray(np.linalg.cholesky(A).T)
xs = rng.standard_normal((reps, p))
start = time.perf_counter()
for i in range(reps):
fn(R0.copy(), xs[i].copy())
return (time.perf_counter() - start) / reps


def bench_swapping(backend, p=100, k=10, seed=0):
"""Time swapping_css with pycss.subset_selection's cholupdate swapped out."""
rng = np.random.default_rng(seed)
M = rng.standard_normal((2 * p, p))
Sigma = M.T @ M / (2 * p)
original = ss.cholupdate
ss.cholupdate = backend
try:
start = time.perf_counter()
ss.swapping_css(Sigma, k, S_init=np.arange(k))
return time.perf_counter() - start
finally:
ss.cholupdate = original


def main():
print("cholupdate micro-benchmark (mean per call)")
print(f"{'p':>6} {'numpy (ms)':>12} {'choldate (ms)':>14} {'numpy/choldate':>15}")
for p in [10, 50, 100, 250, 500, 1000]:
t_np = bench_update(np_cholupdate, p) * 1e3
if c_cholupdate is not None:
t_c = bench_update(c_cholupdate, p) * 1e3
print(f"{p:>6} {t_np:>12.4f} {t_c:>14.4f} {t_np / t_c:>15.1f}")
else:
print(f"{p:>6} {t_np:>12.4f} {'n/a':>14} {'n/a':>15}")

print("\nswapping_css end-to-end (p=100, k=10, single init)")
t_np = bench_swapping(np_cholupdate)
print(f"numpy backend: {t_np:.3f} s")
if c_cholupdate is not None:
t_c = bench_swapping(c_cholupdate)
print(f"choldate backend: {t_c:.3f} s")


if __name__ == "__main__":
main()
Loading