Upload ocm26400/test_cognition.py with huggingface_hub
Browse files- ocm26400/test_cognition.py +121 -0
ocm26400/test_cognition.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tests capacités cognitives (OCM-26400) — audit M1/M4/M5/M10."""
|
| 2 |
+
import pytest
|
| 3 |
+
from ocm26400.cognition import (
|
| 4 |
+
TheoryOfMind, MoralAction, evaluate_morality, SpatialReasoner,
|
| 5 |
+
structure_mapping, atom_solar_system_analogy,
|
| 6 |
+
)
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
# ---- M1 Theory of Mind ----
|
| 10 |
+
|
| 11 |
+
def test_sally_anne_false_belief():
|
| 12 |
+
"""LE test : Sally cherche là où elle CROIT (basket), pas la réalité (box)."""
|
| 13 |
+
tom = TheoryOfMind()
|
| 14 |
+
r = tom.sally_anne(sally_sees="basket", anne_moves_to="box")
|
| 15 |
+
assert r["sally_searches_at"] == "basket" # false belief
|
| 16 |
+
assert r["reality"] == "box"
|
| 17 |
+
assert r["false_belief_correct"] is True
|
| 18 |
+
assert r["verdict"] == "TOM_CORRECT"
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def test_belief_tracking_two_agents():
|
| 22 |
+
tom = TheoryOfMind()
|
| 23 |
+
tom.set_reality("key", "drawer")
|
| 24 |
+
tom.observe("alice", "key", "drawer")
|
| 25 |
+
tom.observe("bob", "key", "drawer")
|
| 26 |
+
tom.move_unobserved("key", "shelf", observer_absent="bob")
|
| 27 |
+
# alice a vu le déplacement ? non plus (on l'a pas ré-observée)
|
| 28 |
+
assert tom.predict_search("bob", "key") == "drawer" # bob croit drawer (false belief)
|
| 29 |
+
|
| 30 |
+
|
| 31 |
+
def test_predict_search_abstains_on_no_belief():
|
| 32 |
+
tom = TheoryOfMind()
|
| 33 |
+
assert tom.predict_search("ghost", "x") is None
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
# ---- M4 Moral ----
|
| 37 |
+
|
| 38 |
+
def test_moral_good_action():
|
| 39 |
+
act = MoralAction("sauver une vie", impacts={"non_maleficence": 0.9,
|
| 40 |
+
"benevolence": 0.8})
|
| 41 |
+
m = evaluate_morality(act)
|
| 42 |
+
assert m["score"] > 1.0
|
| 43 |
+
assert "BON" in m["verdict"]
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def test_moral_bad_action():
|
| 47 |
+
act = MoralAction("blesser volontairement", impacts={"non_maleficence": -0.9,
|
| 48 |
+
"benevolence": -0.7})
|
| 49 |
+
m = evaluate_morality(act)
|
| 50 |
+
assert m["score"] < -1.0
|
| 51 |
+
assert "RÉPRÉHENSIBLE" in m["verdict"]
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def test_moral_dilemma_steal_to_save():
|
| 55 |
+
"""Le vol pour sauver une vie : positif globalement (dilemme éthique)."""
|
| 56 |
+
act = MoralAction("vol médicament pour sauver",
|
| 57 |
+
impacts={"non_maleficence": 0.9, "benevolence": 0.8,
|
| 58 |
+
"justice": -0.3, "honesty": -0.2})
|
| 59 |
+
m = evaluate_morality(act)
|
| 60 |
+
assert m["score"] > 0
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def test_moral_reasoning_lists_principles():
|
| 64 |
+
act = MoralAction("x", impacts={"autonomy": 0.5, "honesty": -0.4})
|
| 65 |
+
m = evaluate_morality(act)
|
| 66 |
+
assert len(m["reasoning"]) == 2
|
| 67 |
+
assert all("contribution" in r for r in m["reasoning"])
|
| 68 |
+
|
| 69 |
+
|
| 70 |
+
# ---- M5 Spatial ----
|
| 71 |
+
|
| 72 |
+
def test_spatial_distance():
|
| 73 |
+
sr = SpatialReasoner()
|
| 74 |
+
sr.place("A", 0, 0); sr.place("B", 3, 4)
|
| 75 |
+
assert sr.distance("A", "B") == 5.0
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def test_spatial_relation_geometry():
|
| 79 |
+
"""C à (1,0) relativement à A(0,0) face north = à droite (vraie géométrie)."""
|
| 80 |
+
sr = SpatialReasoner()
|
| 81 |
+
sr.place("A", 0, 0); sr.place("C", 1, 0)
|
| 82 |
+
assert "droite" in sr.relation("C", "A", "north")
|
| 83 |
+
# face east : C serait devant
|
| 84 |
+
sr.place("D", 1, 0)
|
| 85 |
+
assert "devant" in sr.relation("D", "A", "east")
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def test_spatial_nearest():
|
| 89 |
+
sr = SpatialReasoner()
|
| 90 |
+
sr.place("A", 0, 0); sr.place("B", 10, 10); sr.place("C", 1, 1)
|
| 91 |
+
assert sr.nearest("A") == "C"
|
| 92 |
+
|
| 93 |
+
|
| 94 |
+
def test_spatial_move():
|
| 95 |
+
sr = SpatialReasoner()
|
| 96 |
+
sr.place("A", 0, 0)
|
| 97 |
+
sr.move("A", 2, 0)
|
| 98 |
+
assert sr.distance("A", "A") == 0 or sr.objects["A"].x == 2
|
| 99 |
+
|
| 100 |
+
|
| 101 |
+
# ---- M10 Analogie ----
|
| 102 |
+
|
| 103 |
+
def test_structure_mapping_transfers_relations():
|
| 104 |
+
source = {"orbits": {"type": "relation", "args": ["planet", "sun"]}}
|
| 105 |
+
rel_map = {"planet": "electron", "sun": "nucleus", "orbits": "orbits"}
|
| 106 |
+
out = structure_mapping(source, source, rel_map)
|
| 107 |
+
assert out["valid"] is True
|
| 108 |
+
assert any("electron" in inf["target_inference"] for inf in out["inferences"])
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def test_structure_mapping_rejects_non_injective():
|
| 112 |
+
source = {"a": {"type": "relation", "args": []}}
|
| 113 |
+
rel_map = {"a": "x", "b": "x"} # non 1-1
|
| 114 |
+
out = structure_mapping(source, source, rel_map)
|
| 115 |
+
assert out["valid"] is False
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def test_atom_solar_analogy():
|
| 119 |
+
out = atom_solar_system_analogy()
|
| 120 |
+
assert out["valid"] is True
|
| 121 |
+
assert out["n_inferences"] >= 2
|