GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
############################################################################# ## ## GaussSparseGF2.gi Gauss package Simon Goertzen ## ## Copyright 2007-2008 Lehrstuhl B f��r Mathematik, RWTH Aachen ## ## Implementation stuff for performing Gauss alg. on sparse GF(2) matrices. ## ############################################################################# ######################################################## ## Gaussian Algorithms: ######################################################## ## InstallMethod( EchelonMatDestructive, "generic method for matrices", [ IsSparseMatrixGF2Rep ], function( mat ) local nrows, # number of rows in <mat> ncols, # number of columns in <mat> indices, vectors, # list of basis vectors heads, # list of pivot positions in 'vectors' i, # loop over rows j, # loop over columns head, x, rank, list, row_indices, p, a; nrows := mat!.nrows; ncols := mat!.ncols; indices := mat!.indices; heads := ListWithIdenticalEntries( ncols, 0 ); vectors := rec( indices := [] ); for i in [ 1 .. nrows ] do row_indices := indices[i]; # Reduce the row with the known basis vectors. for j in [ 1 .. ncols ] do head := heads[j]; if head <> 0 then p := PositionSet( row_indices, j ); if p <> fail then row_indices := AddRow( vectors.indices[ head ], row_indices ); fi; fi; od; if Length( row_indices ) > 0 then j := row_indices[1]; # We found a new basis vector. Add( vectors.indices, row_indices ); heads[j]:= Length( vectors.indices ); fi; od; # gauss upwards: list := Filtered( heads, x -> x <> 0 ); rank := Length( list ); for j in [ ncols, ncols - 1 .. 1 ] do head := heads[j]; if head <> 0 then a := Difference( [1..head-1], heads{[j+1..ncols]} ); for i in a do p := PositionSet( vectors.indices[i], j ); if p <> fail then vectors.indices[i] := AddRow( vectors.indices[ head ], vectors.indices[i] ); fi; od; fi; od; #order rows: vectors.indices := vectors.indices{list}; list := Filtered( [ 1 .. ncols ], j -> heads[j] <> 0 ); heads{list} := [ 1 .. rank ]; #just for compatibility, vectors are ordered already return rec( heads := heads, vectors := SparseMatrix( rank, ncols, vectors.indices ) ); end ); ## InstallMethod( EchelonMatTransformationDestructive, "method for sparse matrices", [ IsSparseMatrixGF2Rep ], function( mat ) local nrows, # number of rows in <mat> ncols, # number of columns in <mat> indices, vectors, # list of basis vectors heads, # list of pivot positions in 'vectors' coeffs, relations, ring, i, # loop over rows j, # loop over columns T, head, x, rank, list, row_indices, p, a; nrows := mat!.nrows; ncols := mat!.ncols; indices := mat!.indices; heads := List( [ 1 .. ncols ], i -> 0 ); vectors := rec( indices := [] ); coeffs := rec( indices := [] ); relations := rec( indices := [] ); T := rec( indices := List( [ 1 .. nrows ], i -> [i] ) ); for i in [ 1 .. nrows ] do row_indices := indices[i]; # Reduce the row with the known basis vectors. for j in [ 1 .. ncols ] do head := heads[j]; if head <> 0 then p := PositionSet( row_indices, j ); if p <> fail then T.indices[i] := AddRow( coeffs.indices[ head ], T.indices[i] ); row_indices := AddRow( vectors.indices[ head ], row_indices ); fi; fi; od; if Length( row_indices ) > 0 then j := row_indices[1]; # We found a new basis vector. Add( coeffs.indices, T.indices[ i ] ); Add( vectors.indices, row_indices ); heads[j]:= Length( vectors.indices ); else Add( relations.indices, T.indices[ i ] ); fi; od; # gauss upwards: list := Filtered( heads, x -> x <> 0 ); rank := Length( list ); for j in [ ncols, ncols - 1 .. 1 ] do head := heads[j]; if head <> 0 then a := Difference( [1..head-1], heads{[j+1..ncols]} ); for i in a do p := PositionSet( vectors.indices[i], j ); if p <> fail then coeffs.indices[i] := AddRow( coeffs.indices[ head ], coeffs.indices[i] ); vectors.indices[i] := AddRow( vectors.indices[ head ], vectors.indices[i] ); fi; od; fi; od; #order rows: vectors.indices := vectors.indices{list}; coeffs.indices := coeffs.indices{list}; list := Filtered( [1..ncols], j -> heads[j] <> 0 ); heads{list} := [ 1 .. rank ]; #just for compatibility, vectors are ordered already return rec( heads := heads, vectors := SparseMatrix( rank, ncols, vectors.indices ), coeffs := SparseMatrix( rank, nrows, coeffs.indices ), relations := SparseMatrix( nrows - rank, nrows, relations.indices ) ); end ); ## InstallMethod( ReduceMatWithEchelonMat, "for sparse matrices over a ring, second argument must be in REF", [ IsSparseMatrixGF2Rep, IsSparseMatrixGF2Rep ], function( mat, N ) local nrows1, ncols, nrows2, M, i, j, k, x, p, row1_indices, row2_indices; nrows1 := mat!.nrows; nrows2 := N!.nrows; if nrows1 = 0 or nrows2 = 0 then return rec( reduced_matrix := mat ); fi; ncols := mat!.ncols; if ncols <> N!.ncols then return fail; elif ncols = 0 then return rec( reduced_matrix := mat ); fi; M := CopyMat( mat ); for i in [ 1 .. nrows2 ] do row2_indices := N!.indices[i]; if Length( row2_indices ) > 0 then j := row2_indices[1]; for k in [ 1 .. nrows1 ] do p := PositionSet( M!.indices[k], j ); if p <> fail then M!.indices[k] := AddRow( row2_indices, M!.indices[k] ); fi; od; fi; od; return rec( reduced_matrix := M ); end); ## InstallMethod( ReduceMatWithEchelonMatTransformation, "for sparse matrices over a ring, second argument must be in REF", [ IsSparseMatrixGF2Rep, IsSparseMatrixGF2Rep ], function( mat, N ) local nrows1, ncols, nrows2, M, T, i, j, k, x, p, row1_indices, row2_indices; nrows1 := mat!.nrows; nrows2 := N!.nrows; T := SparseZeroMatrix( nrows1, nrows2, GF(2) ); if nrows1 = 0 or nrows2 = 0 then return rec( reduced_matrix := mat, transformation := T ); fi; ncols := mat!.ncols; if ncols <> N!.ncols then return fail; elif ncols = 0 then return rec( reduced_matrix := mat, transformation := T ); fi; M := CopyMat( mat ); for i in [ 1 .. nrows2 ] do row2_indices := N!.indices[i]; if Length( row2_indices ) > 0 then j := row2_indices[1]; for k in [ 1 .. nrows1 ] do p := PositionSet( M!.indices[k], j ); if p <> fail then M!.indices[k] := AddRow( row2_indices, M!.indices[k] ); Add( T!.indices[k], i ); fi; od; fi; od; return rec( reduced_matrix := M, transformation := T ); end ); ## InstallMethod( KernelEchelonMatDestructive, "method for sparse matrices", [ IsSparseMatrixGF2Rep, IsList ], function( mat, L ) local nrows, ncols, indices, vectors, heads, coeffs, relations, i, j, T, head, x, rank, list, row_indices, p; nrows := mat!.nrows; ncols := mat!.ncols; indices := mat!.indices; heads := List( [ 1 .. ncols ], i -> 0 ); vectors := rec( indices := [] ); coeffs := rec( indices := [] ); relations := rec( indices := [] ); T := rec( indices := List( [ 1 .. nrows ] , i -> [] ) ); for i in [ 1 .. Length( L ) ] do T.indices[L[i]] := [i]; od; for i in [ 1 .. nrows ] do # Reduce the row with the known basis vectors. for j in [ 1 .. ncols ] do head := heads[j]; if head <> 0 then p := PositionSet( indices[i], j ); if p <> fail then indices[i] := AddRow( vectors.indices[ head ], indices[i] ); T.indices[i] := AddRow( coeffs.indices[ head ], T.indices[i] ); fi; fi; od; if Length( indices[i] ) > 0 then j := indices[i][1]; # We found a new basis vector. Add( vectors.indices, indices[i] ); heads[j]:= Length( vectors.indices ); Add( coeffs.indices, T.indices[ i ] ); else Add( relations.indices, T.indices[ i ] ); fi; od; return rec( relations := SparseMatrix( Length( relations.indices ), Length( L ), relations.indices ) ); end ); ## InstallMethod( RankDestructive, "method for sparse matrices", [ IsSparseMatrixGF2Rep, IsInt ], function( mat, upper_boundary ) local nrows, ncols, indices, vectors, heads, coeffs, relations, ring, i, j, T, head, x, rank, list, row_indices, p; nrows := mat!.nrows; ncols := mat!.ncols; indices := mat!.indices; heads := List( [ 1 .. ncols ], i -> 0 ); vectors := rec( indices := [] ); for i in [ 1 .. nrows ] do # Reduce the row with the known basis vectors. for j in [ 1 .. ncols ] do head := heads[j]; if head <> 0 then p := PositionSet( indices[i], j ); if p <> fail then indices[i] := AddRow( vectors.indices[ head ], indices[i] ); fi; fi; od; if Length( indices[i] ) > 0 then j := indices[i][1]; # We found a new basis vector. Add( vectors.indices, indices[i] ); heads[j]:= Length( vectors.indices ); if heads[j] = upper_boundary then return upper_boundary; fi; fi; od; return Length( vectors.indices ); end ); InstallGlobalFunction( "RankOfIndicesListList", function( m ) # m must be a list of lists containing sets of positive integers # this is interpreted as the sparse representation of a GF(2) # matrix with as many rows as Length(m). The integers are the # positions of ones in each row. This function basically does # a semi-echelon-form local cleanandextend,pos,seb,v; # Initialise with some reasonable length: seb := rec( vectors := EmptyPlist(QuoInt(Length(m),256)+16), pivots := EmptyPlist(QuoInt(Length(m),256)+16) ); cleanandextend := function(v) # cleans one vector v with seb, returns the relation # as a list of indices or false a new basis vector # has been added to extend seb. local i,rel; rel := EmptyPlist(Length(seb.vectors)); for i in [1..Length(seb.vectors)] do pos := PositionSorted(v,seb.pivots[i]); if pos <= Length(v) and v[pos] = seb.pivots[i] then v := SYMMETRIC_DIFFERENCE_OF_ORDERED_SETS_OF_SMALL_INTEGERS(v,seb.vectors[i]); Add(rel,i); fi; od; if Length(v) > 0 then Add(seb.vectors,v); Add(seb.pivots,v[1]); return false; else return rel; fi; end; for v in m do cleanandextend(v); od; return seb; end );