Skip to content

Commit

Permalink
Merge pull request #16 from ruthhoffmann/ruth
Browse files Browse the repository at this point in the history
Streamlined UnionAutomata and IntersectionAutomaton
  • Loading branch information
ManuelAFDelgado authored Aug 29, 2024
2 parents 73b40b8 + eb4a4fa commit 254eaf6
Show file tree
Hide file tree
Showing 3 changed files with 217 additions and 83 deletions.
76 changes: 44 additions & 32 deletions gap/aut-basics.gi
Original file line number Diff line number Diff line change
Expand Up @@ -605,46 +605,58 @@ end);
## Produces the disjoint union of the automata A and B
##
InstallGlobalFunction(UnionAutomata, function(A, B)
local QA, T, a, i, I, F;

if not (IsAutomatonObj(A) and IsAutomatonObj(B)) then
Error("The arguments must be two automata");
fi;
if A!.type = "nondet" then
A := NFAtoDFA(A);
local QA, T, a, i, I, F, mA,mB;

if not (IsAutomatonObj( A ) and IsAutomatonObj( B )) then
Error( "The arguments must be two automata" );
fi;

if A!.type = "epsilon" then
A := NFAtoDFA(EpsilonToNFA(A));
fi;
if B!.type = "nondet" then
B := NFAtoDFA(B);
mA := AlphabetOfAutomaton(A)-1;
else
mA := AlphabetOfAutomaton(A);
fi;
if B!.type = "epsilon" then
B := NFAtoDFA(EpsilonToNFA(B));
mB := AlphabetOfAutomaton(B)-1;
else
mB := AlphabetOfAutomaton(B);
fi;
if not AlphabetOfAutomatonAsList(A) = AlphabetOfAutomatonAsList(B) then
Error("The arguments must be two automata over the same alphabet");
if mA <> mB then
Error( "The arguments must be two automata over the same alphabet" );
fi;

QA := A!.states;
T := StructuralCopy(A!.transitions);
for a in [1 .. B!.alphabet] do
for i in [1 .. B!.states] do
if B!.transitions[a][i] = 0 then
T[a][QA + i] := 0;
else
T[a][QA + i] := QA + B!.transitions[a][i];
fi;
od;
od;
I := ShallowCopy(A!.initial);
Add(I, QA + B!.initial[1]);
F := ShallowCopy(A!.accepting);
for i in B!.accepting do
Add(F, QA + i);
T := List( A!.transitions, ShallowCopy );
if A!.type <> "epsilon" and B!.type = "epsilon" then
Add(T,[]);
fi;

for a in [ 1 .. B!.alphabet ] do
for i in [ 1 .. B!.states ] do
if B!.transitions[a][i] = 0 then
T[a][QA + i] := 0;
else
T[a][QA + i] := QA + B!.transitions[a][i];
fi;
od;
od;
return(Automaton("nondet", QA + B!.states, AlphabetOfAutomatonAsList(A), T, I, F));
end);
if A!.type = "epsilon" and B!.type <> "epsilon" then
for i in [1..B!.states] do
Add(T[mA+1],0);
od;
fi;
I := ShallowCopy( A!.initial );
Append( I, QA + B!.initial );

F := ShallowCopy( A!.accepting );
Append( F, QA + B!.accepting );

if A!.type = "epsilon" or B!.type = "epsilon" then
return Automaton( "epsilon", QA + B!.states, mA+1, T, I, F );
else
return Automaton( "nondet", QA + B!.states, AlphabetOfAutomatonAsList( A ), T, I, F );
fi;
end );

#############################################################################
##
Expand Down
156 changes: 105 additions & 51 deletions gap/aut-func.gi
Original file line number Diff line number Diff line change
Expand Up @@ -887,79 +887,133 @@ end);
## expressions as arguments
##
InstallGlobalFunction(IntersectionLanguage, function(a1,a2)
local HashPair, ht, init, states, m, i, t1, t2, t, st, a,
nst, he, finals, p, q;
local HashPair, ht, init, states, m, s1, s2, t1, t2, t, i, st, a, x, y, nst, he, finals, p, q, numofinitst,m1,m2;

if IsAutomaton(a1) then
elif IsRationalExpression(a1) then
a1 := RatExpToAut(a1);
if IsAutomaton( a1 ) then
;
elif IsRationalExpression( a1 ) then
a1 := RatExpToAut( a1 );
else
Error("The first argument must be an automaton or a rational expression");
Error( "The first argument must be an automaton or a rational expression" );
fi;
if IsAutomaton(a2) then
elif IsRationalExpression(a2) then
a2 := RatExpToAut(a2);
if IsAutomaton( a2 ) then
;
elif IsRationalExpression( a2 ) then
a2 := RatExpToAut( a2 );
else
Error("The second argument must be an automaton or a rational expression");
Error( "The second argument must be an automaton or a rational expression" );
fi;

if not AlphabetOfAutomatonAsList(a1) = AlphabetOfAutomatonAsList(a2) then
Error("A1 and A2 must have the same alphabet");
if a1!.type = "epsilon" then
m1 := AlphabetOfAutomaton(a1)-1;
else
m1 := AlphabetOfAutomaton(a1);
fi;

if a1!.type = "nondet" then
a1 := NFAtoDFA(a1);
if a2!.type = "epsilon" then
m2 := AlphabetOfAutomaton(a2)-1;
else
m2 := AlphabetOfAutomaton(a2);
fi;
if a2!.type = "nondet" then
a2 := NFAtoDFA(a2);
if m1 <> m2 then
Error( "The arguments must be two automata over the same alphabet" );
fi;
if a1!.type = "epsilon" then
a1 := NFAtoDFA(EpsilonToNFA(a1));

if a1!.type <> "epsilon" then
a1 := Automaton("epsilon", a1!.states, a1!.alphabet+1, Concatenation(a1!.transitions, [ListWithIdenticalEntries(a1!.states,[])]), a1!.initial, a1!.accepting);
fi;
if a2!.type = "epsilon" then
a2 := NFAtoDFA(EpsilonToNFA(a2));
if a2!.type <> "epsilon" then
a2 := Automaton("epsilon", a2!.states, a2!.alphabet+1, Concatenation(a2!.transitions, [ListWithIdenticalEntries(a2!.states,[])]), a2!.initial, a2!.accepting);
fi;
a1 := NullCompletionAut(a1);
a2 := NullCompletionAut(a2);

HashPair := s->HashKeyBag(s,57,0,12);
HashPair := function ( s )
return HashKeyBag( s, 57, 0, 3*GAPInfo.BytesPerVariable );
end;
ht := SparseHashTable( HashPair );
init := [ ];
for s1 in a1!.initial do
for s2 in a2!.initial do
Add(init, [ s1, s2 ]);
AddHashEntry( ht, [ s1, s2 ], Length( init ) );
od;
od;

numofinitst:=Length(init);

ht := SparseHashTable(HashPair);
init := [a1!.initial[1],a2!.initial[1]];
AddHashEntry(ht,init,1);
states := [init];
states := init;
m := a1!.alphabet;
i := 1;
t1 := a1!.transitions;
t2 := a2!.transitions;
t := List([1..m],x->[]);
while i <= Length(states) do
st := states[i];
for a in [1..m] do
nst := [t1[a][st[1]],t2[a][st[2]]];
MakeImmutable(nst);
he := GetHashEntry(ht,nst);
if he = fail then
Add(states,nst);
he := Length(states);
AddHashEntry(ht,nst,he);
fi;
t[a][i] := he;
od;
i := i+1;
t := List( [ 1 .. m ], x -> [ ] );

while i <= Length( states ) do
st := states[i];
for a in [ 1 .. m ] do
t[a][i] := [ ];
if a = m then
for x in t1[a][st[1]] do
nst := [ x, st[2] ];
MakeImmutable( nst );
he := GetHashEntry( ht, nst );
if he = fail then
Add( states, nst );
he := Length( states );
AddHashEntry( ht, nst, he );
fi;
Add(t[a][i], he);
od;
for x in t2[a][st[2]] do
nst := [ st[1], x ];
MakeImmutable( nst );
he := GetHashEntry( ht, nst );
if he = fail then
Add( states, nst );
he := Length( states );
AddHashEntry( ht, nst, he );
fi;
Add(t[a][i], he);
od;
else
for x in t1[a][st[1]] do
for y in t2[a][st[2]] do
nst := [ x, y ];
MakeImmutable( nst );
he := GetHashEntry( ht, nst );
if he = fail then
Add( states, nst );
he := Length( states );
AddHashEntry( ht, nst, he );
fi;
Add(t[a][i], he);
od;
od;
fi;
od;
i := i + 1;
od;
finals := [];
for p in a1!.accepting do
for q in a2!.accepting do
he := GetHashEntry(ht,[p,q]);
finals := [ ];
for p in a1!.accepting do
for q in a2!.accepting do
he := GetHashEntry( ht, [ p, q ] );
if he <> fail then
AddSet(finals,he);
AddSet( finals, he );
fi;
od;
od;
od;
return Automaton("det",Length(states),AlphabetOfAutomatonAsList(a1),t,[1],finals);
end);

init := [ ];
for s1 in a1!.initial do
for s2 in a2!.initial do
he := GetHashEntry( ht, [ s1, s2 ] );
if he <> fail then
Add( init, he );
fi;
od;
od;

return Automaton( "epsilon", Length( states ), AlphabetOfAutomatonAsList( a1 ), t, [ 1 .. numofinitst ], finals );

end );



Expand Down
68 changes: 68 additions & 0 deletions tst/testall.tst
Original file line number Diff line number Diff line change
Expand Up @@ -888,6 +888,74 @@ Accepting state: [ 1 ]
gap> NW := InverseAutomatonToGenerators(A);
[ 2, "baBA", "bbA" ]

gap> a1:=RationalExpression("(bUcUd)*ab*");
(bUcUd)*ab*
gap> a2:=RationalExpression("(acUd)*(aU@)");
(acUd)*(aU@)
gap> IntersectionAutomaton(a1,a2);
Error, The arguments must be two automata over the same alphabet

gap> a2:=RationalExpression("(acUd)*(aUb)");
(acUd)*(aUb)
gap> IntersectionAutomaton(a1,a2);
< epsilon automaton on 5 letters with 9 states >

gap> UnionAutomata(a1,a2);
Error, The arguments must be two automata
gap> a1:=RatExpToAut(RationalExpression("(bUcU@)*ab*"));
< deterministic automaton on 3 letters with 3 states >
gap> a2:=RatExpToAut(RationalExpression("(acUd)*(@Ub)"));
< deterministic automaton on 4 letters with 4 states >
gap> UnionAutomata(a1,a2);
Error, The arguments must be two automata over the same alphabet

gap> a1:=RatExpToAut(RationalExpression("(bUcU@)*ab*"));
< deterministic automaton on 3 letters with 3 states >
gap> a2:=RatExpToAut(RationalExpression("(acUd)*(@Ub)"));
< deterministic automaton on 4 letters with 4 states >
gap> UnionAutomata(a1,a2);
Error, The arguments must be two automata over the same alphabet

gap> x:=Automaton("epsilon",3,"01@",[[,[2],[3]],[[1,3],,[1]],[[1],[2],[2]]],[2],[2,3]);;
gap> y:=Automaton("epsilon",3,"01@",[ [ [ 3 ], [ 1 ], [ 1, 2 ] ], [ [ ], [ ], [ 1, 3 ] ], [ [ 1, 3 ], [ 1 ], [ 3 ] ] ],[ 1, 2, 3 ],[ 1, 3 ]);;
gap> UnionAutomata(x,y);
< epsilon automaton on 3 letters with 6 states >
gap> IntersectionAutomaton(x,y);
< epsilon automaton on 3 letters with 3 states >

gap> x:=Automaton("det",3,2,[ [ 0, 2, 0 ], [ 0, 1, 0 ] ],[ 3 ],[ 2 ]);;
gap> y:=Automaton("nondet",3,2,[[,[1,3],],[,[2,3],[1,3]]],[1,2],[1,3]);;
gap> UnionAutomata(x,y);
< non deterministic automaton on 2 letters with 6 states >
gap> UnionAutomata(y,x);
< non deterministic automaton on 2 letters with 6 states >
gap> IntersectionAutomaton(x,y);
< epsilon automaton on 3 letters with 2 states >
gap> IntersectionAutomaton(y,x);
< epsilon automaton on 3 letters with 2 states >

gap> x:=Automaton("epsilon",3,"01@",[[,[2],[3]],[[1,3],,[1]],[[1],[2],[2]]],[2],[2,3]);;
gap> y:=Automaton("nondet",3,2,[[,[1,3],],[,[2,3],[1,3]]],[1,2],[1,3]);;
gap> UnionAutomata(x,y);
< epsilon automaton on 3 letters with 6 states >
gap> UnionAutomata(y,x);
< epsilon automaton on 3 letters with 6 states >
gap> IntersectionAutomaton(x,y);
< epsilon automaton on 3 letters with 3 states >
gap> IntersectionAutomaton(y,x);
< epsilon automaton on 3 letters with 3 states >

gap> x:=Automaton("epsilon",3,"01@",[[,[2],[3]],[[1,3],,[1]],[[1],[2],[2]]],[2],[2,3]);;
gap> y:=Automaton("det",3,2,[ [ 0, 2, 0 ], [ 0, 1, 0 ] ],[ 3 ],[ 2 ]);;
gap> UnionAutomata(x,y);
< epsilon automaton on 3 letters with 6 states >
gap> UnionAutomata(y,x);
< epsilon automaton on 3 letters with 6 states >
gap> IntersectionAutomaton(x,y);
< epsilon automaton on 3 letters with 1 states >
gap> IntersectionAutomaton(y,x);
< epsilon automaton on 3 letters with 1 states >


gap> STOP_TEST( "testall.tst", 10000 );
## The first argument of STOP_TEST should be the name of the test file.
Expand Down

0 comments on commit 254eaf6

Please sign in to comment.