ronboger Claude Opus 4.5 commited on
Commit
e8e66c2
·
1 Parent(s): 6754836

fix: CLI tests and FAISS edge case handling

Browse files

- Fix mock calibration data format (use dicts with S_i/exact/partial keys)
- Filter out FAISS placeholder results (idx=-1) when k > database size
- Handle empty CSV when threshold filters all results
- Fix SLURM partition name for local cluster

All 24 CLI tests now pass.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

protein_conformal/cli.py CHANGED
@@ -246,6 +246,9 @@ def cmd_search(args):
246
  for j in range(args.k):
247
  sim = D[i, j]
248
  idx = I[i, j]
 
 
 
249
  if args.threshold and sim < args.threshold:
250
  continue
251
  row = {
 
246
  for j in range(args.k):
247
  sim = D[i, j]
248
  idx = I[i, j]
249
+ # Skip placeholder results (FAISS returns -1 for non-existent neighbors)
250
+ if idx < 0:
251
+ continue
252
  if args.threshold and sim < args.threshold:
253
  continue
254
  row = {
scripts/slurm_test_clean_embed.sh CHANGED
@@ -1,8 +1,6 @@
1
  #!/bin/bash
2
  #SBATCH --job-name=test_clean_embed
3
- #SBATCH --partition=savio4_gpu
4
- #SBATCH --account=co_doudna
5
- #SBATCH --qos=doudna_gpu4_normal
6
  #SBATCH --nodes=1
7
  #SBATCH --ntasks=1
8
  #SBATCH --cpus-per-task=4
 
1
  #!/bin/bash
2
  #SBATCH --job-name=test_clean_embed
3
+ #SBATCH --partition=gpu
 
 
4
  #SBATCH --nodes=1
5
  #SBATCH --ntasks=1
6
  #SBATCH --cpus-per-task=4
tests/test_cli.py CHANGED
@@ -205,12 +205,18 @@ def test_search_with_threshold(tmp_path):
205
  assert result.returncode == 0
206
  assert output_file.exists()
207
 
208
- df = pd.read_csv(output_file)
209
- # With high threshold, we should have fewer results
210
- assert len(df) <= 3 * 10 # At most 3 queries * 10 neighbors
211
- # All results should be above threshold
212
- if len(df) > 0:
213
- assert df['similarity'].min() >= 0.9
 
 
 
 
 
 
214
 
215
 
216
  def test_search_with_metadata(tmp_path):
@@ -263,15 +269,18 @@ def test_prob_with_mock_data(tmp_path):
263
  """Test prob command with mock calibration data and scores."""
264
  np.random.seed(42)
265
 
266
- # Create mock calibration data (structured like pfam_new_proteins.npy)
267
- # Each sample should have similarity scores and labels
268
  n_calib = 50
269
  cal_data = []
270
  for i in range(n_calib):
271
- # Each sample: (query_emb, lookup_emb, sims, labels, metadata...)
272
  sims = np.random.uniform(0.998, 0.9999, size=10).astype(np.float32)
273
- labels = (np.random.random(10) < 0.2).astype(np.int32)
274
- cal_data.append((None, None, sims, labels, 'mock_protein'))
 
 
 
 
 
275
 
276
  cal_file = tmp_path / "calibration.npy"
277
  np.save(cal_file, np.array(cal_data, dtype=object))
@@ -314,13 +323,18 @@ def test_prob_with_csv_input(tmp_path):
314
  """Test prob command with CSV input (e.g., from search results)."""
315
  np.random.seed(42)
316
 
317
- # Create mock calibration data
318
  n_calib = 30
319
  cal_data = []
320
  for i in range(n_calib):
321
  sims = np.random.uniform(0.998, 0.9999, size=5).astype(np.float32)
322
- labels = (np.random.random(5) < 0.2).astype(np.int32)
323
- cal_data.append((None, None, sims, labels, 'mock'))
 
 
 
 
 
324
 
325
  cal_file = tmp_path / "calibration.npy"
326
  np.save(cal_file, np.array(cal_data, dtype=object))
@@ -366,14 +380,19 @@ def test_calibrate_with_mock_data(tmp_path):
366
  """Test calibrate command with mock calibration data."""
367
  np.random.seed(42)
368
 
369
- # Create mock calibration data with similarity/label pairs
370
  n_samples = 100
371
  cal_data = []
372
  for i in range(n_samples):
373
  sims = np.random.uniform(0.997, 0.9999, size=10).astype(np.float32)
374
  # Create labels: higher similarity -> higher chance of being positive
375
- labels = (sims > 0.9995).astype(np.int32)
376
- cal_data.append((None, None, sims, labels, f'protein_{i}'))
 
 
 
 
 
377
 
378
  cal_file = tmp_path / "calibration.npy"
379
  np.save(cal_file, np.array(cal_data, dtype=object))
 
205
  assert result.returncode == 0
206
  assert output_file.exists()
207
 
208
+ # With high threshold on random embeddings, file may be empty or have few results
209
+ # Random unit vectors have expected cosine similarity ~0, so 0.9 threshold filters most
210
+ try:
211
+ df = pd.read_csv(output_file)
212
+ # With high threshold, we should have fewer results
213
+ assert len(df) <= 3 * 10 # At most 3 queries * 10 neighbors
214
+ # All results should be above threshold
215
+ if len(df) > 0:
216
+ assert df['similarity'].min() >= 0.9
217
+ except pd.errors.EmptyDataError:
218
+ # Empty file is valid - no results passed threshold
219
+ pass
220
 
221
 
222
  def test_search_with_metadata(tmp_path):
 
269
  """Test prob command with mock calibration data and scores."""
270
  np.random.seed(42)
271
 
272
+ # Create mock calibration data (format: array of dicts with S_i, exact, partial)
 
273
  n_calib = 50
274
  cal_data = []
275
  for i in range(n_calib):
 
276
  sims = np.random.uniform(0.998, 0.9999, size=10).astype(np.float32)
277
+ exact_labels = (np.random.random(10) < 0.2).astype(bool)
278
+ partial_labels = exact_labels | (np.random.random(10) < 0.1)
279
+ cal_data.append({
280
+ "S_i": sims,
281
+ "exact": exact_labels,
282
+ "partial": partial_labels,
283
+ })
284
 
285
  cal_file = tmp_path / "calibration.npy"
286
  np.save(cal_file, np.array(cal_data, dtype=object))
 
323
  """Test prob command with CSV input (e.g., from search results)."""
324
  np.random.seed(42)
325
 
326
+ # Create mock calibration data (format: array of dicts with S_i, exact, partial)
327
  n_calib = 30
328
  cal_data = []
329
  for i in range(n_calib):
330
  sims = np.random.uniform(0.998, 0.9999, size=5).astype(np.float32)
331
+ exact_labels = (np.random.random(5) < 0.2).astype(bool)
332
+ partial_labels = exact_labels | (np.random.random(5) < 0.1)
333
+ cal_data.append({
334
+ "S_i": sims,
335
+ "exact": exact_labels,
336
+ "partial": partial_labels,
337
+ })
338
 
339
  cal_file = tmp_path / "calibration.npy"
340
  np.save(cal_file, np.array(cal_data, dtype=object))
 
380
  """Test calibrate command with mock calibration data."""
381
  np.random.seed(42)
382
 
383
+ # Create mock calibration data (format: array of dicts with S_i, exact, partial)
384
  n_samples = 100
385
  cal_data = []
386
  for i in range(n_samples):
387
  sims = np.random.uniform(0.997, 0.9999, size=10).astype(np.float32)
388
  # Create labels: higher similarity -> higher chance of being positive
389
+ exact_labels = (sims > 0.9995).astype(bool)
390
+ partial_labels = (sims > 0.999).astype(bool)
391
+ cal_data.append({
392
+ "S_i": sims,
393
+ "exact": exact_labels,
394
+ "partial": partial_labels,
395
+ })
396
 
397
  cal_file = tmp_path / "calibration.npy"
398
  np.save(cal_file, np.array(cal_data, dtype=object))