<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>CF Bolz-Tereick's Web Page (Posts about prolog)</title><link>https://cfbolz.de/</link><description></description><atom:link href="https://cfbolz.de/categories/prolog.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2025 &lt;a href="mailto:cfbolz@gmx.de"&gt;CF Bolz-Tereick&lt;/a&gt; </copyright><lastBuildDate>Fri, 23 May 2025 19:54:38 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>DFA Minimization using Rational Trees in Prolog</title><link>https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/</link><dc:creator>CF Bolz-Tereick</dc:creator><description>&lt;p&gt;I was inspired by Phil Zucker's blog post &lt;a class="reference external" href="https://www.philipzucker.com/naive_automata/"&gt;Naive Automata
Minimization&lt;/a&gt;. On Twitter he &lt;a class="reference external" href="https://twitter.com/SandMouth/status/1815886804999963028"&gt;wrote&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Maybe a way to actualize this is using knot tied rational trees instead of integer state ids. But this has its own confusions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I really wanted to try to implement this in SWI-Prolog.&lt;/p&gt;
&lt;section id="rational-trees"&gt;
&lt;h2&gt;Rational Trees&lt;/h2&gt;
&lt;p&gt;Rational trees are Prolog terms that are circular in some way. They can be created by unification:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code prolog"&gt;&lt;a id="rest_code_a872ecea9b134744b963e0168004fa6a-1" name="rest_code_a872ecea9b134744b963e0168004fa6a-1" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_a872ecea9b134744b963e0168004fa6a-1"&gt;&lt;/a&gt;&lt;span class="s s-Atom"&gt;?-&lt;/span&gt; &lt;span class="nv"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;a id="rest_code_a872ecea9b134744b963e0168004fa6a-2" name="rest_code_a872ecea9b134744b963e0168004fa6a-2" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_a872ecea9b134744b963e0168004fa6a-2"&gt;&lt;/a&gt;&lt;span class="nv"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now the term &lt;code&gt;X&lt;/code&gt; represents the term &lt;code&gt;f(f(f(f(f(f(...))))))&lt;/code&gt;:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code prolog"&gt;&lt;a id="rest_code_260947e5909c4bcf8ecd216f2305250f-1" name="rest_code_260947e5909c4bcf8ecd216f2305250f-1" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_260947e5909c4bcf8ecd216f2305250f-1"&gt;&lt;/a&gt;&lt;span class="s s-Atom"&gt;?-&lt;/span&gt; &lt;span class="nv"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;X&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="p"&gt;))))))).&lt;/span&gt;
&lt;a id="rest_code_260947e5909c4bcf8ecd216f2305250f-2" name="rest_code_260947e5909c4bcf8ecd216f2305250f-2" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_260947e5909c4bcf8ecd216f2305250f-2"&gt;&lt;/a&gt;&lt;span class="nv"&gt;X&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;A&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;A&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;This is a bit like appending a list to itself in Python:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python-console"&gt;&lt;a id="rest_code_5ac4a7049a8e402e9cd543c0d018fcf3-1" name="rest_code_5ac4a7049a8e402e9cd543c0d018fcf3-1" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_5ac4a7049a8e402e9cd543c0d018fcf3-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;a id="rest_code_5ac4a7049a8e402e9cd543c0d018fcf3-2" name="rest_code_5ac4a7049a8e402e9cd543c0d018fcf3-2" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_5ac4a7049a8e402e9cd543c0d018fcf3-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_5ac4a7049a8e402e9cd543c0d018fcf3-3" name="rest_code_5ac4a7049a8e402e9cd543c0d018fcf3-3" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_5ac4a7049a8e402e9cd543c0d018fcf3-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;
&lt;a id="rest_code_5ac4a7049a8e402e9cd543c0d018fcf3-4" name="rest_code_5ac4a7049a8e402e9cd543c0d018fcf3-4" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_5ac4a7049a8e402e9cd543c0d018fcf3-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;[[...]]&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;SWI-Prolog is good at dealing with these recursive terms, it can unify them
etc. Python is less good. While &lt;code&gt;repr&lt;/code&gt; does the right thing, as we saw
above, &lt;code&gt;==&lt;/code&gt; produces a recursion error:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python-console"&gt;&lt;a id="rest_code_6726af504c0e4c13a3a117e137be3607-1" name="rest_code_6726af504c0e4c13a3a117e137be3607-1" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6726af504c0e4c13a3a117e137be3607-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nn"&gt;copy&lt;/span&gt;
&lt;a id="rest_code_6726af504c0e4c13a3a117e137be3607-2" name="rest_code_6726af504c0e4c13a3a117e137be3607-2" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6726af504c0e4c13a3a117e137be3607-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deepcopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_6726af504c0e4c13a3a117e137be3607-3" name="rest_code_6726af504c0e4c13a3a117e137be3607-3" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6726af504c0e4c13a3a117e137be3607-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;[[...]]&lt;/span&gt;
&lt;a id="rest_code_6726af504c0e4c13a3a117e137be3607-4" name="rest_code_6726af504c0e4c13a3a117e137be3607-4" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6726af504c0e4c13a3a117e137be3607-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deepcopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;
&lt;a id="rest_code_6726af504c0e4c13a3a117e137be3607-5" name="rest_code_6726af504c0e4c13a3a117e137be3607-5" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6726af504c0e4c13a3a117e137be3607-5"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a id="rest_code_6726af504c0e4c13a3a117e137be3607-6" name="rest_code_6726af504c0e4c13a3a117e137be3607-6" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6726af504c0e4c13a3a117e137be3607-6"&gt;&lt;/a&gt;  File &lt;span class="nb"&gt;"&amp;lt;python-input-9&amp;gt;"&lt;/span&gt;, line &lt;span class="m"&gt;1&lt;/span&gt;, in &lt;span class="n"&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_6726af504c0e4c13a3a117e137be3607-7" name="rest_code_6726af504c0e4c13a3a117e137be3607-7" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6726af504c0e4c13a3a117e137be3607-7"&gt;&lt;/a&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="n"&gt;copy&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;deepcopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;
&lt;a id="rest_code_6726af504c0e4c13a3a117e137be3607-8" name="rest_code_6726af504c0e4c13a3a117e137be3607-8" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6726af504c0e4c13a3a117e137be3607-8"&gt;&lt;/a&gt;&lt;span class="gr"&gt;RecursionError&lt;/span&gt;: &lt;span class="n"&gt;maximum recursion depth exceeded&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/section&gt;
&lt;section id="defining-dfas-with-rational-trees"&gt;
&lt;h2&gt;Defining DFAs with Rational Trees&lt;/h2&gt;
&lt;p&gt;We can use these rational trees in Prolog to represent deterministic
finite automata (DFAs) over the alphabet with two characters, &lt;code&gt;a&lt;/code&gt; and
&lt;code&gt;b&lt;/code&gt;. A DFA is a Prolog term representing the starting state. A state is
represented by a term &lt;code&gt;state(Accept, NextStateA, NextStateB)&lt;/code&gt; where
&lt;code&gt;Accept&lt;/code&gt; is &lt;code&gt;true&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;. The two next states are again
&lt;code&gt;state&lt;/code&gt; terms. A next state can also be &lt;code&gt;none&lt;/code&gt;. To achieve loops in
the DFA, we use rational trees to refer to outer states.&lt;/p&gt;
&lt;p&gt;To represent a DFA that matches &lt;code class="docutils literal"&gt;a+&lt;/code&gt; we can use the term &lt;code class="docutils literal"&gt;state(false,
state(true, &lt;span class="pre"&gt;...,&lt;/span&gt; none), none)&lt;/code&gt;, where the &lt;code class="docutils literal"&gt;...&lt;/code&gt; is the &lt;em&gt;second&lt;/em&gt; state term.
We can build it like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code prolog"&gt;&lt;a id="rest_code_9c6db451cae24409a358a4b2a6d9e924-1" name="rest_code_9c6db451cae24409a358a4b2a6d9e924-1" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_9c6db451cae24409a358a4b2a6d9e924-1"&gt;&lt;/a&gt;&lt;span class="nf"&gt;dfa_a_plus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;S1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;a id="rest_code_9c6db451cae24409a358a4b2a6d9e924-2" name="rest_code_9c6db451cae24409a358a4b2a6d9e924-2" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_9c6db451cae24409a358a4b2a6d9e924-2"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;S1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_9c6db451cae24409a358a4b2a6d9e924-3" name="rest_code_9c6db451cae24409a358a4b2a6d9e924-3" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_9c6db451cae24409a358a4b2a6d9e924-3"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;S2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can also build it as a non-minimal DFA with an extra state:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code prolog"&gt;&lt;a id="rest_code_249e9710361b467a9275e760e79ebbde-1" name="rest_code_249e9710361b467a9275e760e79ebbde-1" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_249e9710361b467a9275e760e79ebbde-1"&gt;&lt;/a&gt;&lt;span class="nf"&gt;dfa_a_plus_inefficient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;S1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt; &lt;span class="c1"&gt;% less efficient example automaton for a+ with extra states&lt;/span&gt;
&lt;a id="rest_code_249e9710361b467a9275e760e79ebbde-2" name="rest_code_249e9710361b467a9275e760e79ebbde-2" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_249e9710361b467a9275e760e79ebbde-2"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;S1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_249e9710361b467a9275e760e79ebbde-3" name="rest_code_249e9710361b467a9275e760e79ebbde-3" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_249e9710361b467a9275e760e79ebbde-3"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;S2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_249e9710361b467a9275e760e79ebbde-4" name="rest_code_249e9710361b467a9275e760e79ebbde-4" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_249e9710361b467a9275e760e79ebbde-4"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;S3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_249e9710361b467a9275e760e79ebbde-5" name="rest_code_249e9710361b467a9275e760e79ebbde-5" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_249e9710361b467a9275e760e79ebbde-5"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;S4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Those two terms unify successfully and are thus equivalent:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code prolog"&gt;&lt;a id="rest_code_02d888354d894fcba7ae59c7391d647c-1" name="rest_code_02d888354d894fcba7ae59c7391d647c-1" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_02d888354d894fcba7ae59c7391d647c-1"&gt;&lt;/a&gt;&lt;span class="s s-Atom"&gt;?-&lt;/span&gt; &lt;span class="nf"&gt;dfa_a_plus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;S1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;dfa_a_plus_inefficient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;S2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nv"&gt;S1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;S2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;a id="rest_code_02d888354d894fcba7ae59c7391d647c-2" name="rest_code_02d888354d894fcba7ae59c7391d647c-2" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_02d888354d894fcba7ae59c7391d647c-2"&gt;&lt;/a&gt; &lt;span class="nv"&gt;S1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;S2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="nv"&gt;S1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;% where&lt;/span&gt;
&lt;a id="rest_code_02d888354d894fcba7ae59c7391d647c-3" name="rest_code_02d888354d894fcba7ae59c7391d647c-3" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_02d888354d894fcba7ae59c7391d647c-3"&gt;&lt;/a&gt;     &lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="nv"&gt;S1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="nv"&gt;S1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;They unify because the Prolog rational trees are "observationally equivalent"
in the sense of Phil's blog post, which the Prolog unification algorithm can
check.&lt;/p&gt;
&lt;p&gt;The string representation of SWI-Prolog is not minimal though.
This shows that for the printer uses some internal knowledge about how the
trees were constructed, because from pure Prolog it should not be observable
where the bound logic variable "was" in the structure (the term is ground).&lt;/p&gt;
&lt;/section&gt;
&lt;section id="matching"&gt;
&lt;h2&gt;Matching&lt;/h2&gt;
&lt;p&gt;So far we only defined automata, but we can also write a predicate that matches
one against a list of &lt;code class="docutils literal"&gt;a&lt;/code&gt; or &lt;code class="docutils literal"&gt;b&lt;/code&gt;.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code prolog"&gt;&lt;a id="rest_code_0907b86b29cb4d1e8f53c72d4fa1cfa6-1" name="rest_code_0907b86b29cb4d1e8f53c72d4fa1cfa6-1" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_0907b86b29cb4d1e8f53c72d4fa1cfa6-1"&gt;&lt;/a&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;
&lt;a id="rest_code_0907b86b29cb4d1e8f53c72d4fa1cfa6-2" name="rest_code_0907b86b29cb4d1e8f53c72d4fa1cfa6-2" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_0907b86b29cb4d1e8f53c72d4fa1cfa6-2"&gt;&lt;/a&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s s-Atom"&gt;a&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;Rest&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;StateA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt; &lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Rest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;StateA&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;a id="rest_code_0907b86b29cb4d1e8f53c72d4fa1cfa6-3" name="rest_code_0907b86b29cb4d1e8f53c72d4fa1cfa6-3" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_0907b86b29cb4d1e8f53c72d4fa1cfa6-3"&gt;&lt;/a&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s s-Atom"&gt;b&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;Rest&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;StateB&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt; &lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Rest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;StateB&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Some example calls:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code prolog"&gt;&lt;a id="rest_code_08f33ce3f33c48d69c79ecb495af9733-1" name="rest_code_08f33ce3f33c48d69c79ecb495af9733-1" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_08f33ce3f33c48d69c79ecb495af9733-1"&gt;&lt;/a&gt;&lt;span class="s s-Atom"&gt;?-&lt;/span&gt; &lt;span class="nf"&gt;dfa_a_plus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s s-Atom"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;a&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt; &lt;span class="c1"&gt;% succeeds&lt;/span&gt;
&lt;a id="rest_code_08f33ce3f33c48d69c79ecb495af9733-2" name="rest_code_08f33ce3f33c48d69c79ecb495af9733-2" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_08f33ce3f33c48d69c79ecb495af9733-2"&gt;&lt;/a&gt;
&lt;a id="rest_code_08f33ce3f33c48d69c79ecb495af9733-3" name="rest_code_08f33ce3f33c48d69c79ecb495af9733-3" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_08f33ce3f33c48d69c79ecb495af9733-3"&gt;&lt;/a&gt;&lt;span class="s s-Atom"&gt;?-&lt;/span&gt; &lt;span class="nf"&gt;dfa_a_plus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;a id="rest_code_08f33ce3f33c48d69c79ecb495af9733-4" name="rest_code_08f33ce3f33c48d69c79ecb495af9733-4" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_08f33ce3f33c48d69c79ecb495af9733-4"&gt;&lt;/a&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;a id="rest_code_08f33ce3f33c48d69c79ecb495af9733-5" name="rest_code_08f33ce3f33c48d69c79ecb495af9733-5" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_08f33ce3f33c48d69c79ecb495af9733-5"&gt;&lt;/a&gt;
&lt;a id="rest_code_08f33ce3f33c48d69c79ecb495af9733-6" name="rest_code_08f33ce3f33c48d69c79ecb495af9733-6" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_08f33ce3f33c48d69c79ecb495af9733-6"&gt;&lt;/a&gt;&lt;span class="s s-Atom"&gt;?-&lt;/span&gt; &lt;span class="nf"&gt;dfa_a_plus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s s-Atom"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;b&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;a id="rest_code_08f33ce3f33c48d69c79ecb495af9733-7" name="rest_code_08f33ce3f33c48d69c79ecb495af9733-7" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_08f33ce3f33c48d69c79ecb495af9733-7"&gt;&lt;/a&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;a id="rest_code_08f33ce3f33c48d69c79ecb495af9733-8" name="rest_code_08f33ce3f33c48d69c79ecb495af9733-8" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_08f33ce3f33c48d69c79ecb495af9733-8"&gt;&lt;/a&gt;
&lt;a id="rest_code_08f33ce3f33c48d69c79ecb495af9733-9" name="rest_code_08f33ce3f33c48d69c79ecb495af9733-9" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_08f33ce3f33c48d69c79ecb495af9733-9"&gt;&lt;/a&gt;&lt;span class="s s-Atom"&gt;?-&lt;/span&gt; &lt;span class="nf"&gt;dfa_a_plus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s s-Atom"&gt;b&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;S&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;a id="rest_code_08f33ce3f33c48d69c79ecb495af9733-10" name="rest_code_08f33ce3f33c48d69c79ecb495af9733-10" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_08f33ce3f33c48d69c79ecb495af9733-10"&gt;&lt;/a&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Let's look at a more complicated DFA example, the one from the blog post:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code prolog"&gt;&lt;a id="rest_code_131d0da22f664f9a85d0c26d794bfce3-1" name="rest_code_131d0da22f664f9a85d0c26d794bfce3-1" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_131d0da22f664f9a85d0c26d794bfce3-1"&gt;&lt;/a&gt;&lt;span class="nf"&gt;dfa_blog_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;S1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;a id="rest_code_131d0da22f664f9a85d0c26d794bfce3-2" name="rest_code_131d0da22f664f9a85d0c26d794bfce3-2" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_131d0da22f664f9a85d0c26d794bfce3-2"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;S1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_131d0da22f664f9a85d0c26d794bfce3-3" name="rest_code_131d0da22f664f9a85d0c26d794bfce3-3" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_131d0da22f664f9a85d0c26d794bfce3-3"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;S2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;% states S2 and S3 are equivalent&lt;/span&gt;
&lt;a id="rest_code_131d0da22f664f9a85d0c26d794bfce3-4" name="rest_code_131d0da22f664f9a85d0c26d794bfce3-4" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_131d0da22f664f9a85d0c26d794bfce3-4"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;S3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S3&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_131d0da22f664f9a85d0c26d794bfce3-5" name="rest_code_131d0da22f664f9a85d0c26d794bfce3-5" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_131d0da22f664f9a85d0c26d794bfce3-5"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;S4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S4&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;% states S4 and S5 are equivalent&lt;/span&gt;
&lt;a id="rest_code_131d0da22f664f9a85d0c26d794bfce3-6" name="rest_code_131d0da22f664f9a85d0c26d794bfce3-6" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_131d0da22f664f9a85d0c26d794bfce3-6"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;S5&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;S4&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;(there's a nice diagram in Phil's post.)&lt;/p&gt;
&lt;p&gt;If we want to minimize it, we can assign a number to each unique state
reachable from the starting state. We can use an association list, i.e. a list
where the entries are terms State/Number. States that are observationally
equivalent unify, so we can just use Prolog unification (via the "member"
predicate which checks whether a term is an element of a list).&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code prolog"&gt;&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-1" name="rest_code_6dd148ebf134442ca8d763489a1148d8-1" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-1"&gt;&lt;/a&gt;&lt;span class="nf"&gt;number_states&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-2" name="rest_code_6dd148ebf134442ca8d763489a1148d8-2" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-2"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;% number the states, the first number is 0&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-3" name="rest_code_6dd148ebf134442ca8d763489a1148d8-3" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-3"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;number_states_helper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nv"&gt;LRev&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-4" name="rest_code_6dd148ebf134442ca8d763489a1148d8-4" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-4"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;% reverse the result, because it is built in reverse order&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-5" name="rest_code_6dd148ebf134442ca8d763489a1148d8-5" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-5"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;reverse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;LRev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-6" name="rest_code_6dd148ebf134442ca8d763489a1148d8-6" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-6"&gt;&lt;/a&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-7" name="rest_code_6dd148ebf134442ca8d763489a1148d8-7" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-7"&gt;&lt;/a&gt;&lt;span class="nf"&gt;number_states_helper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt; &lt;span class="c1"&gt;% none doesn't need a number&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-8" name="rest_code_6dd148ebf134442ca8d763489a1148d8-8" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-8"&gt;&lt;/a&gt;&lt;span class="nf"&gt;number_states_helper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Num&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-9" name="rest_code_6dd148ebf134442ca8d763489a1148d8-9" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-9"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;% if we already gave a number to the state, we are done.&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-10" name="rest_code_6dd148ebf134442ca8d763489a1148d8-10" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-10"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;member&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="s s-Atom"&gt;/&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-11" name="rest_code_6dd148ebf134442ca8d763489a1148d8-11" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-11"&gt;&lt;/a&gt;&lt;span class="nf"&gt;number_states_helper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;NumIn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;NumOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;LIn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;LOut&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-12" name="rest_code_6dd148ebf134442ca8d763489a1148d8-12" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-12"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;% if we've never seen the state, we assign it NumIn&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-13" name="rest_code_6dd148ebf134442ca8d763489a1148d8-13" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-13"&gt;&lt;/a&gt;    &lt;span class="o"&gt;not&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;member&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="s s-Atom"&gt;/&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;LIn&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-14" name="rest_code_6dd148ebf134442ca8d763489a1148d8-14" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-14"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;L1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;NumIn&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;LIn&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-15" name="rest_code_6dd148ebf134442ca8d763489a1148d8-15" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-15"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;NextStateA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;NextStateB&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-16" name="rest_code_6dd148ebf134442ca8d763489a1148d8-16" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-16"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;succ&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;NumIn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Num1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-17" name="rest_code_6dd148ebf134442ca8d763489a1148d8-17" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-17"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;number_states_helper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;NextStateA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Num1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Num2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;L1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;L2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_6dd148ebf134442ca8d763489a1148d8-18" name="rest_code_6dd148ebf134442ca8d763489a1148d8-18" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_6dd148ebf134442ca8d763489a1148d8-18"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;number_states_helper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;NextStateB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Num2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;NumOut&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;L2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;LOut&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;We can apply this predicate from the non-minimal DFA:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code prolog"&gt;&lt;a id="rest_code_0f0595c2407245b991e509aeab1514e0-1" name="rest_code_0f0595c2407245b991e509aeab1514e0-1" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_0f0595c2407245b991e509aeab1514e0-1"&gt;&lt;/a&gt;&lt;span class="p"&gt;:-&lt;/span&gt; &lt;span class="nf"&gt;dfa_blog_post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;number_states&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s s-Atom"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s s-Atom"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;a id="rest_code_0f0595c2407245b991e509aeab1514e0-2" name="rest_code_0f0595c2407245b991e509aeab1514e0-2" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_0f0595c2407245b991e509aeab1514e0-2"&gt;&lt;/a&gt;&lt;span class="mi"&gt;3&lt;/span&gt;
&lt;a id="rest_code_0f0595c2407245b991e509aeab1514e0-3" name="rest_code_0f0595c2407245b991e509aeab1514e0-3" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_0f0595c2407245b991e509aeab1514e0-3"&gt;&lt;/a&gt;&lt;span class="s s-Atom"&gt;@&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;S_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;S_2&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nv"&gt;S_2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;S_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;S_2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;S_1&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],[&lt;/span&gt;&lt;span class="nv"&gt;S_1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;S_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;S_1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nv"&gt;S_1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nv"&gt;S_2&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;S_1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nv"&gt;S_1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="nv"&gt;S_2&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The list &lt;code class="docutils literal"&gt;L&lt;/code&gt; of numbered states is kind of hard to grasp due to the messy way
that rational tree terms gets printed, but the important part is that we found
out that three states is the minimal size.&lt;/p&gt;
&lt;p&gt;After we numbered the states we can use that numbering to produce a graphviz
graph for the DFA:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code prolog"&gt;&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-1" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-1" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-1"&gt;&lt;/a&gt;&lt;span class="nf"&gt;to_dot&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-2" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-2" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-2"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;number_states&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;D&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-3" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-3" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-3"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"digraph G {"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s s-Atom"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-4" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-4" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-4"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;'start [label="", shape=none]'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s s-Atom"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-5" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-5" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-5"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"start -&amp;gt; 0"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s s-Atom"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-6" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-6" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-6"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;to_dot_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;L&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-7" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-7" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-7"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"}"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s s-Atom"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-8" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-8" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-8"&gt;&lt;/a&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-9" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-9" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-9"&gt;&lt;/a&gt;&lt;span class="nf"&gt;to_dot_list&lt;/span&gt;&lt;span class="p"&gt;([],&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-10" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-10" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-10"&gt;&lt;/a&gt;&lt;span class="nf"&gt;to_dot_list&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;NumState&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="nv"&gt;Rest&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nv"&gt;FullList&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-11" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-11" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-11"&gt;&lt;/a&gt;    &lt;span class="nv"&gt;State&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;state&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;StateA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;StateB&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-12" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-12" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-12"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;to_dot_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;NumState&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-13" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-13" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-13"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;to_dot_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;StateA&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;NumState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;FullList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;a&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-14" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-14" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-14"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;to_dot_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;StateB&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;NumState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;FullList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s s-Atom"&gt;b&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-15" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-15" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-15"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;to_dot_list&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Rest&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;FullList&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-16" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-16" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-16"&gt;&lt;/a&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-17" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-17" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-17"&gt;&lt;/a&gt;&lt;span class="nf"&gt;to_dot_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-18" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-18" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-18"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Num&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;" [shape=doublecircle]"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s s-Atom"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-19" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-19" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-19"&gt;&lt;/a&gt;&lt;span class="nf"&gt;to_dot_node&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Num&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-20" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-20" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-20"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Num&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;" [shape=circle]"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s s-Atom"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-21" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-21" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-21"&gt;&lt;/a&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-22" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-22" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-22"&gt;&lt;/a&gt;&lt;span class="nf"&gt;to_dot_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s s-Atom"&gt;none&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;_&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-23" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-23" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-23"&gt;&lt;/a&gt;&lt;span class="nf"&gt;to_dot_edge&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;NumPrev&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;FullList&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;Char&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:-&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-24" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-24" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-24"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;member&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;State&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nv"&gt;NumState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;FullList&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;a id="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-25" name="rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-25" href="https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/#rest_code_1d283bf8d55c4681b4b1dd1d45b9a8dc-25"&gt;&lt;/a&gt;    &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;NumPrev&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;" -&amp;gt; "&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;NumState&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;" [label="&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;Char&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"]"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s s-Atom"&gt;nl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If we apply this to the example we get:&lt;/p&gt;
&lt;pre class="literal-block"&gt;digraph G {
start [label="", shape=none]
start -&amp;gt; 0
0 [shape=circle]
0 -&amp;gt; 1 [label=a]
0 -&amp;gt; 1 [label=b]
1 [shape=circle]
1 -&amp;gt; 2 [label=a]
1 -&amp;gt; 1 [label=b]
2 [shape=doublecircle]
2 -&amp;gt; 2 [label=a]
2 -&amp;gt; 2 [label=b]
}&lt;/pre&gt;
&lt;p&gt;Which looks like this:&lt;/p&gt;
&lt;img alt="DFA vizualization" src="https://cfbolz.de/2024-dfa.svg"&gt;
&lt;/section&gt;</description><category>automata</category><category>prolog</category><guid>https://cfbolz.de/posts/dfa-minimization-using-rational-trees-in-prolog/</guid><pubDate>Wed, 24 Jul 2024 18:41:20 GMT</pubDate></item></channel></rss>