106 lines
5.3 KiB
Python
106 lines
5.3 KiB
Python
"""Synthetic CPU-only classification tests; no scientific files are read."""
|
|
import unittest
|
|
|
|
import numpy as np
|
|
import pandas as pd
|
|
|
|
from populations import N_NEURONS, GROUPS, classify
|
|
|
|
|
|
def synthetic_metadata():
|
|
return pd.DataFrame({"neuron_idx": np.arange(N_NEURONS, dtype=np.int32),
|
|
"body_id": np.arange(1, N_NEURONS + 1, dtype=np.uint64),
|
|
"annotation_class": np.full(N_NEURONS, "ALLN", dtype=object),
|
|
"annotation_type": np.full(N_NEURONS, "unresolved_function", dtype=object),
|
|
"neurotransmitter": np.full(N_NEURONS, "acetylcholine", dtype=object),
|
|
"nt_ground_truth": np.full(N_NEURONS, None, dtype=object)})
|
|
|
|
|
|
class PopulationTests(unittest.TestCase):
|
|
def test_exact_groups_are_disjoint_and_full(self):
|
|
meta = synthetic_metadata()
|
|
classes = ["Kenyon_Cell", None, "ALPN", "MBON", "DAN", None, "olfactory", None]
|
|
types = ["KCg", "APL", "VA6_adPN", "MBON01", "PAM15", None, "ORN", None]
|
|
meta.loc[:7, "annotation_class"] = classes
|
|
meta.loc[:7, "annotation_type"] = types
|
|
meta.loc[5, "neurotransmitter"] = " DOPAMINE "
|
|
before = meta.copy(deep=True)
|
|
labels, report = classify(meta)
|
|
self.assertEqual(labels[:8].tolist(), list(GROUPS))
|
|
self.assertEqual(labels.dtype.kind, "U")
|
|
self.assertEqual(sum(report["groups"].values()), N_NEURONS)
|
|
self.assertTrue(report["all_neurons_assigned_once"])
|
|
self.assertTrue(report["pass_all"])
|
|
pd.testing.assert_frame_equal(meta, before)
|
|
|
|
def test_dopamine_does_not_prove_DAN_and_DPM_is_other(self):
|
|
meta = synthetic_metadata()
|
|
meta.loc[:3, "annotation_class"] = [None, None, "DAN", "ALPN"]
|
|
meta.loc[:3, "annotation_type"] = ["DPM", "PAM-not-proof", "PAM15", "VA6_adPN"]
|
|
meta.loc[:3, "neurotransmitter"] = ["dopamine", "dopamine", "gaba", "dopamine"]
|
|
labels, report = classify(meta)
|
|
self.assertEqual(labels[:4].tolist(), ["other", "dopamine_annotation_only", "anatomical_DAN", "ALPN"])
|
|
self.assertEqual(report["DPM_neuron_idx"], [0])
|
|
self.assertEqual(report["flags"]["annotation_DAN_not_metadata_dopamine"], 1)
|
|
self.assertEqual(report["uncertain_DAN_prefix_neuron_idx"], [1])
|
|
self.assertFalse(report["reward_or_valence_inferred"])
|
|
|
|
def test_DPM_and_exact_identity_conflicts_are_unclassified(self):
|
|
meta = synthetic_metadata()
|
|
meta.loc[:2, "annotation_class"] = ["DAN", "Kenyon_Cell", "MBON"]
|
|
meta.loc[:2, "annotation_type"] = ["DPM", "APL", "DPM"]
|
|
labels, report = classify(meta)
|
|
self.assertEqual(labels[:3].tolist(), ["unclassified"] * 3)
|
|
self.assertEqual(report["identity_conflict_neuron_idx"], [0, 1, 2])
|
|
self.assertFalse(report["pass_all"])
|
|
self.assertTrue(report["all_neurons_assigned_once"])
|
|
|
|
def test_prefix_and_missing_evidence_are_not_guessed(self):
|
|
meta = synthetic_metadata()
|
|
meta.loc[:6, "annotation_class"] = [None, "", "unknown", None, None, None, "olfactory"]
|
|
meta.loc[:6, "annotation_type"] = ["PAM15", "PPL1-01", "DAN-like", None, "APL", "DPM", "ALPN-like"]
|
|
labels, report = classify(meta)
|
|
self.assertEqual(labels[:7].tolist(), ["unclassified"] * 4 + ["APL", "other", "other"])
|
|
self.assertEqual(report["uncertain_DAN_prefix_neuron_idx"], [0, 1, 2])
|
|
self.assertEqual(report["groups"]["ALPN"], 0)
|
|
|
|
def test_case_sensitive_anatomical_values_not_normalized_into_identity(self):
|
|
meta = synthetic_metadata()
|
|
meta.loc[:2, "annotation_class"] = ["dan", "ALPN ", "kenyon_cell"]
|
|
meta.loc[:2, "annotation_type"] = ["type", "type", "type"]
|
|
labels, _ = classify(meta)
|
|
self.assertEqual(labels[:3].tolist(), ["other"] * 3)
|
|
|
|
def test_missing_schema_and_numeric_annotations_fail_closed(self):
|
|
meta = synthetic_metadata()
|
|
with self.assertRaises(ValueError):
|
|
classify(meta.drop(columns="nt_ground_truth"))
|
|
meta["annotation_type"] = meta["annotation_type"].astype(object)
|
|
meta.loc[0, "annotation_type"] = 123
|
|
with self.assertRaises(ValueError):
|
|
classify(meta)
|
|
|
|
def test_index_dtype_order_and_duplicate_body_fail_closed(self):
|
|
for failure in ("short", "reordered", "index_dtype", "duplicate_body", "body_dtype"):
|
|
meta = synthetic_metadata()
|
|
if failure == "short": meta = meta.iloc[:-1]
|
|
if failure == "reordered": meta = meta.iloc[::-1]
|
|
if failure == "index_dtype": meta["neuron_idx"] = meta.neuron_idx.astype(np.int64)
|
|
if failure == "duplicate_body": meta.loc[1, "body_id"] = meta.loc[0, "body_id"]
|
|
if failure == "body_dtype": meta["body_id"] = meta.body_id.astype(np.int64)
|
|
with self.subTest(failure=failure), self.assertRaises(ValueError):
|
|
classify(meta)
|
|
|
|
def test_raw_ground_truth_is_only_an_evidence_flag(self):
|
|
meta = synthetic_metadata()
|
|
meta.loc[0, "nt_ground_truth"] = "dopamine"
|
|
labels, report = classify(meta)
|
|
self.assertEqual(labels[0], "other")
|
|
self.assertEqual(report["flags"]["has_raw_ground_truth"], 1)
|
|
self.assertEqual(report["groups"]["anatomical_DAN"], 0)
|
|
self.assertFalse(report["provenance"]["file_hash_verified_by_this_pure_function"])
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|