Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
181 views
unlisted
ubuntu2004
1
from collections import defaultdict
2
3
# pylint does not know sage
4
from sage.misc.flatten import flatten # pylint: disable=import-error
5
6
import admcycles.diffstrata.levelgraph
7
import admcycles.diffstrata.embeddedlevelgraph
8
9
#################################################################
10
#################################################################
11
# Auxiliary functions:
12
#################################################################
13
#################################################################
14
15
16
def unite_embedded_graphs(gen_LGs):
17
return admcycles.diffstrata.embeddedlevelgraph.EmbeddedLevelGraph(
18
*unite_embedded_k_graphs(
19
gen_LGs,
20
admcycles.diffstrata.levelgraph.LevelGraph))
21
22
23
def unite_embedded_k_graphs(gen_LGs, clsLG):
24
"""
25
Create a (disconnected) EmbeddedLevelGraph from a tuple of tuples that generate EmbeddedLevelGraphs.
26
27
(The name is slightly misleading, but of course it does not make sense to actually unite two complete
28
EmbeddedLevelGraphs, as the checks would (and do!) throw errors otherwise! Therefore, this essentially
29
takes the data of a LevelGraph embedded into each connected componenent of a GeneralisedStratum and
30
returns an EmbeddedLevelGraph on the product.)
31
32
This should be used on (products) of BICs in generalised strata.
33
34
INPUT:
35
36
gen_LGs: tuple
37
A tuple of tuples that generate EmbeddedLevelGraphs.
38
More precisely, each tuple is of the form:
39
40
* X (GeneralisedStratum): Enveloping stratum (should be the same for all tuples!)
41
* LG (LevelGraph): Underlying LevelGraph
42
* dmp (dict): (partial) dictionary of marked points
43
* dlevels (dict): (partial) dictionary of levels
44
45
clsELG: class of the graph to return, either EmbeddedLevelGraph or EmbeddedQuadraticLevelGraph
46
47
clsLG: class of the underlying levelgraph, either LevelGraph or QuadraticLevelGraph
48
49
OUTPUT:
50
51
The (disconnected) LevelGraph obtained from the input with the legs
52
renumbered (continuously, starting with 1), and the levels numbered
53
according to the embedding.
54
"""
55
newgenera = []
56
newlevels = []
57
newlegs = []
58
newpoleorders = {}
59
newedges = []
60
newdmp = {}
61
newdlevels = {}
62
max_leg_number = 0
63
oldX = gen_LGs[0][0] # for check that all belong to the same stratum:
64
for emb_g in gen_LGs:
65
# Unpack tuple:
66
X, LG, dmp, dlevels = emb_g
67
if X != oldX:
68
raise RuntimeError(
69
"Can't unite graphs on different Strata! %r" % gen_LGs)
70
# the genera are just appended
71
newgenera += LG.genera
72
# same for the levels, but while we're at it, we might just as well
73
# replace them by their embedding (then newdlevels will be trivial)
74
# and these should be consistens for all graphs in the tuple.
75
# Thus, newdlevels will be the identity.
76
newlevels += [dlevels[l] for l in LG.levels]
77
# the legs will have to be renumbered
78
leg_dict = {} # old number -> new number
79
legs = 0
80
for i, l in enumerate(flatten(LG.legs)):
81
newlegnumber = max_leg_number + i + 1
82
leg_dict[l] = newlegnumber
83
# while we're at it, we add the pole orders:
84
newpoleorders[newlegnumber] = LG.poleorders[l]
85
# For the dictionary of marked points (for the embedding), we
86
# must distinguish if this is a marked point or a half-edge.
87
# Marked points are simply the ones for which we have a key
88
# in dmp :-)
89
try:
90
newdmp[newlegnumber] = dmp[l]
91
except KeyError:
92
pass
93
legs += 1
94
max_leg_number += legs
95
# append (nested) list of legs:
96
newlegs += [[leg_dict[l] for l in comp] for comp in LG.legs]
97
# finally, the edges are renumbered accordingly:
98
newedges += [(leg_dict[e[0]], leg_dict[e[1]]) for e in LG.edges]
99
# the levels are already numbered according to the embedding dict
100
newdlevels = {l: l for l in newlevels}
101
newLG = clsLG(
102
newgenera, newlegs, newedges, newpoleorders, newlevels
103
)
104
return (X, newLG, newdmp, newdlevels)
105
106
107
def sort_with_dict(l):
108
"""
109
Sort a list and provide a dictionary relating old and new indices.
110
111
If x had index i in l, then x has index sorted_dict[i] in the sorted l.
112
113
Args:
114
l (list): List to be sorted.
115
116
Returns:
117
tuple: A tuple consisting of:
118
list: The sorted list l.
119
dict: A dictionary old index -> new index.
120
"""
121
sorted_list = []
122
sorted_dict = {}
123
for i, (j, v) in enumerate(sorted(enumerate(l), key=lambda w: w[1])):
124
sorted_list.append(v)
125
sorted_dict[j] = i
126
return sorted_list, sorted_dict
127
128
129
def get_squished_level(deg_ep, ep):
130
"""
131
Get the (relative) level number of the level squished in ep.
132
133
This is the index of the corresponding BIC in the profile.
134
135
Args:
136
deg_ep (tuple): enhanced profile
137
ep (tuple): enhanced profile
138
139
Raises:
140
RuntimeError: raised if deg_ep is not a degeneration of ep
141
142
Returns:
143
int: relative level number
144
"""
145
deg_p = deg_ep[0]
146
p = set(ep[0])
147
for i, b in enumerate(deg_p):
148
if b not in p:
149
break
150
else:
151
raise RuntimeError("%r is not a degeneration of %r!" % (deg_ep, p))
152
return i
153
154
#################################################################
155
#################################################################
156
# Auxiliary functions for caching:
157
#################################################################
158
#################################################################
159
160
161
def hash_AG(leg_dict, enh_profile):
162
"""
163
The hash of an AdditiveGenerator, built from the psis and the enhanced profile.
164
165
The hash-tuple is (leg-tuple,profile,index), where profile is
166
changed to a tuple and leg-tuple is a nested tuple consisting of
167
tuples (leg,exponent) (or None).
168
169
Args:
170
leg_dict (dict): dictioary for psi powers (leg -> exponent)
171
enh_profile (tuple): enhanced profile
172
173
Returns:
174
tuple: nested tuple
175
"""
176
if leg_dict is None:
177
leg_hash = ()
178
else:
179
leg_hash = tuple(sorted(leg_dict.items()))
180
return (leg_hash, tuple(enh_profile[0]), enh_profile[1])
181
182
183
def adm_key(sig, psis):
184
"""
185
The hash of a psi monomial on a connected stratum without residue conditions.
186
187
This is used for caching the values computed using admcycles (using
188
GeneralisedStratum.adm_evaluate)
189
190
The signature is sorted, the psis are renumbered accordingly and also
191
sorted (with the aim of computing as few duplicates as possible).
192
193
Args:
194
sig (tuple): signature tuple
195
psis (dict): psi dictionary
196
197
Returns:
198
tuple: nested tuple
199
"""
200
sorted_psis = {}
201
sorted_sig = []
202
psi_by_order = defaultdict(list)
203
# sort signature and relabel psis accordingly:
204
# NOTE: Psis are labelled 'mathematically', i.e. 1,...,len(sig)
205
for new_i, (old_i, order) in enumerate(
206
sorted(enumerate(sig), key=lambda k: k[1])):
207
psi_new_i = new_i + 1
208
psi_old_i = old_i + 1
209
sorted_sig.append(order)
210
if psi_old_i in psis:
211
assert not (psi_new_i in sorted_psis)
212
psi_exp = psis[psi_old_i]
213
sorted_psis[psi_new_i] = psi_exp
214
psi_by_order[order].append(psi_exp)
215
# sort psis for points of same order:
216
ordered_sorted_psis = {}
217
i = 0
218
assert len(sig) == len(sorted_sig)
219
while i < len(sig):
220
order = sorted_sig[i]
221
for j, psi_exp in enumerate(sorted(psi_by_order[order])):
222
assert sorted_sig[i + j] == order
223
ordered_sorted_psis[i + j + 1] = psi_exp
224
while i < len(sig) and sorted_sig[i] == order:
225
i += 1
226
return (tuple(sorted_sig), tuple(sorted(ordered_sorted_psis.items())))
227
228