GithubHelp home page GithubHelp logo

caleb531 / automata Goto Github PK

View Code? Open in Web Editor NEW
331.0 331.0 63.0 2 MB

A Python library for simulating finite automata, pushdown automata, and Turing machines

Home Page: https://caleb531.github.io/automata/

License: MIT License

Python 98.75% TeX 1.25%
automata automaton finite-state-machine fsm pushdown-automata python turing-machine turing-machines

automata's People

Contributors

abhinavsinha-adrino avatar caleb531 avatar camilomartinezm avatar christopher-hampson avatar danielskatz avatar dengl11 avatar dependabot[bot] avatar eduardogoulart1 avatar eliotwrobson avatar euanong avatar khoda81 avatar mohamedgouaouri avatar progirep avatar tagl avatar vipul-cariappa avatar ytvwld avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

automata's Issues

NFA can't take same input symbol to different state

Hi,
First of all congratulations for the library. I didn't find a way how to insert same symbol to a different state. Let's assume this automata for example:

image

So in this case with the state is in q0, when receiving 1 could be at q0 or q1. I think that the data structure to declare transitions is not ready for this use case because a dict in python can not have same key with different values. I guess the data structure should change to a list more or less like this:

transitions={
    'q0': [ {'1': 'q0'}, {'1': 'q1'},  {'2': 'q1'} ],
    'q1': [ {'2': 'q0'} ]
}

Thanks

Duplicating states when removing lambdas of NFA

It seems to be duplicating every state when I ask to remove the epsilons.

from automata.fa.nfa import NFA

pattern = 'test|ok'
nfa = NFA.from_regex(pattern)
nfa = nfa.eliminate_lambda()
nfa.show_diagram('test.png')

Output:
image

Regex parsing efficiency

In looking at the current regex parser, it seems like it could be made significantly faster with a more robust implementation (i.e. use specialized classes to reduce overhead when applying the NFA creation operations). We're working on a fast regex parser for our course (since we need to parse large regexes). Would there be interest in adapting a version for integration with this library?

Allow Turing Machine input set to be equal to the tape symbol set

Right now the input symbols has to be a proper subset of the tape symbols (link below). But in our case we would like to have the input equal the allowed tape symbols.

This worked for us locally

    def _read_input_symbol_subset(self):
        if not (self.input_symbols <= self.tape_symbols):
            raise exceptions.MissingSymbolError(
                'The set of tape symbols is missing symbols from the input '
                'symbol set ({})'.format(
                    self.tape_symbols - self.input_symbols))

https://github.com/caleb531/automata/blob/main/automata/tm/tm.py#L14

Implement Automaton __iter__ for enumerating accepted language?

@eliotwrobson So with all the recent discussion around automata ultimately being representations of finite languages, it got me thinking:

What if we implemented an __iter__ method that produced each string in that automaton's accepted language? My thought it we could generate one string at a time via yield, and leave it up to the user if they want to convert the automaton to a list or set. Ideally, we could start with DFAs/NFAs, and then explore if a similar algorithm is possible with PDAs and TMs.

How difficult do you think this would be to implement from an algorithmic perspective? Initially, my thought is generating all permutations of the machine's input symbols, then filter that set down to only those accepted via accepts_input.

Although the number of iterations could be enormous, so maybe we could filter that set further by analyzing transition symbols from the initial state (first character) and final states (possible last characters). But I'm curious if there is an approach that isn't so brute-force.

Conversion from DFA to NFA does not properly handle state names of mixed data types

I found the following bug:
nfa._add_new_state introduces integers into the set of states.

Current code is:

    def _add_new_state(state_set, start=0):
        """Adds new state to the state set and returns it"""
        new_state = start
        while new_state in state_set:
            new_state += 1

        state_set.add(new_state)

        return new_state

which could be fixed, for example, in this way:

    def _add_new_state(state_set, start=0):
        """Adds new state to the state set and returns it"""
        new_state = start
        while str(new_state) in state_set:
            new_state += 1

        state_set.add(str(new_state))

        return str(new_state)

EDIT: this bugfix actually runs into other problems later - this is just an idea


This can lead to logic problems later. For example the following code fails:

from automata.fa.dfa import DFA
from automata.fa.nfa import NFA

A = NFA(
    states={'0', '1'},
    input_symbols={'0'},
    transitions={'0': {'0': {'1'}}, '1': {'0': {'1'}}},
    initial_state='0',
    final_states={'1'}
)

print(A.reverse().states)

B = DFA.from_nfa(A.reverse())

assert(A.accepts_input('00')==B.accepts_input('00'))

Not really an issue just hoping to get some help on usage.

I am very new to automata and have been learning a lot recently having this library has made things so much more fun and interactive for me, so I want to say thanks for all the hard work you all have put into this library.

I am trying to implement an NPDA, and I have struggled to configure the transitions properly. I have reviewed the palindrome example you all have on the doc page. Still, because it was not something I had initially visualized as a state diagram, I am having trouble connecting things in such a way that I can implement my own. If someone could maybe take a few minutes to explain how this simple PDA would be implemented, that would be very helpful to me, and I am sure I could then understand them enough to start doing my own properly. The PDA is a simple a^nb^n, n >=0.

image

Missing branch coverage for DFA.random_word()

@Tagl I am getting pretty close to releasing v7 (hooray!). However, I noticed that there was a slight drop in code coverage with the introduction of DFA.random_word(). Since you implemented that method, I wanted to ask for your help in covering that additional case.

From what I can tell, the outer loop never runs for more than one iteration (either that, or it never completes without a break). In your tests, you seem to be testing high-enough values of k, so I suspect the real gap may be not testing random_word() with a partial DFA (i.e. allow_partial=True). But I could certainly be wrong. 😅

I would appreciate any help you can offer. Screenshots are provided below.

HTML coverage report (locally):
Screenshot 2022-11-10 at 11 40 29 AM

Coveralls Report (link)
Screenshot 2022-11-10 at 11 39 11 AM

Unable to install 5.0.0 with pip

When I run pip install automata-lib==5.0.0, I get the following error message:

ERROR: Could not find a version that satisfies the requirement automata-lib==5.0.0 (from versions: 5.0.0.macosx-10.15-x86_64, 1.0.0.post1, 1.0.0.post2, 1.0.0.post3, 1.0.0.post4, 2.0.0, 2.0.1, 2.1.0, 3.0.0, 3.1.0, 3.1.0.post1, 4.0.0, 4.0.0.post1)
ERROR: No matching distribution found for automata-lib==5.0.0

Make validation optional

I was trying to optimize some code of mine using the library (billions of DFA operations) so I ran a profiler and saw a lot of time was spent doing unions, specifically in PartitionRefinement.refine, cross product graph creation and then what surprised me was the __init__ function. And half of the time was due to validation.
I think validation should be optional, so the user could pass in validate=False as a keyword argument when the user is a 100% sure that the construction is valid.

Thoughts on this?

Feature request: Supporting transition with logical operations on input symbols

I propose adding the functionality to define transition rules which are a logical combination of the input symbols, like:

from automata.fa.dfa import DFA

dfa = DFA(
    states={'q0', 'q1'},
    input_symbols={'x', 'y'},
    transitions={
        'q0': {'x': 'q0', '!x': 'q1'},  # go to q1 if neg x
        'q1': {'x & y': 'q0', 'y': 'q1'}  # go to q1 if x and y
    },
    initial_state='q0',
    final_states={'q1'}
)

print(dfa.read_input('!xy(x & y)'))  # produces q0

One idea for implementation would be to use parser generator libraries such as the good old PLY or its new refreshed version, SLY.

Performance improvement for equality checking

The __eq__ function in the DFA class uses the symmetric difference between both DFAs, which is an O(n^2) time algorithm (and requires creating an auxiliary DFA). The Hopcroft-Karp algorithm doesn't require creating the auxiliary data structures, but requires the use of a union find. I think it's possible to copy in the code from a generic Python Union-Find, but the networkx package has an implementation. Is it too much to introduce this dependency? Any generic union find should work.

Help on user input

Hi im traying to build a program that let you introduce your own NFA and transform it to DFA.
I would like to recive some advice

So I would like to know what would be the best option for TRANSITIONS user input.
image
(In the case where the symbol is repeated)

I have someting like this but it doesn't work, cause how dicts works.
In a while:
transitions_aux ={
input('State?: '): { input('value: '): set(input('transitionState:').split()) },
}
transitions.update(transitions_aux)

Should the user be ask just for a string instead?

Sorry for bad english.

(NFA) InvalidStateError: end state q for transition on q0 is not valid

hey i ve getting this error while using "nfa.accepts_input(my_input_str)" in my project , i tried the same structure of the automata in a seperate file and it functionned properly , im wondering if anyone could guide me a little to figure out what to fix ( or at least the scenarios that may lead to this error thanks!
here's my project link
https://github.com/maryembenali/automata.git

automata.base.exceptions.InvalidStateError: end state q for transition on q0 is not valid

Feature Request: NFA <-> Regular Expression

Hello,

Thank you for your work and very nice library.

If ever you get the time, would it be possible to implement the conversion NFA <-> Regular expression ?

The direction Regex -> NFA is easy, the other one more annoying. A good reference is "Introduction to the Theory of Computation" by Sisper, chapter 1, definition 1.64 for the concept of "generalised nondeterministic finite automaton" which is useful in the conversion NFA -> Regex.

Thank you very much again,
Tristan

Make Automaton instances immutable

@eliotwrobson I want to run something by you that I've been mulling over for the past week.

Part 1: Precomputing lambda closures was a good idea

When you integrated networkx into the library back in ed1cdff, you added logic to precompute+cache the lambda closures for an NFA (via self._lambda_closure_dict). Previously, these lambda closures were (arguably excessively) computed on the fly when reading input via my NFA._get_lambda_closure method.

Given the performance/efficiency consequences of my previous approach, I very much appreciate your contribution here (enough so that I am planning to expose this dictionary as part of the public API, as you may have noticed in my recent commits to develop).

Part 2: Internal Automaton state

However, your changes do introduce an interesting dilemma. You see, before Automata v6 was released with your precomputed lambda closure logic, NFA instances could be mutated without consequence just like every other Automaton instance in the library.

But now that lambda_closures (a derived attribute, in database terms) is in the picture, the NFA instance could end up in a situation where the user updates states, transitions, and/or input_symbols, leaving lambda_closures stale.

As I see it, there are three solutions to this stale lambda_closures situation if we want NFAs to remain fully mutable:

  1. We watch for changes to states, input_symbols, and transitions (which is a nested structure) and automatically recompute lambda_closures when any of the aforementioned attributes change; this is the ideal solution, but not sure how feasible it is to implement

  2. We require the user to explicitly recompute the lambda closures (via some public method) when they choose to mutate the NFA. This is not my favorite approach, as the precomputing of lambda closures is really an implementation detail the user should not be concerned with. But it would balance implementation simplicity with performance.

  3. We revert to caching nothing and computing the lambda closures on the fly when reading input; naturally, this would re-introduce a significant performance consequence, especially if you want to read many input strings with the same NFA instance

Of course, if we make NFAs fully immutable, then this problem goes away, and we don't need to worry about recomputing lambda_closures post-instantiation.

Part 3: Mutable or immutable?

So far, my blurb above is focused primarily on NFAs, but I think it opens up a larger question about whether any automaton should be a mutable structure, or whether it should be immutable. My ultimate aim for this library is to have a consistent and friendly API that makes working with these data structures pleasant and practical. And right now, NFAs are inconsistent with the rest of the Automaton sub-types because NFAs have some additional internal state (i.e. lambda_closures) which must be managed.

Questions for you

Therefore, I will sum up my musings with a few questions regarding your own use of this library:

  1. How frequently are you reading input with the same automaton?
  2. How frequently (if ever) do you mutate an automaton after its instantiation?
  3. What are your thoughts about making all automata instances immutable?
  4. Anything else you want to add?

@abhinavsinha-adrino @Tagl If you guys happen to have a moment, I would love any feedback you have as well. This library isn't any good if it doesn't serve real-world use cases for working with these machines.

Much thanks in advance,
Caleb

Implement NFA features (e.g. input reading) for GNFA

@abhinavsinha-adrino I'm starting to wonder if I misunderstood the purpose of GNFAs when I suggested that we subclass GNFA from NFA. Let me explain the context:

I was trying to write a test for GNFA just now to verify that GNFA.read_input_stepwise() operates as expected. However, I was encountering one error after another in trying to accomplish this. I then realized that, because GNFAs can have non-state values (i.e. regex strings and None) in the transition map, maybe it doesn't make sense to use methods like read_input_stepwise on a GNFA.

This then made me wonder if it was a mistake to subclass GNFA from NFA, since it seems like there could be a whole bunch of methods (like read_input_stepwise) that either would fail or just wouldn't make sense to have available on GNFA.

What do you think? Given what you know about Automata theory and the purpose of GNFAs, would it make sense to have functional methods like GNFA.read_input_stepwise? Would it make more sense semantically to subclass GNFA from FA instead of NFA?

Feature request: DFA Union, Intersection, Complement, etc.

I would like to have operations like these implemented within the library. The most common operations used are probably:

  • Union
  • Intersection
  • Complement
  • Concatenation
  • Reverse
  • Kleene star

Another useful operation would be checking for equivalence of two DFAs.
If an equivalence check is added should the eq method be overloaded with the equivalence check?
I'd like to open a discussion on what additional operations could be added to the library.
I have implementations for all the listed operations in mind and I have already written code for the first three so I'll gladly add these operations myself.

New Construction Idea

@caleb531 was poking around online and found this blog post talking about constructing NFAs that recognize words within a given edit distance of a target word, along with some other cool utilites: http://blog.notdot.net/2010/07/Damn-Cool-Algorithms-Levenshtein-Automata

I think it could potentially be easy to integrate some of these ideas as well, since the blog post provides worked out examples (that could be used as tests) and code implementing these in Python (that should be easy to adapt). What do you think?

EDIT:
After more googling I guess this is like a thing or something, because here's two more blog posts implementing faster algorithms (in Python!):

https://julesjacobs.com/2015/06/17/disqus-levenshtein-simple-and-fast.html
https://fulmicoton.com/posts/levenshtein/

Visual Automata

Hi!

Would you consider implementing the Visual Automata wrapper directly into automata-lib and deprecating Visual Automata?

Support for quantifiers and the shuffle operator in regular expressions

Is there a specific reason why quantifiers for regular expressions are not supported? (so things like a{2,10}) If there is no specific reason, would you be open to a PR that implements it?

Also, what do you think of adding a special regex shuffle operator? This would be very handy to express languages like "all permutations of a,b,c". My proposal would be to use the ~ character and implement a separate parser for that

Type Annotations

Hello,

Thanks for your work on this library! I've been using it as part of the backend for a course I'm TAing. For our purposes, we're working on adding type hints (which can be checked with mypy) to the library. We think this makes the code more readable, and easier to tell what certain functions do. Would there be interest in including these changes in this project? I'd be happy to put up a pull request once we're done.

Best,
Eliot

Adding/applying black to develop branch

@eliotwrobson Another thought for you:

Since I don't know when #129 is going to be finished, I was thinking: it might be nice to backport the black configuration out of your PR into a separate PR to be merged into develop branch. Then I could commit to the feature branch in #129 and run black on the modified files so that they wouldn't conflict with your type hints branch anyway.

In other words, if we can add configuration for black to the develop branch, and apply it to all Python files on develop, and apply it to all files in the open PRs, then we're all on the same page, and we won't have to worry about formatting conflicts ever again. This would also enable us to re-add the CI job sooner.

Thoughts?

How to express the empty string in regexes?

Is there a way to specify the empty string as part of regexes? For example, Python's built-in re.compile allows expressions like '()' or '|', but the parser in this library throws an error on both.

Bug: NFA doesn't support non-string states

from automata.fa.nfa import NFA

nfa = NFA(
    states={0},
    input_symbols={'0'},
    transitions={0: {}},
    initial_state=0,
    final_states=set()
)

print(nfa.accepts_input(''))

I would expect this to print False, because the NFA has no accepting states. Instead, it throws this error:

File "/usr/local/lib/python3.8/site-packages/automata/base/automaton.py", line 41, in accepts_input
    self.read_input(input_str)
  File "/usr/local/lib/python3.8/site-packages/automata/base/automaton.py", line 34, in read_input
    for config in validation_generator:
  File "/usr/local/lib/python3.8/site-packages/automata/fa/nfa.py", line 120, in read_input_stepwise
    self._check_for_input_rejection(current_states)
  File "/usr/local/lib/python3.8/site-packages/automata/fa/nfa.py", line 104, in _check_for_input_rejection
    ', '.join(current_states)))
TypeError: sequence item 0: expected str instance, int found

Would it be possible to replace ', '.join(current_states) with ', '.join(str(state) for state in current_states), or something along those lines?

bug in dfa.minify

I'm using version 5.0.0.

I have a generated DFA that essentially models a room and 2 persons, 0 and 1. The states are labeled "L-XY" where L is the distance from the initial state, X is 0 or 1 depending whether person 0 is in the room, and Y same for person 1. The edges are labeled "xE" or "xL" depending on whether person x enters or leaves the room. When I minify this DFA, I get a one state DFA instead of a four state DFA (0 in the room or not, 1 in the room or not). Code included below.

=============================

from automata.fa.dfa import DFA

xstates = { "0-00", "1-01", "1-10", "2-00", "2-11", "2-00", "3-10", "3-01", "4-00", "errr" }
xinitial = "0-00"
xfinal = set()
xsymbols = { "0E", "0L", "1E", "1L" }
xtransitions = {
"0-00": { "1E": "1-01", "0E": "1-10", "0L": "errr", "1L": "errr" },
"1-01": { "1L": "2-00", "0E": "2-11", "1E": "errr", "0L": "errr" },
"1-10": { "0L": "2-00", "1E": "2-11", "0E": "errr", "1L": "errr" },
"2-00": { "1E": "1-01", "0E": "3-10", "1L": "errr", "0L": "errr" },
"2-11": { "1L": "3-10", "0L": "3-01", "1E": "errr", "0E": "errr" },
"2-00": { "0E": "1-10", "1E": "3-01", "0L": "errr", "1L": "errr" },
"3-10": { "1E": "2-11", "0L": "4-00", "1L": "errr", "0E": "errr" },
"3-01": { "0E": "2-11", "1L": "4-00", "0L": "errr", "1E": "errr" },
"4-00": { "0E": "3-10", "1E": "3-01", "0L": "errr", "1L": "errr" },
"errr": { "0E": "errr", "1E": "errr", "0L": "errr", "1L": "errr" }
}

dfa = DFA(
states=xstates,
input_symbols=xsymbols,
transitions=xtransitions,
initial_state=xinitial,
final_states=xfinal
)

m = dfa.minify()
assert len(m.states) == 4, len(m.states)

Rename Automaton.attributes()

@eliotwrobson So you probably recall that I recently added an Automaton.attributes() method to retrieve all the public (non-underscore-prefixed) attributes on an Automaton instance, inspired by @Tagl's recent PR. However, I find the current name a little ambiguous, since some members that are technically considered attributes are excluded from the resulting dict.

Here are some alternative ideas I have, mostly based on the observation that every public Automaton attribute also appears to be an input parameter to its respective __init__:

  • parameters
  • input_parameters
  • input_params

I'm also considering if this would be better as a @property rather than a method. I like the brevity of omitting any verbiage, but on the flip side, want to property communicate that this method isn't an input parameter in itself.

Thoughts?

Eliminate Lambda/epsilon

I have made a method to transform an ε-NFA to an NFA, or eliminating lambda/epsilon if you will.
Since this is a step in the process of transforming an ε-NFA to DFA, I thought it could be nice to include it in this project?

             0   1   λ
→*q0         ∅  q1  q2
q1    {*q0,q2}  q2   ∅
q2           ∅   ∅   ∅

nfa

transforms to:

             0   1
→*q0         ∅  q1
q1    {*q0,q2}  q2
q2           ∅   ∅

eliminated_nfa

NFA->DFA: when initial state has epsilon transition

Thanks for the awesome library!

I think that there might be a bug for DFA.from_nfa. When the initial state has epsilon transition, the initial states for the NFA do not include other DFA states that can be reached from the DFA initial state via epsilon transition.

Here is a script that I tested:

···
nfa = NFA(
states={'q0', 'q1', 'q2'},
input_symbols={'a', 'b'},
transitions={
'q0': {'': {'q2'}},
'q1': {'a': {'q1'}},
'q2': {'a': {'q1'}}
},
initial_state='q0',
final_states={'q1'}
)

print("NFA: {}".format(nfa.read_input('a')))

dfa = DFA.from_nfa(nfa) # returns an equivalent DFA
print("DFA: ")
print("Initial State: {}".format(dfa.initial_state))
print(dfa.read_input('a') )
···

And here is the output:

NFA: {'q1'}
DFA:
Initial State: {q0}
Traceback (most recent call last):
  File "nfa.py", line 23, in <module>
    print(dfa.read_input('a') )
  File "/usr/local/lib/python3.6/site-packages/automata/base/automaton.py", line 34, in read_input
    for config in validation_generator:
  File "/usr/local/lib/python3.6/site-packages/automata/fa/dfa.py", line 105, in read_input_stepwise
    self._check_for_input_rejection(current_state)
  File "/usr/local/lib/python3.6/site-packages/automata/fa/dfa.py", line 89, in _check_for_input_rejection
    current_state))
automata.base.exceptions.RejectionException: the DFA stopped on a non-final state ({})

Allow DFA transition function to be partial

When I studied Automata Theory (and in the literature), the transition function for a DFA could be partial, this is, that there could be some states which do not have an associated transition for a symbol in the alphabet.
In my particular case, I was trying to get this automata:

But, as I said, some states do not have an associated transition for a symbol (take for example state b). This, in principle, accomplishes the DFA restrictions since there is no state such that for a symbol in the alphabet there is more than one associated transition.
When trying to obtain the above DFA using the transition function:

{
'': {'a': 'a', 'b': 'b'},
'a': {'b': 'ab', 'a': 'aa'},
'b': {'b': 'bb'},
'aa': {'a': 'aa', 'b': 'ab'},
'bb': {'a': 'ba'},
'ab': {'b': 'bb'},
'ba': {'a': 'aa'}
}

I get an exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/guillermh/.local/lib/python3.9/site-packages/automata/fa/dfa.py", line 23, in __init__
    self.validate()
  File "/home/guillermh/.local/lib/python3.9/site-packages/automata/fa/dfa.py", line 67, in validate
    self._validate_transitions(start_state, paths)
  File "/home/guillermh/.local/lib/python3.9/site-packages/automata/fa/dfa.py", line 59, in _validate_transitions
    self._validate_transition_missing_symbols(start_state, paths)
  File "/home/guillermh/.local/lib/python3.9/site-packages/automata/fa/dfa.py", line 29, in _validate_transition_missing_symbols
    raise exceptions.MissingSymbolError(
automata.base.exceptions.MissingSymbolError: state b is missing transitions for symbol a

I think there is a method _validate_transition_missing_symbols in automata.fa.dfa which is the culprit of this thing.
I would make a pull request but right now I haven't got the time to learn how to implement and run tests and testing code coverage (I'm pretty novice in contributing tasks).
But I opened this issue in order to notify about the possible misconception about the formalities of DFA.
Thank you beforehand!

More functionality for DPDA

My next focus was gonna be a little bit on DPDAs. I've started implementing enumeration and iteration.
Are there any specific operations that you're particularly interested in prioritizing?
Some of them may not have any known efficient algorithm (regularity of language) or may even be undecidable (although that's more common for the nondeterministic version).
@caleb531 @eliotwrobson

Some ideas:

  • Enumeration
  • Iteration
  • Union with regular language
  • Intersection with regular language
  • Complement
  • Equivalence
  • Emptiness
  • Regularity (exponential complexity)

Adding directory for usage examples?

So the API documentation reorg (#98 and #99) is complete—great! But as I look through the API documentation, I realize that there is a lot of text to sift through between each block of code.

@Tagl @eliotwrobson What do you think about adding an examples/ directory with Python files that demonstrate the code needed to perform the most common DFA/etc. operations. Since the API is so large at this point, we would probably need to be selective about what use cases we plan to demonstrate.

One option is that we could make each example built around a real-world use case. For example:

# examples/reading_input_from_cli.py
from automata.fa.dfa import DFA

# DFA which matches all binary strings ending in an odd number of '1's
my_dfa = DFA(
    states={'q0', 'q1', 'q2'},
    input_symbols={'0', '1'},
    transitions={
        'q0': {'0': 'q0', '1': 'q1'},
        'q1': {'0': 'q0', '1': 'q2'},
        'q2': {'0': 'q2', '1': 'q1'}
    },
    initial_state='q0',
    final_states={'q1'}
)

try:
    while True:
        if my_dfa.accepts_input(input('Please enter your input: ')):
            print('Accepted')
        else:
            print('Rejected')
except KeyboardInterrupt:
    print('')

Another option is that we could make each example a sort of rapid-fire walkthrough of the different things you can do. For example:

# examples/dfa_reading_input.py
from automata.fa.dfa import DFA

# DFA which matches all binary strings ending in an odd number of '1's
my_dfa = DFA(
    states={'q0', 'q1', 'q2'},
    input_symbols={'0', '1'},
    transitions={
        'q0': {'0': 'q0', '1': 'q1'},
        'q1': {'0': 'q0', '1': 'q2'},
        'q2': {'0': 'q2', '1': 'q1'}
    },
    initial_state='q0',
    final_states={'q1'}
)

my_dfa.read_input('001') # returns a final state (e.g. 'q1')

my_dfa.read_input('001!') # raises RejectionException

# loop over each state of the DFA as you read input
for state in my_dfa.read_input_stepwise('00111'):
    print(state)

# etc.

We may also need to be careful of adding too many examples, such that it becomes an additional maintenance burden on top of the existing API documentation. But it might be helpful for beginners. I'm a bit ambivalent—what do you think?

NFA.to_regex() not implemented

@caleb531 I think by mistake I wrote NFA.to_regex() but this function is not there, instead NFA.from_regex(regex) is there. For converting NFA to regex, one has to convert NFA to GNFA and then GNFA to regex.

Very sorry for this.

Feature Request: Visual confirmation of input

I am new to contributing, so I ask beforehand this time.

I would like to have more visual representation of transitions. I have been working a lot with this library now, and I am wondering if some of these features should be implemented in the source code? I don't want to work on it if it is not of interest :)

Skjermopptak.2021-03-12.kl.20.28.30.mov

Conditions of PDA Acceptance

DPDAs can accept when the stack is empty or a final state has been reached (assuming that all input has been read).

The related code is DPDA._check_for_input_rejection which is basically this:

if current_state not in self.final_states and stack:
            raise exceptions.RejectionException()

This leads to the problem that when the DPDA accepts via final state, it will also accept if the stack is empty after reading a given input, even when no final state has been reached.

I would prefer specifying whether a DPDA accepts either via empty stack or via final state.

(This can probably be deducted by looking at whether self.final_states is empty but I'm not so sure about that.)

problems with GNFA.to_regex

GNFA.to_regex seems to give wrong results sometimes. For example, the following code:

from automata.fa.dfa import DFA
from automata.fa.nfa import NFA
from automata.fa.gnfa import GNFA

nfa = NFA.from_regex('a*')
dfa = DFA.from_nfa(nfa)
gnfa = GNFA.from_dfa(dfa)
print(gnfa.to_regex())

prints a single-character string *, which is not a valid regex.

no reading of the stack

if I in the code put to read '' in the stack, the input will replace what there is in te stack or the input will be on the top with no replacing of what it is in the stack?

Allow non-alphanumeric literal characters in regular expressions

@eliotwrobson Per your brief comment from #112:

Currently, only alphanumeric characters are supported in the regex parsing here (which I'm now realizing is kindof a breaking change from v6, whoops). But I think some way of adding escape characters could be useful to people. I think it's just a matter of reconfiguring the lexer to treat characters coming after a backslash as a literal. But I think it should definitely be in a separate PR.

I think it would be helpful to allow non-alphanumeric characters in a regex, such that you could create a regex for an email address, @username, etc. This enhancement would imply that you can also escape symbols to be literal characters.

Divide API documentation into multiple files

@eliotwrobson @Tagl

Currently, the API documentation in the README is massive and monolithic. It potentially hides the presence of certain features because there is so much to scroll through, and it can make the documentation more overwhelming for newcomers. Sure, there's the Table of Contents, but it is not as easily accessible from any one section (i.e. scroll to the top of the page, then down a bit).

I'm thinking of moving all API documentation (including the TOC) to a new docs/ directory in this repository. It would still all be Markdown, just broken up across multiple files (mostly mapped to the current TOC links).

To ease access from one page to another, I'm thinking of including a link to the TOC at the top and bottom of every documentation page/file. We could also add Next/Prev links (on DFA docs: "Next Up: NFA class"), but this could become cumbersome to manage.

A prerequisite of this reorganization would be to close out all existing PRs that touch the README in any way. I'm creating this Issue to give you guys a heads-up of my intention, and also to solicit feedback.

improvement possible in NFA.kleene_star(self)

Automata version: 5.0.0

Presently kleene_star() function adds two new states (new initial state and new final state). However, this can be done by just adding one new initial state. The following image explains the method: It is not necessary for us to have only one accept state.
image

I'm working on the conversion of regular expressions to NFA, it has to be done piecewise, and I am using the functions concatenate, kleene_star (wrote union myself). If kleene_star function adds only one new state instead of two, it would generate NFA with lesser states in this respect.

Bug

Also there is a bug:

from automata.fa.nfa import NFA
nfa = NFA(
    states={'q0', 'q1'},
    input_symbols={'a'},
    transitions={
        'q0': {'a': {'q1'}}
    },
    initial_state='q0',
    final_states={'q1'}
)

nfa2 = nfa.kleene_star()
Traceback (most recent call last):
  File "/media/abhinav/store/GitHub/automata/test.py", line 12, in <module>
    nfa2 = nfa.kleene_star()
  File "/media/abhinav/store/GitHub/automata/automata/fa/nfa.py", line 213, in kleene_star
    if '' not in new_transitions[state]:
KeyError: 'q1'

It assumes outgoing transitions from every final state which is not necessarily true.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.