1+ """
2+ Test for island parent-child consistency - Programs' parents should be in the corresponding islands
3+ """
4+
5+ import unittest
6+ from openevolve .config import Config
7+ from openevolve .database import ProgramDatabase , Program
8+
9+
10+ class TestIslandParentConsistency (unittest .TestCase ):
11+ """Test that parent-child relationships respect island boundaries"""
12+
13+ def test_parent_child_island_consistency (self ):
14+ """Test that children are added to the same island as their parents"""
15+ config = Config ()
16+ config .database .num_islands = 3
17+ database = ProgramDatabase (config .database )
18+
19+ # Create initial program on island 0
20+ initial_program = Program (
21+ id = "initial" ,
22+ code = "def initial(): pass" ,
23+ metrics = {"score" : 0.5 },
24+ iteration_found = 0
25+ )
26+ database .add (initial_program ) # Should go to island 0 (current_island)
27+
28+ # Verify initial program is on island 0
29+ self .assertIn ("initial" , database .islands [0 ])
30+ self .assertEqual (initial_program .metadata .get ("island" ), 0 )
31+
32+ # Now switch to island 1
33+ database .next_island ()
34+ self .assertEqual (database .current_island , 1 )
35+
36+ # Create a child of the initial program
37+ child_program = Program (
38+ id = "child1" ,
39+ code = "def child1(): pass" ,
40+ parent_id = "initial" , # Parent is on island 0
41+ metrics = {"score" : 0.6 },
42+ iteration_found = 1
43+ )
44+
45+ # Add child without specifying target_island
46+ # This is what happens in process_parallel.py line 445
47+ database .add (child_program )
48+
49+ # With the fix: child should go to parent's island (0), not current_island (1)
50+ parent_island = database .programs ["initial" ].metadata .get ("island" , 0 )
51+ child_island = database .programs ["child1" ].metadata .get ("island" )
52+
53+ # Check if parent is in child's island (this is what the user's assertion checks)
54+ if child_program .parent_id :
55+ # This is the exact check from the issue report - should now pass
56+ self .assertIn (
57+ child_program .parent_id ,
58+ database .islands [child_island ],
59+ "Parent should be in child's island"
60+ )
61+
62+ # Verify child is on same island as parent
63+ self .assertEqual (
64+ parent_island ,
65+ child_island ,
66+ f"Child should be on same island as parent. Parent: island { parent_island } , Child: island { child_island } "
67+ )
68+
69+ def test_multiple_generations_island_drift (self ):
70+ """Test that island drift happens across multiple generations"""
71+ config = Config ()
72+ config .database .num_islands = 4
73+ database = ProgramDatabase (config .database )
74+
75+ # Create a lineage
76+ programs = []
77+ for i in range (10 ):
78+ if i == 0 :
79+ # Initial program
80+ prog = Program (
81+ id = f"prog_{ i } " ,
82+ code = f"def prog_{ i } (): pass" ,
83+ metrics = {"score" : 0.1 * i },
84+ iteration_found = i
85+ )
86+ else :
87+ # Child of previous
88+ prog = Program (
89+ id = f"prog_{ i } " ,
90+ code = f"def prog_{ i } (): pass" ,
91+ parent_id = f"prog_{ i - 1 } " ,
92+ metrics = {"score" : 0.1 * i },
93+ iteration_found = i
94+ )
95+
96+ database .add (prog )
97+ programs .append (prog )
98+
99+ # Switch islands periodically (simulating what happens in evolution)
100+ if i % 3 == 0 :
101+ database .next_island ()
102+
103+ # Check island consistency
104+ inconsistent_pairs = []
105+ for prog in programs :
106+ if prog .parent_id :
107+ parent = database .programs .get (prog .parent_id )
108+ if parent :
109+ parent_island = parent .metadata .get ("island" )
110+ child_island = prog .metadata .get ("island" )
111+
112+ # Check if parent is in child's island
113+ if prog .parent_id not in database .islands [child_island ]:
114+ inconsistent_pairs .append ((prog .parent_id , prog .id ))
115+
116+ # With the fix, we should find NO inconsistent parent-child island assignments
117+ self .assertEqual (
118+ len (inconsistent_pairs ),
119+ 0 ,
120+ f"Found { len (inconsistent_pairs )} inconsistent parent-child pairs: { inconsistent_pairs } "
121+ )
122+
123+ # Verify all parent-child pairs are on the same island
124+ for prog in programs :
125+ if prog .parent_id :
126+ parent = database .programs .get (prog .parent_id )
127+ if parent :
128+ parent_island = parent .metadata .get ("island" )
129+ child_island = prog .metadata .get ("island" )
130+ self .assertEqual (
131+ parent_island ,
132+ child_island ,
133+ f"Parent { prog .parent_id } (island { parent_island } ) and "
134+ f"child { prog .id } (island { child_island } ) should be on same island"
135+ )
136+
137+
138+ def test_explicit_migration_override (self ):
139+ """Test that explicit target_island overrides parent island inheritance"""
140+ config = Config ()
141+ config .database .num_islands = 3
142+ database = ProgramDatabase (config .database )
143+
144+ # Create parent on island 0
145+ parent = Program (
146+ id = "parent" ,
147+ code = "def parent(): pass" ,
148+ metrics = {"score" : 0.5 },
149+ iteration_found = 0
150+ )
151+ database .add (parent ) # Goes to island 0
152+ self .assertIn ("parent" , database .islands [0 ])
153+
154+ # Create child but explicitly send to island 2 (migration)
155+ migrant_child = Program (
156+ id = "migrant" ,
157+ code = "def migrant(): pass" ,
158+ parent_id = "parent" ,
159+ metrics = {"score" : 0.7 },
160+ iteration_found = 1
161+ )
162+ database .add (migrant_child , target_island = 2 ) # Explicit migration
163+
164+ # Verify migrant went to island 2, not parent's island 0
165+ self .assertIn ("migrant" , database .islands [2 ])
166+ self .assertNotIn ("migrant" , database .islands [0 ])
167+ self .assertEqual (migrant_child .metadata .get ("island" ), 2 )
168+
169+ # Parent should still be on island 0
170+ self .assertEqual (database .programs ["parent" ].metadata .get ("island" ), 0 )
171+
172+
173+ if __name__ == "__main__" :
174+ unittest .main ()
0 commit comments