Easier AI

explainable multi-objective active learning, in a few hundred lines

Menzies · 2026
github.com/timm/ezr

index · map · python · maths · ai · se · scripting

ezr.py, mapped

Generated by make ezr_map.md: every section and top-level signature, with its one-line comment.

def atom(s,bools={'True': True, 'False': False}):
pat = r"(\w+)=(\S+)"
the = o(**{k: atom(v) for k,v in re.findall(pat, __doc__ or "")})
def csv(file):

structs

Num = lambda: (0, 0, 0) # n, mu, m2: all Welford keeps
Sym = dict
def is_num(col): return isinstance(col, tuple)
def sd(col): return 0 if col[0] < 2 else sqrt(col[2]/(col[0]-1))
def add(col, v, inc=1): # new Num, or updated Sym; inc=-1 undoes
def adds(lst, it=None): # accumulate a list into it
def size(col):
def div(col): # Num: sd. Sym: entropy
def Tbl(src):
def clone(tbl, rows=[]): return Tbl([tbl.names] + rows)
def addRow(tbl, row=None, inc=1): # inc=-1 pops the last row

distance

def norm(col, v):
def mid(col):
def mids(tbl): # centroid; only ever read over x columns
def ydist(tbl, row):
def _dist(col, a, b):
def xdist(tbl, row, m):
def ymu(tbl, rows):
def ymids(tbl, rows):

acquire

def pop(tbl, best, rest, todo):
def label(tbl, best, rest, row): # keep best pool near sqrt
def acquire(tbl, cap=None):

bayes

def like(col, v, prior=0): # P(v | col)
def likes(tbl, row, nall, nh): # log P(tbl | row), unscaled
def liked(tbls, row): # most likely of several tables
def confuse(pairs): # (got, want)s --> per-klass scores

tree

def xpect(a, b): # sizes are >= the.Leaf, so no zero guard
def cutNum(xy, acc): # (left, right, x) per value boundary
def cutSym(xy, acc): # (in, out, sym), one per symbol
def cut(tbl, rows, ys, acc): # best (col, val) split
def routing(tbl, at, v):
def tree(tbl, rows, edge="", y=None):
def kids(n): return n[5:]
def leaf(tr, row):

report

def leafs(tr):
def show(tbl, tr):

tests

def wins(tbl):
def holdout(tbl):

start-up

def test_help():
  "Show usage, settings, demos"
def test_num():
  "Welford add matches textbook mean and sd"
def test_sym():
  "Syms count; mid is mode; div is entropy"
def test_tbl():
  "Headers route columns to x, y, klass, or nowhere"
def test_cuts():
  "cut returns a legal, routable split"
def test_wins():
  "wins grades the best row 100"
def test_tree():
  "Acquire, grow and show the.File's tree"
def test_holdout():
  "Mean win over 20 train/test holdouts"
def _klass(fit, file="$MOOT/classify/diabetes.csv"):
def test_klassTree():
  "Tree classify diabetes: pd, pf, prec per class"
def test_klassBayes():
  "Bayes classify diabetes: pd, pf, prec per class"
def test_all():
  "Run every demo; exit code counts the crashes"
def run(f=None):
def cli(d, funs, args, n=0):

who calls what

csv      -> atom
add      -> is_num
adds     -> add
size     -> is_num
div      -> is_num sd
Tbl      -> addRow
clone    -> Tbl
addRow   -> add
norm     -> sd
mid      -> is_num
mids     -> mid
ydist    -> norm
_dist    -> is_num norm
xdist    -> _dist
ymu      -> ydist
pop      -> mids xdist
label    -> addRow ydist
acquire  -> clone label pop
like     -> is_num sd size
likes    -> like
liked    -> likes
xpect    -> size div
cutNum   -> adds add
cutSym   -> adds
cut      -> cutNum cutSym is_num size xpect
routing  -> is_num
tree     -> mid ymids routing ydist adds cut
leaf     -> kids
leafs    -> kids
show     -> leafs kids
wins     -> ydist
holdout  -> clone tree acquire ydist leaf
_klass   -> Tbl csv confuse
cli      -> run atom

55 functions; median 5 lines; longest 14.