Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagesmc
Path: blob/master/src/sage/geometry/fan_isomorphism.py
8815 views
1
"""
2
Find isomorphisms between fans.
3
"""
4
5
6
#*****************************************************************************
7
# Copyright (C) 2012 Volker Braun <[email protected]>
8
#
9
# Distributed under the terms of the GNU General Public License (GPL)
10
# as published by the Free Software Foundation; either version 2 of
11
# the License, or (at your option) any later version.
12
# http://www.gnu.org/licenses/
13
#*****************************************************************************
14
15
from exceptions import Exception
16
17
from sage.rings.all import ZZ
18
from sage.matrix.constructor import column_matrix, matrix
19
from sage.geometry.cone import Cone
20
21
22
23
class FanNotIsomorphicError(Exception):
24
"""
25
Exception to return if there is no fan isomorphism
26
"""
27
pass
28
29
30
def fan_isomorphic_necessary_conditions(fan1, fan2):
31
"""
32
Check necessary (but not sufficient) conditions for the fans to be isomorphic.
33
34
INPUT:
35
36
- ``fan1``, ``fan2`` -- two fans.
37
38
OUTPUT:
39
40
Boolean. ``False`` if the two fans cannot be isomorphic. ``True``
41
if the two fans may be isomorphic.
42
43
EXAMPLES::
44
45
sage: fan1 = toric_varieties.P2().fan()
46
sage: fan2 = toric_varieties.dP8().fan()
47
sage: from sage.geometry.fan_isomorphism import fan_isomorphic_necessary_conditions
48
sage: fan_isomorphic_necessary_conditions(fan1, fan2)
49
False
50
"""
51
if fan1.lattice_dim() != fan2.lattice_dim():
52
return False
53
if fan1.dim() != fan2.dim():
54
return False
55
if fan1.nrays() != fan2.nrays():
56
return False
57
if fan1.ngenerating_cones() != fan2.ngenerating_cones():
58
return False
59
if fan1.is_complete() != fan2.is_complete():
60
return False
61
return True
62
63
64
def fan_isomorphism_generator(fan1, fan2):
65
"""
66
Iterate over the isomorphisms from ``fan1`` to ``fan2``.
67
68
ALGORITHM:
69
70
The :meth:`sage.geometry.fan.Fan.vertex_graph` of the two fans is
71
compared. For each graph isomorphism, we attempt to lift it to an
72
actual isomorphism of fans.
73
74
INPUT:
75
76
- ``fan1``, ``fan2`` -- two fans.
77
78
OUTPUT:
79
80
Yields the fan isomorphisms as matrices acting from the right on
81
rays.
82
83
EXAMPLES::
84
85
sage: fan = toric_varieties.P2().fan()
86
sage: from sage.geometry.fan_isomorphism import fan_isomorphism_generator
87
sage: tuple( fan_isomorphism_generator(fan, fan) )
88
(
89
[1 0] [0 1] [ 1 0] [-1 -1] [ 0 1] [-1 -1]
90
[0 1], [1 0], [-1 -1], [ 1 0], [-1 -1], [ 0 1]
91
)
92
93
sage: m1 = matrix([(1, 0), (0, -5), (-3, 4)])
94
sage: m2 = matrix([(3, 0), (1, 0), (-2, 1)])
95
sage: m1.elementary_divisors() == m2.elementary_divisors() == [1,1,0]
96
True
97
sage: fan1 = Fan([Cone([m1*vector([23, 14]), m1*vector([ 3,100])]),
98
... Cone([m1*vector([-1,-14]), m1*vector([-100, -5])])])
99
sage: fan2 = Fan([Cone([m2*vector([23, 14]), m2*vector([ 3,100])]),
100
... Cone([m2*vector([-1,-14]), m2*vector([-100, -5])])])
101
sage: fan_isomorphism_generator(fan1, fan2).next()
102
[18 1 -5]
103
[ 4 0 -1]
104
[ 5 0 -1]
105
106
sage: m0 = identity_matrix(ZZ, 2)
107
sage: m1 = matrix([(1, 0), (0, -5), (-3, 4)])
108
sage: m2 = matrix([(3, 0), (1, 0), (-2, 1)])
109
sage: m1.elementary_divisors() == m2.elementary_divisors() == [1,1,0]
110
True
111
sage: fan0 = Fan([Cone([m0*vector([1,0]), m0*vector([1,1])]),
112
... Cone([m0*vector([1,1]), m0*vector([0,1])])])
113
sage: fan1 = Fan([Cone([m1*vector([1,0]), m1*vector([1,1])]),
114
... Cone([m1*vector([1,1]), m1*vector([0,1])])])
115
sage: fan2 = Fan([Cone([m2*vector([1,0]), m2*vector([1,1])]),
116
... Cone([m2*vector([1,1]), m2*vector([0,1])])])
117
sage: tuple(fan_isomorphism_generator(fan0, fan0))
118
(
119
[1 0] [0 1]
120
[0 1], [1 0]
121
)
122
sage: tuple(fan_isomorphism_generator(fan1, fan1))
123
(
124
[1 0 0] [ -3 -20 28]
125
[0 1 0] [ -1 -4 7]
126
[0 0 1], [ -1 -5 8]
127
)
128
sage: tuple(fan_isomorphism_generator(fan1, fan2))
129
(
130
[18 1 -5] [ 6 -3 7]
131
[ 4 0 -1] [ 1 -1 2]
132
[ 5 0 -1], [ 2 -1 2]
133
)
134
sage: tuple(fan_isomorphism_generator(fan2, fan1))
135
(
136
[ 0 -1 1] [ 0 -1 1]
137
[ 1 -7 2] [ 2 -2 -5]
138
[ 0 -5 4], [ 1 0 -3]
139
)
140
"""
141
if not fan_isomorphic_necessary_conditions(fan1, fan2):
142
return
143
144
graph1 = fan1.vertex_graph()
145
graph2 = fan2.vertex_graph()
146
graph_iso = graph1.is_isomorphic(graph2, edge_labels=True, certify=True)
147
if not graph_iso[0]:
148
return
149
graph_iso = graph_iso[1]
150
151
# Pick a basis of rays in fan1
152
max_cone = fan1(fan1.dim())[0]
153
fan1_pivot_rays = max_cone.rays()
154
fan1_basis = fan1_pivot_rays + fan1.virtual_rays() # A QQ-basis for N_1
155
fan1_pivot_cones = [ fan1.embed(Cone([r])) for r in fan1_pivot_rays ]
156
157
# The fan2 cones as set(set(ray indices))
158
fan2_cones = frozenset(
159
frozenset(cone.ambient_ray_indices())
160
for cone in fan2.generating_cones() )
161
162
# iterate over all graph isomorphisms graph1 -> graph2
163
for perm in graph2.automorphism_group(edge_labels=True):
164
# find a candidate m that maps fan1_basis to the image rays under the graph isomorphism
165
fan2_pivot_cones = [ perm(graph_iso[c]) for c in fan1_pivot_cones ]
166
fan2_pivot_rays = fan2.rays([ c.ambient_ray_indices()[0] for c in fan2_pivot_cones ])
167
fan2_basis = fan2_pivot_rays + fan2.virtual_rays()
168
try:
169
m = matrix(ZZ, fan1_basis).solve_right(matrix(ZZ, fan2_basis))
170
m = m.change_ring(ZZ)
171
except (ValueError, TypeError):
172
continue # no solution
173
174
# check that the candidate m lifts the vertex graph homomorphism
175
graph_image_ray_indices = [ perm(graph_iso[c]).ambient_ray_indices()[0] for c in fan1(1) ]
176
try:
177
matrix_image_ray_indices = [ fan2.rays().index(r*m) for r in fan1.rays() ]
178
except ValueError:
179
continue
180
if graph_image_ray_indices != matrix_image_ray_indices:
181
continue
182
183
# check that the candidate m maps generating cone to generating cone
184
image_cones = frozenset( # The image(fan1) cones as set(set(integers)
185
frozenset(graph_image_ray_indices[i]
186
for i in cone.ambient_ray_indices())
187
for cone in fan1.generating_cones() )
188
if image_cones == fan2_cones:
189
m.set_immutable()
190
yield m
191
192
193
def find_isomorphism(fan1, fan2, check=False):
194
"""
195
Find an isomorphism of the two fans.
196
197
INPUT:
198
199
- ``fan1``, ``fan2`` -- two fans.
200
201
- ``check`` -- boolean (default: False). Passed to the fan
202
morphism constructor, see
203
:func:`~sage.geometry.fan_morphism.FanMorphism`.
204
205
OUTPUT:
206
207
A fan isomorphism. If the fans are not isomorphic, a
208
:class:`FanNotIsomorphicError` is raised.
209
210
EXAMPLE::
211
212
sage: rays = ((1, 1), (0, 1), (-1, -1), (3, 1))
213
sage: cones = [(0,1), (1,2), (2,3), (3,0)]
214
sage: fan1 = Fan(cones, rays)
215
216
sage: m = matrix([[-2,3],[1,-1]])
217
sage: m.det() == -1
218
True
219
sage: fan2 = Fan(cones, [vector(r)*m for r in rays])
220
221
sage: from sage.geometry.fan_isomorphism import find_isomorphism
222
sage: find_isomorphism(fan1, fan2, check=True)
223
Fan morphism defined by the matrix
224
[-2 3]
225
[ 1 -1]
226
Domain fan: Rational polyhedral fan in 2-d lattice N
227
Codomain fan: Rational polyhedral fan in 2-d lattice N
228
229
sage: find_isomorphism(fan1, toric_varieties.P2().fan())
230
Traceback (most recent call last):
231
...
232
FanNotIsomorphicError
233
234
sage: fan1 = Fan(cones=[[1,3,4,5],[0,1,2,3],[2,3,4],[0,1,5]],
235
... rays=[(-1,-1,0),(-1,-1,3),(-1,1,-1),(-1,3,-1),(0,2,-1),(1,-1,1)])
236
sage: fan2 = Fan(cones=[[0,2,3,5],[0,1,4,5],[0,1,2],[3,4,5]],
237
... rays=[(-1,-1,-1),(-1,-1,0),(-1,1,-1),(0,2,-1),(1,-1,1),(3,-1,-1)])
238
sage: fan1.is_isomorphic(fan2)
239
True
240
"""
241
generator = fan_isomorphism_generator(fan1, fan2)
242
try:
243
m = generator.next()
244
except StopIteration:
245
raise FanNotIsomorphicError
246
247
from sage.geometry.fan_morphism import FanMorphism
248
return FanMorphism(m, domain_fan=fan1, codomain=fan2, check=check)
249
250
251
def fan_2d_cyclically_ordered_rays(fan):
252
"""
253
Return the rays of a 2-dimensional ``fan`` in cyclic order.
254
255
INPUT:
256
257
- ``fan`` -- a 2-dimensional fan.
258
259
OUTPUT:
260
261
A :class:`~sage.geometry.point_collection.PointCollection`
262
containing the rays in one particular cyclic order.
263
264
EXAMPLES::
265
266
sage: rays = ((1, 1), (-1, -1), (-1, 1), (1, -1))
267
sage: cones = [(0,2), (2,1), (1,3), (3,0)]
268
sage: fan = Fan(cones, rays)
269
sage: fan.rays()
270
N( 1, 1),
271
N(-1, -1),
272
N(-1, 1),
273
N( 1, -1)
274
in 2-d lattice N
275
sage: from sage.geometry.fan_isomorphism import fan_2d_cyclically_ordered_rays
276
sage: fan_2d_cyclically_ordered_rays(fan)
277
N(-1, -1),
278
N(-1, 1),
279
N( 1, 1),
280
N( 1, -1)
281
in 2-d lattice N
282
283
TESTS::
284
285
sage: fan = Fan(cones=[], rays=[], lattice=ZZ^2)
286
sage: from sage.geometry.fan_isomorphism import fan_2d_cyclically_ordered_rays
287
sage: fan_2d_cyclically_ordered_rays(fan)
288
Empty collection
289
in Ambient free module of rank 2 over the principal ideal domain Integer Ring
290
"""
291
assert fan.lattice_dim() == 2
292
import math
293
rays = [ (math.atan2(r[0],r[1]), r) for r in fan.rays() ]
294
rays = [ r[1] for r in sorted(rays) ]
295
from sage.geometry.point_collection import PointCollection
296
return PointCollection(rays, fan.lattice())
297
298
299
def fan_2d_echelon_forms(fan):
300
"""
301
Return echelon forms of all cyclically ordered ray matrices.
302
303
Note that the echelon form of the ordered ray matrices are unique
304
up to different cyclic orderings.
305
306
INPUT:
307
308
- ``fan`` -- a fan.
309
310
OUTPUT:
311
312
A set of matrices. The set of all echelon forms for all different
313
cyclic orderings.
314
315
EXAMPLES::
316
317
sage: fan = toric_varieties.P2().fan()
318
sage: from sage.geometry.fan_isomorphism import fan_2d_echelon_forms
319
sage: fan_2d_echelon_forms(fan)
320
frozenset([[ 1 0 -1]
321
[ 0 1 -1]])
322
323
sage: fan = toric_varieties.dP7().fan()
324
sage: list(fan_2d_echelon_forms(fan))
325
[
326
[ 1 0 -1 0 1] [ 1 0 -1 -1 0] [ 1 0 -1 -1 1] [ 1 0 -1 -1 0]
327
[ 0 1 0 -1 -1], [ 0 1 1 0 -1], [ 0 1 1 0 -1], [ 0 1 0 -1 -1],
328
<BLANKLINE>
329
[ 1 0 -1 0 1]
330
[ 0 1 1 -1 -1]
331
]
332
333
TESTS::
334
335
sage: rays = [(1, 1), (-1, -1), (-1, 1), (1, -1)]
336
sage: cones = [(0,2), (2,1), (1,3), (3,0)]
337
sage: fan1 = Fan(cones, rays)
338
sage: from sage.geometry.fan_isomorphism import fan_2d_echelon_form, fan_2d_echelon_forms
339
sage: echelon_forms = fan_2d_echelon_forms(fan1)
340
sage: S4 = CyclicPermutationGroup(4)
341
sage: rays.reverse()
342
sage: cones = [(3,1), (1,2), (2,0), (0,3)]
343
sage: for i in range(100):
344
... m = random_matrix(ZZ,2,2)
345
... if abs(det(m)) != 1: continue
346
... perm = S4.random_element()
347
... perm_cones = [ (perm(c[0]+1)-1, perm(c[1]+1)-1) for c in cones ]
348
... perm_rays = [ rays[perm(i+1)-1] for i in range(len(rays)) ]
349
... fan2 = Fan(perm_cones, rays=[m*vector(r) for r in perm_rays])
350
... assert fan_2d_echelon_form(fan2) in echelon_forms
351
"""
352
if fan.nrays() == 0:
353
return frozenset()
354
rays = list(fan_2d_cyclically_ordered_rays(fan))
355
echelon_forms = []
356
for i in range(2):
357
for j in range(len(rays)):
358
echelon_forms.append(column_matrix(rays).echelon_form())
359
first = rays.pop(0)
360
rays.append(first)
361
rays.reverse()
362
return frozenset(echelon_forms)
363
364
365
def fan_2d_echelon_form(fan):
366
"""
367
Return echelon form of a cyclically ordered ray matrix.
368
369
INPUT:
370
371
- ``fan`` -- a fan.
372
373
OUTPUT:
374
375
A matrix. The echelon form of the rays in one particular cyclic
376
order.
377
378
EXAMPLES::
379
380
sage: fan = toric_varieties.P2().fan()
381
sage: from sage.geometry.fan_isomorphism import fan_2d_echelon_form
382
sage: fan_2d_echelon_form(fan)
383
[ 1 0 -1]
384
[ 0 1 -1]
385
"""
386
ray_matrix = fan_2d_cyclically_ordered_rays(fan).matrix()
387
return ray_matrix.transpose().echelon_form()
388
389
390