From 952ab8563909872bcb75eb96502585473687ac9d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Mon, 31 Aug 2026 09:01:35 +0000 Subject: [PATCH] Add xfail regression test for RampedHalfAndHalf ignoring max_depth (#259) Co-authored-by: Alcides Fonseca --- .../tree_based/initializer_test.py | 50 ++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/tests/representations/tree_based/initializer_test.py b/tests/representations/tree_based/initializer_test.py index 197f87e8..80173f1c 100644 --- a/tests/representations/tree_based/initializer_test.py +++ b/tests/representations/tree_based/initializer_test.py @@ -3,6 +3,8 @@ from dataclasses import dataclass from typing import Annotated +import pytest + from geneticengine.problems import SingleObjectiveProblem @@ -11,10 +13,15 @@ from geneticengine.random.sources import NativeRandomSource from geneticengine.representations.tree.initializations import ( FullDecider, + MaxDepthDecider, PositionIndependentGrowDecider, ProgressivelyTerminalDecider, ) -from geneticengine.representations.tree.operators import FullInitializer, GrowInitializer +from geneticengine.representations.tree.operators import ( + FullInitializer, + GrowInitializer, + RampedHalfAndHalfInitializer, +) from geneticengine.representations.tree.treebased import TreeBasedRepresentation from geneticengine.grammar.metahandlers.floats import FloatRange from geneticengine.grammar.metahandlers.ints import IntervalRange @@ -53,6 +60,22 @@ class C(A): two: A +@abstract +class Expr: + pass + + +@dataclass +class Leaf(Expr): + pass + + +@dataclass +class Branch(Expr): + left: Expr + right: Expr + + class TestInitializers: def test_full(self): target_size = 10 @@ -84,6 +107,31 @@ def test_pi_grow(self): for ind in population: assert ind.get_phenotype().gengy_distance_to_term <= target_depth + @pytest.mark.xfail(strict=True, reason="https://github.com/alcides/GeneticEngine/issues/259") + def test_ramped_half_and_half_respects_max_depth(self): + """Regression test for https://github.com/alcides/GeneticEngine/issues/259. + + RampedHalfAndHalfInitializer receives a max_depth, but ignores it: + initialize() calls representation.create_genotype() without a + depth-bounded decider, so individuals are generated with the + representation's default decider instead. + """ + target_size = 20 + target_depth = 3 + + g = extract_grammar([Leaf, Branch], Expr) + f = RampedHalfAndHalfInitializer(max_depth=target_depth) + p = SingleObjectiveProblem(lambda x: 3) + rs = NativeRandomSource(5) + repr = TreeBasedRepresentation(grammar=g, decider=MaxDepthDecider(rs, g, max_depth=10)) + + population = list(f.initialize(p, repr, rs, target_size)) + assert len(population) == target_size + depths = [ind.get_phenotype().gengy_distance_to_term for ind in population] + assert all( + depth <= target_depth for depth in depths + ), f"RampedHalfAndHalfInitializer(max_depth={target_depth}) produced trees with depths {depths}" + def test_progressive(self): target_size = 10