diff --git a/docs/notebooks/DFA.ipynb b/docs/notebooks/DFA.ipynb new file mode 100644 index 00000000..deff1a69 --- /dev/null +++ b/docs/notebooks/DFA.ipynb @@ -0,0 +1,1051 @@ +{ + "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": 1, + "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": 2, + "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": 3, + "id": "60c3b6c4", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "d4d71d25-7f64-4422-8f51-3dd4e75bc206\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "s0\n", + "\n", + "\n", + "s0\n", + "\n", + "\n", + "\n", + "d4d71d25-7f64-4422-8f51-3dd4e75bc206->s0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "s0->s0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "s1\n", + "\n", + "s1\n", + "\n", + "\n", + "\n", + "s0->s1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "s3\n", + "\n", + "s3\n", + "\n", + "\n", + "\n", + "s1->s3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "s2\n", + "\n", + "s2\n", + "\n", + "\n", + "\n", + "s1->s2\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "s3->s1\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "s3->s2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "s2->s0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "s4\n", + "\n", + "s4\n", + "\n", + "\n", + "\n", + "s2->s4\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "s4->s3\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "s4->s4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "DFA(states={'s3', 's4', 's1', 's0', 's2'}, 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": 3, + "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": 4, + "id": "9825667c", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Please enter your input: 1111\n", + "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. An arbitrary finite set $\\Sigma$, called the **input alphabet**.\n", + "2. Another arbitrary finite set **Q**, whose elements are called **states**.\n", + "3. An arbitrary **transition** function $\\delta: Q \\times \\Sigma \\rightarrow Q$\n", + "4. A **state state** $\\textbf{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. **q1** is the only accepting state, denoted by the concentric circles." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "67d76f2b", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "5ccc4c6e-93d3-458a-ab49-ca52e457156e\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "q0\n", + "\n", + "q0\n", + "\n", + "\n", + "\n", + "5ccc4c6e-93d3-458a-ab49-ca52e457156e->q0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "q0->q0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "q1\n", + "\n", + "\n", + "q1\n", + "\n", + "\n", + "\n", + "q0->q1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "q1->q0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "q2\n", + "\n", + "q2\n", + "\n", + "\n", + "\n", + "q1->q2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "q2->q1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "q2->q2\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\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": 5, + "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, **q0** is denoted by the unattached arrow pointing at it. The string \"1\" would be accepted because you would navigate from **q0** to **q1**. \"11\" would create the chain **q0 -> q1 -> q2**, which would be rejected. \"111\" would return to **q1**, 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": 6, + "id": "d646b08e", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "191de082-f7fa-46a9-b62f-a2b8cdb4ad38\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "{q2, q0}\n", + "\n", + "{q2, q0}\n", + "\n", + "\n", + "\n", + "191de082-f7fa-46a9-b62f-a2b8cdb4ad38->{q2, q0}\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "{q2, q0}->{q2, q0}\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "{q1}\n", + "\n", + "\n", + "{q1}\n", + "\n", + "\n", + "\n", + "{q2, q0}->{q1}\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "{q1}->{q2, q0}\n", + "\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "DFA(states={frozenset({'q2', 'q0'}), frozenset({'q1'})}, 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": 6, + "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", + "Automatic languages have several closure properties –– Let *L* and *L'* be arbitrary automatic languages over an arbitrary alphabet $\\Sigma$. Then,\n", + "- $\\bar L = \\Sigma^{*}\\backslash L$ is automatic\n", + "- L $\\cup$ L' is automatic\n", + "- L $\\cap$ L' is automatic\n", + "- L $\\backslash$ L' is automatic\n", + "- L $\\oplus$ L' is automatic\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": 7, + "id": "a789002f", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "23f8adbf-d6bb-4e11-9a76-480fe5eaf9e7\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "23f8adbf-d6bb-4e11-9a76-480fe5eaf9e7->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "0,1\n", + "\n", + "\n", + "\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": 7, + "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": 9, + "id": "fd2eb520", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Please enter your input: 111\n", + "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": 17, + "id": "92403fa9", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Please enter your input: 101\n", + "Accepted\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": 28, + "id": "b762dae6", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "cc0bc40f-8c8b-4c6d-bd38-ee923a9d2dcb\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "cc0bc40f-8c8b-4c6d-bd38-ee923a9d2dcb->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "2->0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "2->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "0,1\n", + "\n", + "\n", + "\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": 28, + "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_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": 31, + "id": "1623dda1", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "6b41593b-390b-4a67-86b9-58e0333387d1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "6b41593b-390b-4a67-86b9-58e0333387d1->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "2->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "0,1\n", + "\n", + "\n", + "\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": 31, + "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": "Python 3 (ipykernel)", + "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.12" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/mkdocs.yml b/mkdocs.yml index 06ca5cc9..f1849f8b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -56,6 +56,7 @@ plugins: # enabled_if_env: ENABLE_PDF_EXPORT - macros: enabled_if_env: ENABLE_PDF_EXPORT + - mkdocs-jupyter - mkdocstrings: enabled: !ENV [ENABLE_MKDOCSTRINGS, true] custom_templates: templates @@ -116,6 +117,8 @@ nav: - index.md - migration.md - characteristics.md + - Theory: + - DFA Introduction: notebooks/DFA.ipynb - Examples: - examples/fa-examples.md - examples/perf-examples.md diff --git a/requirements.docs.txt b/requirements.docs.txt index 043ca8ec..2117a03c 100644 --- a/requirements.docs.txt +++ b/requirements.docs.txt @@ -1,4 +1,5 @@ mkdocs==1.4.2 +mkdocs-jupyter==0.24.8 mkdocs-material==9.1.9 mkdocs-macros-plugin==0.7.0 mkdocstrings==0.23.0