Skip to content

Aggregate-averaging synchronization

Two logistic maps coupled by aggregate averaging, which synchronize their chaos for strong coupling.

  • Name: aggregate_averaging_synchronization
  • Field: physics/synchronization
  • Formalism: discrete map
  • References: Gros 2015

Factory

@register_model(
    field='physics/synchronization',
    tags=('synchronization', 'chaos', 'map', 'logistic map'),
    references=('Gros 2015',),
    defaults={'until': 50, 'dt': 1},
)
def aggregate_averaging_synchronization(
    x1: float = 0.3,
    x2: float = 0.6,
    *,
    r: float = 4.0,
    kappa: float = 0.4,
) -> System:
    """Return two logistic maps coupled by aggregate averaging, which synchronize their chaos for strong coupling.

    Each map iterates on the aggregate of its own value and the other's, `x_i(t + 1) = r a_i (1 - a_i)` with
    `a_1 = (1 - kappa) x_1 + kappa x_2` and `a_2 = (1 - kappa) x_2 + kappa x_1`. The synchronized orbit is stable for
    `kappa > (r - 1) / (2 r)`, which is `3 / 8` for `r = 4`, and the maps agree in one step for `kappa = 1 / 2`.

    Args:
        x1:
            The initial value of the first map.

        x2:
            The initial value of the second map.

        r:
            The growth rate of the logistic map.

        kappa:
            The coupling strength.

    Returns:
        system:
            The model, with the values `first.x` and `second.x`.

    Examples:
        >>> from skcomplex.models.physics.synchronization import aggregate_averaging_synchronization
        >>> aggregate_averaging_synchronization(kappa=0.1).interactions[0].kappa
        0.1
    """
    return System(
        elements=[LogisticUnitElement('first', x=x1), LogisticUnitElement('second', x=x2)],
        interactions=[
            AggregateAveragingInteraction(f'averaging_{target}', source=source, target=target, r=r, kappa=kappa)
            for source, target in (('second', 'first'), ('first', 'second'))
        ],
    )

Components

class LogisticUnitElement(Element):
    """Value `x` in `[0, 1]` of a logistic map, which maps of the coupling iterate.

    Args:
        label:
            The label of the unit.

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

        x:
            The initial value.

    Attributes:
        default_visual_:
            The visual of the unit when it sets none.

    Examples:
        >>> from skcomplex.models.physics.synchronization import LogisticUnitElement
        >>> LogisticUnitElement('unit').x
        0.3
    """

    def __init__(
        self,
        label: str,
        visual: VisualSpec | None = None,
        *,
        x: Annotated[float, StateSpec()] = 0.3,
    ) -> None:
        super().__init__(label, visual=visual)
        self.x = x

class AggregateAveragingInteraction(MapInteraction):
    """Logistic map of an aggregate, `x_t(t + 1) = r a (1 - a)` with `a = (1 - kappa) x_t + kappa x_s`.

    Args:
        label:
            The label of the interaction, unique among the parts of its system.

        source:
            The path of the unit whose value the aggregate reads.

        target:
            The path of the unit whose value the map sets.

        r:
            The growth rate of the logistic map.

        kappa:
            The weight of the source in the aggregate.

    Examples:
        >>> from types import SimpleNamespace
        >>> from sage.all__sagemath_symbolics import var
        >>> from skcomplex.models.physics.synchronization import AggregateAveragingInteraction
        >>> averaging = AggregateAveragingInteraction('averaging', source='a', target='b').check().prepare()
        >>> source, target = SimpleNamespace(x=var('u')), SimpleNamespace(x=var('v'))
        >>> namespace = SimpleNamespace(source=source, target=target, r=4, kappa=var('kappa'))
        >>> [law.value.factor() for law in averaging.run(namespace).laws_]
        [-4*(kappa*u - kappa*v + v)*(kappa*u - kappa*v + v - 1)]
    """

    def __init__(
        self,
        label: str,
        *,
        source: Annotated[str, RoleSpec()],
        target: Annotated[str, RoleSpec()],
        r: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 4.0,
        kappa: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 0.4,
    ) -> None:
        super().__init__(label, target=target, state='x', function=self._average)
        self.source = source
        self.r = r
        self.kappa = kappa

    def _average(self, namespace: SimpleNamespace) -> Expression:
        """Return the logistic map `r a (1 - a)` of the aggregate `a = (1 - kappa) x_t + kappa x_s`."""
        aggregate = (1 - namespace.kappa) * namespace.target.x + namespace.kappa * namespace.source.x
        return namespace.r * aggregate * (1 - aggregate)

Equations

first.x: x_first(t + 1) == -((kappa_averaging_first - 1)*x_first(t) - kappa_averaging_first*x_second(t) + 1)*((kappa_averaging_first - 1)*x_first(t) - kappa_averaging_first*x_second(t))*r_averaging_first
second.x: x_second(t + 1) == -(kappa_averaging_second*x_first(t) - (kappa_averaging_second - 1)*x_second(t))*(kappa_averaging_second*x_first(t) - (kappa_averaging_second - 1)*x_second(t) - 1)*r_averaging_second