GAP 4.8.9 installation with standard packages -- copy to your CoCalc project to get it
#############################################################################
##
#W Autom.gi FGA package Christian Sievers
##
## Methods to create and compute with inverse automata
##
#Y 2003 - 2012
##
DeclareRepresentation( "IsSimpleInvAutomatonRep",
IsComponentObjectRep and IsInvAutomatonCategory and
IsAttributeStoringRep and IsCollection,
# [ "initial", "terminal", "states", "group" ] );
[ "states", "group" ] );
InstallMethod( TrivialInvAutomaton,
[ IsFreeGroup ],
function(G)
local state;
state := FGA_newstate();
return Objectify( NewType( FamilyObj( G ),
IsSimpleInvAutomatonRep and IsMutable),
rec(initial:=state, terminal:=state, group:=G) );
end );
InstallMethod( InvAutomatonInsertGenerator,
IsCollsElms,
[ IsSimpleInvAutomatonRep and IsMutable, IsElementOfFreeGroup ],
function(A,gen)
FGA_AutomInsertGeneratorLetterRep( A, LetterRepAssocWord( gen ) );
end );
InstallMethod( \in,
"for a simple inverse automaton",
IsElmsColls,
[ IsElementOfFreeGroup, IsSimpleInvAutomatonRep ],
function(g,A)
return IsIdenticalObj(FGA_deltas( A!.initial,
LetterRepAssocWord(g)),
A!.terminal);
end );
InstallMethod( PrintObj,
[ IsSimpleInvAutomatonRep ],
function(A)
Print("<simple inverse automaton representation>");
end );
InstallMethod( AsGroup,
"for a simple inverse Automaton",
[ IsSimpleInvAutomatonRep ],
function(A)
local G;
if IsMutable(A) then
TryNextMethod();
fi;
G := rec ();
ObjectifyWithAttributes( G,
NewType( FamilyObj( A ),
IsFreeGroup and IsAttributeStoringRep and
HasOneImmutable and HasFreeGroupAutomaton ),
OneImmutable, One( A!.group ),
FreeGroupAutomaton, A );
return G;
end );
InstallGlobalFunction( FGA_newstate,
function()
return (rec (delta:=[], deltainv:=[]));
end );
InstallGlobalFunction( FGA_connectpos,
function(s1, s2, g)
s1.delta[g] := s2;
s2.deltainv[g] := s1;
end );
InstallGlobalFunction( FGA_connect,
function(s1, s2, g)
if g>0 then
FGA_connectpos(s1, s2, g);
else
FGA_connectpos(s2, s1, -g);
fi;
end );
InstallGlobalFunction( FGA_define,
function(state, gen)
local nstate;
nstate := FGA_newstate();
FGA_connect(state, nstate, gen);
return nstate; # !!!
end );
# "active"
InstallGlobalFunction( FGA_find,
function(s)
while IsBound(s.isnow) do
s := s.isnow;
od;
return s;
# todo: path compression
end );
InstallGlobalFunction( FGA_merge,
function(s1, s2, Q)
s1 := FGA_find(s1);
s2 := FGA_find(s2);
if IsNotIdenticalObj(s1,s2) then
s2.isnow := s1;
Add(Q,s2);
fi;
end );
InstallGlobalFunction( FGA_coincidence,
function(s1,s2)
local Q, s, g, s0, s01, delta, deltainv;
Q := [];
FGA_merge(s1, s2, Q);
for s in Q do
delta := ShallowCopy(s.delta);
for g in BoundPositions(delta) do
Unbind(delta[g].deltainv[g]);
od;
deltainv := ShallowCopy(s.deltainv);
for g in BoundPositions(deltainv) do
Unbind(deltainv[g].delta[g]);
od;
for g in BoundPositions(delta) do
s0 := FGA_find(s);
s01 := FGA_find(delta[g]);
if IsBound(s0.delta[g]) then
FGA_merge(s01, s0.delta[g], Q);
elif IsBound(s01.deltainv[g]) then
FGA_merge(s0, s01.deltainv[g], Q);
else
FGA_connectpos(s0, s01, g);
fi;
od;
for g in BoundPositions(deltainv) do
s0 := FGA_find(s);
s01 := FGA_find(deltainv[g]);
if IsBound(s0.deltainv[g]) then
FGA_merge(s01, s0.deltainv[g], Q);
elif IsBound(s01.delta[g]) then
FGA_merge(s0, s01.delta[g], Q);
else
FGA_connectpos(s01, s0, g);
fi;
od;
od;
end );
InstallGlobalFunction( FGA_delta,
function(state, gen)
if gen>0 then
return ATf(state.delta, gen);
else
return ATf(state.deltainv, -gen);
fi;
end );
InstallGlobalFunction( FGA_deltas,
function(state, genlist)
return IteratedF(genlist, FGA_delta, state);
end );
InstallGlobalFunction( FGA_TmpState,
function(state, genlist)
local undo, oldstate, i;
i := 1;
while state <> fail and i <= Size(genlist) do
oldstate := state;
state := FGA_delta( oldstate, genlist[i] );
i := i+1;
od;
if state = fail then
i := i-1;
if genlist[i] > 0 then
undo := function() Unbind(oldstate.delta[genlist[i]]); end;
else
undo := function() Unbind(oldstate.deltainv[-genlist[i]]); end;
fi;
state := Iterated( genlist{[i..Size(genlist)]}, FGA_define, oldstate );
else
undo := ReturnTrue;
fi;
return rec( state:=state, undo:=undo);
end );
InstallGlobalFunction( FGA_trace,
function(s,w)
local i, s1;
s1 := s;
i := 1;
while i <= Length(w) and s1 <> fail do
s := s1;
s1 := FGA_delta(s, w[i]);
i := i+1;
od;
if s1 = fail then
return rec(state:=s, index:=i-1);
else
return rec(state:=s1, index:=i);
fi;
end );
InstallGlobalFunction( FGA_backtrace,
function(s,w,j)
local i, s1;
s1 := s;
i := Length(w);
while i >= j and s1 <> fail do
s := s1;
s1 := FGA_delta(s, -w[i]);
i := i-1;
od;
if s1 = fail then
return rec(state:=s, index:=i+1);
else
return rec(state:=s1, index:=i);
fi;
end );
InstallGlobalFunction( FGA_InsertGenerator,
function(s, gen)
return FGA_InsertGeneratorLetterRep(s, LetterRepAssocWord(gen));
end );
InstallGlobalFunction( FGA_AutomInsertGeneratorLetterRep,
function(A, w)
A!.initial := FGA_InsertGeneratorLetterRep( A!.initial, w);
A!.terminal := A!.initial;
end );
InstallGlobalFunction( FGA_InsertGeneratorLetterRep,
function(s, w)
local i, t, bt, s1, s2;
t := FGA_trace(s, w);
bt := FGA_backtrace(s, w, t.index);
s1 := t.state;
s2 := bt.state;
if t.index > bt.index then # trace complete
FGA_coincidence(s1, s2);
else
if IsIdenticalObj(s1, s2) then
while w[t.index] = -w[bt.index] do
s1 := FGA_define(s1, w[t.index]);
t.index := t.index + 1;
bt.index := bt.index - 1;
od;
s2 := s1;
fi;
for i in [t.index .. bt.index-1] do
s1 := FGA_define(s1, w[i]);
od;
FGA_connect(s1, s2, w[bt.index]);
fi;
return FGA_find(s);
end );
InstallGlobalFunction( FGA_FromGroupWithGenerators,
# gens -> Iterated(gens, FGA_InsertGenerator, FGA_newstate()) );
function(G)
local s;
s := Iterated(GeneratorsOfGroup(G), FGA_InsertGenerator, FGA_newstate());
return Objectify( NewType( FamilyObj( G ),IsSimpleInvAutomatonRep),
rec(initial:=s, terminal:=s, group:=G) );
end );
InstallGlobalFunction( FGA_FromGeneratorsLetterRep,
function(gens,G)
local s;
s := Iterated(gens, FGA_InsertGeneratorLetterRep, FGA_newstate());
return Objectify( NewType( FamilyObj( G ),
IsSimpleInvAutomatonRep and IsMutable),
rec(initial:=s, terminal:=s, group:=G) );
end );
InstallGlobalFunction( FGA_Check,
function(s, w)
return IsIdenticalObj(FGA_deltas(s, w), s);
end );
InstallGlobalFunction( FGA_FindGeneratorsAndStates,
function(A)
local Q, Gens, nq, q, i, nr, freegens;
q := A!.initial;
nr := 0;
Gens := [];
q.repr := [];
Q := [q];
for nq in Q do
freegens := [];
nr := nr + 1;
nq.nr := nr;
for i in BoundPositions(nq.delta) do
q := nq.delta[i];
if IsBound(q.repr) then
if nq.repr = [] or nq.repr[Length(nq.repr)] <> -i then
Add(Gens, Concatenation(nq.repr, [i], -Reversed(q.repr)));
freegens[i] := Length(Gens);
fi;
else
q.repr := ShallowCopy(nq.repr);
Add(q.repr, i);
Add(Q, q);
fi;
od;
for i in BoundPositions(nq.deltainv) do
q := nq.deltainv[i];
if not(IsBound(q.repr)) then
q.repr := ShallowCopy(nq.repr);
Add(q.repr, -i);
Add(Q, q);
fi;
od;
if freegens <> [] then
nq.freegens := freegens;
fi;
od;
###
SetFGA_States(A, Q);
SetFGA_GeneratorsLetterRep(A, Gens);
end );
InstallGlobalFunction( FGA_initial,
A -> A!.initial );
InstallGlobalFunction( FGA_repr,
state -> state.repr );
InstallMethod( FGA_GeneratorsLetterRep,
"for simple inverse Automata",
[ IsSimpleInvAutomatonRep ],
function(A)
FGA_FindGeneratorsAndStates(A);
return FGA_GeneratorsLetterRep(A);
end );
InstallMethod( FGA_States,
"for simple inverse Automata",
[ IsSimpleInvAutomatonRep ],
function(A)
FGA_FindGeneratorsAndStates(A);
return FGA_States(A);
end );
InstallGlobalFunction( FGA_reducedPos,
function(A)
local i, states, n;
i := 0;
states := FGA_States(A);
repeat
i := i+1;
n := Size(BoundPositions(states[i].delta)) +
Size(BoundPositions(states[i].deltainv));
until n > 2 or ( n=2 and i=1);
return i;
end );
InstallGlobalFunction( FGA_Index,
function(A)
local states, r;
states := FGA_States(A);
r := Size(FreeGeneratorsOfWholeGroup(A!.group));
if ForAny( List( states, s -> s.delta),
delta -> not IsDenseList(delta) or Size(delta) <> r ) then
return infinity;
fi;
return Size(states);
end );
InstallGlobalFunction( FGA_AsWordLetterRepInFreeGenerators,
function(g,A)
local s,x,f,w;
FGA_States(A); # do work in the automaton if needed
w := [];
s := A!.initial;
for x in g do
if x > 0 then
if IsBound(s.freegens) and IsBound(s.freegens[x]) then
Add(w, s.freegens[x]);
fi;
s := ATf(s.delta, x);
if s = fail then
return fail;
fi;
else
s := ATf(s.deltainv, -x);
if s=fail then
return fail;
fi;
if IsBound(s.freegens) and IsBound(s.freegens[-x]) then
Add(w, -s.freegens[-x]);
fi;
fi;
od;
if IsNotIdenticalObj( s, A!.terminal ) then
return fail;
fi;
return w;
end );
#############################################################################
##
#E