Checkpoint the party/runtime stack before share-program and malicious-mode work.
Ship the TLS mesh, composer, Beaver/Yao/leaf MPC, prep/online paths, apps, and docs so the tree is pushable before elevating share_expr, security_mode, and prep resume. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
695f8e84f7
commit
0d22946a0e
1835 changed files with 170291 additions and 2849 deletions
618
tools/cas_window/generate.py
Normal file
618
tools/cas_window/generate.py
Normal file
|
|
@ -0,0 +1,618 @@
|
|||
#!/usr/bin/env python3
|
||||
"""Cubic windows for the Appendix D maps that were still cleartext-only.
|
||||
|
||||
Sollya builds a longest-feasible partition whose fixed-point cubic stays
|
||||
within half an ulp. Mathematica (Remez), Maple (numapprox[minimax]), and
|
||||
MATLAB/Chebfun (minimax) each fit the same pieces. The shipped cubic is
|
||||
the one with the smallest absolute error after rounding coefficients to
|
||||
2^{-(k+16)}.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import math
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
from dataclasses import dataclass
|
||||
from decimal import Decimal, ROUND_HALF_UP
|
||||
from pathlib import Path
|
||||
|
||||
PREC = (8, 12, 16, 20, 24, 28, 32)
|
||||
SOLLYA = "sollya"
|
||||
MATH = "/home/dawkteraitch/Wolfram/15.0/Executables/math"
|
||||
MAPLE = "/home/dawkteraitch/maple2025/bin/maple"
|
||||
MATLAB = "/home/dawkteraitch/MATLAB/R2026a/bin/matlab"
|
||||
WORK = Path("/tmp/cas-lic/window")
|
||||
EXPR = {
|
||||
"lecun": "(17159/10000) * tanh((2 * x) / 3)",
|
||||
"hard": "expm1(x) * (x + 1) / 2",
|
||||
}
|
||||
NUMBER = re.compile(r"[+-]?(?:\d+\.\d*|\d+\.?\d*)(?:[eE][+-]?\d+)?")
|
||||
|
||||
|
||||
@dataclass
|
||||
class Piece:
|
||||
left: int
|
||||
right: int
|
||||
coeffs: list[int] # c0..c3 at scale 2^q
|
||||
err: float
|
||||
source: str
|
||||
|
||||
|
||||
class Sollya:
|
||||
def __init__(self) -> None:
|
||||
self.proc: subprocess.Popen[str] | None = None
|
||||
self.start()
|
||||
|
||||
def start(self) -> None:
|
||||
if self.proc is not None:
|
||||
self.proc.kill()
|
||||
self.proc.wait()
|
||||
self.proc = subprocess.Popen(
|
||||
["stdbuf", "-o0", "-e0", SOLLYA],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT,
|
||||
text=True,
|
||||
bufsize=1,
|
||||
)
|
||||
self._run("verbosity = 0;\nprec = 120;\n", "READY")
|
||||
|
||||
def close(self) -> None:
|
||||
if self.proc is not None:
|
||||
self.proc.kill()
|
||||
self.proc.wait()
|
||||
self.proc = None
|
||||
|
||||
def _run(self, script: str, sentinel: str) -> list[str]:
|
||||
assert self.proc is not None and self.proc.stdin and self.proc.stdout
|
||||
self.proc.stdin.write(script)
|
||||
self.proc.stdin.write(f'print("{sentinel}");\n')
|
||||
self.proc.stdin.flush()
|
||||
lines: list[str] = []
|
||||
while True:
|
||||
line = self.proc.stdout.readline()
|
||||
if line == "":
|
||||
raise RuntimeError("sollya exited")
|
||||
if sentinel in line:
|
||||
return lines
|
||||
lines.append(line.rstrip("\n"))
|
||||
|
||||
def fit(
|
||||
self, name: str, left: int, right: int, k: int, q: int, tol: float
|
||||
) -> tuple[float, list[int]] | None:
|
||||
# fpminimax is ill-conditioned on a tiny interval at 0 and can report
|
||||
# an error above half an ulp for a one-ulp step. Taylor is the
|
||||
# fallback, and the search keeps the smaller certified error.
|
||||
mini = self._polynomial(name, left, right, k, q, taylor=False)
|
||||
if mini is not None and mini[0] <= tol:
|
||||
return mini
|
||||
tay = self._polynomial(name, left, right, k, q, taylor=True)
|
||||
cands = [item for item in (mini, tay) if item is not None]
|
||||
if not cands:
|
||||
return None
|
||||
return min(cands, key=lambda item: item[0])
|
||||
|
||||
def _polynomial(
|
||||
self, name: str, left: int, right: int, k: int, q: int, taylor: bool
|
||||
) -> tuple[float, list[int]] | None:
|
||||
expr = EXPR[name]
|
||||
if taylor:
|
||||
# taylor(f, n, t) returns p such that p(x-t) is the expansion at t.
|
||||
build = (
|
||||
f"t0 = (({left})+({right}))/2 * 2^(-{k});\n"
|
||||
f"pshift = taylor({expr}, 3, t0);\n"
|
||||
f"p = pshift(x - t0);"
|
||||
)
|
||||
else:
|
||||
build = (
|
||||
f"p = fpminimax({expr}, 3, [|80,80,80,80|], "
|
||||
f"[{left}*2^(-{k}), {right}*2^(-{k})], absolute);"
|
||||
)
|
||||
script = f"""
|
||||
{build}
|
||||
qbits = {q};
|
||||
i0 = nearestint(coeff(p, 0) * 2^qbits);
|
||||
i1 = nearestint(coeff(p, 1) * 2^qbits);
|
||||
i2 = nearestint(coeff(p, 2) * 2^qbits);
|
||||
i3 = nearestint(coeff(p, 3) * 2^qbits);
|
||||
pr = i0*2^(-qbits) + i1*2^(-qbits)*x + i2*2^(-qbits)*x^2 + i3*2^(-qbits)*x^3;
|
||||
e = dirtyinfnorm(({expr}) - pr, [{left}*2^(-{k}), {right}*2^(-{k})]);
|
||||
print(i0);
|
||||
print(i1);
|
||||
print(i2);
|
||||
print(i3);
|
||||
print(e);
|
||||
"""
|
||||
try:
|
||||
lines = self._run(script, "FITDONE")
|
||||
except RuntimeError:
|
||||
self.start()
|
||||
return None
|
||||
if any("Error" in line or "error" in line for line in lines):
|
||||
self.start()
|
||||
return None
|
||||
nums: list[str] = []
|
||||
for line in lines:
|
||||
nums.extend(NUMBER.findall(line))
|
||||
if len(nums) < 5:
|
||||
self.start()
|
||||
return None
|
||||
coeffs = [int(Decimal(nums[i]).to_integral_value(ROUND_HALF_UP)) for i in range(4)]
|
||||
err = max(float(n) for n in nums[4:])
|
||||
if not math.isfinite(err):
|
||||
return None
|
||||
return err, coeffs
|
||||
|
||||
def score(self, name: str, left: int, right: int, k: int, q: int, coeffs: list[int]) -> float | None:
|
||||
expr = EXPR[name]
|
||||
c0, c1, c2, c3 = coeffs
|
||||
script = f"""
|
||||
pr = ({c0})*2^(-{q}) + ({c1})*2^(-{q})*x + ({c2})*2^(-{q})*x^2 + ({c3})*2^(-{q})*x^3;
|
||||
e = dirtyinfnorm(({expr}) - pr, [{left}*2^(-{k}), {right}*2^(-{k})]);
|
||||
print(e);
|
||||
"""
|
||||
try:
|
||||
lines = self._run(script, "SCOREDONE")
|
||||
except RuntimeError:
|
||||
self.start()
|
||||
return None
|
||||
if any("Error" in line or "error" in line for line in lines):
|
||||
self.start()
|
||||
return None
|
||||
nums: list[str] = []
|
||||
for line in lines:
|
||||
nums.extend(NUMBER.findall(line))
|
||||
if not nums:
|
||||
return None
|
||||
err = max(float(n) for n in nums)
|
||||
return err if math.isfinite(err) else None
|
||||
|
||||
|
||||
def half_ulp(k: int) -> float:
|
||||
return 2.0 ** (-(k + 1))
|
||||
|
||||
|
||||
def q_of(k: int) -> int:
|
||||
return k + 16
|
||||
|
||||
|
||||
def quantize(coeffs: list[str], q: int) -> list[int]:
|
||||
scale = Decimal(2) ** q
|
||||
out: list[int] = []
|
||||
for raw in coeffs:
|
||||
value = Decimal(raw) * scale
|
||||
if value >= 0:
|
||||
out.append(int(value.to_integral_value(ROUND_HALF_UP)))
|
||||
else:
|
||||
out.append(-int((-value).to_integral_value(ROUND_HALF_UP)))
|
||||
return out
|
||||
|
||||
|
||||
def lecun_tail(k: int) -> int:
|
||||
num = 17159 * (1 << k)
|
||||
den = 10000
|
||||
return (num + den // 2) // den
|
||||
|
||||
|
||||
def lecun_end(k: int) -> int:
|
||||
"""First raw at which the constant tail is within 1/4 ulp of the asymptote gap."""
|
||||
tail = lecun_tail(k) / 2**k
|
||||
# f increases to 1.7159. Leave a quarter ulp for rounding the tail itself.
|
||||
gap = 0.25 * (2 ** (-k))
|
||||
target = 1.7159 - gap
|
||||
lo, hi = 0.0, 40.0
|
||||
for _ in range(80):
|
||||
mid = (lo + hi) / 2
|
||||
if 1.7159 * math.tanh(2 * mid / 3) >= target:
|
||||
hi = mid
|
||||
else:
|
||||
lo = mid
|
||||
return max(1, math.ceil(hi * 2**k - 1e-9))
|
||||
|
||||
|
||||
def march(sollya: Sollya, name: str, start: int, end: int, k: int) -> list[Piece]:
|
||||
q = q_of(k)
|
||||
tol = half_ulp(k)
|
||||
pieces: list[Piece] = []
|
||||
left = start
|
||||
guess = 1
|
||||
while left < end:
|
||||
step = max(1, guess)
|
||||
last: tuple[int, float, list[int]] | None = None
|
||||
fail: int | None = None
|
||||
while left + step <= end:
|
||||
got = sollya.fit(name, left, left + step, k, q, tol)
|
||||
if got is not None and got[0] <= tol:
|
||||
last = (left + step, got[0], got[1])
|
||||
step *= 2
|
||||
else:
|
||||
fail = min(end, left + step)
|
||||
break
|
||||
else:
|
||||
if last is None or last[0] != end:
|
||||
got = sollya.fit(name, left, end, k, q, tol)
|
||||
if got is not None and got[0] <= tol:
|
||||
last = (end, got[0], got[1])
|
||||
fail = None
|
||||
else:
|
||||
fail = end
|
||||
if fail is None and last is not None:
|
||||
right, err, coeffs = last
|
||||
else:
|
||||
lo = last[0] if last is not None else left
|
||||
hi = fail if fail is not None else end
|
||||
best = last
|
||||
while hi - lo > 1:
|
||||
mid = (lo + hi) // 2
|
||||
got = sollya.fit(name, left, mid, k, q, tol)
|
||||
if got is not None and got[0] <= tol:
|
||||
best = (mid, got[0], got[1])
|
||||
lo = mid
|
||||
else:
|
||||
hi = mid
|
||||
if best is None:
|
||||
got = sollya.fit(name, left, left + 1, k, q, tol)
|
||||
if got is None:
|
||||
raise RuntimeError(f"{name} k={k} no cubic on [{left}, {left+1}]")
|
||||
best = (left + 1, got[0], got[1])
|
||||
right, err, coeffs = best
|
||||
pieces.append(Piece(left, right, coeffs, err, "sollya"))
|
||||
guess = max(1, right - left)
|
||||
left = right
|
||||
print(f" {name} k={k} pieces={len(pieces)} span={right - pieces[-1].left} err={err:.3e}", flush=True)
|
||||
return pieces
|
||||
|
||||
|
||||
def write_intervals(pieces_by_key: dict[tuple[str, int], list[Piece]], path: Path) -> None:
|
||||
with path.open("w") as handle:
|
||||
for (name, k), pieces in pieces_by_key.items():
|
||||
q = q_of(k)
|
||||
for piece in pieces:
|
||||
handle.write(f"{name} {k} {piece.left} {piece.right} {q}\n")
|
||||
|
||||
|
||||
def run_maple(interval_path: Path, out_path: Path) -> None:
|
||||
program = WORK / "maple_batch.mpl"
|
||||
program.write_text(
|
||||
f"""
|
||||
Digits := 40:
|
||||
with(numapprox):
|
||||
fd := fopen("{out_path}", WRITE):
|
||||
infd := fopen("{interval_path}", READ):
|
||||
while true do
|
||||
line := readline(infd);
|
||||
if line = 0 then break; end if;
|
||||
parts := sscanf(line, "%s %d %d %d %d");
|
||||
gname := parts[1];
|
||||
a := parts[3] * 2^(-parts[2]);
|
||||
b := parts[4] * 2^(-parts[2]);
|
||||
try
|
||||
if gname = "lecun" then
|
||||
p := minimax(17159/10000*tanh(2*x/3), x = a .. b, [3, 0]);
|
||||
else
|
||||
p := minimax((exp(x)-1)*(x+1)/2, x = a .. b, [3, 0]);
|
||||
end if;
|
||||
p := expand(p);
|
||||
fprintf(fd, "%.25e %.25e %.25e %.25e\\n",
|
||||
evalf(coeff(p, x, 0)), evalf(coeff(p, x, 1)),
|
||||
evalf(coeff(p, x, 2)), evalf(coeff(p, x, 3)));
|
||||
catch:
|
||||
fprintf(fd, "FAIL\\n");
|
||||
end try;
|
||||
end do:
|
||||
fclose(fd):
|
||||
fclose(infd):
|
||||
quit:
|
||||
"""
|
||||
)
|
||||
subprocess.run([MAPLE, "-q", str(program)], check=True, timeout=3600)
|
||||
|
||||
|
||||
def run_math(interval_path: Path, out_path: Path) -> None:
|
||||
program = WORK / "mma_batch.wl"
|
||||
program.write_text(
|
||||
r"""
|
||||
pow[base_, 0] := 1;
|
||||
pow[base_, n_Integer] := base^n;
|
||||
lecun[x_] := 17159/10000*Tanh[2*x/3];
|
||||
hard[x_] := (Exp[x] - 1)*(x + 1)/2;
|
||||
remez[ff_, a_, b_] := Module[
|
||||
{n = 3, m = 5, mid, half, nodes, coef, mat, rhs, sol, grid, err, idx, drop, tpoly, xpoly},
|
||||
mid = N[(a + b)/2, 40];
|
||||
half = N[(b - a)/2, 40];
|
||||
nodes = Sort[Table[N[Cos[Pi*(k + 1/2)/m], 40], {k, 0, m - 1}]];
|
||||
coef = {0, 0, 0, 0};
|
||||
Do[
|
||||
mat = N[Table[If[j <= n + 1, pow[nodes[[i]], j - 1], -(-1)^i], {i, m}, {j, m}], 40];
|
||||
rhs = N[Table[ff[mid + half*nodes[[i]]], {i, m}], 40];
|
||||
sol = Quiet[Check[LinearSolve[mat, rhs], $Failed]];
|
||||
If[sol === $Failed || Head[sol] =!= List, Break[]];
|
||||
coef = N[sol[[1 ;; n + 1]], 40];
|
||||
grid = N[Subdivide[-1, 1, 256], 40];
|
||||
err = Table[
|
||||
coef[[1]] + coef[[2]]*grid[[i]] + coef[[3]]*grid[[i]]^2 + coef[[4]]*grid[[i]]^3
|
||||
- ff[mid + half*grid[[i]]],
|
||||
{i, Length[grid]}];
|
||||
idx = {1, Length[grid]};
|
||||
Do[
|
||||
If[Abs[err[[i]]] >= Abs[err[[i - 1]]] && Abs[err[[i]]] >= Abs[err[[i + 1]]],
|
||||
AppendTo[idx, i]],
|
||||
{i, 2, Length[grid] - 1}];
|
||||
While[Length[idx] > m,
|
||||
drop = Ordering[Abs[err[[idx]]], 1][[1]];
|
||||
idx = Delete[idx, drop];
|
||||
];
|
||||
If[Length[idx] == m, nodes = Sort[N[grid[[idx]], 40]]];
|
||||
, {8}];
|
||||
tpoly = coef[[1]] + coef[[2]]*((x - mid)/half) + coef[[3]]*((x - mid)/half)^2
|
||||
+ coef[[4]]*((x - mid)/half)^3;
|
||||
xpoly = Expand[N[tpoly, 30]];
|
||||
Table[N[Coefficient[xpoly, x, k], 20], {k, 0, 3}]
|
||||
];
|
||||
lines = ReadList["""
|
||||
+ f'"{interval_path}"'
|
||||
+ r""", String];
|
||||
out = OpenWrite["""
|
||||
+ f'"{out_path}"'
|
||||
+ r"""];
|
||||
Do[
|
||||
parts = StringSplit[line];
|
||||
If[Length[parts] < 5, Continue[]];
|
||||
name = parts[[1]];
|
||||
kk = ToExpression[parts[[2]]];
|
||||
left = ToExpression[parts[[3]]];
|
||||
right = ToExpression[parts[[4]]];
|
||||
aa = N[left*2^(-kk), 30];
|
||||
bb = N[right*2^(-kk), 30];
|
||||
ff = If[name == "lecun", lecun, hard];
|
||||
cs = Quiet[Check[remez[ff, aa, bb], $Failed]];
|
||||
If[cs === $Failed || Head[cs] =!= List,
|
||||
WriteString[out, "FAIL\n"],
|
||||
WriteString[out, StringRiffle[Map[ToString[CForm[#]] &, cs], " "], "\n"]
|
||||
];
|
||||
, {line, lines}];
|
||||
Close[out];
|
||||
Exit[];
|
||||
"""
|
||||
)
|
||||
subprocess.run([MATH, "-script", str(program)], check=True, timeout=3600)
|
||||
|
||||
|
||||
def run_matlab(interval_path: Path, out_path: Path) -> None:
|
||||
program = WORK / "matlab_batch.m"
|
||||
program.write_text(
|
||||
f"""
|
||||
fid = fopen('{interval_path}', 'r');
|
||||
out = fopen('{out_path}', 'w');
|
||||
while true
|
||||
line = fgetl(fid);
|
||||
if ~ischar(line), break; end
|
||||
parts = strsplit(strtrim(line));
|
||||
if numel(parts) < 5, continue; end
|
||||
name = parts{{1}};
|
||||
kk = str2double(parts{{2}});
|
||||
left = str2double(parts{{3}});
|
||||
right = str2double(parts{{4}});
|
||||
aa = left * 2^(-kk);
|
||||
bb = right * 2^(-kk);
|
||||
try
|
||||
if strcmp(name, 'lecun')
|
||||
f = chebfun(@(x) 1.7159*tanh(2*x/3), [aa, bb]);
|
||||
else
|
||||
f = chebfun(@(x) expm1(x).*(x+1)/2, [aa, bb]);
|
||||
end
|
||||
p = minimax(f, 3);
|
||||
c = poly(p);
|
||||
c = c(:).';
|
||||
if numel(c) < 4
|
||||
c = [zeros(1, 4-numel(c)), c];
|
||||
end
|
||||
fprintf(out, '%.20e %.20e %.20e %.20e\\n', c(4), c(3), c(2), c(1));
|
||||
catch
|
||||
fprintf(out, 'FAIL\\n');
|
||||
end
|
||||
end
|
||||
fclose(fid);
|
||||
fclose(out);
|
||||
"""
|
||||
)
|
||||
subprocess.run(
|
||||
[MATLAB, "-batch", f"run('{program}')"],
|
||||
check=True,
|
||||
timeout=7200,
|
||||
)
|
||||
|
||||
|
||||
def load_coeff_file(path: Path) -> list[list[str] | None]:
|
||||
rows: list[list[str] | None] = []
|
||||
for line in path.read_text().splitlines():
|
||||
line = line.strip()
|
||||
if not line or "FAIL" in line:
|
||||
rows.append(None)
|
||||
continue
|
||||
parts = NUMBER.findall(line.replace("D", "E"))
|
||||
if len(parts) < 4:
|
||||
rows.append(None)
|
||||
continue
|
||||
rows.append(parts[:4])
|
||||
return rows
|
||||
|
||||
|
||||
def tournament(
|
||||
sollya: Sollya,
|
||||
pieces_by_key: dict[tuple[str, int], list[Piece]],
|
||||
others: dict[str, list[list[str] | None]],
|
||||
) -> dict[str, int]:
|
||||
wins = {"sollya": 0, "mathematica": 0, "maple": 0, "matlab": 0}
|
||||
index = 0
|
||||
for key in pieces_by_key:
|
||||
name, k = key
|
||||
q = q_of(k)
|
||||
for piece in pieces_by_key[key]:
|
||||
best_err = piece.err
|
||||
best_coeffs = piece.coeffs
|
||||
best_source = "sollya"
|
||||
for source in ("mathematica", "maple", "matlab"):
|
||||
row = others[source][index] if index < len(others[source]) else None
|
||||
if row is None:
|
||||
continue
|
||||
try:
|
||||
coeffs = quantize(row, q)
|
||||
except Exception:
|
||||
continue
|
||||
err = sollya.score(name, piece.left, piece.right, k, q, coeffs)
|
||||
if err is not None and err < best_err:
|
||||
best_err = err
|
||||
best_coeffs = coeffs
|
||||
best_source = source
|
||||
piece.coeffs = best_coeffs
|
||||
piece.err = best_err
|
||||
piece.source = best_source
|
||||
wins[best_source] += 1
|
||||
index += 1
|
||||
print(f"tournament {name} k={k} done", flush=True)
|
||||
return wins
|
||||
|
||||
|
||||
def split_i128(value: int) -> tuple[int, int]:
|
||||
raw = value + (1 << 128) if value < 0 else value
|
||||
lo = raw & ((1 << 64) - 1)
|
||||
hi = (raw >> 64) & ((1 << 64) - 1)
|
||||
if hi >= (1 << 63):
|
||||
hi -= 1 << 64
|
||||
return hi, lo
|
||||
|
||||
|
||||
TAGS = {"lecun": "LECUN", "hard": "HARDELISH"}
|
||||
|
||||
|
||||
def emit_table(name: str, k: int, pieces: list[Piece]) -> str:
|
||||
knots = [pieces[0].left] + [piece.right for piece in pieces]
|
||||
q = q_of(k)
|
||||
tag = TAGS[name]
|
||||
lines = [f"static constexpr std::int64_t {tag}_K{k}_KNOTS[] = {{"]
|
||||
lines.append(" " + ", ".join(str(knot) for knot in knots) + ",")
|
||||
lines.append("};")
|
||||
lines.append(f"static constexpr grotto::principal_detail::cubic_bits {tag}_K{k}_PIECES[] = {{")
|
||||
for piece in pieces:
|
||||
his, los = [], []
|
||||
for coeff in piece.coeffs:
|
||||
hi, lo = split_i128(coeff)
|
||||
his.append(str(hi))
|
||||
los.append(f"{lo}u")
|
||||
lines.append(" {{" + ", ".join(his) + "}, {" + ", ".join(los) + "}},")
|
||||
lines.append("};")
|
||||
lines.append(
|
||||
f"static constexpr window_table {tag}_K{k}_TABLE"
|
||||
f"{{{tag}_K{k}_KNOTS, {tag}_K{k}_PIECES, {len(pieces)}, {q}}};"
|
||||
)
|
||||
lines.append("")
|
||||
return "\n".join(lines)
|
||||
|
||||
|
||||
def emit(pieces_by_key: dict[tuple[str, int], list[Piece]], wins: dict[str, int], path: Path) -> None:
|
||||
chunks = [
|
||||
"// Generated on mocha2.",
|
||||
"// Sollya fpminimax chose the knots (absolute error at most half an ulp).",
|
||||
"// Each cubic is the best of Sollya, Mathematica Remez, Maple numapprox[minimax],",
|
||||
"// and MATLAB/Chebfun minimax after rounding coefficients to 2^{-(k+16)}.",
|
||||
"// Wins: " + ", ".join(f"{name} {count}" for name, count in wins.items()) + ".",
|
||||
"// lecun is the positive half of an odd map; hard covers (-1, 0).",
|
||||
"// Do not edit.",
|
||||
"#pragma once",
|
||||
"",
|
||||
]
|
||||
ks = [k for (name, k) in pieces_by_key if name == "lecun"]
|
||||
for name in ("lecun", "hard"):
|
||||
for k in ks:
|
||||
chunks.append(emit_table(name, k, pieces_by_key[(name, k)]))
|
||||
tag = TAGS[name]
|
||||
refs = ",\n ".join(f"&{tag}_K{k}_TABLE" for k in ks)
|
||||
chunks.append(
|
||||
f"static constexpr window_table const* const {tag}[{len(ks)}] = {{\n {refs},\n}};"
|
||||
)
|
||||
chunks.append("")
|
||||
tails = ", ".join(str(lecun_tail(k)) for k in ks)
|
||||
chunks.append(f"static constexpr std::int64_t LECUN_TAIL[{len(ks)}] = {{\n {tails},\n}};")
|
||||
chunks.append("")
|
||||
path.write_text("\n".join(chunks))
|
||||
|
||||
|
||||
def summarize(pieces_by_key: dict[tuple[str, int], list[Piece]], wins: dict[str, int]) -> None:
|
||||
summary = {"wins": wins, "functions": {}}
|
||||
for name in ("lecun", "hard"):
|
||||
summary["functions"][name] = {}
|
||||
for k in selected_precisions():
|
||||
pieces = pieces_by_key[(name, k)]
|
||||
summary["functions"][name][str(k)] = {
|
||||
"parts": len(pieces),
|
||||
"max_abs_error": max(piece.err for piece in pieces),
|
||||
"half_ulp": half_ulp(k),
|
||||
}
|
||||
(WORK / "summary.json").write_text(json.dumps(summary, indent=2))
|
||||
print(json.dumps(summary, indent=2), flush=True)
|
||||
|
||||
|
||||
def self_test() -> None:
|
||||
sollya = Sollya()
|
||||
got = sollya.fit("lecun", 0, 1 << 15, 16, q_of(16), half_ulp(16))
|
||||
print("sollya", got[0] if got else None)
|
||||
sollya.close()
|
||||
|
||||
|
||||
def selected_precisions() -> tuple[int, ...]:
|
||||
if "--k" in sys.argv:
|
||||
return (int(sys.argv[sys.argv.index("--k") + 1]),)
|
||||
return PREC
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if len(sys.argv) > 1 and sys.argv[1] == "--self-test":
|
||||
self_test()
|
||||
return
|
||||
WORK.mkdir(parents=True, exist_ok=True)
|
||||
sollya = Sollya()
|
||||
pieces_by_key: dict[tuple[str, int], list[Piece]] = {}
|
||||
for k in selected_precisions():
|
||||
end = lecun_end(k)
|
||||
print(f"march lecun k={k} end={end} tail={lecun_tail(k)}", flush=True)
|
||||
pieces_by_key[("lecun", k)] = march(sollya, "lecun", 0, end, k)
|
||||
one = 1 << k
|
||||
print(f"march hard k={k}", flush=True)
|
||||
pieces_by_key[("hard", k)] = march(sollya, "hard", -one, 0, k)
|
||||
interval_path = WORK / "intervals.txt"
|
||||
write_intervals(pieces_by_key, interval_path)
|
||||
print("intervals", sum(len(v) for v in pieces_by_key.values()), flush=True)
|
||||
jobs = {
|
||||
"maple": (run_maple, WORK / "maple_coeffs.txt"),
|
||||
"mathematica": (run_math, WORK / "mma_coeffs.txt"),
|
||||
"matlab": (run_matlab, WORK / "matlab_coeffs.txt"),
|
||||
}
|
||||
for name, (fn, out) in jobs.items():
|
||||
# Run sequentially inside this process: MATLAB and Maple both want a license,
|
||||
# and a failure is easier to see one at a time. They do not share state.
|
||||
print("running", name, flush=True)
|
||||
if out.exists():
|
||||
out.unlink()
|
||||
fn(interval_path, out)
|
||||
print("finished", name, out.stat().st_size, flush=True)
|
||||
expected = sum(len(v) for v in pieces_by_key.values())
|
||||
others = {
|
||||
"maple": load_coeff_file(WORK / "maple_coeffs.txt"),
|
||||
"mathematica": load_coeff_file(WORK / "mma_coeffs.txt"),
|
||||
"matlab": load_coeff_file(WORK / "matlab_coeffs.txt"),
|
||||
}
|
||||
for source, rows in others.items():
|
||||
if len(rows) != expected:
|
||||
raise SystemExit(f"{source} wrote {len(rows)} rows, expected {expected}")
|
||||
wins = tournament(sollya, pieces_by_key, others)
|
||||
sollya.close()
|
||||
emit(pieces_by_key, wins, WORK / "window_extra_tables.inc")
|
||||
summarize(pieces_by_key, wins)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue