from sage.structure.sage_object import SageObject
[docs]
class FlowPolygonMap(SageObject):
r"""
The map obtained as the return map of the flow on the sides of a (convex)
polygon.
Formally, this can be defined as follows: one start with two partition into
(finitely many) intervals of a given interval. The map corresponds to
changing from the first partition to the second. In other words, points are
identified as pairs ``(i, x)`` where ``i`` is an atom and ``x`` is the
relative position in this atom.
Contrarily to an interval exchange transformation, things here are going
from bottom to top.
Note that this could also be used for homothetic surfaces. And to some
extent to half translation surface.
EXAMPLES::
sage: from flatsurf.geometry.interval_exchange_transformation import FlowPolygonMap
sage: T = FlowPolygonMap(QQ, [0,1,2], [2,3,1], [2,1,0], [1,3,2])
sage: [T.forward_image(0,x) for x in range(3)]
[(2, 0), (1, 0), (1, 1)]
sage: [T.forward_image(1,x) for x in range(4)]
[(1, 1), (1, 2), (0, 0), (0, 1)]
sage: [T.forward_image(2,x) for x in range(1)]
[(0, 1)]
"""
def __init__(self, ring, bot_labels, bot_lengths, top_labels, top_lengths):
r"""
INPUT:
- ``ring`` -- the base ring for the lengths of the interval
- ``bot_labels`` -- labels for the bottom partition
- ``bot_lengths`` -- lengths for the bottom partition
- ``top_labels`` -- labels for the top partition
- ``top_lengths`` -- lengths for top partition
"""
if not all(x > ring.zero() for x in bot_lengths):
raise ValueError
if not all(x > ring.zero() for x in top_lengths):
raise ValueError
if len(bot_labels) != len(bot_lengths):
raise ValueError
if len(top_labels) != len(top_lengths):
raise ValueError
if sum(top_lengths) != sum(bot_lengths):
raise ValueError
self._ring = ring
self._bot_labels = bot_labels
self._top_labels = top_labels
self._bot_labels_to_index = {j: i for i, j in enumerate(bot_labels)}
self._top_labels_to_index = {j: i for i, j in enumerate(top_labels)}
if len(self._bot_labels) != len(self._bot_labels_to_index):
raise ValueError("non unique labels for bot: {}".format(bot_labels))
if len(self._top_labels) != len(self._top_labels_to_index):
raise ValueError("non unique labels in top: {}".format(top_labels))
self._bot_lengths = list(map(ring, bot_lengths))
self._top_lengths = list(map(ring, top_lengths))
# forward image of intervals
it = 0
lt = x1 = top_lengths[it]
self._forward_images = []
for ib in range(len(bot_lengths)):
lenb = bot_lengths[ib]
self._forward_images.append((it, lt - x1))
while lenb and lenb >= x1:
lenb -= x1
it += 1
if it < len(top_lengths):
lt = x1 = top_lengths[it]
else:
lt = ring.zero()
if lenb:
x1 -= lenb
# backward image of intervals
ib = 0
lb = x1 = bot_lengths[ib]
self._backward_images = []
for it in range(len(top_lengths)):
lent = top_lengths[it]
self._backward_images.append((ib, lb - x1))
while lent and lent >= x1:
lent -= x1
ib += 1
if ib < len(bot_lengths):
lb = x1 = bot_lengths[ib]
else:
lb = ring.zero()
if lent:
x1 -= lent
[docs]
def length_bot(self, i):
i = self._bot_labels_to_index[i]
return self._bot_lengths[i]
[docs]
def length_top(self, i):
i = self._top_labels_to_index[i]
return self._top_lengths[i]
def _repr_(self):
s = ["Flow polygon map:"]
s.append(" " + " ".join(str(x) for x in self._top_labels))
s.append(" " + " ".join(str(x) for x in self._bot_labels))
s.append("top lengths: {}".format(self._top_lengths))
s.append("bot lengths: {}".format(self._bot_lengths))
return "\n".join(s)
[docs]
def forward_image(self, i, x):
r"""
Return the forward image.
EXAMPLES::
sage: from flatsurf.geometry.interval_exchange_transformation import FlowPolygonMap
Singularities are always sent to a ``(i,0)``::
sage: T = FlowPolygonMap(QQ, [0,1], [2,1], [2,3,4], [1,1,1])
sage: T.forward_image(0, 0)
(2, 0)
sage: T.forward_image(0, 1) # could have equally been (2, 1)
(3, 0)
"""
i = self._bot_labels_to_index[i]
if x < self._ring.zero() or x > self._bot_lengths[i]:
raise ValueError("x = {} is out of the interval".format(x))
j, y = self._forward_images[i]
if x + y < self._top_lengths[j]:
return (self._top_labels[j], x + y)
x -= self._top_lengths[j] - y
j += 1
while x > self._top_lengths[j]:
x -= self._top_lengths[j]
j += 1
return (self._top_labels[j], x)
[docs]
def backward_image(self, i, x):
r"""
EXAMPLES::
sage: from flatsurf.geometry.interval_exchange_transformation import \
....: FlowPolygonMap
sage: x = polygen(ZZ)
sage: K.<sqrt2> = NumberField(x^2 - 2, embedding=AA(2).sqrt())
sage: T = FlowPolygonMap(K, [0,1,2],
....: [sqrt2,1+sqrt2,1], [2,0,1], [1,sqrt2,1+sqrt2])
sage: T.backward_image(*T.forward_image(1, 1))
(1, 1)
sage: T.backward_image(*T.forward_image(0, 1))
(0, 1)
sage: T.backward_image(*T.forward_image(0, sqrt2-1))
(0, sqrt2 - 1)
sage: T.backward_image(*T.forward_image(1, sqrt2-1))
(1, sqrt2 - 1)
Singularities are always sent to a ``(i,0)``::
sage: T = FlowPolygonMap(QQ, [2,3,4], [1,1,1], [0,1], [2,1])
sage: T.backward_image(0, 0)
(2, 0)
sage: T.backward_image(0, 1) # could have equally been (2, 1)
(3, 0)
TESTS::
sage: T = FlowPolygonMap(K, [0,1,2],
....: [5*sqrt2,1,1], [2,1,0], [1,1,5*sqrt2])
sage: for x in range(1,8):
....: p0 = (0,x)
....: for n in range(5):
....: p1 = T.forward_image(*p0)
....: assert T.backward_image(*p1) == p0, "p0 = {}, p1 = {}".format(p0,p1)
....: p0 = p1
"""
i = self._top_labels_to_index[i]
if x < self._ring.zero() or x > self._top_lengths[i]:
raise ValueError("x = {} is out of the interval".format(x))
j, y = self._backward_images[i]
if x + y < self._bot_lengths[j]:
return (self._bot_labels[j], x + y)
x -= self._bot_lengths[j] - y
j += 1
while x > self._bot_lengths[j]:
x -= self._bot_lengths[j]
j += 1
return (self._bot_labels[j], x)