This is an automated email from the ASF dual-hosted git repository. guanmingchiu pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/mahout.git
commit 509c28f6588caf8f6ab0153221ff0daa59fdbe40 Author: Guan-Ming (Wesley) Chiu <[email protected]> AuthorDate: Mon Jan 5 19:32:04 2026 +0800 [QDP] Fix benchmark to use batch encoding (#794) --- qdp/Cargo.lock | 23 +++++++++++++ qdp/qdp-python/Cargo.toml | 1 + qdp/qdp-python/benchmark/benchmark_latency.py | 12 +++---- qdp/qdp-python/src/lib.rs | 48 +++++++++++++++++++++++++++ 4 files changed, 77 insertions(+), 7 deletions(-) diff --git a/qdp/Cargo.lock b/qdp/Cargo.lock index d65291be6..9e902660e 100644 --- a/qdp/Cargo.lock +++ b/qdp/Cargo.lock @@ -937,6 +937,22 @@ dependencies = [ "libm", ] +[[package]] +name = "numpy" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aac2e6a6e4468ffa092ad43c39b81c79196c2bb773b8db4085f695efe3bba17" +dependencies = [ + "libc", + "ndarray", + "num-complex", + "num-integer", + "num-traits", + "pyo3", + "pyo3-build-config", + "rustc-hash", +] + [[package]] name = "nvtx" version = "1.3.0" @@ -1174,6 +1190,7 @@ dependencies = [ name = "qdp-python" version = "0.1.0" dependencies = [ + "numpy", "pyo3", "qdp-core", ] @@ -1248,6 +1265,12 @@ version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + [[package]] name = "rustc_version" version = "0.4.1" diff --git a/qdp/qdp-python/Cargo.toml b/qdp/qdp-python/Cargo.toml index 8232f822b..d00373bc0 100644 --- a/qdp/qdp-python/Cargo.toml +++ b/qdp/qdp-python/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["cdylib"] [dependencies] pyo3 = { version = "0.27", features = ["extension-module"] } +numpy = "0.27" qdp-core = { path = "../qdp-core" } [features] diff --git a/qdp/qdp-python/benchmark/benchmark_latency.py b/qdp/qdp-python/benchmark/benchmark_latency.py index bd6903f62..b1c776d1e 100644 --- a/qdp/qdp-python/benchmark/benchmark_latency.py +++ b/qdp/qdp-python/benchmark/benchmark_latency.py @@ -129,18 +129,16 @@ def run_mahout(num_qubits: int, total_batches: int, batch_size: int, prefetch: i print(f"[Mahout] Init failed: {exc}") return 0.0, 0.0 + vector_len = 1 << num_qubits sync_cuda() start = time.perf_counter() processed = 0 - for batch in prefetched_batches( - total_batches, batch_size, 1 << num_qubits, prefetch - ): + for batch in prefetched_batches(total_batches, batch_size, vector_len, prefetch): normalized = normalize_batch(batch) - for sample in normalized: - qtensor = engine.encode(sample.tolist(), num_qubits, "amplitude") - _ = torch.utils.dlpack.from_dlpack(qtensor) - processed += 1 + qtensor = engine.encode_batch(normalized, num_qubits, "amplitude") + _ = torch.utils.dlpack.from_dlpack(qtensor) + processed += normalized.shape[0] sync_cuda() duration = time.perf_counter() - start diff --git a/qdp/qdp-python/src/lib.rs b/qdp/qdp-python/src/lib.rs index 153674d83..1dc60da70 100644 --- a/qdp/qdp-python/src/lib.rs +++ b/qdp/qdp-python/src/lib.rs @@ -14,6 +14,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use numpy::{PyReadonlyArray2, PyUntypedArrayMethods}; use pyo3::exceptions::PyRuntimeError; use pyo3::ffi; use pyo3::prelude::*; @@ -246,6 +247,53 @@ impl QdpEngine { }) } + /// Encode a batch of samples from NumPy array (zero-copy, most efficient) + /// + /// Args: + /// batch_data: 2D NumPy array of shape [num_samples, sample_size] with dtype float64 + /// num_qubits: Number of qubits for encoding + /// encoding_method: Encoding strategy ("amplitude", "angle", or "basis") + /// + /// Returns: + /// QuantumTensor: DLPack tensor containing all encoded states + /// Shape: [num_samples, 2^num_qubits] + /// + /// Example: + /// >>> engine = QdpEngine(device_id=0) + /// >>> batch = np.random.randn(64, 4).astype(np.float64) + /// >>> qtensor = engine.encode_batch(batch, 2, "amplitude") + /// >>> torch_tensor = torch.from_dlpack(qtensor) # Shape: [64, 4] + fn encode_batch( + &self, + batch_data: PyReadonlyArray2<f64>, + num_qubits: usize, + encoding_method: &str, + ) -> PyResult<QuantumTensor> { + let shape = batch_data.shape(); + let num_samples = shape[0]; + let sample_size = shape[1]; + + // Get contiguous slice from numpy array (zero-copy if already contiguous) + let data_slice = batch_data + .as_slice() + .map_err(|_| PyRuntimeError::new_err("NumPy array must be contiguous (C-order)"))?; + + let ptr = self + .engine + .encode_batch( + data_slice, + num_samples, + sample_size, + num_qubits, + encoding_method, + ) + .map_err(|e| PyRuntimeError::new_err(format!("Batch encoding failed: {}", e)))?; + Ok(QuantumTensor { + ptr, + consumed: false, + }) + } + /// Encode from PyTorch Tensor /// /// Args:
