Spaces:
Runtime error
Runtime error
docs: consolidate documentation and add working patterns
Browse files- Update CLAUDE.md with effective working patterns from sessions
- Merge SESSION_SUMMARY.md into DEVELOPMENT.md changelog
- Archive outdated docs/QUICKSTART.md (superseded by GETTING_STARTED.md)
- Add docs/archive/ to .gitignore
Key additions to CLAUDE.md:
- Verification-first development approach
- Incremental validation patterns
- Session continuity checklist
- Key files reference section
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- .gitignore +1 -0
- CLAUDE.md +127 -229
- DEVELOPMENT.md +96 -181
- SESSION_SUMMARY.md +0 -149
- docs/QUICKSTART.md +0 -207
.gitignore
CHANGED
|
@@ -215,3 +215,4 @@ CLEAN_repo/
|
|
| 215 |
notebooks_archive/
|
| 216 |
scripts/archive/
|
| 217 |
notebooks/*/archive/
|
|
|
|
|
|
| 215 |
notebooks_archive/
|
| 216 |
scripts/archive/
|
| 217 |
notebooks/*/archive/
|
| 218 |
+
docs/archive/
|
CLAUDE.md
CHANGED
|
@@ -1,39 +1,50 @@
|
|
| 1 |
# Claude Code Guidelines for CPR
|
| 2 |
|
| 3 |
-
##
|
| 4 |
-
|
| 5 |
-
###
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
|
| 7 |
-
-
|
| 8 |
-
- DO NOT use `| head -n X` or `| tail -n X` to truncate output - these cause buffering problems
|
| 9 |
-
- Instead, let commands complete fully, or use `-max-lines` flags if the command supports them
|
| 10 |
-
- For log monitoring, prefer reading files directly rather than piping through filters
|
| 11 |
|
| 12 |
-
##
|
| 13 |
|
| 14 |
-
|
| 15 |
-
-
|
| 16 |
-
-
|
|
|
|
| 17 |
|
| 18 |
### IMPORTANT: Use $HOME2 for storage, not $HOME
|
| 19 |
-
|
| 20 |
-
- `$
|
| 21 |
-
- `$HOME2
|
| 22 |
-
-
|
| 23 |
-
- For pip cache: `export PIP_CACHE_DIR=$HOME2/.pip_cache`
|
| 24 |
-
- For conda envs: use `$HOME2/miniconda3` or shared conda at `/shared/software/miniconda3/latest`
|
| 25 |
-
- For temporary build files: use `$HOME2/tmp` or project directories
|
| 26 |
-
- NEVER create caches or large files in `$HOME` - builds will fail with disk quota errors
|
| 27 |
|
| 28 |
### IMPORTANT: Use SLURM for GPU or heavy CPU tasks
|
| 29 |
-
|
| 30 |
-
-
|
| 31 |
-
-
|
| 32 |
-
-
|
| 33 |
-
- For GPU jobs: `#SBATCH --partition=gpu`
|
| 34 |
-
- For CPU builds: `#SBATCH --partition=standard`
|
| 35 |
-
- Example SLURM scripts in `scripts/slurm_*.sh`
|
| 36 |
-
- Always use `eval "$(/shared/software/miniconda3/latest/bin/conda shell.bash hook)"` for conda in SLURM jobs
|
| 37 |
|
| 38 |
---
|
| 39 |
|
|
@@ -43,241 +54,128 @@
|
|
| 43 |
- **Title**: "Functional protein mining with conformal guarantees"
|
| 44 |
- **Journal**: Nature Communications (2025) 16:85
|
| 45 |
- **DOI**: https://doi.org/10.1038/s41467-024-55676-y
|
| 46 |
-
- **Authors**: Ron S. Boger, Seyone Chithrananda, Anastasios N. Angelopoulos, Peter H. Yoon, Michael I. Jordan, Jennifer A. Doudna
|
| 47 |
|
| 48 |
-
###
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
| 53 |
|
| 54 |
### Core Algorithms (in `protein_conformal/util.py`)
|
| 55 |
-
- `get_thresh_FDR()` / `get_thresh_new_FDR()` - FDR threshold
|
| 56 |
-
- `get_thresh_new()` - FNR threshold
|
| 57 |
-
- `simplifed_venn_abers_prediction()` - Calibrated
|
| 58 |
-
- `scope_hierarchical_loss()` - Hierarchical loss
|
| 59 |
-
- `load_database()` / `query()` - FAISS operations
|
| 60 |
-
|
| 61 |
-
### Data Files (Zenodo: https://zenodo.org/records/14272215)
|
| 62 |
-
- `pfam_new_proteins.npy` (2.5 GB) - Pfam calibration data
|
| 63 |
-
- `lookup_embeddings.npy` (1.1 GB) - UniProt embeddings
|
| 64 |
-
- `afdb_embeddings_protein_vec.npy` (4.7 GB) - AFDB embeddings
|
| 65 |
|
| 66 |
-
###
|
| 67 |
-
|
| 68 |
-
|
| 69 |
|
| 70 |
---
|
| 71 |
|
| 72 |
-
##
|
| 73 |
-
|
| 74 |
-
### 2025-01-28 14:30 PST - Initial Session
|
| 75 |
-
|
| 76 |
-
**Completed:**
|
| 77 |
-
- [x] Merged `origin/gradio-ron` into local (NOT pushed to origin/main)
|
| 78 |
-
- [x] Removed duplicate `src/protein_conformal/` directory (2,280 lines)
|
| 79 |
-
- [x] Removed `pfam/tmp.py` temp file
|
| 80 |
-
- [x] Created `pyproject.toml` with modern packaging and `cpr` CLI entry point
|
| 81 |
-
- [x] Created test infrastructure: `tests/conftest.py`, `tests/test_util.py`
|
| 82 |
-
- [x] Created documentation: `DEVELOPMENT.md`, `docs/INSTALLATION.md`, `docs/QUICKSTART.md`
|
| 83 |
-
- [x] Created `REPO_ORGANIZATION.md` mapping paper figures to code
|
| 84 |
-
- [x] Added `docker-compose.yml`
|
| 85 |
-
- [x] Read and analyzed the full Nature Communications paper
|
| 86 |
-
|
| 87 |
-
**Current Branch:** `refactor/cpr-cleanup-and-tests` (4 commits ahead of origin/main)
|
| 88 |
-
|
| 89 |
-
**Key Findings:**
|
| 90 |
-
- CLEAN data file exists and is NOT empty (84 MB)
|
| 91 |
-
- `results/fdr_thresholds.csv` is nearly empty (just headers)
|
| 92 |
-
- Local main has merge but origin/main is clean - can reset if needed
|
| 93 |
-
|
| 94 |
-
**Pending Tasks:**
|
| 95 |
-
1. Download Zenodo data files
|
| 96 |
-
2. Verify JCVI Syn3.0 results (39.6% annotation rate) - HIGHEST PRIORITY
|
| 97 |
-
3. Run test suite and fix failures
|
| 98 |
-
4. Add Protein-Vec model weights (user will provide)
|
| 99 |
-
5. Create CLI entry point
|
| 100 |
-
|
| 101 |
-
**Questions for User:**
|
| 102 |
-
- Protein-Vec weights location? → User will add to folder
|
| 103 |
-
- Zenodo download? → User asked Claude to download
|
| 104 |
|
| 105 |
-
|
|
|
|
| 106 |
|
| 107 |
-
###
|
|
|
|
|
|
|
|
|
|
|
|
|
| 108 |
|
| 109 |
-
|
| 110 |
-
-
|
| 111 |
-
-
|
| 112 |
-
-
|
| 113 |
-
- [x] Fixed test suite - all 27 tests pass
|
| 114 |
-
- [x] Created CLI: `cpr embed`, `cpr search`, `cpr verify`
|
| 115 |
-
- [x] Extracted Protein-Vec models and copied necessary Python files
|
| 116 |
-
- [x] Fixed `setup.py` conflict with `pyproject.toml`
|
| 117 |
-
- [x] Fixed `__init__.py` to not require gradio for core imports
|
| 118 |
-
- [x] Created `DATA.md` documenting data requirements (GitHub vs Zenodo)
|
| 119 |
-
- [x] Created `LOCAL_NOTES.md` (gitignored) for cluster-specific info
|
| 120 |
-
- [x] Organized `unknown_aa_seqs.*` files into `data/gene_unknown/`
|
| 121 |
-
|
| 122 |
-
**Key Files Changed:**
|
| 123 |
-
- `protein_conformal/__init__.py` - Made gradio import optional
|
| 124 |
-
- `protein_conformal/util.py` - Fixed FDR bug, numpy deprecation
|
| 125 |
-
- `protein_conformal/cli.py` - NEW: CLI entry point
|
| 126 |
-
- `tests/test_util.py` - Fixed incorrect test expectation
|
| 127 |
-
- `setup.py` - Fixed src/ directory reference
|
| 128 |
-
- `DATA.md` - NEW: Data documentation
|
| 129 |
-
|
| 130 |
-
**Verification Results:**
|
| 131 |
-
- FDR threshold (α=0.1): λ = 0.999980225003127
|
| 132 |
-
- Syn3.0 hits: 59/149 = 39.6% (matches paper Figure 2A)
|
| 133 |
|
| 134 |
-
|
| 135 |
-
-
|
| 136 |
-
-
|
|
|
|
|
|
|
| 137 |
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
|
| 144 |
---
|
| 145 |
|
| 146 |
-
##
|
|
|
|
|
|
|
| 147 |
|
| 148 |
**Completed:**
|
| 149 |
-
-
|
| 150 |
-
|
| 151 |
-
-
|
| 152 |
-
|
| 153 |
-
-
|
| 154 |
-
-
|
| 155 |
-
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
| `conformal_pfam_with_lookup_dataset.npy` (LEAKY) | 10,000 | 3.00% | 0.9999644648 |
|
| 165 |
-
| Paper reported | — | — | 0.9999802250 |
|
| 166 |
-
|
| 167 |
-
The backup dataset has **data leakage**: first 50 samples all have "PF01266;" (same Pfam family).
|
| 168 |
-
The correct dataset (`pfam_new_proteins.npy`) has diverse families and matches paper threshold.
|
| 169 |
-
|
| 170 |
-
**Files Changed:**
|
| 171 |
-
- `scripts/slurm_calibrate_fdr.sh` - Fixed to use correct dataset
|
| 172 |
-
- `DEVELOPMENT.md` - Added data leakage warning
|
| 173 |
-
- `scripts/verify_dali.py` - NEW: DALI verification
|
| 174 |
-
- `scripts/verify_clean.py` - NEW: CLEAN verification
|
| 175 |
-
- `scripts/quick_fdr_check.py` - NEW: Dataset comparison
|
| 176 |
-
- `Dockerfile`, `apptainer.def` - NEW: Container definitions
|
| 177 |
-
|
| 178 |
-
**Verification Summary:**
|
| 179 |
-
| Claim | Paper | Reproduced | Status |
|
| 180 |
-
|-------|-------|------------|--------|
|
| 181 |
-
| Syn3.0 annotation | 39.6% (59/149) | 39.6% (59/149) | ✓ EXACT |
|
| 182 |
-
| FDR threshold | 0.9999802250 | 0.9999820199 | ✓ (~0.002% diff) |
|
| 183 |
-
| DALI TPR | 82.8% | 81.8% | ✓ (~1% diff) |
|
| 184 |
-
| DALI reduction | 31.5% | 31.5% | ✓ EXACT |
|
| 185 |
-
| CLEAN loss | ≤ α=1.0 | 0.97 | ✓ |
|
| 186 |
-
|
| 187 |
-
**Next Steps:**
|
| 188 |
-
1. Test precomputed probability lookup CSV is reproducible
|
| 189 |
-
2. Add `cpr prob --precomputed` for fast probability with model-specific calibration
|
| 190 |
-
3. Build and test Docker/Apptainer images
|
| 191 |
-
4. Integrate full CLEAN model verification (requires CLEAN package)
|
| 192 |
|
| 193 |
---
|
| 194 |
|
| 195 |
-
### 2026-02-
|
| 196 |
|
| 197 |
**Completed:**
|
| 198 |
-
-
|
| 199 |
-
-
|
| 200 |
-
-
|
| 201 |
-
-
|
| 202 |
-
-
|
| 203 |
-
- [x] Tested CLEAN embeddings on GPU - **WORKING**
|
| 204 |
-
- [x] Committed and pushed Apptainer fixes to origin
|
| 205 |
-
|
| 206 |
-
**CLEAN Embedding Test Results:**
|
| 207 |
-
```
|
| 208 |
-
GPU: NVIDIA H200
|
| 209 |
-
Embeddings shape: (2, 128) # CLEAN uses 128-dim, not 1024 like Protein-Vec
|
| 210 |
-
Min: -2.7802, Max: 2.5827, Mean: 0.0498
|
| 211 |
-
```
|
| 212 |
-
- Requires: `pip install fair-esm>=2.0.0`
|
| 213 |
-
- CLEAN model weights: `CLEAN_repo/app/data/pretrained/CLEAN_pretrained/`
|
| 214 |
-
|
| 215 |
-
**Blocked:**
|
| 216 |
-
- **Apptainer build**: glibc 2.33/2.34 mismatch - PyTorch 2.1.0 has older glibc than cluster's fakeroot
|
| 217 |
-
- **Fix**: Update to `pytorch/pytorch:2.4.0-cuda12.1-cudnn9-runtime` base image
|
| 218 |
-
|
| 219 |
-
**Running Jobs:**
|
| 220 |
-
- Job 1012489: FDR thresholds (exact match) - ~50 min, still on α=0.001
|
| 221 |
-
- Job 1012530: FNR thresholds (exact + partial) - just started
|
| 222 |
-
|
| 223 |
-
**Files Created/Modified:**
|
| 224 |
-
- `scripts/compute_fnr_table.py` - NEW: FNR threshold computation
|
| 225 |
-
- `scripts/slurm_compute_fnr_thresholds.sh` - NEW: SLURM job for FNR
|
| 226 |
-
- `scripts/compute_fdr_table.py` - Added `--partial` flag
|
| 227 |
-
- `scripts/slurm_compute_fdr_thresholds.sh` - Increased time/memory
|
| 228 |
-
- `apptainer.def` - Added `%setup` section for mount points
|
| 229 |
-
|
| 230 |
-
**Next Steps:**
|
| 231 |
-
1. Wait for FDR job to complete, verify α=0.1 ≈ 0.999980225
|
| 232 |
-
2. Submit partial match FDR job once exact matches verified
|
| 233 |
-
3. Update README with CLEAN embedding instructions
|
| 234 |
-
4. Update Apptainer base image to PyTorch 2.4+
|
| 235 |
-
5. Update GETTING_STARTED.md with computed thresholds
|
| 236 |
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
```
|
| 242 |
-
### YYYY-MM-DD HH:MM TZ - Session Description
|
| 243 |
|
| 244 |
-
|
| 245 |
-
- [ ] Task 1
|
| 246 |
-
- [ ] Task 2
|
| 247 |
|
| 248 |
-
|
| 249 |
-
- Issue 1
|
| 250 |
|
| 251 |
-
|
| 252 |
-
-
|
| 253 |
-
-
|
| 254 |
-
```
|
| 255 |
|
| 256 |
---
|
| 257 |
|
| 258 |
-
## Best Practices
|
| 259 |
|
| 260 |
### Testing
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
|
|
|
|
|
|
| 264 |
|
| 265 |
### Git Workflow
|
| 266 |
-
- Work on feature branches,
|
| 267 |
-
-
|
| 268 |
-
- Use descriptive
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 269 |
|
| 270 |
### Code Style
|
| 271 |
-
- Follow
|
| 272 |
- Use numpy for numerical operations
|
| 273 |
-
- Use FAISS for similarity search
|
| 274 |
-
|
| 275 |
-
### Notebooks
|
| 276 |
-
- Notebooks are for analysis/visualization, not core logic
|
| 277 |
-
- Core algorithms should be in `protein_conformal/`
|
| 278 |
-
- Notebooks should import from the package, not duplicate code
|
| 279 |
-
|
| 280 |
-
### Documentation
|
| 281 |
-
- Update `REPO_ORGANIZATION.md` when adding new notebooks
|
| 282 |
-
- Keep this log updated with timestamped entries
|
| 283 |
-
- Document any deviations from paper methods
|
|
|
|
| 1 |
# Claude Code Guidelines for CPR
|
| 2 |
|
| 3 |
+
## Working Patterns That Help
|
| 4 |
+
|
| 5 |
+
### Verification-First Development
|
| 6 |
+
- Before changing code, verify current behavior matches expectations
|
| 7 |
+
- Run existing tests before and after changes
|
| 8 |
+
- For paper reproduction, verify numbers match before claiming success
|
| 9 |
+
- Use `scripts/verify_*.py` to check paper claims
|
| 10 |
+
|
| 11 |
+
### Incremental Validation
|
| 12 |
+
- When running long jobs, check intermediate results (e.g., α=0.1 before waiting for all α levels)
|
| 13 |
+
- Use SLURM job logs to monitor progress: `cat logs/job_*.log | tail -20`
|
| 14 |
+
- Submit fast/reduced trials first to validate approach, then full runs
|
| 15 |
+
|
| 16 |
+
### Cleanup as You Go
|
| 17 |
+
- Archive (don't delete) old scripts - they may have useful patterns
|
| 18 |
+
- Use `scripts/archive/` and `notebooks/*/archive/` for superseded code
|
| 19 |
+
- Keep only essential SLURM scripts in main directories
|
| 20 |
+
- Consolidate documentation rather than creating new files
|
| 21 |
+
|
| 22 |
+
### Session Continuity
|
| 23 |
+
- Check `DEVELOPMENT.md` changelog for recent work
|
| 24 |
+
- Check running SLURM jobs: `squeue -u ronb`
|
| 25 |
+
- Check `results/*.csv` for computed values
|
| 26 |
+
- The development log below tracks session-to-session context
|
| 27 |
|
| 28 |
+
---
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
+
## Bash Guidelines
|
| 31 |
|
| 32 |
+
### IMPORTANT: Avoid commands that cause output buffering issues
|
| 33 |
+
- DO NOT pipe through `head`, `tail`, `less`, or `more` when monitoring
|
| 34 |
+
- Use command-specific flags: `git log -n 10` not `git log | head -10`
|
| 35 |
+
- For log files, read directly rather than piping through filters
|
| 36 |
|
| 37 |
### IMPORTANT: Use $HOME2 for storage, not $HOME
|
| 38 |
+
- `$HOME` (/home/ronb) has limited quota - builds will fail
|
| 39 |
+
- `$HOME2` (/groups/doudna/projects/ronb/) has 2 PB storage
|
| 40 |
+
- Set: `APPTAINER_CACHEDIR=$HOME2/.apptainer_cache`
|
| 41 |
+
- Set: `PIP_CACHE_DIR=$HOME2/.pip_cache`
|
|
|
|
|
|
|
|
|
|
|
|
|
| 42 |
|
| 43 |
### IMPORTANT: Use SLURM for GPU or heavy CPU tasks
|
| 44 |
+
- NEVER run GPU code on login nodes - submit to SLURM
|
| 45 |
+
- Partitions: `standard` (CPU), `gpu` (GPU), `memory` (high-mem)
|
| 46 |
+
- Always use `eval "$(/shared/software/miniconda3/latest/bin/conda shell.bash hook)"` in SLURM
|
| 47 |
+
- Example scripts: `scripts/slurm_*.sh`
|
|
|
|
|
|
|
|
|
|
|
|
|
| 48 |
|
| 49 |
---
|
| 50 |
|
|
|
|
| 54 |
- **Title**: "Functional protein mining with conformal guarantees"
|
| 55 |
- **Journal**: Nature Communications (2025) 16:85
|
| 56 |
- **DOI**: https://doi.org/10.1038/s41467-024-55676-y
|
|
|
|
| 57 |
|
| 58 |
+
### Verified Paper Claims ✅
|
| 59 |
+
| Claim | Paper Value | Verified Value |
|
| 60 |
+
|-------|-------------|----------------|
|
| 61 |
+
| Syn3.0 annotation (α=0.1) | 39.6% (59/149) | 39.6% (59/149) |
|
| 62 |
+
| FDR threshold (α=0.1) | 0.9999802250 | 0.9999801 |
|
| 63 |
+
| DALI TPR | 82.8% | 81.8% |
|
| 64 |
+
| DALI DB reduction | 31.5% | 31.5% |
|
| 65 |
+
| CLEAN loss ≤ α | 1.0 | 0.97 |
|
| 66 |
|
| 67 |
### Core Algorithms (in `protein_conformal/util.py`)
|
| 68 |
+
- `get_thresh_FDR()` / `get_thresh_new_FDR()` - FDR threshold
|
| 69 |
+
- `get_thresh_new()` - FNR threshold
|
| 70 |
+
- `simplifed_venn_abers_prediction()` - Calibrated probabilities
|
| 71 |
+
- `scope_hierarchical_loss()` - Hierarchical loss
|
| 72 |
+
- `load_database()` / `query()` - FAISS operations
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
|
| 74 |
+
### ⚠️ Data Leakage Warning
|
| 75 |
+
**DO NOT USE** `conformal_pfam_with_lookup_dataset.npy` from backup directories.
|
| 76 |
+
**USE** `pfam_new_proteins.npy` from Zenodo - produces correct threshold.
|
| 77 |
|
| 78 |
---
|
| 79 |
|
| 80 |
+
## Key Files Reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
|
| 82 |
+
### CLI
|
| 83 |
+
- `protein_conformal/cli.py` - Main CLI (`cpr embed`, `cpr search`, `cpr verify`)
|
| 84 |
|
| 85 |
+
### Threshold Computation
|
| 86 |
+
- `scripts/compute_fdr_table.py` - FDR thresholds (use `--partial` for partial match)
|
| 87 |
+
- `scripts/compute_fnr_table.py` - FNR thresholds
|
| 88 |
+
- `scripts/slurm_compute_fdr_thresholds.sh` - SLURM wrapper
|
| 89 |
+
- `scripts/slurm_compute_fnr_thresholds.sh` - SLURM wrapper
|
| 90 |
|
| 91 |
+
### Verification
|
| 92 |
+
- `scripts/verify_syn30.py` - JCVI Syn3.0 (Figure 2A)
|
| 93 |
+
- `scripts/verify_dali.py` - DALI prefiltering (Tables 4-6)
|
| 94 |
+
- `scripts/verify_clean.py` - CLEAN enzyme (Tables 1-2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
+
### Results
|
| 97 |
+
- `results/fdr_thresholds.csv` - FDR thresholds with stats
|
| 98 |
+
- `results/fnr_thresholds.csv` - FNR exact match thresholds
|
| 99 |
+
- `results/fnr_thresholds_partial.csv` - FNR partial match thresholds
|
| 100 |
+
- `results/dali_thresholds.csv` - DALI prefiltering results
|
| 101 |
|
| 102 |
+
### Documentation
|
| 103 |
+
- `GETTING_STARTED.md` - User quick-start (most important)
|
| 104 |
+
- `DEVELOPMENT.md` - Dev status and changelog
|
| 105 |
+
- `DATA.md` - Data file documentation
|
| 106 |
+
- `REPO_ORGANIZATION.md` - Paper figures → code mapping
|
| 107 |
|
| 108 |
---
|
| 109 |
|
| 110 |
+
## Development Log
|
| 111 |
+
|
| 112 |
+
### 2026-02-03 - Cleanup & Consolidation
|
| 113 |
|
| 114 |
**Completed:**
|
| 115 |
+
- Archived 16 redundant scripts to `scripts/archive/`
|
| 116 |
+
- Archived duplicate Python files from `notebooks/pfam/`
|
| 117 |
+
- Consolidated threshold CSVs (removed "simple" versions)
|
| 118 |
+
- Added full threshold tables to `GETTING_STARTED.md`
|
| 119 |
+
- Merged `SESSION_SUMMARY.md` into `DEVELOPMENT.md`
|
| 120 |
+
- Archived outdated `docs/QUICKSTART.md`
|
| 121 |
+
- Updated this file with working patterns
|
| 122 |
+
|
| 123 |
+
**FDR Job Status:**
|
| 124 |
+
- Job 1012664 (fdr-fast): 20 trials, α=0.1 verified as 0.99998006
|
| 125 |
+
|
| 126 |
+
**Final Structure:**
|
| 127 |
+
- 4 SLURM scripts (build, embed, fdr, fnr)
|
| 128 |
+
- 4 results CSVs (fdr, fnr, fnr_partial, dali)
|
| 129 |
+
- 51 tests passing
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
|
| 131 |
---
|
| 132 |
|
| 133 |
+
### 2026-02-02 - Verification & CLI
|
| 134 |
|
| 135 |
**Completed:**
|
| 136 |
+
- Verified Syn3.0: 59/149 = 39.6% ✅
|
| 137 |
+
- Fixed FDR bug (1D/2D array handling)
|
| 138 |
+
- Created CLI with `embed`, `search`, `verify` commands
|
| 139 |
+
- Created verification scripts for DALI, CLEAN
|
| 140 |
+
- Investigated data leakage in backup dataset
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 141 |
|
| 142 |
+
**Environment:**
|
| 143 |
+
- Conda: `conformal-s` (Python 3.11.10)
|
| 144 |
+
- Packages: faiss 1.9.0, torch 2.5.0, numpy 1.26.4
|
|
|
|
|
|
|
|
|
|
| 145 |
|
| 146 |
+
---
|
|
|
|
|
|
|
| 147 |
|
| 148 |
+
### 2026-01-28 - Initial Session
|
|
|
|
| 149 |
|
| 150 |
+
- Removed duplicate `src/protein_conformal/`
|
| 151 |
+
- Created `pyproject.toml` and test infrastructure
|
| 152 |
+
- Created initial documentation
|
|
|
|
| 153 |
|
| 154 |
---
|
| 155 |
|
| 156 |
+
## Best Practices
|
| 157 |
|
| 158 |
### Testing
|
| 159 |
+
```bash
|
| 160 |
+
pytest tests/ -v # Run all tests
|
| 161 |
+
pytest tests/test_util.py -v # Just util tests
|
| 162 |
+
pytest tests/test_cli.py -v # Just CLI tests
|
| 163 |
+
```
|
| 164 |
|
| 165 |
### Git Workflow
|
| 166 |
+
- Work on feature branches, not main
|
| 167 |
+
- Run tests before committing
|
| 168 |
+
- Use descriptive commits referencing paper figures/tables
|
| 169 |
+
|
| 170 |
+
### SLURM Jobs
|
| 171 |
+
```bash
|
| 172 |
+
squeue -u ronb # Check running jobs
|
| 173 |
+
cat logs/job_*.log | tail -20 # Check recent output (use Read tool)
|
| 174 |
+
scancel JOBID # Cancel a job
|
| 175 |
+
```
|
| 176 |
|
| 177 |
### Code Style
|
| 178 |
+
- Follow patterns in `protein_conformal/util.py`
|
| 179 |
- Use numpy for numerical operations
|
| 180 |
+
- Use FAISS for similarity search
|
| 181 |
+
- Notebooks for analysis, package for algorithms
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DEVELOPMENT.md
CHANGED
|
@@ -1,203 +1,107 @@
|
|
| 1 |
# Development Notes: CPR Refactoring Project
|
| 2 |
|
| 3 |
-
This document tracks the ongoing refactoring of the Conformal Protein Retrieval (CPR) codebase
|
| 4 |
|
| 5 |
-
**Paper**: [Functional protein mining with conformal guarantees](https://www.nature.com/articles/s41467-024-55676-y) (Nature Communications,
|
| 6 |
|
| 7 |
**Authors**: Ron S. Boger, Seyone Chithrananda, Anastasios N. Angelopoulos, Peter H. Yoon, Michael I. Jordan, Jennifer A. Doudna
|
| 8 |
|
| 9 |
---
|
| 10 |
|
| 11 |
-
## Current Status
|
| 12 |
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
1. **Merged Gradio UI branch** (`origin/gradio-ron` → `main`)
|
| 16 |
-
- Added Gradio web interface in `protein_conformal/backend/`
|
| 17 |
-
- Added Dockerfile and `environment.yml`
|
| 18 |
-
- Reorganized notebooks into `notebooks/` directory
|
| 19 |
-
- Added FDR/FNR threshold precomputation scripts
|
| 20 |
-
- Added `requirements.txt`
|
| 21 |
-
|
| 22 |
-
2. **Removed duplicate code**
|
| 23 |
-
- Deleted `src/protein_conformal/` (duplicate of `protein_conformal/`)
|
| 24 |
-
- Deleted `pfam/tmp.py` (temporary debug file)
|
| 25 |
-
|
| 26 |
-
3. **Set up modern Python packaging**
|
| 27 |
-
- Created `pyproject.toml` with:
|
| 28 |
-
- Package metadata and dependencies
|
| 29 |
-
- CLI entry point: `cpr` command
|
| 30 |
-
- Optional dependency groups: `[gui]`, `[api]`, `[dev]`, `[all]`
|
| 31 |
-
- pytest and code quality tool configuration
|
| 32 |
-
|
| 33 |
-
4. **Created test infrastructure**
|
| 34 |
-
- `tests/` directory with pytest fixtures
|
| 35 |
-
- `tests/conftest.py` - shared fixtures for testing
|
| 36 |
-
- `tests/test_util.py` - comprehensive test suite for `protein_conformal/util.py`
|
| 37 |
-
|
| 38 |
-
### Test Coverage
|
| 39 |
-
|
| 40 |
-
The test suite covers:
|
| 41 |
-
|
| 42 |
-
| Module | Functions Tested |
|
| 43 |
-
|--------|------------------|
|
| 44 |
-
| FASTA parsing | `read_fasta()` |
|
| 45 |
-
| FAISS operations | `load_database()`, `query()` |
|
| 46 |
-
| Risk metrics | `risk()`, `risk_1d()`, `calculate_false_negatives()`, `calculate_true_positives()` |
|
| 47 |
-
| Conformal thresholds | `get_thresh_new()`, `get_thresh_new_FDR()`, `get_thresh_FDR()` |
|
| 48 |
-
| Venn-Abers | `simplifed_venn_abers_prediction()`, `get_isotone_regression()` |
|
| 49 |
-
| Hierarchical loss | `scope_hierarchical_loss()` |
|
| 50 |
-
| Validation | `validate_lhat_new()` |
|
| 51 |
-
|
| 52 |
-
### Known Test Cases from Notebooks
|
| 53 |
-
|
| 54 |
-
From `notebooks/scope/analyze_scope_protein_vec.ipynb`:
|
| 55 |
-
- **FDR threshold**: `alpha=0.1, delta=0.5, N=100` → `lhat=0.999987906879849`, `risk=0.0358`
|
| 56 |
-
- **Data shape**: 400 queries × 14,777 lookup proteins
|
| 57 |
-
- **Similarity range**: 0.9992... to 0.9999...
|
| 58 |
-
- **Hierarchical loss**: `scope_hierarchical_loss('a.1.1.1', 'a.1.1.1')` → `(0, True)`
|
| 59 |
-
|
| 60 |
-
---
|
| 61 |
-
|
| 62 |
-
## Planned Work
|
| 63 |
-
|
| 64 |
-
### Phase 1: Validate Current Code (This Branch)
|
| 65 |
-
|
| 66 |
-
1. **Run test suite** and fix any failures
|
| 67 |
-
2. **Add integration tests** with small sample data
|
| 68 |
-
3. **Verify numerical reproducibility** against notebook outputs
|
| 69 |
-
|
| 70 |
-
### Phase 2: CLI Implementation
|
| 71 |
-
|
| 72 |
-
Create a clean CLI interface:
|
| 73 |
-
|
| 74 |
-
```bash
|
| 75 |
-
# Embedding
|
| 76 |
-
cpr embed input.fasta -o embeddings.npy --model protein-vec
|
| 77 |
-
|
| 78 |
-
# Search with FDR control
|
| 79 |
-
cpr search query.npy --lookup lookup.npy --fdr 0.1 -o results.csv
|
| 80 |
-
|
| 81 |
-
# Search with FNR control
|
| 82 |
-
cpr search query.npy --lookup lookup.npy --fnr 0.1 -o results.csv
|
| 83 |
-
|
| 84 |
-
# Compute probabilities
|
| 85 |
-
cpr probs results.csv --calibration pfam_new_proteins.npy --partial
|
| 86 |
-
|
| 87 |
-
# Launch GUI
|
| 88 |
-
cpr gui --port 7860
|
| 89 |
-
```
|
| 90 |
-
|
| 91 |
-
### Phase 3: Documentation
|
| 92 |
-
|
| 93 |
-
1. **Installation guide** with all dependencies
|
| 94 |
-
2. **Quick start** with example workflows
|
| 95 |
-
3. **Data download instructions** (Zenodo files)
|
| 96 |
-
4. **API reference** for programmatic use
|
| 97 |
-
|
| 98 |
-
### Phase 4: Code Cleanup
|
| 99 |
-
|
| 100 |
-
1. Remove remaining duplicate/dead code
|
| 101 |
-
2. Standardize imports and module structure
|
| 102 |
-
3. Add type hints to public functions
|
| 103 |
-
4. Ensure consistent error handling
|
| 104 |
-
|
| 105 |
-
---
|
| 106 |
-
|
| 107 |
-
## Required Data Files
|
| 108 |
-
|
| 109 |
-
### From Zenodo (https://zenodo.org/records/14272215)
|
| 110 |
|
| 111 |
-
|
| 112 |
-
|------|------|---------|
|
| 113 |
-
| `pfam_new_proteins.npy` | 2.5 GB | **CORRECT** calibration data for FDR/FNR control |
|
| 114 |
-
|
| 115 |
-
#### ⚠️ Data Leakage Warning
|
| 116 |
-
|
| 117 |
-
**DO NOT USE** `conformal_pfam_with_lookup_dataset.npy` from the backup directory. This dataset has **data leakage**:
|
| 118 |
-
- First 50 samples all have the same Pfam family "PF01266;" repeated
|
| 119 |
-
- Positive rate is 3.00% (vs 0.22% in correct dataset)
|
| 120 |
-
- Produces incorrect FDR threshold (~0.999965 vs paper's ~0.999980)
|
| 121 |
|
| 122 |
-
|
| 123 |
-
-
|
| 124 |
-
|
| 125 |
-
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
-
|
| 128 |
-
| `lookup_embeddings.npy` | 1.1 GB | UniProt protein embeddings (lookup database) |
|
| 129 |
-
| `lookup_embeddings_meta_data.tsv` | 560 MB | Metadata for lookup proteins |
|
| 130 |
-
| `afdb_embeddings_protein_vec.npy` | 4.7 GB | AlphaFold DB embeddings |
|
| 131 |
-
| `AFDB_sequences.fasta` | 671 MB | AlphaFold DB sequences |
|
| 132 |
-
|
| 133 |
-
### Protein-Vec Model Weights
|
| 134 |
-
|
| 135 |
-
**TODO**: Document where to obtain Protein-Vec model weights. The embedding script expects:
|
| 136 |
-
- `protein_vec_models/protein_vec.ckpt`
|
| 137 |
-
- `protein_vec_models/protein_vec_params.json`
|
| 138 |
|
| 139 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
|
| 141 |
---
|
| 142 |
|
| 143 |
-
## File Structure
|
| 144 |
|
| 145 |
```
|
| 146 |
conformal-protein-retrieval/
|
| 147 |
├── protein_conformal/ # Main package
|
| 148 |
│ ├── __init__.py
|
| 149 |
-
│ ├──
|
|
|
|
| 150 |
│ ├── embed_protein_vec.py # Protein-Vec embedding
|
| 151 |
-
│ ├── scope_utils.py # SCOPe
|
| 152 |
-
│
|
| 153 |
-
|
| 154 |
-
│
|
| 155 |
-
│
|
| 156 |
-
│
|
| 157 |
-
|
| 158 |
-
│ ├── search.py
|
| 159 |
-
│ ├── get_probs.py
|
| 160 |
-
│ ├── precompute_SVA_probs.py
|
| 161 |
-
│ └── ...
|
| 162 |
├── notebooks/ # Analysis notebooks
|
| 163 |
-
│ ├── pfam/
|
| 164 |
-
│ ├── scope/
|
| 165 |
-
│ ├──
|
| 166 |
-
│ └──
|
| 167 |
├── tests/ # Test suite
|
| 168 |
│ ├── conftest.py
|
| 169 |
-
│
|
| 170 |
-
|
| 171 |
-
├──
|
| 172 |
-
├──
|
| 173 |
-
|
|
|
|
|
|
|
|
|
|
| 174 |
```
|
| 175 |
|
| 176 |
-
|
| 177 |
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
│ │ ├���─ fasta.py
|
| 192 |
-
│ │ └── results.py
|
| 193 |
-
│ └── gui/ # Gradio interface (moved from backend/)
|
| 194 |
-
├── tests/
|
| 195 |
-
├── docs/ # NEW: Documentation
|
| 196 |
-
│ ├── installation.md
|
| 197 |
-
│ ├── quickstart.md
|
| 198 |
-
│ └── api.md
|
| 199 |
-
└── examples/ # NEW: Example scripts
|
| 200 |
-
```
|
| 201 |
|
| 202 |
---
|
| 203 |
|
|
@@ -216,17 +120,28 @@ pytest tests/ --cov=protein_conformal --cov-report=html
|
|
| 216 |
|
| 217 |
---
|
| 218 |
|
| 219 |
-
##
|
| 220 |
|
| 221 |
-
1.
|
| 222 |
-
2.
|
| 223 |
-
3.
|
| 224 |
-
4. Submit PR for review
|
| 225 |
|
| 226 |
---
|
| 227 |
|
| 228 |
-
##
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 229 |
|
| 230 |
-
|
| 231 |
-
-
|
| 232 |
-
-
|
|
|
|
|
|
| 1 |
# Development Notes: CPR Refactoring Project
|
| 2 |
|
| 3 |
+
This document tracks the ongoing refactoring of the Conformal Protein Retrieval (CPR) codebase.
|
| 4 |
|
| 5 |
+
**Paper**: [Functional protein mining with conformal guarantees](https://www.nature.com/articles/s41467-024-55676-y) (Nature Communications, 2025)
|
| 6 |
|
| 7 |
**Authors**: Ron S. Boger, Seyone Chithrananda, Anastasios N. Angelopoulos, Peter H. Yoon, Michael I. Jordan, Jennifer A. Doudna
|
| 8 |
|
| 9 |
---
|
| 10 |
|
| 11 |
+
## Current Status
|
| 12 |
|
| 13 |
+
**Branch**: `refactor/cpr-cleanup-and-tests`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
|
| 15 |
+
### Verified Paper Results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
+
| Claim | Paper | Reproduced | Status |
|
| 18 |
+
|-------|-------|------------|--------|
|
| 19 |
+
| Syn3.0 annotation | 39.6% (59/149) | 39.6% (59/149) | ✅ EXACT |
|
| 20 |
+
| FDR threshold (α=0.1) | 0.9999802250 | 0.9999801 | ✅ Match |
|
| 21 |
+
| DALI TPR | 82.8% | 81.8% | ✅ ~1% diff |
|
| 22 |
+
| DALI reduction | 31.5% | 31.5% | ✅ EXACT |
|
| 23 |
+
| CLEAN loss | ≤ α=1.0 | 0.97 | ✅ Pass |
|
| 24 |
|
| 25 |
+
### Completed Work
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
|
| 27 |
+
#### Phase 1: Code Cleanup ✅
|
| 28 |
+
- Removed duplicate `src/protein_conformal/` directory
|
| 29 |
+
- Archived 16 redundant SLURM/shell scripts
|
| 30 |
+
- Archived duplicate Python files from notebooks
|
| 31 |
+
- Fixed FDR threshold bug (1D/2D array handling)
|
| 32 |
+
- Fixed numpy deprecation warnings
|
| 33 |
+
|
| 34 |
+
#### Phase 2: CLI Implementation ✅
|
| 35 |
+
- Created `cpr` CLI with subcommands: `embed`, `search`, `verify`
|
| 36 |
+
- Unified `cpr search` accepts both FASTA and embeddings
|
| 37 |
+
- Added `--fdr`, `--fnr`, `--threshold`, `--no-filter` options
|
| 38 |
+
- Multi-model support: `--model protein-vec` or `--model clean`
|
| 39 |
+
|
| 40 |
+
#### Phase 3: Testing ✅
|
| 41 |
+
- 51 tests total (27 util + 24 CLI)
|
| 42 |
+
- All tests passing
|
| 43 |
+
- Regression tests for paper-critical values
|
| 44 |
+
|
| 45 |
+
#### Phase 4: Documentation ✅
|
| 46 |
+
- `GETTING_STARTED.md` - comprehensive user guide
|
| 47 |
+
- `DATA.md` - data file documentation
|
| 48 |
+
- `REPO_ORGANIZATION.md` - paper figures → code mapping
|
| 49 |
+
- Full threshold tables in docs
|
| 50 |
+
|
| 51 |
+
#### Phase 5: Containerization (Partial)
|
| 52 |
+
- Created `Dockerfile` and `apptainer.def`
|
| 53 |
+
- Apptainer build blocked by glibc mismatch (needs PyTorch 2.4+ base)
|
| 54 |
|
| 55 |
---
|
| 56 |
|
| 57 |
+
## File Structure
|
| 58 |
|
| 59 |
```
|
| 60 |
conformal-protein-retrieval/
|
| 61 |
├── protein_conformal/ # Main package
|
| 62 |
│ ├── __init__.py
|
| 63 |
+
│ ├── cli.py # CLI entry point (`cpr` command)
|
| 64 |
+
│ ├── util.py # Core algorithms
|
| 65 |
│ ├── embed_protein_vec.py # Protein-Vec embedding
|
| 66 |
+
│ ├── scope_utils.py # SCOPe utilities
|
| 67 |
+
│ └── backend/ # Gradio interface
|
| 68 |
+
├── scripts/ # Standalone scripts
|
| 69 |
+
│ ├── compute_fdr_table.py # FDR threshold computation
|
| 70 |
+
│ ├── compute_fnr_table.py # FNR threshold computation
|
| 71 |
+
│ ├── verify_*.py # Verification scripts
|
| 72 |
+
│ └── slurm_*.sh # SLURM job scripts (4 kept)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 73 |
├── notebooks/ # Analysis notebooks
|
| 74 |
+
│ ├── pfam/ # Pfam/Syn3.0 analysis
|
| 75 |
+
│ ├── scope/ # SCOPe/DALI analysis
|
| 76 |
+
│ ├── clean_selection/ # CLEAN enzyme analysis
|
| 77 |
+
│ └── ec/ # EC classification
|
| 78 |
├── tests/ # Test suite
|
| 79 |
│ ├── conftest.py
|
| 80 |
+
│ ├── test_util.py # 27 tests
|
| 81 |
+
│ └── test_cli.py # 24 tests
|
| 82 |
+
├── results/ # Computed thresholds
|
| 83 |
+
│ ├── fdr_thresholds.csv
|
| 84 |
+
│ ├── fnr_thresholds.csv
|
| 85 |
+
│ ├── fnr_thresholds_partial.csv
|
| 86 |
+
│ └── dali_thresholds.csv
|
| 87 |
+
└── data/ # Data files (see DATA.md)
|
| 88 |
```
|
| 89 |
|
| 90 |
+
---
|
| 91 |
|
| 92 |
+
## Data Files
|
| 93 |
+
|
| 94 |
+
### ⚠️ Data Leakage Warning
|
| 95 |
+
|
| 96 |
+
**DO NOT USE** `conformal_pfam_with_lookup_dataset.npy` from backup directories. This dataset has data leakage:
|
| 97 |
+
- First 50 samples all have the same Pfam family "PF01266;"
|
| 98 |
+
- Positive rate is 3.00% (vs 0.22% in correct dataset)
|
| 99 |
+
- Produces incorrect FDR threshold
|
| 100 |
+
|
| 101 |
+
**USE**: `pfam_new_proteins.npy` from Zenodo with:
|
| 102 |
+
- 1,864 diverse samples
|
| 103 |
+
- 0.22% positive rate
|
| 104 |
+
- Produces threshold matching paper
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
|
| 106 |
---
|
| 107 |
|
|
|
|
| 120 |
|
| 121 |
---
|
| 122 |
|
| 123 |
+
## Remaining Work
|
| 124 |
|
| 125 |
+
1. **Complete FDR threshold table** - job running, α=0.1 verified
|
| 126 |
+
2. **Fix Apptainer build** - update to PyTorch 2.4+ base image
|
| 127 |
+
3. **Merge to main** - after final verification
|
|
|
|
| 128 |
|
| 129 |
---
|
| 130 |
|
| 131 |
+
## Changelog
|
| 132 |
+
|
| 133 |
+
### 2026-02-03
|
| 134 |
+
- Archived 16 redundant scripts to `scripts/archive/`
|
| 135 |
+
- Consolidated threshold CSVs, added full tables to GETTING_STARTED.md
|
| 136 |
+
- Removed duplicate Python files from notebooks
|
| 137 |
+
|
| 138 |
+
### 2026-02-02
|
| 139 |
+
- Verified JCVI Syn3.0 result: 59/149 = 39.6% ✅
|
| 140 |
+
- Fixed FDR threshold bug in `get_thresh_FDR()`
|
| 141 |
+
- Created CLI: `cpr embed`, `cpr search`, `cpr verify`
|
| 142 |
+
- All 51 tests passing
|
| 143 |
|
| 144 |
+
### 2026-01-28
|
| 145 |
+
- Initial cleanup session
|
| 146 |
+
- Removed duplicate `src/protein_conformal/`
|
| 147 |
+
- Created `pyproject.toml` and test infrastructure
|
SESSION_SUMMARY.md
DELETED
|
@@ -1,149 +0,0 @@
|
|
| 1 |
-
# CPR Cleanup Session Summary - 2026-02-03
|
| 2 |
-
|
| 3 |
-
## Overview
|
| 4 |
-
|
| 5 |
-
This session focused on cleaning up and organizing the Conformal Protein Retrieval repository for public release.
|
| 6 |
-
|
| 7 |
-
---
|
| 8 |
-
|
| 9 |
-
## Major Changes
|
| 10 |
-
|
| 11 |
-
### 1. CLI Refactoring
|
| 12 |
-
|
| 13 |
-
**Consolidated to single `cpr search` command** that accepts both FASTA and embeddings:
|
| 14 |
-
|
| 15 |
-
```bash
|
| 16 |
-
# From FASTA (auto-embeds)
|
| 17 |
-
cpr search --input proteins.fasta --output results.csv --fdr 0.1
|
| 18 |
-
|
| 19 |
-
# From embeddings
|
| 20 |
-
cpr search --input embeddings.npy --output results.csv --fdr 0.1
|
| 21 |
-
```
|
| 22 |
-
|
| 23 |
-
- Removed `cpr find` command (was redundant)
|
| 24 |
-
- Added `--fnr` option for FNR-based thresholding
|
| 25 |
-
- Added `--threshold` for manual threshold specification
|
| 26 |
-
- Added `--no-filter` for exploratory searches
|
| 27 |
-
|
| 28 |
-
### 2. Documentation Updates
|
| 29 |
-
|
| 30 |
-
**GETTING_STARTED.md** - Comprehensive rewrite:
|
| 31 |
-
- Added statistical guarantees section (expected marginal FDR)
|
| 32 |
-
- Added wget/curl commands for Zenodo downloads
|
| 33 |
-
- Documented all CLI commands with examples
|
| 34 |
-
- Added CLEAN enzyme classification setup
|
| 35 |
-
- Added legacy script usage documentation
|
| 36 |
-
- Fixed threshold table with actual computed values
|
| 37 |
-
|
| 38 |
-
**data/gene_unknown/README.md** - NEW:
|
| 39 |
-
- Documents JCVI Syn3.0 data source
|
| 40 |
-
- Cites Hutchison et al. Science 2016
|
| 41 |
-
- Explains gene naming conventions
|
| 42 |
-
|
| 43 |
-
### 3. Data Files
|
| 44 |
-
|
| 45 |
-
**Added to git** (previously gitignored):
|
| 46 |
-
- `data/gene_unknown/unknown_aa_seqs.fasta` - 149 test sequences
|
| 47 |
-
- `data/gene_unknown/unknown_aa_seqs.npy` - Pre-computed embeddings
|
| 48 |
-
- `data/gene_unknown/README.md` - Source documentation
|
| 49 |
-
|
| 50 |
-
**Cleaned from results/**:
|
| 51 |
-
- Removed stale demo outputs (`1A7F_*.csv`, `search_results.csv`, etc.)
|
| 52 |
-
- Kept: `dali_thresholds.csv`, `fdr_thresholds.csv`, `fnr_thresholds.csv`
|
| 53 |
-
|
| 54 |
-
### 4. Notebook Cleanup
|
| 55 |
-
|
| 56 |
-
**Cleaned:**
|
| 57 |
-
- `notebooks/pfam/genes_unknown.ipynb` - Uses relative paths, cleared outputs, added documentation
|
| 58 |
-
|
| 59 |
-
**Archived (originals preserved):**
|
| 60 |
-
- `notebooks/archive/genes_unknown_original.ipynb`
|
| 61 |
-
- `notebooks/archive/analyze_clean_hierarchical_loss_protein_vec_original.ipynb`
|
| 62 |
-
- `notebooks/archive/scope_dali_prefilter_foldseek_original.ipynb`
|
| 63 |
-
|
| 64 |
-
**Not cleaned (left as reference):**
|
| 65 |
-
- CLEAN notebooks - require CLEAN package and external data
|
| 66 |
-
- DALI/SCOPe notebooks - require structural data
|
| 67 |
-
- Other pfam/ec notebooks - less critical for users
|
| 68 |
-
|
| 69 |
-
### 5. Apptainer Container
|
| 70 |
-
|
| 71 |
-
**Fixed:**
|
| 72 |
-
- Added `%setup` section to create mount points before container init
|
| 73 |
-
- Updated base image to PyTorch 2.4.0 (glibc compatibility)
|
| 74 |
-
- Changed `faiss-gpu` to `faiss-cpu` (pip compatibility)
|
| 75 |
-
|
| 76 |
-
**Status:** Build job pending (1012582)
|
| 77 |
-
|
| 78 |
-
### 6. Threshold Computation
|
| 79 |
-
|
| 80 |
-
**FNR Thresholds (exact match):** COMPLETED
|
| 81 |
-
| α | Threshold (λ) |
|
| 82 |
-
|---|---------------|
|
| 83 |
-
| 0.001 | 0.9997904 |
|
| 84 |
-
| 0.01 | 0.9998495 |
|
| 85 |
-
| 0.05 | 0.9998899 |
|
| 86 |
-
| 0.1 | 0.9999076 |
|
| 87 |
-
| 0.15 | 0.9999174 |
|
| 88 |
-
| 0.2 | 0.9999245 |
|
| 89 |
-
|
| 90 |
-
**FNR Thresholds (partial match):** In progress (job 1012547, backup 1012624)
|
| 91 |
-
|
| 92 |
-
**FDR Thresholds:** In progress (16 jobs for 8 alphas × exact/partial)
|
| 93 |
-
|
| 94 |
-
---
|
| 95 |
-
|
| 96 |
-
## Scripts Added
|
| 97 |
-
|
| 98 |
-
| Script | Purpose |
|
| 99 |
-
|--------|---------|
|
| 100 |
-
| `scripts/compute_fdr_table.py` | Compute FDR thresholds |
|
| 101 |
-
| `scripts/compute_fnr_table.py` | Compute FNR thresholds |
|
| 102 |
-
| `scripts/submit_fdr_parallel.sh` | Submit parallel FDR jobs |
|
| 103 |
-
| `scripts/merge_fdr_results.py` | Merge individual alpha results |
|
| 104 |
-
| `scripts/slurm_compute_fnr_partial.sh` | Compute partial-match FNR |
|
| 105 |
-
|
| 106 |
-
---
|
| 107 |
-
|
| 108 |
-
## Commits Made
|
| 109 |
-
|
| 110 |
-
1. `1554371` - fix: use faiss-cpu in Apptainer
|
| 111 |
-
2. `e7c1683` - docs: comprehensive GETTING_STARTED.md update
|
| 112 |
-
3. `b95214f` - refactor: consolidate CLI to single 'cpr search' command
|
| 113 |
-
4. `e940b6e` - fix: use actual computed FNR thresholds in docs
|
| 114 |
-
5. `1d2170e` - docs: add test example using included JCVI Syn3.0 data
|
| 115 |
-
6. `e8a96b0` - data: add JCVI Syn3.0 test sequences with documentation
|
| 116 |
-
7. `49462de` - chore: clean up stale results, add partial FNR script
|
| 117 |
-
|
| 118 |
-
---
|
| 119 |
-
|
| 120 |
-
## Running Jobs
|
| 121 |
-
|
| 122 |
-
| Job ID | Name | Status | Time/Limit |
|
| 123 |
-
|--------|------|--------|------------|
|
| 124 |
-
| 1012547 | fnr-thresholds | RUNNING | 3.5h/4h |
|
| 125 |
-
| 1012624 | fnr-partial (backup) | PENDING | 0/12h |
|
| 126 |
-
| 1012550-1012559 | fdr-exact/partial | RUNNING | ~3.5h/8h |
|
| 127 |
-
| 1012560-1012565 | fdr-exact/partial | PENDING | 0/8h |
|
| 128 |
-
| 1012582 | apptainer-build | PENDING | 0/2h |
|
| 129 |
-
|
| 130 |
-
---
|
| 131 |
-
|
| 132 |
-
## Remaining Work
|
| 133 |
-
|
| 134 |
-
1. **Wait for jobs to complete** - FDR/FNR thresholds, Apptainer build
|
| 135 |
-
2. **Merge FDR results** - Run `python scripts/merge_fdr_results.py`
|
| 136 |
-
3. **Update GETTING_STARTED.md** - Add final computed thresholds
|
| 137 |
-
4. **Test Apptainer** - Verify container works
|
| 138 |
-
5. **Create PR** - From `refactor/cpr-cleanup-and-tests` to `main`
|
| 139 |
-
|
| 140 |
-
---
|
| 141 |
-
|
| 142 |
-
## Verification Results
|
| 143 |
-
|
| 144 |
-
| Claim | Paper | Reproduced | Status |
|
| 145 |
-
|-------|-------|------------|--------|
|
| 146 |
-
| Syn3.0 annotation | 39.6% (59/149) | 38.9-39.6% (58-59/149) | ✓ |
|
| 147 |
-
| FDR threshold (α=0.1) | 0.9999802 | 0.9999802 | ✓ |
|
| 148 |
-
| DALI TPR | 82.8% | 81.8% | ✓ |
|
| 149 |
-
| DALI reduction | 31.5% | 31.5% | ✓ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/QUICKSTART.md
DELETED
|
@@ -1,207 +0,0 @@
|
|
| 1 |
-
# Quick Start Guide
|
| 2 |
-
|
| 3 |
-
This guide shows how to use Conformal Protein Retrieval for common tasks.
|
| 4 |
-
|
| 5 |
-
## Overview
|
| 6 |
-
|
| 7 |
-
CPR provides **statistically rigorous protein search** with two key guarantees:
|
| 8 |
-
|
| 9 |
-
1. **False Discovery Rate (FDR) Control**: Limit the fraction of incorrect matches among your results
|
| 10 |
-
2. **False Negative Rate (FNR) Control**: Limit the fraction of true matches you miss
|
| 11 |
-
|
| 12 |
-
Additionally, CPR provides **calibrated probabilities** for each hit using Venn-Abers prediction.
|
| 13 |
-
|
| 14 |
-
---
|
| 15 |
-
|
| 16 |
-
## Basic Workflow
|
| 17 |
-
|
| 18 |
-
```
|
| 19 |
-
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
| 20 |
-
│ Input │────▶│ Embed │────▶│ Search │────▶│ Results │
|
| 21 |
-
│ FASTA │ │ (Protein- │ │ (FAISS + │ │ + Probs │
|
| 22 |
-
│ │ │ Vec) │ │ Conformal) │ │ │
|
| 23 |
-
└─────────────┘ └─────────────┘ └─────────────┘ └─────────────┘
|
| 24 |
-
```
|
| 25 |
-
|
| 26 |
-
---
|
| 27 |
-
|
| 28 |
-
## Example 1: Search with FDR Control
|
| 29 |
-
|
| 30 |
-
Find protein homologs while controlling the false discovery rate at 10%.
|
| 31 |
-
|
| 32 |
-
### Step 1: Embed your query proteins
|
| 33 |
-
|
| 34 |
-
```bash
|
| 35 |
-
python protein_conformal/embed_protein_vec.py \
|
| 36 |
-
--input_file my_proteins.fasta \
|
| 37 |
-
--output_file my_proteins_embeddings.npy \
|
| 38 |
-
--path_to_protein_vec protein_vec_models
|
| 39 |
-
```
|
| 40 |
-
|
| 41 |
-
### Step 2: Search with FDR control
|
| 42 |
-
|
| 43 |
-
```bash
|
| 44 |
-
python scripts/search.py \
|
| 45 |
-
--query_embedding my_proteins_embeddings.npy \
|
| 46 |
-
--query_fasta my_proteins.fasta \
|
| 47 |
-
--lookup_embedding data/lookup_embeddings.npy \
|
| 48 |
-
--lookup_fasta data/lookup_embeddings_meta_data.tsv \
|
| 49 |
-
--fdr \
|
| 50 |
-
--fdr_lambda 0.99996425 \
|
| 51 |
-
--output results.csv
|
| 52 |
-
```
|
| 53 |
-
|
| 54 |
-
The `--fdr_lambda` value is a pre-computed threshold that ensures FDR ≤ 10% for exact Pfam matches.
|
| 55 |
-
|
| 56 |
-
### Step 3: Get calibrated probabilities
|
| 57 |
-
|
| 58 |
-
```bash
|
| 59 |
-
python scripts/get_probs.py \
|
| 60 |
-
--precomputed \
|
| 61 |
-
--precomputed_path data/pfam_sims_to_probs.csv \
|
| 62 |
-
--input results.csv \
|
| 63 |
-
--output results_with_probs.csv \
|
| 64 |
-
--partial
|
| 65 |
-
```
|
| 66 |
-
|
| 67 |
-
---
|
| 68 |
-
|
| 69 |
-
## Example 2: Search with FNR Control
|
| 70 |
-
|
| 71 |
-
Find protein homologs while ensuring you don't miss more than 10% of true matches.
|
| 72 |
-
|
| 73 |
-
```bash
|
| 74 |
-
python scripts/search.py \
|
| 75 |
-
--query_embedding my_proteins_embeddings.npy \
|
| 76 |
-
--query_fasta my_proteins.fasta \
|
| 77 |
-
--lookup_embedding data/lookup_embeddings.npy \
|
| 78 |
-
--lookup_fasta data/lookup_embeddings_meta_data.tsv \
|
| 79 |
-
--fnr \
|
| 80 |
-
--fnr_lambda 0.99974871 \
|
| 81 |
-
--output results_fnr.csv
|
| 82 |
-
```
|
| 83 |
-
|
| 84 |
-
---
|
| 85 |
-
|
| 86 |
-
## Example 3: Using the Gradio GUI
|
| 87 |
-
|
| 88 |
-
Launch the web interface for interactive exploration:
|
| 89 |
-
|
| 90 |
-
```bash
|
| 91 |
-
python -m protein_conformal.gradio_app --port 7860
|
| 92 |
-
```
|
| 93 |
-
|
| 94 |
-
Then open http://localhost:7860 in your browser.
|
| 95 |
-
|
| 96 |
-
Features:
|
| 97 |
-
- Paste sequences or upload FASTA files
|
| 98 |
-
- Choose FDR or FNR control
|
| 99 |
-
- Visualize results with 3D structures
|
| 100 |
-
- Export results to CSV
|
| 101 |
-
|
| 102 |
-
---
|
| 103 |
-
|
| 104 |
-
## Example 4: Programmatic Use (Python)
|
| 105 |
-
|
| 106 |
-
```python
|
| 107 |
-
import numpy as np
|
| 108 |
-
from protein_conformal.util import (
|
| 109 |
-
load_database,
|
| 110 |
-
query,
|
| 111 |
-
read_fasta,
|
| 112 |
-
get_thresh_FDR,
|
| 113 |
-
risk,
|
| 114 |
-
simplifed_venn_abers_prediction
|
| 115 |
-
)
|
| 116 |
-
|
| 117 |
-
# Load your query embeddings
|
| 118 |
-
query_embeddings = np.load('my_proteins_embeddings.npy')
|
| 119 |
-
|
| 120 |
-
# Load the lookup database
|
| 121 |
-
lookup_embeddings = np.load('data/lookup_embeddings.npy')
|
| 122 |
-
index = load_database(lookup_embeddings)
|
| 123 |
-
|
| 124 |
-
# Search (k nearest neighbors)
|
| 125 |
-
D, I = query(index, query_embeddings, k=100)
|
| 126 |
-
|
| 127 |
-
# D contains similarity scores
|
| 128 |
-
# I contains indices into the lookup database
|
| 129 |
-
|
| 130 |
-
# Filter by FDR threshold
|
| 131 |
-
fdr_threshold = 0.99996425
|
| 132 |
-
hits = D >= fdr_threshold
|
| 133 |
-
|
| 134 |
-
# Get probabilities for a specific hit
|
| 135 |
-
# (requires calibration data)
|
| 136 |
-
cal_data = np.load('data/pfam_new_proteins.npy', allow_pickle=True)
|
| 137 |
-
# ... extract X_cal, Y_cal from cal_data ...
|
| 138 |
-
p0, p1 = simplifed_venn_abers_prediction(X_cal, Y_cal, similarity_score)
|
| 139 |
-
probability = (p0 + p1) / 2
|
| 140 |
-
```
|
| 141 |
-
|
| 142 |
-
---
|
| 143 |
-
|
| 144 |
-
## Pre-computed Thresholds
|
| 145 |
-
|
| 146 |
-
For convenience, here are pre-computed thresholds for common use cases:
|
| 147 |
-
|
| 148 |
-
### Pfam (Exact Match)
|
| 149 |
-
|
| 150 |
-
| Alpha (Error Rate) | FDR Lambda | FNR Lambda |
|
| 151 |
-
|--------------------|------------|------------|
|
| 152 |
-
| 0.01 (1%) | TBD | TBD |
|
| 153 |
-
| 0.05 (5%) | TBD | TBD |
|
| 154 |
-
| 0.10 (10%) | 0.99996425 | 0.99974871 |
|
| 155 |
-
| 0.20 (20%) | TBD | TBD |
|
| 156 |
-
|
| 157 |
-
### Pfam (Partial Match)
|
| 158 |
-
|
| 159 |
-
Partial matches tolerate hits to the same clan/superfamily even if the exact Pfam domain differs.
|
| 160 |
-
|
| 161 |
-
| Alpha (Error Rate) | FDR Lambda | FNR Lambda |
|
| 162 |
-
|--------------------|------------|------------|
|
| 163 |
-
| 0.10 (10%) | TBD | TBD |
|
| 164 |
-
|
| 165 |
-
---
|
| 166 |
-
|
| 167 |
-
## Understanding the Output
|
| 168 |
-
|
| 169 |
-
### Search Results CSV
|
| 170 |
-
|
| 171 |
-
| Column | Description |
|
| 172 |
-
|--------|-------------|
|
| 173 |
-
| `query_seq` | Query protein sequence |
|
| 174 |
-
| `query_meta` | Query metadata from FASTA header |
|
| 175 |
-
| `lookup_seq` | Matched protein sequence |
|
| 176 |
-
| `D_score` | Cosine similarity score (0-1) |
|
| 177 |
-
| `lookup_entry` | UniProt entry ID |
|
| 178 |
-
| `lookup_pfam` | Pfam domain annotations |
|
| 179 |
-
| `lookup_protein_names` | Protein name |
|
| 180 |
-
|
| 181 |
-
### With Probabilities
|
| 182 |
-
|
| 183 |
-
| Column | Description |
|
| 184 |
-
|--------|-------------|
|
| 185 |
-
| `prob_exact_p0` | Lower bound probability of exact match |
|
| 186 |
-
| `prob_exact_p1` | Upper bound probability of exact match |
|
| 187 |
-
| `prob_partial_p0` | Lower bound probability of partial match |
|
| 188 |
-
| `prob_partial_p1` | Upper bound probability of partial match |
|
| 189 |
-
|
| 190 |
-
The **calibrated probability** of a match is typically taken as the average: `(p0 + p1) / 2`
|
| 191 |
-
|
| 192 |
-
---
|
| 193 |
-
|
| 194 |
-
## Tips
|
| 195 |
-
|
| 196 |
-
1. **Start with FDR control** if you want high-confidence hits (fewer false positives)
|
| 197 |
-
2. **Use FNR control** if you want comprehensive coverage (don't miss true hits)
|
| 198 |
-
3. **Check the similarity distribution** in your results to calibrate expectations
|
| 199 |
-
4. **Use partial match probabilities** when exact Pfam annotation isn't critical
|
| 200 |
-
|
| 201 |
-
---
|
| 202 |
-
|
| 203 |
-
## Next Steps
|
| 204 |
-
|
| 205 |
-
- See [notebooks/pfam/](../notebooks/pfam/) for detailed Pfam analysis
|
| 206 |
-
- See [notebooks/scope/](../notebooks/scope/) for structural classification examples
|
| 207 |
-
- See [notebooks/ec/](../notebooks/ec/) for enzyme classification examples
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|