Spaces:
Running
Running
| import numpy as np | |
| # Constants for shadowing analysis | |
| SHADOWING_EPSILON_THRESHOLD = 1e-3 | |
| def compute_epsilon_t(distances): | |
| """ | |
| Compute epsilon(t) as the running maximum of distances. | |
| Args: | |
| distances: Array of distances between two trajectories at each time point | |
| Returns: | |
| Array representing epsilon(t) - the running maximum of distances | |
| """ | |
| return np.maximum.accumulate(distances) | |
| def find_shadowing_breakdown_time(epsilon_t, t_eval, epsilon_threshold=SHADOWING_EPSILON_THRESHOLD): | |
| """ | |
| Find the shadowing breakdown time t* where epsilon(t) exceeds a threshold. | |
| Args: | |
| epsilon_t: Array of epsilon(t) values (running maximum of distances) | |
| t_eval: Time points corresponding to epsilon(t) values | |
| epsilon_threshold: Threshold above which shadowing is considered broken | |
| Returns: | |
| tuple: (shadowing_time, shadowing_length, shadowing_ratio) | |
| shadowing_time: Time t* where epsilon(t) > epsilon_threshold (or None if never exceeded) | |
| shadowing_length: Length of time where epsilon(t) <= epsilon_threshold | |
| shadowing_ratio: Ratio of valid shadowing time to total time | |
| """ | |
| if len(epsilon_t) != len(t_eval): | |
| raise ValueError("epsilon_t and t_eval must have the same length") | |
| # Find the first time where epsilon(t) exceeds the threshold | |
| exceed_indices = np.where(epsilon_t > epsilon_threshold)[0] | |
| if len(exceed_indices) == 0: | |
| # If threshold is never exceeded, shadowing holds for the entire duration | |
| shadowing_time = None # Indicates no breakdown occurred | |
| shadowing_length = t_eval[-1] - t_eval[0] | |
| shadowing_ratio = 1.0 | |
| else: | |
| # Take the first occurrence where threshold is exceeded | |
| first_exceed_idx = exceed_indices[0] | |
| shadowing_time = t_eval[first_exceed_idx] | |
| shadowing_length = t_eval[first_exceed_idx] - t_eval[0] | |
| shadowing_ratio = shadowing_length / (t_eval[-1] - t_eval[0]) | |
| return shadowing_time, shadowing_length, shadowing_ratio | |
| def compute_shadowing_diagnostics(dop_solution, nn_solution, t_eval, epsilon_threshold=SHADOWING_EPSILON_THRESHOLD): | |
| """ | |
| Compute comprehensive shadowing diagnostics by comparing two solutions. | |
| Args: | |
| dop_solution: Dictionary with 'x' and 'y' arrays from DOP853 solver | |
| nn_solution: Dictionary with 'x' and 'y' arrays from neural network solver | |
| t_eval: Time points array | |
| epsilon_threshold: Threshold for shadowing breakdown | |
| Returns: | |
| dict: Dictionary containing shadowing diagnostics | |
| """ | |
| # Calculate distance between DOP853 and NN solutions | |
| dist = np.sqrt((dop_solution['x'] - nn_solution['x'])**2 + (dop_solution['y'] - nn_solution['y'])**2) | |
| # Calculate epsilon(t) as the running maximum (sup norm) | |
| epsilon_t = compute_epsilon_t(dist) | |
| # Calculate shadowing breakdown diagnostics | |
| shadowing_time, shadowing_length, shadowing_ratio = find_shadowing_breakdown_time( | |
| epsilon_t, t_eval, epsilon_threshold | |
| ) | |
| return { | |
| 'epsilon_t': epsilon_t, | |
| 'distances': dist, | |
| 'shadowing_time': shadowing_time, | |
| 'shadowing_length': shadowing_length, | |
| 'shadowing_ratio': shadowing_ratio, | |
| 'epsilon_threshold': epsilon_threshold, | |
| 'has_breakdown': shadowing_time is not None | |
| } |