Skip to content

Potts

The q-state Potts model on a torus, ordered below the critical temperature 1 / ln(1 + sqrt q).

  • Name: potts
  • Field: physics/statistical_mechanics
  • Formalism: Monte Carlo sampling
  • References: Potts 1952; Wu 1982

Factory

@register_model(
    field='physics/statistical_mechanics',
    tags=('lattice', 'Monte Carlo', 'phase transition', 'spins'),
    references=('Potts 1952', 'Wu 1982'),
    defaults={'until': 200, 'dt': 1},
)
def potts(
    size: int | Sequence[int] = 64,
    *,
    q: int = 3,
    temperature: float = 1.0,
    J: float = 1.0,
    sampler: str = 'heat-bath',
    random_state: int | np.random.Generator | None = 0,
) -> System:
    """Return the q-state Potts model on a torus, ordered below the critical temperature `1 / ln(1 + sqrt q)`.

    Args:
        size:
            The side of the square lattice, or its shape.

        q:
            The number of states.

        temperature:
            The temperature in units of the coupling.

        J:
            The coupling between neighbouring spins.

        sampler:
            The acceptance rule, `'metropolis'`, `'glauber'`, or `'heat-bath'`.

        random_state:
            The seed or generator of the random initial states.

    Returns:
        system:
            The model, with the states `spins.color`.

    Examples:
        >>> from skcomplex.models.physics.statistical_mechanics import potts
        >>> potts(8, q=4).interactions[0].q
        4
    """
    shape = _square(size)
    colors = np.random.default_rng(random_state).integers(0, q, size=shape).astype(float)
    space = LatticeSpace(shape)
    group = LatticeElement(
        'spins',
        element=PottsSpinElement,
        space=space,
        values={'color': colors, 'J': J, 'temperature': temperature},
    )
    return System(
        elements=[group],
        interactions=[PottsSamplingInteraction('sampling', target='spins', q=q, rule=sampler)],
    )

Components

class PottsSpinElement(Element):
    """Spin of the q-state Potts model, in a state from 0 to `q - 1`, with its coupling `J` and its temperature.

    A `PottsSamplingInteraction` redraws the states by Monte Carlo sweeps at the temperature of the spins.

    Args:
        label:
            The label of the spin or the lattice group.

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

        color:
            The initial state.

        J:
            The coupling between neighbouring spins.

        temperature:
            The temperature in units of the coupling.

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

    Examples:
        >>> from skcomplex.models.physics.statistical_mechanics import PottsSpinElement
        >>> PottsSpinElement('spin').temperature
        1.0
    """

    def __init__(
        self,
        label: str,
        visual: VisualSpec | None = None,
        *,
        color: Annotated[float, StateSpec(domain=NN)] = 0.0,
        J: Annotated[float, ParameterSpec()] = 1.0,
        temperature: Annotated[float, ParameterSpec(domain=RealSet.open(0, oo))] = 1.0,
    ) -> None:
        super().__init__(label, visual=visual)
        self.color = color
        self.J = J
        self.temperature = temperature

class PottsSamplingInteraction(SpinSamplingInteraction):
    """Monte Carlo sweeps of Potts spins in states `0` to `q - 1`, of local energy `-J` times the like neighbours.

    Every move proposes a state drawn from the `q` states. The coupling `J` and the temperature are parameters of the
    spins.

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

        target:
            The path of the group of spins.

        q:
            The number of states.

        state:
            The name of the state that holds the spin.

        rule:
            The acceptance, `'metropolis'`, `'glauber'`, or `'heat-bath'`.

        neighborhood:
            The neighbours of a spin, or `None` for the von Neumann neighbourhood.

    Examples:
        >>> from skcomplex.models.physics.common.spins import PottsSamplingInteraction
        >>> PottsSamplingInteraction('potts', target='spins', q=4).check().values
        (0.0, 1.0, 2.0, 3.0)
    """

    def __init__(
        self,
        label: str,
        *,
        target: Annotated[str, RoleSpec()],
        q: int = 3,
        state: str = 'color',
        rule: str = 'heat-bath',
        neighborhood: BaseNeighborhood | None = None,
    ) -> None:
        super().__init__(
            label,
            target=target,
            state=state,
            energy=self._energy,
            move='random-value',
            rule=rule,
            neighborhood=neighborhood,
            values=tuple(float(value) for value in range(q)),
        )
        self.q = q

    def _energy(self, namespace: SimpleNamespace) -> Expression:
        """Return the local energy `-J` times the number of neighbours in the state of the spin."""
        color, other = getattr(namespace.target, self.state), getattr(namespace.target.other, self.state)
        return -namespace.target.J * neighbors(where(other == color, 1, 0), over=self.neighborhood)