Source code for flatsurf.geometry.relative_homology

r"""
This module contains a lazy implementation of a relative homology,
$H_1(S,\Sigma; R)$, where $S$ is a similarity surface, $\Sigma$ is the singularities
or vertices, and $R$ is a ring.

This implementation works for finite or infinite surfaces. For infinite surfaces,
we define relative homology formally. It is simply $R^E$ where $E$ is the edge set
modulo equivalences of two types:
1) If $e$ is an edge, and $e'$ is its opposite edge oriented counterclockwise from the polygon they bound then $e+e'=0$ in homology.
2) The sum of edges around a polygon is zero.
"""
######################################################################
#  This file is part of sage-flatsurf.
#
#        Copyright (C) 2016-2019 Vincent Delecroix
#                           2023 Julian Rüth
#
#
#  sage-flatsurf is free software: you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation, either version 2 of the License, or
#  (at your option) any later version.
#
#  sage-flatsurf is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with sage-flatsurf. If not, see <https://www.gnu.org/licenses/>.
######################################################################

from sage.structure.element import ModuleElement
from sage.modules.module import Module
from sage.rings.integer_ring import ZZ


[docs]def cmp(x, y): r""" TESTS:: sage: from flatsurf.geometry.relative_homology import cmp sage: cmp(0, 1) -1 sage: cmp(0, 0) 0 sage: cmp(1, 0) 1 """ return bool(x > y) - bool(x < y)
[docs]class RelativeHomologyClass(ModuleElement): # Implementation notes: # self._d will be a dictionary mapping mapping pairs (label, edge) to the base_ring # By convention a pair will be in the dictionary only if its image is non-zero. def __init__(self, parent, d): r"""Do not call directly!""" # This should be a dict if not isinstance(d, dict): raise ValueError( "RelativeHomologyClass.__init__ must be passed a dictionary." ) self._d = d ModuleElement.__init__(self, parent=parent) def _rmul_(self, c): if c == self.parent().base_ring().zero(): return self.parent().zero() d = {} r = self.parent().base_ring() for k, v in self._d.items(): d[k] = r(c * v) return self.parent()._element_from_dict(d) def _add_(self, other): d = {} r = self.parent().base_ring() for k, v in self._d.items(): if k in other._d: total = v + other._d[k] if total != self.parent().base_ring().zero(): d[k] = r(total) else: d[k] = r(v) for k, v in other._d.items(): if k not in self._d: d[k] = r(v) return self.parent()._element_from_dict(d) def _neg_(self): return self._rmul_(-self.parent().base_ring().one()) def __cmp__(self, other): # Construct a set of keys s = set() for k, v in self._d.items(): s.add(k) for k, v in other._d.items(): s.add(k) zero = self.parent().base_ring().zero() for k in s: c = cmp(self._d.get(k, zero), other._d.get(k, zero)) if c != 0: return c return 0 def __hash__(self): return hash(self._d) def _repr_(self): return repr(self._d)
[docs] def weight(self, label, e): r"""Return the weight of the indexed edge.""" return self._d.get((label, e), self.parent().base_ring().zero())
[docs] def weighted_edges(self): r"""Return the set of pairs (label,e) representing edges with non-zero weights.""" return list(self._d.keys())
[docs] def edges_with_weights(self): r""" Returns a list of items of the form ((label,e),w) where (label,e) represents and edge and w represents the non-zero weight assigned.""" return self._d.items()
[docs]class RelativeHomology(Module): Element = RelativeHomologyClass def __init__(self, surface, base_ring=ZZ): self._base_ring = base_ring from flatsurf.geometry.categories import SimilaritySurfaces if surface not in SimilaritySurfaces(): raise ValueError( "RelativeHomology only defined for SimilaritySurfaces (and better)." ) self._s = surface self._cached_edges = {} Module.__init__(self, base_ring)
[docs] def base_ring(self): return self._base_ring
def _element_from_dict(self, d): return self.element_class(self, d) def _element_constructor_(self, x): if isinstance(x, RelativeHomologyClass): d = {} for k, v in x._d.items(): v2 = self._base_ring(v) if v2 != self._base_ring.zero(): d[k] = v2 return self.element_class(self, d)
[docs] def zero(self): return self.element_class(self, {})
def __cmp__(self, other): if not isinstance(other, RelativeHomology): return cmp(type(other), RelativeHomology) c = cmp(self.base_ring(), other.base_ring()) if c != 0: return c return cmp(self._s, other._s)
[docs] def edge(self, label, e): r"""Return the homology class of the edge with the provided polygon label and edge index oriented counter-clockwise around the polygon.""" try: # If already cached, return the cached copy. return self._cached_edges[(label, e)] except KeyError: # not cached! num_edges = len(self._s.polygon(label).vertices()) # Check to see if all other edges of the polygon are cached. has_all_others = True for i in range(1, num_edges): e2 = (e + i) % num_edges if (label, e2) not in self._cached_edges: has_all_others = False break if has_all_others: # If all the other edges in the polygon are cached then we # know this edge's homology class is the negation of their sum. e2 = (e + 1) % num_edges total = -self._cached_edges[(label, e2)] for i in range(2, num_edges): e2 = (e + i) % num_edges total -= self._cached_edges[(label, e2)] # Cache this edge's value and the opposite edge's value. self._cached_edges[(label, e)] = total label2, e2 = self._s.opposite_edge(label, e) self._cached_edges[(label2, e2)] = -total return total else: # At least one other edge is not cached, so we can think of # the current edge as a generator. d = {} d[(label, e)] = self._base_ring.one() v = self._element_from_dict(d) # Cache this edge's value and the opposite edge's value. self._cached_edges[(label, e)] = v label2, e2 = self._s.opposite_edge(label, e) self._cached_edges[(label2, e2)] = -v return v