Files
digital-drosophila/models/male-cns-lif/olfactory-conditioning-lab-v1/test_biological_contract.py
T
2026-09-30 17:09:40 +08:00

154 lines
7.5 KiB
Python

import unittest
import numpy as np
import pandas as pd
import biological_contract as b
def fixture():
types = ["KC-a", "KC-b", "PAM15", "PPL103", "MBON01", "MBON12", "other-DAN"]
classes = ["Kenyon_Cell", "Kenyon_Cell", "DAN", "DAN", "MBON", "MBON", "DAN"]
instances = ["KC-a_L", "KC-b_R", "PAM15(y5B'2a)_L", "PPL103(y2a'1)_R",
"MBON01(y5B'2a)_L", "MBON12(y2a'1)_R", "other-DAN"]
meta = pd.DataFrame(dict(neuron_idx=np.arange(7, dtype=np.int32),
body_id=np.arange(7, dtype=np.uint64) + np.uint64(2**63),
annotation_type=pd.Series(types, dtype=object), annotation_class=pd.Series(classes, dtype=object),
annotation_instance=pd.Series(instances, dtype=object)))
dan = meta.iloc[[2, 3, 6]].drop(columns="annotation_class").copy()
mbon = meta.iloc[[4, 5]].drop(columns="annotation_class").copy()
dan["mb_compartment_verbatim"] = ["y5B'2a", "y2a'1", None]
dan["mb_compartment_mapping_status"] = ["explicit_single_parenthesized_token"] * 2 + ["no_parenthesized_token"]
mbon["mb_compartment_verbatim"] = ["y5B'2a", "y2a'1"]
mbon["mb_compartment_mapping_status"] = "explicit_single_parenthesized_token"
src = np.array([0, 0, 1, 1, 2, 3], np.int32)
dst = np.array([4, 5, 4, 5, 4, 5], np.int32)
raw = np.array([4, 8, 12, 16, 20, 24], np.int64)
def table(ids):
rows = []
for i in ids:
row = dict(edge_id=i, raw_synapse_count=int(raw[i]))
for label, node in (("source", src[i]), ("target", dst[i])):
for key in ("neuron_idx", "body_id", "annotation_type", "annotation_instance"):
row[label + "_" + key] = meta.iloc[node][key]
row["target_compartment_verbatim"] = "y5B'2a" if dst[i] == 4 else "y2a'1"
rows.append(row)
out = pd.DataFrame(rows)
for key in ("source_neuron_idx", "target_neuron_idx"):
out[key] = out[key].astype(np.int32)
for key in ("source_body_id", "target_body_id"):
out[key] = out[key].astype(np.uint64)
# Match the observed frozen parquet object dtype; local pandas 3 may
# infer its new string extension dtype from constructor literals.
for prefix in ("source", "target"):
for suffix in ("annotation_type", "annotation_instance"):
out[prefix + "_" + suffix] = out[prefix + "_" + suffix].astype(object)
out["edge_id"] = out.edge_id.astype(np.int64)
out["raw_synapse_count"] = out.raw_synapse_count.astype(np.int64)
return out
candidates = table(range(4))
candidates["plasticity_polarity"] = np.int64(-1)
confirmed = table([4, 5])
confirmed["source_compartment_verbatim"] = confirmed.target_compartment_verbatim
confirmed["exact_compartment_match"] = True
return meta, dan, mbon, confirmed, candidates, src, dst, raw
class Biology(unittest.TestCase):
def test_complete_graph_binds_every_edge_array_length(self):
meta, *_, src, dst, raw = fixture()
graph = dict(nodes=np.arange(7, dtype=np.int32), edge_id=np.arange(6, dtype=np.int64),
src=src, dst=dst, signed_raw=raw)
b._complete_graph_identity(meta, graph, n=7, e=6)
for names in (("src",), ("dst",), ("signed_raw",), ("src", "dst", "signed_raw")):
changed = dict(graph)
for name in names:
changed[name] = changed[name][:-1]
with self.subTest(names=names), self.assertRaisesRegex(ValueError, "endpoint/weight lengths"):
b._complete_graph_identity(meta, changed, n=7, e=6)
for name in ("nodes", "edge_id"):
changed = dict(graph)
changed[name] = changed[name].reshape(1, -1)
with self.subTest(name=name), self.assertRaises(ValueError):
b._complete_graph_identity(meta, changed, n=7, e=6)
def test_exact_id_compartment_and_real_edge_contract(self):
values = fixture()
populations, audit = b.audit_interface_tables(*values)
self.assertEqual(populations["PAM15"].tolist(), [2])
self.assertEqual(populations["MBON12"].tolist(), [5])
self.assertEqual(audit["whole_DAN_mapping_unknown"], 1)
self.assertFalse(audit["global_unknowns_filled"])
self.assertEqual(audit["all_candidate_edges_retained"], 4)
self.assertEqual(audit["interfaces"]["PAM15"]["candidate_contacts"], 16)
self.assertEqual(audit["interfaces"]["PPL103"]["confirmed_DAN_MBON_contacts"], 24)
self.assertGreater(audit["interfaces"]["PAM15"]["DAN_members"][0]["body_id"], 2**63)
self.assertTrue(pd.isna(values[1].iloc[-1].mb_compartment_verbatim))
def test_unknown_selected_mapping_is_not_filled_from_type(self):
for field, new_value in (("mb_compartment_verbatim", None),
("mb_compartment_mapping_status", "unknown")):
values = fixture()
values[1].loc[2, field] = new_value
with self.assertRaisesRegex(ValueError, "Unknown or mismatched"):
b.audit_interface_tables(*values)
def test_changed_body_id_or_type_or_instance_rejected(self):
for field, new_value in (("body_id", np.uint64(5)), ("annotation_type", "PAM_OTHER"),
("annotation_instance", "inferred")):
values = fixture()
values[1].loc[2, field] = new_value
with self.assertRaisesRegex(ValueError, "Identity field mismatch"):
b.audit_interface_tables(*values)
def test_unmapped_dan_cannot_be_silently_removed(self):
values = list(fixture())
values[1] = values[1].iloc[:2].copy()
with self.assertRaisesRegex(ValueError, "complete exact-annotation"):
b.audit_interface_tables(*values)
def test_missing_actual_edge_fails_not_guessed(self):
values = list(fixture())
values[3] = values[3].iloc[:1].copy()
with self.assertRaisesRegex(ValueError, "unknown or mismatched actual target"):
b.audit_interface_tables(*values)
def test_edge_identity_and_contacts_and_polarity(self):
changes = ((3, "source_body_id", np.uint64(8)),
(3, "target_annotation_type", "MBON_OTHER"),
(4, "raw_synapse_count", np.int64(1)),
(4, "plasticity_polarity", np.int64(1)))
for which, field, value in changes:
values = fixture()
values[which].loc[values[which].index[0], field] = value
with self.subTest(field=field), self.assertRaises(ValueError):
b.audit_interface_tables(*values)
def test_false_or_nonboolean_confirmed_flag_rejected(self):
for value in (False, "True"):
values = fixture()
values[3]["exact_compartment_match"] = value
with self.assertRaisesRegex(ValueError, "unknown compartment"):
b.audit_interface_tables(*values)
def test_graph_endpoint_and_sign_change_rejected(self):
for where in (5, 6, 7):
values = fixture()
values[where][0] = -1 if where == 7 else 6
with self.assertRaises(ValueError):
b.audit_interface_tables(*values)
def test_schema_and_duplicate_identity_rejected(self):
values = list(fixture())
values[0] = values[0].drop(columns="annotation_class")
with self.assertRaises(ValueError):
b.audit_interface_tables(*values)
values = fixture()
values[0].loc[1, "body_id"] = values[0].loc[0, "body_id"]
with self.assertRaises(ValueError):
b.audit_interface_tables(*values)
if __name__ == "__main__":
unittest.main()