Drop7 Research
approaches/lifetime-objective/suite-validation/leaf_weights.pyPython118 lines · 4.6 KB
#!/usr/bin/env python3
"""Is the frozen leaf missing features, or missing weights?

Fits the frozen leaf's own 19 features to the exactly-achievable clear rate on
the training split and compares the fitted direction with the frozen weight
vector from approaches/fair-expectimax/reference/fair-only-horizon.cpp:53-71.

Both vectors are put on the same footing by scaling each weight by the feature's
standard deviation over the corpus - that is the change in leaf units produced by
a one-sigma change in the feature, which is the only sense in which a
+180-per-open-column and a -1250-per-danger-unit weight are comparable.  Each
vector is then normalised to unit L2 norm, so what is compared is the DIRECTION
the leaf points in, not its arbitrary overall scale.

Usage: leaf_weights.py <structure.csv> [--target achievableClears]
"""

import csv
import os
import sys

sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
import stats  # noqa: E402

# fair-only-horizon.cpp:53-71, in the order fairLeaf accumulates them.
FROZEN = [
    ("leaf_open_columns", 180.0),
    ("leaf_height_load", -20.0),
    ("leaf_solid_cells", -620.0),
    ("leaf_cracked_cells", -220.0),
    ("leaf_numbered_cells", -18.0),
    ("leaf_high_low_numbers", -90.0),
    ("leaf_direct_potential", 1600.0),
    ("leaf_latent_chain_potential", 700.0),
    ("leaf_cracked_exposure", 100.0),
    ("leaf_solid_exposure", 40.0),
    ("leaf_adjacent_ones", -550.0),
    ("leaf_triple_twos", -750.0),
    ("leaf_dead_low_numbers", -120.0),
    ("leaf_covered_height_risk", -95.0),
    ("leaf_low_number_height_risk", -85.0),
    ("leaf_danger_height_squared", -1250.0),
    ("leaf_roughness", 0.0),
    ("leaf_rise_pressure", -35.0),
    ("leaf_next_disc_vertical_options", 220.0),
]


def fnv1a64(data: bytes) -> int:
    h = 1469598103934665603
    for byte in data:
        h ^= byte
        h = (h * 1099511628211) & 0xFFFFFFFFFFFFFFFF
    return h


def main():
    path = sys.argv[1]
    target = "achievableClears"
    if "--target" in sys.argv:
        target = sys.argv[sys.argv.index("--target") + 1]
    with open(path) as handle:
        rows = [r for r in csv.DictReader(handle) if int(r["completionsSolved"]) > 0]
    train = [r for r in rows
             if fnv1a64(("structure-holdout-v1:" + r["id"]).encode()) % 1000 < 600]

    names = [n for n, _ in FROZEN]
    columns = [[float(r[n]) for r in train] for n in names]
    sds = [stats.stdev(c) or 1.0 for c in columns]
    means = [stats.mean(c) for c in columns]
    design = [[(float(r[n]) - means[i]) / sds[i] for i, n in enumerate(names)]
              for r in train]
    ys = [float(r[target]) for r in train]
    beta = stats.ols(design, ys)
    fitted = beta[1:]

    # the frozen vector, expressed as leaf units per one-sigma of each feature
    frozen_sigma = [w * sds[i] for i, (_, w) in enumerate(FROZEN)]

    def unit(v):
        norm = sum(x * x for x in v) ** 0.5 or 1.0
        return [x / norm for x in v]

    f_u, b_u = unit(frozen_sigma), unit(fitted)
    cosine = sum(a * b for a, b in zip(f_u, b_u))

    all_rows_target = [float(r[target]) for r in rows]
    print(f"# target `{target}`, {len(train)} training positions of {len(rows)}\n")
    print(f"**Cosine similarity between the frozen leaf's weight direction and the "
          f"direction that best predicts the achievable clear rate: "
          f"{cosine:+.4f}.**\n")
    print("| # | leaf feature | frozen weight | frozen, per sigma (normalised) | "
          "fitted, per sigma (normalised) | sign | univariate Spearman |")
    print("| --: | --- | ---: | ---: | ---: | :---: | ---: |")
    scored = []
    for i, (name, weight) in enumerate(FROZEN):
        rho = stats.spearman([float(r[name]) for r in rows], all_rows_target)
        agree = "ok" if (f_u[i] == 0 and b_u[i] == 0) or \
                        (f_u[i] * b_u[i] > 0) else (
            "**zero**" if weight == 0 else "**FLIP**")
        scored.append((abs(b_u[i]), i, name, weight, f_u[i], b_u[i], agree, rho))
    for _, i, name, weight, fu, bu, agree, rho in sorted(scored, reverse=True):
        short = name[len("leaf_"):]
        print(f"| {i + 1} | `{short}` | {weight:+,.0f} | {fu:+.4f} | {bu:+.4f} | "
              f"{agree} | {rho:+.4f} |")

    flips = [s for s in scored if s[6] == "**FLIP**"]
    zeros = [s for s in scored if s[6] == "**zero**"]
    print(f"\nSign disagreements: **{len(flips)} of 19** "
          f"({', '.join('`' + s[2][len('leaf_'):] + '`' for s in flips)}).")
    if zeros:
        print(f"Frozen weight exactly zero while the fit wants it: "
              f"{', '.join('`' + s[2][len('leaf_'):] + '`' for s in zeros)}.")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())