| from __future__ import annotations |
|
|
| from typing import List, Tuple |
|
|
| import pandas as pd |
|
|
|
|
| def generate_collatz_tree( |
| backbone_length: int = 11, |
| branch_length: int = 5, |
| max_depth: int = 3, |
| ) -> pd.DataFrame: |
| """ |
| Generate an inverse Collatz tree structure starting from 1, with controlled |
| depth and branch expansion, based on the reverse Collatz rule: |
| |
| If (n - 1) % 3 == 0 and n is even, then (n - 1) / 3 is a valid preimage. |
| |
| The construction uses: |
| - a "backbone" of powers of 2 starting at 1, |
| - branches that grow from specific backbone nodes and from later branch nodes, |
| - recursive expansion up to exactly `max_depth` levels. |
| |
| Parameters |
| ---------- |
| backbone_length : int, default=11 |
| Length of the backbone (powers of 2). |
| Backbone nodes are exactly: 1, 2, 4, ..., 2^(backbone_length - 1). |
| branch_length : int, default=5 |
| Length of each branch created from valid reverse steps. |
| Branch nodes are formed by repeated doubling from the branch root. |
| Branches always have exactly `branch_length` nodes. |
| max_depth : int, default=3 |
| Number of recursive branch-expansion levels. |
| |
| Returns |
| ------- |
| pd.DataFrame |
| DataFrame with two columns: |
| - "Source": child node (preimage under the Collatz map), |
| - "Target": parent node (image under the Collatz map). |
| |
| These edges define a directed tree (or forest) rooted at 1. |
| """ |
|
|
| if backbone_length < 2: |
| raise ValueError("backbone_length must be at least 2.") |
|
|
| if branch_length < 1: |
| raise ValueError("branch_length must be at least 1.") |
|
|
| if max_depth < 0: |
| raise ValueError("max_depth must be non-negative.") |
|
|
| |
| branches: List[List[int]] = [[2 ** i for i in range(backbone_length)]] |
|
|
| |
| edges: List[Tuple[int, int]] = [(2 ** i, 2 ** (i - 1)) for i in range(1, backbone_length)] + [(1,4)] |
|
|
| |
| branch_creating_numbers: List[int] = [ |
| 2 ** i for i in range(4, backbone_length, 2) |
| ] |
|
|
| |
| for _ in range(max_depth): |
| next_level_branch_creators: List[int] = [] |
|
|
| for branch_num in branch_creating_numbers: |
| |
| parent = (branch_num - 1) // 3 |
| edges.append((parent, branch_num)) |
|
|
| |
| new_branch = [parent * (2 ** i) for i in range(branch_length)] |
| branches.append(new_branch) |
|
|
| |
| edges.extend( |
| (new_branch[i + 1], new_branch[i]) |
| for i in range(len(new_branch) - 1) |
| ) |
|
|
| |
| next_level_branch_creators.extend( |
| n for n in new_branch[1:] if (n - 1) % 3 == 0 |
| ) |
|
|
| branch_creating_numbers = next_level_branch_creators |
|
|
| df_edges = pd.DataFrame(edges, columns=["Source", "Target"]).drop_duplicates() |
|
|
| return df_edges |