How to draw the picture of a closed horocycle in $textSL_2(mathbbZ) backslash mathbbH$?

The name of the pictureThe name of the pictureThe name of the pictureClash Royale CLAN TAG#URR8PPP











up vote
0
down vote

favorite












I am trying to draw the image of the path $[0,1] + frac17i in mathbbH$ under the image of $textSL_2(mathbbZ)$.



enter image description here



This is the example of a horocycle - the image of a horocycle under the $textSL_2(mathbbZ)$ group action. Here is the code I used to generate the points (in numPy):



N = 7

x = np.arange(0,1,0.0001)
z = 1.0j/N + x


for t in range(10):
z = ( (z.real % 1) - 0.5 ) + z.imag*1j
z = (np.abs(z) < 1)*(-1.0/z) + (np.abs(z) > 1 )*z


This seems pretty consistent with the algorithm given by William Stein.




Do the following until $z$ is in $mathcalF$:



  1. Replace $z$ by $z+n$ where $nin mathbbZ$ is an
    integer such that $|textRe(z+n)| leq frac12$.


  2. If $|z|<1$, replace $z$ by $-1/z$.








share|cite|improve this question






















  • What exactly is your question?
    – MvG
    Aug 23 at 18:42










  • @MvG how to debug the numpy code. it's mathematics so i put it here
    – cactus314
    Aug 24 at 0:49










  • I don't see how to connect the $[0,1]+tfrac17i$ from your question with the $[-10,10]+sqrt3+tfrac17i$ I read in your NumPy code. I also wouldn't call this a horocycle, but only part of a horocycle. Is this distinction relevant to your question? I can see how the code corresponds to the citation, and the resulting picture looks plausible at first glance, so what do you want to debug? What is it you want to achieve that you don't already have? What is currently wrong and you want to make it right?
    – MvG
    Aug 24 at 21:10















up vote
0
down vote

favorite












I am trying to draw the image of the path $[0,1] + frac17i in mathbbH$ under the image of $textSL_2(mathbbZ)$.



enter image description here



This is the example of a horocycle - the image of a horocycle under the $textSL_2(mathbbZ)$ group action. Here is the code I used to generate the points (in numPy):



N = 7

x = np.arange(0,1,0.0001)
z = 1.0j/N + x


for t in range(10):
z = ( (z.real % 1) - 0.5 ) + z.imag*1j
z = (np.abs(z) < 1)*(-1.0/z) + (np.abs(z) > 1 )*z


This seems pretty consistent with the algorithm given by William Stein.




Do the following until $z$ is in $mathcalF$:



  1. Replace $z$ by $z+n$ where $nin mathbbZ$ is an
    integer such that $|textRe(z+n)| leq frac12$.


  2. If $|z|<1$, replace $z$ by $-1/z$.








share|cite|improve this question






















  • What exactly is your question?
    – MvG
    Aug 23 at 18:42










  • @MvG how to debug the numpy code. it's mathematics so i put it here
    – cactus314
    Aug 24 at 0:49










  • I don't see how to connect the $[0,1]+tfrac17i$ from your question with the $[-10,10]+sqrt3+tfrac17i$ I read in your NumPy code. I also wouldn't call this a horocycle, but only part of a horocycle. Is this distinction relevant to your question? I can see how the code corresponds to the citation, and the resulting picture looks plausible at first glance, so what do you want to debug? What is it you want to achieve that you don't already have? What is currently wrong and you want to make it right?
    – MvG
    Aug 24 at 21:10













up vote
0
down vote

favorite









up vote
0
down vote

favorite











I am trying to draw the image of the path $[0,1] + frac17i in mathbbH$ under the image of $textSL_2(mathbbZ)$.



enter image description here



This is the example of a horocycle - the image of a horocycle under the $textSL_2(mathbbZ)$ group action. Here is the code I used to generate the points (in numPy):



N = 7

x = np.arange(0,1,0.0001)
z = 1.0j/N + x


for t in range(10):
z = ( (z.real % 1) - 0.5 ) + z.imag*1j
z = (np.abs(z) < 1)*(-1.0/z) + (np.abs(z) > 1 )*z


This seems pretty consistent with the algorithm given by William Stein.




Do the following until $z$ is in $mathcalF$:



  1. Replace $z$ by $z+n$ where $nin mathbbZ$ is an
    integer such that $|textRe(z+n)| leq frac12$.


  2. If $|z|<1$, replace $z$ by $-1/z$.








share|cite|improve this question














I am trying to draw the image of the path $[0,1] + frac17i in mathbbH$ under the image of $textSL_2(mathbbZ)$.



enter image description here



This is the example of a horocycle - the image of a horocycle under the $textSL_2(mathbbZ)$ group action. Here is the code I used to generate the points (in numPy):



N = 7

x = np.arange(0,1,0.0001)
z = 1.0j/N + x


for t in range(10):
z = ( (z.real % 1) - 0.5 ) + z.imag*1j
z = (np.abs(z) < 1)*(-1.0/z) + (np.abs(z) > 1 )*z


This seems pretty consistent with the algorithm given by William Stein.




Do the following until $z$ is in $mathcalF$:



  1. Replace $z$ by $z+n$ where $nin mathbbZ$ is an
    integer such that $|textRe(z+n)| leq frac12$.


  2. If $|z|<1$, replace $z$ by $-1/z$.










share|cite|improve this question













share|cite|improve this question




share|cite|improve this question








edited Aug 25 at 0:42

























asked Aug 23 at 10:09









cactus314

15.1k41861




15.1k41861











  • What exactly is your question?
    – MvG
    Aug 23 at 18:42










  • @MvG how to debug the numpy code. it's mathematics so i put it here
    – cactus314
    Aug 24 at 0:49










  • I don't see how to connect the $[0,1]+tfrac17i$ from your question with the $[-10,10]+sqrt3+tfrac17i$ I read in your NumPy code. I also wouldn't call this a horocycle, but only part of a horocycle. Is this distinction relevant to your question? I can see how the code corresponds to the citation, and the resulting picture looks plausible at first glance, so what do you want to debug? What is it you want to achieve that you don't already have? What is currently wrong and you want to make it right?
    – MvG
    Aug 24 at 21:10

















  • What exactly is your question?
    – MvG
    Aug 23 at 18:42










  • @MvG how to debug the numpy code. it's mathematics so i put it here
    – cactus314
    Aug 24 at 0:49










  • I don't see how to connect the $[0,1]+tfrac17i$ from your question with the $[-10,10]+sqrt3+tfrac17i$ I read in your NumPy code. I also wouldn't call this a horocycle, but only part of a horocycle. Is this distinction relevant to your question? I can see how the code corresponds to the citation, and the resulting picture looks plausible at first glance, so what do you want to debug? What is it you want to achieve that you don't already have? What is currently wrong and you want to make it right?
    – MvG
    Aug 24 at 21:10
















What exactly is your question?
– MvG
Aug 23 at 18:42




What exactly is your question?
– MvG
Aug 23 at 18:42












@MvG how to debug the numpy code. it's mathematics so i put it here
– cactus314
Aug 24 at 0:49




@MvG how to debug the numpy code. it's mathematics so i put it here
– cactus314
Aug 24 at 0:49












I don't see how to connect the $[0,1]+tfrac17i$ from your question with the $[-10,10]+sqrt3+tfrac17i$ I read in your NumPy code. I also wouldn't call this a horocycle, but only part of a horocycle. Is this distinction relevant to your question? I can see how the code corresponds to the citation, and the resulting picture looks plausible at first glance, so what do you want to debug? What is it you want to achieve that you don't already have? What is currently wrong and you want to make it right?
– MvG
Aug 24 at 21:10





I don't see how to connect the $[0,1]+tfrac17i$ from your question with the $[-10,10]+sqrt3+tfrac17i$ I read in your NumPy code. I also wouldn't call this a horocycle, but only part of a horocycle. Is this distinction relevant to your question? I can see how the code corresponds to the citation, and the resulting picture looks plausible at first glance, so what do you want to debug? What is it you want to achieve that you don't already have? What is currently wrong and you want to make it right?
– MvG
Aug 24 at 21:10











1 Answer
1






active

oldest

votes

















up vote
0
down vote













What to expect



I assume that since you write about a closed horocycle in your question title, you actually mean the horocycle $mathbb R+tfrac17i$ instead of the horocycle arc $[0,1]+tfrac17i$ you use in your description. On the other hand, since translations by $1$ turn that arc into the complete horocycle, it probably doesn't make much of a difference either way.



I started with a quick experiment using Cinderella. There I declared three transformations: an inversion in the unit circle (which thanks to the symmetry of this problem works just as well as the $zmapsto-tfrac1z$ you have, even though it's in fact $zmapstotfrac1bar z$), a translation by one unit to the right and its inverse. Then I defined a symmetry group consisting of these, and applied it to the line $y=tfrac17$. The result gave me an idea what to expect:



Cinderella construction



I've also created a better version, with more iterations, better highlighting of fundamental domain, and generally more hand-tuning (click for bigger version):



Better figure



So how do you draw this? Naively speaking, you just draw those circles that intersect the fundamental domain. There are $7$ big circles and $2$ smaller ones. The big ones have radius $tfrac72$ and center $x$ coordinates $-3,-2,-1,0,1,2,3$. The smaller ones have radius $tfrac78$ and center $x$ coordinates $pmtfrac12$. The $y$ coordinate of the center is always equal to the radius, since a horocycle touches the real axis. Getting the infinitely many smaller circles in a picture like the ones above is a lot more work but not needed if you focus on one fundamental domain only.



Debugging



So where did your code go wrong? Take another look at this part:



( (z.real % 1) - 0.5 )


This should better be



(((z.real + 0.5) % 1) - 0.5)


or some such, so that it is idempotent for values already in the range $(-tfrac12,tfrac12)$. You could also write that line as



z = z - np.round(z.real)


I could find the error by just plugging $z=tfrac17i$ into your algorithm, which I knew should end up at $7i$ but got some real component thanks to this bug.



How I created my image



The picture I created above came out of some Sage computation. The core idea is to represent a circle as a homogeneous integer coordinate vector, namely as $(x,y,x^2+y^2-r^2,1)^T$ or some multiple thereof. The elementary transformations would be



$$
S=beginpmatrix-1&0&0&0\0&1&0&0\0&0&0&1\0&0&1&0endpmatrixqquad
T=beginpmatrix1&0&0&1\0&1&0&0\2&0&1&1\0&0&0&1endpmatrixqquad
T^-1=beginpmatrix1&0&0&-1\0&1&0&0\-2&0&1&1\0&0&0&1endpmatrix
$$



The line $mathbb R+tfrac17i$ can be written as $(0,7,2,0)^T$; it's image under operation $S$ is the circle $(0,7,0,2)$, a circle with center at $(0,tfrac72)$. So starting from this, it is possible to enumerate circles like this:



from __future__ import division
import math, collections
queue = collections.deque()
seen = set()

def see(v):
if v not in seen:
seen.add(v)
queue.append(v)

see((0, 7, 2, 0))
for t in range(10000): # Limit chosen arbitrarily
v = queue.popleft()
see((-v[0], v[1], v[3], v[2]))
see((v[0]+v[3], v[1], 2*v[0]+v[2]+v[3], v[3]))
see((v[0]-v[3], v[1], -2*v[0]+v[2]+v[3], v[3]))

def dehom(v):
x = v[0]/v[3]
y = v[1]/v[3]
r = math.sqrt(x*x + y*y - v[2]/v[3])
return (x, y, r)

circles = [dehom(v) for v in seen if v[3]]
circles.sort(key = lambda xyr: (-xyr[2], xyr[0])) # sort by r desc, then x asc
for x, y, r in circles:
if r > 0.005 and x+r > -2 and x-r < 2: # Limit roughly to picture scope
print("circle center , radius ".format(x, y, r))


You can easily replace the print statement with an SVG write, or some draw command, or some such. Using integer arithmetic avoids rounding errors when checking for presence in the set. Usually you'd want some step somewhere to normalize the representatives, in order to avoid having different multiples of the same vector in the set. But since all three operations leave the second coordinate unmodified, we can only ever get one possible representative, namely the one with second coordinate equal to $7$.






share|cite|improve this answer






















    Your Answer




    StackExchange.ifUsing("editor", function ()
    return StackExchange.using("mathjaxEditing", function ()
    StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix)
    StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["$", "$"], ["\\(","\\)"]]);
    );
    );
    , "mathjax-editing");

    StackExchange.ready(function()
    var channelOptions =
    tags: "".split(" "),
    id: "69"
    ;
    initTagRenderer("".split(" "), "".split(" "), channelOptions);

    StackExchange.using("externalEditor", function()
    // Have to fire editor after snippets, if snippets enabled
    if (StackExchange.settings.snippets.snippetsEnabled)
    StackExchange.using("snippets", function()
    createEditor();
    );

    else
    createEditor();

    );

    function createEditor()
    StackExchange.prepareEditor(
    heartbeatType: 'answer',
    convertImagesToLinks: true,
    noModals: false,
    showLowRepImageUploadWarning: true,
    reputationToPostImages: 10,
    bindNavPrevention: true,
    postfix: "",
    noCode: true, onDemand: true,
    discardSelector: ".discard-answer"
    ,immediatelyShowMarkdownHelp:true
    );



    );













     

    draft saved


    draft discarded


















    StackExchange.ready(
    function ()
    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmath.stackexchange.com%2fquestions%2f2891934%2fhow-to-draw-the-picture-of-a-closed-horocycle-in-textsl-2-mathbbz-backs%23new-answer', 'question_page');

    );

    Post as a guest






























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes








    up vote
    0
    down vote













    What to expect



    I assume that since you write about a closed horocycle in your question title, you actually mean the horocycle $mathbb R+tfrac17i$ instead of the horocycle arc $[0,1]+tfrac17i$ you use in your description. On the other hand, since translations by $1$ turn that arc into the complete horocycle, it probably doesn't make much of a difference either way.



    I started with a quick experiment using Cinderella. There I declared three transformations: an inversion in the unit circle (which thanks to the symmetry of this problem works just as well as the $zmapsto-tfrac1z$ you have, even though it's in fact $zmapstotfrac1bar z$), a translation by one unit to the right and its inverse. Then I defined a symmetry group consisting of these, and applied it to the line $y=tfrac17$. The result gave me an idea what to expect:



    Cinderella construction



    I've also created a better version, with more iterations, better highlighting of fundamental domain, and generally more hand-tuning (click for bigger version):



    Better figure



    So how do you draw this? Naively speaking, you just draw those circles that intersect the fundamental domain. There are $7$ big circles and $2$ smaller ones. The big ones have radius $tfrac72$ and center $x$ coordinates $-3,-2,-1,0,1,2,3$. The smaller ones have radius $tfrac78$ and center $x$ coordinates $pmtfrac12$. The $y$ coordinate of the center is always equal to the radius, since a horocycle touches the real axis. Getting the infinitely many smaller circles in a picture like the ones above is a lot more work but not needed if you focus on one fundamental domain only.



    Debugging



    So where did your code go wrong? Take another look at this part:



    ( (z.real % 1) - 0.5 )


    This should better be



    (((z.real + 0.5) % 1) - 0.5)


    or some such, so that it is idempotent for values already in the range $(-tfrac12,tfrac12)$. You could also write that line as



    z = z - np.round(z.real)


    I could find the error by just plugging $z=tfrac17i$ into your algorithm, which I knew should end up at $7i$ but got some real component thanks to this bug.



    How I created my image



    The picture I created above came out of some Sage computation. The core idea is to represent a circle as a homogeneous integer coordinate vector, namely as $(x,y,x^2+y^2-r^2,1)^T$ or some multiple thereof. The elementary transformations would be



    $$
    S=beginpmatrix-1&0&0&0\0&1&0&0\0&0&0&1\0&0&1&0endpmatrixqquad
    T=beginpmatrix1&0&0&1\0&1&0&0\2&0&1&1\0&0&0&1endpmatrixqquad
    T^-1=beginpmatrix1&0&0&-1\0&1&0&0\-2&0&1&1\0&0&0&1endpmatrix
    $$



    The line $mathbb R+tfrac17i$ can be written as $(0,7,2,0)^T$; it's image under operation $S$ is the circle $(0,7,0,2)$, a circle with center at $(0,tfrac72)$. So starting from this, it is possible to enumerate circles like this:



    from __future__ import division
    import math, collections
    queue = collections.deque()
    seen = set()

    def see(v):
    if v not in seen:
    seen.add(v)
    queue.append(v)

    see((0, 7, 2, 0))
    for t in range(10000): # Limit chosen arbitrarily
    v = queue.popleft()
    see((-v[0], v[1], v[3], v[2]))
    see((v[0]+v[3], v[1], 2*v[0]+v[2]+v[3], v[3]))
    see((v[0]-v[3], v[1], -2*v[0]+v[2]+v[3], v[3]))

    def dehom(v):
    x = v[0]/v[3]
    y = v[1]/v[3]
    r = math.sqrt(x*x + y*y - v[2]/v[3])
    return (x, y, r)

    circles = [dehom(v) for v in seen if v[3]]
    circles.sort(key = lambda xyr: (-xyr[2], xyr[0])) # sort by r desc, then x asc
    for x, y, r in circles:
    if r > 0.005 and x+r > -2 and x-r < 2: # Limit roughly to picture scope
    print("circle center , radius ".format(x, y, r))


    You can easily replace the print statement with an SVG write, or some draw command, or some such. Using integer arithmetic avoids rounding errors when checking for presence in the set. Usually you'd want some step somewhere to normalize the representatives, in order to avoid having different multiples of the same vector in the set. But since all three operations leave the second coordinate unmodified, we can only ever get one possible representative, namely the one with second coordinate equal to $7$.






    share|cite|improve this answer


























      up vote
      0
      down vote













      What to expect



      I assume that since you write about a closed horocycle in your question title, you actually mean the horocycle $mathbb R+tfrac17i$ instead of the horocycle arc $[0,1]+tfrac17i$ you use in your description. On the other hand, since translations by $1$ turn that arc into the complete horocycle, it probably doesn't make much of a difference either way.



      I started with a quick experiment using Cinderella. There I declared three transformations: an inversion in the unit circle (which thanks to the symmetry of this problem works just as well as the $zmapsto-tfrac1z$ you have, even though it's in fact $zmapstotfrac1bar z$), a translation by one unit to the right and its inverse. Then I defined a symmetry group consisting of these, and applied it to the line $y=tfrac17$. The result gave me an idea what to expect:



      Cinderella construction



      I've also created a better version, with more iterations, better highlighting of fundamental domain, and generally more hand-tuning (click for bigger version):



      Better figure



      So how do you draw this? Naively speaking, you just draw those circles that intersect the fundamental domain. There are $7$ big circles and $2$ smaller ones. The big ones have radius $tfrac72$ and center $x$ coordinates $-3,-2,-1,0,1,2,3$. The smaller ones have radius $tfrac78$ and center $x$ coordinates $pmtfrac12$. The $y$ coordinate of the center is always equal to the radius, since a horocycle touches the real axis. Getting the infinitely many smaller circles in a picture like the ones above is a lot more work but not needed if you focus on one fundamental domain only.



      Debugging



      So where did your code go wrong? Take another look at this part:



      ( (z.real % 1) - 0.5 )


      This should better be



      (((z.real + 0.5) % 1) - 0.5)


      or some such, so that it is idempotent for values already in the range $(-tfrac12,tfrac12)$. You could also write that line as



      z = z - np.round(z.real)


      I could find the error by just plugging $z=tfrac17i$ into your algorithm, which I knew should end up at $7i$ but got some real component thanks to this bug.



      How I created my image



      The picture I created above came out of some Sage computation. The core idea is to represent a circle as a homogeneous integer coordinate vector, namely as $(x,y,x^2+y^2-r^2,1)^T$ or some multiple thereof. The elementary transformations would be



      $$
      S=beginpmatrix-1&0&0&0\0&1&0&0\0&0&0&1\0&0&1&0endpmatrixqquad
      T=beginpmatrix1&0&0&1\0&1&0&0\2&0&1&1\0&0&0&1endpmatrixqquad
      T^-1=beginpmatrix1&0&0&-1\0&1&0&0\-2&0&1&1\0&0&0&1endpmatrix
      $$



      The line $mathbb R+tfrac17i$ can be written as $(0,7,2,0)^T$; it's image under operation $S$ is the circle $(0,7,0,2)$, a circle with center at $(0,tfrac72)$. So starting from this, it is possible to enumerate circles like this:



      from __future__ import division
      import math, collections
      queue = collections.deque()
      seen = set()

      def see(v):
      if v not in seen:
      seen.add(v)
      queue.append(v)

      see((0, 7, 2, 0))
      for t in range(10000): # Limit chosen arbitrarily
      v = queue.popleft()
      see((-v[0], v[1], v[3], v[2]))
      see((v[0]+v[3], v[1], 2*v[0]+v[2]+v[3], v[3]))
      see((v[0]-v[3], v[1], -2*v[0]+v[2]+v[3], v[3]))

      def dehom(v):
      x = v[0]/v[3]
      y = v[1]/v[3]
      r = math.sqrt(x*x + y*y - v[2]/v[3])
      return (x, y, r)

      circles = [dehom(v) for v in seen if v[3]]
      circles.sort(key = lambda xyr: (-xyr[2], xyr[0])) # sort by r desc, then x asc
      for x, y, r in circles:
      if r > 0.005 and x+r > -2 and x-r < 2: # Limit roughly to picture scope
      print("circle center , radius ".format(x, y, r))


      You can easily replace the print statement with an SVG write, or some draw command, or some such. Using integer arithmetic avoids rounding errors when checking for presence in the set. Usually you'd want some step somewhere to normalize the representatives, in order to avoid having different multiples of the same vector in the set. But since all three operations leave the second coordinate unmodified, we can only ever get one possible representative, namely the one with second coordinate equal to $7$.






      share|cite|improve this answer
























        up vote
        0
        down vote










        up vote
        0
        down vote









        What to expect



        I assume that since you write about a closed horocycle in your question title, you actually mean the horocycle $mathbb R+tfrac17i$ instead of the horocycle arc $[0,1]+tfrac17i$ you use in your description. On the other hand, since translations by $1$ turn that arc into the complete horocycle, it probably doesn't make much of a difference either way.



        I started with a quick experiment using Cinderella. There I declared three transformations: an inversion in the unit circle (which thanks to the symmetry of this problem works just as well as the $zmapsto-tfrac1z$ you have, even though it's in fact $zmapstotfrac1bar z$), a translation by one unit to the right and its inverse. Then I defined a symmetry group consisting of these, and applied it to the line $y=tfrac17$. The result gave me an idea what to expect:



        Cinderella construction



        I've also created a better version, with more iterations, better highlighting of fundamental domain, and generally more hand-tuning (click for bigger version):



        Better figure



        So how do you draw this? Naively speaking, you just draw those circles that intersect the fundamental domain. There are $7$ big circles and $2$ smaller ones. The big ones have radius $tfrac72$ and center $x$ coordinates $-3,-2,-1,0,1,2,3$. The smaller ones have radius $tfrac78$ and center $x$ coordinates $pmtfrac12$. The $y$ coordinate of the center is always equal to the radius, since a horocycle touches the real axis. Getting the infinitely many smaller circles in a picture like the ones above is a lot more work but not needed if you focus on one fundamental domain only.



        Debugging



        So where did your code go wrong? Take another look at this part:



        ( (z.real % 1) - 0.5 )


        This should better be



        (((z.real + 0.5) % 1) - 0.5)


        or some such, so that it is idempotent for values already in the range $(-tfrac12,tfrac12)$. You could also write that line as



        z = z - np.round(z.real)


        I could find the error by just plugging $z=tfrac17i$ into your algorithm, which I knew should end up at $7i$ but got some real component thanks to this bug.



        How I created my image



        The picture I created above came out of some Sage computation. The core idea is to represent a circle as a homogeneous integer coordinate vector, namely as $(x,y,x^2+y^2-r^2,1)^T$ or some multiple thereof. The elementary transformations would be



        $$
        S=beginpmatrix-1&0&0&0\0&1&0&0\0&0&0&1\0&0&1&0endpmatrixqquad
        T=beginpmatrix1&0&0&1\0&1&0&0\2&0&1&1\0&0&0&1endpmatrixqquad
        T^-1=beginpmatrix1&0&0&-1\0&1&0&0\-2&0&1&1\0&0&0&1endpmatrix
        $$



        The line $mathbb R+tfrac17i$ can be written as $(0,7,2,0)^T$; it's image under operation $S$ is the circle $(0,7,0,2)$, a circle with center at $(0,tfrac72)$. So starting from this, it is possible to enumerate circles like this:



        from __future__ import division
        import math, collections
        queue = collections.deque()
        seen = set()

        def see(v):
        if v not in seen:
        seen.add(v)
        queue.append(v)

        see((0, 7, 2, 0))
        for t in range(10000): # Limit chosen arbitrarily
        v = queue.popleft()
        see((-v[0], v[1], v[3], v[2]))
        see((v[0]+v[3], v[1], 2*v[0]+v[2]+v[3], v[3]))
        see((v[0]-v[3], v[1], -2*v[0]+v[2]+v[3], v[3]))

        def dehom(v):
        x = v[0]/v[3]
        y = v[1]/v[3]
        r = math.sqrt(x*x + y*y - v[2]/v[3])
        return (x, y, r)

        circles = [dehom(v) for v in seen if v[3]]
        circles.sort(key = lambda xyr: (-xyr[2], xyr[0])) # sort by r desc, then x asc
        for x, y, r in circles:
        if r > 0.005 and x+r > -2 and x-r < 2: # Limit roughly to picture scope
        print("circle center , radius ".format(x, y, r))


        You can easily replace the print statement with an SVG write, or some draw command, or some such. Using integer arithmetic avoids rounding errors when checking for presence in the set. Usually you'd want some step somewhere to normalize the representatives, in order to avoid having different multiples of the same vector in the set. But since all three operations leave the second coordinate unmodified, we can only ever get one possible representative, namely the one with second coordinate equal to $7$.






        share|cite|improve this answer














        What to expect



        I assume that since you write about a closed horocycle in your question title, you actually mean the horocycle $mathbb R+tfrac17i$ instead of the horocycle arc $[0,1]+tfrac17i$ you use in your description. On the other hand, since translations by $1$ turn that arc into the complete horocycle, it probably doesn't make much of a difference either way.



        I started with a quick experiment using Cinderella. There I declared three transformations: an inversion in the unit circle (which thanks to the symmetry of this problem works just as well as the $zmapsto-tfrac1z$ you have, even though it's in fact $zmapstotfrac1bar z$), a translation by one unit to the right and its inverse. Then I defined a symmetry group consisting of these, and applied it to the line $y=tfrac17$. The result gave me an idea what to expect:



        Cinderella construction



        I've also created a better version, with more iterations, better highlighting of fundamental domain, and generally more hand-tuning (click for bigger version):



        Better figure



        So how do you draw this? Naively speaking, you just draw those circles that intersect the fundamental domain. There are $7$ big circles and $2$ smaller ones. The big ones have radius $tfrac72$ and center $x$ coordinates $-3,-2,-1,0,1,2,3$. The smaller ones have radius $tfrac78$ and center $x$ coordinates $pmtfrac12$. The $y$ coordinate of the center is always equal to the radius, since a horocycle touches the real axis. Getting the infinitely many smaller circles in a picture like the ones above is a lot more work but not needed if you focus on one fundamental domain only.



        Debugging



        So where did your code go wrong? Take another look at this part:



        ( (z.real % 1) - 0.5 )


        This should better be



        (((z.real + 0.5) % 1) - 0.5)


        or some such, so that it is idempotent for values already in the range $(-tfrac12,tfrac12)$. You could also write that line as



        z = z - np.round(z.real)


        I could find the error by just plugging $z=tfrac17i$ into your algorithm, which I knew should end up at $7i$ but got some real component thanks to this bug.



        How I created my image



        The picture I created above came out of some Sage computation. The core idea is to represent a circle as a homogeneous integer coordinate vector, namely as $(x,y,x^2+y^2-r^2,1)^T$ or some multiple thereof. The elementary transformations would be



        $$
        S=beginpmatrix-1&0&0&0\0&1&0&0\0&0&0&1\0&0&1&0endpmatrixqquad
        T=beginpmatrix1&0&0&1\0&1&0&0\2&0&1&1\0&0&0&1endpmatrixqquad
        T^-1=beginpmatrix1&0&0&-1\0&1&0&0\-2&0&1&1\0&0&0&1endpmatrix
        $$



        The line $mathbb R+tfrac17i$ can be written as $(0,7,2,0)^T$; it's image under operation $S$ is the circle $(0,7,0,2)$, a circle with center at $(0,tfrac72)$. So starting from this, it is possible to enumerate circles like this:



        from __future__ import division
        import math, collections
        queue = collections.deque()
        seen = set()

        def see(v):
        if v not in seen:
        seen.add(v)
        queue.append(v)

        see((0, 7, 2, 0))
        for t in range(10000): # Limit chosen arbitrarily
        v = queue.popleft()
        see((-v[0], v[1], v[3], v[2]))
        see((v[0]+v[3], v[1], 2*v[0]+v[2]+v[3], v[3]))
        see((v[0]-v[3], v[1], -2*v[0]+v[2]+v[3], v[3]))

        def dehom(v):
        x = v[0]/v[3]
        y = v[1]/v[3]
        r = math.sqrt(x*x + y*y - v[2]/v[3])
        return (x, y, r)

        circles = [dehom(v) for v in seen if v[3]]
        circles.sort(key = lambda xyr: (-xyr[2], xyr[0])) # sort by r desc, then x asc
        for x, y, r in circles:
        if r > 0.005 and x+r > -2 and x-r < 2: # Limit roughly to picture scope
        print("circle center , radius ".format(x, y, r))


        You can easily replace the print statement with an SVG write, or some draw command, or some such. Using integer arithmetic avoids rounding errors when checking for presence in the set. Usually you'd want some step somewhere to normalize the representatives, in order to avoid having different multiples of the same vector in the set. But since all three operations leave the second coordinate unmodified, we can only ever get one possible representative, namely the one with second coordinate equal to $7$.







        share|cite|improve this answer














        share|cite|improve this answer



        share|cite|improve this answer








        edited Aug 27 at 8:09

























        answered Aug 26 at 23:28









        MvG

        29.8k44597




        29.8k44597



























             

            draft saved


            draft discarded















































             


            draft saved


            draft discarded














            StackExchange.ready(
            function ()
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fmath.stackexchange.com%2fquestions%2f2891934%2fhow-to-draw-the-picture-of-a-closed-horocycle-in-textsl-2-mathbbz-backs%23new-answer', 'question_page');

            );

            Post as a guest













































































            這個網誌中的熱門文章

            How to combine Bézier curves to a surface?

            Mutual Information Always Non-negative

            Why am i infinitely getting the same tweet with the Twitter Search API?