Skip to content

Cusp normal forms

The cusp normal form x' = β1 + β2 x - x³, bistable inside the cusp 27 β1² < 4 β2³.

  • Name: cusp_form
  • Field: mathematics/dynamical_systems
  • Formalism: ordinary differential equations
  • References: Gros 2015; Dilão 2023

Factory

@register_model(
    field='mathematics/dynamical_systems',
    tags=('bifurcation', 'normal form', 'ode', 'bistability', 'hysteresis'),
    references=('Gros 2015', 'Dilão 2023'),
    defaults={'until': 20.0, 'dt': 0.1},
)
def cusp_form(x: float = 0.5, *, beta1: float = 0.0, beta2: float = 1.0) -> System:
    """Return the cusp normal form `x' = β1 + β2 x - x³`, bistable inside the cusp `27 β1² < 4 β2³`.

    Args:
        x:
            The initial value.

        beta1:
            The asymmetry.

        beta2:
            The splitting parameter.

    Returns:
        system:
            The model, with the state `form.x`.

    Examples:
        >>> from skcomplex.models.mathematics.dynamical_systems import cusp_form
        >>> cusp_form(beta1=0.2).elements[0].beta1
        0.2
    """
    return System(elements=[CuspElement('form', x=x, beta1=beta1, beta2=beta2)])

Components

class CuspElement(Element):
    """Normal form `x' = beta1 + beta2 x - x^3` of the cusp bifurcation.

    Args:
        label:
            The label of the element.

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

        x:
            The initial value.

        beta1:
            The first unfolding parameter, the asymmetry.

        beta2:
            The second unfolding parameter, the splitting.

    Examples:
        >>> from skcomplex.models.mathematics.dynamical_systems import CuspElement
        >>> CuspElement('form', beta2=2.0).beta2
        2.0
    """

    def __init__(
        self,
        label: str,
        visual: VisualSpec | None = None,
        *,
        x: Annotated[object, StateSpec()] = 0.5,
        beta1: Annotated[object, ParameterSpec()] = 0.0,
        beta2: Annotated[object, ParameterSpec()] = 1.0,
    ) -> None:
        super().__init__(label, visual=visual)
        self.x = x
        self.beta1 = beta1
        self.beta2 = beta2

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

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

        Returns:
            laws:
                The laws of the element.
        """
        return [RateLaw(s.x, s.beta1 + s.beta2 * s.x - s.x**3)]

Equations

form.x: diff(x_form(t), t) == -x_form(t)^3 + beta2_form*x_form(t) + beta1_form