-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmutable_labelled_map.py
More file actions
1331 lines (1007 loc) · 47.2 KB
/
mutable_labelled_map.py
File metadata and controls
1331 lines (1007 loc) · 47.2 KB
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
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from sage.all import Permutation # Import sage library
from labelled_map import LabelledMap, transitiveCouplePermutation
from mutable_topological_demi_edge import MutableTopologicalDemiEdge
from rotating_permutation_utils_abstractor import RotatingPermutationUtilsAbstractor
from rotating_permutation import RotatingPermutation
class MutableLabelledMap(LabelledMap):
"""
This class represents a mutable labelled map.
Note that the indices of the halfedges may change (without guarantee on which halfedges will be changed) when the map is modified, so if you wish to keep a reference to a particular demi-edge, consider instead its MutableTopologicalDemiEdge with ``self.X(index)``; it is guaranteed that when getting a MutableTopologicalDemiEdge from self, after modifications of the map, the topological demi-edge will always point to the correct label of the demi-edge (unless the demi-edge is deleted).
All the methods returning maps may return a LabelledMap (not a MutableLabelledMap); you may need to make them mutable by casting them (e.g if your map is myMap, use myMapMutable = MutableLabelledMap(lmap = myMap))
Attributes
----------
sigma : Permutation or MapPermutation
Permutation that maps a half-edge to the half-edge incident to
it in a anti-clockwise direction around the vertex it belongs to.
alpha : Permutation or MapPermutation
Fixed-point free involution whose cycles are given by the edges.
phi: Permutation or MapPermutation
Permutation that maps a half-edges to the half-edge next to it
in his face in the clockwise orde.
m: The number of edge of the map
g: The genus of the map
f: The number of face of the map
q: The number of demi edges of the map
"""
def __init__(
self,
sigma: Permutation | None = None,
alpha: Permutation | None = None,
adj: list[tuple[int, ...]] | None = None,
lmap: LabelledMap | None = None
):
r"""
Init the MutableLabelledMap
INPUT:
- ``sigma`` -- Permutation | MapPermutation | None ; Permutation that maps a half-edge
to the half-edge incident to it in anti-clockwise direction around
the vertex it belongs to.
- ``alpha`` -- Permutation | MapPermutation | None ;Fixed-point free involution whose cycles are given by the edges.
- ``ajd``-- list[tuple[int, ...]] | None ; and adjacency list be careful the order of the
node in your adjaceny will be used to choose the embedding
- ``lmap`` -- LabelledMap | None
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
NOTE:
O(mlog(m)) where m is the size of the map
"""
if isinstance(lmap, LabelledMap):
self.__init__(lmap.sigma, lmap.alpha)
return
super().__init__(sigma, alpha, adj)
self.sigma = RotatingPermutation(self.sigma)
self.alpha = RotatingPermutation(self.alpha)
self.phi = RotatingPermutation(self.phi)
self.phiUtilsAbstractor = RotatingPermutationUtilsAbstractor(self.phi)
self.sigmaUtilsAbstractor = RotatingPermutationUtilsAbstractor(
self.sigma)
for e in range(1, self.q + 1):
self.topologicalMap[e] = MutableTopologicalDemiEdge(self, e)
def X(self, demiEdge: int) -> MutableTopologicalDemiEdge:
"""
Return the MutableTopologicalDemiEdge associated to demiEdge.
INPUT:
- ``demiEdge`` -- int ; an index associated to a demiEdge
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: m = MutableLabelledMap(alpha = alpha,sigma=sigma)
sage: m.X(1)
X(1)
NOTE:
Complexity is O(1)
"""
return self.getTopologicalDemiEdge(demiEdge)
def getTopologicalDemiEdge(self, demiEdge: int) -> MutableTopologicalDemiEdge:
"""
The MutableTopologicalDemiEdge associated to demiEdge
INPUT:
- ``demiEdge`` -- int ; An index associated to a demiEdge
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: m = MutableLabelledMap(alpha = alpha,sigma=sigma)
sage: m.getTopologicalDemiEdge(1)
X(1)
NOTE:
Complexity is O(1)
"""
return self.topologicalMap[demiEdge]
def willStillBeConnectedAfterEdgeDeletion(self, demiEdge: int) -> bool:
"""
This method return a boolean indicating if self will still be connected after deleting the edge corresponding to demiEdge
If self of is a planar map it is efficient O(log(m)) otherwise it is O(m)
INPUT:
- ``demiEdge`` -- int; An index representing the demi edge corresponding to the edge we want to delete
OUTPUT:
A boolean indicating whether or not self will still be connected if the edge is deleted
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm.willStillBeConnectedAfterEdgeDeletion(2)
False
NOTE:
O(log(m)) if self is a Planar map otherwise O(m)
"""
# In case of higher genus than 0
if self.genus() > 0:
return self._willStillBeConnectedAfterEdgeDeletionHighGenus(
demiEdge)
if self.m == 1:
return False
otherHalf = self.alpha(demiEdge)
# If they are not on the same face
# It means that it will still be connected after
return (not self.areOnTheSameFace(demiEdge, otherHalf))
def willStillBeConnectedAfterNodeDeletion(self, demiEdge: int) -> bool:
"""
This method return a boolean indicating if self will still be connected after deleting the node corresponding to demiEdge
If self of is a planar map it is efficient O(deg(node)*log(m)) otherwise it is O(m)
INPUT:
- ``demiEdge`` -- int ; An index representing the demi edge corresponding to the node we want to delete
OUTPUT:
A boolean indicating whether or not self will still be connected if the node is deleted
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm.willStillBeConnectedAfterNodeDeletion(2)
False
NOTE:
O(deg(node)log(m)) where node is the node attached to demiEdge if self is planar map otherwise O(m+deg(node)log(m))
"""
# In case of higher genus than 0
if self.genus() > 0:
return self._willStillBeConnectedAfterNodeDeletionHighGenus(
demiEdge)
curDemiEdge = demiEdge
N = self.sigma.numberInCycle(demiEdge)
# If the node has only one Edge
if N == 1:
return True
lst = []
# We're looking at each demiEdge on the node
for _ in range(N):
lst.append(curDemiEdge)
# Here we see that our node is link to a node with only one edge
# We thus returned false
if self.phi(curDemiEdge) == self.alpha(curDemiEdge):
return False
curDemiEdge = self.sigma(curDemiEdge)
# WE MUST NOT have two demiEdges on the node on the same face
# for our map to stay connected
return not self.checkTwoInTheSameFace(lst)
def _willStillBeConnectedAfterEdgeDeletionHighGenus(self, demiEdge: int) -> bool:
"""
A helper function use in case of high genus (>0) to check if after deleting the edge associated to demiEdge self
will still be connected.
INPUT:
- ``demiEdge`` -- int
OUTPUT:
A boolean indicating if self will still be connected after demiEdge deletion
EXAMPLES::
sage: alphaH = Permutation([(1,2),(3,4),(5,6)])
sage: sigmaH = Permutation([(1,4,5,3),(6,2)])
sage: mmH = MutableLabelledMap(sigma=sigmaH,alpha=alphaH)
sage: mmH.g
1
sage: mmH._willStillBeConnectedAfterEdgeDeletionHighGenus(1)
True
NOTE:
O(m)
"""
try:
myCopy = self.copy()
myCopy._BruteDeleteEdge(demiEdge)
return transitiveCouplePermutation(myCopy.sigma, myCopy.alpha)
except BaseException:
return False
def _willStillBeConnectedAfterNodeDeletionHighGenus(self, demiEdge: int) -> bool:
"""
A helper function use in case of high genus (>0) to check if after deleting the node associated to demiEdge self
will still be connected.
INPUT:
- ``demiEdge`` -- int
OUTPUT:
A boolean indicating if self will still be connected after the deletion of the node on which demiEdge
is attached
EXAMPLES::
sage: alphaH = Permutation([(1,2),(3,4),(5,6)])
sage: sigmaH = Permutation([(1,4,5,3),(6,2)])
sage: mmH = MutableLabelledMap(sigma=sigmaH,alpha=alphaH)
sage: mmH.g
1
sage: mmH._willStillBeConnectedAfterNodeDeletionHighGenus(1)
False
NOTE:
O(m+deg(node)log(m))
"""
try:
myCopy = self.copy()
myCopy._BruteDeleteNode(demiEdge)
return transitiveCouplePermutation(myCopy.sigma, myCopy.alpha)
except BaseException:
return False
def addEdge(self, startDemiEdge: int, endDemiEdge: int) -> tuple[MutableTopologicalDemiEdge, MutableTopologicalDemiEdge]:
"""
This will add an edge between the node of startDemiEdge to endDemiEdge(note that they
need to be on the same node otherwise this will raise an error), the edge will be added as follow ,
let denote (A,B) the demi edges composing this new edge A will be on the same node as startDemiEdge but before it
and B on the same node as endDemiEdge but before it.It will return two MutableTopologicalDemiEdge topoDemiEdgeA,topoDemiEdgeB
corresponding to the new demi edge A and B
INPUT:
- ``startDemiEdge`` -- int ; A demi edge of self
- ``endDemiEdge`` -- int ; A demi edge of self
- ``startDemiEdge`` -- int
- ``endDemiEdge`` -- int ;must be on the same face as startDemiEdge or it will raise an error
OUTPUT:
topoDemiEdgeA,topoDemiEdgeB as described above
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm.faces()
[(1, 3, 2, 5, 4, 7, 11, 16, 18, 13, 12, 15, 14, 19, 20, 17, 9, 8, 10, 6)]
sage: mm.addEdge(3,7)
(X(22), X(21))
sage: mm.faces()
[(1, 22, 7, 11, 16, 18, 13, 12, 15, 14, 19, 20, 17, 9, 8, 10, 6),
(2, 5, 4, 21, 3)]
NOTE:
O(log(m))
"""
if not self.areOnTheSameFace(startDemiEdge, endDemiEdge):
raise ValueError(
f"{startDemiEdge} and {endDemiEdge} aren't on the same face")
# This will go before endDemiEdge
newIndexEnd = self.q + 1
# This will go before startDemiEdge
newIndexStart = self.q + 2
# We add the new edge to alpha
self._addEdgeToAlpha()
# We add the new edge to sigma
# The order must be coherent with the choice made
# newIndexStart,newIndexEnd
self.sigma.addBefore(endDemiEdge)
self.sigma.addBefore(startDemiEdge)
# We add the new demi edge to phi
self.phi.cutAdd(startDemiEdge, endDemiEdge, newIndexStart, newIndexEnd)
self.topologicalMap[newIndexStart] = MutableTopologicalDemiEdge(
self, newIndexStart)
self.topologicalMap[newIndexEnd] = MutableTopologicalDemiEdge(
self, newIndexEnd)
return self.X(newIndexStart), self.X(newIndexEnd)
def _swapTopologicalDemiEdgeValue(self, demiEdge: int, otherDemiEdge: int) -> None:
"""
This will change the index of the topologica demi edge associate to the demi edge of label demi edge to otherDemiEdge
and same thing but swapping the role or demiEdge and otherDemiEdge
INPUT:
- ``demiEdge`` -- int
- ``otherDemiEdge`` -- int
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: A = mm.X(1)
sage: B = mm.X(2)
sage: A,B
(X(1), X(2))
sage: mm._swapTopologicalDemiEdgeValue(1,2)
sage: A,B
(X(2), X(1))
NOTE:
O(1)
"""
topoDemiEdge = self.X(demiEdge)
otherTopoDemiEdge = self.X(otherDemiEdge)
topoDemiEdge._swapIndex(otherTopoDemiEdge)
self.topologicalMap[demiEdge] = otherTopoDemiEdge
self.topologicalMap[otherDemiEdge] = topoDemiEdge
def simpleSwap(self, demiEdge: int, otherDemiEdge: int) -> None:
"""
This function relabel demiEdge into otherDemiEdge and otherDemiEdge into demiEdge
INPUT:
- ``demiEdge`` -- int
- ``otherDemiEdge`` -- int
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: A = mm.X(1)
sage: B = mm.X(2)
sage: A,B
(X(1), X(2))
sage: mm.simpleSwap(2,1)
sage: A,B
(X(2), X(1))
NOTE:
O(log(m))
"""
self._swapTopologicalDemiEdgeValue(demiEdge, otherDemiEdge)
self.phi.swapIndex(demiEdge, otherDemiEdge)
self.alpha.swapIndex(demiEdge, otherDemiEdge)
self.sigma.swapIndex(demiEdge, otherDemiEdge)
def labelToTheEnd(self, listIndexes: list[int]) -> None:
"""
This function relabel the indexes in listIndexes to take
if there is say k indexes in listIndexes (and they are valid) it
will relabel them into value in m,...,m-k+1
INPUT:
- ``listIndexes`` -- List[int]; A list of valid demi edge in self( otherwise it will raise an error)
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm.faces()
[(1, 3, 2, 5, 4, 7, 11, 16, 18, 13, 12, 15, 14, 19, 20, 17, 9, 8, 10, 6)]
sage: mm.labelToTheEnd([7,5,3])
sage: mm.faces()
[(1, 18, 2, 19, 4, 20, 11, 16, 3, 13, 12, 15, 14, 5, 7, 17, 9, 8, 10, 6)]
NOTE:
O(klog(m)) where k = len(listIndexes)
"""
for i in listIndexes:
if i != int(i) or i > self.q or i <= 0:
raise ValueError("The list of indexes isn't correct")
corres = self.phi.labelToTheEnd(listIndexes)
self.alpha.labelToTheEnd(listIndexes)
self.sigma.labelToTheEnd(listIndexes)
for index in listIndexes:
if index in corres:
self._swapTopologicalDemiEdgeValue(index, corres[index])
def _BruteDeleteEdge(self, demiEdge: int) -> None:
"""
This is an helper method it delete the demiEdge but don't check for
connectivity before , if use in the wrong way it can break the connectivity invariant
and thus many other method
INPUT:
- ``demiEdge`` -- int ; demi-edge on the edge to delete
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm.faces()
[(1, 3, 2, 5, 4, 7, 11, 16, 18, 13, 12, 15, 14, 19, 20, 17, 9, 8, 10, 6)]
sage: mm.addEdge(3,11)
(X(22), X(21))
sage: mm.faces()
[(1, 22, 11, 16, 18, 13, 12, 15, 14, 19, 20, 17, 9, 8, 10, 6),
(2, 5, 4, 7, 21, 3)]
sage: mm.deleteEdge(22)
sage: mm.faces()
[(1, 3, 2, 5, 4, 7, 11, 16, 18, 13, 12, 15, 14, 19, 20, 17, 9, 8, 10, 6)]
NOTE:
O(log(m))
"""
otherDemiEdge = self.alpha(demiEdge)
if demiEdge != self.q or otherDemiEdge != self.q-1:
self.labelToTheEnd([demiEdge, otherDemiEdge])
self._BruteDeleteEdge(self.q)
return
# TopologicalDemiEdge thing
topoDemiEdge = self.X(demiEdge)
otherTopoDemiEdge = topoDemiEdge.c
self._removeTopologicalDemiEdge(topoDemiEdge)
self._removeTopologicalDemiEdge(otherTopoDemiEdge)
# Actual removing
self.sigma.deleteLastKIndex(2)
self.alpha.deleteLastKIndex(2)
if not self.areOnTheSameFace(demiEdge, otherDemiEdge):
# We are merging the face
self.phi.mergeDelete(demiEdge, otherDemiEdge)
else:
# Because demiEdge doesn't break the connectivity
# demiEdge and otherDemiEdge are on the same face
# And the face will be cut
# Look Strange cause it mean that deleting a edge will increase the
# Number of face but not so much because it won't increase the number of
# On the current surface just the map obteined has more face on
# The new lower genus surface
# Note that this case only happen in genus > 0
self.phi.cutDelete(demiEdge, otherDemiEdge)
def deleteEdge(self, demiEdge: int, trust=False) -> None:
"""
This function delete demiEdge from self in case of planar map it is efficient O(log(m)) otherwise O(m) for higher genus
map if trust = False. If trust = True as a parameter it is always of complexity O(log(m)), but it can break the invariant
saying that the map is connected thus make all the other method unsafe by default trust = False.
INPUT:
- ``demiEdge`` -- int ; a demi edge corresponding on the edge to delete
- ``trust`` -- boolean ; a parameter telling the function if it should trust the fact that the map will stay connected after deleting demiEdge default is False
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm.addEdge(3,11)
(X(22), X(21))
sage: mm.faces()
[(1, 22, 11, 16, 18, 13, 12, 15, 14, 19, 20, 17, 9, 8, 10, 6),
(2, 5, 4, 7, 21, 3)]
sage: mm.deleteEdge(22)
sage: mm.faces()
[(1, 3, 2, 5, 4, 7, 11, 16, 18, 13, 12, 15, 14, 19, 20, 17, 9, 8, 10, 6)]
NOTE:
O(log(m)) if self is planar or trust = True otherwise O(m)
"""
if not trust and not self.willStillBeConnectedAfterEdgeDeletion(
demiEdge):
raise ValueError(
f"After deleting the edge associated to {demiEdge} the graph won't be connected anymore")
self._BruteDeleteEdge(demiEdge)
def _addEdgeToAlpha(self) -> None:
"""
Add one edge compose of demi edges self.size() and self.size()+1 to alpha toward the end
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm.alpha.pretty_print()
Rotating permutation: [(1, 3), (2, 5), (4, 6), (7, 9), (8, 10), (11, 13), (12, 15), (14, 17), (16, 18), (19, 20)]
sage: mm._addEdgeToAlpha()
sage: mm.alpha.pretty_print()
Rotating permutation: [(1, 3), (2, 5), (4, 6), (7, 9), (8, 10), (11, 13), (12, 15), (14, 17), (16, 18), (19, 20), (21, 22)]
NOTE:
O(log(m))
"""
self.alpha.stretch(2)
self.alpha.addAfterGeneral(self.alpha.size(), self.alpha.size() - 1)
def _addTopologicalDemiEdge(self, demiEdge: int) -> None:
"""
Add a new MutableTopologicalDemiEdge to self with index associated to demiEdge
INPUT:
- ``demiEdge`` -- int
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: try:
....: mm.X(42)
....: except:
....: print("NOT TOPO")
....:
NOT TOPO
sage: mm._addTopologicalDemiEdge(42)
sage: mm.X(42)
X(42)
NOTE:
O(1)
"""
self.topologicalMap[demiEdge] = MutableTopologicalDemiEdge(
self, demiEdge)
def _removeTopologicalDemiEdge(self, topoDemiEdge: MutableTopologicalDemiEdge) -> None:
"""
Remove and make invalid topoDemiEdge
INPUT:
- ``topoDemiEdge`` -- TopologicalDemiEdge
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: A = mm.X(10)
sage: mm._removeTopologicalDemiEdge(A)
sage: try:
....: mm.X(10)
....: except:
....: print("NOT TOPO")
....:
NOT TOPO
NOTE:
O(1)
"""
self.topologicalMap.pop(topoDemiEdge.raw)
topoDemiEdge._invalidate()
def addEdgeAfter(self, demiEdge: int) -> MutableTopologicalDemiEdge:
"""
This function will create an new edge attached to the same node as demi edge such that it is after
demiEdge in the trigonometric order.
INPUT:
- ``demiEdge`` -- int
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: A = mm.X(10)
sage: A.n,A.pn
(X(10), X(10))
sage: mm.addEdgeAfter(10)
X(22)
sage: mm.addEdgeBefore(10)
X(24)
sage: A.n,A.pn
(X(21), X(23))
NOTE:
O(log(m))
"""
newDemiEdge = self.q + 1
otherNewDemiEdge = self.q + 2
# TopologicalDemiEdge things
self._addTopologicalDemiEdge(newDemiEdge)
self._addTopologicalDemiEdge(otherNewDemiEdge)
# Updating sigma
self.sigma.addAfter(demiEdge)
self.sigma.stretch(1)
# Updating alpha
self._addEdgeToAlpha()
# Updating phi
self.phi.addAfter(self.alpha(demiEdge))
self.phi.addAfter(newDemiEdge)
return self.X(otherNewDemiEdge)
def addEdgeBefore(self, demiEdge: int) -> MutableTopologicalDemiEdge:
"""
This function will create an new edge attached to the same node as demi edge such that it is before
demiEdge in the trigonometric order.
INPUT:
- ``demiEdge`` -- int
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: A = mm.X(10)
sage: A.n,A.pn
(X(10), X(10))
sage: mm.addEdgeAfter(10)
X(22)
sage: mm.addEdgeBefore(10)
X(24)
sage: A.n,A.pn
(X(21), X(23))
NOTE:
O(log(m))
"""
return self.addEdgeAfter(self.sigma.inverseApply(demiEdge))
def deleteNode(self, demiEdge: int, trust=False) -> None:
"""
This method will delete the node attached to demiEdge, when trust = False (default) it is efficient in case of planar map O(deg(node)*log(m))
but for higher genus map it is of complexity O(m+deg(node)*log(m)). If trust = True as a parameter it is always of complexity O(log(m)), but it can break the invariant
saying that the map is connected thus make all the other method unsafe by default trust = False.
It will raise an error if the graph isn't connected after the operation and trust is set to False.
INPUT:
- ``demiEdge`` -- int ; The demi edge on the node to delete
- ``trust`` -- bool ; a parameter telling the method if it should trust the fact that the map will stay connected after deleting demiEdge default is False
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm.nodes()
[(1, 2, 4),
(3,),
(5,),
(6, 7, 8),
(9, 11, 12, 14),
(10,),
(13, 16),
(15,),
(17, 19),
(18,),
(20,)]
sage: mm.deleteNode(3)
sage: mm.nodes()
[(1, 17),
(2, 4),
(3,),
(5,),
(6, 7, 8),
(9, 11, 12, 14),
(10,),
(13, 16),
(15,),
(18,)]
NOTE:
O(deg(node)*log(m)) if self is planar and O(m+deg(node)*log(m)) otherwise
"""
if not trust and not self.willStillBeConnectedAfterNodeDeletion(
demiEdge):
raise ValueError(
f"After deleting the node associated to {demiEdge} the graph won't be connected anymore")
self._BruteDeleteNode(demiEdge)
def _BruteDeleteNode(self, demiEdge: int) -> None:
"""
This function will delete the node attached to self without checking if it will break
the connectivity invariant thus it i dangerous if not used correctly
INPUT:
- ``demiEdge`` -- int ; The demi edge on the node to delete
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm.nodes()
[(1, 2, 4),
(3,),
(5,),
(6, 7, 8),
(9, 11, 12, 14),
(10,),
(13, 16),
(15,),
(17, 19),
(18,),
(20,)]
sage: mm._BruteDeleteNode(3)
sage: mm.nodes()
[(1, 17),
(2, 4),
(3,),
(5,),
(6, 7, 8),
(9, 11, 12, 14),
(10,),
(13, 16),
(15,),
(18,)]
NOTE:
O(deg(node)*log(m))
"""
curDemiEdge = demiEdge
N = self.numberInTheSameNode(curDemiEdge)
while N > 0:
self.simpleSwap(curDemiEdge, self.q)
curDemiEdge = self.q
self.simpleSwap(self.alpha(curDemiEdge), self.q - 1)
otherDemiEdge = self.q - 1
nxtDemiEdge = self.sigma(curDemiEdge)
if self.areOnTheSameNode(otherDemiEdge, curDemiEdge):
if nxtDemiEdge == otherDemiEdge:
nxtDemiEdge = self.sigma(otherDemiEdge)
N -= 2
else:
N -= 1
self._BruteDeleteEdge(curDemiEdge)
curDemiEdge = nxtDemiEdge
def contractFace(self, demiEdge: int) -> None:
"""
Contract the face on which demiEdge is in
INPUT:
- ``demiEdge`` -- int
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm.addEdge(3,4)
(X(22), X(21))
sage: mm.faces()
[(1, 22, 4, 7, 11, 16, 18, 13, 12, 15, 14, 19, 20, 17, 9, 8, 10, 6),
(2, 5, 21, 3)]
sage: mm.addEdge(4,7)
(X(24), X(23))
sage: mm.faces()
[(1, 22, 24, 7, 11, 16, 18, 13, 12, 15, 14, 19, 20, 17, 9, 8, 10, 6),
(2, 5, 21, 3),
(4, 23)]
sage: mm.contractFace(5)
sage: mm.faces()
[(1, 3, 17, 9, 8, 10, 6, 5, 7, 11, 16, 18, 13, 12, 15, 14), (2, 4)]
NOTE:
O(tlog(m)) where t is the number of edge on the face containing demiEdge
"""
if self.numberOfFaces() <= 2:
raise ValueError("Cannot contract a face when there is <= 2 faces")
curDemiEdge = demiEdge
N = self.numberInTheSameFace(curDemiEdge)
while N > 0:
self.simpleSwap(curDemiEdge, self.q)
curDemiEdge = self.q
self.simpleSwap(self.alpha(curDemiEdge), self.q - 1)
otherDemiEdge = self.q - 1
nxtDemiEdge = self.phi(curDemiEdge)
if self.areOnTheSameFace(otherDemiEdge, curDemiEdge):
if nxtDemiEdge == otherDemiEdge:
nxtDemiEdge = self.phi(otherDemiEdge)
N -= 2
else:
N -= 1
self.contractEdge(curDemiEdge)
curDemiEdge = nxtDemiEdge
def copy(self) -> "MutableLabelledMap":
"""
OUTPUT:
Returns A copy of self
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm.copy() == mm
True
NOTE:
O(m)
"""
return MutableLabelledMap(sigma=Permutation(self.sigma.to_list()), alpha=Permutation(self.alpha.to_list()))
def contractEdge(self, demiEdge: int) -> None:
"""
Contract in self the edge corresponding to demiEdge,demiEdge is on a loop edge it will just delete the edge
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm.faces()
[(1, 3, 2, 5, 4, 7, 11, 16, 18, 13, 12, 15, 14, 19, 20, 17, 9, 8, 10, 6)]
sage: mm.contractEdge(3)
sage: mm.faces()
[(1, 3, 17, 9, 8, 10, 6, 2, 5, 4, 7, 11, 16, 18, 13, 12, 15, 14)]
NOTE:
O(log(m))
"""
if self.m == 1:
raise ValueError(
"Cannot contract an edge in a map with only one edge")
# If this is a loop
if self.areOnTheSameNode(demiEdge, self.alpha(demiEdge)):
# We delete the edge
# We can trust here cause a loop can't make the graph not connected
self.deleteEdge(demiEdge, trust=True)
return
if demiEdge != self.q:
self.labelToTheEnd([demiEdge, self.alpha(demiEdge)])
self.contractEdge(self.q)
return
otherDemiEdge = self.alpha(demiEdge)
# Merging the nodes here
self.sigma.mergeDelete(demiEdge, otherDemiEdge)
self.phi.deleteLastKIndex(2)
self.alpha.deleteLastKIndex(2)
def copyOnDemiEdge(self, demiEdge: int, otherMap: LabelledMap, otherDemiEdge: int) -> tuple[MutableTopologicalDemiEdge, list[MutableTopologicalDemiEdge]]:
"""
Given that demiEdge is such that it is attached to a node of degree one (otherwise this function will raise
an error),this function will attach demiEdge before otherDemiEdge and then copy otherMap from this point on,
I will return a MutableTopologicalDemiEdge corresponding to demiEdge in self but also a list of MutableTopologicalDemiEdge call it topoDemiEdgeList
corresponding to the new MutableTopologicalDemiEdge of all the demi edge added from otherMap, such if a demi edge is of index i
in otherMap than topoDemiEdgeList[i] is the MutableTopologicalDemiEdge corresponding to the copy of the demi edge inself.Note that this function
won't modify otherMap(if otherMap isn't strictly the same object in memory as self).
INPUT:
- ``demiEdge`` -- int ; A demi edge on a node of degree one in self
- ``otherMap`` -- LabelledMap; Another map it can be LabelledMap,RootedMap or MutableLabelledMap otherDemiEdge: A demi edge of otherMap
EXAMPLES::
sage: alpha = Permutation([3, 5, 1, 6, 2, 4, 9, 10, 7, 8, 13, 15, 11, 17, 12, 18, 14, 16, 20, 19])
sage: sigma = Permutation([2, 4, 3, 1, 5, 7, 8, 6, 11, 10, 12, 14, 16, 9, 15, 13, 19, 18, 17, 20])
sage: mm = MutableLabelledMap(alpha=alpha,sigma=sigma)
sage: mm.n
11
sage: lst = mm.copyOnDemiEdge(3,mm,4)
sage: mm.n
21
NOTE:
O(p(log(m)+log(p))) where p = otherMap.m and m is the number of edge of self,
note that it is much more efficient than O(p+m) mainly when m>>p
"""
if not self.sigma(demiEdge) == demiEdge:
raise ValueError(
f"{demiEdge} must be on a node with degree one to call copyOnDemiEdge on it")
C = self.q
otherMap = otherMap.copy()
# Alpha modification
alphaCyclesToAdd = otherMap.alpha.to_cycles()
for i in range(len(alphaCyclesToAdd)):
a, b = alphaCyclesToAdd[i]
alphaCyclesToAdd[i] = [a + C, b + C]
self.alpha.addCycles(alphaCyclesToAdd)