Source code for pocketpartition.core.kunz._polyhedron

"""
KunzPolyhedron — membership testing for the Kunz polyhedral cone P(m).
"""

__all__ = ['KunzPolyhedron']


[docs] class KunzPolyhedron: """ The Kunz polyhedron for a given multiplicity m. The Kunz polyhedron ``P(m)`` is the rational polyhedral cone in ``R^{m-1}`` defined by the inequalities: - ``c_i + c_j >= c_{i+j}`` for all ``1 <= i <= j <= m-1`` with ``i+j < m`` - ``c_i + c_j + 1 >= c_{i+j-m}`` for all ``1 <= i <= j <= m-1`` with ``i+j > m`` Every numerical semigroup of multiplicity m corresponds to an integer lattice point inside ``P(m)``, and conversely every such lattice point with all positive coordinates determines a numerical semigroup. Parameters ---------- m : int The multiplicity. Must be a positive integer. Attributes ---------- m : int The multiplicity this polyhedron was built for. corner : tuple of float The ``m`` equally-spaced points ``(0, 1/m, 2/m, ..., (m-1)/m)``, representing the origin corner of the polyhedron in the normalised coordinate chart. Examples -------- >>> from pocketpartition.core.kunz import KunzPolyhedron, kunz_tuple >>> from pocketpartition import NumericalSemigroup >>> kp = KunzPolyhedron(3) >>> S = NumericalSemigroup(generators=[3, 4, 5]) >>> kp.is_point(kunz_tuple(S)) True """ def __init__(self, m: int): """ Initialise the Kunz polyhedron for multiplicity m. Parameters ---------- m : int Must be a positive integer. Raises ------ TypeError If ``m`` is not an integer. ValueError If ``m`` is not a positive integer. """ if not isinstance(m, int): raise TypeError(f"m must be an integer, got {type(m).__name__}.") if m <= 0: raise ValueError("m must be a positive integer.") self.m = m self.corner = tuple([i / m for i in range(m)])
[docs] def is_point(self, p: tuple) -> bool: """ Check whether a coordinate vector lies inside the Kunz polyhedron. Parameters ---------- p : tuple of int or float A vector of length ``m - 1``. Entry ``p[k]`` represents the coordinate ``c_{k+1}`` (1-indexed residue ``k+1`` mod ``m``). Returns ------- bool ``True`` if ``p`` satisfies all Kunz polyhedron inequalities, ``False`` otherwise. Notes ----- The inequalities checked are, for all ``1 <= i <= j <= m-1``: - ``i + j < m`` → ``c_i + c_j >= c_{i+j}`` - ``i + j > m`` → ``c_i + c_j + 1 >= c_{i+j-m}`` - ``i + j == m`` → always satisfied for non-negative coordinates Raises ------ ValueError If ``p`` does not have length ``m - 1``. Examples -------- >>> kp = KunzPolyhedron(3) >>> kp.is_point((1, 1)) # Kunz tuple of <3, 4, 5> True >>> kp.is_point((-1, 1)) # negative coordinate False """ # p has length m-1, representing c_1 ... c_{m-1} (1-indexed residues). # p[k] corresponds to c_{k+1}. m = self.m if len(p) != m - 1: raise ValueError( f"p must have length m-1 = {m - 1}, got {len(p)}." ) if any(x < 0 for x in p): return False # residues are 1-indexed; i and j run from 1 to m-1 for i in range(1, m): for j in range(i, m): ci = p[i - 1] cj = p[j - 1] s = i + j if s < m: if ci + cj < p[s - 1]: return False elif s > m: if ci + cj + 1 < p[s - m - 1]: return False # s == m: ci + cj + 1 >= 1 is always satisfied for non-negative values return True