Projective Resolutions

AUTHORS:

class pGroupCohomology.resolution.G_ALG

A wrapper for David Green’s C-type for group algebras of finite \(p\)-groups.

NOTE:

This extension class is internally used in RESL. Its purpose is simply to provide a couple of very basic methods around the underlying C-type.

The user is warned not to use this class independently!

When an instance of G_ALG is attribute of an instance of RESL, they share some C-data. So, when deallocating them, it has to be taken care that the shared data are not freed twice (which would result in a segmentation fault). Our solution is that these C-data are freed when the RESL instance is deallocated, but are usually not freed if the G_ALG instance is deallocated.

Hence, if one would create a G_ALG instance independent from a RESL instance, the C-data would not be freed, resulting in a memory leak. Our solution for this second problem is to provide an optional argument ‘dependent’. If it is True (which is default) then the G_ALG instance behaves like being part of a RESL instance, and the C-data are not deallocated when the instance is deleted.

In the following examples, we define dependent=False, and then the C-data will be properly deallocated.

An instance of G_ALG can be created using files that are created with makeGroupData() or makeSpecialGroupData().

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, G_ALG
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: G = G_ALG(gstem,folder=gps_folder,dependent=False)
sage: G
GF(2)[8gp3]
coef()

Return the characteristic of the base field.

EXAMPLES:

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, G_ALG
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: G = G_ALG(gstem,folder=gps_folder,dependent=False)
sage: G.coef()
2
kG_map(M, x)

Image of an element of a right \(\mathbb F_pG\)-module under a \(\mathbb F_pG\)-module morphism.

INPUT:

  • M – \(((s\cdot r) \times |G|)\) Matrix_gfpn_dense matrix, representing a right-\(\mathbb F_pG\)-module morphism from a free right \(\mathbb F_pG\)-module of rank \(r\) to a free right \(\mathbb F_pG\)-module of rank \(s\), where \(G\) is a finite \(p\)-group. The data of M are organized in \(s\) blocks of \(r\) rows.

  • x – \((r \times |G|)\) Matrix_gfpn_dense matrix representing an element of a free right \(\mathbb F_pG\)-module of rank \(r\)

OUTPUT:

A \((s \times |G|)\) Matrix_gfpn_dense matrix representing the image of x under the map represented by M

EXAMPLES:

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, G_ALG
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: G = G_ALG(gstem,folder=gps_folder,dependent=False)
sage: M = MTX(MatrixSpace(GF(2),4,8, implementation=MTX), [[1,0,0,0,0,0,0,0],[0,1,0,1,0,1,0,1],[1,0,0,0,0,0,0,0],[1,0,1,0,1,0,1,0]])
sage: G.kG_map(M,MTX(MatrixSpace(GF(2),2,8, implementation=MTX), [[1,0,1,0,1,0,1,0],[0,1,0,1,0,1,0,1]]))
[1 1 1 1 1 1 1 1]
[0 0 0 1 1 1 1 0]
sage: G.kG_map(M,MTX(MatrixSpace(GF(2),2,8, implementation=MTX), [[0,1,0,1,0,1,0,1],[1,0,1,0,1,0,1,0]]))
[1 1 1 1 1 1 1 1]
[1 0 0 0 0 1 1 0]

The image of the sum is the sum of the images:

sage: G.kG_map(M, MTX(MatrixSpace(GF(2),2,8, implementation=MTX), [[1,1,1,1,1,1,1,1],[1,1,1,1,1,1,1,1]]))
[0 0 0 0 0 0 0 0]
[1 0 0 1 1 0 0 0]
l_action(M)

Return matrix for left action on \(kG\) by the element represented by a vector.

INPUT:

M – a Matrix_gfpn_dense matrix with a single row and \(|G|\) columns, representing an element of the group algebra of a finite \(p\)-group \(G\)

OUTPUT:

A \(|G|\times |G|\) Matrix_gfpn_dense matrix describing the left action of the given element on the group algebra. The result of the left action is obtained by matrix multiplication from the right side.

EXAMPLES:

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, G_ALG
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: G = G_ALG(gstem,folder=gps_folder,dependent=False)
sage: G.l_action(MTX(MatrixSpace(GF(2),1,8, implementation=MTX), [[1,0,0,1,0,1,1,0]]))
[1 0 0 1 0 1 1 0]
[0 1 0 0 0 1 0 1]
[0 0 1 0 0 0 0 1]
[0 0 0 1 0 0 0 1]
[0 0 0 0 1 0 0 0]
[0 0 0 0 0 1 0 0]
[0 0 0 0 0 0 1 0]
[0 0 0 0 0 0 0 1]
sage: MTX(MatrixSpace(GF(2),1,8, implementation=MTX), [[0,1,1,0,0,0,0,0]])*G.l_action(MTX(MatrixSpace(GF(2),1,8, implementation=MTX), [[0,1,1,0,1,0,0,0]]))
[0 0 0 1 1 0 1 0]
sage: G.kG_map(MTX(MatrixSpace(GF(2),1,8, implementation=MTX), [[0,1,1,0,1,0,0,0]]),MTX(MatrixSpace(GF(2),1,8, implementation=MTX), [[0,1,1,0,0,0,0,0]]))
[0 0 0 1 1 0 1 0]
order()

Return order of the group.

EXAMPLES:

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, G_ALG
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: G = G_ALG(gstem,folder=gps_folder,dependent=False)
sage: G.order()
8
r_action(M)

Return matrix for right action on kG by the element represented by a vector.

INPUT:

M – a Matrix_gfpn_dense matrix with a single row and \(|G|\) columns, representing an element of the group algebra of a finite \(p\)-group \(G\)

OUTPUT:

A \(|G|\times |G|\) Matrix_gfpn_dense matrix describing the right action of the given element on the group algebra. The result of the action is obtained by matrix multiplication from the right side.

EXAMPLES:

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, G_ALG
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: G = G_ALG(gstem,folder=gps_folder,dependent=False)
sage: print(G.r_action(MTX(MatrixSpace(GF(2),1,8, implementation=MTX), [[1,0,0,1,0,1,1,0]])))
[1 0 0 1 0 1 1 0]
[0 1 0 0 0 0 0 1]
[0 0 1 0 0 0 1 1]
[0 0 0 1 0 0 0 1]
[0 0 0 0 1 0 0 0]
[0 0 0 0 0 1 0 0]
[0 0 0 0 0 0 1 0]
[0 0 0 0 0 0 0 1]
sage: MTX(MatrixSpace(GF(2),1,8, implementation=MTX), [[0,1,1,0,0,0,0,0]])*G.r_action(MTX(MatrixSpace(GF(2),1,8, implementation=MTX), [[0,1,1,0,1,0,0,0]]))
[0 0 0 1 1 1 0 0]
sage: G.kG_map(MTX(MatrixSpace(GF(2),1,8, implementation=MTX), [[0,1,1,0,0,0,0,0]]), MTX(MatrixSpace(GF(2),1,8, implementation=MTX), [[0,1,1,0,1,0,0,0]]))
[0 0 0 1 1 1 0 0]
class pGroupCohomology.resolution.LIFTcontainer

An extension class whose purpose is to cache the lifts of chain maps of a resolution to itself.

A typical use case is the computation of cohomology rings. One frequently has to compute cup products of \(m\)- and \(n\)-cochains. To that purpose, the cochains are transformed into chain maps of degree \(m\) and \(n\). Then, the two chain maps are composed, and the result is transformed into a \((m+n)\)-cochain. For computing the composition, the \(m\)-th lift of the second chain map needs to be known.

Now, if many cup products have to be computed, it is reasonable to cache previously computed lifts.

NOTE:

Internally, any RESL instance has a member that is a LIFTcontainer instance, and if the cup product of cochains is computed (see COCH for more details), caching is automatically done.

EXAMPLE:

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL, LIFTcontainer
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: L = LIFTcontainer(R)
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: C = MTX(MatrixSpace(GF(2),1,3, implementation=MTX), [[1,0,1]], mutable=False)
sage: C3 = R.liftChainMap(R.CochainToChainmap(2,C))
sage: C3
(
      [1 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [1 0 0 0 0 0 0 0]
      [0 0 0 1 0 0 0 0]
      [0 0 0 0 0 0 0 0]
3, 1, [1 0 0 0 0 0 0 0]
)
sage: L[3,2,C] = C3[2]
sage: L.out()
{(3, 2): {[1 0 1]: [1 0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0 0]
  [1 0 0 0 0 0 0 0]
  [0 0 0 1 0 0 0 0]
  [0 0 0 0 0 0 0 0]
  [1 0 0 0 0 0 0 0]}}
sage: L[3,2,C]
[1 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[1 0 0 0 0 0 0 0]
[0 0 0 1 0 0 0 0]
[0 0 0 0 0 0 0 0]
[1 0 0 0 0 0 0 0]
export()

Store cached lifts on disk. The file names are determined by the parent of self.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL, LIFTcontainer
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: L = LIFTcontainer(R)
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: C = MTX(MatrixSpace(GF(2),1,3, implementation=MTX), [[1,0,1]], mutable=False)
sage: C3 = R.liftChainMap(R.CochainToChainmap(2,C))
sage: C3
(
      [1 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [1 0 0 0 0 0 0 0]
      [0 0 0 1 0 0 0 0]
      [0 0 0 0 0 0 0 0]
3, 1, [1 0 0 0 0 0 0 0]
)
sage: L[3,2,C] = C3[2]
sage: L.out()
{(3, 2): {[1 0 1]: [1 0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0 0]
  [0 0 0 0 0 0 0 0]
  [1 0 0 0 0 0 0 0]
  [0 0 0 1 0 0 0 0]
  [0 0 0 0 0 0 0 0]
  [1 0 0 0 0 0 0 0]}}
sage: L.export()
sage: L.out()
{(3, 2): {'file': '.../8gp3/dat/L8gp3n3d2'}}

Here are the saved contents:

sage: E = load(L.out()[(3,2)]['file'])
sage: E
[(
         [1 0 0 0 0 0 0 0]
         [0 0 0 0 0 0 0 0]
         [0 0 0 0 0 0 0 0]
         [0 0 0 0 0 0 0 0]
         [1 0 0 0 0 0 0 0]
         [0 0 0 1 0 0 0 0]
         [0 0 0 0 0 0 0 0]
[1 0 1], [1 0 0 0 0 0 0 0]
)]
sage: E[0][0] == C
True
sage: E[0][1] == C3[2]
True
out()

Return the dictionary of cached lifts.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL, LIFTcontainer
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: L = LIFTcontainer(R)
sage: L.out()
{}

In principle, any data can be stored in a LIFTcontainer.

sage: L[1,2,3] = 4
sage: L.out()
{(1, 2): {3: 4}}
parent()

Return the resolution for which self was defined.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL, LIFTcontainer
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: L = LIFTcontainer(R)
sage: R is L.parent()
True
class pGroupCohomology.resolution.MasseyDefiningSystems(all=True, *L)

A tool for computing defining systems for Massey products.

NOTE:

This class is used behind the scenes in massey_products().

INPUT:

Y_1,Y_2,...: Yoneda cochains (YCOCH) over a common resolution

The method value() returns a list of all possible values (given by Yoneda cochains) for defining systems of the Massey products of Y_1,Y2,....

EXAMPLES:

sage: from pGroupCohomology import CohomologyRing
sage: from pGroupCohomology.resolution import MasseyDefiningSystems
sage: CohomologyRing.doctest_setup()       # reset, block web access, use temporary workspace
sage: H = CohomologyRing(8,3)
sage: H.make()
sage: H.rels()
['b_1_0*b_1_1']

Since the product of the two degree one generators of H vanish, it makes sense to compute Massey products:

sage: Y1 = H.2.yoneda_cocycle()
sage: Y2 = H.3.yoneda_cocycle()
sage: M = MasseyDefiningSystems(Y1,Y2,Y1)
sage: P = M.values()
sage: len(P)
16
sage: print(P[0][0])
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0]
sage: print(P[0][1])
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
sage: print(P[1][0])
[1 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0]
sage: print(P[1][1])
[1 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 1 0 1 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]

Hence, there are both trivial and non-trivial cocycles in the Massey product of H.2, H.3, H.2.

values()

Return all possible values (Yoneda cochains) of defining systems of Massey products.

EXAMPLES:

We use an example with a non-commutative cohomology ring.:

sage: from pGroupCohomology import CohomologyRing
sage: from pGroupCohomology.resolution import MasseyDefiningSystems
sage: CohomologyRing.doctest_setup()       # reset, block web access, use temporary workspace
sage: H = CohomologyRing(9,2)
sage: H.make()
sage: H.3
a_1_0: 1-Cocycle in H^*(SmallGroup(9,2); GF(3))
sage: Y = H.3.yoneda_cocycle()
sage: M = MasseyDefiningSystems(Y,Y,Y)
sage: P = M.values()
sage: len(P)
81
sage: print(P[0][0])
[0 0 0 0 0 0 0 0 0]
[2 0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]
sage: print(P[1][0])
[0 0 0 0 0 0 0 0 0]
[2 1 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]
sage: print(P[2][0])
[0 0 0 0 0 0 0 0 0]
[2 2 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0 0]

Hence, in this case, the Massey product only contains different non-trivial cocycles.

class pGroupCohomology.resolution.RESL

Computating minimal projective resolutions for finite \(p\)-groups with coefficients in GF(p).

INPUT:

  • gstem – a string, providing a short unique descriptor of a finite \(p\)-group (see makeSpecialGroupData())

  • gps_folder (optional) – a string, defining the folder in which data for the group specified by gstem can be found. Default: gps_folder=''

  • res_folder (optional) – a string, defining the folder in which the data for the resolution will be stored. Default: res_folder=''

NOTE:

  • Usually, one wouldn’t create an instance of RESL on its own. The normal usage is to create a cohomology ring by CohomologyRing(), which internally will produce an instance of RESL.

OUTPUT:

A resolution, which is given by data files in res_folder, where the file names start with 'Res'+gstem.

EXAMPLES:

Usually, objects of type RESL will only play a role when computing a cohomology ring. But as such, they are hardly visible, and will hardly ever be directly used. Nevertheless, we hope that the following examples give some insight on how the RESL class works.

Creating a RESL object

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL, coho_logger
sage: makeGroupData(8,3,folder=tmp_root)

The gstem is '8gp3', so, the group data are stored in the folder os.path.join(tmp_root,'8gp3'), to which we refer as the stem folder. The function makeGroupData() also creates a subdirectory 'dat' of the stem folder, which is intended to be used for storing the resolution.

sage: gstem='8gp3'
sage: gps_folder = os.path.join(tmp_root,gstem)
sage: res_folder = os.path.join(gps_folder, 'dat')
sage: R = RESL(gstem,gps_folder,res_folder)
sage: R
Resolution of GF(2)[8gp3]
sage: print(R)
Resolution:
0 <- GF(2) <- GF(2)[8gp3]

So far, only term number zero of the resolution was created. We compute up to term number four, logging the computation:

sage: from pGroupCohomology import CohomologyRing
sage: CohomologyRing.global_options('info')
sage: R.nextDiff()
sage: R.nextDiff()
Resolution of GF(2)[8gp3]:
    Computing next term
    > rk P_02 =   3
sage: R.nextDiff()
    Computing next term
    > rk P_03 =   4
sage: R.nextDiff()
    Computing next term
    > rk P_04 =   5

nextDiff() writes data into the file res_folder. By default, if data from previous computations are present, they will be automatically reloaded. This can be switched off by unsetting the option 'reload'. We illustrate reloading here, by re-defining R:

sage: R = RESL(gstem,gps_folder,res_folder)
sage: R.nextDiff()
sage: R.nextDiff()
Resolution of GF(2)[8gp3]:
    Differential reloaded
    > rk P_02 =   3
sage: R.nextDiff()
    Differential reloaded
    > rk P_03 =   4
sage: R.nextDiff()
    Differential reloaded
    > rk P_04 =   5
sage: print(R)
Resolution:
0 <- GF(2) <- GF(2)[8gp3] <- rank 2 <- rank 3 <- rank 4 <- rank 5

There is a copy method for resolutions, and it is also possible to save and load RESL objects:

sage: S = copy(R)
sage: print(S)
Resolution:
0 <- GF(2) <- GF(2)[8gp3] <- rank 2 <- rank 3 <- rank 4 <- rank 5
sage: S = loads(dumps(R))
Resolution of GF(2)[8gp3]:
    Differential reloaded
    > rk P_02 =   3
    Differential reloaded
    > rk P_03 =   4
    Differential reloaded
    > rk P_04 =   5
sage: print(S)
Resolution:
0 <- GF(2) <- GF(2)[8gp3] <- rank 2 <- rank 3 <- rank 4 <- rank 5

However, R and its copy S are not fully independent, as they share the same files on the disk. Moreover, when saving R into a file ‘file.sobj’ then ‘file.sobj’ would not contain the data provided by the files in res_folder; instead, it contains pointers to these data files. Therefore, moving ‘file.sobj’ to a different platform does not suffice to reconstruct the resolution. If res_folder and gps_folder are absolute path names, then even moving the whole root folder to another location would not allow for reloading the resolution, since the paths break.

See pGroupCohomology for a discussion of that problem. However, the problem is rather easy to work around for RESL: All data files used and produced by RESL have a unique location relative to gps_folder and res_folder. And all methods producing the data files would also be able to reload the data from the files. So, a re-construction of the RESL object is easy provided the option ‘reload’ is used, and moving the folders thus is possible:

sage: tmp_root2 = tmp_dir()
sage: new_gps_folder = os.path.join(tmp_root2,gstem)
sage: new_res_folder = os.path.join(new_gps_folder,'dat')
sage: import os
sage: os.rename(tmp_root,tmp_root2)
sage: S = RESL(gstem,new_gps_folder,new_res_folder)
sage: S.nextDiff()
sage: S.nextDiff()
Resolution of GF(2)[8gp3]:
    Differential reloaded
    > rk P_02 =   3
sage: CohomologyRing.global_options('warn')
sage: del S
sage: os.rename(tmp_root2,tmp_root)

Differentials

A RESL object represents a minimal free resolution for a finite \(p\)-group \(G\), hence, it provides a sequence of free \(\mathbb F_p\)-modules that are related by homomorphisms, the differentials. The construction of the resolution relies on C-programs developped by David Green. They involve a certain non-commutative Groebner basis theory due to David Green.

A homomorphism from a rank \(r\) to a rank \(s\) free \(\mathbb F_p\)-module can be described by a \(r\times s\) matrix with coefficients in \(\mathbb F_p\). An element of \(\mathbb F_pG\) can be represented by a tuple of length \(|G|\) of elements of \(\mathbb F_p\). Therefore, the data for the differentials are stored as matrices with \(|G|\) columns and \(r\times s\) rows. Since David Green’s programs use C-MeatAxe for linear algebra over finite fields, our RESL class relies on our C-MeatAxe wrapper Matrix_gfpn_dense.

If sufficiently many terms of the resolution are computed (using nextDiff()), the differentials can be easily requested:

sage: print(R)
Resolution:
0 <- GF(2) <- GF(2)[8gp3] <- rank 2 <- rank 3 <- rank 4 <- rank 5
sage: R[2]
[0 1 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0]
[0 0 0 0 0 0 1 0]
[0 0 0 0 0 1 0 0]

So, indeed the matrix has the right dimension: The group is of order 8, and the first and second term of the resolution are of rank 2 and 3, respectively:

sage: R.rank(1)
2
sage: R.rank(2)
3

Blocks of \(s=2\) rows of the matrix correspond to elements in the image of the differential:

sage: R[2][0:2]
[0 1 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
sage: R[2][2:4]
[0 0 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0]
sage: R[2][4:6]
[0 0 0 0 0 0 1 0]
[0 0 0 0 0 1 0 0]

These blocks encode a cochain, hence, a map from some term of the resolution (here: a term of rank two, corresponding to the number of rows) to the group algebra (whose elements are encoded by a \(1\times |G|\) matrix.

The salient feature of a resolution is that the composition of two differentials is zero. This can be verified as follows (where we use get_slice(), since R[2][0:2] would return a matrix relying on a different implementation):

sage: R.applyDiff(1,R[2].get_slice(0,2))
[0 0 0 0 0 0 0 0]
sage: R.applyDiff(1,R[2].get_slice(2,4))
[0 0 0 0 0 0 0 0]
sage: R.applyDiff(1,R[2].get_slice(4,6))
[0 0 0 0 0 0 0 0]

Cochains and chain maps

A \(d\)-cochain \(c\) is a \(\mathbb F_pG\)-module morphism from the \(d\)-th term of the resolution to \(\mathbb F_p\).

Embedding \(\mathbb F_p=\mathbb F_p\cdot 1 \subset \mathbb F_pG\), we obtain a map to the 0-th term of the resolution. This gives rise to a chain map of degree \(-d\), hence, a collection of a map from the \((n+d)\)-th term \(R_{n+d}\) to the \(n\)-th term \(R_n\) of the resolution, for all \(n\ge 0\), that commute with the differentials.

It is iteratively constructed as follows. Let the map \(c_n: R_{n+d}\to R_n\) be already known. We compose the differential \(\partial_{n+1+d}\) with it and obtain a map \(\partial_{n+1+d}\circ c_n: R_{n+1+d}\to R_n\). It is easy to show that its image is contained in the image of \(\partial_{d+1}\). We choose one of the pre-images, and obtain the next term \(c_{n+1}: R_{n+1+d}\to R_{n+1}\). We refer to that construction as ‘lifting’.

Here is a step-by-step example. Note that COCH provides this functionality with high-level functions, hence, it is not needed to perform the following steps manually.

First, we define a \(1\times 3\) matrix that represents a 2-cochain, and construct the lowest term of the corresponding chain map:

sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: C = MTX(MatrixSpace(GF(2),1,3, implementation=MTX),[[1,0,1]])
sage: c0 = R.CochainToChainmap(2,C)
sage: c0
(
      [1 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
2, 0, [1 0 0 0 0 0 0 0]
)

In the next section, we discuss two differnt ways to lift the cochain. Here is the result:

sage: c1 = R.liftChainMap(c0)
sage: c1
(
      [1 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [1 0 0 0 0 0 0 0]
      [0 0 0 1 0 0 0 0]
      [0 0 0 0 0 0 0 0]
3, 1, [1 0 0 0 0 0 0 0]
)

We verify whether the result is correct. So, we first compose the third differential with c0:

sage: d3c0 = R.composeChainMaps(R[3],c0[2],3,2,0)
sage: d3c0
[0 1 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 1 0 0 0 0 1 0]
[0 0 1 0 0 0 0 0]

The matrix defining c1 contains 4 blocks of 2 rows, and we verify that their images under the first differential coincide with the rows of d3c0:

sage: d3c0.get_slice(0,1) == R.applyDiff(1,c1[2].get_slice(0,2))
True
sage: d3c0.get_slice(1,2) == R.applyDiff(1,c1[2].get_slice(2,4))
True
sage: d3c0.get_slice(2,3) == R.applyDiff(1,c1[2].get_slice(4,6))
True
sage: d3c0.get_slice(3,4) == R.applyDiff(1,c1[2].get_slice(6,8))
True

So, associated with the cochain C we obtain a chain map c. For obtaining the cup product of C with itself, we compose c with itself. We obtain a chain map of degree \(-4\), and its lowest term, composed with the augmentation map \(\mathbb F_pG\to \mathbb F_p\) yields the cochain C*C. But first, we have to lift c one step further:

sage: c2 = R.liftChainMap(c1)

This can be composed with c0:

sage: cc = R.composeChainMaps(c2[2],c0[2],4,2,0)
sage: cc
[1 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[1 0 0 0 0 0 0 0]

The basis of \(\mathbb F_pG\) is chosen so that the kernel of the augmentation map is given by all columns after the first. Hence, the cup product of C with itself is given by [1,0,0,0,1].

Using the class COCH, the computation is of course much more comfortable. For using this class, we need to create a cohomology ring, since we consider cochains as (representatives of) cohomology classes:

sage: from pGroupCohomology.cochain import COCH
sage: from pGroupCohomology import CohomologyRing
sage: CohomologyRing.doctest_setup()       # reset, block web access, use temporary workspace
sage: CohomologyRing.set_workspace(tmp_root)
sage: H = CohomologyRing(8,3, from_scratch=True)
sage: C = COCH(H,2,'C',[1,0,1])
sage: C*C
(C)*(C): 4-Cocycle in H^*(D8; GF(2))
sage: print(C*C)
4-Cocycle in H^*(D8; GF(2)),
represented by
[1 0 0 0 1]

Urbild Groebner bases and autolift

We have seen in the previous section that for dealing with chain maps and for computing cup products one has to determine pre-images of differentials. In cohomology computations, these operations occur extremely often, so, it is essential to make them fast.

When successively computing the terms of a resolutions, David Green’s programs construct so-called Urbild Groebner bases and stores them in files in the directory res_folder (as provided in the definition of R). They can also be used to lift chain maps. Using c1 from above, we get:

sage: CohomologyRing.global_options('info')
sage: c2U = R.liftChainMap(c1)

However, that method is rather slow. It is also possible to use some linear algebra to pick an element of the pre-image, but this requires to construct certain data first:

sage: R.makeAutolift(2)
Resolution of GF(2)[8gp3]:
    Make degree 2 autolift data
sage: c2A = R.liftChainMap(c1)

It takes some time to make the autolift data, but if they are present, the lifting is much faster. Hence, if possible they are used. If one wants to use the Urbild Groebner bases, this can still be done with ugb_liftChainMap(), although this has a slightly different syntax. The autolift method used to be more than 250 times faster than the Urbild Groebner basis method, but optimisations in recent package versions made the running time almost equal on some machines, while on other machines there still is a factor of about 10:

sage: CohomologyRing.global_options('warn')
sage: timeit.eval('cX = R.liftChainMap(c1)')                        # random
625 loops, best of 3: 230 µs per loop
sage: timeit.eval('cX = R.ugb_liftChainMap(c1[0]+1,c1[1]+1,c1[2])') # random
625 loops, best of 3: 297 µs per loop

In general, the lifts obtained with both methods are not the same (they may vary up to elements in the radical, hence, in the kernel of the augmentation map), but here they are the same:

sage: R.liftChainMap(c1)[2]==R.ugb_liftChainMap(c1[0]+1,c1[1]+1,c1[2])
True

Note that the construction of the autolift data in a certain degree only pays off if there will be many lifts to that degree. Hence, when computing a cohomology ring, we use the autolift method by default only up to degree 4.

But there is also another problem: The memory consumption. For some more complicated groups, the Urbild Groebner bases are huge in higher degrees, and the autolift data would even be worse. Therefore, if the non-default option 'sparse' is used, certain other data will be temporarily saved on disk before loading the Urbild Groebner bases, and vice versa.

See COHO for examples.

ChainmapToCochain(X)

Represent a chain map of degree \(n\) by a \(n\)-cochain.

INPUT:

(n,0,M)M is a \(\operatorname{rank}(P_n) \times |G|\) Matrix_gfpn_dense matrix, where \(P_n\) is the \(n\)-th term of self and \(G\) is the finite \(p\)-group under consideration.

OUTPUT:

A \(1 \times |G|\) Matrix_gfpn_dense matrix representing a \(n\)-cochain.

NOTE:

By our choice of bases of modules over the group algebra, the result only depends on the first column of M.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: C = MTX(MatrixSpace(GF(2),1,3, implementation=MTX), [[1,0,1]])
sage: print(R.ChainmapToCochain(R.CochainToChainmap(2,C)))
[1 0 1]
CochainToChainmap(*args, **kwds)

RESL.CochainToChainmap(self, long n, Matrix_gfpn_dense Coc) -> tuple

Represent a cochain (given by a matrix) by a chain map to the zeroeth term of self

INPUT: - n – an integer - C – a Matrix_gfpn_dense matrix with only one row, representing a \(n\)-cochain

OUTPUT:

(n,0,M), where the Matrix_gfpn_dense matrix M represents the lowest term of a chain map of degree \(n\).

NOTE:

By our choice of basis for modules over the group algebra, the matrix M is zero except in the first column, which is given by the transpose of C.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: C = MTX(MatrixSpace(GF(2),1,3, implementation=MTX), [[1,0,1]])
sage: c = R.CochainToChainmap(2,C)
sage: c
(
      [1 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
2, 0, [1 0 0 0 0 0 0 0]
)
DiffList()

Return the list of computed differentials of a resolution, respectively the path to saved data.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: from pGroupCohomology import CohomologyRing
sage: CohomologyRing.global_options('sparse')
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()

Since in our test we use the non-default option ‘sparse’, most of the differentials are not kept in memory but saved on disk, and R.difflist() points to their location:

sage: R.DiffList()
[
[0 1 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0],
 '.../8gp3/dat/Res8gp3d02.bin',
 '.../8gp3/dat/Res8gp3d03.bin'
]
G_ALG()

Return the G_ALG object over which the resolution is defined.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.G_ALG()
GF(2)[8gp3]
applyDiff(n, x)

Apply \(n\)-th differential map to an element \(x\) of the \(n\)-th term of self.

INPUT:

  • n – integer, determining a term of self

  • x – \((r \times |G|)\) Matrix_gfpn_dense matrix, where \(r\) is the projective rank of the \(n\)-th term of self, and \(G\) is the group upon which \(R\) is defined.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.rank(2)
3
sage: R.rank(3)
4

We will verify that the composition of the third differential with the second differential vanishes. Since the rank of the second term of the resolution is 3, the four blocks of 3 rows of the matrix R[3] correspond to generators of the image of the differntial:

sage: R.applyDiff(2,R[3].get_slice(0,3))
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
sage: R.applyDiff(2,R[3].get_slice(3,6))
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
sage: R.applyDiff(2,R[3].get_slice(6,9))
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
sage: R.applyDiff(2,R[3].get_slice(9,12))
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
coef()

Return the characteristic of the field over which self is defined.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.coef()
2
composeChainMaps(M1, M2, s, r, q)

Compose chain maps \(M1: P_s \to P_r\) with \(M2: P_r\to P_q\), where \(P\) is self.

INPUT:

  • M1, M2Matrix_gfpn_dense matrices defining morphisms from the \(s\)-th to the \(r\)-th respectively from the \(r\)-th to the \(q\)-th term of self

  • s, r, q – integers, refering to terms of self

OUTPUT:

A Matrix_gfpn_dense matrix representing the composition of M1 with M2, a chain map from the s-th to the q-th term of self.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()

We verify that the composition of two differentials vanishes:

sage: print(R.composeChainMaps(R[2],R[1],2,1,0))
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
sage: print(R.composeChainMaps(R[3],R[2],3,2,1))
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
composeListOfMaps(M1, s, L2)

Compose one chain map with a list of chain maps.

INPUT:

  • M1Matrix_gfpn_dense matrix defining a morphism from the \(s\)-th to the \(r\)-th term of self

  • s – an integer, referring to a term of self

  • L – a list/tuple whose elements are triples (r, q_i, M_i), where M_i is a Matrix_gfpn_dense matrix describing a morphism from the \(r\)-th to the \(q_i\)-th term of self

OUTPUT:

A list of triples [s,q_i,N_i], where N_i is a Matrix_gfpn_dense matrix representing a morphism from the \(s\)-th to the \(q_i\)-th term of self, namely the composition of M with M_i

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: C = MTX(MatrixSpace(GF(2),1,3, implementation=MTX), [[1,0,1]])
sage: c = R.CochainToChainmap(2,C)
sage: c
(
      [1 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
2, 0, [1 0 0 0 0 0 0 0]
)
sage: L = [(2,1,R[2]), c]
sage: Comp = R.composeListOfMaps(R[3],3,L)
sage: R.composeChainMaps(R[3],L[0][2],3,2,1) == Comp[0][2]
True
sage: R.composeChainMaps(R[3],c[2],3,2,0) == Comp[1][2]
True
deg()

Return the number of terms of self that have been computed so far.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.deg()
3
exportAction()

Internally used: Save list of \(G\)-action matrices in a file.

EXAMPLE:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL, coho_logger
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.exportAction()
sage: load(os.path.join(res_folder,'A8gp3'))
[
[1 0 0 0 0 0 0 0]  [0 1 0 0 0 0 0 0]  [0 0 1 0 0 0 0 0]
[0 1 0 0 0 0 0 0]  [0 0 0 0 0 0 0 0]  [0 0 0 1 0 0 0 0]
[0 0 1 0 0 0 0 0]  [0 0 0 0 1 0 0 0]  [0 0 0 0 0 0 0 0]
[0 0 0 1 0 0 0 0]  [0 0 0 0 0 1 0 0]  [0 0 0 0 0 0 0 0]
[0 0 0 0 1 0 0 0]  [0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 1 0]
[0 0 0 0 0 1 0 0]  [0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 0 1]
[0 0 0 0 0 0 1 0]  [0 0 0 0 0 0 0 1]  [0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 1], [0 0 0 0 0 0 0 0], [0 0 0 0 0 0 0 0],
<BLANKLINE>
[0 0 0 1 0 0 0 0]  [0 0 0 0 1 0 0 0]  [0 0 0 0 0 1 0 0]
[0 0 0 0 0 0 0 0]  [0 0 0 0 0 1 0 0]  [0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 1 0]  [0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 0 1]
[0 0 0 0 0 0 0 1]  [0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 0 1]  [0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0], [0 0 0 0 0 0 0 0], [0 0 0 0 0 0 0 0],
<BLANKLINE>
[0 0 0 0 0 0 1 0]  [0 0 0 0 0 0 0 1]
[0 0 0 0 0 0 0 1]  [0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]  [0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0], [0 0 0 0 0 0 0 0]
]

The G-action matrices are used to compute the autolift data, and only there. Hence, the action matrices will be imported if the autolift data are computed, and exported if this is finished:

sage: R.nextDiff()
sage: R.nextDiff()
sage: from pGroupCohomology import CohomologyRing
sage: CohomologyRing.global_options('info')
sage: R.makeAutolift(2)
Resolution of GF(2)[8gp3]:
    Make degree 2 autolift data
exportLifts()

Save cached lifts into files.

EXAMPLE:

sage: from pGroupCohomology import CohomologyRing
sage: from pGroupCohomology.cochain import COCH
sage: CohomologyRing.doctest_setup()       # reset, block web access, use temporary workspace
sage: H = CohomologyRing(8,3, from_scratch=True, options='sparse')
sage: C = COCH(H,1,'C',[0,1])
sage: print(C*C)
2-Cocycle in H^*(D8; GF(2)),
represented by
[0 1 0]
sage: R = H.resolution()

Since we use the non-default sparse option, some of the data is stored in a file:

sage: sorted(R.getLifts().items())
[((1, 1),
  {'file': '.../8gp3/dat/L8gp3n1d1'}),
 ((2, 1),
  {[0 1]: ((
      [0 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [1 0 0 0 0 0 0 0]
      [0 0 0 0 1 0 0 0]
2, 1, [0 0 0 0 0 0 0 0]
),
    2)})]
sage: R.exportLifts()

Now, both lifts are stored on disk:

sage: sorted(R.getLifts().items())
[((1, 1), {'file': '.../8gp3/dat/L8gp3n1d1'}),
 ((2, 1), {'file': '.../8gp3/dat/L8gp3n2d1'})]
find_bounding_chain(n, M, check=False)

Find a chain that yields a given \(n-1\) chain under the \(n\)-th differential.

INPUT:

  • n – integer, determining a term of self

  • M\((r \times |G|)\) Matrix_gfpn_dense matrix, where \(r\) is the projective rank of the \((n-1)\)-th term of self, and \(G\) is the group upon which \(R\) is defined. M represents a chain.

  • check (optional bool) – if True, verify whether the input is in the kernel of the \((n-1)\)-th boundary map.

OUTPUT:

A \(n\)-chain, represented by a \((s \times |G|)\) Matrix_gfpn_dense matrix, where \(s\) is the projective rank of the \(n\)-th term of self.

EXAMPLES:

sage: from pGroupCohomology import CohomologyRing
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: CohomologyRing.doctest_setup()       # reset, block web access, use temporary workspace
sage: H = CohomologyRing(8,3)
sage: H.make()
sage: R = H.resolution()
sage: M1 = R.find_bounding_chain(1, MTX(MatrixSpace(GF(2),1,8, implementation=MTX), [[0,1,1,0,1,1,0,1]]))
sage: print(M1)
[1 0 0 0 1 0 0 0]
[1 1 0 0 0 1 0 0]
sage: print(R.applyDiff(1,M1))
[0 1 1 0 1 1 0 1]
sage: M2 = R.find_bounding_chain(2, MTX(MatrixSpace(GF(2),2,8, implementation=MTX), [[0,1,0,1,0,0,1,0],[0,0,0,0,0,1,0,0]]))
sage: print(M2)
[1 0 1 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[1 0 0 0 0 0 0 0]
sage: print(R.applyDiff(2,M2))
[0 1 0 1 0 0 1 0]
[0 0 0 0 0 1 0 0]

Note that by default it is not verified whether the input is in the image of the \(n\)-th boundary map. In this case, the output would be nonsense. So, in case of doubt, one may use the optional parameter check:

sage: FOO =  R.find_bounding_chain(2, MTX(MatrixSpace(GF(2), 2,8, implementation=MTX), [[0,1,0,1,0,0,1,0],[0,1,1,0,0,1,0,0]]))
sage: print(FOO)
[1 0 1 0 0 0 0 0]
[1 0 0 0 0 0 0 0]
[1 0 0 0 0 0 0 0]
sage: print(R.applyDiff(2,FOO))
[0 1 0 1 0 0 1 0]
[0 0 1 0 0 1 0 0]
sage: print(R.find_bounding_chain(2, MTX(MatrixSpace(GF(2),2,8, implementation=MTX), [[0,1,0,1,0,0,1,0],[0,1,1,0,0,1,0,0]]), check=True))
Traceback (most recent call last):
...
ValueError: The given chain is no cycle
firstDiff()

Make first differential for self.

This function is usually called from nextDiff().

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: from pGroupCohomology import CohomologyRing
sage: CohomologyRing.reset()
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.firstDiff()
sage: print(R)
Resolution:
0 <- GF(2) <- GF(2)[8gp3] <- rank 2

An error is raised if it is attempted to compute the first term again.

sage: R.firstDiff()
Traceback (most recent call last):
...
IndexError: First differential is already computed
free_ugb()

Deallocate the currently loaded Urbild Groebner basis.

EXAMPLE:

sage: from pGroupCohomology import CohomologyRing
sage: CohomologyRing.doctest_setup()       # reset, block web access, use temporary workspace
sage: from pGroupCohomology.cochain import COCH
sage: H = CohomologyRing(8,3, from_scratch=True)
sage: R = H.resolution()
sage: R.nextDiff()
sage: R.nextDiff()
sage: CohomologyRing.global_options('debug','nosparse')
sage: C = COCH(H,1,'C',[1,0])
sage: D = C*C
Resolution of GF(2)[D8]:
          Compute C*C
          Compose chain maps R_2 -> R_1 -> R_0
          Lift with Urbild Groebner basis in degree 1
          load Urbild Groebner basis

Now, the Urbild Groebner basis is allocated. Since we use the option 'nosparse', it is not automatically deallocated, and is used to lift further cochains:

sage: C = COCH(H,1,'C',[0,1])
sage: D = C*C
          Compute C*C
          Compose chain maps R_2 -> R_1 -> R_0
          Lift with Urbild Groebner basis in degree 1

Now we deallocate it manually. In the subsequent computation, it is reloaded again:

sage: R.free_ugb()
          deallocate Urbild Groebner basis
sage: C = COCH(H,1,'C',[1,1])
sage: D = C*C
          Compute C*C
          Compose chain maps R_2 -> R_1 -> R_0
          Lift with Urbild Groebner basis in degree 1
          load Urbild Groebner basis
getLifts()

Return the dictionary of cached lifts.

NOTE:

That function was only created in order to provide a doc test for setLift().

EXAMPLE:

sage: from pGroupCohomology import CohomologyRing
sage: from pGroupCohomology.cochain import COCH
sage: CohomologyRing.doctest_setup()       # reset, block web access, use temporary workspace
sage: H = CohomologyRing(8,3, from_scratch=True, options='sparse')
sage: R = H.resolution()
sage: R.nextDiff()
sage: R.nextDiff()
sage: C = COCH(H,1,'C',[0,1])
sage: R.getLifts()
{}
sage: print(C*C)
2-Cocycle in H^*(D8; GF(2)),
represented by
[0 1 0]

Now, two lifts of C (considered as a chain map of degree one) are cached. If the sparse option is used (which we do here, although it is currently not the default), one of the lifts is exported to a file in order to save memory:

sage: sorted(R.getLifts().items())
[((1, 1),
  {'file': '.../8gp3/dat/L8gp3n1d1'}),
 ((2, 1),
  {[0 1]: ((
      [0 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [1 0 0 0 0 0 0 0]
      [0 0 0 0 1 0 0 0]
2, 1, [0 0 0 0 0 0 0 0]
),
    2)})]
grouporder()

Return the order of the group over wich self is defined.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.grouporder()
8
importAction()

Reload list of \(G\)-action matrices that have been exported using exportAction().

EXAMPLE:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL, coho_logger
sage: from pGroupCohomology import CohomologyRing
sage: CohomologyRing.reset()
sage: CohomologyRing.global_options('sparse')
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.exportAction()
sage: R.nextDiff()
sage: R.nextDiff()

In the documentation of exportAction(), it is shown that usually the action matrices are imported by makeAutolift(). But it is alright to do it manually; it can be seen in the log that the matrices will not be imported twice (and note that the action is to be imported only since we use the non-default sparse option):

sage: CohomologyRing.global_options('debug')
sage: R.importAction()
Resolution of GF(2)[8gp3]:
          > import action matrices
sage: R.makeAutolift(2)
          Make degree 2 autolift data
label()

Return a short descriptor of this resolution.

EXAMPLES:

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.label()
'Res8gp3'
liftChainMap(X)

Lift Chain Map.

INPUT:

(n,d,M) – a Matrix_gfpn_dense matrix M representing a morphism from the \(n\)-th to the \(d\)-th term of self, with \(d<n\).

OUTPUT:

(n+1,d+1,N) – A Matrix_gfpn_dense matrix N representing the lift of M to a morphism from the \((n+1)\)-th to the \((d+1)\)-th term of self.

NOTE:

Uses the autolift method, if possible. See RESL for an explanation of the notion ‘lift’ and of the autolift method.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: C = MTX(MatrixSpace(GF(2),1,3, implementation=MTX), [[1,0,1]])
sage: c = R.CochainToChainmap(2,C)
sage: c
(
      [1 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
2, 0, [1 0 0 0 0 0 0 0]
)
sage: cLift = R.liftChainMap(c)
sage: cLift
(
      [1 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
      [1 0 0 0 0 0 0 0]
      [0 0 0 1 0 0 0 0]
      [0 0 0 0 0 0 0 0]
3, 1, [1 0 0 0 0 0 0 0]
)
load_ugb(d)

Load Urbild Groebner basis for lifts from degree \(d-1\) to \(d\).

EXAMPLE:

sage: from pGroupCohomology import CohomologyRing
sage: from pGroupCohomology.cochain import COCH
sage: CohomologyRing.doctest_setup()       # reset, block web access, use temporary workspace
sage: H = CohomologyRing(8,3, from_scratch=True)
sage: R = H.resolution()
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: CohomologyRing.global_options('debug','nosparse')
sage: C = COCH(H,1,'C',[0,1])

Now, we load the Urbild Groebner basis for lifts to degree 1. Hence,
it is not needed to load it again in the subsequent computation.
::

    sage: R.load_ugb(1)
    Resolution of GF(2)[D8]:
              load Urbild Groebner basis
    sage: D = C*C
              Compute C*C
              Compose chain maps R_2 -> R_1 -> R_0
              Lift with Urbild Groebner basis in degree 1

But if we load the Urbild Groebner basis in a different degree, the
correct one will be automatically reloaded when necessary::

    sage: R.load_ugb(2)
              load Urbild Groebner basis
    sage: C = COCH(H,1,'C',[1,0])
    sage: D = C*C
              Compute C*C
              Compose chain maps R_2 -> R_1 -> R_0
              Lift with Urbild Groebner basis in degree 1
              load Urbild Groebner basis
makeAutolift(d)

Produce internal data that allow to quickly lift chain maps to one degree.

INPUT:

d – the degree into which it shall be lifted

EXAMPLE:

sage: from pGroupCohomology import CohomologyRing
sage: from pGroupCohomology.cochain import COCH
sage: CohomologyRing.doctest_setup()       # reset, block web access, use temporary workspace
sage: H = CohomologyRing(8,3, from_scratch=True)
sage: R = H.resolution()
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: CohomologyRing.global_options('debug')
sage: C = COCH(H,1,'C',[0,1])

For computing a cup product, the necessary lift of a chain map is done with the so-called autolift method, provided it is available. This is not yet the case, so a different, but much slower method (Urbild Groebner bases) is used:

sage: D = C*C
Resolution of GF(2)[D8]:
          Compute C*C
          Compose chain maps R_2 -> R_1 -> R_0
          Lift with Urbild Groebner basis in degree 1
          load Urbild Groebner basis

Now we create the data. And by consequence, a method is used that usually is a bit faster (but the time spent with computing the autolift data should be taken into account as well):

sage: R.makeAutolift(1)
          Make degree 1 autolift data
          > import action matrices
sage: C = COCH(H,1,'C',[1,0])
sage: D = C*C
          Compute C*C
          Compose chain maps R_2 -> R_1 -> R_0
          > Lift with the autolift method
nextDiff()

Compute next unknown differential of the resolution.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: from pGroupCohomology import CohomologyRing
sage: CohomologyRing.reset()
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R
Resolution of GF(2)[8gp3]

So far, only term number zero of the resolution was created. We compute up to term number four:

sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: print(R)
Resolution:
0 <- GF(2) <- GF(2)[8gp3] <- rank 2 <- rank 3 <- rank 4 <- rank 5
sage: R[3]
[0 1 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 1 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 1 0]
[0 0 0 0 0 0 0 0]
[0 1 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 1 0 0]
[0 0 1 0 0 0 0 0]
rank(n=- 1)

Return projective rank(s) of a term or all terms of self.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: print(R)
Resolution:
0 <- GF(2) <- GF(2)[8gp3] <- rank 2 <- rank 3 <- rank 4
sage: R.rank()
(1, 2, 3, 4)
sage: R.rank(2)
3
setLift(C, n_max)

Make a trivial entry in the list of known lifts for a given cochain.

INPUT:

  • C, a cochain defined over self

  • n, maximal degree to which the cochain shall be lifted

NOTE:

This function should only be of internal use

EXAMPLE:

sage: from pGroupCohomology import CohomologyRing
sage: CohomologyRing.doctest_setup()       # reset, block web access, use temporary workspace
sage: H = CohomologyRing(8,3, from_scratch=True)
sage: R = H.resolution()
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()

Now we construct a cochain:

sage: from pGroupCohomology.cochain import COCH
sage: C = COCH(H,2,'C',[1,0,1])
sage: R.getLifts()
{}
sage: R.setLift(C,4)
sage: list(R.getLifts().keys())
[(2, 2)]

Hence, now there are cochains (i.e., chain maps) of degree 2 whose lifts are known in degree 2.

sage: list(R.getLifts().values())
[{[1 0 1]: ((
      [1 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
2, 0, [1 0 0 0 0 0 0 0]
),
   4)}]

In fact, the known lift is the trivial one:

sage: R.getLifts()[(2,2)][C.MTX()][0] == R.CochainToChainmap(2,C.MTX())
True
ugb_liftChainMap(n, d, M)

Lift a chain map using Urbild Groebner bases.

INPUT:

  • n, d – integers, \(d<n\)

  • M – a Matrix_gfpn_dense matrix representing a morphism from the \((n-1)\)-th to the \((d-1)\)-th term of self.

OUTPUT:

A Matrix_gfpn_dense matrix representing the lift to a morphism from the \(n\)-th to the \(d\)-th term of self.

NOTE:

See RESL for an explanation of the notion ‘lift’. It certainly is odd that the syntax of this method differs from the syntax of liftChainMap(). Sorry.

EXAMPLES:

First, we create the basic data for the dihedral group of order 8 (compare makeGroupData()):

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, RESL
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: makeGroupData(8,3,folder=tmp_root)
sage: gstem='8gp3'
sage: gps_folder=os.path.join(tmp_root,gstem)
sage: res_folder=os.path.join(gps_folder,'dat')
sage: R=RESL(gstem,gps_folder,res_folder)
sage: R.nextDiff()
sage: R.nextDiff()
sage: R.nextDiff()
sage: C = MTX(MatrixSpace(GF(2),1,3, implementation=MTX), [[1,0,1]])
sage: c = R.CochainToChainmap(2,C)
sage: c
(
      [1 0 0 0 0 0 0 0]
      [0 0 0 0 0 0 0 0]
2, 0, [1 0 0 0 0 0 0 0]
)
sage: cLift = R.ugb_liftChainMap(3,1,c[2])
sage: cLift
[1 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[0 0 0 0 0 0 0 0]
[1 0 0 0 0 0 0 0]
[0 0 0 1 0 0 0 0]
[0 0 0 0 0 0 0 0]
[1 0 0 0 0 0 0 0]
yoneda_coboundary(X, Y, n, i)

INPUT:

  • X, Y: Matrix_gfpn_dense matrices representing the terms \(\phi_n^i: P_n\to P_{n-i}\) and \(\phi_{n+1}^i: P_{n+1}\to P_{n-i+1}\) of an element \(\phi^i\) of degree \(i\) in the Yoneda complex, where \(P_\ast\) is the underlying resolution.

  • n, i: integers, \(i \le n\).

OUTPUT:

Z: Matrix_gfpn_dense matrix representing the term \((\partial \phi^i)_{n+1}: P_{n+1}\to P_{n-i}\) representing the Yoneda coboundary of \(\phi^i\).

NOTE:

This method is mainly of internal use.

THEORY:

If \(d_\ast: P_\ast \to P_{\ast-1}\) denotes the boundary maps of \(P_\ast\) then \((\partial \phi^i)_{n+1} = \phi_n\circ d_{n+1} - (-1)^i d_{n-i+1}\circ \phi_{n+1}^i\).

TESTS:

sage: from pGroupCohomology import CohomologyRing
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: CohomologyRing.doctest_setup()       # reset, block web access, use temporary workspace
sage: H = CohomologyRing(8,3)
sage: H.make()
sage: YC = (H.2.yoneda_cocycle()*H.3.yoneda_cocycle()).find_cobounding_yoneda_cochains()
sage: [H.resolution().yoneda_coboundary(Y[0],Y[1],Y.deg(),Y.deg())==Y.coboundary()[0] for Y in YC]
[True, True, True, True]
class pGroupCohomology.resolution.RESL_sparse_unpickle_class

Used for unpickling class instances of RESL.

EXAMPLES:

sage: from pGroupCohomology import CohomologyRing
sage: CohomologyRing.doctest_setup()       # reset, block web access, use temporary workspace
sage: H = CohomologyRing(8,3)
sage: H.make()
sage: R = H.resolution()
sage: print(R)
Resolution:
0 <- GF(2) <- GF(2)[D8] <- rank 2 <- rank 3 <- rank 4
sage: R == loads(dumps(R))   # indirect doctest
True
sage: R is loads(dumps(R))
False
pGroupCohomology.resolution.baseMTX(f, m, n, i, j)

Return an immutable Matrix_gfpn_dense matrix with a single mark 1.

INPUT:

  • f – integer, the field size.

  • m, n – integers, row and column number.

  • i, j – integers, position of the single mark 1.

OUTPUT:

An immutable \((m\times n)\) Matrix_gfpn_dense matrix over \(GF(f)\) with a single entry 1 at \((i,j)\)

EXAMPLE:

sage: from pGroupCohomology.resolution import baseMTX
sage: baseMTX(3, 4, 5, 1, 2)
[0 0 0 0 0]
[0 0 1 0 0]
[0 0 0 0 0]
[0 0 0 0 0]
pGroupCohomology.resolution.makeGroupData(q, n, folder, ElAb=False, Forced=False)

Create basic data files the cohomology computation of SmallGroup(q,n).

INPUT:

  • q – the order of some finite \(p\)-group \(G\)

  • n – the number of the group in the SmallGroups library

  • folder – name of a directory in which the data files will be stored. The directory will be created, if necessary.

  • ElAb (optional bool, default False) – indicates whether the group is elementary abelian.

  • Forced (optional bool, default False) – if True, force recomputation.

OUTPUT:

Various files will be created in subdirectories of the specified folder. These files provide information needed to construct a minimal projective resolution of the group, using David Green’s programs.

ALGORITHM:

This function is based on Gap functions written by David Green.

  • First, it is checked whether there already are certain files in the specified directory. If they are, nothing is done, unless the optional argument 'forced' is True. The present data are not checked for consistency. So, if data are corrupted, one may empty the folder or simply specify 'forced=True').

  • Minimal generators for the group are computed, giving rise to a certain basis of the group algebra \(\mathbb F_pG\).

  • The matrices for left and right action of \(G\) on the group algebra are computed.

  • The greatest central elementary abelian subgroup of \(G\) and representatives for the conjugacy classes of maximal elementary abelian subgroups of \(G\) are computed.

The data are stored in files.

EXAMPLES:

We construct the data for the dihedral group of order 8, which is number 3 in the SmallGroups library. For illustration, we use logging:

sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, coho_logger
sage: from pGroupCohomology import CohomologyRing
sage: CohomologyRing.global_options('info')
sage: makeGroupData(8,3,folder=tmp_root)
<module>:
    Computing basic setup for Small Group number 1 of order 2
    Computing basic setup for Small Group number 2 of order 4
    Computing basic setup for Small Group number 3 of order 8
sage: CohomologyRing.global_options('warn')

The files defining the basis for the group algebra of the dihedral group and its special subgroups are located in subfolders of tmp_root, whose name are given by the order and the SmallGroups library number of the respective group. We call this the stem folder of the group. It looks like this:

sage: f = open(os.path.join(tmp_root,'8gp3','8gp3.nontips'))
sage: print(f.read())
2 8 4 3 2 R
(1);
a;
b;
ab;
ba;
aba;
bab;
baba;
sage: f.close()
sage: f = open(os.path.join(tmp_root,'4gp2','4gp2.nontips'))
sage: print(f.read())
2 4 2 3 2 R
(1);
a;
b;
ba;
sage: f.close()

Other files, contained in the sub-directory ‘sgp’ of the stem folder, describe the embedding of the elementary abelian subgroups. The first subgroup will always be the greatest central elementary abelian. Here is a matrix defining the embedding of the third special subgroup, which is elementary abelian of order 4. The matrix is a MeatAxe matrix (see Matrix_gfpn_dense).

sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: M = MTX.from_filename(os.path.join(tmp_root,'8gp3','sgp','8gp3sg3.ima'))
sage: print(M)
[1 0 0 0 0 0 0 0]
[0 0 0 1 1 1 1 1]
[0 1 0 0 0 0 0 0]
[0 0 0 0 0 1 0 1]
pGroupCohomology.resolution.makeSpecialGroupData(H, GStem, folder)

Creating data files for computing the cohomology of a finite \(p\)-Group.

INPUT:

  • H – a finite \(p\)-group defined in the libgap interface

  • GStem – a string, providing a short and unique descriptor of H

  • folder (optional string) – name of a directory in which the data files will be stored. The directory will be created, if necessary.

OUTPUT:

See makeGroupData()

NOTE:

In contrast to makeGroupData(), this function does not have an optional argument forced. So, if corrupted data are present for the given folder and the given GStem, they must be removed before invoking makeSpecialGroupData.

ALGORITHM:

See makeGroupData()

EXAMPLES:

We construct the data for the dihedral group of order 8. In contrast to the example for makeGroupData(), we define it directly in the Gap interface:

sage: G = libgap.DihedralGroup(8)
sage: GStem = 'DihedralGroup'
sage: tmp_root = tmp_dir()
sage: from pGroupCohomology.resolution import makeGroupData, makeSpecialGroupData, coho_logger

Again, we log the computation.

sage: from pGroupCohomology import CohomologyRing
sage: CohomologyRing.global_options('info')
sage: makeSpecialGroupData(G,GStem,folder=tmp_root)
<module>:
    Computing basic setup for Small Group number 1 of order 2
    Computing basic setup for Small Group number 2 of order 4
    Computing basic setup for DihedralGroup
sage: CohomologyRing.global_options('warn')

Now, all data concerning G are in subfolders of the stem folder of G, which is os.path.join(tmp_root,GStem). Also the file names make use of the given GStem. Here are the contents, analogous to the example of makeGroupData():

sage: f = open(os.path.join(tmp_root,GStem,GStem+'.nontips'))
sage: print(f.read())
2 8 4 3 2 R
(1);
a;
b;
ba;
bb;
bba;
bbb;
bbba;
sage: import os
sage: sorted(os.listdir(os.path.join(tmp_root,GStem,'sgp')))
['DihedralGroup.sgs',
 'DihedralGroupsg1.ima',
 'DihedralGroupsg1.irg',
 'DihedralGroupsg2.ima',
 'DihedralGroupsg2.irg',
 'DihedralGroupsg3.ima',
 'DihedralGroupsg3.irg']
sage: from sage.matrix.matrix_gfpn_dense import Matrix_gfpn_dense as MTX
sage: M = MTX.from_filename(os.path.join(tmp_root,GStem,'sgp',GStem+'sg3.ima'))
sage: print(M)
[1 0 0 0 0 0 0 0]
[0 0 0 0 1 0 0 0]
[0 1 1 1 0 0 0 0]
[0 0 0 0 0 1 1 1]

Note that the result is different from the result obtained with makeGroupData(8,3) (see makeGroupData()): We consider two different presentations of the dihedral group, so the output is not necessarily identical (but certainly isomorphic).