""" Tests for Phase 5: Spelke's Core Knowledge Systems Validates that innate priors produce biologically correct behavior: - Object permanence tracking - Physics predictions (gravity, bounce, support) - Numerosity discrimination (Weber ratio) - Agent detection and social evaluation Author: Algorembrant, Rembrant Oyangoren Albeos (2026) """ import numpy as np from hippocampaif.core_knowledge.object_system import ObjectSystem from hippocampaif.core_knowledge.agent_system import AgentSystem from hippocampaif.core_knowledge.number_system import NumberSystem from hippocampaif.core_knowledge.geometry_system import GeometrySystem from hippocampaif.core_knowledge.social_system import SocialSystem from hippocampaif.core_knowledge.physics_system import PhysicsSystem, PhysicsState def test_object_permanence(): """Objects persist when occluded — they don't vanish.""" obj_sys = ObjectSystem(max_objects=10, max_occlusion_frames=30) # Register an object obj_id = obj_sys.register_object(np.array([5.0, 5.0]), size=1.0) # Update with the object visible obj_sys.update([{'position': np.array([5.0, 5.0]), 'size': 1.0}]) assert obj_sys.num_objects == 1 # Object disappears (occluded) obj_sys.update([]) # No detections # Object should STILL be tracked (permanence!) assert obj_sys.num_objects == 1, "Object permanence violated — object was deleted!" # Predicted position should exist pred = obj_sys.predict_occluded(obj_id) assert pred is not None, "System lost track of occluded object" print(" PASS Object Permanence (objects persist when occluded)") def test_object_continuity_violation(): """Objects cannot teleport — continuity violations generate surprise.""" obj_sys = ObjectSystem() # Track a moving object obj_sys.register_object(np.array([0.0, 0.0])) obj_sys.update([{'position': np.array([1.0, 0.0])}]) obj_sys.update([{'position': np.array([2.0, 0.0])}]) # Teleport the object far away (violation!) violations = obj_sys.update([{'position': np.array([100.0, 0.0])}]) continuity_violations = [v for v in violations if v['type'] == 'continuity_violation'] assert len(continuity_violations) > 0, "System failed to detect teleportation!" print(" PASS Object Continuity (teleportation detected)") def test_physics_gravity(): """Unsupported objects fall downward.""" phys = PhysicsSystem(gravity=9.8, friction=0.0, dt=0.1) # An object at rest in the air state = PhysicsState( position=np.array([5.0, 0.0]), velocity=np.array([0.0, 0.0]), mass=1.0 ) trajectory = phys.predict_trajectory(state, steps=20) # Y should increase (falling down in y-down coords) assert trajectory[-1][1] > trajectory[0][1], "Object did not fall under gravity!" print(" PASS Physics Gravity (objects fall downward)") def test_physics_bounce(): """Objects bounce off walls (elasticity prior).""" phys = PhysicsSystem(gravity=0.0, friction=0.0, dt=0.1) # Ball moving right toward a wall state = PhysicsState( position=np.array([8.0, 5.0]), velocity=np.array([5.0, 0.0]), elasticity=1.0, radius=0.5 ) bounds = (np.array([0.0, 0.0]), np.array([10.0, 10.0])) trajectory = phys.predict_trajectory(state, steps=20, bounds=bounds) # Ball should have bounced back (negative x velocity at some point) x_positions = [t[0] for t in trajectory] went_right = any(x > 8.0 for x in x_positions) came_back = any(x < 8.0 for x in x_positions[5:]) assert went_right or came_back, "Ball did not bounce off wall!" print(" PASS Physics Bounce (elastic collision with boundary)") def test_physics_support(): """Unsupported objects should fall; supported objects should not.""" phys = PhysicsSystem() # Object on a surface surfaces = [{'y': 10.0, 'x_min': 0, 'x_max': 20}] supported = phys.check_support(np.array([5.0, 9.7]), 0.5, surfaces) assert supported, "Object on surface should be supported" not_supported = phys.check_support(np.array([5.0, 5.0]), 0.5, surfaces) assert not not_supported, "Object in air should NOT be supported" print(" PASS Physics Support (support detection)") def test_number_subitizing(): """Exact enumeration for 1-4 items.""" num_sys = NumberSystem(weber_fraction=0.15, subitize_limit=4) for n in range(1, 5): result = num_sys.perceive_numerosity(n) assert result['exact'] is True, f"Should subitize {n} items exactly" assert result['estimate'] == n, f"Subitized count wrong for {n}" # Larger numbers should NOT be exact result = num_sys.perceive_numerosity(20) assert result['exact'] is False, "20 items should not be subitized" print(" PASS Number Subitizing (exact 1-4, approximate >4)") def test_number_weber_ratio(): """Discrimination follows Weber's law: ratio matters, not difference.""" num_sys = NumberSystem(weber_fraction=0.15) # Easy ratio (1:2) — should be highly discriminable easy = num_sys.compare(10, 20) assert easy['discriminability'] > 2.0, "1:2 ratio should be easy to discriminate" # Hard ratio (9:10) — should be harder hard = num_sys.compare(9, 10) assert hard['discriminability'] < easy['discriminability'], \ "9:10 should be harder than 10:20" print(" PASS Number Weber Ratio (ratio-dependent discrimination)") def test_geometry_spatial_relations(): """Basic spatial relation computations.""" geo = GeometrySystem() rel = geo.spatial_relation(np.array([0.0, 0.0]), np.array([5.0, -3.0])) assert rel['right_of'] is True assert rel['above'] is True # y=-3 is above y=0 in image coords assert rel['distance'] > 0 print(" PASS Geometry Spatial Relations") def test_geometry_deformation(): """Smooth deformation fields from Distortable Canvas paper.""" geo = GeometrySystem() # Create a test image image = np.random.rand(28, 28) # Create deformation field u, v = geo.create_deformation_field((28, 28), smoothness=3.0) # Apply deformation warped = geo.apply_deformation(image, u, v) assert warped.shape == image.shape, "Warped image shape mismatch" # Canvas distance should be positive dist = geo.canvas_distance(u, v) assert dist > 0, "Canvas distance should be positive for non-zero deformation" # Dual distance dual = geo.dual_distance(image, image, u * 0, v * 0) assert dual == 0.0 or abs(dual) < 1e-10, \ "Zero deformation of image to itself should have near-zero distance" print(" PASS Geometry Deformation (Distortable Canvas)") def test_agent_detection(): """Self-propelled entities with direction changes should be classified as agents.""" agent_sys = AgentSystem(self_propulsion_threshold=0.1) # Simulate an agent with self-propelled direction changes positions = [ np.array([0.0, 0.0]), np.array([1.0, 0.0]), np.array([2.0, 0.0]), np.array([2.0, 1.0]), # Direction change! np.array([1.0, 1.0]), # Another direction change! np.array([0.0, 2.0]), ] for pos in positions: agent_sys.update_entity(entity_id=0, position=pos, was_contacted=False) score = agent_sys.get_agency_score(0) assert score > 0.3, f"Self-propelled entity with direction changes should have agency score > 0.3, got {score}" print(" PASS Agent Detection (self-propulsion + direction change)") def test_social_helper_preference(): """Helpers should be preferred over hinderers.""" soc = SocialSystem() # Entity 1 helps entity 0 soc.observe_interaction(actor_id=1, target_id=0, outcome='help') soc.observe_interaction(actor_id=1, target_id=0, outcome='help') # Entity 2 hinders entity 0 soc.observe_interaction(actor_id=2, target_id=0, outcome='hinder') soc.observe_interaction(actor_id=2, target_id=0, outcome='hinder') preferred = soc.evaluate_preference(1, 2) assert preferred == 1, "Helper should be preferred over hinderer!" score_helper = soc.get_prosocial_score(1) score_hinderer = soc.get_prosocial_score(2) assert score_helper > score_hinderer, "Helper score should exceed hinderer score" print(" PASS Social Helper Preference (prosocial > antisocial)") def run_all_tests(): print("============================================================") print("HippocampAIF Phase 5: Core Knowledge Tests") print("============================================================") test_object_permanence() test_object_continuity_violation() test_physics_gravity() test_physics_bounce() test_physics_support() test_number_subitizing() test_number_weber_ratio() test_geometry_spatial_relations() test_geometry_deformation() test_agent_detection() test_social_helper_preference() print("\n============================================================") print("ALL PHASE 5 TESTS PASSED") print("============================================================") if __name__ == "__main__": run_all_tests()