Graph-smooth sparse orthogonal PCA with a split-variable solver and a synthetic-first evaluation. This repository contains the Track-A manuscript, code, and reproducible synthetic experiments.
- PCA (dense baseline)
- Minimal A-ManPG (external sparse orthogonal baseline, no graph)
- SparseNoGraph (in-family ablation with (\lambda_2=0))
- Proposed (sparse + graph-smooth + joint orthogonality)
- Chain robustness (seeds 0–4)
- SBM robustness (seeds 0–4)
- SBM (\lambda_2) sweep (single seed)
Run a smoke test (r=1, rho=5.0, eta_A=0.05):
uv run python main.py configs/smoke_r1.yamlRun the small r=3 config:
uv run python main.py configs/small_r3.yaml- Run robustness configs (chain + SBM, seeds 0–4):
for s in 0 1 2 3 4; do
uv run python main.py configs/robust_chain_seed${s}.yaml
uv run python main.py configs/robust_sbm_seed${s}.yaml
done- Run SBM (\lambda_2) sweep:
for val in 0p00 0p05 0p10 0p20 0p50; do
uv run python main.py configs/sbm_lambda2_${val}.yaml
done- Regenerate the sweep panel figure:
uv run python - <<'PY'
from pathlib import Path
import json
import numpy as np
import matplotlib.pyplot as plt
root = Path('outputs')
order = ['0p00','0p05','0p10','0p20','0p50']
lams = [0.0,0.05,0.1,0.2,0.5]
support_f1 = []
smooth_norm = []
expl_var = []
for tag in order:
metrics = json.loads((root / f'sbm_lambda2_{tag}' / 'metrics.json').read_text())
proposed = metrics['Proposed']
support_f1.append(proposed['support_metrics']['union']['f1'])
smooth_norm.append(proposed['graph_smoothness_norm_trueL'])
expl_var.append(proposed['shared_explained_variance'])
fig, axes = plt.subplots(1, 3, figsize=(10, 3))
axes[0].plot(lams, support_f1, marker='o')
axes[0].set_title('Support F1')
axes[0].set_xlabel('lambda2')
axes[1].plot(lams, smooth_norm, marker='o')
axes[1].set_title('Graph Smoothness (norm)')
axes[1].set_xlabel('lambda2')
axes[2].plot(lams, expl_var, marker='o')
axes[2].set_title('Shared Explained Variance')
axes[2].set_xlabel('lambda2')
plt.tight_layout()
Path('figures').mkdir(exist_ok=True)
plt.savefig('figures/sbm_lambda2_sweep_panel.png', dpi=200)
PY- Update tables in
latex/manuscript_sample.texusing the newly generated outputs. The tables in the manuscript should match the means/stds computed from themetrics.jsonfiles inoutputs/.
Compile from the repo root (not from inside latex/):
pdflatex latex/manuscript_sample.tex
BIBINPUTS=latex: bibtex manuscript_sample
pdflatex latex/manuscript_sample.tex
pdflatex latex/manuscript_sample.texEach run writes to outputs/<run_name>/:
artifacts.npzandartifacts.jsonmanifest.jsonmetrics.jsonandmetrics.csvplots/convergence traces
The frozen Track-A manuscript PDF is stored at:
paper/paper1-trackA-v1.pdf