<Section> <Heading>The Memory Problem</Heading> (to finish properly - just a sketch so far) <P/> The problem with the standard approach is that each stage of the resolution requries more and more memory to calculate. The <Package>HAP</Package> computes the resolution in the fast, naive way. The important stage here is computing the kernel of a module homorphism. In the standard <Package>HAP</Package> case, a homorphism is represented by a <M>m</M> by <M>n|G|</M> matrix of generators. This is expanded out to a full <M>m|G|</M> by <M>n|G|</M> F-homorphism matrix and the kernel found using &GAP;'s <K>NullspaceMat</K>, which needs about 2.5 times the storage of the input matrix. <P/> <Package>HAPprime</Package> makes more use of the module structure to compute the kernel without ever expanding the whole F-homorphism matrix, and so saving a lot of memory. <P/> One solution is to divide the homorphism <M>M</M> into two halves (which we shall call <M>U</M> and <M>V</M>). The kernel of the homorphism is then the sum of the kernels of <M>U</M> and <M>V</M>, together with the pre-image in <M>U</M> and <M>V</M> of the intersection of <M>U</M> and <M>V</M>. <P/> The kernels of <M>U</M> and <M>V</M> can be computed in exactly the same manner, so this part is divide-and-conquer. Unfortunately, the intersection of <M>U</M> and <M>V</M> cannot be computed in a divide and conquer manner. What is worse, the standard &GAP; method for computing the intersection of two spaces (<K>SumIntersectionMat</K>) would take 3.5 times the memory of the original <M>m|G|</M> by <M>n|G|</M> matrix, i.e. worse than the naive approach! </Section> <Section> <Heading>Solution 1: Using Compressed F-Echelon Representation</Heading> The standard method for calculating the intersection of two vector spaces starts with having a F-basis for both in echelon form. We have developed a method for expressing an echelon F-basis in a compressed form, which only takes about 1/30 the memory of expressing the full expanded F-basis. We can do this for each of <M>U</M> and <M>V</M> in turn, and then calculate the intersection while leaving one of them in compressed form: this only takes about twice as as much memory as the full expansion of either <M>U</M> or <M>V</M>. <P/> However, this is slow and it it is not obvious how the memory usage can be reduced further. </Section> <Section> <Heading>Solution 2: G-Echelon Generators</Heading> The problem with Solution 1 is computing the intersection. Solution 2 massages the homorphism generators to make the intersection as small as possible (and ideally to zero). <P/> The function <K>EchelonModuleGenerators</K> takes a set of module generators and returns a new set for the same module, but which are in something close to a semi-echelon form. Consider the following example: <Example><![CDATA[ gap> G := SmallGroup(16, 5); <pc group of size 16 with 4 generators> gap> v := RandomMat(6, 64, GF(2)); <a 6x64 matrix over GF2> gap> DisplayModule(v,G); [...11.1.11...11.|1.1.111.11.1.11.|.......111..1111|11.1..11.1.11..1] [..1..1..1111.111|11..1..1....1..1|.1..11.11..1.11.|.....111....11..] [.111..1.1111.11.|...11111.1...1.1|.1.1111.....11.1|..1.11111.11....] [.1..11....11...1|111.1..1..1..111|.1.11....1.11..1|1.111.1.1111.111] [.1.1...1...1.1..|...1111..11.....|.1..1..1.....1.1|1111.11....11.11] [11....1.1..1.1.1|11.111111.11.1.1|1.111..1.......1|.1.1....1.1..11.] gap> e := EchelonModuleGenerators(v, G);; gap> DisplayModule(e.generators,G); [.1.1...1...1.1..|................|................|................] [................|...............1|................|................] [................|11....1......1..|................|................] [................|................|..111..1..1.11..|................] [................|................|................|11.1.1..1.111.1.] ]]></Example> Both sets of vectors generate exactly the same vector space, but the generators after <K>EchelonModuleGenerators</K> are in a much nicer form. The modules generates by rows 1, 4 and 5 are independent, as is the module generated by the combination of rows 2 and 3. The nullspace of the overall matrix is simply the sum of the nullspaces of those four subspaces: there is no intersection to be calculated. <P/> This split nullspace calculation can be performed using the function <K>GensNullspaceGModuleNew</K>: <Example><![CDATA[ gap> GensNullspaceGModuleNew(rec(gens:=e.generators, group:=G)); [ <a GF2 vector of length 80> ] ]]></Example> <P/> In the case of the kernels required for a resolution, the decomposition is rarely as nice, but it is still hoped that the intersection will be small (this still needs checking, however!). </Section>