Skip to content

Thomas cyclically symmetric attractor

The Thomas flow x' = -a x + sin y with its cyclic permutations, chaotic for a = 0.18.

  • Name: thomas
  • Field: mathematics/dynamical_systems
  • Formalism: ordinary differential equations
  • References: Thomas 1999; Dilão 2023

Select this system

from skcomplex.systems import create_simulation, create_system, describe

system = create_system('thomas')
entry = describe('thomas')
trajectory = create_simulation('thomas').prepare().run().trajectory_

Factory

@register_system(
    field='mathematics/dynamical_systems',
    tags=('chaos', 'ode', 'cyclic symmetry', 'strange attractor'),
    references=('Thomas 1999', 'Dilão 2023'),
    defaults={'until': 500.0, 'dt': 0.1},
)
def thomas(x: float = 0.1, y: float = 0.0, z: float = 0.0, *, a: float = 0.18) -> System:
    """Return the Thomas flow `x' = -a x + sin y` with its cyclic permutations, chaotic for `a = 0.18`.

    The flow is symmetric under the cyclic permutation of its coordinates. As the damping `a` falls the fixed points
    give way to limit cycles and then to chaos, and at `a = 0` the orbits wander through a lattice of cells, Thomas's
    labyrinth chaos (Dilão 2023, Exercise 3.23).

    Args:
        x:
            The initial first coordinate.

        y:
            The initial second coordinate.

        z:
            The initial third coordinate.

        a:
            The damping of every coordinate.

    Returns:
        system:
            The system, with the states `thomas.x`, `thomas.y`, and `thomas.z`.

    Examples:
        >>> from skcomplex.systems.mathematics.dynamical_systems import thomas
        >>> thomas(a=0.2).elements[0].a
        0.2
    """
    return System(elements=[ThomasElement('thomas', x=x, y=y, z=z, a=a)])

Components

Elements:

  • ThomasElement, from Element
class ThomasElement(Element):
    """Thomas flow `x' = -a x + sin y`, `y' = -a y + sin z`, `z' = -a z + sin x`, cyclically symmetric and damped.

    Args:
        label:
            The label of the element.

        visual:
            How the element is drawn, or `None` for `default_visual_`.

        x:
            The initial first coordinate.

        y:
            The initial second coordinate.

        z:
            The initial third coordinate.

        a:
            The damping of every coordinate.

    Examples:
        >>> from skcomplex.systems.mathematics.dynamical_systems import ThomasElement
        >>> ThomasElement('thomas', a=0.2).a
        0.2
    """

    def __init__(
        self,
        label: str,
        visual: VisualSpec | None = None,
        *,
        x: Annotated[object, StateSpec()] = 0.1,
        y: Annotated[object, StateSpec()] = 0.0,
        z: Annotated[object, StateSpec()] = 0.0,
        a: Annotated[object, ParameterSpec()] = 0.18,
    ) -> None:
        super().__init__(label, visual=visual)
        self.x = x
        self.y = y
        self.z = z
        self.a = a

    def _get_laws(self, s: SimpleNamespace) -> list[BaseLaw]:
        """Return the rates of the three coordinates.

        Args:
            s:
                The namespace of the symbols the laws can use.

        Returns:
            laws:
                The laws of the element.
        """
        return [
            RateLaw(s.x, -s.a * s.x + sin(s.y)),
            RateLaw(s.y, -s.a * s.y + sin(s.z)),
            RateLaw(s.z, -s.a * s.z + sin(s.x)),
        ]

Equations

thomas.x: diff(x_thomas(t), t) == -a_thomas*x_thomas(t) + sin(y_thomas(t))
thomas.y: diff(y_thomas(t), t) == -a_thomas*y_thomas(t) + sin(z_thomas(t))
thomas.z: diff(z_thomas(t), t) == -a_thomas*z_thomas(t) + sin(x_thomas(t))