This is an automated email from the ASF dual-hosted git repository.
viiccwen 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 b9e33e662 fix(qdp): apply shared max qubit validation (#1442)
b9e33e662 is described below
commit b9e33e662ef76557ffbeba18b4ddcec852c4e9b5
Author: Vic Wen <[email protected]>
AuthorDate: Thu Jul 16 16:19:52 2026 +0800
fix(qdp): apply shared max qubit validation (#1442)
---
qdp/qdp-core/src/gpu/encodings/amplitude.rs | 8 ++++-
qdp/qdp-core/src/gpu/encodings/mod.rs | 2 +-
qdp/qdp-core/tests/gpu_ptr_encoding.rs | 53 +++++++++++++++++++++++++++++
qdp/qdp-core/tests/gpu_validation.rs | 10 ++++++
qdp/qdp-kernels/src/kernel_config.h | 7 ----
5 files changed, 71 insertions(+), 9 deletions(-)
diff --git a/qdp/qdp-core/src/gpu/encodings/amplitude.rs
b/qdp/qdp-core/src/gpu/encodings/amplitude.rs
index 7cf70d9ec..7c8d61490 100644
--- a/qdp/qdp-core/src/gpu/encodings/amplitude.rs
+++ b/qdp/qdp-core/src/gpu/encodings/amplitude.rs
@@ -22,7 +22,7 @@
use std::sync::Arc;
-use super::QuantumEncoder;
+use super::{QuantumEncoder, validate_qubit_count};
#[cfg(target_os = "linux")]
use crate::error::cuda_error_to_string;
use crate::error::{MahoutError, Result};
@@ -308,6 +308,7 @@ impl QuantumEncoder for AmplitudeEncoder {
num_qubits: usize,
stream: *mut c_void,
) -> Result<GpuStateVector> {
+ validate_qubit_count(num_qubits)?;
let state_len = 1 << num_qubits;
if input_len == 0 {
return Err(MahoutError::InvalidInput(
@@ -371,6 +372,7 @@ impl QuantumEncoder for AmplitudeEncoder {
num_qubits: usize,
stream: *mut c_void,
) -> Result<GpuStateVector> {
+ validate_qubit_count(num_qubits)?;
let state_len = 1 << num_qubits;
if sample_size == 0 {
return Err(MahoutError::InvalidInput(
@@ -469,6 +471,7 @@ impl QuantumEncoder for AmplitudeEncoder {
) -> Result<GpuStateVector> {
crate::profile_scope!("AmplitudeEncoder::encode_batch_f32");
+ validate_qubit_count(num_qubits)?;
let state_len = 1 << num_qubits;
if sample_size == 0 {
@@ -603,6 +606,7 @@ impl QuantumEncoder for AmplitudeEncoder {
num_qubits: usize,
stream: *mut c_void,
) -> Result<GpuStateVector> {
+ validate_qubit_count(num_qubits)?;
let state_len = 1 << num_qubits;
if sample_size == 0 {
return Err(MahoutError::InvalidInput(
@@ -859,6 +863,7 @@ impl AmplitudeEncoder {
));
}
+ validate_qubit_count(num_qubits)?;
let state_len = 1usize << num_qubits;
if input_len > state_len {
return Err(MahoutError::InvalidInput(format!(
@@ -928,6 +933,7 @@ impl AmplitudeEncoder {
num_qubits: usize,
stream: *mut c_void,
) -> Result<GpuStateVector> {
+ validate_qubit_count(num_qubits)?;
let state_len = 1 << num_qubits;
if num_samples == 0 {
return Err(MahoutError::InvalidInput(
diff --git a/qdp/qdp-core/src/gpu/encodings/mod.rs
b/qdp/qdp-core/src/gpu/encodings/mod.rs
index 8d0fd5b4c..787b631e5 100644
--- a/qdp/qdp-core/src/gpu/encodings/mod.rs
+++ b/qdp/qdp-core/src/gpu/encodings/mod.rs
@@ -26,7 +26,7 @@ use cudarc::driver::CudaDevice;
use std::ffi::c_void;
/// Maximum number of qubits supported (16GB GPU memory limit)
-/// This constant must match MAX_QUBITS in qdp-kernels/src/kernel_config.h
+/// Shared by all QDP encoder validation paths.
pub const MAX_QUBITS: usize = 30;
/// Validates qubit count against practical limits.
diff --git a/qdp/qdp-core/tests/gpu_ptr_encoding.rs
b/qdp/qdp-core/tests/gpu_ptr_encoding.rs
index 48ba65f84..f9396e1d4 100644
--- a/qdp/qdp-core/tests/gpu_ptr_encoding.rs
+++ b/qdp/qdp-core/tests/gpu_ptr_encoding.rs
@@ -19,6 +19,8 @@
#![cfg(target_os = "linux")]
use cudarc::driver::{DevicePtr, DeviceSlice};
+use qdp_core::gpu::encodings::MAX_QUBITS;
+use qdp_core::gpu::{AmplitudeEncoder, QuantumEncoder};
use qdp_core::{MahoutError, Precision, QdpEngine};
use std::ffi::c_void;
@@ -40,8 +42,59 @@ fn engine_f32() -> Option<QdpEngine> {
common::qdp_engine_with_precision(Precision::Float32)
}
+fn assert_max_qubits_error<T>(result: qdp_core::Result<T>) {
+ assert!(
+ matches!(result, Err(MahoutError::InvalidInput(msg)) if
+ msg.contains("exceeds") && msg.contains(&MAX_QUBITS.to_string())),
+ "amplitude GPU-pointer path should use the shared qubit limit"
+ );
+}
+
// ---- Validation / error-path tests (return before using pointer) ----
+#[test]
+fn test_amplitude_gpu_pointer_paths_reject_excessive_qubits() {
+ let Some(engine) = common::qdp_engine() else {
+ return;
+ };
+
+ let Some((_f64_device, f64_data_d)) = common::copy_f64_to_device(&[1.0])
else {
+ return;
+ };
+ let Some((f32_device, f32_data_d)) =
common::copy_f32_to_device(&[1.0_f32]) else {
+ return;
+ };
+ let f64_ptr = *f64_data_d.device_ptr() as *const f64 as *const c_void;
+ let f32_ptr = *f32_data_d.device_ptr() as *const f32;
+
+ // Use the platform word size so a missing validation call fails at the
shift without
+ // attempting an enormous GPU allocation.
+ let excessive_qubits = usize::BITS as usize;
+
+ assert_max_qubits_error(unsafe {
+ engine.encode_from_gpu_ptr(f64_ptr, 1, excessive_qubits, "amplitude")
+ });
+ assert_max_qubits_error(unsafe {
+ engine.encode_batch_from_gpu_ptr(f64_ptr, 1, 1, excessive_qubits,
"amplitude")
+ });
+ assert_max_qubits_error(unsafe {
+ engine.encode_from_gpu_ptr_f32(f32_ptr, 1, excessive_qubits)
+ });
+ assert_max_qubits_error(unsafe {
+ engine.encode_batch_from_gpu_ptr_f32(f32_ptr, 1, 1, excessive_qubits)
+ });
+ assert_max_qubits_error(unsafe {
+ AmplitudeEncoder.encode_batch_from_gpu_ptr_f32(
+ &f32_device,
+ f32_ptr as *const c_void,
+ 1,
+ 1,
+ excessive_qubits,
+ std::ptr::null_mut(),
+ )
+ });
+}
+
#[test]
fn test_encode_from_gpu_ptr_unknown_method() {
let Some(engine) = common::qdp_engine() else {
diff --git a/qdp/qdp-core/tests/gpu_validation.rs
b/qdp/qdp-core/tests/gpu_validation.rs
index 291f92dce..74becfccf 100644
--- a/qdp/qdp-core/tests/gpu_validation.rs
+++ b/qdp/qdp-core/tests/gpu_validation.rs
@@ -127,6 +127,16 @@ fn test_input_validation_max_qubits() {
}
_ => panic!("Expected InvalidInput error for max qubits"),
}
+
+ // Use the platform word size so a missing validation call fails at the
shift without
+ // attempting an enormous GPU allocation.
+ let excessive_qubits = usize::BITS as usize;
+ let result = engine.encode_batch_f32(&[1.0_f32], 1, 1, excessive_qubits,
"amplitude");
+ assert!(
+ matches!(result, Err(MahoutError::InvalidInput(msg)) if
+ msg.contains("exceeds") && msg.contains(&MAX_QUBITS.to_string())),
+ "f32 batch path should use the shared qubit limit"
+ );
}
#[test]
diff --git a/qdp/qdp-kernels/src/kernel_config.h
b/qdp/qdp-kernels/src/kernel_config.h
index 9a5708ac3..069f69a3b 100644
--- a/qdp/qdp-kernels/src/kernel_config.h
+++ b/qdp/qdp-kernels/src/kernel_config.h
@@ -50,13 +50,6 @@
// This is a hardware limitation, not a tunable parameter
#define CUDA_MAX_GRID_DIM_1D 2147483647
-// ============================================================================
-// Qubit Limits
-// ============================================================================
-// Maximum qubits supported (16GB GPU memory limit)
-// This limit ensures state vectors fit within practical GPU memory constraints
-#define MAX_QUBITS 30
-
// ============================================================================
// FWT (Fast Walsh-Hadamard Transform) Configuration
// ============================================================================