From 4f555e24d9e95f45db258041c899070e1751b999 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 12 Apr 2026 22:56:40 +0200 Subject: [PATCH 01/32] Add IsFlatPlistMatrixRep matrix object type Store dense matrices as plain row lists for faster entry access. Some microbenchmarks show the benefit, namely that performance for basic arithmetic in many cases matches that of ist-of-list matrices, while IsPlistMatrixRep is basically always slower, sometimes quite substantially so. Setup: R:=GF(257);;n:=20;; M1:=IdentityMat(n,R);; M2:=IdentityMatrix(IsPlistMatrixRep,R,n);; M3:=IdentityMatrix(IsFlatPlistMatrixRep,R,n);; Addition: gap> for i in [1..1000] do x:=M1+M1; od; time; 4 gap> for i in [1..1000] do x:=M2+M2; od; time; 12 gap> for i in [1..1000] do x:=M3+M3; od; time; 4 Multiplication: gap> for i in [1..1000] do x:=M1*M1; od; time; 66 gap> for i in [1..1000] do x:=M2*M2; od; time; 1448 gap> for i in [1..1000] do x:=M3*M3; od; time; 60 Inversion: gap> for i in [1..1000] do x:=Inverse(M1); od; time; 7 gap> for i in [1..1000] do x:=Inverse(M2); od; time; 83 gap> for i in [1..1000] do x:=Inverse(M3); od; time; 4 Powers: gap> gap> for i in [1..1000] do x:=M1^5; od; time; 187 gap> for i in [1..1000] do x:=M2^5; od; time; 4350 gap> for i in [1..1000] do x:=M3^5; od; time; 182 Element access (plist-of-plist benefits from a direct kernel implementation; we could add that for both IsPlistMatrixRep and IsFlatPlistMatrixRep, if so desired) gap> for i in [1..1000000] do x:=M1[1,1]; od; time; 19 gap> for i in [1..1000000] do x:=M2[1,1]; od; time; 78 gap> for i in [1..1000000] do x:=M3[1,1]; od; time; 66 Example of a typical matrix property: IsDiagonalMat gap> for i in [1..10000] do x:=IsDiagonalMat(M1); od; time; 132 gap> for i in [1..10000] do x:=IsDiagonalMat(M2); od; time; 191 gap> for i in [1..10000] do x:=IsDiagonalMat(M3); od; time; 146 IsOne (surprisingly is slower for IsFlatPlistMatrixRep, I do not yet know why) gap> for i in [1..10000] do x:=IsOne(M1); od; time; 144 gap> for i in [1..10000] do x:=IsOne(M2); od; time; 145 gap> for i in [1..10000] do x:=IsOne(M3); od; time; 172 PositionNonZeroInRow is among the core functionality for row reduction: gap> for i in [1..1000000] do x:=PositionNonZeroInRow(M1,1); od; time; 334 gap> for i in [1..1000000] do x:=PositionNonZeroInRow(M2,1); od; time; 486 gap> for i in [1..1000000] do x:=PositionNonZeroInRow(M3,1); od; time; 376 Co-authored-by: Codex --- benchmark/matobj/bench-matelm-access.g | 18 +- benchmark/matobj/bench-matobj-creation.g | 13 +- doc/ref/matobj.xml | 1 + lib/matobjflatplist.gd | 46 ++ lib/matobjflatplist.gi | 509 ++++++++++++++++++ lib/read3.g | 1 + lib/read5.g | 1 + tst/testinstall/MatrixObj/CompanionMatrix.tst | 16 + .../MatrixObj/ElementaryMatrices.tst | 8 + tst/testinstall/MatrixObj/IdentityMatrix.tst | 18 + tst/testinstall/MatrixObj/Matrix.tst | 11 + tst/testinstall/MatrixObj/ZeroMatrix.tst | 18 + tst/testinstall/MatrixObj/matobjflatplist.tst | 191 +++++++ tst/testinstall/MatrixObj/testmatobj.g | 2 + 14 files changed, 843 insertions(+), 10 deletions(-) create mode 100644 lib/matobjflatplist.gd create mode 100644 lib/matobjflatplist.gi create mode 100644 tst/testinstall/MatrixObj/matobjflatplist.tst diff --git a/benchmark/matobj/bench-matelm-access.g b/benchmark/matobj/bench-matelm-access.g index 889ba71801..3ef1f6a9cb 100644 --- a/benchmark/matobj/bench-matelm-access.g +++ b/benchmark/matobj/bench-matelm-access.g @@ -136,17 +136,23 @@ RunMatTest := function(desc, m) TestWritingMatrix(m); end; -m:=IdentityMat(10);; -RunMatTest("integer matrix", m); +m:=IdentityMat(10,GF(257));; # plain list of plain lists +RunMatTest("GF(257) plist-of-plist matrix", m); -m:=IdentityMat(10,GF(2));; +m:=IdentityMatrix(GF(257), 10); # IsPlistMatrixRep +RunMatTest("GF(257) IsPlistMatrixRep", m); + +m:=IdentityMatrix(GF(257), 10); # IsFlatPlistMatrixRep +RunMatTest("GF(257) IsFlatPlistMatrixRep", m); + +m:=IdentityMat(10,GF(2));; # plain list of IsGF2VectorRep RunMatTest("GF(2) rowlist", m); -m:=IdentityMat(10,GF(2));; ConvertToMatrixRep(m);; +m:=IdentityMatrix(GF(2), 10); # IsGF2MatrixRep RunMatTest("GF(2) compressed matrix", m); -m:=IdentityMat(10,GF(7));; +m:=IdentityMat(10,GF(7));; # plain list of Is8BitVectorRep RunMatTest("GF(7) rowlist", m); -m:=IdentityMat(10,GF(7));; ConvertToMatrixRep(m);; +m:=IdentityMatrix(GF(7), 10); # Is8BitMatrixRep RunMatTest("GF(7) compressed matrix", m); diff --git a/benchmark/matobj/bench-matobj-creation.g b/benchmark/matobj/bench-matobj-creation.g index d74e5f040e..8ce1af3b6a 100644 --- a/benchmark/matobj/bench-matobj-creation.g +++ b/benchmark/matobj/bench-matobj-creation.g @@ -91,18 +91,23 @@ RunMatTest := function(desc, ring) TestCreatingMatrix(ring); end; -RunMatTest("GF(2)", GF(2)); -RunMatTest("Rationals", Rationals); - RunMatObjTest := function(desc, filter, ring) Print("\n"); PrintBoxed(Concatenation("Testing ", desc)); TestCreatingMatrixObj(filter, ring); end; -RunMatObjTest("GF(2) IsPlistMatrixRep", IsPlistMatrixRep, GF(2)); +RunMatTest("GF(257)", GF(257)); +RunMatObjTest("GF(257) IsPlistMatrixRep", IsPlistMatrixRep, GF(257)); +RunMatObjTest("GF(257) IsFlatPlistMatrixRep", IsFlatPlistMatrixRep, GF(257)); + +RunMatTest("Integers", Integers); RunMatObjTest("integer IsPlistMatrixRep", IsPlistMatrixRep, Integers); +RunMatObjTest("integer IsFlatPlistMatrixRep", IsFlatPlistMatrixRep, Integers); + +RunMatTest("Rationals", Rationals); RunMatObjTest("rational IsPlistMatrixRep", IsPlistMatrixRep, Rationals); +RunMatObjTest("rational IsFlatPlistMatrixRep", IsFlatPlistMatrixRep, Rationals); # TODO: other reps # TODO: other compare with creating plist-of-plist diff --git a/doc/ref/matobj.xml b/doc/ref/matobj.xml index ddbaa2871d..c1faa94a46 100644 --- a/doc/ref/matobj.xml +++ b/doc/ref/matobj.xml @@ -374,6 +374,7 @@ described in this chapter is supported. <#Include Label="IsGF2MatrixRep"> <#Include Label="Is8BitMatrixRep"> <#Include Label="IsPlistMatrixRep"> +<#Include Label="IsFlatPlistMatrixRep"> <#Include Label="IsZmodnZMatrixRep"> diff --git a/lib/matobjflatplist.gd b/lib/matobjflatplist.gd new file mode 100644 index 0000000000..b4b801dab3 --- /dev/null +++ b/lib/matobjflatplist.gd @@ -0,0 +1,46 @@ +############################################################################# +## +## This file is part of GAP, a system for computational discrete algebra. +## +## SPDX-License-Identifier: GPL-2.0-or-later +## +## Copyright of GAP belongs to its developers, whose names are too numerous +## to list here. Please refer to the COPYRIGHT file for details. +## + +############################################################################ +# +# Dense matrix objects backed by plain lists of plain row lists. +# + +############################################################################# +## +## <#GAPDoc Label="IsFlatPlistMatrixRep"> +## +## +## +## +## An object obj in describes +## a matrix object (see ) whose entries are stored +## as a dense plain list of dense plain row lists. +##

+## This representation is optimized for efficient entry access via +## M[i,j]. Unlike , it is not a row +## list matrix, so direct row access via M[i] is not supported. +## +## +## <#/GAPDoc> +## +DeclareRepresentation( "IsFlatPlistMatrixRep", + IsMatrixObj and IsPositionalObjectRep + and IsCopyable + and IsNoImmediateMethodsObject + and HasNumberRows and HasNumberColumns + and HasBaseDomain and HasOneOfBaseDomain and HasZeroOfBaseDomain, + [] ); + + +# Internal positions for flat plist matrices. +BindConstant( "FBDPOS", 1 ); +BindConstant( "FCOLSPOS", 2 ); +BindConstant( "FROWSPOS", 3 ); diff --git a/lib/matobjflatplist.gi b/lib/matobjflatplist.gi new file mode 100644 index 0000000000..8343c42552 --- /dev/null +++ b/lib/matobjflatplist.gi @@ -0,0 +1,509 @@ +############################################################################# +## +## This file is part of GAP, a system for computational discrete algebra. +## +## SPDX-License-Identifier: GPL-2.0-or-later +## +## Copyright of GAP belongs to its developers, whose names are too numerous +## to list here. Please refer to the COPYRIGHT file for details. +## + +############################################################################ +# +# Dense matrix objects backed by plain lists of plain row lists. +# + +BindGlobal( "CopyFlatPlistMatrixRow", + function( row ) + local copy, i, len; + len := Length( row ); + copy := []; + if 0 < len then + copy[len] := row[len]; + for i in [ 1 .. len - 1 ] do + copy[i] := row[i]; + od; + fi; + return copy; + end ); + +BindGlobal( "InverseRowsForFlatPlistMatrix", + function( M ) + local bd, rows; + + if NrRows( M ) <> NrCols( M ) then + return fail; + elif NrRows( M ) = 0 then + rows := []; + return rows; + fi; + + bd := BaseDomain( M ); + if IsFinite( bd ) and IsField( bd ) then + return INV_MAT_DEFAULT_MUTABLE( M![FROWSPOS] ); + fi; + return INV_MATRIX_MUTABLE( M![FROWSPOS] ); + end ); + +BindGlobal( "MakeIsFlatPlistMatrixRep", + function( basedomain, ncols, list, check ) + local fam, types, typ, row; + + fam := CollectionsFamily( FamilyObj( basedomain ) ); + if not IsBound( fam!.FlatPlistMatrixRepTypes ) then + fam!.FlatPlistMatrixRepTypes := [ + NewType( fam, IsFlatPlistMatrixRep ), + NewType( fam, IsFlatPlistMatrixRep and IsMutable ), + ]; + fam!.FlatPlistMatrixRepTypesEasyCompare := [ + NewType( fam, IsFlatPlistMatrixRep and CanEasilyCompareElements ), + NewType( fam, IsFlatPlistMatrixRep and CanEasilyCompareElements and IsMutable ), + ]; + fi; + if HasCanEasilyCompareElements( Representative( basedomain ) ) and + CanEasilyCompareElements( Representative( basedomain ) ) then + types := fam!.FlatPlistMatrixRepTypesEasyCompare; + else + types := fam!.FlatPlistMatrixRepTypes; + fi; + if IsMutable( list ) then + typ := types[2]; + else + typ := types[1]; + fi; + + if check and ValueOption( "check" ) <> false then + for row in list do + if not IsPlistRep( row ) then + Error( "the entries of must be plain lists" ); + elif Length( row ) <> ncols then + Error( "the entries of must have length " ); + elif not IsSubset( basedomain, row ) then + Error( "the elements in must lie in " ); + fi; + od; + fi; + + return Objectify( typ, [ basedomain, ncols, list ] ); + end ); + + +InstallTagBasedMethod( NewMatrix, + IsFlatPlistMatrixRep, + function( filter, basedomain, ncols, list ) + local nd, rows, i, row; + + if Length( list ) > 0 and not IsVectorObj( list[1] ) then + nd := NestingDepthA( list ); + if nd < 2 or nd mod 2 = 1 then + if Length( list ) mod ncols <> 0 then + Error( "NewMatrix: Length of is not a multiple of " ); + fi; + list := List( [ 0, ncols .. Length( list ) - ncols ], + i -> list{ [ i + 1 .. i + ncols ] } ); + fi; + fi; + + rows := 0 * [ 1 .. Length( list ) ]; + for i in [ 1 .. Length( list ) ] do + row := list[i]; + if IsVectorObj( row ) then + row := Unpack( row ); + fi; + rows[i] := CopyFlatPlistMatrixRow( row ); + od; + if not IsMutable( list ) then + MakeImmutable( rows ); + fi; + return MakeIsFlatPlistMatrixRep( basedomain, ncols, rows, true ); + end ); + + +InstallTagBasedMethod( NewZeroMatrix, + IsFlatPlistMatrixRep, + function( filter, basedomain, rows, cols ) + local list, row, i, z; + list := 0 * [ 1 .. rows ]; + z := Zero( basedomain ); + for i in [ 1 .. rows ] do + row := ListWithIdenticalEntries( cols, z ); + list[i] := row; + od; + return MakeIsFlatPlistMatrixRep( basedomain, cols, list, false ); + end ); + + +InstallMethod( ConstructingFilter, + [ "IsFlatPlistMatrixRep" ], + M -> IsFlatPlistMatrixRep ); + +InstallMethod( CompatibleVectorFilter, + [ "IsFlatPlistMatrixRep" ], + M -> IsPlistVectorRep ); + + +InstallMethod( BaseDomain, + [ "IsFlatPlistMatrixRep" ], + M -> M![FBDPOS] ); + +InstallMethod( NumberRows, + [ "IsFlatPlistMatrixRep" ], + M -> Length( M![FROWSPOS] ) ); + +InstallMethod( NumberColumns, + [ "IsFlatPlistMatrixRep" ], + M -> M![FCOLSPOS] ); + +InstallMethod( \[\], + [ "IsFlatPlistMatrixRep", "IsPosInt" ], + function( M, pos ) + ErrorNoReturn( "row access unsupported; use M[i,j] or RowsOfMatrix(M)" ); + end ); + +InstallMethod( MatElm, + [ "IsFlatPlistMatrixRep", "IsPosInt", "IsPosInt" ], + { M, row, col } -> M![FROWSPOS][row,col] ); + +InstallMethod( SetMatElm, + [ "IsFlatPlistMatrixRep and IsMutable", "IsPosInt", "IsPosInt", "IsObject" ], + function( M, row, col, ob ) + if ValueOption( "check" ) <> false then + if not ob in BaseDomain( M ) then + Error( " must lie in the base domain of " ); + elif not row in [1..NrRows(M)] then + Error( " is out of bounds" ); + elif not col in [1..NrCols(M)] then + Error( " is out of bounds" ); + fi; + fi; + M![FROWSPOS][row,col] := ob; + end ); + + +InstallMethod( Unpack, + [ "IsFlatPlistMatrixRep" ], + M -> List( M![FROWSPOS], CopyFlatPlistMatrixRow ) ); + +InstallMethod( ShallowCopy, + [ "IsFlatPlistMatrixRep" ], + M -> MakeIsFlatPlistMatrixRep( BaseDomain(M), NrCols(M), + List( M![FROWSPOS], CopyFlatPlistMatrixRow ), false ) ); + +InstallMethod( MutableCopyMatrix, + [ "IsFlatPlistMatrixRep" ], + M -> MakeIsFlatPlistMatrixRep( BaseDomain(M), NrCols(M), + List( M![FROWSPOS], CopyFlatPlistMatrixRow ), false ) ); + +InstallMethod( ExtractSubMatrix, + [ "IsFlatPlistMatrixRep", "IsList", "IsList" ], + function( M, rowspos, colspos ) + local list, i; + list := M![FROWSPOS]{ rowspos }{ colspos }; + return MakeIsFlatPlistMatrixRep( BaseDomain(M), Length( colspos ), list, false ); + end ); + +InstallMethod( CopySubMatrix, + [ "IsFlatPlistMatrixRep", "IsFlatPlistMatrixRep and IsMutable", + "IsList", "IsList", "IsList", "IsList" ], + function( M, N, srcrows, dstrows, srccols, dstcols ) + local i; + if ValueOption( "check" ) <> false and + not IsIdenticalObj( BaseDomain(M), BaseDomain(N) ) then + Error( " and are not compatible" ); + fi; + N![FROWSPOS]{dstrows}{dstcols} := M![FROWSPOS]{srcrows}{srccols}; + end ); + +InstallMethod( TransposedMatMutable, + [ "IsFlatPlistMatrixRep" ], + function( M ) + local list; + list := TransposedMatMutable(M![FROWSPOS]); + return MakeIsFlatPlistMatrixRep( BaseDomain(M), NrRows(M), list, false ); + end ); + +InstallMethod( \+, + [ "IsFlatPlistMatrixRep", "IsFlatPlistMatrixRep" ], + function( a, b ) + if ValueOption( "check" ) <> false and + ( not IsIdenticalObj( BaseDomain( a ), BaseDomain( b ) ) or + NrRows( a ) <> NrRows( b ) or + NrCols( a ) <> NrCols( b ) ) then + Error( " and are not compatible" ); + fi; + return MakeIsFlatPlistMatrixRep( BaseDomain( a ), NrCols( a ), + a![FROWSPOS] + b![FROWSPOS], false ); + end ); + +InstallMethod( \-, + [ "IsFlatPlistMatrixRep", "IsFlatPlistMatrixRep" ], + function( a, b ) + if ValueOption( "check" ) <> false and + ( not IsIdenticalObj( BaseDomain( a ), BaseDomain( b ) ) or + NrRows( a ) <> NrRows( b ) or + NrCols( a ) <> NrCols( b ) ) then + Error( " and are not compatible" ); + fi; + return MakeIsFlatPlistMatrixRep( BaseDomain( a ), NrCols( a ), + a![FROWSPOS] - b![FROWSPOS], false ); + end ); + +InstallMethod( AdditiveInverseMutable, + [ "IsFlatPlistMatrixRep" ], + M -> MakeIsFlatPlistMatrixRep( BaseDomain( M ), NrCols( M ), + AdditiveInverseMutable( M![FROWSPOS] ), false ) ); + +InstallMethod( ZeroMutable, + [ "IsFlatPlistMatrixRep" ], + function( M ) + local z; + z := MakeIsFlatPlistMatrixRep( BaseDomain( M ), NrCols( M ), + ZeroMutable( M![FROWSPOS] ), false ); + SetIsZero( z, true ); + return z; + end ); + +InstallMethod( InverseMutable, + [ "IsFlatPlistMatrixRep" ], + function( M ) + local rows; + + rows := InverseRowsForFlatPlistMatrix( M ); + if rows = fail then + return fail; + fi; + return MakeIsFlatPlistMatrixRep( BaseDomain( M ), NrCols( M ), rows, false ); + end ); + +InstallMethod( \*, + [ "IsFlatPlistMatrixRep", "IsFlatPlistMatrixRep" ], + function( a, b ) + local rowsA, colsA, rowsB, colsB, bd, list, i; + + rowsA := NumberRows( a ); + colsA := NumberColumns( a ); + rowsB := NumberRows( b ); + colsB := NumberColumns( b ); + bd := BaseDomain( a ); + + if ValueOption( "check" ) <> false then + if colsA <> rowsB then + ErrorNoReturn( "\\*: Matrices do not fit together" ); + elif not IsIdenticalObj( bd, BaseDomain( b ) ) then + ErrorNoReturn( "\\*: Matrices not over same base domain" ); + fi; + fi; + + if rowsA = 0 or colsB = 0 then + list := []; + elif colsA = 0 then # colsA = rowsB + list := 0 * [ 1 .. rowsA ]; + for i in [ 1 .. rowsA ] do + list[i] := ListWithIdenticalEntries( colsB, Zero( bd ) ); + od; + else + list := a![FROWSPOS] * b![FROWSPOS]; + fi; + if not IsMutable( a ) and not IsMutable( b ) then + MakeImmutable( list ); + fi; + return MakeIsFlatPlistMatrixRep( bd, colsB, list, false ); + end ); + +InstallMethod( \*, + [ "IsFlatPlistMatrixRep", "IsVectorObj" ], + function( M, v ) + local rows, cols, bd, res, i, j; + + rows := NumberRows( M ); + cols := NumberColumns( M ); + bd := BaseDomain( M ); + + if ValueOption( "check" ) <> false then + if cols <> Length( v ) then + Error( " and are not compatible" ); + elif not IsIdenticalObj( bd, BaseDomain( v ) ) then + Error( " and are not compatible" ); + fi; + fi; + + # special case for empty matrices + if rows = 0 or cols = 0 then + return ZeroVector( rows, v ); + fi; + + # "unpack" cheaply and then delegate to kernel implementation + if IsPlistVectorRep(v) then + res := v![ELSPOS]; + elif IsList(v) then + res := v; + else + res := Unpack(v); + fi; + res := M![FROWSPOS] * res; + return Vector( res, v ); + end ); + +InstallMethod( \*, + [ "IsVectorObj", "IsFlatPlistMatrixRep" ], + function( v, M ) + local rows, cols, bd, res, i, j; + + rows := NumberRows( M ); + cols := NumberColumns( M ); + bd := BaseDomain( M ); + + if ValueOption( "check" ) <> false then + if Length( v ) <> rows then + Error( " and are not compatible" ); + elif not IsIdenticalObj( BaseDomain( v ), bd ) then + Error( " and are not compatible" ); + fi; + fi; + + # special case for empty matrices + if rows = 0 or cols = 0 then + return ZeroVector( cols, v ); + fi; + + # "unpack" cheaply and then delegate to kernel implementation + if IsPlistVectorRep(v) then + res := v![ELSPOS]; + elif IsList(v) then + res := v; + else + res := Unpack(v); + fi; + res := res * M![FROWSPOS]; + return Vector( res, v ); + end ); + +InstallMethod( ChangedBaseDomain, + [ "IsFlatPlistMatrixRep", "IsRing" ], + function( M, r ) + local n; + n := NewMatrix( IsFlatPlistMatrixRep, r, NrCols(M), M![FROWSPOS] ); + if not IsMutable( M ) then + MakeImmutable( n ); + fi; + return n; + end ); + + +InstallMethod( MultMatrixRowLeft, + [ "IsFlatPlistMatrixRep and IsMutable", "IsInt", "IsObject" ], + function( mat, row, scalar ) + MultMatrixRowLeft(mat![FROWSPOS], row, scalar); + end ); + +InstallMethod( MultMatrixRowRight, + [ "IsFlatPlistMatrixRep and IsMutable", "IsInt", "IsObject" ], + function( mat, row, scalar ) + MultMatrixRowRight(mat![FROWSPOS], row, scalar); + end ); + +InstallMethod( AddMatrixRowsLeft, + [ "IsFlatPlistMatrixRep and IsMutable", "IsInt", "IsInt", "IsObject" ], + function( mat, row1, row2, scalar ) + AddMatrixRowsLeft( mat![FROWSPOS], row1, row2, scalar ); + end ); + +InstallMethod( AddMatrixRowsRight, + [ "IsFlatPlistMatrixRep and IsMutable", "IsInt", "IsInt", "IsObject" ], + function( mat, row1, row2, scalar ) + AddMatrixRowsRight( mat![FROWSPOS], row1, row2, scalar ); + end ); + +InstallMethod( PositionNonZeroInRow, + [ "IsFlatPlistMatrixRep", "IsPosInt" ], + function( mat, row ) + return PositionNonZero( mat![FROWSPOS][row] ); + end ); + +InstallMethod( PositionNonZeroInRow, + [ "IsFlatPlistMatrixRep", "IsPosInt", "IsInt" ], + function( mat, row, from ) + return PositionNonZero( mat![FROWSPOS][row], from ); + end ); + +InstallMethod( SwapMatrixRows, + [ "IsFlatPlistMatrixRep and IsMutable", "IsInt", "IsInt" ], + function( mat, row1, row2 ) + SwapMatrixRows(mat![FROWSPOS], row1, row2); + end ); + +InstallMethod( SwapMatrixColumns, + [ "IsFlatPlistMatrixRep and IsMutable", "IsInt", "IsInt" ], + function( mat, col1, col2 ) + SwapMatrixColumns(mat![FROWSPOS], col1, col2); + end ); + + +InstallMethod( PostMakeImmutable, + [ "IsFlatPlistMatrixRep" ], + function( M ) + local row; + MakeImmutable( M![FROWSPOS] ); + end ); + + +InstallMethod( ViewObj, [ "IsFlatPlistMatrixRep" ], + function( M ) + Print( "<" ); + if not IsMutable( M ) then + Print( "immutable " ); + fi; + Print( NrRows(M), "x", NrCols(M), + "-matrix over ", BaseDomain(M), ">" ); + end ); + +InstallMethod( PrintObj, [ "IsFlatPlistMatrixRep" ], + function( M ) + Print( "NewMatrix(IsFlatPlistMatrixRep" ); + if IsFinite( BaseDomain(M) ) and IsField( BaseDomain(M) ) then + Print( ",GF(", Size( BaseDomain(M) ), ")," ); + else + Print( ",", String( BaseDomain(M) ), "," ); + fi; + Print( NumberColumns( M ), ",", Unpack( M ), ")" ); + end ); + +InstallMethod( Display, [ "IsFlatPlistMatrixRep" ], + function( M ) + local i; + Print( "<" ); + if not IsMutable( M ) then + Print( "immutable " ); + fi; + Print( NrRows(M), "x", NrCols(M), + "-matrix over ", BaseDomain(M), ":\n" ); + for i in [ 1 .. NrRows(M) ] do + if i = 1 then + Print( "[" ); + else + Print( " " ); + fi; + Print( M![FROWSPOS][i], "\n" ); + od; + Print( "]>\n" ); + end ); + +InstallMethod( String, [ "IsFlatPlistMatrixRep" ], + function( M ) + local st; + st := "NewMatrix(IsFlatPlistMatrixRep"; + Add( st, ',' ); + if IsFinite( BaseDomain(M) ) and IsField( BaseDomain(M) ) then + Append( st, "GF(" ); + Append( st, String( Size( BaseDomain(M) ) ) ); + Append( st, ")," ); + else + Append( st, String( BaseDomain(M) ) ); + Append( st, "," ); + fi; + Append( st, String( NumberColumns( M ) ) ); + Add( st, ',' ); + Append( st, String( Unpack( M ) ) ); + Add( st, ')' ); + return st; + end ); diff --git a/lib/read3.g b/lib/read3.g index baf3cf714c..7d345ae67e 100644 --- a/lib/read3.g +++ b/lib/read3.g @@ -107,6 +107,7 @@ ReadLib( "wordass.gd" ); ReadLib( "matobj2.gd" ); ReadLib( "matobjplist.gd" ); +ReadLib( "matobjflatplist.gd" ); ReadLib( "matobjnz.gd" ); # files dealing with rewriting systems diff --git a/lib/read5.g b/lib/read5.g index e0042c6bf4..94a4132f8b 100644 --- a/lib/read5.g +++ b/lib/read5.g @@ -114,6 +114,7 @@ ReadLib( "vecmat.gi" ); ReadLib( "vec8bit.gi" ); ReadLib( "mat8bit.gi" ); ReadLib( "matobjplist.gi" ); +ReadLib( "matobjflatplist.gi" ); ReadLib( "matobjnz.gi" ); ReadLib( "meataxe.gi" ); ReadLib( "meatauto.gi" ); diff --git a/tst/testinstall/MatrixObj/CompanionMatrix.tst b/tst/testinstall/MatrixObj/CompanionMatrix.tst index b5e27bbb2c..5821ec8281 100644 --- a/tst/testinstall/MatrixObj/CompanionMatrix.tst +++ b/tst/testinstall/MatrixObj/CompanionMatrix.tst @@ -67,6 +67,22 @@ gap> TestCompanionMatrix(IsPlistMatrixRep, x+1, F); gap> TestCompanionMatrix(IsPlistMatrixRep, x^2+x+1, F); <2x2-matrix over Rationals> +# +# IsFlatPlistMatrixRep +# +gap> F:= GF(251);; x:= X(F);; +gap> TestCompanionMatrix(IsFlatPlistMatrixRep, x+1, F); +<1x1-matrix over GF(251)> +gap> TestCompanionMatrix(IsFlatPlistMatrixRep, x^2+x+1, F); +<2x2-matrix over GF(251)> + +# +gap> F:= Integers;; x:= X(F);; +gap> TestCompanionMatrix(IsFlatPlistMatrixRep, x+1, F); +<1x1-matrix over Integers> +gap> TestCompanionMatrix(IsFlatPlistMatrixRep, x^2+x+1, F); +<2x2-matrix over Integers> + # # IsPlistRep # diff --git a/tst/testinstall/MatrixObj/ElementaryMatrices.tst b/tst/testinstall/MatrixObj/ElementaryMatrices.tst index e2080c550a..ae67b4dd8b 100644 --- a/tst/testinstall/MatrixObj/ElementaryMatrices.tst +++ b/tst/testinstall/MatrixObj/ElementaryMatrices.tst @@ -67,6 +67,14 @@ true gap> TestElementaryTransforms( mat, -1 ); gap> TestWholeMatrixTransforms( mat, -1 ); +# +gap> mat := NewMatrix(IsFlatPlistMatrixRep, Integers, 3, +> [ [ 2, 4, 5 ], [ 7, 11, -4 ], [ -3, 20, 0 ] ] );; +gap> IsFlatPlistMatrixRep(mat); +true +gap> TestElementaryTransforms( mat, -1 ); +gap> TestWholeMatrixTransforms( mat, -1 ); + # gap> TestPositionNonZeroInRow([ [ 1 ] ]); gap> TestPositionNonZeroInRow(Matrix([ [ 1 ] ])); diff --git a/tst/testinstall/MatrixObj/IdentityMatrix.tst b/tst/testinstall/MatrixObj/IdentityMatrix.tst index 1cabf541aa..c42e7191d0 100644 --- a/tst/testinstall/MatrixObj/IdentityMatrix.tst +++ b/tst/testinstall/MatrixObj/IdentityMatrix.tst @@ -73,6 +73,24 @@ gap> TestIdentityMatrix(IsPlistMatrixRep, Integers mod 4, 0); gap> TestIdentityMatrix(IsPlistMatrixRep, Integers mod 4, -1); Error, IdentityMatrix: the dimension must be non-negative +# +# IsFlatPlistMatrixRep +# +gap> TestIdentityMatrix(IsFlatPlistMatrixRep, GF(2), 2); +<2x2-matrix over GF(2)> +gap> TestIdentityMatrix(IsFlatPlistMatrixRep, GF(2), 0); +<0x0-matrix over GF(2)> +gap> TestIdentityMatrix(IsFlatPlistMatrixRep, GF(2), -1); +Error, IdentityMatrix: the dimension must be non-negative + +# +gap> TestIdentityMatrix(IsFlatPlistMatrixRep, Integers, 2); +<2x2-matrix over Integers> +gap> TestIdentityMatrix(IsFlatPlistMatrixRep, Integers, 0); +<0x0-matrix over Integers> +gap> TestIdentityMatrix(IsFlatPlistMatrixRep, Integers, -1); +Error, IdentityMatrix: the dimension must be non-negative + # # Test IdentityMatrix variant which "guesses" a suitable representation, i.e.: # IdentityMatrix( , , ) diff --git a/tst/testinstall/MatrixObj/Matrix.tst b/tst/testinstall/MatrixObj/Matrix.tst index 85d6c9d80f..15298ab499 100644 --- a/tst/testinstall/MatrixObj/Matrix.tst +++ b/tst/testinstall/MatrixObj/Matrix.tst @@ -45,5 +45,16 @@ gap> Display(m); gap> m = Matrix( IsPlistMatrixRep, GF(2), [1,2,3,4] * Z(2), 2 ); true +# +gap> m := Matrix( IsFlatPlistMatrixRep, GF(2), [[1,2],[3,4]] * Z(2) ); +<2x2-matrix over GF(2)> +gap> Display(m); +<2x2-matrix over GF(2): +[[ Z(2)^0, 0*Z(2) ] + [ Z(2)^0, 0*Z(2) ] +]> +gap> m = Matrix( IsFlatPlistMatrixRep, GF(2), [1,2,3,4] * Z(2), 2 ); +true + # gap> STOP_TEST("Matrix.tst"); diff --git a/tst/testinstall/MatrixObj/ZeroMatrix.tst b/tst/testinstall/MatrixObj/ZeroMatrix.tst index 2affaa554b..8a5c86c4b9 100644 --- a/tst/testinstall/MatrixObj/ZeroMatrix.tst +++ b/tst/testinstall/MatrixObj/ZeroMatrix.tst @@ -79,6 +79,24 @@ gap> TestZeroMatrix(IsPlistMatrixRep, Integers mod 4, 2, 0); gap> TestZeroMatrix(IsPlistMatrixRep, Integers mod 4, 0, 3); <0x3-matrix over (Integers mod 4)> +# +# IsFlatPlistMatrixRep +# +gap> TestZeroMatrix(IsFlatPlistMatrixRep, GF(2), 2, 3); +<2x3-matrix over GF(2)> +gap> TestZeroMatrix(IsFlatPlistMatrixRep, GF(2), 2, 0); +<2x0-matrix over GF(2)> +gap> TestZeroMatrix(IsFlatPlistMatrixRep, GF(2), 0, 3); +<0x3-matrix over GF(2)> + +# +gap> TestZeroMatrix(IsFlatPlistMatrixRep, Integers, 2, 3); +<2x3-matrix over Integers> +gap> TestZeroMatrix(IsFlatPlistMatrixRep, Integers, 2, 0); +<2x0-matrix over Integers> +gap> TestZeroMatrix(IsFlatPlistMatrixRep, Integers, 0, 3); +<0x3-matrix over Integers> + # # Test ZeroMatrix variant which "guesses" a suitable representation, i.e.: # ZeroMatrix( , , ) diff --git a/tst/testinstall/MatrixObj/matobjflatplist.tst b/tst/testinstall/MatrixObj/matobjflatplist.tst new file mode 100644 index 0000000000..6454cd6d8b --- /dev/null +++ b/tst/testinstall/MatrixObj/matobjflatplist.tst @@ -0,0 +1,191 @@ +#@local e, v, v2, w, M, z, rows, a, b, c, d, p, ev, n, ai, inv, zm, zs +gap> START_TEST( "matobjflatplist.tst" ); + +# +# MakeIsPlistVectorRep: test input validation +# +gap> e:= MakeIsPlistVectorRep( Integers, [], true ); + +gap> v:= MakeIsPlistVectorRep( Integers, [ 1 ], true ); + +gap> v2:= NewVector( IsPlistVectorRep, Integers, [ 1, 0 ] ); + +gap> w:= MakeIsPlistVectorRep( Rationals, [ 1 ], true ); + + +# +gap> MakeIsFlatPlistMatrixRep( Integers, 2, [ v ], true ); +Error, the entries of must be plain lists +gap> M:= MakeIsFlatPlistMatrixRep( Integers, 2, [], true ); +<0x2-matrix over Integers> +gap> MakeIsFlatPlistMatrixRep( Integers, 1, [ [ 1 ] ], true ); +<1x1-matrix over Integers> +gap> MakeIsFlatPlistMatrixRep( Integers, 2, [ [ 1 ] ], true ); +Error, the entries of must have length +gap> MakeIsFlatPlistMatrixRep( Integers, 1, [ [ 1/2 ] ], true ); +Error, the elements in must lie in +gap> MakeIsFlatPlistMatrixRep( GF(2), 1, [ [ Z(2) ] ], true ); +<1x1-matrix over GF(2)> +gap> MakeIsFlatPlistMatrixRep( GF(2), 1, [ [ Z(4) ] ], true ); +Error, the elements in must lie in + +# +gap> NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [] ); +<0x2-matrix over Integers> +gap> NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [ 1 ] ); +Error, NewMatrix: Length of is not a multiple of +gap> NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [ [ 1 ] ] ); +Error, the entries of must have length +gap> NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [ [ 1, 2 ] ] ); +<1x2-matrix over Integers> +gap> NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [ v ] ); +Error, the entries of must have length +gap> M:= NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [ [ 1, 2 ], [ 3, 4 ] ] ); +<2x2-matrix over Integers> +gap> IsMutable( M ); +true +gap> Unpack( M ); +[ [ 1, 2 ], [ 3, 4 ] ] +gap> M[2,1]; +3 +gap> M[1]; +Error, row access unsupported; use M[i,j] or RowsOfMatrix(M) +gap> M[2,2] := 5;; +gap> Unpack( M ); +[ [ 1, 2 ], [ 3, 5 ] ] + +# +gap> rows:= RowsOfMatrix( M ); +[ , + ] +gap> Length( rows ); +2 +gap> IsPlistVectorRep( rows[1] ); +true +gap> Unpack( rows[1] ); +[ 1, 2 ] +gap> M[1,1] := 77;; +gap> rows[1][1]; +1 + +# +gap> M:= NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [ v2, v2 ] );; +gap> Unpack( M ); +[ [ 1, 0 ], [ 1, 0 ] ] + +# +# Empty matrices and empty vectors +# +# +gap> a:= NewZeroMatrix( IsFlatPlistMatrixRep, Integers, 2, 0 ); +<2x0-matrix over Integers> +gap> b:= NewZeroMatrix( IsFlatPlistMatrixRep, Integers, 0, 3 ); +<0x3-matrix over Integers> +gap> c:= NewZeroMatrix( IsFlatPlistMatrixRep, Integers, 0, 0 ); +<0x0-matrix over Integers> +gap> d:= NewZeroMatrix( IsFlatPlistMatrixRep, Integers, 0, 2 ); +<0x2-matrix over Integers> +gap> z:= NewZeroMatrix( IsFlatPlistMatrixRep, Integers, 2, 3 ); +<2x3-matrix over Integers> +gap> IsMutable( z ); +true +gap> Unpack( z ); +[ [ 0, 0, 0 ], [ 0, 0, 0 ] ] + +# +gap> M:= NewIdentityMatrix( IsFlatPlistMatrixRep, Integers, 2 );; +gap> Unpack( M ); +[ [ 1, 0 ], [ 0, 1 ] ] + +# +gap> a = NewMatrix( IsFlatPlistMatrixRep, Integers, 0, [ [], [] ] ); +true +gap> b = NewMatrix( IsFlatPlistMatrixRep, Integers, 3, [] ); +true +gap> c = NewMatrix( IsFlatPlistMatrixRep, Integers, 0, [] ); +true +gap> d = NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [] ); +true + +# +gap> p:= a * b; +<2x3-matrix over Integers> +gap> Unpack( p ); +[ [ 0, 0, 0 ], [ 0, 0, 0 ] ] +gap> p:= d * a; +<0x0-matrix over Integers> +gap> Unpack( p ); +[ ] +gap> p:= c * b; +<0x3-matrix over Integers> +gap> Unpack( p ); +[ ] + +# empty vector times (necessarily also empty) matrix +gap> ev:= NewVector( IsPlistVectorRep, Integers, [] ); + +gap> a * ev; + +gap> Unpack( last ); +[ 0, 0 ] +gap> ev * b; + +gap> Unpack( last ); +[ 0, 0, 0 ] + +# non-empty vector times empty matrix +gap> v2:= NewVector( IsPlistVectorRep, Integers, [ 1, 2 ] ); + +gap> d * v2; + +gap> Unpack( last ); +[ ] +gap> Unpack( v2 * a ); +[ ] + +# +gap> b + b; +<0x3-matrix over Integers> +gap> b - b; +<0x3-matrix over Integers> +gap> -b; +<0x3-matrix over Integers> +gap> ZeroMutable( b ); +<0x3-matrix over Integers> + +# +gap> InverseMutable( c ); +<0x0-matrix over Integers> +gap> InverseSameMutability( c ); +<0x0-matrix over Integers> + +# +gap> M:= NewMatrix( IsFlatPlistMatrixRep, Integers, 3, [ [ 0, 0, 2 ], [ 0, 0, 0 ] ] );; +gap> PositionNonZeroInRow( M, 1 ); +3 +gap> PositionNonZeroInRow( M, 1, 2 ); +3 +gap> PositionNonZeroInRow( M, 2 ); +4 + +# +gap> M:= NewMatrix( IsFlatPlistMatrixRep, Integers, 3, +> [ [ 1, 2, 3 ], [ 4, 5, 6 ] ] );; +gap> MultMatrixRowLeft( M, 1, -1 );; +gap> Unpack( M ); +[ [ -1, -2, -3 ], [ 4, 5, 6 ] ] +gap> MultMatrixRowRight( M, 2, 2 );; +gap> Unpack( M ); +[ [ -1, -2, -3 ], [ 8, 10, 12 ] ] +gap> AddMatrixRowsLeft( M, 1, 2, 3 );; +gap> Unpack( M ); +[ [ 23, 28, 33 ], [ 8, 10, 12 ] ] +gap> AddMatrixRowsRight( M, 2, 1, 2 );; +gap> Unpack( M ); +[ [ 23, 28, 33 ], [ 54, 66, 78 ] ] +gap> SwapMatrixRows( M, 1, 2 );; +gap> Unpack( M ); +[ [ 54, 66, 78 ], [ 23, 28, 33 ] ] + +# +gap> STOP_TEST( "matobjflatplist.tst" ); diff --git a/tst/testinstall/MatrixObj/testmatobj.g b/tst/testinstall/MatrixObj/testmatobj.g index 1f5a21ebc2..33e1fd963f 100644 --- a/tst/testinstall/MatrixObj/testmatobj.g +++ b/tst/testinstall/MatrixObj/testmatobj.g @@ -211,6 +211,8 @@ TestWholeMatrixTransforms := function(mat, scalar) basedomain := BaseDomain(mat); if IsPlistMatrixRep(mat) then src := NewMatrix(IsPlistMatrixRep, basedomain, NrCols(mat), src); + elif IsFlatPlistMatrixRep(mat) then + src := NewMatrix(IsFlatPlistMatrixRep, basedomain, NrCols(mat), src); elif Is8BitMatrixRep(mat) then ConvertToMatrixRep(src, basedomain); fi; From 532d82478eed529f4990d667563c766d2c3f2c2e Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 15 Apr 2026 00:38:12 +0200 Subject: [PATCH 02/32] hook up documentation --- doc/ref/makedocreldata.g | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/ref/makedocreldata.g b/doc/ref/makedocreldata.g index f2f1697bed..c31333eca0 100644 --- a/doc/ref/makedocreldata.g +++ b/doc/ref/makedocreldata.g @@ -119,6 +119,7 @@ GAPInfo.ManualDataRef:= rec( "../../lib/matobj2.gd", "../../lib/matobjnz.gd", "../../lib/matobjplist.gd", + "../../lib/matobjflatplist.gd", "../../lib/matrix.gd", "../../lib/meataxe.gi", "../../lib/memory.gd", From ad21cf161d1fb0a0d591473b2c6c47ea48c13bba Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 15 Apr 2026 00:39:31 +0200 Subject: [PATCH 03/32] Fix gaplint warnings --- lib/matobjflatplist.gi | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/matobjflatplist.gi b/lib/matobjflatplist.gi index 8343c42552..f489775b32 100644 --- a/lib/matobjflatplist.gi +++ b/lib/matobjflatplist.gi @@ -197,7 +197,7 @@ InstallMethod( MutableCopyMatrix, InstallMethod( ExtractSubMatrix, [ "IsFlatPlistMatrixRep", "IsList", "IsList" ], function( M, rowspos, colspos ) - local list, i; + local list; list := M![FROWSPOS]{ rowspos }{ colspos }; return MakeIsFlatPlistMatrixRep( BaseDomain(M), Length( colspos ), list, false ); end ); @@ -206,7 +206,6 @@ InstallMethod( CopySubMatrix, [ "IsFlatPlistMatrixRep", "IsFlatPlistMatrixRep and IsMutable", "IsList", "IsList", "IsList", "IsList" ], function( M, N, srcrows, dstrows, srccols, dstcols ) - local i; if ValueOption( "check" ) <> false and not IsIdenticalObj( BaseDomain(M), BaseDomain(N) ) then Error( " and are not compatible" ); @@ -313,7 +312,7 @@ InstallMethod( \*, InstallMethod( \*, [ "IsFlatPlistMatrixRep", "IsVectorObj" ], function( M, v ) - local rows, cols, bd, res, i, j; + local rows, cols, bd, res; rows := NumberRows( M ); cols := NumberColumns( M ); @@ -347,7 +346,7 @@ InstallMethod( \*, InstallMethod( \*, [ "IsVectorObj", "IsFlatPlistMatrixRep" ], function( v, M ) - local rows, cols, bd, res, i, j; + local rows, cols, bd, res; rows := NumberRows( M ); cols := NumberColumns( M ); @@ -442,7 +441,6 @@ InstallMethod( SwapMatrixColumns, InstallMethod( PostMakeImmutable, [ "IsFlatPlistMatrixRep" ], function( M ) - local row; MakeImmutable( M![FROWSPOS] ); end ); From 50e1f3bc8d8fc9f34062f8241ab1444b5208b748 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 15 Apr 2026 00:43:31 +0200 Subject: [PATCH 04/32] Make tst/testspecial/method-not-found-where.g more robust The number of methods for + may change quite a lot --- tst/testspecial/method-not-found-where.g | 2 +- tst/testspecial/method-not-found-where.g.out | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/tst/testspecial/method-not-found-where.g b/tst/testspecial/method-not-found-where.g index 16ce4149c4..49079f4b75 100644 --- a/tst/testspecial/method-not-found-where.g +++ b/tst/testspecial/method-not-found-where.g @@ -1,5 +1,5 @@ # test method-not-found traceback rendering while preserving helper access -f := a -> a + a;; +f := a -> IsDiagonalMat(a);; f(()); ShowMethods(1); Where(5); diff --git a/tst/testspecial/method-not-found-where.g.out b/tst/testspecial/method-not-found-where.g.out index c7496f32b2..30601cd166 100644 --- a/tst/testspecial/method-not-found-where.g.out +++ b/tst/testspecial/method-not-found-where.g.out @@ -1,19 +1,19 @@ gap> # test method-not-found traceback rendering while preserving helper access -gap> f := a -> a + a;; +gap> f := a -> IsDiagonalMat(a);; gap> f(()); Error, no method found! For debugging hints type ?Recovery from NoMethodFound -Error, no 1st choice method found for `+' on 2 arguments +Error, no 1st choice method found for `IsDiagonalMatrix' on 1 arguments Stack trace: -*[1] a + a +*[1] IsDiagonalMat( a ) @ *stdin*:3 ( ) called from read-eval loop at *stdin*:4 type 'quit;' to quit to outer loop brk> ShowMethods(1); -#I Searching Method for + with 2 arguments: -#I Total: 138 entries +#I Searching Method for IsDiagonalMatrix with 1 argument: +#I Total: 3 entries brk> Where(5); -*[1] a + a +*[1] IsDiagonalMat( a ) @ *stdin*:3 ( ) called from read-eval loop at *errin*:2 From 42d750152d22598f618de156142088b176e0e29b Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 15 Apr 2026 01:46:46 +0200 Subject: [PATCH 05/32] address some review comments --- lib/matobjflatplist.gi | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/matobjflatplist.gi b/lib/matobjflatplist.gi index f489775b32..2ca5804910 100644 --- a/lib/matobjflatplist.gi +++ b/lib/matobjflatplist.gi @@ -17,13 +17,10 @@ BindGlobal( "CopyFlatPlistMatrixRow", function( row ) local copy, i, len; len := Length( row ); - copy := []; - if 0 < len then - copy[len] := row[len]; - for i in [ 1 .. len - 1 ] do - copy[i] := row[i]; - od; - fi; + copy := EmptyPlist( len ); + for i in [ 1 .. len ] do + copy[i] := row[i]; + od; return copy; end ); @@ -104,13 +101,14 @@ InstallTagBasedMethod( NewMatrix, fi; fi; - rows := 0 * [ 1 .. Length( list ) ]; + rows := EmptyPlist( Length( list ) ); for i in [ 1 .. Length( list ) ] do row := list[i]; if IsVectorObj( row ) then - row := Unpack( row ); + rows[i] := Unpack( row ); + else + rows[i] := CopyFlatPlistMatrixRow( row ); fi; - rows[i] := CopyFlatPlistMatrixRow( row ); od; if not IsMutable( list ) then MakeImmutable( rows ); @@ -123,7 +121,7 @@ InstallTagBasedMethod( NewZeroMatrix, IsFlatPlistMatrixRep, function( filter, basedomain, rows, cols ) local list, row, i, z; - list := 0 * [ 1 .. rows ]; + list := EmptyPlist( rows ); z := Zero( basedomain ); for i in [ 1 .. rows ] do row := ListWithIdenticalEntries( cols, z ); @@ -296,7 +294,7 @@ InstallMethod( \*, if rowsA = 0 or colsB = 0 then list := []; elif colsA = 0 then # colsA = rowsB - list := 0 * [ 1 .. rowsA ]; + list := EmptyPlist( rowsA ); for i in [ 1 .. rowsA ] do list[i] := ListWithIdenticalEntries( colsB, Zero( bd ) ); od; From 5610ade32dae7df25b0d5344dbc73d31b80433a9 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Wed, 15 Apr 2026 01:51:08 +0200 Subject: [PATCH 06/32] Remove CopyFlatPlistMatrixRow --- lib/matobjflatplist.gi | 19 ++++--------------- 1 file changed, 4 insertions(+), 15 deletions(-) diff --git a/lib/matobjflatplist.gi b/lib/matobjflatplist.gi index 2ca5804910..bb502d0dde 100644 --- a/lib/matobjflatplist.gi +++ b/lib/matobjflatplist.gi @@ -13,17 +13,6 @@ # Dense matrix objects backed by plain lists of plain row lists. # -BindGlobal( "CopyFlatPlistMatrixRow", - function( row ) - local copy, i, len; - len := Length( row ); - copy := EmptyPlist( len ); - for i in [ 1 .. len ] do - copy[i] := row[i]; - od; - return copy; - end ); - BindGlobal( "InverseRowsForFlatPlistMatrix", function( M ) local bd, rows; @@ -107,7 +96,7 @@ InstallTagBasedMethod( NewMatrix, if IsVectorObj( row ) then rows[i] := Unpack( row ); else - rows[i] := CopyFlatPlistMatrixRow( row ); + rows[i] := PlainListCopy( row ); fi; od; if not IsMutable( list ) then @@ -180,17 +169,17 @@ InstallMethod( SetMatElm, InstallMethod( Unpack, [ "IsFlatPlistMatrixRep" ], - M -> List( M![FROWSPOS], CopyFlatPlistMatrixRow ) ); + M -> List( M![FROWSPOS], ShallowCopy ) ); InstallMethod( ShallowCopy, [ "IsFlatPlistMatrixRep" ], M -> MakeIsFlatPlistMatrixRep( BaseDomain(M), NrCols(M), - List( M![FROWSPOS], CopyFlatPlistMatrixRow ), false ) ); + List( M![FROWSPOS], ShallowCopy ), false ) ); InstallMethod( MutableCopyMatrix, [ "IsFlatPlistMatrixRep" ], M -> MakeIsFlatPlistMatrixRep( BaseDomain(M), NrCols(M), - List( M![FROWSPOS], CopyFlatPlistMatrixRow ), false ) ); + List( M![FROWSPOS], ShallowCopy ), false ) ); InstallMethod( ExtractSubMatrix, [ "IsFlatPlistMatrixRep", "IsList", "IsList" ], From db60cfe77d9fceaddd9fe9044321e109026d2909 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Thu, 16 Apr 2026 16:19:07 +0200 Subject: [PATCH 07/32] Rename IsFlatPlistMatrixRep to IsGenericMatrixRep --- benchmark/matobj/bench-matelm-access.g | 4 +- benchmark/matobj/bench-matobj-creation.g | 6 +- doc/ref/matobj.xml | 2 +- lib/matobjflatplist.gd | 8 +- lib/matobjflatplist.gi | 118 +++++++++--------- tst/testinstall/MatrixObj/CompanionMatrix.tst | 10 +- .../MatrixObj/ElementaryMatrices.tst | 4 +- tst/testinstall/MatrixObj/IdentityMatrix.tst | 14 +-- tst/testinstall/MatrixObj/Matrix.tst | 4 +- tst/testinstall/MatrixObj/ZeroMatrix.tst | 14 +-- tst/testinstall/MatrixObj/matobjflatplist.tst | 52 ++++---- tst/testinstall/MatrixObj/testmatobj.g | 4 +- 12 files changed, 120 insertions(+), 120 deletions(-) diff --git a/benchmark/matobj/bench-matelm-access.g b/benchmark/matobj/bench-matelm-access.g index 3ef1f6a9cb..f58d365830 100644 --- a/benchmark/matobj/bench-matelm-access.g +++ b/benchmark/matobj/bench-matelm-access.g @@ -142,8 +142,8 @@ RunMatTest("GF(257) plist-of-plist matrix", m); m:=IdentityMatrix(GF(257), 10); # IsPlistMatrixRep RunMatTest("GF(257) IsPlistMatrixRep", m); -m:=IdentityMatrix(GF(257), 10); # IsFlatPlistMatrixRep -RunMatTest("GF(257) IsFlatPlistMatrixRep", m); +m:=IdentityMatrix(GF(257), 10); # IsGenericMatrixRep +RunMatTest("GF(257) IsGenericMatrixRep", m); m:=IdentityMat(10,GF(2));; # plain list of IsGF2VectorRep RunMatTest("GF(2) rowlist", m); diff --git a/benchmark/matobj/bench-matobj-creation.g b/benchmark/matobj/bench-matobj-creation.g index 8ce1af3b6a..fdf568d716 100644 --- a/benchmark/matobj/bench-matobj-creation.g +++ b/benchmark/matobj/bench-matobj-creation.g @@ -99,15 +99,15 @@ end; RunMatTest("GF(257)", GF(257)); RunMatObjTest("GF(257) IsPlistMatrixRep", IsPlistMatrixRep, GF(257)); -RunMatObjTest("GF(257) IsFlatPlistMatrixRep", IsFlatPlistMatrixRep, GF(257)); +RunMatObjTest("GF(257) IsGenericMatrixRep", IsGenericMatrixRep, GF(257)); RunMatTest("Integers", Integers); RunMatObjTest("integer IsPlistMatrixRep", IsPlistMatrixRep, Integers); -RunMatObjTest("integer IsFlatPlistMatrixRep", IsFlatPlistMatrixRep, Integers); +RunMatObjTest("integer IsGenericMatrixRep", IsGenericMatrixRep, Integers); RunMatTest("Rationals", Rationals); RunMatObjTest("rational IsPlistMatrixRep", IsPlistMatrixRep, Rationals); -RunMatObjTest("rational IsFlatPlistMatrixRep", IsFlatPlistMatrixRep, Rationals); +RunMatObjTest("rational IsGenericMatrixRep", IsGenericMatrixRep, Rationals); # TODO: other reps # TODO: other compare with creating plist-of-plist diff --git a/doc/ref/matobj.xml b/doc/ref/matobj.xml index c1faa94a46..ec282ab5d8 100644 --- a/doc/ref/matobj.xml +++ b/doc/ref/matobj.xml @@ -374,7 +374,7 @@ described in this chapter is supported. <#Include Label="IsGF2MatrixRep"> <#Include Label="Is8BitMatrixRep"> <#Include Label="IsPlistMatrixRep"> -<#Include Label="IsFlatPlistMatrixRep"> +<#Include Label="IsGenericMatrixRep"> <#Include Label="IsZmodnZMatrixRep"> diff --git a/lib/matobjflatplist.gd b/lib/matobjflatplist.gd index b4b801dab3..a9707baebc 100644 --- a/lib/matobjflatplist.gd +++ b/lib/matobjflatplist.gd @@ -15,12 +15,12 @@ ############################################################################# ## -## <#GAPDoc Label="IsFlatPlistMatrixRep"> +## <#GAPDoc Label="IsGenericMatrixRep"> ## -## +## ## ## -## An object obj in describes +## An object obj in describes ## a matrix object (see ) whose entries are stored ## as a dense plain list of dense plain row lists. ##

@@ -31,7 +31,7 @@ ## ## <#/GAPDoc> ## -DeclareRepresentation( "IsFlatPlistMatrixRep", +DeclareRepresentation( "IsGenericMatrixRep", IsMatrixObj and IsPositionalObjectRep and IsCopyable and IsNoImmediateMethodsObject diff --git a/lib/matobjflatplist.gi b/lib/matobjflatplist.gi index bb502d0dde..bb5a16486d 100644 --- a/lib/matobjflatplist.gi +++ b/lib/matobjflatplist.gi @@ -31,19 +31,19 @@ BindGlobal( "InverseRowsForFlatPlistMatrix", return INV_MATRIX_MUTABLE( M![FROWSPOS] ); end ); -BindGlobal( "MakeIsFlatPlistMatrixRep", +BindGlobal( "MakeIsGenericMatrixRep", function( basedomain, ncols, list, check ) local fam, types, typ, row; fam := CollectionsFamily( FamilyObj( basedomain ) ); if not IsBound( fam!.FlatPlistMatrixRepTypes ) then fam!.FlatPlistMatrixRepTypes := [ - NewType( fam, IsFlatPlistMatrixRep ), - NewType( fam, IsFlatPlistMatrixRep and IsMutable ), + NewType( fam, IsGenericMatrixRep ), + NewType( fam, IsGenericMatrixRep and IsMutable ), ]; fam!.FlatPlistMatrixRepTypesEasyCompare := [ - NewType( fam, IsFlatPlistMatrixRep and CanEasilyCompareElements ), - NewType( fam, IsFlatPlistMatrixRep and CanEasilyCompareElements and IsMutable ), + NewType( fam, IsGenericMatrixRep and CanEasilyCompareElements ), + NewType( fam, IsGenericMatrixRep and CanEasilyCompareElements and IsMutable ), ]; fi; if HasCanEasilyCompareElements( Representative( basedomain ) ) and @@ -75,7 +75,7 @@ BindGlobal( "MakeIsFlatPlistMatrixRep", InstallTagBasedMethod( NewMatrix, - IsFlatPlistMatrixRep, + IsGenericMatrixRep, function( filter, basedomain, ncols, list ) local nd, rows, i, row; @@ -102,12 +102,12 @@ InstallTagBasedMethod( NewMatrix, if not IsMutable( list ) then MakeImmutable( rows ); fi; - return MakeIsFlatPlistMatrixRep( basedomain, ncols, rows, true ); + return MakeIsGenericMatrixRep( basedomain, ncols, rows, true ); end ); InstallTagBasedMethod( NewZeroMatrix, - IsFlatPlistMatrixRep, + IsGenericMatrixRep, function( filter, basedomain, rows, cols ) local list, row, i, z; list := EmptyPlist( rows ); @@ -116,43 +116,43 @@ InstallTagBasedMethod( NewZeroMatrix, row := ListWithIdenticalEntries( cols, z ); list[i] := row; od; - return MakeIsFlatPlistMatrixRep( basedomain, cols, list, false ); + return MakeIsGenericMatrixRep( basedomain, cols, list, false ); end ); InstallMethod( ConstructingFilter, - [ "IsFlatPlistMatrixRep" ], - M -> IsFlatPlistMatrixRep ); + [ "IsGenericMatrixRep" ], + M -> IsGenericMatrixRep ); InstallMethod( CompatibleVectorFilter, - [ "IsFlatPlistMatrixRep" ], + [ "IsGenericMatrixRep" ], M -> IsPlistVectorRep ); InstallMethod( BaseDomain, - [ "IsFlatPlistMatrixRep" ], + [ "IsGenericMatrixRep" ], M -> M![FBDPOS] ); InstallMethod( NumberRows, - [ "IsFlatPlistMatrixRep" ], + [ "IsGenericMatrixRep" ], M -> Length( M![FROWSPOS] ) ); InstallMethod( NumberColumns, - [ "IsFlatPlistMatrixRep" ], + [ "IsGenericMatrixRep" ], M -> M![FCOLSPOS] ); InstallMethod( \[\], - [ "IsFlatPlistMatrixRep", "IsPosInt" ], + [ "IsGenericMatrixRep", "IsPosInt" ], function( M, pos ) ErrorNoReturn( "row access unsupported; use M[i,j] or RowsOfMatrix(M)" ); end ); InstallMethod( MatElm, - [ "IsFlatPlistMatrixRep", "IsPosInt", "IsPosInt" ], + [ "IsGenericMatrixRep", "IsPosInt", "IsPosInt" ], { M, row, col } -> M![FROWSPOS][row,col] ); InstallMethod( SetMatElm, - [ "IsFlatPlistMatrixRep and IsMutable", "IsPosInt", "IsPosInt", "IsObject" ], + [ "IsGenericMatrixRep and IsMutable", "IsPosInt", "IsPosInt", "IsObject" ], function( M, row, col, ob ) if ValueOption( "check" ) <> false then if not ob in BaseDomain( M ) then @@ -168,29 +168,29 @@ InstallMethod( SetMatElm, InstallMethod( Unpack, - [ "IsFlatPlistMatrixRep" ], + [ "IsGenericMatrixRep" ], M -> List( M![FROWSPOS], ShallowCopy ) ); InstallMethod( ShallowCopy, - [ "IsFlatPlistMatrixRep" ], - M -> MakeIsFlatPlistMatrixRep( BaseDomain(M), NrCols(M), + [ "IsGenericMatrixRep" ], + M -> MakeIsGenericMatrixRep( BaseDomain(M), NrCols(M), List( M![FROWSPOS], ShallowCopy ), false ) ); InstallMethod( MutableCopyMatrix, - [ "IsFlatPlistMatrixRep" ], - M -> MakeIsFlatPlistMatrixRep( BaseDomain(M), NrCols(M), + [ "IsGenericMatrixRep" ], + M -> MakeIsGenericMatrixRep( BaseDomain(M), NrCols(M), List( M![FROWSPOS], ShallowCopy ), false ) ); InstallMethod( ExtractSubMatrix, - [ "IsFlatPlistMatrixRep", "IsList", "IsList" ], + [ "IsGenericMatrixRep", "IsList", "IsList" ], function( M, rowspos, colspos ) local list; list := M![FROWSPOS]{ rowspos }{ colspos }; - return MakeIsFlatPlistMatrixRep( BaseDomain(M), Length( colspos ), list, false ); + return MakeIsGenericMatrixRep( BaseDomain(M), Length( colspos ), list, false ); end ); InstallMethod( CopySubMatrix, - [ "IsFlatPlistMatrixRep", "IsFlatPlistMatrixRep and IsMutable", + [ "IsGenericMatrixRep", "IsGenericMatrixRep and IsMutable", "IsList", "IsList", "IsList", "IsList" ], function( M, N, srcrows, dstrows, srccols, dstcols ) if ValueOption( "check" ) <> false and @@ -201,15 +201,15 @@ InstallMethod( CopySubMatrix, end ); InstallMethod( TransposedMatMutable, - [ "IsFlatPlistMatrixRep" ], + [ "IsGenericMatrixRep" ], function( M ) local list; list := TransposedMatMutable(M![FROWSPOS]); - return MakeIsFlatPlistMatrixRep( BaseDomain(M), NrRows(M), list, false ); + return MakeIsGenericMatrixRep( BaseDomain(M), NrRows(M), list, false ); end ); InstallMethod( \+, - [ "IsFlatPlistMatrixRep", "IsFlatPlistMatrixRep" ], + [ "IsGenericMatrixRep", "IsGenericMatrixRep" ], function( a, b ) if ValueOption( "check" ) <> false and ( not IsIdenticalObj( BaseDomain( a ), BaseDomain( b ) ) or @@ -217,12 +217,12 @@ InstallMethod( \+, NrCols( a ) <> NrCols( b ) ) then Error( " and are not compatible" ); fi; - return MakeIsFlatPlistMatrixRep( BaseDomain( a ), NrCols( a ), + return MakeIsGenericMatrixRep( BaseDomain( a ), NrCols( a ), a![FROWSPOS] + b![FROWSPOS], false ); end ); InstallMethod( \-, - [ "IsFlatPlistMatrixRep", "IsFlatPlistMatrixRep" ], + [ "IsGenericMatrixRep", "IsGenericMatrixRep" ], function( a, b ) if ValueOption( "check" ) <> false and ( not IsIdenticalObj( BaseDomain( a ), BaseDomain( b ) ) or @@ -230,27 +230,27 @@ InstallMethod( \-, NrCols( a ) <> NrCols( b ) ) then Error( " and are not compatible" ); fi; - return MakeIsFlatPlistMatrixRep( BaseDomain( a ), NrCols( a ), + return MakeIsGenericMatrixRep( BaseDomain( a ), NrCols( a ), a![FROWSPOS] - b![FROWSPOS], false ); end ); InstallMethod( AdditiveInverseMutable, - [ "IsFlatPlistMatrixRep" ], - M -> MakeIsFlatPlistMatrixRep( BaseDomain( M ), NrCols( M ), + [ "IsGenericMatrixRep" ], + M -> MakeIsGenericMatrixRep( BaseDomain( M ), NrCols( M ), AdditiveInverseMutable( M![FROWSPOS] ), false ) ); InstallMethod( ZeroMutable, - [ "IsFlatPlistMatrixRep" ], + [ "IsGenericMatrixRep" ], function( M ) local z; - z := MakeIsFlatPlistMatrixRep( BaseDomain( M ), NrCols( M ), + z := MakeIsGenericMatrixRep( BaseDomain( M ), NrCols( M ), ZeroMutable( M![FROWSPOS] ), false ); SetIsZero( z, true ); return z; end ); InstallMethod( InverseMutable, - [ "IsFlatPlistMatrixRep" ], + [ "IsGenericMatrixRep" ], function( M ) local rows; @@ -258,11 +258,11 @@ InstallMethod( InverseMutable, if rows = fail then return fail; fi; - return MakeIsFlatPlistMatrixRep( BaseDomain( M ), NrCols( M ), rows, false ); + return MakeIsGenericMatrixRep( BaseDomain( M ), NrCols( M ), rows, false ); end ); InstallMethod( \*, - [ "IsFlatPlistMatrixRep", "IsFlatPlistMatrixRep" ], + [ "IsGenericMatrixRep", "IsGenericMatrixRep" ], function( a, b ) local rowsA, colsA, rowsB, colsB, bd, list, i; @@ -293,11 +293,11 @@ InstallMethod( \*, if not IsMutable( a ) and not IsMutable( b ) then MakeImmutable( list ); fi; - return MakeIsFlatPlistMatrixRep( bd, colsB, list, false ); + return MakeIsGenericMatrixRep( bd, colsB, list, false ); end ); InstallMethod( \*, - [ "IsFlatPlistMatrixRep", "IsVectorObj" ], + [ "IsGenericMatrixRep", "IsVectorObj" ], function( M, v ) local rows, cols, bd, res; @@ -331,7 +331,7 @@ InstallMethod( \*, end ); InstallMethod( \*, - [ "IsVectorObj", "IsFlatPlistMatrixRep" ], + [ "IsVectorObj", "IsGenericMatrixRep" ], function( v, M ) local rows, cols, bd, res; @@ -365,10 +365,10 @@ InstallMethod( \*, end ); InstallMethod( ChangedBaseDomain, - [ "IsFlatPlistMatrixRep", "IsRing" ], + [ "IsGenericMatrixRep", "IsRing" ], function( M, r ) local n; - n := NewMatrix( IsFlatPlistMatrixRep, r, NrCols(M), M![FROWSPOS] ); + n := NewMatrix( IsGenericMatrixRep, r, NrCols(M), M![FROWSPOS] ); if not IsMutable( M ) then MakeImmutable( n ); fi; @@ -377,62 +377,62 @@ InstallMethod( ChangedBaseDomain, InstallMethod( MultMatrixRowLeft, - [ "IsFlatPlistMatrixRep and IsMutable", "IsInt", "IsObject" ], + [ "IsGenericMatrixRep and IsMutable", "IsInt", "IsObject" ], function( mat, row, scalar ) MultMatrixRowLeft(mat![FROWSPOS], row, scalar); end ); InstallMethod( MultMatrixRowRight, - [ "IsFlatPlistMatrixRep and IsMutable", "IsInt", "IsObject" ], + [ "IsGenericMatrixRep and IsMutable", "IsInt", "IsObject" ], function( mat, row, scalar ) MultMatrixRowRight(mat![FROWSPOS], row, scalar); end ); InstallMethod( AddMatrixRowsLeft, - [ "IsFlatPlistMatrixRep and IsMutable", "IsInt", "IsInt", "IsObject" ], + [ "IsGenericMatrixRep and IsMutable", "IsInt", "IsInt", "IsObject" ], function( mat, row1, row2, scalar ) AddMatrixRowsLeft( mat![FROWSPOS], row1, row2, scalar ); end ); InstallMethod( AddMatrixRowsRight, - [ "IsFlatPlistMatrixRep and IsMutable", "IsInt", "IsInt", "IsObject" ], + [ "IsGenericMatrixRep and IsMutable", "IsInt", "IsInt", "IsObject" ], function( mat, row1, row2, scalar ) AddMatrixRowsRight( mat![FROWSPOS], row1, row2, scalar ); end ); InstallMethod( PositionNonZeroInRow, - [ "IsFlatPlistMatrixRep", "IsPosInt" ], + [ "IsGenericMatrixRep", "IsPosInt" ], function( mat, row ) return PositionNonZero( mat![FROWSPOS][row] ); end ); InstallMethod( PositionNonZeroInRow, - [ "IsFlatPlistMatrixRep", "IsPosInt", "IsInt" ], + [ "IsGenericMatrixRep", "IsPosInt", "IsInt" ], function( mat, row, from ) return PositionNonZero( mat![FROWSPOS][row], from ); end ); InstallMethod( SwapMatrixRows, - [ "IsFlatPlistMatrixRep and IsMutable", "IsInt", "IsInt" ], + [ "IsGenericMatrixRep and IsMutable", "IsInt", "IsInt" ], function( mat, row1, row2 ) SwapMatrixRows(mat![FROWSPOS], row1, row2); end ); InstallMethod( SwapMatrixColumns, - [ "IsFlatPlistMatrixRep and IsMutable", "IsInt", "IsInt" ], + [ "IsGenericMatrixRep and IsMutable", "IsInt", "IsInt" ], function( mat, col1, col2 ) SwapMatrixColumns(mat![FROWSPOS], col1, col2); end ); InstallMethod( PostMakeImmutable, - [ "IsFlatPlistMatrixRep" ], + [ "IsGenericMatrixRep" ], function( M ) MakeImmutable( M![FROWSPOS] ); end ); -InstallMethod( ViewObj, [ "IsFlatPlistMatrixRep" ], +InstallMethod( ViewObj, [ "IsGenericMatrixRep" ], function( M ) Print( "<" ); if not IsMutable( M ) then @@ -442,9 +442,9 @@ InstallMethod( ViewObj, [ "IsFlatPlistMatrixRep" ], "-matrix over ", BaseDomain(M), ">" ); end ); -InstallMethod( PrintObj, [ "IsFlatPlistMatrixRep" ], +InstallMethod( PrintObj, [ "IsGenericMatrixRep" ], function( M ) - Print( "NewMatrix(IsFlatPlistMatrixRep" ); + Print( "NewMatrix(IsGenericMatrixRep" ); if IsFinite( BaseDomain(M) ) and IsField( BaseDomain(M) ) then Print( ",GF(", Size( BaseDomain(M) ), ")," ); else @@ -453,7 +453,7 @@ InstallMethod( PrintObj, [ "IsFlatPlistMatrixRep" ], Print( NumberColumns( M ), ",", Unpack( M ), ")" ); end ); -InstallMethod( Display, [ "IsFlatPlistMatrixRep" ], +InstallMethod( Display, [ "IsGenericMatrixRep" ], function( M ) local i; Print( "<" ); @@ -473,10 +473,10 @@ InstallMethod( Display, [ "IsFlatPlistMatrixRep" ], Print( "]>\n" ); end ); -InstallMethod( String, [ "IsFlatPlistMatrixRep" ], +InstallMethod( String, [ "IsGenericMatrixRep" ], function( M ) local st; - st := "NewMatrix(IsFlatPlistMatrixRep"; + st := "NewMatrix(IsGenericMatrixRep"; Add( st, ',' ); if IsFinite( BaseDomain(M) ) and IsField( BaseDomain(M) ) then Append( st, "GF(" ); diff --git a/tst/testinstall/MatrixObj/CompanionMatrix.tst b/tst/testinstall/MatrixObj/CompanionMatrix.tst index 5821ec8281..a2b1330b57 100644 --- a/tst/testinstall/MatrixObj/CompanionMatrix.tst +++ b/tst/testinstall/MatrixObj/CompanionMatrix.tst @@ -68,19 +68,19 @@ gap> TestCompanionMatrix(IsPlistMatrixRep, x^2+x+1, F); <2x2-matrix over Rationals> # -# IsFlatPlistMatrixRep +# IsGenericMatrixRep # gap> F:= GF(251);; x:= X(F);; -gap> TestCompanionMatrix(IsFlatPlistMatrixRep, x+1, F); +gap> TestCompanionMatrix(IsGenericMatrixRep, x+1, F); <1x1-matrix over GF(251)> -gap> TestCompanionMatrix(IsFlatPlistMatrixRep, x^2+x+1, F); +gap> TestCompanionMatrix(IsGenericMatrixRep, x^2+x+1, F); <2x2-matrix over GF(251)> # gap> F:= Integers;; x:= X(F);; -gap> TestCompanionMatrix(IsFlatPlistMatrixRep, x+1, F); +gap> TestCompanionMatrix(IsGenericMatrixRep, x+1, F); <1x1-matrix over Integers> -gap> TestCompanionMatrix(IsFlatPlistMatrixRep, x^2+x+1, F); +gap> TestCompanionMatrix(IsGenericMatrixRep, x^2+x+1, F); <2x2-matrix over Integers> # diff --git a/tst/testinstall/MatrixObj/ElementaryMatrices.tst b/tst/testinstall/MatrixObj/ElementaryMatrices.tst index ae67b4dd8b..2204e66d9c 100644 --- a/tst/testinstall/MatrixObj/ElementaryMatrices.tst +++ b/tst/testinstall/MatrixObj/ElementaryMatrices.tst @@ -68,9 +68,9 @@ gap> TestElementaryTransforms( mat, -1 ); gap> TestWholeMatrixTransforms( mat, -1 ); # -gap> mat := NewMatrix(IsFlatPlistMatrixRep, Integers, 3, +gap> mat := NewMatrix(IsGenericMatrixRep, Integers, 3, > [ [ 2, 4, 5 ], [ 7, 11, -4 ], [ -3, 20, 0 ] ] );; -gap> IsFlatPlistMatrixRep(mat); +gap> IsGenericMatrixRep(mat); true gap> TestElementaryTransforms( mat, -1 ); gap> TestWholeMatrixTransforms( mat, -1 ); diff --git a/tst/testinstall/MatrixObj/IdentityMatrix.tst b/tst/testinstall/MatrixObj/IdentityMatrix.tst index c42e7191d0..74f429b3f2 100644 --- a/tst/testinstall/MatrixObj/IdentityMatrix.tst +++ b/tst/testinstall/MatrixObj/IdentityMatrix.tst @@ -74,21 +74,21 @@ gap> TestIdentityMatrix(IsPlistMatrixRep, Integers mod 4, -1); Error, IdentityMatrix: the dimension must be non-negative # -# IsFlatPlistMatrixRep +# IsGenericMatrixRep # -gap> TestIdentityMatrix(IsFlatPlistMatrixRep, GF(2), 2); +gap> TestIdentityMatrix(IsGenericMatrixRep, GF(2), 2); <2x2-matrix over GF(2)> -gap> TestIdentityMatrix(IsFlatPlistMatrixRep, GF(2), 0); +gap> TestIdentityMatrix(IsGenericMatrixRep, GF(2), 0); <0x0-matrix over GF(2)> -gap> TestIdentityMatrix(IsFlatPlistMatrixRep, GF(2), -1); +gap> TestIdentityMatrix(IsGenericMatrixRep, GF(2), -1); Error, IdentityMatrix: the dimension must be non-negative # -gap> TestIdentityMatrix(IsFlatPlistMatrixRep, Integers, 2); +gap> TestIdentityMatrix(IsGenericMatrixRep, Integers, 2); <2x2-matrix over Integers> -gap> TestIdentityMatrix(IsFlatPlistMatrixRep, Integers, 0); +gap> TestIdentityMatrix(IsGenericMatrixRep, Integers, 0); <0x0-matrix over Integers> -gap> TestIdentityMatrix(IsFlatPlistMatrixRep, Integers, -1); +gap> TestIdentityMatrix(IsGenericMatrixRep, Integers, -1); Error, IdentityMatrix: the dimension must be non-negative # diff --git a/tst/testinstall/MatrixObj/Matrix.tst b/tst/testinstall/MatrixObj/Matrix.tst index 15298ab499..bc07b33465 100644 --- a/tst/testinstall/MatrixObj/Matrix.tst +++ b/tst/testinstall/MatrixObj/Matrix.tst @@ -46,14 +46,14 @@ gap> m = Matrix( IsPlistMatrixRep, GF(2), [1,2,3,4] * Z(2), 2 ); true # -gap> m := Matrix( IsFlatPlistMatrixRep, GF(2), [[1,2],[3,4]] * Z(2) ); +gap> m := Matrix( IsGenericMatrixRep, GF(2), [[1,2],[3,4]] * Z(2) ); <2x2-matrix over GF(2)> gap> Display(m); <2x2-matrix over GF(2): [[ Z(2)^0, 0*Z(2) ] [ Z(2)^0, 0*Z(2) ] ]> -gap> m = Matrix( IsFlatPlistMatrixRep, GF(2), [1,2,3,4] * Z(2), 2 ); +gap> m = Matrix( IsGenericMatrixRep, GF(2), [1,2,3,4] * Z(2), 2 ); true # diff --git a/tst/testinstall/MatrixObj/ZeroMatrix.tst b/tst/testinstall/MatrixObj/ZeroMatrix.tst index 8a5c86c4b9..a21962bfcf 100644 --- a/tst/testinstall/MatrixObj/ZeroMatrix.tst +++ b/tst/testinstall/MatrixObj/ZeroMatrix.tst @@ -80,21 +80,21 @@ gap> TestZeroMatrix(IsPlistMatrixRep, Integers mod 4, 0, 3); <0x3-matrix over (Integers mod 4)> # -# IsFlatPlistMatrixRep +# IsGenericMatrixRep # -gap> TestZeroMatrix(IsFlatPlistMatrixRep, GF(2), 2, 3); +gap> TestZeroMatrix(IsGenericMatrixRep, GF(2), 2, 3); <2x3-matrix over GF(2)> -gap> TestZeroMatrix(IsFlatPlistMatrixRep, GF(2), 2, 0); +gap> TestZeroMatrix(IsGenericMatrixRep, GF(2), 2, 0); <2x0-matrix over GF(2)> -gap> TestZeroMatrix(IsFlatPlistMatrixRep, GF(2), 0, 3); +gap> TestZeroMatrix(IsGenericMatrixRep, GF(2), 0, 3); <0x3-matrix over GF(2)> # -gap> TestZeroMatrix(IsFlatPlistMatrixRep, Integers, 2, 3); +gap> TestZeroMatrix(IsGenericMatrixRep, Integers, 2, 3); <2x3-matrix over Integers> -gap> TestZeroMatrix(IsFlatPlistMatrixRep, Integers, 2, 0); +gap> TestZeroMatrix(IsGenericMatrixRep, Integers, 2, 0); <2x0-matrix over Integers> -gap> TestZeroMatrix(IsFlatPlistMatrixRep, Integers, 0, 3); +gap> TestZeroMatrix(IsGenericMatrixRep, Integers, 0, 3); <0x3-matrix over Integers> # diff --git a/tst/testinstall/MatrixObj/matobjflatplist.tst b/tst/testinstall/MatrixObj/matobjflatplist.tst index 6454cd6d8b..19d24c17d6 100644 --- a/tst/testinstall/MatrixObj/matobjflatplist.tst +++ b/tst/testinstall/MatrixObj/matobjflatplist.tst @@ -14,33 +14,33 @@ gap> w:= MakeIsPlistVectorRep( Rationals, [ 1 ], true ); # -gap> MakeIsFlatPlistMatrixRep( Integers, 2, [ v ], true ); +gap> MakeIsGenericMatrixRep( Integers, 2, [ v ], true ); Error, the entries of must be plain lists -gap> M:= MakeIsFlatPlistMatrixRep( Integers, 2, [], true ); +gap> M:= MakeIsGenericMatrixRep( Integers, 2, [], true ); <0x2-matrix over Integers> -gap> MakeIsFlatPlistMatrixRep( Integers, 1, [ [ 1 ] ], true ); +gap> MakeIsGenericMatrixRep( Integers, 1, [ [ 1 ] ], true ); <1x1-matrix over Integers> -gap> MakeIsFlatPlistMatrixRep( Integers, 2, [ [ 1 ] ], true ); +gap> MakeIsGenericMatrixRep( Integers, 2, [ [ 1 ] ], true ); Error, the entries of must have length -gap> MakeIsFlatPlistMatrixRep( Integers, 1, [ [ 1/2 ] ], true ); +gap> MakeIsGenericMatrixRep( Integers, 1, [ [ 1/2 ] ], true ); Error, the elements in must lie in -gap> MakeIsFlatPlistMatrixRep( GF(2), 1, [ [ Z(2) ] ], true ); +gap> MakeIsGenericMatrixRep( GF(2), 1, [ [ Z(2) ] ], true ); <1x1-matrix over GF(2)> -gap> MakeIsFlatPlistMatrixRep( GF(2), 1, [ [ Z(4) ] ], true ); +gap> MakeIsGenericMatrixRep( GF(2), 1, [ [ Z(4) ] ], true ); Error, the elements in must lie in # -gap> NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [] ); +gap> NewMatrix( IsGenericMatrixRep, Integers, 2, [] ); <0x2-matrix over Integers> -gap> NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [ 1 ] ); +gap> NewMatrix( IsGenericMatrixRep, Integers, 2, [ 1 ] ); Error, NewMatrix: Length of is not a multiple of -gap> NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [ [ 1 ] ] ); +gap> NewMatrix( IsGenericMatrixRep, Integers, 2, [ [ 1 ] ] ); Error, the entries of must have length -gap> NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [ [ 1, 2 ] ] ); +gap> NewMatrix( IsGenericMatrixRep, Integers, 2, [ [ 1, 2 ] ] ); <1x2-matrix over Integers> -gap> NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [ v ] ); +gap> NewMatrix( IsGenericMatrixRep, Integers, 2, [ v ] ); Error, the entries of must have length -gap> M:= NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [ [ 1, 2 ], [ 3, 4 ] ] ); +gap> M:= NewMatrix( IsGenericMatrixRep, Integers, 2, [ [ 1, 2 ], [ 3, 4 ] ] ); <2x2-matrix over Integers> gap> IsMutable( M ); true @@ -69,7 +69,7 @@ gap> rows[1][1]; 1 # -gap> M:= NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [ v2, v2 ] );; +gap> M:= NewMatrix( IsGenericMatrixRep, Integers, 2, [ v2, v2 ] );; gap> Unpack( M ); [ [ 1, 0 ], [ 1, 0 ] ] @@ -77,15 +77,15 @@ gap> Unpack( M ); # Empty matrices and empty vectors # # -gap> a:= NewZeroMatrix( IsFlatPlistMatrixRep, Integers, 2, 0 ); +gap> a:= NewZeroMatrix( IsGenericMatrixRep, Integers, 2, 0 ); <2x0-matrix over Integers> -gap> b:= NewZeroMatrix( IsFlatPlistMatrixRep, Integers, 0, 3 ); +gap> b:= NewZeroMatrix( IsGenericMatrixRep, Integers, 0, 3 ); <0x3-matrix over Integers> -gap> c:= NewZeroMatrix( IsFlatPlistMatrixRep, Integers, 0, 0 ); +gap> c:= NewZeroMatrix( IsGenericMatrixRep, Integers, 0, 0 ); <0x0-matrix over Integers> -gap> d:= NewZeroMatrix( IsFlatPlistMatrixRep, Integers, 0, 2 ); +gap> d:= NewZeroMatrix( IsGenericMatrixRep, Integers, 0, 2 ); <0x2-matrix over Integers> -gap> z:= NewZeroMatrix( IsFlatPlistMatrixRep, Integers, 2, 3 ); +gap> z:= NewZeroMatrix( IsGenericMatrixRep, Integers, 2, 3 ); <2x3-matrix over Integers> gap> IsMutable( z ); true @@ -93,18 +93,18 @@ gap> Unpack( z ); [ [ 0, 0, 0 ], [ 0, 0, 0 ] ] # -gap> M:= NewIdentityMatrix( IsFlatPlistMatrixRep, Integers, 2 );; +gap> M:= NewIdentityMatrix( IsGenericMatrixRep, Integers, 2 );; gap> Unpack( M ); [ [ 1, 0 ], [ 0, 1 ] ] # -gap> a = NewMatrix( IsFlatPlistMatrixRep, Integers, 0, [ [], [] ] ); +gap> a = NewMatrix( IsGenericMatrixRep, Integers, 0, [ [], [] ] ); true -gap> b = NewMatrix( IsFlatPlistMatrixRep, Integers, 3, [] ); +gap> b = NewMatrix( IsGenericMatrixRep, Integers, 3, [] ); true -gap> c = NewMatrix( IsFlatPlistMatrixRep, Integers, 0, [] ); +gap> c = NewMatrix( IsGenericMatrixRep, Integers, 0, [] ); true -gap> d = NewMatrix( IsFlatPlistMatrixRep, Integers, 2, [] ); +gap> d = NewMatrix( IsGenericMatrixRep, Integers, 2, [] ); true # @@ -160,7 +160,7 @@ gap> InverseSameMutability( c ); <0x0-matrix over Integers> # -gap> M:= NewMatrix( IsFlatPlistMatrixRep, Integers, 3, [ [ 0, 0, 2 ], [ 0, 0, 0 ] ] );; +gap> M:= NewMatrix( IsGenericMatrixRep, Integers, 3, [ [ 0, 0, 2 ], [ 0, 0, 0 ] ] );; gap> PositionNonZeroInRow( M, 1 ); 3 gap> PositionNonZeroInRow( M, 1, 2 ); @@ -169,7 +169,7 @@ gap> PositionNonZeroInRow( M, 2 ); 4 # -gap> M:= NewMatrix( IsFlatPlistMatrixRep, Integers, 3, +gap> M:= NewMatrix( IsGenericMatrixRep, Integers, 3, > [ [ 1, 2, 3 ], [ 4, 5, 6 ] ] );; gap> MultMatrixRowLeft( M, 1, -1 );; gap> Unpack( M ); diff --git a/tst/testinstall/MatrixObj/testmatobj.g b/tst/testinstall/MatrixObj/testmatobj.g index 33e1fd963f..5d1ac8de44 100644 --- a/tst/testinstall/MatrixObj/testmatobj.g +++ b/tst/testinstall/MatrixObj/testmatobj.g @@ -211,8 +211,8 @@ TestWholeMatrixTransforms := function(mat, scalar) basedomain := BaseDomain(mat); if IsPlistMatrixRep(mat) then src := NewMatrix(IsPlistMatrixRep, basedomain, NrCols(mat), src); - elif IsFlatPlistMatrixRep(mat) then - src := NewMatrix(IsFlatPlistMatrixRep, basedomain, NrCols(mat), src); + elif IsGenericMatrixRep(mat) then + src := NewMatrix(IsGenericMatrixRep, basedomain, NrCols(mat), src); elif Is8BitMatrixRep(mat) then ConvertToMatrixRep(src, basedomain); fi; From f74372412b2f6ea8ee5e2d30a9c975c6c906d1fb Mon Sep 17 00:00:00 2001 From: Max Horn Date: Thu, 16 Apr 2026 16:21:27 +0200 Subject: [PATCH 08/32] Rename files matobjflatplist -> matobjgeneric --- doc/ref/makedocreldata.g | 2 +- lib/{matobjflatplist.gd => matobjgeneric.gd} | 0 lib/{matobjflatplist.gi => matobjgeneric.gi} | 0 lib/read3.g | 2 +- lib/read5.g | 2 +- .../MatrixObj/{matobjflatplist.tst => matobjgeneric.tst} | 4 ++-- 6 files changed, 5 insertions(+), 5 deletions(-) rename lib/{matobjflatplist.gd => matobjgeneric.gd} (100%) rename lib/{matobjflatplist.gi => matobjgeneric.gi} (100%) rename tst/testinstall/MatrixObj/{matobjflatplist.tst => matobjgeneric.tst} (98%) diff --git a/doc/ref/makedocreldata.g b/doc/ref/makedocreldata.g index c31333eca0..22993e3497 100644 --- a/doc/ref/makedocreldata.g +++ b/doc/ref/makedocreldata.g @@ -119,7 +119,7 @@ GAPInfo.ManualDataRef:= rec( "../../lib/matobj2.gd", "../../lib/matobjnz.gd", "../../lib/matobjplist.gd", - "../../lib/matobjflatplist.gd", + "../../lib/matobjgeneric.gd", "../../lib/matrix.gd", "../../lib/meataxe.gi", "../../lib/memory.gd", diff --git a/lib/matobjflatplist.gd b/lib/matobjgeneric.gd similarity index 100% rename from lib/matobjflatplist.gd rename to lib/matobjgeneric.gd diff --git a/lib/matobjflatplist.gi b/lib/matobjgeneric.gi similarity index 100% rename from lib/matobjflatplist.gi rename to lib/matobjgeneric.gi diff --git a/lib/read3.g b/lib/read3.g index 7d345ae67e..b3fdbbc96a 100644 --- a/lib/read3.g +++ b/lib/read3.g @@ -107,7 +107,7 @@ ReadLib( "wordass.gd" ); ReadLib( "matobj2.gd" ); ReadLib( "matobjplist.gd" ); -ReadLib( "matobjflatplist.gd" ); +ReadLib( "matobjgeneric.gd" ); ReadLib( "matobjnz.gd" ); # files dealing with rewriting systems diff --git a/lib/read5.g b/lib/read5.g index 94a4132f8b..cb4a8365b1 100644 --- a/lib/read5.g +++ b/lib/read5.g @@ -114,7 +114,7 @@ ReadLib( "vecmat.gi" ); ReadLib( "vec8bit.gi" ); ReadLib( "mat8bit.gi" ); ReadLib( "matobjplist.gi" ); -ReadLib( "matobjflatplist.gi" ); +ReadLib( "matobjgeneric.gi" ); ReadLib( "matobjnz.gi" ); ReadLib( "meataxe.gi" ); ReadLib( "meatauto.gi" ); diff --git a/tst/testinstall/MatrixObj/matobjflatplist.tst b/tst/testinstall/MatrixObj/matobjgeneric.tst similarity index 98% rename from tst/testinstall/MatrixObj/matobjflatplist.tst rename to tst/testinstall/MatrixObj/matobjgeneric.tst index 19d24c17d6..7a79bf0146 100644 --- a/tst/testinstall/MatrixObj/matobjflatplist.tst +++ b/tst/testinstall/MatrixObj/matobjgeneric.tst @@ -1,5 +1,5 @@ #@local e, v, v2, w, M, z, rows, a, b, c, d, p, ev, n, ai, inv, zm, zs -gap> START_TEST( "matobjflatplist.tst" ); +gap> START_TEST( "matobjgeneric.tst" ); # # MakeIsPlistVectorRep: test input validation @@ -188,4 +188,4 @@ gap> Unpack( M ); [ [ 54, 66, 78 ], [ 23, 28, 33 ] ] # -gap> STOP_TEST( "matobjflatplist.tst" ); +gap> STOP_TEST( "matobjgeneric.tst" ); From d4b060271ab4735c144c1dda126cfbccc9f40011 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 17 Apr 2026 15:24:08 +0200 Subject: [PATCH 09/32] Inline InverseRowsForFlatPlistMatrix --- lib/matobjgeneric.gi | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index bb5a16486d..a7dddd1d03 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -13,24 +13,6 @@ # Dense matrix objects backed by plain lists of plain row lists. # -BindGlobal( "InverseRowsForFlatPlistMatrix", - function( M ) - local bd, rows; - - if NrRows( M ) <> NrCols( M ) then - return fail; - elif NrRows( M ) = 0 then - rows := []; - return rows; - fi; - - bd := BaseDomain( M ); - if IsFinite( bd ) and IsField( bd ) then - return INV_MAT_DEFAULT_MUTABLE( M![FROWSPOS] ); - fi; - return INV_MATRIX_MUTABLE( M![FROWSPOS] ); - end ); - BindGlobal( "MakeIsGenericMatrixRep", function( basedomain, ncols, list, check ) local fam, types, typ, row; @@ -252,13 +234,19 @@ InstallMethod( ZeroMutable, InstallMethod( InverseMutable, [ "IsGenericMatrixRep" ], function( M ) - local rows; + local bd, rows; - rows := InverseRowsForFlatPlistMatrix( M ); - if rows = fail then + bd := BaseDomain( M ); + if NrRows( M ) <> NrCols( M ) then return fail; + elif NrRows( M ) = 0 then + rows := []; + elif IsFinite( bd ) and IsField( bd ) then + rows := INV_MAT_DEFAULT_MUTABLE( M![FROWSPOS] ); + else + rows := INV_MATRIX_MUTABLE( M![FROWSPOS] ); fi; - return MakeIsGenericMatrixRep( BaseDomain( M ), NrCols( M ), rows, false ); + return MakeIsGenericMatrixRep( bd, NrCols( M ), rows, false ); end ); InstallMethod( \*, From 084a85f650ea4eaea8d3d8a99c28b7a7fe851985 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 17 Apr 2026 15:33:03 +0200 Subject: [PATCH 10/32] MakeIsGenericMatrixRep requires plist --- lib/matobjgeneric.gi | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index a7dddd1d03..5ec6f8f2a6 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -41,6 +41,9 @@ BindGlobal( "MakeIsGenericMatrixRep", fi; if check and ValueOption( "check" ) <> false then + if not IsPlistRep( list ) then + Error( " must be a plain list" ); + fi; for row in list do if not IsPlistRep( row ) then Error( "the entries of must be plain lists" ); From 96eed928c4c6dde1cbcfcb45baf45556ef74a691 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 17 Apr 2026 15:49:08 +0200 Subject: [PATCH 11/32] Tighten MakeIsGenericMatrixRep --- lib/matobjgeneric.gi | 27 +++++++++++++-------------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index 5ec6f8f2a6..ab276f37a9 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -19,21 +19,20 @@ BindGlobal( "MakeIsGenericMatrixRep", fam := CollectionsFamily( FamilyObj( basedomain ) ); if not IsBound( fam!.FlatPlistMatrixRepTypes ) then - fam!.FlatPlistMatrixRepTypes := [ - NewType( fam, IsGenericMatrixRep ), - NewType( fam, IsGenericMatrixRep and IsMutable ), - ]; - fam!.FlatPlistMatrixRepTypesEasyCompare := [ - NewType( fam, IsGenericMatrixRep and CanEasilyCompareElements ), - NewType( fam, IsGenericMatrixRep and CanEasilyCompareElements and IsMutable ), - ]; - fi; - if HasCanEasilyCompareElements( Representative( basedomain ) ) and - CanEasilyCompareElements( Representative( basedomain ) ) then - types := fam!.FlatPlistMatrixRepTypesEasyCompare; - else - types := fam!.FlatPlistMatrixRepTypes; + if HasCanEasilyCompareElements( Representative( basedomain ) ) and + CanEasilyCompareElements( Representative( basedomain ) ) then + fam!.FlatPlistMatrixRepTypes := [ + NewType( fam, IsGenericMatrixRep and CanEasilyCompareElements ), + NewType( fam, IsGenericMatrixRep and CanEasilyCompareElements and IsMutable ), + ]; + else + fam!.FlatPlistMatrixRepTypes := [ + NewType( fam, IsGenericMatrixRep ), + NewType( fam, IsGenericMatrixRep and IsMutable ), + ]; + fi; fi; + types := fam!.FlatPlistMatrixRepTypes; if IsMutable( list ) then typ := types[2]; else From a68d18f44eaffb76d4680db263325b8f41283bea Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 17 Apr 2026 17:46:01 +0200 Subject: [PATCH 12/32] Inverse should error, not return fail --- lib/matobjgeneric.gi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index ab276f37a9..5d8ef4b80f 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -240,7 +240,7 @@ InstallMethod( InverseMutable, bd := BaseDomain( M ); if NrRows( M ) <> NrCols( M ) then - return fail; + ErrorNoReturn( "InverseMutable: matrix must be square" ); elif NrRows( M ) = 0 then rows := []; elif IsFinite( bd ) and IsField( bd ) then From 2bf59b78f87f5114e94424bae8914f454426f858 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 17 Apr 2026 17:45:50 +0200 Subject: [PATCH 13/32] Generalize vec*mat and mat*vec --- lib/matobjgeneric.gi | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index 5d8ef4b80f..f28e7a0eb5 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -286,8 +286,9 @@ InstallMethod( \*, return MakeIsGenericMatrixRep( bd, colsB, list, false ); end ); -InstallMethod( \*, - [ "IsGenericMatrixRep", "IsVectorObj" ], +InstallOtherMethod( \*, + [ "IsGenericMatrixRep", "IsRowVectorOrVectorObj" ], + {} -> RankFilter(IsPlistVectorRep), # rank above method for [IsScalar, IsPlistVectorRep] function( M, v ) local rows, cols, bd, res; @@ -320,8 +321,9 @@ InstallMethod( \*, return Vector( res, v ); end ); -InstallMethod( \*, - [ "IsVectorObj", "IsGenericMatrixRep" ], +InstallOtherMethod( \*, + [ "IsRowVectorOrVectorObj", "IsGenericMatrixRep" ], + {} -> RankFilter(IsPlistVectorRep), # rank above method for [IsPlistVectorRep, IsScalar] function( v, M ) local rows, cols, bd, res; From ed1e34b7cf05f2e9da18fa7087fbc8dd272dde7e Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 17 Apr 2026 17:45:50 +0200 Subject: [PATCH 14/32] More tests --- tst/testinstall/MatrixObj/matobjgeneric.tst | 125 ++++++++++++++++++-- 1 file changed, 116 insertions(+), 9 deletions(-) diff --git a/tst/testinstall/MatrixObj/matobjgeneric.tst b/tst/testinstall/MatrixObj/matobjgeneric.tst index 7a79bf0146..2e2924f7a5 100644 --- a/tst/testinstall/MatrixObj/matobjgeneric.tst +++ b/tst/testinstall/MatrixObj/matobjgeneric.tst @@ -1,4 +1,4 @@ -#@local e, v, v2, w, M, z, rows, a, b, c, d, p, ev, n, ai, inv, zm, zs +#@local e, v, v2, w, M, z, rows, a, b, c, d, p, ev, n, ai, inv, zm, zs, N, T, lp, lp0, s gap> START_TEST( "matobjgeneric.tst" ); # @@ -77,15 +77,15 @@ gap> Unpack( M ); # Empty matrices and empty vectors # # -gap> a:= NewZeroMatrix( IsGenericMatrixRep, Integers, 2, 0 ); +gap> a:= ZeroMatrix( IsGenericMatrixRep, Integers, 2, 0 ); <2x0-matrix over Integers> -gap> b:= NewZeroMatrix( IsGenericMatrixRep, Integers, 0, 3 ); +gap> b:= ZeroMatrix( IsGenericMatrixRep, Integers, 0, 3 ); <0x3-matrix over Integers> -gap> c:= NewZeroMatrix( IsGenericMatrixRep, Integers, 0, 0 ); +gap> c:= ZeroMatrix( IsGenericMatrixRep, Integers, 0, 0 ); <0x0-matrix over Integers> -gap> d:= NewZeroMatrix( IsGenericMatrixRep, Integers, 0, 2 ); +gap> d:= ZeroMatrix( IsGenericMatrixRep, Integers, 0, 2 ); <0x2-matrix over Integers> -gap> z:= NewZeroMatrix( IsGenericMatrixRep, Integers, 2, 3 ); +gap> z:= ZeroMatrix( IsGenericMatrixRep, Integers, 2, 3 ); <2x3-matrix over Integers> gap> IsMutable( z ); true @@ -160,7 +160,7 @@ gap> InverseSameMutability( c ); <0x0-matrix over Integers> # -gap> M:= NewMatrix( IsGenericMatrixRep, Integers, 3, [ [ 0, 0, 2 ], [ 0, 0, 0 ] ] );; +gap> M:= Matrix( IsGenericMatrixRep, Integers, [ [ 0, 0, 2 ], [ 0, 0, 0 ] ] );; gap> PositionNonZeroInRow( M, 1 ); 3 gap> PositionNonZeroInRow( M, 1, 2 ); @@ -169,8 +169,7 @@ gap> PositionNonZeroInRow( M, 2 ); 4 # -gap> M:= NewMatrix( IsGenericMatrixRep, Integers, 3, -> [ [ 1, 2, 3 ], [ 4, 5, 6 ] ] );; +gap> M:= Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2, 3 ], [ 4, 5, 6 ] ] );; gap> MultMatrixRowLeft( M, 1, -1 );; gap> Unpack( M ); [ [ -1, -2, -3 ], [ 4, 5, 6 ] ] @@ -187,5 +186,113 @@ gap> SwapMatrixRows( M, 1, 2 );; gap> Unpack( M ); [ [ 54, 66, 78 ], [ 23, 28, 33 ] ] +# +# Test Inverse / InverseMutable +# +gap> M:= Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ] );; +gap> InverseMutable( M ); +Error, InverseMutable: matrix must be square +gap> M:= Matrix( IsGenericMatrixRep, GF(2), [ [ Z(2)^0, Z(2)^0 ], [ Z(2)^0, 0*Z(2) ] ] );; +gap> Display( InverseMutable( M ) ); +<2x2-matrix over GF(2): +[[ 0*Z(2), Z(2)^0 ] + [ Z(2)^0, Z(2)^0 ] +]> +gap> M:= Matrix( IsGenericMatrixRep, Rationals, [ [ 1, 2 ], [ 3, 5 ] ] );; +gap> Display( InverseMutable( M ) ); +<2x2-matrix over Rationals: +[[ -5, 2 ] + [ 3, -1 ] +]> + +# +gap> M:= Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ] ] );; +gap> M[1,1] := 1/2; +Error, must lie in the base domain of +gap> M[3,1] := 1; +Error, is out of bounds +gap> M[1,3] := 1; +Error, is out of bounds + +# +gap> a:= Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ] ] );; +gap> b:= Matrix( IsGenericMatrixRep, Rationals, [ [ 1, 2 ] ] );; +gap> a + b; +Error, and are not compatible +gap> a - b; +Error, and are not compatible +gap> c:= Matrix( IsGenericMatrixRep, Integers, [ [ 1 ], [ 2 ] ] );; +gap> c * c; +Error, \*: Matrices do not fit together +gap> d:= Matrix( IsGenericMatrixRep, Rationals, [ [ 1 ], [ 2 ] ] );; +gap> a * d; +Error, \*: Matrices not over same base domain + +# +gap> M:= Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ] ] );; +gap> N:= ShallowCopy( M );; +gap> N[1,1] := 99;; +gap> Unpack( M ); +[ [ 1, 2 ], [ 3, 4 ] ] +gap> Unpack( N ); +[ [ 99, 2 ], [ 3, 4 ] ] +gap> N:= MutableCopyMatrix( M );; +gap> N[1,2] := 77;; +gap> Unpack( M ); +[ [ 1, 2 ], [ 3, 4 ] ] +gap> Unpack( N ); +[ [ 1, 77 ], [ 3, 4 ] ] +gap> Unpack( ExtractSubMatrix( M, [ 2, 1 ], [ 2 ] ) ); +[ [ 4 ], [ 2 ] ] +gap> T:= ZeroMatrix( IsGenericMatrixRep, Integers, 2, 3 );; +gap> CopySubMatrix( M, T, [ 2, 1 ], [ 1, 2 ], [ 2 ], [ 3 ] );; +gap> Unpack( T ); +[ [ 0, 0, 4 ], [ 0, 0, 2 ] ] +gap> CopySubMatrix( Matrix( IsGenericMatrixRep, Rationals, [ [ 1, 2 ] ] ), +> T, [ 1 ], [ 1 ], [ 1 ], [ 1 ] ); +Error, and are not compatible +gap> Unpack( TransposedMatMutable( M ) ); +[ [ 1, 3 ], [ 2, 4 ] ] +gap> N:= ChangedBaseDomain( M, Rationals );; +gap> BaseDomain( N ); +Rationals +gap> Unpack( N ); +[ [ 1, 2 ], [ 3, 4 ] ] +gap> IsMutable( N ); +true +gap> MakeImmutable( M );; +gap> N:= ChangedBaseDomain( M, Rationals );; +gap> IsMutable( N ); +false +gap> String( Matrix( IsGenericMatrixRep, GF(2), [ [ Z(2)^0, 0*Z(2) ] ] ) ); +"NewMatrix(IsGenericMatrixRep,GF(2),2,[ [ Z(2)^0, 0*Z(2) ] ])" +gap> Print( Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ] ] ), "\n" ); +NewMatrix(IsGenericMatrixRep,Integers,2,[ [ 1, 2 ], [ 3, 4 ] ]) + +# +gap> M:= Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ] ] );; +gap> v:= NewVector( IsPlistVectorRep, Integers, [ 1, 2 ] );; +gap> Unpack( M * v ); +[ 5, 11 ] +gap> Unpack( v * M ); +[ 7, 10 ] +gap> lp:= [ 1, 2 ];; +gap> M * lp; +[ 5, 11 ] +gap> lp * M; +[ 7, 10 ] +gap> a:= ZeroMatrix( IsGenericMatrixRep, Integers, 2, 0 );; +gap> b:= ZeroMatrix( IsGenericMatrixRep, Integers, 0, 3 );; + +# multiplication with empty list is not well-defined: we don't know if +# is meant to be a vector of length 0, or something else; so rather +# error out +gap> a * []; +Error, no method found! For debugging hints type ?Recovery from NoMethodFound +Error, no 1st choice method found for `BaseDomain' on 1 arguments +gap> [] * b; +Error, no method found! For debugging hints type ?Recovery from NoMethodFound +Error, no 1st choice method found for `BaseDomain' on 1 arguments + # gap> STOP_TEST( "matobjgeneric.tst" ); From c8f6e276b70b288df2909c5250c1aa8bb580a540 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 17 Apr 2026 20:42:40 +0200 Subject: [PATCH 15/32] Unify matobj family code --- lib/matobjgeneric.gi | 16 +++++++++------- lib/matobjnz.gi | 2 +- lib/matobjplist.gi | 35 ++++++++++++++++------------------- 3 files changed, 26 insertions(+), 27 deletions(-) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index f28e7a0eb5..ba9590400e 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -15,12 +15,15 @@ BindGlobal( "MakeIsGenericMatrixRep", function( basedomain, ncols, list, check ) - local fam, types, typ, row; + local fam, typ, row; + fam := CollectionsFamily( ElementsFamily( FamilyObj( basedomain ) ) ); - fam := CollectionsFamily( FamilyObj( basedomain ) ); + # Currently there is no special handling depending on 'basedomain', + # the types are always cached in 'fam'. if not IsBound( fam!.FlatPlistMatrixRepTypes ) then - if HasCanEasilyCompareElements( Representative( basedomain ) ) and - CanEasilyCompareElements( Representative( basedomain ) ) then + # initialize type cache + # TODO: make this thread safe for HPC-GAP + if CanEasilyCompareElementsFamily( fam ) then fam!.FlatPlistMatrixRepTypes := [ NewType( fam, IsGenericMatrixRep and CanEasilyCompareElements ), NewType( fam, IsGenericMatrixRep and CanEasilyCompareElements and IsMutable ), @@ -32,11 +35,10 @@ BindGlobal( "MakeIsGenericMatrixRep", ]; fi; fi; - types := fam!.FlatPlistMatrixRepTypes; if IsMutable( list ) then - typ := types[2]; + typ := fam!.FlatPlistMatrixRepTypes[2]; else - typ := types[1]; + typ := fam!.FlatPlistMatrixRepTypes[1]; fi; if check and ValueOption( "check" ) <> false then diff --git a/lib/matobjnz.gi b/lib/matobjnz.gi index c2edf17d3a..2bb5a476a5 100644 --- a/lib/matobjnz.gi +++ b/lib/matobjnz.gi @@ -669,7 +669,7 @@ InstallTagBasedMethod( NewZeroMatrix, m[i] := ZeroVector( cols, e ); od; m := [basedomain,e,cols,m]; - Objectify( NewType(CollectionsFamily(FamilyObj(basedomain)), + Objectify( NewType(CollectionsFamily(ElementsFamily(FamilyObj(basedomain))), filter and IsMutable), m ); return m; end ); diff --git a/lib/matobjplist.gi b/lib/matobjplist.gi index c8b2a3ecbe..4cd2c4cbe4 100644 --- a/lib/matobjplist.gi +++ b/lib/matobjplist.gi @@ -123,32 +123,29 @@ BindGlobal( "MakeIsPlistVectorRep", BindGlobal( "MakeIsPlistMatrixRep", function( basedomain, emptyvector, ncols, list, check ) local fam, types, typ, row; - fam:= CollectionsFamily( FamilyObj( basedomain ) ); + fam:= CollectionsFamily( ElementsFamily( FamilyObj( basedomain ) ) ); # Currently there is no special handling depending on 'basedomain', # the types are always cached in 'fam'. if not IsBound( fam!.PlistMatrixRepTypes ) then # initialize type cache # TODO: make this thread safe for HPC-GAP - fam!.PlistMatrixRepTypes:= [ - NewType( fam, IsPlistMatrixRep ), - NewType( fam, IsPlistMatrixRep and IsMutable ), - ]; - fam!.PlistMatrixRepTypesEasyCompare:= [ - NewType( fam, IsPlistMatrixRep and CanEasilyCompareElements ), - NewType( fam, IsPlistMatrixRep and CanEasilyCompareElements and IsMutable ), - ]; - fi; - if HasCanEasilyCompareElements( Representative( basedomain ) ) and - CanEasilyCompareElements( Representative( basedomain ) ) then - types:= fam!.PlistMatrixRepTypesEasyCompare; - else - types:= fam!.PlistMatrixRepTypes; + if CanEasilyCompareElementsFamily( fam ) then + fam!.PlistMatrixRepTypes:= [ + NewType( fam, IsPlistMatrixRep and CanEasilyCompareElements ), + NewType( fam, IsPlistMatrixRep and CanEasilyCompareElements and IsMutable ), + ]; + else + fam!.PlistMatrixRepTypes:= [ + NewType( fam, IsPlistMatrixRep ), + NewType( fam, IsPlistMatrixRep and IsMutable ), + ]; + fi; fi; if IsMutable( list ) then - typ:= types[2]; + typ:= fam!.PlistMatrixRepTypes[2]; else - typ:= types[1]; + typ:= fam!.PlistMatrixRepTypes[1]; fi; if check and ValueOption( "check" ) <> false then @@ -160,10 +157,10 @@ BindGlobal( "MakeIsPlistMatrixRep", for row in list do if not IsPlistVectorRep( row ) then Error( "the entries of must be in 'IsPlistVectorRep'" ); - elif not IsIdenticalObj( basedomain, row![BDPOS] ) then - Error( "the entries of must have the given base domain" ); elif Length( row![ELSPOS] ) <> ncols then Error( "the entries of must have length " ); + elif not IsIdenticalObj( basedomain, row![BDPOS] ) then + Error( "the entries of must have the given base domain" ); fi; od; fi; From b7d5528d804f5ba9728caf0186d04f20876ffb80 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 17 Apr 2026 21:01:56 +0200 Subject: [PATCH 16/32] More tests --- tst/testinstall/MatrixObj/matobjgeneric.tst | 106 ++++++++++++++++++-- 1 file changed, 96 insertions(+), 10 deletions(-) diff --git a/tst/testinstall/MatrixObj/matobjgeneric.tst b/tst/testinstall/MatrixObj/matobjgeneric.tst index 2e2924f7a5..7ed965513e 100644 --- a/tst/testinstall/MatrixObj/matobjgeneric.tst +++ b/tst/testinstall/MatrixObj/matobjgeneric.tst @@ -193,11 +193,24 @@ gap> M:= Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ] gap> InverseMutable( M ); Error, InverseMutable: matrix must be square gap> M:= Matrix( IsGenericMatrixRep, GF(2), [ [ Z(2)^0, Z(2)^0 ], [ Z(2)^0, 0*Z(2) ] ] );; -gap> Display( InverseMutable( M ) ); +gap> N:= InverseMutable( M );; +gap> Display( N ); <2x2-matrix over GF(2): [[ 0*Z(2), Z(2)^0 ] [ Z(2)^0, Z(2)^0 ] ]> +gap> IsOne( N * M ); +true +gap> IsOne( M * N ); +true +gap> IsMutable( M * N ); +true +gap> MakeImmutable(M);; +gap> IsMutable( M * N ); +true +gap> MakeImmutable(N);; +gap> IsMutable( M * N ); +false gap> M:= Matrix( IsGenericMatrixRep, Rationals, [ [ 1, 2 ], [ 3, 5 ] ] );; gap> Display( InverseMutable( M ) ); <2x2-matrix over Rationals: @@ -228,6 +241,8 @@ gap> d:= Matrix( IsGenericMatrixRep, Rationals, [ [ 1 ], [ 2 ] ] );; gap> a * d; Error, \*: Matrices not over same base domain +# +# ShallowCopy, MutableCopyMatrix # gap> M:= Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ] ] );; gap> N:= ShallowCopy( M );; @@ -242,6 +257,10 @@ gap> Unpack( M ); [ [ 1, 2 ], [ 3, 4 ] ] gap> Unpack( N ); [ [ 1, 77 ], [ 3, 4 ] ] + +# +# ExtractSubMatrix, CopySubMatrix +# gap> Unpack( ExtractSubMatrix( M, [ 2, 1 ], [ 2 ] ) ); [ [ 4 ], [ 2 ] ] gap> T:= ZeroMatrix( IsGenericMatrixRep, Integers, 2, 3 );; @@ -251,8 +270,16 @@ gap> Unpack( T ); gap> CopySubMatrix( Matrix( IsGenericMatrixRep, Rationals, [ [ 1, 2 ] ] ), > T, [ 1 ], [ 1 ], [ 1 ], [ 1 ] ); Error, and are not compatible + +# +# TransposedMatMutable +# gap> Unpack( TransposedMatMutable( M ) ); [ [ 1, 3 ], [ 2, 4 ] ] + +# +# ChangedBaseDomain +# gap> N:= ChangedBaseDomain( M, Rationals );; gap> BaseDomain( N ); Rationals @@ -264,29 +291,88 @@ gap> MakeImmutable( M );; gap> N:= ChangedBaseDomain( M, Rationals );; gap> IsMutable( N ); false -gap> String( Matrix( IsGenericMatrixRep, GF(2), [ [ Z(2)^0, 0*Z(2) ] ] ) ); + +# +# ViewObj, PrintObj, Display, String +# +gap> M := Matrix( IsGenericMatrixRep, GF(2), [ [ Z(2)^0, 0*Z(2) ] ] ); +<1x2-matrix over GF(2)> +gap> Print(M, "\n"); +NewMatrix(IsGenericMatrixRep,GF(2),2,[ [ Z(2)^0, 0*Z(2) ] ]) +gap> Display(M); +<1x2-matrix over GF(2): +[[ Z(2)^0, 0*Z(2) ] +]> +gap> String(M); +"NewMatrix(IsGenericMatrixRep,GF(2),2,[ [ Z(2)^0, 0*Z(2) ] ])" + +# +gap> MakeImmutable( M ); + +gap> Print(M, "\n"); +NewMatrix(IsGenericMatrixRep,GF(2),2,[ [ Z(2)^0, 0*Z(2) ] ]) +gap> Display(M); + +gap> String(M); "NewMatrix(IsGenericMatrixRep,GF(2),2,[ [ Z(2)^0, 0*Z(2) ] ])" -gap> Print( Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ] ] ), "\n" ); + +# +gap> M :=Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ] ] ); +<2x2-matrix over Integers> +gap> Print(M, "\n"); NewMatrix(IsGenericMatrixRep,Integers,2,[ [ 1, 2 ], [ 3, 4 ] ]) +gap> Display(M); +<2x2-matrix over Integers: +[[ 1, 2 ] + [ 3, 4 ] +]> +gap> String(M); +"NewMatrix(IsGenericMatrixRep,Integers,2,[ [ 1, 2 ], [ 3, 4 ] ])" +# +gap> MakeImmutable( M ); + +gap> Print(M, "\n"); +NewMatrix(IsGenericMatrixRep,Integers,2,[ [ 1, 2 ], [ 3, 4 ] ]) +gap> Display(M); + +gap> String(M); +"NewMatrix(IsGenericMatrixRep,Integers,2,[ [ 1, 2 ], [ 3, 4 ] ])" + +# +# vec * mat, mat * vec # gap> M:= Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ] ] );; gap> v:= NewVector( IsPlistVectorRep, Integers, [ 1, 2 ] );; -gap> Unpack( M * v ); +gap> Display( M * v ); + Unpack( v * M ); +> +gap> Display( v * M ); + lp:= [ 1, 2 ];; -gap> M * lp; +> +gap> M * [ 1, 2 ]; [ 5, 11 ] -gap> lp * M; +gap> [ 1, 2 ] * M; [ 7, 10 ] -gap> a:= ZeroMatrix( IsGenericMatrixRep, Integers, 2, 0 );; -gap> b:= ZeroMatrix( IsGenericMatrixRep, Integers, 0, 3 );; + +# error handling +gap> M * [ 1, 2, 3 ]; +Error, and are not compatible +gap> [ 1, 2, 3 ] * M; +Error, and are not compatible # multiplication with empty list is not well-defined: we don't know if # is meant to be a vector of length 0, or something else; so rather # error out +gap> a:= ZeroMatrix( IsGenericMatrixRep, Integers, 2, 0 );; +gap> b:= ZeroMatrix( IsGenericMatrixRep, Integers, 0, 3 );; gap> a * []; Error, no method found! For debugging hints type ?Recovery from NoMethodFound Error, no 1st choice method found for `BaseDomain' on 1 arguments From fe073074f7cab89d153f79d18429b06b9dd22ce0 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 17 Apr 2026 21:08:18 +0200 Subject: [PATCH 17/32] Simplify code --- lib/matobjgeneric.gi | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index ba9590400e..0ba4bb3de6 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -282,9 +282,6 @@ InstallMethod( \*, else list := a![FROWSPOS] * b![FROWSPOS]; fi; - if not IsMutable( a ) and not IsMutable( b ) then - MakeImmutable( list ); - fi; return MakeIsGenericMatrixRep( bd, colsB, list, false ); end ); @@ -361,12 +358,7 @@ InstallOtherMethod( \*, InstallMethod( ChangedBaseDomain, [ "IsGenericMatrixRep", "IsRing" ], function( M, r ) - local n; - n := NewMatrix( IsGenericMatrixRep, r, NrCols(M), M![FROWSPOS] ); - if not IsMutable( M ) then - MakeImmutable( n ); - fi; - return n; + return NewMatrix( IsGenericMatrixRep, r, NrCols(M), M![FROWSPOS] ); end ); From fa15a91e39bacb57036c445ef60966f90313aeec Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 17 Apr 2026 21:27:12 +0200 Subject: [PATCH 18/32] Yet more filter/family code simplification --- lib/matobjgeneric.gi | 22 ++-- lib/matobjplist.gi | 107 +++++++++----------- tst/testinstall/MatrixObj/matobjgeneric.tst | 65 ++++++++++-- tst/testinstall/MatrixObj/matobjplist.tst | 74 ++++++++++++-- 4 files changed, 179 insertions(+), 89 deletions(-) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index 0ba4bb3de6..794b723572 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -15,25 +15,23 @@ BindGlobal( "MakeIsGenericMatrixRep", function( basedomain, ncols, list, check ) - local fam, typ, row; - fam := CollectionsFamily( ElementsFamily( FamilyObj( basedomain ) ) ); + local efam, fam, filter, typ, row; + efam := ElementsFamily( FamilyObj( basedomain ) ); + fam := CollectionsFamily( FamilyObj( basedomain ) ); # Currently there is no special handling depending on 'basedomain', # the types are always cached in 'fam'. if not IsBound( fam!.FlatPlistMatrixRepTypes ) then # initialize type cache # TODO: make this thread safe for HPC-GAP - if CanEasilyCompareElementsFamily( fam ) then - fam!.FlatPlistMatrixRepTypes := [ - NewType( fam, IsGenericMatrixRep and CanEasilyCompareElements ), - NewType( fam, IsGenericMatrixRep and CanEasilyCompareElements and IsMutable ), - ]; - else - fam!.FlatPlistMatrixRepTypes := [ - NewType( fam, IsGenericMatrixRep ), - NewType( fam, IsGenericMatrixRep and IsMutable ), - ]; + filter := IsGenericMatrixRep; + if CanEasilyCompareElementsFamily( efam ) then + filter := filter and CanEasilyCompareElements; fi; + fam!.FlatPlistMatrixRepTypes := [ + NewType( fam, filter ), + NewType( fam, filter and IsMutable ), + ]; fi; if IsMutable( list ) then typ := fam!.FlatPlistMatrixRepTypes[2]; diff --git a/lib/matobjplist.gi b/lib/matobjplist.gi index 4cd2c4cbe4..17d3cb9ac7 100644 --- a/lib/matobjplist.gi +++ b/lib/matobjplist.gi @@ -34,55 +34,43 @@ ## BindGlobal( "MakeIsPlistVectorRep", function( basedomain, list, check ) - local fam, types, typ; - fam := FamilyObj(basedomain); - #types := _PlistVectorRepTypeCache(basedomain); - - # special case: integers - if IsIntegers(basedomain) then - if not IsBound(basedomain!.PlistVectorRepTypes) then - # initialize type cache - # TODO: make this thread safe for HPC-GAP - basedomain!.PlistVectorRepTypes := [ - NewType(fam, IsPlistVectorRep and IsIntVector and CanEasilyCompareElements), - NewType(fam, IsPlistVectorRep and IsIntVector and CanEasilyCompareElements and IsMutable), - ]; - fi; - types := basedomain!.PlistVectorRepTypes; - elif IsFFECollection(basedomain) then - if not IsBound(basedomain!.PlistVectorRepTypes) then - # initialize type cache - # TODO: make this thread safe for HPC-GAP - basedomain!.PlistVectorRepTypes := [ - NewType(fam, IsPlistVectorRep and IsFFEVector and CanEasilyCompareElements), - NewType(fam, IsPlistVectorRep and IsFFEVector and CanEasilyCompareElements and IsMutable), - ]; - fi; - types := basedomain!.PlistVectorRepTypes; + local efam, fam, filter, types, typ; + + efam := ElementsFamily( FamilyObj( basedomain ) ); + fam := FamilyObj( basedomain ); + + # we store the types in the base domain if the filter carries + # information specific to it + if IsBound( basedomain!.PlistVectorRepTypes ) then + types := basedomain!.PlistVectorRepTypes; + elif IsBound( fam!.PlistVectorRepTypes ) and not IsIntegers(basedomain) then + types := fam!.PlistVectorRepTypes; else - if not IsBound(fam!.PlistVectorRepTypes) then - # initialize type cache - # TODO: make this thread safe for HPC-GAP - fam!.PlistVectorRepTypes := [ - NewType(fam, IsPlistVectorRep), - NewType(fam, IsPlistVectorRep and IsMutable), - ]; - fam!.PlistVectorRepTypesEasyCompare := [ - NewType(fam, IsPlistVectorRep and CanEasilyCompareElements), - NewType(fam, IsPlistVectorRep and CanEasilyCompareElements and IsMutable), - ]; - fi; - if HasCanEasilyCompareElements(Representative(basedomain)) and - CanEasilyCompareElements(Representative(basedomain)) then - types := fam!.PlistVectorRepTypesEasyCompare; - else - types := fam!.PlistVectorRepTypes; - fi; + # initialize type cache + # TODO: make this thread safe for HPC-GAP + filter := IsPlistVectorRep; + if CanEasilyCompareElementsFamily( efam ) then + filter := filter and CanEasilyCompareElements; + fi; + if IsIntegers(basedomain) then + filter := filter and IsIntVector; + elif IsFFECollection(basedomain) then + filter := filter and IsFFEVector; + fi; + types := [ + NewType( fam, filter ), + NewType( fam, filter and IsMutable ), + ]; + if IsIntegers(basedomain) then + basedomain!.PlistVectorRepTypes := types; + else + fam!.PlistVectorRepTypes := types; + fi; fi; - if IsMutable(list) then - typ := types[2]; + if IsMutable( list ) then + typ := types[2]; else - typ := types[1]; + typ := types[1]; fi; if check and ValueOption( "check" ) <> false then @@ -122,30 +110,29 @@ BindGlobal( "MakeIsPlistVectorRep", ## BindGlobal( "MakeIsPlistMatrixRep", function( basedomain, emptyvector, ncols, list, check ) - local fam, types, typ, row; - fam:= CollectionsFamily( ElementsFamily( FamilyObj( basedomain ) ) ); + local efam, fam, filter, typ, row; + + efam := ElementsFamily( FamilyObj( basedomain ) ); + fam := CollectionsFamily( FamilyObj( basedomain ) ); # Currently there is no special handling depending on 'basedomain', # the types are always cached in 'fam'. if not IsBound( fam!.PlistMatrixRepTypes ) then # initialize type cache # TODO: make this thread safe for HPC-GAP - if CanEasilyCompareElementsFamily( fam ) then - fam!.PlistMatrixRepTypes:= [ - NewType( fam, IsPlistMatrixRep and CanEasilyCompareElements ), - NewType( fam, IsPlistMatrixRep and CanEasilyCompareElements and IsMutable ), - ]; - else - fam!.PlistMatrixRepTypes:= [ - NewType( fam, IsPlistMatrixRep ), - NewType( fam, IsPlistMatrixRep and IsMutable ), - ]; + filter := IsPlistMatrixRep; + if CanEasilyCompareElementsFamily( efam ) then + filter := filter and CanEasilyCompareElements; fi; + fam!.PlistMatrixRepTypes := [ + NewType( fam, filter ), + NewType( fam, filter and IsMutable ), + ]; fi; if IsMutable( list ) then - typ:= fam!.PlistMatrixRepTypes[2]; + typ := fam!.PlistMatrixRepTypes[2]; else - typ:= fam!.PlistMatrixRepTypes[1]; + typ := fam!.PlistMatrixRepTypes[1]; fi; if check and ValueOption( "check" ) <> false then diff --git a/tst/testinstall/MatrixObj/matobjgeneric.tst b/tst/testinstall/MatrixObj/matobjgeneric.tst index 7ed965513e..dcdef45c8f 100644 --- a/tst/testinstall/MatrixObj/matobjgeneric.tst +++ b/tst/testinstall/MatrixObj/matobjgeneric.tst @@ -203,20 +203,49 @@ gap> IsOne( N * M ); true gap> IsOne( M * N ); true +gap> M:= Matrix( IsGenericMatrixRep, Rationals, [ [ 1, 2 ], [ 3, 5 ] ] );; +gap> N:= InverseMutable( M );; +gap> Display( N ); +<2x2-matrix over Rationals: +[[ -5, 2 ] + [ 3, -1 ] +]> + +# +# mutability after operations +# +gap> M:= Matrix( IsGenericMatrixRep, Rationals, [ [ 1, 2 ], [ 3, 5 ] ] );; +gap> IsMutable(M); +true +gap> N:= InverseSameMutability( M );; +gap> IsMutable(N); +true gap> IsMutable( M * N ); true +gap> IsMutable( M + N ); +true +gap> IsMutable( M - N ); +true +gap> IsMutable( -M ); +true gap> MakeImmutable(M);; gap> IsMutable( M * N ); true -gap> MakeImmutable(N);; +gap> IsMutable( M + N ); +true +gap> IsMutable( M - N ); +true +gap> N:= InverseSameMutability( M );; +gap> IsMutable(N); +false gap> IsMutable( M * N ); false -gap> M:= Matrix( IsGenericMatrixRep, Rationals, [ [ 1, 2 ], [ 3, 5 ] ] );; -gap> Display( InverseMutable( M ) ); -<2x2-matrix over Rationals: -[[ -5, 2 ] - [ 3, -1 ] -]> +gap> IsMutable( M + N ); +false +gap> IsMutable( M - N ); +false +gap> IsMutable( -M ); +false # gap> M:= Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ] ] );; @@ -380,5 +409,27 @@ gap> [] * b; Error, no method found! For debugging hints type ?Recovery from NoMethodFound Error, no 1st choice method found for `BaseDomain' on 1 arguments +# +# families +# +gap> M:= NewZeroMatrix( IsGenericMatrixRep, Integers, 2, 3 ); +<2x3-matrix over Integers> +gap> IsMutable( M ); +true +gap> IsCyclotomicCollColl( M ); +true +gap> IsFFECollColl( M ); +false + +# +gap> M:= NewZeroMatrix( IsGenericMatrixRep, GF(257), 2, 3 ); +<2x3-matrix over GF(257)> +gap> IsMutable( M ); +true +gap> IsCyclotomicCollColl( M ); +false +gap> IsFFECollColl( M ); +true + # gap> STOP_TEST( "matobjgeneric.tst" ); diff --git a/tst/testinstall/MatrixObj/matobjplist.tst b/tst/testinstall/MatrixObj/matobjplist.tst index 584ec3da07..5a609ef25e 100644 --- a/tst/testinstall/MatrixObj/matobjplist.tst +++ b/tst/testinstall/MatrixObj/matobjplist.tst @@ -39,10 +39,8 @@ gap> NewVector( IsPlistVectorRep, Integers, [ 1/2 ] );; Error, the elements in must lie in # -gap> NewZeroVector( IsPlistVectorRep, Integers, 0 );; -gap> z:= NewZeroVector( IsPlistVectorRep, Integers, 1 );; -gap> IsMutable( z ); -true +gap> NewZeroVector( IsPlistVectorRep, Integers, 0 ); + # gap> NewMatrix( IsPlistMatrixRep, Integers, 2, [] );; @@ -58,18 +56,74 @@ gap> IsMutable( M ) and ForAll( [ 1 .. Length( M ) ], i -> IsMutable( M[i] ) ); true # -gap> NewZeroMatrix( IsPlistMatrixRep, Integers, 0, 0 );; -gap> NewZeroMatrix( IsPlistMatrixRep, Integers, 2, 0 );; -gap> NewZeroMatrix( IsPlistMatrixRep, Integers, 0, 3 );; -gap> M:= NewZeroMatrix( IsPlistMatrixRep, Integers, 2, 3 );; +gap> NewZeroMatrix( IsPlistMatrixRep, Integers, 0, 0 ); +<0x0-matrix over Integers> +gap> NewZeroMatrix( IsPlistMatrixRep, Integers, 2, 0 ); +<2x0-matrix over Integers> +gap> NewZeroMatrix( IsPlistMatrixRep, Integers, 0, 3 ); +<0x3-matrix over Integers> +gap> M:= NewZeroMatrix( IsPlistMatrixRep, Integers, 2, 3 ); +<2x3-matrix over Integers> gap> IsMutable( M ) and ForAll( [ 1 .. Length( M ) ], i -> IsMutable( M[i] ) ); true # -gap> NewIdentityMatrix( IsPlistMatrixRep, Integers, 0 );; -gap> M:= NewIdentityMatrix( IsPlistMatrixRep, Integers, 2 );; +gap> NewIdentityMatrix( IsPlistMatrixRep, Integers, 0 ); +<0x0-matrix over Integers> +gap> M:= NewIdentityMatrix( IsPlistMatrixRep, Integers, 2 ); +<2x2-matrix over Integers> gap> IsMutable( M ) and ForAll( [ 1 .. Length( M ) ], i -> IsMutable( M[i] ) ); true +# +# special filters and families +# +gap> v:= NewVector( IsPlistVectorRep, Integers, [ 1, 2, 0 ] ); + +gap> IsMutable( v ); +true +gap> IsIntVector( v ); +true +gap> IsFFEVector( v ); +false +gap> IsCyclotomicCollection( v ); +true +gap> IsFFECollection( v ); +false + +# +gap> v:= NewVector( IsPlistVectorRep, GF(257), Z(257)^0 * [ 1, 2, 0 ] ); + +gap> IsMutable( v ); +true +gap> IsIntVector( v ); +false +gap> IsFFEVector( v ); +true +gap> IsCyclotomicCollection( v ); +false +gap> IsFFECollection( v ); +true + +# +gap> M:= NewZeroMatrix( IsPlistMatrixRep, Integers, 2, 3 ); +<2x3-matrix over Integers> +gap> IsMutable( M ); +true +gap> IsCyclotomicCollColl( M ); +true +gap> IsFFECollColl( M ); +false + +# +gap> M:= NewZeroMatrix( IsPlistMatrixRep, GF(257), 2, 3 ); +<2x3-matrix over GF(257)> +gap> IsMutable( M ); +true +gap> IsCyclotomicCollColl( M ); +false +gap> IsFFECollColl( M ); +true + # gap> STOP_TEST( "matobjplist.tst" ); From 29dfe270adf4e644238fc916fc2a5019185a31e0 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 17 Apr 2026 22:38:28 +0200 Subject: [PATCH 19/32] more test coverage --- lib/matobjgeneric.gi | 4 +--- tst/testinstall/MatrixObj/matobjgeneric.tst | 23 ++++++++++++++++++++- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index 794b723572..aafcc3649c 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -40,9 +40,7 @@ BindGlobal( "MakeIsGenericMatrixRep", fi; if check and ValueOption( "check" ) <> false then - if not IsPlistRep( list ) then - Error( " must be a plain list" ); - fi; + Assert( 0, IsPlistRep( list ) ); for row in list do if not IsPlistRep( row ) then Error( "the entries of must be plain lists" ); diff --git a/tst/testinstall/MatrixObj/matobjgeneric.tst b/tst/testinstall/MatrixObj/matobjgeneric.tst index dcdef45c8f..f6d3e86c11 100644 --- a/tst/testinstall/MatrixObj/matobjgeneric.tst +++ b/tst/testinstall/MatrixObj/matobjgeneric.tst @@ -377,7 +377,8 @@ gap> String(M); # vec * mat, mat * vec # gap> M:= Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ] ] );; -gap> v:= NewVector( IsPlistVectorRep, Integers, [ 1, 2 ] );; +gap> v:= Vector( Integers, [ 1, 2 ] ); + gap> Display( M * v ); Display( v * M ); + +# gap> M * [ 1, 2 ]; [ 5, 11 ] gap> [ 1, 2 ] * M; [ 7, 10 ] +# +gap> R:= Integers mod 6;; +gap> M:= Matrix( IsGenericMatrixRep, R, One(R) * [ [ 1, 2 ], [ 3, 4 ] ] );; +gap> v:= NewVector( IsZmodnZVectorRep, R, One(R) * [ 1, 2 ] ); + +gap> Display( M * v ); + +gap> Display( v * M ); + + # error handling gap> M * [ 1, 2, 3 ]; Error, and are not compatible gap> [ 1, 2, 3 ] * M; Error, and are not compatible +gap> M * [ Z(2), Z(2) ]; +Error, and are not compatible +gap> [ Z(2), Z(2) ] * M; +Error, and are not compatible # multiplication with empty list is not well-defined: we don't know if # is meant to be a vector of length 0, or something else; so rather From dc91fc924b46aedba12f3b099bc63c9b0807bf40 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 17 Apr 2026 22:55:11 +0200 Subject: [PATCH 20/32] oops --- tst/testinstall/MatrixObj/matobjgeneric.tst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tst/testinstall/MatrixObj/matobjgeneric.tst b/tst/testinstall/MatrixObj/matobjgeneric.tst index f6d3e86c11..5f9b3c5a0e 100644 --- a/tst/testinstall/MatrixObj/matobjgeneric.tst +++ b/tst/testinstall/MatrixObj/matobjgeneric.tst @@ -1,4 +1,4 @@ -#@local e, v, v2, w, M, z, rows, a, b, c, d, p, ev, n, ai, inv, zm, zs, N, T, lp, lp0, s +#@local e, v, v2, w, M, z, rows, a, b, c, d, p, ev, n, ai, inv, zm, zs, N, T, lp, lp0, s, R gap> START_TEST( "matobjgeneric.tst" ); # From c6e222c018a5ba82478aacd444b899917d7cad65 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Fri, 17 Apr 2026 22:55:40 +0200 Subject: [PATCH 21/32] cleanup --- tst/testinstall/MatrixObj/matobjgeneric.tst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tst/testinstall/MatrixObj/matobjgeneric.tst b/tst/testinstall/MatrixObj/matobjgeneric.tst index 5f9b3c5a0e..748248ff47 100644 --- a/tst/testinstall/MatrixObj/matobjgeneric.tst +++ b/tst/testinstall/MatrixObj/matobjgeneric.tst @@ -1,4 +1,4 @@ -#@local e, v, v2, w, M, z, rows, a, b, c, d, p, ev, n, ai, inv, zm, zs, N, T, lp, lp0, s, R +#@local e, v, v2, w, M, z, rows, a, b, c, d, p, ev, n, ai, inv, zm, zs, N, T, R gap> START_TEST( "matobjgeneric.tst" ); # From 88cd852ea5fb3f4a7c21571477c9b099d1a1c748 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Tue, 21 Apr 2026 00:16:28 +0200 Subject: [PATCH 22/32] some tweaks --- doc/ref/makedocreldata.g | 2 +- doc/ref/matobj.xml | 2 +- lib/matobjgeneric.gd | 8 ++++---- lib/matobjgeneric.gi | 16 ++++++++-------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/doc/ref/makedocreldata.g b/doc/ref/makedocreldata.g index 22993e3497..7422aa85b4 100644 --- a/doc/ref/makedocreldata.g +++ b/doc/ref/makedocreldata.g @@ -119,7 +119,7 @@ GAPInfo.ManualDataRef:= rec( "../../lib/matobj2.gd", "../../lib/matobjnz.gd", "../../lib/matobjplist.gd", - "../../lib/matobjgeneric.gd", + #"../../lib/matobjgeneric.gd", # TODO: enable this if we decide to keep IsGenericMatrixRep "../../lib/matrix.gd", "../../lib/meataxe.gi", "../../lib/memory.gd", diff --git a/doc/ref/matobj.xml b/doc/ref/matobj.xml index ec282ab5d8..7b2b79f43e 100644 --- a/doc/ref/matobj.xml +++ b/doc/ref/matobj.xml @@ -374,7 +374,7 @@ described in this chapter is supported. <#Include Label="IsGF2MatrixRep"> <#Include Label="Is8BitMatrixRep"> <#Include Label="IsPlistMatrixRep"> -<#Include Label="IsGenericMatrixRep"> + <#Include Label="IsZmodnZMatrixRep"> diff --git a/lib/matobjgeneric.gd b/lib/matobjgeneric.gd index a9707baebc..261f5878ca 100644 --- a/lib/matobjgeneric.gd +++ b/lib/matobjgeneric.gd @@ -40,7 +40,7 @@ DeclareRepresentation( "IsGenericMatrixRep", [] ); -# Internal positions for flat plist matrices. -BindConstant( "FBDPOS", 1 ); -BindConstant( "FCOLSPOS", 2 ); -BindConstant( "FROWSPOS", 3 ); +# Internal positions for IsGenericMatrixRep: +BindConstant( "FBDPOS", 1 ); # BaseDomain +BindConstant( "FCOLSPOS", 2 ); # NumberColumns +BindConstant( "FROWSPOS", 3 ); # "rows" as a plist-of-plists diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index aafcc3649c..b73381bceb 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -21,22 +21,22 @@ BindGlobal( "MakeIsGenericMatrixRep", # Currently there is no special handling depending on 'basedomain', # the types are always cached in 'fam'. - if not IsBound( fam!.FlatPlistMatrixRepTypes ) then + if not IsBound( fam!.GenericMatrixRepTypes ) then # initialize type cache # TODO: make this thread safe for HPC-GAP filter := IsGenericMatrixRep; if CanEasilyCompareElementsFamily( efam ) then filter := filter and CanEasilyCompareElements; fi; - fam!.FlatPlistMatrixRepTypes := [ + fam!.GenericMatrixRepTypes := [ NewType( fam, filter ), NewType( fam, filter and IsMutable ), ]; fi; if IsMutable( list ) then - typ := fam!.FlatPlistMatrixRepTypes[2]; + typ := fam!.GenericMatrixRepTypes[2]; else - typ := fam!.FlatPlistMatrixRepTypes[1]; + typ := fam!.GenericMatrixRepTypes[1]; fi; if check and ValueOption( "check" ) <> false then @@ -135,17 +135,17 @@ InstallMethod( MatElm, InstallMethod( SetMatElm, [ "IsGenericMatrixRep and IsMutable", "IsPosInt", "IsPosInt", "IsObject" ], - function( M, row, col, ob ) + function( M, row, col, val ) if ValueOption( "check" ) <> false then - if not ob in BaseDomain( M ) then - Error( " must lie in the base domain of " ); + if not val in BaseDomain( M ) then + Error( " must lie in the base domain of " ); elif not row in [1..NrRows(M)] then Error( " is out of bounds" ); elif not col in [1..NrCols(M)] then Error( " is out of bounds" ); fi; fi; - M![FROWSPOS][row,col] := ob; + M![FROWSPOS][row,col] := val; end ); From 8aad6eaceccf61a82ed045e0e9eefca55ae9aca7 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Tue, 21 Apr 2026 00:33:25 +0200 Subject: [PATCH 23/32] revert a family change --- lib/matobjnz.gi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/matobjnz.gi b/lib/matobjnz.gi index 2bb5a476a5..c2edf17d3a 100644 --- a/lib/matobjnz.gi +++ b/lib/matobjnz.gi @@ -669,7 +669,7 @@ InstallTagBasedMethod( NewZeroMatrix, m[i] := ZeroVector( cols, e ); od; m := [basedomain,e,cols,m]; - Objectify( NewType(CollectionsFamily(ElementsFamily(FamilyObj(basedomain))), + Objectify( NewType(CollectionsFamily(FamilyObj(basedomain)), filter and IsMutable), m ); return m; end ); From 90de684138021b2212660b0131e8994ed4ff3d89 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 26 Apr 2026 22:47:14 +0200 Subject: [PATCH 24/32] Inverse: return fail if matrix is square but not invertible --- lib/matobjgeneric.gi | 3 +++ tst/testinstall/MatrixObj/matobjgeneric.tst | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index b73381bceb..0348f2767a 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -246,6 +246,9 @@ InstallMethod( InverseMutable, else rows := INV_MATRIX_MUTABLE( M![FROWSPOS] ); fi; + if rows = fail then + return fail; + fi; return MakeIsGenericMatrixRep( bd, NrCols( M ), rows, false ); end ); diff --git a/tst/testinstall/MatrixObj/matobjgeneric.tst b/tst/testinstall/MatrixObj/matobjgeneric.tst index 748248ff47..d8d6685df0 100644 --- a/tst/testinstall/MatrixObj/matobjgeneric.tst +++ b/tst/testinstall/MatrixObj/matobjgeneric.tst @@ -192,6 +192,13 @@ gap> Unpack( M ); gap> M:= Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ], [ 5, 6 ] ] );; gap> InverseMutable( M ); Error, InverseMutable: matrix must be square + +# +gap> M:= ZeroMatrix( IsGenericMatrixRep, Integers, 2, 2 );; +gap> InverseMutable( M ); +fail + +# gap> M:= Matrix( IsGenericMatrixRep, GF(2), [ [ Z(2)^0, Z(2)^0 ], [ Z(2)^0, 0*Z(2) ] ] );; gap> N:= InverseMutable( M );; gap> Display( N ); @@ -203,6 +210,8 @@ gap> IsOne( N * M ); true gap> IsOne( M * N ); true + +# gap> M:= Matrix( IsGenericMatrixRep, Rationals, [ [ 1, 2 ], [ 3, 5 ] ] );; gap> N:= InverseMutable( M );; gap> Display( N ); From 2d9cd66d8b9cd0a71d31691de601b9dfedbc8539 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 26 Apr 2026 22:50:41 +0200 Subject: [PATCH 25/32] Remove MakeImmutable call from NewMatrix method. Thomas Breuer informed me: "According to the documentation, > The returned object is mutable if and only if filter implies IsCopyable. That is, the mutability of the result does not depend on the input list. --- lib/matobjgeneric.gi | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index 0348f2767a..259fb90d99 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -81,9 +81,6 @@ InstallTagBasedMethod( NewMatrix, rows[i] := PlainListCopy( row ); fi; od; - if not IsMutable( list ) then - MakeImmutable( rows ); - fi; return MakeIsGenericMatrixRep( basedomain, ncols, rows, true ); end ); From 14d4606f98d5f8b0443ac799ce91268209bbbd95 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 26 Apr 2026 22:53:13 +0200 Subject: [PATCH 26/32] adjust test output --- tst/testinstall/MatrixObj/matobjgeneric.tst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tst/testinstall/MatrixObj/matobjgeneric.tst b/tst/testinstall/MatrixObj/matobjgeneric.tst index d8d6685df0..e1bc6c3ca1 100644 --- a/tst/testinstall/MatrixObj/matobjgeneric.tst +++ b/tst/testinstall/MatrixObj/matobjgeneric.tst @@ -259,7 +259,7 @@ false # gap> M:= Matrix( IsGenericMatrixRep, Integers, [ [ 1, 2 ], [ 3, 4 ] ] );; gap> M[1,1] := 1/2; -Error, must lie in the base domain of +Error, must lie in the base domain of gap> M[3,1] := 1; Error, is out of bounds gap> M[1,3] := 1; From 1b486bbdcdcc2971d575f793d674b38108439608 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Sun, 26 Apr 2026 22:56:29 +0200 Subject: [PATCH 27/32] Adjust ChangedBaseDomain result mutability The documentation states: > The result is mutable if and only if v (M) is mutable. --- lib/matobjgeneric.gi | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index 259fb90d99..c8a539a2f7 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -354,7 +354,12 @@ InstallOtherMethod( \*, InstallMethod( ChangedBaseDomain, [ "IsGenericMatrixRep", "IsRing" ], function( M, r ) - return NewMatrix( IsGenericMatrixRep, r, NrCols(M), M![FROWSPOS] ); + local A; + A := NewMatrix( IsGenericMatrixRep, r, NrCols(M), M![FROWSPOS] ); + if not IsMutable( M ) then + MakeImmutable(A); + fi; + return A; end ); From 3c1f16039b0c4636a4fc6214e4094e2423b178b1 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 27 Apr 2026 11:26:50 +0200 Subject: [PATCH 28/32] Remove spurious (wrong) SetIsZero --- lib/matobjgeneric.gi | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index c8a539a2f7..ab2fd0668d 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -224,7 +224,6 @@ InstallMethod( ZeroMutable, local z; z := MakeIsGenericMatrixRep( BaseDomain( M ), NrCols( M ), ZeroMutable( M![FROWSPOS] ), false ); - SetIsZero( z, true ); return z; end ); From c663080b7e3ccb495b832a88df76d370a65202f9 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 27 Apr 2026 11:27:48 +0200 Subject: [PATCH 29/32] Hook IsGenericMatrixRep back into the manual --- doc/ref/makedocreldata.g | 2 +- doc/ref/matobj.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/ref/makedocreldata.g b/doc/ref/makedocreldata.g index 7422aa85b4..22993e3497 100644 --- a/doc/ref/makedocreldata.g +++ b/doc/ref/makedocreldata.g @@ -119,7 +119,7 @@ GAPInfo.ManualDataRef:= rec( "../../lib/matobj2.gd", "../../lib/matobjnz.gd", "../../lib/matobjplist.gd", - #"../../lib/matobjgeneric.gd", # TODO: enable this if we decide to keep IsGenericMatrixRep + "../../lib/matobjgeneric.gd", "../../lib/matrix.gd", "../../lib/meataxe.gi", "../../lib/memory.gd", diff --git a/doc/ref/matobj.xml b/doc/ref/matobj.xml index 7b2b79f43e..ec282ab5d8 100644 --- a/doc/ref/matobj.xml +++ b/doc/ref/matobj.xml @@ -374,7 +374,7 @@ described in this chapter is supported. <#Include Label="IsGF2MatrixRep"> <#Include Label="Is8BitMatrixRep"> <#Include Label="IsPlistMatrixRep"> - +<#Include Label="IsGenericMatrixRep"> <#Include Label="IsZmodnZMatrixRep"> From a1b8de315d5c832359cf37e5272d57f8ce19ca81 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 11 May 2026 22:44:34 +0200 Subject: [PATCH 30/32] matobj: clarify generic and plist matrix reps Explain more clearly how IsGenericMatrixRep differs from IsPlistMatrixRep in the manual. Document that the plist representation supports row access, while the generic one is the recommended general-purpose choice and points readers to the row and column reduction operations as an alternative to direct row manipulation. AI-assisted with Codex for drafting and refining the manual wording. Co-authored-by: Codex --- lib/matobjgeneric.gd | 13 +++++++++++-- lib/matobjplist.gd | 11 ++++++++++- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/matobjgeneric.gd b/lib/matobjgeneric.gd index 261f5878ca..e6fc7789c1 100644 --- a/lib/matobjgeneric.gd +++ b/lib/matobjgeneric.gd @@ -24,9 +24,18 @@ ## a matrix object (see ) whose entries are stored ## as a dense plain list of dense plain row lists. ##

-## This representation is optimized for efficient entry access via -## M[i,j]. Unlike , it is not a row +## Unlike , this representation is not a row ## list matrix, so direct row access via M[i] is not supported. +## Instead, it is intended as the general-purpose representation for +## matrix objects. +##

+## This representation supports the usual matrix operations and is often more +## efficient than . If existing code uses row +## access, it is often possible to replace this by the operations from +## Section . +##

+## Use instead if you need the rows to be +## available as vector objects in . ## ## ## <#/GAPDoc> diff --git a/lib/matobjplist.gd b/lib/matobjplist.gd index 087d5b0330..93bf879dd1 100644 --- a/lib/matobjplist.gd +++ b/lib/matobjplist.gd @@ -71,6 +71,16 @@ DeclareRepresentation( "IsPlistVectorRep", ## a matrix object (see ) that behaves similar to ## a list of its rows, in the sense of . ##

+## In particular, the rows can be accessed via M[i], +## and they are vector objects in . +## This representation is useful if one wants to work explicitly with +## such row objects. +##

+## For most purposes, is the better choice. +## Unlike , it does not support direct row +## access, but it is intended as the general-purpose representation for +## matrix objects. +##

## implies , ## thus matrix objects in this representation can be mutable. ## @@ -126,4 +136,3 @@ BindConstant( "ELSPOS", 2 ); # Two filters to speed up some methods: DeclareFilter( "IsIntVector" ); DeclareFilter( "IsFFEVector" ); - From 59fb07d918dd12f1fe1159cdfa57eeaa5cfeb042 Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 11 May 2026 22:53:50 +0200 Subject: [PATCH 31/32] Rename constant --- lib/matobjgeneric.gd | 6 ++--- lib/matobjgeneric.gi | 62 ++++++++++++++++++++++---------------------- 2 files changed, 34 insertions(+), 34 deletions(-) diff --git a/lib/matobjgeneric.gd b/lib/matobjgeneric.gd index e6fc7789c1..d82837e91e 100644 --- a/lib/matobjgeneric.gd +++ b/lib/matobjgeneric.gd @@ -50,6 +50,6 @@ DeclareRepresentation( "IsGenericMatrixRep", # Internal positions for IsGenericMatrixRep: -BindConstant( "FBDPOS", 1 ); # BaseDomain -BindConstant( "FCOLSPOS", 2 ); # NumberColumns -BindConstant( "FROWSPOS", 3 ); # "rows" as a plist-of-plists +BindConstant( "GEN_MAT_REP_BASEDOMAIN_POS", 1 ); # BaseDomain +BindConstant( "GEN_MAT_REP_NCOLS_POS", 2 ); # number of columns (needed to represent 0 x m matrices) +BindConstant( "GEN_MAT_REP_ROWS_POS", 3 ); # "rows" as a plist-of-plists diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index ab2fd0668d..35f522b26a 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -110,15 +110,15 @@ InstallMethod( CompatibleVectorFilter, InstallMethod( BaseDomain, [ "IsGenericMatrixRep" ], - M -> M![FBDPOS] ); + M -> M![GEN_MAT_REP_BASEDOMAIN_POS] ); InstallMethod( NumberRows, [ "IsGenericMatrixRep" ], - M -> Length( M![FROWSPOS] ) ); + M -> Length( M![GEN_MAT_REP_ROWS_POS] ) ); InstallMethod( NumberColumns, [ "IsGenericMatrixRep" ], - M -> M![FCOLSPOS] ); + M -> M![GEN_MAT_REP_NCOLS_POS] ); InstallMethod( \[\], [ "IsGenericMatrixRep", "IsPosInt" ], @@ -128,7 +128,7 @@ InstallMethod( \[\], InstallMethod( MatElm, [ "IsGenericMatrixRep", "IsPosInt", "IsPosInt" ], - { M, row, col } -> M![FROWSPOS][row,col] ); + { M, row, col } -> M![GEN_MAT_REP_ROWS_POS][row,col] ); InstallMethod( SetMatElm, [ "IsGenericMatrixRep and IsMutable", "IsPosInt", "IsPosInt", "IsObject" ], @@ -142,29 +142,29 @@ InstallMethod( SetMatElm, Error( " is out of bounds" ); fi; fi; - M![FROWSPOS][row,col] := val; + M![GEN_MAT_REP_ROWS_POS][row,col] := val; end ); InstallMethod( Unpack, [ "IsGenericMatrixRep" ], - M -> List( M![FROWSPOS], ShallowCopy ) ); + M -> List( M![GEN_MAT_REP_ROWS_POS], ShallowCopy ) ); InstallMethod( ShallowCopy, [ "IsGenericMatrixRep" ], M -> MakeIsGenericMatrixRep( BaseDomain(M), NrCols(M), - List( M![FROWSPOS], ShallowCopy ), false ) ); + List( M![GEN_MAT_REP_ROWS_POS], ShallowCopy ), false ) ); InstallMethod( MutableCopyMatrix, [ "IsGenericMatrixRep" ], M -> MakeIsGenericMatrixRep( BaseDomain(M), NrCols(M), - List( M![FROWSPOS], ShallowCopy ), false ) ); + List( M![GEN_MAT_REP_ROWS_POS], ShallowCopy ), false ) ); InstallMethod( ExtractSubMatrix, [ "IsGenericMatrixRep", "IsList", "IsList" ], function( M, rowspos, colspos ) local list; - list := M![FROWSPOS]{ rowspos }{ colspos }; + list := M![GEN_MAT_REP_ROWS_POS]{ rowspos }{ colspos }; return MakeIsGenericMatrixRep( BaseDomain(M), Length( colspos ), list, false ); end ); @@ -176,14 +176,14 @@ InstallMethod( CopySubMatrix, not IsIdenticalObj( BaseDomain(M), BaseDomain(N) ) then Error( " and are not compatible" ); fi; - N![FROWSPOS]{dstrows}{dstcols} := M![FROWSPOS]{srcrows}{srccols}; + N![GEN_MAT_REP_ROWS_POS]{dstrows}{dstcols} := M![GEN_MAT_REP_ROWS_POS]{srcrows}{srccols}; end ); InstallMethod( TransposedMatMutable, [ "IsGenericMatrixRep" ], function( M ) local list; - list := TransposedMatMutable(M![FROWSPOS]); + list := TransposedMatMutable(M![GEN_MAT_REP_ROWS_POS]); return MakeIsGenericMatrixRep( BaseDomain(M), NrRows(M), list, false ); end ); @@ -197,7 +197,7 @@ InstallMethod( \+, Error( " and are not compatible" ); fi; return MakeIsGenericMatrixRep( BaseDomain( a ), NrCols( a ), - a![FROWSPOS] + b![FROWSPOS], false ); + a![GEN_MAT_REP_ROWS_POS] + b![GEN_MAT_REP_ROWS_POS], false ); end ); InstallMethod( \-, @@ -210,20 +210,20 @@ InstallMethod( \-, Error( " and are not compatible" ); fi; return MakeIsGenericMatrixRep( BaseDomain( a ), NrCols( a ), - a![FROWSPOS] - b![FROWSPOS], false ); + a![GEN_MAT_REP_ROWS_POS] - b![GEN_MAT_REP_ROWS_POS], false ); end ); InstallMethod( AdditiveInverseMutable, [ "IsGenericMatrixRep" ], M -> MakeIsGenericMatrixRep( BaseDomain( M ), NrCols( M ), - AdditiveInverseMutable( M![FROWSPOS] ), false ) ); + AdditiveInverseMutable( M![GEN_MAT_REP_ROWS_POS] ), false ) ); InstallMethod( ZeroMutable, [ "IsGenericMatrixRep" ], function( M ) local z; z := MakeIsGenericMatrixRep( BaseDomain( M ), NrCols( M ), - ZeroMutable( M![FROWSPOS] ), false ); + ZeroMutable( M![GEN_MAT_REP_ROWS_POS] ), false ); return z; end ); @@ -238,9 +238,9 @@ InstallMethod( InverseMutable, elif NrRows( M ) = 0 then rows := []; elif IsFinite( bd ) and IsField( bd ) then - rows := INV_MAT_DEFAULT_MUTABLE( M![FROWSPOS] ); + rows := INV_MAT_DEFAULT_MUTABLE( M![GEN_MAT_REP_ROWS_POS] ); else - rows := INV_MATRIX_MUTABLE( M![FROWSPOS] ); + rows := INV_MATRIX_MUTABLE( M![GEN_MAT_REP_ROWS_POS] ); fi; if rows = fail then return fail; @@ -275,7 +275,7 @@ InstallMethod( \*, list[i] := ListWithIdenticalEntries( colsB, Zero( bd ) ); od; else - list := a![FROWSPOS] * b![FROWSPOS]; + list := a![GEN_MAT_REP_ROWS_POS] * b![GEN_MAT_REP_ROWS_POS]; fi; return MakeIsGenericMatrixRep( bd, colsB, list, false ); end ); @@ -311,7 +311,7 @@ InstallOtherMethod( \*, else res := Unpack(v); fi; - res := M![FROWSPOS] * res; + res := M![GEN_MAT_REP_ROWS_POS] * res; return Vector( res, v ); end ); @@ -346,7 +346,7 @@ InstallOtherMethod( \*, else res := Unpack(v); fi; - res := res * M![FROWSPOS]; + res := res * M![GEN_MAT_REP_ROWS_POS]; return Vector( res, v ); end ); @@ -354,7 +354,7 @@ InstallMethod( ChangedBaseDomain, [ "IsGenericMatrixRep", "IsRing" ], function( M, r ) local A; - A := NewMatrix( IsGenericMatrixRep, r, NrCols(M), M![FROWSPOS] ); + A := NewMatrix( IsGenericMatrixRep, r, NrCols(M), M![GEN_MAT_REP_ROWS_POS] ); if not IsMutable( M ) then MakeImmutable(A); fi; @@ -365,56 +365,56 @@ InstallMethod( ChangedBaseDomain, InstallMethod( MultMatrixRowLeft, [ "IsGenericMatrixRep and IsMutable", "IsInt", "IsObject" ], function( mat, row, scalar ) - MultMatrixRowLeft(mat![FROWSPOS], row, scalar); + MultMatrixRowLeft(mat![GEN_MAT_REP_ROWS_POS], row, scalar); end ); InstallMethod( MultMatrixRowRight, [ "IsGenericMatrixRep and IsMutable", "IsInt", "IsObject" ], function( mat, row, scalar ) - MultMatrixRowRight(mat![FROWSPOS], row, scalar); + MultMatrixRowRight(mat![GEN_MAT_REP_ROWS_POS], row, scalar); end ); InstallMethod( AddMatrixRowsLeft, [ "IsGenericMatrixRep and IsMutable", "IsInt", "IsInt", "IsObject" ], function( mat, row1, row2, scalar ) - AddMatrixRowsLeft( mat![FROWSPOS], row1, row2, scalar ); + AddMatrixRowsLeft( mat![GEN_MAT_REP_ROWS_POS], row1, row2, scalar ); end ); InstallMethod( AddMatrixRowsRight, [ "IsGenericMatrixRep and IsMutable", "IsInt", "IsInt", "IsObject" ], function( mat, row1, row2, scalar ) - AddMatrixRowsRight( mat![FROWSPOS], row1, row2, scalar ); + AddMatrixRowsRight( mat![GEN_MAT_REP_ROWS_POS], row1, row2, scalar ); end ); InstallMethod( PositionNonZeroInRow, [ "IsGenericMatrixRep", "IsPosInt" ], function( mat, row ) - return PositionNonZero( mat![FROWSPOS][row] ); + return PositionNonZero( mat![GEN_MAT_REP_ROWS_POS][row] ); end ); InstallMethod( PositionNonZeroInRow, [ "IsGenericMatrixRep", "IsPosInt", "IsInt" ], function( mat, row, from ) - return PositionNonZero( mat![FROWSPOS][row], from ); + return PositionNonZero( mat![GEN_MAT_REP_ROWS_POS][row], from ); end ); InstallMethod( SwapMatrixRows, [ "IsGenericMatrixRep and IsMutable", "IsInt", "IsInt" ], function( mat, row1, row2 ) - SwapMatrixRows(mat![FROWSPOS], row1, row2); + SwapMatrixRows(mat![GEN_MAT_REP_ROWS_POS], row1, row2); end ); InstallMethod( SwapMatrixColumns, [ "IsGenericMatrixRep and IsMutable", "IsInt", "IsInt" ], function( mat, col1, col2 ) - SwapMatrixColumns(mat![FROWSPOS], col1, col2); + SwapMatrixColumns(mat![GEN_MAT_REP_ROWS_POS], col1, col2); end ); InstallMethod( PostMakeImmutable, [ "IsGenericMatrixRep" ], function( M ) - MakeImmutable( M![FROWSPOS] ); + MakeImmutable( M![GEN_MAT_REP_ROWS_POS] ); end ); @@ -454,7 +454,7 @@ InstallMethod( Display, [ "IsGenericMatrixRep" ], else Print( " " ); fi; - Print( M![FROWSPOS][i], "\n" ); + Print( M![GEN_MAT_REP_ROWS_POS][i], "\n" ); od; Print( "]>\n" ); end ); From 3395d786541ede4440a70d83609cea7713e651fe Mon Sep 17 00:00:00 2001 From: Max Horn Date: Mon, 11 May 2026 22:57:22 +0200 Subject: [PATCH 32/32] formatting --- lib/matobjgeneric.gi | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/matobjgeneric.gi b/lib/matobjgeneric.gi index 35f522b26a..c2aab52999 100644 --- a/lib/matobjgeneric.gi +++ b/lib/matobjgeneric.gi @@ -136,9 +136,9 @@ InstallMethod( SetMatElm, if ValueOption( "check" ) <> false then if not val in BaseDomain( M ) then Error( " must lie in the base domain of " ); - elif not row in [1..NrRows(M)] then + elif not row in [ 1 .. NrRows( M ) ] then Error( " is out of bounds" ); - elif not col in [1..NrCols(M)] then + elif not col in [ 1 .. NrCols( M ) ] then Error( " is out of bounds" ); fi; fi;