1##############################################################################
2##
3#W  gpdaut.gi              GAP4 package `groupoids'              Chris Wensley
4#W                                                                & Emma Moore
5#Y  Copyright (C) 2000-2019, Emma Moore and Chris Wensley,
6#Y  School of Computer Science, Bangor University, U.K.
7##
8
9#############################################################################
10##
11#M  GroupoidAutomorphismByObjectPermNC
12#M  GroupoidAutomorphismByObjectPerm
13##
14InstallMethod( GroupoidAutomorphismByObjectPermNC ,
15    "for a single piece groupoid and a permutation of objects", true,
16    [ IsGroupoid and IsDirectProductWithCompleteDigraphDomain,
17      IsHomogeneousList ], 0,
18function( gpd, oims )
19
20    local obs, gens, ngens, images, i, a, pt, ph, mor, L;
21
22    obs := gpd!.objects;
23    gens := GeneratorsOfGroupoid( gpd );
24    ngens := Length( gens );
25    images := [1..ngens];
26    for i in [1..ngens] do
27        a := gens[i];
28        pt := Position( obs, a![2] );
29        ph := Position( obs, a![3] );
30        images[i] := Arrow( gpd, a![1], oims[pt], oims[ph] );
31    od;
32    if ( fail in images ) then
33        Error( "the set of images contains 'fail'" );
34    fi;
35    mor := GroupoidHomomorphismFromSinglePiece( gpd, gpd, gens, images );
36    SetIsInjectiveOnObjects( mor, true );
37    SetIsSurjectiveOnObjects( mor, true );
38    L := [1..Length(obs)];
39    SortParallel( ShallowCopy( oims ), L );
40    SetOrder( mor, Order( PermList( L ) ) );
41    SetIsGroupoidAutomorphismByObjectPerm( mor, true );
42    return mor;
43end );
44
45InstallMethod( GroupoidAutomorphismByObjectPermNC ,
46    "for a single piece groupoid with rays rep", true,
47    [ IsGroupoid and IsSinglePieceRaysRep, IsHomogeneousList ], 0,
48function( gpd, oims )
49
50    local iso, inv, sgpd, aut;
51
52    iso := IsomorphismStandardGroupoid( gpd, ObjectList( gpd ) );
53    inv := InverseGeneralMapping( iso );
54    sgpd := Image( iso );
55    aut := GroupoidAutomorphismByObjectPermNC( sgpd, oims );
56    return iso * aut * inv;
57end );
58
59InstallMethod( GroupoidAutomorphismByObjectPermNC,
60    "for a hom discrete groupoid and a permutation of objects", true,
61    [ IsHomogeneousDiscreteGroupoid, IsHomogeneousList ], 0,
62function( gpd, oims )
63
64    local gpd1, gp, id, homs, mor, L;
65
66    gpd1 := Pieces( gpd )[1];
67    gp := gpd1!.magma;
68    id := IdentityMapping( gp );
69    homs := List( oims, o -> id );
70    mor := GroupoidHomomorphismFromHomogeneousDiscreteNC
71               ( gpd, gpd, homs, oims );
72    SetIsInjectiveOnObjects( mor, true );
73    SetIsSurjectiveOnObjects( mor, true );
74    L := [1..Length(oims)];
75    SortParallel( ShallowCopy( oims ), L );
76    SetOrder( mor, Order( PermList( L ) ) );
77    SetIsGroupoidAutomorphismByObjectPerm( mor, true );
78    return mor;
79end );
80
81InstallMethod( GroupoidAutomorphismByObjectPerm,
82    "for a groupoid and a permutation of objects", true,
83    [ IsGroupoid, IsHomogeneousList ], 0,
84function( gpd, oims )
85
86    local obs, pos;
87
88    obs := ObjectList( gpd );
89    pos := PermList( List( oims, o -> Position( obs, o ) ) );
90    if ( pos = fail ) then
91        Error( "object images not a permutation of the objects" );
92    fi;
93    return GroupoidAutomorphismByObjectPermNC( gpd, oims );
94end );
95
96#############################################################################
97##
98#M  GroupoidAutomorphismByGroupAutoNC
99#M  GroupoidAutomorphismByGroupAuto
100##
101InstallMethod( GroupoidAutomorphismByGroupAutoNC ,
102    "for a groupoid and an automorphism of the root group", true,
103    [ IsGroupoid and IsSinglePiece, IsGroupHomomorphism and IsBijective ], 0,
104function( gpd, hom )
105
106    local gens, images, i, a, mor;
107
108    gens := GeneratorsOfGroupoid( gpd );
109    images := ShallowCopy( gens );
110    for i in [1..Length(gens) - Length(ObjectList(gpd)) + 1] do
111        a := gens[i];
112        images[i] := Arrow( gpd, ImageElm(hom,a![1]), a![2], a![3] );
113    od;
114    mor := GroupoidHomomorphismFromSinglePiece( gpd, gpd, gens, images );
115    SetIsInjectiveOnObjects( mor, true );
116    SetIsSurjectiveOnObjects( mor, true );
117    SetOrder( mor, Order( hom ) );
118    SetIsGroupoidAutomorphismByGroupAuto( mor, true );
119    return mor;
120end );
121
122InstallMethod( GroupoidAutomorphismByGroupAuto,
123    "for a groupoid and an automorphism of the root group", true,
124    [ IsGroupoid and IsSinglePiece, IsGroupHomomorphism ], 0,
125function( gpd, hom )
126
127    local rgp;
128
129    rgp := gpd!.magma;
130    if not ( (Source(hom) = rgp) and (Range(hom) = rgp) ) then
131        Error( "hom not an endomorphism of the root group" );
132    fi;
133    if not IsBijective( hom ) then
134        Error( "hom is not an automorphism" );
135    fi;
136    return GroupoidAutomorphismByGroupAutoNC( gpd, hom );
137end );
138
139#############################################################################
140##
141#M  GroupoidAutomorphismByGroupAutosNC
142#M  GroupoidAutomorphismByGroupAutos
143##
144InstallMethod( GroupoidAutomorphismByGroupAutosNC ,
145    "for homogeneous discrete groupoid and automorphism list", true,
146    [ IsHomogeneousDiscreteGroupoid, IsHomogeneousList ], 0,
147function( gpd, homs )
148
149    local obs, orders, m;
150
151    obs := ObjectList( gpd );
152    m := GroupoidHomomorphismFromHomogeneousDiscreteNC( gpd, gpd, homs, obs );
153    SetIsEndoGeneralMapping( m, true );
154    SetIsInjectiveOnObjects( m, true );
155    SetIsSurjectiveOnObjects( m, true );
156    orders := List( homs, h -> Order( h ) );
157    SetOrder( m, Lcm( orders ) );
158    return m;
159end );
160
161InstallMethod( GroupoidAutomorphismByGroupAutos,
162    "for homogeneous discrete groupoid and automorphism list", true,
163    [ IsHomogeneousDiscreteGroupoid, IsHomogeneousList ], 0,
164function( gpd, homs )
165
166    local pieces, len, i, p, g, h;
167
168    pieces := Pieces( gpd );
169    len := Length( pieces );
170    if not ( len = Length( homs ) ) then
171        Error( "length of <homs> not equal to number of objects on <gpd>," );
172    fi;
173    for i in [1..len] do
174        p := pieces[i];
175        g := p!.magma;
176        h := homs[i];
177        if not ( (Source(h) = g) and (Range(h) = g) ) then
178            Error( "<h> not an endomorphism of group <g> at object <i>," );
179        fi;
180        if not IsBijective( h ) then
181            Error( "<h> not an automorphism of group <g> at object <i>," );
182        fi;
183    od;
184    return GroupoidAutomorphismByGroupAutosNC( gpd, homs );
185end );
186
187#############################################################################
188##
189#M  GroupoidAutomorphismByRayShiftsNC
190#M  GroupoidAutomorphismByRayShifts
191##
192InstallMethod( GroupoidAutomorphismByRayShiftsNC ,
193    "for a groupoid and a list of elements of the root group", true,
194    [ IsGroupoid and IsSinglePiece, IsHomogeneousList ], 0,
195function( gpd, shifts )
196
197    local gens, ngens, nobs, images, i, k, a, mor;
198
199    gens := GeneratorsOfGroupoid( gpd );
200    ngens := Length( gens );
201    nobs := Length( gpd!.objects );
202    images := ShallowCopy( gens );
203    k := ngens - nobs;
204    for i in [2..nobs] do
205        a := gens[i+k];
206        images[i+k] := Arrow( gpd, a![1]*shifts[i], a![2], a![3] );
207    od;
208    mor := GroupoidHomomorphismFromSinglePiece( gpd, gpd, gens, images );
209    SetOrder( mor, Lcm( List( shifts, i -> Order(i) ) ) );
210    SetIsInjectiveOnObjects( mor, true );
211    SetIsSurjectiveOnObjects( mor, true );
212    SetIsGroupoidAutomorphismByRayShifts( mor, true );
213    return mor;
214end );
215
216InstallMethod( GroupoidAutomorphismByRayShifts,
217    "for a groupoid and a list of elements of the root group", true,
218    [ IsGroupoid and IsSinglePiece, IsHomogeneousList ], 0,
219function( gpd, shifts )
220
221    local rgp, rays, nobs, conj;
222
223    rgp := gpd!.magma;
224    rays := gpd!.rays;
225    nobs := Length( gpd!.objects );
226    if not ForAll( shifts, s -> s in rgp ) then
227        Error( "ray shifts not all in the root group" );
228    fi;
229    if not ( shifts[1] = One( rgp ) ) then
230        Error( "the first ray shift is not the identity" );
231    fi;
232    return GroupoidAutomorphismByRayShiftsNC( gpd, shifts );
233end );
234
235#############################################################################
236##
237#M  GroupoidInnerAutomorphism
238##
239InstallMethod( GroupoidInnerAutomorphism,
240    "for a groupoid and an element", true,
241    [ IsGroupoid, IsGroupoidElement ], 0,
242function( gpd, e )
243    Error( "not yet implemented for unions of groupoids" );
244end );
245
246InstallMethod( GroupoidInnerAutomorphism,
247    "for a groupoid and an element", true,
248    [ IsGroupoid and IsSinglePieceDomain, IsGroupoidElement ], 0,
249function( gpd, e )
250
251    local gens, images;
252
253    Info( InfoGroupoids, 3, "GroupoidInnerAutomorphism from single piece" );
254    gens := GeneratorsOfGroupoid( gpd );
255    images := List( gens, g -> g^e );
256    return GroupoidHomomorphism( gpd, gpd, gens, images );
257end );
258
259InstallMethod( GroupoidInnerAutomorphism,
260    "for a groupoid and an element", true,
261    [ IsGroupoid and IsDiscreteDomainWithObjects, IsGroupoidElement ], 0,
262function( gpd, e )
263
264    local obs, nobs, pos, id, auts;
265
266    Info( InfoGroupoids, 3, "GroupoidInnerAutomorphism from discrete domain" );
267    obs := gpd!.objects;
268    nobs := Length( obs );
269    pos := Position( obs, e![2] );
270    id := IdentityMapping( gpd!.magma );
271    auts := List( [1..nobs], i -> id );
272    auts[pos] := InnerAutomorphism( gpd!.magma, e![1] );
273    return GroupoidAutomorphismByGroupAutosNC( gpd, auts );
274end );
275
276InstallOtherMethod( GroupoidInnerAutomorphism,
277    "for a groupoid, a subgroupoid, and an element", true,
278    [ IsGroupoid and IsSinglePieceDomain, IsGroupoid, IsGroupoidElement ], 0,
279function( gpd, sub, e )
280
281    local gens, images, inn, res;
282
283    Info( InfoGroupoids, 3, "GroupoidInnerAutomorphism for a subgroupoid" );
284    if not IsSubgroupoid( gpd, sub ) then
285        Error( "sub is not a subgroupoid of gpd" );
286    fi;
287    gens := GeneratorsOfGroupoid( gpd );
288    images := List( gens, g -> g^e );
289    inn := GroupoidHomomorphism( gpd, gpd, gens, images );
290    res := RestrictedMappingGroupoids( inn, sub );
291    return res;
292end );
293
294#############################################################################
295##
296#M  Size( <agpd> ) . . . . . . . . . . . . . . . . . for a connected groupoid
297##
298InstallMethod( Size, "for a groupoid automorphism group", true,
299    [ IsAutomorphismGroupOfGroupoid ], 0,
300function( agpd )
301
302    local gpd, gp, n, aut;
303
304    gpd := AutomorphismDomain( agpd );
305    gp := gpd!.magma;
306    if IsSinglePieceDomain( gpd ) then
307        n := Length( ObjectList( gpd ) );
308        aut := AutomorphismGroup( gpd!.magma );
309        return Factorial( n ) * Size( aut ) * Size( gp )^(n-1);
310    elif IsDiscreteDomainWithObjects( gpd )
311             and IsHomogeneousDomainWithObjects( gpd ) then
312        n := Length( ObjectList( gpd ) );
313        aut := AutomorphismGroup( gpd!.magma );
314        return Factorial( n ) * Size( aut )^n;
315    fi;
316    return fail;
317end );
318
319##############################################################################
320##
321#M  NiceObjectAutoGroupGroupoid( <gpd>, <aut> ) . . create a nice monomorphism
322##
323InstallMethod( NiceObjectAutoGroupGroupoid, "for a single piece groupoid", true,
324    [ IsGroupoid and IsSinglePieceDomain, IsAutomorphismGroupOfGroupoid ], 0,
325function( gpd, aut )
326
327    local genaut, rgp, genrgp, nrgp, agp, genagp, nagp, iso1, im1, iso2,
328          pagp, iso, obs, n, L, symm, gensymm, nsymm, ngp, genngp, nngp,
329          ninfo, nemb, gens1, i, pi, imi, j, gens2, k, krgp, ikrgp,
330          pasy, esymm, epagp, genpasy, gens12, actgp, ok, action, sdp,
331          sinfo, siso, ssdp, epasy, engp, gennorm, norm, a, c, c1, c2, c12,
332          agens1, agens2, agens3, agens, nat, autgen, eno, gim1, gim2, gim3,
333          niceob, nicemap;
334
335    genaut := GeneratorsOfGroup( aut );
336    ## first: automorphisms of the root group
337    rgp := gpd!.magma;
338    genrgp := GeneratorsOfGroup( rgp );
339    nrgp := Length( genrgp );
340    agp := AutomorphismGroup( rgp );
341    genagp := SmallGeneratingSet( agp );
342    nagp := Length( genagp );
343    iso1 := IsomorphismPermGroup( agp );
344    im1 := Image( iso1 );
345    iso2 := SmallerDegreePermutationRepresentation( im1 );
346    pagp := Image( iso2 );
347    iso := iso1*iso2;
348    agens1 := List( genagp, a -> ImageElm( iso, a ) );
349    ## second: permutations of the objects
350    obs := gpd!.objects;
351    n := Length( obs );
352    if ( n = 1 ) then
353        return pagp;
354    elif ( n = 2 ) then
355        gensymm := [ (1,2) ];
356    else
357        L := [2..n];
358        Append( L, [1] );
359        gensymm := [ PermList(L), (1,2) ];  #? replace (1,2) with (n-1,n) ??
360    fi;
361    symm := Group( gensymm );
362    nsymm := Length( gensymm );
363    ## third: ray products
364    ngp := DirectProduct( ListWithIdenticalEntries( n, rgp ) );
365    genngp := GeneratorsOfGroup( ngp );
366    nngp := Length( genngp );
367    ninfo := DirectProductInfo( ngp );
368    ## force construction of the n embeddings
369    for i in [1..n] do
370        k := Embedding( ngp, i );
371    od;
372    nemb := ninfo!.embeddings;
373    # action of agp on ngp
374    gens1 := [1..nagp];
375    for i in [1..nagp] do
376        k := genagp[i];
377        krgp := List( genrgp, r -> ImageElm( k, r ) );
378        ikrgp := [ ];
379        for j in [1..n] do
380            Append( ikrgp, List( krgp, x -> ImageElm( nemb[j], x ) ) );
381        od;
382        gens1[i] := GroupHomomorphismByImages( ngp, ngp, genngp, ikrgp );
383    od;
384    ## action of symm on ngp = rgp^n
385    gens2 := [1..nsymm];
386    for i in [1..nsymm] do
387        pi := gensymm[i]^-1;
388        gens2[i] := GroupHomomorphismByImages( ngp, ngp, genngp,
389            List( [1..nngp], j -> genngp[ RemInt( j-1, nrgp ) + 1
390            + nrgp * (( QuoInt( j-1, nrgp ) + 1 )^pi - 1 ) ] ) );
391    od;
392    pasy := DirectProduct( pagp, symm );
393    epagp := Embedding( pasy, 1 );
394    agens1 := List( agens1, g -> ImageElm( epagp, g ) );
395    esymm := Embedding( pasy, 2 );
396    agens2 := List( gensymm, g -> ImageElm( esymm, g ) );
397    ## genpasy := GeneratorsOfGroup( pasy );
398    genpasy := Concatenation( agens1, agens2 );
399    #  construct the semidirect product
400    gens12 := Concatenation( gens1, gens2 );
401    actgp := Group( gens12 );
402    ok := IsGroupOfAutomorphisms( actgp );
403    action := GroupHomomorphismByImages( pasy, actgp, genpasy, gens12 );
404    sdp := SemidirectProduct( pasy, action, ngp );
405    Info( InfoGroupoids, 2, "sdp has ",
406          Length(GeneratorsOfGroup(sdp)), " gens." );
407    sinfo := SemidirectProductInfo( sdp );
408    siso := SmallerDegreePermutationRepresentation( sdp );
409    ssdp := Image( siso );
410    epasy := Embedding( sdp, 1 ) * siso;
411    agens1 := List( agens1, g -> ImageElm( epasy, g ) );
412    agens2 := List( agens2, g -> ImageElm( epasy, g ) );
413    engp := Embedding( sdp, 2 ) * siso;
414    #  why [3..nngp]?  no doubt because Sn has two generators?
415    agens3 := List( genngp{[3..nngp]}, g -> ImageElm( engp, g ) );
416    #  construct the normal subgroup
417    gennorm := [1..nrgp ];
418    for i in [1..nrgp] do
419        c := genrgp[i];
420        a := GroupHomomorphismByImages( rgp, rgp, genrgp, List(genrgp,x->x^c) );
421        c1 := ImageElm( epasy, ImageElm( epagp, ImageElm( iso, a ) ) );
422        c := c^-1;
423        c2 := ImageElm( engp,
424            Product( List( [1..n], j->ImageElm( nemb[j], c ) ) ) );
425        gennorm[i] := c1 * c2;
426    od;
427    norm := Subgroup( ssdp, gennorm );
428    ok := IsNormal( ssdp, norm );
429    if not ok then
430        Error( "norm should be a normal subgroup of ssdp" );
431    fi;
432    nat := NaturalHomomorphismByNormalSubgroup( ssdp, norm );
433    niceob := Image( nat );
434    eno := ListWithIdenticalEntries( n+2, 0 );
435    eno[1] := iso * epagp * epasy * nat;   ## agp->pagp->pasy->ssdp->niceob
436    eno[2] := esymm * epasy * nat;         ##      symm->pasy->ssdp->niceob
437    for i in [1..n] do
438        gim1 := List( genrgp, g -> ImageElm( nemb[i], g ) );
439        gim2 := List( gim1, g -> ImageElm( engp, g ) );
440        gim3 := List( gim2, g -> ImageElm( nat, g ) );
441        eno[i+2] := GroupHomomorphismByImages( rgp, niceob, genrgp, gim3 );
442    od;
443    autgen := Concatenation( agens1, agens2, agens3 );
444    agens := List( autgen, g -> ImageElm( nat, g ) );
445    return [ niceob, eno ];
446end );
447
448InstallMethod( NiceObjectAutoGroupGroupoid, "for a hom discrete groupoid", true,
449    [ IsHomogeneousDiscreteGroupoid, IsAutomorphismGroupOfGroupoid ], 0,
450function( gpd, aut )
451
452    local pieces, obs, m, p1, g1, geng1, ng1, ag1, genag1, nag,
453          iso1, ag2, genag2, mag2, genmag2, nmag2, minfo, i, k, memb,
454          K, L, symm, gensymm, imact, pi, actgp, ok, action, sdp, sinfo,
455          siso, ssdp, esymm, egensymm, emag2, egenmag2, agens,
456          im1, im2, im3, eno;
457
458    pieces := Pieces( gpd );
459    obs := ObjectList( gpd );
460    m := Length( obs );
461    ## first: automorphisms of the object groups
462    p1 := pieces[1];
463    g1 := p1!.magma;
464    geng1 := GeneratorsOfGroup( g1 );
465    ng1 := Length( geng1 );
466    ag1 := AutomorphismGroup( g1 );
467    genag1 := SmallGeneratingSet( ag1 );
468    nag := Length( genag1 );
469    iso1 := IsomorphismPermGroup( ag1 );
470    genag2 := List( genag1, g -> ImageElm( iso1, g ) );
471    ag2 := Group( genag2 );
472    mag2 := DirectProduct( ListWithIdenticalEntries( m, ag2 ) );
473    genmag2 := GeneratorsOfGroup( mag2 );
474    nmag2 := Length( genmag2 );
475    minfo := DirectProductInfo( mag2 );
476    ## force construction of the m embeddings
477    for i in [1..m] do
478        k := Embedding( mag2, i );
479    od;
480    memb := minfo!.embeddings;
481    ## second: permutations of the objects
482    if ( m = 1 ) then
483        Error( "only one object, so no permutations" );
484    elif ( m = 2 ) then
485        K := [1];
486        gensymm := [ (1,2) ];
487    else
488        K := [1,2];
489        L := [2..m];
490        Append( L, [1] );
491        gensymm := [ PermList(L), (1,2) ];  #? replace (1,2) with (m-1,m) ??
492    fi;
493    symm := Group( gensymm );
494    ## action of symm on mag2 = ag2^m
495    imact := ShallowCopy( K );
496    for i in K do
497        pi := gensymm[i]^-1;
498        imact[i] := GroupHomomorphismByImages( mag2, mag2, genmag2,
499            List( [1..nmag2], j -> genmag2[ RemInt( j-1, nag ) + 1
500            + nag * (( QuoInt( j-1, nag ) + 1 )^pi - 1 ) ] ) );
501    od;
502    #  construct the semidirect product: symm acting on mag2
503    actgp := Group( imact );
504    ok := IsGroupOfAutomorphisms( actgp );
505    action := GroupHomomorphismByImages( symm, actgp, gensymm, imact );
506    sdp := SemidirectProduct( symm, action, mag2 );
507    Info( InfoGroupoids, 2, "sdp has ",
508          Length(GeneratorsOfGroup(sdp)), " gens." );
509    sinfo := SemidirectProductInfo( sdp );
510    ## (13/04/16) comment this out for the moment and replace with identity
511    ## siso := SmallerDegreePermutationRepresentation( sdp );
512    siso := IdentityMapping( sdp );
513    ssdp := Image( siso );
514    esymm := Embedding( sdp, 1 ) * siso;
515    egensymm := List( gensymm, g -> ImageElm( esymm, g ) );
516    emag2 := Embedding( sdp, 2 ) * siso;
517    egenmag2 := List( genmag2{[1..nag]}, g -> ImageElm( emag2, g ) );
518    eno := ListWithIdenticalEntries( m+1, 0 );
519    eno[1] := esymm;
520    for i in [1..m] do
521        im1 := List( genag1, g -> ImageElm( iso1, g ) );
522        im2 := List( im1, g -> ImageElm( memb[i], g ) );
523        im3 := List( im2, g -> ImageElm( emag2, g ) );
524        eno[i+1] := GroupHomomorphismByImages( ag1, ssdp, genag1, im3 );
525    od;
526    agens := Concatenation( egensymm, egenmag2 );
527    return [ ssdp, agens, eno ];
528end );
529
530#############################################################################
531##
532#M  AutomorphismGroupOfGroupoid( <gpd> )
533##
534InstallMethod( AutomorphismGroupOfGroupoid, "for one-object groupoid", true,
535    [ IsGroupoid and IsSinglePieceDomain and IsDiscreteDomainWithObjects ], 0,
536function( gpd )
537
538    local autgen, rgp, agp, genagp, a, a0, ok, id, aut;
539
540    Info( InfoGroupoids, 2,
541          "AutomorphismGroupOfGroupoid for one-object groupoids" );
542    autgen := [ ];
543    rgp := gpd!.magma;
544    agp := AutomorphismGroup( rgp );
545    genagp := SmallGeneratingSet( agp );
546    for a in genagp do
547        a0 := GroupoidAutomorphismByGroupAutoNC( gpd, a );
548        ok := IsAutomorphismWithObjects( a0 );
549        Add( autgen, a0 );
550    od;
551    id := IdentityMapping( gpd );
552    aut := GroupWithGenerators( autgen, id );
553    SetIsAutomorphismGroup( aut, true );
554    SetIsFinite( aut, true );
555    SetIsAutomorphismGroupOfGroupoid( aut, true );
556    SetAutomorphismGroup( gpd, aut );
557    SetAutomorphismDomain( aut, gpd );
558    Info( InfoGroupoids, 2, "nice object not yet coded in this case" );
559    return aut;
560end );
561
562InstallMethod( AutomorphismGroupOfGroupoid, "for a single piece groupoid",
563    true, [ IsGroupoid and IsSinglePieceDomain ], 0,
564function( gpd )
565
566    local autgen, nautgen, rgp, genrgp, agp, genagp, a, obs, n, imobs,
567          L, ok, id, ids, cids, i, c, aut, niceob, nicemap, rgh, ioo, ior;
568
569    Info( InfoGroupoids, 2,
570          "AutomorphismGroupOfGroupoid for single piece groupoids" );
571    ##  first: automorphisms of the root group
572    autgen := [ ];
573    rgp := gpd!.magma;
574    genrgp := GeneratorsOfGroup( rgp );
575    agp := AutomorphismGroup( rgp );
576    genagp := SmallGeneratingSet( agp );
577    for a in genagp do
578        Add( autgen, GroupoidAutomorphismByGroupAutoNC( gpd, a ) );
579    od;
580    ##  second: permutations of the objects
581    obs := gpd!.objects;
582    n := Length( obs );
583    if ( n = 2 ) then
584        imobs := [ obs[2], obs[1] ];
585        Add( autgen, GroupoidAutomorphismByObjectPerm( gpd, imobs ) );
586    else
587        L := [2..n];
588        Append( L, [1] );
589        imobs := List( L, i -> obs[i] );
590        Add( autgen, GroupoidAutomorphismByObjectPerm( gpd, imobs ) );
591        imobs := ShallowCopy( obs );
592        imobs[1] := obs[2];
593        imobs[2] := obs[1];
594        Add( autgen, GroupoidAutomorphismByObjectPerm( gpd, imobs ) );
595    fi;
596    ##  third: add in the ray prods
597    ids := List( obs, o -> One( rgp ) );
598    for i in [2..n] do
599        for c in genrgp do
600            cids := ShallowCopy( ids );
601            cids[i] := c;
602            Add( autgen, GroupoidAutomorphismByRayShifts( gpd, cids ) );
603        od;
604    od;
605    nautgen := Length( autgen );
606    ##  generating set for the automorphism group now complete
607    for a in autgen do
608        ok := IsAutomorphismWithObjects( a );
609    od;
610    id := IdentityMapping( gpd );
611    ## imobs := L[0];
612    aut := GroupWithGenerators( autgen, id );
613    SetIsAutomorphismGroup( aut, true );
614    SetIsFinite( aut, true );
615    SetIsAutomorphismGroupOfGroupoid( aut, true );
616    niceob := NiceObjectAutoGroupGroupoid( gpd, aut );
617    SetNiceObject( aut, niceob[1] );
618    SetEmbeddingsInNiceObject( aut, niceob[2] );
619    #?  SetInnerAutomorphismsAutomorphismGroup( aut, ?? );
620    SetAutomorphismGroup( gpd, aut );
621    SetAutomorphismDomain( aut, gpd );
622
623    ## now construct nicemap using GroupHomomorphismByFunction
624    nicemap := GroupHomomorphismByFunction( aut, niceob[1],
625        function( alpha )
626            local G, autG, q, eno, obG, nobG, oha, j, ioa, Lpos, Lmap;
627            G := Source( alpha );
628            autG := AutomorphismGroup( G );
629            q := One( NiceObject( autG ) );
630            eno := EmbeddingsInNiceObject( autG );
631            obG := ObjectList( G );
632            nobG := Length( obG );
633            rgh := RootGroupHomomorphism( alpha );
634            q := ImageElm( eno[1], rgh );
635            ior := ImageElementsOfRays( alpha );
636            for j in [1..nobG] do
637                q := q * ImageElm( eno[j+2], ior[j] );
638            od;
639            ioo := ImagesOfObjects( alpha );
640            Lpos := List( ioo, j -> Position( obG, j ) );
641            Lmap := MappingPermListList( [1..nobG], Lpos );
642            q := q * ImageElm( eno[2], Lmap );
643            return q;
644        end);
645
646    SetNiceMonomorphism( aut, nicemap );
647    ## SetIsHandledByNiceMonomorphism( aut, true );
648    SetIsCommutative( aut, IsCommutative( niceob[1] ) );
649    if HasName( gpd ) then
650        SetName( aut, Concatenation( "Aut(", Name( gpd ), ")" ) );
651    fi;
652    return aut;
653end );
654
655InstallMethod( AutomorphismGroupOfGroupoid, "for a hom. discrete groupoid",
656    true, [ IsHomogeneousDiscreteGroupoid ], 0,
657function( gpd )
658
659    local pieces, autgen, nautgen, p1, g1, ag1, id1, genag1, a, b, auts,
660          obs, n, imobs, L, ok, id, ids, aut, niceob, nicemap;
661
662    Info( InfoGroupoids, 2,
663          "AutomorphismGroupOfGroupoid for homogeneous discrete groupoids" );
664    pieces := Pieces( gpd );
665    obs := ObjectList( gpd );
666    n := Length( obs );
667    ##  first: automorphisms of the first object group
668    autgen := [ ];
669    p1 := pieces[1];
670    g1 := p1!.magma;
671    ag1 := AutomorphismGroup( g1 );
672    id1 := IdentityMapping( g1 );
673    auts := ListWithIdenticalEntries( n, id1 );
674    genag1 := SmallGeneratingSet( ag1 );
675    for a in genag1 do
676        auts[1] := a;
677        b := GroupoidAutomorphismByGroupAutos( gpd, auts );
678        SetIsGroupWithObjectsHomomorphism( b, true );
679        Add( autgen, b );
680    od;
681    ##  second: permutations of the objects
682    if ( n = 2 ) then
683        imobs := [ obs[2], obs[1] ];
684        Add( autgen, GroupoidAutomorphismByObjectPerm( gpd, imobs ) );
685    else
686        L := [2..n];
687        Append( L, [1] );
688        imobs := List( L, i -> obs[i] );
689        Add( autgen, GroupoidAutomorphismByObjectPerm( gpd, imobs ) );
690        imobs := ShallowCopy( obs );
691        imobs[1] := obs[2];
692        imobs[2] := obs[1];
693        Add( autgen, GroupoidAutomorphismByObjectPerm( gpd, imobs ) );
694    fi;
695    ##  generating set for the automorphism group now complete
696    nautgen := Length( autgen );
697    for a in autgen do
698        ok := IsAutomorphismWithObjects( a );
699    od;
700    id := IdentityMapping( gpd );
701    ## imobs := L[0];
702    aut := GroupWithGenerators( autgen, id );
703    SetIsAutomorphismGroup( aut, true );
704    SetIsFinite( aut, true );
705    SetIsAutomorphismGroupOfGroupoid( aut, true );
706    niceob := NiceObjectAutoGroupGroupoid( gpd, aut );
707    SetNiceObject( aut, niceob[1] );
708    SetEmbeddingsInNiceObject( aut, niceob[3] );
709    SetAutomorphismGroup( gpd, aut );
710    SetAutomorphismDomain( aut, gpd );
711
712    ## now construct nicemap using GroupHomomorphismByFunction
713    nicemap := GroupHomomorphismByFunction( aut, niceob[1],
714        function( alpha )
715            local G, autG, q, eno, obG, nobG, oha, j, ioa, Lpos, Lmap;
716            G := Source( alpha );
717            autG := AutomorphismGroup( G );
718            q := One( NiceObject( autG ) );
719            eno := EmbeddingsInNiceObject( autG );
720            obG := ObjectList( G );
721            nobG := Length( obG );
722            oha := ObjectHomomorphisms( alpha );
723            for j in [1..nobG] do
724                q := q * ImageElm( eno[j+1], oha[j] );
725            od;
726            ioa := ImagesOfObjects( alpha );
727            Lpos := List( ioa, j -> Position( obG, j ) );
728            Lmap := MappingPermListList( Lpos, [1..nobG] );
729            q := q * ImageElm( eno[1], Lmap );
730            return q;
731        end);
732
733    SetNiceMonomorphism( aut, nicemap );
734    ## SetIsHandledByNiceMonomorphism( aut, true );
735    #?  SetInnerAutomorphismsAutomorphismGroup( aut, ?? );
736    SetIsCommutative( aut, IsCommutative( niceob[1] ) );
737    if HasName( gpd ) then
738        SetName( aut, Concatenation( "Aut(", Name( gpd ), ")" ) );
739    fi;
740    return aut;
741end );
742
743InstallMethod( AutomorphismGroupOfGroupoid, "for an arbitrary groupoid",
744    true, [ IsGroupoid ], 0,
745function( gpd )
746    Info( InfoGroupoids, 1,
747          "use AutomorphismGroupoidOfGroupoid for a union of pieces" );
748    return fail;
749end );
750
751## ========================================================================
752##                     Homogeneous groupoid automorphisms
753## ======================================================================== ##
754
755InstallMethod( \in,
756    "method for an automorphism of a single object groupoid", true,
757    [ IsGroupWithObjectsHomomorphism, IsGroupoidHomomorphismCollection ],
758    0,
759function( arr, aut0 )
760
761    local o;
762
763    o := ObjectList( aut0 )[1];
764    if not ( ( o = arr![2] ) and ( o = arr![3] ) ) then
765        return false;
766    fi;
767    return ( arr![1] in aut0!.magma );
768end );
769
770InstallMethod( \in,
771    "method for an automorphism of a single piece groupoid", true,
772    [ IsGroupWithObjectsHomomorphism, IsAutomorphismGroupOfGroupoid ],
773    0,
774function( a, aut )
775
776    local gpd, gp, data;
777
778    gpd := AutomorphismDomain( aut );
779    gp := gpd!.magma;
780    data := MappingToSinglePieceData( a )[1];
781    if not ( data[1] in AutomorphismGroup( gp ) ) then
782        return false;
783    fi;
784    if not ForAll( data[2], o -> o in ObjectList( gpd ) ) then
785        return false;
786    fi;
787    if not ForAll( data[3], e -> e in gp ) then
788        return false;
789    fi;
790    return true;
791end );
792
793InstallMethod( \in,
794    "for groupoid hom and automorphism group : discrete", true,
795    [ IsGroupoidHomomorphismFromHomogeneousDiscrete and IsGroupoidHomomorphism,
796      IsAutomorphismGroupOfGroupoid ], 0,
797function( a, aut )
798
799    local gens, g1, gpd, obs, imobs, G, AG;
800
801    gens := GeneratorsOfGroup( aut );
802    gpd := AutomorphismDomain( aut );
803    if not ( ( Source(a) = gpd ) and ( Range(a) = gpd ) ) then
804        Info( InfoGroupoids, 2, "require Source(a) = Range(a) = gpd" );
805        return false;
806    fi;
807    obs := ObjectList( gpd );
808    imobs := ShallowCopy( ImagesOfObjects( a ) );
809    Sort( imobs );
810    if not ( obs = imobs ) then
811        Info( InfoGroupoids, 2, "incorrect images of objects" );
812        return false;
813    fi;
814    G := Source(a)!.magma;
815    AG := AutomorphismGroup( G );
816    if not ForAll( ObjectHomomorphisms(a), h -> h in AG ) then
817        Info( InfoGroupoids, 2, "object homomorphisms incorrect" );
818        return false;
819    fi;
820    #? is there anything else to test?
821    return true;
822end );
823
824##############################################################################
825##  methods added 11/09/18 for automorphisms of homogeneous groupoids
826
827InstallMethod( GroupoidAutomorphismByPiecesPermNC,
828    "for a homogeneous groupoid and a permutation of the pieces", true,
829    [ IsGroupoid and IsHomogeneousDomainWithObjects, IsPerm ], 0,
830function( gpd, p )
831
832    local pieces, n, isos, mor;
833
834    pieces := Pieces( gpd );
835    n := Length( pieces );
836    isos := List( [1..n],
837                i -> IsomorphismNewObjects( pieces[i], pieces[i^p]!.objects ) );
838    mor := HomomorphismByUnion( gpd, gpd, isos );
839    SetOrder( mor, Order( p ) );
840    SetIsGroupoidAutomorphismByPiecesPerm( mor, true );
841    return mor;
842end );
843
844InstallMethod( GroupoidAutomorphismByPiecesPerm,
845    "for a homogeneous groupoid and a permutation of pieces", true,
846    [ IsGroupoid and IsHomogeneousDomainWithObjects, IsPerm ], 0,
847function( gpd, p )
848
849    local pieces, n, obs, pos;
850
851    pieces := Pieces( gpd );
852    n := Length( pieces );
853    if ( LargestMovedPoint( p ) > n ) then
854        Error( "degree of permutation too large" );
855    fi;
856    return GroupoidAutomorphismByPiecesPermNC( gpd, p );
857end );
858
859#############################################################################
860##
861#M  IsomorphismClassPositionsOfGroupoid
862##
863InstallMethod( IsomorphismClassPositionsOfGroupoid, "for a gpd with pieces",
864    true, [ IsGroupoid ], 0,
865function( gpd )
866
867    local P, lenP, found, i, classes, L, j, iso;
868
869    P := Pieces( gpd );
870    lenP := Length( P );
871    found := ListWithIdenticalEntries( lenP, false );
872    i := 0;
873    classes := [ ];
874    while ( i < lenP ) do
875        i := i+1;
876        while ( ( i <= lenP ) and found[i] ) do
877            i := i + 1;
878        od;
879        if ( i <= lenP ) then
880            L := [ i ];
881            found[i] := true;
882            for j in [i+1..lenP] do
883                if not found[j] then
884                    iso := IsomorphismGroupoids( P[i], P[j] );
885                    if not ( iso = fail ) then
886                        Add( L, j );
887                        found[j] := true;
888                        fi;
889                    fi;
890                od;
891            Add( classes, L );
892        fi;
893    od;
894    return classes;
895end );
896
897#############################################################################
898##
899#M  AutomorphismGroupoidOfGroupoid
900##
901InstallMethod( AutomorphismGroupoidOfGroupoid, "for a single piece groupoid",
902    true, [ IsGroupoid and IsSinglePieceDomain ], 0,
903function( gpd )
904
905    local obs, A;
906
907    A := AutomorphismGroupOfGroupoid( gpd );
908    obs := ObjectList( gpd );
909    return DomainWithSingleObject( A, obs );
910end );
911
912InstallMethod( AutomorphismGroupoidOfGroupoid, "for a homogeneous groupoid",
913    true, [ IsGroupoid and IsHomogeneousDomainWithObjects ], 0,
914function( gpd )
915
916    local pieces, obs, n, p1, ap1, rays, aut, niceob, nicemap;
917
918    Info( InfoGroupoids, 2,
919          "AutomorphismGroupoidOfGroupoid for homogeneous groupoids" );
920    pieces := Pieces( gpd );
921    obs := List( pieces, p -> p!.objects );
922    n := Length( pieces );
923    p1 := pieces[1];
924    ap1 := AutomorphismGroupOfGroupoid( p1 );
925    rays := Concatenation( [ One( ap1 ) ],
926            List( [1..n-1], i -> IsomorphismNewObjects( p1, obs[i+1] ) ) );
927    aut := SinglePieceGroupoidWithRays( ap1, obs, rays );
928    if HasName( gpd ) then
929        SetName( aut, Concatenation( "Aut(", Name( gpd ), ")" ) );
930    fi;
931    SetAutomorphismDomain( aut, gpd );
932    return aut;
933end );
934
935InstallMethod( AutomorphismGroupoidOfGroupoid, "for a groupoid", true,
936    [ IsGroupoid ], 0,
937function( gpd )
938
939    local pieces, cpos, numc, obs, comp, i, lenc, pos, pi, auti, geni,
940          lenpi, idp, isos, obsi, j, pj, isoj, invj, genj, autj, isosj;
941
942    pieces := Pieces( gpd );
943    cpos := IsomorphismClassPositionsOfGroupoid( gpd );
944    numc := Length( cpos );
945    obs := List( pieces, p -> p!.objects );
946    obs := List( cpos, K -> obs{K} );
947    obs := List( obs, K -> Set( Flat( K ) ) );
948    comp := ListWithIdenticalEntries( numc, 0 );
949    for i in [1..numc] do
950        pos := cpos[i];
951        lenc := Length( pos );
952        pi := pieces[ pos[1] ];
953        auti := AutomorphismGroupOfGroupoid( pi );
954        if HasName( pi ) then
955            SetName( auti, Concatenation( "Aut(", Name(pi), ")" ) );
956        fi;
957        geni := GeneratorsOfGroup( auti );
958        lenpi := Length( pi!.objects );
959        if ( lenc = 1 ) then
960            comp[i] := DomainWithSingleObject( auti, pi!.objects );
961        else
962            obsi := List( pos, k -> pieces[k]!.objects );
963            isos := ListWithIdenticalEntries( lenpi, 0 );
964            isos[1] := IdentityMapping( auti );
965            for j in [2..lenc] do
966                pj := pieces[ pos[j] ];
967                isoj := IsomorphismGroupoids( pi, pj );
968                invj := InverseGeneralMapping( isoj );
969                genj := List( geni, g -> invj * g * isoj );
970                autj := Group( genj );
971                SetAutomorphismGroupOfGroupoid( pj, autj );
972                if HasName( pj ) then
973                    SetName( autj, Concatenation( "Aut(", Name(pj), ")" ) );
974                fi;
975                isosj := GroupHomomorphismByImagesNC(auti,autj,geni,genj);
976                SetIsInjective( isosj, true );
977                SetIsSurjective( isosj, true );
978                isos[j] := isosj;
979            od;
980            comp[i] := GroupoidByIsomorphisms( auti, obsi, isos );
981        fi;
982    od;
983    return UnionOfPieces( comp );
984end );
985
986