-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhyperbolicHoneycombMeasurements.py
executable file
·341 lines (284 loc) · 12.1 KB
/
hyperbolicHoneycombMeasurements.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
#!/usr/bin/python
'''
Here's a more detailed outline of how I'd go about drawing
the intersection of {3,3,7} with the sphere at infinity.
I'll probably do this in a few days if no one else does it first.
To get started, consider the center cell
of a cell-centered {3,3,7}.
The intersection of that cell with the sphere at infinity
consists of three little regular spherical triangles;
we need to know the euclidean coords of one of these
little spherical triangles.
We can find that in 4 steps:
Step 1: compute the cell mid-radius r31{3,3,7}.
The reference paper "Regular Honeycombs In Hyperbolic Space" by HSM Coxeter
doesn't give a direct formula for cell mid-radius r31,
but it gives a formula for the cell in-radius r32.
From that, we can use the identity
sin(A)/sinh(a) = sin(B)/sinh(b) = sin(C)/sinh(c)
on the right hyperbolic triangle
formed by the cell center, face center, and edge center:
sin(pi/2)/sinh(r31) = sin(pi/r)/sinh(r32)
i.e. 1/sinh(r31) = sin(pi/r)/sinh(r32)
i.e. r31 = asinh(sinh(r32)/sin(pi/r))
So, plug in the formula for r32 from the paper:
r32{p,q,r} = acosh(sin(pi/p)*cos(pi/r)/sqrt(1-cos(pi/p)^2-cos(pi/q)^2))
and now we have a formula for r31{p,q,r}, the cell mid-radius:
r31{p,q,r} = asinh(sinh(acosh(sin(pi/p)*cos(pi/r)/sqrt(1-cos(pi/p)^2-cos(pi/q)^2)))/sin(pi/r))
(This can certainly be simplified, using the identity
sinh(acosh(x)) = sqrt(x^2-1), and probably further simplifications,
but I'm not bothering here.)
(And note, I'm not positive I got all of the above exactly right,
but the method should be sound.)
Substitute p=3,q=3,r=7 to get the desired cell-mid-radius of {3,3,7}.
Step 2: from r31, compute the euclidean distance
from the origin to an edge of the center cell in the poincare ball model.
If I recall correctly, that will be tanh(r31/2).
Step 3: from that, compute the actual coords of the two endpoints-at-infinity
of one edge of the center cell.
For definiteness, align the center cell
with the regular euclidean tetrahedron
with verts:
(-1,-1,1)
(-1,1,-1)
(1,-1,-1)
(1,1,1)
The center of the cell's edge closest to joining -1,-1,1 to 1,1,1
lies on the +z axis, so by Step 2 this edge center is:
(0,0,tanh(r31/2))
The two endpoints-at-infinity of that edge
will be (-sqrt(1/2),-sqrt(1/2),0) and (sqrt(1/2),sqrt(1/2),0)
"translated by (0,0,tanh(r31/2))",
i.e. transformed by the translation
that takes the origin to that edge center (0,0,tanh(r31/2)).
Recall that for any points p,t in the poincare ball
(of any number of dimensions), p translated by t is:
((1 + 2*p.t + p.p)*t + (1-t.t)*p) / (1 + 2*p.t + p.p * t.t)
where "." denotes dot product. (Hope I wrote that down right.)
So plug in:
t = (0,0,tanh(r31/2))
p = (sqrt(1/2),sqrt(1/2),0)
(a bunch of terms simplify and drop out since p.p=1 and p.t=0, but whatever);
The resulting endpoint coords are (a,a,b) for some a,b
(then the other endpoint is (-a,-a,b), but we don't need that at this point).
Step 4: rotate one of those endpoints-at-infinity
around the appropriate axis
to get the other two vertices of the little spherical triangle.
The three spherical triangle vertices are:
(a,a,b)
(b,a,a)
(a,b,a)
(where a,b are the result of step 3).
=========================================================================
So now we have one little spherical triangle.
Now, choose a set of 3 generators for the rotational symmetry group
of {3,3,7}, and use them repeatedly to send the triangle everywhere.
There are lots of choices of 3 generators; here's one:
A: plain old euclidean rotation by 120 degrees about the vector (1,1,1)
B: plain old euclidean rotation by 120 degrees about the vector (1,1,-1)
C: rotation by 2*pi/7 about an edge of {3,3,7}.
for this, we can use the composition of:
translate the edge center to the origin
(i.e. translate the origin to minus the edge center)
followed by plain old euclidean rotation of 2*pi/7 about this edge-through-the-origin
followed by translating the origin back to the original edge center
A specific edge center, and the translation formula,
can be found in Step 3 above.
Don
'''
import sys
import os
from cmath import pi, sqrt, cos,sin,tan, asin, cosh,sinh,acosh,asinh,tanh
# length of edge of characteristic simplex.
# actually returns its cosh^2, its cosh and its value.
# If i0,i1 is:
# 0,1 -> vertex to edge center (i.e. half edge length, i.e. dual cell in-radius if 3d)
# 0,2 -> vertex to face center (i.e. face circum-radius, i.e. dual cell mid-radius if 3d)
# 1,2 -> edge center to face center (i.e. face in-radius, i.e. dual face in-radius if 3d)
# 0,3 -> vertex to cell center (i.e. cell circum-radius, i.e. dual cell circum-radius if 3d)
# 1,3 -> edge center to cell center (i.e. cell mid-radius, i.e. dual face circum-radius if 3d)
# 2,3 -> face center to cell center (i.e. cell in-radius, i.e. dual half edge length if 3d)
def measure(schlafli, i0,i1):
if i0 == i1:
return 0.,0.
if i0 > i1:
i0,i1 = i1,i0
if len(schlafli) == 0:
# 0 dimensional surface, {}
# can't get here-- i0,i1 must be 0,0 which was handled above
assert False
if len(schlafli) == 1:
# 1 dimensional surface, {p}
p = schlafli
if (i0,i1) == (0,1):
assert False # I don't think this is well-defined... maybe infinite?
else:
assert False
elif len(schlafli) == 2:
# 2 dimensional surface, {p,q}
p,q = schlafli
if (i0,i1) == (0,1):
# half edge length
coshValue = cos(pi/p)/sin(pi/q)
elif (i0,i1) == (1,2):
# face in-radius
coshValue = cos(pi/q)/sin(pi/p)
elif (i0,i1) == (0,2):
# face circum-radius
coshValue = 1/(tan(pi/p)*tan(pi/q))
else:
assert False
elif len(schlafli) == 3:
# 3 dimensional surface, {p,q,r}
p,q,r = schlafli
def sin_pi_over_h(p,q):
# = sqrt(1 - cos^2 (pi / h(p,q)))
# = sqrt(1 - (cos(pi/p)^2 + cos(pi/q)^2)
return sqrt(1 - (cos(pi/p)**2 + cos(pi/q)**2))
if (i0,i1) == (0,1):
# half edge length
coshValue = cos(pi/p)*sin(pi/r)/sin_pi_over_h(q,r)
elif (i0,i1) == (2,3):
# cell in-radius
coshValue = sin(pi/p)*cos(pi/r)/sin_pi_over_h(p,q)
elif (i0,i1) == (0,3):
# cell circum-radius
coshValue = cos(pi/p)*cos(pi/q)*cos(pi/r)/(sin_pi_over_h(p,q)*sin_pi_over_h(q,r))
elif (i0,i1) == (0,2):
# 2d face circum-radius
cosh2_r01,cosh_r01,r01 = measure(schlafli,0,1)
sinh_r01 = sqrt(cosh2_r01-1)
sinhValue = sinh_r01/sin(pi/p)
coshValue = sqrt(1+sinhValue**2)
#do('coshValue')
# hmm, this doesn't seem to be simplifying much
sinh2Value = ((cos(pi/p)*sin(pi/r))**2/(1 - (cos(pi/q)**2 + cos(pi/r)**2))-1)/sin(pi/p)**2
cosh2Value = 1+sinh2Value
coshValue = sqrt(cosh2Value)
#do('coshValue')
# oh wait, wolframalpha says sinh2Value is equal to cot^2(pi/p)cos^2(pi/q)/(sin^2(pi/q)-cos^2(pi/r))
# maybe get h back out of the equation and it wil turn somewhat nice
# TODO: try to simplify some more
h = pi/asin(sin_pi_over_h(q,r))
sinhValue = sqrt((cos(pi/p)*sin(pi/r)/sin(pi/h))**2-1)/sin(pi/p)
coshValue = sqrt(1+sinhValue**2)
coshValue = sqrt(1+((cos(pi/p)*sin(pi/r)/sin(pi/h))**2-1)/sin(pi/p)**2)
#do('coshValue')
# huh? this is wacked, it's not equal to what I thought above
#sinh2Value = sin(pi/p)**2*cos(pi/q)**2/sin_pi_over_h(q,r)**2/cos(pi/p)**2
sinh2Value = cos(pi/p)**2/sin(pi/p)**2 * cos(pi/q)**2 / (sin(pi/q)**2 - cos(pi/r)**2)
cosh2Value = 1+sinh2Value
coshValue = sqrt(cosh2Value)
#do('coshValue')
elif (i0,i1) == (1,3):
# cell mid-radius
return measure([r,q,p], 0,2)
elif (i0,i1) == (1,2):
# 2d face in-radius
# We can calculate this in one of two ways,
# using the hyperbolic right triangle identities:
# cos A = tanh b / tanh c
# sinh a / sin A = sinh c / 1
# => sinh a = sinh c * sqrt(1 - (tanh b / tanh c)^2)
# with either: b=r01, c=r02
# or: b=r23, c=r13
# But, it simplifies to the simplest of all
# (don't ask me how, I used wolframalpha
# on a horrendous expression after
# breaking it up into bite sized pieces):
coshValue = cos(pi/q) / (sin(pi/p) * sin(pi/r))
else:
assert False # illegal
elif len(schlafli) == 4:
# 4 dimensional surface, {p,q,r,s}
p,q,r,s = schlafli
if (i0,i1) == (0,1):
# half edge length
assert False # unimplemented
elif (i0,i1) == (3,4):
# facet in-radius
assert False # unimplemented
elif (i0,i1) == (0,4):
# facet circum-radius
assert False # unimplemented
else:
assert False # illegal
else:
assert False # unimplemented
return coshValue**2, coshValue,acosh(coshValue)
# Little test program
if __name__ == '__main__':
def do(s):
import inspect
answer = eval(s, globals(), inspect.currentframe().f_back.f_locals)
print ' '+s+' = '+`answer`
tau = (sqrt(5)+1)/2
do('measure([7,3],0,1)')
do('measure([7,3],0,2)')
do('measure([7,3],1,2)')
do('measure([7/2.,7],0,1)')
do('measure([7/2.,7],0,2)')
do('measure([7/2.,7],1,2)')
do('cos(2*pi/7)/sin(pi/7)') # 0,1 from book
do('1/(tan(pi/7)*tan(2*pi/7))') # 0,3 from book
do('1/(2*sin(pi/7))') # 2,3 from book
do('measure([5,3,4],0,1)')
do('measure([5,3,4],0,3)')
do('measure([5,3,4],2,3)')
do('.5*tau**2') # 0,1 from book
do('.5*tau**4') # 0,3 from book
do('.5*sqrt(5)*tau') # 2,3 from book
do('measure([5,3,5],0,1)')
do('measure([5,3,5],0,3)')
do('measure([5,3,5],2,3)')
do('.25*sqrt(5)*tau**3') # 0,1 and 2,3 from book
do('.25*tau**8') # 0,3 from book
do('measure([6,3,3],0,1)')
do('measure([6,3,3],0,3)')
do('measure([6,3,3],2,3)')
do('measure([3,6,3],0,1)')
do('measure([3,6,3],0,3)')
do('measure([3,6,3],2,3)')
do('measure([4,4,3],0,1)')
do('measure([4,4,3],0,3)')
do('measure([4,4,3],2,3)')
do('measure([7,3,3],0,1)')
do('measure([7,3,3],0,3)')
do('measure([7,3,3],2,3)')
do('measure([7,3],0,1)')
do('measure([7,3,2],0,1)')
do('measure([7,3,3],0,1)')
do('measure([7,3,4],0,1)')
do('measure([7,3,5],0,1)')
do('measure([7,3,6],0,1)')
do('measure([7,3,7],0,1)')
do('measure([7,3,8],0,1)')
def coshHalfEdgeLength(p,q,r):
return cos(pi/p)*sin(pi/r)/sqrt(1-cos(pi/q)**2-cos(pi/r)**2)
def halfEdgeLength(p,q,r):
return acosh(cos(pi/p)*sin(pi/r)/sqrt(1-cos(pi/q)**2-cos(pi/r)**2))
do('halfEdgeLength(7,3,2)')
do('halfEdgeLength(7,3,3)')
do('halfEdgeLength(7,3,4)')
do('halfEdgeLength(7,3,5)')
do('halfEdgeLength(7,3,6)')
do('halfEdgeLength(7,3,7)')
do('halfEdgeLength(7,3,8)')
for n in [2,3,4,5,6,7,8,9]:
p = 7
q = 3
r = n
print " {"+`p`+","+`q`+","+`r`+"} -> acosh("+`coshHalfEdgeLength(p,q,r)`+") = "+`halfEdgeLength(p,q,r)`+""
do('halfEdgeLength(2,3,7)')
do('measure([3,3,7], 0,1)')
do('measure([3,3,7], 0,2)')
do('measure([3,3,7], 0,3)')
#do('measure([3,3,7], 1,2)')
do('measure([3,3,7], 1,3)')
do('measure([3,3,7], 2,3)')
do('measure([5,3,4],0,1)')
do('measure([5,3,4],0,2)')
do('measure([5,3,4],0,3)')
do('measure([5,3,4],1,2)')
do('measure([5,3,4],1,3)')
do('measure([5,3,4],2,3)')