126 ap = argparse.ArgumentParser()
127 ap.add_argument(
"--n", type=int, default=64)
128 ap.add_argument(
"--condition", required=
True,
129 help=
"comma-separated condition keys; each sampled theta is "
130 "emitted once PER CONDITION so the same parameter "
131 "vector is evaluated at every one. That pairing is the "
132 "point when the target is a crossover: character is only "
133 "constrained by 300 C AND 350 C together, never by one.")
134 ap.add_argument(
"--seed", type=int, default=20260813)
135 ap.add_argument(
"--base-design", type=Path, default=HERE /
"design/T3_rev6.csv")
136 ap.add_argument(
"--base-row", default=
"38")
137 ap.add_argument(
"--base-condition", default=
"N2")
138 ap.add_argument(
"--out", type=Path, required=
True)
139 a = ap.parse_args(argv)
141 base_rows = [r
for r
in csv.DictReader(a.base_design.open())
142 if r[
"row_id"] == a.base_row
and r[
"condition"] == a.base_condition]
144 raise SystemExit(f
"base row {a.base_row}/{a.base_condition} not found in "
147 params = json.loads((HERE /
"design/T3_rev6.meta.json").read_text())[
"parameters"]
148 missing = [k
for k
in SAMPLED
if k
not in params]
150 raise SystemExit(f
"sampled keys absent from the parameter list: {missing}")
152 rng = random.Random(a.seed)
156 conditions = [c.strip()
for c
in a.condition.split(
",")
if c.strip()]
157 cols = [
"row_id",
"condition",
"cond_row_id",
"matrix",
"base_idx",
158 "param_j",
"theta_id"] + params
161 for i, pt
in enumerate(pts):
165 for u, k
in zip(pt, keys):
166 lo, hi, scale = SAMPLED[k]
168 theta[k] = repr(10.0 ** (math.log10(lo) + u * (math.log10(hi)
171 theta[k] = repr(lo + u * (hi - lo))
174 for cond
in conditions:
175 row = {
"row_id": rid,
"condition": cond,
"cond_row_id": rid,
176 "matrix":
"L",
"base_idx": i,
"param_j": -1,
"theta_id": i}
181 a.out.parent.mkdir(parents=
True, exist_ok=
True)
182 with a.out.open(
"w", newline=
"")
as f:
183 w = csv.DictWriter(f, fieldnames=cols)
187 sha = hashlib.sha256(a.out.read_bytes()).hexdigest()
189 "tier":
"T8_calibration",
190 "kind":
"latin_hypercube",
194 "conditions": conditions,
195 "sampled": {k: {
"lo": v[0],
"hi": v[1],
"scale": v[2],
196 "original_prior": ORIGINAL_PRIOR.get(k),
197 "widened": k
in ORIGINAL_PRIOR}
198 for k, v
in SAMPLED.items()},
199 "base_row": f
"{a.base_design.name}:{a.base_row}/{a.base_condition}",
200 "parameters": params,
201 "design_sha256": sha,
202 "note": (
"Space-filling, NOT Saltelli: this design is for ESTIMATION "
203 "(match the measurement), not attribution (Sobol indices), so "
204 "it carries no A/B/AB_j pairing and merge_and_sobol must not be "
207 a.out.with_suffix(
".meta.json").write_text(json.dumps(meta, indent=1))
208 print(f
"wrote {a.out} ({a.n} rows, {len(keys)} sampled, condition={a.condition})")
209 print(f
" sha256 {sha[:16]}")
210 for k, (lo, hi, sc)
in SAMPLED.items():
211 vals = [float(r[k])
for r
in out]
212 print(f
" {k:14s} [{lo:g}, {hi:g}] {sc:3s} -> "
213 f
"min={min(vals):.4g} max={max(vals):.4g}")