Lattice Maxwell and gauge link fields
Link variables on the cell complex of a periodic lattice, sampled or evolved in real time.
- Name:
lattice_gauge_fields - Field:
physics/waves - Formalism: Monte Carlo sampling
- References: Schwichtenberg 2020; Wilson 1974
Select this system
from skcomplex.systems import create_simulation, create_system, describe
system = create_system('lattice_gauge_fields')
entry = describe('lattice_gauge_fields')
trajectory = create_simulation('lattice_gauge_fields').prepare().run().trajectory_
Factory
@register_system(
field='physics/waves',
tags=('gauge', 'lattice', 'monte carlo', 'maxwell', 'cell complex'),
references=('Schwichtenberg 2020', 'Wilson 1974'),
defaults={'until': 5000, 'dt': 1},
settings=_gauge_settings,
observables=_gauge_observables,
)
def lattice_gauge_fields(
size: int = 32,
*,
dimension: int = 2,
n: int | None = None,
beta: float = 2.0,
dynamics: str = 'monte-carlo',
charges: Sequence[float] | None = None,
angles: Sequence[float] | None = None,
amplitude: float = 0.1,
seed: int = 0,
) -> System:
"""Return link variables on the cell complex of a periodic lattice, sampled or evolved in real time.
With Monte Carlo dynamics, link angles sample the Wilson action `-beta sum_p cos(theta_p)` under discrete time, as
U(1) angles or as the angles `2 pi k / n` of Z_n. With Hamiltonian dynamics, a potential on the links follows the
lattice Maxwell equations `A_t = -E` and `E_t = codifferential(dA)` under continuous time, from static charges on
the sites and a transverse wave. In two dimensions the U(1) mean plaquette at large size is `I_1(beta) / I_0(beta)`.
Args:
size:
The number of sites along every axis.
dimension:
The number of axes of the lattice.
n:
The order of the Z_n group, or `None` for U(1).
beta:
The inverse coupling of the Wilson action.
dynamics:
`'monte-carlo'` or `'hamiltonian'`.
charges:
The static charge of every site, of total 0, or `None` for none, with Hamiltonian dynamics.
angles:
The initial link angles, or `None` for random angles, with Monte Carlo dynamics.
amplitude:
The amplitude of the initial transverse wave, with Hamiltonian dynamics.
seed:
The seed of the initial link angles.
Returns:
system:
The system.
Raises:
ValidationError:
The dynamics are unknown, or the charges do not hold one value per site of total 0.
Examples:
>>> from skcomplex.systems.physics.waves import lattice_gauge_fields
>>> [element.label for element in lattice_gauge_fields(4).elements]
['links']
>>> [element.label for element in lattice_gauge_fields(4, dynamics='hamiltonian').elements]
['links', 'charges']
"""
if dynamics not in DYNAMICS:
error_msg = f'Unknown dynamics {dynamics!r}. Use one of {", ".join(DYNAMICS)}.'
raise ValidationError(error_msg)
space = CellComplexSpace(LatticeSpace((size,) * dimension)).prepare()
if dynamics == 'hamiltonian':
return _maxwell_system(space, charges, amplitude)
return _monte_carlo_system(space, n, beta, angles, seed)
Components
Elements:
GaugeLinkElement, fromElement
Interactions:
WilsonSamplingInteraction, fromSamplingInteraction
class GaugeLinkElement(Element):
"""Link of a lattice with an angle `theta`, the U(1) or Z_N variable of a lattice gauge theory.
Args:
label:
The label of the link or the group on the links.
visual:
How the link is drawn, or `None` for `default_visual_`.
theta:
The angle of the link.
Attributes:
default_visual_:
The visual of the link when it sets none.
Examples:
>>> from skcomplex.systems.physics.waves import GaugeLinkElement
>>> GaugeLinkElement('link', theta=0.5).theta
0.5
"""
def __init__(
self,
label: str,
visual: VisualSpec | None = None,
*,
theta: Annotated[float, StateSpec(period=2 * pi)] = 0.0,
) -> None:
super().__init__(label, visual=visual)
self.theta = theta
class WilsonSamplingInteraction(SamplingInteraction):
"""Monte Carlo sampling of link variables with the local Wilson action of the plaquettes of a link.
For link angles the action is `-beta sum_p cos(theta_p)`, where `theta_p` is the oriented sum of the link angles
around the plaquette `p`. For the matrices of a group `SU(N)` it is `-(beta / N) sum_p Re tr U_p`, where `U_p` is
the path-ordered product of the link matrices around `p`, and the move `'rotate'` multiplies a link by a random
matrix of the group near the identity of spread `width`.
Args:
label:
The label of the interaction, unique among the parts of its system.
target:
The path of the group on the edges of a cell complex.
beta:
The inverse coupling of the Wilson action.
state:
The name of the state that holds the link angles.
move:
`'rotate'` for U(1) or a matrix group, or `'random-value'` for Z_N.
values:
The angles a random-value move draws from.
width:
The largest change of a rotation.
rule:
The acceptance, `'metropolis'`, `'glauber'`, or `'heat-bath'`.
schedule:
`'colored'` or `'random-sequential'`.
group:
The matrix group of the links, or `None` for link angles.
hits:
The number of moves proposed per link and sweep of matrix links.
Examples:
>>> from skcomplex.systems.physics.common.fields import WilsonSamplingInteraction
>>> WilsonSamplingInteraction('wilson', target='links', beta=2.0).beta
2.0
>>> from skcomplex.core.specs import MatrixGroupSpec
>>> color = WilsonSamplingInteraction('wilson', target='links', state='U', group=MatrixGroupSpec('SU', 3))
>>> color.objective.group.degree
3
"""
def __init__(
self,
label: str,
*,
target: Annotated[str, RoleSpec()],
beta: Annotated[float, ParameterSpec(domain=RealSet.closed_open(0, oo))] = 2.0,
state: str = 'theta',
move: str = 'rotate',
values: Sequence[float] = (),
width: float = 1.5,
rule: str = 'metropolis',
schedule: str = 'colored',
group: MatrixGroupSpec | None = None,
hits: int = 1,
) -> None:
super().__init__(
label,
target=target,
state=state,
objective=self._get_action if group is None else HolonomyScoreSpec(self._get_trace_action, group, hits),
move=move,
rule=rule,
schedule=schedule,
values=values,
width=width,
)
self.beta = beta
self.group = group
self.hits = hits
def _get_action(self, namespace: SimpleNamespace) -> Expression:
"""Return the terms of the Wilson action that hold the angle of a link."""
theta = getattr(namespace.target, self.state)
return -namespace.beta * neighbors(cos(exterior_derivative(theta)), over=CofaceNeighborhood())
def _get_trace_action(self, holonomies: ComplexArray, beta: float) -> Array:
"""Return the Wilson action `-(beta / N) Re tr U_p` of the holonomies of the plaquettes."""
degree = holonomies.shape[-1]
return -beta / degree * np.trace(holonomies, axis1=-2, axis2=-1).real