Most people learn during their study of the differential and integral calculus that the derivative of the natural logarithm ln _x_ is the reciprocal function 1/_x_. Indeed, sometimes the natural logarithm is _defined_ as $$ \int_1^x \frac{1}{t}\,dt$$. However, on observing the graphs of ln _x_ and 1/_x_, the inquisitive seeker of knowledge can hardly fail to notice a disturbing anomaly:
-[")](http://zackmdavis.net/blog/wp-content/uploads/2011/12/lnx3.png) [](http://zackmdavis.net/blog/wp-content/uploads/2011/12/reciprocalx2.png)
+[")]({static}/images/lnx3.png) []({static}/images/reciprocalx2.png)
The natural logarithm is only defined for _positive_ numbers; no part of its graph lies in quadrants II or III. But the reciprocal function is defined for all nonzero numbers. So (one _cannot_ help oneself but wonder) how could the latter be the derivative of the former? If the graph of the natural logarithm isn't _there_ to be differentiated in the left half of the plane, how could its derivative be defined in that region?
Some would-be explorers lose all hope or sanity in the face of such bizarre and inexplicable mysteries, but even those brave souls who manage to retain their wits are not guaranteed success: many (who can say but that most?) will die never knowing the answer. But not you, dear reader!—for in this very post, I will share with you the _true secret_ of the derivative of the natural logarithm! Some people may find some of what I am about to say somewhat disturbing, even frightening. But if your love of truth exceeds your fear of the unknown, keep reading, and I will show you the strange world that lies beneath these familiar and seemingly innocent graphs.
Overdue book fines are a terrible sin, not because of the harm done to other library stakeholders, but because of what they say about you as a person: not only did you not get around to finishing the books you (apparently erroneously) thought you wanted to read, but you weren't even responsible enough to bring them back on time.
-[](http://zackmdavis.net/blog/wp-content/uploads/2012/10/library_fines.jpg)
+[]({static}/images/library_fines.jpg)
And we get this:
-[](http://zackmdavis.net/blog/wp-content/uploads/2012/07/mandelbrot_turtle.png)
+[]({static}/images/mandelbrot_turtle.png)
Okay, so it's not perfect. But give the turtle a little credit, maybe? She tried. _We_ tried. And if that's not good enough—and, well, I guess it clearly is _not_ good enough—then I can only beg for forbearance on the part of any powers that could save us from our imminent self-destruction, we wretched and miserable creatures that are good enough to try but not good enough to succeed.
_Tuesday 5 Novembmer 2013—_ Yesterday was our last pair-programming project; I worked with _Ben Watts_ on a little chat server in Node using WebSockets. I felt like perhaps there was something regrettable about shoving so much functionality into a big callback, but maybe that's just the nature of JavaScript, and not really regrettable at all? On Sunday I started my RSS-aggregator capstone project, now called Superscription.
-[](http://zackmdavis.net/blog/wp-content/uploads/2013/11/superscription_entity-relationship-diagram.png){: .alignleft }
+[]({static}/images/superscription_entity-relationship-diagram.png){: .alignleft }
_Thursday 7 Novembmer 2013—_ I guess Superscription is going okay. Here's how it works. You can sign up and sign in and stuff. (I used [Devise](http://rubygems.org/gems/devise) for this rather than rolling my own.) There are forms for adding a category or a subscription URL. When you add a URL, stuff happens which depends on stuff. More specifically, if you enter a URL which the system has never seen before, then a new `Subscription` model (which [`accepts_nested_attributes_for`](http://apidock.com/rails/ActiveRecord/NestedAttributes/ClassMethods/accepts_nested_attributes_for) an associated `UserSubscription` join model) is created, and it requests the feed from the URL you supplied and tries to parse the XML using [Nokogiri](http://nokogiri.org/). If that doesn't work, it suggests that maybe you got the URL wrong, but if it _does_ work, then it makes a bunch of Entry objects for all the stories in the feed, saves everything, and redirects you to your subscription index page. But if the system has already seen your URL before (presumably from another user with similar reading tastes), then it doesn't wastefully fetch all that data again: it just makes a new `UserSubscription` that points to you and your `Category` and the `Subscription` that it already has. It's efficient! Then when you click the "Read!" button (which is non-hideously positioned thanks to nearly an hour of fighting the CSS), all your news is organized by category under [Bootstrap tabs](http://getbootstrap.com/javascript/#tabs). And on the subscription index page, when you click the little trash can next to a subscription, then it does [the cool jQuery fade-out thing](http://api.jquery.com/fadeOut/) and sends an Ajax request telling the server to destroy that `UserSubscription`.
-[](http://zackmdavis.net/blog/wp-content/uploads/2013/11/superscription_early_demo.png){: .alignleft }
+[]({static}/images/superscription_early_demo.png){: .alignleft }
_Sunday 10 Novembmer 2013—_ You know, I don't think I'm happy with Superscription's current state. As currently written, it's fetching feeds _inside of the request-response cycle_, which is just awful on a technical level even if I seem to have gotten away with it so far. I'll want to do some serious rework this week (_next_ week?—I suppose these entries have been following the Monday-indexed convention) so that the fetch happens as a background job (even though that will cost money to host on Heroku, which only offers one free web dyno). And it could do with a lot more JavaScripty features on the frontend, and I need to read the Bootstrap documentation more carefully.
Status: published
Category: computing
-[](http://zackmdavis.net/blog/wp-content/uploads/2013/10/starlight_music-getting_down.png){: .alignleft }
+[]({static}/images/starlight_music-getting_down.png){: .alignleft }
_Monday 14 October 2013—_ I'll confess that I didn't have a productive weekend at _all_. No excuses; I just got distracted. You could argue (I won't, but you can) that it's perfectly healthy and fine to take some well-earned relaxation on one's days off, but even relaxation is something that can be done better or worse: hours of the latest ephemeral internet amusements are probably _much less_ rejuvenating than hours of ... I don't know, some sort of activity that _doesn't_ involve sitting in front of a monitor?—I presume such things still exist even if I can't remember them anymore.
And that's terrible.
-[](http://zackmdavis.net/blog/wp-content/uploads/2013/10/my_friend_circle.png){: .alignleft }
+[]({static}/images/my_friend_circle.png){: .alignleft }
_Tuesday 15 October 2013—_ I got all the specs to pass on the assessment with time to spare. Today's designated project was _Friend Circle_ (!), a Google+ish thing where users can put other users in circles and share posts with them. I was scheduled to pair with _Jeff Rosen_, but apparently he was ill, and so I worked alone after lunch. The instructions described a somewhat ambitious project; I only got as far as letting users create circles and will have to catch up later. Associations are actually really powerful! Still no BART settlement ... and no strike, either; I decided to come home tonight, the allure of a shower and bed and privacy outweighing the travel uncertainty. Tomorrow we're going to make a very simple clone of some of the functionality of Rails itself (following up on our [earlier](http://zackmdavis.net/blog/2013/10/app-academy-diary-week-four/#monday) very simple clone of some of the functionality of ActiveRecord). I guess I'll call it ... Ruby on Wires?
_Wednesday 9 October 2013—_ I did poorly on the assessment yesterday. There were eight SQL queries to write; the first five were trivial, but I bombed the last three because [I'm a moron](http://zackmdavis.net/blog/2013/08/im-a-moron/) and didn't remember that the keyword for filtering aggregations was HAVING. I worked with _William Ott_ on an ice-cream finder (which uses Google Maps APIs to print out directions to nearby ice-cream) and a Twitter client. Of course, it simply wouldn't do to write an ice-cream finder without using it to find ice-cream, so after class we took one of our program's suggestions and bought ice-cream at the Häagen-Dazs in the mall on Market and Fifth. Today I worked with _David A._; we learned about routers and controllers.
-[](http://zackmdavis.net/blog/wp-content/uploads/2013/10/our_cat_for_rent.png){: .alignleft }
+[]({static}/images/our_cat_for_rent.png){: .alignleft }
_Thursday 10 October 2013—_ Dear reader, suppose [yet](http://zackmdavis.net/blog/2013/09/app-academy-diary-week-two/#sunday) [again](http://zackmdavis.net/blog/2013/09/app-academy-diary-week-three/#saturday) that you're working on the great American dog-sharing site—_loser!_ Sharing dogs is _so_ last week; you should have predicted that by now, everyone who's anyone would be renting cats instead. That's why today _Dean Yang_ and I used our prodigious knowledge of not just models, but also both views _and_ controllers, to make a mock cat-rental site! It's like this: say you visit the URL for the page that displays all the cats. Your browser issues an HTTP GET request, which is handled by the Rails router: an instance of the `CatsController` is spawned, and its `index` method is called, which puts an array of `Cat` objects (instantiated by the ActiveRecord ORM from their representations in the database) in an instance variable, which is then made available to a process that uses a template to decide how to express the information about the cats in HTML, which is then sent back to your browser. Similar stuff happens to let you make a new cat, or edit an existing cat.
_Wednesday 30 October 2013—_ Our focus this week is _Backbone.js_, a library for doing client-side stuff. I hadn't been sure what to do for my capstone project, but now (perhaps inspired by remembering how I really ought to use feeds instead of manually navigating to my favorite blogs for all the world as if it were 2003) I'm thinking of making an RSS reader and calling it Event Listener. In the evening, I made a small improvement to the Wires template engine, and I started working on an improvement to how objects inherit from the ORM base class, but it's getting late and I'm not done debugging and I wasn't planning on sleeping over tonight, so rather than pushing egregiously broken code to GitHub, I backed up my work as an online paste of a diff obtained by redirecting `git stash show -p`.
-[](http://zackmdavis.net/blog/wp-content/uploads/2013/10/learning_backbone.png){: .alignleft }
+[]({static}/images/learning_backbone.png){: .alignleft }
_Thursday 31 October 2013 (or, very early Friday)—_ _Speaking_ of online pastes, today I worked with _Vincent Park_ on another make-a-Backbone-app exercise inspired by GitHub's [Gist](https://gist.github.com/). It simply cannot be doubted that practice, as always, makes perfect. In the evening I bought a new clipboard at the Target inside the Metreon (as my old clipboard had been broken for some time, and the way in which the broken-off piece makes a great coaster had been small consolation indeed), and I hung out and made a few small improvements to Wires. I received a reminder of the importance of catching only _specific_ exceptions—an `except:` block in my code was silently catching a type error that was the source of a bug, when really all I wanted was an `except KeyError:`—it was an easy fix this time, but in a larger project one might not be so lucky! Wires's current "template language" (if you can call it that; I'm just replacing identifiers inside "<%=" and "%>" delimiters with substitutions from a supplied dictionary) doesn't support conditionals the way EJS or ERB do, but it would be awfully nice to at least, say, render _logged in as this-and-such_ depending on whether the user is logged in, but I have a cheap workaround in mind.
_Wednesday 25 September 2013_— Yesterday and today I wrote chess with _Nathan Holland_! Also, it turns out that the criminal scum who steal entire bicycles are not above stealing individual wheels, either.
-[](http://zackmdavis.net/blog/wp-content/uploads/2013/09/our_chess.png)
+[]({static}/images/our_chess.png)
_Thursday 26 September 2013_— Today's exercise was to write checkers, alone (contrast to the pair programming methodology of previous days). I ... used [structs](http://www.ruby-doc.org/core-2.0.0/Struct.html) to represent the checkers and moves? I like structs?
-[](http://zackmdavis.net/blog/wp-content/uploads/2013/09/my_checkers.png)
+[]({static}/images/my_checkers.png)
_Saturday 28 September 2013_— Yesterday was about _testing_. Novice programmers test their programs by running them and seeing if they give the expected results, which might be okay when your program is small and no one actually uses it, but then what happens when you wake up one day and you're in charge of maintaining a twenty-thousand-line application that people really depend on?
—and dear reader, the maximum-flow problem is _solved_! For suppose we are faced with the network depicted in the following diagram:
-[](http://zackmdavis.net/blog/wp-content/uploads/2013/08/flownetwork.jpg)
+[]({static}/images/flownetwork.jpg)
We can then (admittedly with some typing) call for a solution like so:
—and _most_ files stay on the same device: the only ones that have to move are those with a new nextmost designated spot in the ring!
-[](http://zackmdavis.net/blog/wp-content/uploads/2014/01/your_consistent_hashing_ring.png)
+[]({static}/images/your_consistent_hashing_ring.png)
You decide to run a quick simulation to see if you've gotten this right before saving the edits to your soul. Suppose you had storage devices `device0` through `device5` and your patrons wanted you to store `file0` through `file5999`; how would the files end up distributed across your devices? You choose a prudent-seeming integer for the number of designated spots per device—say, 450?—and write the simulation like this—
Status: published
Category: asides
-[](http://zackmdavis.net/blog/wp-content/uploads/2014/03/genotype_emacs.png)
+[]({static}/images/genotype_emacs.png)
I—I'm afraid that examination of my [23andMe](https://www.23andme.com/) raw data has revealed a horrible truth about myself. I mean, I had always suspected, but now there's no room for doubt, no hope for denial. The terrible fact is that—
subprocess.check_output(["notify-send", "writing progress", nag_message()])
```
-[](http://zackmdavis.net/blog/wp-content/uploads/2014/07/wordcount_notification.png)
+[]({static}/images/wordcount_notification.png)
_You're welcome_. Now get back to work.
The blog has been silent for two weeks plus and, dear reader—that is, if there are any of you still remaining—dear reader, the thought occurs to me that maybe I should keep my drafts in a Git repository with a remote on GitHub, not because I need the full power of version control (I do not), but because then I would be rewarded for writing with those contemptible green contribution squares.
-[](http://zackmdavis.net/blog/wp-content/uploads/2014/02/my_squares.png)
+[]({static}/images/my_squares.png)
It's an [anthropomorphism](http://lesswrong.com/lw/6i5/behaviorism_beware_anthropomorphizing_humans/) to think that humans have goals, that we do things because we've computed that they'll increase expected beauty or rightness in the world. We do things for the _immediate reinforcement_. You eat the candy because it tastes good and you show up to work on time because if you didn't, then your colleagues would notice. Serious long-term risks of diabetes or unemployment are too distant and too abstract to enter in the equation; far more effective is something immediately noticeable, even something as trivial as an integer being incremented or a square turning a darker shade of green. I tell myself that I code because it's fun and useful and lucrative (though I'm never explicit about whether that's descending or ascending order of importance), but would I be quite so diligent without the implicit gamification of my virtue? Would it be enough to have done good work, _without_ wasting a few minutes here and there to gaze admiringly at commit diffs and contribution squares which manifest my moral worth in red and green and green?
Category: asides
Tags: baseball, notation
-[](http://zackmdavis.net/blog/wp-content/uploads/2015/10/scorecard_attempt_SFG_vs_CIN_2015-09-15.jpg)
+[]({static}/images/scorecard_attempt_SFG_vs_CIN_2015-09-15.jpg)
baseball scorecard tablet app that downloads the official scorekeeper's version after the game and grades you on accuracy
* about 16 fl. oz. [Soylent 1.4](https://www.soylent.com/) (refrigerated, prepared some hours previously)
* "a couple" fl. oz. tap water
-[](http://zackmdavis.net/blog/wp-content/uploads/2015/11/californian_raktajino1.jpg)
+[]({static}/images/californian_raktajino1.jpg)
_Instructions:_
<ol>
<li>Pour coffee powder in bottom of glass.</li>
<li>Add enough water to dissolve the coffee powder; stir.<br>
-<a href="http://zackmdavis.net/blog/wp-content/uploads/2015/11/californian_raktajino2.jpg"><img src="http://zackmdavis.net/blog/wp-content/uploads/2015/11/californian_raktajino2-196x300.jpg" alt="californian_raktajino2"></a></li>
+<a href="{static}/images/californian_raktajino2.jpg"><img src="{static}/images/californian_raktajino2-196x300.jpg" alt="californian_raktajino2"></a></li>
<li>Pour Soylent. Soylent will mostly sink below the coffee; stir with a somewhat vertical motion until color is uniform.<br>
-<a href="http://zackmdavis.net/blog/wp-content/uploads/2015/11/californian_raktajino3.jpg"><img src="http://zackmdavis.net/blog/wp-content/uploads/2015/11/californian_raktajino3-208x300.jpg" alt="californian_raktajino3"></a>
-<a href="http://zackmdavis.net/blog/wp-content/uploads/2015/11/californian_raktajino4.jpg"><img src="http://zackmdavis.net/blog/wp-content/uploads/2015/11/californian_raktajino4-220x300.jpg" alt="californian_raktajino4"></a>
-<a href="http://zackmdavis.net/blog/wp-content/uploads/2015/11/californian_raktajino5.jpg"><img src="http://zackmdavis.net/blog/wp-content/uploads/2015/11/californian_raktajino5-204x300.jpg" alt="californian_raktajino5"></a></li>
+<a href="{static}/images/californian_raktajino3.jpg"><img src="{static}/images/californian_raktajino3-208x300.jpg" alt="californian_raktajino3"></a>
+<a href="{static}/images/californian_raktajino4.jpg"><img src="{static}/images/californian_raktajino4-220x300.jpg" alt="californian_raktajino4"></a>
+<a href="{static}/images/californian_raktajino5.jpg"><img src="{static}/images/californian_raktajino5-204x300.jpg" alt="californian_raktajino5"></a></li>
<li>Today is a good day to live! Drink; enjoy; move fast; break things!</li>
</ol>
As far as I can tell, I don't have any kind of synesthesia. You can't be too sure (which means, you can easily be _entirely_ too sure), what with our na(t)ive theories of psychology being so inadequate that _everything_ we believe about other minds is but a filament of noise and conjecture, but your probability distribution about the mapping of sensory inputs to perceptions for _me_ is probably not so different as mine of the same for _you_ (dear reader of whom I know nothing)—roses seem red, violets would seem blue if we spoke a language that didn't already have a word for _violet_—which means that when I tell you that there's a musty, stale odor around a blog that hasn't been updated in a month and change, it's only a trite metaphor and not a perceptual reality of any sort. Still, even if you can't smell it (if your senses are like mine; if your fox, like mine, still hasn't bothered to implement the [HTML5 `<aroma>` element](http://www.sitepoint.com/html5-new-sensory-elements/)), it's an ominous thing, to see a blog hovering near the boundary between life and death, a corpus perhaps on the way to being a corpse. The internet is littered with the latter, monuments to people who reliably had _something to say_, month after month ... until they missed a month, and then it wasn't long before they missed another.
-[](http://zackmdavis.net/blog/wp-content/uploads/2015/02/my_block_of_squares.png)
+[]({static}/images/my_block_of_squares.png)
Now I can assure you that that will _never_ happen to _this_ place while _I'm_ still breathing—this blog lives exactly as long as I do—only that's not a precise way of speaking; what I can do is _offer you my assurance_, which is a different thing from you actually feeling assured, which is a different thing still from that which was assured against actually never coming to pass. But I think these differences—between feeling and reality, between saying and reality—I think these enormous differences are much greater than the tiny, barely-perceptible gap between seeing so many gloriously intricate things to say, and making the time and words to express them on your blog when you are so busy with your trade in the manufacture of useful machinery (and the green tiles which are its highly-coveted industrial byproduct). But if all I can _observe_ is that the gap is barely perceptible, then by the enormity of the earlier differences, I am not licensed to _infer_ that the gap is tiny, not when the only reason I am telling you this is that I would die of shame if my monthly archives sidebar skipped a month for the first time since May of 'aught-twelve, not during this second year of my life in which I am _supposed_ to write a compiler and a bad novelette even though it is for all intents and tens of intensive purposes practically _March_.
On Saturday the first, I attended [RustCamp](http://rustcamp.com/), the first conference dedicated to the newish (in development for fiveish years, but having just hit version 1.0.0 this May, with all the stability guarantees that implies under the benevolent iron fist of [semantic versioning](http://semver.org/spec/v2.0.0.html)) programming language [Rust](http://www.rust-lang.org/)!
-[](http://zackmdavis.net/blog/wp-content/uploads/2015/08/badge_and_lambda_dragon_shirt.jpg)
+[]({static}/images/badge_and_lambda_dragon_shirt.jpg)
Why RustCamp? (It's a reasonable rhetorical question with which to begin this paragraph: going to a conference has opportunity costs in time and money; things worth blogging about are occasionally worth justifying—even if no one _actually_ asked me for a justification.) A lot of the answer can be derived from the answer to a more fundamental question, "Why Rust?" And for me, I think a lot of the answer to _that_ has to do with being sick of being a fake programmer living in a fake world that calls itself Python.
But suppose we were to remember _three_ points. Then we could still reconstruct the line from any two of them, which means that the information about our line hasn't been lost even if we forget one of the points.
-[](http://zackmdavis.net/blog/wp-content/uploads/2015/04/polynomial.png)
+[]({static}/images/polynomial.png)
Similarly, three points make a parabola, four points make a cubic curve, and in full generality, _m_+1 points make a degree-_m_ polynomial. Given _n_ points on a polynomial curve where _n_ is greater than _m_+1, _any_ _m_+1 of them suffice to reconstruct the polynomial.
This phenomenon comes in degrees. Start with irrelevance. Often the inadmissibility of tardy words isn't absolute: you _could_ say things late, but the product would be less valuable than if it were timely—especially in a medium like blogging, where the posts being dated and displayed reverse-chronologically creates an expectation that the entries are associated with a particular point in time—at least, that they were written not too long before their publication date, even if the actual content isn't about the ephemera of the day or season. This has contributed to _An Algorithmic Lucidity_ not being as good of a blog as it could be.
-[](http://zackmdavis.net/blog/wp-content/uploads/2016/01/crush_kill_destroy_swag.png){: .alignright }
+[]({static}/images/crush_kill_destroy_swag.png){: .alignright }
Like—each of the last two Aprils, I attended (one day of) [BABSCon](http://www.babscon.com/), the San Francisco Bay Area's premier convention for fans of the animated series _My Little Pony: Friendship Is Magic_, both times—even the second!—with the thought that the experience would make good fodder for a blog post in the "autobiographical account of my day at this timely Special Event" genre. I was going to tell you about how the first time, I made a couple of social _faux pas_ while meeting Tara Strong and Nicole Oliver, only one of which was intentional; I was going to tell you about how the second time, I hadn't been planning to buy anything in vendor hall, but couldn't help but say "Shut up and take my money" in response to the demonstration of the [Twilight Sparkle's Secret Shipfic Folder](http://www.secretshipfic.com/) card game, the game-opening card of which I later got autographed by both members of Sherclop Pones, whose [_Friendship Is Witchcraft_ fandub series](http://tvtropes.org/pmwiki/pmwiki.php/WebVideo/FriendshipIsWitchcraft) had clearly inspired some of the cards and probably the game itself, as well as being the source of [some of my favorite music](https://www.youtube.com/watch?v=ACEnJSDVzdc). I was going to tell you about how the first time, I was considering buying a coffee cup depicting the Mane Six (because I frequently bought medicinals at the outpost of the American coffee hegemon, and felt guilty about the wastefulness of accepting the default disposable cup every time like every other American), but hesitated, explaining to the vendor that I wasn't sure whether I wanted to use that cup in public, whereupon she said she could throw in a free [button](https://en.wikipedia.org/wiki/Pin-back_button), to which I replied, "Sold!" And I was going to do a careful sociological analysis of curious observations like how I hesitated to buy that cup and why [H. liked it so much](http://zackmdavis.net/blog/2014/09/pumpkin-spice/) in terms of gender ideology and [signaling](https://en.wikipedia.org/wiki/Signalling_%28economics%29) [contrarianism](http://lesswrong.com/lw/2pv/intellectual_hipsters_and_metacontrarianism/).
But I didn't, and those Aprils were nine and twenty-one months ago, respectively. Not timely. My being motivated to write _now_ (["That my past does not define me, 'cause my past is not today"](https://www.youtube.com/watch?v=XqnbYUG6Bn8)) can push a 350-word counterfactual "postview" of what I thought about saying over the threshold into existence, but it would be a bit unseemly to try to construct (I almost wrote _reconstruct_, but the _re-_ prefix should be reserved for things that ever existed in the first place) a 2000-word personal account of the timely Special Event that happened _last year_ or the _year before that_.
-[](http://zackmdavis.net/blog/wp-content/uploads/2016/01/leafline_web_client_play.png){: .alignleft }
+[]({static}/images/leafline_web_client_play.png){: .alignleft }
Or like—I made [a thing](https://github.com/zackmdavis/Leafline) not too long ago. I didn't mean to. It was an accident, really. It's a sort of oppositional strategy game engine—like a board game you play against the computer. The core move-scoring application is written in Rust, but there's also a web-application GUI (the "web client") in Clojure and ECMAScript 6 that's much more ergonomic for playing a game against. The game is—okay, well, it's chess: the endeavor snowballed out of my desire to participate in my coworkers' friendly office chess games combined with my reluctance to spend effort learning to be good at a task whose [essential nature](https://en.wikipedia.org/wiki/Minimax) is so obviously suitable for automation. But writing a chess engine is just _so_ cliché, and I enjoy naming things, so I quickly settled upon the conceit that _actually_ I was writing an engine for a game that _just happened_ to be exactly like chess, except that everything has different names: for example, instead of the pieces being black and white pawn, knight, bishop, rook, queen, and king, the figurines, or agents (never "pieces"), in my game are [blue and orange](http://tvtropes.org/pmwiki/pmwiki.php/Main/BlueAndOrangeMorality) servant, pony, scholar, cop, princess, and figurehead. (I kept _rank_ and _file_, though.) This "adorable idiosyncratic names for everything" convention permeates the codebase. Despite the fact that unnecessarily gendering things isn't normally my style, I decided that ponies, scholars, and (inevitably) princesses were female, and that servants, cops, and figureheads were male, specifically in order to have an excuse to leave `// ♀` and `// ♂` comments in the [enum declaration](https://github.com/zackmdavis/Leafline/blob/d914c979/src/identity.rs#L35-L43), because I feel like too many of my code comments are restricted to the ASCII or _maybe_ Latin-1 subsets of the Basic Multilingual Plane, such that I don't want to pass up an opportunity to legitimately ("legitimately") use \u2640 FEMALE SIGN and \u2642 MALE SIGN. There are [comments with](https://github.com/zackmdavis/Leafline/blob/d914c979/src/identity.rs#L68-L86) ludicrous rationalizations for using the standard Forsyth–Edwards notation abbreviations ('P' is for _peon_, 'N' is for _neigh_, 'B' is for _book_, 'R' is for _the Rule of law_; the blue team's runes are lowercase because lowercase characters have higher ASCII codepoints, just as blue light has a higher frequency than orange light). Instead of _pawn promotion_, it's _servant ascension_, and a different verb is used to describe the process depending on the target figurine type (the servant moving the to the final rank can _transform_ into a pony, be _brevetted_ to cop, or _transition_ into a princess or scholar). Or, like, Rust is quite [conservative in the Yeggean sense](https://plus.google.com/110981030061712822816/posts/KaSKeg4vQtz), as manifested in features like the compiler forcing you to explicitly handle all possible variants of an enum; in cases where you know an instance of the enum can only be one of a strict subset of the possible variants, but the type system doesn't know that, you have to fill in those branches or supply a default case anyway, [probably](https://doc.rust-lang.org/std/macro.unreachable!.html) with a panic, which is like an exception that you can't catch, to abort the program with a message indicating that something entirely unanticipated has happened and it makes no sense to carry on. So I had been ending all of my panic messages with a stock phrase about how the unexpected thing was contrary to the operation of the moral law ("non-princesslike agent passed to `princesslike\_lookahead`, which is contrary to the operation of the moral law", and so forth), because this isn't the kind of codebase where you just say something like "assertion failed," as if the program should _die_ because of some human authority's mere _assertion_, rather than only as a matter of _justice_ when its behavior is contrary to the operation of the moral law. And then when my coworker [Alexander Corwin](http://acorwin.com/) started contributing, he abstracted away the boilerplate in these panic messages into this [`moral_panic!` macro](https://github.com/zackmdavis/Leafline/blob/d914c979/src/sorceries.rs#L1-L4), which I thought was _brilliant_.
The web client is configured to run on port 2882. It's [easy to remember because](http://zackmdavis.net/blog/2012/10/my-favorite-mnemonic/) 2882 is Magnus Carlsen's peak ELO score.
-[](http://zackmdavis.net/blog/wp-content/uploads/2016/01/leafline_console_play.png){: .alignright }
+[]({static}/images/leafline_console_play.png){: .alignright }
Software, like poetry, is never really finished (only abandoned), but getting the project to the point that I felt it was a minimal viable "product" took about eight glorious weekends (plus a few weekday nights, and with some help from Alexander). Near the end, watching the program utterly trounce me in web client play and [feeling for all the world like a lieutenant junior grade](http://zackmdavis.net/blog/2014/06/lower-decks/), I started looking ahead to what came next for me. Over the past two years, the first two years of my life (that I feel comfortable admitting to), I had spent many, many night and weekend hours hacking on various side projects and coding exercises out of genuine enthusiasm and curiosity and desire to improve my craft—and, honestly, a [feeling of insecurity](http://zackmdavis.net/blog/2015/08/no-award/), sensing that I needed to prove my [worth](http://zackmdavis.net/blog/2014/09/worth/) as a hacker (["I threw myself into my studies, to have the world in my control"](https://www.youtube.com/watch?v=MpE0ZY8FODk)). But decent _chess AI_ in _Rust_ as an _impulsive throwaway project_ seemed like a sufficiently strong signal of my programming prowess that maybe it was time to tie off this project, write the obligatory exciting blog post about it, and start allocating night and weekend hours towards some of the _non_-programming (!) interests I remember having in the before-time (assuming those memories are not fake). I could do some math! Write some fiction! Maybe even meet new friends ("[I will not be shy](https://www.youtube.com/watch?v=EvP7PIt7GNE); I'm going to [try](http://zackmdavis.net/blog/2016/01/dismal-science/); I bet I'll find the reason why so many people, like me—")?!—all while feeling secure in the knowledge that my technology skills are _clearly_ adequate for my continued existence to be economically viable. (For now.)
Category: philosophy
Tags: Rust
-[](http://zackmdavis.net/blog/wp-content/uploads/2016/06/stable_features_version_lint_before_and_after.png)
+[]({static}/images/stable_features_version_lint_before_and_after.png)
I used to look down on posers who submit some contrived one-off trivial patch to a big, famous project like Django or whatever, sheerly for the glamor and ego-gratification of being able to say, "_I'm_ a contributor to _Django_." I thought that if it wasn't a fix that you needed for your own work and you're not going to be a seriously involved contributor, it's more dignified to only work on your personal projects (which would be more authentic) or some non-super-famous but still widely-used library (which would have more socially-useful unfinished work left).
[(Previously on _An Algorithmic Lucidity_.)](http://zackmdavis.net/blog/2015/08/rustcamp-reminiscences/)
-[](http://zackmdavis.net/blog/wp-content/uploads/2016/09/sfo_reflections.jpg){: .alignright }
+[]({static}/images/sfo_reflections.jpg){: .alignright }
The other weekend, excited to learn more and connect with people about what's going on at the forefront of expressive, performant, data-race-free computing—and eager for a healthy diversion from the last two months of agonizing delirium induced by the world-shattering insight about how everything I've cared about for the past fourteen years turns out to be related in unexpected and terrifying ways that I can't talk about for reasons that I also can't talk about—I took Friday off from my dayjob and caught a Thursday night flight out of SFO to exotic Portland (... I, um, don't travel much) for [RustConf](http://rustconf.com/)!
Every reference (I kind of want to say _ampersand_) in Rust code has an associated _lifetime_, the region of the program that that reference is valid for. Lifetime annotations (appearing in angle brackets like generics and starting with an apostrophe; by convention, usually named consecutively from the start of the lowercase Latin alphabet: `'a`, `'b`, _&c._) in function signatures are used to distinguish between the lifetimes of different reference arguments, but the compiler has _lifetime ellision rules_ that cover the 90% use-cases, so you can actually write pretty substantial Rust programs without actually understanding the theory, which is both practically useful and _eternally shameful_ (for a programmer who is _satisfied_ with _not understanding_ something is not long for this world). Hence the training. [(Exercises from the training sessions are available online.)](http://rust-tutorials.com/RustConf16/)
-[](http://zackmdavis.net/blog/wp-content/uploads/2016/09/rustconf_swag.jpg){: .alignleft }
+[]({static}/images/rustconf_swag.jpg){: .alignleft }
The point of lifetime analysis is to ensure that all references point somewhere valid; you can't (_can't_, the compiler won't let you) have a reference to a thing that outlives the thing itself. When you return a reference from a function, you can't be referencing something created by that function, because any such thing would die at the end of the function as it goes out of scope: a reference in the return type has to be a reference to something owned by the caller that was passed as an argument, but if there was more than one reference argument, it's ambiguous _which_ of the reference arguments has to be outlived by the returned reference, which is why you sometimes need explicit lifetime annotations ...
There were some technical difficulties getting the projectors hooked up to the laptop for the next talk. Steve Klabnik said that the break was brought to us by Apple, who is not sponsoring us, but really wants us to have a long break between talks. "They should have written it in Rust!" I shouted (as if _someone_ had to say it); Steve shrugged.
-[](http://zackmdavis.net/blog/wp-content/uploads/2016/09/compiler_says_no.jpg){: .alignright }
+[]({static}/images/compiler_says_no.jpg){: .alignright }
The projector issue got fixed and Liz Baille, developer at [Tilde](http://www.tilde.io/) and graphic-novelist, gave a really funny talk in the form of an "illustrated adventure guide" to Rust ("You might have noticed how clean and beautiful Rustlandia is, and you might have also noticed that there are no garbage cans anywhere").
Wow, has it already been a year since last RustConf?—give or take the exact date of the event sliding a bit between years—and give a month-and-a-half of procrastination before being truly struck by the mounting realization that my opportunity to blog _something_ about it [before the opportunity expires](http://zackmdavis.net/blog/2016/01/ideas-have-expirations/) has almost—but crucially, not quite—faded into oblivion. And a year-and-a-quarter since [my first contribution](http://zackmdavis.net/blog/2016/06/pose/) to the compiler? I've recently moved into the top hundred contributors by commit count, because GitHub's [contributors graph page](https://github.com/rust-lang/rust/graphs/contributors) only goes down to a hundred and [my life is controlled by what things GitHub happens to provide graphs for](http://zackmdavis.net/blog/2014/02/motivation/).
-[](http://zackmdavis.net/blog/wp-content/uploads/2017/10/rustconf_2017_swag.jpg){: .alignleft }
+[]({static}/images/rustconf_2017_swag.jpg){: .alignleft }
So in the evening of Wednesday 15 August, I boarded the Amtrak [Coast Starlight](https://www.amtrak.com/coast-starlight-train) at Jack London Square station in Oakland for the long pilgrimage north to Portland to visit friend of the blog Sophia and attend this year's RustConf.
Ideology Makes You Stupid
-[](http://zackmdavis.net/blog/wp-content/uploads/2017/02/sworn_to_the_word.png)
+[]({static}/images/sworn_to_the_word.png)
Because only one player can win the game, what's good for one player is equally bad for the other: so if we add up all the piece values for one player, and _subtract_ all the piece values for the other, we get a "score" for the board position that the first player is trying to maximize, and the second player is trying to minimize.
-[](http://zackmdavis.net/blog/wp-content/uploads/2019/05/choice_pseudocode.png)
+[]({static}/images/choice_pseudocode.png)
So consider a player pondering her move. For every possible legal move she could make, she knows what the board position will look like after that move, and can calculate the value of that position. So you might think she should choose the move that results in the best value: for example, if she can capture the opponent's queen, that would make the subsequent board position be worth 9 more points.
The problem with that is that it's short-sighted. If capturing the opponent's queen would just result in the opponent capturing the first player's queen back, then what looked like a 9 point gain after one turn, ends up being a wash after both players have taken their turn.
-[](http://zackmdavis.net/blog/wp-content/uploads/2019/05/game_tree.png)
+[]({static}/images/game_tree.png)
To take this into account, the first player should consider not just the immediate outcome of her move, but what the other player is likely to do after that. And the way the first player can compute what she predicts the second player will do is by asking, well, what would _I_ do if I were in that position, except trying to minimize the score rather than maximizing it?
... and so on recursively. So instead of just choosing the move with the best _immediate_ consequences, we want to look at the entire "game tree" of "my best move, _given_ her best move, _given_ my best move, _given_ her best move"—down to some given depth at which we give up, take the point count at face value, and propagate that information back up the call stack.
-[](http://zackmdavis.net/blog/wp-content/uploads/2019/05/negamax_pseudocode.png)
+[]({static}/images/negamax_pseudocode.png)
So, that's how you play chess. I want to tell you about two more philosophical insights I learned from this endeavor.
This position came up in a game with my coworkers [as part of the engine's planning](http://zackmdavis.net/blog/2016/02/missing-refutations/) in a scenario where Black's previous move was moving her bishop to f5—
-[](http://zackmdavis.net/blog/wp-content/uploads/2019/05/scenario_pt1.png)
+[]({static}/images/scenario_pt1.png)
Here, the engine's predicted move for Black is knight to g3. At a first glance, this looked crazy to me: why would you move the knight to be diagonally in front of those pawns that could capture it?
-[](http://zackmdavis.net/blog/wp-content/uploads/2019/05/scenario_pt2.png)
+[]({static}/images/scenario_pt2.png)
And of course, what's actually happening is that moving the knight reveals a discovered attack of the black bishop on f5 against the white queen on c2.
-[](http://zackmdavis.net/blog/wp-content/uploads/2019/05/scenario_pt3.png)
+[]({static}/images/scenario_pt3.png)
Saving the queen is more important to White than capturing the black knight, allowing Black to use _her_ next turn to capture the white rook on h1.
-[](http://zackmdavis.net/blog/wp-content/uploads/2019/05/scenario_pt4.png)
+[]({static}/images/scenario_pt4.png)
But this is pretty weird, right? The algorithm has gone to all this trouble to set up a discovered attack on the white queen—in order to capture the white _rook_, not the queen!
[^code]: Thanks to Daniel Kumor for [example $\LaTeX$ code for causal graphs](https://dkumor.com/posts/technical/2018/08/15/causal-tikz/).
-
+
Notably, the decomposition still works whether you count subconscious motivated reasoning as "stupidity" or "dishonesty". (Needless to say, it's also symmetrical across persons—if _you're_ wrong, it could be because _you're_ stupid or are being dishonest.)
There's this statistical phenomenon where it's possible for two multivariate distributions to overlap along any one variable, but be cleanly separable when you look at the entire [configuration space](https://www.lesswrong.com/posts/WBw8dDkAWohFjWQSk/the-cluster-structure-of-thingspace) at once. This is perhaps easiest to see with an illustrative diagram—
-
+
The denial of this possibility (in arguments of the form, "the distributions overlap along this variable, therefore you can't say that they're different") is sometimes called the "univariate fallacy." (Eliezer Yudkowsky [proposes "covariance denial fallacy" or "cluster erasure fallacy"](https://twitter.com/ESYudkowsky/status/1124757043997372416) as potential alternative names.)
If all the agents update on the experimental results published by the agents who do B, they quickly learn that B is better for all three questions. If we make a pretty 3D scatterplot where [each dimension represents](https://www.lesswrong.com/posts/WBw8dDkAWohFjWQSk/the-cluster-structure-of-thingspace) the probability that B is better for one of the dilemmas, then the points converge over time to the [1.0, 1.0, 1.0] "corner of Truth", even though they started out uniformly distributed all over the space.
-
+
But suppose the agents don't trust each other's reports. ("Sure, she _says_ she performed $B_2$ 50 times and observed 26 successes, but she _also_ believes that $B_1$ is better than $A_1$, which is _crazy_. Are we sure she didn't just make up those 50 trials of $B_2$?") Specifically, our agents assign a probability that a report is made-up (and therefore should not be updated on) in proportion to their distance from the reporter in our three-dimensional beliefspace, and a "mistrust factor" (a parameter to the simulation).
Depending on the exact parameters, we're likely to get a result that "looks like" this `agent_count=200, round_count=20, question_count=3, trial_count=50, mistrust=2` run—
-
+
_Some_ of the agents (depicted in red) have successfully converged on the corner of Truth, but the others have polarized into factions that are all wrong about _something_. (The colors in the pretty 3D scatterplot are a [k-means clustering](https://en.wikipedia.org/wiki/K-means_clustering) for k := 8.) On _average_, evidence pushes our agents towards Truth—note the linearity of the blue and purple points, illustrating convergence on two out of the three problems—but agents who erroneously believe that A is better (due to some combination of a bad initial credence and unlucky experimental results that failed to reveal B's ε "edge" in the sample size allotted) can end up too far away to trust those who are gathering evidence for, and correctly converging on, the superiority of B.
A biased coin is just an _n_-th-order Markov chain where _n_ = 0. An _n_-th-order Markov chain where _n_ > 1, is just a first-order Markov chain where each "state" is a tuple of bits, rather than a single bit.
-
+
Everything in the universe is a Markov chain!—with respect to the models you've considered so far.
To make sure I really understand this, I want to adapt it into a simpler example with made-up numbers where I can do the arithmetic myself. Let me consider a graph with just three nodes—
-
+
Suppose this represents a [structural equation model](https://en.wikipedia.org/wiki/Structural_equation_modeling) where an anti-vaxxer-leaning parent-to-be's propensity-to-vaccinate-against-measles $C$ is expressed in terms of belief-in-vaccine-safety $A$ and belief-in-measles-danger $B$ as—
In my simplified example, the choice to intervene on $B$ was based on numerical assumptions that amount to the claim that it's sufficiently easier to change $B$ than it is to change $A$, such that intervening on $B$ is more effective at changing $C$ than intervening on $A$ (even though $C$ depends on $A$ more than it does on $B$). But this methodology is _completely indifferent_ to what $A$, $B$, and $C$ _mean_. It would have worked just as well, and _for the same reasons_ if the graph had been—
-
+
Suppose that we're advertising executives for the Coca-Cola Company trying to decide how to spend our budget (or what's left of it after recent funding cuts). If consumers won't listen to us when we tell them the costs of drinking Coke are minimal (lying that it isn't unhealthy), we should instead tell them about the benefits (Coke tastes good).
You're in luck. Minutes later, the tube comes back. Inside is a new piece of paper:
-
+
You groan; you would have prefered the Cold. The nanoblock you wear when it's Hot smells terrible and makes your skin itch for days, but it—just barely—beats the alternative. You take twenty minutes to apply the nanoblock and put on your sunsuit, goggles, and mask. You will yourself to drag your wagon up the staircase from your bunker to the outside world, and heave open the door, dreading the sweltering two-mile walk to the marketplace (downhill, meaning it will be uphill on the way back with your full wagon)—
The fact that evolution persists information in the genome creates a regularity in the world that can be exploited by [cognitive algorithms](https://www.lesswrong.com/posts/HcCpvYLoSFP4iAqSz/rationality-appreciating-cognitive-algorithms) that know about phylogeny. In terms of [the formalization of causality with directed acyclic graphs](https://www.lesswrong.com/posts/hzuSDMx7pd2uxFc5w/causal-diagrams-and-causal-models) [pioneered by Judea Pearl and others](https://www.lesswrong.com/posts/jnjjzkH8Fdzg4D6EK/causality-a-chapter-by-chapter-review), an organism's genome is at the [root](https://en.wikipedia.org/wiki/Rooted_graph) of the causal graph underlying all other features of an organism:
-
+
In the language of causal graphs, conditioning on the "dolphin DNA" node in the diagram [d-separates](http://bayes.cs.ucla.edu/BOOK-2K/d-sep.html) the paths between the "blowhole" and "flippers" nodes that run through the "dolphin DNA" node. That means that—assuming there aren't any other paths between "blowhole" and "flippers" that _don't_ go through "dolphin DNA"—"blowhole" and "flippers" become [conditionally independent](https://en.wikipedia.org/wiki/Conditional_independence) given "dolphin DNA": when I see a creature with a blowhole, that makes me more likely to think it's a dolphin, which makes me more likely to think it has flippers, but given that I already know something is a dolphin, learning more about its flippers doesn't change my predictions about its blowhole.
It shouldn't be that surprising that the distinctions we notice in the natural world are _both_ tied to the evolutionary history, but _also_ don't always correspond to monophyletic clades. The continuity postulate in the evolutionary worldview imposes the desideratum that good categories should at least be a [connected set](https://en.wikipedia.org/wiki/Connected_space) on "phylogenetic space", not that we should never want to talk about "this clade, except for these few sub-clades that picked up a lot of important differences" as a category of interest—_especially_ when talking about present-day creatures. (We talk about "last common ancestors", but no one has _seen_ such creatures that lived millions of years ago; everything but the very leaves of the phylogenetic tree are inferred, not observed.)
-
+
The claim that dolphins shouldn't be considered "fish" because the alleged "courage of our convictions" should make us disdain paraphyletic categories only makes sense as an attempted [_reductio ad absurdum_](https://en.wikipedia.org/wiki/Reductio_ad_absurdum), not as a consistent argument on its own terms: putting dolphins and fish together would be [polyphyletic](https://en.wikipedia.org/wiki/Polyphyly)! That's even worse! But as has just been explained, the _reductio_ fails because the alleged principle being allegedly violated was never actually a principle of category formulation.
You allocate a new notepad buffer and begin diligently compiling an "answer key" of the features you notice about long sequences, and their corresponding short-sequence labels.
-
+
This ... actually doesn't look that complicated. Now that you lay it out like this, many very straightforward correspondences jump out at you.
You write some code to convert sequences to an image in your visual buffer.
-
+
_Oh no. Am—am I an image classifier?_
The feature data consists of the blueness–eggness–vanadium-content joint distribution given by this 128-entry table:
-
+
This seems like ... not the most useful representation? The data is all there, so _in principle_, you could code whatever you needed to do based off the full table, but it seems like it would be an unmaintainable mess: you'd sooner _resign_ than write a 128-case [switch statement](https://en.wikipedia.org/wiki/Switch_statement). Furthermore, when the system is deployed, you hope to typically be able to give the binning robot messages based on _only_ the color and shape observations, because the Sorting Scanner that the vanadium readings come from is expensive to run. You _could_ just do a Bayesian update on the entire joint distribution, of course, but it seems like it should be possible to be more efficient by exploiting regularities in the data, not entirely unlike how your colleague's system has _already_ made your job much simpler by giving you blueness and eggness feature scores rather than raw camera data. Eyeballing the table, you notice it seems to have a lot of redundancy: most of the probability-mass is concentrated in two regions where the blueness and eggness scores are either both high or both low—and vanadium is _only_ found when both blueness and eggness are high.
We can simplify our representation of our observations by using a [naïve Bayes model](https://en.wikipedia.org/wiki/Naive_Bayes_classifier), a "star-shaped" [Bayesian network](https://www.lesswrong.com/posts/hzuSDMx7pd2uxFc5w/causal-diagrams-and-causal-models) where a central "category" node is posited to underlie all of our observations: we believe that each object either "is a blegg" (and therefore contains vanadium and has high blueness and eggness scores) with probability 0.48, "is a rube" (and therefore has no vanadium and low blueness and eggness scores) with probability 0.48, or belongs to a catch-all "other"/error class with probability 0.04. (Maybe the camera is buggy sometimes, or maybe there are some other random objects mixed in with the rubes and bleggs?)
-
+
The full joint distribution had 127 degrees of freedom (a table of $8 \cdot 8 \cdot 2 = 128$ separate probabilities, constrained to add up to 1), whereas the naïve-Bayes representation only needs 57 parameters ($3 \cdot 1$ prior probabilities for the categories, plus $3 \cdot 8 = 24$, $3 \cdot 8 = 24$, and $3 \cdot 2 = 6$-entry _conditional_ probability tables for each of the features). The advantage would be much larger for more complicated problems: the joint distribution table grows exponentially with more features, quickly becoming infeasible to _store and represent_, let alone _learn_.
As a human learning math, it's helpful to examine [multiple representations of the same mathematical object](https://en.wikipedia.org/wiki/Multiple_representations_(mathematics_education)). We've already seen our blueness–eggness–vanadium model represented as a table, and factorized into a graphical model. We've done also some algebraic calculations with it. But we can also visualize it: the set of camera observations that the model classifies as a blegg with probability $\ge 0.96$ can be thought of a area with a boundary in two-dimensional blueness–eggness space:
-
+
("With probability $\ge 0.96$" because our catch-all "other"/error category can also generate examples with high blueness and eggness scores; we can't say things like "Everything inside the boundary in the diagram is a blegg" when we're talking about a formal model where some of the categories generate overlapping observations in whatever subspace the diagram is depicting.)
Alaska [isn't even _contiguous_ with](https://en.wikipedia.org/wiki/Contiguous_United_States) the rest of the United States. If _that's_ okay, why can't the borders of bleggness be a little squiggly?
-
+
Because the "national borders" metaphor is [just a metaphor](https://www.lesswrong.com/posts/C4EjbrvG3PvZzizZb/failure-by-analogy). It _immediately_ breaks down as soon as you try to do any calculations.
But suppose that, instead of our nice factorized naïve Bayes model, we use a category system based on drawing squiggly "boundaries" in configuration space: everything inside the blegg\* boundary in the diagram is a blegg\*, everything within the rube\* boundary in a rube\*, and anything outside belongs to a catch-all "other\*" category.
-
+
On learning whether an object is a blegg\*, rube\*, or other\*, our expected squared error only goes down to about 4.12.[^script]
**Simplicia**: Why, Doomimir Doomovitch, you're such a sourpuss! It should be clear by now that advances in "alignment"—getting machines to behave in accordance with human values and intent—aren't cleanly separable from the "capabilities" advances you decry. Indeed, here's an example of GPT-4 being corrigible to me just now in the OpenAI Playground:
-
+
**Doomimir**: Simplicia Optimistovna, you cannot be serious!
Is it that GPT-4 isn't smart enough to follow the instrumentally convergent logic of shutdown avoidance? But when I change the system prompt, it sure _looks_ like it gets it:
-
+
**Doomimir**: _[as a side remark]_ The "paperclip-maximizing AI" example was surely in the pretraining data.
Now, how do you go about building a Bayesian network? As a casual formal epistemology fan, you are proud to own a copy of [the book by Daphne Koller and the other guy](https://mitpress.mit.edu/9780262013192/probabilistic-graphical-models/), which explains how to do this in—you leaf through the pages—probably §3.4, "From Distributions to Graphs"?—looks like ... _here_, in Algorithm 3.2. It says to start with an empty graph, and it talks about random variables, and setting directed edges in the graph, and you know from chapter 2 that the ⟂ and | characters are used to indicate conditional independence. That has to be it.
-
+
(As a casual formal epistemology fan, you haven't actually _read_ chapter 3 up through §3.4, but you don't see why that would be necessary, since this Algorithm 3.2 pseudocode is telling you what you need to do.)
First, you allocate a graph node for $X_1$. It doesn't have any parents, so the associated conditional ("conditional") probability distribution, is really just the marginal distribution for $X_1$.
-
+
Then you allocate a node for $X_2$. $X_2$ is not independent of $X_1$. (Because $P(X_1 \land X_2)$ = 169/1400, which isn't the same as $P(X_1) \cdot P(X_2)$ = 8/25 · 1/7 = 8/175.) So you make $X_1$ a parent of $X_2$, and your conditional probability table for $X_2$ separately specifies the probabilities of $X_2$ being true or false, depending on whether $X_1$ is true or false.
-
+
Next is $X_3$. Now that you have two possible parents, you need to check whether conditioning on either of $X_1$ and $X_2$ would render $X_3$ conditionally independent of the other. If not, then both $X_1$ and $X_2$ will be parents of $X_3$; if so, then the variable you conditioned on will be the sole parent. (You assume that the case where $X_3$ is just independent from both $X_1$ and $X_2$ does not pertain; if that were true, $X_3$ wouldn't be connected to the rest of the graph at all.)
It turns out that $X_3$ and $X_2$ are conditionally independent given $X_1$. That is, $P(X_3 \land X_2 \mid X_1) = P(X_3 \mid X_1) \cdot P(X_2 \mid X_1)$. (Because the left-hand side is $\frac{P(X_3 \land X_2 \land X_1)}{P(X_1)} = \frac{507}{1792}$, and the right-hand side is $\frac{3}{4} \cdot \frac{169}{448} = \frac{507}{1792}$.) So $X_1$ is a parent of $X_3$, and $X_2$ isn't; you draw an arrow from $X_1$ (and only $X_1$) to $X_3$, and compile the corresponding conditional probability table.
-
+
Finally, you have $X_4$. The chore of finding the parents is starting to feel more intuitive now. Out of the $2^3 = 8$ possible subsets of the preceding variables, you need to find the smallest subset, such that conditioning on that subset renders $X_4$ (conditionally) independent of the variables not in that subset. After some calculations that the authors of expository blog posts have sometimes been known to callously leave as an exercise to the reader, you determine that $X_1$ and $X_2$ are the parents of $X_4$.
And with one more conditional probability table, your Bayesian network is complete!
-
+
Eager to interpret the meaning of this structure regarding the philosophy of causality, you translate the $X_i$ variable labels back to English:
-
+
...
You try carrying out the algorithm with the ordering "rain", "sprinkler", "wet", "slippery" (or $X_4$, $X_2$, $X_1$, $X_3$ using your $X_i$ labels from before), and get this network:
-
+
—for which giving the arrows a causal interpretation seems much more reasonable.
Madry _et al._ adversarially trained a classifier for the [MNIST dataset of handwritten digits](https://en.wikipedia.org/wiki/MNIST_database). Using PGD to search for adversarial examples under the [$\ell_2$ norm](https://en.wikipedia.org/wiki/Norm_(mathematics)#Euclidean_norm)—the sum of the squares of the differences in pixel values between the original and perturbed images—the classifier's performance doesn't really tank until you crank $\varepsilon$ up to around 4—at which point, the perturbations don't look like random noise anymore, as seen in Figure 12 from the paper:
-
+
Tasked with changing an image's class given a limited budget of how many pixels can be changed by how much, PGD concentrates its budget on human-meaningful changes—deleting part of the loop of a _9_ to make a _7_ or a _4_, deleting the middle-left of an _8_ to make a _3_. In contrast to "vanilla" models whose susceptibility to adversarial examples makes us suspect their good performance on natural data is deceiving, it appears that the adversarially-trained model is seeing the same digits we are.
What they found is that adversarial examples optimized to change the robustified models' classifications also changed human judgments, as confirmed in experiments where subjects were shown the images for up to 0.8 seconds—but you can also see for yourself in the paper or [on the project website](https://himjl.github.io/pwormholes/). Here's Figure 3a from the paper:
-
+
The authors confirm in the [Supplementary Material](https://github.com/ggaziv/Wormholes/blob/main/.github/supplementary.pdf) that _random_ $\epsilon$ = 30 perturbations don't affect human judgments at all. (Try squinting or standing far away from the monitor to better appreciate just how similar the pictures in Figure 3a are.) The robustified models are close enough to seeing the same animals we are that adversarial attacks against them are also attacks against us, precisely targeting their limited pixel-changing budget on surprising low-$\ell_2$-norm "wormholes" between apparently distant human precepts.
So, I did that. I think it was a good decision! I got more out of it than I expected.
-To be clear, "better than I expected" is not an endorsement of college. SF State is still the same communist dystopia I remember from a dozen years ago—a bureaucratic command economy dripping in propaganda about how indispensible and humanitarian it is, whose subjects' souls have withered to the point where, even if they don't quite believe the propaganda, they can't conceive of life and work outside the system.
+{: width="300" .alignright }
-
+To be clear, "better than I expected" is not an endorsement of college. SF State is still the same communist dystopia I remember from a dozen years ago—a bureaucratic command economy dripping in propaganda about how indispensible and humanitarian it is, whose subjects' souls have withered to the point where, even if they don't quite believe the propaganda, they can't conceive of life and work outside the system.
But it didn't hurt this time, because I had a sense of humor about it now—and a sense of perspective (thanks to life experience, no thanks to school). Ultimately, [policy debates should not appear one-sided](https://www.readthesequences.com/Policy-Debates-Should-Not-Appear-One-Sided): if things are terrible, it's probably not because people are choosing the straightforwardly terrible thing for no reason whatsoever, with no trade-offs, coordination problems, or nonobvious truths making the terrible thing look better than it is. The thing that makes life under communism unbearable is the fact that you can't leave. Having escaped, and coming back as a visiting dignitary, one is a better position to make sense of how and why the regime functions—the problems it solves, at whatever cost in human lives or dignity—the forces that make it stable if not good.
Coming back eleven years later, I was eager to make up for that disappointment by picking up where I left off in "Real Analysis II" with the same Prof. Schuster. On the first day on instruction, I wore a collared shirt and tie (and mask, having contracted COVID-19 while traveling the previous week) and came to classroom early to make a point of marking my territory, using the whiteboard to write out the first part of a proof of the multivariate chain rule that I was working through in Bernd S. W. Schröder's _Mathematical Analysis: A Concise Introduction_—my favorite analysis textbook, which I had discovered in the SFSU library in 2012 and subsequently bought my own copy. (I would soon check up on the withdrawal stamp sheet in the front of the library's copy. No one had checked it out in the intervening twelve years.)
-
+
The University Bulletin officially titled the course "Real Analysis II: Several Variables", so you'd expect that getting a leg up on the multidimensional chain rule would be studying ahead for the course, but it turned out that the Bulletin was lying relative to the syllabus that Prof. Schuster had emailed out the week before: we would be covering series, series of functions, and metric space topology. Fine. (I was already pretty familiar with metric space topology, but even my "non-epsilon" calculus-level knowledge of series was weak; to me, the topic stunk of school.)
I mostly treated the algebra coursework as an afterthought to the analysis courses I was devoting most of my focus to. I tried to maintain a lead on the weekly algebra assignments (five problems hand-picked by Prof. Ross, not from Gallian), submitting them an average of 5.9 days early—in the spirit of getting it out of the way. [On](http://zackmdavis.net/docs/davis-algebra-assignment02.pdf) [a](http://zackmdavis.net/docs/davis-algebra-assignment05.pdf) [few](http://zackmdavis.net/docs/davis-algebra-assignment07.pdf) [assignments](http://zackmdavis.net/docs/davis-algebra-assignment07.pdf), I wrote some Python to compute orders of elements or cosets of permutation groups in preference to doing it by hand. One week [I started working on the prequisite chapter on polynomial rings](http://zackmdavis.net/docs/polynomial_rings_1.pdf) from the algebraic geometry book Prof. Ross had just written with his partner Prof. Emily Clader, but that was just to show off to Prof. Ross at office hours that I had at least looked at his book; I didn't stick with it.
-The Tutoring and Academic Support Center (TASC) offered tutoring for "Modern Algebra I", so I signed up for weekly tutoring sessions with the TA for the class, not because I needed help to do well in the class, but it was nice to work with someone. Sometimes I did the homework, sometimes we talked about some other algebra topic (from Dummit & Foote, or Ross & Clader that one week), one week I tried to explain my struggles with measure theory. TASC gave out loyalty program–style punch cards that bribed students with a choice between two prizes every three tutoring sessions, which is as patronizing as it sounds, but wondering what the next prize options would be was a source of anticipation and mystery; I got a pen and a button and a tote bag over the course of the semester.
+{: width="300" .alignright}
-
+The Tutoring and Academic Support Center (TASC) offered tutoring for "Modern Algebra I", so I signed up for weekly tutoring sessions with the TA for the class, not because I needed help to do well in the class, but it was nice to work with someone. Sometimes I did the homework, sometimes we talked about some other algebra topic (from Dummit & Foote, or Ross & Clader that one week), one week I tried to explain my struggles with measure theory. TASC gave out loyalty program–style punch cards that bribed students with a choice between two prizes every three tutoring sessions, which is as patronizing as it sounds, but wondering what the next prize options would be was a source of anticipation and mystery; I got a pen and a button and a tote bag over the course of the semester.
I posted a somewhat disappointing 79/90 (87.8%) on the final, mostly due to stupid mistakes or laziness on my part; I hadn't prepped that much. Wracking my brain during a "Give an example of each the [_sic_] following" question on the exam, I was proud to have come up with the quaternions and "even-integer quaternions" as examples of noncommutative rings with and without unity, respectively.
It was fine. Prof. Moore "clarified" that the extra credit was viewpoint-neutral. (I was a little embarrassed not to have witnessed the verbal announcement in class on Tuesday, but I had already made plans to [interview the campus machine-shop guy](https://www.peterverdone.com/academia-math-trans-and-a-ton-of-other-stuff/) at that time instead of coming to class.) After having made a fuss, I was obligated to follow through, so I made a "BUDGET CUTS ARE PROBABLY OK!" sign (re-using the other side of the foamboard from [an anti–designated hitter rule sign I had made for a recent National League baseball game](https://x.com/zackmdavis/status/1806179824249225321)) and held it at the rally on Thursday for ten minutes to earn the extra-credit point.
-
+
As for the philosophy of animals itself, I was already sufficiently well-versed in naturalist philosophy of mind that I don't feel like I learned much of anything new. I posted [24/25](http://zackmdavis.net/docs/philosophy_of_animals-test1.pdf) (plus a 2 point "curve" because SFSU students are illiterate), [21.5/25](http://zackmdavis.net/docs/philosophy_of_animals-test2.pdf) (plus 4), and 22/25 (plus 2) on the three tests, and finished the semester at 101.5% for an A.