Custom Systems

How to register and use your own chaotic systems.

The hidden-attractors-fo library is designed to be extensible. You can define your own systems and seamlessly use them with the built-in fractional solvers, seed generators, and basin classification tools.

The SystemDefinition Tuples

To add a new system, you must construct a SystemDefinition and pass it to register_system(). A valid definition requires:

  1. rhs(x, params): The vector field function. Must support 2D arrays (shape (N, 3)) for batch processing.
  2. jacobian(x, params): The derivative matrix. Must return a 3x3 array.
  3. default_params(): A function that returns a dictionary of the default parameters.
  4. equilibria_fn(params): A function that computes and returns a dictionary of named equilibria.
  5. name: A unique string identifier.
  6. description: A short string description.

Example: Registering the Chen Attractor

Let’s define and register the classic Chen attractor:

x˙=a(yx)y˙=(ca)xxz+cyz˙=xybz\begin{align} \dot{x} &= a(y - x) \\ \dot{y} &= (c - a)x - xz + cy \\ \dot{z} &= xy - bz \end{align}

1. Define the components

import numpy as np
from hidden_attractors.systems import SystemDefinition, register_system

def chen_params():
    return {"a": 35.0, "b": 3.0, "c": 28.0}

def chen_rhs(x, params):
    # Support both 1D (single point) and 2D (batch of points)
    is_1d = x.ndim == 1
    if is_1d:
        x = x.reshape(1, -1)
    
    a, b, c = params["a"], params["b"], params["c"]
    
    out = np.zeros_like(x)
    out[:, 0] = a * (x[:, 1] - x[:, 0])
    out[:, 1] = (c - a) * x[:, 0] - x[:, 0] * x[:, 2] + c * x[:, 1]
    out[:, 2] = x[:, 0] * x[:, 1] - b * x[:, 2]
    
    return out[0] if is_1d else out

def chen_jacobian(x, params):
    a, b, c = params["a"], params["b"], params["c"]
    x1, x2, x3 = x[0], x[1], x[2]
    
    return np.array([
        [-a,        a,    0],
        [c - a - x3, c,   -x1],
        [x2,        x1,   -b]
    ])

def chen_equilibria(params):
    a, b, c = params["a"], params["b"], params["c"]
    
    # Chen has three equilibria
    z_eq = (2*a*c - c**2) / a
    if z_eq < 0:
        return {"O0": np.array([0.0, 0.0, 0.0])}
        
    xy_eq = np.sqrt(b * z_eq)
    
    return {
        "O0": np.array([0.0, 0.0, 0.0]),
        "O+": np.array([xy_eq, xy_eq, z_eq]),
        "O-": np.array([-xy_eq, -xy_eq, z_eq])
    }

2. Register the system

chen_def = SystemDefinition(
    name="chen",
    rhs=chen_rhs,
    jacobian=chen_jacobian,
    default_params=chen_params,
    equilibria_fn=chen_equilibria,
    description="Chen 1999 attractor"
)

register_system(chen_def)

3. Use the system

Once registered, you can use "chen" exactly like "chua-nonsmooth" in any workflow or CLI command.

from hidden_attractors import get_system

system = get_system("chen")
print(system.equilibria_fn(system.default_params()))

Or from the terminal (if you saved the registration code in your environment initialization):

hidden-attractors-integer-chua --system chen --output results_chen/