&FG;-modules

Let &FG; be the group ring of the group &G; over the field &FF;. In this package we only consider the case where &G; is a finite p-groups and \mathbb{F} = \mathbb{F}_p is the field of p elements. In addition, we only consider free &FG;-modules.

The FpGModuleGF datatype

Modules and submodules of free &FG;-modules are represented in &HAPprime; using the FpGModuleGF datatype, where the `GF' stands for `Generator Form'. This defines a module using a group G and a set of G-generating vectors for the module's vector space, together with a group action which operates on those vectors. A free &FG;-module &FG; can be considered as a vector space \mathbb{F}^{|G|} whose basis is the elements of G. An element of &FGn; is the direct sum of n copies of &FG; and, as an element of FpGModuleGF, is represented as a vector of length n|G| with coefficients in &FF;. Representing our &FG;-module elements as vectors is ideal for our purposes since &GAP;'s representation and manipulation of vectors and matrices over small prime fields is very efficient in both memory and computation time.

The &HAP; package defines a FpGModule object, which is similar but stores a vector space basis rather than a &G;-generating set for the module's vector space. Storing a &G;-generating set saves memory, both in passive storage and in allowing more efficient versions of some computation algorithms.

There are a number of construction functions for FpGModuleGFs: see for details. A &FG;-module is defined by the following: gens, a list of &G;-generating vectors for the underlying vector space. These do not need to be minimal - they could even be a vector space basis. The MinimalGeneratorsModule functions () can be used to convert a module to one with a minimal set of generators. group, the group &G; for the module action, a function action(g, u) that represents the module's group action on vectors. It takes a group element g \in G and a vector u of length actionBlockSize and returns another vector v of the same length that is the product v = gu. If action is not provided, the canonical group permutation action is used. If the vector u is an integer multiple of actionBlockSize in length, the function action acts block-wise on the vector. actionBlockSize, the length of vectors upon which action operates. This is usually the order of the group, |G| (for example for the canonical action), but it is possible to specify this to support other possible group actions that might act on larger vectors. actionBlockSize will always be equal to the ambient dimension of the module \mathbb{F}G^1. The group, action and block size are internally wrapped up into a record groupAndAction, with entries group, action and actionBlockSize. This is used to simplify the passing of parameters to some functions.

Some additional information is sometimes needed to construct particular classes of FpGModuleGF: ambientDimension, the length of vectors in the generating set: for a module &FGn;, this is equal to n\times actionBlockSize. This is needed in the case when the list of generating vectors is empty. form, a string detailing whether the generators are known to be minimal or not, and if so in which format. It can be one of "unknown", "fullcanonical", "minimal", "echelon" or "semiechelon". Some algorithms require a particular form, and algorithms such as that manipulate a module's generators to create these forms set this entry.
Implementation details: Block echelon form
Generating vectors and their block structure

Consider the vector representation of an element in the &FG;-module (\mathbb{F}G)^2, where G is a group of order four: v \in (\mathbb{F}G)^2 = (g_1 + g_3, g_1 + g_2 + g_4) = [1 0 1 0 | 1 1 0 1] v in (FG)^2 = (g_1 + g_3, g_1 + g_2 + g_4) = [1 0 1 0 | 1 1 0 1] The first block of four entries in the vector correspond to the first &FG; summand and the second block to the second summand (and the group elements are numbered in the order provided by the &GAP; function ).

Given a G-generating set for a &FG;-module, the usual group action permutes the group elements, and thus the effect on the vector is to permute the equivalent vector elements. Each summand is independent, and so elements are permuted within the blocks (normally of size |G|) seen in the example above. A consequence of this is that if any block (i.e. summand) in a generator is entirely zero, then it remains zero under group (or &FF;) multiplication and so that generator contributes nothing to that part of the vector space. This fact enables some of the structure of the module's vector space to be inferred from the G-generators, without needing a full vector space basis . A desirable set of G-generators is one that has many zero blocks, and what we call the `block echelon' form is one that has this property.

Matrix echelon reduction and head elements

The block echelon form of a &FG;-module generating set is analagous to the echelon form of matrices, used as a first stage in many matrix algorithms, and we first briefly review matrix echelon form. In a (row) echelon-form matrix, the head element in each row (the first non-zero entry) is the identity, and is to the right of the head in the previous row. A consequence of this is that the values below each head are all zero. All zero rows are at the bottom of the matrix (or are removed). &GAP; also defines a semi-echelon form, in which it is guaranteed that all values below each head is zero, but not that each head is to the right of those above it.

Matrices can be converted into (semi-)echelon form by using Gaussian elimination to perform row reduction (for example the &GAP; function ). A typical algorithm gradually builds up a list of matrix rows with unique heads, which will eventually be an echelon-form set of basis elements for the row space of the matrix. This set is initialised with the first row of the matrix, and the algorithm is then applied to each subsequent row in turn. The basis rows in the current set are used to reduce the next row of the matrix: if, after reduction, it is non-zero then it will have a unique head and is added to the list of basis rows; if it is zero then it may be removed. The final set of vectors will be a semi-echelon basis for the row space of the original matrix, which can then be permuted to give an echelon basis if required.
Echelon block structure and minimal generators

In the same way that the echelon form is useful for vector space generators, we can convert a set of &FG;-module generators into echelon form. However, unlike multiplication by a field element, the group action on generating vectors also permutes the vector elements, so a strict echelon form is less useful. Instead, we define a `block echelon' form, treating the blocks in the vector (see example above) as the &FG;-elements to be converted into echelon form. In block-echelon form, the first non-zero block in each row is as far to the right as possible. Often, the first non-zero block in a row will be to the right of the first non-zero block in the row above, but when several generating vectors are needed in a block, this may not be the case. The following example creates a random submodule of &FGn; by picking five generating vectors at random. This module is first displayed with the original generators, and then they are converted to block echelon form using the the &HAPprime; function . The two generating sets both span the same vector space (i.e. the same &FG; module), but the latter representation is much more useful. gap> M := FpGModuleGF(RandomMat(5, 32, GF(2)), DihedralGroup(8));; gap> Display(M); Module over the group ring of Group( [ f1, f2, f3 ] ) in characteristic 2 with 5 generators in FG^4. [.1..1.1.|.1....1.|1111.11.|11.1111.] [11.1..1.|1....11.|1...111.|1...11..] [11..1.1.|1.1.1...|11...1..|.11.11..] [11111111|111...1.|.11...1.|.1..1111] [.1111111|1.1.111.|..1..1..|1.111...] gap> echM := EchelonModuleGenerators(M); rec( module := Module over the group ring of <pc group of size 8 with 3 generators> in characteristic 2 with 4 generators in FG^ 4. Generators are in minimal echelon form., headblocks := [ 1, 2, 3, 4 ] ) gap> Display(echM.module); Module over the group ring of Group( [ f1, f2, f3 ] ) in characteristic 2 with 4 generators in FG^4. [.1..1.1.|.1....1.|1111.11.|11.1111.] [........|.1111..1|111.1...|.11.11.1] [........|........|.1..1.1.|.1.1.111] [........|........|........|..1111.1] Generators are in minimal echelon form.gap> gap> M = echM.module; true

The main algorithm for converting a set of generators into echelon form is . The generators for the &FG; module are represented as rows of a matrix, and (with the canonical action) the first |G| columns of this matrix correspond to the first block, the next |G| columns to the second block, and so on. The first block of the matrix is taken and the vector space V_b spanned by the rows of that block is found (which will be a a subspace of \mathbb{F}^{|G|}). Taking the rows in the first block, find (by gradually leaving out rows) a minimal subset that generates V_b. The rows of the full matrix that correspond to this minimal subset form the first rows of the block-echelon form generators. Taking those rows, and all G-multiples of them, now calculate a semi-echelon basis for the vector space that they generate (using ). This is used to reduce all of the other generators. Since the rows we have chosen span the space of the first block, the first block in all the other rows will be reduced to zero. We can now move on to the second block.

We now look at the rows of the matrix that start (have their first non-zero entry) in the second block. In addition, some of the generators used for the first block might additionally give rise to vector space basis vectors with head elements in the second blocks. The rows need to be stored during the first stage and reused here. We find a minimal set of the matrix rows with second-block heads that, when taken with any second-block heads from the first stage, generate the entire space spanned by the second block. The vector-space basis from this new minimal set is then used to reduce the rest of the generating rows as before, reducing all of the other rows' second blocks to zero. The process is then repeated for each other block. Any generators that are completely zero are then removed. The algorithm is summarised in the following pseudocode: Let X be the list of generators still to convert (initially all the generators) Let Xe = [] be the list of generators already in block-echelon form Define X{b} to represent the $b$th block from generators X Define V(X) to represent the vector space generated by generators X ------------------------------------------------------------------------------- for b in [1..blocks] 1. Find a minimal set of generators Xm from X such that V(Xm{b} + Xe{b}) = V(X{b} + Xe{b}) 2. Remove Xm from X and add them to Xe 3. Find a semi-echelon basis for V(Xe) and use this to reduce the elements of block b in remaining vectors of X to zero end for

The result of this algorithm is a new generating set for the module that is minimal in the sense that no vector can be removed from the set and leave it still spanning the same vector space. In the case of a &FG;-module with &FF;=GF(2), this is a globally minimal set: there is no possible alternative set with fewer generators.
Intersection of two modules

Knowing the block structure of the modules enables the intersection of two modules to be calculated more efficiently. Consider two modules U and V with the block structure as given in this example: gap> DisplayBlocks(U); [*..] [**.] [.*.] gap> DisplayBlocks(V); [.**] [.**] [..*] To calculate the intersection of the two modules, it is normal to expand out the two modules to find the vector space U_\mathbb{F} and V_\mathbb{F} of the two modules and calculate the intersection as a standard vector-space intersection. However, in this case, since U has no elements in the last block, and V has no elements in the first block, the intersection must only have elements in the middle block. This means that the first generator of U and the last generator of V can not be in the intersection and can be ignored for the purposes of the intersection calculation. In general, rather than expanding the entirety of U and V into an \mathbb{F}-basis to calculate their intersection, one can expand U and V more intelligently into \mathbb{F}-bases U'_\mathbb{F} and V'_\mathbb{F} which are smaller than U_\mathbb{F} and V_\mathbb{F} but have the same intersection.

Having determined (by comparing the block structure of U and V) that only the middle block in our example contributes to the intersection, we only need to expand out the rows of U and V that have heads in that block. The first generator of U generates no elements in the middle block, and trivially be ignored. The second row of U may or may not contribute to the intersection: this will need expanding out and echelon reduced. The expanded rows that don't have heads in the central block can then be discarded, with the other rows forming part of the basis of U'_\mathbb{F}. Likewise, the third generator of U is expanded and echelon reduced to give the rest of the basis for U'_\mathbb{F}. To find V'_\mathbb{F}, the first two generators are expanded, semi-echelon reduced and the rows with heads in the middle block kept. The third generator can be ignored. Finally, the intersection of U'_\mathbb{F} and V'_\mathbb{F} can found using, for example, .

This algorithm, implemented in the function , will (for modules whose generators have zero columns) use less memory than a full vector-space expansion, and in the case where U and V have no intersection, may need to perform no expansion at all. In the worst case, both U and V will need a full expansion, using no more memory than the naive implementation. Since any full expansion is done row-by-row, with echelon reduction each time, it will in general still require less memory (but will be slower).
Construction functions
<#Include Label="FpGModuleGF_DTmanFpGModule_Con"> <#Include Label="FpGModuleFromFpGModuleGF_DTmanFpGModule_Con"> <#Include Label="MutableCopyModule_DTmanFpGModule_Con"> <#Include Label="CanonicalAction_DTmanFpGModule_Con">

Example: Constructing a FpGModuleGF

The example below constructs four different &FG;-modules, where &G; is the quaternion group of order eight, and the action is the canonical action in each case: M is the module (\mathbb{F}G)^3 S is the submodule of (\mathbb{F}G)^3 with elements only in the first summand T is a random submodule M generated by five elements U is the trivial (zero) submodule of (\mathbb{F}G)^4 We check whether S, T and U are submodules of M, examine a random element from M, and convert S into a &HAP; FpGModule. For the other functions used in this example, see Section . gap> G := SmallGroup(8, 4);; gap> M := FpGModuleGF(G, 3); Full canonical module FG^3 over the group ring of <pc group of size 8 with 3 generators> in characteristic 2 gap> gen := ListWithIdenticalEntries(24, Zero(GF(2)));; gap> gen[1] := One(GF(2));; gap> S := FpGModuleGF([gen], G); Module over the group ring of <pc group of size 8 with 3 generators> in characteristic 2 with 1 generator in FG^ 3. Generators are in minimal echelon form. gap> T := RandomSubmodule(M, 5); Module over the group ring of <pc group of size 8 with 3 generators> in characteristic 2 with 5 generators in FG^3. gap> U := FpGModuleGF(32, CanonicalGroupAndAction(G)); Module over the group ring of <pc group of size 8 with 3 generators> in characteristic 2 with 0 generators in FG^ 4. Generators are in minimal echelon form. gap> gap> IsSubModule(M, S); true gap> IsSubModule(M, T); true gap> IsSubModule(M, U); false gap> gap> e := RandomElement(M); <a GF2 vector of length 24> gap> Display([e]); . 1 1 . . 1 . . . . . 1 . . 1 1 . . 1 . 1 . . 1 gap> IsModuleElement(S, e); false gap> IsModuleElement(T, e); true gap> gap> FpGModuleFromFpGModuleGF(S); Module of dimension 8 over the group ring of <pc group of size 8 with 3 generators> in characteristic 2
Data access functions
<#Include Label="ModuleGroup_DTmanFpGModule_Dat"> <#Include Label="ModuleGroupOrder_DTmanFpGModule_Dat"> <#Include Label="ModuleAction_DTmanFpGModule_Dat"> <#Include Label="ModuleActionBlockSize_DTmanFpGModule_Dat"> <#Include Label="ModuleGroupAndAction_DTmanFpGModule_Dat"> <#Include Label="ModuleCharacteristic_DTmanFpGModule_Dat"> <#Include Label="ModuleField_DTmanFpGModule_Dat"> <#Include Label="ModuleAmbientDimension_DTmanFpGModule_Dat"> <#Include Label="AmbientModuleDimension_DTmanFpGModule_Dat"> <#Include Label="DisplayBlocks_DTmanFpGModule_Dat">
Example: Accessing data about a FpGModuleGF

In the following example, we construct three terms of a (minimal) resolution of the dihedral group of order eight, which is a chain complex of &FG;-modules. (\mathbb{F}G)^3 \to (\mathbb{F}G)^3 \to \mathbb{F}G \to \mathbb{F} \to 0 (FG)^3 -> (FG)^2 -> FG -> F -> 0 We obtain the last homomorphism in this chain complex and calculate its kernel, returning this as a FpGModuleGF. We can use the data access functions described above to extract information about this module.

See Chapters and respectively for more information about &FG;-module homomorphisms and resolutions in &HAPprime;

gap> R := ResolutionPrimePowerGroupRadical(DihedralGroup(8), 2); Resolution of length 2 in characteristic 2 for <pc group of size 8 with 3 generators> . No contracting homotopy available. A partial contracting homotopy is available.

gap> phi := BoundaryFpGModuleHomomorphismGF(R, 2); <Module homomorphism>

gap> M := KernelOfModuleHomomorphism(phi); Module over the group ring of <pc group of size 8 with 3 generators> in characteristic 2 with 15 generators in FG^3.

gap> # Now find out things about this module M gap> ModuleGroup(M); <pc group of size 8 with 3 generators> gap> ModuleGroupOrder(M); 8 gap> ModuleAction(M); function( g, v ) ... end gap> ModuleActionBlockSize(M); 8 gap> ModuleGroupAndAction(M); rec( group := <pc group of size 8 with 3 generators>, action := function( g, v ) ... end, actionOnRight := function( g, v ) ... end, actionBlockSize := 8 ) gap> ModuleCharacteristic(M); 2 gap> ModuleField(M); GF(2) gap> ModuleAmbientDimension(M); 24 gap> AmbientModuleDimension(M); 3
Generator and vector space functions
<#Include Label="FpGModuleGenerators_DTmanFpGModule_Gen"> <#Include Label="FpGModuleGeneratorsAreMinimal_DTmanFpGModule_Gen"> <#Include Label="FpGModuleGeneratorsAreEchelonForm_DTmanFpGModule_Gen"> <#Include Label="ModuleIsFullCanonical_DTmanFpGModule_Gen"> <#Include Label="FpGModuleGeneratorsForm_DTmanFpGModule_Gen"> <#Include Label="ModuleRank_DTmanFpGModule_Gen"> <#Include Label="ModuleVectorSpaceBasis_DTmanFpGModule_Gen"> <#Include Label="ModuleVectorSpaceDimension_DTmanFpGModule_Gen"> <#Include Label="MinimalGeneratorsModule_DTmanFpGModule_Gen"> <#Include Label="RadicalOfModule_DTmanFpGModule_Gen">

Example: Generators and basis vectors of a FpGModuleGF

Starting with the same module as in the earlier example (Section ), we now investigate the generators of the module M. The generating vectors (of which there are 15) returned by the function are not a minimal set, but the function creates a new object N representing the same module, but now with only four generators. The vector space spanned by these generators has 15 basis vectors, so representing the module by a G-generating set instead is much more efficient. (The original generating set in M was in fact an &FF;-basis, so the dimension of the vector space should come as no surprise.)

We can also find the radical of the module, and this is used internally for the faster, but more memory-hungry, . gap> R := ResolutionPrimePowerGroupRadical(DihedralGroup(8), 2);; gap> phi := BoundaryFpGModuleHomomorphismGF(R, 2);; gap> M := KernelOfModuleHomomorphism(phi);; gap> # gap> ModuleGenerators(M); [ <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24> ] gap> ModuleGeneratorsAreMinimal(M); false gap> ModuleGeneratorsForm(M); "unknown" gap> # gap> N := MinimalGeneratorsModuleGF(M); Module over the group ring of <pc group of size 8 with 3 generators> in characteristic 2 with 4 generators in FG^ 3. Generators are in minimal echelon form. gap> M = N; # Check that the new module spans the same space true gap> ModuleGeneratorsAreEchelonForm(N); true gap> ModuleIsFullCanonical(N); false gap> M = N; true gap> ModuleVectorSpaceBasis(N); [ <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24>, <a GF2 vector of length 24> ] gap> ModuleVectorSpaceDimension(N); 15 gap> # gap> N2 := MinimalGeneratorsModuleRadical(M); Module over the group ring of <pc group of size 8 with 3 generators> in characteristic 2 with 4 generators in FG^ 3. Generators are minimal. gap> # gap> R := RadicalOfModule(M); Module over the group ring of <pc group of size 8 with 3 generators> in characteristic 2 with 120 generators in FG^3. gap> N2 = N; true
Block echelon functions
<#Include Label="EchelonFpGModuleGeneratorsDestructive_DTmanFpGModule_Blo"> <#Include Label="ReverseEchelonModuleGenerators_DTmanFpGModule_Blo">

Example: Converting a FpGModuleGF to block echelon form

We can construct a larger module than in the earlier examples (Sections and ) by taking the kernel of the third boundary homomorphism of a minimal resolution of a group of order 32, which as returned by the function has a generating set with many redundant generators. We display the block structure of the generators of this module after applying various block echelon reduction functions. gap> R := ResolutionPrimePowerGroupRadical(SmallGroup(32, 10), 3);; gap> phi := BoundaryFpGModuleHomomorphismGF(R, 3);; gap> # gap> M := KernelOfModuleHomomorphism(phi); Module over the group ring of <pc group of size 32 with 5 generators> in characteristic 2 with 65 generators in FG^4. gap> # gap> N := SemiEchelonModuleGenerators(M); rec( module := Module over the group ring of <pc group of size 32 with 5 generators> in characteristic 2 with 5 generators in FG^ 4. Generators are in minimal semi-echelon form. , headblocks := [ 2, 3, 1, 1, 3 ] ) gap> DisplayBlocks(N.module); Module over the group ring of Group( [ f1, f2, f3, f4, f5 ] ) in characteristic 2 with 5 generators in FG^4. [.*.*] [..**] [***.] [****] [..**] Generators are in minimal semi-echelon form. gap> N2 := SemiEchelonModuleGeneratorsMinMem(M); rec( module := Module over the group ring of <pc group of size 32 with 5 generators> in characteristic 2 with 9 generators in FG^4. , headblocks := [ 2, 1, 3, 1, 1, 4, 1, 3, 4 ] ) gap> DisplayBlocks(N2.module); Module over the group ring of Group( [ f1, f2, f3, f4, f5 ] ) in characteristic 2 with 9 generators in FG^4. [.*..] [**..] [..**] [****] [****] [...*] [****] [..**] [...*] gap> # gap> EchelonModuleGeneratorsDestructive(M);; gap> DisplayBlocks(M); Module over the group ring of Group( [ f1, f2, f3, f4, f5 ] ) in characteristic 2 with 5 generators in FG^4. [***.] [****] [.*.*] [..**] [..**] Generators are in minimal echelon form. gap> ReverseEchelonModuleGeneratorsDestructive(M); Module over the group ring of <pc group of size 32 with 5 generators> in characteristic 2 with 5 generators in FG^ 4. Generators are in minimal echelon form. gap> DisplayBlocks(M); Module over the group ring of Group( [ f1, f2, f3, f4, f5 ] ) in characteristic 2 with 5 generators in FG^4. [***.] [****] [.*..] [..*.] [..**] Generators are in minimal echelon form.
Sum and intersection functions
<#Include Label="DirectSumOfModules_DTmanFpGModule_Sum"> <#Include Label="DirectDecompositionOfModule_DTmanFpGModule_Sum"> <#Include Label="IntersectionModulesDestructive_DTmanFpGModule_Sum"> <#Include Label="SumModules_DTmanFpGModule_Sum">

Example: Sum and intersection of FpGModuleGFs

We can construct the direct sum of &FG;-modules, and (attempt to) calculate a direct decomposition of a module. For example, we can show that (\mathbb{F}G)^2 \oplus \mathbb{F}G = \mathbb{F}G \oplus \mathbb{F}G \oplus \mathbb{F}G (FG)^2 (+) FG = FG (+) FG (+) FG gap> G := CyclicGroup(64);; gap> FG := FpGModuleGF(G, 1); Full canonical module FG^1 over the group ring of <pc group of size 64 with 6 generators> in characteristic 2 gap> FG2 := FpGModuleGF(G, 2); Full canonical module FG^2 over the group ring of <pc group of size 64 with 6 generators> in characteristic 2 gap> M := DirectSumOfModules(FG2, FG); Full canonical module FG^3 over the group ring of <pc group of size 64 with 6 generators> in characteristic 2 gap> DirectDecompositionOfModule(M); [ Module over the group ring of <pc group of size 64 with 6 generators> in characteristic 2 with 1 generator in FG^ 1. Generators are in minimal echelon form. , Module over the group ring of <pc group of size 64 with 6 generators> in characteristic 2 with 1 generator in FG^ 1. Generators are in minimal echelon form. , Module over the group ring of <pc group of size 64 with 6 generators> in characteristic 2 with 1 generator in FG^ 1. Generators are in minimal echelon form. ]

We can also compute the sum and intersection of &FG;-modules. In the example below we construct two submodules of &FG;, where &G; is the dihedral group of order four: M is the submodule generated by g_1 + g_2, and N is the submodule generated by g_1 + g_2 + g_3 + g_4. We calculate their sum and intersection. Since N is in this case a submodule of M it is easy to check that the correct results are obtained. gap> G := DihedralGroup(4);; gap> M := FpGModuleGF([[1,1,0,0]]*One(GF(2)), G); Module over the group ring of <pc group of size 4 with 2 generators> in characteristic 2 with 1 generator in FG^ 1. Generators are in minimal echelon form. gap> N := FpGModuleGF([[1,1,1,1]]*One(GF(2)), G); Module over the group ring of <pc group of size 4 with 2 generators> in characteristic 2 with 1 generator in FG^ 1. Generators are in minimal echelon form. gap> # gap> S := SumModules(M,N); Module over the group ring of <pc group of size 4 with 2 generators> in characteristic 2 with 2 generators in FG^1. gap> I := IntersectionModules(M,N); Module over the group ring of <pc group of size 4 with 2 generators> in characteristic 2 with 1 generator in FG^1. gap> # gap> S = M and I = N; true
Miscellaneous functions
<#Include Label="Equals_DTmanFpGModule_Misc"> <#Include Label="IsModuleElement_DTmanFpGModule_Misc"> <#Include Label="IsSubModule_DTmanFpGModule_Misc"> <#Include Label="RandomElement_DTmanFpGModule_Misc"> <#Include Label="RandomSubmodule_DTmanFpGModule_Misc">