Files
TheRock/rocm-libraries/dnn-providers/integration-tests/migration-scripts/find_case.py
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

351 lines
11 KiB
Python

#!/usr/bin/env python3
# Copyright © Advanced Micro Devices, Inc., or its affiliates.
# SPDX-License-Identifier: MIT
"""
Query bundle cases by any parameter — shape, dtype, layout, op, input range.
Reads sweep.json + graph.template.json directly (single source of truth).
Examples::
# List all batchnorm cases
python3 find_case.py --op Batchnorm
# Find fp16 nhwc cases
python3 find_case.py --dtype fp16 --layout nhwc
# Find cases that have an epsilon input (any range)
python3 find_case.py --input epsilon
# Find cases where epsilon is in [-1,1]
python3 find_case.py --input epsilon:-1,1
# Find by shape
python3 find_case.py --shape 1x16x3x3
# Combine filters
python3 find_case.py --op Batchnorm --dtype bfp16 --input scale:-2,2
# Show full detail for a specific case (by id or substring)
python3 find_case.py --id f446b9
"""
import argparse
import json
import os
import re
import sys
from pathlib import Path
SCRIPT_DIR = Path(__file__).parent
DEFAULT_BUNDLE_DIR = SCRIPT_DIR.parent / "integration_test_bundles"
def _uid_to_role(template):
roles = {}
for node in template.get("nodes", []):
for port, uid_val in node.get("inputs", {}).items():
name = port.removesuffix("_tensor_uid").removesuffix("_uid")
if isinstance(uid_val, int):
roles[uid_val] = name
for port, uid_val in node.get("outputs", {}).items():
name = port.removesuffix("_tensor_uid").removesuffix("_uid")
if isinstance(uid_val, int):
roles[uid_val] = name
return roles
def _infer_layout(dims, strides):
if not dims or not strides or len(dims) != len(strides):
return None
if len(dims) < 3:
return None
pairs = list(zip(dims, strides))
spatial = pairs[2:]
channel = pairs[1]
if not spatial:
return None
if channel[1] <= spatial[-1][1]:
names = {3: "nlc", 4: "nhwc", 5: "ndhwc"}
else:
names = {3: "ncl", 4: "nchw", 5: "ncdhw"}
return names.get(len(dims))
def _num(v):
if isinstance(v, float):
s = f"{v:g}"
return s.replace(".", "p") if "." in s else s
return str(v)
def _fmt_range(spec, role="?"):
if not isinstance(spec, dict):
return f"{role}={spec}"
if "lo" in spec and "hi" in spec:
rng = f"[{_num(spec['lo'])},{_num(spec['hi'])}]"
elif "value" in spec:
rng = (
f"={_num(spec['value'])}"
if isinstance(spec["value"], (int, float))
else f"={spec['value']}"
)
else:
rng = spec.get("kind", "?")
seed = f" seed={spec['seed']}" if "seed" in spec else ""
return f"{role}{rng}{seed}"
def _load_all_cases(bundle_dir):
cases = []
for root, dirs, files in os.walk(bundle_dir):
if "sweep.json" not in files:
continue
sweep_path = os.path.join(root, "sweep.json")
template_path = os.path.join(root, "graph.template.json")
rel = os.path.relpath(root, bundle_dir)
parts = rel.split(os.sep)
tier = parts[0] if len(parts) > 0 else ""
op = parts[1] if len(parts) > 1 else ""
topo = parts[2] if len(parts) > 2 else ""
try:
with open(sweep_path) as f:
sweep = json.load(f)
except (json.JSONDecodeError, OSError):
continue
template = {}
if os.path.exists(template_path):
try:
with open(template_path) as f:
template = json.load(f)
except (json.JSONDecodeError, OSError):
pass
roles = _uid_to_role(template)
for case in sweep.get("cases", []):
values = case.get("values", {})
tensors = values.get("tensors", [])
rep = _rep_tensor(tensors)
dims = rep.get("dims", [])
strides = rep.get("strides", [])
raw_dt = (values.get("io_data_type") or rep.get("data_type", "")).lower()
dt = _DTYPE_DISPLAY.get(raw_dt, raw_dt)
layout = _infer_layout(dims, strides) or ""
meta = case.get("metadata", {})
inputs = meta.get("inputs", {})
input_roles = {}
for uid, spec in inputs.items():
try:
role = roles.get(int(uid), f"t{uid}")
except (ValueError, TypeError):
role = f"t{uid}"
input_roles[role] = spec
cases.append(
{
"id": case.get("id", ""),
"tier": tier,
"op": op,
"topology": topo,
"shape": dims,
"dtype": dt,
"layout": layout,
"inputs": input_roles,
"seed": meta.get("seed"),
"origin": meta.get("reference_source", ""),
"suite": f"{tier}_{op}_{topo}",
"gtest": f"{tier}_{op}_{topo}/{case.get('id', '')}",
"_raw_inputs": inputs,
"_roles": roles,
}
)
return cases
def _rep_tensor(tensors):
if not tensors:
return {}
best = tensors[0]
best_rank = len(best.get("dims", []))
best_nonunit = sum(1 for d in best.get("dims", []) if d > 1)
for t in tensors[1:]:
rank = len(t.get("dims", []))
nonunit = sum(1 for d in t.get("dims", []) if d > 1)
if rank > best_rank or (rank == best_rank and nonunit > best_nonunit):
best = t
best_rank = rank
best_nonunit = nonunit
return best
def _parse_input_filter(s):
m = re.match(r"^(\w+):\[?([^,\[\]]+),([^,\[\]]+)\]?$", s)
if m:
return (
m.group(1),
float(m.group(2).replace("p", ".")),
float(m.group(3).replace("p", ".")),
)
if re.match(r"^\w+$", s):
return (s, None, None)
print(
f" Invalid input filter: {s!r} (expected: role or role:lo,hi)",
file=sys.stderr,
)
sys.exit(1)
_DTYPE_DISPLAY = {
"bfloat16": "bfp16",
"half": "fp16",
"float": "fp32",
"double": "fp64",
}
_DTYPE_ALIASES = {v: v for v in _DTYPE_DISPLAY.values()}
_DTYPE_ALIASES.update({k: v for k, v in _DTYPE_DISPLAY.items()})
_DTYPE_ALIASES["bf16"] = "bfp16"
def _matches(case, args):
if args.op and args.op.lower() not in case["op"].lower():
return False
if args.dtype:
needle = _DTYPE_ALIASES.get(args.dtype.lower(), args.dtype.lower())
if needle != case["dtype"]:
return False
if args.layout and args.layout.lower() != case["layout"]:
return False
if args.shape:
target = [int(x) for x in args.shape.split("x")]
if case["shape"] != target:
return False
if args.tier and args.tier.lower() != case["tier"].lower():
return False
if args.id and args.id not in case["id"]:
return False
if args.origin and args.origin.lower() not in case["origin"].lower():
return False
for rf in args.input or []:
role, lo, hi = _parse_input_filter(rf)
spec = case["inputs"].get(role)
if not spec:
return False
if lo is not None and isinstance(spec, dict):
if abs(spec.get("lo", 0) - lo) > 1e-9 or abs(spec.get("hi", 0) - hi) > 1e-9:
return False
return True
def _print_table(cases):
if not cases:
print(" No matching cases found.")
return
headers = ["suite", "id", "shape", "dtype", "layout", "inputs"]
rows = []
for c in cases:
shape = "x".join(str(d) for d in c["shape"]) if c["shape"] else "-"
if c["inputs"]:
inp = " | ".join(
_fmt_range(spec, role) for role, spec in sorted(c["inputs"].items())
)
else:
inp = "-"
rows.append(
[c["suite"], c["id"], shape, c["dtype"] or "-", c["layout"] or "-", inp]
)
widths = [len(h) for h in headers]
for row in rows:
for i, cell in enumerate(row):
widths[i] = max(widths[i], len(cell))
fmt = " ".join(f"{{:<{w}}}" for w in widths)
print(fmt.format(*headers))
print(fmt.format(*["-" * w for w in widths]))
for row in rows:
print(fmt.format(*row))
def _print_detail(case):
print(f"\n Case: {case['id']}")
print(f" Suite: {case['suite']}")
print(f" GTest: --gtest_filter='{case['gtest']}'")
shape = "x".join(str(d) for d in case["shape"]) if case["shape"] else "-"
print(f" Shape: {shape}")
print(f" Dtype: {case['dtype'] or '-'}")
print(f" Layout: {case['layout'] or '-'}")
print(f" Seed: {case['seed']}")
if case["origin"]:
print(f" Origin: {case['origin']}")
if case["inputs"]:
print(f" Inputs:")
for role, spec in sorted(case["inputs"].items()):
print(f" {_fmt_range(spec, role)}")
else:
print(f" Inputs: (no overrides — uses per-op defaults)")
def main() -> int:
ap = argparse.ArgumentParser(
description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
)
ap.add_argument("--bundle-dir", type=Path, default=DEFAULT_BUNDLE_DIR)
ap.add_argument("--op", help="filter by operation name (substring)")
ap.add_argument("--dtype", help="filter by data type (e.g. fp16, bfp16, fp32)")
ap.add_argument("--layout", help="filter by layout (e.g. nhwc, nchw)")
ap.add_argument("--shape", help="filter by shape (e.g. 1x16x3x3)")
ap.add_argument("--tier", help="filter by tier (quick, full, standard)")
ap.add_argument(
"--input",
action="append",
help="filter by input role, e.g. --input epsilon or --input epsilon:-1,1",
)
ap.add_argument("--id", help="filter by case id (substring match)")
ap.add_argument(
"--origin", help="filter by reference_source C++ suite name (substring)"
)
ap.add_argument(
"--detail", action="store_true", help="show full detail for each match"
)
args = ap.parse_args()
all_cases = _load_all_cases(args.bundle_dir)
if not any(
[
args.op,
args.dtype,
args.layout,
args.shape,
args.tier,
args.input,
args.id,
args.origin,
]
):
print(f" {len(all_cases)} total cases in {args.bundle_dir}")
print(
f" Use --op, --dtype, --layout, --shape, --input, --id, --origin to filter."
)
return 0
matches = [c for c in all_cases if _matches(c, args)]
print(f" {len(matches)}/{len(all_cases)} cases match\n")
if args.detail:
for c in matches:
_print_detail(c)
else:
_print_table(matches)
return 0
if __name__ == "__main__":
sys.exit(main())