| 1 | r""" |
| 2 | Right-Angled Artin Groups |
| 3 | |
| 4 | AUTHORS: |
| 5 | |
| 6 | - Travis Scrimshaw (2013-09-01): Initial version |
| 7 | """ |
| 8 | |
| 9 | ############################################################################## |
| 10 | # Copyright (C) 2013 Travis Scrimshaw <tscrim at ucdavis.edu> |
| 11 | # |
| 12 | # Distributed under the terms of the GNU General Public License (GPL) |
| 13 | # |
| 14 | # The full text of the GPL is available at: |
| 15 | # |
| 16 | # http://www.gnu.org/licenses/ |
| 17 | ############################################################################## |
| 18 | |
| 19 | from sage.misc.cachefunc import cached_method |
| 20 | from sage.structure.list_clone import ClonableArray |
| 21 | from sage.structure.unique_representation import UniqueRepresentation |
| 22 | from sage.groups.group import Group |
| 23 | from sage.rings.integer import Integer |
| 24 | from sage.rings.integer_ring import IntegerRing |
| 25 | from sage.graphs.graph import Graph |
| 26 | |
| 27 | class RightAngledArtinGroup(Group, UniqueRepresentation): |
| 28 | r""" |
| 29 | The right-angled Artin group defined by a graph `G`. |
| 30 | |
| 31 | Let `\Gamma = \{V(\Gamma), E(\Gamma)\}` be a simple graph. |
| 32 | A *right-angled Artin group* (commonly abbriated as RAAG) is the group |
| 33 | |
| 34 | .. MATH:: |
| 35 | |
| 36 | A_{\Gamma} = \langle g_v : v \in V(\Gamma) |
| 37 | \mid [g_u, g_v] \text{ if } \{u, v\} \notin E(\Gamma) \rangle. |
| 38 | |
| 39 | These are sometimes known as graph groups or partitally commutative groups. |
| 40 | This RAAG's contains both free groups, given by the complete graphs, |
| 41 | and free abelian groups, given by disjoint vertices. |
| 42 | |
| 43 | .. NOTE:: |
| 44 | |
| 45 | This is the opposite convention of some papers. |
| 46 | |
| 47 | EXAMPLES:: |
| 48 | |
| 49 | sage: Gamma = Graph(4) |
| 50 | sage: G = RightAngledArtinGroup(Gamma) |
| 51 | sage: a,b,c,d = G.gens() |
| 52 | sage: a*c*d^4*a^-3*b |
| 53 | v0^-2*v1*v2*v3^4 |
| 54 | |
| 55 | sage: Gamma = graphs.CompleteGraph(4) |
| 56 | sage: G = RightAngledArtinGroup(Gamma) |
| 57 | sage: a,b,c,d = G.gens() |
| 58 | sage: a*c*d^4*a^-3*b |
| 59 | v0*v2*v3^4*v0^-3*v1 |
| 60 | |
| 61 | sage: Gamma = graphs.CycleGraph(5) |
| 62 | sage: G = RightAngledArtinGroup(Gamma) |
| 63 | sage: G |
| 64 | Right-angled Artin group on 5 generators |
| 65 | sage: a,b,c,d,e = G.gens() |
| 66 | sage: e^-1*c*b*e*b^-1*c^-4 |
| 67 | v2^-3 |
| 68 | """ |
| 69 | @staticmethod |
| 70 | def __classcall_private__(cls, G): |
| 71 | """ |
| 72 | Normalize input to ensure a unique representation. |
| 73 | |
| 74 | TESTS:: |
| 75 | |
| 76 | sage: G1 = RightAngledArtinGroup(graphs.CycleGraph(5)) |
| 77 | sage: Gamma = Graph([(0,1),(1,2),(2,3),(3,4),(4,0)]) |
| 78 | sage: G2 = RightAngledArtinGroup(Gamma) |
| 79 | sage: G3 = RightAngledArtinGroup([(0,1),(1,2),(2,3),(3,4),(4,0)]) |
| 80 | sage: G1 is G2 and G2 is G3 |
| 81 | True |
| 82 | """ |
| 83 | if not isinstance(G, Graph): |
| 84 | G = Graph(G) |
| 85 | G = G.copy() |
| 86 | G._immutable = True |
| 87 | return super(RightAngledArtinGroup, cls).__classcall__(cls, G) |
| 88 | |
| 89 | def __init__(self, G): |
| 90 | """ |
| 91 | Initialize ``self``. |
| 92 | |
| 93 | INPUT: |
| 94 | |
| 95 | - ``G`` -- a graph |
| 96 | |
| 97 | TESTS:: |
| 98 | |
| 99 | sage: G = RightAngledArtinGroup(graphs.CycleGraph(5)) |
| 100 | sage: TestSuite(G).run() |
| 101 | """ |
| 102 | self._graph = G |
| 103 | Group.__init__(self) |
| 104 | |
| 105 | def _repr_(self): |
| 106 | """ |
| 107 | Return a string representation of ``self``. |
| 108 | |
| 109 | TESTS:: |
| 110 | |
| 111 | sage: RightAngledArtinGroup(graphs.CycleGraph(5)) |
| 112 | Right-angled Artin group on 5 generators |
| 113 | """ |
| 114 | return "Right-angled Artin group on {} generators".format(self._graph.num_verts()) |
| 115 | |
| 116 | def gen(self, i): |
| 117 | """ |
| 118 | Return the ``i``-th generator of ``self``. |
| 119 | |
| 120 | EXAMPLES:: |
| 121 | |
| 122 | sage: Gamma = graphs.CycleGraph(5) |
| 123 | sage: G = RightAngledArtinGroup(Gamma) |
| 124 | sage: G.gen(2) |
| 125 | v2 |
| 126 | """ |
| 127 | return self.element_class(self, [(i, 1)]) |
| 128 | |
| 129 | def gens(self): |
| 130 | """ |
| 131 | Return the generators of ``self``. |
| 132 | |
| 133 | EXAMPLES:: |
| 134 | |
| 135 | sage: Gamma = graphs.CycleGraph(5) |
| 136 | sage: G = RightAngledArtinGroup(Gamma) |
| 137 | sage: G.gens() |
| 138 | (v0, v1, v2, v3, v4) |
| 139 | sage: Gamma = Graph([('x', 'y'), ('y', 'zeta')]) |
| 140 | sage: G = RightAngledArtinGroup(Gamma) |
| 141 | sage: G.gens() |
| 142 | (vx, vy, vzeta) |
| 143 | """ |
| 144 | return tuple(self.gen(i) for i in range(self._graph.num_verts())) |
| 145 | |
| 146 | def ngens(self): |
| 147 | """ |
| 148 | Return the number of generators of ``self``. |
| 149 | |
| 150 | EXAMPLES:: |
| 151 | |
| 152 | sage: Gamma = graphs.CycleGraph(5) |
| 153 | sage: G = RightAngledArtinGroup(Gamma) |
| 154 | sage: G.ngens() |
| 155 | 5 |
| 156 | """ |
| 157 | return self._graph.num_verts() |
| 158 | |
| 159 | def cardinality(self): |
| 160 | """ |
| 161 | Return the number of group elements. |
| 162 | |
| 163 | OUTPUT: |
| 164 | |
| 165 | Infinity. |
| 166 | |
| 167 | EXAMPLES:: |
| 168 | |
| 169 | sage: Gamma = graphs.CycleGraph(5) |
| 170 | sage: G = RightAngledArtinGroup(Gamma) |
| 171 | sage: G.cardinality() |
| 172 | +Infinity |
| 173 | """ |
| 174 | from sage.rings.infinity import Infinity |
| 175 | return Infinity |
| 176 | |
| 177 | order = cardinality |
| 178 | |
| 179 | def as_permutation_group(self): |
| 180 | """ |
| 181 | Raises a ``ValueError`` error since right-angled Artin groups |
| 182 | are infinite, so they have no isomorphic permutation group. |
| 183 | |
| 184 | EXAMPLES:: |
| 185 | |
| 186 | sage: Gamma = graphs.CycleGraph(5) |
| 187 | sage: G = RightAngledArtinGroup(Gamma) |
| 188 | sage: G.as_permutation_group() |
| 189 | Traceback (most recent call last): |
| 190 | ... |
| 191 | ValueError: the group is infinite |
| 192 | """ |
| 193 | raise ValueError("the group is infinite") |
| 194 | |
| 195 | def graph(self): |
| 196 | """ |
| 197 | Return the defining graph of ``self``. |
| 198 | |
| 199 | EXAMPLES:: |
| 200 | |
| 201 | sage: Gamma = graphs.CycleGraph(5) |
| 202 | sage: G = RightAngledArtinGroup(Gamma) |
| 203 | sage: G.graph() |
| 204 | Cycle graph: Graph on 5 vertices |
| 205 | """ |
| 206 | return self._graph.copy() |
| 207 | |
| 208 | @cached_method |
| 209 | def one(self): |
| 210 | """ |
| 211 | Return the identity element `1`. |
| 212 | |
| 213 | EXAMPLES:: |
| 214 | |
| 215 | sage: Gamma = graphs.CycleGraph(5) |
| 216 | sage: G = RightAngledArtinGroup(Gamma) |
| 217 | sage: G.one() |
| 218 | 1 |
| 219 | """ |
| 220 | return self.element_class(self, []) |
| 221 | |
| 222 | one_element = one |
| 223 | |
| 224 | def _element_constructor_(self, x): |
| 225 | """ |
| 226 | Construct an element of ``self`` from ``x``. |
| 227 | |
| 228 | TESTS:: |
| 229 | |
| 230 | sage: Gamma = graphs.CycleGraph(5) |
| 231 | sage: G = RightAngledArtinGroup(Gamma) |
| 232 | sage: elt = G([[0,3], [3,1], [2,1], [1,1], [3,1]]); elt |
| 233 | v0^3*v3*v2*v1*v3 |
| 234 | sage: G(elt) |
| 235 | v0^3*v3*v2*v1*v3 |
| 236 | sage: G(1) |
| 237 | 1 |
| 238 | """ |
| 239 | if isinstance(x, RightAngledArtinGroup.Element): |
| 240 | if x.parent() is self: |
| 241 | return x |
| 242 | raise ValueError("there is no coercion from {} into {}".format(x.parent(), self)) |
| 243 | if x == 1: |
| 244 | return self.one() |
| 245 | verts = self._graph.vertices() |
| 246 | x = map(lambda s: [verts.index(s[0]), s[1]], x) |
| 247 | return self.element_class(self, self._normal_form(x)) |
| 248 | |
| 249 | def _normal_form(self, word): |
| 250 | """ |
| 251 | Return the normal form of the word ``word``. Helper function for |
| 252 | creaing elements. |
| 253 | |
| 254 | EXAMPLES:: |
| 255 | |
| 256 | sage: Gamma = graphs.CycleGraph(5) |
| 257 | sage: G = RightAngledArtinGroup(Gamma) |
| 258 | sage: G._normal_form([[0,2], [3,1], [2,1], [0,1], [1,1], [3,1]]) |
| 259 | [[0, 3], [3, 1], [2, 1], [1, 1], [3, 1]] |
| 260 | sage: a,b,c,d,e = G.gens() |
| 261 | sage: a^2 * d * c * a * b * d |
| 262 | v0^3*v3*v2*v1*v3 |
| 263 | sage: a*b*d == d*a*b and a*b*d == a*d*b |
| 264 | True |
| 265 | sage: a*c*a^-1*c^-1 |
| 266 | 1 |
| 267 | sage: (a*b*c*d*e)^2 * (a*b*c*d*e)^-2 |
| 268 | 1 |
| 269 | """ |
| 270 | pos = 0 |
| 271 | G = self._graph |
| 272 | w = map(list, word) # Make a (2 level) deep copy |
| 273 | while pos < len(w): |
| 274 | comm_set = [w[pos][0]] # The current set of totally commuting elements |
| 275 | i = pos + 1 |
| 276 | |
| 277 | while i < len(w): |
| 278 | letter = w[i][0] # The current letter |
| 279 | # Check if this could fit in the commuting set |
| 280 | if letter in comm_set: |
| 281 | # Try to move it in |
| 282 | if any(G.has_edge(w[j][0], letter) for j in range(pos + len(comm_set), i)): |
| 283 | # We can't, so go onto the next letter |
| 284 | i += 1 |
| 285 | continue |
| 286 | j = comm_set.index(letter) |
| 287 | w[pos+j][1] += w[i][1] |
| 288 | w.pop(i) |
| 289 | i -= 1 # Since we removed a syllable |
| 290 | # Check cancellations |
| 291 | if w[pos+j][1] == 0: |
| 292 | w.pop(pos+j) |
| 293 | comm_set.pop(j) |
| 294 | i -= 1 |
| 295 | if len(comm_set) == 0: |
| 296 | pos = 0 # Start again since cancellation can be pronounced effects |
| 297 | break |
| 298 | elif all( not G.has_edge(w[j][0], letter) for j in range(pos, i)): |
| 299 | j = 0 |
| 300 | for x in comm_set: |
| 301 | if x > letter: |
| 302 | break |
| 303 | j += 1 |
| 304 | w.insert(pos+j, w.pop(i)) |
| 305 | comm_set.insert(j, letter) |
| 306 | |
| 307 | i += 1 |
| 308 | pos += len(comm_set) |
| 309 | return w |
| 310 | |
| 311 | class Element(ClonableArray): |
| 312 | """ |
| 313 | An element of a right-angled Artin group (RAAG). |
| 314 | |
| 315 | Elements of RAAGs are modeled as lists of pairs ``[i, p]`` where |
| 316 | ``i`` is the index of a vertex in the defining graph (with some |
| 317 | fixed order of the vertices) and ``p`` is the power. |
| 318 | """ |
| 319 | def check(self): |
| 320 | """ |
| 321 | Check if ``self`` is a valid element. Nothing to check. |
| 322 | |
| 323 | TESTS:: |
| 324 | |
| 325 | sage: Gamma = graphs.CycleGraph(5) |
| 326 | sage: G = RightAngledArtinGroup(Gamma) |
| 327 | sage: elt = G.gen(2) |
| 328 | sage: elt.check() |
| 329 | """ |
| 330 | pass |
| 331 | |
| 332 | def _repr_(self): |
| 333 | """ |
| 334 | Return a string representation of ``self``. |
| 335 | |
| 336 | TESTS:: |
| 337 | |
| 338 | sage: Gamma = graphs.CycleGraph(5) |
| 339 | sage: G = RightAngledArtinGroup(Gamma) |
| 340 | sage: a,b,c,d,e = G.gens() |
| 341 | sage: a * b^2 * e^-3 |
| 342 | v0*v1^2*v4^-3 |
| 343 | sage: Gamma = Graph([('x', 'y'), ('y', 'zeta')]) |
| 344 | sage: G = RightAngledArtinGroup(Gamma) |
| 345 | sage: x,y,z = G.gens() |
| 346 | sage: z * y^-2 * x^3 |
| 347 | vx^3*vy^-2*vzeta |
| 348 | """ |
| 349 | if len(self) == 0: |
| 350 | return '1' |
| 351 | v = self.parent()._graph.vertices() |
| 352 | to_str = lambda i,p: "v{}".format(i) if p == 1 else "v{}^{}".format(i, p) |
| 353 | return '*'.join(to_str(v[i], p) for i,p in self) |
| 354 | |
| 355 | def _latex_(self): |
| 356 | r""" |
| 357 | Return a LaTeX representation of ``self``. |
| 358 | |
| 359 | TESTS:: |
| 360 | |
| 361 | sage: Gamma = graphs.CycleGraph(5) |
| 362 | sage: G = RightAngledArtinGroup(Gamma) |
| 363 | sage: a,b,c,d,e = G.gens() |
| 364 | sage: latex(a*b*e^-4*d^3) |
| 365 | \sigma_{0}\sigma_{1}\sigma_{4}^{-4}\sigma_{3}^{3} |
| 366 | sage: latex(G.one()) |
| 367 | 1 |
| 368 | sage: Gamma = Graph([('x', 'y'), ('y', 'zeta')]) |
| 369 | sage: G = RightAngledArtinGroup(Gamma) |
| 370 | sage: x,y,z = G.gens() |
| 371 | sage: latex(x^-5*y*z^3) |
| 372 | \sigma_{\text{\texttt{x}}}^{-5}\sigma_{\text{\texttt{y}}}\sigma_{\text{\texttt{zeta}}}^{3} |
| 373 | """ |
| 374 | if len(self) == 0: |
| 375 | return '1' |
| 376 | |
| 377 | from sage.misc.latex import latex |
| 378 | latexrepr = '' |
| 379 | v = self.parent()._graph.vertices() |
| 380 | for i,p in self: |
| 381 | latexrepr += "\\sigma_{{{}}}".format(latex(v[i])) |
| 382 | if p != 1: |
| 383 | latexrepr += "^{{{}}}".format(p) |
| 384 | return latexrepr |
| 385 | |
| 386 | def _mul_(self, y): |
| 387 | """ |
| 388 | Return ``self`` multiplied by ``y``. |
| 389 | |
| 390 | TESTS:: |
| 391 | |
| 392 | sage: Gamma = graphs.CycleGraph(5) |
| 393 | sage: G = RightAngledArtinGroup(Gamma) |
| 394 | sage: a,b,c,d,e = G.gens() |
| 395 | sage: a * b |
| 396 | v0*v1 |
| 397 | sage: b * a |
| 398 | v1*v0 |
| 399 | sage: a*b*c*d*e |
| 400 | v0*v1*v2*v3*v4 |
| 401 | sage: a^2*d*c*a*b*d |
| 402 | v0^3*v3*v2*v1*v3 |
| 403 | sage: e^-1*a*b*d*c*a^-2*e*d*b^2*e*b^-3 |
| 404 | v4^-1*v0*v3*v1*v0^-2*v2*v1^-1*v4*v3*v4 |
| 405 | """ |
| 406 | P = self.parent() |
| 407 | lst = list(self) + list(y) |
| 408 | return self.__class__(self.parent(), P._normal_form(lst)) |
| 409 | |
| 410 | def __invert__(self): |
| 411 | """ |
| 412 | Return the inverse of ``self``. |
| 413 | |
| 414 | TESTS:: |
| 415 | |
| 416 | sage: Gamma = graphs.CycleGraph(5) |
| 417 | sage: G = RightAngledArtinGroup(Gamma) |
| 418 | sage: a,b,c,d,e = G.gens() |
| 419 | sage: (a * b)^-2 |
| 420 | v1^-1*v0^-1*v1^-1*v0^-1 |
| 421 | """ |
| 422 | return self.__class__(self.parent(), map(lambda x: [x[0], -x[1]], reversed(self))) |
| 423 | |