Basin Classification

Classifying trajectories to identify hidden attractors.

The core goal of this library is determining whether an attractor is hidden or self-excited. This is done by examining the relationship between the attractor’s basin and the system’s unstable equilibria. The basins module provides the tools to perform this classification automatically.

The BasinLabel Enum

Every trajectory integrated by the workflows is ultimately assigned a label from the BasinLabel enumeration:

from hidden_attractors.basins import BasinLabel

print(BasinLabel.SELF_EXCITED)     # Trajectory converges to or originates from an equilibrium neighborhood
print(BasinLabel.HIDDEN_CANDIDATE) # Trajectory forms an attractor disconnected from all equilibria
print(BasinLabel.DIVERGENT)        # Trajectory escapes to infinity
print(BasinLabel.INCONCLUSIVE)     # Numerical errors or NaNs during integration

Basic Classification

If you have integrated a trajectory and just want to classify its final point, use classify_basin(). This is very fast and requires only the last point of the simulation.

from hidden_attractors.basins import classify_basin
from hidden_attractors.models import equilibria_nonsmooth, chua_nonsmooth_parameters
import numpy as np

params = chua_nonsmooth_parameters()
eq = equilibria_nonsmooth(params)

# Assume final_point is the last row of your integration array
final_point = np.array([1.51, 0.02, -1.49])

label = classify_basin(
    final_point=final_point,
    equilibria=eq,
    thresholds={
        "self_excited": 0.5,  # Distance to be considered "touching" an equilibrium
        "divergent": 100.0    # Distance to be considered escaped to infinity
    }
)

print(label) # BasinLabel.SELF_EXCITED (it's very close to O+)

Thresholds

The thresholds dictionary controls the geometric boundaries of the classification:

  • self_excited: If the Euclidean distance between the final_point and any equilibrium is less than this value, it’s classified as self-excited. This defines the “small neighborhood” around the unstable equilibria.
  • divergent: If any coordinate of the final_point exceeds this value, it’s marked divergent.

Strict Trajectory Classification

While classify_basin only looks at the final point, sometimes a trajectory might form a large attractor that doesn’t converge to a point, but does periodically swing very close to an equilibrium. To classify the entire attractor cloud, use classify_trajectory().

from hidden_attractors.basins import classify_trajectory

# x is the full trajectory array of shape (N, 3)
label = classify_trajectory(
    trajectory=x,
    equilibria=eq,
    thresholds={"self_excited": 0.5, "divergent": 100.0}
)

This function computes the minimum distance from any point in the trajectory to any equilibrium. If that minimum distance is less than the self_excited threshold, the entire attractor is considered self-excited, even if the final point happens to be far away at the moment integration stopped.

This is the most robust way to verify a hidden candidate.