Projective Resolutions — "p_group_cohomology" 3.3.2 documentation

# Projective Resolutions

AUTHORS:

- Simon King <simon.king@uni-jena.de> (Cython code)

- David J. Green <david.green@uni-jena.de> (underlying C-code)

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`, `M2` – `Matrix_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:

    - `M1` – `Matrix_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).