unlisted
ubuntu2004from collections import defaultdict12# pylint does not know sage3from sage.misc.flatten import flatten # pylint: disable=import-error45import admcycles.diffstrata.levelgraph6import admcycles.diffstrata.embeddedlevelgraph78#################################################################9#################################################################10# Auxiliary functions:11#################################################################12#################################################################131415def unite_embedded_graphs(gen_LGs):16return admcycles.diffstrata.embeddedlevelgraph.EmbeddedLevelGraph(17*unite_embedded_k_graphs(18gen_LGs,19admcycles.diffstrata.levelgraph.LevelGraph))202122def unite_embedded_k_graphs(gen_LGs, clsLG):23"""24Create a (disconnected) EmbeddedLevelGraph from a tuple of tuples that generate EmbeddedLevelGraphs.2526(The name is slightly misleading, but of course it does not make sense to actually unite two complete27EmbeddedLevelGraphs, as the checks would (and do!) throw errors otherwise! Therefore, this essentially28takes the data of a LevelGraph embedded into each connected componenent of a GeneralisedStratum and29returns an EmbeddedLevelGraph on the product.)3031This should be used on (products) of BICs in generalised strata.3233INPUT:3435gen_LGs: tuple36A tuple of tuples that generate EmbeddedLevelGraphs.37More precisely, each tuple is of the form:3839* X (GeneralisedStratum): Enveloping stratum (should be the same for all tuples!)40* LG (LevelGraph): Underlying LevelGraph41* dmp (dict): (partial) dictionary of marked points42* dlevels (dict): (partial) dictionary of levels4344clsELG: class of the graph to return, either EmbeddedLevelGraph or EmbeddedQuadraticLevelGraph4546clsLG: class of the underlying levelgraph, either LevelGraph or QuadraticLevelGraph4748OUTPUT:4950The (disconnected) LevelGraph obtained from the input with the legs51renumbered (continuously, starting with 1), and the levels numbered52according to the embedding.53"""54newgenera = []55newlevels = []56newlegs = []57newpoleorders = {}58newedges = []59newdmp = {}60newdlevels = {}61max_leg_number = 062oldX = gen_LGs[0][0] # for check that all belong to the same stratum:63for emb_g in gen_LGs:64# Unpack tuple:65X, LG, dmp, dlevels = emb_g66if X != oldX:67raise RuntimeError(68"Can't unite graphs on different Strata! %r" % gen_LGs)69# the genera are just appended70newgenera += LG.genera71# same for the levels, but while we're at it, we might just as well72# replace them by their embedding (then newdlevels will be trivial)73# and these should be consistens for all graphs in the tuple.74# Thus, newdlevels will be the identity.75newlevels += [dlevels[l] for l in LG.levels]76# the legs will have to be renumbered77leg_dict = {} # old number -> new number78legs = 079for i, l in enumerate(flatten(LG.legs)):80newlegnumber = max_leg_number + i + 181leg_dict[l] = newlegnumber82# while we're at it, we add the pole orders:83newpoleorders[newlegnumber] = LG.poleorders[l]84# For the dictionary of marked points (for the embedding), we85# must distinguish if this is a marked point or a half-edge.86# Marked points are simply the ones for which we have a key87# in dmp :-)88try:89newdmp[newlegnumber] = dmp[l]90except KeyError:91pass92legs += 193max_leg_number += legs94# append (nested) list of legs:95newlegs += [[leg_dict[l] for l in comp] for comp in LG.legs]96# finally, the edges are renumbered accordingly:97newedges += [(leg_dict[e[0]], leg_dict[e[1]]) for e in LG.edges]98# the levels are already numbered according to the embedding dict99newdlevels = {l: l for l in newlevels}100newLG = clsLG(101newgenera, newlegs, newedges, newpoleorders, newlevels102)103return (X, newLG, newdmp, newdlevels)104105106def sort_with_dict(l):107"""108Sort a list and provide a dictionary relating old and new indices.109110If x had index i in l, then x has index sorted_dict[i] in the sorted l.111112Args:113l (list): List to be sorted.114115Returns:116tuple: A tuple consisting of:117list: The sorted list l.118dict: A dictionary old index -> new index.119"""120sorted_list = []121sorted_dict = {}122for i, (j, v) in enumerate(sorted(enumerate(l), key=lambda w: w[1])):123sorted_list.append(v)124sorted_dict[j] = i125return sorted_list, sorted_dict126127128def get_squished_level(deg_ep, ep):129"""130Get the (relative) level number of the level squished in ep.131132This is the index of the corresponding BIC in the profile.133134Args:135deg_ep (tuple): enhanced profile136ep (tuple): enhanced profile137138Raises:139RuntimeError: raised if deg_ep is not a degeneration of ep140141Returns:142int: relative level number143"""144deg_p = deg_ep[0]145p = set(ep[0])146for i, b in enumerate(deg_p):147if b not in p:148break149else:150raise RuntimeError("%r is not a degeneration of %r!" % (deg_ep, p))151return i152153#################################################################154#################################################################155# Auxiliary functions for caching:156#################################################################157#################################################################158159160def hash_AG(leg_dict, enh_profile):161"""162The hash of an AdditiveGenerator, built from the psis and the enhanced profile.163164The hash-tuple is (leg-tuple,profile,index), where profile is165changed to a tuple and leg-tuple is a nested tuple consisting of166tuples (leg,exponent) (or None).167168Args:169leg_dict (dict): dictioary for psi powers (leg -> exponent)170enh_profile (tuple): enhanced profile171172Returns:173tuple: nested tuple174"""175if leg_dict is None:176leg_hash = ()177else:178leg_hash = tuple(sorted(leg_dict.items()))179return (leg_hash, tuple(enh_profile[0]), enh_profile[1])180181182def adm_key(sig, psis):183"""184The hash of a psi monomial on a connected stratum without residue conditions.185186This is used for caching the values computed using admcycles (using187GeneralisedStratum.adm_evaluate)188189The signature is sorted, the psis are renumbered accordingly and also190sorted (with the aim of computing as few duplicates as possible).191192Args:193sig (tuple): signature tuple194psis (dict): psi dictionary195196Returns:197tuple: nested tuple198"""199sorted_psis = {}200sorted_sig = []201psi_by_order = defaultdict(list)202# sort signature and relabel psis accordingly:203# NOTE: Psis are labelled 'mathematically', i.e. 1,...,len(sig)204for new_i, (old_i, order) in enumerate(205sorted(enumerate(sig), key=lambda k: k[1])):206psi_new_i = new_i + 1207psi_old_i = old_i + 1208sorted_sig.append(order)209if psi_old_i in psis:210assert not (psi_new_i in sorted_psis)211psi_exp = psis[psi_old_i]212sorted_psis[psi_new_i] = psi_exp213psi_by_order[order].append(psi_exp)214# sort psis for points of same order:215ordered_sorted_psis = {}216i = 0217assert len(sig) == len(sorted_sig)218while i < len(sig):219order = sorted_sig[i]220for j, psi_exp in enumerate(sorted(psi_by_order[order])):221assert sorted_sig[i + j] == order222ordered_sorted_psis[i + j + 1] = psi_exp223while i < len(sig) and sorted_sig[i] == order:224i += 1225return (tuple(sorted_sig), tuple(sorted(ordered_sorted_psis.items())))226227228