Illusion Slopes

Max’s personal homepage

Posts

Hugo migration

Posted

If you’re reading this, I finally updated the backend of the site to use a new framework—from Jekyll to Hugo. I also took the opportunity to redo the homepage a bit. I don’t have as many blog-post-shaped thoughts as I used to, so I reduced the post previews to just links to create room for more freeform content.

The Hugo migration was a bit of work because I took my time and did it the fun way. The old version of the site used Jekyll and had accrued a lot of Jekyll’s “isms,” along with weird conventions of my own. I wrote a custom content migration script to transform Jekyll tags automatically where possible and flag for manual attention where needed.

I think/hope it worked. Old post links will redirect you to the new URLs. For RSS subscribers, depending on how your reader is configured, you may pick up a new copy of every post; you can just mark as read everything before today’s date.

Systems for making systems

Posted

I read a comment stating that git (a tool programmers use to collaborate on code, sort of like Word’s Track Changes) is not a version control system but a “system for creating a version control system.”

That got me thinking about other such “metasystems” I use, and the frustration that arises when one expects a mere system and instead gets the meta thing.

PageRanking People and Blogs interviews

Posted

I recently learned about PageRank, the original Google algorithm for ranking webpages. It works by constructing a Markov chain on links between pages and computing the stationary distribution. The stationary probability of a given page measures the page’s centrality in the web, an index of popularity.

I wanted to try implementing the algorithm myself and fiddle with a few Rust libraries, so I wrote a script that runs PageRank on interviews from the People and Blogs series. P&B interviews are a tidy dataset for the algorithm, because Manu always asks interviewees to recommend other blogs, then he uses these recommendations to pick subsequent interviewees. This means the graph is well connected despite its small size.

I was going to share the results here, but ranking blogs by popularity seems against the spirit of the indie web ethos, so you’ll have to run the program yourself. (But to make it clear I’m not covering anything up, let me acknowledge that my interview is in a 61-way tie for last place, with all the other blogs that had no links to them.)

A few observations from this exercise below.

Find your perfect match with integer programming

Posted

Owen Lacey blogged about a reality game show called Are You the One? in which contestants win a prize by guessing the soulmate ordained for them by the show’s producers. Specifically, there are nn men and nn women, and each one has an unknown “perfect” match; to win the prize, the contestants (as a group) must pair everyone up correctly.

During each episode, the contestants get two kinds of clues:

  1. Truth Booth, where the contestants submit a single couple and learn whether that couple is a perfect match.
  2. Match Up, where the contestants submit a matching (assigning everyone to a couple) and learn the number (but not the identity) of perfect matches present in their matching.

The game ends after Match Up if all nn couples in the matching are correct. Definitely check out Owen’s post, which has a better (and illustrated!) explanation of the rules.

Below, I present an efficient algorithm for playing Are You the One. It exploits both the informational clues and contestants’ intuitions to find perfect matches quickly. With modest assumptions on the quality of players’ intuitions, my algorithm wins by episode 10 in 100% of simulated seasons.

Pytest + Ruff + Mypy

Posted

There is a pytest-ruff plugin for Pytest (a Python testing framework) that will automatically run ruff format --check and ruff check as part of your test suite. The pytest-mypy plugin does the same thing for Mypy. These plugins are handy, but for some reason , the tests they generate are exempted from the pytest -k ... logic. Normally, you can use -k to select a subset of tests to run, but if you install one of the plugins mentioned, that plugin’s tests run no matter what. This can slow you down if, like me, you develop on a slow computer.

So, here’s what I use instead of pytest-ruff and pytest-mypy: A humble test/test_qa.py file with three functions.

import subprocess
import sys
from pathlib import Path

import mypy.api

REPO_ROOT = Path(__file__).parent.parent


def test_mypy():
    stdout, stderr, returncode = mypy.api.run([str(REPO_ROOT)])
    sys.stdout.write(stdout)
    sys.stderr.write(stderr)
    assert returncode == 0


def test_ruff_format():
    subprocess.run(
        [sys.executable, "-m", "ruff", "format", "--check"],
        cwd=REPO_ROOT,
        check=True,
    )


def test_ruff_lint():
    subprocess.run(
        [sys.executable, "-m", "ruff", "check"],
        cwd=REPO_ROOT,
        check=True,
    )

(I run Mypy via its Python API instead of the command line to save a tiny bit of subprocess.run() overhead, but it doesn’t make a massive difference.)

This gets you all the functionality of the plugins, but with two fewer dependencies, and the -k flag works:

# Run tests for an in-progress feature and skip QA
pytest -k test_some_feature

# Run only the QA tests
pytest -k test_qa

# Run all tests
pytest