<?xml version='1.0' encoding='UTF-8'?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><id>https://adamfontenot.com</id><title>Adam Fontenot</title><updated>2025-09-09T04:46:31.510836+00:00</updated><author><name>Adam Fontenot</name></author><link href="https://adamfontenot.com" rel="alternate" title="Adam Fontenot"/><link href="https://adamfontenot.com" rel="self" title="Adam Fontenot"/><generator uri="https://lkiesow.github.io/python-feedgen" version="1.0.0">python-feedgen</generator><entry><id>https://adamfontenot.com/post/fixing_a_broken_qr_code_in_fez</id><title>Fixing a broken QR code in FEZ</title><updated>2021-01-13T05:20:19+00:00</updated><author><name>Adam Fontenot</name></author><content type="html">&lt;p&gt;I&amp;rsquo;ve been playing &lt;a href="http://www.fezgame.com/"&gt;FEZ&lt;/a&gt; for the first time recently, and came across this room where some NPCs are constructing a QR code on the wall:&lt;/p&gt;
&lt;figure id="__yafg-figure-1"&gt;
&lt;img alt="fez_qr_original.png" src="https://adamfontenot.com/files/fez_qr_original.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This seemed very likely to be a puzzle. It&amp;rsquo;s almost a complete QR (most of what&amp;rsquo;s missing is the top left positioning square), and anything under the scaffolding can be reconstructed, so it stands some chance of scanning after a few fixups.&lt;/p&gt;
&lt;p&gt;I imported the screenshot in &lt;a href="https://www.gimp.org/"&gt;GIMP&lt;/a&gt;. I combined it with another screenshot of the same scene to remove the characters, leaving only the scaffolding. I used the Color Exchange tool (Colors &amp;gt; Map &amp;gt; Color Exchange) to replace the lighter purple color in the background with white. I then used the Threshold tool (Colors &amp;gt; Threshold) to make anything else in the image black. &lt;/p&gt;
&lt;p&gt;Then, in another layer, I created a grid precisely aligned with the blocks in the image to help me see and fill in the missing portions. GIMP has a tool for that (Filters &amp;gt; Render &amp;gt; Pattern &amp;gt; Grid). I replaced the partial blocks hidden by scaffolding with completed blocks. This result looked like this (the grid is at 25% opacity):&lt;/p&gt;
&lt;figure id="__yafg-figure-2"&gt;
&lt;img alt="fez_clean_grid_sm.png" src="https://adamfontenot.com/files/fez_clean_grid_sm.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Unfortunately the result still wouldn&amp;rsquo;t scan, even when I added the third position symbol back to the top left. Too much seemed to be missing, at least for the apps I tried. But since this seemed to be a puzzle that the developers intended you to solve, I assumed there was something more here, so I broke out the QR code spec.&lt;/p&gt;
&lt;p&gt;The first thing to understand about QR codes is that they&amp;rsquo;re relatively simple ways of encoding binary data (ones and zeros) using black (1) and white (0) blocks. Certain parts of the image are only used to align scanners: they don&amp;rsquo;t contain any data. Other parts contain &lt;em&gt;format&lt;/em&gt; information: this tells a scanner what kind of QR code it is and how to decode it. I was hoping this QR code would contain valid format information, as otherwise I&amp;rsquo;d have to test all the possibilities. Here&amp;rsquo;s an illustration of the different parts of a QR code. Image credit: &lt;a href="https://commons.wikimedia.org/wiki/File:QR_Code_Structure_Example_3.svg"&gt;Wikipedia&lt;/a&gt;.&lt;/p&gt;
&lt;figure id="__yafg-figure-3"&gt;
&lt;img alt="An illustration of a QR code" src="https://adamfontenot.com/files/QR_Code_Structure_Example_3.svg"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;From the size (25x25), we know this is a V2 QR code, which doesn&amp;rsquo;t have version information. The first question to answer was whether the QR code still contained valid Format Information. This information is error corrected &lt;em&gt;and&lt;/em&gt; duplicated within the image. It appears in the QR code in the sections highlighted in blue:&lt;/p&gt;
&lt;figure id="__yafg-figure-4"&gt;
&lt;img alt="fez_qr_orig_format_sm.png" src="https://adamfontenot.com/files/fez_qr_orig_format_sm.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;One of the Format Information bit strings seems complete: it combines the top-right portion with the bottom-left portion. I&amp;rsquo;m working with the assumption (that I might need to change later) that anything not in the top left quadrant is &amp;ldquo;finished&amp;rdquo; by the NPCs, meaning it&amp;rsquo;s trustworthy. Hopefully this means I won&amp;rsquo;t need to apply error correction to the Format Information. &lt;/p&gt;
&lt;p&gt;Information in QR codes is &lt;em&gt;masked&lt;/em&gt; (&lt;a href="https://en.wikipedia.org/wiki/Exclusive_or"&gt;XORed&lt;/a&gt; with a known pattern) to break up large chunks of white or black that give scanners difficulty. The Format Information uses the static mask of &amp;ldquo;101010000010010&amp;rdquo;&lt;sup id="fnref:4-1"&gt;&lt;a class="footnote-ref" href="#fn:4-1"&gt;1&lt;/a&gt;&lt;/sup&gt;. Wikipedia incorrectly says this mask is &amp;ldquo;101011001010101&amp;rdquo;.&lt;sup id="fnref:4-2"&gt;&lt;a class="footnote-ref" href="#fn:4-2"&gt;2&lt;/a&gt;&lt;/sup&gt; The data in the QR code will use a &lt;em&gt;different&lt;/em&gt; mask, chosen dynamically by the encoder so as to minimize large white or black chunks.&lt;/p&gt;
&lt;p&gt;Here is where I began to be surprised by how far I could go using only GIMP. I expected to be importing the data into Python relatively quickly, but being able to toggle masks on the QR code directly and observe the result visually turned out to be very handy.&lt;/p&gt;
&lt;p&gt;Using the new &amp;ldquo;Exclusion&amp;rdquo; layer mode, you can selectively invert parts of the underlying image using a black and white layer. Take care to observe the correct bit order when creating the mask (&lt;a href="https://en.wikipedia.org/wiki/Bit_numbering#Most_significant_bit"&gt;MSB&lt;/a&gt;: for the Format Information, that means bottom to top, left to right), as seen here:&lt;/p&gt;
&lt;figure id="__yafg-figure-5"&gt;
&lt;img alt="fez_format_bitorder_sm.png" src="https://adamfontenot.com/files/fez_format_bitorder_sm.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Applying the mask, we see the following:&lt;/p&gt;
&lt;figure id="__yafg-figure-6"&gt;
&lt;img alt="fez_format_bitmask.png" src="https://adamfontenot.com/files/fez_mask_illust.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Note that because of the way the GIMP Exclusion layer works, we have to use white as the &amp;ldquo;on&amp;rdquo; bit for the mask. Black would be more appropriate since that&amp;rsquo;s what the QR standard uses for an &amp;ldquo;on&amp;rdquo; bit.&lt;/p&gt;
&lt;p&gt;According to the spec, the first two bits give the error correction level, and the third through fifth bits give the data mask information. That&amp;rsquo;s once again in MSB order, so bits 14 and 13, and 12, 11, 10, respectively. In this case, we have &amp;ldquo;11&amp;rdquo; for the error correction level, and &amp;ldquo;001&amp;rdquo; for the data mask. That tells us that we have error correction level &amp;ldquo;Q&amp;rdquo; (which we&amp;rsquo;re ignoring for now), and &amp;ldquo;&lt;em&gt;i&lt;/em&gt; mod 2 = 0&amp;rdquo; for the mask (this is one of several options for a mask that is chosen by an encoder). This means that the mask is active (flipping the underlying data bits) for every even number row, starting at 0 for the top row.&lt;/p&gt;
&lt;p&gt;We can quickly create such a mask in GIMP by making a small portion of it (just two blocks, in this case), then expanding that with Filters &amp;gt; Map &amp;gt; Tile. We then trim out any portion that is part of a position symbol, timing symbol, alignment symbol, quiet zone, or Format Information (so that only the data will be masked). The spec (and other references) are pretty clear about what these areas are, so I&amp;rsquo;ll skip to the result here:&lt;/p&gt;
&lt;figure id="__yafg-figure-7"&gt;
&lt;img alt="fez_fullmask.png" src="https://adamfontenot.com/files/fez_fullmask.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;We&amp;rsquo;re now ready to begin trying to read the raw data! The order of bits is detailed in section 8.7 of the spec. We start with the bottom right block, then go left, then go back and up, then left again, reading upwards in a step-wise pattern. When we reach the position symbol at the top we move to the 2 block wide column to the left, and this time step down. (Discovering that QR code data starts with the bottom right and that it ends with error correction data gave me hope that the QR code was decipherable.) The reading order for the intricate sections is given precisely in the spec. I&amp;rsquo;ve indicated the interesting parts below.&lt;/p&gt;
&lt;figure id="__yafg-figure-8"&gt;
&lt;img alt="fez_readingorder_sm.png" src="https://adamfontenot.com/files/fez_readingorder_sm.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Section 8.4 of the spec indicates that the data stream is prefixed by a header, where the first four bits are a &amp;ldquo;mode indicator&amp;rdquo;, followed by a length indicator. If you&amp;rsquo;re just encoding raw binary data in a QR code, you may as well just use 8 bit bytes, which is mode &amp;ldquo;0100&amp;rdquo;. But if you&amp;rsquo;re representing numeric or alphanumeric data, you can encode it more efficiently. The FEZ QR code begins with &amp;ldquo;0010&amp;rdquo;, which indicates an alphanumeric QR code. &lt;/p&gt;
&lt;p&gt;This means that the next 9 bits (for a V2 QR code) are used to give the number of characters in the data. The next 9 bits of our QR code are &amp;ldquo;000010111&amp;rdquo;, which is the number 23 represented in binary. &lt;/p&gt;
&lt;p&gt;For maximum efficiency, alphanumeric mode encodes 45 possible characters into blocks of 2 characters per 11 bits. (45^2 / 2^11 ≈ 98.9%, which is pretty good efficiency). To access the characters, we divide the 11 bit integer by 45. The result without the remainder is the first character, and the remainder is the second character.&lt;sup id="fnref:4-3"&gt;&lt;a class="footnote-ref" href="#fn:4-3"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Each number represents a character according to its position in the following string:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that we have an odd number of characters, so to save every bit possible the last character will be encoded as a single 6 bit value, instead of wasting another 5 bits.&lt;/p&gt;
&lt;p&gt;With all this information in hand, we can transcribe the data from the QR code. Anything after the last piece of data will be part of the error correction bits, which we&amp;rsquo;re hoping we don&amp;rsquo;t need. Now that we know &lt;em&gt;how much&lt;/em&gt; data there is (22 × 11 / 2 + 6 bits), we can also identify each portion of the QR code by its function:&lt;/p&gt;
&lt;figure id="__yafg-figure-9"&gt;
&lt;img alt="fez_functions_sm.png" src="https://adamfontenot.com/files/fez_functions_sm.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Blue represents Format Information (as before). Green represents data. Red represents error correction. Anything purely black or white is a static part of the QR code. &lt;/p&gt;
&lt;p&gt;I have transcribed the data like so:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;10011011100: 27 29 (R T)
11001101111: 36 27 (Space R)
10100111101: 29 36 (T Space)
01111001110: 21 29 (L T)
11001101111: 36 27 (Space R)
10100111101: 29 36 (T Space)
01111001110: 21 29 (L T)
11001101001: 36 21 (Space L)
10100111101: 29 36 (T Space)
01111001110: 21 29 (L T)
11001101111: 36 27 (Space R)
011101: 29 (T)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In other words, the data says &amp;ldquo;RT RT LT RT LT LT LT RT&amp;rdquo;. This is a sequence of inputs you can make with the controller in FEZ. You usually receive a collectable for doing so.&lt;/p&gt;
&lt;p&gt;I also wrote some Python code to do the decoding automatically, which you can find here: &lt;a href="https://adamfontenot.com/files/fez_qr.py"&gt;fez_qr.py&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;With all the information we now have, we can actually generate the final, corrected version of the QR code. To do that, we&amp;rsquo;ll need a library that allows us to set the parameters manually (because an automatic QR code creation tool might choose a different mask or error correction level, giving a completely different result). I used the one &lt;a href="https://www.nayuki.io/page/qr-code-generator-library"&gt;here&lt;/a&gt;. Exporting the resulting SVG and layering the result on the original, we can see that it&amp;rsquo;s nearly a perfect match!&lt;/p&gt;
&lt;figure id="__yafg-figure-10"&gt;
&lt;img alt="fez_qr_final.png" src="https://adamfontenot.com/files/fez_qr_final.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Having finished all this, I reopened FEZ, and entered the input sequence. And &lt;strong&gt;NOTHING HAPPENED&lt;/strong&gt;. I tried multiple times to make sure I wasn&amp;rsquo;t screwing anything up. &lt;/p&gt;
&lt;p&gt;I looked up the puzzle online, and &lt;a href="https://gaming.stackexchange.com/questions/62587/is-this-qr-code-in-fez-useful"&gt;apparently&lt;/a&gt; a completed version of the QR code is in a later stage of the game, and this unfinished one is just for &amp;ldquo;lore&amp;rdquo;. I find this explanation frustrating, because it clearly seems to have been designed so that an enterprising person could reconstruct the code themselves. Everything you need is there. Yet I have to admit&amp;hellip; it doesn&amp;rsquo;t work. You can&amp;rsquo;t enter the code in this room, you have to enter it elsewhere.&lt;/p&gt;
&lt;p&gt;I think this is a mistake on the game designers&amp;rsquo; part. If a puzzle gives you everything you need to solve it, the solution should work. It&amp;rsquo;s rather frustrating to put that much effort into solving a puzzle only to find out that your effort wasn&amp;rsquo;t anticipated, and the puzzle is actually solved &lt;em&gt;for you&lt;/em&gt; later in the game. &lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:4-1"&gt;
&lt;p&gt;According to section 8.9 of the QR code specification. ISO/IEC 18004:2000.&amp;#160;&lt;a class="footnote-backref" href="#fnref:4-1" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4-2"&gt;
&lt;p&gt;As of 11 January 2021. &lt;a href="https://en.wikipedia.org/w/index.php?title=QR_code&amp;amp;oldid=999748921#Encoding"&gt;https://en.wikipedia.org/w/index.php?title=QR_code&amp;amp;oldid=999748921#Encoding&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:4-2" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4-3"&gt;
&lt;p&gt;It&amp;rsquo;s pretty easy to understand how this works. In a base 10 system, you can &amp;ldquo;encode&amp;rdquo; 2 characters out of a set of 10 possible characters in a 2 digit number: &amp;ldquo;12&amp;rdquo; for example. Dividing by 10 and ignoring the remainder gets you back the first character (&amp;ldquo;1&amp;rdquo;), and dividing by 10 and taking the remainder gets you the second character (&amp;ldquo;2&amp;rdquo;). This system works exactly the same, it&amp;rsquo;s just that it&amp;rsquo;s a base 45 system. If we used base 45, you&amp;rsquo;d be able to just read off the two characters after printing the number.&amp;#160;&lt;a class="footnote-backref" href="#fnref:4-3" title="Jump back to footnote 3 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><published>2021-01-13T05:20:19+00:00</published></entry><entry><id>https://adamfontenot.com/post/solving_the_vehicle_routing_problem_for_research_biologists</id><title>Solving the vehicle routing problem for research biologists</title><updated>2021-06-20T15:05:15.494622-07:00</updated><author><name>Adam Fontenot</name></author><content type="html">&lt;p&gt;&lt;a href="https://github.com/afontenot/research-traveling-salesman"&gt;Code accompanying this blog post is available on Github.&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;My partner is a research scientist working in a lab group. Every year, members
of the lab are expected to go on long driving trips to collect specimens for 
research. This labor is shared among the members of the lab (in practice, the 
graduate student members of the lab). So even though one lab member&amp;rsquo;s work 
might only focus on a single species, a research trip might collect many 
different species, even if the lab member who requires them is not present on 
the trip.&lt;/p&gt;
&lt;p&gt;In practice, the planned destinations are known at the beginning of the 
academic quarter. The trips need to be planned out in such a way that effort 
and route duplication is minimized, while each individual only goes on as many 
trips as their workload will bear.&lt;/p&gt;
&lt;p&gt;While not a professional programmer or computer scientist by any means, I&amp;rsquo;ve 
been tinkering with software long enough that coming up with ways to solve 
problems like this is not that hard for me, and so I&amp;rsquo;ve been asked on several 
occasions now to solve the routing problem for them. Having done that, I 
thought I&amp;rsquo;d document my process (using entirely open source software and data) 
and provide a few hacked together scripts to simplify the process.&lt;/p&gt;
&lt;p&gt;The core piece of the puzzle is &lt;a href="http://vroom-project.org/"&gt;Vroom&lt;/a&gt;, a software 
project explicitly designed to solve the vehicle routing problem in an 
efficient way. It&amp;rsquo;s designed to integrate with 
&lt;a href="http://project-osrm.org/"&gt;OSRM&lt;/a&gt;, a routing engine built with OpenStreetMap 
data. If you&amp;rsquo;re curious about the system, I recommend &lt;a href="https://www.youtube.com/watch?v=HHkwYvFMRiI"&gt;this underappreciated 
talk&lt;/a&gt; about Vroom.&lt;/p&gt;
&lt;p&gt;Dealing with constraints can be complicated. Fortunately, I never had to worry 
about blackout dates, because the routes once calculated could begin whenever 
the driver was ready to start. That said, Vroom has one significant limitation: 
the only metric it allows you to optimize is &lt;em&gt;total&lt;/em&gt; drive time. If there is 3 
weeks worth of work to do, and it minimizes total drive time to send one 
vehicle on a &lt;em&gt;very&lt;/em&gt; long trip, &lt;a href="https://github.com/VROOM-Project/vroom/issues/466#issuecomment-794367170"&gt;Vroom will do 
that&lt;/a&gt;.
 You can&amp;rsquo;t tell Vroom to, for example, minimize the length of the &lt;em&gt;longest&lt;/em&gt; 
trip, or to make all trips as close together in length as possible. I thought 
at first that this would make Vroom unusable for my tasks (it certainly doesn&amp;rsquo;t 
seem like it would work for hot pizza delivery, for example), but with a lot of 
handheld tinkering you can get it to work using availability constraints on the 
vehicles. I also didn&amp;rsquo;t find any software that would handle the problem better.&lt;/p&gt;
&lt;p&gt;While OSRM does provide a very generous free API, it&amp;rsquo;s still too limited for 
the kind of routing I needed to do. Vroom works by requesting a routing matrix 
from OSRM that contains the travel times between every &lt;em&gt;pair&lt;/em&gt; of points you 
submit. Sometimes I would be given a thousand or more destinations. This meant 
I had to run a routing server myself. Fortunately I have an always-on server 
that was more than up to the task.&lt;/p&gt;
&lt;p&gt;You can find the routing server at 
&lt;a href="https://github.com/Project-OSRM/osrm-backend"&gt;osrm-backend&lt;/a&gt;. There&amp;rsquo;s a Docker 
image, but as I prefer to run software in different containers I simply built 
it myself. The map data I needed (the US West subregion) is available to 
download from &lt;a href="https://download.geofabrik.de/north-america.html"&gt;Geofabrik&lt;/a&gt;. 
After pre-processing, the data for the western United States is &amp;ldquo;only&amp;rdquo; 14 GB.&lt;/p&gt;
&lt;p&gt;Very clear instructions for setting up the software are available on the 
project&amp;rsquo;s &lt;a href="https://github.com/Project-OSRM/osrm-backend/wiki/Running-OSRM"&gt;wiki 
page&lt;/a&gt; on 
Github. You&amp;rsquo;ll probably want to use the MLD pipeline as the pre-processing step 
is &lt;em&gt;much&lt;/em&gt; faster. The page recommends the CH pipeline specifically for large 
distance matrices (exactly what we have), but as we&amp;rsquo;re only going to submit a 
small number of requests to this server in its lifetime, we can afford for 
them to be slightly slower.&lt;/p&gt;
&lt;p&gt;Vroom has a documented JSON format that describes the available vehicles, the 
destinations they need to reach, and other constraints. I wrote a script to 
create the necessary input for Vroom based on data in a spreadsheet (exported 
to a TSV file).&lt;/p&gt;
&lt;p&gt;The format of this file is &lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Latitude &amp;lt;tab&amp;gt; Longitude &amp;lt;tab&amp;gt; Skills &amp;lt;tab&amp;gt; Time
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Skills is probably the most interesting feature I made available. Sometimes one 
scientist is particular good at identifying one species, and so the plan is to 
make sure they&amp;rsquo;re in the vehicle for every destination that involves collecting 
that species. In the spreadsheet, the scientists can put a number (or several 
numbers separated by a comma) in the corresponding Skills cell. Each vehicle 
has a corresponding list of numbers which identify the scientists in each 
vehicle (a scientist can go on multiple trips and so be in multiple vehicles). 
Vroom will use the skills feature to only send a vehicle to a destination if 
there is a scientist in the vehicle who can identify the specimen there.&lt;/p&gt;
&lt;p&gt;The Time field is occasionally useful too. It&amp;rsquo;s an estimation of how long the 
vehicle will spend at the destination not moving (in seconds). In most cases, 
the scientists I was routing for would simply park the car, clip a branch of a 
tree or shrub, and get back in the car, so this was effectively zero. In some 
cases, however, the GPS point could be a mile or more from the road, meaning 
that a longer hike was in order.&lt;/p&gt;
&lt;p&gt;The scripts that I&amp;rsquo;m making available are very much hacked together, not up to 
my usual standard for code I make public. For example, there are important 
constants in the &lt;code&gt;makejson.py&lt;/code&gt; file that have to be set or adjusted when the 
script is run. I didn&amp;rsquo;t come up with a convenient way to use &lt;code&gt;argparse&lt;/code&gt; for 
this. &lt;/p&gt;
&lt;p&gt;We have the already-discussed list of VEHICLES:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;VEHICLES = [[1,2],[3,4]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This identifies two vehicles / trips, where two scientists (numbered 1 and 2) 
are in the first, while two other scientists (numbered 3 and 4) are in the 
second.&lt;/p&gt;
&lt;p&gt;We have lists of starting coordinates and ending coordinates for each vehicle. 
These are in &lt;code&gt;[longitude, latitude]&lt;/code&gt; format because that&amp;rsquo;s what Vroom itself 
uses.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;STARTCOORDS = [[-118.44, 34.068], [-118.44, 34.068]]
ENDCOORDS = [[-118.44, 34.068], [-118.44, 34.068]]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note how the starting and ending coordinates for the vehicles are all the same 
in this case. That&amp;rsquo;s a common pattern (starting and ending at the university), 
but you&amp;rsquo;re not locked into doing that by any means.&lt;/p&gt;
&lt;p&gt;Last we have TIMEAVAIL:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;TIMEAVAIL = [400000, 400000] # seconds
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is the messiest part. It&amp;rsquo;s the total drive time allowed for each car. In 
&lt;em&gt;theory&lt;/em&gt;, this would be perfectly known to the participants in advance (they 
could allow more time for stops, breaks, sleeping, etc., as needed). In 
practice, though, it&amp;rsquo;s a balancing act. The research trips &lt;em&gt;have&lt;/em&gt; to be done, 
so even if finishing all of them would take more time than the scientists 
theoretically have, put together, that just means that they have to find more 
time from somewhere. So in practice the only workable approach that I found was 
setting this value unreasonably high for each car, then reducing it 
selectively to get routes that looked reasonable. When you can&amp;rsquo;t reduce it 
any more without the router failing, you have a roughly optimal solution.&lt;/p&gt;
&lt;p&gt;Vroom will print a large JSON file with the results (and the routes themselves 
from OSRM in &lt;code&gt;polyline&lt;/code&gt; format), and so I have another script that takes the 
result and prints the result as a long list of coordinates for each vehicle, 
and also exports the routes (as lists of coordinates) to text files, so that 
they can be shown on a map. Theoretically I ought to create KMLs or something 
more useful, but this turned out to be more complicated than I had hoped to do 
in Python and so I ran out of time.&lt;/p&gt;
&lt;p&gt;My repository contains a &lt;code&gt;run.sh&lt;/code&gt; file that shows how I run the program for my
specific setup. Obviously you&amp;rsquo;ll need all the pieces in place (Vroom, OSRM on
a server, etc) in place for this to work. Running the script, even for a large
list of destinations, takes only 3-5 seconds, so re-running it while changing
the parameters is no big deal. &lt;/p&gt;
&lt;p&gt;The results are extremely nice looking:&lt;/p&gt;
&lt;figure id="__yafg-figure-36"&gt;
&lt;img alt="A map showing a solution to the vehicle routing problem in 
California" src="https://adamfontenot.com/files/routing_problem_map.jpg"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Thanks to &lt;a href="https://www.gpsvisualizer.com/"&gt;GPS Visualizer&lt;/a&gt; for making it easy 
to plot these routes. &lt;/p&gt;
&lt;p&gt;This solution shows three different routes, constrained to a limit of about a 
week per trip. Several of the points have additional constraints, such that 
only one scientist can collect the sample, and this has still allowed for a 
nice solution. In many cases, it&amp;rsquo;s possible for human intuition to come up with 
a map that looks a lot like this one, but getting the exact order of 
coordinates right turns out to be crucial in saving hours of drive time.&lt;/p&gt;
&lt;p&gt;At the end of the day, I suspect my exact approach won&amp;rsquo;t be completely workable 
for most people. You&amp;rsquo;ll have slightly different constraints than me, a 
different input format for your data, or some other problem. That&amp;rsquo;s why I&amp;rsquo;m 
just sticking these scripts online instead of spending the time to make sure 
they&amp;rsquo;re as functional and well-written as they could be. Hopefully, others will 
be able to look to them for help in solving their own routing problems.&lt;/p&gt;</content><published>2021-06-20T15:05:15.494622-07:00</published></entry><entry><id>https://adamfontenot.com/post/how_i_defeated_wordle_with_python</id><title>How I defeated Wordle with Python</title><updated>2022-01-11T19:07:12.866986+00:00</updated><author><name>Adam Fontenot</name></author><content type="html">&lt;p&gt;Wordle is a &lt;a href="https://www.powerlanguage.co.uk/wordle/"&gt;simple online word game&lt;/a&gt;.
It is played by similar rules to the classic game 
&lt;a href="https://en.wikipedia.org/wiki/Mastermind_(board_game)"&gt;Mastermind&lt;/a&gt;, which is
known in the United States through the version licensed to Hasbro. Wordle is
played with words instead of arbitrary configurations of pegs or numbers, and
there is one added twist: each of your guesses must &lt;em&gt;also&lt;/em&gt; be a word in
Wordle&amp;rsquo;s dictionary, not any arbitrary configuration of letters.&lt;/p&gt;
&lt;p&gt;For those unfamiliar with Mastermind, I recommend clicking through to try the
game or reading this brief description of the rules: every day, the game
randomly chooses a five letter word from a predetermined list of possible
words. On each of your turns, you will guess a word that must be in a
dictionary contained by the game. If you guess the word within 6 turns, you
win. The game tracks your streak of wins and the number of guesses it takes
you to solve each one.&lt;/p&gt;
&lt;p&gt;For each guess, the game will tell you whether each letter&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;does not exist in the correct answer at all&lt;/li&gt;
&lt;li&gt;exists in the correct answer, but is at the incorrect location&lt;/li&gt;
&lt;li&gt;is the correct letter and is at the correct location&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each letter in the guess has a 1-1 correspondence with the same letter in
the answer, if there is one. This means that if the correct answer is &amp;ldquo;means&amp;rdquo;,
and you guess &amp;ldquo;green&amp;rdquo;, you would see the following:&lt;/p&gt;
&lt;figure id="__yafg-figure-49"&gt;
&lt;img alt="the word &amp;quot;green&amp;quot; with the first &amp;quot;e&amp;quot; and the &amp;quot;n&amp;quot; highlighted in yellow" src="https://adamfontenot.com/files/green.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The yellow color indicates that the chosen letter appears in the solution, but
that it is in the wrong place in our guess. The second &amp;ldquo;e&amp;rdquo; appears in grey,
because there is no second &amp;ldquo;e&amp;rdquo; in the solution. There &lt;em&gt;must&lt;/em&gt; be a 1-1
correspondence between each guessed letter and a solution letter, and if they
were both highlighted in yellow, the &amp;ldquo;e&amp;rdquo; in the solution would correspond to
&lt;em&gt;two&lt;/em&gt; letters in the guess.&lt;/p&gt;
&lt;p&gt;Likewise, a green letter, indicating a letter in the correct location, takes
precedence over a letter in the incorrect location. So if the solution was
&amp;ldquo;rules&amp;rdquo;, we would see the following:&lt;/p&gt;
&lt;figure id="__yafg-figure-50"&gt;
&lt;img alt="the word &amp;quot;green&amp;quot; with the &amp;quot;r&amp;quot; highlighted in yellow, and the second &amp;quot;e&amp;quot; highlighted in green" src="https://adamfontenot.com/files/green2.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Here as before only one &amp;ldquo;e&amp;rdquo; is highlighted, but the second is in green rather
than the first being in yellow, since the letter in a correct location takes
precedence over a letter in an incorrect location.&lt;/p&gt;
&lt;h2&gt;I&amp;rsquo;d like to solve the puzzle&lt;/h2&gt;
&lt;p&gt;When I come across a puzzle like this, I&amp;rsquo;m immediately compelled to think about
optimal ways to play it. For example, the player is in the same knowledge
position at the beginning of the game every time. Since the opponent is not
adversarial (a random 5 letter word is picked, not a word intended to be
problematic for any particular guessing strategy), the optimal strategy
necessarily means guessing the same word first every time. Few human players
likely play this way.&lt;/p&gt;
&lt;p&gt;In fact, given that the solution word is drawn from an unchanging list of words
before the game begins, this means you can just pre-calculate the optimal guess
given each previous guess and response from the game, laying out every possible
game state in a tree-shaped diagram.&lt;/p&gt;
&lt;p&gt;I have written a solver for Wordle in Python which searches for an optimal (per
several rules, see below) game tree, and saves it in a JSON file, which turns
out to only contain about 15 KB when compressed. The optimal game tree for
Wordle therefore turns out to be pretty small!&lt;/p&gt;
&lt;h3&gt;How the solver works&lt;/h3&gt;
&lt;p&gt;The ideal game tree would have 3 constraints:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every guess is a word that the game will accept&lt;/li&gt;
&lt;li&gt;The maximum path to a solution is less than seven guesses&lt;/li&gt;
&lt;li&gt;The average path to a solution is minimized&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I have made several simplifications in order to quickly get a reasonable
solution:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every guess is a word that the game will consider using as a solution&lt;/li&gt;
&lt;li&gt;Each guess results in the smallest number of live possible solutions, on
average.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A constraint to keep the maximum path under seven guesses turns out not to be
necessary, because a solver with just the two constraints above will never take
more than five.&lt;/p&gt;
&lt;p&gt;As it turns out, the game actually has two word lists: one is the set of words
it will consider using for solutions, just over 2000 words. The other is the
full dictionary of words it will accept as guesses. I found both these lists
in the Javascript source code of the game. Clever use of the full list of
guessable words would in same cases allow faster solves, but my solver is so
efficient even without this that I haven&amp;rsquo;t seen fit to implement it yet. So my
program only guesses words that could, in theory, be used by the program as
solutions.&lt;/p&gt;
&lt;p&gt;Likewise, always guessing the word that will result in (on average) the
smallest number of possible solutions is only an approximation of optimal
guessing. There are 2314^6 = 153525361154699100736 different possible routes
through the game, and so brute forcing your way to an optimal solution is
(while not unthinkable as it is in chess), probably unworkable in Python.
Clever pruning of the search tree should help (and I&amp;rsquo;m going to look at this at
some point), but is not as easy as it is in games with an opponent. In chess,
you can simplify the tree by assuming that the opponent will always make the
move that is worst for you. In Wordle, because the solution is random, you
have to always consider all possibilities and try to find the guess that is
optimal on average.&lt;/p&gt;
&lt;p&gt;Still, though, reducing the average number of live solutions as much as
possible is a very good approximation of ideal play. Consider the following
situation:&lt;/p&gt;
&lt;figure id="__yafg-figure-51"&gt;
&lt;img alt="A series of Wordle guesses: &amp;quot;raise&amp;quot;, &amp;quot;fiend&amp;quot;, &amp;quot;pyymy&amp;quot;, followed by the correct solution, &amp;quot;tiger&amp;quot;" src="https://adamfontenot.com/files/raisefiendpygmy.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;After the second guess, the computer makes the interesting choice &amp;ldquo;pygmy&amp;rdquo;.
This may seem counter-intuitive. Not only do we already have two vowels
confirmed to be in the word, &amp;ldquo;i&amp;rdquo; and &amp;ldquo;e&amp;rdquo;, the guess actually contains &amp;ldquo;y&amp;rdquo;
twice, which reduces the number of letters contained in the guess. As a matter
of fact however, this guess is rather astute. The computer will always find
the solution on the fourth guess depending on the outcome of this query.&lt;/p&gt;
&lt;p&gt;Using &amp;lsquo;A&amp;rsquo; to mean absent, &amp;lsquo;P&amp;rsquo; to mean present, and &amp;lsquo;C&amp;rsquo; to mean correct:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;AAAAA → liver
AAAPA → timer
AAPAA → giver
AACAA → tiger
PAAAA → viper
CAAAA → piper
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Being in a point in the game tree where there are six descendent guesses
means that there are six possible solutions. Clearly, optimal behavior would be
to always find the solution on the next turn. A brute force search would find
this solution, but so would the heuristic of eliminating as many possibilities
as possible. With &amp;ldquo;pygmy&amp;rdquo;, we always eliminate five possible solutions, the
best result possible in this case.&lt;/p&gt;
&lt;p&gt;Using a heuristic like this is much faster than a brute force search, because
it generates a guess in each situation without needing any recursion at all.&lt;/p&gt;
&lt;h3&gt;Results&lt;/h3&gt;
&lt;p&gt;This was just a quick little project for me, taking a few hours, so the code is
relatively unoptimized. Determining the best first guess requires searching
every combination of possible guess and possible solution, and this takes
several hours. The complete tree is generated in only a few minutes after that.
Because it is stored as JSON, a player for the game is included that doesn&amp;rsquo;t
have to do any searching - it simply reads the next guess out of the game tree.&lt;/p&gt;
&lt;p&gt;Stats:&lt;/p&gt;
&lt;p&gt;The solution is found, on average, in 3.51 guesses. In the worst case scenario,
the solution is found is 5 guesses. The histogram of outcomes is as follows:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;1: 1 (0.0%)
2: 65 (2.8%)
3: 1085 (46.9%)
4: 1074 (46.4%)
5: 90 (3.9%)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If anyone improves on my solution by utilizing the complete dictionary or
achieving a brute force solve of the game, I would be curious to hear how much
you manage to improve on these statistics.&lt;/p&gt;
&lt;h3&gt;Updates&lt;/h3&gt;
&lt;p&gt;Jan 11: I discovered that the program was treating all guesses that resulted in
the same average reduction to the live possibilities as equivalent. We can,
without violating the constraints given above, choose to prefer guesses that
are also possible solutions to the current puzzle. Adopting this change reduced
the average solution distance from 3.68 to 3.51 guesses.&lt;/p&gt;
&lt;h2&gt;Code&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/afontenot/wordle-solver"&gt;All of my code for this project&lt;/a&gt;
is open sourced, under a GPL3 license, on Github. This includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;words.txt - a list of the words used in possible solutions on Wordle&lt;/li&gt;
&lt;li&gt;solver.py - uses words.txt to generate the solution (solution.json)&lt;/li&gt;
&lt;li&gt;solution.json - a pregenerated copy of the solution&lt;/li&gt;
&lt;li&gt;stats.py - analyzes solution.json to generate the stats given above&lt;/li&gt;
&lt;li&gt;player.py - an interactive prompt that uses the solution to play the game&lt;/li&gt;
&lt;li&gt;game.py - in case you want to get addicted to Wordle rather than play once a
day, this game will generate endless puzzles for you to solve&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All the Python code should run on recent versions of Python 3, including the
Pypy implementation for additional speed.&lt;/p&gt;</content><published>2022-01-05T19:00:43+00:00</published></entry><entry><id>https://adamfontenot.com/post/looking_at_the_distribution_of_ratings_on_goodreads</id><title>Looking at the distribution of ratings on Goodreads</title><updated>2022-02-27T07:49:04+00:00</updated><author><name>Adam Fontenot</name></author><content type="html">&lt;p&gt;&lt;a href="https://www.goodreads.com/"&gt;Goodreads&lt;/a&gt; is a popular site for tracking
books you have read and finding new ones using your ratings. Many people
have noticed that Goodreads ratings have a surprisingly small variance:
it seems like most popular books have ratings around ~3.8. This suggests 
that a book the community assesses as fairly poor and one that it says 
is great will be closer in rating than the descriminatory capacity of a 
single user (you can&amp;rsquo;t grant half-stars in your Goodreads ratings)!&lt;/p&gt;
&lt;p&gt;Is the situation really as bad as all that? Can anything be done about it?&lt;/p&gt;
&lt;p&gt;One user scraped Goodreads&amp;rsquo; API fairly recently and made the data
&lt;a href="https://www.kaggle.com/bahramjannesarr/goodreads-book-datasets-10m"&gt;publicly available&lt;/a&gt;.
I downloaded this data and performed some analysis on the distribution
of ratings on the site. &lt;/p&gt;
&lt;p&gt;One thing I found right away is that &amp;ldquo;only&amp;rdquo; about half of the films in the
dataset have ratings between 3 and 4. There&amp;rsquo;s a bigger issue, though: about
10% of the films have a rating of precisely 4. That&amp;rsquo;s because this includes
a ton of films with only a few ratings, and 4 is the most common rating on
the site. What we need is a &lt;em&gt;model&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;What we&amp;rsquo;d like to have is a &lt;em&gt;distribution&lt;/em&gt; function describing our knowledge
about the dataset. This is usually called a Bayesian &lt;em&gt;prior&lt;/em&gt;. Let&amp;rsquo;s consider
a simplified example.&lt;/p&gt;
&lt;h3&gt;Bayesian update&lt;/h3&gt;
&lt;p&gt;If the data were in the form of thumb-ups and thumb-downs, our data would
consist of a proportion of thumb-ups out of the total number of ratings. In
other words, it would be
&lt;a href="https://en.wikipedia.org/wiki/Binomial_distribution"&gt;binomial&lt;/a&gt; data. This
entails that our prior distribution would be the corresponding
&lt;a href="https://en.wikipedia.org/wiki/Conjugate_prior"&gt;conjugate prior&lt;/a&gt;, the Beta
distribution. If we fit a Beta distribution to the data, we would get a
function with two parameters, &lt;code&gt;α&lt;/code&gt; and &lt;code&gt;β&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These parameters have a deeper meaning. Bayesian update allows you to
introduce new information to the prior knowledge you have, and tells you
what your resulting beliefs about the question at hand should be. When your
prior is a Beta distribution, the parameters are simply added to the new
evidence. If you have a new book with &lt;code&gt;x&lt;/code&gt; thumb-ups and &lt;code&gt;y&lt;/code&gt; thumb-downs,
the prior evidence suggests that the book&amp;rsquo;s true proportion of thumb-ups to
thumb-downs is &lt;code&gt;x+α : y+β&lt;/code&gt;. Because the resulting rating is adjusted by
adding these &amp;ldquo;fake&amp;rdquo; ratings, &lt;code&gt;α&lt;/code&gt; and &lt;code&gt;β&lt;/code&gt; are sometimes called
&lt;a href="https://en.wikipedia.org/wiki/Additive_smoothing#Pseudocount"&gt;pseudocounts&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Alternatively, with a simple substitution, you can think of yourself as
adding &lt;code&gt;c = α + β&lt;/code&gt; fake ratings with the average value &lt;code&gt;m = α / (α+β)&lt;/code&gt;. 
This approach is given the name &amp;ldquo;bayesian average&amp;rdquo; by
&lt;a href="https://fulmicoton.com/posts/bayesian_rating/"&gt;Paul Masurel, here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We don&amp;rsquo;t have just two possibilities, of course, we have five: five
different star ratings. A distribution with several options seems
like it would be appropriately characterized by the generalization of
the beta distribution known as the 
&lt;a href="https://en.wikipedia.org/wiki/Dirichlet_distribution"&gt;Dirichlet&lt;/a&gt;.
This approach is what Masurel suggests:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you have the relevant data and infinite time, you may set these two
values by fitting a Dirichlet distribution on the dataset of the ratings of
all your computer books. However it is very common to just choose a pair of
parameter that mimick the behavior that we are looking for.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I&amp;rsquo;ve seen this approcah recommended by several others as well.
Okay, I have the relevant data and infinite time, but let&amp;rsquo;s consider
whether fitting a Dirichlet really makes sense here. I believe that it
actually does not!&lt;/p&gt;
&lt;p&gt;The problem is that the Dirichlet (and actually, the beta distribution)
are conjugate priors for distributions of &lt;em&gt;categorial&lt;/em&gt; variables, not
discrete ones. The binomial distribution, for instance, has &lt;em&gt;success&lt;/em&gt;
and &lt;em&gt;failure&lt;/em&gt; conditions, not 0 and 1. The multinomial distribution that
has any number of distinct categories.&lt;/p&gt;
&lt;p&gt;Why is this an issue? Well, suppose we had distinct categories. Users could
describe their reaction to a book as &amp;ldquo;green&amp;rdquo;, &amp;ldquo;orange&amp;rdquo;, &amp;ldquo;red&amp;rdquo;, &amp;ldquo;yellow&amp;rdquo;, or
&amp;ldquo;blue&amp;rdquo;. (Assume that users wouldn&amp;rsquo;t immediately derive a mutually agreed upon
ranking system for these colors, i.e. they really are just &lt;em&gt;colors&lt;/em&gt;.) In that
case, there would be no inherent correlation between colors, and you couldn&amp;rsquo;t
average them into an overall color. Star ratings are precisely the opposite.
They&amp;rsquo;re &lt;em&gt;numerical&lt;/em&gt; ratings giving a quality score, and usually what we care
about is just the average.&lt;/p&gt;
&lt;p&gt;If you fit a Dirichlet distribution to star ratings, the resulting distribution
will be &lt;em&gt;underconfident&lt;/em&gt; about the behavior of the &lt;em&gt;average&lt;/em&gt; star rating,
because it treats the ratings not as comparable numbers but as incomparable
categories. More specifically, a set of star ratings will converge to their 
individual true values as new ratings come in more slowly than their average
will converge to &lt;em&gt;its&lt;/em&gt; true value.&lt;/p&gt;
&lt;p&gt;As proof of this, here is the result I get when fitting the
Dirichlet distribution to the Goodreads data.&lt;sup id="fnref:9-1"&gt;&lt;a class="footnote-ref" href="#fn:9-1"&gt;1&lt;/a&gt;&lt;/sup&gt; I am plotting the Dirichlet
reparameterized to show the strength of the prior on the &lt;em&gt;average&lt;/em&gt; value.&lt;/p&gt;
&lt;figure id="__yafg-figure-34"&gt;
&lt;img alt="plot of average values in the Goodreads data vs the prior derived from the
Dirichlet model" src="https://adamfontenot.com/files/dirichlet_average_value.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;As you can clearly see, the result is underconfident. Don&amp;rsquo;t use the Dirichlet
as a prior for star ratings, especially when you only care about the average! &lt;/p&gt;
&lt;p&gt;What should you use instead? I think the &lt;em&gt;beta&lt;/em&gt; distribution is actually
more reasonable here. Instead of modeling the proportion of successes and 
failures in a series of Bernoulli trials, we can think of it as modeling the
&lt;em&gt;average value&lt;/em&gt; of a series of individual equally weighted &lt;em&gt;fractional&lt;/em&gt; 
successes, where e.g. 5 stars means perfect success and 1 means complete
failure.&lt;/p&gt;
&lt;p&gt;If anyone knows of a better way of motivating the use of a particular
distribution here, please do contact me by email or on Github and let me know.&lt;/p&gt;
&lt;h3&gt;The results&lt;/h3&gt;
&lt;p&gt;I filtered out books with fewer than 50 ratings, to eliminate books for which
no rating consensus has yet been formed. (I also implemented some special
handling for books that have at least one star rating which no one has yet
given them, though this is a technical detail which I&amp;rsquo;ll let the interested
read the code to see.)&lt;/p&gt;
&lt;figure id="__yafg-figure-35"&gt;
&lt;img alt="plot of average values in the Goodreads data with a fitted beta
distribution" src="https://adamfontenot.com/files/beta_fit_goodreads.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The beta distribution appears to be a very good fit for the data.
Experimentally, the distribution of dirichlet averages seems to become more and
more beta-like as the strength of the prior increases.&lt;/p&gt;
&lt;p&gt;The smoothed and filtered values show the Goodreads ratings to be worse
(less informative) than we initially feared. Fully 80% of adjusted
ratings are between 3.5 and 4.2, a range considerably less than the one
star&amp;rsquo;s worth of discrimination available to individual users when rating
a book. Virtually every book on the platform that has enough ratings to
be reliable gets at least a 3, and virtually none gets more than a 4.5.&lt;/p&gt;
&lt;h3&gt;A partial solution&lt;/h3&gt;
&lt;p&gt;Given that we have a model of the data, we can adjust the average rating
of books with too few ratings to give ourselves a more accurate estimate of
the true average of the book. Furthermore, we can use the model to determine
what percentile a given rating corresponds to: my analysis suggests that a
book with a Bayesian rating of 4 is better rated than 2/3 of books on
Goodreads.&lt;/p&gt;
&lt;p&gt;For those who are interested in more accurate ratings (for books with a small
number of them) and the ability to see a book&amp;rsquo;s percentile, I have made a
&lt;a href="https://github.com/afontenot/goodreads-rating-distribution"&gt;user script&lt;/a&gt; 
which does just that!&lt;/p&gt;
&lt;p&gt;Does the ability to see rankings instead of a raw score solve the problem with
Goodreads? It certainly makes things better, but in my opinion it doesn&amp;rsquo;t solve
the basic problem, which is that Goodread ratings just kinda suck. They&amp;rsquo;re 
inconsistent, prone to audience biases, and fairly arbitrary.&lt;/p&gt;
&lt;p&gt;A sampling:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/4671.The_Great_Gatsby"&gt;The Great Gatsby&lt;/a&gt;: 55%&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/18135.Romeo_and_Juliet"&gt;Romeo and Juliet&lt;/a&gt;: 27%&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/15823480-anna-karenina"&gt;Anna Karenina&lt;/a&gt;: 76%&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/40121980-deaf-republic"&gt;Deaf Republic&lt;/a&gt;: 99%&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/153747.Moby_Dick_or_the_Whale"&gt;Moby Dick&lt;/a&gt;: 7%&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/1885.Pride_and_Prejudice"&gt;Pride and Prejudice&lt;/a&gt;: 95%&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/36047860-milkman"&gt;Milkman&lt;/a&gt;: 8%&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/17250.The_Crucible"&gt;The Crucible&lt;/a&gt;: 11%&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.goodreads.com/book/show/2956.The_Adventures_of_Huckleberry_Finn"&gt;The Adventures of Huckleberry Finn&lt;/a&gt;: 37%&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As it happens, I think all of these works are great. (Books I hated are
similarly all over the place.) The problem is not just that the ratings
are so widely varied, it&amp;rsquo;s that there&amp;rsquo;s no apparent rhyme or reason to the
order these books are in. When a film I think is great has a relatively low
rating on IMDb (example: 
&lt;a href="https://www.imdb.com/title/tt7939766/"&gt;I&amp;rsquo;m Thinking of Ending Things&lt;/a&gt;), there
is usually an obvious, comprehensible reason why most people didn&amp;rsquo;t like (or
were indifferent to) the film. Likewise, I thought
&lt;a href="https://www.imdb.com/title/tt1408101/"&gt;Star Trek Into Darkness&lt;/a&gt; was quite bad,
but I understand why most people liked it. Nothing of the sort can be said for
Goodreads: not many people dislike &lt;em&gt;Shakespeare&lt;/em&gt; &amp;mdash; so what&amp;rsquo;s happening here?
Unlike with IMDb, where the ratings (though clearly inflated, with most movies
between a six and an eight) do frequently indicate something about the quality
of the movie, I&amp;rsquo;ve found no way to extract the same value from ratings on
Goodreads.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:9-1"&gt;
&lt;p&gt;Here, I&amp;rsquo;m eliding how we arrive at the precise &lt;code&gt;α&lt;/code&gt; and &lt;code&gt;β&lt;/code&gt; values: the
Dirichlet is parametized by a vector &lt;code&gt;α&lt;/code&gt; that behaves exactly the same way as
the &lt;code&gt;α&lt;/code&gt; and &lt;code&gt;β&lt;/code&gt; values of the beta distribution. Since our data has 5
possibilities, &lt;code&gt;α&lt;/code&gt; will contain 5 values. To update a Dirichlet prior, each of
the values is added to the corresponding value in the evidence vector. As
Masurel shows, since we care only about the &lt;em&gt;average&lt;/em&gt; value we can simplify the
resulting &lt;code&gt;α&lt;/code&gt; vector down to two variables; the &lt;em&gt;sum&lt;/em&gt; of the vector, and the
average (number of stars) that a book with exactly &lt;code&gt;α&lt;/code&gt; reviews would have.
These values are just the &lt;code&gt;c&lt;/code&gt; and &lt;code&gt;m&lt;/code&gt; of the bayesian average approach.&amp;#160;&lt;a class="footnote-backref" href="#fnref:9-1" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><published>2022-02-27T07:49:04+00:00</published></entry><entry><id>https://adamfontenot.com/post/statistics_assisted_weight_loss_with_pyweight</id><title>Statistics-assisted weight loss with pyWeight</title><updated>2023-04-12T05:13:18.329538+00:00</updated><author><name>Adam Fontenot</name></author><content type="html">&lt;p&gt;Content note: this article contains a discussion of weight loss and healthy
eating, and includes my before and after pictures at the end. You can view a
version of this document without these pictures &lt;a href="/files/wlnopics.html"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;h1&gt;Quick summary&lt;/h1&gt;
&lt;p&gt;I lost 80 pounds following a diet focused on calorie restriction and satiety by
writing software to track my weight and suggest adjustments. The program is
&lt;a href="https://github.com/afontenot/pyweight"&gt;PyWeight&lt;/a&gt;
and it is available as a free download for Windows or Linux.&lt;sup id="fnref:15-1"&gt;&lt;a class="footnote-ref" href="#fn:15-1"&gt;1&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;The premise of the diet is that it&amp;rsquo;s possible to achieve a precise
&lt;em&gt;rate&lt;/em&gt; of weight loss when restricting your intake without counting the
calories in all the food you eat or knowing your daily energy expenditure. A
reasonable estimate of how much you&amp;rsquo;re eating is good enough, when assisted by 
technology.&lt;/p&gt;
&lt;p&gt;Because a calorie deficit and the resulting rate of weight loss are 
mathematically related, software that measures your average rate of weight loss 
can accurately determine whether you&amp;rsquo;re eating too many or too few calories, 
even if you don&amp;rsquo;t count them exactly. You can use the information the software 
provides to hone in on your desired rate of weight loss by making small 
adjustments to your diet over time.&lt;/p&gt;
&lt;p&gt;If this sounds interesting to you, read on.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;People who want to follow this diet should talk to a doctor beforehand.&lt;/strong&gt;&lt;/p&gt;
&lt;h1&gt;Why everyone is gaining weight and why it&amp;rsquo;s so hard to lose it&lt;/h1&gt;
&lt;p&gt;Some people believe that their bodies are genetically programmed to reach and 
maintain weight at a particular &amp;ldquo;set point&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;This clearly can&amp;rsquo;t be the whole story. In the last 40 years, the average weight 
of the adult population in most western countries has increased significantly. 
If these weights reflect our natural set points, then we are forced to conclude 
that just about everyone else who ever lived somehow never reached their 
body&amp;rsquo;s natural weight. We&amp;rsquo;re the lucky few &amp;mdash; except that apparently we&amp;rsquo;re 
&lt;em&gt;unlucky&lt;/em&gt;, since this supposed biological imperative just so happens to be bad 
for us!&lt;/p&gt;
&lt;p&gt;Yet neither can the set point view be completely wrong. It accords too well
with several common experiences.&lt;/p&gt;
&lt;p&gt;When someone in a country without a fast-food culture moves to one with an 
urban environment, they frequently gain a moderate amount of weight and then 
stabilize at a new point from which they find it difficult to gain or lose.&lt;/p&gt;
&lt;p&gt;Similarly, people who do manage to lose weight often hit plateaus &amp;mdash; low 
points at which they find further weight loss to be much more difficult. Some 
people regain lost weight until they&amp;rsquo;re about where they started. What&amp;rsquo;s 
really happening?&lt;/p&gt;
&lt;p&gt;I suggest that the truth lies somewhere in between. You &lt;em&gt;do&lt;/em&gt; have a distinct 
set of variables that dictate the weight you will approach in the long term. 
These include your genetic makeup (with its effects on your metabolism, 
skeletal structure, and so on), age, and hormone levels. But these variables 
also include diet and exercise, two variables that are partially under your
control.&lt;/p&gt;
&lt;p&gt;In the rest of this article, I will refer to the weight you are approaching
given your current daily intake (and other variables) as your &amp;ldquo;equilibrium
weight&amp;rdquo;, to distinguish this from the simplistic &amp;ldquo;set point&amp;rdquo; view.&lt;/p&gt;
&lt;p&gt;Holding everything else constant, if you are currently maintaining your body 
weight and you permanently reduce your intake by 100 calories per day, you 
will eventually stabilize at a new equilibrium point about 10 pounds lighter 
than your current weight.&lt;sup id="fnref:15-2"&gt;&lt;a class="footnote-ref" href="#fn:15-2"&gt;2&lt;/a&gt;&lt;/sup&gt; A cut of this size is equivalent to drinking one 
fewer can of &lt;em&gt;Coca-Cola&lt;/em&gt; each day at work (five days a week).&lt;/p&gt;
&lt;h2&gt;Why losing weight is hard, and maintaining weight loss is even harder.&lt;/h2&gt;
&lt;p&gt;There&amp;rsquo;s one additional but very important wrinkle: one of the variables that
dictates your equilibrium weight is your personal history of weight changes,
because of its effects on your metabolism and body composition.&lt;/p&gt;
&lt;p&gt;Consider three genetically identical people of my height and age. They
all weigh 235 pounds.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Person 1: has weighed 235 pounds for many years&lt;/li&gt;
&lt;li&gt;Person 2: recently lost weight from 350 pounds&lt;/li&gt;
&lt;li&gt;Person 3: recently gained weight from 150 pounds&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;According to one model of diet performance&lt;sup id="fnref:15-3"&gt;&lt;a class="footnote-ref" href="#fn:15-3"&gt;3&lt;/a&gt;&lt;/sup&gt;, they will need to eat as 
follows to maintain their present weight:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Person 1: 3050 calories&lt;/li&gt;
&lt;li&gt;Person 2: 3000 calories&lt;/li&gt;
&lt;li&gt;Person 3: 3400 calories&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In other words, if all three want to lose weight, the person with the thin
&amp;ldquo;history&amp;rdquo; has an advantage. Because their body has not yet adjusted to
carrying this extra weight, they will burn an extra 400 calories a day for
free.&lt;/p&gt;
&lt;p&gt;A 400 calorie per day difference is a whopping huge one. It&amp;rsquo;s about the
equivalent of eating a pint of your favorite Ben and Jerry&amp;rsquo;s ice cream every 
Monday, Wednesday, and Friday. On top of that, the previously 350 pound
individual was able to eat about 3800 calories a day at maintenance. So just to
maintain their &lt;em&gt;current&lt;/em&gt; level of weight loss, they have to get used to eating
800 fewer calories than they are used to.&lt;/p&gt;
&lt;p&gt;This may seem unfair. These are people who are genetically identical, the same
age, the same height, and the same weight. But one of them will maintain at
400 calories a day higher than the other. It &lt;em&gt;is&lt;/em&gt; a little unfair, but remember
that these are ultimately temporary metabolic effects. Eventually Person 3,
who gained 85 pounds, will become Person 1, who has carried that weight for a
long time.&lt;sup id="fnref:15-4"&gt;&lt;a class="footnote-ref" href="#fn:15-4"&gt;4&lt;/a&gt;&lt;/sup&gt; The body doesn&amp;rsquo;t provide you with a metabolic advantage forever.&lt;/p&gt;
&lt;p&gt;This leads to two important principles for maintaining weight loss:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Until your body becomes similar to that of a typical person who maintains at
 your new weight, you will face transitory metabolic effects that resist your
 weight loss. This means that until your body adapts, you will have to eat 
 less than others of the same weight can eat.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;When you lose weight, you become a smaller person. Even once your metabolism 
 stabilizes, you will need fewer calories to maintain your weight than you did 
 at your original body weight. Your hunger systems may have accepted dieting, 
 but when the time comes to maintain they may be slow to adapt to your new 
 maintenance intake, and you may find them in revolt.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most people ignore one or both of these principles. It&amp;rsquo;s tempting to treat
diets as one-time interventions similar to a medical procedure. This simply 
will not work, as the foregoing discussion indicates. You can&amp;rsquo;t eat like an 
overweight person who has temporarily starved themselves to fit into a smaller 
pair of jeans. What you &lt;em&gt;must&lt;/em&gt; do if you want to keep weight off is to
permanently eat like the thin person you now are. You must lower your
equilibrium weight through consistent, long term improvements in diet and
physical activity.&lt;/p&gt;
&lt;p&gt;Most people recognize on some level that when they regain weight after dieting,
it&amp;rsquo;s because they &amp;ldquo;ate too much again&amp;rdquo;. What many do not realize, however, is
that &amp;ldquo;too much&amp;rdquo; &lt;em&gt;means something different&lt;/em&gt; after dieting than it did before.&lt;/p&gt;
&lt;p&gt;The worst part about all this is that you&amp;rsquo;ll begin to feel the effects of the
two principles &lt;em&gt;while&lt;/em&gt; you&amp;rsquo;re still trying to lose weight.&lt;/p&gt;
&lt;p&gt;Most people start a diet expecting to lose weight at a relatively quick rate 
&amp;mdash; and they do their first week or two. Unfortunately, most of this weight 
loss is fake. Running a calorie deficit means that your body burns through its 
store of glycogen, which is what causes it to tap your energy reserves (fat). 
Along with the loss of this weight, you will see a one-time drop that&amp;rsquo;s just 
water weight; your body has less food temporarily stored in your stomach and 
intestines as it&amp;rsquo;s being digested. Your body will also adapt to reduced intake 
relatively quickly, and your metabolic rate will drop slightly.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Metabolism is not magic.&lt;/strong&gt; Your body obeys the law of conservation of
energy when your metabolism &amp;ldquo;slows&amp;rdquo;, and it doesn&amp;rsquo;t have a more efficient
&amp;ldquo;starvation mode&amp;rdquo; that uses fewer calories to perform the same tasks. (If it
did, natural selection would surely have dictated that we would &lt;em&gt;always&lt;/em&gt; be in
that mode, and thereby need to eat less.) Rather, a &amp;ldquo;slowed&amp;rdquo; metabolism means
that you will either be slightly colder (unlikely to be by enough to make a
serious difference) or that you will have less energy &lt;em&gt;available&lt;/em&gt;, and
therefore perform fewer tasks. Maybe you will feel like exercising less, or
maybe you will perform tasks in a lazier fashion, or maybe you&amp;rsquo;ll simply sleep
more. Putting all this together, it means that you can expect to lose some
fraction of your caloric deficit to temporary metabolic changes.&lt;/p&gt;
&lt;p&gt;If you don&amp;rsquo;t get discouraged and continue dieting, your weight loss will
continue apace, but unfortunately you start to run into the problem discussed
above: your reduced size is resulting in a smaller energy expenditure. Research
suggests that for most reasonably large deficits, it would take &lt;em&gt;years&lt;/em&gt; for
this effect to neutralize further weight loss (at which point you would have
achieved a new equilibrium), but in the mean time your losses each week will
get smaller and smaller. No wonder most people get discouraged! They end up
cheating or adding calories to their diets little by little, and eventually
they stall out. This is why many people hit plateaus in their diets. A slowing
rate of weight loss leads to discouragement, and discouragement leads to a
failure to adhere strictly to the diet, and &lt;em&gt;this&lt;/em&gt; leads to you hitting a new
equilibrium point much earlier than expected.&lt;/p&gt;
&lt;p&gt;Because of your body&amp;rsquo;s remarkable capacity to adapt to changes in food
availability, you have only two choices if you want to overcome this problem.
You can start out with a lower calorie budget, precisely calibrated to match
the metabolic changes you anticipate. &lt;em&gt;Or&lt;/em&gt; you can adjust your calorie intake
as you go along, to maintain a constant deficit. I strongly suspect that the
latter approach is easier to follow and less discouraging, so it is the
approach PyWeight is based on.&lt;/p&gt;
&lt;h2&gt;Where is the obesity epidemic coming from?&lt;/h2&gt;
&lt;p&gt;This talk of equilibria makes an obesity epidemic rather surprising. Even if
people are eating more than before, why aren&amp;rsquo;t they simply reaching new
slightly higher equilibrium points, like our theoretical immigrant? After all,
if the way one developed a body that required 4000 calories just to maintain
itself was to start eating 4000 calories a day, no one would ever do that. Most
people with normal body weights couldn&amp;rsquo;t even stomach 4000 calories for many
consecutive days.&lt;/p&gt;
&lt;p&gt;But the evidence for epidemic obesity is clear. Here in the U.S., the state of 
Mississippi had the highest rate of obesity in 1995, at 19.4%. In 2010, a mere
15 years later, Colorado had the &lt;em&gt;lowest&lt;/em&gt; rate of obesity in the country, at
19.8%, and 12 states were over 30%.&lt;sup id="fnref:15-5"&gt;&lt;a class="footnote-ref" href="#fn:15-5"&gt;5&lt;/a&gt;&lt;/sup&gt; Today, more than 40% of American adults
have obesity.&lt;sup id="fnref:15-6"&gt;&lt;a class="footnote-ref" href="#fn:15-6"&gt;6&lt;/a&gt;&lt;/sup&gt; This is happening quickly enough that it doesn&amp;rsquo;t work to
posit genetic or behavioral changes in a new generation of adults: the &lt;em&gt;same
people&lt;/em&gt; who were thin 10-20 years ago have obesity now. So what&amp;rsquo;s happening?&lt;/p&gt;
&lt;p&gt;Of course, when we&amp;rsquo;re talking about the obesity problem, we&amp;rsquo;re talking about
averages. Some people &lt;em&gt;do&lt;/em&gt; maintain an equilibrium point that&amp;rsquo;s just
overweight. Other people don&amp;rsquo;t become overweight at all. Yet in this country,
we seem to have reached the point where the majority of people put on weight
year over year, endlessly.&lt;/p&gt;
&lt;p&gt;You can square an equilibrium view of body weight with this fact using the
suggestion that we&amp;rsquo;ve reached some kind of tipping point. For some reason,
people are driven to eat slightly more than they need to maintain, and for many
of them the rate at which they increase their intake has begun to &lt;em&gt;outpace&lt;/em&gt; the
body&amp;rsquo;s compensatory mechanisms.&lt;/p&gt;
&lt;p&gt;The body is calibrated to give you hunger signals that will result in you
eating approximately what you need to eat to maintain your current weight, on
the sort of diet most people had in the past. Someone who could maintain at
2000 calories a day and significantly overate&lt;sup id="fnref:15-7"&gt;&lt;a class="footnote-ref" href="#fn:15-7"&gt;7&lt;/a&gt;&lt;/sup&gt; would gain weight, but after
doing that their body would still only be giving them hunger signals
appropriate to maintain their new body weight &amp;mdash; or more precisely slightly
&lt;em&gt;less&lt;/em&gt; until they adapted. (Effectively, the two principles mentioned above are
functioning in reverse here.) So this person would not automatically continue
to gain weight. The body&amp;rsquo;s negative feedback of constantly feeling too full
would tend to keep their weight in a healthy range.&lt;/p&gt;
&lt;p&gt;Our hunger signals are no longer accurate. The body still says &amp;ldquo;I need 2000 
calories&amp;rdquo;, but it doesn&amp;rsquo;t feel full until you&amp;rsquo;ve eaten 2100, thanks to the low
satiety calorie-dense diets of today. When you gain enough weight on such a
diet, your body will start to say &amp;ldquo;I need 2100 calories&amp;rdquo;, but now it takes
2200 to fill you. And because eating food takes time and costs money, your
increased appetite will be drawn towards quicker and cheaper options, further
exacerbating the problem. Before, the body gave you you a signal that you 
should eat less food the day after a big meal. Now, even a big meal isn&amp;rsquo;t 
enough to make you feel full.&lt;/p&gt;
&lt;p&gt;This suggests we should change our view of personal responsibility. People in
the last 30 years are facing a problem that virtually no one in the past faced:
their stomachs are lying to them about how much they should eat.&lt;/p&gt;
&lt;p&gt;Recall the equilibrium equation: your equilibrium weight is a function of your
genetics, body composition, age, hormones, diet, and exercise. If you consider 
this equation on a societal level, you can replace diet and exercise with just 
one variable: environment. In the absence of a strong effort to control these 
two variables, the health environment you are in will dictate them to you, and 
therefore control your weight.&lt;/p&gt;
&lt;p&gt;Faced with the environment that most people in Western countries face, the 
average person is going to gain weight over time. So on a broad social level, 
that&amp;rsquo;s what we see. Blaming individual people for the consequences is not just 
counterproductive, it&amp;rsquo;s an &lt;em&gt;inaccurate&lt;/em&gt; accounting of what has happened.&lt;/p&gt;
&lt;p&gt;Consider the following scientific model of average weight gain&lt;sup id="fnref:15-8"&gt;&lt;a class="footnote-ref" href="#fn:15-8"&gt;8&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;figure id="__yafg-figure-38"&gt;
&lt;img alt="" src="/files/nihms.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;What this model indicates is that the weight gain of the average American over
the last decades can be explained by a relatively tiny gap between energy
intake and energy expenditure. In fact, the average gap for the period examined
in this study was only about 7 calories a day. The problem doesn&amp;rsquo;t amount to
7 calories here or there, the epidemic stems from the fact that this gap is so
reliably &lt;em&gt;sustained&lt;/em&gt;. High calorie junk food is causing many of us to overeat,
day after day, with no feedback telling us to cut back for a while. This has
gone on for so long that in 2005 the average person needed to cut out more
than 200 calories a day to achieve the equilibrium point of the average person
in the 1970s. This number, of course, has only continued to rise.&lt;/p&gt;
&lt;p&gt;Watch this excellent five minute clip detailing some of the causes of calorie
overproduction in the United States:&lt;/p&gt;
&lt;figure id="__yafg-figure-39"&gt;
&lt;video controls poster="/files/twotnclip.jpg" src="/files/twotnclip.mp4" title="Source: The Weight of the Nation, 2012. HBO Documentary Films."&gt;&lt;/video&gt;
&lt;figcaption&gt;Source: The Weight of the Nation, 2012. HBO Documentary Films.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;You might be tempted to decide the takeaway of this analysis is &lt;em&gt;political&lt;/em&gt;:
corporations are profiting off of human suffering on a massive scale, so some
social action is needed to fix the problem. Insert regulation, social
democracy, or whatever your preferred solution happens to be. That&amp;rsquo;s all well
and good, but it&amp;rsquo;s of little use to someone suffering the effects here and now.
This country will happily let you die in the street. In all probability, things
are not going to get better on a timescale that matters to you. The only option
is to take matters into your own hands and exploit the two variables under your
control to the maximum extent you can. For some people, that will be enough.&lt;/p&gt;
&lt;h2&gt;The good news&lt;/h2&gt;
&lt;p&gt;The one bright spot is that for most people the food environment is not yet bad
enough to make it impossible for them to control their weights. If you are not
very constrained in your diet and exercise choices, then you have a fairly good
chance of overcoming your environment.&lt;/p&gt;
&lt;p&gt;As for the other variables in the equilibrium equation that are outside of your
control (e.g. genetics and metabolic effects), the good news is that these
almost always have a relatively minor impact on your weight. Most people have a
basal metabolic rate (the number of calories you burn each day just by being
alive) within 10 percent of the rate estimated by the Mifflin-St Jeor equation.
&amp;ldquo;Slow&amp;rdquo; metabolism is not usually the cause of weight gain.&lt;sup id="fnref:15-9"&gt;&lt;a class="footnote-ref" href="#fn:15-9"&gt;9&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;This means that many people &lt;em&gt;can&lt;/em&gt; change their equilibrium point by managing
their calorie intake and becoming more physically active.&lt;/p&gt;
&lt;h2&gt;Addendum: it&amp;rsquo;s okay to try to lose weight, I think&lt;/h2&gt;
&lt;p&gt;Some authors, including philosopher Kate Manne, claim that encouraging 
dieting and even dieting yourself are
&lt;a href="https://www.nytimes.com/2022/01/03/opinion/diet-resolution.html"&gt;immoral&lt;/a&gt;. 
I agree that negative self-image and exploitation in the diet industry are 
both things we should condemn, but for this to have consequences for individual 
dieting decisions something more is needed. &lt;/p&gt;
&lt;p&gt;I think that&amp;rsquo;s why Manne&amp;rsquo;s article, though it defends an ethical conclusion,
ultimately rests on two empirical claims. She writes that &amp;ldquo;fat bodies can
be not only healthy but also athletic&amp;rdquo; and &amp;ldquo;the vast majority of diets fail to
make people any thinner or any healthier in the long term&amp;rdquo;. These claims are 
both true, and suggestive, but don&amp;rsquo;t on their own deliver her conclusion.&lt;/p&gt;
&lt;p&gt;It is true that &lt;em&gt;some&lt;/em&gt; fat bodies are healthy. Similarly, many people who 
don&amp;rsquo;t get the recommended 30 minutes of exercise every day are also healthy. 
Despite this, since exercise and a reduction in overweight tend to result in 
health improvements, it can make sense to pursue both.&lt;/p&gt;
&lt;p&gt;Likewise, the fact that most diets fail cannot deliver the conclusion either. 
If this claim is to show that it would be a bad idea for &lt;em&gt;me&lt;/em&gt; to start a diet, 
it needs to be true that &lt;em&gt;I&lt;/em&gt; can have no expectation of success, so that trying 
to lose weight is similar to playing the lottery. I think this is false. There 
are reasons that most diets fail, but dieting is not tantamount to fighting 
genetic or environmental determinism, as I argued in the previous section. I
explain further why I think it is reasonable to expect success in the next
section.&lt;/p&gt;
&lt;p&gt;I suspect that any versions of Manne&amp;rsquo;s empirical claims strong enough for her 
conclusion will be false.&lt;/p&gt;
&lt;p&gt;The claim that some fat bodies are healthy suggests a stronger version that 
would suffice: most fat bodies are healthier than the same bodies would be if 
they were thin. Dieting would then, in all likelihood, make you less healthy.
In this form, the route to Manne&amp;rsquo;s conclusion that encouraging dieting or
dieting yourself &amp;ldquo;is a morally bad practice&amp;rdquo; is obvious, but the premise seems
clearly false. There certainly is a correlation between body weight and health.
As I will discuss later, some (contentious) studies suggest that being
&lt;em&gt;slightly&lt;/em&gt; overweight does not increase mortality, but no medical authorities
will endorse the notion that obesity is healthy.&lt;/p&gt;
&lt;p&gt;Likewise, if diets could be shown to be inherently dangerous, unhealthy, or 
doomed to fail, it would be easy to reach the conclusion. Manne hints at this: 
she says that in one month early in her diet, she didn&amp;rsquo;t eat for 17 out of 30 
days. This is indisputably a bad approach to dieting. If most people trying to 
lose weight go for diet fads and ineffective products, or take dangerous 
or disordered approaches like extreme fasting, that would help explain the 
failure rate.&lt;/p&gt;
&lt;p&gt;However, it seems false that these problems are inherent to dieting. I ate 
food every day on my diet. Manne herself seems to have ended up with a 
sensible diet: she lost about 50 pounds in a year, close to the common 
recommendation of a pound per week. The diet industry is certainly blameworthy 
for distracting people from methods likely to be successful and promoting 
fads, but this alone can&amp;rsquo;t show that dieting is wrong.&lt;/p&gt;
&lt;h1&gt;How to succeed at dieting&lt;/h1&gt;
&lt;p&gt;Most diets fail. In a major longitudinal UK study, less than 20% of people with 
obesity managed to decrease their BMI by at least one category without a
subsequent increase, over a 9 year period. 78% of people who lost at least 5%
of their body weight gained enough back that after 5 years they were back
within 5% of their original weight, or even higher.&lt;sup id="fnref:15-10"&gt;&lt;a class="footnote-ref" href="#fn:15-10"&gt;10&lt;/a&gt;&lt;/sup&gt; This indicates that
while plenty of diets do succeed (the popular claim that 95% of diets fail is
incorrect), successful dieters are by no means the majority.&lt;/p&gt;
&lt;p&gt;I don&amp;rsquo;t believe a 20% success rate should be discounted. It suggests that diet 
success is possible and that we should be looking at what the successful 
people do. If we were to discover that a particular intervention to help people 
quit smoking had a 80% recidivism rate, we would &lt;em&gt;still&lt;/em&gt; encourage people to 
take the program (because 20% is much better than zero).&lt;/p&gt;
&lt;p&gt;Part of the issue with assessing the success of a typical diet is that if you
lower the bar of what counts as successful weight loss, then the success rate
goes up. If you demand studies involving weight loss of a quantity closer to
what I was hoping to lose, examples become even rarer. Most studies are testing
specific short-term interventions to see what works and what doesn&amp;rsquo;t, not
looking at how to achieve a long-lasting high level of weight loss. It&amp;rsquo;s hard
to get reliable data about the prevalence of successful diets resulting in long
term weight loss of 25% of body weight or more. A large portion of attempts
likely fail.&lt;/p&gt;
&lt;p&gt;The reasons why most diets fail go beyond simple misunderstandings of what it
takes to succeed, of course. Food is &lt;em&gt;tempting&lt;/em&gt;. Not eating food when you are
hungry sucks. The restrictions that a diet imposes can be difficult to 
implement. The nutritional deficits imposed by Western eating can become even
more severe on a calorie restriction, especially for those who can&amp;rsquo;t afford to
make their own food. For many people, a personal trainer and gym membership are
unaffordable, and for some even a doctor visit is out of reach. (It was for me
when I began my diet! Thanks, America.)&lt;/p&gt;
&lt;p&gt;Given the high rate of failure, what I went looking for was a diet that could
&lt;em&gt;guarantee&lt;/em&gt; success to those who stick to it and and don&amp;rsquo;t cheat. My hope was
that following such a diet would not only bring me the weight loss I was 
looking for, but that the certainty of success might provide additional
motivation to stick with the diet.&lt;/p&gt;
&lt;p&gt;As a result of studying physics in college, I was naturally inclined to follow
a &amp;ldquo;calories in, calories out&amp;rdquo; diet. This comes with something like a success 
guarantee: if you consume fewer calories than your body uses each day, you 
&lt;em&gt;will&lt;/em&gt; lose weight. Other diets &lt;em&gt;may&lt;/em&gt; be successful as well, but only if they 
end up resulting in calorie reduction anyway. Diets may have some 
individualized metabolic effects but these appear to be minor in
comparison.&lt;sup id="fnref:15-11"&gt;&lt;a class="footnote-ref" href="#fn:15-11"&gt;11&lt;/a&gt;&lt;/sup&gt;&lt;sup id="fnref:15-12"&gt;&lt;a class="footnote-ref" href="#fn:15-12"&gt;12&lt;/a&gt;&lt;/sup&gt; Perhaps some other diet is easier to maintain than
calorie restriction, but no other diet could provide the guarantee of
success.&lt;sup id="fnref:15-13"&gt;&lt;a class="footnote-ref" href="#fn:15-13"&gt;13&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Counting calories has a reputation for being difficult, which is part of the
reason diets that dress up the calorie counting by using &amp;ldquo;points&amp;rdquo; systems are
so popular. It&amp;rsquo;s not only easy to underestimate the number of calories that
you&amp;rsquo;re eating, it&amp;rsquo;s also difficult to determine with accuracy how many calories
you&amp;rsquo;re burning. Moreover, if you weigh yourself once a week or so, your
measurements might get swamped by variations in water weight or having an 
unusually large lunch, so it&amp;rsquo;s hard to know if you&amp;rsquo;re making progress. If you 
weigh yourself once a day, the value on the scale is likely to fluctuate so 
much that you constantly fly into a panic over an imaginary weight gain.&lt;/p&gt;
&lt;p&gt;Furthermore, you might already be disheartened by reading the previous
discussion of why it&amp;rsquo;s hard to lose weight. As you lose weight, the number of
calories you need just to stay at maintenance is going to be decreasing. How is
it possible to reliably stay ahead of the curve? It turns out that there&amp;rsquo;s a
simple trick that can be summed up in a single paragraph, and this trick
obviates most of the complexity of weight loss. This trick is:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Record your weight every day. Every two weeks, calculate your approximate
weight loss using your record. If you are losing weight too quickly, eat
more the next two weeks. If you are losing weight too slowly, eat less the next
two weeks.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s it. Weight loss, summarized. By only worrying about whether your &lt;em&gt;rate&lt;/em&gt;
of weight loss matches your predetermined goal, and not calories or points or 
what have you, you can hone in on a reliable rate of weight loss over time with
relatively little effort. Accurately counting calories or determining your
daily expenditure is not required.&lt;/p&gt;
&lt;p&gt;You can do this yourself with just pencil and paper if you like. But technology
will help you in two ways: you can more accurately calculate your rate of
weight loss using techniques like linear regression, and you can estimate how
many &lt;em&gt;calories&lt;/em&gt; you missed your target by (instead of pounds), to make it
easier to adjust your intake in the future.&lt;/p&gt;
&lt;p&gt;This idea was inspired by the book
&lt;a href="https://www.fourmilab.ch/hackdiet/"&gt;The Hacker&amp;rsquo;s Diet&lt;/a&gt;. The book&amp;rsquo;s version of
this scheme goes something like this: if we assume that each pound of fat lost
contains about 3500 calories, then the math becomes quite simple. If you need
to lose &lt;code&gt;x&lt;/code&gt; pounds, and you maintain a deficit of about &lt;code&gt;y&lt;/code&gt; calories a day, it
will take you about &lt;code&gt;3500 * x / y&lt;/code&gt; days to do that. At a deficit of 500
calories a day, you will lose about a pound a week. If after two weeks you&amp;rsquo;ve
only lost 1 pound, you know you&amp;rsquo;re missing your target by 250 calories per day.&lt;/p&gt;
&lt;p&gt;Unfortunately, this relies on the myth that a pound of fat contains 3500
calories. It turns out the correct number is closer to 4280.&lt;sup id="fnref:15-14"&gt;&lt;a class="footnote-ref" href="#fn:15-14"&gt;14&lt;/a&gt;&lt;/sup&gt; The slightly
better version of the theory, which says that the average caloric density of
the weight you lose is 3500 calories, is an inaccurate approximation. You do
lose some fat free mass while dieting, and this contains around 824 calories
per pound. So 3500 calories per pound is only accurate if about 3/4 of the
pounds you lose are from fat. This is only a reasonable estimate for very
overweight people at the beginning of a diet. Later on, as you approach an
optimal body weight, you will lose a larger proportion of lean mass. If you
were to continue losing weight indefinitely, you would of course eventually
have nothing to lose &lt;em&gt;but&lt;/em&gt; lean mass. This would be extremely bad for your
health.&lt;/p&gt;
&lt;p&gt;Because of this complication, PyWeight uses an equation from a real diet
model&lt;sup id="fnref:15-15"&gt;&lt;a class="footnote-ref" href="#fn:15-15"&gt;15&lt;/a&gt;&lt;/sup&gt; to estimate how many calories were contained in the weight you lost
in a given interval, and therefore how many calories you are away from hitting
your weight loss rate target. This means it&amp;rsquo;s possible to target a linear rate
of weight loss despite all the non-linear effects. (For what it&amp;rsquo;s worth, when
you&amp;rsquo;re targeting a constant rate of weight loss rather than a constant,
pre-determined calorie budget, the problems caused by inaccurate estimates are
greatly lessened. In fact, you&amp;rsquo;d probably be fine with the old 3500 calories
per pound estimate. The upshot of PyWeight&amp;rsquo;s advice is usually just &amp;ldquo;try to
eat a little less / more next week&amp;rdquo;, just as it would be if you were using
pencil and paper.)&lt;/p&gt;
&lt;p&gt;With the Hacker&amp;rsquo;s Diet, the calculations are all done in a series of
complicated spreadsheets which are provided with the book. PyWeight
allows entering your measurements directly in a built-in log, and all
calculations are done for you automatically. It also provides intake adjustment
advice at a user-selectable frequency.&lt;/p&gt;
&lt;p&gt;The linear regression method used by the Hacker&amp;rsquo;s Diet can also be improved
upon. Doing a naive linear regression by breaking the data into intervals 
lasting 2-3 weeks means that &lt;em&gt;only&lt;/em&gt; the data from those weeks figures into the
calculation of your current rate of weight loss. But this is throwing away
data; you can determine a much more accurate starting weight for each interval
if you also make use of the data from the previous interval. PyWeight takes
exactly this approach with a technique called spline interpolation. A possible
alternative would be to use a moving average, but I believe this would give
worse results because of the high degree of variability in weight measurements
and the fact that the diet is built around attempting to lose weight at a
constant rate.&lt;/p&gt;
&lt;p&gt;As a side effect of this improvement, PyWeight is able to generate very nice
looking graphs of your weight loss. Here is mine from the last ~7 months. (I
lost the first 20 pounds using the methods described in this article before I
wrote PyWeight.)&lt;/p&gt;
&lt;figure id="__yafg-figure-40"&gt;
&lt;img alt="my graph in the pyweight program" src="/files/weightloss.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The idea behind using a program to track your weight is that despite the fact
that our hunger systems are broken because of modern calorie dense diets, we
can use technology to replace this body function, letting simple math
regulate how much we eat. This method actually works even &lt;em&gt;better&lt;/em&gt; than simple 
calorie counting, because it adapts to your changing energy needs as you lose 
weight. If you want to keep up a one pound per week deficit, you&amp;rsquo;ll need to 
reduce your intake over time, and this approach will help you do so 
automatically.&lt;/p&gt;
&lt;p&gt;Even though you&amp;rsquo;re measuring your weight every day, you don&amp;rsquo;t have to &lt;em&gt;worry&lt;/em&gt;
about what the scale says in the way you do with ordinary calorie counting. An
unusually large meal might send your apparent weight up 1-3 pounds for several
days, but this is mostly water weight that will soon go away. Looking at a 
statistical measure of your weight loss over a period of weeks smooths out 
these misleading bumps in the graph.&lt;/p&gt;
&lt;p&gt;Looking at my graph, you can see several of these blips, some of them quite 
large. My daily scale measurement increased 6.8 pounds over the course of four 
days in late December, but this did not represent &lt;em&gt;any&lt;/em&gt; long term weight gain.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not going to tell anyone that this approach makes dieting a sure thing.
A great deal of willpower is still involved. But knowing exactly what you&amp;rsquo;re
doing and trying to keep up a consistent rate of weight loss makes it much
easier. Steven Novella helpfully
&lt;a href="https://sciencebasedmedicine.org/calories-thermodynamics-and-weight/"&gt;writes&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I would conclude that weight control is difficult, but not impossible. In
addition, I believe that the popular weight loss industry is making the problem
worse by distracting the public from those strategies for which there is at
least some evidence of efficacy and focusing on minute, short term, and
probably insignificant effects from manipulating macronutrient proportions.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;My goal with this diet is to focus on what we &lt;em&gt;know&lt;/em&gt; works (consistently eating
fewer calories than you burn) and find a way to make sure I am doing that with
small course corrections over time. I began this diet with a high degree of
confidence that it would work if I didn&amp;rsquo;t get burned out and give up, and that
confidence was rewarded. As you can see from the graph above, I was able to
maintain an extremely consistent rate of weight loss over time; I never
deviated from my monthly target by more than a few percent (except around the
holidays and a strenuous four day hiking trip in mid-February during which I 
snacked almost continuously).&lt;/p&gt;
&lt;p&gt;It&amp;rsquo;s not just losing the weight that gives me confidence this method works. 
It&amp;rsquo;s the fact that I lost the weight at almost &lt;em&gt;exactly&lt;/em&gt; the rate I planned in 
advance! I think many individuals can see success in dieting if they can follow
a similar approach.&lt;sup id="fnref:15-16"&gt;&lt;a class="footnote-ref" href="#fn:15-16"&gt;16&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;No method for individual correction can solve the underlying problem. So long 
as the United States fails to get a handle on its poor nutrition environment, 
obesity will continue to be a serious issue. Proposing diets as the solution 
is like thinking that encouraging people to recycle can prevent climate change.
However, until the social causes of obesity are resolved, individuals are on 
their own. Diets will not reverse the long term obesity trend, but they &lt;em&gt;can&lt;/em&gt;
help dedicated individuals who are fortunate enough in their circumstances.&lt;/p&gt;
&lt;h1&gt;Setting goals&lt;/h1&gt;
&lt;p&gt;I started out just above 240 pounds, which at my height was a hair into the
obese range (30+) by the BMI metric. BMI is not necessarily a good indicator of
individual health, but the truth is I felt bad. I did not feel like 
exercising, or even going for a walk. Hiking long distances, one of the
activities I&amp;rsquo;ve enjoyed the most over the years, was certainly out of the 
question. While it&amp;rsquo;s not &lt;em&gt;impossible&lt;/em&gt; to be in good shape at the size I was,
it&amp;rsquo;s certainly much more difficult.&lt;/p&gt;
&lt;p&gt;How bad is it, in general, to be overweight? There has been some recent
controversy on this point in the medical world. The long-time consensus has
been that while individuals with BMIs between 25 and 30 are not &lt;em&gt;necessarily&lt;/em&gt;
unhealthy, overweight is correlated with heart disease and diabetes, as well as
increased all-cause mortality.&lt;/p&gt;
&lt;p&gt;One 2005 study suggested that being in the overweight category was associated 
with &lt;em&gt;reduced&lt;/em&gt; all-cause mortality relative to the normal (18.5 - 25) BMI
category.&lt;sup id="fnref:15-17"&gt;&lt;a class="footnote-ref" href="#fn:15-17"&gt;17&lt;/a&gt;&lt;/sup&gt; A follow up meta-analysis&lt;sup id="fnref:15-18"&gt;&lt;a class="footnote-ref" href="#fn:15-18"&gt;18&lt;/a&gt;&lt;/sup&gt; by the same primary author
confirmed this finding, and also found the difference between mild obesity and
normal body weight to be insignificant. However, a much larger third
analysis&lt;sup id="fnref:15-19"&gt;&lt;a class="footnote-ref" href="#fn:15-19"&gt;19&lt;/a&gt;&lt;/sup&gt; disagreed, finding that after controlling for smoking, prior
chronic disease, and length of follow-up, both overweight and obesity were
correlated with increased all-cause mortality risk. Moreover, they were able to
reproduce the findings of the second paper by &lt;em&gt;removing&lt;/em&gt; those quality controls.
(The danger of leaving in smokers is that a disproportionate number of smokers
will have healthy body weights because nicotine functions as an appetite
suppressant. Smoking is much worse for you than a few extra pounds, so these
people make &amp;ldquo;normal&amp;rdquo; body weights look less healthy on average.)&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m not here to say that the third study was right and the first and second
were wrong. I don&amp;rsquo;t have the expertise to do that, to begin with. The primary
author of the first two papers wrote a response paper which claimed that the
approach taken by the third was biased and suggested that the exclusion of
smokers might lead to &lt;em&gt;more&lt;/em&gt; bias than leaving them in.&lt;sup id="fnref:15-20"&gt;&lt;a class="footnote-ref" href="#fn:15-20"&gt;20&lt;/a&gt;&lt;/sup&gt; The result has
been an increasingly bitter academic feud over claims of &amp;ldquo;bias&amp;rdquo; with no 
obvious conclusion (to me).&lt;/p&gt;
&lt;p&gt;However, a very nice fourth paper&lt;sup id="fnref:15-21"&gt;&lt;a class="footnote-ref" href="#fn:15-21"&gt;21&lt;/a&gt;&lt;/sup&gt;, a systematic review of the issue,
reached more or less the same conclusions as the third. It was significantly
larger than &lt;em&gt;either&lt;/em&gt; of the others, containing almost 10 million individuals
in the never-smoking category alone, and none of the authors overlapped with
those of the third study. This study found that an ideal BMI was approximately
23, with negligible differences between BMIs from 22 to 25. Furthermore, the
paper reproduced the most important finding of the third: the conclusion was
reversed when filtering was removed. A BMI of 27.5 was associated with a lower
mortality risk than one of 23, and a BMI of 32.5 lower than one of 20!&lt;/p&gt;
&lt;p&gt;Because this study subdivided &amp;ldquo;normal&amp;rdquo; BMI into many categories, the
conclusions are a bit more fine-grained. The results may help explain why in
some studies the 18.5-25 BMI range compares unfavorably with the 25-30 range.
The dangers of being underweight are quite extreme: among never-smokers, the
study found that a BMI of 17.5 was associated with a 35% increased mortality
risk.  Someone with a BMI of less than 20 contributes to the average outcome
for the normal BMI range (if their BMI is 18.5 or higher), but they&amp;rsquo;re exposed
to more of the risks of underweight compared to someone with a BMI of 23, which
the study found to be optimal.&lt;/p&gt;
&lt;p&gt;I believe it&amp;rsquo;s very important for casual interpreters of medical research to
remember to look at papers in context. All this bickering is essentially over
a 5-10% difference. If you believe the fourth paper, a BMI of 27.5 is
associated with a 7% increased all-cause mortality risk versus a BMI of 23. If
you believe the second paper, having a normal BMI might be associated with 6%
more all-cause mortality risk versus an overweight BMI. Personally, I&amp;rsquo;m
inclined to believe the former, because the studies are larger, it&amp;rsquo;s more in
line with the medical consensus view, and because the exclusion of smokers in
the analysis seems justified. But I see no reason for &lt;em&gt;me&lt;/em&gt; to argue with
someone who believes the latter. To my mind, the sane middle ground is found by
keeping your BMI well out of the obese range, and at a level where you &lt;em&gt;feel&lt;/em&gt;
able to keep yourself in good shape. My guess is that the difference between 
the two is swamped by just getting plenty of exercise.&lt;/p&gt;
&lt;p&gt;For me, the truth is that I wasn&amp;rsquo;t able to feel healthy or stay fit at the
weight I was, and the BMI number merely served as a painful (though possibly
useful) reminder of that.&lt;/p&gt;
&lt;p&gt;For the sake of having a goal weight, I picked the arbitrary target of 160
pounds. This happens to correspond nicely to taking 10 points of my BMI, and
struck me as unlikely to be unsafely low. It corresponds to a BMI of slightly
above 20, and my plan is to put on a few pounds of muscle over the next year. 
The last time in my life when I was thin was around the age of 10 or 11 (when I 
had temporarily become a vegetarian), and I wanted to experience that again.&lt;/p&gt;
&lt;p&gt;It remained only to choose a rate at which I was going to try losing the
weight. The UK&amp;rsquo;s National Health Service
&lt;a href="https://www.nhs.uk/live-well/healthy-weight/should-you-lose-weight-fast/"&gt;says that&lt;/a&gt;
a weight loss rate of 0.5 to 1 kg a week is safe. 1 kg a week is equivalent to
9.45 pounds lost every 30 days. I rounded this up to a target of 10 pounds per
30 days. I&amp;rsquo;m considerably taller than the average person, and so there is a 
larger gap between my daily energy expenditure and my basal metabolic rate than
for the typical dieter. I think this was a safe target for me.&lt;/p&gt;
&lt;p&gt;I emphasize that if you&amp;rsquo;re going to pursue fast weight loss, you should talk to 
a doctor about that. You may very well have more success and invite fewer risks 
by targeting a pound per week (for some people, even less). In my case, without 
access to professional advice, I made the best judgment I could about what 
would be safe and effective for me.&lt;/p&gt;
&lt;h1&gt;How to get enough nutrition on a diet that doesn&amp;rsquo;t suck&lt;/h1&gt;
&lt;p&gt;It can be helpful to get a rough estimation of how many calories you should
budget for yourself. In my case, something like 1800 calories a day at the
start of the diet was appropriate. One nice thing about taking a statistical
approach to weight loss is that even if you screw up this estimate quite badly,
you&amp;rsquo;re unlikely to put yourself in any danger as long as your target rate is
reasonable. If you lose too much weight in the first weeks of the diet, you
will receive a sharp corrective directing you to eat several hundred more
calories every day. PyWeight is thus self-correcting.&lt;sup id="fnref:15-22"&gt;&lt;a class="footnote-ref" href="#fn:15-22"&gt;22&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Cutting a thousand calories out of your usual intake can make it difficult to
get the nutrients your body expects to have available. It&amp;rsquo;s important to have
a plan for getting the necessary vitamins, minerals, and essential amino acids.&lt;/p&gt;
&lt;p&gt;This is an area where The Hacker&amp;rsquo;s Diet is not very helpful. It says&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Since we&amp;rsquo;re efficient food processing machines, it&amp;rsquo;s possible to reduce all 
the complexity of food to a single number that gives the total energy the body 
can extract from it—the calorie.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The extreme form of this is the &lt;a href="http://www.cnn.com/2010/HEALTH/11/08/twinkie.diet.professor/index.html"&gt;infamous Twinkie diet&lt;/a&gt;. You
will lose weight on such a diet; it might also kill you. (It&amp;rsquo;s interesting that
the actual Twinkie diet guy didn&amp;rsquo;t just eat Twinkies. A full third of his
calories came from fruits and vegetables along with a protein shake. Eating no
protein at all for months on end would probably not have gone well.)&lt;/p&gt;
&lt;p&gt;To be fair, the book also suggests eating a balanced diet. As a vegan, I think
there&amp;rsquo;s some cause for me to be concerned, especially on a diet as restricted
as mine. I took a vegan multivitamin (&lt;a href="https://www.devanutrition.com/"&gt;DEVA&lt;/a&gt;
tiny) every day of the diet, to ensure I wasn&amp;rsquo;t vitamin deficient. I really 
like this multivitamin because (a) it provides ~100% RDA of vitamins I was
likely to be deficient on, instead of 10000% like some multis with potential
side effects, and (b) it was small and easy to swallow. I also have a B-12
supplement that I take about once a week (as do most strict vegans).&lt;/p&gt;
&lt;p&gt;So the remaining concern was protein. Getting enough protein isn&amp;rsquo;t generally a
problem for vegans&lt;sup id="fnref:15-23"&gt;&lt;a class="footnote-ref" href="#fn:15-23"&gt;23&lt;/a&gt;&lt;/sup&gt; and even on a restricted diet it wasn&amp;rsquo;t one for me 
either, at least in terms of quantity. The issue is that there are
&lt;a href="https://en.wikipedia.org/wiki/Essential_amino_acid"&gt;minimum amounts&lt;/a&gt; of 
specific amino acids that we have to consume for building cells and other
important stuff our bodies do. I wanted to make sure I was hitting the WHO
minimum recommendations fairly consistently, even though I didn&amp;rsquo;t think a mild
deficiency for a few months was likely to be harmful. (After all, the Twinkie
guy didn&amp;rsquo;t fall apart.)&lt;/p&gt;
&lt;p&gt;In order to hit these targets, I ended up on a diet that was pretty protein
loaded. I hit the recommended amino acid intakes almost every day, even though
I was on a diet with considerably fewer calories. At the same time, 
I also emphasized eating a lot of fiber and low-density foods. The point of
this is that both fiber and foods with a high bulk to calorie ratio will make 
you feel full. Note that neither of these strategies will directly cause you to
lose more weight than otherwise. They&amp;rsquo;re simply ways to make a low calorie diet
easier to maintain by reducing hunger.&lt;sup id="fnref:15-24"&gt;&lt;a class="footnote-ref" href="#fn:15-24"&gt;24&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Many vegan foods are complete proteins (that is, they include all of the 
essential amino acids). Most aren&amp;rsquo;t perfectly balanced between the amino
acids, so you might have to eat more than enough of one to get enough of all
the others. (This is one of the reasons why a balanced diet is often suggested,
since eating many different protein sources will help balance the ratios out.)
Note that this problem isn&amp;rsquo;t unique to plant-based diets — beef isn&amp;rsquo;t perfectly
balanced either, nor are most other animal-foods. It&amp;rsquo;s only something to worry
about for someone on an intense calorie restriction.&lt;/p&gt;
&lt;p&gt;In trying to come up with meal plans, I found &lt;a href="https://www.wolframalpha.com"&gt;WolframAlpha&lt;/a&gt; indispensable. You can enter an amount of food,
and it will tell you how much of each amino acid it has (assuming it&amp;rsquo;s in their
database). &lt;/p&gt;
&lt;p&gt;Lentils and soy turned out to be useful for hitting my high protein and fiber
requirements on a low calorie diet. It&amp;rsquo;s actually possible to hit the WHO amino
acid recommendations and much more than 100% of your fiber using mostly these
sources on a mere 1200 calories a day.&lt;/p&gt;
&lt;p&gt;You don&amp;rsquo;t have to eat the same thing constantly on this diet. (In fact the
whole point is that you don&amp;rsquo;t need to be precise about what you eat or count
calories exactly.) However, if you&amp;rsquo;re cutting so much that you need to be
concerned about nutritional deficiencies, planning your meals may help. It&amp;rsquo;s
probably still best to mix it up some of the time, just to ward off any
additional danger of malnutrition.&lt;/p&gt;
&lt;p&gt;On many days, I ate whatever kind of food I wanted (or even went out to eat)
and just went with a rough estimate of my calories. The point is that as long
as you are hitting your nutritional requirements, and can get even a &lt;em&gt;rough&lt;/em&gt;
estimate of the number of calories you eat, you&amp;rsquo;ll be fine. The best part is
that you don&amp;rsquo;t even have to write anything down if you don&amp;rsquo;t want. It&amp;rsquo;s a
calorie &amp;ldquo;counting&amp;rdquo; diet, but all the counting is quietly being done
automatically for you by enhancing the quality of your &lt;em&gt;estimations&lt;/em&gt; over time
until they&amp;rsquo;re about as good as a real count. (Of course, should you struggle
making the necessary adjustments to hit your targets, you might find a food
diary indispensable, at least for a short time.)&lt;/p&gt;
&lt;p&gt;If it is at all feasible for you, I do suggest making as many of your own meals
as possible, and making them as highly nutritious as you can. Fast food is
far worse than you or I probably realize, and it tends to be extremely calorie
dense without satisfying.&lt;/p&gt;
&lt;h1&gt;Keys to success&lt;/h1&gt;
&lt;p&gt;I think adopting the following attitudes and practices while dieting helped me
achieve success:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Commit to a lifestyle change, not a brief period of restriction.&lt;/p&gt;
&lt;p&gt;As discussed above, this is actually mandatory if you want to keep the
weight off afterwards. This means that you need to recognize that you&amp;rsquo;ll be
giving something up (you will have to &lt;em&gt;permanently&lt;/em&gt; eat less food), and
decide that the cost is worth it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Commit to weighing yourself every day.&lt;/p&gt;
&lt;p&gt;Forming any habit can be irritating, but when your body has been sending
you dishonest signals for years, this is really the only choice. Many
people who are successful at maintaining weight loss continue to weigh
themselves regularly for &lt;em&gt;years&lt;/em&gt; afterwards.&lt;sup id="fnref:15-25"&gt;&lt;a class="footnote-ref" href="#fn:15-25"&gt;25&lt;/a&gt;&lt;/sup&gt; You&amp;rsquo;ll need to do so &lt;em&gt;at
least&lt;/em&gt; until your hunger system is retrained.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Don&amp;rsquo;t declare any food off limits (other than pre-existing dietary
restrictions).&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s no good reason to set yourself up for a cycle of failure and 
self-blame. One of the most common reasons for dieting failure is that 
people prohibit certain foods entirely, crave them incessantly, and 
then binge when the pressure proves too much or they have a &amp;ldquo;cheat&amp;rdquo; day. 
The resulting feelings of shame may drive them away from the diet entirely, 
especially if the number on the scale goes up the next day. (Usually, this 
is a &lt;em&gt;temporary&lt;/em&gt; result of eating a lot of food. The whole point of using a 
&lt;em&gt;statistical&lt;/em&gt; measure of your weight loss rate is that you don&amp;rsquo;t have to 
worry about the daily fluctuations!) Throw out the concept of cheat days; 
every day is a diet day, but every day is also a day that you &lt;em&gt;may&lt;/em&gt; have a 
doughnut if you absolutely must have one. As long as the line on the weight 
chart keeps going down at the rate it&amp;rsquo;s supposed to, you have nothing to 
worry about.&lt;/p&gt;
&lt;p&gt;Try to develop a more healthy decision making process about what you eat. 
Ask yourself: am I willing to trade being hungry later for eating this 
sugary thing now? Speaking for myself, I ate almost no added sugar during 
the diet. It was never prohibited, though. I put a teaspoon of real 
sweetener in my tea. I had a little square of a chocolate bar once every 
week or two. I had a slice of pie or a few cookies over Thanksgiving and 
Christmas. I had a beer every now and then. Nothing off limits.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Never give up.&lt;/p&gt;
&lt;p&gt;There will be days when you want to. If you have a tendency towards anxiety 
or depression, or you use food to cope with problems, you are going to 
have to face that head on. You&amp;rsquo;ll have to handle feelings of hunger and
reduced energy. It&amp;rsquo;s even likely that there will be days where you screw it
up. The difference between the people who succeed and those who fail is 
that people who succeed &lt;em&gt;don&amp;rsquo;t tell themselves that they failed&lt;/em&gt;. Failure 
isn&amp;rsquo;t what happens when you have one bad day, it&amp;rsquo;s what happens when you 
give up.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h1&gt;Conclusions&lt;/h1&gt;
&lt;p&gt;I lost more than a third of my original body weight. The guarantee that you
&lt;em&gt;will&lt;/em&gt; lose the weight if you follow the simple instructions output by the
program and dutifully weigh yourself every day was enough to push me through
the hunger pangs and inclination to give up. Here are the usual before and
after pictures.&lt;/p&gt;
&lt;figure id="__yafg-figure-41"&gt;
&lt;img alt="Picture of me from 2019" src="/files/beforewl1.jpg" title="Picture taken August 2019 at ~243 pounds. I was in a wedding."&gt;
&lt;figcaption&gt;Picture taken August 2019 at ~243 pounds. I was in a wedding.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure id="__yafg-figure-42"&gt;
&lt;img alt="Picture of me from 2022" src="/files/afterwl1.jpg" title="Picture taken March 2022 at ~160 pounds. Appearance by choice this time. ☺️"&gt;
&lt;figcaption&gt;Picture taken March 2022 at ~160 pounds. Appearance by choice this time. ☺️&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure id="__yafg-figure-43"&gt;
&lt;img alt="Picture of me from 2022" src="/files/afterwl2.jpg" title="Picture taken March 2022. This is the belt I was wearing in the first picture."&gt;
&lt;figcaption&gt;Picture taken March 2022. This is the belt I was wearing in the first picture.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Is this a good way to lose weight? For me, the dominant consideration in
answering this question is &amp;ldquo;is this a &lt;em&gt;successful&lt;/em&gt; way to lose weight?&amp;rdquo;. That&amp;rsquo;s
because most diets are unsuccessful, and so successfully losing a significant
amount of weight is already a high bar to clear.&lt;/p&gt;
&lt;p&gt;I think it&amp;rsquo;s reasonable to say that this can be a &lt;em&gt;safe&lt;/em&gt; way to lose weight.
If losing the standard recommendation of 1-2 pounds a week is a safe rate of
weight loss for you, PyWeight will not tell you to do anything stupid. It is
merely a tool to help keep you on track. You can choose a slower rate of weight
loss and it will come off just as reliably, but will require more patience.&lt;/p&gt;
&lt;p&gt;The other question, of course, is can I keep it off? That remains to be seen,
but I&amp;rsquo;m planning to continue weight tracking for the rest of my life while
eating at maintenance (net-zero calories in-out every month). This is something
PyWeight can also help you do. If you set your desired deficit to zero, any
time you begin to gain weight PyWeight will quickly warn you to reduce your
intake.&lt;/p&gt;
&lt;p&gt;The fact of the matter is that even if I knew for certain that I&amp;rsquo;ll gain it all
back within five years, I&amp;rsquo;d still do it all again. I really do &lt;em&gt;feel&lt;/em&gt; that much
better, just as a result of the weight loss. You can&amp;rsquo;t assess the quality of
someone&amp;rsquo;s life by what they happen to weigh when they die; even if I eventually
regain weight I will have bought myself years of enjoyment in the mean time
that can&amp;rsquo;t be taken away. That said, I understand that most people are looking
for long term weight loss. I hereby commit to publish an update to this article
with my current weight one year from the date this was posted, and then again
a year after that.&lt;/p&gt;
&lt;p&gt;Thanks to anyone who took the time to read all the way through this. Feel free
to ask me any questions that you have.&lt;/p&gt;
&lt;h1&gt;Addendum (2023):&lt;/h1&gt;
&lt;p&gt;It&amp;rsquo;s now been one year since I published this article. Here&amp;rsquo;s my scale as of
this morning:&lt;/p&gt;
&lt;figure id="__yafg-figure-44"&gt;
&lt;img alt="A digital bathroom scale reading 160.6 lbs" src="/files/2023_weight_update.jpg"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;I think it&amp;rsquo;s fair to call this a complete success!&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:15-1"&gt;
&lt;p&gt;PyWeight is free and licensed under the GPL v3, which means you can
modify it under certain conditions to meet your own needs.&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-1" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-2"&gt;
&lt;p&gt;Hall, K. D., Heymsfield, S. B., Kemnitz, J. W., Klein, S., Schoeller, D.
A., &amp;amp; Speakman, J. R. (2012). Energy balance and its components: implications
for body weight regulation. The American journal of clinical nutrition, 95(4),
989–994. &lt;a href="https://pubmed.ncbi.nlm.nih.gov/22434603/"&gt;https://pubmed.ncbi.nlm.nih.gov/22434603/&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-2" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-3"&gt;
&lt;p&gt;The Body Weight Planner by the National Institute of Diabetes and
Digestive and Kidney Diseases: &lt;a href="https://www.niddk.nih.gov/bwp"&gt;https://www.niddk.nih.gov/bwp&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-3" title="Jump back to footnote 3 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-4"&gt;
&lt;p&gt;One failing of the NIDDK model referenced in [3] is that it doesn&amp;rsquo;t seem
to show this when modeling long term maintenance. When you put in a starting
weight of 235 pounds, the model says you would require 3050 calories to
maintain. For someone who gains 85 pounds to reach 235, it says they will
maintain at 3400. No matter how long you run the simulation, it&amp;rsquo;s hard to get it
to drop this maintenance level significantly below 3400. This is logically
impossible. The only way someone &lt;em&gt;becomes&lt;/em&gt; the first sort of person who
maintains at 3050 is to first be a person who weighs 150 pounds and gains 85. It
&lt;em&gt;must&lt;/em&gt; be possible to transition from one to the other, although their model
does not show this. Moreover, if we assume that your metabolism &lt;em&gt;does&lt;/em&gt;
eventually adapt, a 100 calorie decrease in your intake should &lt;em&gt;eventually&lt;/em&gt;
result in a loss of closer to 22 pounds.&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-4" title="Jump back to footnote 4 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-5"&gt;
&lt;p&gt;&lt;a href="https://stateofchildhoodobesity.org/adult-obesity/"&gt;https://stateofchildhoodobesity.org/adult-obesity/&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-5" title="Jump back to footnote 5 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-6"&gt;
&lt;p&gt;&lt;a href="https://www.cdc.gov/obesity/data/adult.html"&gt;https://www.cdc.gov/obesity/data/adult.html&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-6" title="Jump back to footnote 6 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-7"&gt;
&lt;p&gt;This sort of sporadic overeating could actually be a helpful adaptation.
If you live in a place where food is often scarce, a rare occasion when a fatty
or sugary food was available might be a good time to gorge yourself well past
the point of satiation, in anticipation of lean months to come.&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-7" title="Jump back to footnote 7 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-8"&gt;
&lt;p&gt;Hall, K. D., Sacks, G., Chandramohan, D., Chow, C. C., Wang, Y. C., 
Gortmaker, S. L., &amp;amp; Swinburn, B. A. (2011). Quantification of the effect of
energy imbalance on bodyweight. Lancet (London, England), 378(9793), 826–837.
&lt;a href="https://pubmed.ncbi.nlm.nih.gov/21872751/"&gt;https://pubmed.ncbi.nlm.nih.gov/21872751/&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-8" title="Jump back to footnote 8 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-9"&gt;
&lt;p&gt;A study found that 82% of people are within 10% of the BMR estimated by 
the Mifflin St. Jeor equation (including 75% of people with obesity), and more
than half are within 5%. See &lt;a href="https://pubmed.ncbi.nlm.nih.gov/23631843/"&gt;https://pubmed.ncbi.nlm.nih.gov/23631843/&lt;/a&gt;. See
also &lt;a href="https://pubmed.ncbi.nlm.nih.gov/27184275/"&gt;https://pubmed.ncbi.nlm.nih.gov/27184275/&lt;/a&gt; for evidence that metabolic
issues are not (usually) the cause of obesity.&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-9" title="Jump back to footnote 9 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-10"&gt;
&lt;p&gt;Fildes, A., Charlton, J., Rudisill, C., Littlejohns, P., Prevost, A. T.,
&amp;amp; Gulliford, M. C. (2015). Probability of an Obese Person Attaining Normal Body
Weight: Cohort Study Using Electronic Health Records. American journal of public
health, 105(9), e54–e59. &lt;a href="https://pubmed.ncbi.nlm.nih.gov/26180980/"&gt;https://pubmed.ncbi.nlm.nih.gov/26180980/&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-10" title="Jump back to footnote 10 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-11"&gt;
&lt;p&gt;For example, much has been made of the supposed ability of ketogenic
diets to increase metabolism. &lt;a href="https://pubmed.ncbi.nlm.nih.gov/16389240/"&gt;https://pubmed.ncbi.nlm.nih.gov/16389240/&lt;/a&gt;
discusses this and attributes to a low-carbohydrate diet that resulted in
ketosis an increased expenditure of less than 100 calories a day. (This was
attributed to increased thermogenesis, not any special property of ketosis.)
It&amp;rsquo;s not nothing, but it indicates that most of the success of a ketogenic diet
will be from calorie reduction anyway, along with potentially increased satiety.
A high protein diet could also potentially provide a feeling of having more
energy, resulting in an increase in physical activity. In my diet, I decided to
eat plenty of clean protein for exactly these reasons, along with the desire to
achieve a complete amino acid profile. I think these reasons make sense, even
though I&amp;rsquo;m not counting on &amp;ldquo;ketosis&amp;rdquo; as a weight loss mechanism.&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-11" title="Jump back to footnote 11 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-12"&gt;
&lt;p&gt;Given footnote [11], you might expect a typically high-carb vegan diet
to show the opposite effect (reduced metabolism). Not so. On the contrary, a 
study looking at the effects of a vegan diet found that it &lt;em&gt;increased&lt;/em&gt; the 
thermic effect of food by 16%, which was associated with a significant increase
in weight loss compared to a control group. 
&lt;a href="https://pubmed.ncbi.nlm.nih.gov/31021710/"&gt;https://pubmed.ncbi.nlm.nih.gov/31021710/&lt;/a&gt; Despite following a vegan diet
myself, I don&amp;rsquo;t believe the overall results for this or any other diet show
that you can &amp;ldquo;hack&amp;rdquo; the weight loss process by causing the body to run hotter,
at least not significantly. In reading many papers while writing this, what
struck me the most was how many of them were limited or flawed outright. I
believe it is best to follow a diet that is healthy and sustainable over the
long term, even if a few clinical trials would have you believe that one
approach or another could net you a 10-20% faster rate of weight loss. On this
note, one very large study found that following a vegetarian or high-carb diet
was positively correlated with possessing a lower BMI.
&lt;a href="https://pubmed.ncbi.nlm.nih.gov/11320946/"&gt;https://pubmed.ncbi.nlm.nih.gov/11320946/&lt;/a&gt; Thus, regardless of its weight loss
credentials, a high-protein diet may not be as successful at weight
maintenance.&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-12" title="Jump back to footnote 12 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-13"&gt;
&lt;p&gt;A calorie-restriction diet can only guarantee success if you actually
follow it, which means accurately calculating your energy expenditure (the 
number of calories you burn every day) and accurately counting the number of
calories you eat. Both are hard to do. My methodology gives you a 
quasi-guarantee of success if you can follow instructions like &amp;ldquo;eat fewer 
calories this week than last week&amp;rdquo;, even if you don&amp;rsquo;t know your true metabolic
rate or can&amp;rsquo;t count calories accurately.&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-13" title="Jump back to footnote 13 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-14"&gt;
&lt;p&gt;Hall K. D. (2008). What is the required energy deficit per unit weight 
loss?. International journal of obesity (2005), 32(3), 573–576.
&lt;a href="https://pubmed.ncbi.nlm.nih.gov/17848938/"&gt;https://pubmed.ncbi.nlm.nih.gov/17848938/&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-14" title="Jump back to footnote 14 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-15"&gt;
&lt;p&gt;Ibid.&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-15" title="Jump back to footnote 15 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-16"&gt;
&lt;p&gt;I mean this in the sense that well-situated people can have a high
degree of confidence in their success. Luck is involved in the question of
whether you are &amp;ldquo;well-situated&amp;rdquo; or not. Many people are not so fortunate: if
you don&amp;rsquo;t have time to cook your own food, are in a &amp;ldquo;food desert&amp;rdquo;, have limited
access to health care, are the victim of misinformation, or have mental health
issues or an eating disorder, then you&amp;rsquo;re unlucky in the latter sense. The
unfortunate fact of America&amp;rsquo;s public health crisis is that &lt;em&gt;most&lt;/em&gt; people are in
at least one of these groups. That&amp;rsquo;s why public health experts don&amp;rsquo;t see
individual behavioral changes (dieting) as the solution to the problem. It&amp;rsquo;s
not that relatively well-placed, high-information people can&amp;rsquo;t see success (if
they approach dieting with a reasonable plan), it&amp;rsquo;s that most people don&amp;rsquo;t have
that access. So when I say that dieters &lt;em&gt;do&lt;/em&gt; have some control over whether
they succeed, this doesn&amp;rsquo;t mean that a dieter who fails has only themselves to
blame. Individuals are &amp;ldquo;free&amp;rdquo; to quit eating as much in much the same way as
you are &amp;ldquo;free&amp;rdquo; to quit your job. It&amp;rsquo;s true, but it misses the point.&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-16" title="Jump back to footnote 16 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-17"&gt;
&lt;p&gt;Flegal, K. M., Graubard, B. I., Williamson, D. F., &amp;amp; Gail, M. H.
(2005). Excess deaths associated with underweight, overweight, and obesity. 
JAMA, 293(15), 1861–1867. &lt;a href="https://pubmed.ncbi.nlm.nih.gov/15840860/"&gt;https://pubmed.ncbi.nlm.nih.gov/15840860/&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-17" title="Jump back to footnote 17 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-18"&gt;
&lt;p&gt;Flegal, K. M., Kit, B. K., Orpana, H., &amp;amp; Graubard, B. I. (2013). 
Association of all-cause mortality with overweight and obesity using standard 
body mass index categories: a systematic review and meta-analysis. JAMA, 
309(1), 71–82. &lt;a href="https://pubmed.ncbi.nlm.nih.gov/23280227/"&gt;https://pubmed.ncbi.nlm.nih.gov/23280227/&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-18" title="Jump back to footnote 18 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-19"&gt;
&lt;p&gt;Global BMI Mortality Collaboration, Di Angelantonio E, Bhupathiraju ShN,
t al. Body-mass index and all-cause mortality: individual-participant-data
meta-analysis of 239 prospective studies in four continents. Lancet. 
2016;388(10046):776-786. &lt;a href="https://pubmed.ncbi.nlm.nih.gov/27423262/"&gt;https://pubmed.ncbi.nlm.nih.gov/27423262/&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-19" title="Jump back to footnote 19 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-20"&gt;
&lt;p&gt;Flegal KM, Ioannidis JPA. A meta-analysis but not a systematic review:
an evaluation of the Global BMI Mortality Collaboration. J Clin Epidemiol. 
2017;88:21-29. &lt;a href="https://pubmed.ncbi.nlm.nih.gov/28435099/"&gt;https://pubmed.ncbi.nlm.nih.gov/28435099/&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-20" title="Jump back to footnote 20 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-21"&gt;
&lt;p&gt;Aune, D., Sen, A., Prasad, M., Norat, T., Janszky, I., Tonstad,
S.,Romundstad, P., &amp;amp; Vatten, L. J. (2016). BMI and all cause mortality: 
systematic review and non-linear dose-response meta-analysis of 230 cohort 
studies with 3.74 million deaths among 30.3 million participants. BMJ (Clinical 
research ed.), 353, i2156. &lt;a href="https://pubmed.ncbi.nlm.nih.gov/27146380/"&gt;https://pubmed.ncbi.nlm.nih.gov/27146380/&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-21" title="Jump back to footnote 21 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-22"&gt;
&lt;p&gt;Within reason, of course. If you modify the source code of PyWeight
to let you target 5 pounds of weight loss per week, then the program will
happily tell you that you need to eat a &lt;em&gt;negative&lt;/em&gt; number of calories.
Statistics can&amp;rsquo;t force you to start out with sane parameters, but as long as
they are reasonable for someone of your height and weight (e.g. 1-2 pounds a
week for most people), PyWeight will guide you onto the proper path.&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-22" title="Jump back to footnote 22 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-23"&gt;
&lt;p&gt;Craig, W. J., Mangels, A. R., &amp;amp; American Dietetic Association (2009). 
Position of the American Dietetic Association: vegetarian diets. Journal of the
American Dietetic Association, 109(7), 1266–1282.
&lt;a href="https://pubmed.ncbi.nlm.nih.gov/19562864/"&gt;https://pubmed.ncbi.nlm.nih.gov/19562864/&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-23" title="Jump back to footnote 23 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-24"&gt;
&lt;p&gt;This basic strategy of eating plenty of vegetables and fruits, along
with clean protein and fiber to help you feel fuller, is more or less that 
recommended by the UK&amp;rsquo;s National Health Service. 
&lt;a href="https://assets.nhs.uk/tools/download-panels/data/weight-loss/pdf/all-weeks.pdf"&gt;https://assets.nhs.uk/tools/download-panels/data/weight-loss/pdf/all-weeks.pdf&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-24" title="Jump back to footnote 24 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:15-25"&gt;
&lt;p&gt;Wing, R. R., &amp;amp; Phelan, S. (2005). Long-term weight loss maintenance. The
American journal of clinical nutrition, 82(1 Suppl), 222S–225S.
&lt;a href="https://pubmed.ncbi.nlm.nih.gov/16002825/"&gt;https://pubmed.ncbi.nlm.nih.gov/16002825/&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:15-25" title="Jump back to footnote 25 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><published>2022-04-11T19:33:38.358267-07:00</published></entry><entry><id>https://adamfontenot.com/post/libreoffice_7_4_has_a_new_approach_to_text_rendering</id><title>LibreOffice 7.4 has a new approach to text rendering</title><updated>2022-08-18T17:36:37-07:00</updated><author><name>Adam Fontenot</name></author><content type="html">&lt;p&gt;I&amp;rsquo;ve used &lt;a href="https://www.libreoffice.org/"&gt;LibreOffice&lt;/a&gt; for quick word
processing tasks for years now &amp;mdash; since before it was forked from
OpenOffice in fact. However, for years I&amp;rsquo;ve drafted
documents in another tool, before copying them into LibreOffice for
the final export to PDF. The reason is that the font rendering in
LibreOffice is infamous for how terrible it is, with a large number
of issues reporting going back years.&lt;/p&gt;
&lt;p&gt;As an example, one of these reports was 
&lt;a href="https://bugs.documentfoundation.org/show_bug.cgi?id=142940"&gt;mine&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s a brief explanation of the problem. Fonts set the amount of
space (called &amp;ldquo;tracking&amp;rdquo;) around each
&lt;a href="https://en.wikipedia.org/wiki/Glyph"&gt;glyph&lt;/a&gt;. Certain combinations 
of characters do not look right with the default amount of space,
and a feature called pair kerning allows a text renderer (like
LibreOffice) to adjust this space for individual letter pairs. For
instance, the large open bowl of the uppercase &amp;ldquo;C&amp;rdquo; looks too empty
by default, so the next letter is often brought closer to it.&lt;/p&gt;
&lt;p&gt;Unfortunately, LibreOffice&amp;rsquo;s rendering on low resolution screens was
so bad that enabling pair kerning (it has been the default for many
years) made it worse. In some cases the failure is catastrophic:&lt;/p&gt;
&lt;figure id="__yafg-figure-29"&gt;
&lt;img alt="LibreOffice 7.3 rendering &amp;quot;California&amp;quot; with pair kerning" src="/files/libreoffice_font_rendering_california_3xbw_pointscaled.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;This is a real screenshot taken on my computer. I wrote software to
upscale the image so that the problem is clear. Each individual
(red, green, or blue) sub-pixel as shown on my screen is being
re-rendered at nine times its original size, in grayscale. This
helps to capture the effect of
&lt;a href="https://freddie.witherden.org/pages/font-rasterisation/#sub-pixel-rendering"&gt;sub-pixel anti-aliasing&lt;/a&gt;,
which uses the shape of the pixels on modern LCD screens to triple
the effective horizontal resolution of the text. Here is the
original screenshot, for comparison:&lt;/p&gt;
&lt;figure id="__yafg-figure-30"&gt;
&lt;img alt="California, in its original zoom" src="/files/libreoffice_font_rendering_california.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Pair kerning is implicated here because it is actually this feature,
when enabled, that turns &amp;ldquo;California&amp;rdquo; into &amp;ldquo;Califomia&amp;rdquo;. The font
shown here is Liberation Serif 12pt, at 120% page zoom. Hinting is
disabled system-wide.&lt;/p&gt;
&lt;h2&gt;Why is text rendering on LibreOffice 7.3 so bad?&lt;/h2&gt;
&lt;p&gt;The problem was caused by an approach to text rendering that
predates the introduction of the codebase into version control in
the year 2000. Back then, text renderers were rather stupid, and did
not do a good job of rendering strings of text in a readable way on
the screen. LibreOffice (then OpenOffice) therefore did its own
glyph positioning.&lt;/p&gt;
&lt;p&gt;LibreOffice knew where the glyphs &lt;em&gt;should&lt;/em&gt; go (called the printing
position, because it was where the glyphs would be placed when sent
to a high resolution output like a printer), but just sending these
&amp;ldquo;ideal&amp;rdquo; locations to the text renderer would result in subpar text.&lt;/p&gt;
&lt;p&gt;On the other hand, if LibreOffice rendered the text at the preferred
screen position, the width of the text would not match up with the
printer&amp;rsquo;s output (because the screen metrics of the font differ, e.g.
due to &lt;a href="https://en.wikipedia.org/wiki/Font_hinting"&gt;hinting&lt;/a&gt;).
LibreOffice is a WYSIWYG (what you see is what you get) editor, so
this is unacceptable. Lines that exceed the word processor&amp;rsquo;s margins
must reflow, so you would end up with cases where the printed
document would have different lines than the document as shown on
the screen.&lt;/p&gt;
&lt;p&gt;Instead the authors adopted a
&lt;a href="https://wiki.openoffice.org/wiki/Writer/WYSIWYG"&gt;compromise&lt;/a&gt;: 75%
of each glyph&amp;rsquo;s placement is determined by the ideal position
according to the screen metric, and 25% is determined by where the
glyph would be placed if its right border was aligned with the right
border of the ideal printing position. WYSIWYG is made possible by
realigning the text with the printing position every time white space
appears in the text.&lt;/p&gt;
&lt;p&gt;Unfortunately, the end result is not very good by the standards of
a decade later. On a low resolution screen like mine, pixels are
quite large relative to the size of the glyphs:&lt;/p&gt;
&lt;figure id="__yafg-figure-31"&gt;
&lt;img alt="illustration of the size of one pixel" src="/files/onepx_illustration.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The above shows close to ideal kerning for the r/n letter
combination for this font at 12pt on my screen. As you can see,
being off by one pixel is enough to make &amp;ldquo;rn&amp;rdquo; into &amp;ldquo;m&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Despite LibreOffice making its best effort to place glyphs
intelligently, most of that work was thrown away at the last stage
in the rendering process, because the glyph positioning engine did
not support 
&lt;a href="https://freddie.witherden.org/pages/font-rasterisation/#sub-pixel-positioning"&gt;sub-pixel positioning&lt;/a&gt;.
This is a different concept than sub-pixel anti-aliasing. While the
font renderers used by LibreOffice have generally supported the
latter, sub-pixel positioning support is much more recent.
Sub-pixel anti-aliasing (sometimes more generally called sub-pixel
rendering) is the use of the thin red, green, or blue components of
LCD screen pixels to improve the resolution of text. Sub-pixel
positioning &lt;em&gt;uses&lt;/em&gt; anti-aliasing (potentially including sub-pixel
anti-aliasing) in order to render text at positions more precise
than a single pixel.&lt;/p&gt;
&lt;p&gt;This is crucial to the proper appearance of text. Because pixels on
low-resolution screens are so (relatively) large, rounding the
location of glyphs to the nearest pixel generates consistently bad
results. Combine that with pair kerning (which tends to move glyphs
closer together, and also changes the heuristic described above to
favor the printing position), and you&amp;rsquo;re asking for catastrophic
failures.&lt;/p&gt;
&lt;h2&gt;What does LibreOffice 7.4 do differently?&lt;/h2&gt;
&lt;p&gt;For at least the last 22 years, LibreOffice has done its own glyph
positioning under the assumption that the underlying font
rasterization engines were not capable of creating high quality text
on LCD screens when passed the &amp;ldquo;true&amp;rdquo; positions of each character
(i.e. the printing positions).&lt;/p&gt;
&lt;p&gt;This was true for most of LibreOffice&amp;rsquo;s history. After all, while
rendering has been acceptable on Linux for at least a decade (with
the proper settings and a recent enough FreeType), commonly used
libraries like Pango / Cairo did not support sub-pixel positioning
until
&lt;a href="https://blogs.gnome.org/mclasen/2019/08/07/pango-1-44-wrap-up/"&gt;very recently&lt;/a&gt;.
&lt;a href="https://www.qt.io/"&gt;Qt&lt;/a&gt; was ahead of the game for a long time,
supporting it since some time around 2015.&lt;/p&gt;
&lt;p&gt;During the LibreOffice 7.4 development cycle, Caolán McNamara
&lt;a href="https://git.libreoffice.org/core/+/39a57fa8c047227060915534e64c4e90affa4b1a%5E%21"&gt;experimented&lt;/a&gt;
with this behavior, initially adding an option to just kick the
printing positions down to the rasterizer and let it place them on
the screen using its own algorithms. The result was successful
enough and fixed so many
&lt;a href="https://bugs.documentfoundation.org/show_bug.cgi?id=144862"&gt;bugs&lt;/a&gt;
that it was made the default mode in LibreOffice 7.4.&lt;/p&gt;
&lt;p&gt;Because the underlying rasterizers now support sub-pixel
positioning, this change results in LibreOffice getting that for
free, in addition to the intended change of eliminating the buggy
positioning code from the render path.&lt;/p&gt;
&lt;p&gt;The results are breathtaking:&lt;/p&gt;
&lt;figure id="__yafg-figure-32"&gt;
&lt;img alt="gif of improvements with McNamara patches" src="/files/california_fixed.gif"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;It&amp;rsquo;s rare to see such an enormous improvement in a piece of software
in a minor version bump, and this one appears to have gone almost
unnoticed. It&amp;rsquo;s even rarer to see an improvement like this with a
simplifying change, in this case one that makes a bunch of old code
irrelevant.&lt;/p&gt;
&lt;p&gt;My thanks to Caolán McNamara for working on this, and for
backporting the patches to LibreOffice 7.3 at my request.
LibreOffice is now a viable choice for me when drafting documents.&lt;/p&gt;
&lt;p&gt;I hesitate to share the following before and after picture of some
text at the original size for fear it may be misconstrued. Don&amp;rsquo;t
expect this text to look great on your system, even if you view it
at 100% zoom (which you should &amp;mdash; right click to open the image in
a new tab). Font rendering happens at a very low level, relying on
details like screen DPI and pixel orientation that may vary from
screen to screen. This is also a bit of a stress test, rendering
some serif fonts with many details on a low resolution device at
12pt.&lt;/p&gt;
&lt;figure id="__yafg-figure-33"&gt;
&lt;img alt="comparison between 7.3 and 7.4" src="/files/lo_before_after.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;Rather than deciding which rendering looks better to you overall,
focus on the differences in glyph placement. Where are the glyphs
too close together? Where are there ugly gaps? To my eye,
LibreOffice 7.4 has significantly reduced severe positioning errors
(&amp;ldquo;met&amp;rdquo; in the fourth line of the Garamond sample) while achieving
superior rendering even in cases where LibreOffice was already
acceptable (every &amp;ldquo;th&amp;rdquo; pair is slightly too close in Caslon), thanks
to sub-pixel positioning.&lt;/p&gt;</content><published>2022-08-18T17:36:37-07:00</published></entry><entry><id>https://adamfontenot.com/post/improving_font_rendering_in_old_ui_toolkits_with_a_1_line_pango_patch</id><title>Improving font rendering in old UI toolkits with a one line Pango patch</title><updated>2023-04-18T21:46:09.635740+00:00</updated><author><name>Adam Fontenot</name></author><content type="html">&lt;h2&gt;Context&lt;/h2&gt;
&lt;p&gt;First let me give enough technical information to make this blog post
comprehensible to newcomers, and then dive in with a little bit of
history. Those already familiar with how font rendering works can skip
the next section.&lt;/p&gt;
&lt;h3&gt;Font rendering basics&lt;/h3&gt;
&lt;p&gt;Most computer screens are made up of little square pixels. On most of these
screens, each pixel is composed of three subpixels &amp;mdash; vertical columns of
red, green, and blue LEDs (usually in that order). In other words, the entire
pixel is never &amp;ldquo;blue&amp;rdquo;; a pixel appears blue to you when its blue subpixel is
turned on, and its red and green subpixels are turned off.&lt;/p&gt;
&lt;p&gt;Modern fonts are vector based. This means that instead of being defined in
terms of which pixels are to be turned on, and which are to be turned off,
the font designer instead specifies &lt;em&gt;precisely&lt;/em&gt; the shape of the corners and
the curves for each glyph, and the font rendering system is responsible for
deciding which pixels to activate. This is why you can zoom into the text on
a webpage without it looking blurry or blocky.&lt;/p&gt;
&lt;p&gt;So how does the font rendering system decide whether to turn a pixel on or
off? Well, the simplest way to do that (for black text on a white background)
is to pretend that the pixel is a square. Ask what portion of the square is
&amp;ldquo;covered&amp;rdquo; by the glyph you are drawing; if that number is greater than 50%,
make the pixel black. Otherwise, make it white. Unfortunately this looks
terrible. The pixels on almost every screen are large enough that if you draw
fonts this way, you&amp;rsquo;ll be able to see the blocky pixel edges.&lt;/p&gt;
&lt;p&gt;The solution is called anti-aliasing. As a bit of a simplification, you can
imagine that instead of drawing each pixel black or white, you instead take
the percentage of the pixel that is covered by the glyph and make the pixel
that fraction black. So if the 75% of the pixel is covered by the glyph, you
set the pixel to 0.25 * maximum brightness (all white).&lt;/p&gt;
&lt;p&gt;This is complicated slightly by subpixel anti-aliasing. On many screens,
especially older ones, the pixels are still large enough that the text is
hard to read or blurry when you render this way. However, remember how I said
that each pixel actually has three vertical subpixels? What if your renderer
treated each of these as separate for the purpose of anti-aliasing? It would
then be able to increase the horizontal resolution of text by 3 times! That&amp;rsquo;s
exactly what subpixel anti-aliasing is.&lt;/p&gt;
&lt;p&gt;Done naively, this results in strong color fringing on the edge of characters,
since if the left edge of a character is just barely in a pixel, you have to
activate the &amp;ldquo;blue&amp;rdquo; subpixel (on the pixel&amp;rsquo;s right edge), and not the green or
red ones. So it&amp;rsquo;s usually combined with what&amp;rsquo;s called LCD filtering, which is
a set of complicated techniques for adjusting the anti-aliasing so as to
minimize these color fringes as much as possible. The resulting approach has
been around for decades now, most prominently as Microsoft&amp;rsquo;s ClearType. More
generally, it&amp;rsquo;s also sometimes called subpixel &lt;em&gt;rendering&lt;/em&gt;, although I find
that label unhelpful.&lt;/p&gt;
&lt;p&gt;I say all that because these techniques are well known, and it&amp;rsquo;s important to
clarify that I&amp;rsquo;m talking about something &lt;em&gt;different&lt;/em&gt; that has been around on
most operating systems for much less time. Let me introduce this concept with
a new question: &lt;em&gt;where&lt;/em&gt; on the screen can you place a glyph?&lt;/p&gt;
&lt;p&gt;This may seem like a silly question, but hold on. My screen has 1920 pixels
in each row. Does that mean that there are 1920 positions at which a font
renderer could insert an &amp;ldquo;a&amp;rdquo; character, for instance? No. Imagine looking at
a book, which we&amp;rsquo;ll say is printed with traditional hot metal typesetting. No
pixels were involved anywhere in this process. There was only the &lt;em&gt;right&lt;/em&gt;
place to put the &amp;ldquo;a&amp;rdquo;, and an infinite number of wrong places. Will the right
place to put each glyph always align with the same position relative to the
pixel? Of course not!&lt;/p&gt;
&lt;p&gt;But that&amp;rsquo;s exactly what most font rendering engines assumed, for years and
years. (Turns out there are good reasons to do this: it improves performance
by reducing the size of the font cache.) &amp;ldquo;Wait,&amp;rdquo; you might say. &amp;ldquo;Old screens
had &lt;em&gt;huge&lt;/em&gt; pixels! Didn&amp;rsquo;t this look terrible?&amp;rdquo; Yes. Yes it did. But there
were bigger problems; with huge pixels, complex vector fonts can be very hard
to read. To fix this, font renderers adopted a technique we haven&amp;rsquo;t discussed
yet called &lt;em&gt;hinting&lt;/em&gt;. This basically amounts to mangling the shape of each
glyph so that it aligns better with the pixel edges. The result is sharper,
&lt;em&gt;sometimes&lt;/em&gt; easier-to-read text. Strong hinting is a key component of
ClearType, whereas macOS is famous for doing little or no hinting in order to
better preserve glyph shape. For our purposes, all that it&amp;rsquo;s important to know
is that after distorting glyphs to fit the screen pixels, it doesn&amp;rsquo;t matter so
much what the correct pixel &lt;em&gt;offsets&lt;/em&gt; are. Getting them right might even be
counterproductive if it makes the glyph blurrier.&lt;/p&gt;
&lt;p&gt;So for a while, there was little reason to bother with subpixel positioning
(as opposed to subpixel &lt;em&gt;rendering&lt;/em&gt;, discussed above). But times changed,
screens improved, and now it&amp;rsquo;s important. This is in part because hinting has
fallen out of fashion, except on desktop Windows.&lt;/p&gt;
&lt;p&gt;If you would like to learn more about these topics, the best introduction I
know is the slightly outdated
&lt;a href="https://freddie.witherden.org/pages/font-rasterisation/#sub-pixel-rendering"&gt;Treatise on Font Rasterisation&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;A brief technical history&lt;/h3&gt;
&lt;p&gt;In 2019, the text rendering library Pango finally released with support for
subpixel positioning, enabled by default. This had made many people very angry
and has been widely regarded as a bad move.&lt;/p&gt;
&lt;p&gt;The issue is that Pango depends on another library called Cairo, and at the
point when Pango released this feature, Cairo
&lt;a href="https://blogs.gnome.org/mclasen/2019/08/07/pango-1-44-wrap-up/"&gt;did not yet support it&lt;/a&gt;.
The result, if you had the most recent releases of both Pango and Cairo, was
&amp;ldquo;unpleasantly uneven glyph placement&amp;rdquo; to quote the Pango developer. As a
consequence, they reverted the change in
&lt;a href="https://download.gnome.org/sources/pango/1.44/pango-1.44.3.news"&gt;Pango 1.44.3&lt;/a&gt;.
Unfortunately, this decision was never revisited. Programs (and UI toolkits)
must still opt in to subpixel positioning, despite the fact that a version of
Cairo that supports it was released years ago now.&lt;/p&gt;
&lt;p&gt;Recent UI toolkits (like Qt or GTK) either enable subpixel positioning support
in Pango (GTK) or implement it themselves (Qt). However, not all software uses
the latest versions of these toolkits. How serious is this problem?&lt;/p&gt;
&lt;p&gt;For Qt it&amp;rsquo;s not that bad. Both Qt 5 and 6 support subpixel positioning, which
covers the vast majority of applications. However, only GTK 4 enables it. That
leaves GTK 2 and GTK 3. Is this an issue in practice?&lt;/p&gt;
&lt;p&gt;As of April 2023, on Arch Linux,&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;86 packages require GTK 4&lt;/li&gt;
&lt;li&gt;561 packages require GTK 3&lt;/li&gt;
&lt;li&gt;125 packages require GTK 2&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Note that this is only a rough estimate, for a variety of reasons. It suggests
a bad state of affairs, nonetheless. Only ~11% of packages that require one of
GTK 2/3/4 depend on GTK 4. This is surprising, since the last major release
of GTK 2 was in
&lt;a href="https://mail.gnome.org/archives/gtk-devel-list/2011-January/msg00042.html"&gt;January 2011&lt;/a&gt;,
and it was officially end-of-lifed in
&lt;a href="https://blog.gtk.org/2020/12/16/gtk-4-0/"&gt;2020&lt;/a&gt;, but it&amp;rsquo;s what we have to
deal with. It probably makes sense to plan on supporting GTK 3 applications
indefinitely.&lt;/p&gt;
&lt;p&gt;I asked the GTK developers about adding support for subpixel positioning to
GTK 3, but they said that development focus was entirely on GTK 4 and that GTK
3 wouldn&amp;rsquo;t receive new features. That puts us at something of an impasse. The
Pango developers haven&amp;rsquo;t made subpixel positioning the default. GTK developers
are only working on GTK 4. And the vast majority of GTK applications are using
GTK 2 or 3.&lt;/p&gt;
&lt;h2&gt;Let&amp;rsquo;s fix it ourselves&lt;/h2&gt;
&lt;p&gt;Applications using pango are supposed to call&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pango_context_set_round_glyph_positions (context, FALSE)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;to disable rounding glyph positions to whole pixels. If an application doesn&amp;rsquo;t
set the option, it falls back on Pango&amp;rsquo;s default (rounded positions, so no
subpixel positioning). Hold on, though. The legacy UI frameworks aren&amp;rsquo;t
explicitly disabling subpixel positioning &amp;mdash; they&amp;rsquo;re just not enabling it. So
if we change the default in Pango, we&amp;rsquo;ll immediately get &lt;em&gt;everything&lt;/em&gt; using
Pango working, for free!&lt;/p&gt;
&lt;p&gt;Turns out this is a one line code change.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;diff --git a/pango/pango-context.c b/pango/pango-context.c
index cbc4f173..82b44136 100644
--- a/pango/pango-context.c
+++ b/pango/pango-context.c
@@ -69,7 +69,7 @@ pango_context_init (PangoContext *context)
context-&amp;gt;set_language = NULL;
context-&amp;gt;language = pango_language_get_default ();
context-&amp;gt;font_map = NULL;
-  context-&amp;gt;round_glyph_positions = TRUE;
+  context-&amp;gt;round_glyph_positions = FALSE;

context-&amp;gt;font_desc = pango_font_description_new ();
pango_font_description_set_family_static (context-&amp;gt;font_desc, "serif");
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Starting applications after compiling in this change was nothing short of
stunning. I think my jaw actually dropped. (Note: the &amp;ldquo;large text&amp;rdquo; images
are redrawn from actual screenshots by treating each vertical subpixel like
a 3x1 grayscale pixel.&lt;/p&gt;
&lt;p&gt;The Quod Libet music player (gif):&lt;/p&gt;
&lt;figure id="__yafg-figure-45"&gt;
&lt;img alt="before and after gif" src="/files/pango_before_after.gif"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The GIMP about screen (before):&lt;/p&gt;
&lt;figure id="__yafg-figure-46"&gt;
&lt;img alt="before image" src="/files/gimp_before_pango_patch.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;The GIMP about screen (after):&lt;/p&gt;
&lt;figure id="__yafg-figure-47"&gt;
&lt;img alt="after image" src="/files/gimp_after_pango_patch.png"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;A gif close-up. Note: since my blog shrinks images to fit in the margins, the
best way to look at this image is by opening it in a new tab.&lt;/p&gt;
&lt;figure id="__yafg-figure-48"&gt;
&lt;img alt="before vs after gif" src="/files/gimp_before_after_pango.gif"&gt;
&lt;figcaption&gt;&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p&gt;That&amp;rsquo;s a huge difference! I&amp;rsquo;m going to add Pango to the slowly growing list of
packages I have to build myself to include important patches. The results,
though, are worth it.&lt;/p&gt;</content><published>2023-04-11T17:45:00-04:00</published></entry><entry><id>https://adamfontenot.com/post/they_static_now__they_static_now_</id><title>They static now? They static now!</title><updated>2024-02-27T22:42:20+00:00</updated><author><name>Adam Fontenot</name></author><content type="html">&lt;h2&gt;Origins&lt;/h2&gt;
&lt;p&gt;When I started this site back in 2014, I only had a couple of years
of experience writing Python, but I wanted to try writing a real
(meaning &amp;ldquo;dynamic&amp;rdquo;) site, so I went with the Flask framework.&lt;/p&gt;
&lt;p&gt;The site started as a single Python file and a few static CSS files
and Jinja2 templates. When I did the conversion this weekend, it was
still a single Python file, a few static CSS files, and the same
Jinja templates.&lt;/p&gt;
&lt;p&gt;My early inexperience resulted in a site with some odd design
decisions, and in the first year or two I added features (like an RSS
feed) by adding more functions to my single file. Don&amp;rsquo;t get me wrong,
it&amp;rsquo;s cool that you can write a real web server so easily, but the
result was an unmaintainable mess with some very questionable
decisions.&lt;/p&gt;
&lt;p&gt;One early decision I made was to avoid using any kind of database. My
blog posts were (and are) simple Markdown files. To add a new post to
the site, I simply copied it to a directory on my server and the Flask
application did the rest. I went with the easiest possible solution to
most problems; for example, the URL paths to individual posts were
simple numbers counting up from the first post, &lt;code&gt;/post/0&lt;/code&gt; for example.&lt;/p&gt;
&lt;p&gt;This led to problems, because the post numbering is just determined by
the &lt;code&gt;.md&lt;/code&gt; files the Flask application finds. Flask is not an
especially friendly framework for, say, running a background task at a
specific interval and setting a global variable containing the markup
for all the blog posts it finds. The easy solution is to look up the
files every time someone visits the page, so that&amp;rsquo;s what I did.&lt;/p&gt;
&lt;p&gt;Even worse, there&amp;rsquo;s not a one-to-one relationship between the post
paths and the Markdown files on disk. If someone sends &lt;code&gt;GET /post/5&lt;/code&gt;
then what post does that represent? For all the Flask app knows, a new
post has been added with a modification time in between what &lt;em&gt;used&lt;/em&gt; to
be posts 3 and 4, so the only thing to do is reload every single post
on every single page view.&lt;/p&gt;
&lt;p&gt;I partially rewrote the application in 2019. Because the code had
turned into such an ugly mess, it wasn&amp;rsquo;t fun to work with, so I once
again turned to easy solutions. I put a bunch of caching in front of
the heavy calls, so that only the first request for a post would be
slow, and then the rest would be fast, inside the cache window. I also
finally switched to URL paths based on the title by default. This
still didn&amp;rsquo;t provide a one-to-one relationship between URLs and
Markdown files (since characters that aren&amp;rsquo;t safe get removed), so
this didn&amp;rsquo;t solve the underlying issue of having to fetch all the
posts on every page view.&lt;/p&gt;
&lt;p&gt;For a long time, I intended to migrate the blog off the old server
(running Ubuntu 18.04!) to a new one. Over the weekend, I finally got
around to this, and in the process fixed the issues with the site.&lt;/p&gt;
&lt;h2&gt;Changes&lt;/h2&gt;
&lt;p&gt;My criteria for the transition were the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The site must not visually change.&lt;/li&gt;
&lt;li&gt;The site should be static HTML.&lt;/li&gt;
&lt;li&gt;The static HTML for existing posts must not be created via scraping
   the old site and saving the files.&lt;/li&gt;
&lt;li&gt;The process for writing new posts should be relatively painless.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The original development of the site, as I have said, was a learning
opportunity for me. I&amp;rsquo;ve made other (and much better) sites since, and
these don&amp;rsquo;t have quite so many problems! I was stuck on the old site
primarily because the obvious transition (to static HTML) seemed so
difficult given my requirements.&lt;/p&gt;
&lt;p&gt;When I finally found the time, I decided to try an interesting
approach. Rewrite my existing code once more, to remove bugs, and also
remove the dependency on Flask. Instead, I have a &lt;code&gt;main()&lt;/code&gt; function
that collects changes to the source files and renders them via the
same functions called by Flask. The resulting HTML is then written to
disk in a directory specified by a config file, and I &lt;code&gt;rsync&lt;/code&gt; the
result to my server.&lt;/p&gt;
&lt;p&gt;Overall, this turned out to be way easier than I expected, and in fact
I fixed quite a few issues with the legacy code in the process.&lt;/p&gt;
&lt;p&gt;Perhaps most importantly, I decided to just use Jinja2 directly. This
allows me to use my existing templates, meaning that the site doesn&amp;rsquo;t
visually change at all. I performed some cleanup, but in other
respects this required minimal work.&lt;/p&gt;
&lt;p&gt;You can replace Flask&amp;rsquo;s ubiquitous &lt;code&gt;render_template&lt;/code&gt; with a simple
shim:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;    def render_template(template_name, *args, **kwargs):
        template = JINJA.get_template(template_name)
        return template.render(*args, **kwargs)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;JINJA&lt;/code&gt;, here, is just a global Jinja environment.&lt;/p&gt;
&lt;p&gt;Perhaps most importantly, I&amp;rsquo;ve developed a workflow for shipping
Python apps that I&amp;rsquo;m pretty happy with. With my deployment scripts, I
can recreate my sites on a new server in a matter of minutes.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;m also no longer relying on file modification times for my &amp;ldquo;last
updated&amp;rdquo; dates on posts, thanks to the
&lt;a href="https://python-markdown.github.io/extensions/meta_data/"&gt;meta&lt;/a&gt;
extension for Python-Markdown, which I actually added back in 2019.&lt;/p&gt;
&lt;p&gt;Anyway, this update isn&amp;rsquo;t terribly interesting to most people, I&amp;rsquo;m
sure. I&amp;rsquo;m writing this to reward myself for getting it done. I do
think the idea of rewriting existing server code to just render
templates is a good one, so if you&amp;rsquo;re in a similar situation with your
own site, perhaps give that a try.&lt;/p&gt;</content><published>2024-02-27T22:42:20+00:00</published></entry><entry><id>https://adamfontenot.com/post/why_dune_ii_undermines_its_protagonist_s_achievements</id><title>Why Dune II undermines its protagonist's achievements</title><updated>2024-03-08T21:27:48+00:00</updated><author><name>Adam Fontenot</name></author><content type="html">&lt;h1&gt;DUNE&lt;/h1&gt;
&lt;p&gt;I wanna talk about Dune, and this is my blog, so we&amp;rsquo;re gonna talk about
Dune. This post will contain spoilers for the films &lt;em&gt;Dune I&lt;/em&gt; and &lt;em&gt;II&lt;/em&gt;.
Please mind the caveat that I haven&amp;rsquo;t read the &lt;em&gt;Dune&lt;/em&gt; books, and I&amp;rsquo;m
unfamiliar with most writing, academic and otherwise, about &lt;em&gt;Dune&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;There is a lot to say about &lt;em&gt;Dune II&lt;/em&gt;. The film seems to invite
comparison, criticism, and debate. Obvious responses range from the
perfunctory &amp;mdash; &amp;ldquo;ambitious&amp;rdquo; &amp;mdash; to the superlative &amp;mdash; &amp;ldquo;the most
ambitious fantasy epic since &lt;em&gt;The Lord of the Rings&lt;/em&gt;.&amp;rdquo; True, but doesn&amp;rsquo;t
capture what makes it interesting.&lt;/p&gt;
&lt;p&gt;There&amp;rsquo;s enough ambition in recent cinema. The arthouse and import
markets are more widely accessible than ever. Even in the mainstream,
Martin Scorsese is making three-and-a-half hour period pieces for Apple
TV in English and subtitled Osage. &lt;em&gt;Dune II&lt;/em&gt; is long, and it&amp;rsquo;s slow
paced, and that makes it, if not ambitious exactly, then at least proof
of what the popcorn munchers are now willing to put up with. (In my
case, I also put up with a projector with a dozen dead pixels.)&lt;/p&gt;
&lt;p&gt;In reaction to this emphasis on &lt;em&gt;Dune II&amp;rsquo;s&lt;/em&gt; artistic pretensions, one
might espouse the notion that the film succeeds primarily as pure
entertainment. I mean, yeah, it&amp;rsquo;s really good? I don&amp;rsquo;t claim to know
what brings people like me back to the theater. Certainly not two hours
of inhaling our late arriving neighbor&amp;rsquo;s perfume. I enjoyed every minute
of &lt;em&gt;Dune II&lt;/em&gt;, that much is true. Judging the film purely on this axis is
overly simplistic, though. The way I enjoyed &lt;em&gt;Dune II&lt;/em&gt; is more like the
way I enjoyed &lt;em&gt;Ben-Hur&lt;/em&gt; than &lt;em&gt;Mad Max: Fury Road&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;I want to try out some ways of characterizing the kind of
story &lt;em&gt;Dune II&lt;/em&gt; tells, and try to work out how this kind of storytelling
might be at the root of what makes the film work.&lt;/p&gt;
&lt;h2&gt;Kings and Queens&lt;/h2&gt;
&lt;p&gt;Royalty feature heavily in theater. Drama&amp;rsquo;s love of kings derives not
(or not only) from their patronage but more directly from the fact that
they are Subjects, writ large. The dramatic monarch is a being with
nearly unlimited power to shape the world. They enact the ideal of a
human being in that their stories employ a dichotomy of will and world
in clean schematic form that comports with how we understand ourselves.&lt;/p&gt;
&lt;p&gt;There is a terrific film, &lt;em&gt;Deux jours, une nuit&lt;/em&gt;, which illustrates this
point by way of contrast. In the film, the bosses of a manufacturing
plant manage to fire Sandra, a young mother on medical leave, by bribing
her coworkers to vote for her dismissal with a bonus. She rejects the
result as illegitimate because they voted publicly, and pressures her
manager to redo the vote with secret ballots. Granted this reprieve, she
spends the weekend &amp;mdash; the remainder of the film &amp;mdash; trying to convince
her coworkers to vote for her come Monday.&lt;/p&gt;
&lt;p&gt;Sandra is a woman with no power in a desperate situation. The film
depicts human comradery and the material forces that break it down, and
asks how, or whether, people can have a sense of freedom when they lack
control over their circumstances.&lt;/p&gt;
&lt;p&gt;The characters of classical drama have moments of power and success,
weakness and failure, they see the fulfillment of desires and reversals
of fortune. Many a royal Macbeth has faced his Birnam Wood. These men
and women nevertheless have their wills and desires sharply defined; the
world is shaped by them and their contests, like landscapes carved in a
war between giants. They are maximally free in that they possess the
capacity to determine the conditions they will face.&lt;/p&gt;
&lt;p&gt;Sandra resembles most modern protagonists in that her story defines its
world without reference to her; she is not active in its construction.
Her story is not about her efforts to shape this world. Instead, it
forms an unalterable backdrop to her struggle against its effects.&lt;/p&gt;
&lt;p&gt;Most modern characters reside somewhere in the middle of the spectrum I
have drawn between deific beings who shape the world with their wills
and powerless humans buffeted about by an environment they cannot hope
to control. That said, we have largely relocated the former to the realm
of fantasy.&lt;/p&gt;
&lt;p&gt;Perhaps the superhero comes closest to enacting this character type in
its original form. They are never seriously threatened by ordinary
people, who exist in their stories as a kind of foil. Their worlds are
stages for combat, shaped physically by their conflicts. Yet these
characters are not free to act in the way of the queens and kings. Their
limits are set not by the world itself so much as by metatextual
considerations.&lt;/p&gt;
&lt;p&gt;A &amp;ldquo;hero&amp;rdquo;, for example, is by definition a character possessing goals
aligned with the survival and well-being of ordinary humans. Villains
possess the opposite set of motivations, and their stories are about
their attempts to pound one another into submission with raw force, not
to win a contest of wills or political intrigue.&lt;/p&gt;
&lt;p&gt;By contrast, the real life King Henry II participated in two major
controversies. The first was his effort to define and cement his power
as monarch in contradistinction to that of the Church, a struggle that
ended in the murder of the Archbishop Thomas Becket. The second was a
feud between himself, his wife Eleanor, and his sons over his
possessions and the inheritance of his kingdom. This boiled over into
military conflict when several of his sons, along with Eleanor, rebelled
against him.&lt;/p&gt;
&lt;p&gt;With comic book characters, at least on the big screen, &amp;ldquo;their&amp;rdquo;
interests are never really their interests. They are never petty. They
do not engage in decade-long quarrels with the Catholic church over the
right to levy taxes. Even anti-heroes, in addition to their propensity
to violence and unsavory language, are by definition among those who
show up when it&amp;rsquo;s time to get the gang back together to save the Earth
for the umpteenth time. Their interests are still ours, they&amp;rsquo;re just
unreasonably grumpy about it.&lt;/p&gt;
&lt;p&gt;No one really believes in this royal character type any more. Power like
this is parochial at best, and fleeting. Fictional worlds shaped
entirely by their characters are the stuff of the stage, and this stage,
today, is a small one. George and Martha dominate the terrain of &lt;em&gt;Who&amp;rsquo;s
Afraid of Virginia Woolf?&lt;/em&gt;, but this is possible because they are giants
only in the space of their small home.&lt;/p&gt;
&lt;p&gt;Even our ideas of history no longer focus on the capacity of individuals
to shape their present and future. &amp;ldquo;Great man&amp;rdquo; theories have no
traction, and this limits our capacity to accept them in fictional
contexts as well.&lt;/p&gt;
&lt;h2&gt;The Lord of the Rings &lt;em&gt;in space&lt;/em&gt;&lt;/h2&gt;
&lt;p&gt;Aragorn possesses the kingly virtues, by right and by nature, and
governs the world as he wills. Simultaneously, the &lt;em&gt;Rings&lt;/em&gt; books accord
him heroic status &lt;em&gt;and&lt;/em&gt; rule of Gondor by inheritance, but modern
readers will fail to see a connection between these two facts. If
Aragorn had been a bad king, as many of the Numenoreans were, Gondor
would have had to tough it out, and perhaps even fallen into decay. If
he had lacked the capacity for the leadership others entrusted to him,
he would never have become king in the first place.&lt;/p&gt;
&lt;p&gt;As readers (or viewers of an adaptation), we accept this account not
because of any particular theory of kingship but because we understand
Aragorn as a traditional protagonist. Aragorn is the only human being
who can use the &lt;em&gt;palantíri&lt;/em&gt; without corruption. Arthur takes the sword
from the stone. Luke is the only Jedi with the power to confront Darth
Vader. Thor is the only guy who can lift the big hammer.&lt;/p&gt;
&lt;p&gt;If, as I have suggested, we struggle to accept these characters, how
does Tolkien avoid making Aragorn&amp;rsquo;s story risible? In two ways: the
inclusion of antagonist characters of equal or greater power, and with a
extratextual imposition curtailing the ways Aragorn&amp;rsquo;s power can be
expressed. Gandalf, Galadriel, Aragorn, and Elrond could potentially
mount a frontal assault on their enemy, Sauron, but the &lt;em&gt;moral&lt;/em&gt; cost
would be too great. Because they are restrained, the focus of the story
lies instead on the actions of a couple of nobodies who destroy Sauron&amp;rsquo;s
power through an Achilles&amp;rsquo; Heel mechanism.&lt;/p&gt;
&lt;p&gt;Paul Atreides seems cut from similar cloth. Never, in Dune II, does
there seem to be a credible material threat to his aims. He starts the
film down and out, of course, but people instinctively flock to his
leadership. His family&amp;rsquo;s military commander, Gurney Halleck, a William
Marshal type, turns out to be alive and to know the secret location of
his family&amp;rsquo;s stash of nuclear weapons. He has a willing army of millions
in the south, and his opponents are bumbling idiots or smirking
capricious newcomers.&lt;/p&gt;
&lt;p&gt;Like Aragorn, Paul holds back because he knows what will unfold if he
uses these powers wrongly. Something like two thirds of &lt;em&gt;Dune II&lt;/em&gt;
features him wandering around in the desert trying to escape destiny
because he can foresee the violence and suffering that will come as a
result of it. He&amp;rsquo;s the rightful heir of the house of Atreides, but can&amp;rsquo;t
claim it directly. Like Aragorn, his coming is prophesied and awaited
by the faithful.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Is&lt;/em&gt; Dune just &lt;em&gt;Lord of the Rings&lt;/em&gt; in space, then, with guns replacing
swords (wait, the Fremen use swords!)? Well, no, obviously not. There is
no substitute for the cental &lt;em&gt;Paul&lt;/em&gt; plot, no ring to put to the fire and
set all aright. Eventually, as his mother predicts, he follows his fate
to the south. From there, the narrative moves remarkably quickly. He
wins the fundamentalist Fremen sects to his side in mere days with his
fulfillment of prophecy, devises a plan to use nuclear weapons on his
enemies, leads an assault on the capital, captures the emperor, and
kills his one rival for the throne.&lt;/p&gt;
&lt;p&gt;We accept this narrative because Dune II, as it tells this story about
Paul&amp;rsquo;s rise to power, is simultaneously deconstructing narratives about
rising to power. The prophecy itself is a sham, having been invented by
his mother along with a religious sect in order to win him followers. He
must undergo trials to prove himself, like drinking poison, but he has
been trained to be able to drink poison and live. He promises to lead
his followers to &amp;ldquo;Paradise&amp;rdquo;, but this is visually undercut by a shot
showing them boarding starships to go to war with his enemies. His
choices, especially his use of atomic weapons, results in his girlfriend
breaking up with him, and she, no Arwen, is a fiercer warrior than he.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Dune II&lt;/em&gt; redeems the overpowered nature of its lead character with
bitter political cynicism about where that power originates and how it
is sustained. When Immortan Joe promises his followers &amp;ldquo;you will ride
eternal, shiny and chrome&amp;rdquo;, it is not so important whether we believe
him. A belief in an afterlife, a metal Valhalla, is culturally
appropriate for the War Boys. When Paul promises the Fremen to take them
to green places with enormous oceans, it matters that we see cynicism.
Belief and its power are the thematic core of the film, and if Paul is
&lt;em&gt;not&lt;/em&gt; the messiah, the Fremen seem poised to make him so through sheer
conviction. But there &lt;em&gt;is&lt;/em&gt; a difference, and an important one, between
the two.&lt;/p&gt;
&lt;p&gt;Much of the dramatic tension of the film comes from Paul&amp;rsquo;s attempts to
prove himself, as when he rides one of the planet&amp;rsquo;s giant sandworms, but
to what end? The effect of these actions is to convince the Fremen that
he is their prophesied messiah, and he&amp;rsquo;s not. Even if he manages to do
everything that the messiah is supposed to do, it&amp;rsquo;s still the
enactment of a story told for the purpose of manipulating the Fremen,
and gaining power over them, for generations. What good is that to them?&lt;/p&gt;
&lt;p&gt;Paul&amp;rsquo;s fortunes change the moment he embraces this &amp;ldquo;destiny&amp;rdquo;. Rather
than fighting a war of attrition in the north, in a matter of days he&amp;rsquo;s
reclaimed the place of his family on top of Arrakis, and has his sights
set on even greater ambitions. On the scale of a rather plodding film,
it&amp;rsquo;s hard to convey just how fast this seems to happen. There&amp;rsquo;s little
depiction of the difficulty of moving an army of great size hundreds of
miles north, nor of the practical concerns with taking the emperor
captive. Even the fighting, carefully choreographed in earlier scenes,
becomes perfunctory here &amp;mdash; two lines of foot soldiers charging
headlong into each other, with the outcome already decided.&lt;/p&gt;
&lt;h2&gt;The Dunepire strikes back&lt;/h2&gt;
&lt;p&gt;It&amp;rsquo;s at this point in the film that, as in &lt;em&gt;The Empire Strikes Back&lt;/em&gt;,
the fate of the galaxy is determined by single combat. This borders on
the farcical. The emperor appoints a member of the Harkonnen family, a
young man he&amp;rsquo;s only just met, to be his champion. This poor kid never
had a chance. The movie, all too late, tries to set him up as a big-bad
worth fearing, but doesn&amp;rsquo;t (and can&amp;rsquo;t) succeed, because Paul&amp;rsquo;s power by
this point verges on absolute. He can literally see the future, now
thinks in necessities instead of possibilities. Paul&amp;rsquo;s triumph is the
expected victory of brains over brawn, though the film establishes that
he has that too &amp;mdash; the very first thing we see him doing in this series
is practicing combat.&lt;/p&gt;
&lt;p&gt;The film bears other comparisons to &lt;em&gt;Empire&lt;/em&gt; as well. A central
component of Paul&amp;rsquo;s transformation from insouciant desert vagabond to
contender for the throne of the empire is the discovery that he is the
grandchild of Baron Harkonnen.&lt;/p&gt;
&lt;p&gt;When Luke Skywalker is revealed to be the son of his enemy, Darth Vader,
the purpose of the moment is to justify an unexpected choice: Vader
offers Luke an alliance with the goal of overthrowing the emperor.
Downstream from this immediate impact is Luke coming to have the belief
that Vader is not entirely evil, and that their relationship could be
the basis of his redemption.&lt;/p&gt;
&lt;p&gt;This moment spread outward from &lt;em&gt;Empire&lt;/em&gt; to become a cultural
touchstone. In &lt;em&gt;Empire&lt;/em&gt;, the fact of their relationship gives us an
explanation for why Vader does what he does. Rather than simply kill
Luke, he comes up with a solution that would unite them while still
achieving his aims. Likewise, the moment helps us understand why Luke
treats Vader as redeemable, rather than an enemy to be destroyed at all
costs. When this took on cultural significance, the meaning changed. To
reveal that the protagonist is related to the antagonist is to imply not
merely that they can &lt;em&gt;change&lt;/em&gt; to become like one another but that they
are also, fundamentally, already alike in some respect.&lt;/p&gt;
&lt;p&gt;Relying purely on established cultural meaning in this way is lazy
storytelling, and unfortunately &lt;em&gt;Dune II&lt;/em&gt; doesn&amp;rsquo;t rise above it. Paul
does not take his ties to the Harkonnens as a reason to believe he can
reason with them, or to give him a moral obligation to win them away
from their violent ways, but as reason to believe &lt;em&gt;of himself&lt;/em&gt; that
&amp;ldquo;fighting like Harkonnens&amp;rdquo; would be appropriate. This makes no sense at
all in the context of the story; as audience we only comprehend this
because we know the cultural signification of revealing a protagonist&amp;rsquo;s
heritage. Even if we take for granted the racial assumption that
Harkonnens are inherently violent, this at best gives us an explanation
for why Paul might be prone to violence (though the film seems to have
established that he is not). It does &lt;em&gt;not&lt;/em&gt; give Paul himself a reason to
start behaving more violently than he otherwise would have!&lt;/p&gt;
&lt;p&gt;In the context of Paul&amp;rsquo;s arc, this revelation is the inflection point in
a radical transformation. Paul begins the film determined not to engage
the fundamentalist Fremen because of his visions, which prognosticate
mass suffering and death if he does so. He reverses course only after
the Harkonnens destroy the Fremen&amp;rsquo;s northern stronghold, making it clear
that a long painful defeat will result from his current plans. He drinks
a potion which radically enhances his powers of foresight, at which
point he immediately embraces his apparent destiny as Fremen messiah
figure and future ruler of the empire.&lt;/p&gt;
&lt;p&gt;Paul defends his change of plans to his Fremen girlfriend, Chani, by
stating that the path he takes is the only way. Unfortunately, we have
little means by which to judge this claim. We&amp;rsquo;re shown only fleeting
images from his visions, and they not only don&amp;rsquo;t explain his actions, it
isn&amp;rsquo;t even clear what he means by the claim that no other choice is
possible.&lt;/p&gt;
&lt;p&gt;No other choice &lt;em&gt;for whom?&lt;/em&gt; Whose aims does he prioritize? If Paul takes
the path he does because otherwise the Harkonnens will remain in control
of Arrakis and his father will remain unavenged, but trades for this the
collapse of the empire and a horrific civil war, that&amp;rsquo;s an &lt;em&gt;interesting&lt;/em&gt;
thing for him to do. It reflects the private ambitions and unique
loyalties that we expect from a royal character. &lt;em&gt;Dune II&lt;/em&gt;, instead,
muddies the water by giving us no clear sense of what choice, exactly,
has been made.&lt;/p&gt;
&lt;p&gt;Chani, understandably enough, dislikes this profound change in Paul&amp;rsquo;s
personality and drive. She takes on the role of viewer insert in the
film. We&amp;rsquo;re clearly meant to disapprove of Paul&amp;rsquo;s exploitation of
religious fanaticism, and Chani embodies this. In fact she does so &lt;em&gt;too
much&lt;/em&gt;. &lt;em&gt;Dune II&lt;/em&gt; deftly emphasizes showing over telling, but it&amp;rsquo;s
possible to &amp;ldquo;show&amp;rdquo; so much that the audience feels shouted at, and that
happens with many of Chani&amp;rsquo;s scenes. Nearly every time Paul does
something we&amp;rsquo;re meant to feel skeptical of, we&amp;rsquo;re handed a cutaway
to a reaction from Chani, who emotes her strong disapproval. We don&amp;rsquo;t
need this level of confirmation that, in fact, the movie knows it&amp;rsquo;s a
bad thing to seed prophecies about yourself in an isolated community
over multiple generations just so you can come along and fulfill them.&lt;/p&gt;
&lt;h2&gt;Would you still love me if I were a worm?&lt;/h2&gt;
&lt;p&gt;Chani promises Paul that she will stick by him so long as he &amp;ldquo;remains
true to himself&amp;rdquo;. Her rejection of him when he changes course is the
most overt sign that he&amp;rsquo;s gone astray.&lt;/p&gt;
&lt;p&gt;While this allows her to serve as the film&amp;rsquo;s moral center, it also
frustrates our efforts to see her, like Paul, as a complete person. How
&lt;em&gt;would&lt;/em&gt; she respond, for example, if Paul told her that his visions
revealed that the Fremen would be wiped out should he not take the
specific path he does? I think we have to imagine, hewing as she does to
righteous means, that her opinion would be the same. Yet the film misses
the chance to give her depth by having her struggle with questions like
these. Everyone else in the film is granted a point of view, while she
is burdened with carrying the point of view of the filmmaker.&lt;/p&gt;
&lt;p&gt;Paul and Chani consummate their relationship in a scene immediately
after he successfully rides one of the giant sandworms of Arrakis for
the first time. This is not just amusing symbolism, but another instance
of Chani serving as audience surrogate. Timothée Chalamet has, so to
speak, wooed us with his feats, and &lt;em&gt;Dune II&lt;/em&gt; consistently succeeds in
its efforts to feel like a true adventure film, with Paul a
swashbuckling protagonist despite our misgivings.&lt;/p&gt;
&lt;p&gt;Villeneuve&amp;rsquo;s chops as an action director shine through here; there&amp;rsquo;s
none of the grunting and panting that typically conveys effort in
blockbusters; here the whole story is told with the camera. Paul&amp;rsquo;s
platoon of believers proclaim him the &amp;ldquo;Mahdi&amp;rdquo; (one of several elements
in the film clearly inspired by Islam), and the brilliance of the film
is such that for a minute we feel that they&amp;rsquo;re right. We &lt;em&gt;want&lt;/em&gt; them to
be right.&lt;/p&gt;
&lt;p&gt;The subservient attitude of Paul&amp;rsquo;s Fremen acolytes points up Chani&amp;rsquo;s
insistence on her own independence. &amp;ldquo;I&amp;rsquo;d like very much to be equal to
you,&amp;rdquo; Paul tells her early in their relationship. This line sticks in
one&amp;rsquo;s memory precisely because the world of &lt;em&gt;Dune&lt;/em&gt; is such a
thoroughgoingly unequal one. There is absolutely no pretense that Paul
is the equal of anyone else in the story. He is a Duke, the heir of the
house of Atreides. He has preternatural gifts of leadership, prodigious
fighting skill, and strong tactical capabilities. He is literally born
to play the specific role he does, with his future-sight giving him
textual awareness of his own position which serves as a kind of plot
armor, which the film emphasizes while simultaneously deconstructing. If
he has &amp;ldquo;main character syndrome&amp;rdquo;, it&amp;rsquo;s because he &lt;em&gt;is&lt;/em&gt; the main
character, and he knows it.&lt;/p&gt;
&lt;p&gt;Outside of Paul himself, the universe displays overwhelming inequality.
With the empire carved into family-run fiefdoms, brutality becomes the
necessary condition of survival and success. As the emperor explains to
Paul, he killed Paul&amp;rsquo;s father &amp;ldquo;because he was weak&amp;rdquo;, where &amp;ldquo;weak&amp;rdquo; serves
primarily as proof of incompetence. Villeneuve does not depict the
emperor as especially evil, but as a cunning and bureaucratically
capable man placed at the top of a world in which violence is the only
reliable mechanism of governance.&lt;/p&gt;
&lt;p&gt;When Paul declares himself the equal, or (rather) aspiring equal, of
Chani, what does this &amp;ldquo;equality&amp;rdquo; comprise? He seems to gesture toward
liberal notions of political equality that have no place in this
world. Paul is, after all, an outsider. He comes among the Fremen as an
obvious Lawrence-of-Arabia type, promoting the idea that they will guide
themselves to freedom, but always with the implicit premise that he will
be at the front as their leader and inspiration.&lt;/p&gt;
&lt;p&gt;Perhaps he means merely &lt;em&gt;sexual&lt;/em&gt; equality? This seems plausible enough,
in light of the relative good treatment of women by the northern,
non-fundamentalist Fremen. Yet he cannot mean this in a way that
comports with modern feminist notions (a brief joke about mansplaining
notwithstanding). Fremen society remains heavily segregated, and
emancipation for the women of Arrakis never becomes a serious
suggestion.&lt;/p&gt;
&lt;h2&gt;The women of Dune&lt;/h2&gt;
&lt;p&gt;Moreover, never does the enslavement of women we see in the world of
&lt;em&gt;Dune&lt;/em&gt; become a political subject. Paul wants revenge on the Harkonnens,
and (at least secondarily) victory for the Fremen, but we have not a
shred of evidence that the condition of Baron Harkonnen&amp;rsquo;s slaves ever
crosses his mind.&lt;/p&gt;
&lt;p&gt;When women are, like Chani, depicted as political actors in this world,
it is in their capacity as individuals. The &lt;em&gt;collective&lt;/em&gt; interests of
women have no significance. Comparing this to how seriously the film
takes the status of the Fremen illustrates the severity of this
omission. Their ability to defend themselves and make independent
decisions features centrally in the film, and Paul&amp;rsquo;s status as white
savior serves as a clear threat.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Dune II&lt;/em&gt; certainly likes to point out to us when women are being
mistreated, but in the absence of any serious attempt to make this
relevant to the actions and decisions of the characters, these
depictions serve crudely as mere illustrations of the violent,
despicable natures of the film&amp;rsquo;s antagonists. Contrast &lt;em&gt;Fury Road:&lt;/em&gt;
George Miller &lt;em&gt;wants&lt;/em&gt; us to ask what women&amp;rsquo;s freedom looks like under
conditions of chaos and collapse, what it means to be unified against
misogyny.&lt;/p&gt;
&lt;p&gt;Political power for women in &lt;em&gt;Dune&lt;/em&gt; comes primarily in the form of
membership in a religious order, the &lt;em&gt;Bene Gesserit&lt;/em&gt;. The official power
structure of the empire flows from the emperor through his vassals, a
hierarchy based on authority and brute strength. This contrasts with the
subtle methods of the &lt;em&gt;Bene Gesserit&lt;/em&gt;, who work by influence,
manipulation, and subterfuge.&lt;/p&gt;
&lt;p&gt;Their goals remain vague, at best, in &lt;em&gt;Dune I&lt;/em&gt; and &lt;em&gt;Dune II&lt;/em&gt;. They, in
conspiracy with Paul&amp;rsquo;s mother Jessica, have laid the foundations for his
power by spreading the prophecies among the Fremen, while at the same
time attempting to bring about the birth of one who could fulfill them.&lt;/p&gt;
&lt;p&gt;As their plans for Arrakis materialize, they seek to determine whether
Baron Harkonnen&amp;rsquo;s nephew Feyd-Rautha can be manipulated to their
interests. (He is, it turns out, easily taken in by attractive ladies.)
This episode serves to illustrate the vast power of the &lt;em&gt;Bene Gesserit&lt;/em&gt;.
Their schemes span across decades or centuries; they have the ability to
directly manipulate the minds of others; their envoys have the ear of
those with formal power, including in the halls of the emperor. It&amp;rsquo;s not
clear, in other words, why they&amp;rsquo;re not already running things. Could it
be that they are restrained to passitivity by an authorial intervention?&lt;/p&gt;
&lt;p&gt;Paul, oddly, is a feminized character. His mother breaks the rules of
the &lt;em&gt;Bene Gesserit&lt;/em&gt; (who admit only women) because she wants to give him
an edge, teaching him to use the &lt;em&gt;Voice&lt;/em&gt; &amp;mdash; the power to cause others
to instinctively obey through speech. Likewise, Paul becomes initiated
in &lt;em&gt;Dune II&lt;/em&gt; to their most sacred ritual, drinking the Water of Life,
which places him in a profound trance and gives him the ability to see
the future. We&amp;rsquo;re introduced to this act in the film when Jessica does
it, in order to become one of the order&amp;rsquo;s spiritual leaders (a Reverend
Mother).&lt;/p&gt;
&lt;p&gt;Paul&amp;rsquo;s decision to drink the liquid takes on more significance from the
fact that doing so is fatal to men. The fundamentalists interpret his
survival as another proof of his messianic status, but more directly, it
codes Paul as female. His potency derives from his ability to utilize
the &amp;ldquo;feminine&amp;rdquo; powers of the &lt;em&gt;Bene Gesserit&lt;/em&gt; in the male spaces of the
empire. The profound transformation in his attitude and behavior
derives, as we have seen, from the insight wrought by the Water of Life
(&lt;em&gt;Dune&amp;rsquo;s&lt;/em&gt; &amp;ldquo;tree of knowledge&amp;rdquo;, to which his mother beguiles him). He
likewise proceeds to use the &lt;em&gt;Voice&lt;/em&gt; to silence his enemies, many of
whom believe that his participation in the rites is heretical.&lt;/p&gt;
&lt;p&gt;The climactic fight between Paul and Feyd-Rautha involves an opponent
who has been decidedly masculinized. The young Harkonnen enters the film
as a bare-chested gladiatorial fighter, who haughtily disables the
energy shield protecting himself out of sheer machismo. His weakness,
naturally, turns out to be sex. In terms of military strategy, he rains
terror from above, preferring to annihilate his opponents entirely
rather than outmaneuver them.&lt;/p&gt;
&lt;p&gt;What is the significance of this all-female order achieving political
power, at long last, through a man? This move mirrors the
&amp;ldquo;Lawrence-esque&amp;rdquo; elements of the plot; as the white Paul enters the
Fremen world as messiah, so he enters the female space of the &lt;em&gt;Bene
Gesserit&lt;/em&gt; as political agent. To its credit, this is once again
something that &lt;em&gt;Dune II&lt;/em&gt; tries to deconstruct. If Paul does what the
&lt;em&gt;Bene Gesserit&lt;/em&gt; cannot, it is in some sense as their tool, and the
consummation of their plans. The film&amp;rsquo;s examination of this does not
appear particularly thorough, as we&amp;rsquo;re left to guess at what they hope
to achieve.&lt;/p&gt;
&lt;p&gt;Jessica, in her dual role as Paul&amp;rsquo;s mother and &lt;em&gt;Bene Gesserit&lt;/em&gt; Reverend
Mother, displays more agency than the other women of the film. She
serves their aims while also preferentially aiding Paul. Over the course
of the film, she changes from cynical promulgator of Paul&amp;rsquo;s cult to
something of a true believer. In &lt;em&gt;Dune&lt;/em&gt;, the power of a collective mass
of people derives from faith, and so long as the Fremen trust in Paul,
he can (perhaps must) become the person they believe him to be.&lt;/p&gt;
&lt;p&gt;This fact is suggestive of what the film says, in general, about heroes
and actors of the &amp;ldquo;regal&amp;rdquo; type more generally. They are, at the end of
the day, the crest of a wave built on thousands or millions of people
who really believe in something. The &lt;em&gt;Bene Gesserit&lt;/em&gt; are an organization
that understands this principle and manipulates it to their own
advantage. The film makes this point clearly enough, and it is only its
functioning in gender political terms that it leaves uncomfortably hazy.&lt;/p&gt;
&lt;h2&gt;Does the movie need a traditional villain?&lt;/h2&gt;
&lt;p&gt;The interest the &lt;em&gt;Bene Gesserit&lt;/em&gt; take in Feyd-Rautha is never
particularly coherent. He swings against their usual methods, and
obviously lacks Paul&amp;rsquo;s abilities and more importantly his foresight.
Once the movie actually comes to the point of bringing the two into
single combat to decide control of the empire, the outcome is no longer
in doubt.&lt;/p&gt;
&lt;p&gt;For the most part, their attention only serves to build him up as a
rival to Paul. They treat him as a kind of insurance plan; should the
Harkonnens retain control of Arrakis, Feyd-Rautha will inherit enormous
power and influence, and they want a piece of that. Fair enough. But
this doesn&amp;rsquo;t suffice to make him a credible threat, and scenes
emphasizing his effectiveness at violence and love for brutality only
lessen him as a plausible opponent.&lt;/p&gt;
&lt;p&gt;Paul&amp;rsquo;s struggle, in classical fashion, is an internal one. Should he
stay &amp;ldquo;true to himself&amp;rdquo;, as Chani wants, he&amp;rsquo;ll remain a minor
revolutionary figure prosecuting a losing war. If he chooses power, his
opponents are nothing; he wins Arrakis and captures the emperor and
Baron Harkonnen in a few minutes of screen time.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Why does he take such risks?&amp;rdquo; one of his followers asks, after he
chooses to fight Feyd-Rautha himself rather than choose a second. This
question fails to add any tension. The filmmakers must have felt we
needed an exclamation point here, but the duel only serves to undercut
the profound shock of Paul&amp;rsquo;s rapid ascent.&lt;/p&gt;
&lt;p&gt;For his own part, Feyd-Rautha never becomes an interesting character.
Sheer aggression does not a personality make. The film (and certainly
any sequel) needs enemies for Paul who can plausibly contest him , but
it is clear that raw firepower will not suffice. By the time we&amp;rsquo;re
cognizant of Feyd-Rautha as a recurring figure, Paul has already
surpassed him. He becomes a dumb malevolence of the same sort as Azog,
the forgettable orc mini-boss in the failed adaptation of &lt;em&gt;The Hobbit&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;More generally, many viewers will want more dramatic tension in &lt;em&gt;Dune
II&lt;/em&gt;. Paul is, and feels like, a character who is on the way somewhere
&amp;mdash; and he ends the film still on the way there. The film entertains,
and I found the pacing to my taste, but Paul lacks a real buffer to his
ambitions.&lt;/p&gt;
&lt;p&gt;This feeling is perhaps not helped by the casual way Chalamet plays the
character, at times. While on most occasions Paul feels appropriately
energetic and enthusiastic, on occasion he&amp;rsquo;s overly familiar, oddly
flippant, too &amp;ldquo;making chocolate of course&amp;rdquo;. One doesn&amp;rsquo;t feel him to be
in conflict, even with himself.&lt;/p&gt;
&lt;p&gt;That said, Chalamet convinces as an action star and romantic lead. He
has palpable chemistry with Zendaya and every scene between the two
oozes charm. The speech he gives to the Fremen toward the end of the
film compels, as does his sense for the changes to his character caused
by the Water of Life.&lt;/p&gt;
&lt;p&gt;Paul, as Chalamet plays him in this second film, is a man without
rivals. He rides the largest worm, he commands the biggest army, he
challenges the most powerful opponents, he meets with every success. If
we as viewers still find this story compelling, I believe it to be the
result of the film&amp;rsquo;s persistent efforts to question and undercut these
achievements. &lt;em&gt;Dune II&lt;/em&gt; allows Paul to do whatever he wants, but reveals
his accomplishments to be ultimately the work of others.&lt;/p&gt;</content><published>2024-03-08T21:27:48+00:00</published></entry><entry><id>https://adamfontenot.com/post/i_have_transitioned</id><title>I have transitioned ... to a new website!</title><updated>2025-09-09T04:34:51+00:00</updated><author><name>Liliane Fontenot</name></author><content type="html">&lt;p&gt;I publicly came out as transgender &lt;a href="https://blog.liliane.io/post/all-the-stupid-reasons-i-didnt-transition-five-years-ago"&gt;last year&lt;/a&gt;, and more recently, changed my name. I&amp;rsquo;m now Liliane Fontenot, and I&amp;rsquo;ve been blogging on &lt;a href="https://blog.liliane.io/"&gt;blog.liliane.io&lt;/a&gt;. I hope that if you followed my work here, you&amp;rsquo;ll continue following me there.&lt;/p&gt;
&lt;p&gt;Eventually, I will likely redirect the whole website to my new one, with static copies of old posts kept around for posterity, but I want to leave this site here for now, and give anyone following me via RSS a clear heads-up to change their URLs.&lt;/p&gt;
&lt;p&gt;Here is a direct link to the new feed: &lt;a href="https://blog.liliane.io/atom.xml"&gt;https://blog.liliane.io/atom.xml&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks for reading!&lt;/p&gt;</content><published>2025-09-09T04:34:51+00:00</published></entry></feed>