From e8de01ffac34d7a9bd27a2b81fefe728fad1dbb1 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sun, 1 Sep 2013 17:48:13 +0000 Subject: [PATCH 01/10] #15137: Implement right-angled Artin groups and general Coxeter groups. --- src/doc/en/reference/groups/index.rst | 1 + src/sage/groups/all.py | 2 + src/sage/groups/raag.py | 423 ++++++++++++++++++++++++++ 3 files changed, 426 insertions(+) create mode 100644 src/sage/groups/raag.py diff --git a/src/doc/en/reference/groups/index.rst b/src/doc/en/reference/groups/index.rst index 384f0976103..3aeb5b20c14 100644 --- a/src/doc/en/reference/groups/index.rst +++ b/src/doc/en/reference/groups/index.rst @@ -12,6 +12,7 @@ Groups sage/groups/finitely_presented sage/groups/finitely_presented_named sage/groups/braid + sage/groups/raag sage/groups/abelian_gps/abelian_group sage/groups/abelian_gps/values sage/groups/abelian_gps/dual_abelian_group diff --git a/src/sage/groups/all.py b/src/sage/groups/all.py index 8f473c530e5..a4a5f525a5c 100644 --- a/src/sage/groups/all.py +++ b/src/sage/groups/all.py @@ -21,6 +21,8 @@ lazy_import('sage.groups.affine_gps.affine_group', 'AffineGroup') lazy_import('sage.groups.affine_gps.euclidean_group', 'EuclideanGroup') +lazy_import('sage.groups.raag', 'RightAngledArtinGroup') + lazy_import('sage.groups', 'groups_catalog', 'groups') lazy_import('sage.groups.semimonomial_transformations.semimonomial_transformation_group', diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py new file mode 100644 index 00000000000..72b38e99097 --- /dev/null +++ b/src/sage/groups/raag.py @@ -0,0 +1,423 @@ +r""" +Right-Angled Artin Groups + +AUTHORS: + +- Travis Scrimshaw (2013-09-01): Initial version +""" + +############################################################################## +# Copyright (C) 2013 Travis Scrimshaw +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# The full text of the GPL is available at: +# +# http://www.gnu.org/licenses/ +############################################################################## + +from sage.misc.cachefunc import cached_method +from sage.structure.list_clone import ClonableArray +from sage.structure.unique_representation import UniqueRepresentation +from sage.groups.group import Group +from sage.rings.integer import Integer +from sage.rings.integer_ring import IntegerRing +from sage.graphs.graph import Graph + +class RightAngledArtinGroup(Group, UniqueRepresentation): + r""" + The right-angled Artin group defined by a graph `G`. + + Let `\Gamma = \{V(\Gamma), E(\Gamma)\}` be a simple graph. + A *right-angled Artin group* (commonly abbriated as RAAG) is the group + + .. MATH:: + + A_{\Gamma} = \langle g_v : v \in V(\Gamma) + \mid [g_u, g_v] \text{ if } \{u, v\} \notin E(\Gamma) \rangle. + + These are sometimes known as graph groups or partitally commutative groups. + This RAAG's contains both free groups, given by the complete graphs, + and free abelian groups, given by disjoint vertices. + + .. NOTE:: + + This is the opposite convention of some papers. + + EXAMPLES:: + + sage: Gamma = Graph(4) + sage: G = RightAngledArtinGroup(Gamma) + sage: a,b,c,d = G.gens() + sage: a*c*d^4*a^-3*b + v0^-2*v1*v2*v3^4 + + sage: Gamma = graphs.CompleteGraph(4) + sage: G = RightAngledArtinGroup(Gamma) + sage: a,b,c,d = G.gens() + sage: a*c*d^4*a^-3*b + v0*v2*v3^4*v0^-3*v1 + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: G + Right-angled Artin group on 5 generators + sage: a,b,c,d,e = G.gens() + sage: e^-1*c*b*e*b^-1*c^-4 + v2^-3 + """ + @staticmethod + def __classcall_private__(cls, G): + """ + Normalize input to ensure a unique representation. + + TESTS:: + + sage: G1 = RightAngledArtinGroup(graphs.CycleGraph(5)) + sage: Gamma = Graph([(0,1),(1,2),(2,3),(3,4),(4,0)]) + sage: G2 = RightAngledArtinGroup(Gamma) + sage: G3 = RightAngledArtinGroup([(0,1),(1,2),(2,3),(3,4),(4,0)]) + sage: G1 is G2 and G2 is G3 + True + """ + if not isinstance(G, Graph): + G = Graph(G) + G = G.copy() + G._immutable = True + return super(RightAngledArtinGroup, cls).__classcall__(cls, G) + + def __init__(self, G): + """ + Initialize ``self``. + + INPUT: + + - ``G`` -- a graph + + TESTS:: + + sage: G = RightAngledArtinGroup(graphs.CycleGraph(5)) + sage: TestSuite(G).run() + """ + self._graph = G + Group.__init__(self) + + def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: RightAngledArtinGroup(graphs.CycleGraph(5)) + Right-angled Artin group on 5 generators + """ + return "Right-angled Artin group on {} generators".format(self._graph.num_verts()) + + def gen(self, i): + """ + Return the ``i``-th generator of ``self``. + + EXAMPLES:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: G.gen(2) + v2 + """ + return self.element_class(self, [(i, 1)]) + + def gens(self): + """ + Return the generators of ``self``. + + EXAMPLES:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: G.gens() + (v0, v1, v2, v3, v4) + sage: Gamma = Graph([('x', 'y'), ('y', 'zeta')]) + sage: G = RightAngledArtinGroup(Gamma) + sage: G.gens() + (vx, vy, vzeta) + """ + return tuple(self.gen(i) for i in range(self._graph.num_verts())) + + def ngens(self): + """ + Return the number of generators of ``self``. + + EXAMPLES:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: G.ngens() + 5 + """ + return self._graph.num_verts() + + def cardinality(self): + """ + Return the number of group elements. + + OUTPUT: + + Infinity. + + EXAMPLES:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: G.cardinality() + +Infinity + """ + from sage.rings.infinity import Infinity + return Infinity + + order = cardinality + + def as_permutation_group(self): + """ + Raises a ``ValueError`` error since right-angled Artin groups + are infinite, so they have no isomorphic permutation group. + + EXAMPLES:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: G.as_permutation_group() + Traceback (most recent call last): + ... + ValueError: the group is infinite + """ + raise ValueError("the group is infinite") + + def graph(self): + """ + Return the defining graph of ``self``. + + EXAMPLES:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: G.graph() + Cycle graph: Graph on 5 vertices + """ + return self._graph.copy() + + @cached_method + def one(self): + """ + Return the identity element `1`. + + EXAMPLES:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: G.one() + 1 + """ + return self.element_class(self, []) + + one_element = one + + def _element_constructor_(self, x): + """ + Construct an element of ``self`` from ``x``. + + TESTS:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: elt = G([[0,3], [3,1], [2,1], [1,1], [3,1]]); elt + v0^3*v3*v2*v1*v3 + sage: G(elt) + v0^3*v3*v2*v1*v3 + sage: G(1) + 1 + """ + if isinstance(x, RightAngledArtinGroup.Element): + if x.parent() is self: + return x + raise ValueError("there is no coercion from {} into {}".format(x.parent(), self)) + if x == 1: + return self.one() + verts = self._graph.vertices() + x = map(lambda s: [verts.index(s[0]), s[1]], x) + return self.element_class(self, self._normal_form(x)) + + def _normal_form(self, word): + """ + Return the normal form of the word ``word``. Helper function for + creaing elements. + + EXAMPLES:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: G._normal_form([[0,2], [3,1], [2,1], [0,1], [1,1], [3,1]]) + [[0, 3], [3, 1], [2, 1], [1, 1], [3, 1]] + sage: a,b,c,d,e = G.gens() + sage: a^2 * d * c * a * b * d + v0^3*v3*v2*v1*v3 + sage: a*b*d == d*a*b and a*b*d == a*d*b + True + sage: a*c*a^-1*c^-1 + 1 + sage: (a*b*c*d*e)^2 * (a*b*c*d*e)^-2 + 1 + """ + pos = 0 + G = self._graph + w = map(list, word) # Make a (2 level) deep copy + while pos < len(w): + comm_set = [w[pos][0]] # The current set of totally commuting elements + i = pos + 1 + + while i < len(w): + letter = w[i][0] # The current letter + # Check if this could fit in the commuting set + if letter in comm_set: + # Try to move it in + if any(G.has_edge(w[j][0], letter) for j in range(pos + len(comm_set), i)): + # We can't, so go onto the next letter + i += 1 + continue + j = comm_set.index(letter) + w[pos+j][1] += w[i][1] + w.pop(i) + i -= 1 # Since we removed a syllable + # Check cancellations + if w[pos+j][1] == 0: + w.pop(pos+j) + comm_set.pop(j) + i -= 1 + if len(comm_set) == 0: + pos = 0 # Start again since cancellation can be pronounced effects + break + elif all( not G.has_edge(w[j][0], letter) for j in range(pos, i)): + j = 0 + for x in comm_set: + if x > letter: + break + j += 1 + w.insert(pos+j, w.pop(i)) + comm_set.insert(j, letter) + + i += 1 + pos += len(comm_set) + return w + + class Element(ClonableArray): + """ + An element of a right-angled Artin group (RAAG). + + Elements of RAAGs are modeled as lists of pairs ``[i, p]`` where + ``i`` is the index of a vertex in the defining graph (with some + fixed order of the vertices) and ``p`` is the power. + """ + def check(self): + """ + Check if ``self`` is a valid element. Nothing to check. + + TESTS:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: elt = G.gen(2) + sage: elt.check() + """ + pass + + def _repr_(self): + """ + Return a string representation of ``self``. + + TESTS:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: a,b,c,d,e = G.gens() + sage: a * b^2 * e^-3 + v0*v1^2*v4^-3 + sage: Gamma = Graph([('x', 'y'), ('y', 'zeta')]) + sage: G = RightAngledArtinGroup(Gamma) + sage: x,y,z = G.gens() + sage: z * y^-2 * x^3 + vx^3*vy^-2*vzeta + """ + if len(self) == 0: + return '1' + v = self.parent()._graph.vertices() + to_str = lambda i,p: "v{}".format(i) if p == 1 else "v{}^{}".format(i, p) + return '*'.join(to_str(v[i], p) for i,p in self) + + def _latex_(self): + r""" + Return a LaTeX representation of ``self``. + + TESTS:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: a,b,c,d,e = G.gens() + sage: latex(a*b*e^-4*d^3) + \sigma_{0}\sigma_{1}\sigma_{4}^{-4}\sigma_{3}^{3} + sage: latex(G.one()) + 1 + sage: Gamma = Graph([('x', 'y'), ('y', 'zeta')]) + sage: G = RightAngledArtinGroup(Gamma) + sage: x,y,z = G.gens() + sage: latex(x^-5*y*z^3) + \sigma_{\text{\texttt{x}}}^{-5}\sigma_{\text{\texttt{y}}}\sigma_{\text{\texttt{zeta}}}^{3} + """ + if len(self) == 0: + return '1' + + from sage.misc.latex import latex + latexrepr = '' + v = self.parent()._graph.vertices() + for i,p in self: + latexrepr += "\\sigma_{{{}}}".format(latex(v[i])) + if p != 1: + latexrepr += "^{{{}}}".format(p) + return latexrepr + + def _mul_(self, y): + """ + Return ``self`` multiplied by ``y``. + + TESTS:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: a,b,c,d,e = G.gens() + sage: a * b + v0*v1 + sage: b * a + v1*v0 + sage: a*b*c*d*e + v0*v1*v2*v3*v4 + sage: a^2*d*c*a*b*d + v0^3*v3*v2*v1*v3 + sage: e^-1*a*b*d*c*a^-2*e*d*b^2*e*b^-3 + v4^-1*v0*v3*v1*v0^-2*v2*v1^-1*v4*v3*v4 + """ + P = self.parent() + lst = list(self) + list(y) + return self.__class__(self.parent(), P._normal_form(lst)) + + def __invert__(self): + """ + Return the inverse of ``self``. + + TESTS:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: a,b,c,d,e = G.gens() + sage: (a * b)^-2 + v1^-1*v0^-1*v1^-1*v0^-1 + """ + return self.__class__(self.parent(), map(lambda x: [x[0], -x[1]], reversed(self))) + From 0f6f1b49532de4b6a55310debeb0a1f0aa6df1ca Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 9 Jan 2014 22:09:18 -0800 Subject: [PATCH 02/10] Changed to use new immutable graph backend. --- src/sage/groups/raag.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index 72b38e99097..43adbfa6a55 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -81,9 +81,9 @@ def __classcall_private__(cls, G): True """ if not isinstance(G, Graph): - G = Graph(G) - G = G.copy() - G._immutable = True + G = Graph(G, immutable=True) + else: + G = G.copy(immutable=True) return super(RightAngledArtinGroup, cls).__classcall__(cls, G) def __init__(self, G): @@ -201,9 +201,9 @@ def graph(self): sage: Gamma = graphs.CycleGraph(5) sage: G = RightAngledArtinGroup(Gamma) sage: G.graph() - Cycle graph: Graph on 5 vertices + Graph on 5 vertices """ - return self._graph.copy() + return self._graph @cached_method def one(self): @@ -269,6 +269,7 @@ def _normal_form(self, word): """ pos = 0 G = self._graph + v = G.vertices() w = map(list, word) # Make a (2 level) deep copy while pos < len(w): comm_set = [w[pos][0]] # The current set of totally commuting elements @@ -279,7 +280,7 @@ def _normal_form(self, word): # Check if this could fit in the commuting set if letter in comm_set: # Try to move it in - if any(G.has_edge(w[j][0], letter) for j in range(pos + len(comm_set), i)): + if any(G.has_edge(v[w[j][0]], v[letter]) for j in range(pos + len(comm_set), i)): # We can't, so go onto the next letter i += 1 continue @@ -295,7 +296,7 @@ def _normal_form(self, word): if len(comm_set) == 0: pos = 0 # Start again since cancellation can be pronounced effects break - elif all( not G.has_edge(w[j][0], letter) for j in range(pos, i)): + elif all( not G.has_edge(v[w[j][0]], v[letter]) for j in range(pos, i)): j = 0 for x in comm_set: if x > letter: @@ -344,7 +345,7 @@ def _repr_(self): sage: G = RightAngledArtinGroup(Gamma) sage: x,y,z = G.gens() sage: z * y^-2 * x^3 - vx^3*vy^-2*vzeta + vzeta*vy^-2*vx^3 """ if len(self) == 0: return '1' From 7856bc45c5c3fd64a7303b7e4465b7add71ef7eb Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Sat, 11 Jan 2014 23:55:00 -0800 Subject: [PATCH 03/10] Made changes and expanded documentation. --- src/sage/groups/raag.py | 61 ++++++++++++++++++++++++++++++++++++++--- 1 file changed, 57 insertions(+), 4 deletions(-) diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index 43adbfa6a55..92a592e1d1c 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -44,6 +44,28 @@ class RightAngledArtinGroup(Group, UniqueRepresentation): This is the opposite convention of some papers. + Right-angled Artin groups contain many remarkable properties and have a + very rich structure despite their simple presentation. Here are some + known facts: + + - The word problem is solvable. + - They are known to be rigid; that is for any finite simple graphs + `\Delta` and `\Gamma`, we have `A_{\Delta} \cong A_{\Gamma}` if and + only if `\Delta \cong \Gamma` [Droms1987]_. + - They embed as a finite index subgroup of a right-angled Coxeter group + (which is the same definition as above except with the additional + relations `g_v^2 = 1` for all `v \in V(\Gamma)`). + - In [BB1997]_, it was shown they contain subgroups that statisfy the + property `FP_2` but are not finitely presented by considering the + kernal of `\phi : A_{\Gamma} \to \ZZ` by `g_v \mapsto 1` (i.e. words of + exponent sum 0). + - `A_{\Gamma}` has a finite `K(\pi, 1)` space. + - `A_{\Gamma}` acts freely and cocompactly on a finite dimensional + `CAT(0)` space, and so it is biautomatic. + - Given an Artin group `B` with generators `s_i`, then any subgroup + generated by a collection of `v_i = s_i^{k_i}` where `k_i \geq 2` is a + RAAG where `[v_i, v_j] = 1` if and only if `[s_i, s_j] = 1` [CP2001]_. + EXAMPLES:: sage: Gamma = Graph(4) @@ -61,10 +83,32 @@ class RightAngledArtinGroup(Group, UniqueRepresentation): sage: Gamma = graphs.CycleGraph(5) sage: G = RightAngledArtinGroup(Gamma) sage: G - Right-angled Artin group on 5 generators + Right-angled Artin group of Graph on 5 vertices sage: a,b,c,d,e = G.gens() sage: e^-1*c*b*e*b^-1*c^-4 v2^-3 + + REFERENCES: + + .. [Charney2006] Ruth Charney. + *An introduction to right-angled Artin groups*. + http://people.brandeis.edu/~charney/papers/RAAGfinal.pdf, + :arxiv:`math/0610668`. + + .. [BB1997] Mladen Bestvina and Noel Brady. + *Morse theory and finiteness properties of groups*. Invent. Math. + **129** (1997). No. 3, 445-470. www.math.ou.edu/~nbrady/papers/morse.ps‎. + + .. [Droms1987] Carl Droms. *Isomorphisms of graph groups*. Proc. of + the Amer. Math. Soc. **100** (1987). No 3. + http://educ.jmu.edu/~dromscg/vita/preprints/Isomorphisms.pdf + + .. [CP2001] John Crisp and Luis Paris. *The solution to a conjecture of + Tits on the subgroup generated by the squares of the generators of an + Artin group*. Invent. Math. **145** (2001). No 1, 19-36. + :arxiv:`math/0003133`. + + - :wikipedia:`Artin_group#Right-angled_Artin_groups` """ @staticmethod def __classcall_private__(cls, G): @@ -79,11 +123,20 @@ def __classcall_private__(cls, G): sage: G3 = RightAngledArtinGroup([(0,1),(1,2),(2,3),(3,4),(4,0)]) sage: G1 is G2 and G2 is G3 True + + Handle the empty graph:: + + sage: RightAngledArtinGroup(Graph()) + Traceback (most recent call last): + ... + ValueError: the graph must not be empty """ if not isinstance(G, Graph): G = Graph(G, immutable=True) else: G = G.copy(immutable=True) + if G.num_verts() == 0: + raise ValueError("the graph must not be empty") return super(RightAngledArtinGroup, cls).__classcall__(cls, G) def __init__(self, G): @@ -109,9 +162,9 @@ def _repr_(self): TESTS:: sage: RightAngledArtinGroup(graphs.CycleGraph(5)) - Right-angled Artin group on 5 generators + Right-angled Artin group of Graph on 5 vertices """ - return "Right-angled Artin group on {} generators".format(self._graph.num_verts()) + return "Right-angled Artin group of {}".format(self._graph) def gen(self, i): """ @@ -249,7 +302,7 @@ def _element_constructor_(self, x): def _normal_form(self, word): """ Return the normal form of the word ``word``. Helper function for - creaing elements. + creating elements. EXAMPLES:: From 457f00d48a863dcf34eb93a95243d203ec7c69ad Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 13 Jan 2014 08:39:34 -0800 Subject: [PATCH 04/10] Added FP group and gap to raag.py. --- src/sage/groups/raag.py | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index 92a592e1d1c..a6e7453a33c 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -166,6 +166,22 @@ def _repr_(self): """ return "Right-angled Artin group of {}".format(self._graph) + def gap(self): + """ + Return a Gap representation of ``self`` (as a finitely + presented group). + + EXAMPLES:: + + sage: Gamma = graphs.PathGraph(5).complement() + sage: G = RightAngledArtinGroup(Gamma) + sage: G.gap() + + """ + return self.as_finitely_presented_group().gap() + + _gap_ = gap + def gen(self, i): """ Return the ``i``-th generator of ``self``. @@ -245,6 +261,26 @@ def as_permutation_group(self): """ raise ValueError("the group is infinite") + def as_finitely_presented_group(self): + """ + Return ``self`` as a + :class:`~sage.groups.finitely_presented.FinitelyPresentedGroup`. + + EXAMPLES:: + + sage: Gamma = graphs.PathGraph(5).complement() + sage: G = RightAngledArtinGroup(Gamma) + sage: G.as_finitely_presented_group() + Finitely presented group < v0, v1, v2, v3, v4 | + v0*v1*v0^-1*v1^-1, v1*v2*v1^-1*v2^-1, + v2*v3*v2^-1*v3^-1, v3*v4*v3^-1*v4^-1 > + """ + from sage.groups.free_group import FreeGroup + F = FreeGroup(names=['v{}'.format(v) for v in self._graph.vertices()]) + CG = Graph(self._graph).complement() # Make sure it's mutable + CG.relabel() + return F / [F([i+1, j+1, -i-1, -j-1]) for i,j in CG.edges(False)] #+/- 1 for indexing + def graph(self): """ Return the defining graph of ``self``. @@ -403,7 +439,7 @@ def _repr_(self): if len(self) == 0: return '1' v = self.parent()._graph.vertices() - to_str = lambda i,p: "v{}".format(i) if p == 1 else "v{}^{}".format(i, p) + to_str = lambda name,p: "v{}".format(name) if p == 1 else "v{}^{}".format(name, p) return '*'.join(to_str(v[i], p) for i,p in self) def _latex_(self): From 2dff54390f37ddca1021cf69c1c5a5a32b52074d Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Thu, 16 Jan 2014 10:11:07 -0800 Subject: [PATCH 05/10] Fixed doctest from merge. --- src/sage/groups/raag.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index a6e7453a33c..1fd57d3a853 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -83,7 +83,7 @@ class RightAngledArtinGroup(Group, UniqueRepresentation): sage: Gamma = graphs.CycleGraph(5) sage: G = RightAngledArtinGroup(Gamma) sage: G - Right-angled Artin group of Graph on 5 vertices + Right-angled Artin group of Cycle graph sage: a,b,c,d,e = G.gens() sage: e^-1*c*b*e*b^-1*c^-4 v2^-3 @@ -162,7 +162,7 @@ def _repr_(self): TESTS:: sage: RightAngledArtinGroup(graphs.CycleGraph(5)) - Right-angled Artin group of Graph on 5 vertices + Right-angled Artin group of Cycle graph """ return "Right-angled Artin group of {}".format(self._graph) @@ -290,7 +290,7 @@ def graph(self): sage: Gamma = graphs.CycleGraph(5) sage: G = RightAngledArtinGroup(Gamma) sage: G.graph() - Graph on 5 vertices + Cycle graph: Graph on 5 vertices """ return self._graph From 6e6d0ef424287bd89bc2f2a7d9fdc7b4d6ffc5f7 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 16 Jun 2014 11:51:58 -0700 Subject: [PATCH 06/10] Fixing bad merge and added RAAG's to the catalog. --- src/sage/graphs/base/static_sparse_backend.pyx | 3 +-- src/sage/groups/groups_catalog.py | 1 + src/sage/groups/misc_gps/misc_groups_catalog.py | 2 ++ src/sage/groups/raag.py | 14 +++++++------- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/sage/graphs/base/static_sparse_backend.pyx b/src/sage/graphs/base/static_sparse_backend.pyx index ad9f9b44cc7..85af27fc10b 100644 --- a/src/sage/graphs/base/static_sparse_backend.pyx +++ b/src/sage/graphs/base/static_sparse_backend.pyx @@ -373,8 +373,7 @@ class StaticSparseBackend(CGraphBackend): cdef StaticSparseCGraph cg = StaticSparseCGraph(G) self._cg = cg - # .directed and not ._directed. Because of CGraph. - self.directed = cg.directed + self._directed = cg._directed vertices = G.vertices() self._order = len(vertices) diff --git a/src/sage/groups/groups_catalog.py b/src/sage/groups/groups_catalog.py index a26097ec44b..f36bf4b1a30 100644 --- a/src/sage/groups/groups_catalog.py +++ b/src/sage/groups/groups_catalog.py @@ -62,6 +62,7 @@ - :func:`groups.misc.Braid ` - :func:`groups.misc.CoxeterGroup ` - :func:`groups.misc.Free ` + - :class:`groups.misc.RightAngledArtin ` - :func:`groups.misc.SemimonomialTransformation ` - :func:`groups.misc.WeylGroup ` diff --git a/src/sage/groups/misc_gps/misc_groups_catalog.py b/src/sage/groups/misc_gps/misc_groups_catalog.py index 5ef0f2a9b2b..df9b84f1c70 100644 --- a/src/sage/groups/misc_gps/misc_groups_catalog.py +++ b/src/sage/groups/misc_gps/misc_groups_catalog.py @@ -19,3 +19,5 @@ from sage.groups.semimonomial_transformations.semimonomial_transformation_group import SemimonomialTransformationGroup as SemimonomialTransformation from sage.combinat.root_system.coxeter_group import CoxeterGroup from sage.combinat.root_system.weyl_group import WeylGroup +from sage.groups.raag import RightAngledArtinGroup as RightAngledArtin + diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index 1fd57d3a853..8d393839fb9 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -40,7 +40,7 @@ class RightAngledArtinGroup(Group, UniqueRepresentation): This RAAG's contains both free groups, given by the complete graphs, and free abelian groups, given by disjoint vertices. - .. NOTE:: + .. WARNING:: This is the opposite convention of some papers. @@ -226,7 +226,7 @@ def ngens(self): return self._graph.num_verts() def cardinality(self): - """ + r""" Return the number of group elements. OUTPUT: @@ -244,12 +244,12 @@ def cardinality(self): return Infinity order = cardinality - + def as_permutation_group(self): - """ - Raises a ``ValueError`` error since right-angled Artin groups + r""" + Raise a ``ValueError`` error since right-angled Artin groups are infinite, so they have no isomorphic permutation group. - + EXAMPLES:: sage: Gamma = graphs.CycleGraph(5) @@ -262,7 +262,7 @@ def as_permutation_group(self): raise ValueError("the group is infinite") def as_finitely_presented_group(self): - """ + r""" Return ``self`` as a :class:`~sage.groups.finitely_presented.FinitelyPresentedGroup`. From 86c8c872af6c1a1967b1f4045f13e4c3e0f482f6 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 16 Jun 2014 15:45:19 -0700 Subject: [PATCH 07/10] Rewrote RAAG as Gap group and did some logic/pickling cleanup of FP groups. --- src/sage/groups/finitely_presented.py | 32 +----- src/sage/groups/raag.py | 137 +++++++++++++++----------- 2 files changed, 80 insertions(+), 89 deletions(-) diff --git a/src/sage/groups/finitely_presented.py b/src/sage/groups/finitely_presented.py index 55362b6e546..df6c85ff62f 100644 --- a/src/sage/groups/finitely_presented.py +++ b/src/sage/groups/finitely_presented.py @@ -198,7 +198,7 @@ def __init__(self, parent, x, check=True): if not isinstance(x, GapElement): F = parent.free_group() free_element = F(x) - fp_family = parent.one().gap().FamilyObj() + fp_family = parent.gap().Identity().FamilyObj() x = libgap.ElementOfFpGroup(fp_family, free_element.gap()) ElementLibGAP.__init__(self, parent, x) @@ -219,10 +219,6 @@ def __reduce__(self): sage: F. = FreeGroup('a, b, c') sage: G = F.quotient([a*b*c/(b*c*a), a*b*c/(c*a*b)]) - sage: G.__reduce__() - (, - (Free Group on generators {a, b, c}, - (a*b*c*a^-1*c^-1*b^-1, a*b*c*b^-1*a^-1*c^-1))) sage: G.inject_variables() Defining a, b, c sage: x = a*b*c @@ -776,32 +772,6 @@ def __init__(self, free_group, relations): ParentLibGAP.__init__(self, parent_gap) Group.__init__(self) - def __reduce__(self): - """ - Used in pickling. - - TESTS:: - - sage: F = FreeGroup(4) - sage: F.inject_variables() - Defining x0, x1, x2, x3 - sage: G = F.quotient([x0*x2, x3*x1*x3, x2*x1*x2]) - sage: G.__reduce__() - (, - (Free Group on generators {x0, x1, x2, x3}, - (x0*x2, x3*x1*x3, x2*x1*x2))) - - sage: F. = FreeGroup() - sage: F.inject_variables() - Defining a, b, c - sage: G = F / [a*b*c/(b*c*a), a*b*c/(c*a*b)] - sage: G.__reduce__() - (, - (Free Group on generators {a, b, c}, - (a*b*c*a^-1*c^-1*b^-1, a*b*c*b^-1*a^-1*c^-1))) - """ - return (FinitelyPresentedGroup, (self._free_group, self._relations)) - def _repr_(self): """ Return a string representation. diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index 8d393839fb9..30dd3797127 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -18,13 +18,13 @@ from sage.misc.cachefunc import cached_method from sage.structure.list_clone import ClonableArray -from sage.structure.unique_representation import UniqueRepresentation -from sage.groups.group import Group +from sage.groups.finitely_presented import FinitelyPresentedGroup, FinitelyPresentedGroupElement +from sage.groups.free_group import FreeGroup from sage.rings.integer import Integer from sage.rings.integer_ring import IntegerRing from sage.graphs.graph import Graph -class RightAngledArtinGroup(Group, UniqueRepresentation): +class RightAngledArtinGroup(FinitelyPresentedGroup): r""" The right-angled Artin group defined by a graph `G`. @@ -153,7 +153,11 @@ def __init__(self, G): sage: TestSuite(G).run() """ self._graph = G - Group.__init__(self) + F = FreeGroup(names=['v{}'.format(v) for v in self._graph.vertices()]) + CG = Graph(G).complement() # Make sure it's mutable + CG.relabel() # Standardize the labels + rels = tuple(F([i+1, j+1, -i-1, -j-1]) for i,j in CG.edges(False)) #+/- 1 for indexing + FinitelyPresentedGroup.__init__(self, F, rels) def _repr_(self): """ @@ -166,22 +170,6 @@ def _repr_(self): """ return "Right-angled Artin group of {}".format(self._graph) - def gap(self): - """ - Return a Gap representation of ``self`` (as a finitely - presented group). - - EXAMPLES:: - - sage: Gamma = graphs.PathGraph(5).complement() - sage: G = RightAngledArtinGroup(Gamma) - sage: G.gap() - - """ - return self.as_finitely_presented_group().gap() - - _gap_ = gap - def gen(self, i): """ Return the ``i``-th generator of ``self``. @@ -193,7 +181,7 @@ def gen(self, i): sage: G.gen(2) v2 """ - return self.element_class(self, [(i, 1)]) + return self.element_class( self, ((i, 1),) ) def gens(self): """ @@ -261,26 +249,6 @@ def as_permutation_group(self): """ raise ValueError("the group is infinite") - def as_finitely_presented_group(self): - r""" - Return ``self`` as a - :class:`~sage.groups.finitely_presented.FinitelyPresentedGroup`. - - EXAMPLES:: - - sage: Gamma = graphs.PathGraph(5).complement() - sage: G = RightAngledArtinGroup(Gamma) - sage: G.as_finitely_presented_group() - Finitely presented group < v0, v1, v2, v3, v4 | - v0*v1*v0^-1*v1^-1, v1*v2*v1^-1*v2^-1, - v2*v3*v2^-1*v3^-1, v3*v4*v3^-1*v4^-1 > - """ - from sage.groups.free_group import FreeGroup - F = FreeGroup(names=['v{}'.format(v) for v in self._graph.vertices()]) - CG = Graph(self._graph).complement() # Make sure it's mutable - CG.relabel() - return F / [F([i+1, j+1, -i-1, -j-1]) for i,j in CG.edges(False)] #+/- 1 for indexing - def graph(self): """ Return the defining graph of ``self``. @@ -306,7 +274,7 @@ def one(self): sage: G.one() 1 """ - return self.element_class(self, []) + return self.element_class(self, ()) one_element = one @@ -345,7 +313,7 @@ def _normal_form(self, word): sage: Gamma = graphs.CycleGraph(5) sage: G = RightAngledArtinGroup(Gamma) sage: G._normal_form([[0,2], [3,1], [2,1], [0,1], [1,1], [3,1]]) - [[0, 3], [3, 1], [2, 1], [1, 1], [3, 1]] + ([0, 3], [3, 1], [2, 1], [1, 1], [3, 1]) sage: a,b,c,d,e = G.gens() sage: a^2 * d * c * a * b * d v0^3*v3*v2*v1*v3 @@ -382,10 +350,10 @@ def _normal_form(self, word): w.pop(pos+j) comm_set.pop(j) i -= 1 - if len(comm_set) == 0: + if not comm_set: pos = 0 # Start again since cancellation can be pronounced effects break - elif all( not G.has_edge(v[w[j][0]], v[letter]) for j in range(pos, i)): + elif all(not G.has_edge(v[w[j][0]], v[letter]) for j in range(pos, i)): j = 0 for x in comm_set: if x > letter: @@ -396,9 +364,9 @@ def _normal_form(self, word): i += 1 pos += len(comm_set) - return w + return tuple(w) - class Element(ClonableArray): + class Element(FinitelyPresentedGroupElement): """ An element of a right-angled Artin group (RAAG). @@ -406,18 +374,41 @@ class Element(ClonableArray): ``i`` is the index of a vertex in the defining graph (with some fixed order of the vertices) and ``p`` is the power. """ - def check(self): + def __init__(self, parent, lst): + """ + Initialize ``self``. + + TESTS:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: elt = G.prod(G.gens()) + sage: TestSuite(elt).run() + """ + self._data = lst + elt = [] + for i,p in lst: + if p > 0: + elt.extend([i+1]*p) + elif p < 0: + elt.extend([-i-1]*-p) + FinitelyPresentedGroupElement.__init__(self, parent, elt) + + def __reduce__(self): """ - Check if ``self`` is a valid element. Nothing to check. + Used in pickling. TESTS:: sage: Gamma = graphs.CycleGraph(5) sage: G = RightAngledArtinGroup(Gamma) - sage: elt = G.gen(2) - sage: elt.check() + sage: elt = G.prod(G.gens()) + sage: loads(dumps(elt)) == elt + True """ - pass + P = self.parent() + V = P._graph.vertices() + return (P, ([[V[i], p] for i,p in self._data],)) def _repr_(self): """ @@ -436,11 +427,11 @@ def _repr_(self): sage: z * y^-2 * x^3 vzeta*vy^-2*vx^3 """ - if len(self) == 0: + if not self._data: return '1' v = self.parent()._graph.vertices() to_str = lambda name,p: "v{}".format(name) if p == 1 else "v{}^{}".format(name, p) - return '*'.join(to_str(v[i], p) for i,p in self) + return '*'.join(to_str(v[i], p) for i,p in self._data) def _latex_(self): r""" @@ -461,13 +452,13 @@ def _latex_(self): sage: latex(x^-5*y*z^3) \sigma_{\text{\texttt{x}}}^{-5}\sigma_{\text{\texttt{y}}}\sigma_{\text{\texttt{zeta}}}^{3} """ - if len(self) == 0: + if not self._data: return '1' from sage.misc.latex import latex latexrepr = '' v = self.parent()._graph.vertices() - for i,p in self: + for i,p in self._data: latexrepr += "\\sigma_{{{}}}".format(latex(v[i])) if p != 1: latexrepr += "^{{{}}}".format(p) @@ -494,7 +485,35 @@ def _mul_(self, y): v4^-1*v0*v3*v1*v0^-2*v2*v1^-1*v4*v3*v4 """ P = self.parent() - lst = list(self) + list(y) + lst = self._data + y._data + return self.__class__(P, P._normal_form(lst)) + + def __pow__(self, n): + """ + Implement exponentiation. + + TESTS:: + + sage: Gamma = graphs.CycleGraph(5) + sage: G = RightAngledArtinGroup(Gamma) + sage: elt = G.prod(G.gens()) + sage: elt**3 + v0*v1*v2*v3*v4*v0*v1*v2*v3*v4*v0*v1*v2*v3*v4 + sage: elt^-2 + v4^-1*v3^-1*v2^-1*v1^-1*v0^-1*v4^-1*v3^-1*v2^-1*v1^-1*v0^-1 + sage: elt^0 + 1 + """ + P = self.parent() + if not n: + return P.one() + + if n < 0: + lst = sum([self._data for i in range(-n)], ()) # Positive product + lst = map(lambda x: [x[0], -x[1]], reversed(lst)) # Now invert + return self.__class__(P, P._normal_form(lst)) + + lst = sum([self._data for i in range(n)], ()) return self.__class__(self.parent(), P._normal_form(lst)) def __invert__(self): @@ -509,5 +528,7 @@ def __invert__(self): sage: (a * b)^-2 v1^-1*v0^-1*v1^-1*v0^-1 """ - return self.__class__(self.parent(), map(lambda x: [x[0], -x[1]], reversed(self))) + P = self.parent() + lst = map(lambda x: [x[0], -x[1]], reversed(self._data)) + return self.__class__(P, P._normal_form(lst)) From d78fc558eed8254313d1aac039e17eb8883cbca9 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 16 Jun 2014 18:15:00 -0700 Subject: [PATCH 08/10] Improving documentation from talking with Miguel. --- src/sage/groups/raag.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index 30dd3797127..5712dcdb14a 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -1,6 +1,11 @@ r""" Right-Angled Artin Groups +A *right-angled Artin group* (often abbrivated as RAAG) is a group which +has a presentation whose only relations are commutators between generators. +These are also known as graph groups, since they are (uniquely) encoded by +(simple) graphs, or partially commmutative groups. + AUTHORS: - Travis Scrimshaw (2013-09-01): Initial version @@ -66,6 +71,9 @@ class RightAngledArtinGroup(FinitelyPresentedGroup): generated by a collection of `v_i = s_i^{k_i}` where `k_i \geq 2` is a RAAG where `[v_i, v_j] = 1` if and only if `[s_i, s_j] = 1` [CP2001]_. + The normal forms for RAAG's in Sage are those described in [VW1994]_ and + gathers commuting groups together. + EXAMPLES:: sage: Gamma = Graph(4) @@ -108,6 +116,9 @@ class RightAngledArtinGroup(FinitelyPresentedGroup): Artin group*. Invent. Math. **145** (2001). No 1, 19-36. :arxiv:`math/0003133`. + .. [VW94] Leonard Van Wyk. *Graph groups are biautomatic*. J. Pure Appl. + Alg. **94** (1994). no. 3, 341-352. + - :wikipedia:`Artin_group#Right-angled_Artin_groups` """ @staticmethod From ce328e86d829356b8a5d17270b0d615556a86780 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 16 Jun 2014 18:29:33 -0700 Subject: [PATCH 09/10] Removed non-ascii character. --- src/sage/groups/raag.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index 5712dcdb14a..f322ee8d6b6 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -105,7 +105,7 @@ class RightAngledArtinGroup(FinitelyPresentedGroup): .. [BB1997] Mladen Bestvina and Noel Brady. *Morse theory and finiteness properties of groups*. Invent. Math. - **129** (1997). No. 3, 445-470. www.math.ou.edu/~nbrady/papers/morse.ps‎. + **129** (1997). No. 3, 445-470. www.math.ou.edu/~nbrady/papers/morse.ps. .. [Droms1987] Carl Droms. *Isomorphisms of graph groups*. Proc. of the Amer. Math. Soc. **100** (1987). No 3. From 473384d5da5a387464e3ba66d6f2a5ef28bb3ee5 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 16 Jun 2014 18:34:20 -0700 Subject: [PATCH 10/10] Fixed citation. --- src/sage/groups/raag.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/groups/raag.py b/src/sage/groups/raag.py index f322ee8d6b6..461221fc241 100644 --- a/src/sage/groups/raag.py +++ b/src/sage/groups/raag.py @@ -116,7 +116,7 @@ class RightAngledArtinGroup(FinitelyPresentedGroup): Artin group*. Invent. Math. **145** (2001). No 1, 19-36. :arxiv:`math/0003133`. - .. [VW94] Leonard Van Wyk. *Graph groups are biautomatic*. J. Pure Appl. + .. [VW1994] Leonard Van Wyk. *Graph groups are biautomatic*. J. Pure Appl. Alg. **94** (1994). no. 3, 341-352. - :wikipedia:`Artin_group#Right-angled_Artin_groups`