Package: dfa
DFAA simple python implementation of a DFA.Table of ContentsInstallationUsageMembership QueriesTransitions and TracesNon-boolean output alphabetsMoore MachinesDFA <-> DictionaryComputing Reachable StatesSampling PathsRunning interactively (Co-Routine API)Visualizing DFAsFeatures:State can be any Hashable object.Alphabet can be any finite sequence of Hashable objects.Designed to be immutable and hashable (assuming components are
immutable and hashable).Design choice to allow transition map and accepting set to be
given as functions rather than an explicitdictorset.InstallationIf you just need to usedfa, you can just run:$ pip install dfaFor developers, note that this project uses thepoetrypython package/dependency
management tool. Please familarize yourself with it and then
run:$ poetry installUsageThedfaapi is centered around theDFAobject.By default, theDFAobject models aDeterministic Finite Acceptor,
e.g., a recognizer of a Regular Language.Example Usage:fromdfaimportDFAdfa1=DFA(start=0,inputs={0,1},label=lambdas:(s%4)==3,transition=lambdas,c:(s+c)%4,)dfa2=DFA(start="left",inputs={"move right","move left"},label=lambdas:s=="left",transition=lambdas,c:"left"ifc=="move left"else"right",)Membership Queriesassertdfa1.label([1,1,1,1])assertnotdfa1.label([1,0])assertdfa2.label(["move right"]*100+["move left"])assertnotdfa2.label(["move left","move right"])Transitions and Tracesassertdfa1.transition([1,1,1])==3assertlist(dfa1.trace([1,1,1]))==[0,1,2,3]Non-boolean output alphabetsSometimes, it is useful to model an automata which can label a word
using a non-Boolean alphabet. For example,{True, False, UNSURE}.TheDFAobject supports this by specifying the output alphabet.UNSURE=Nonedefmy_labeler(s):ifs%4==2:returnNonereturn(s%4)==3dfa3=DFA(start=0,inputs={0,1},label=my_labeler,transition=lambdas,c:(s+c)%4,outputs={True,False,UNSURE},)Note:Ifoutputsis set toNone, then no checks are done that
the outputs are within the output alphabet.dfa3=DFA(start=0,inputs={0,1},label=my_labeler,transition=lambdas,c:(s+c)%4,outputs=None,)Moore MachinesFinally, by reinterpreting the structure of theDFAobject, one can
model a Moore Machine. For example, in 3 state counter,dfa1, the
Moore Machine can output the current count.assertdfa1.transduce(())==()assertdfa1.transduce((1,))==(False,)assertdfa1.transduce((1,1,1,1))==(False,False,False,True)Language QueriesUtility functions are available for testing if a language:Is empty:utils.find_wordIs equivilent to another language:utils.find_equiv_counterexampleIs a subset of a another language:utils.find_subset_counterexampleThese operate by returningNoneif the property holds, i.e.,lang(dfa1) = ∅, lang(dfa1) ≡ lang(dfa2), lang(dfa1) ⊆ lang(dfa2), and
returning a counterexampleWordotherwise.DFA <-> DictionaryNote thatdfaprovides helper functions for going from a dictionary
based representation of a deterministic transition system to aDFAobject and back.fromdfaimportdfa2dict,dict2dfa# DFA encoded a nested dictionaries with the following# signature.#     <state>: (<label>, {<action>: <next state>})dfa_dict={0:(False,{0:0,1:1}),1:(False,{0:1,1:2}),2:(False,{0:2,1:3}),3:(True,{0:3,1:0})}# Dictionary -> DFAdfa=dict2dfa(dfa_dict,start=0)# DFA -> Dictionarydfa_dict2,start=dfa2dict(dfa)assert(dfa_dict,0)==(dfa_dict2,start)Computing Reachable States# Perform a depth first traversal to collect all reachable states.assertdfa1.states()=={0,1,2,3}Finding Words and Access stringsTo generate accepting strings (words) in a DFA (breadth first using string length) one can use thedfa.utils.wordsfunction:fromdfa.utils.importdfa2dict,words,find_wordsdfa_dict={0:(False,{0:0,1:1}),1:(False,{0:1,1:2}),2:(False,{0:2,1:3}),3:(True,{0:3,1:0})}lang=dict2dfa(dfa_dict,start=0)xs=set(fn.take(5,words(lang)))assertlen(xs)==5assertall(lang.label(x)forxinxs)To get a single word, a helper function is provided indfa.utils.find_wordwhich returnsNoneif the language of the DFA is empty:# ... Same as above ...x=find_word(lang)assertxisnotNoneassertlang.label(x)Often times, it is useful to sample a path between two states, sayaandb.dfasupports this usingdfa.utils.paths. This function
returns a generator of words,w, such thatdfa.transition(w, start=b) == a. For example:fromdfa.utilsimportpathsaccess_strings=paths(dfa1,start=0,end=1,# Optional. If not specified returns all paths# starting at `start`.max_length=7,#  Defaults to float('inf')randomize=True,#  Randomize the order. Shorter paths still found first.)forwordinaccess_strings:assertdfa1.transition(word,start=0)==1DFA minimizationDFAs can be minimized using theminimizemethod.my_dfa=my_dfa.minimize()DFA advancement (progression)One can create the DFA starting at the state indexed by a given word by using
theadvancemethod.my_dfa=my_dfa.advance(word)Running interactively (Co-Routine API)dfasupports interactively stepping through aDFAobject via
co-routines. This is particularly useful when using DFA in a control
loop. For example, the following code counts how many1's it takes
to advancedfa1's state back to the start state.machine=dfa1.run()next(machine)state=Nonecount=0whilestate!=dfa1.start:count+=1state=machine.send(1)Visualizing DFAsdfaoptionally supports visualizing DFAs using graphviz. To use this
functionality be sure to installdfausing with thedrawoption:pipinstalldfa[draw]orpoetryinstall-EdrawThen one can simply usedfa.draw.write_dotto write a.dotfile
representing the DFA. This.dotfile can be rendered using any
graphviz supporting tool.fromdfa.drawimportwrite_dotwrite_dot(dfa1,"path/to/dfa1.dot")Using thedotcommand in linux results in the following rendering ofdfa1.$ dot -Tsvg path/to/dfa1.dot > dfa1.svgVisualization of dfa1 using graphviz.