diff --git a/src/breakshaft/graph_walker.py b/src/breakshaft/graph_walker.py index 9e8c627..7a08af4 100644 --- a/src/breakshaft/graph_walker.py +++ b/src/breakshaft/graph_walker.py @@ -111,18 +111,36 @@ class GraphWalker: return head @classmethod - def explode_callgraph_branches(cls, g: Callgraph, from_types: frozenset[type]) -> list[CallgraphVariant]: + def explode_callgraph_branches(cls, g: Callgraph, from_types: frozenset[type], + priority_threshold: float = -1e9, + min_consumed_types: int = 0) -> list[CallgraphVariant]: + """ + Взрыв графа преобразований с pruning. + + Args: + g: Граф преобразований + from_types: Исходные типы + priority_threshold: Минимальный приоритет для рассмотрения (pruning) + min_consumed_types: Минимальное количество consumed_types (pruning) + + Returns: + list[CallgraphVariant]: Варианты преобразований + """ # Кэширование: создаём хэш графа # Хэш графа = хэш всех вариантов g_hash = hash(frozenset(g.variants)) if g.variants else 0 - cache_key = (g_hash, hash(from_types)) + cache_key = (g_hash, hash(from_types), hash(priority_threshold), hash(min_consumed_types)) # Проверяем кэш if cache_key in cls._explode_cache: return cls._explode_cache[cache_key] # Вычисляем лениво через generator - variants = list(cls._explode_callgraph_branches_lazy(g, from_types)) + variants = list(cls._explode_callgraph_branches_lazy( + g, from_types, + priority_threshold=priority_threshold, + min_consumed_types=min_consumed_types + )) # Сохраняем в кэш cls._explode_cache[cache_key] = variants @@ -130,22 +148,46 @@ class GraphWalker: return variants @classmethod - def _explode_callgraph_branches_lazy(cls, g: Callgraph, from_types: frozenset[type]): + def _explode_callgraph_branches_lazy(cls, g: Callgraph, from_types: frozenset[type], + priority_threshold: float = -1e9, + min_consumed_types: int = 0): """ - Ленивая версия explode_callgraph_branches (generator). + Ленивая версия explode_callgraph_branches (generator) с pruning. + + Args: + g: Граф преобразований + from_types: Исходные типы + priority_threshold: Минимальный приоритет для рассмотрения (pruning) + min_consumed_types: Минимальное количество consumed_types (pruning) Yields: CallgraphVariant: Варианты преобразований по одному """ for variant in g.variants: if len(variant.subgraphs) == 0: - yield variant + # Pruning: проверяем порог приоритета + variant_priority = variant.injector.priority if isinstance(variant.injector.priority, (int, float)) else 0.0 + if variant_priority >= priority_threshold: + yield variant + continue + + # Pruning: проверяем consumed_types + if len(variant.consumed_from_types) < min_consumed_types: + continue + + # Pruning: проверяем приоритет + variant_priority = variant.injector.priority if isinstance(variant.injector.priority, (int, float)) else 0.0 + if variant_priority < priority_threshold: continue # Собираем ленивые итераторы для подграфов subg_iterators = [] for subg in variant.subgraphs: - combinations = list(cls._explode_callgraph_branches_lazy(subg, from_types)) + combinations = list(cls._explode_callgraph_branches_lazy( + subg, from_types, + priority_threshold=priority_threshold, + min_consumed_types=min_consumed_types + )) if len(combinations) == 0: subg_iterators.append([None]) else: