Skip to content

Commit

Permalink
Python code: detect whether a geodesic is a multiple of a core curve.
Browse files Browse the repository at this point in the history
  • Loading branch information
unhyperbolic committed Apr 19, 2024
1 parent 85c53aa commit 4259c85
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 42 deletions.
8 changes: 7 additions & 1 deletion python/drilling/cusps.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@

from ..snap.t3mlite import Mcomplex, simplex

from . import exceptions

from typing import Tuple, Optional, Sequence

# @dataclass
Expand Down Expand Up @@ -66,11 +68,15 @@ def index_geodesics_and_add_post_drill_infos(

for i, g in enumerate(geodesics):
if g.core_curve_cusp:
if g.core_curve_multiplicity not in [-1, +1]:
raise exceptions.GeodesicMultipleOfCoreCurve(
g.word, g.core_curve_multiplicity)

g.core_curve_cusp.post_drill_info = CuspPostDrillInfo(
index=n + i,
peripheral_matrix=_multiply_filling_matrix(
g.core_curve_cusp.filling_matrix,
g.core_curve_direction))
g.core_curve_multiplicity))
else:
g.index = n + i

Expand Down
7 changes: 7 additions & 0 deletions python/drilling/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,13 @@ def __init__(self, maximal_tube_radius):
"The maximal tube radius about the given system of geodesics "
"was estimated to be: %r." % maximal_tube_radius)

class GeodesicMultipleOfCoreCurve(DrillGeodesicError):
def __init__(self, word, multiplicity):
self.word = word
self.multiplicity = multiplicity
super().__init__(
"The geodesic %s is a %d-fold multiple of a core curve." % (
word, multiplicity))

class UnfinishedTraceGeodesicError(DrillGeodesicError):
def __init__(self, steps):
Expand Down
62 changes: 21 additions & 41 deletions python/geometric_structure/geodesic/geodesic_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from .line import R13LineWithMatrix
from .fixed_points import r13_fixed_line_of_psl2c_matrix
from .multiplicity import compute_and_verify_multiplicity

from .. import word_to_psl2c_matrix

Expand Down Expand Up @@ -68,7 +69,7 @@ class GeodesicInfo:
will either:
1. Detect that the closed geodesic is actually a core curve of a
filled cusp and set core_curve_cusp and core_curve_direction
filled cusp and set core_curve_cusp and core_curve_multiplicity
accordingly. This means that instead tracing the geodesic
through the triangulation, the client has to unfill the
corresponding cusp instead.
Expand Down Expand Up @@ -140,7 +141,7 @@ def __init__(self,
# Output of find_tet_or_core_corve: sign (+1/-1) indicating whether
# the given geodesic and the core curve run parallel or
# anti-parallel.
core_curve_direction : int = 0,
core_curve_multiplicity : Optional[int] = None,

# Field filled by client to indicate which index the cusp resulting
# from drilling this geodesic is supposed to have.
Expand All @@ -155,7 +156,7 @@ def __init__(self,
self.tet = tet
self.lifted_tetrahedra = lifted_tetrahedra
self.core_curve_cusp = core_curve_cusp
self.core_curve_direction = core_curve_direction
self.core_curve_multiplicity = core_curve_multiplicity
self.index = index

def find_tet_or_core_curve(self) -> None:
Expand All @@ -173,7 +174,7 @@ def find_tet_or_core_curve(self) -> None:
self.tet = None
self.lifted_tetrahedra = ()
self.core_curve_cusp = None
self.core_curve_direction = 0
self.core_curve_multiplicity = None

# Walks from tetrahedron to tetrahedron (transforming the start point
# and other data) trying to get the start_point closer and closer to
Expand All @@ -197,7 +198,7 @@ def find_tet_or_core_curve(self) -> None:
# Verify that the the geodesic is really the core curve and
# determine whether the geodesic and core curve or parallel
# or anti-parallel.
self.core_curve_direction = self._verify_direction_of_core_curve(
self.core_curve_multiplicity = self._multiplicity_of_core_curve(
tet, cusp_curve_vertex)
self.core_curve_cusp = tet.Class[cusp_curve_vertex]
return
Expand Down Expand Up @@ -420,50 +421,29 @@ def _find_cusp_if_core_curve(

return None

def _verify_direction_of_core_curve(self,
tet : Tetrahedron,
vertex : int) -> int:
def _multiplicity_of_core_curve(self,
tet : Tetrahedron,
vertex : int) -> int:
"""
Verify that geodesic and core curve are indeed the same and
return sign indicating whether they are parallel or anti-parallel.
Verify that geodesic is indeed a multiple of the core curve (including
sign).
"""

if self.line is None:
raise Exception(
"There is a bug in the code: it is trying to verify that "
"geodesic is a core curve without being given a line.")

# We do this by checking whether the corresponding
# Decktransformations corresponding to each are the same.

# To check whether two Decktransformations are the same, we let each
# act on the basepoint (which is the incenter of the base tetrahedron)
# and compute the distance between the two images.
# We know that the ball about the basepoint with radius the inradius
# of the base tetrahedron injects into the manifold. Thus,
# the distance between the two images is either 0 or larger than
# two times the inradius.
# Hence, if we can prove the distance to be less than the inradius,
# we know that the two Decktransformations are the same.

# Decktransformation corresponding geodesic acting on basepoint
a = self.line.o13_matrix * self.mcomplex.R13_baseTetInCenter

# Decktransformation corresponding to core curve acting on basepoint
m = tet.core_curves[vertex].o13_matrix
b0 = m * self.mcomplex.R13_baseTetInCenter
if distance_r13_points(a, b0) < self.mcomplex.baseTetInRadius:
return +1

# Decktransformation corresponding to core curve with opposite
# orientation acting on basepoint.
b1 = o13_inverse(m) * self.mcomplex.R13_baseTetInCenter
if distance_r13_points(a, b1) < self.mcomplex.baseTetInRadius:
return -1

raise InsufficientPrecisionError(
"Geodesic is very close to a core curve but could not verify it is "
"the core curve. Increasing the precision will probably fix this.")
try:
return compute_and_verify_multiplicity(
tet.core_curves[vertex],
self.line,
self.mcomplex)
except InsufficientPrecisionError:
raise InsufficientPrecisionError(
"Geodesic is very close to a core curve but could not verify "
"it is the core curve. Increasing the precision will probably "
"fix this.")

def compute_geodesic_info(mcomplex : Mcomplex,
word) -> GeodesicInfo:
Expand Down
127 changes: 127 additions & 0 deletions python/geometric_structure/geodesic/multiplicity.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
from .line import R13LineWithMatrix
from ...hyperboloid.distances import distance_r13_points
from ...hyperboloid import o13_inverse
from ...exceptions import InsufficientPrecisionError

def compute_and_verify_multiplicity(line : R13LineWithMatrix,
line_power : R13LineWithMatrix,
mcomplex):
"""
Assume that line and line_power are created from matrices from the geometric
representation.
Verify that the matrix of line_power is a signed multiple of the matrix
of line and return the multiple.
"""

# First use real length to compute multiple up to sign.
multiplicity = _compute_absolute_multiplicity(
line.complex_length.real(),
line_power.complex_length.real(),
mcomplex.verified)

sign = _determine_and_verify_sign(
multiplicity,
line.o13_matrix,
line_power.o13_matrix,
mcomplex)

return sign * multiplicity

_epsilon = 0.001
_max_multiple = 500

def _compute_absolute_multiplicity(
length,
length_multiple,
verified):

r = length_multiple / length

if verified:
is_int, r_int = r.is_int()
if not is_int:
raise InsufficientPrecisionError(
"When verifying that a geodesic is a multiple of another "
"geodesic, the interval for the multiplicity does not contain "
"a unique integer.")
else:
r_int = r.round()
if abs(r_int - r) > _epsilon:
raise InsufficientPrecisionError(
"When verifying that a geodesic is a multiple of another "
"geodesic, the floating point approximation for the "
"multiplicity is too far off an integer.")
r_int = int(r_int)

if not r_int > 0:
if verified:
raise RuntimeError(
"When verifying that a geodesic is a multiple of another "
"geodesic, we got zero for multiplicity. This is a bug.")
else:
raise InsufficientPrecisionError(
"When verifying that a geodesic is a multiple of another "
"geodesic, we got zero for multiplicity. Increasing the "
"precision might help.")
if not r_int < _max_multiple:
raise RuntimeError(
"When verifying that a geodesic is a multiple of another "
"geodesic, we got a multiple (%d) higher than what we support "
"(%d)" % (r_int, _max_multiple))

return r_int

def _determine_and_verify_sign(multiplicity, m, m_power, mcomplex):
"""
Given matrices m and m_power coming from the geometric representation,
verify that either m^multiplicity = m_power^sign for either sign = +1
or sign = -1. Return sign.
"""

base_pt = mcomplex.R13_baseTetInCenter

# Compute image of base point under m^multiplicity.
pt = base_pt
for i in range(multiplicity):
pt = m * pt

# Check whether it is equal to image under m_power
if _are_images_of_basepoints_equal(
pt, m_power * base_pt, mcomplex):
return +1

# Check whether it is equal to image under m_power^-1
if _are_images_of_basepoints_equal(
pt, o13_inverse(m_power) * base_pt, mcomplex):
return -1

raise RuntimeError(
"Given geodesic is not a multiple of other given geodesic.")

def _are_images_of_basepoints_equal(pt0, pt1, mcomplex):
"""
Given two images of the base point under Deck transformations,
check whether they are the same (and thus correspond to the same
Deck transformation).
"""

# We use that the the base point is the incenter of the base
# tetrahedron. Thus, we know that if the minimum distance for
# them to be distinct is the in-radius of the base tetrahedron.
#
# We add in a factor of 1/2 for safety.

d = distance_r13_points(pt0, pt1)
if d < mcomplex.baseTetInRadius / 2:
return True
if d > mcomplex.baseTetInRadius / 2:
return False

raise InsufficientPrecisionError(
"When determining whether a geodesic is a multiple of another "
"geodesic, we could not verify that the images of the base point "
"under the corresponding matrices are the same or not.\n"
"Distance between basepoints: %r\n"
"Cut-off: %r" % (d, mcomplex.baseTetInRadius / 2))

0 comments on commit 4259c85

Please sign in to comment.