kragen
h = X
for i in range(nlayers):
z = h @ P[2 * i] + P[2 * i + 1]
# ...
h = np.maximum(z, 0) if i < nlayers - 1 else z # ReLU; linear output
# ...
logit = h[:, 0]
# ...
# accuracy of the last forward pass, i.e. before the final update
return ((logit > 0) == (y > 0.5)).mean()
looks like a perfectly orthodox evaluation of a multilayer perceptron, with weights and biases in P, and that's what it's evaluating the accuracy on. I dumped out P for one of the [8, 64, 1] networks, and it does indeed look like about 650 parameters. I was going to post it here, but printed out as text it's like three pages long, so I won't.An 80386 is about 10–13 Dhrystone MIPS, while a 200MHz Pentium Pro is about 90–450, according to https://netlib.org/performance/html/dhrystone.data.col0.html. So we're talking about a further slowdown on the order of 20×. I don't know how to estimate how much that would lower its Elo.
The article says:
> By making a number of adjustments to the algorithm and tuning it to the Pentium, we can increase its Elo to 2846 ± 35 at 1 second per move, 3109 ± 79 at 10 seconds per move, and 3180 ± 80 at 30 seconds per move.
So making it 30× faster would raise its 1-second Elo by about 334 points, and ln(30) is 3.4, so that's about 100 points per factor of e. ln(10) is 2.3, so we'd interpolate a prediction of 230 points gained at 10× faster, reasonably close to the 263 points reported.
If we extrapolate that in the other direction, ln(20) is 3.0, so on the 80386 we'd expect a loss of about 300 Elo points, down from 2846 to 2550 or so. Still enough to wipe the floor with Crafty, but low enough that Kasparov would usually beat it.
But we don't really know if that extrapolation is valid.
And, with more work, that's true of computers in general.
# Fits small MLPs to 8-bit parity (the full 256-row truth table) with
# full-batch AdamW, written out in numpy (no PyTorch, so it won't match a
# PyTorch run bit for bit), and reports for each shape how many of 20 seeds
# reach 100% accuracy and the median accuracy. Needs Python 3.9+ and numpy.
import itertools
import numpy as np
X = np.array(list(itertools.product([0, 1], repeat=8)), float) # all 256 inputs
y = X.sum(1) % 2
def nparams(widths: list[int]) -> int:
"""Parameter count of a dense MLP with these layer widths, biases included."""
return sum(a * b + b for a, b in zip(widths, widths[1:]))
def train(
widths: list[int],
seed: int,
steps: int = 4000,
lr: float = 1e-2,
wd: float = 1e-4,
) -> float:
"""Full-batch AdamW on all 256 inputs; returns the final training accuracy."""
rng = np.random.default_rng(seed)
P = [] # parameters: W0, b0, W1, b1, ...
for a, b in zip(widths, widths[1:]):
bound = 1 / np.sqrt(a) # same as PyTorch nn.Linear's default init
P += [rng.uniform(-bound, bound, (a, b)), rng.uniform(-bound, bound, b)]
m = [np.zeros_like(p) for p in P] # Adam first moments
v = [np.zeros_like(p) for p in P] # Adam second moments
nlayers = len(widths) - 1
for step in range(1, steps + 1):
hs = [X] # hs[i] is the input to layer i
zs = [] # pre-activations
h = X
for i in range(nlayers):
z = h @ P[2 * i] + P[2 * i + 1]
zs.append(z)
h = np.maximum(z, 0) if i < nlayers - 1 else z # ReLU; linear output
hs.append(h)
logit = h[:, 0]
prob = 0.5 * (1 + np.tanh(logit / 2)) # sigmoid without overflow
g = ((prob - y) / len(y))[:, None] # d(mean cross-entropy)/d(logit)
G = [None] * len(P) # gradients, parallel to P
for i in reversed(range(nlayers)):
G[2 * i] = hs[i].T @ g
G[2 * i + 1] = g.sum(0)
if i > 0:
g = (g @ P[2 * i].T) * (zs[i - 1] > 0)
# AdamW, using PyTorch's default values for betas (0.9, 0.999) and eps (1e-8)
for k in range(len(P)):
P[k] -= lr * wd * P[k] # decoupled weight decay
m[k] = 0.9 * m[k] + 0.1 * G[k]
v[k] = 0.999 * v[k] + 0.001 * G[k] ** 2
P[k] -= lr * (m[k] / (1 - 0.9 ** step)) / (
np.sqrt(v[k] / (1 - 0.999 ** step)) + 1e-8)
# accuracy of the last forward pass, i.e. before the final update
return ((logit > 0) == (y > 0.5)).mean()
for w in [[8, 1, 2, 2, 2, 1], [8, 8, 4, 2, 1], [8, 8, 1], [8, 16, 1], [8, 64, 1]]:
accs = [train(w, seed) for seed in range(20)]
print(w, nparams(w), "params; seeds at 100%:", sum(a == 1 for a in accs),
"/ 20; median acc", np.median(accs))
I might not know enough about ANNs to spot if it's cheating on some way, but it looks legit. It does indeed output that the [8, 64, 1] configuration gets 100% accuracy on all 20 seeds.8-bit parity is a^b^c^d^e^f^g^h, which you can compute with minimal depth as ((a^b)^(c^d))^((e^f)^(g^h)). You can compute x^y as x&~y | y&~x. With ReLU activation, i | j is ReLU(i + j): weights of 1, bias of 0. And i & ~j is ReLU(i - j): weights of 1 and -1, still bias of 0. So a two-input XOR is three neurons in two layers, and the tree of 7 binary XORs is 21 neurons in 6 hidden layers. I don't know why Astra needed 433 parameters. Only 42 nonzero parameters are needed. Maybe the other 391 parameters are zeros that the Transformer model demands to be present.
With an RNN you can compute the parity of a string of any length with only three neurons.
Actually, I handed that to Claude Opus 5.5 to check, and it sensibly pointed out that x&~y is mutually exclusive with y&~x, so XOR doesn't even need the third neuron, except in the last layer; you can just fold the addition into the weights of the next layer, which means you only need 4 layers and 15 neurons. But about half of those neurons need 4 nonzero input weights instead of 2, so you still need 42 nonzero weights. It's a good thing you don't need those OR neurons, too, because my definition is wrong: ReLU(1+1) = 2.
Also it explained how to design a 28-parameter net that I don't quite understand, and also a single-hidden-layer network with 8 ReLUs, which I also don't understand, but it needs more parameters: $\text{parity}=s+\sum_{k=1}^{7}2(-1)^k\,\mathrm{ReLU}(s-k)$ where I think $s=\sum_i x_i$.
Somewhat more surprisingly, it also said it didn't have any trouble fitting a 641-parameter network to this problem with AdamW.
I don't think it's accurate that "almost all the money in personal computing was made in mobile". Quite a bit of money was made in personal computing before the iPhone, and quite a bit of money has been made in non-mobile computing since the iPhone.
I also don't think it's accurate that mobile "didn't use any of these ideas". Android's UI is not quite as close to Smalltalk-78 as what I'm using right now, but you still have pointers, scrollbars, text selections, proportional fonts, vertical-line cursors, buttons, menus, object-oriented programming, sometimes a bytecode interpreter, and even windows. You just don't have as much freedom with where you position the windows. Android's UI is still a lot closer to Smalltalk-78 than it is to, for example, acme, GENESYS, the ITS HACTRN, FORTH, or PDP-11 Unix.
By giving hundreds of workers each a powerful desk-sized Alto minicomputer for their own needs, with a high-end graphical display, they gained insight into what desktop personal computing would look like 15 years later, enough insight to shape it profoundly. It's been 55 years, and although multitouch screens are proliferating, I'm still typing this on a keyboard hooked up to a framebuffer-driven display with a mouse hooked up to it, displaying hypertext, festooned with windows, icons, and a mouse that moves an onscreen pointer at the same 135° angle they settled on 55 years ago, positioning a vertical line to indicate the insertion point, with scrollbars. The display has twice as many pixels, the pixels are color, the mouse pointer is white rather than black, the screen is a flat LCD, the scrollbars are on the right, and the cursor blinks, but it's very recognizably descended from https://smalltalkzoo.computerhistory.org/HOPL-St78.html rather than, for example, ITS or GENESYS.
Only people with the luxury of unlimited zero-cost CPU cycles had a chance of seeing what the future looked like, and that allowed them to find out what worked and what didn't work in that environment. Nowadays, the regular workers who don't have the luxury of unlimited zero-cost tokens are manually reviewing voluminous AI code, because, even though that's a lot of slow work, they're still much faster programming that way than they were without it. Anthropic, instead, is doing insane things like eliciting a billion lines of Lean proofs to show that existing open-source libraries can't fail their OSS-FUZZ tests — and fixing all the bugs that effort turned up, in the process.
As an example, as I understand it, if a hotel chain allows an employee to stay in one of their hotels for free when they go on vacation, the tax value of that benefit is what they would have charged someone for that hotel room, not how much it increased the hotel's costs to have one more hotel room full — which is generally almost nothing.
Of course, the IRS does agree with you when it comes to expenditures that are for the employer's benefit, rather than the employee's. If it didn't, almost all companies would have negative profits from the IRS's point of view.
They changed that a few years ago, but plausibly the underlying prioritization of end-user experience is still lower than Ubuntu's degree of user focus.
I still wish Mark would rename his company "Ubuntu". He's a great guy, the company does great work, but I keep getting undeserved credit for it because we wouldn't sell him the domain name.
Many people will prefer Markdown or Commonmark for markup.
She didn't have much choice of fonts on the pre-Graftrax Epson MX-80 she used. I think it was Pica or Elite.
Typesetting is certainly very satisfying, but it's not essential to writing.
You might object that, when it was first released, before Windows, it was not the first text editor on the operating system, which was MS-DOS, and so "Notepad" might have already been too generic even then. Microsoft would have agreed with you, because that version was called "Multi-Tool Notepad".
"Hey, how do I insert one file into another in text editor?"
"Which text editor?"
"Text editor."
"Sounds like a personal problem."
Atto seems to be a little smaller than that version of ae, though.
I think this alias was an aid to porting Lisp from some other Emacs variant in the 80s that used "dot" instead of "point". Never figured out which. It isn't mg because mg doesn't run Lisp.