# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['dfa']

package_data = \
{'': ['*']}

install_requires = \
['attrs>=19.1,<20.0', 'funcy>=1.12,<2.0', 'lazytree>=0.3.1,<0.4.0']

extras_require = \
{'draw': ['pydot>=1.4,<2.0']}

setup_kwargs = {
    'name': 'dfa',
    'version': '1.1.0',
    'description': 'Python library for modeling DFAs, Moore Machines, and Transition Systems.',
    'long_description': '# DFA\n\nA simple python implementation of a DFA. \n\n[![Build Status](https://cloud.drone.io/api/badges/mvcisback/dfa/status.svg)](https://cloud.drone.io/mvcisback/dfa)\n[![Docs](https://img.shields.io/badge/API-link-color)](https://mvcisback.github.io/dfa)\n[![codecov](https://codecov.io/gh/mvcisback/dfa/branch/master/graph/badge.svg)](https://codecov.io/gh/mvcisback/dfa)\n[![PyPI version](https://badge.fury.io/py/dfa.svg)](https://badge.fury.io/py/dfa)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n<!-- markdown-toc start - Don\'t edit this section. Run M-x markdown-toc-generate-toc again -->\n**Table of Contents**\n\n- [Installation](#installation)\n- [Usage](#usage)\n    - [Membership Queries](#membership-queries)\n    - [Transitions and Traces](#transitions-and-traces)\n    - [Non-boolean output alphabets](#non-boolean-output-alphabets)\n    - [Moore Machines](#moore-machines)\n    - [Composition](#composition)\n    - [DFA <-> Dictionary](#dfa---dictionary)\n    - [Computing Reachable States](#computing-reachable-states)\n    - [Sampling Paths](#sampling-paths)\n    - [Running interactively (Co-Routine API)](#running-interactively-co-routine-api)\n    - [Sampling Paths](#special-alphabets)\n    - [Visualizing DFAs](#visualizing-dfas)\n\n<!-- markdown-toc end -->\n\n\n**Features:**\n\n1. State can be any Hashable object.\n2. Alphabet can be any finite sequence of Hashable objects.\n3. Designed to be immutable and hashable (assuming components are\n   immutable and hashable).\n4. Design choice to allow transition map and accepting set to be\n   given as functions rather than an explicit `dict` or `set`.\n\n# Installation\n\nIf you just need to use `dfa`, you can just run:\n\n`$ pip install dfa`\n\nFor developers, note that this project uses the\n[poetry](https://poetry.eustace.io/) python package/dependency\nmanagement tool. Please familarize yourself with it and then\nrun:\n\n`$ poetry install`\n\n# Usage\n\nThe `dfa` api is centered around the `DFA` object. \n\nBy default, the `DFA` object models a `Deterministic Finite Acceptor`,\ne.g., a recognizer of a Regular Language. \n\n**Example Usage:**\n```python\nfrom dfa import DFA\n\ndfa1 = DFA(\n    start=0,\n    inputs={0, 1},\n    label=lambda s: (s % 4) == 3,\n    transition=lambda s, c: (s + c) % 4,\n)\n\ndfa2 = DFA(\n    start="left",\n    inputs={"move right", "move left"},\n    label=lambda s: s == "left",\n    transition=lambda s, c: "left" if c == "move left" else "right",\n)\n```\n\n## Membership Queries\n\n```python\nassert dfa1.label([1, 1, 1, 1])\nassert not dfa1.label([1, 0])\n\nassert dfa2.label(["move right"]*100 + ["move left"])\nassert not dfa2.label(["move left", "move right"])\n```\n\n## Transitions and Traces\n\n```python\nassert dfa1.transition([1, 1, 1]) == 3\nassert list(dfa1.trace([1, 1, 1])) == [0, 1, 2, 3]\n```\n\n## Non-boolean output alphabets\n\nSometimes, it is useful to model an automata which can label a word\nusing a non-Boolean alphabet. For example, `{True, False, UNSURE}`.\n\nThe `DFA` object supports this by specifying the output alphabet.\n\n```python\nUNSURE = None\n\ndef my_labeler(s):\n    if s % 4 == 2:\n       return None\n    return (s % 4) == 3\n\n\ndfa3 = DFA(\n    start=0,\n    inputs={0, 1},\n    label=my_labeler,\n    transition=lambda s, c: (s + c) % 4,\n    outputs={True, False, UNSURE},\n)\n```\n\n**Note:** If `outputs` is set to `None`, then no checks are done that\nthe outputs are within the output alphabet.\n\n```python\ndfa3 = DFA(\n    start=0,\n    inputs={0, 1},\n    label=my_labeler,\n    transition=lambda s, c: (s + c) % 4,\n    outputs=None,\n)\n```\n\n## Moore Machines\n\nFinally, by reinterpreting the structure of the `DFA` object, one can\nmodel a Moore Machine. For example, in 3 state counter, `dfa1`, the\nMoore Machine can output the current count.\n\n```python\nassert dfa1.transduce(()) == ()\nassert dfa1.transduce((1,)) == (False,)\nassert dfa1.transduce((1, 1, 1, 1)) == (False, False, False, True)\n```\n\n## Composition\n\n`DFA` objects can be combined in two ways:\n\n1. (Synchronous) Cascading Composition: Feed outputs of one `DFA` into another.\n\n```python\nmod_5 = DFA(\n    start=0,\n    label=lambda s: s,\n    transition=lambda s, c: (s + c) % 5,\n    inputs={0, 1},\n    outputs={0, 1, 2, 3, 4},\n)\neq_0 = DFA(\n    start=0,\n    label=lambda s: s == 0,\n    transition=lambda s, c: c,\n    inputs={0, 1, 2, 3, 4},\n    outputs={True, False}\n)\n\neq_0_mod_5 = eq_0 << mod_5\nassert eq_0_mod_5.label([0, 0, 0, 0])\nassert not eq_0_mod_5.label([0, 1, 0, 0, 0])\n```\n\nNote that we use Moore Machine semantics (as opposed to Mealy).  Thus\n`eq_0`\'s input is determined by `mod_5`\'s state *before* seeing the\ninput. Thus, the following holds.\n\n```python\nassert not eq_0_mod_5.label([1, 1, 1, 1, 1])\nassert eq_0_mod_5.label([1, 1, 1, 1, 1, 0])\n```\n\n2. (Synchronous) Parallel Composition: Run two `DFA`s in parallel.\n\n```python\nparity = DFA(\n    start=0, inputs={0, 1}, label=lambda s: s,\n    transition=lambda s, c: (s + c) & 1,\n)\n\nself_composed = parity | parity\n\nassert self_composed.label([(0, 0), (1, 0)]) == (1, 0)\n```\n\n**Note** Parallel composition results in a `DFA` with\n`dfa.ProductAlphabet` input and outputs.\n\n\n## DFA <-> Dictionary\n\nNote that `dfa` provides helper functions for going from a dictionary\nbased representation of a deterministic transition system to a `DFA`\nobject and back.\n\n```python\nfrom dfa import dfa2dict, dict2dfa\n\n# DFA encoded a nested dictionaries with the following\n# signature.\n#     <state>: (<label>, {<action>: <next state>})\n\ndfa_dict = {\n    0: (False, {0: 0, 1: 1}),\n    1: (False, {0: 1, 1: 2}),\n    2: (False, {0: 2, 1: 3}), \n    3: (True, {0: 3, 1: 0})\n}\n\n# Dictionary -> DFA\ndfa = dict2dfa(dfa_dict, start=0)\n\n# DFA -> Dictionary\ndfa_dict2, start = dfa2dict(dfa)\n\nassert (dfa_dict, 0) == (dfa_dict2, start)\n```\n\n## Computing Reachable States\n\n```python\n# Perform a depth first traversal to collect all reachable states.\nassert dfa1.states() == {0, 1, 2, 3}\n```\n\n## Sampling Paths\n\nOften times, it is useful to sample a path between two states, say `a`\nand `b`. `dfa` supports this using `dfa.utils.paths`. This function\nreturns a generator of words, `w`, such that `dfa.transition(w,\nstart=b) == a`. For example:\n\n\n```python\nfrom dfa.utils import paths\n\naccess_strings = paths(\n    dfa1, \n    start=0,\n    end=1,  # Optional. If not specified returns all paths\n            # starting at `start`.\n    max_length=7,  #  Defaults to float(\'inf\')\n    randomize=True,  #  Randomize the order. Shorter paths still found first.\n)\n\nfor word in access_strings:\n    assert dfa1.transition(word, start=0) == 1\n```\n\n## Running interactively (Co-Routine API)\n\n`dfa` supports interactively stepping through a `DFA` object via\nco-routines. This is particularly useful when using DFA in a control\nloop. For example, the following code counts how many `1`\'s it takes\nto advance `dfa1`\'s state back to the start state.\n\n```python\n\nmachine = dfa1.run()\n\nstart = next(machine)\nstate = None\n\ncount = 0\nwhile state != start:\n    count += 1\n    state = machine.send(1)\n```\n\n## Special Alphabets\n\nOften times, explicitly representing an alphabet is tedious,\nimpossible, or inefficient. \n\nAs such, `dfa` currently provides the following special symbolic\nalphabets (with more planned in the future).\n\n1. `dfa.SupAlphabet`: Alphabet that contains all possible inputs\n   (beware of [Russell\'s paradox}(https://en.wikipedia.org/wiki/Russell\'s_paradox)).\n1. `dfa.ProductAlphabet(left, right)`: Alphabet representing the Cartesian product\n   of `left` and `right` alphabets.\n\n\n## Visualizing DFAs\n\n`dfa` optionally supports visualizing DFAs using graphviz. To use this\nfunctionality be sure to install `dfa` using with the `draw` option:\n\n```python\npip install dfa[draw]\n```\n\nor \n\n```python\npoetry install -E draw\n```\n\nThen one can simply use `dfa.draw.write_dot` to write a `.dot` file\nrepresenting the DFA. This `.dot` file can be rendered using any\ngraphviz supporting tool.\n\n```python\nfrom dfa.draw import write_dot\n\nwrite_dot(dfa1, "path/to/dfa1.dot")\n```\n\nUsing the `dot` command in linux results in the following rendering of `dfa1`.\n\n`$ dot -Tsvg path/to/dfa1.dot > dfa1.svg`\n\n<figure>\n  <img src="assets/dfa1.svg" alt="visualization of dfa1" width=500px>\n  <figcaption>\n    Visualization of dfa1 using graphviz.\n  </figcaption>\n</figure>\n',
    'author': 'Marcell Vazquez-Chanlatte',
    'author_email': 'marcell.vc@eecs.berkeley.edu',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/mvcisback/dfa',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'extras_require': extras_require,
    'python_requires': '>=3.7,<4.0',
}


setup(**setup_kwargs)
