"""Branch formation via symmetry breaking and bifurcation (MERCURIAL B)."""
from dataclasses import dataclass
from typing import List, Tuple
import numpy as np
from mercurial.branches.branch import RealityBranch
from mercurial.branches.decoherence import DecoherenceMeasure
from mercurial.core.state_space import HilbertSpace, StateVector
[docs]
@dataclass
class BifurcationPoint:
"""Parameters at which a branch splits."""
time: float
critical_state: StateVector
symmetry_parameter: float
new_branch_seed: StateVector
[docs]
class SymmetryBreaking:
"""
Simulates branch formation via spontaneous symmetry breaking.
Key concept: When a control parameter crosses a critical threshold,
the symmetric state becomes unstable, and two (or more) new stable
branches emerge.
"""
def __init__(
self,
space: HilbertSpace,
critical_parameter: float = 1.0,
bifurcation_strength: float = 0.1,
):
self.space = space
self.theta_crit = critical_parameter
self.strength = bifurcation_strength
self.decoherence = DecoherenceMeasure()
[docs]
def compute_order_parameter(self, state: StateVector) -> float:
"""
Order parameter that breaks symmetry (e.g., magnetization, phase difference).
Simplified: variance of components.
"""
return np.var(state.components.real) + np.var(state.components.imag)
[docs]
def is_instability_condition(self, state: StateVector, parameter: float) -> bool:
"""
Check if system is at a bifurcation point.
"""
return parameter >= self.theta_crit
[docs]
def generate_new_branch_states(
self, parent_state: StateVector, n_branches: int = 2
) -> List[StateVector]:
"""
Generate n_branches new branch states from the parent state.
Uses random orthogonal perturbations.
"""
new_states = []
dim = len(parent_state.components)
for _ in range(n_branches):
# Generate random orthogonal perturbation
perturbation = np.random.normal(0, self.strength, size=dim) + 1j * np.random.normal(
0, self.strength, size=dim
)
# Project onto orthogonal subspace of parent state
parent_norm = parent_state.components / np.linalg.norm(parent_state.components)
perturbation = perturbation - np.dot(perturbation, parent_norm) * parent_norm
new_components = parent_state.components + perturbation
# Normalize
new_components = new_components / np.linalg.norm(new_components)
new_states.append(StateVector(parent_state.space, new_components, parent_state.time))
return new_states
[docs]
def evolve_to_bifurcation(
self,
initial_state: StateVector,
parameter_evolution: List[Tuple[float, float]],
dt: float = 0.01,
) -> List[BifurcationPoint]:
"""
Simulate the evolution of a branch as a control parameter changes.
parameter_evolution: list of (time, parameter_value)
Returns list of bifurcation points.
"""
bifurcations = []
state = initial_state
current_time = 0.0
param_idx = 0
param_value = parameter_evolution[0][1] if parameter_evolution else 0.0
while (
current_time < parameter_evolution[-1][0] and param_idx < len(parameter_evolution) - 1
):
# Update parameter
next_time, next_param = parameter_evolution[param_idx + 1]
param_value = next_param
current_time = next_time
param_idx += 1
# Check for bifurcation
if self.is_instability_condition(state, param_value):
# Generate new branches
new_states = self.generate_new_branch_states(state)
for new_state in new_states:
bifurcations.append(
BifurcationPoint(
time=current_time,
critical_state=state,
symmetry_parameter=param_value,
new_branch_seed=new_state,
)
)
# For simplicity, continue with the first new branch
state = new_states[0]
return bifurcations