{"id":26207395,"url":"https://github.com/oelin/context-free-planning","last_synced_at":"2025-07-30T18:10:02.478Z","repository":{"id":89778599,"uuid":"590619579","full_name":"oelin/context-free-planning","owner":"oelin","description":"Finding feasible solutions to planning problems using generative context-free grammars.","archived":false,"fork":false,"pushed_at":"2023-03-19T20:56:47.000Z","size":29,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-12T05:33:02.633Z","etag":null,"topics":["artificial-intelligence","context-free-grammars","path-planning"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/oelin.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-01-18T20:28:26.000Z","updated_at":"2023-11-04T06:32:45.000Z","dependencies_parsed_at":"2023-03-12T11:01:11.181Z","dependency_job_id":null,"html_url":"https://github.com/oelin/context-free-planning","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/oelin/context-free-planning","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oelin%2Fcontext-free-planning","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oelin%2Fcontext-free-planning/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oelin%2Fcontext-free-planning/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oelin%2Fcontext-free-planning/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/oelin","download_url":"https://codeload.github.com/oelin/context-free-planning/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/oelin%2Fcontext-free-planning/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":267914774,"owners_count":24164791,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-30T02:00:09.044Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["artificial-intelligence","context-free-grammars","path-planning"],"created_at":"2025-03-12T05:32:20.596Z","updated_at":"2025-07-30T18:10:02.470Z","avatar_url":"https://github.com/oelin.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Context Free Planning\n\nFinding feasible solutions to planning problems using generative context-free grammars.\n\n## 1. Introduction\n\nContext-free planning is a method for finding feasible solutions to deterministic planning problems. \n\nIt relies on the fact that such problems can often be modelled as automata, implying that they also have a corresponding context-free representation. Using a context-free grammar (CFG), we can then generate solutions automatically from the given set of production rules.\n\n\n## 2. Modelling Problems With Automata\n\nThe first step in context-free planning is to devise an automaton which represents the problem of interest. For example, consider the problem of navigating within a 3x3 board towards some target cell.\n\n```\n+---+---+---+\n| A |   |   |\n+---+---+---+\n|   |   |   |\n+---+---+---+\n|   |   | X |\n+---+---+---+\n```\n\nLet $M = ( Q, \\Sigma, \\delta, q_0, F)$ be a finite state automaton, where:\n\n- $Q =  \\{ (x, y) | x, y \\in \\{0,\\dots, 2\\} \\}$\n\n- $\\Sigma = \\{ 0, \\dots, 3\\}$\n\n- $\\delta((x, y), 0) = (x, y -1)$\n\n- $\\delta((x, y), 1) = (x + 1, y)$\n\n- $\\delta((x, y), 2) = (x, y + 1)$\n\n- $\\delta((x,y), 3) = (x - 1, y)$\n\n- $\\delta((x,y), a) = (x, y)$ if $a$ would move the agent out-of-bounds.\n\n- $q_0 = (0, 0)$\n\n- $F = \\{ (2, 2) \\}$\n\nThis automaton accepts a language $\\mathcal{L}$, consisting of all strings which represent successful paths to the target. If we can find a way to generate strings in $\\mathcal{L}$, then we effectively generate solutions to the problem. \n\n\n## 3. Solving Problems With Context-free Grammars\n\nAn interesting feature of context-free languages is that they're a *superset* of regular languages. Then for any finite state automaton accepting some language there exists a context-free grammar accepting the same language. \n\nSpecifically, if $M = ( Q, \\Sigma, \\delta, q_0, F)$ is a finite state automaton then the equivalent context-free grammar is given by  $G = (V, \\Sigma', P, S)$, where:\n\n- $V =Q$\n\n- $\\Sigma' = \\Sigma \\cup \\{\\epsilon\\}$\n\n- $P = \\{ q_i \\to a(q_j)\\ |\\  \\delta(q_i,a)=q_j \\} \\cup \\{ q_i \\to \\epsilon \\\\ |\\ \\delta(q_i, a) \\in F\\}$\n\n- $S  = q_0$\n\nThis context-free grammar accepts the same language as $M$, however it additionally allows us to *generate* strings from the language using production rules. Applying this to the navigation problem, we can now solve it using a context-free grammar *constructed from* the automaton. Constructing the grammar, and generating strings are both linear time operations in the size of the original automaton. Hence, this approach is suitable for problems involving relatively small state or action spaces.\n\n\n## 4. Optimal Planning\n\nSo far, we have provided an efficient method for solving deterministic planning problems using context-free grammars. However, we have not considered the extent to which these solutions are optimal. In general, an arbitrary solution selected from the language of all solutions will not be \"optimal\" for some measure of optimality. In many problems, there are an infinite number of sub-optimal solution while only a finite number of optimal ones. Navigation to a target is one such problem.\n\n\n## 5. Implementation \n\nIn this section, we describe how to implement context-free planning in Python. We'll again return to the navigation problem as it's both simple and practically useful to some extent. To start, we implement a class for representing finite state automata; which isn't much more than a tuple. The `has()` method takes a string and runs it through the automaton, returning whether the string was accepted or not. The transition function must be a callable which maps between state-symbol tuples and states.\n\n```py\nfrom typing import Type, Union, Callable, Tuple\nfrom dataclasses import dataclass\nfrom functools import reduce\n```\n\n```py\nState: Type = str \nSymbol: Type = str\nString: Type = Tuple[Symbol]\n```\n\n```py\n@dataclass\nclass Automaton:\n\t\"\"\"\n\tImplements a finite state automaton.\n\t\"\"\"\n\t\n\tstates: set[State]\n\talphabet: set[Symbol]\n\ttransition: Callable[Tuple[State, Symbol], State]\n\tstart: State\n\tfinal: set[State]\n\t\n\t\n\tdef reduce(self, string: String) -\u003e State:\n\t\t\"\"\"\n\t\tReduces a string using the automaton's state transition \n\t\tfunction.\n\t\t\"\"\"\n\t\t\n\t\treturn reduce(self.transition, string, self.start)\t\n\n\n\tdef has(self, string: String) -\u003e bool:\n\t\t\"\"\"\n\t\tChecks whether a string is in the automaton's language.\n\t\t\"\"\"\n\t\t\n\t\treturn self.reduce(string) in self.final \n```\n\nNext, let's define a class for representing context-free grammars. The `expand()` method expands symbol by recursively applying production rules until only terminal symbols are left. The production rule function must map between variable symbols and strings.\n\n```py\n@dataclass \nclass Grammar:\n\t\"\"\"\n\tImplements a typical context-free grammar.\n\t\"\"\"\n\t\n\tvariables: set[Symbol]\n\tterminals: set[Symbol]\n\tproduction: Callable[Symbol, String]\n\tstart: Symbol\n\t\n\t\n\tdef expand(self, symbol: Symbol) -\u003e String:\n\t\t\"\"\"\n\t\tExpands a symbol by recursively applying production \n\t\trules until only terminal symbols are left.\n\t\t\"\"\"\n\t\t\n\t\treturn symbol if symbol not in self.variables else ''.join(map(\n\t\t\tself.expand,\n\t\t\tself.production(symbol),\n\t\t))\n```\n\n\nWith these two classes defined, we can now work on creating a function which convert an automaton *into* an equivalent context-free grammar. We will use the same construction as defined in (3). \n\n\n```py\nimport random\nfrom collactions import defaultdict\n```\n\n\n```py\ndef convertAutomatonToGrammar(automaton: Automaton) -\u003e Grammar:\n\t\"\"\"\n\tConverts an automaton into a context-free grammar.\n\t\"\"\"\n\t\n\t\n\tvariables = automaton.states \n\tterminals = automaton.alphabet \n\tstart = automaton.start \n\t\n\t\n\tproductions = defaultdict(lambda: [])\n\t\n\t\n\tfor variable in variables:\n\t\tfor terminal in terminals:\n\t\t\n\t\t\tstate = automaton.transition((variable, terminal))\n\t\t\t\n\t\t\tif state in automaton.final:\n\t\t\t\tstate = '$'\n\t\t\t\n\t\t\tproductions[variable].append((terminal, state))\n\t\n\t\n\tdef production(symbol: Symbol) -\u003e String:\n\t\treturn random.choice(productions[symbol])\n\t\n\t\n\treturn Grammar(\n\t\tvariables, \n\t\tterminals,\n\t\tproduction,\n\t\tstart,\n\t)\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foelin%2Fcontext-free-planning","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Foelin%2Fcontext-free-planning","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Foelin%2Fcontext-free-planning/lists"}