unlisted
ubuntu2004#### Functions for working with reflections, hyperplanes, cutting, and shards123# INPUT: A quiver.4# OUTPUT: The Cartan matrix associated to the undirected graph obtained by5# forgetting the orientation of the quiver.6def cartan(Q):7G = Q.to_undirected()8return 2*identity_matrix(len(G.vertices(sort=True))) - G.adjacency_matrix()910# INPUT: A Cartan matrix11# OUTPUT: A list of matrices representing the reflections associated to the Cartan matrix12def reflections(cartan):13rank = cartan.nrows()14reflection_list = []15for i in range(rank):16reflection = identity_matrix(QQ, rank) - (cartan * diagonal_matrix(QQ, [j == i for j in range(rank)]))17reflection_list.append(reflection)18return reflection_list1920# INPUT: A Cartan matrix, an index (representing a simple root alpha_0),21# and a sequence of indices (representing a sequence of reflections s_1, s_2, ..., s_l)22# OUTPUT: If the expression which applies the reflections to the simple root is not positive, throws a ValueError.23# Otherwise, returns a pair consisting of the root s_l s_{l-1} ... s_1(alpha_0) (the "plane", as a 1 x n matrix)24# and a list of truncations s_l s_{l-1} ... s_{t+1}(alpha_t), which represent the roots cutting the hyperplane.25# (the "fractures", as 1 x n matrices)26def plane_and_fractures(cartan, starting_root, reflect_seq):27rank = cartan.nrows()28# Base case: no reflections29if len(reflect_seq) == 0:30return (Matrix([i == starting_root - 1 for i in range(rank)]), [])31# Recursive step32else:33reflects = reflections(cartan)34old_fractures = plane_and_fractures(cartan, starting_root, reflect_seq[:-1])35next_reflection = reflects[reflect_seq[-1] - 1]36new_plane = old_fractures[0] * next_reflection37difference = new_plane - old_fractures[0]38if not difference[0, reflect_seq[-1] - 1] > 0:39raise ValueError('This is not a positive expression, witnessed by the {0}th reflection.'.format(len(reflect_seq)))40else:41new_fractures = [s * next_reflection for s in old_fractures[1]] + [Matrix([i == reflect_seq[-1] - 1 for i in range(rank)])]42return (new_plane, new_fractures)4344# INPUT: A pair (hyperplane H, a list of hyperplanes) where each hyperplane is45# specified by a 1 x n matrix.46# OUTPUT: A list of vectors defining a hyperplane arrangement in R^{n-1}47# isomorphic to the arrangement within H defined by its intersections48# with the other hyperplanes49def restricted_fractures(planes):50ambient_plane = list(list(planes[0])[0])51fractures = [list(list(p)[0]) for p in planes[1]]52scale = max(ambient_plane)53# find index at which coefficient of H is nonzero54normal_pivot = ambient_plane.index(scale)55ambient_plane.pop(normal_pivot)56ambient_plane_vector_trunc = vector(ambient_plane)57restricted_fractures = []58# for each other hyperplane, subtract appropriate multiple of H to zero out59# previously selected index60for plane in fractures:61rescale = plane[normal_pivot] / scale62plane.pop(normal_pivot)63restricted_fractures.append(vector(plane) - rescale*ambient_plane_vector_trunc)64return restricted_fractures6566# INPUT: A list of vectors representing equations of hyperplanes67# and a point in the space those hyperplanes occupy68# OUTPUT: If the point lies on a hyperplane, throws a ValueError.69# Otherwise, returns a string of '+' and '-', indicating for each hyperplane70# (in the order they were provided) which side the point lies on71def manual_sign_vector(planes, point):72vector = ''73for p in planes:74eval = (Matrix(p) * point)[0]75if eval > 0:76vector += '+'77elif eval < 0:78vector += '-'79else:80raise ValueError('This point lies on a hyperplane.')81return vector8283# INPUT: A Cartan matrix, an index (representing a simple root alpha_0),84# and a sequence of indices (representing a sequence of reflections s_1, s_2, ..., s_l)85# OUTPUT: A list of sign vectors describing the regions of the arrangement of shards within86# the hyperplane defined by s_l s_{l-1} ... s_1(alpha_0). The jth sign describes87# which side of the jth fracture (as output by plane_and_fractures) the region lies on.88def region_signs(cartan, starting_root, reflect_seq):89fractures = restricted_fractures(plane_and_fractures(cartan, starting_root, reflect_seq))90H = HyperplaneArrangements(QQ, tuple('x{0}'.format(i+1) for i in range(cartan.nrows() - 1)))91arr = H([[tuple(f), 0] for f in fractures])92sign_vectors = [manual_sign_vector(fractures, r.representative_point()) for r in arr.regions()]93return sign_vectors9495# INPUT: A Cartan matrix, an index (representing a simple root alpha_0),96# a sequence of indices (representing a sequence of reflections s_1, s_2, ..., s_l),97# and a sequence of signs specifying a region of the arrangement of shards98# within the hyperplane defined by s_l s_{l-1} ... s_1(alpha_0), as output by region_signs.99# OUTPUT: A Polyhedron, the dual cone to the specified shard.100def dual_shard(cartan, start_root, reflect_seq, signs):101p_and_f = plane_and_fractures(cartan, start_root, reflect_seq)102root = p_and_f[0]103fracs = p_and_f[1]104ray_list = []105# specifying that the shard lies on one side of a hyperplane corresponds to106# adding a ray dual to that half-plane to the dual cone of the shard107for i in range(len(fracs)):108if signs[i] == '+':109ray_list.append(list(-fracs[i][0]))110elif signs[i] == '-':111ray_list.append(list(fracs[i][0]))112return Polyhedron(rays=ray_list, lines=[list(root[0])])113114#### Functions for working with representations of preprojective algebras and reflection functors115116# An enriched class for directed graphs in which each edge has a counterpart pointing the other direction.117class DoubleQuiver(sage.graphs.digraph.DiGraph):118119# Constructor can take an existing DoubleQuiver (to prevent repeated edge doubling) or a general DiGraph.120# In the latter case it adds a reverse for every edge. In the new quiver, the original edge is renamed121# by adding a '0' to the end of its name, while its reverse counterpart is named by adding a '1' to the122# end of the original edge's name.123def __init__(self, digraph, multiedges=True, **kwargs):124if isinstance(digraph, DoubleQuiver):125super(DoubleQuiver, self).__init__(digraph, multiedges=True, **kwargs)126self._edge_pairs = digraph.edge_pairs()127else:128_edges = []129self._edge_pairs = {}130for e in digraph.edges(sort=True):131self._edge_pairs[(e[0], e[1], e[2] + '0')] = (e[1], e[0], e[2] + '1')132self._edge_pairs[(e[1], e[0], e[2] + '1')] = (e[0], e[1], e[2] + '0')133_edges.append((e[0], e[1], e[2] + '0'))134_edges.append((e[1], e[0], e[2] + '1'))135super(DoubleQuiver, self).__init__(_edges, multiedges=True, **kwargs)136137# Take an edge as input and return its counterpart.138def flip(self, edge):139return self._edge_pairs[edge]140141def edge_pairs(self):142return self._edge_pairs143144145# INPUT: A representation of a DoubleQuiver.146# OUTPUT: Whether the representation satisfies the relation necessary to be a representation of the preprojective algebra.147def satisfies_preproj_relation(rep):148q = rep.quiver()149for v in q.vertices(sort=True):150dim = rep.dimension(v)151# check preprojective algebra relation at vertex v152if not sum((rep.get_map(q.flip(e)) * rep.get_map(e) * (1 if e[2][-1] == '0' else -1)).matrix() for e in q.outgoing_edge_iterator(v)) == 0:153return False154return True155156157# INPUT: A list of vector spaces and an optional base ring.158# OUTPUT: A tuple (direct sum of spaces, list of inclusion maps as matrices, list of projection maps as matrices)159def direct_sum_with_maps(spaces, ring=QQ):160# empty sum161if len(spaces) == 0:162return (VectorSpace(ring, 0), [], [])163164# proceed recursively, using sum of two vector spaces165else:166#ring = spaces[0].base_ring()167168# take sum of all spaces but the last one169prelim_sum = direct_sum_with_maps(spaces[:-1])170171# take sum of all spaces including the last one172full_sum = prelim_sum[0].direct_sum(spaces[-1])173174# two useful block matrices: inclusion of prelim_sum into full_sum, and inclusion of last space into full_sum.175binary_sum_inclusion_maps = (End(prelim_sum[0]).identity().matrix().augment(Hom(prelim_sum[0], spaces[-1]).zero().matrix()), Hom(spaces[-1], prelim_sum[0]).zero().matrix().augment(End(spaces[-1]).identity().matrix()))176177# compose the first of the above with inclusion maps into prelim_sum178list_of_inclusion_maps = [M * binary_sum_inclusion_maps[0] for M in prelim_sum[1]] + [binary_sum_inclusion_maps[1]]179180# same as above, but for projection maps181binary_sum_projection_maps = ((End(prelim_sum[0]).identity().matrix().augment(Hom(prelim_sum[0], spaces[-1]).zero().matrix())).transpose(), (Hom(spaces[-1], prelim_sum[0]).zero().matrix().augment(End(spaces[-1]).identity().matrix())).transpose())182list_of_projection_maps = [binary_sum_projection_maps[0] * M for M in prelim_sum[2]] + [binary_sum_projection_maps[1]]183184return (full_sum, list_of_inclusion_maps, list_of_projection_maps)185186187# INPUT: A representation of a DoubleQuiver, (the label of) a vertex of the quiver,188# and whether to require that the representation is in NoQuot (assumed by default)189# OUTPUT: If no_quot_check == True and the representation has S_i as a quotient,190# throws a ValueError. Otherwise, returns the representation obtained by applying191# the positive reflection functor at that vertex.192def reflection_functor_plus(rep, vertex, no_quot_check=True):193# check for preprojectivity194if not satisfies_preproj_relation(rep):195raise ValueError('This representation does not satisfy the preprojective relation.')196197# extract underlying parameters of representation198base_ring = rep.base_ring()199Q = rep.quiver()200PQ = Q.path_semigroup()201spaces = {v: rep.get_space(v) for v in Q.vertices(sort=True)}202maps = {e: rep.get_map(e) for e in Q.edges(sort=True)}203204205# pull out the given vertex's space and the arrows / maps incident to the given vertex206Vi = spaces[vertex]207in_edges = Q.incoming_edges([vertex])208maps_in = [maps[e] for e in in_edges]209# make sure paired edges have the same index210out_edges = [Q.flip(e) for e in in_edges]211maps_out = [maps[e] for e in out_edges]212213# sum together all incident spaces214Vdi = direct_sum_with_maps([spaces[e[0]] for e in in_edges], ring=base_ring)215216217# if the space at the given vertex is 0, there's nothing to do.218if Vdi[0].dimension() == 0 and rep.get_space(vertex).dimension() == 0:219return rep220221# concatenate inward maps to get a map Vdi -> Vi222concat_matrix_in = sum(Vdi[2][i] * maps[in_edges[i]].matrix() * (1 if in_edges[i][2][-1] == '0' else -1) for i in range(len(in_edges)))223map_in = linear_transformation(Vdi[0], Vi, concat_matrix_in)224225# concatenate outward maps to get a map Vi -> Vdi226concat_matrix_out = sum(maps[out_edges[i]].matrix() * Vdi[1][i] for i in range(len(out_edges)))227map_out = linear_transformation(Vi, Vdi[0], concat_matrix_out)228229# if the sum of inward maps is not surjective, then rep has the simple at vertex as a quotient230if (no_quot_check and not map_in.is_surjective()):231raise ValueError('This representation has S_{0} as a quotient.'.format(vertex))232233# compute kernel of map Vdi -> Vi234kernel = map_in.kernel()235236# define new maps out of and into kernel237# UPDATE: the ".matrix()" at the end of each expression may be unnecessary for Sage's quiver representation238# constructor. I don't think this messes things up either way, though. Leaving it in for now.239new_maps_out = {out_edges[i]: linear_transformation(Vdi[0], spaces[out_edges[i][1]], Vdi[2][i]).restrict_domain(kernel).matrix() for i in range(len(out_edges))}240new_maps_in = {in_edges[i]: linear_transformation(spaces[in_edges[i][0]], Vdi[0], Vdi[1][i] * concat_matrix_in * concat_matrix_out * (1 if in_edges[i][2][-1] == '0' else -1)).restrict_codomain(kernel).matrix() for i in range(len(in_edges))}241242# update space at given vertex243spaces.update({vertex: kernel})244245# update maps incident to given vertex246maps.update(new_maps_out)247maps.update(new_maps_in)248249# return reflected representation250new_rep = PQ.representation(base_ring, spaces, maps)251return new_rep252253254# INPUT: A representation of a DoubleQuiver, (the label of) a vertex of the quiver,255# and whether to require that the representation is in NoSub (assumed by default)256# OUTPUT: If no_sub_check == True and the representation has S_i as a submodule,257# throws a ValueError. Otherwise, returns the representation obtained by applying258# the negative reflection functor at that vertex.259def reflection_functor_minus(rep, vertex, no_sub_check=True):260# check for preprojectivity261if not satisfies_preproj_relation(rep):262raise ValueError('This representation does not satisfy the preprojective relation.')263264# extract underlying parameters of representation265base_ring = rep.base_ring()266Q = rep.quiver()267PQ = Q.path_semigroup()268spaces = {v: rep.get_space(v) for v in Q.vertices(sort=True)}269maps = {e: rep.get_map(e) for e in Q.edges(sort=True)}270271272# pull out the given vertex's space and the arrows incident to the given vertex273Vi = spaces[vertex]274out_edges = Q.outgoing_edges([vertex])275maps_out = [maps[e] for e in out_edges]276# make sure paired edges have the same index277in_edges = [Q.flip(e) for e in out_edges]278maps_in = [maps[e] for e in in_edges]279280# sum together all incident spaces281Vdi = direct_sum_with_maps([spaces[e[1]] for e in out_edges], ring=base_ring)282283# if the space at the given vertex is 0, there's nothing to do.284if Vdi[0].dimension() == 0 and rep.get_space(vertex).dimension() == 0:285return rep286287# concatenate inward maps (twisted by sign) to get a map Vdi -> Vi288concat_matrix_in = sum(Vdi[2][i] * maps[in_edges[i]].matrix() * (1 if in_edges[i][2][-1] == '0' else -1) for i in range(len(in_edges)))289map_in = linear_transformation(Vdi[0], Vi, concat_matrix_in)290291# concatenate outward maps to get a map Vi -> Vdi292concat_matrix_out = sum(maps[out_edges[i]].matrix() * Vdi[1][i] for i in range(len(out_edges)))293map_out = linear_transformation(Vi, Vdi[0], concat_matrix_out)294295# if the sum of outward maps is not injective, then rep has the simple at vertex as a submodule296if (no_sub_check and not map_out.is_injective()):297raise ValueError('This representation has S_{0} as a subrepresentation.'.format(vertex))298299# compute cokernel of map Vi -> Vdi300cokernel, proj, lift = Vdi[0].quotient_abstract(map_out.image())301302# define new maps out of and into kernel303# UPDATE: the ".matrix()" at the end of each expression may be unnecessary for Sage's quiver representation304# constructor. I don't think this messes things up either way, though. Leaving it in for now.305new_maps_out = {out_edges[i]: (linear_transformation(Vdi[0], spaces[out_edges[i][1]], Vdi[2][i]) * map_out * map_in * lift).matrix() for i in range(len(out_edges))}306new_maps_in = {in_edges[i]: (proj * linear_transformation(spaces[in_edges[i][0]], Vdi[0], Vdi[1][i]) * (1 if in_edges[i][2][-1] == '0' else -1)).matrix() for i in range(len(in_edges))}307308# update space at given vertex309spaces.update({vertex: cokernel})310311# update maps incident to given vertex312maps.update(new_maps_out)313maps.update(new_maps_in)314315# return reflected representation316new_rep = PQ.representation(base_ring, spaces, maps)317return new_rep318319320# INPUT: A representation of a DoubleQuiver and a list of pairs (vertex, sign), where sign is either '+' or '-'.321# OUTPUT: The result of applying the sequence of reflection functors in left-to-right order.322def reflection_functor_seq(rep, vertices):323if len(vertices) == 0:324return rep325elif vertices[-1][1] == '+':326return reflection_functor_plus(reflection_functor_seq(rep, vertices[:-1]), vertices[-1][0])327elif vertices[-1][1] == '-':328return reflection_functor_minus(reflection_functor_seq(rep, vertices[:-1]), vertices[-1][0])329330331# INPUT: A (not necessarily doubled) quiver, a vertex, a list of vertices, and a string of '+' and '-'.332# OUTPUT: The representation obtained by starting with the simple at the first vertex and applying333# the sequence of reflection functors given by the other vertices and signs.334# NOTE: The output of this function may not actually be a shard module!335def shard_module(Q, start_vertex, reflect_vertices, sign_seq):336DQ = DoubleQuiver(Q)337PDQ = DQ.path_semigroup()338reflect_seq = list(zip(reflect_vertices, list(sign_seq)))339return reflection_functor_seq(PDQ.S(QQ, start_vertex), reflect_seq)340341342343# INPUT: A pair (hyperplane H, a list of hyperplanes) where each hyperplane is344# given by a 1 x n matrix.345# OUTPUT: A list of lists, each of which consists of indices into the list in the input346# corresponding to hyperplanes which give the same result when intersected with H.347def walls(p_and_f):348wall_grouping = []349for i in range(len(p_and_f[1])):350independent = True351for j in range(len(wall_grouping)):352w = wall_grouping[j]353subsystem_matrix = Matrix([p_and_f[0][0], p_and_f[1][w[0]][0], p_and_f[1][i][0]])354if rank(subsystem_matrix) == 2:355wall_grouping[j] = w + [i]356independent = False357if independent:358wall_grouping = wall_grouping + [[i]]359return wall_grouping360361362# INPUT: A representation of a DoubleQuiver and a sequence of vertices of the quiver363# OUTPUT: A string of '+' and '-' such that the given representation can be obtained364# by another representation by applying reflection functors at the given indices with365# these signs.366# (In other words, we can apply reflection functors at the given sequence of indices367# in reverse order with the opposite signs, and the intermediate representations thus368# obtained will be NoQuot or NoSub, as appropriate.)369def undo_reflections(rep, reflect_seq):370if len(reflect_seq) == 0:371return ''372try:373new_rep = reflection_functor_minus(rep, reflect_seq[-1])374return undo_reflections(new_rep, reflect_seq[:-1]) + '+'375except ValueError:376new_rep = reflection_functor_plus(rep, reflect_seq[-1])377return undo_reflections(new_rep, reflect_seq[:-1]) + '-'378379380# Given a shard module:381# - finds reduced expression for root382# - finds module's sign sequence with respect to this reduced expression383# - for each wall associated to the reduced expression, flips those signs and tries computing384# the ensuing shard module (NOTE! This may be super inefficient, and for larger situations it may be385# better to implement this with convex geometry jiggery-pokery!)386# - also it depends on the truth of David & Hugh's conjecture, but oh well387# - return the reduced expression, the sign sequence, and the list of walls for which this works388389# This method determines, given an expression for a representation in terms of reflection functors,390# the minimal flips we can make in the signs of those reflection functors391# such that the results are also legitimate sequences of reflection functors.392#393# INPUT: A representation of a DoubleQuiver394# and a pair (vertex, sequence of vertices) representing a positive expression395# (simple root, sequence of reflections) for beta, the dimension vector of the representation396# OUTPUT:397# - the provided expression (for legacy reasons)398# - a sequence of signs such that, if we start with the simple module associated399# to the simple root in our expression and apply the reflection functors400# associated to the reflections in our expression with these signs, we obtain401# the given representation402# - a collection of lists of indices representing signs we can flip in this sequence403# such that we produce another valid sequence of reflection functors.404# Each index represents an entry in the output of walls(), representing one or more hyperplanes405# which cut the hyperplane defined by our dimension vector in the same way.406# - for each list of indices in the previous output, the hyperplanes produced by plane_and_fractures407# which correspond to those indices408def shard_module_walls(module, chosen_expression):409root = module.dimension_vector()410G = module.quiver().to_undirected()411cartan_matrix = cartan(G)/2 + identity_matrix(len(G.vertices(sort=True)))412#if chosen_expression == None:413# expression = get_reduced_expression(cartan_matrix, root)414#else:415expression = chosen_expression416signs = undo_reflections(module, expression[1])417p_and_f = plane_and_fractures(cartan_matrix, expression[0], expression[1])418all_walls = walls(p_and_f)419420border_walls = []421for w in all_walls:422new_signs = ''423for i in range(len(signs)):424if i in w:425if signs[i] == '+':426new_signs = new_signs + '-'427elif signs[i] == '-':428new_signs = new_signs + '+'429else:430new_signs = new_signs + signs[i]431try:432flipped_rep = shard_module(module.quiver(), expression[0], expression[1], new_signs)433border_walls.append(w)434except:435pass436return (expression, signs, border_walls, [p_and_f[1][w[0]] for w in border_walls])437438439440441442443444