Source code for bigger.encoding
""" A module for representing and manipulating maps between Triangulations. """
from __future__ import annotations
from typing import Callable, Generic, Iterator, List, overload
import bigger
from bigger.types import Edge
[docs]class Move(Generic[Edge]):
"""A function that takes :class:`Laminations <bigger.lamination.Lamination>` on one :class:`~bigger.triangulation.Triangulation` to another."""
def __init__(
self,
source: bigger.Triangulation[Edge],
target: bigger.Triangulation[Edge],
action: Callable[[bigger.Lamination[Edge]], bigger.Lamination[Edge]],
inv_action: Callable[[bigger.Lamination[Edge]], bigger.Lamination[Edge]],
) -> None:
self.source = source
self.target = target
self.action = action
self.inv_action = inv_action
def __invert__(self) -> bigger.Move[Edge]:
return Move(self.target, self.source, self.inv_action, self.action)
def __call__(self, lamination: bigger.Lamination[Edge]) -> bigger.Lamination[Edge]:
if lamination.is_finitely_supported() and not lamination: # Optimisation for empty laminations.
return self.target.empty_lamination()
return self.action(lamination)
[docs] def encode(self) -> bigger.Encoding[Edge]:
"""Return the :class:`~bigger.encoding.Encoding` consisting of only this Move."""
return bigger.Encoding([self])
[docs]class Encoding(Generic[Edge]):
"""A sequence of :class:`Moves <bigger.encoding.Move>` to apply to a :class:`~bigger.lamination.Lamination`."""
def __init__(self, sequence: List[bigger.Move[Edge]]) -> None:
self.sequence = sequence
self.source = self.sequence[-1].source
self.target = self.sequence[0].target
def __iter__(self) -> Iterator[bigger.Move[Edge]]:
# Iterate through self.sequence in application order (i.e. reverse).
return iter(reversed(self.sequence))
def __mul__(self, other: bigger.Encoding[Edge]) -> bigger.Encoding[Edge]:
return Encoding(self.sequence + other.sequence)
def __invert__(self) -> bigger.Encoding[Edge]:
return Encoding([~move for move in self])
def __len__(self) -> int:
return len(self.sequence)
@overload
def __getitem__(self, index: slice) -> bigger.Encoding[Edge]:
...
@overload
def __getitem__(self, index: int) -> bigger.Move[Edge]:
...
def __getitem__(self, index: slice | int) -> bigger.Encoding[Edge] | bigger.Move[Edge]:
if isinstance(index, slice):
start = 0 if index.start is None else index.start % len(self)
stop = len(self) if index.stop is None else index.stop % len(self)
if start == stop:
if 0 <= start < len(self):
return self.sequence[start].target.identity()
elif start == len(self):
return self.source.identity()
else:
raise IndexError("list index out of range")
elif stop < start:
raise IndexError("list index out of range")
else: # start < stop.
return Encoding(self.sequence[index])
else:
return self.sequence[index]
def __call__(self, lamination: bigger.Lamination[Edge]) -> bigger.Lamination[Edge]:
if lamination.is_finitely_supported() and not lamination: # Optimisation for empty laminations.
return self.target.empty_lamination()
for move in self:
lamination = move(lamination)
return lamination
def __pow__(self, power: int) -> bigger.Encoding[Edge]:
if power == 0:
return self.source.identity()
abs_power = Encoding(self.sequence * abs(power))
return abs_power if power > 0 else ~abs_power
[docs] def conjugate_by(self, other: Encoding[Edge]) -> Encoding[Edge]:
"""Return this Encoding conjugated by other."""
return ~other * self * other