Skip to content

Commit 739091f

Browse files
fingolfincdwensley
authored andcommitted
meataxe: simplify using MutableBasis (gap-system#6290)
Replace most of the custom code for solving equation systems by a user of GAP's MutableBasis machinery. This makes the code easier to understand and also provides a minor speed up.
1 parent ea252d0 commit 739091f

File tree

2 files changed

+39
-154
lines changed

2 files changed

+39
-154
lines changed

lib/meatauto.gi

Lines changed: 36 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -70,164 +70,46 @@ local f, d1, d2, e, z, g1, g2, r, b, n, a, gp, i, j, k;
7070
end);
7171

7272

73-
# the following code is essentially due to Michael Smith
73+
BindGlobal("SMTX_AddEqns", function(eqns, newmat)
74+
local newrow;
7475

75-
# These routines are designed to accumulate a system of linear equations
76-
#
77-
# M_1 X = V_1, M_2 X = V_2 ... M_t X = V_t
78-
#
79-
# Where each M_i is an m_i*n matrix, X is the unknown length n vector, and
80-
# each V is an length m_i vector. The equations can be added as each batch
81-
# is calculated. Here is some pseudo-code to demonstrate:
82-
#
83-
# eqns := newEqns (n, field);
84-
# i := 1;
85-
# repeat
86-
# <calculate M_i and V_i>
87-
# addEqns(M_i, V_i)
88-
# increment i;
89-
# until i > t or eqns.failed;
90-
# if not eqns.failed then
91-
# S := solveEqns(eqns);
92-
# fi;
93-
#
94-
# As demonstrated by the example, an early notification of failure is
95-
# available by checking ".failed". All new equations are sifted with respect
96-
# to the current set, and only added if they are independent of the current
97-
# set. If a new equation reduces to the zero row and a nonzero vector
98-
# entry, then there is no solution and this is immediately returned by
99-
# setting eqns.failed to true. The function solveEqns has an already
100-
# triangulised system of equations, so it simply reduces above the pivots
101-
# and returns the solution vector.
102-
103-
104-
BindGlobal("SMTX_AddEqns",function ( eqns, newmat, newvec)
105-
local n, weights, mat, vec, ReduceRow, t,
106-
newweight, newrow, newrhs, i, l, k;
107-
108-
# Add a bunch of equations to the system of equations in <eqns>. Each
109-
# row of <newmat> is the left-hand side of a new equation, and the
110-
# corresponding row of <newvec> the right-hand side. Each equation in
111-
# filtered against the current echelonised system stored in <eqns> and
112-
# then added if it is independent of the system. As soon as a
113-
# left-hand side reduces to 0 with a non-zero right-hand side, the flag
114-
# <eqns.failed> is set.
115-
116-
Info(InfoMtxHom,6,"addEqns: entering" );
117-
118-
n := eqns.dim;
119-
weights := eqns.weights;
120-
mat := eqns.mat;
121-
vec := eqns.vec;
122-
123-
# reduce the (lhs,rhs) against the semi-echelonised current matrix,
124-
# and return either: (1) the reduced rhs if the lhs reduces to zero,
125-
# or (2) a list containing the new echelon weight, the new row and
126-
# the new rhs for the system, and the row number that this
127-
# equation should placed.
128-
ReduceRow := function (lhs, rhs)
129-
local lead, i, z;
130-
lead := PositionNonZero(lhs);
131-
Assert(0, n = Length(lhs));
132-
if lead > n then
133-
return rhs;
134-
fi;
135-
lhs := ShallowCopy(lhs);
136-
for i in [1..Length(weights)] do
137-
if weights[i] = lead then
138-
z := lhs[lead];
139-
AddVector(lhs, mat[i], -z); # lhs is a vector
140-
rhs := rhs - z * vec[i]; # rhs is a scalar
141-
lead := PositionNonZero(lhs, lead);
142-
if lead > n then
143-
return rhs;
144-
fi;
145-
elif weights[i] > lead then
146-
return [lead, lhs, rhs, i];
147-
fi;
148-
od;
149-
return [lead, lhs, rhs, Length(weights)+1];
150-
end;
151-
152-
for k in [1..Length(newmat)] do
153-
t := ReduceRow(newmat[k], newvec[k]);
154-
155-
if IsList(t) then
156-
# new equation
157-
newweight := t[1];
158-
newrow := t[2];
159-
newrhs := t[3];
160-
i := t[4]; # position for new row
161-
162-
# normalise so that leading entry is 1
163-
newrhs := newrhs / newrow[newweight];
164-
newrow := newrow / newrow[newweight]; # NB: in this order
165-
166-
if i = Length(mat)+1 then
167-
# add new equation to end of list
168-
Add(mat, newrow);
169-
Add(vec, newrhs);
170-
Add(weights, newweight);
171-
else
172-
l := Length(mat);
173-
# move down other rows to make space for this new one...
174-
mat{[i+1..l+1]} := mat{[i..l]};
175-
vec{[i+1..l+1]} := vec{[i..l]};
176-
# and then slot it in
177-
mat[i] := newrow;
178-
vec[i] := newrhs;
179-
weights{[i+1..l+1]} := weights{[i..l]};
180-
weights[i] := newweight;
181-
fi;
76+
Info(InfoMtxHom,6,"addEqns: entering (n = ", eqns.dim, ", rank = ", NrBasisVectors(eqns.mb), ")" );
18277

183-
else
184-
# no new equation, check whether inconsistent due to
185-
# nonzero rhs reduction
186-
187-
if not IsZero(t) then
188-
Info(InfoMtxHom,6,"addEqns: FAIL!" );
189-
eqns.failed := true;
190-
return eqns; # return immediately
191-
fi;
192-
fi;
78+
# reduce each row of newmat against the semi-echelonised current matrix;
79+
# if we find any new pivots, insert them
80+
for newrow in newmat do
81+
CloseMutableBasis(eqns.mb, newrow);
19382
od;
19483
end);
19584

19685
BindGlobal("SMTX_NewEqns",function (dim, field)
19786
return rec(
19887
dim := dim, # number of variables
19988
field := field, # field over which the equation hold
200-
mat := [], # left-hand sides of system
201-
weights := [], # echelon weights for lhs matrix
202-
vec := [], # right-hand sides of system
203-
failed := false, # flag to indicate inconsistent system
204-
index := [], # index for row ordering
89+
mb := MutableBasis(field, [], ZeroVector(field, dim)),
20590
);
20691
end);
20792

20893
BindGlobal("SMTX_KillAbovePivotsEqns",function (eqns)
20994
# Eliminate entries above pivots. Note that the pivot entries are
21095
# all 1 courtesy of SMTX_AddEqns.
21196

212-
local m, n, zero, i, c, j, factor;
97+
local m, i, c, j, factor;
21398

21499
Info(InfoMtxHom,6,"killAbovePivotsEqns: entering" );
100+
215101
m := Length(eqns.mat);
216-
n := eqns.dim;
217-
if m > 0 then
218-
zero := Zero(eqns.field);
219-
for i in [1..m] do
220-
c := eqns.weights[i];
221-
for j in [1..i-1] do
222-
factor := eqns.mat[j,c];
223-
if factor <> zero then
224-
Info(InfoMtxHom,6,"solveEqns: kill mat[",j,",",c,"]");
225-
AddVector(eqns.mat[j], eqns.mat[i], -factor);
226-
eqns.vec[j] := eqns.vec[j] - factor*eqns.vec[i];
227-
fi;
228-
od;
102+
for i in [1..m] do
103+
c := eqns.pivots[i];
104+
Assert(0, IsOne(eqns.mat[i,c]));
105+
for j in [1..i-1] do
106+
factor := eqns.mat[j,c];
107+
if not IsZero(factor) then
108+
Info(InfoMtxHom,6,"solveEqns: kill mat[",j,",",c,"]");
109+
AddVector(eqns.mat[j], eqns.mat[i], -factor);
110+
fi;
229111
od;
230-
fi;
112+
od;
231113
Info(InfoMtxHom,6,"killAbovePivotsEqns: leaving" );
232114
end);
233115

@@ -243,6 +125,11 @@ BindGlobal("SMTX_NullspaceEqns",function(e)
243125

244126
local mat, n, one, zerovec, i, k, nullspace, row;
245127

128+
# HACK: convert MutableBasis into the "old" format expected by this code
129+
e.mat := ShallowCopy(e.mb!.basisVectors);
130+
e.pivots := List(e.mat, PositionNonZero);
131+
SortParallel(e.pivots, e.mat);
132+
246133
SMTX_KillAbovePivotsEqns(e);
247134
mat := e.mat;
248135

@@ -624,10 +511,11 @@ end);
624511
BindGlobal("SpinHom",function (V, W)
625512
local nv, nw, F, zero, minusone, zeroW, gV, gW, k, U, echu, r, homs, s, work, ans, v0,
626513
M, x, pos, z, echm, t, v, echv, a, u, e, start, oldlen, ag, m, uu, ret,
627-
c, s1, X, mat, uuc, uic, newhoms, hom, Uhom, imv0, imv0c, image, i, j, l;
514+
c, s1, X, mat, uuc, uic, newhoms, hom, Uhom, imv0, imv0c, image, i, j, l,
515+
nullspace, nullspace_row;
628516

629517
# Compute Hom(V,W) for G-modules <V> and <W>. The algorithm starts with
630-
# the trivial submodule <U> of <V> for which Hom(U,V) is trivial. It
518+
# the trivial submodule <U> of <V> for which Hom(U,W) is trivial. It
631519
# then computes Hom(U',W) for U' a submodule generated by <U> and a
632520
# single element <v0> in <V>. This U' becomes the next <U> as the process
633521
# is iterated, ending when <U'> = <V>. The element <v0> is chosen in a
@@ -820,7 +708,7 @@ local nv, nw, F, zero, minusone, zeroW, gV, gW, k, U, echu, r, homs, s, work, an
820708
od;
821709
Append(mat, X);
822710
ConvertToMatrixRep(mat);
823-
SMTX_AddEqns(e, TransposedMat(mat), zeroW);
711+
SMTX_AddEqns(e, TransposedMat(mat));
824712
fi;
825713
od;
826714
od;
@@ -833,15 +721,14 @@ local nv, nw, F, zero, minusone, zeroW, gV, gW, k, U, echu, r, homs, s, work, an
833721
until oldlen = Length(v);
834722

835723
# we have the system of equations, so find its solution space
836-
837-
ans:=SMTX_NullspaceEqns(e);
724+
nullspace:=SMTX_NullspaceEqns(e);
838725

839726
# Now build the homomorphisms
840727

841728
newhoms:=[];
842-
for i in [1..Length(ans)] do
729+
for nullspace_row in nullspace do
843730

844-
# Each row of ans is of the form:
731+
# Each row of nullspace is of the form:
845732
#
846733
# [ b_1, b_2, ..., b_s, c_1, c_2, ..., c_t ]
847734
#
@@ -853,8 +740,8 @@ local nv, nw, F, zero, minusone, zeroW, gV, gW, k, U, echu, r, homs, s, work, an
853740
Uhom:=NullMat(r, nw, F);
854741
ConvertToMatrixRep(Uhom, F);
855742
for l in [1..s] do
856-
if ans[i][l] <> zero then
857-
AddMatrix(Uhom, homs[l], ans[i][l]);
743+
if nullspace_row[l] <> zero then
744+
AddMatrix(Uhom, homs[l], nullspace_row[l]);
858745
fi;
859746
od;
860747
for l in [1..r] do
@@ -864,8 +751,8 @@ local nv, nw, F, zero, minusone, zeroW, gV, gW, k, U, echu, r, homs, s, work, an
864751

865752
imv0:=ZeroMutable(zeroW);
866753
for l in [1..t] do
867-
if ans[i][s+l] <> zero then
868-
AddVector(imv0, M[l], ans[i][s+l]);
754+
if nullspace_row[s+l] <> zero then
755+
AddVector(imv0, M[l], nullspace_row[s+l]);
869756
fi;
870757
od;
871758
imv0c:=EchResidueCoeffs(M, echm, imv0,1);

tst/testinstall/meatauto.tst

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@ gap> START_TEST("meatauto.tst");
44
#
55
# SMTX_NewEqns, SMTX_NullspaceEqns
66
#
7-
gap> e := SMTX_NewEqns(3, GF(5));
8-
rec( dim := 3, failed := false, field := GF(5), index := [ ], mat := [ ],
9-
vec := [ ], weights := [ ] )
7+
gap> e := SMTX_NewEqns(3, GF(5));;
8+
gap> SMTX_AddEqns(e, [[1,0,0]*Z(5)^0]);
109
gap> SMTX_NullspaceEqns(e);
11-
[ [ Z(5)^0, 0*Z(5), 0*Z(5) ], [ 0*Z(5), Z(5)^0, 0*Z(5) ],
12-
[ 0*Z(5), 0*Z(5), Z(5)^0 ] ]
10+
[ [ 0*Z(5), Z(5)^0, 0*Z(5) ], [ 0*Z(5), 0*Z(5), Z(5)^0 ] ]
1311

1412
#
1513
# MTX.BasisModuleEndomorphisms

0 commit comments

Comments
 (0)