Source code for mercurial.branches.alignment

"""Cross-branch alignment dynamics with level coupling (MERCURIAL C)."""

from dataclasses import dataclass
from typing import List, Optional

import numpy as np

from mercurial.hierarchy.coupling import LevelCoupling


[docs] @dataclass class AlignmentEvent: branch_a: int branch_b: int position: np.ndarray time: float coupling_strength: float duration: float
[docs] def coupling_envelope(self, t: float) -> float: dt = t - self.time if dt < 0 or dt > self.duration: return 0.0 decay = np.exp(-dt / (self.duration / 3)) return self.coupling_strength * decay
[docs] class BranchAlignment: """ Implements alignment probability with cross‑level adjacency optimization. """
[docs] def __init__( self, branch_levels: List[int], base_coupling: float = 0.1, alignment_threshold: float = 0.7, decay_length: float = 1.0, k_align: float = 1.0, S_crit: float = 10.0, tau_align: float = 1.0, ): """ Parameters ---------- branch_levels : List[int] LADDER level for each branch. base_coupling : float K_0 for same-level coupling. alignment_threshold : float Minimum similarity to consider alignment. decay_length : float ℓ_adj for cross-level decay. k_align : float Boltzmann constant for entropy factor. S_crit : float Critical entropy threshold. tau_align : float Alignment decay time constant. """ self.branch_levels = branch_levels self.base_coupling = base_coupling self.theta_align = alignment_threshold self.decay_length = decay_length self.k_align = k_align self.S_crit = S_crit self.tau_align = tau_align self.level_coupling = LevelCoupling(base_coupling, decay_length) self._precompute_coupling_matrix()
def _precompute_coupling_matrix(self): """Precompute K_ij for all branch pairs.""" n = len(self.branch_levels) self.K_matrix = np.zeros((n, n)) for i in range(n): for j in range(n): self.K_matrix[i, j] = self.level_coupling.coupling_strength( self.branch_levels[i], self.branch_levels[j] )
[docs] def alignment_probability( self, branch_i: int, branch_j: int, entropy_i: float, entropy_j: float, base_similarity: float = 1.0, ) -> float: """ p_align = K_ij * base_similarity * exp(-(S_i+S_j)/k_align) No hard threshold – allows low probabilities for cross‑level. """ K_ij = self.K_matrix[branch_i, branch_j] S_total = entropy_i + entropy_j entropy_factor = np.exp(-S_total / self.k_align) p = K_ij * base_similarity * entropy_factor # Optional: apply a soft threshold (sigmoid) if needed, but not zero return p
[docs] def compute_transfer( self, pattern_source: np.ndarray, coupling: float, mapping: Optional[np.ndarray] = None, use_isomorphism: bool = True, ) -> np.ndarray: if use_isomorphism and mapping is None: from mercurial.branches.isomorphism import IsomorphismMapper IsomorphismMapper() # For demonstration, we'd need target pattern; here just identity # In real use, mapping would be precomputed between branch patterns mapping = np.eye(len(pattern_source)) if mapping is None: mapping = np.eye(len(pattern_source)) transformed = mapping @ pattern_source return coupling * transformed
[docs] def alignment_dynamics(self, lambda0: float, t: float) -> float: return lambda0 * np.exp(-t / self.tau_align)