import numpy as np def calculate_frustration_score(path_vertices): """ Analyzes the 'winding' of the Sperner Walk to detect topological frustration. Frustration Score is defined as the ratio of Total Path Length to Net Displacement. Interpretability: - ~1.0: Perfect Alignment. The solver moved directly to the consensus. - 1.5 - 3.0: Moderate Complexity. Non-linear trade-offs. - > 3.0: High Topological Frustration. Objectives are conflicting or orthogonally opposed. Args: path_vertices (list of array-like): Sequence of points (centroids) visited by the solver. Returns: float: The Frustration Score. Returns 1.0 for short paths (<2 steps). Returns 999.0 if net displacement is effectively zero (loop). """ if not path_vertices or len(path_vertices) < 2: return 1.0 path = np.array(path_vertices) # 1. Calculate total distance walked (sum of Euclidean steps) diffs = path[1:] - path[:-1] distances = np.linalg.norm(diffs, axis=1) total_dist = np.sum(distances) # 2. Calculate displacement (Start to Finish) start = path[0] end = path[-1] displacement = np.linalg.norm(end - start) # Avoid division by zero if displacement < 1e-9: return 999.0 # Loop detected or returned to start # Ratio: How much did we wander? frustration_index = total_dist / displacement return float(frustration_index)