SPB Git

spb/wp34_uqo

Public
7.0 KB · 129 lines python
1"""ECDLP study generator/driver for the Rust engine (Axis A: H04, H08, H09, H10, H11).23Studies (each writes results/synthetic_ecdlp/<study>_<tag>.jobs.json; `--run` executes locally, otherwise the jobs are4shipped to a cluster node and executed with run_batch_remote.py):5  scaling_generic  sizes 20..56, 20 random prime-order curves per size, configs bsgs / rho floyd / rho brent / rho dp /6                   rho dp+neg (r=128) / kangaroo (full interval)7  scaling_j0       sizes 24..52 on j=0 curves (p ≡ 1 mod 3), configs rho dp (r=128) / +neg / +aut3 / +neg+aut38  sweep_r          36- and 40-bit generic curves × r ∈ {3,4,8,16,20,32,64,128} × mixed ∈ {0,2,4}, 40 instances9  parallel         48-bit generic curves, threads ∈ {1,2,4,8,16,28}, 6 instances10  tmto             bsgs memory sweep (36 bits) and rho dp-bit sweep (40 bits), 20 instances11Instances are generated with PARI/GP point counting (laptop); jobs are flat objects in the engine's batch format.12"""13import argparse, json, math, random, sys, pathlib14ROOT = pathlib.Path(__file__).resolve().parents[2]15sys.path.insert(0, str(ROOT / "src"))16from wp34.ec import random_prime_order_curve, cube_roots_of_unity, endomorphism_lambda1718OUT = ROOT / "results/synthetic_ecdlp"192021def gen_instances(sizes, per_size, seed, j0):22    rng = random.Random(seed); inst = []23    for bits in sizes:24        for i in range(per_size):25            E = random_prime_order_curve(bits, rng, j0=j0)26            d = rng.randrange(1, E.n); Q = E.mul(d, E.G)27            rec = {"bits": bits, "idx": i, "p": str(E.p), "a": str(E.a), "b": str(E.b), "n": str(E.n),28                   "gx": str(E.G[0]), "gy": str(E.G[1]), "qx": str(Q[0]), "qy": str(Q[1]), "expected_d": str(d)}29            if j0:30                for beta in cube_roots_of_unity(E.p):31                    try:32                        lam = endomorphism_lambda(E, beta); rec["beta"] = str(beta); rec["lambda"] = str(lam); break33                    except ValueError:34                        continue35                assert "lambda" in rec36            inst.append(rec)37    return inst383940def base(rec):41    return {k: rec[k] for k in ("p", "a", "b", "n", "gx", "gy", "qx", "qy", "expected_d")}424344def dp_default(bits):45    return max(1, bits // 4 - 2)464748def study_scaling_generic(seed):49    sizes = [20, 24, 28, 32, 36, 40, 44, 48, 52, 56]50    inst = gen_instances(sizes, 60, seed, False); jobs = []51    for rec in inst:52        b = base(rec); lab = f"g{rec['bits']}b-{rec['idx']}"; bits = rec["bits"]53        if bits <= 48:54            jobs.append(dict(b, alg="bsgs", seed=seed, threads=1, label=lab + "/bsgs"))55        if bits <= 48:56            jobs.append(dict(b, alg="rho", cycle="floyd", r=20, seed=seed, threads=1, label=lab + "/rho_floyd"))57            jobs.append(dict(b, alg="rho", cycle="brent", r=20, seed=seed, threads=1, label=lab + "/rho_brent"))58        jobs.append(dict(b, alg="rho", cycle="dp", r=20, dp=dp_default(bits), seed=seed, threads=1, label=lab + "/rho_dp"))59        jobs.append(dict(b, alg="rho", cycle="dp", r=128, dp=dp_default(bits), seed=seed, threads=1, label=lab + "/rho_dp_r128"))60        jobs.append(dict(b, alg="rho", cycle="dp", r=128, dp=dp_default(bits), neg=True, seed=seed, threads=1, label=lab + "/rho_dp_neg"))61        jobs.append(dict(b, alg="kangaroo", dp=dp_default(bits), seed=seed, threads=1, label=lab + "/kangaroo"))62    return jobs, {"sizes": sizes, "per_size": 60}636465def study_scaling_j0(seed):66    sizes = [24, 28, 32, 36, 40, 44, 48, 52]67    inst = gen_instances(sizes, 60, seed + 1, True); jobs = []68    for rec in inst:69        b = base(rec); lab = f"j{rec['bits']}b-{rec['idx']}"; bits = rec["bits"]; dp = dp_default(bits)70        jobs.append(dict(b, alg="rho", cycle="dp", r=128, dp=dp, seed=seed, threads=1, label=lab + "/rho_dp"))71        jobs.append(dict(b, alg="rho", cycle="dp", r=128, dp=dp, neg=True, seed=seed, threads=1, label=lab + "/rho_dp_neg"))72        jobs.append(dict(b, alg="rho", cycle="dp", r=128, dp=dp, aut3=True, beta=rec["beta"], seed=seed, threads=1, label=lab + "/rho_dp_aut3", **{"lambda": rec["lambda"]}))73        jobs.append(dict(b, alg="rho", cycle="dp", r=128, dp=dp, neg=True, aut3=True, beta=rec["beta"], seed=seed, threads=1, label=lab + "/rho_dp_neg_aut3", **{"lambda": rec["lambda"]}))74        jobs.append(dict(b, alg="rho", cycle="dp", r=20, dp=dp, neg=True, aut3=True, beta=rec["beta"], seed=seed, threads=1, label=lab + "/rho_dp_neg_aut3_r20", **{"lambda": rec["lambda"]}))75    return jobs, {"sizes": sizes, "per_size": 60}767778def study_sweep_r(seed):79    inst = gen_instances([36, 40], 40, seed + 2, False); jobs = []80    for rec in inst:81        b = base(rec); lab = f"g{rec['bits']}b-{rec['idx']}"; dp = dp_default(rec["bits"])82        for r in (3, 4, 8, 16, 20, 32, 64, 128):83            for mixed in (0, 2, 4):84                kw = {"mixed": mixed} if mixed else {}85                jobs.append(dict(b, alg="rho", cycle="dp", r=r, dp=dp, seed=seed, threads=1, label=f"{lab}/r{r}_m{mixed}", **kw))86        # negative control: a bad walk — r = 2 (far from random), and Pollard's original 3-partition analogue r=3 is above87        jobs.append(dict(b, alg="rho", cycle="dp", r=2, dp=dp, seed=seed, threads=1, label=f"{lab}/r2_m0"))88    return jobs, {"sizes": [36, 40], "per_size": 40, "r": [2, 3, 4, 8, 16, 20, 32, 64, 128], "mixed": [0, 2, 4]}899091def study_parallel(seed):92    inst = gen_instances([56], 12, seed + 3, False); jobs = []93    for rec in inst:94        b = base(rec); lab = f"g56b-{rec['idx']}"95        for t in (1, 2, 4, 8, 16, 28):96            jobs.append(dict(b, alg="rho", cycle="dp", r=128, dp=12, neg=True, seed=seed, threads=t, label=f"{lab}/t{t}"))97    return jobs, {"sizes": [56], "per_size": 12, "threads": [1, 2, 4, 8, 16, 28]}9899100def study_tmto(seed):101    jobs = []102    for rec in gen_instances([36], 20, seed + 4, False):103        b = base(rec); lab = f"g36b-{rec['idx']}"104        for mem in (2**10, 2**12, 2**14, 2**16, 2**18, 2**20):105            jobs.append(dict(b, alg="bsgs", memory=str(mem), seed=seed, threads=1, label=f"{lab}/bsgs_m{mem}"))106    for rec in gen_instances([40], 20, seed + 5, False):107        b = base(rec); lab = f"g40b-{rec['idx']}"108        for dp in (2, 4, 6, 8, 10, 12, 14):109            jobs.append(dict(b, alg="rho", cycle="dp", r=128, dp=dp, seed=seed, threads=1, label=f"{lab}/rho_dp{dp}"))110    return jobs, {"bsgs_bits": 36, "rho_bits": 40, "per_size": 20}111112113STUDIES = {"scaling_generic": study_scaling_generic, "scaling_j0": study_scaling_j0, "sweep_r": study_sweep_r,114           "parallel": study_parallel, "tmto": study_tmto}115116117def main():118    ap = argparse.ArgumentParser(); ap.add_argument("study", choices=list(STUDIES) + ["all"]); ap.add_argument("--seed", type=int, default=34)119    a = ap.parse_args(); OUT.mkdir(parents=True, exist_ok=True)120    for name in (STUDIES if a.study == "all" else [a.study]):121        jobs, params = STUDIES[name](a.seed)122        (OUT / f"{name}.jobs.json").write_text(json.dumps(jobs))123        (OUT / f"{name}.params.json").write_text(json.dumps(dict(params, seed=a.seed, study=name)))124        print(f"{name}: {len(jobs)} jobs → {OUT / (name + '.jobs.json')}")125126127if __name__ == "__main__":128    main()129
Files · main
Menu

Theme