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)