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()