This is an automated email from the ASF dual-hosted git repository.

guan404ming pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/mahout.git


The following commit(s) were added to refs/heads/main by this push:
     new 6a1ea6a31 fix(qdp): reject invalid non-amplitude benchmark combos 
(#1303)
6a1ea6a31 is described below

commit 6a1ea6a315054975e0551135f4bf88de4e3e0f9a
Author: Vic Wen <[email protected]>
AuthorDate: Thu May 14 10:33:03 2026 +0800

    fix(qdp): reject invalid non-amplitude benchmark combos (#1303)
    
    * fix(qdp): reject invalid non-amplitude benchmark combos
    
    * test(qdp): cover non-happy benchmark CLI cases
---
 qdp/qdp-python/benchmark/README.md                 |  13 +-
 qdp/qdp-python/benchmark/benchmark_latency.py      |  20 +--
 qdp/qdp-python/benchmark/benchmark_throughput.py   |  20 +--
 qdp/qdp-python/benchmark/utils.py                  |  35 ++++
 testing/conftest.py                                |   1 +
 .../qdp_python/test_benchmark_cli_validation.py    | 185 +++++++++++++++++++++
 testing/qdp_python/test_fallback.py                |   5 +-
 7 files changed, 253 insertions(+), 26 deletions(-)

diff --git a/qdp/qdp-python/benchmark/README.md 
b/qdp/qdp-python/benchmark/README.md
index 348441fdc..0dbea81ee 100644
--- a/qdp/qdp-python/benchmark/README.md
+++ b/qdp/qdp-python/benchmark/README.md
@@ -80,7 +80,7 @@ Notes:
 ```bash
 uv run --project qdp/qdp-python python 
qdp/qdp-python/benchmark/benchmark_latency.py --qubits 16 --batches 200 
--batch-size 64 --prefetch 16
 uv run --project qdp/qdp-python python 
qdp/qdp-python/benchmark/benchmark_latency.py --frameworks mahout,pennylane
-uv run --project qdp/qdp-python python 
qdp/qdp-python/benchmark/benchmark_latency.py --encoding-method basis
+uv run --project qdp/qdp-python python 
qdp/qdp-python/benchmark/benchmark_latency.py --frameworks mahout 
--encoding-method basis
 ```
 
 Notes:
@@ -88,6 +88,9 @@ Notes:
 - `--frameworks` is a comma-separated list or `all`.
   Options: `mahout`, `pennylane`, `qiskit-init`, `qiskit-statevector`.
 - `--encoding-method` selects the encoding method: `amplitude` (default), 
`angle`, `basis`, `iqp`, or `iqp-z`.
+- In these CLIs, non-`amplitude` benchmark modes are currently Mahout-only.
+  Cross-framework parity exists only for `amplitude`, so `angle`, `basis`,
+  `iqp`, and `iqp-z` must use `--frameworks mahout`.
 - The latency test reports average milliseconds per vector.
 - Flags:
   - `--qubits`: controls the input length together with `--encoding-method`.
@@ -110,14 +113,18 @@ output.
 ```bash
 uv run --project qdp/qdp-python python 
qdp/qdp-python/benchmark/benchmark_throughput.py --qubits 16 --batches 200 
--batch-size 64 --prefetch 16
 uv run --project qdp/qdp-python python 
qdp/qdp-python/benchmark/benchmark_throughput.py --frameworks mahout,pennylane
-uv run --project qdp/qdp-python python 
qdp/qdp-python/benchmark/benchmark_throughput.py --encoding-method basis
+uv run --project qdp/qdp-python python 
qdp/qdp-python/benchmark/benchmark_throughput.py --frameworks mahout 
--encoding-method basis
 ```
 
 Notes:
 
 - `--frameworks` is a comma-separated list or `all`.
-  Options: `mahout`, `pennylane`, `qiskit`.
+  Options: `mahout`, `mahout-amd`, `pennylane`, `pennylane-amdgpu`,
+  `pytorch-ref`, `qiskit`.
 - `--encoding-method` selects the encoding method: `amplitude` (default), 
`angle`, `basis`, `iqp`, or `iqp-z`.
+- In these CLIs, non-`amplitude` benchmark modes are currently Mahout-only.
+  Cross-framework parity exists only for `amplitude`, so `angle`, `basis`,
+  `iqp`, and `iqp-z` must use `--frameworks mahout`.
 - For synthetic inputs, amplitude uses `2^qubits`, angle and `iqp-z` use 
`qubits`,
   basis uses one basis index, and `iqp` uses `qubits + qubits*(qubits-1)/2`.
 - Throughput is reported in vectors/sec (higher is better).
diff --git a/qdp/qdp-python/benchmark/benchmark_latency.py 
b/qdp/qdp-python/benchmark/benchmark_latency.py
index 3c736a3be..ab48faec4 100644
--- a/qdp/qdp-python/benchmark/benchmark_latency.py
+++ b/qdp/qdp-python/benchmark/benchmark_latency.py
@@ -31,7 +31,11 @@ import time
 import torch
 from qumat_qdp import QdpBenchmark
 
-from benchmark.utils import normalize_batch, prefetched_batches
+from benchmark.utils import (
+    normalize_batch,
+    prefetched_batches,
+    validate_framework_selection,
+)
 
 BAR = "=" * 70
 SEP = "-" * 70
@@ -267,16 +271,10 @@ def main() -> None:
     except ValueError as exc:
         parser.error(str(exc))
 
-    # TODO: fix this with #1252 in the future.
-    if args.encoding_method in {"iqp", "iqp-z"}:
-        unsupported = [name for name in frameworks if name != "mahout"]
-        if unsupported:
-            print(
-                "Warning: IQP benchmarks in this script currently support only 
"
-                "framework 'mahout'; skipping unsupported frameworks: "
-                f"{', '.join(unsupported)}."
-            )
-            frameworks = ["mahout"]
+    try:
+        frameworks = validate_framework_selection(frameworks, 
args.encoding_method)
+    except ValueError as exc:
+        parser.error(str(exc))
 
     total_vectors = args.batches * args.batch_size
     vector_len = _sample_dim(args.qubits, args.encoding_method)
diff --git a/qdp/qdp-python/benchmark/benchmark_throughput.py 
b/qdp/qdp-python/benchmark/benchmark_throughput.py
index 481dca5c0..f174eab15 100644
--- a/qdp/qdp-python/benchmark/benchmark_throughput.py
+++ b/qdp/qdp-python/benchmark/benchmark_throughput.py
@@ -88,7 +88,11 @@ import numpy as np
 import torch
 from qumat_qdp import QdpBenchmark
 
-from benchmark.utils import normalize_batch, prefetched_batches
+from benchmark.utils import (
+    normalize_batch,
+    prefetched_batches,
+    validate_framework_selection,
+)
 
 BAR = "=" * 70
 SEP = "-" * 70
@@ -511,16 +515,10 @@ def main() -> None:
     except ValueError as exc:
         parser.error(str(exc))
 
-    # TODO: fix this with #1252 in the future.
-    if args.encoding_method in {"iqp", "iqp-z"}:
-        unsupported = [name for name in frameworks if name != "mahout"]
-        if unsupported:
-            print(
-                "Warning: IQP benchmarks in this script currently support only 
"
-                "framework 'mahout'; skipping unsupported frameworks: "
-                f"{', '.join(unsupported)}."
-            )
-            frameworks = ["mahout"]
+    try:
+        frameworks = validate_framework_selection(frameworks, 
args.encoding_method)
+    except ValueError as exc:
+        parser.error(str(exc))
 
     total_vectors = args.batches * args.batch_size
     vector_len = _sample_dim(args.qubits, args.encoding_method)
diff --git a/qdp/qdp-python/benchmark/utils.py 
b/qdp/qdp-python/benchmark/utils.py
index bc9b577db..d013b73ed 100644
--- a/qdp/qdp-python/benchmark/utils.py
+++ b/qdp/qdp-python/benchmark/utils.py
@@ -31,6 +31,41 @@ import numpy as np
 import torch
 
 
+def validate_framework_selection(
+    frameworks: list[str],
+    encoding_method: str,
+    *,
+    allowed_non_amplitude_frameworks: tuple[str, ...] = ("mahout",),
+) -> list[str]:
+    """
+    Validate that the selected frameworks provide a fair benchmark comparison.
+
+    These benchmark CLIs currently support non-amplitude modes only on the
+    Mahout path. Cross-framework parity is therefore available only for
+    amplitude encoding.
+    """
+    if encoding_method == "amplitude":
+        return frameworks
+
+    unsupported = [
+        framework
+        for framework in frameworks
+        if framework not in allowed_non_amplitude_frameworks
+    ]
+    if unsupported:
+        allowed = ", ".join(allowed_non_amplitude_frameworks)
+        requested = ", ".join(frameworks)
+        raise ValueError(
+            "These benchmark CLIs currently support non-amplitude encodings "
+            "only on the Mahout path; cross-framework parity exists only for "
+            "amplitude encoding. "
+            f"For encoding_method={encoding_method!r}, use frameworks limited 
to: "
+            f"{allowed}. Requested: {requested}."
+        )
+
+    return frameworks
+
+
 def build_sample(
     seed: int, vector_len: int, encoding_method: str = "amplitude"
 ) -> np.ndarray:
diff --git a/testing/conftest.py b/testing/conftest.py
index 88e49deef..28c1e150d 100644
--- a/testing/conftest.py
+++ b/testing/conftest.py
@@ -63,6 +63,7 @@ def pytest_collection_modifyitems(config, items):
         "test_torch_ref.py",
         "test_fallback.py",
         "test_benchmark_utils.py",
+        "test_benchmark_cli_validation.py",
     }
 
     for item in items:
diff --git a/testing/qdp_python/test_benchmark_cli_validation.py 
b/testing/qdp_python/test_benchmark_cli_validation.py
new file mode 100644
index 000000000..6af902ea1
--- /dev/null
+++ b/testing/qdp_python/test_benchmark_cli_validation.py
@@ -0,0 +1,185 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from __future__ import annotations
+
+import importlib.util
+import sys
+import types
+from pathlib import Path
+
+import pytest
+
+_QDP_PYTHON = Path(__file__).resolve().parents[2] / "qdp" / "qdp-python"
+if str(_QDP_PYTHON) not in sys.path:
+    sys.path.insert(0, str(_QDP_PYTHON))
+
+
+def _install_local_benchmark_package() -> None:
+    utils_path = _QDP_PYTHON / "benchmark" / "utils.py"
+    utils_spec = importlib.util.spec_from_file_location("benchmark.utils", 
utils_path)
+    assert utils_spec is not None
+    assert utils_spec.loader is not None
+    utils_module = importlib.util.module_from_spec(utils_spec)
+    utils_spec.loader.exec_module(utils_module)
+
+    benchmark_package = types.ModuleType("benchmark")
+    benchmark_package.__path__ = [str(_QDP_PYTHON / "benchmark")]
+    setattr(benchmark_package, "utils", utils_module)
+
+    sys.modules["benchmark"] = benchmark_package
+    sys.modules["benchmark.utils"] = utils_module
+
+
+def _load_module(name: str, relative_path: str):
+    _install_local_benchmark_package()
+    module_path = _QDP_PYTHON / relative_path
+    spec = importlib.util.spec_from_file_location(name, module_path)
+    assert spec is not None
+    assert spec.loader is not None
+    module = importlib.util.module_from_spec(spec)
+    spec.loader.exec_module(module)
+    return module
+
+
+benchmark_latency = _load_module(
+    "qdp_benchmark_latency", "benchmark/benchmark_latency.py"
+)
+benchmark_throughput = _load_module(
+    "qdp_benchmark_throughput", "benchmark/benchmark_throughput.py"
+)
+
+
[email protected](
+    ("module", "frameworks", "encoding_method"),
+    [
+        (benchmark_latency, ["mahout", "pennylane"], "angle"),
+        (benchmark_latency, ["mahout", "qiskit-init"], "basis"),
+        (benchmark_latency, ["mahout", "qiskit-statevector"], "iqp"),
+        (benchmark_throughput, ["mahout", "qiskit"], "angle"),
+        (benchmark_throughput, ["mahout", "mahout-amd"], "basis"),
+        (benchmark_throughput, ["mahout", "pytorch-ref"], "iqp-z"),
+    ],
+)
+def test_non_amplitude_cross_framework_combinations_are_rejected(
+    module, frameworks, encoding_method
+):
+    with pytest.raises(ValueError, match="currently support non-amplitude 
encodings"):
+        module.validate_framework_selection(frameworks, encoding_method)
+
+
[email protected]("module", [benchmark_latency, benchmark_throughput])
[email protected]("encoding_method", ["angle", "basis", "iqp", "iqp-z"])
+def test_mahout_only_non_amplitude_runs_remain_allowed(module, 
encoding_method):
+    assert module.validate_framework_selection(["mahout"], encoding_method) == 
[
+        "mahout"
+    ]
+
+
[email protected]("module", [benchmark_latency, benchmark_throughput])
+def test_amplitude_cross_framework_comparisons_remain_allowed(module):
+    frameworks = ["mahout", "pennylane"]
+
+    assert module.validate_framework_selection(frameworks, "amplitude") == 
frameworks
+
+
[email protected](
+    ("module", "framework"),
+    [
+        (benchmark_latency, "pennylane"),
+        (benchmark_latency, "qiskit-init"),
+        (benchmark_throughput, "qiskit"),
+        (benchmark_throughput, "mahout-amd"),
+        (benchmark_throughput, "pytorch-ref"),
+    ],
+)
+def test_non_amplitude_single_framework_runs_are_mahout_only(module, 
framework):
+    with pytest.raises(ValueError, match="currently support non-amplitude 
encodings"):
+        module.validate_framework_selection([framework], "basis")
+
+
+def test_latency_main_rejects_invalid_non_amplitude_cli_combo(monkeypatch, 
capsys):
+    monkeypatch.setattr(benchmark_latency.torch.cuda, "is_available", lambda: 
True)
+    monkeypatch.setattr(
+        sys,
+        "argv",
+        [
+            "benchmark_latency.py",
+            "--frameworks",
+            "mahout,pennylane",
+            "--encoding-method",
+            "angle",
+        ],
+    )
+
+    with pytest.raises(SystemExit) as exc_info:
+        benchmark_latency.main()
+
+    assert exc_info.value.code == 2
+    assert "currently support non-amplitude encodings" in 
capsys.readouterr().err
+
+
+def test_throughput_main_rejects_invalid_non_amplitude_cli_combo(monkeypatch, 
capsys):
+    monkeypatch.setattr(
+        sys,
+        "argv",
+        [
+            "benchmark_throughput.py",
+            "--frameworks",
+            "mahout,qiskit",
+            "--encoding-method",
+            "basis",
+        ],
+    )
+
+    with pytest.raises(SystemExit) as exc_info:
+        benchmark_throughput.main()
+
+    assert exc_info.value.code == 2
+    assert "currently support non-amplitude encodings" in 
capsys.readouterr().err
+
+
+def test_latency_main_rejects_default_all_frameworks_for_non_amplitude(
+    monkeypatch, capsys
+):
+    monkeypatch.setattr(benchmark_latency.torch.cuda, "is_available", lambda: 
True)
+    monkeypatch.setattr(
+        sys,
+        "argv",
+        ["benchmark_latency.py", "--encoding-method", "iqp-z"],
+    )
+
+    with pytest.raises(SystemExit) as exc_info:
+        benchmark_latency.main()
+
+    assert exc_info.value.code == 2
+    assert "currently support non-amplitude encodings" in 
capsys.readouterr().err
+
+
+def test_throughput_main_rejects_default_all_frameworks_for_non_amplitude(
+    monkeypatch, capsys
+):
+    monkeypatch.setattr(
+        sys,
+        "argv",
+        ["benchmark_throughput.py", "--encoding-method", "basis"],
+    )
+
+    with pytest.raises(SystemExit) as exc_info:
+        benchmark_throughput.main()
+
+    assert exc_info.value.code == 2
+    assert "currently support non-amplitude encodings" in 
capsys.readouterr().err
diff --git a/testing/qdp_python/test_fallback.py 
b/testing/qdp_python/test_fallback.py
index 381fb6d95..9b34ff12b 100644
--- a/testing/qdp_python/test_fallback.py
+++ b/testing/qdp_python/test_fallback.py
@@ -93,7 +93,10 @@ class TestBackendDetection:
 
 class TestLoaderPytorchBackend:
     def test_loader_helpers_cover_iqp_family_edges(self):
-        from qumat_qdp.loader import _build_sample, _sample_dim
+        from qumat_qdp import loader as loader_mod
+
+        _build_sample = getattr(loader_mod, "_build_sample")
+        _sample_dim = getattr(loader_mod, "_sample_dim")
 
         assert _sample_dim(3, "basis") == 1
         assert _sample_dim(3, "angle") == 3

Reply via email to