File size: 9,315 Bytes
32d978d | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 | """
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()
|