From 050e0275fbf3f2dcad1aabc067a71ca2271046ae Mon Sep 17 00:00:00 2001 From: Eliot Robson Date: Thu, 6 Feb 2025 22:01:45 -0600 Subject: [PATCH] Added DFA example notebook (#234) Co-authored-by: Kylie Zhang --- README.md | 1 + docs/index.md | 2 +- docs/people.md | 1 + example_notebooks/DFA.ipynb | 1048 +++++++++++++++++++++++++++++++++++ 4 files changed, 1051 insertions(+), 1 deletion(-) create mode 100644 example_notebooks/DFA.ipynb diff --git a/README.md b/README.md index 8fd67711..02ff1227 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,7 @@ Links: - [**Documentation**](https://caleb531.github.io/automata/) - [**Examples**](https://caleb531.github.io/automata/examples/fa-examples/) +- [**Example Notebooks**](https://github.com/caleb531/automata/tree/main/example_notebooks) - [**Migration Guide**](https://caleb531.github.io/automata/migration/) - [**API**](https://caleb531.github.io/automata/api/) diff --git a/docs/index.md b/docs/index.md index 9000d4ec..d39ec1eb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -13,7 +13,7 @@ Automata is a Python 3 library implementing structures and algorithms for manipu pushdown automata, and Turing machines. The algorithms have been optimized and are capable of processing large inputs. Visualization logic has also been implemented. This package is suitable for both researchers wishing to manipulate automata and for instructors teaching courses on theoretical -computer science. +computer science. See [example jupyter notebooks.](https://github.com/caleb531/automata/tree/main/example_notebooks) For an overview on automata theory, see [this Wikipedia article][wikipedia-article], and for a more comprehensive introduction to each of these topics, see [these lecture notes][lecture-notes]. diff --git a/docs/people.md b/docs/people.md index 54eb8887..46457f98 100644 --- a/docs/people.md +++ b/docs/people.md @@ -11,3 +11,4 @@ - [Tagl](https://github.com/Tagl) - [CamiloMartinezM](https://github.com/CamiloMartinezM) - [abhinavsinha-adrino](https://github.com/abhinavsinha-adrino) +- [skyien-z](https://github.com/skyien-z) diff --git a/example_notebooks/DFA.ipynb b/example_notebooks/DFA.ipynb new file mode 100644 index 00000000..4a90bf12 --- /dev/null +++ b/example_notebooks/DFA.ipynb @@ -0,0 +1,1048 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "dd1b7bfa", + "metadata": {}, + "source": [ + "## Finite State Machine (FSM)\n", + "\n", + "(Text and examples modified and borrowed from the one and only [Prof. Jeff Erickson](http://algorithms.wtf/#models))\n", + "\n", + "A finite-state machine is a formal model of any system/machine/algorithm that can exist in a finite number of states. Transitions among these states is based on a sequence of input symbols. For example, the following algorithm `MultipleOf5` determines whether a binary string `w[0..n-1]` of bits represents a multiple of 5:" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "b502be80", + "metadata": {}, + "outputs": [], + "source": [ + "def mutlipleOf5(w):\n", + " rem, n = 0, len(w)\n", + " for i in range(n):\n", + " rem = (2 * rem + ord(w[i])) % 5\n", + " if rem == 0:\n", + " return True\n", + " else:\n", + " return False" + ] + }, + { + "cell_type": "markdown", + "id": "e9963cf8", + "metadata": {}, + "source": [ + "(We can test this function out. **12** is **1100** in binary while **15** is **1111** in binary. So, `multipleOf5(\"1100\")` should return `False` while `multipleOf5(\"1111\")` should return `True`. Try it out yourself!)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "046153fa", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "False\n", + "True\n" + ] + } + ], + "source": [ + "print(mutlipleOf5(\"1100\"))\n", + "print(mutlipleOf5(\"1111\"))" + ] + }, + { + "cell_type": "markdown", + "id": "2467cf8f", + "metadata": {}, + "source": [ + "We can envision the variable `rem` having 5 distinct values: 0, 1, 2, 3, 4, and can consequently represent it using a FSM with each state s1, s2, s3, s4 representing a possible value of `rem`." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "60c3b6c4", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "3c35251b-d2a1-4825-986d-f16796d4dd24\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "s0\r\n", + "\r\n", + "\r\n", + "s0\r\n", + "\r\n", + "\r\n", + "\r\n", + "3c35251b-d2a1-4825-986d-f16796d4dd24->s0\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "s0->s0\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "s1\r\n", + "\r\n", + "s1\r\n", + "\r\n", + "\r\n", + "\r\n", + "s0->s1\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "s2\r\n", + "\r\n", + "s2\r\n", + "\r\n", + "\r\n", + "\r\n", + "s1->s2\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "s3\r\n", + "\r\n", + "s3\r\n", + "\r\n", + "\r\n", + "\r\n", + "s1->s3\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "s2->s0\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "s4\r\n", + "\r\n", + "s4\r\n", + "\r\n", + "\r\n", + "\r\n", + "s2->s4\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "s3->s1\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "s3->s2\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "s4->s3\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "s4->s4\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n" + ], + "text/plain": [ + "DFA(states={'s3', 's2', 's0', 's4', 's1'}, input_symbols={'0', '1'}, transitions={'s0': {'0': 's0', '1': 's1'}, 's1': {'0': 's2', '1': 's3'}, 's2': {'0': 's4', '1': 's0'}, 's3': {'0': 's1', '1': 's2'}, 's4': {'0': 's3', '1': 's4'}}, initial_state='s0', final_states={'s0'}, allow_partial=False)" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "from automata.fa.dfa import DFA\n", + "\n", + "mutlipleOf5_fsm = DFA(\n", + " states={'s0', 's1', 's2', 's3', 's4'},\n", + " input_symbols={'0', '1'},\n", + " transitions={\n", + " 's0': {'0': 's0', '1': 's1'},\n", + " 's1': {'0': 's2', '1': 's3'},\n", + " 's2': {'0': 's4', '1': 's0'},\n", + " 's3': {'0': 's1', '1': 's2'},\n", + " 's4': {'0': 's3', '1': 's4'}\n", + " },\n", + " initial_state='s0',\n", + " final_states={'s0'}\n", + ")\n", + "\n", + "mutlipleOf5_fsm" + ] + }, + { + "cell_type": "markdown", + "id": "66dfd52f", + "metadata": {}, + "source": [ + "Run the code below a few times to verify its correctness for youself:" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "9825667c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Accepted\n" + ] + } + ], + "source": [ + "if mutlipleOf5_fsm.accepts_input(input('Please enter your input: ')):\n", + " print('Accepted')\n", + "else:\n", + " print('Rejected')" + ] + }, + { + "cell_type": "markdown", + "id": "5e48007f", + "metadata": {}, + "source": [ + "## DFAs\n", + "\n", + "Finite-State Machines are also known as deterministic finite-state automata. It's \"deterministic\" because the behavior of the machine is completely determined by the input strings. (We will cover NFAs –– non-deterministic finite-state automata –– in a later section.)\n", + "\n", + "Formally, every finite-state machine consists of five components:\n", + "1. A finite set $\\Sigma$, the **input alphabet**.\n", + "2. Another finite set $Q$, the **states**.\n", + "3. A **transition** function $\\delta: Q \\times \\Sigma \\rightarrow Q$.\n", + "4. A **state state** $s \\in Q$.\n", + "5. A subset $A \\subseteq Q$ of **accepting states**.\n", + "\n", + "Scroll above and note how `mutlipleOf5_fsm` is precisely defined by these five components as inputs." + ] + }, + { + "cell_type": "markdown", + "id": "acbea5da", + "metadata": {}, + "source": [ + "### How does it work?\n", + "The behavior of a finite-state machine is governed by an input string $\\omega$, which is a finite\n", + "sequence of symbols from the input alphabet $\\Sigma$. \n", + "\n", + "The machine reads the symbols in $\\omega$ one at a time in order (from left to right). At all times, the machine has a current state $q$; initially $q$ is the machine’s start state $s$. Each time the machine reads a symbol $a$ from the input string, its current state transitions from $q$ to $\\delta(q, a)$. \n", + "\n", + "After all the characters have been read, the machine accepts $\\omega$ if the current state is in $A$ and rejects $\\omega$ otherwise. " + ] + }, + { + "cell_type": "markdown", + "id": "d8a94836", + "metadata": {}, + "source": [ + "#### Let's explore another example:\n", + "The following DFA accepts all binary strings ending in an odd number of '1's. Write out a few binary strings for yourself and trace through the graph to prove its correctness. $q_1$ is the only accepting state, denoted by the concentric circles." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "67d76f2b", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "9ee8ede3-cd3e-43ab-a0fb-6bfb1e1ee681\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "q0\r\n", + "\r\n", + "q0\r\n", + "\r\n", + "\r\n", + "\r\n", + "9ee8ede3-cd3e-43ab-a0fb-6bfb1e1ee681->q0\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "q0->q0\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "q1\r\n", + "\r\n", + "\r\n", + "q1\r\n", + "\r\n", + "\r\n", + "\r\n", + "q0->q1\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "q1->q0\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "q2\r\n", + "\r\n", + "q2\r\n", + "\r\n", + "\r\n", + "\r\n", + "q1->q2\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "q2->q1\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "q2->q2\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n" + ], + "text/plain": [ + "DFA(states={'q2', 'q0', 'q1'}, 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'}, allow_partial=False)" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "odd_ones_ending_dfa = DFA(\n", + " states={'q0', 'q1', 'q2'},\n", + " input_symbols={'0', '1'},\n", + " transitions={\n", + " 'q0': {'0': 'q0', '1': 'q1'},\n", + " 'q1': {'0': 'q0', '1': 'q2'},\n", + " 'q2': {'0': 'q2', '1': 'q1'}\n", + " },\n", + " initial_state='q0',\n", + " final_states={'q1'}\n", + ")\n", + "\n", + "odd_ones_ending_dfa" + ] + }, + { + "cell_type": "markdown", + "id": "743d59b2", + "metadata": {}, + "source": [ + "The start state, $q_0$ is denoted by the unattached arrow pointing at it. The string \"1\" would be accepted because you would navigate from $q_0$ to $q_1$. \"11\" would create the chain $q_0 \\to q_1 \\to q_2$, which would be rejected. \"111\" would return to $q_1$, and be accepted.\n", + "\n", + "Careful analysis shows that that this DFA, with three states and six transitions can be written more with only two states and three transitions:" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "d646b08e", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "2da6daae-2f11-4be5-a85b-fb0ad678534e\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "{q2, q0}\r\n", + "\r\n", + "{q2, q0}\r\n", + "\r\n", + "\r\n", + "\r\n", + "2da6daae-2f11-4be5-a85b-fb0ad678534e->{q2, q0}\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "{q2, q0}->{q2, q0}\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "{q1}\r\n", + "\r\n", + "\r\n", + "{q1}\r\n", + "\r\n", + "\r\n", + "\r\n", + "{q2, q0}->{q1}\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "{q1}->{q2, q0}\r\n", + "\r\n", + "\r\n", + "0,1\r\n", + "\r\n", + "\r\n", + "\r\n" + ], + "text/plain": [ + "DFA(states={frozenset({'q1'}), frozenset({'q2', 'q0'})}, input_symbols={'0', '1'}, transitions={frozenset({'q2', 'q0'}): {'0': {'q2', 'q0'}, '1': {'q1'}}, frozenset({'q1'}): {'0': {'q2', 'q0'}, '1': {'q2', 'q0'}}}, initial_state={'q2', 'q0'}, final_states={frozenset({'q1'})}, allow_partial=False)" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "minimal_odd_ones_ending_dfa = odd_ones_ending_dfa.minify(retain_names=True)\n", + "minimal_odd_ones_ending_dfa" + ] + }, + { + "cell_type": "markdown", + "id": "76dc2040", + "metadata": {}, + "source": [ + "Notice how this **minimal DFA** `minimal_odd_ones_ending_dfa` accepts and rejects all the strings that `odd_ones_ending_dfa` does. We say that both `minimal_odd_ones_ending_dfa` and `odd_ones_ending_dfa` have the same language. " + ] + }, + { + "cell_type": "markdown", + "id": "4c519ec2", + "metadata": {}, + "source": [ + "## Finite State Automata and Formal Languages\n", + "\n", + "The language of a finite state machine $M$, denoted $L(M)$, is the set of all strings in $\\Sigma^{*}$[[1]](#cite_note-1) that *M* accepts. More formally, if $M = (\\Sigma, Q, \\delta, s, A)$, then $L(M) := \\{w \\in \\Sigma^{*} \\mid \\delta^{*}(s, w) \\in A\\}$[[2]](#cite_note-2). We call a language **automatic**, or **regular**, if it is the language of some finite state machine.\n", + "\n", + "Reuglar languages have several closure properties –– Let $L$ and $L'$ be arbitrary regular languages over an arbitrary alphabet $\\Sigma$. Then,\n", + "- $\\bar L = \\Sigma^{*}\\backslash L$ is regular,\n", + "- $L \\cup L'$ is regular,\n", + "- $L \\cap L'$ is regular,\n", + "- $L \\backslash L'$ is regular, and\n", + "- $L \\oplus L'$ is regular.\n", + "\n", + "By Kleene's Theorem, for any regular expression R, there is a DFA $M$ such that $L(R) = L(M)$. For any DFA $M$, there is a regular expression $R$ such that $L(M) = L(R)$, which implies that the set of ***regular*** languages is also closed under the simple boolean operations defined above. We are going through this very quickly and very briefly –– please refer to [these lecture notes](https://jeffe.cs.illinois.edu/teaching/algorithms/models/03-automata.pdf) for more detail.\n", + "\n", + "### Closure Property Examples\n", + "We can easily create a DFA that accepts all binary strings except for those that end in an odd number of 1's:" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "a789002f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "b9603bca-a991-421b-8429-9a8897694699\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "b9603bca-a991-421b-8429-9a8897694699->1\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "1->1\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "1->0\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "0->1\r\n", + "\r\n", + "\r\n", + "0,1\r\n", + "\r\n", + "\r\n", + "\r\n" + ], + "text/plain": [ + "DFA(states={0, 1}, input_symbols={'0', '1'}, transitions={0: {'0': 1, '1': 1}, 1: {'0': 1, '1': 0}}, initial_state=1, final_states={1}, allow_partial=False)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "odd_ones_ending_dfa.complement(retain_names=False, minify=True)" + ] + }, + { + "cell_type": "markdown", + "id": "b144a520", + "metadata": {}, + "source": [ + "Trace it through with some examples to prove its correctness to yourself.\n", + "\n", + "Next, we can construct a DFA that accepts all binary strings that represent a multiple of 5 OR end in an odd number of 1's:" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "fd2eb520", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Accepted\n" + ] + } + ], + "source": [ + "union_dfa = mutlipleOf5_fsm.union(odd_ones_ending_dfa, retain_names=False, minify=True)\n", + "if union_dfa.accepts_input(input('Please enter your input: ')):\n", + " print('Accepted')\n", + "else:\n", + " print('Rejected')" + ] + }, + { + "cell_type": "markdown", + "id": "0d85a7fd", + "metadata": {}, + "source": [ + "Notice how '111' (7 in decimal) and '1111' (15 in decimal) are both accepted?\n", + "\n", + "We can also use the intersection operation to contructor a DFA that accepts only binary strings that represent a multiple of 5 AND end in a odd number of 1's: " + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "92403fa9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Rejected\n" + ] + } + ], + "source": [ + "intersection_dfa = mutlipleOf5_fsm.intersection(odd_ones_ending_dfa, retain_names=False, minify=True)\n", + "if intersection_dfa.accepts_input(input('Please enter your input: ')):\n", + " print('Accepted')\n", + "else:\n", + " print('Rejected')" + ] + }, + { + "cell_type": "markdown", + "id": "5f210909", + "metadata": {}, + "source": [ + "Now, both '111' (7 in decimal) and '1111' (15 in decimal) are rejected; however, '101' (5 in decimal) is accepted because it satisfies both conditions.\n", + "\n", + "## Creation Functions\n", + "\n", + "Now that we've covered the basics, we can move to the more advanced creation functions.\n", + "\n", + "`from_substring` \"directly computes the minimal DFA recognizing strings containing the given substring.\" For example, if I want a DFA that accepts binary strings that contain the substring \"101\", I can write the following:" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "b762dae6", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "16b6b840-9be9-4448-acd8-e8b4c208daf9\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "16b6b840-9be9-4448-acd8-e8b4c208daf9->0\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "0->0\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "0->1\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "1->1\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "2\r\n", + "\r\n", + "2\r\n", + "\r\n", + "\r\n", + "\r\n", + "1->2\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "2->0\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "3\r\n", + "\r\n", + "\r\n", + "3\r\n", + "\r\n", + "\r\n", + "\r\n", + "2->3\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "3->3\r\n", + "\r\n", + "\r\n", + "0,1\r\n", + "\r\n", + "\r\n", + "\r\n" + ], + "text/plain": [ + "DFA(states={0, 1, 2, 3}, input_symbols={'0', '1'}, transitions={0: {'0': 0, '1': 1}, 1: {'0': 2, '1': 1}, 2: {'0': 0, '1': 3}, 3: {'0': 3, '1': 3}}, initial_state=0, final_states={3}, allow_partial=False)" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "one_zero_one_substring_dfa = DFA.from_substring(\n", + " input_symbols={'0', '1'},\n", + " substring=\"101\",\n", + " contains=True,\n", + " must_be_suffix=False\n", + ")\n", + "\n", + "one_zero_one_substring_dfa" + ] + }, + { + "cell_type": "markdown", + "id": "c016e474", + "metadata": {}, + "source": [ + "Notice how the DFA enters an accepting state immediately once a \"101\" is read?\n", + "\n", + "We can even generate a DFA that accepts binary strings containing the *subsequence* \"101\":" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "1623dda1", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "feca6cbf-b86f-4b11-82e7-96302379621a\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "feca6cbf-b86f-4b11-82e7-96302379621a->0\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "\r\n", + "0->0\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "0->1\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "1->1\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "2\r\n", + "\r\n", + "2\r\n", + "\r\n", + "\r\n", + "\r\n", + "1->2\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "2->2\r\n", + "\r\n", + "\r\n", + "0\r\n", + "\r\n", + "\r\n", + "\r\n", + "3\r\n", + "\r\n", + "\r\n", + "3\r\n", + "\r\n", + "\r\n", + "\r\n", + "2->3\r\n", + "\r\n", + "\r\n", + "1\r\n", + "\r\n", + "\r\n", + "\r\n", + "3->3\r\n", + "\r\n", + "\r\n", + "0,1\r\n", + "\r\n", + "\r\n", + "\r\n" + ], + "text/plain": [ + "DFA(states={0, 1, 2, 3}, input_symbols={'0', '1'}, transitions={0: {'0': 0, '1': 1}, 1: {'0': 2, '1': 1}, 2: {'0': 2, '1': 3}, 3: {'0': 3, '1': 3}}, initial_state=0, final_states={3}, allow_partial=False)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "one_zero_one_dfa = DFA.from_subsequence(\n", + " input_symbols={'0', '1'},\n", + " subsequence=\"101\",\n", + " contains=True,\n", + ")\n", + "\n", + "one_zero_one_dfa" + ] + }, + { + "cell_type": "markdown", + "id": "bfa19104", + "metadata": {}, + "source": [ + "Isn't that cool? Note the differences between the two DFA's and the strings that they accept. If you don't know the differene between a substring and subsequence, write out all the strings that are accepted by each DFA and look for patterns.\n", + "\n", + "There's plenty more DFA functions to explare on the [DFA class API](../../api/fa/class-dfa). Go crazy!" + ] + }, + { + "cell_type": "markdown", + "id": "aa29ff9d", + "metadata": {}, + "source": [ + "- The Kleene Closure $\\Sigma^{*}$ is the set of all strings obtained by concatenating a sequence of zero or more strings from $\\Sigma$\\.\n", + "\n", + "- $\\delta^{*}$ extends the definition of the transition function $\\delta: Q \\times \\Sigma \\rightarrow Q$ of any finite-state machine to $\\delta^{*}: Q \\times \\Sigma^{*} \\rightarrow Q$:\n", + "\\begin{equation}\n", + "\\delta^{*}(q,w) =\n", + " \\begin{cases}\n", + " q & \\text{if $w = \\epsilon$} \\\\\n", + " \\delta^{*}(\\delta(q, a), x) & \\text{if $w = ax$}\n", + " \\end{cases}\n", + "\\end{equation}" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.9.16" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +}