<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.9.0">Jekyll</generator><link href="https://blog.evilbyte.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog.evilbyte.com/" rel="alternate" type="text/html" /><updated>2021-03-11T03:19:20+00:00</updated><id>https://blog.evilbyte.com/feed.xml</id><title type="html">The Rubber Duck of Despair</title><subtitle>Random programming stuff and the continuing adventures of Monk and Deck</subtitle><author><name>Jack Briody</name><email>jackbriody@gmail.com</email><uri>/</uri></author><entry><title type="html">Python Dev Environment: Virtualenv and Wrapper</title><link href="https://blog.evilbyte.com/python/setup/python-devenv/" rel="alternate" type="text/html" title="Python Dev Environment: Virtualenv and Wrapper" /><published>2018-01-22T00:00:00+00:00</published><updated>2018-01-22T00:00:00+00:00</updated><id>https://blog.evilbyte.com/python/setup/python-devenv</id><content type="html" xml:base="https://blog.evilbyte.com/python/setup/python-devenv/">&lt;h2 id=&quot;so-you-want-to-do-some-python&quot;&gt;So you want to do some Python&lt;/h2&gt;
&lt;p&gt;Have you ever searched the web for a recipe, like for Kung Pao Chicken, and the top few links all have like 4.8 stars with 500+ reviews? Then you go to a link and it ends up being 184 pictures with an 18,900 word narrative about cooking Kung Pao chicken and some random stuff about how the family cat helps with the cooking and at the end is a note-card size recipe. I’m not going to do that here–but I think virtual environments require at least a little introduction matter. I’ll keep the intro short and try to get to the important stuff quickly.&lt;/p&gt;

&lt;h2 id=&quot;python-virtual-environments&quot;&gt;Python Virtual Environments&lt;/h2&gt;

&lt;p&gt;There are a number of ways to do virtual environments in Python–“virtualenv”, “venv”, and even Anaconda/Conda. &lt;strong&gt;This is the set up that currently works for me.&lt;/strong&gt; Even if you don’t end up using a setup exactly like this I hope you’ll at least come away with a better understanding of how these environment work and how to make them work better for you.&lt;/p&gt;

&lt;p&gt;For starters a virtual environment is really a two way street. It lets you limit and know exactly what and which versions of python components you are using and also lets you work with many different components and versions of the same components without creating a mess of inconisitent packages and versions. Assuming you already have pip installed you can start to get a general idea of where v-envs take you by running &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip list&lt;/code&gt; then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip list --user&lt;/code&gt;. The first will show all packages installed, the second will show only the “user” level packages installed. V-envs will add another layer and a big reason for it is to keep from just throwing more and more stuff into the global or user scope.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;–This comes with a bit of a hidden cost that can be frustrating at first. Tools and applications you may be used to using don’t understand that they are in a virtual environment. So you fire up idle or a jupyter notebook and can’t get it to load the code you just wrote or the package you just installed in your virtual environment. So, we’ll fix that.&lt;/p&gt;

&lt;h2 id=&quot;software-install-back-to-the-main-story&quot;&gt;Software Install (Back to the main story)&lt;/h2&gt;
&lt;p&gt;Well we’re going to start off by installing all the stuff you’ll need to get started. All of this assumes you’re running inside a terminal.&lt;/p&gt;

&lt;p&gt;The first items are the &lt;strong&gt;OS&lt;/strong&gt; level stuff. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;sudo&lt;/code&gt; is used to run stuff as “root”–you’re essentially installing items that will go into system wide directories/usage. It is highly likely that some, if not all, of these are already installed–apt will simply tell you if they are and not do anything.&lt;/p&gt;

&lt;p&gt;I won’t be covering any “git” stuff, but you’ll likely want it at some point (and gitk is just a nice tool for looking at info for a local git repo). Git doesn’t actually require any configuration to work inside a virtual environment–essentially it is like a honey-badger and just doesn’t care.&lt;/p&gt;

&lt;p&gt;I will cover config for both Idle and Jupyter–if you use other tools they may need similar config changes to work and I hope these two as examples will at least be a good starting point.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;sudo apt-get install python3
sudo apt-get install python-pip python3-pip 
sudo apt-get install git gitk idle3 idle
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Now for the “user” stuff. pip and pip3 install python packages; the “–user” flag installs it to your local user python “stuff”. There isn’t really any major compelling reason to not install this stuff system wide–but it really doesn’t hurt to get into the habit of not installing everything globally.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;pip install --upgrade pip --user
pip3 install --upgrade pip --user
pip install virtualenv virtualenvwrapper --user
pip3 install virtualenv ipython3 virtualenvwrapper jupyter --user
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;configuration-a-bunch-of-nitty-gritty-in-one-place&quot;&gt;Configuration: A bunch of nitty-gritty in one place&lt;/h2&gt;
&lt;p&gt;I’m going to start with the virtual environment set up then we’ll quickly set up idle for v-envs. After that I’ll do a quick whirl-wind tour of what we’ve done and basics of using this type of setup. Finally, I’ll do the jupyter notebook set up–which is a bit more complex.&lt;/p&gt;

&lt;h3 id=&quot;one-create-some-directories&quot;&gt;One: Create some directories&lt;/h3&gt;
&lt;p&gt;As your usual user (i.e. not as root); in your home directory (just type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt; to get there if you aren’t there already) we need to create two directories. So run . . .&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;mkdir py_envs
mkdir .virtualenv
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;two-edit-bashrc&quot;&gt;TWO: Edit .bashrc&lt;/h3&gt;
&lt;p&gt;The .bashrc file is a special file/script that gets run anytime you start a new terminal session. It is found in your home directory but is usually hidden because it starts with a “.” (you can see that sort of file with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls -l&lt;/code&gt;). Anyway edit it with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idle .bashrc&lt;/code&gt; and add the following lines at the end . . .&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;VIRTUALENVWRAPPER_PYTHON&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/usr/bin/python3
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;WORKON_HOME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/.virtualenvs
&lt;span class=&quot;nb&quot;&gt;export &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT_HOME&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$HOME&lt;/span&gt;/py_envs
&lt;span class=&quot;nb&quot;&gt;source&lt;/span&gt; /usr/local/bin/virtualenvwrapper.sh
&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;vw&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;'virtualenvwrapper'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;You can use Ctrl-s Ctrl-q to save and quit. Then just run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source .bashrc&lt;/code&gt;. (The “source” command is a funky bit of magic that will run a file in your current session/environment–usually if you run a script or program it gets its own environment which goes away once the script/program exits. In this case we are actually changing our current shell environment. You will not need to do that if you start a new terminal as the new session will run the new modified .bashrc.)&lt;/p&gt;

&lt;p&gt;When you run source .bashrc you should see it do a few things to your .virtualenvs directory. That is from the “source /usr/local/bin/virtualenvwrapper.sh” line. The source command is again used here to essentially keep the results of virtualenvwrapper.sh as part of your environment.&lt;/p&gt;

&lt;p&gt;The last line added to the .bashrc file is simply an alias–lets you just type “vw” rather than “virtualenvwrapper”. At this point you can actually just run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vw&lt;/code&gt;–it doesn’t actually do anything except show you all the commands available in the “virtualenvwrapper” system. We have more configuration to do but it is a good sneak peak at what we’re getting to.&lt;/p&gt;

&lt;h3 id=&quot;three-configure-some-virtualenv-items&quot;&gt;THREE: Configure some virtualenv items&lt;/h3&gt;
&lt;p&gt;Final config stuff. Go into your .virtualenvs directory (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd .virtualenvs&lt;/code&gt;); and just to see what is there type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt;. You should see a bunch of things like “postactivate” and “premkproject”. These are all files that are run/launched because of various virtual environment actions (their names pretty much explain when). We’ll be editing 2 of them:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idle postactivate&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;add:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nv&quot;&gt;PROJECT_SOURCE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;cat&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$VIRTUAL_ENV&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/.project&quot;&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;((&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$# &lt;/span&gt;&lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; 0 &lt;span class=&quot;o&quot;&gt;))&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;then
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;builtin cd&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$PROJECT_SOURCE&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;else
        &lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;builtin cd&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$@&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;fi&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#if [ ! -d &quot;notebooks&quot; ]; then&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#  mkdir notebooks&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#fi&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#jupyter-notebook notebooks/ &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;#echo $! &amp;gt; notebooks/pid.file &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;The last five lines are actually commented out. If you remove the pound signs they’d create a notebooks directory in your virtual environment and also launch jupyter notebook anytime you activeate your environment. Only useful if you really use notebooks a lot.&lt;/p&gt;

&lt;p&gt;The uncommented lines at the start make it so the “cd” command will put you back in your virtual environments root directory (rather than your home directory).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idle postdeactivate&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;add:&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;#NPID=`cat &quot;$PROJECT_SOURCE/notebooks/pid.file&quot;`&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;#kill -9 $NPID &lt;/span&gt;

&lt;span class=&quot;nb&quot;&gt;unset&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;This essentially reverses the items from postactivate. The commented lines would kill the notebook server if you are running it. The uncommented line sets the “cd” command back to normal.&lt;/p&gt;

&lt;h3 id=&quot;four-edit-idle&quot;&gt;FOUR: Edit Idle&lt;/h3&gt;
&lt;p&gt;So, we’re actually done with the v-env setup. But we still need to get idle to work in a virtual environment.&lt;/p&gt;

&lt;p&gt;Again, if you aren’t in your user’s home directory type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt; to get there. Then run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkdir bin&lt;/code&gt;. Now run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cp /usr/bin/idle bin/&lt;/code&gt; – so we’ve just copied the OS/global version of idle to your personal user’s bin directory. Now run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idle bin/idle&lt;/code&gt; and change the first line from “#! /usr/bin/python” to “#!/usr/bin/env python”. Ctrl-S Ctrl-Q to save and quit. (Yes that was some inception level shit right there.)&lt;/p&gt;

&lt;p&gt;What this does: Your user’s “bin” directory should be the first in your path–so running “idle” will now use ~/bin/idle. (You can run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;which idle&lt;/code&gt; to verify that.) Changing the first line makes it so idle will use your current environments python–this will work from inside a virtual environment or when you are not in a virtual environment.&lt;/p&gt;

&lt;p&gt;The one big reason to do this in your user’s bin directory is so that when idle gets updated your changes won’t get squashed.&lt;/p&gt;

&lt;h2 id=&quot;---and-were-done&quot;&gt;. . . and we’re done&lt;/h2&gt;
&lt;p&gt;So, that is it, virtualenv and virtualenvwrapper are now ready to go.&lt;/p&gt;

&lt;h2 id=&quot;but-wait-there-is-more---&quot;&gt;But wait, there is more . . .&lt;/h2&gt;

&lt;p&gt;So now you can type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;vw&lt;/code&gt; again just to see what the general commands are and their descripts. We’re going to start with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkproject first_test&lt;/code&gt;. . . . And it will create your virtualenv and put you in its directory. Your command line should now start with “(first_test)”. Now run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip list&lt;/code&gt;–not much there. And a few more things to try . . .&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;lssitepackages&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toggleglobalsitepackages&lt;/code&gt; followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip list&lt;/code&gt; then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;togglegloabalsitepackages&lt;/code&gt; again–“toggle…” will essentially add your global site packages to your environment. Can be useful at times. (And if you aren’t familiar with “tab-completion” try just &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;toggl[tab]&lt;/code&gt; so you don’t have to type all that.)&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cdsitepackages&lt;/code&gt;; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ls&lt;/code&gt;; &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt;–“cdsite…” is occasionally useful, but the main point here is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cd&lt;/code&gt;, at least for now, returns you to your venv directory and not your user’s home directory.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And finally run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deactivate&lt;/code&gt;. This takes you out of your virtual environment (although you’ll still be in its directory). Now run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mkproject second_test&lt;/code&gt;. You can run some of the previous commands again but they’ll pretty much show the same stuff. So run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;deactivate&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;Now–the command you’ll really need–run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;workon [TAB]&lt;/code&gt;. You should get a list of your v-envs. Continuing by type &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f[TAB]&lt;/code&gt; should auto-complete to “workon first_test”. Hit [ENTER] and we’re back in the first_test v-env.&lt;/p&gt;

&lt;h2 id=&quot;and-now-for-something-completely-different&quot;&gt;And now for something completely different&lt;/h2&gt;
&lt;p&gt;While still in “first_test” run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;idle np_test.py &amp;amp;&lt;/code&gt;. Then type or paste . . .&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;np_array&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;np_array&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Then hit [f5] to run the code. Even if you have numpy already installed outside your v-env you will get an error. So go back to the terminal (where it starts with (first_test)) and run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip install numpy&lt;/code&gt; (you can then run &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip list&lt;/code&gt; again). Now go back to the file in idle and hit [f5] again.&lt;/p&gt;

&lt;p&gt;You’ve now installed numpy in your virtual environment.&lt;/p&gt;

&lt;p&gt;At this point you can try some commands like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip freeze&lt;/code&gt;, then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;wipeenv&lt;/code&gt; followed by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;pip list&lt;/code&gt; (numpy should be gone).&lt;/p&gt;

&lt;p&gt;As far as “idle”&lt;/p&gt;

&lt;p&gt;http://zafar.cc/2016/12/13/python-virtual-environment-and-jupyter-notebooks/&lt;/p&gt;

&lt;p&gt;[‘/home/jack/.virtualenvs/numbers/lib/python3.5/site-packages’,
 ‘’,
 ‘/usr/lib/python35.zip’,
 ‘/usr/lib/python3.5’,
 ‘/usr/lib/python3.5/plat-x86_64-linux-gnu’,
 ‘/usr/lib/python3.5/lib-dynload’,
 ‘/home/jack/.local/lib/python3.5/site-packages’,
 ‘/usr/local/lib/python3.5/dist-packages’,
 ‘/usr/lib/python3/dist-packages’,
 ‘/home/jack/.local/lib/python3.5/site-packages/IPython/extensions’,
 ‘/home/jack/.ipython’]&lt;/p&gt;

&lt;p&gt;sys.path = [
    ‘/home/jack/venvs/numbers/notebooks’,
    ‘/home/jack/.virtualenvs/numbers/lib/python35.zip’,
    ‘/home/jack/.virtualenvs/numbers/lib/python3.5’,
    ‘/home/jack/.virtualenvs/numbers/lib/python3.5/plat-x86_64-linux-gnu’,
    ‘/home/jack/.virtualenvs/numbers/lib/python3.5/lib-dynload’,
    ‘/usr/lib/python3.5’,
    ‘/usr/lib/python3.5/plat-x86_64-linux-gnu’,
    ‘/home/jack/.virtualenvs/numbers/lib/python3.5/site-packages’,
    ‘/home/jack/.virtualenvs/numbers/lib/python3.5/site-packages/bookofnumbers-0.1.0-py3.5.egg’,
    ‘/home/jack/.local/lib/python3.5/site-packages’,
    ‘/usr/local/lib/python3.5/dist-packages’,
    ‘/usr/lib/python3/dist-packages’,
]
USER_BASE: ‘/home/jack/.local’ (exists)
USER_SITE: ‘/home/jack/.local/lib/python3.5/site-packages’ (exists)
ENABLE_USER_SITE: True&lt;/p&gt;</content><author><name>Jack Briody</name><email>jackbriody@gmail.com</email><uri>/</uri></author><category term="python" /><category term="setup" /><category term="python" /><category term="setup" /><summary type="html">So you want to do some Python Have you ever searched the web for a recipe, like for Kung Pao Chicken, and the top few links all have like 4.8 stars with 500+ reviews? Then you go to a link and it ends up being 184 pictures with an 18,900 word narrative about cooking Kung Pao chicken and some random stuff about how the family cat helps with the cooking and at the end is a note-card size recipe. I’m not going to do that here–but I think virtual environments require at least a little introduction matter. I’ll keep the intro short and try to get to the important stuff quickly. Python Virtual Environments There are a number of ways to do virtual environments in Python–“virtualenv”, “venv”, and even Anaconda/Conda. This is the set up that currently works for me. Even if you don’t end up using a setup exactly like this I hope you’ll at least come away with a better understanding of how these environment work and how to make them work better for you. For starters a virtual environment is really a two way street. It lets you limit and know exactly what and which versions of python components you are using and also lets you work with many different components and versions of the same components without creating a mess of inconisitent packages and versions. Assuming you already have pip installed you can start to get a general idea of where v-envs take you by running pip list then pip list --user. The first will show all packages installed, the second will show only the “user” level packages installed. V-envs will add another layer and a big reason for it is to keep from just throwing more and more stuff into the global or user scope. The Problem–This comes with a bit of a hidden cost that can be frustrating at first. Tools and applications you may be used to using don’t understand that they are in a virtual environment. So you fire up idle or a jupyter notebook and can’t get it to load the code you just wrote or the package you just installed in your virtual environment. So, we’ll fix that. Software Install (Back to the main story) Well we’re going to start off by installing all the stuff you’ll need to get started. All of this assumes you’re running inside a terminal. The first items are the OS level stuff. sudo is used to run stuff as “root”–you’re essentially installing items that will go into system wide directories/usage. It is highly likely that some, if not all, of these are already installed–apt will simply tell you if they are and not do anything. I won’t be covering any “git” stuff, but you’ll likely want it at some point (and gitk is just a nice tool for looking at info for a local git repo). Git doesn’t actually require any configuration to work inside a virtual environment–essentially it is like a honey-badger and just doesn’t care. I will cover config for both Idle and Jupyter–if you use other tools they may need similar config changes to work and I hope these two as examples will at least be a good starting point. sudo apt-get install python3 sudo apt-get install python-pip python3-pip sudo apt-get install git gitk idle3 idle Now for the “user” stuff. pip and pip3 install python packages; the “–user” flag installs it to your local user python “stuff”. There isn’t really any major compelling reason to not install this stuff system wide–but it really doesn’t hurt to get into the habit of not installing everything globally. pip install --upgrade pip --user pip3 install --upgrade pip --user pip install virtualenv virtualenvwrapper --user pip3 install virtualenv ipython3 virtualenvwrapper jupyter --user Configuration: A bunch of nitty-gritty in one place I’m going to start with the virtual environment set up then we’ll quickly set up idle for v-envs. After that I’ll do a quick whirl-wind tour of what we’ve done and basics of using this type of setup. Finally, I’ll do the jupyter notebook set up–which is a bit more complex. One: Create some directories As your usual user (i.e. not as root); in your home directory (just type cd to get there if you aren’t there already) we need to create two directories. So run . . . mkdir py_envs mkdir .virtualenv TWO: Edit .bashrc The .bashrc file is a special file/script that gets run anytime you start a new terminal session. It is found in your home directory but is usually hidden because it starts with a “.” (you can see that sort of file with ls -l). Anyway edit it with idle .bashrc and add the following lines at the end . . . export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3 export WORKON_HOME=$HOME/.virtualenvs export PROJECT_HOME=$HOME/py_envs source /usr/local/bin/virtualenvwrapper.sh alias vw='virtualenvwrapper' You can use Ctrl-s Ctrl-q to save and quit. Then just run source .bashrc. (The “source” command is a funky bit of magic that will run a file in your current session/environment–usually if you run a script or program it gets its own environment which goes away once the script/program exits. In this case we are actually changing our current shell environment. You will not need to do that if you start a new terminal as the new session will run the new modified .bashrc.) When you run source .bashrc you should see it do a few things to your .virtualenvs directory. That is from the “source /usr/local/bin/virtualenvwrapper.sh” line. The source command is again used here to essentially keep the results of virtualenvwrapper.sh as part of your environment. The last line added to the .bashrc file is simply an alias–lets you just type “vw” rather than “virtualenvwrapper”. At this point you can actually just run vw–it doesn’t actually do anything except show you all the commands available in the “virtualenvwrapper” system. We have more configuration to do but it is a good sneak peak at what we’re getting to. THREE: Configure some virtualenv items Final config stuff. Go into your .virtualenvs directory (cd .virtualenvs); and just to see what is there type ls. You should see a bunch of things like “postactivate” and “premkproject”. These are all files that are run/launched because of various virtual environment actions (their names pretty much explain when). We’ll be editing 2 of them: idle postactivate add: PROJECT_SOURCE=`cat &quot;$VIRTUAL_ENV/.project&quot;` cd () { if (( $# == 0 )) then builtin cd $PROJECT_SOURCE else builtin cd &quot;$@&quot; fi } cd #if [ ! -d &quot;notebooks&quot; ]; then # mkdir notebooks #fi #jupyter-notebook notebooks/ &amp;gt; /dev/null 2&amp;gt;&amp;amp;1 &amp;amp; #echo $! &amp;gt; notebooks/pid.file The last five lines are actually commented out. If you remove the pound signs they’d create a notebooks directory in your virtual environment and also launch jupyter notebook anytime you activeate your environment. Only useful if you really use notebooks a lot. The uncommented lines at the start make it so the “cd” command will put you back in your virtual environments root directory (rather than your home directory). idle postdeactivate add: #NPID=`cat &quot;$PROJECT_SOURCE/notebooks/pid.file&quot;` #kill -9 $NPID unset -f cd This essentially reverses the items from postactivate. The commented lines would kill the notebook server if you are running it. The uncommented line sets the “cd” command back to normal. FOUR: Edit Idle So, we’re actually done with the v-env setup. But we still need to get idle to work in a virtual environment. Again, if you aren’t in your user’s home directory type cd to get there. Then run mkdir bin. Now run cp /usr/bin/idle bin/ – so we’ve just copied the OS/global version of idle to your personal user’s bin directory. Now run idle bin/idle and change the first line from “#! /usr/bin/python” to “#!/usr/bin/env python”. Ctrl-S Ctrl-Q to save and quit. (Yes that was some inception level shit right there.) What this does: Your user’s “bin” directory should be the first in your path–so running “idle” will now use ~/bin/idle. (You can run which idle to verify that.) Changing the first line makes it so idle will use your current environments python–this will work from inside a virtual environment or when you are not in a virtual environment. The one big reason to do this in your user’s bin directory is so that when idle gets updated your changes won’t get squashed. . . . and we’re done So, that is it, virtualenv and virtualenvwrapper are now ready to go. But wait, there is more . . . So now you can type vw again just to see what the general commands are and their descripts. We’re going to start with mkproject first_test. . . . And it will create your virtualenv and put you in its directory. Your command line should now start with “(first_test)”. Now run pip list–not much there. And a few more things to try . . . lssitepackages toggleglobalsitepackages followed by pip list then togglegloabalsitepackages again–“toggle…” will essentially add your global site packages to your environment. Can be useful at times. (And if you aren’t familiar with “tab-completion” try just toggl[tab] so you don’t have to type all that.) cdsitepackages; ls; cd–“cdsite…” is occasionally useful, but the main point here is that cd, at least for now, returns you to your venv directory and not your user’s home directory. And finally run deactivate. This takes you out of your virtual environment (although you’ll still be in its directory). Now run mkproject second_test. You can run some of the previous commands again but they’ll pretty much show the same stuff. So run deactivate again. Now–the command you’ll really need–run workon [TAB]. You should get a list of your v-envs. Continuing by type f[TAB] should auto-complete to “workon first_test”. Hit [ENTER] and we’re back in the first_test v-env. And now for something completely different While still in “first_test” run idle np_test.py &amp;amp;. Then type or paste . . . import numpy as np np_array = np.array([0, 2, 4, 5]) print(np_array) Then hit [f5] to run the code. Even if you have numpy already installed outside your v-env you will get an error. So go back to the terminal (where it starts with (first_test)) and run pip install numpy (you can then run pip list again). Now go back to the file in idle and hit [f5] again. You’ve now installed numpy in your virtual environment. At this point you can try some commands like pip freeze, then wipeenv followed by pip list (numpy should be gone). As far as “idle” http://zafar.cc/2016/12/13/python-virtual-environment-and-jupyter-notebooks/ [‘/home/jack/.virtualenvs/numbers/lib/python3.5/site-packages’, ‘’, ‘/usr/lib/python35.zip’, ‘/usr/lib/python3.5’, ‘/usr/lib/python3.5/plat-x86_64-linux-gnu’, ‘/usr/lib/python3.5/lib-dynload’, ‘/home/jack/.local/lib/python3.5/site-packages’, ‘/usr/local/lib/python3.5/dist-packages’, ‘/usr/lib/python3/dist-packages’, ‘/home/jack/.local/lib/python3.5/site-packages/IPython/extensions’, ‘/home/jack/.ipython’] sys.path = [ ‘/home/jack/venvs/numbers/notebooks’, ‘/home/jack/.virtualenvs/numbers/lib/python35.zip’, ‘/home/jack/.virtualenvs/numbers/lib/python3.5’, ‘/home/jack/.virtualenvs/numbers/lib/python3.5/plat-x86_64-linux-gnu’, ‘/home/jack/.virtualenvs/numbers/lib/python3.5/lib-dynload’, ‘/usr/lib/python3.5’, ‘/usr/lib/python3.5/plat-x86_64-linux-gnu’, ‘/home/jack/.virtualenvs/numbers/lib/python3.5/site-packages’, ‘/home/jack/.virtualenvs/numbers/lib/python3.5/site-packages/bookofnumbers-0.1.0-py3.5.egg’, ‘/home/jack/.local/lib/python3.5/site-packages’, ‘/usr/local/lib/python3.5/dist-packages’, ‘/usr/lib/python3/dist-packages’, ] USER_BASE: ‘/home/jack/.local’ (exists) USER_SITE: ‘/home/jack/.local/lib/python3.5/site-packages’ (exists) ENABLE_USER_SITE: True</summary></entry><entry><title type="html">CDNF Part II: Take the namedtuples bowling</title><link href="https://blog.evilbyte.com/python/namedtuple/boolean_algebra/logic/boolean2/" rel="alternate" type="text/html" title="CDNF Part II: Take the namedtuples bowling" /><published>2017-09-01T00:00:00+00:00</published><updated>2017-09-01T00:00:00+00:00</updated><id>https://blog.evilbyte.com/python/namedtuple/boolean_algebra/logic/boolean2</id><content type="html" xml:base="https://blog.evilbyte.com/python/namedtuple/boolean_algebra/logic/boolean2/">&lt;h1 id=&quot;reducing-from-normal-form&quot;&gt;Reducing from Normal Form&lt;/h1&gt;
&lt;p&gt;The basics of reducing is that you need two terms like ABC and ABC’. This can be reduced to AB(C + C’). In Logic (C + C’) evaluates to 1–so you have AB(1) which is AB. Essentially the C and C’ cancel each other so you go from 2 terms, ABC and ABC’, to one term AB.&lt;/p&gt;

&lt;p&gt;The general rules are:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Individual inputs must have a 1 to 1 match for inputs/letters. So ABC and ABD fail on this front.&lt;/li&gt;
  &lt;li&gt;Of the inputs only one can differ in its primed vs. unprimed form. So ABC and AB’C’ cannot be reduced. Extracting (C + C’) in this case leaves us with AB + AB’–which simply goes against the rules.&lt;/li&gt;
  &lt;li&gt;Finally, when performing reductions, terms of any generation can be used multiple times to come up with new terms.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I hope to make a lot of this clearer shortly, but for starters it is important to note that each generation will have the same number of inputs as all other items in the generation. For the bowling example the 1st generation will all have 4 inputs (and will also have the same set of inputs A, B, C, and D). 2nd generation will have 3 inputs–however, because of reduction, the set of inputs will not necessarily match. You could have terms with ABC and other terms with ACD. The 3rd generation will have 2 inputs in each term.&lt;/p&gt;

&lt;h2 id=&quot;situation-1-as-an-example&quot;&gt;Situation 1 as an Example&lt;/h2&gt;
&lt;p&gt;So for Situation 1 we have the following terms in the 1st generation:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;                ones    Ancestors
1    ABCD       4       1
2    ABCD'      3       2
3    ABC'D      3       3
4    ABC'D'     2       4
5    AB'CD      3       5
6    AB'CD'     2       6
7    AB'C'D     2       7
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Term 1 (ABCD) can be “minimized” with 3 other terms; 2, 3, and 5 (ABCD’, ABC’D, and AB’CD)–each of which differ from ABCD by having only 1 “primed” term. This results in 3 new 2nd generation terms–ABC, ABD, and ACD. ABC is derived from Term 1 (ABCD) and Term2 (ABCD’) to get ABC(D + D’) == ABC. ABD is T1 (ABCD) and T3 (ABC’D)–&amp;gt;ABD, etc.&lt;/p&gt;

&lt;p&gt;Term 4 – Term2 and Term3 to get ABD’ and ABC’&lt;/p&gt;

&lt;p&gt;Term 6 – Term2 and Term5 to get ACD’ and AB’C&lt;/p&gt;

&lt;p&gt;Term 7 – Term3 and Term5 to get AC’D and AB’D&lt;/p&gt;

&lt;p&gt;Generation 2 looks like:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;            ones    Ancestors
8   ABC     3       1, 2
9   ABD     3       1, 3
10  ACD     3       1, 5
11  ABD'    2       4, 2
12  ABC'    2       4, 3
13  ACD'    2       6, 2
14  AB'C    2       6, 5
15  AC'D    2       7, 3
16  AB'D    2       7, 5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;So for generation 2 we only have . . .&lt;/p&gt;
&lt;ul&gt;
  &lt;li&gt;Term9 (ABD) and Term11 (ABD’) –&amp;gt; AB   Ancestors(Term1, Term3, Term2, Term4)&lt;/li&gt;
  &lt;li&gt;Term10 (ACD) and Term13 (ACD’) –&amp;gt; AC  Ancestors(Term2, Term6, Term1, Term5)&lt;/li&gt;
  &lt;li&gt;Term10 (ACD) and Term15 (AC’D) –&amp;gt; AD  Ancestors(Term7, Term5, Term1, Term5)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Generation 3 ends up being:&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;            ones    Ancestors
17  AB      2       1, 3, 2, 4
18  AC      2       2, 6, 1, 5
19  AD      2       7, 5, 1, 5
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For future reference things to note . . .&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;The ones column essentially helps us with Rule 2 above–terms can only differ by 1 item. ABC (111) has 3 ones, it can not be reduced with AB’C’ (100) as there is more than 1 different item (it has one 1). It can be compared with AB’C (101) as there are 2 ones. It could also be compared with ABD’ (11-0) but would fail based on rule 1.&lt;/li&gt;
  &lt;li&gt;The other item, may be the most important, is the ancestors. To form a complete reduction we need to have all the minterms from our original canonical form (generation 1) represented in our reduced terms. In this case generation 3; AB, AC, and AD; cover all 7 terms from the original canonical form. The ancestor lists for those 3 terms include 1, 2, 3, 4, 5, 6, and 7.&lt;/li&gt;
  &lt;li&gt;There is also no point in doing redundant minimizations. Term 8 (ABC) for example can be used to reduce with Term12 (ABC’) and Term14 (AB’C). But reducing T8 with T12 giving AB with 1, 2, 3, 4 as ancestors is the same as reducing T9 (ABD) with T11 (ABD’) with 1, 2, 3, 4 as ancestors. T8 with T14 similarly is the same as T10 and T13 to get AC with 1, 2, 5, 6 as ancestors.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;back-to-why-this-matters&quot;&gt;Back to why this matters&lt;/h2&gt;
&lt;p&gt;It seems the most common case and use for reducing from a canonical form is with electronics/circuits. But as part of that whole “Turing Machine” thing it is actually something that occurs almost constantly in code–more often than not we are dealing with simpler cases and intuitively arrive at a minimized or close to minimized form.&lt;/p&gt;

&lt;h1 id=&quot;quine-mccluskey-and-petricks-method-finally&quot;&gt;Quine-McCluskey and Petrick’s Method (finally)&lt;/h1&gt;
&lt;p&gt;First some links:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Quine%E2%80%93McCluskey_algorithm&quot;&gt;Wikipedia: Quine-McCluskey&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Petrick%27s_method&quot;&gt;Wikipedia: Petrick’s Method&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.allaboutcircuits.com/technical-articles/everything-about-the-quine-mccluskey-method/&quot;&gt;Circuits and Q-M&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.allaboutcircuits.com/technical-articles/prime-implicant-simplification-using-petricks-method/&quot;&gt;Circuits and Petrick&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In a nutshell–getting to a final minimized form involves using Quine-McClusky to reduce your terms from the canonical form. Once no more reduction is possible Petrick’s Method can be used to select a set of terms that will “cover” all the terms from the original canonical form.&lt;/p&gt;

&lt;h2 id=&quot;now-for-some-code&quot;&gt;Now for some code&lt;/h2&gt;
&lt;p&gt;So, we finally get to the namedtuple portion of our program. Throughout my implementation a list of namedtuples (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;term_list&lt;/code&gt;) is used for storing information on the terms and the reductions. The tuples definition is:&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Term = namedtuple('Term', 'termset used ones source generation final')&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Some of the named fields should be familiar from the example above. The &lt;strong&gt;major&lt;/strong&gt; use for this is for the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;term_list&lt;/code&gt; variable that stores all of our generations and reductions. Within our “namedtuple” Term the fields are . . .&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;termset – these are actual python sets that will contain the inidividual inputs for a term. So ABC would be (“A”, “B”, “C”), ABD’ would be (“A”, “B”, “D’”).&lt;/li&gt;
  &lt;li&gt;used – this will be True or False. If a term is “used” that means it was used to create a term for a later generation and can be ignored in finding our final reduction.&lt;/li&gt;
  &lt;li&gt;ones – A simple count of the number of 1s or un-primed inputs. ABC would have this as 3, ABD’ would have it as 2. Ultimately we sort each generation by the “ones” column.&lt;/li&gt;
  &lt;li&gt;source – specifies which terms were used in creating this term. Generation 1 terms will have source set to themselves. Gen 2 would have items like (0, 3), Gen 3 would have (0, 3, 2, 4), etc.&lt;/li&gt;
  &lt;li&gt;generation – simply the generation of this item&lt;/li&gt;
  &lt;li&gt;final – by default it is “None”. After processing the items needed for our final reduction will have either “Required” or “Added” in this field.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The basic layout of the code/functions is . . .&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://blog.evilbyte.com/assets/images/pycallgraph.png&quot; alt=&quot;quinemc implementation&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;quinemc&quot;&gt;quinemc()&lt;/h2&gt;
&lt;p&gt;To perform a reduction you’d simply call quinemc . . .&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def quinemc(myitem, highorder_a=True, full_results=False):&lt;/code&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;myitem – can be an int, a string containing a canonical form, or a list containing terms from a canonical form. If you provide an int quinemc will call canonical(myitem) to get the canonical form. If you provide a string or list you’ll get an error if it is not a true canonical form (e.g. “ABC + ABD”).&lt;/li&gt;
  &lt;li&gt;highorder_a – like canonical() this determines whether A is the highorder bit or loworder bit. Default is A as highorder.&lt;/li&gt;
  &lt;li&gt;full_results – The default (False) simply returns the reduced form as a string. If set to True quinemc() returns 3 items
    &lt;ul&gt;
      &lt;li&gt;A string of the reduced form&lt;/li&gt;
      &lt;li&gt;The term_list list–a list containing all of the Term namedtuples&lt;/li&gt;
      &lt;li&gt;For some reductions there will be multiple ways to create a final form. If this is the case the 3rd item will be a dictionary containing all of the possible completions.&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Examples&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quinemc(65024)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quinemc(&quot;ABCD + ABCD' + ABC'D + ABC'D' + AB'CD + AB'CD' + AB'C'D&quot;)&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;quinemc([&quot;ABCD&quot;, &quot;ABCD'&quot;, &quot;ABC'D&quot;, &quot;ABC'D'&quot;, &quot;AB'CD&quot;, &quot;AB'CD'&quot;, &quot;AB'C'D&quot;])&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;a, b, c = quinemc(65024, full_results=True)&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All 4 are equivalent and give “AB + AC + AD” as a result.&lt;/p&gt;

&lt;p&gt;quinemc() itself just validates that the input is either an int, string, or list. Then also validates that the input item is in fact a canonical form (lines 11-14). From there it simply calls _minimize_() where the real processing takes place. In case you are wondering the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;cdnf&lt;/code&gt; variable is just a list of the terms like . . .&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;['ABCD', &quot;ABCD'&quot;, &quot;ABC'D&quot;, &quot;ABC'D'&quot;, &quot;AB'CD&quot;, &quot;AB'CD'&quot;, &quot;AB'C'D&quot;]&lt;/code&gt;&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/74c6f2a1b1f6aa20d3caa123f203738e.js?file=quinemc.py&quot;&gt; &lt;/script&gt;

&lt;h2 id=&quot;_minimize_&quot;&gt;_minimize_()&lt;/h2&gt;
&lt;script src=&quot;https://gist.github.com/74c6f2a1b1f6aa20d3caa123f203738e.js?file=minimize.py&quot;&gt; &lt;/script&gt;

&lt;p&gt;_minimize_() is essentially the driver function. It simply calls 3 other functions. The first two (in order), _create_first_generation_ and _merge_terms_ are the Quine-McCluskey portion. The last, _implicants_, is the Petrick method portion that finds our final result(s).&lt;/p&gt;

&lt;p&gt;The final bit of code handles special cases. The first is for a zero input. The second is for cases like quinemc(15) or quinemc(255) where all the terms end up canceling out.&lt;/p&gt;

&lt;h2 id=&quot;_create_first_generation_&quot;&gt;_create_first_generation_()&lt;/h2&gt;
&lt;p&gt;For our first step we are simply taking the terms from our canonical form and putting them into the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;term_list&lt;/code&gt; namedtuple. For situation 1, quinemc(65024), we get a 1st gen term_list of . . .&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Term(termset={&quot;C'&quot;, 'A', 'B', &quot;D'&quot;}, used=False, ones=2, source=[0], generation=1, final=None)  
Term(termset={'A', 'C', &quot;D'&quot;, &quot;B'&quot;}, used=False, ones=2, source=[1], generation=1, final=None)  
Term(termset={&quot;C'&quot;, 'A', &quot;B'&quot;, 'D'}, used=False, ones=2, source=[2], generation=1, final=None)  
Term(termset={'A', 'C', 'B', &quot;D'&quot;}, used=False, ones=3, source=[3], generation=1, final=None)  
Term(termset={&quot;C'&quot;, 'A', 'B', 'D'}, used=False, ones=3, source=[4], generation=1, final=None)  
Term(termset={'A', 'C', &quot;B'&quot;, 'D'}, used=False, ones=3, source=[5], generation=1, final=None)  
Term(termset={'A', 'C', 'B', 'D'}, used=False, ones=4, source=[6], generation=1, final=None)  
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;script src=&quot;https://gist.github.com/74c6f2a1b1f6aa20d3caa123f203738e.js?file=create_first_generation.py&quot;&gt; &lt;/script&gt;

&lt;p&gt;This is simply taking the 7 terms from our canonical form and putting them into the “Term” namedtuple data structure. The first bit of code converts our terms to python sets (line 3). Line 6 exists only to clean up duplicate terms if quinemc was called with a string or list. Lines 8/9 actually creates our Term namedtuple. We then sort it by the number of “ones” at line 10 and then update our namedtuple with each items index at lines 11 &amp;amp; 12. (_replace is something from the &lt;a href=&quot;https://docs.python.org/2/library/collections.html#collections.namedtuple&quot;&gt;namedtuple&lt;/a&gt; item–essentially a short cut for updating an immutable object. As the name implies it isn’t “updating” the immutable tuple but is replacing it with a copy of the original and a single value changed.)&lt;/p&gt;

&lt;p&gt;As far as our “Term” namedtuple goes–_merge_terms_ will use python set functions so “termset” puts our terms into “sets”. The “used” flag will also be used by merge_terms; ultimately only items that are still &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;used=False&lt;/code&gt; will be potential terms for our final result. As noted before it only makes sense to compare terms where the number of “ones” differs by 1 so the whole list is sorted by how many “ones” are in a term.&lt;/p&gt;

&lt;p&gt;For our first generation the “source” list is essentially a reference to the item itself. This simplifies things in merge_terms so that we do not need to use list indexes for our first gen items.&lt;/p&gt;

&lt;p&gt;“generation” is another item used by _merge_terms_. Again, it only makes sense to compare/reduce items that exist in the same generation as the number of inputs needs to match. Each successive generation will reduce the number of items in the “termset” by one. So, as in this case, Generation 1 has 4 inputs; if we can reduce anything then all Generation 2 items will have 3 inputs; if Gen 2 items can be reduced all Generation 3 items will have 2 inputs; finally if possible Generation 4 would have termsets with a single input.&lt;/p&gt;

&lt;p&gt;“final” is ultimately used by the _implicants_/Petrick’s method part of the code.&lt;/p&gt;

&lt;h2 id=&quot;_merge_terms_-and-_create_new_terms_&quot;&gt;_merge_terms_ and _create_new_terms_&lt;/h2&gt;
&lt;script src=&quot;https://gist.github.com/74c6f2a1b1f6aa20d3caa123f203738e.js?file=merge_terms.py&quot;&gt; &lt;/script&gt;

&lt;p&gt;_merge_terms_ is pretty simple. It is called for each generation and it then tries to minimize that generation. When nothing can be minimized done becomes True.&lt;/p&gt;

&lt;p&gt;_create_new_terms_ is where we perform our actual reductions. Taking terms like ABC and ABC’ to get AB. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;working_list&lt;/code&gt; is simply a stripped down version of our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;term_list&lt;/code&gt; variable containing only items for the current “generation”. Lines 21 and 22 do a nested loop over the items in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;working_list&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The “tricky” bit is the “sym_set” variable. Line 24 does a set comparison of our term_list.termset items and gets a “difference” set on them. So comparing ABC to ABC’ results in a “sym_set” of (C, C’). Line 26 then checks whether we have only 2 items in our “sym_set”–this would fail if we had AB’C’ and ABC. Then also checks that the inputs (e.g. C) match – &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;list(sym_set)[0][0] == list(sym_set)[1][0]&lt;/code&gt;. If that all works out we then create a new merged term.&lt;/p&gt;

&lt;p&gt;Lines 31 - 36 avoid doing duplicate sources for reductions.&lt;/p&gt;

&lt;p&gt;After all of this work is done the used_dict dictionary is used to update our global term_list to mark all items that have been used in merges.&lt;/p&gt;

&lt;p&gt;At this point our &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;term_list&lt;/code&gt; looks like . . .&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Term(termset={'B', 'A', &quot;C'&quot;, &quot;D'&quot;}, used=True, ones=2, source=[0], generation=1, final=None)
Term(termset={&quot;B'&quot;, 'C', 'A', &quot;D'&quot;}, used=True, ones=2, source=[1], generation=1, final=None)
Term(termset={&quot;B'&quot;, 'A', &quot;C'&quot;, 'D'}, used=True, ones=2, source=[2], generation=1, final=None)
Term(termset={'B', 'C', 'A', &quot;D'&quot;}, used=True, ones=3, source=[3], generation=1, final=None)
Term(termset={'B', 'A', &quot;C'&quot;, 'D'}, used=True, ones=3, source=[4], generation=1, final=None)
Term(termset={&quot;B'&quot;, 'C', 'A', 'D'}, used=True, ones=3, source=[5], generation=1, final=None)
Term(termset={'B', 'C', 'A', 'D'}, used=True, ones=4, source=[6], generation=1, final=None)
Term(termset={'B', 'A', &quot;D'&quot;}, used=True, ones=2, source=[0, 3], generation=2, final=None)
Term(termset={'B', 'A', &quot;C'&quot;}, used=True, ones=2, source=[0, 4], generation=2, final=None)
Term(termset={'C', 'A', &quot;D'&quot;}, used=True, ones=2, source=[1, 3], generation=2, final=None)
Term(termset={&quot;B'&quot;, 'C', 'A'}, used=True, ones=2, source=[1, 5], generation=2, final=None)
Term(termset={'A', &quot;C'&quot;, 'D'}, used=True, ones=2, source=[2, 4], generation=2, final=None)
Term(termset={&quot;B'&quot;, 'A', 'D'}, used=True, ones=2, source=[2, 5], generation=2, final=None)
Term(termset={'B', 'C', 'A'}, used=True, ones=3, source=[3, 6], generation=2, final=None)
Term(termset={'B', 'A', 'D'}, used=True, ones=3, source=[4, 6], generation=2, final=None)
Term(termset={'C', 'A', 'D'}, used=True, ones=3, source=[5, 6], generation=2, final=None)
Term(termset={'B', 'A'}, used=False, ones=2, source=[0, 3, 4, 6], generation=3, final=None)
Term(termset={'C', 'A'}, used=False, ones=2, source=[1, 3, 5, 6], generation=3, final=None)
Term(termset={'A', 'D'}, used=False, ones=2, source=[2, 4, 5, 6], generation=3, final=None)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Major things to note–the generations I think are clearer here. The _merge_terms_ function has set “used” to true for everything in Gen 1 and Gen 2. Looking at Gen 2 you can see in the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;source&lt;/code&gt; column that all 7 items (0-6) have been used. Generation 3 points to an important part of the code in this regard. Line 32 of _create_new_terms_ prevents us from doing duplicate “merges”, but the preceding lines (28 and 29) mark the items we aren’t actually using as duplicates as far as “used” is concerned.&lt;/p&gt;

&lt;p&gt;The for loop at lines 39 - 42 is the code that actually goes through and marks the individual terms as “used”. This ends the Quine-McCluskey portion of the reduction. For Petrick’s method we will only need to consider the Term items that have “used=False”.&lt;/p&gt;

&lt;p&gt;If you look at the Simplifying with Quine-McCluskey Method section at &lt;a href=&quot;https://www.allaboutcircuits.com/technical-articles/prime-implicant-simplification-using-petricks-method/&quot;&gt;Circuits and Petrick&lt;/a&gt; The table under “Now to find the prime implicants:” is essentially what we have accomplished. Each of the 3 columns represent a generation. Within a generation the terms are grouped by the number of 1s. And finally check marks have been added for each term that has been used in a reduction.&lt;/p&gt;

&lt;h1 id=&quot;petricks-method&quot;&gt;Petrick’s Method&lt;/h1&gt;
&lt;p&gt;From Situation 1 we have ended up with 3 terms that may be needed to form our final result.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Term(termset={'B', 'A'}, used=False, ones=2, source=[0, 3, 4, 6], generation=3, final=None)
Term(termset={'C', 'A'}, used=False, ones=2, source=[1, 3, 5, 6], generation=3, final=None)
Term(termset={'A', 'D'}, used=False, ones=2, source=[2, 4, 5, 6], generation=3, final=None)
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;On paper starting Petrick’s method would require putting this into a table form with 1 column for each term from our canonical form (generation 1) and a row for each term that hasn’t been used. The remainder of the grid is filled in using the “source” values for the terms–with the goal being to find a set of terms that “fills” all of the columns.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;    0   1   2   3   4   5   6
---------------------------------
AB  x           x   x       x    
AC      x       x       x   x
AD          x       x   x   x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;A “required” prime implicant can be found by finding columns that only contain 1 “x”–that essentially means that term is the only one that “covers” the corresponding term from the canonical form. In this case columns 0, 1, and 2 all have single x’s and they are each for one of our 3 terms.&lt;/p&gt;

&lt;p&gt;The other consideration is that all terms from the canonical form must be covered. In this case this is met using the 3 “required” terms. (In fact any time when all of the “used=False” terms are required terms they would cover all of the canonical terms.)&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Situation 2&lt;/em&gt; is slightly more interesting. In that case the final reduction looks like . . .&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Term(termset={'B', &quot;D'&quot;, &quot;C'&quot;, 'A'}, used=True, ones=2, source=[0], generation=1, final=None)
Term(termset={&quot;D'&quot;, &quot;B'&quot;, 'C', 'A'}, used=True, ones=2, source=[1], generation=1, final=None)
Term(termset={&quot;C'&quot;, &quot;B'&quot;, 'D', 'A'}, used=True, ones=2, source=[2], generation=1, final=None)
Term(termset={'B', &quot;C'&quot;, 'D', 'A'}, used=True, ones=3, source=[3], generation=1, final=None)
Term(termset={&quot;B'&quot;, 'C', 'D', 'A'}, used=True, ones=3, source=[4], generation=1, final=None)
Term(termset={'B', &quot;C'&quot;, 'A'}, used=False, ones=2, source=[0, 3], generation=2, final='None')
Term(termset={'A', &quot;B'&quot;, 'C'}, used=False, ones=2, source=[1, 4], generation=2, final='None')
Term(termset={&quot;C'&quot;, 'D', 'A'}, used=False, ones=2, source=[2, 3], generation=2, final=None)
Term(termset={&quot;B'&quot;, 'D', 'A'}, used=False, ones=2, source=[2, 4], generation=2, final='None') 
&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;In this case the canonical form only has 5 terms and the reduction is complete with generation 2–and all of the generation 2 items have “used=False”. In this case our table looks like . . .&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        0   1   2   3   4
--------------------------
ABC'    x           x
AB'C        x           x
AC'D            x   x
AB'D            x       x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this case only the 1st 2 terms are required (ABC’ and AB’C) because columns 0 and 1 are the only ones with a single X. But we still need to make sure all of the terms from the original canonical form are covered. ABC’ covers 0 and 3, AB’C cover 1 and 4. So the only “uncovered” item is term 2–both AC’D and AB’D will take care of column 2. Since they are both the same length we could use either one so . . .&lt;/p&gt;

&lt;p&gt;ABC’ + AB’C + AC’D   and
ABC’ + AB’C + AB’D&lt;/p&gt;

&lt;p&gt;are equivalent. And logically this makes sense if you consider the Situation 2 story where Bob and Clara won’t bowl together.&lt;/p&gt;

&lt;p&gt;Ultimately, for the code purposes, none of our 4 situations actually exercise all of the code (they really are pretty simple situations). For that I’ll be using 2046 (‘0b11111111110’). canonical(2046) and quinemc(2046) give us . . .&lt;/p&gt;

&lt;p&gt;“AB’CD’ + AB’C’D + AB’C’D’ + A’BCD + A’BCD’ + A’BC’D + A’BC’D’ + A’B’CD + A’B’CD’ + A’B’C’D”
and
“AB’D’ + B’C’D + A’B + A’C”&lt;/p&gt;

&lt;p&gt;I really have no idea what Alan, Bob, Clara and Declan are doing here, but again note the canonical form starts with 10 terms corresponding to the number of 1’s in the binary representation. After reduction we end up with . . .&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Term(termset={&quot;D'&quot;, &quot;C'&quot;, &quot;B'&quot;, 'A'}, used=True, ones=1, source=[0], generation=1, final=None)
Term(termset={'B', &quot;C'&quot;, &quot;A'&quot;, &quot;D'&quot;}, used=True, ones=1, source=[1], generation=1, final=None)
Term(termset={&quot;D'&quot;, &quot;B'&quot;, &quot;A'&quot;, 'C'}, used=True, ones=1, source=[2], generation=1, final=None)
Term(termset={&quot;C'&quot;, &quot;B'&quot;, 'D', &quot;A'&quot;}, used=True, ones=1, source=[3], generation=1, final=None)
Term(termset={&quot;D'&quot;, &quot;B'&quot;, 'C', 'A'}, used=True, ones=2, source=[4], generation=1, final=None)
Term(termset={&quot;C'&quot;, &quot;B'&quot;, 'D', 'A'}, used=True, ones=2, source=[5], generation=1, final=None)
Term(termset={'B', &quot;D'&quot;, &quot;A'&quot;, 'C'}, used=True, ones=2, source=[6], generation=1, final=None)
Term(termset={'B', &quot;C'&quot;, 'D', &quot;A'&quot;}, used=True, ones=2, source=[7], generation=1, final=None)
Term(termset={&quot;B'&quot;, 'D', &quot;A'&quot;, 'C'}, used=True, ones=2, source=[8], generation=1, final=None)
Term(termset={'B', 'D', &quot;A'&quot;, 'C'}, used=True, ones=3, source=[9], generation=1, final=None)
**Term(termset={&quot;D'&quot;, &quot;B'&quot;, 'A'}, used=False, ones=1, source=[0, 4], generation=2, final='None')
**Term(termset={&quot;C'&quot;, &quot;B'&quot;, 'A'}, used=False, ones=1, source=[0, 5], generation=2, final=None)
Term(termset={'B', &quot;D'&quot;, &quot;A'&quot;}, used=True, ones=1, source=[1, 6], generation=2, final=None)
Term(termset={'B', &quot;C'&quot;, &quot;A'&quot;}, used=True, ones=1, source=[1, 7], generation=2, final=None)
**Term(termset={&quot;D'&quot;, &quot;B'&quot;, 'C'}, used=False, ones=1, source=[2, 4], generation=2, final=None)
Term(termset={&quot;D'&quot;, &quot;A'&quot;, 'C'}, used=True, ones=1, source=[2, 6], generation=2, final=None)
Term(termset={&quot;B'&quot;, &quot;A'&quot;, 'C'}, used=True, ones=1, source=[2, 8], generation=2, final=None)
**Term(termset={&quot;C'&quot;, 'D', &quot;B'&quot;}, used=False, ones=1, source=[3, 5], generation=2, final='None')
Term(termset={&quot;C'&quot;, 'D', &quot;A'&quot;}, used=True, ones=1, source=[3, 7], generation=2, final=None)
Term(termset={&quot;B'&quot;, 'D', &quot;A'&quot;}, used=True, ones=1, source=[3, 8], generation=2, final=None)
Term(termset={'B', &quot;A'&quot;, 'C'}, used=True, ones=2, source=[6, 9], generation=2, final=None)
Term(termset={'B', 'D', &quot;A'&quot;}, used=True, ones=2, source=[7, 9], generation=2, final=None)
Term(termset={'D', &quot;A'&quot;, 'C'}, used=True, ones=2, source=[8, 9], generation=2, final=None)
**Term(termset={'B', &quot;A'&quot;}, used=False, ones=1, source=[1, 6, 7, 9], generation=3, final='None')
**Term(termset={&quot;A'&quot;, 'C'}, used=False, ones=1, source=[2, 6, 8, 9], generation=3, final='None')
**Term(termset={'D', &quot;A'&quot;}, used=False, ones=1, source=[3, 7, 8, 9], generation=3, final=None)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I’ve added “**” in front of the unused terms just to highlight the sporadic locations of unused terms. The short list of only items where “used=False” is . . .&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;Term(termset={&quot;D'&quot;, &quot;B'&quot;, 'A'}, used=False, ones=1, source=[0, 4], generation=2, final='None')
Term(termset={&quot;C'&quot;, &quot;B'&quot;, 'A'}, used=False, ones=1, source=[0, 5], generation=2, final=None)
Term(termset={&quot;D'&quot;, &quot;B'&quot;, 'C'}, used=False, ones=1, source=[2, 4], generation=2, final=None)
Term(termset={&quot;C'&quot;, 'D', &quot;B'&quot;}, used=False, ones=1, source=[3, 5], generation=2, final='None')
Term(termset={'B', &quot;A'&quot;}, used=False, ones=1, source=[1, 6, 7, 9], generation=3, final='None')
Term(termset={&quot;A'&quot;, 'C'}, used=False, ones=1, source=[2, 6, 8, 9], generation=3, final='None')
Term(termset={'D', &quot;A'&quot;}, used=False, ones=1, source=[3, 7, 8, 9], generation=3, final=None)&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;From this our table would look like . . .&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        0   1   2   3   4   5   6   7   8   9
---------------------------------------------
AB'D'   x               x    
AB'C'   x                   x    
B'CD'           x       x    
B'C'D               x       x
A'B         x                   x   x       x
A'C             x               x       x   x
A'D                 x               x   x   x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;In this case the only required term is A’B as column 1 is the only one with a single x. It is probably obvious that shorter terms from later generations have more sources/ancestors–so they will often take care of larger groups. Anyway, removing A’B and all of the columns it covers (1, 6, 7, 9) gives us . . .&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;        0   2   3   4   5   8
------------------------------
AB'D'   x           x    
AB'C'   x               x    
B'CD'       x       x    
B'C'D           x       x
A'C         x               x
A'D             x           x
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;From this table it should be obvious that you’ll need either AB’D’ or AB’C’ to cover column 0, and you’ll also need one of A’C or A’D to cover column 8. I personally think doing this by hand is a pain–but ultimately you end up with 2 equivalent alternatives&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;“AB’D’ + B’C’D + A’B + A’C”&lt;/li&gt;
  &lt;li&gt;“AB’C’ + B’CD’ + A’B + A’D”&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;petricks-method-and-the-_implicants_-code&quot;&gt;Petrick’s Method and the _implicants_ code&lt;/h2&gt;
&lt;p&gt;Going back to the call graph _implicants_() uses 3 functions–_get_columns_(), _make_find_dict_(), and _check_combinations_().&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/74c6f2a1b1f6aa20d3caa123f203738e.js?file=implicants.py&quot;&gt; &lt;/script&gt;

&lt;p&gt;Our first order of business in _implicants_ is to make one large list containing all of the “source=[]” items from our terms where used=False. (Lines 5 and 6) This list will contain duplicates as it is used to find the required sources on Line 8 where we look for sources that appear once. For quinemc(2046) this list looks like . . .&lt;/p&gt;

&lt;p&gt;[0, 4, 0, 5, 2, 4, 3, 5, 1, 6, 7, 9, 2, 6, 8, 9, 3, 7, 8, 9]&lt;/p&gt;

&lt;p&gt;So, we have “0, 4” for AB’D’, then “0, 5” for AB’C’, etc. The only source that appears only once is 1–so our required list ends up looking like [1].&lt;/p&gt;

&lt;p&gt;With that information in hand we now move onto _keep_columns_(). At this point the code starts to use some set operations and there are more of those to come. The major function of this code is to A) find and mark “required” terms and B) create two python sets: 1) the sources from our required items and 2) one containing the sources for the rest or the terms.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/74c6f2a1b1f6aa20d3caa123f203738e.js?file=get_columns.py&quot;&gt; &lt;/script&gt;

&lt;p&gt;The for loop on lines 5 - 10 handles this. We “enumerate()” over the term_list where used=False–enumerate is important because we will need the index in term_list to update the “required” items.&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Line 6 – we perform set intersection of required and the current terms sources. An intersection will return any values that are in both sets. The items in “required” will only be in one of our term’s source lists; however a term could potentially cover more than one required item which is why line 6 used &amp;gt;= 1.&lt;/li&gt;
  &lt;li&gt;Lines 7 &amp;amp; 8 – if our current term is required we update term_list and set final=”Required”. We also add the sources for the required term to our ignore &lt;strong&gt;list&lt;/strong&gt;–it is a list at this point.&lt;/li&gt;
  &lt;li&gt;Line 10 – if our current term isn’t required we add its sources to a “keep” &lt;strong&gt;list&lt;/strong&gt;&lt;/li&gt;
  &lt;li&gt;Line 12 – finally we generate a list of everything that is in “keep” but not in “ignore” by treating them as sets and using the set difference operator.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So for quinemc(2046) after the for loop “keep” contains [0, 4, 0, 5, 2, 4, 3, 5, 2, 6, 8, 9, 3, 7, 8, 9]. Ignore contains [1, 6, 7, 9]. And the final result after performing a set difference on the 2 is [0, 2, 3, 4, 5, 8]. Which is the same as the table above–we’ve removed the source items for the A’B term and marked that term as “Required”.&lt;/p&gt;

&lt;p&gt;For something like our Situation 1 (quinemc(65024)) “keep” will end up empty as the 3 terms cover all terms from the canonical expression. Back in _implicants_ this situation is covered on line 11 where we set “finished” to True so we can skip the next bits of processing.&lt;/p&gt;

&lt;p&gt;Our next little bit of code in _implicants_ is lines 13 through 19 which essentially checks to see if a single term will cover the remaining columns. It first, however, makes a detour to create “find_dict” by calling _make_find_dict_&lt;/p&gt;

&lt;h2 id=&quot;_make_find_dict_&quot;&gt;_make_find_dict_&lt;/h2&gt;
&lt;script src=&quot;https://gist.github.com/74c6f2a1b1f6aa20d3caa123f203738e.js?file=make_find_dict.py&quot;&gt; &lt;/script&gt;

&lt;p&gt;At this point in _implicants_ we have “keep_columns” which tells us which columns we still need to include in our final result. However, in our big “term_list” list each term still contains the columns we want to ignore in their “source” items. _make_find_dict_ addresses this by creating a dictionary containing a terms index from “term_list” along with a new namedtuple containing that term_list item’s sources with columns we want to ignore removed. This simplifies later set operations and our new namedtuple also includes a “length” item as it is possible to find many different reductions but some can be longer than others. It also covers the situation where an item in “term_list” can be completely ignored because the ancestors/sources it covers are already covered by a required item.&lt;/p&gt;

&lt;p&gt;To illustrate what is going on–for quinemc(2046) our current term_list of “used=False” items (with term_list indexes) is . . .&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10 Term(termset={&quot;B'&quot;, 'A', &quot;D'&quot;}, used=False, ones=1, source=[0, 4], generation=2, final='None')
11 Term(termset={&quot;B'&quot;, &quot;C'&quot;, 'A'}, used=False, ones=1, source=[0, 5], generation=2, final=None)
14 Term(termset={&quot;B'&quot;, 'C', &quot;D'&quot;}, used=False, ones=1, source=[2, 4], eneration=2, final=None)
17 Term(termset={&quot;B'&quot;, &quot;C'&quot;, 'D'}, used=False, ones=1, source=[3, 5], generation=2, final='None')
23 Term(termset={&quot;A'&quot;, 'B'}, used=False, ones=1, source=[1, 6, 7, 9], generation=3, final='Required')
24 Term(termset={'C', &quot;A'&quot;}, used=False, ones=1, source=[2, 6, 8, 9], generation=3, final='None')
25 Term(termset={'D', &quot;A'&quot;}, used=False, ones=1, source=[3, 7, 8, 9], generation=3, final=None)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;Note 23 (A’B) is marked as required and so we want to ignore [1, 6, 7, 9]. When _make_find_dict_() is finished we end up with a find_dict that looks like . ..&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;10: search_tuple(sourceSet={0, 4}, length=3)
11: search_tuple(sourceSet={0, 5}, length=3)
14: search_tuple(sourceSet={2, 4}, length=3)
17: search_tuple(sourceSet={3, 5}, length=3)
24: search_tuple(sourceSet={8, 2}, length=2)
25: search_tuple(sourceSet={8, 3}, length=2)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;For 10, 11, 14, and 17 nothing changes; for 24 and 25 [6, 9] and [7, 9] (respectively) have been removed. And length is the length of each item’s term.&lt;/p&gt;

&lt;h2 id=&quot;final-step--_check_combinations_&quot;&gt;Final Step – _check_combinations_&lt;/h2&gt;
&lt;p&gt;After _make_find_dict_() we head back into _implicants_() and back into lines 13-19 where we are checking to see if a single term will complete our minimized form.&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;find_dict&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_make_find_dict_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;term_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keep_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;find_dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;items&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;():&lt;/span&gt;
            &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;set&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keep_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;val&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;sourceSet&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;term_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;term_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;idx&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_replace&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;final&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Added&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;n&quot;&gt;finished&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;
                &lt;span class=&quot;k&quot;&gt;break&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;At this point we have the new “find_dict” dictionary and “keep_columns” which contains the columns/canonical terms we still need to cover. The for loop at 15-19 (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;for idx, val ...&lt;/code&gt;) simply loops through our newly created “find_dict” and checks whether any of the “sourceSet” items there are an exact match for “keep_columns”. If any one item does cover all the needed columns then we are done–we simply update “term_list” at the index taken from find_dict’s key and set that term_list item’s final attribute to “added”.&lt;/p&gt;

&lt;p&gt;So ultimately the final attribute within term_list will have one of 3 values . . .&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;None – The default. The term is not used in our final minimized form&lt;/li&gt;
  &lt;li&gt;Required – Denotes prime implicant set in _get_columns_(). Again, these are terms that are the &lt;strong&gt;only ones&lt;/strong&gt; that cover one or more of our original canonical terms.&lt;/li&gt;
  &lt;li&gt;Added – These are the additional terms needed to cover the remaining items from our canonical form. Essentially there is a choice of multiple possible terms and we attempt to find the best/shortest set that will fill out our final minimized version.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this point in _implicants_, if a single term will not work we need to get a little more drastic and try and find combinations of terms that will work. So with . . .&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;    &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;not&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;finished&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
        &lt;span class=&quot;n&quot;&gt;possible_terms&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;_check_combinations_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find_dict&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;term_list&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keep_columns&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;. . . in _implicants_, we head into _check_combinations_&lt;/p&gt;

&lt;h2 id=&quot;_check_combinations_&quot;&gt;_check_combinations_&lt;/h2&gt;
&lt;script src=&quot;https://gist.github.com/74c6f2a1b1f6aa20d3caa123f203738e.js?file=check_combinations.py&quot;&gt; &lt;/script&gt;

&lt;p&gt;This function actually does two things and for good design principles the second half &lt;strong&gt;should&lt;/strong&gt; probably be split off into its own function. I’m going to claim it is a borderline case and that leaving it as is is slightly less complex than the complexity of moving the second half out.&lt;/p&gt;

&lt;p&gt;So, the first half of the code (upto line 16) works quite a bit like the code to check if a single term will match/cover all items in the “keep_columns” list. The difference here is we take the “sourceSet” items from “find_dict” and mash them together in all possible combinations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lines 7 and 8&lt;/strong&gt; – Line 6 “x” is a counter going from 2 to the number of keys in “find_dict”. Line 7 creates all combinations of those keys starting with all combinations of 2 keys, then 3 keys, etc. Funky thing here is that for a “find_dict” containing 7 items (for example) we may find a solution using a combination of just 2 items. However, the code continues to look for solutions that would be combinations of 3, then 4, then 5, etc. For a complex case this does add a ton of probably unneeded computation. But I haven’t been able to rule out the possibilities of a corner case where 3 terms would actually be shorter than 2 terms (for example AB + BC’ + AD’ is considered shorter than ABCD + ABC’D’).&lt;/p&gt;

&lt;p&gt;Anyway, using our example of quinmc(2046) with find_dict containing keys of 10, 11, 14, 17, 24, and 25 our code starts with “items” containing (10, 11), then (10, 14), etc. Ultimately for 7 items we end up with 15 combinations of indexes. [And we are dealing with combinations–order doesn’t matter. Permutations, where order matters isn’t what we need.]&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lines 9 and 10&lt;/strong&gt; – we set up variables local to the for loop. Each time we go through a combination (10, 11) then (10, 14) we are resetting “combined_sources” and “temp_count”.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lines 11-13&lt;/strong&gt; – We look up the items for our current combination [10, 11] and mash their sources together in “combined_sources”. We also use “find_dict” to keep track of how long this result is. So for [10, 11] combined_sources would be {0, 4, 5}, for [10, 14] it would be {0, 2, 4}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 14&lt;/strong&gt; – Just like searching for a single item that will cover everything we need we check whether combined sources covers what we need. Even if this succeeds we will continue checking all combinations. Essentially, if there are any number of ways to complete our reduction using 2 terms we will look at them all. So for example we’d check if set([0, 2, 3, 4, 5, 8]) == {0, 4, 5}–and it will finally succeed when we find a group of items from “find_dict” covering all of set([0, 2, 3, 4, 5, 8]). (Might be good to remind you at this point that every item in “find_dict” will only contain 1 or more of [0, 2, 3, 4, 5, 8] – but in this case would never contain a 1 or 6 as they have been stripped out.}&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lines 15-16&lt;/strong&gt; – if we find a match we add the list of keys to the matches list. We also add its length to matches_idx.&lt;/p&gt;

&lt;h3 id=&quot;the-second-half&quot;&gt;The second half.&lt;/h3&gt;

&lt;p&gt;a, b, c = quinemc(548738420677, full_results=True)
8534
13 possible items&lt;/p&gt;

&lt;p&gt;“A’BC’DF’ + A’BC’D’F + B’C’D’F’ + AB’C’F’ + AB’C’E’ + AB’C’D’ + A’BD’E’ + A’B’CD’ + A’B’DE + A’CDE”&lt;/p&gt;

&lt;p&gt;“A’BC’DF’ + A’BC’D’F + B’C’D’F’ + AB’C’F’ + AB’C’E’ + AB’C’D’ + A’BD’E’ + A’B’CD’ + A’B’DE + A’CDE”&lt;/p&gt;

&lt;p&gt;“A’BC’DF’ + A’BC’D’F + B’C’D’F’ + AB’C’F’ + AB’C’E’ + AB’C’D’ + A’BD’E’ + A’B’CD’ + A’B’DE + A’CDE”&lt;/p&gt;

&lt;p&gt;“A’BC’DF’ + A’BC’D’F + B’C’D’F’ + AB’C’F’ + AB’C’E’ + AB’C’D’ + A’BD’E’ + A’B’CD’ + A’B’DE + A’CDE”&lt;/p&gt;

&lt;p&gt;{0, 2, 3, 4, 8, 9, 11, 12, 14, 17, 18, 21}&lt;/p&gt;

&lt;p&gt;find dict
34: search_tuple(sourceSet={9, 2}, length=5)        –
49: search_tuple(sourceSet={9, 18}, length=5)
67: search_tuple(sourceSet={0, 4}, length=4)        –
68: search_tuple(sourceSet={0, 8, 2, 3}, length=4)  –
69: search_tuple(sourceSet={0, 3, 11, 4}, length=4) –
70: search_tuple(sourceSet={14}, length=4)
73: search_tuple(sourceSet={8, 17, 2}, length=4)
74: search_tuple(sourceSet={8, 17, 3, 12}, length=4)
75: search_tuple(sourceSet={3, 11, 12, 21}, length=4)
76: search_tuple(sourceSet={4, 14}, length=4)
77: search_tuple(sourceSet={11, 4}, length=4)
78: search_tuple(sourceSet={11, 21}, length=4)
79: search_tuple(sourceSet={18}, length=4)
34 search_tuple(sourceSet={9, 2}, length=5)
67 search_tuple(sourceSet={0, 4}, length=4)
68 search_tuple(sourceSet={0, 8, 2, 3}, length=4)
69 search_tuple(sourceSet={0, 3, 11, 4}, length=4)
70 search_tuple(sourceSet={14}, length=4)
73 search_tuple(sourceSet={8, 17, 2}, length=4)
74 search_tuple(sourceSet={8, 17, 3, 12}, length=4)
75 search_tuple(sourceSet={3, 11, 12, 21}, length=4)
76 search_tuple(sourceSet={4, 14}, length=4)
77 search_tuple(sourceSet={11, 4}, length=4)
78 search_tuple(sourceSet={11, 21}, length=4)
79 search_tuple(sourceSet={18}, length=4)
49 search_tuple(sourceSet={9, 18}, length=5)&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;if len(matches) &amp;gt; 0:
    min_index = match_idx.index(min(match_idx))
    indexes = [i for i, v in enumerate(match_idx) if v == min(match_idx)]
    print(&quot;indexes&quot;, indexes)
    print(&quot;matches&quot;, matches)
    for idx, value in enumerate(matches):
        if idx in indexes:
            for i in value:
                if idx == min_index:
                    term_list[i] = term_list[i]._replace(final=&quot;Added&quot;)
                # also create a list of all options that will fit the bill
                if (not set(matches[min_index]).issubset(matches[idx])) or min_index == idx:
                    possible_terms[idx].append(term_list[i])
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;</content><author><name>Jack Briody</name><email>jackbriody@gmail.com</email><uri>/</uri></author><category term="python" /><category term="namedtuple" /><category term="boolean_algebra" /><category term="logic" /><category term="python" /><category term="namedtuple" /><category term="boolean_algebra" /><category term="logic" /><summary type="html">Reducing from Normal Form The basics of reducing is that you need two terms like ABC and ABC’. This can be reduced to AB(C + C’). In Logic (C + C’) evaluates to 1–so you have AB(1) which is AB. Essentially the C and C’ cancel each other so you go from 2 terms, ABC and ABC’, to one term AB. The general rules are: Individual inputs must have a 1 to 1 match for inputs/letters. So ABC and ABD fail on this front. Of the inputs only one can differ in its primed vs. unprimed form. So ABC and AB’C’ cannot be reduced. Extracting (C + C’) in this case leaves us with AB + AB’–which simply goes against the rules. Finally, when performing reductions, terms of any generation can be used multiple times to come up with new terms. I hope to make a lot of this clearer shortly, but for starters it is important to note that each generation will have the same number of inputs as all other items in the generation. For the bowling example the 1st generation will all have 4 inputs (and will also have the same set of inputs A, B, C, and D). 2nd generation will have 3 inputs–however, because of reduction, the set of inputs will not necessarily match. You could have terms with ABC and other terms with ACD. The 3rd generation will have 2 inputs in each term. Situation 1 as an Example So for Situation 1 we have the following terms in the 1st generation: ones Ancestors 1 ABCD 4 1 2 ABCD' 3 2 3 ABC'D 3 3 4 ABC'D' 2 4 5 AB'CD 3 5 6 AB'CD' 2 6 7 AB'C'D 2 7 Term 1 (ABCD) can be “minimized” with 3 other terms; 2, 3, and 5 (ABCD’, ABC’D, and AB’CD)–each of which differ from ABCD by having only 1 “primed” term. This results in 3 new 2nd generation terms–ABC, ABD, and ACD. ABC is derived from Term 1 (ABCD) and Term2 (ABCD’) to get ABC(D + D’) == ABC. ABD is T1 (ABCD) and T3 (ABC’D)–&amp;gt;ABD, etc. Term 4 – Term2 and Term3 to get ABD’ and ABC’ Term 6 – Term2 and Term5 to get ACD’ and AB’C Term 7 – Term3 and Term5 to get AC’D and AB’D Generation 2 looks like: ones Ancestors 8 ABC 3 1, 2 9 ABD 3 1, 3 10 ACD 3 1, 5 11 ABD' 2 4, 2 12 ABC' 2 4, 3 13 ACD' 2 6, 2 14 AB'C 2 6, 5 15 AC'D 2 7, 3 16 AB'D 2 7, 5 So for generation 2 we only have . . . Term9 (ABD) and Term11 (ABD’) –&amp;gt; AB Ancestors(Term1, Term3, Term2, Term4) Term10 (ACD) and Term13 (ACD’) –&amp;gt; AC Ancestors(Term2, Term6, Term1, Term5) Term10 (ACD) and Term15 (AC’D) –&amp;gt; AD Ancestors(Term7, Term5, Term1, Term5) Generation 3 ends up being: ones Ancestors 17 AB 2 1, 3, 2, 4 18 AC 2 2, 6, 1, 5 19 AD 2 7, 5, 1, 5 For future reference things to note . . . The ones column essentially helps us with Rule 2 above–terms can only differ by 1 item. ABC (111) has 3 ones, it can not be reduced with AB’C’ (100) as there is more than 1 different item (it has one 1). It can be compared with AB’C (101) as there are 2 ones. It could also be compared with ABD’ (11-0) but would fail based on rule 1. The other item, may be the most important, is the ancestors. To form a complete reduction we need to have all the minterms from our original canonical form (generation 1) represented in our reduced terms. In this case generation 3; AB, AC, and AD; cover all 7 terms from the original canonical form. The ancestor lists for those 3 terms include 1, 2, 3, 4, 5, 6, and 7. There is also no point in doing redundant minimizations. Term 8 (ABC) for example can be used to reduce with Term12 (ABC’) and Term14 (AB’C). But reducing T8 with T12 giving AB with 1, 2, 3, 4 as ancestors is the same as reducing T9 (ABD) with T11 (ABD’) with 1, 2, 3, 4 as ancestors. T8 with T14 similarly is the same as T10 and T13 to get AC with 1, 2, 5, 6 as ancestors. Back to why this matters It seems the most common case and use for reducing from a canonical form is with electronics/circuits. But as part of that whole “Turing Machine” thing it is actually something that occurs almost constantly in code–more often than not we are dealing with simpler cases and intuitively arrive at a minimized or close to minimized form. Quine-McCluskey and Petrick’s Method (finally) First some links: Wikipedia: Quine-McCluskey Wikipedia: Petrick’s Method Circuits and Q-M Circuits and Petrick In a nutshell–getting to a final minimized form involves using Quine-McClusky to reduce your terms from the canonical form. Once no more reduction is possible Petrick’s Method can be used to select a set of terms that will “cover” all the terms from the original canonical form. Now for some code So, we finally get to the namedtuple portion of our program. Throughout my implementation a list of namedtuples (term_list) is used for storing information on the terms and the reductions. The tuples definition is: Term = namedtuple('Term', 'termset used ones source generation final') Some of the named fields should be familiar from the example above. The major use for this is for the term_list variable that stores all of our generations and reductions. Within our “namedtuple” Term the fields are . . . termset – these are actual python sets that will contain the inidividual inputs for a term. So ABC would be (“A”, “B”, “C”), ABD’ would be (“A”, “B”, “D’”). used – this will be True or False. If a term is “used” that means it was used to create a term for a later generation and can be ignored in finding our final reduction. ones – A simple count of the number of 1s or un-primed inputs. ABC would have this as 3, ABD’ would have it as 2. Ultimately we sort each generation by the “ones” column. source – specifies which terms were used in creating this term. Generation 1 terms will have source set to themselves. Gen 2 would have items like (0, 3), Gen 3 would have (0, 3, 2, 4), etc. generation – simply the generation of this item final – by default it is “None”. After processing the items needed for our final reduction will have either “Required” or “Added” in this field. The basic layout of the code/functions is . . . quinemc() To perform a reduction you’d simply call quinemc . . . def quinemc(myitem, highorder_a=True, full_results=False): myitem – can be an int, a string containing a canonical form, or a list containing terms from a canonical form. If you provide an int quinemc will call canonical(myitem) to get the canonical form. If you provide a string or list you’ll get an error if it is not a true canonical form (e.g. “ABC + ABD”). highorder_a – like canonical() this determines whether A is the highorder bit or loworder bit. Default is A as highorder. full_results – The default (False) simply returns the reduced form as a string. If set to True quinemc() returns 3 items A string of the reduced form The term_list list–a list containing all of the Term namedtuples For some reductions there will be multiple ways to create a final form. If this is the case the 3rd item will be a dictionary containing all of the possible completions. Examples quinemc(65024) quinemc(&quot;ABCD + ABCD' + ABC'D + ABC'D' + AB'CD + AB'CD' + AB'C'D&quot;) quinemc([&quot;ABCD&quot;, &quot;ABCD'&quot;, &quot;ABC'D&quot;, &quot;ABC'D'&quot;, &quot;AB'CD&quot;, &quot;AB'CD'&quot;, &quot;AB'C'D&quot;]) a, b, c = quinemc(65024, full_results=True) All 4 are equivalent and give “AB + AC + AD” as a result. quinemc() itself just validates that the input is either an int, string, or list. Then also validates that the input item is in fact a canonical form (lines 11-14). From there it simply calls _minimize_() where the real processing takes place. In case you are wondering the cdnf variable is just a list of the terms like . . . ['ABCD', &quot;ABCD'&quot;, &quot;ABC'D&quot;, &quot;ABC'D'&quot;, &quot;AB'CD&quot;, &quot;AB'CD'&quot;, &quot;AB'C'D&quot;] _minimize_() _minimize_() is essentially the driver function. It simply calls 3 other functions. The first two (in order), _create_first_generation_ and _merge_terms_ are the Quine-McCluskey portion. The last, _implicants_, is the Petrick method portion that finds our final result(s). The final bit of code handles special cases. The first is for a zero input. The second is for cases like quinemc(15) or quinemc(255) where all the terms end up canceling out. _create_first_generation_() For our first step we are simply taking the terms from our canonical form and putting them into the term_list namedtuple. For situation 1, quinemc(65024), we get a 1st gen term_list of . . . Term(termset={&quot;C'&quot;, 'A', 'B', &quot;D'&quot;}, used=False, ones=2, source=[0], generation=1, final=None) Term(termset={'A', 'C', &quot;D'&quot;, &quot;B'&quot;}, used=False, ones=2, source=[1], generation=1, final=None) Term(termset={&quot;C'&quot;, 'A', &quot;B'&quot;, 'D'}, used=False, ones=2, source=[2], generation=1, final=None) Term(termset={'A', 'C', 'B', &quot;D'&quot;}, used=False, ones=3, source=[3], generation=1, final=None) Term(termset={&quot;C'&quot;, 'A', 'B', 'D'}, used=False, ones=3, source=[4], generation=1, final=None) Term(termset={'A', 'C', &quot;B'&quot;, 'D'}, used=False, ones=3, source=[5], generation=1, final=None) Term(termset={'A', 'C', 'B', 'D'}, used=False, ones=4, source=[6], generation=1, final=None) This is simply taking the 7 terms from our canonical form and putting them into the “Term” namedtuple data structure. The first bit of code converts our terms to python sets (line 3). Line 6 exists only to clean up duplicate terms if quinemc was called with a string or list. Lines 8/9 actually creates our Term namedtuple. We then sort it by the number of “ones” at line 10 and then update our namedtuple with each items index at lines 11 &amp;amp; 12. (_replace is something from the namedtuple item–essentially a short cut for updating an immutable object. As the name implies it isn’t “updating” the immutable tuple but is replacing it with a copy of the original and a single value changed.) As far as our “Term” namedtuple goes–_merge_terms_ will use python set functions so “termset” puts our terms into “sets”. The “used” flag will also be used by merge_terms; ultimately only items that are still used=False will be potential terms for our final result. As noted before it only makes sense to compare terms where the number of “ones” differs by 1 so the whole list is sorted by how many “ones” are in a term. For our first generation the “source” list is essentially a reference to the item itself. This simplifies things in merge_terms so that we do not need to use list indexes for our first gen items. “generation” is another item used by _merge_terms_. Again, it only makes sense to compare/reduce items that exist in the same generation as the number of inputs needs to match. Each successive generation will reduce the number of items in the “termset” by one. So, as in this case, Generation 1 has 4 inputs; if we can reduce anything then all Generation 2 items will have 3 inputs; if Gen 2 items can be reduced all Generation 3 items will have 2 inputs; finally if possible Generation 4 would have termsets with a single input. “final” is ultimately used by the _implicants_/Petrick’s method part of the code. _merge_terms_ and _create_new_terms_ _merge_terms_ is pretty simple. It is called for each generation and it then tries to minimize that generation. When nothing can be minimized done becomes True. _create_new_terms_ is where we perform our actual reductions. Taking terms like ABC and ABC’ to get AB. working_list is simply a stripped down version of our term_list variable containing only items for the current “generation”. Lines 21 and 22 do a nested loop over the items in working_list. The “tricky” bit is the “sym_set” variable. Line 24 does a set comparison of our term_list.termset items and gets a “difference” set on them. So comparing ABC to ABC’ results in a “sym_set” of (C, C’). Line 26 then checks whether we have only 2 items in our “sym_set”–this would fail if we had AB’C’ and ABC. Then also checks that the inputs (e.g. C) match – list(sym_set)[0][0] == list(sym_set)[1][0]. If that all works out we then create a new merged term. Lines 31 - 36 avoid doing duplicate sources for reductions. After all of this work is done the used_dict dictionary is used to update our global term_list to mark all items that have been used in merges. At this point our term_list looks like . . . Term(termset={'B', 'A', &quot;C'&quot;, &quot;D'&quot;}, used=True, ones=2, source=[0], generation=1, final=None) Term(termset={&quot;B'&quot;, 'C', 'A', &quot;D'&quot;}, used=True, ones=2, source=[1], generation=1, final=None) Term(termset={&quot;B'&quot;, 'A', &quot;C'&quot;, 'D'}, used=True, ones=2, source=[2], generation=1, final=None) Term(termset={'B', 'C', 'A', &quot;D'&quot;}, used=True, ones=3, source=[3], generation=1, final=None) Term(termset={'B', 'A', &quot;C'&quot;, 'D'}, used=True, ones=3, source=[4], generation=1, final=None) Term(termset={&quot;B'&quot;, 'C', 'A', 'D'}, used=True, ones=3, source=[5], generation=1, final=None) Term(termset={'B', 'C', 'A', 'D'}, used=True, ones=4, source=[6], generation=1, final=None) Term(termset={'B', 'A', &quot;D'&quot;}, used=True, ones=2, source=[0, 3], generation=2, final=None) Term(termset={'B', 'A', &quot;C'&quot;}, used=True, ones=2, source=[0, 4], generation=2, final=None) Term(termset={'C', 'A', &quot;D'&quot;}, used=True, ones=2, source=[1, 3], generation=2, final=None) Term(termset={&quot;B'&quot;, 'C', 'A'}, used=True, ones=2, source=[1, 5], generation=2, final=None) Term(termset={'A', &quot;C'&quot;, 'D'}, used=True, ones=2, source=[2, 4], generation=2, final=None) Term(termset={&quot;B'&quot;, 'A', 'D'}, used=True, ones=2, source=[2, 5], generation=2, final=None) Term(termset={'B', 'C', 'A'}, used=True, ones=3, source=[3, 6], generation=2, final=None) Term(termset={'B', 'A', 'D'}, used=True, ones=3, source=[4, 6], generation=2, final=None) Term(termset={'C', 'A', 'D'}, used=True, ones=3, source=[5, 6], generation=2, final=None) Term(termset={'B', 'A'}, used=False, ones=2, source=[0, 3, 4, 6], generation=3, final=None) Term(termset={'C', 'A'}, used=False, ones=2, source=[1, 3, 5, 6], generation=3, final=None) Term(termset={'A', 'D'}, used=False, ones=2, source=[2, 4, 5, 6], generation=3, final=None) Major things to note–the generations I think are clearer here. The _merge_terms_ function has set “used” to true for everything in Gen 1 and Gen 2. Looking at Gen 2 you can see in the source column that all 7 items (0-6) have been used. Generation 3 points to an important part of the code in this regard. Line 32 of _create_new_terms_ prevents us from doing duplicate “merges”, but the preceding lines (28 and 29) mark the items we aren’t actually using as duplicates as far as “used” is concerned. The for loop at lines 39 - 42 is the code that actually goes through and marks the individual terms as “used”. This ends the Quine-McCluskey portion of the reduction. For Petrick’s method we will only need to consider the Term items that have “used=False”. If you look at the Simplifying with Quine-McCluskey Method section at Circuits and Petrick The table under “Now to find the prime implicants:” is essentially what we have accomplished. Each of the 3 columns represent a generation. Within a generation the terms are grouped by the number of 1s. And finally check marks have been added for each term that has been used in a reduction. Petrick’s Method From Situation 1 we have ended up with 3 terms that may be needed to form our final result. Term(termset={'B', 'A'}, used=False, ones=2, source=[0, 3, 4, 6], generation=3, final=None) Term(termset={'C', 'A'}, used=False, ones=2, source=[1, 3, 5, 6], generation=3, final=None) Term(termset={'A', 'D'}, used=False, ones=2, source=[2, 4, 5, 6], generation=3, final=None) On paper starting Petrick’s method would require putting this into a table form with 1 column for each term from our canonical form (generation 1) and a row for each term that hasn’t been used. The remainder of the grid is filled in using the “source” values for the terms–with the goal being to find a set of terms that “fills” all of the columns. 0 1 2 3 4 5 6 --------------------------------- AB x x x x AC x x x x AD x x x x A “required” prime implicant can be found by finding columns that only contain 1 “x”–that essentially means that term is the only one that “covers” the corresponding term from the canonical form. In this case columns 0, 1, and 2 all have single x’s and they are each for one of our 3 terms. The other consideration is that all terms from the canonical form must be covered. In this case this is met using the 3 “required” terms. (In fact any time when all of the “used=False” terms are required terms they would cover all of the canonical terms.) Situation 2 is slightly more interesting. In that case the final reduction looks like . . . Term(termset={'B', &quot;D'&quot;, &quot;C'&quot;, 'A'}, used=True, ones=2, source=[0], generation=1, final=None) Term(termset={&quot;D'&quot;, &quot;B'&quot;, 'C', 'A'}, used=True, ones=2, source=[1], generation=1, final=None) Term(termset={&quot;C'&quot;, &quot;B'&quot;, 'D', 'A'}, used=True, ones=2, source=[2], generation=1, final=None) Term(termset={'B', &quot;C'&quot;, 'D', 'A'}, used=True, ones=3, source=[3], generation=1, final=None) Term(termset={&quot;B'&quot;, 'C', 'D', 'A'}, used=True, ones=3, source=[4], generation=1, final=None) Term(termset={'B', &quot;C'&quot;, 'A'}, used=False, ones=2, source=[0, 3], generation=2, final='None') Term(termset={'A', &quot;B'&quot;, 'C'}, used=False, ones=2, source=[1, 4], generation=2, final='None') Term(termset={&quot;C'&quot;, 'D', 'A'}, used=False, ones=2, source=[2, 3], generation=2, final=None) Term(termset={&quot;B'&quot;, 'D', 'A'}, used=False, ones=2, source=[2, 4], generation=2, final='None') In this case the canonical form only has 5 terms and the reduction is complete with generation 2–and all of the generation 2 items have “used=False”. In this case our table looks like . . . 0 1 2 3 4 -------------------------- ABC' x x AB'C x x AC'D x x AB'D x x In this case only the 1st 2 terms are required (ABC’ and AB’C) because columns 0 and 1 are the only ones with a single X. But we still need to make sure all of the terms from the original canonical form are covered. ABC’ covers 0 and 3, AB’C cover 1 and 4. So the only “uncovered” item is term 2–both AC’D and AB’D will take care of column 2. Since they are both the same length we could use either one so . . . ABC’ + AB’C + AC’D and ABC’ + AB’C + AB’D are equivalent. And logically this makes sense if you consider the Situation 2 story where Bob and Clara won’t bowl together. Ultimately, for the code purposes, none of our 4 situations actually exercise all of the code (they really are pretty simple situations). For that I’ll be using 2046 (‘0b11111111110’). canonical(2046) and quinemc(2046) give us . . . “AB’CD’ + AB’C’D + AB’C’D’ + A’BCD + A’BCD’ + A’BC’D + A’BC’D’ + A’B’CD + A’B’CD’ + A’B’C’D” and “AB’D’ + B’C’D + A’B + A’C” I really have no idea what Alan, Bob, Clara and Declan are doing here, but again note the canonical form starts with 10 terms corresponding to the number of 1’s in the binary representation. After reduction we end up with . . . Term(termset={&quot;D'&quot;, &quot;C'&quot;, &quot;B'&quot;, 'A'}, used=True, ones=1, source=[0], generation=1, final=None) Term(termset={'B', &quot;C'&quot;, &quot;A'&quot;, &quot;D'&quot;}, used=True, ones=1, source=[1], generation=1, final=None) Term(termset={&quot;D'&quot;, &quot;B'&quot;, &quot;A'&quot;, 'C'}, used=True, ones=1, source=[2], generation=1, final=None) Term(termset={&quot;C'&quot;, &quot;B'&quot;, 'D', &quot;A'&quot;}, used=True, ones=1, source=[3], generation=1, final=None) Term(termset={&quot;D'&quot;, &quot;B'&quot;, 'C', 'A'}, used=True, ones=2, source=[4], generation=1, final=None) Term(termset={&quot;C'&quot;, &quot;B'&quot;, 'D', 'A'}, used=True, ones=2, source=[5], generation=1, final=None) Term(termset={'B', &quot;D'&quot;, &quot;A'&quot;, 'C'}, used=True, ones=2, source=[6], generation=1, final=None) Term(termset={'B', &quot;C'&quot;, 'D', &quot;A'&quot;}, used=True, ones=2, source=[7], generation=1, final=None) Term(termset={&quot;B'&quot;, 'D', &quot;A'&quot;, 'C'}, used=True, ones=2, source=[8], generation=1, final=None) Term(termset={'B', 'D', &quot;A'&quot;, 'C'}, used=True, ones=3, source=[9], generation=1, final=None) **Term(termset={&quot;D'&quot;, &quot;B'&quot;, 'A'}, used=False, ones=1, source=[0, 4], generation=2, final='None') **Term(termset={&quot;C'&quot;, &quot;B'&quot;, 'A'}, used=False, ones=1, source=[0, 5], generation=2, final=None) Term(termset={'B', &quot;D'&quot;, &quot;A'&quot;}, used=True, ones=1, source=[1, 6], generation=2, final=None) Term(termset={'B', &quot;C'&quot;, &quot;A'&quot;}, used=True, ones=1, source=[1, 7], generation=2, final=None) **Term(termset={&quot;D'&quot;, &quot;B'&quot;, 'C'}, used=False, ones=1, source=[2, 4], generation=2, final=None) Term(termset={&quot;D'&quot;, &quot;A'&quot;, 'C'}, used=True, ones=1, source=[2, 6], generation=2, final=None) Term(termset={&quot;B'&quot;, &quot;A'&quot;, 'C'}, used=True, ones=1, source=[2, 8], generation=2, final=None) **Term(termset={&quot;C'&quot;, 'D', &quot;B'&quot;}, used=False, ones=1, source=[3, 5], generation=2, final='None') Term(termset={&quot;C'&quot;, 'D', &quot;A'&quot;}, used=True, ones=1, source=[3, 7], generation=2, final=None) Term(termset={&quot;B'&quot;, 'D', &quot;A'&quot;}, used=True, ones=1, source=[3, 8], generation=2, final=None) Term(termset={'B', &quot;A'&quot;, 'C'}, used=True, ones=2, source=[6, 9], generation=2, final=None) Term(termset={'B', 'D', &quot;A'&quot;}, used=True, ones=2, source=[7, 9], generation=2, final=None) Term(termset={'D', &quot;A'&quot;, 'C'}, used=True, ones=2, source=[8, 9], generation=2, final=None) **Term(termset={'B', &quot;A'&quot;}, used=False, ones=1, source=[1, 6, 7, 9], generation=3, final='None') **Term(termset={&quot;A'&quot;, 'C'}, used=False, ones=1, source=[2, 6, 8, 9], generation=3, final='None') **Term(termset={'D', &quot;A'&quot;}, used=False, ones=1, source=[3, 7, 8, 9], generation=3, final=None) I’ve added “**” in front of the unused terms just to highlight the sporadic locations of unused terms. The short list of only items where “used=False” is . . . Term(termset={&quot;D'&quot;, &quot;B'&quot;, 'A'}, used=False, ones=1, source=[0, 4], generation=2, final='None') Term(termset={&quot;C'&quot;, &quot;B'&quot;, 'A'}, used=False, ones=1, source=[0, 5], generation=2, final=None) Term(termset={&quot;D'&quot;, &quot;B'&quot;, 'C'}, used=False, ones=1, source=[2, 4], generation=2, final=None) Term(termset={&quot;C'&quot;, 'D', &quot;B'&quot;}, used=False, ones=1, source=[3, 5], generation=2, final='None') Term(termset={'B', &quot;A'&quot;}, used=False, ones=1, source=[1, 6, 7, 9], generation=3, final='None') Term(termset={&quot;A'&quot;, 'C'}, used=False, ones=1, source=[2, 6, 8, 9], generation=3, final='None') Term(termset={'D', &quot;A'&quot;}, used=False, ones=1, source=[3, 7, 8, 9], generation=3, final=None) From this our table would look like . . . 0 1 2 3 4 5 6 7 8 9 --------------------------------------------- AB'D' x x AB'C' x x B'CD' x x B'C'D x x A'B x x x x A'C x x x x A'D x x x x In this case the only required term is A’B as column 1 is the only one with a single x. It is probably obvious that shorter terms from later generations have more sources/ancestors–so they will often take care of larger groups. Anyway, removing A’B and all of the columns it covers (1, 6, 7, 9) gives us . . . 0 2 3 4 5 8 ------------------------------ AB'D' x x AB'C' x x B'CD' x x B'C'D x x A'C x x A'D x x From this table it should be obvious that you’ll need either AB’D’ or AB’C’ to cover column 0, and you’ll also need one of A’C or A’D to cover column 8. I personally think doing this by hand is a pain–but ultimately you end up with 2 equivalent alternatives “AB’D’ + B’C’D + A’B + A’C” “AB’C’ + B’CD’ + A’B + A’D” Petrick’s Method and the _implicants_ code Going back to the call graph _implicants_() uses 3 functions–_get_columns_(), _make_find_dict_(), and _check_combinations_(). Our first order of business in _implicants_ is to make one large list containing all of the “source=[]” items from our terms where used=False. (Lines 5 and 6) This list will contain duplicates as it is used to find the required sources on Line 8 where we look for sources that appear once. For quinemc(2046) this list looks like . . . [0, 4, 0, 5, 2, 4, 3, 5, 1, 6, 7, 9, 2, 6, 8, 9, 3, 7, 8, 9] So, we have “0, 4” for AB’D’, then “0, 5” for AB’C’, etc. The only source that appears only once is 1–so our required list ends up looking like [1]. With that information in hand we now move onto _keep_columns_(). At this point the code starts to use some set operations and there are more of those to come. The major function of this code is to A) find and mark “required” terms and B) create two python sets: 1) the sources from our required items and 2) one containing the sources for the rest or the terms. The for loop on lines 5 - 10 handles this. We “enumerate()” over the term_list where used=False–enumerate is important because we will need the index in term_list to update the “required” items. Line 6 – we perform set intersection of required and the current terms sources. An intersection will return any values that are in both sets. The items in “required” will only be in one of our term’s source lists; however a term could potentially cover more than one required item which is why line 6 used &amp;gt;= 1. Lines 7 &amp;amp; 8 – if our current term is required we update term_list and set final=”Required”. We also add the sources for the required term to our ignore list–it is a list at this point. Line 10 – if our current term isn’t required we add its sources to a “keep” list Line 12 – finally we generate a list of everything that is in “keep” but not in “ignore” by treating them as sets and using the set difference operator. So for quinemc(2046) after the for loop “keep” contains [0, 4, 0, 5, 2, 4, 3, 5, 2, 6, 8, 9, 3, 7, 8, 9]. Ignore contains [1, 6, 7, 9]. And the final result after performing a set difference on the 2 is [0, 2, 3, 4, 5, 8]. Which is the same as the table above–we’ve removed the source items for the A’B term and marked that term as “Required”. For something like our Situation 1 (quinemc(65024)) “keep” will end up empty as the 3 terms cover all terms from the canonical expression. Back in _implicants_ this situation is covered on line 11 where we set “finished” to True so we can skip the next bits of processing. Our next little bit of code in _implicants_ is lines 13 through 19 which essentially checks to see if a single term will cover the remaining columns. It first, however, makes a detour to create “find_dict” by calling _make_find_dict_ _make_find_dict_ At this point in _implicants_ we have “keep_columns” which tells us which columns we still need to include in our final result. However, in our big “term_list” list each term still contains the columns we want to ignore in their “source” items. _make_find_dict_ addresses this by creating a dictionary containing a terms index from “term_list” along with a new namedtuple containing that term_list item’s sources with columns we want to ignore removed. This simplifies later set operations and our new namedtuple also includes a “length” item as it is possible to find many different reductions but some can be longer than others. It also covers the situation where an item in “term_list” can be completely ignored because the ancestors/sources it covers are already covered by a required item. To illustrate what is going on–for quinemc(2046) our current term_list of “used=False” items (with term_list indexes) is . . . 10 Term(termset={&quot;B'&quot;, 'A', &quot;D'&quot;}, used=False, ones=1, source=[0, 4], generation=2, final='None') 11 Term(termset={&quot;B'&quot;, &quot;C'&quot;, 'A'}, used=False, ones=1, source=[0, 5], generation=2, final=None) 14 Term(termset={&quot;B'&quot;, 'C', &quot;D'&quot;}, used=False, ones=1, source=[2, 4], eneration=2, final=None) 17 Term(termset={&quot;B'&quot;, &quot;C'&quot;, 'D'}, used=False, ones=1, source=[3, 5], generation=2, final='None') 23 Term(termset={&quot;A'&quot;, 'B'}, used=False, ones=1, source=[1, 6, 7, 9], generation=3, final='Required') 24 Term(termset={'C', &quot;A'&quot;}, used=False, ones=1, source=[2, 6, 8, 9], generation=3, final='None') 25 Term(termset={'D', &quot;A'&quot;}, used=False, ones=1, source=[3, 7, 8, 9], generation=3, final=None) Note 23 (A’B) is marked as required and so we want to ignore [1, 6, 7, 9]. When _make_find_dict_() is finished we end up with a find_dict that looks like . .. 10: search_tuple(sourceSet={0, 4}, length=3) 11: search_tuple(sourceSet={0, 5}, length=3) 14: search_tuple(sourceSet={2, 4}, length=3) 17: search_tuple(sourceSet={3, 5}, length=3) 24: search_tuple(sourceSet={8, 2}, length=2) 25: search_tuple(sourceSet={8, 3}, length=2) For 10, 11, 14, and 17 nothing changes; for 24 and 25 [6, 9] and [7, 9] (respectively) have been removed. And length is the length of each item’s term. Final Step – _check_combinations_ After _make_find_dict_() we head back into _implicants_() and back into lines 13-19 where we are checking to see if a single term will complete our minimized form. if not finished: find_dict = _make_find_dict_(term_list, keep_columns) for idx, val in find_dict.items(): if set(keep_columns) == val.sourceSet: term_list[idx] = term_list[idx]._replace(final=&quot;Added&quot;) finished = True break At this point we have the new “find_dict” dictionary and “keep_columns” which contains the columns/canonical terms we still need to cover. The for loop at 15-19 (for idx, val ...) simply loops through our newly created “find_dict” and checks whether any of the “sourceSet” items there are an exact match for “keep_columns”. If any one item does cover all the needed columns then we are done–we simply update “term_list” at the index taken from find_dict’s key and set that term_list item’s final attribute to “added”. So ultimately the final attribute within term_list will have one of 3 values . . . None – The default. The term is not used in our final minimized form Required – Denotes prime implicant set in _get_columns_(). Again, these are terms that are the only ones that cover one or more of our original canonical terms. Added – These are the additional terms needed to cover the remaining items from our canonical form. Essentially there is a choice of multiple possible terms and we attempt to find the best/shortest set that will fill out our final minimized version. At this point in _implicants_, if a single term will not work we need to get a little more drastic and try and find combinations of terms that will work. So with . . . if not finished: possible_terms = _check_combinations_(find_dict, term_list, keep_columns) . . . in _implicants_, we head into _check_combinations_ _check_combinations_ This function actually does two things and for good design principles the second half should probably be split off into its own function. I’m going to claim it is a borderline case and that leaving it as is is slightly less complex than the complexity of moving the second half out. So, the first half of the code (upto line 16) works quite a bit like the code to check if a single term will match/cover all items in the “keep_columns” list. The difference here is we take the “sourceSet” items from “find_dict” and mash them together in all possible combinations. Lines 7 and 8 – Line 6 “x” is a counter going from 2 to the number of keys in “find_dict”. Line 7 creates all combinations of those keys starting with all combinations of 2 keys, then 3 keys, etc. Funky thing here is that for a “find_dict” containing 7 items (for example) we may find a solution using a combination of just 2 items. However, the code continues to look for solutions that would be combinations of 3, then 4, then 5, etc. For a complex case this does add a ton of probably unneeded computation. But I haven’t been able to rule out the possibilities of a corner case where 3 terms would actually be shorter than 2 terms (for example AB + BC’ + AD’ is considered shorter than ABCD + ABC’D’). Anyway, using our example of quinmc(2046) with find_dict containing keys of 10, 11, 14, 17, 24, and 25 our code starts with “items” containing (10, 11), then (10, 14), etc. Ultimately for 7 items we end up with 15 combinations of indexes. [And we are dealing with combinations–order doesn’t matter. Permutations, where order matters isn’t what we need.] Lines 9 and 10 – we set up variables local to the for loop. Each time we go through a combination (10, 11) then (10, 14) we are resetting “combined_sources” and “temp_count”. Lines 11-13 – We look up the items for our current combination [10, 11] and mash their sources together in “combined_sources”. We also use “find_dict” to keep track of how long this result is. So for [10, 11] combined_sources would be {0, 4, 5}, for [10, 14] it would be {0, 2, 4} Line 14 – Just like searching for a single item that will cover everything we need we check whether combined sources covers what we need. Even if this succeeds we will continue checking all combinations. Essentially, if there are any number of ways to complete our reduction using 2 terms we will look at them all. So for example we’d check if set([0, 2, 3, 4, 5, 8]) == {0, 4, 5}–and it will finally succeed when we find a group of items from “find_dict” covering all of set([0, 2, 3, 4, 5, 8]). (Might be good to remind you at this point that every item in “find_dict” will only contain 1 or more of [0, 2, 3, 4, 5, 8] – but in this case would never contain a 1 or 6 as they have been stripped out.} Lines 15-16 – if we find a match we add the list of keys to the matches list. We also add its length to matches_idx. The second half. a, b, c = quinemc(548738420677, full_results=True) 8534 13 possible items “A’BC’DF’ + A’BC’D’F + B’C’D’F’ + AB’C’F’ + AB’C’E’ + AB’C’D’ + A’BD’E’ + A’B’CD’ + A’B’DE + A’CDE” “A’BC’DF’ + A’BC’D’F + B’C’D’F’ + AB’C’F’ + AB’C’E’ + AB’C’D’ + A’BD’E’ + A’B’CD’ + A’B’DE + A’CDE” “A’BC’DF’ + A’BC’D’F + B’C’D’F’ + AB’C’F’ + AB’C’E’ + AB’C’D’ + A’BD’E’ + A’B’CD’ + A’B’DE + A’CDE” “A’BC’DF’ + A’BC’D’F + B’C’D’F’ + AB’C’F’ + AB’C’E’ + AB’C’D’ + A’BD’E’ + A’B’CD’ + A’B’DE + A’CDE” {0, 2, 3, 4, 8, 9, 11, 12, 14, 17, 18, 21} find dict 34: search_tuple(sourceSet={9, 2}, length=5) – 49: search_tuple(sourceSet={9, 18}, length=5) 67: search_tuple(sourceSet={0, 4}, length=4) – 68: search_tuple(sourceSet={0, 8, 2, 3}, length=4) – 69: search_tuple(sourceSet={0, 3, 11, 4}, length=4) – 70: search_tuple(sourceSet={14}, length=4) 73: search_tuple(sourceSet={8, 17, 2}, length=4) 74: search_tuple(sourceSet={8, 17, 3, 12}, length=4) 75: search_tuple(sourceSet={3, 11, 12, 21}, length=4) 76: search_tuple(sourceSet={4, 14}, length=4) 77: search_tuple(sourceSet={11, 4}, length=4) 78: search_tuple(sourceSet={11, 21}, length=4) 79: search_tuple(sourceSet={18}, length=4) 34 search_tuple(sourceSet={9, 2}, length=5) 67 search_tuple(sourceSet={0, 4}, length=4) 68 search_tuple(sourceSet={0, 8, 2, 3}, length=4) 69 search_tuple(sourceSet={0, 3, 11, 4}, length=4) 70 search_tuple(sourceSet={14}, length=4) 73 search_tuple(sourceSet={8, 17, 2}, length=4) 74 search_tuple(sourceSet={8, 17, 3, 12}, length=4) 75 search_tuple(sourceSet={3, 11, 12, 21}, length=4) 76 search_tuple(sourceSet={4, 14}, length=4) 77 search_tuple(sourceSet={11, 4}, length=4) 78 search_tuple(sourceSet={11, 21}, length=4) 79 search_tuple(sourceSet={18}, length=4) 49 search_tuple(sourceSet={9, 18}, length=5) if len(matches) &amp;gt; 0: min_index = match_idx.index(min(match_idx)) indexes = [i for i, v in enumerate(match_idx) if v == min(match_idx)] print(&quot;indexes&quot;, indexes) print(&quot;matches&quot;, matches) for idx, value in enumerate(matches): if idx in indexes: for i in value: if idx == min_index: term_list[i] = term_list[i]._replace(final=&quot;Added&quot;) # also create a list of all options that will fit the bill if (not set(matches[min_index]).issubset(matches[idx])) or min_index == idx: possible_terms[idx].append(term_list[i])</summary></entry><entry><title type="html">Closures or: How I learned to stop classing and love the function</title><link href="https://blog.evilbyte.com/python/closures/python-closures/" rel="alternate" type="text/html" title="Closures or: How I learned to stop classing and love the function" /><published>2017-08-01T00:00:00+00:00</published><updated>2017-08-01T00:00:00+00:00</updated><id>https://blog.evilbyte.com/python/closures/python-closures</id><content type="html" xml:base="https://blog.evilbyte.com/python/closures/python-closures/">&lt;h2 id=&quot;python-closures&quot;&gt;Python Closures&lt;/h2&gt;
&lt;p&gt;I’m not going to say closures are simple–if you come from an object oriented background or are starting off with no background and learning a functional language they can be a bit of a mind-bender. Once you get past the world of syntax (the “ifs”, “whiles”, and “fors”) you essentially enter the world of “how do I put all that together?”. For languages with functions as “first-class objects” (FCOs) you’ll probably encounter closures or closure like constructs.&lt;/p&gt;

&lt;p&gt;You could probably get away with writing a ton of code in python without ever using a closure–but they can be a great tool to make your code simpler, more useful, and easier to extend and maintain. I don’t think you’d survive reading or writing Java Script without closures. I’m going to, however, focus on python.&lt;/p&gt;

\[\begin{align*}
  &amp;amp; \phi(x,y) = \phi \left(\sum_{i=1}^n x_ie_i, \sum_{j=1}^n y_je_j \right)
  = \sum_{i=1}^n \sum_{j=1}^n x_i y_j \phi(e_i, e_j) = \\
  &amp;amp; (x_1, \ldots, x_n) \left( \begin{array}{ccc}
      \phi(e_1, e_1) &amp;amp; \cdots &amp;amp; \phi(e_1, e_n) \\
      \vdots &amp;amp; \ddots &amp;amp; \vdots \\
      \phi(e_n, e_1) &amp;amp; \cdots &amp;amp; \phi(e_n, e_n)
    \end{array} \right)
  \left( \begin{array}{c}
      y_1 \\
      \vdots \\
      y_n
    \end{array} \right)
\end{align*}\]

&lt;h2 id=&quot;wikipedia--eli5&quot;&gt;Wikipedia != ELI5&lt;/h2&gt;
&lt;p&gt;Wikipedia starts their article on closures with . . .&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;In programming languages, closures (also lexical closures or function closures) are techniques for implementing lexically scoped name binding in languages with first-class functions. Operationally, a closure is a record storing a function[a] together with an environment:[1] a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.[b] A closure—unlike a plain function—allows the function to access those captured variables through the closure’s copies of their values or references, even when the function is invoked outside their scope.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;. . . which is pretty dense. Dense in the sense it is accurate, complete, and correct; not in the “idiot” sense. It is, however, the R-rated version; I’ll try to stick to the PG or PG-13 version.&lt;/p&gt;

&lt;h2 id=&quot;to-the-code&quot;&gt;To the Code&lt;/h2&gt;
&lt;p&gt;Before parsing that and going into a bit of detail on the “Why?”/”When?” of closures I’m going to get to the code. I’ll be using FizzBuzz for the first example–in many other places I’ve seen simple “adders” used for example closures and as far as examples go they seem limited.&lt;/p&gt;

&lt;p&gt;So the basic fizzbuzz I’ll use is:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;fizzbuzz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;):&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Fizz&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Buzz&quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;or&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fizzbuzz&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;If you’re wondering how that works the two expressions &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(x % 3 == 0)&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;(x % 5 == 0)&lt;/code&gt; return either &lt;strong&gt;True&lt;/strong&gt; or &lt;strong&gt;False&lt;/strong&gt;. &lt;strong&gt;True&lt;/strong&gt; evaluates to 1 and &lt;strong&gt;False&lt;/strong&gt; to zero. So when x is 6 you end up with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Fizz&quot; * 1 + &quot;Buzz&quot; * 0 or x&lt;/code&gt; then &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Fizz&quot; or x&lt;/code&gt;. In Python logic the following (plus some others) evaluate to False:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;0 (zero)&lt;/li&gt;
  &lt;li&gt;None&lt;/li&gt;
  &lt;li&gt;False&lt;/li&gt;
  &lt;li&gt;any of “”, (), {}, or [].&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href=&quot;http://www.thomas-cokelaer.info/tutorials/python/boolean.html&quot;&gt;See: Notes about Booleans&lt;/a&gt; In &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;Fizz&quot; or x&lt;/code&gt; “Fizz” is taken as true so “x” is ignored. 
For something like x = 7 you end up with &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;&quot; or x&lt;/code&gt;–which is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;False or x&lt;/code&gt; resulting in “x”.&lt;/p&gt;

&lt;p&gt;That, however, is not a closure–it is simply the function we’ll be returning from our closure. The actual closure wraps around the function like . . .&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/ec273e2c8a4e8f9d3ef47a5272e0ad01.js?file=fbclosure.py&quot;&gt; &lt;/script&gt;

&lt;p&gt;With that code you can then run stuff like:&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;fb35&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fbclosure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;fb27&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;fbclosure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fbclosure&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fb35&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fb27&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;x&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;x&lt;/span&gt; &lt;span class=&quot;ow&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;range&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;20&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)])&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fb35&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;224&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fb27&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;224&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;In this case the closure itself is created on &lt;strong&gt;lines 1 and 5&lt;/strong&gt;: &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def fbclosure&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;return fizzbuzz&lt;/code&gt;. I’ve also added the fizz and buzz parameters defaulting to 3 and 5 to make our fizzbuzz a little more versatile. Going back to the Wikipedia definition–the fizz and buzz variables are “lexically scoped names”. Our “function” is fizzbuzz, our environment includes ‘fizz’ and ‘buzz’–they are the “captured” items.&lt;/p&gt;

&lt;p&gt;At this point the Wikipedia definition is starting to take form. Our closure is returning a function, fizzbuzz–although we rename it with lines like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fb35 = fbclosure()&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fb27 = fbclosure(2, 7)&lt;/code&gt;. Our renamed functions store the information/environment of the enclosing closure (“lexically scope”). For fb35() that is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fizz = 3&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buzz = 5&lt;/code&gt;; for fb27() we have &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fizz = 2&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;buzz = 7&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;As far as using it–you can call the closure directly as in the first print statement (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fbclosure()(x)&lt;/code&gt;), but that probably isn’t typical. The real magic and power of functional languages is assigning the enclosed function to a variable which is actually a function.&lt;/p&gt;

&lt;h2 id=&quot;so-why-closures&quot;&gt;So, why Closures?&lt;/h2&gt;
&lt;p&gt;For the most part closures provide two things . . .&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Act as mini-classes/objects&lt;/li&gt;
  &lt;li&gt;Conveniently hide data (i.e. provides a “private” scope)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On Item 1–a closure provides some sort of functionality and each instance of a closure maintains its own state. From wikipedia this is the “lecixally scoped binding”.&lt;/p&gt;

&lt;p&gt;Item 2 builds on Item 1. Java Script and Python are pretty liberal with their scoping. In either language you can trample on or redefine a lot of things–either on purpose or by mistake. The more object oriented/class based languages tend to be defensive (private-ish) by default–functional languages are quite a bit more liberal.&lt;/p&gt;

&lt;p&gt;That said, though, while the defensive programming aspect can be important the real power of closures is in Item 1. In languages with functions as first class objects encapsulating and reusing important functions saves a lot of work, lines of code, and provides consistency. For a smaller application you avoid repeating code with functions. When an application grows you will typically start collecting code into modules or classes.&lt;/p&gt;

&lt;p&gt;Closures provide something of a middle ground. Rather than having to continually call a function with a set of parameters or instantiate a class–common bits of code can be centralized so they are easily re-used throughout an application or by multiple developers.&lt;/p&gt;

&lt;h2 id=&quot;another-example-multiple-functions&quot;&gt;Another Example: Multiple Functions&lt;/h2&gt;
&lt;p&gt;This may be quite a bit less common, but a closure in python &lt;strong&gt;can&lt;/strong&gt; return multiple functions. Fizzbuzz isn’t a particularly good example for this, but something like simple geometry does fit the bill. Iconoclast that I am I’m going to use triangles for this example rather than the more typical points and circles that seem to be the rage.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/ec273e2c8a4e8f9d3ef47a5272e0ad01.js?file=triangle_b.py&quot;&gt; &lt;/script&gt;

&lt;p&gt;In this case &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def triangle(a, b, c)&lt;/code&gt; is our enclosure. Then it gets a little funky. At the bottome &lt;strong&gt;Lines 18-24&lt;/strong&gt; we define an empty function &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T()&lt;/code&gt; then assign two of our functions to it &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T.area = area&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T.height = height&lt;/code&gt;. We then complete our closure by simply returning &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;T&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you come from an object oriented background this is very much like simply creating a minimalistic “object”. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;t345 = triangle(3, 4, 5)&lt;/code&gt; will create an object with two functions, area() and height(). Our third function, semiperimeter(), remains hidden, essentially a private function used only internally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE:&lt;/strong&gt; This code will only work in Python 3–Python 2 does not have the “nonlocal” feature.&lt;/p&gt;

&lt;p&gt;Okay, back to our functions. For the most part the 3 functions are pretty straightforward and standard. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;semiperimeter()&lt;/code&gt; is only used internally by &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;area()&lt;/code&gt;. Both &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;area()&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;height()&lt;/code&gt; are exposed to end-users with our closure. Should probably note that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;height()&lt;/code&gt; calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;area()&lt;/code&gt; which also calls &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;semiperimeter()&lt;/code&gt;. So, anyway, you can run code like . . .&lt;/p&gt;

&lt;figure class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;language-python&quot; data-lang=&quot;python&quot;&gt;&lt;span class=&quot;n&quot;&gt;tri&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;triangle&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;tri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;height&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;  &lt;span class=&quot;c1&quot;&gt;# ((2, 3.799671038392666), (4, 1.899835519196333), (5, 1.5198684153570663))
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;tri&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;area&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;    &lt;span class=&quot;c1&quot;&gt;# 3.799671038392666&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/figure&gt;

&lt;p&gt;But then there is that &lt;strong&gt;nonlocal&lt;/strong&gt; stuff in &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;area()&lt;/code&gt;. According to area’s signature/definition (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;def area(A=a, B=b, C=c):&lt;/code&gt;) we can call it and replace any combination of our closures sides to get an area using the passed in values. So &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tri.area(3)&lt;/code&gt;, replaceing “a = 2” with “a = 3” will return 6.0. The first thing to note is that if nothing is passed in (&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tri.area()&lt;/code&gt;) it will simply use the a, b, and c values from the closure. So what does &lt;strong&gt;nonlocal&lt;/strong&gt; do? If you comment out the nonlocal related lines (&lt;strong&gt;7 and 8&lt;/strong&gt;) then run . ..&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;tri = triangle(2,4,5)
tri.area(3)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;. . . it seems to run fine. But tri.area(3) returns “3.2113081446662823” when the area of a 3, 4, 5 triangle should be 6.0!!! What?!? The problem here is that &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;area()&lt;/code&gt; relies on &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;semiperimeter()&lt;/code&gt;. Without &lt;strong&gt;nonlocal&lt;/strong&gt; our area fucntion is using a = 3, b = 4, and c = 5, but semiperimeter is still using a = 2 from the original closure. So &lt;strong&gt;line 7&lt;/strong&gt; tells area that we need to (possibly) change the enclosure’s copies of a, b, and c; &lt;strong&gt;line 8&lt;/strong&gt; then updates those variables temporarily so that our call to semiperimeter will use a = 2. So, with the nonlocal lines in place, ‘tri.area(3)’ will correctly return 6.0.&lt;/p&gt;

&lt;p&gt;The &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;height()&lt;/code&gt; function however is never affected by the nonlocal changes. A call like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tri.area(3)&lt;/code&gt; is only a temporary change, once it returns its results the original enclosure keeps “a = 2”.&lt;/p&gt;

&lt;p&gt;You may be saying “What’s the difference between &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nonlocal&lt;/code&gt; and &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;global&lt;/code&gt;?”. Good question. In this case our a, b, and c are not global variables–they are enclosed within our closure. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;nonlocal&lt;/code&gt; looks for variables in the immediate enclosing scope, but &lt;strong&gt;will not&lt;/strong&gt; go as far as global scope. (Essentially if the immediate enclosing scope is global, nonlocal will not go there.)&lt;/p&gt;

&lt;p&gt;If you’re totally new to Python then A) you probably shouldn’t be reading my mad and meandering ravings, but more importantly B) you should probably really get accustomed to going to PEPs for the inside-skinny on Python language feature. A PEP (Python Enhancement Proposal) is essentially a concise clear design document for a Python language feature or change. They are generally very well written and extremely clear on 1) what the problem/issue is, 2) how the issue relates to the over all Python language universe, 3) a break down of what the proposed solutions to the issue are and what the advantages/disadvantages of a solution entail, and finally 4) what the proposed solution is/entails. Anyway, for nonlocal &lt;a href=&quot;https://www.python.org/dev/peps/pep-3104/&quot;&gt;PEP 3104&lt;/a&gt; is a great read not only for nonlocal but also as a pretty accessible primer on scoping issues for Python and how they compare/relate to other languages.&lt;/p&gt;

&lt;p&gt;Now, back to triangle()–&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;height()&lt;/code&gt; could be altered in a similar way as &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;area()&lt;/code&gt; so you could “fudge” side lengths to find some required height.&lt;/p&gt;

&lt;p&gt;In hindsight this may have been a crud example and I should’ve stuck with circles and points.&lt;/p&gt;

&lt;h2 id=&quot;where-to-go-from-here&quot;&gt;Where to go from here&lt;/h2&gt;
&lt;p&gt;Please do not use closures as a hammer and everything is a nail. (The triangle example is a good example.) But some ideas to play around with . . .&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Implement a data structure like a linked list with your nodes as closures&lt;/li&gt;
  &lt;li&gt;Do some geometry crud with points as closures&lt;/li&gt;
  &lt;li&gt;Populate a list with different versions of the same enclosure and do something with it.&lt;/li&gt;
  &lt;li&gt;Implement a deck of cards as a closure with shuffle() resetting and randomizing the deck and getcard() returning the next card. Potentially allow your closure to use multiple decks (e.g. a 6 deck shoe).&lt;/li&gt;
  &lt;li&gt;Go outside and hit some golf balls or whatever you do to take your mind off of things. Quite often the best way to learn is to not learn. (Seriously, you’ll do your best thinking when you aren’t thinking.)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Like any other aspect of learning the idioms and intricacies of a specific language becoming familiar with them, playing with them, and being able to read/write them is the actual “hard” part of learning a language. You will come across closures in other people’s code and learning when to use them or go a different direction is an important step in writing good code. Python, by design, lets you do quite a bit with a few lines of code. It is very often referred to as a “multi-paradigm” language–you can accomplish the same goals writing objected-oriented classes or in a more purely functional (or even imperative) manner. And that is the beauty of Python–once you’ve added closures, how to make your objects iterable, or how to put a set of functions into a module you’ll begin to see how all of these tools fit into a work belt and start to design software rather than write lines of code.&lt;/p&gt;

&lt;p&gt;After that soapbox-ish last paragraph I’m going to admit that I’m still much more of an “end result” programmer than a “design” programmer. I’ll write lines of code and functions to solve a problem. I’ll look back at my creation and say “This is goo…d?–this should probably be an object in a class, this should probably be a separate set of functions in a module, can I drop this function with a simple list comprehension?, this is not my beautiful house . . .”&lt;/p&gt;

&lt;h2 id=&quot;links-and-inspirations&quot;&gt;Links and Inspirations&lt;/h2&gt;
&lt;p&gt;I in no way endorse, collude with, or vouch for the moral integrity of any of the writers for the links below. But I found at least parts of these pages interesting and useful. Googling is 80% of the battle. Somewhat JS Module heavy but closures seem to be almost endemic in JS and python/python closures are probably a gateway drug to JS closures.&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.protechtraining.com/bookshelf/python_fundamentals_tutorial/functional_programming?ncr=1&quot;&gt;Python Fundamentals&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://stackoverflow.com/questions/19287485/using-a-closure-with-multiple-functions-without-making-variable-global-in-python&quot;&gt;Python Closure–Multiple Functions&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.programiz.com/python-programming/closure&quot;&gt;Python–Programiz&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Closure_(computer_programming)&quot;&gt;Wikipedia&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://simple.wikipedia.org/wiki/Closure_(computer_science)&quot;&gt;Wikipedia–simple&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://toddmotto.com/mastering-the-module-pattern/&quot;&gt;JS Module Pattern&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://yuiblog.com/blog/2007/06/12/module-pattern/&quot;&gt;JS Module Pattern II&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;http://www.adequatelygood.com/JavaScript-Module-Pattern-In-Depth.html&quot;&gt;JS Module Pattern III&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;&lt;a href=&quot;https://www.joezimjs.com/javascript/javascript-closures-and-the-module-pattern/&quot;&gt;JS What is a Closure&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</content><author><name>Jack Briody</name><email>jackbriody@gmail.com</email><uri>/</uri></author><category term="python" /><category term="Closures" /><category term="python" /><category term="Closures" /><summary type="html">Python Closures I’m not going to say closures are simple–if you come from an object oriented background or are starting off with no background and learning a functional language they can be a bit of a mind-bender. Once you get past the world of syntax (the “ifs”, “whiles”, and “fors”) you essentially enter the world of “how do I put all that together?”. For languages with functions as “first-class objects” (FCOs) you’ll probably encounter closures or closure like constructs. You could probably get away with writing a ton of code in python without ever using a closure–but they can be a great tool to make your code simpler, more useful, and easier to extend and maintain. I don’t think you’d survive reading or writing Java Script without closures. I’m going to, however, focus on python. \[\begin{align*} &amp;amp; \phi(x,y) = \phi \left(\sum_{i=1}^n x_ie_i, \sum_{j=1}^n y_je_j \right) = \sum_{i=1}^n \sum_{j=1}^n x_i y_j \phi(e_i, e_j) = \\ &amp;amp; (x_1, \ldots, x_n) \left( \begin{array}{ccc} \phi(e_1, e_1) &amp;amp; \cdots &amp;amp; \phi(e_1, e_n) \\ \vdots &amp;amp; \ddots &amp;amp; \vdots \\ \phi(e_n, e_1) &amp;amp; \cdots &amp;amp; \phi(e_n, e_n) \end{array} \right) \left( \begin{array}{c} y_1 \\ \vdots \\ y_n \end{array} \right) \end{align*}\] Wikipedia != ELI5 Wikipedia starts their article on closures with . . . In programming languages, closures (also lexical closures or function closures) are techniques for implementing lexically scoped name binding in languages with first-class functions. Operationally, a closure is a record storing a function[a] together with an environment:[1] a mapping associating each free variable of the function (variables that are used locally, but defined in an enclosing scope) with the value or reference to which the name was bound when the closure was created.[b] A closure—unlike a plain function—allows the function to access those captured variables through the closure’s copies of their values or references, even when the function is invoked outside their scope. . . . which is pretty dense. Dense in the sense it is accurate, complete, and correct; not in the “idiot” sense. It is, however, the R-rated version; I’ll try to stick to the PG or PG-13 version. To the Code Before parsing that and going into a bit of detail on the “Why?”/”When?” of closures I’m going to get to the code. I’ll be using FizzBuzz for the first example–in many other places I’ve seen simple “adders” used for example closures and as far as examples go they seem limited. So the basic fizzbuzz I’ll use is: def fizzbuzz(x): return &quot;Fizz&quot; * (x % 3 == 0) + &quot;Buzz&quot; * (x % 5 == 0) or x print([fizzbuzz(x) for x in range(0,20)]) If you’re wondering how that works the two expressions (x % 3 == 0) and (x % 5 == 0) return either True or False. True evaluates to 1 and False to zero. So when x is 6 you end up with &quot;Fizz&quot; * 1 + &quot;Buzz&quot; * 0 or x then &quot;Fizz&quot; or x. In Python logic the following (plus some others) evaluate to False: 0 (zero) None False any of “”, (), {}, or []. See: Notes about Booleans In &quot;Fizz&quot; or x “Fizz” is taken as true so “x” is ignored. For something like x = 7 you end up with &quot;&quot; or x–which is False or x resulting in “x”. That, however, is not a closure–it is simply the function we’ll be returning from our closure. The actual closure wraps around the function like . . . With that code you can then run stuff like: fb35 = fbclosure() fb27 = fbclosure(2, 7) print([fbclosure()(x) for x in range(0,20)]) print([fb35(x) for x in range(0,20)]) print([fb27(x) for x in range(0,20)]) print(fb35(224)) print(fb27(224)) In this case the closure itself is created on lines 1 and 5: def fbclosure and return fizzbuzz. I’ve also added the fizz and buzz parameters defaulting to 3 and 5 to make our fizzbuzz a little more versatile. Going back to the Wikipedia definition–the fizz and buzz variables are “lexically scoped names”. Our “function” is fizzbuzz, our environment includes ‘fizz’ and ‘buzz’–they are the “captured” items. At this point the Wikipedia definition is starting to take form. Our closure is returning a function, fizzbuzz–although we rename it with lines like fb35 = fbclosure() or fb27 = fbclosure(2, 7). Our renamed functions store the information/environment of the enclosing closure (“lexically scope”). For fb35() that is fizz = 3 and buzz = 5; for fb27() we have fizz = 2 and buzz = 7. As far as using it–you can call the closure directly as in the first print statement (fbclosure()(x)), but that probably isn’t typical. The real magic and power of functional languages is assigning the enclosed function to a variable which is actually a function. So, why Closures? For the most part closures provide two things . . . Act as mini-classes/objects Conveniently hide data (i.e. provides a “private” scope) On Item 1–a closure provides some sort of functionality and each instance of a closure maintains its own state. From wikipedia this is the “lecixally scoped binding”. Item 2 builds on Item 1. Java Script and Python are pretty liberal with their scoping. In either language you can trample on or redefine a lot of things–either on purpose or by mistake. The more object oriented/class based languages tend to be defensive (private-ish) by default–functional languages are quite a bit more liberal. That said, though, while the defensive programming aspect can be important the real power of closures is in Item 1. In languages with functions as first class objects encapsulating and reusing important functions saves a lot of work, lines of code, and provides consistency. For a smaller application you avoid repeating code with functions. When an application grows you will typically start collecting code into modules or classes. Closures provide something of a middle ground. Rather than having to continually call a function with a set of parameters or instantiate a class–common bits of code can be centralized so they are easily re-used throughout an application or by multiple developers. Another Example: Multiple Functions This may be quite a bit less common, but a closure in python can return multiple functions. Fizzbuzz isn’t a particularly good example for this, but something like simple geometry does fit the bill. Iconoclast that I am I’m going to use triangles for this example rather than the more typical points and circles that seem to be the rage. In this case def triangle(a, b, c) is our enclosure. Then it gets a little funky. At the bottome Lines 18-24 we define an empty function T() then assign two of our functions to it T.area = area and T.height = height. We then complete our closure by simply returning T. If you come from an object oriented background this is very much like simply creating a minimalistic “object”. t345 = triangle(3, 4, 5) will create an object with two functions, area() and height(). Our third function, semiperimeter(), remains hidden, essentially a private function used only internally. NOTE: This code will only work in Python 3–Python 2 does not have the “nonlocal” feature. Okay, back to our functions. For the most part the 3 functions are pretty straightforward and standard. semiperimeter() is only used internally by area(). Both area() and height() are exposed to end-users with our closure. Should probably note that height() calls area() which also calls semiperimeter(). So, anyway, you can run code like . . . tri = triangle(2,4,5) tri.height() # ((2, 3.799671038392666), (4, 1.899835519196333), (5, 1.5198684153570663)) tri.area() # 3.799671038392666 But then there is that nonlocal stuff in area(). According to area’s signature/definition (def area(A=a, B=b, C=c):) we can call it and replace any combination of our closures sides to get an area using the passed in values. So tri.area(3), replaceing “a = 2” with “a = 3” will return 6.0. The first thing to note is that if nothing is passed in (tri.area()) it will simply use the a, b, and c values from the closure. So what does nonlocal do? If you comment out the nonlocal related lines (7 and 8) then run . .. tri = triangle(2,4,5) tri.area(3) . . . it seems to run fine. But tri.area(3) returns “3.2113081446662823” when the area of a 3, 4, 5 triangle should be 6.0!!! What?!? The problem here is that area() relies on semiperimeter(). Without nonlocal our area fucntion is using a = 3, b = 4, and c = 5, but semiperimeter is still using a = 2 from the original closure. So line 7 tells area that we need to (possibly) change the enclosure’s copies of a, b, and c; line 8 then updates those variables temporarily so that our call to semiperimeter will use a = 2. So, with the nonlocal lines in place, ‘tri.area(3)’ will correctly return 6.0. The height() function however is never affected by the nonlocal changes. A call like tri.area(3) is only a temporary change, once it returns its results the original enclosure keeps “a = 2”. You may be saying “What’s the difference between nonlocal and global?”. Good question. In this case our a, b, and c are not global variables–they are enclosed within our closure. nonlocal looks for variables in the immediate enclosing scope, but will not go as far as global scope. (Essentially if the immediate enclosing scope is global, nonlocal will not go there.) If you’re totally new to Python then A) you probably shouldn’t be reading my mad and meandering ravings, but more importantly B) you should probably really get accustomed to going to PEPs for the inside-skinny on Python language feature. A PEP (Python Enhancement Proposal) is essentially a concise clear design document for a Python language feature or change. They are generally very well written and extremely clear on 1) what the problem/issue is, 2) how the issue relates to the over all Python language universe, 3) a break down of what the proposed solutions to the issue are and what the advantages/disadvantages of a solution entail, and finally 4) what the proposed solution is/entails. Anyway, for nonlocal PEP 3104 is a great read not only for nonlocal but also as a pretty accessible primer on scoping issues for Python and how they compare/relate to other languages. Now, back to triangle()–height() could be altered in a similar way as area() so you could “fudge” side lengths to find some required height. In hindsight this may have been a crud example and I should’ve stuck with circles and points. Where to go from here Please do not use closures as a hammer and everything is a nail. (The triangle example is a good example.) But some ideas to play around with . . . Implement a data structure like a linked list with your nodes as closures Do some geometry crud with points as closures Populate a list with different versions of the same enclosure and do something with it. Implement a deck of cards as a closure with shuffle() resetting and randomizing the deck and getcard() returning the next card. Potentially allow your closure to use multiple decks (e.g. a 6 deck shoe). Go outside and hit some golf balls or whatever you do to take your mind off of things. Quite often the best way to learn is to not learn. (Seriously, you’ll do your best thinking when you aren’t thinking.) Like any other aspect of learning the idioms and intricacies of a specific language becoming familiar with them, playing with them, and being able to read/write them is the actual “hard” part of learning a language. You will come across closures in other people’s code and learning when to use them or go a different direction is an important step in writing good code. Python, by design, lets you do quite a bit with a few lines of code. It is very often referred to as a “multi-paradigm” language–you can accomplish the same goals writing objected-oriented classes or in a more purely functional (or even imperative) manner. And that is the beauty of Python–once you’ve added closures, how to make your objects iterable, or how to put a set of functions into a module you’ll begin to see how all of these tools fit into a work belt and start to design software rather than write lines of code. After that soapbox-ish last paragraph I’m going to admit that I’m still much more of an “end result” programmer than a “design” programmer. I’ll write lines of code and functions to solve a problem. I’ll look back at my creation and say “This is goo…d?–this should probably be an object in a class, this should probably be a separate set of functions in a module, can I drop this function with a simple list comprehension?, this is not my beautiful house . . .” Links and Inspirations I in no way endorse, collude with, or vouch for the moral integrity of any of the writers for the links below. But I found at least parts of these pages interesting and useful. Googling is 80% of the battle. Somewhat JS Module heavy but closures seem to be almost endemic in JS and python/python closures are probably a gateway drug to JS closures. Python Fundamentals Python Closure–Multiple Functions Python–Programiz Wikipedia Wikipedia–simple JS Module Pattern JS Module Pattern II JS Module Pattern III JS What is a Closure</summary></entry><entry><title type="html">PLCAS: Python List Comprehension Anxiety Syndrome</title><link href="https://blog.evilbyte.com/python/listcomprehension/plcas/" rel="alternate" type="text/html" title="PLCAS: Python List Comprehension Anxiety Syndrome" /><published>2017-08-01T00:00:00+00:00</published><updated>2017-08-01T00:00:00+00:00</updated><id>https://blog.evilbyte.com/python/listcomprehension/plcas</id><content type="html" xml:base="https://blog.evilbyte.com/python/listcomprehension/plcas/">&lt;h2 id=&quot;plcas-python-list-comprehension-anxiety-syndrome&quot;&gt;PLCAS: Python List Comprehension Anxiety Syndrome&lt;/h2&gt;
&lt;p&gt;PLCAS &lt;strong&gt;is&lt;/strong&gt; a real disease. The latest DSM (Diagnostic and Statistical Manual of Mental Disorders) was finalized before PLCAS was discovered as a full-blown mental issue. But it should be covered in the next edition.&lt;/p&gt;

&lt;p&gt;However, if you think you may suffer from PLCAS you are not alone. There are ways to live with this potentially debilitating disease and most importantly, ways to a cure.&lt;/p&gt;

&lt;h2 id=&quot;tldwr--too-long-dont-want-to-read&quot;&gt;TL;DWR – Too Long; Don’t Want to Read&lt;/h2&gt;
&lt;p&gt;Trey Hunter’s &lt;a href=&quot;http://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/&quot;&gt;Python List comprehensions now in color&lt;/a&gt; will turn you into a LC pro. Or at least get you on the path to building PLCs (Python List Comprehensions) when you are reviewing your own code.&lt;/p&gt;

&lt;p&gt;My personal suggestion–don’t worry about an LC when writing your code if you’re happier doing a quick for loop.&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;define your list, e.g. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;mylist = []&lt;/code&gt;&lt;/li&gt;
  &lt;li&gt;create a for loop&lt;/li&gt;
  &lt;li&gt;once the code is working go back and look for all of your &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xxxlist = []&lt;/code&gt; and use Hunter’s info to change them.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Do that a couple of times and you’ll quickly start to stomp out list comprehensions. The reality is they’re just a tool–none of us are going to become the Pablo Picasso of list comprehensions. Being able to write and read them is a great skill. Anyway, Pablo Picasso was a bit of an asshole.&lt;/p&gt;

&lt;p&gt;Now back to my meandering BS.&lt;/p&gt;

&lt;h3 id=&quot;do-i-have-plcas&quot;&gt;Do I have PLCAS?&lt;/h3&gt;
&lt;p&gt;Common symptoms of PLCAS include:&lt;/p&gt;
&lt;ol&gt;
  &lt;li&gt;Spending 3 hours trying to write a list comprehension when you could’ve written the code in a “for” loop in 20 minutes&lt;/li&gt;
  &lt;li&gt;A constant nagging suspicion that people will dismiss your code because you don’t have a list comprehension at least once for every 30 lines of code&lt;/li&gt;
  &lt;li&gt;Ignoring your loved ones so that you can read every Stack Overflow question that mentions list comprehensions&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;first-steps-to-a-cure&quot;&gt;First Steps to a Cure&lt;/h3&gt;
&lt;p&gt;Again, remember, you are not alone. That said–the first real step is realising you don’t need to use list comprehensions. One of the hallmarks of PLCAS is the belief that you are being judged for not using them. This is not true. A readable nested for loop is far better than an obtuse list comprehension. [However, once you are comfortable with them list comprehensions will not only make your code easier to read but will help immensely when looking at other people’s code.]&lt;/p&gt;

&lt;p&gt;Write the code that makes the most sense to you. A program that is correct is far superior to anything that is “clever” but hard to follow.&lt;/p&gt;

&lt;h3 id=&quot;available-drugs&quot;&gt;Available Drugs&lt;/h3&gt;
&lt;p&gt;Trey Hunter has the cure. He beautifully explains and breaks down how to create a list comprehension from a for loop. If list comprehensions aren’t coming to you naturally write your code in for loops. Then go back and look for short ones that can be “condensed”. You will pretty quickly learn how to do a list comprehension. And just as importantly how to grok what is going on in ones in other peoples code.&lt;/p&gt;

&lt;p&gt;Even after you’re comfortable with list comprehensions and you need to write soemthing that you know is probably doable with an LC–but it just isn’t coming to you. Write the “for” loop and move on. “for” loops have existed since the first programming language crawled from the primordial assembly language muck. They are the “learning to walk” of programming. LCs are like learning to ride a bike, drive a car, or swim.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;http://treyhunner.com/2015/12/python-list-comprehensions-now-in-color/&quot;&gt;PLCAS Cure&lt;/a&gt;&lt;/p&gt;

&lt;h2 id=&quot;a-personal-example&quot;&gt;A personal example&lt;/h2&gt;
&lt;p&gt;This may be a bit of an obtuse example because it somewhat depends on knowing a bit about what the over all program was doing. But for my own code to get things done I wrote . . .&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;result = []

for x in final_result:
    temp_item = (sorted(list(x.termset)))
    result.append(&quot;&quot;.join(temp_item))
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;. . . based on Trey Hunter’s help I changed it to . . .&lt;/p&gt;
&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;result = [&quot;&quot;.join(sorted(temp_item.termset)) for temp_item in final_result]
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;And this is a great example of why you shouldn’t care. It is possible in this case the list comprehension would be faster–but speed isn’t really a concern here. So it comes down to which is quicker for you to write. If you’re new to Python it may not be obvious or easy to see how to do a list comprehension using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&quot;&quot;.join(zed)&lt;/code&gt;. But getting it down in your code as a simple for loop should make it clearer to figure out how a comprehension can be composed.&lt;/p&gt;

&lt;p&gt;After a few go-rounds with convertering for loops to LCs the relationship between for loops and list comprehensions should start to become second nature. Writing code you’ll start to skip the for loop part and just do a LC. Reading code you’ll (likely) find list comprehensions make things alot easier to read.&lt;/p&gt;

&lt;h3 id=&quot;final-notes&quot;&gt;Final Notes&lt;/h3&gt;
&lt;p&gt;Always write code that makes sense to you. Obviously don’t be stupid about it but spending hours or days looking for an elegant solution is almost always wasted time if you can spend 25% of that time and get a working piece of code. And that goes for everything–not just list comprehensions. You’ll be able to get your code up and running faster and, I hope, you’ll also know the areas you want to go back to to improve.&lt;/p&gt;

&lt;p&gt;As Donald Knuth said “premature optimization is the root of all evil”. Refactoring &lt;strong&gt;is&lt;/strong&gt; how applications and systems are written today. Finding ways to optimize your code after you’ve gotten stuff working is truly a great way to learn more about coding and software development. Worrying about or agonizing over the “perfect solution” whether it is a list comprehension or anything else is usually wasted time–at least until you have “a” solution.&lt;/p&gt;

&lt;p&gt;In short “keep coding and carry on”.&lt;/p&gt;</content><author><name>Jack Briody</name><email>jackbriody@gmail.com</email><uri>/</uri></author><category term="python" /><category term="ListComprehension" /><category term="python" /><category term="ListComprehension" /><summary type="html">PLCAS: Python List Comprehension Anxiety Syndrome PLCAS is a real disease. The latest DSM (Diagnostic and Statistical Manual of Mental Disorders) was finalized before PLCAS was discovered as a full-blown mental issue. But it should be covered in the next edition. However, if you think you may suffer from PLCAS you are not alone. There are ways to live with this potentially debilitating disease and most importantly, ways to a cure. TL;DWR – Too Long; Don’t Want to Read Trey Hunter’s Python List comprehensions now in color will turn you into a LC pro. Or at least get you on the path to building PLCs (Python List Comprehensions) when you are reviewing your own code. My personal suggestion–don’t worry about an LC when writing your code if you’re happier doing a quick for loop. define your list, e.g. mylist = [] create a for loop once the code is working go back and look for all of your xxxlist = [] and use Hunter’s info to change them. Do that a couple of times and you’ll quickly start to stomp out list comprehensions. The reality is they’re just a tool–none of us are going to become the Pablo Picasso of list comprehensions. Being able to write and read them is a great skill. Anyway, Pablo Picasso was a bit of an asshole. Now back to my meandering BS. Do I have PLCAS? Common symptoms of PLCAS include: Spending 3 hours trying to write a list comprehension when you could’ve written the code in a “for” loop in 20 minutes A constant nagging suspicion that people will dismiss your code because you don’t have a list comprehension at least once for every 30 lines of code Ignoring your loved ones so that you can read every Stack Overflow question that mentions list comprehensions First Steps to a Cure Again, remember, you are not alone. That said–the first real step is realising you don’t need to use list comprehensions. One of the hallmarks of PLCAS is the belief that you are being judged for not using them. This is not true. A readable nested for loop is far better than an obtuse list comprehension. [However, once you are comfortable with them list comprehensions will not only make your code easier to read but will help immensely when looking at other people’s code.] Write the code that makes the most sense to you. A program that is correct is far superior to anything that is “clever” but hard to follow. Available Drugs Trey Hunter has the cure. He beautifully explains and breaks down how to create a list comprehension from a for loop. If list comprehensions aren’t coming to you naturally write your code in for loops. Then go back and look for short ones that can be “condensed”. You will pretty quickly learn how to do a list comprehension. And just as importantly how to grok what is going on in ones in other peoples code. Even after you’re comfortable with list comprehensions and you need to write soemthing that you know is probably doable with an LC–but it just isn’t coming to you. Write the “for” loop and move on. “for” loops have existed since the first programming language crawled from the primordial assembly language muck. They are the “learning to walk” of programming. LCs are like learning to ride a bike, drive a car, or swim. PLCAS Cure A personal example This may be a bit of an obtuse example because it somewhat depends on knowing a bit about what the over all program was doing. But for my own code to get things done I wrote . . . result = [] for x in final_result: temp_item = (sorted(list(x.termset))) result.append(&quot;&quot;.join(temp_item)) . . . based on Trey Hunter’s help I changed it to . . . result = [&quot;&quot;.join(sorted(temp_item.termset)) for temp_item in final_result] And this is a great example of why you shouldn’t care. It is possible in this case the list comprehension would be faster–but speed isn’t really a concern here. So it comes down to which is quicker for you to write. If you’re new to Python it may not be obvious or easy to see how to do a list comprehension using &quot;&quot;.join(zed). But getting it down in your code as a simple for loop should make it clearer to figure out how a comprehension can be composed. After a few go-rounds with convertering for loops to LCs the relationship between for loops and list comprehensions should start to become second nature. Writing code you’ll start to skip the for loop part and just do a LC. Reading code you’ll (likely) find list comprehensions make things alot easier to read. Final Notes Always write code that makes sense to you. Obviously don’t be stupid about it but spending hours or days looking for an elegant solution is almost always wasted time if you can spend 25% of that time and get a working piece of code. And that goes for everything–not just list comprehensions. You’ll be able to get your code up and running faster and, I hope, you’ll also know the areas you want to go back to to improve. As Donald Knuth said “premature optimization is the root of all evil”. Refactoring is how applications and systems are written today. Finding ways to optimize your code after you’ve gotten stuff working is truly a great way to learn more about coding and software development. Worrying about or agonizing over the “perfect solution” whether it is a list comprehension or anything else is usually wasted time–at least until you have “a” solution. In short “keep coding and carry on”.</summary></entry><entry><title type="html">CDNF Part I: Canonical Fodder</title><link href="https://blog.evilbyte.com/python/namedtuple/boolean_algebra/logic/tuples-boolean/" rel="alternate" type="text/html" title="CDNF Part I: Canonical Fodder" /><published>2017-08-01T00:00:00+00:00</published><updated>2017-08-01T00:00:00+00:00</updated><id>https://blog.evilbyte.com/python/namedtuple/boolean_algebra/logic/tuples-boolean</id><content type="html" xml:base="https://blog.evilbyte.com/python/namedtuple/boolean_algebra/logic/tuples-boolean/">&lt;h1 id=&quot;canonical-normal-forms-and-minimization&quot;&gt;Canonical Normal Forms and Minimization&lt;/h1&gt;
&lt;p&gt;Quite a while ago I read parts of Randy Hyde’s &lt;strong&gt;The Art of Assembly Language Programming (AoA)&lt;/strong&gt;. The section on boolean logic and Canonical Normal forms intrigued me for some reason. The whole book is available online and the specific section is &lt;a href=&quot;http://www.plantation-productions.com/Webster/www.artofasm.com/Linux/PDFs/DigitalDesign.pdf&quot;&gt;3.4 Canonical Forms&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;More recently I’ve been messing around with functions that take numbers and output “something”–things like &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;fizzbuzz(6)&lt;/code&gt; or &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;isprime(17)&lt;/code&gt;. Canonical Normal Forms came to mind and that also led to Quine-McCluskey reduction (more on Q-M later). So I wrote something up in Python.&lt;/p&gt;

&lt;p&gt;In case you’re wondering–the title is meant to be a reference to Camper Van Beethoven 1985 smash hit “Take the Skinheads Bowling”. And I used a named tuple for doing quite a bit of the work in &lt;a href=&quot;https://github.com/jmbriody/bookofnumbers/blob/master/bookofnumbers/cdnf.py&quot;&gt;cdnf.py&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;canonical-disjunctive-normal-forms-explained&quot;&gt;Canonical (Disjunctive) Normal Forms explained&lt;/h2&gt;
&lt;p&gt;So the basic gist of CDNFs is that for any set of inputs (e.g. A, B, and C) all the logical functions of those inputs can be written with “minterms” that include all 3 inputs. For example “A’BC + AB’C” and “A’BC + AB’C + A’BC’ + A’B’C’” would both be CDNFs with 3 inputs. Each minterm’s input items are ANDed (so ABC is “A AND B AND C”). And the individual minterms are ORd (using “+”). And finally ‘ is used to represent negation, so B’ is “NOT B”. The “OR” portion is where the Disjunctive part comes from; for Conjunctive Normal Forms each input is ORd within a minterm and each minterm is ANDd–but I won’t be dealing with Conjunctive Normal Forms here.&lt;/p&gt;

&lt;p&gt;These normal forms can be ordered based on binary numbers (or converting ints to binary). For CDNF each “1” bit in the binary representation “points” to a minterm. For example the CDNF of 228 is “ABC + ABC’ + AB’C + A’BC’”. In binary 228 is 11100100–so the number of terms in the CDNF corresponds to the number of 1’s in the binary representation. The individual terms are derived from the columns of a truth table.&lt;/p&gt;

&lt;p&gt;So for our example of 228 (where BP is bitposition) . . .&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;BP  |  A  |  B  |  C  | 228 | Term
--------------------------------------
0   |  0  |  0  |  0  |  0  |
1   |  0  |  0  |  1  |  0  |
2   |  0  |  1  |  0  |  1  | A'BC' (010)
3   |  0  |  1  |  1  |  0  |
4   |  1  |  0  |  0  |  0  |
5   |  1  |  0  |  1  |  1  | AB'C  (101)
6   |  1  |  1  |  0  |  1  | ABC'  (110)
7   |  1  |  1  |  1  |  1  | ABC   (111)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;. . . every row where 228 has a 1 is used to create a term. The term itself is created from the A, B, and C columns. If the letter is a zero it is “primed”–meaning NOT B. If the letter is a one it is un-primed.&lt;/p&gt;

&lt;p&gt;For the canonical() function it is important to note that the Bit Position (BP) corresponds to the binary version of the BP. So BP 5 == 101 which is a minterm of AB’C. BP 0 = 000 – A’B’C’.&lt;/p&gt;

&lt;p&gt;Note: Art of Assembly uses “A” as the low order bit–so it is the rightmost letter going 010101.  Others, such as &lt;a href=&quot;http://www.32x8.com/&quot;&gt;Logic Circuit Simplification&lt;/a&gt; do the reverse where the last letter (in this case C) is the low order bit. For my canonical() A is the high order bit as in the table above–for 3 inputs it is 00001111 and C is 01010101. The second argument to canonical() can be used to switch this so A would be 01010101 and C 00001111.&lt;/p&gt;

&lt;h2 id=&quot;the-canonical-code&quot;&gt;The Canonical() code&lt;/h2&gt;
&lt;p&gt;The code itself is actually pretty minimal. There may be a bit of hinky razza-ma-taz going on but it is probably more concise than the explanation above.&lt;/p&gt;

&lt;script src=&quot;https://gist.github.com/28dc5d1e3e8c6016e969185836baf0cf.js&quot;&gt; &lt;/script&gt;

&lt;p&gt;The basic definition is &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canonical(x, highorder_a=True, includef=False)&lt;/code&gt; . . .&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;x – is our int to convert&lt;/li&gt;
  &lt;li&gt;highorder_a – lets us switch between A being the HO and LO bit&lt;/li&gt;
  &lt;li&gt;includef – if True simply adds “F(x) =” in front of the result&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Walking through the code using &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;canonical(228)&lt;/code&gt; gives us . . .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lines 5 and 6&lt;/strong&gt; Convert the int to binary and reverse the order–essentially for a binary number the right-most bit is our BP 0, next is BP 1, etc. So we need to reverse it. For 228–Binary is 11100100, reversed is ‘00100111’.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Lines 8 to 10&lt;/strong&gt; Next we determine how many inputs/letters we need. This part may take a bit of thinking, but essentially the length of the binary version of our input will determine the number of inputs/letters we will need. So for 228 the binary representation is 8 digits. So its length can be represented by a 3 digit binary. This corresponds to the number of columns (A, B, C) needed. We stick with a minimum of 2 letters mostly so the 1st 15 integers provide standard results.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 12&lt;/strong&gt; Here we are just making a list of the binary represenations
of the Bit Positions where a 1 appears. So for 228 we have BPs (using a zero indexed array) at 2, 5, 6, 7. (That is the &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;i in re.finditer('1', binary)&lt;/code&gt; of the list comprehension.) And this is why we reversed the bits on line 6. &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;format(i.start(),  '0' + str(letters) + 'b' )&lt;/code&gt; converts 2, 5, 6, and 7 to a binary format with a zero fill based on the number of letters. For 228 with BPs of 2, 5, 6, 7 we end up with [‘010’, ‘101’, ‘110’, ‘111’]–which are the binary versions of our minterms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 13&lt;/strong&gt; We now take our list from line 12 and feed each item into the &lt;em&gt;minterm&lt;/em&gt;() function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;_minterm_()&lt;/strong&gt;
&lt;strong&gt;Line 21&lt;/strong&gt; Simply creating a list of all the letters. Very much overkill as A-Za-z would allow more inputs/permutations than the universe can contain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 23 and 24&lt;/strong&gt; If we want A as the low order bit we reverse the digits for each of our minterms.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 26 to 29&lt;/strong&gt; Finally, we simply loop over the digits in our term. For each digit we add the next letter–then if the digit is a zero we add ‘ after the letter. So 111 becomes ABC and 110 becomes ABC’.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Line 14&lt;/strong&gt; Back in canonical we convert the new list of minterms to a string with “ + “ between each item.&lt;/p&gt;

&lt;h1 id=&quot;from-canonical-to-a-minimized-version&quot;&gt;From Canonical to a Minimized version&lt;/h1&gt;
&lt;p&gt;So what’s the big deal about a canonical expression? Essentially it represents all of the “terms” from a truth table. From the canonical version it is possible to minimize the expression to make things simpler. This means both electronics and code and other applications don’t have to deal with typically large numbers of circuits or if/else statements.&lt;/p&gt;

&lt;p&gt;And now for an example . . .&lt;/p&gt;

&lt;h2 id=&quot;lets-go-bowling&quot;&gt;Let’s go Bowling!&lt;/h2&gt;
&lt;p&gt;So, Alan likes to bowl. But he never bowls alone. He has three friends that he bowls with: Bob, Clara, and Declan. For the examples I’ll use 4 distinct situations . . .&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation 1&lt;/strong&gt; The base case of Alan never bowling alone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation 2&lt;/strong&gt; Same as 1 but Bob owes Clara money so Bob won’t bowl if Clara is bowling&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation 3&lt;/strong&gt; Same as 2 (Bob still owes Clara) but Bob will bowl with Clara if Declan is also bowling because Declan is a really funny guy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation 4&lt;/strong&gt; Bob has payed Clara–but Alan will only bowl if there is an even number (2 or 4) bowlers because the bowling alley charges a higher rate for 3 bowlers.&lt;/p&gt;

&lt;p&gt;All 4 of these situations are summarized in the table below. Columns A, B, C, and D specify whether someone is available/willing to bowl. The columns 1, 2, 3, and 4 are the 4 situations a 1 means they go bowling–zero means they do not go bowling.&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;ABCD  1 2 3 4  Row #
----  -------  ------
0000  0 0 0 0   0
0001  0 0 0 0   1
0010  0 0 0 0   2
0011  0 0 0 0   3
0100  0 0 0 0   4
0101  0 0 0 0   5
0110  0 0 0 0   6
0111  0 0 0 0   7
1000  0 0 0 0   8
1001  1 1 1 1   9 x
1010  1 1 1 1  10 x
1011  1 1 1 0  11 y
1100  1 1 1 1  12 x
1101  1 1 1 0  13 y
1110  1 0 0 0  14 z
1111  1 0 1 1  15 y2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;ul&gt;
  &lt;li&gt;Rows 0-7: Alan can’t bowl so everything is zero&lt;/li&gt;
  &lt;li&gt;Row 8: Alan can bowl but none of his friends can (zeros). A=1, B,C,D=0&lt;/li&gt;
  &lt;li&gt;Rows 9, 10, 12 (x): Alan can bowl and exactly 1 friend can so all are true&lt;/li&gt;
  &lt;li&gt;Row 11 and 13 (y): S 1, 2, and 3 are true because either Clara or Bob can’t bowl. S4 is false because it is only 3 bowlers.&lt;/li&gt;
  &lt;li&gt;Row 14 (z): S 2 and 3 are false because Bob and Clara won’t bowl together. And S4 is False again because of 3 bowlers&lt;/li&gt;
  &lt;li&gt;Row 16 (y2): Only false for S2 where Bob absolutely will not bowl with Clara.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2 id=&quot;canonical-to-minimized-forms-for-bowling&quot;&gt;Canonical to minimized forms for Bowling&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Situation 1 – 65024&lt;/strong&gt;
There are 7 “True” instances so we’ll have 7 minterms. Starting with row 15 the minterm would be ABCD, next would be ABCD’, etc. We could manually translate these terms–but we can also just use &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int('1111111000000000', 2)&lt;/code&gt; to get 65024 and feed it to canonical(). To get . . .&lt;/p&gt;

&lt;p&gt;ABCD + ABCD’ + ABC’D + ABC’D’ + AB’CD + AB’CD’ + AB’C’D&lt;/p&gt;

&lt;p&gt;. . . which represents all cases where Alan will bowl. Using some premises of logical operations/reductions this can be reduced to . . .&lt;/p&gt;

&lt;p&gt;AB + AC + AD&lt;/p&gt;

&lt;p&gt;This probably seems obvious from the description of Situation 1. If we know Alan will only bowl if at least one of his friends is we aren’t going to come up with all the permutations of the canonical form–we’d simply say Alan will bowl if Bob does (Clara and/or Declan can bowl as well); if Bob isn’t bowling but Clara is Alan will bowl (Declan may or may not); if neither Bob or Clara are bowling but Declan is Alan will bowl.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation 2 – 15872&lt;/strong&gt;
Next up we have the situation where Bob and Clara will not bowl together &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;int('0011111000000000', 2)&lt;/code&gt; or 15872. The canonical form is . . .&lt;/p&gt;

&lt;p&gt;ABC’D + ABC’D’ + AB’CD + AB’CD’ + AB’C’D&lt;/p&gt;

&lt;p&gt;Note the 1st 4 terms. In the 1st 2 Clara isn’t bowling (C’) so Bob can. Terms 3 and 4 Bob isn’t bowling (B’) so Clara can. Fifth term Bob and Clara are probably together having angry sex while Alan and Declan are bowling [more on Bob and Clara later]. The minimized form is:&lt;/p&gt;

&lt;p&gt;ABC’ + AB’C + AC’D&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation 3 – 48640&lt;/strong&gt;
Bob and Clara will only bowl together if Declan is bowling. . . .&lt;/p&gt;

&lt;p&gt;ABCD + ABC’D + ABC’D’ + AB’CD + AB’CD’ + AB’C’D&lt;/p&gt;

&lt;p&gt;This is where things may start to be counter-intuitive. 2nd Term has Declan bowling so both Bob and Clara “can” bowl–but Clara isn’t. This simply means Clara wasn’t going to bowl regardless and it needs to be represented. The minimized version really clears this up . . .&lt;/p&gt;

&lt;p&gt;ABC’ + AB’C + AD&lt;/p&gt;

&lt;p&gt;The final term “AD” essentially allows Bob and Clara to bowl together at that point. The first 2 terms would allow Bob or Clara to bowl with Alan alone or Alan and Declan. Note the first 2 terms here are the same as Situation 2, but the final term removes the C’ restriction.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Situation 4 – 38400&lt;/strong&gt;
This one is a bit funky. We need 2 or 4 bowlers. (Bob and Clara have settled up.) The canonical form is:&lt;/p&gt;

&lt;p&gt;ABCD + ABC’D’ + AB’CD’ + AB’C’D&lt;/p&gt;

&lt;p&gt;and the minmized form is:&lt;/p&gt;

&lt;p&gt;ABCD + ABC’D’ + AB’CD’ + AB’C’D&lt;/p&gt;

&lt;p&gt;So this one can not be minimized at all. Essentially either all 4 play (ABCD) or two of them absolutely can not play.&lt;/p&gt;

&lt;h3 id=&quot;so-whats-up-with-bob-and-clara&quot;&gt;So What’s Up with Bob and Clara&lt;/h3&gt;
&lt;p&gt;In a strange twist of the universe all of these inputs and variables we so non-chalantly throw around actually lead very full lives. Bob and Clara seem to constantly argue and get on each others nerves–but they have been carrying on a rather secret relationship. Declan is almost constantly commenting that they should “get a room”; but Alan has had a thing for Clara for quite a while. Bob and Clara essentially came up with a plan that if they can get Alan and Declan to bowl together more often they’d be tied up at the bowling alley and Bob and Clara could safely have their sordid rendevous.&lt;/p&gt;

&lt;p&gt;Clara has been trying to get her friend Erica to take up bowling in the hopes of distracting Alan. But that all gets a little too complex for what is meant to be a relatively simple example. But seriously–why am I explaining this to you. Can’t you just let Bob and Clara live their lives? Really, the next time you just non-chalantly throw around a B’ or D’ just remember Bob may be out having a great time so he is not available, but he could also be sitting at home playing REM’s “Everybody Hurts” on a constant loop while watching the Powerpuff Girls on netflix just waiting for his phone to ring.&lt;/p&gt;

&lt;p&gt;Oops. Sorry, where was I. Oh . . . Heading into Quine-McCluskey. Which I’ll take up in Part II since this is getting a bit long.&lt;/p&gt;</content><author><name>Jack Briody</name><email>jackbriody@gmail.com</email><uri>/</uri></author><category term="python" /><category term="namedtuple" /><category term="boolean_algebra" /><category term="logic" /><category term="python" /><category term="namedtuple" /><category term="boolean_algebra" /><category term="logic" /><category term="I" /><summary type="html">Canonical Normal Forms and Minimization Quite a while ago I read parts of Randy Hyde’s The Art of Assembly Language Programming (AoA). The section on boolean logic and Canonical Normal forms intrigued me for some reason. The whole book is available online and the specific section is 3.4 Canonical Forms. More recently I’ve been messing around with functions that take numbers and output “something”–things like fizzbuzz(6) or isprime(17). Canonical Normal Forms came to mind and that also led to Quine-McCluskey reduction (more on Q-M later). So I wrote something up in Python. In case you’re wondering–the title is meant to be a reference to Camper Van Beethoven 1985 smash hit “Take the Skinheads Bowling”. And I used a named tuple for doing quite a bit of the work in cdnf.py. Canonical (Disjunctive) Normal Forms explained So the basic gist of CDNFs is that for any set of inputs (e.g. A, B, and C) all the logical functions of those inputs can be written with “minterms” that include all 3 inputs. For example “A’BC + AB’C” and “A’BC + AB’C + A’BC’ + A’B’C’” would both be CDNFs with 3 inputs. Each minterm’s input items are ANDed (so ABC is “A AND B AND C”). And the individual minterms are ORd (using “+”). And finally ‘ is used to represent negation, so B’ is “NOT B”. The “OR” portion is where the Disjunctive part comes from; for Conjunctive Normal Forms each input is ORd within a minterm and each minterm is ANDd–but I won’t be dealing with Conjunctive Normal Forms here. These normal forms can be ordered based on binary numbers (or converting ints to binary). For CDNF each “1” bit in the binary representation “points” to a minterm. For example the CDNF of 228 is “ABC + ABC’ + AB’C + A’BC’”. In binary 228 is 11100100–so the number of terms in the CDNF corresponds to the number of 1’s in the binary representation. The individual terms are derived from the columns of a truth table. So for our example of 228 (where BP is bitposition) . . . BP | A | B | C | 228 | Term -------------------------------------- 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 2 | 0 | 1 | 0 | 1 | A'BC' (010) 3 | 0 | 1 | 1 | 0 | 4 | 1 | 0 | 0 | 0 | 5 | 1 | 0 | 1 | 1 | AB'C (101) 6 | 1 | 1 | 0 | 1 | ABC' (110) 7 | 1 | 1 | 1 | 1 | ABC (111) . . . every row where 228 has a 1 is used to create a term. The term itself is created from the A, B, and C columns. If the letter is a zero it is “primed”–meaning NOT B. If the letter is a one it is un-primed. For the canonical() function it is important to note that the Bit Position (BP) corresponds to the binary version of the BP. So BP 5 == 101 which is a minterm of AB’C. BP 0 = 000 – A’B’C’. Note: Art of Assembly uses “A” as the low order bit–so it is the rightmost letter going 010101. Others, such as Logic Circuit Simplification do the reverse where the last letter (in this case C) is the low order bit. For my canonical() A is the high order bit as in the table above–for 3 inputs it is 00001111 and C is 01010101. The second argument to canonical() can be used to switch this so A would be 01010101 and C 00001111. The Canonical() code The code itself is actually pretty minimal. There may be a bit of hinky razza-ma-taz going on but it is probably more concise than the explanation above. The basic definition is canonical(x, highorder_a=True, includef=False) . . . x – is our int to convert highorder_a – lets us switch between A being the HO and LO bit includef – if True simply adds “F(x) =” in front of the result Walking through the code using canonical(228) gives us . . . Lines 5 and 6 Convert the int to binary and reverse the order–essentially for a binary number the right-most bit is our BP 0, next is BP 1, etc. So we need to reverse it. For 228–Binary is 11100100, reversed is ‘00100111’. Lines 8 to 10 Next we determine how many inputs/letters we need. This part may take a bit of thinking, but essentially the length of the binary version of our input will determine the number of inputs/letters we will need. So for 228 the binary representation is 8 digits. So its length can be represented by a 3 digit binary. This corresponds to the number of columns (A, B, C) needed. We stick with a minimum of 2 letters mostly so the 1st 15 integers provide standard results. Line 12 Here we are just making a list of the binary represenations of the Bit Positions where a 1 appears. So for 228 we have BPs (using a zero indexed array) at 2, 5, 6, 7. (That is the i in re.finditer('1', binary) of the list comprehension.) And this is why we reversed the bits on line 6. format(i.start(), '0' + str(letters) + 'b' ) converts 2, 5, 6, and 7 to a binary format with a zero fill based on the number of letters. For 228 with BPs of 2, 5, 6, 7 we end up with [‘010’, ‘101’, ‘110’, ‘111’]–which are the binary versions of our minterms. Line 13 We now take our list from line 12 and feed each item into the minterm() function. _minterm_() Line 21 Simply creating a list of all the letters. Very much overkill as A-Za-z would allow more inputs/permutations than the universe can contain. Line 23 and 24 If we want A as the low order bit we reverse the digits for each of our minterms. Line 26 to 29 Finally, we simply loop over the digits in our term. For each digit we add the next letter–then if the digit is a zero we add ‘ after the letter. So 111 becomes ABC and 110 becomes ABC’. Line 14 Back in canonical we convert the new list of minterms to a string with “ + “ between each item. From Canonical to a Minimized version So what’s the big deal about a canonical expression? Essentially it represents all of the “terms” from a truth table. From the canonical version it is possible to minimize the expression to make things simpler. This means both electronics and code and other applications don’t have to deal with typically large numbers of circuits or if/else statements. And now for an example . . . Let’s go Bowling! So, Alan likes to bowl. But he never bowls alone. He has three friends that he bowls with: Bob, Clara, and Declan. For the examples I’ll use 4 distinct situations . . . Situation 1 The base case of Alan never bowling alone. Situation 2 Same as 1 but Bob owes Clara money so Bob won’t bowl if Clara is bowling Situation 3 Same as 2 (Bob still owes Clara) but Bob will bowl with Clara if Declan is also bowling because Declan is a really funny guy. Situation 4 Bob has payed Clara–but Alan will only bowl if there is an even number (2 or 4) bowlers because the bowling alley charges a higher rate for 3 bowlers. All 4 of these situations are summarized in the table below. Columns A, B, C, and D specify whether someone is available/willing to bowl. The columns 1, 2, 3, and 4 are the 4 situations a 1 means they go bowling–zero means they do not go bowling. ABCD 1 2 3 4 Row # ---- ------- ------ 0000 0 0 0 0 0 0001 0 0 0 0 1 0010 0 0 0 0 2 0011 0 0 0 0 3 0100 0 0 0 0 4 0101 0 0 0 0 5 0110 0 0 0 0 6 0111 0 0 0 0 7 1000 0 0 0 0 8 1001 1 1 1 1 9 x 1010 1 1 1 1 10 x 1011 1 1 1 0 11 y 1100 1 1 1 1 12 x 1101 1 1 1 0 13 y 1110 1 0 0 0 14 z 1111 1 0 1 1 15 y2 Rows 0-7: Alan can’t bowl so everything is zero Row 8: Alan can bowl but none of his friends can (zeros). A=1, B,C,D=0 Rows 9, 10, 12 (x): Alan can bowl and exactly 1 friend can so all are true Row 11 and 13 (y): S 1, 2, and 3 are true because either Clara or Bob can’t bowl. S4 is false because it is only 3 bowlers. Row 14 (z): S 2 and 3 are false because Bob and Clara won’t bowl together. And S4 is False again because of 3 bowlers Row 16 (y2): Only false for S2 where Bob absolutely will not bowl with Clara. Canonical to minimized forms for Bowling Situation 1 – 65024 There are 7 “True” instances so we’ll have 7 minterms. Starting with row 15 the minterm would be ABCD, next would be ABCD’, etc. We could manually translate these terms–but we can also just use int('1111111000000000', 2) to get 65024 and feed it to canonical(). To get . . . ABCD + ABCD’ + ABC’D + ABC’D’ + AB’CD + AB’CD’ + AB’C’D . . . which represents all cases where Alan will bowl. Using some premises of logical operations/reductions this can be reduced to . . . AB + AC + AD This probably seems obvious from the description of Situation 1. If we know Alan will only bowl if at least one of his friends is we aren’t going to come up with all the permutations of the canonical form–we’d simply say Alan will bowl if Bob does (Clara and/or Declan can bowl as well); if Bob isn’t bowling but Clara is Alan will bowl (Declan may or may not); if neither Bob or Clara are bowling but Declan is Alan will bowl. Situation 2 – 15872 Next up we have the situation where Bob and Clara will not bowl together int('0011111000000000', 2) or 15872. The canonical form is . . . ABC’D + ABC’D’ + AB’CD + AB’CD’ + AB’C’D Note the 1st 4 terms. In the 1st 2 Clara isn’t bowling (C’) so Bob can. Terms 3 and 4 Bob isn’t bowling (B’) so Clara can. Fifth term Bob and Clara are probably together having angry sex while Alan and Declan are bowling [more on Bob and Clara later]. The minimized form is: ABC’ + AB’C + AC’D Situation 3 – 48640 Bob and Clara will only bowl together if Declan is bowling. . . . ABCD + ABC’D + ABC’D’ + AB’CD + AB’CD’ + AB’C’D This is where things may start to be counter-intuitive. 2nd Term has Declan bowling so both Bob and Clara “can” bowl–but Clara isn’t. This simply means Clara wasn’t going to bowl regardless and it needs to be represented. The minimized version really clears this up . . . ABC’ + AB’C + AD The final term “AD” essentially allows Bob and Clara to bowl together at that point. The first 2 terms would allow Bob or Clara to bowl with Alan alone or Alan and Declan. Note the first 2 terms here are the same as Situation 2, but the final term removes the C’ restriction. Situation 4 – 38400 This one is a bit funky. We need 2 or 4 bowlers. (Bob and Clara have settled up.) The canonical form is: ABCD + ABC’D’ + AB’CD’ + AB’C’D and the minmized form is: ABCD + ABC’D’ + AB’CD’ + AB’C’D So this one can not be minimized at all. Essentially either all 4 play (ABCD) or two of them absolutely can not play. So What’s Up with Bob and Clara In a strange twist of the universe all of these inputs and variables we so non-chalantly throw around actually lead very full lives. Bob and Clara seem to constantly argue and get on each others nerves–but they have been carrying on a rather secret relationship. Declan is almost constantly commenting that they should “get a room”; but Alan has had a thing for Clara for quite a while. Bob and Clara essentially came up with a plan that if they can get Alan and Declan to bowl together more often they’d be tied up at the bowling alley and Bob and Clara could safely have their sordid rendevous. Clara has been trying to get her friend Erica to take up bowling in the hopes of distracting Alan. But that all gets a little too complex for what is meant to be a relatively simple example. But seriously–why am I explaining this to you. Can’t you just let Bob and Clara live their lives? Really, the next time you just non-chalantly throw around a B’ or D’ just remember Bob may be out having a great time so he is not available, but he could also be sitting at home playing REM’s “Everybody Hurts” on a constant loop while watching the Powerpuff Girls on netflix just waiting for his phone to ring. Oops. Sorry, where was I. Oh . . . Heading into Quine-McCluskey. Which I’ll take up in Part II since this is getting a bit long.</summary></entry></feed>