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

hcr 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 2524716d4 refactor: standardize qdp-core GPU integration tests (#1172)
2524716d4 is described below

commit 2524716d462a1b499cbcf9c5cc2511690138458c
Author: Vic Wen <[email protected]>
AuthorDate: Thu Mar 12 15:24:49 2026 +0800

    refactor: standardize qdp-core GPU integration tests (#1172)
    
    * refactor: add shred CUDA init helper
    
    * refactor: streamline DLPack memory management in tests
    
    * refactor: rename testing files with gpu prefix
    
    * docs: update docs
---
 docs/qdp/testing.md                                |   6 +-
 qdp/docs/test/README.md                            |   6 +-
 qdp/qdp-core/tests/common/mod.rs                   |  99 ++++
 .../tests/{api_workflow.rs => gpu_api_workflow.rs} | 144 ++---
 qdp/qdp-core/tests/{dlpack.rs => gpu_dlpack.rs}    | 100 ++--
 .../tests/{iqp_encoding.rs => gpu_iqp_encoding.rs} | 155 ++----
 .../{memory_safety.rs => gpu_memory_safety.rs}     |  51 +-
 qdp/qdp-core/tests/gpu_norm_f32.rs                 |  30 +-
 qdp/qdp-core/tests/gpu_ptr_encoding.rs             | 601 +++++++--------------
 .../tests/{validation.rs => gpu_validation.rs}     |  37 +-
 10 files changed, 455 insertions(+), 774 deletions(-)

diff --git a/docs/qdp/testing.md b/docs/qdp/testing.md
index ce7d659d9..b5b35a2d6 100644
--- a/docs/qdp/testing.md
+++ b/docs/qdp/testing.md
@@ -47,9 +47,9 @@ Unit tests for QDP core library covering input validation, 
API workflows, and me
 cargo test --package qdp-core
 
 # Run specific test file
-cargo test --package qdp-core --test validation
-cargo test --package qdp-core --test api_workflow
-cargo test --package qdp-core --test memory_safety
+cargo test --package qdp-core --test gpu_validation
+cargo test --package qdp-core --test gpu_api_workflow
+cargo test --package qdp-core --test gpu_memory_safety
 ```
 
 ## Requirements
diff --git a/qdp/docs/test/README.md b/qdp/docs/test/README.md
index 1c24ba783..15445045c 100644
--- a/qdp/docs/test/README.md
+++ b/qdp/docs/test/README.md
@@ -42,9 +42,9 @@ Unit tests for QDP core library covering input validation, 
API workflows, and me
 cargo test --package qdp-core
 
 # Run specific test file
-cargo test --package qdp-core --test validation
-cargo test --package qdp-core --test api_workflow
-cargo test --package qdp-core --test memory_safety
+cargo test --package qdp-core --test gpu_validation
+cargo test --package qdp-core --test gpu_api_workflow
+cargo test --package qdp-core --test gpu_memory_safety
 ```
 
 ## Requirements
diff --git a/qdp/qdp-core/tests/common/mod.rs b/qdp/qdp-core/tests/common/mod.rs
index 25e43c262..5eb128f4a 100644
--- a/qdp/qdp-core/tests/common/mod.rs
+++ b/qdp/qdp-core/tests/common/mod.rs
@@ -14,6 +14,16 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#[cfg(target_os = "linux")]
+use std::sync::Arc;
+
+#[cfg(target_os = "linux")]
+use cudarc::driver::{CudaDevice, CudaSlice};
+#[cfg(target_os = "linux")]
+use qdp_core::dlpack::DLManagedTensor;
+#[cfg(target_os = "linux")]
+use qdp_core::{Precision, QdpEngine};
+
 /// Creates normalized test data (f64)
 #[allow(dead_code)] // Used by multiple test modules
 pub fn create_test_data(size: usize) -> Vec<f64> {
@@ -25,3 +35,92 @@ pub fn create_test_data(size: usize) -> Vec<f64> {
 pub fn create_test_data_f32(size: usize) -> Vec<f32> {
     (0..size).map(|i| (i as f32) / (size as f32)).collect()
 }
+
+/// Returns a CUDA device handle, or `None` when CUDA is unavailable for the 
test environment.
+#[cfg(target_os = "linux")]
+#[allow(dead_code)]
+pub fn cuda_device() -> Option<Arc<CudaDevice>> {
+    CudaDevice::new(0).ok()
+}
+
+/// Returns a QDP engine, or `None` when GPU-backed engine initialization is 
unavailable.
+#[cfg(target_os = "linux")]
+#[allow(dead_code)]
+pub fn qdp_engine() -> Option<QdpEngine> {
+    QdpEngine::new(0).ok()
+}
+
+/// Returns a QDP engine with the requested precision, or `None` when 
unavailable.
+#[cfg(target_os = "linux")]
+#[allow(dead_code)]
+pub fn qdp_engine_with_precision(precision: Precision) -> Option<QdpEngine> {
+    QdpEngine::new_with_precision(0, precision).ok()
+}
+
+/// Copies f64 host data to the default CUDA device, or returns `None` when 
unavailable.
+#[cfg(target_os = "linux")]
+#[allow(dead_code)]
+pub fn copy_f64_to_device(data: &[f64]) -> Option<(Arc<CudaDevice>, 
CudaSlice<f64>)> {
+    let device = cuda_device()?;
+    let slice = device.htod_sync_copy(data).ok()?;
+    Some((device, slice))
+}
+
+/// Copies f32 host data to the default CUDA device, or returns `None` when 
unavailable.
+#[cfg(target_os = "linux")]
+#[allow(dead_code)]
+pub fn copy_f32_to_device(data: &[f32]) -> Option<(Arc<CudaDevice>, 
CudaSlice<f32>)> {
+    let device = cuda_device()?;
+    let slice = device.htod_sync_copy(data).ok()?;
+    Some((device, slice))
+}
+
+/// Copies usize host data to the default CUDA device, or returns `None` when 
unavailable.
+#[cfg(target_os = "linux")]
+#[allow(dead_code)]
+pub fn copy_usize_to_device(data: &[usize]) -> Option<(Arc<CudaDevice>, 
CudaSlice<usize>)> {
+    let device = cuda_device()?;
+    let slice = device.htod_sync_copy(data).ok()?;
+    Some((device, slice))
+}
+
+/// Asserts a DLPack tensor is 2D with the expected shape.
+#[cfg(target_os = "linux")]
+#[allow(dead_code)]
+pub unsafe fn assert_dlpack_shape_2d(dlpack_ptr: *mut DLManagedTensor, dim0: 
i64, dim1: i64) {
+    assert!(!dlpack_ptr.is_null(), "DLPack pointer should not be null");
+
+    let tensor = unsafe { &(*dlpack_ptr).dl_tensor };
+    assert_eq!(tensor.ndim, 2, "DLPack tensor should be 2D");
+
+    let shape = unsafe { std::slice::from_raw_parts(tensor.shape, 2) };
+    assert_eq!(shape[0], dim0, "Unexpected first dimension");
+    assert_eq!(shape[1], dim1, "Unexpected second dimension");
+}
+
+/// Asserts a DLPack tensor is 2D with the expected shape and then frees it 
via its deleter.
+#[cfg(target_os = "linux")]
+#[allow(dead_code)]
+pub unsafe fn assert_dlpack_shape_2d_and_delete(
+    dlpack_ptr: *mut DLManagedTensor,
+    dim0: i64,
+    dim1: i64,
+) {
+    unsafe { assert_dlpack_shape_2d(dlpack_ptr, dim0, dim1) };
+
+    unsafe { take_deleter_and_delete(dlpack_ptr) };
+}
+
+/// Takes the DLPack deleter from the managed tensor and invokes it exactly 
once.
+#[cfg(target_os = "linux")]
+#[allow(dead_code)]
+pub unsafe fn take_deleter_and_delete(dlpack_ptr: *mut DLManagedTensor) {
+    assert!(!dlpack_ptr.is_null(), "DLPack pointer should not be null");
+
+    let managed = unsafe { &mut *dlpack_ptr };
+    let deleter = managed
+        .deleter
+        .take()
+        .expect("DLPack deleter should be present");
+    unsafe { deleter(dlpack_ptr) };
+}
diff --git a/qdp/qdp-core/tests/api_workflow.rs 
b/qdp/qdp-core/tests/gpu_api_workflow.rs
similarity index 68%
rename from qdp/qdp-core/tests/api_workflow.rs
rename to qdp/qdp-core/tests/gpu_api_workflow.rs
index bc94d4e34..6c8e65166 100644
--- a/qdp/qdp-core/tests/api_workflow.rs
+++ b/qdp/qdp-core/tests/gpu_api_workflow.rs
@@ -16,8 +16,6 @@
 
 // API workflow tests: Engine initialization and encoding
 
-#[cfg(target_os = "linux")]
-use cudarc::driver::CudaDevice;
 #[cfg(target_os = "linux")]
 use qdp_core::MahoutError;
 use qdp_core::QdpEngine;
@@ -52,12 +50,9 @@ fn test_engine_initialization() {
 fn test_amplitude_encoding_workflow() {
     println!("Testing amplitude encoding workflow...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let data = common::create_test_data(1024);
@@ -70,15 +65,8 @@ fn test_amplitude_encoding_workflow() {
 
     // Simulate PyTorch behavior: manually call deleter to free GPU memory
     unsafe {
-        let managed = &mut *dlpack_ptr;
-        assert!(managed.deleter.is_some(), "Deleter must be present");
-
         println!("Calling deleter to free GPU memory");
-        let deleter = managed
-            .deleter
-            .take()
-            .expect("Deleter function pointer is missing!");
-        deleter(dlpack_ptr);
+        common::take_deleter_and_delete(dlpack_ptr);
         println!("PASS: Memory freed successfully");
     }
 }
@@ -88,12 +76,9 @@ fn test_amplitude_encoding_workflow() {
 fn test_amplitude_encoding_async_pipeline() {
     println!("Testing amplitude encoding async pipeline path...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     // Use 200000 elements to trigger async pipeline path (ASYNC_THRESHOLD = 
131072)
@@ -106,15 +91,8 @@ fn test_amplitude_encoding_async_pipeline() {
     println!("PASS: Encoding succeeded, DLPack pointer valid");
 
     unsafe {
-        let managed = &mut *dlpack_ptr;
-        assert!(managed.deleter.is_some(), "Deleter must be present");
-
         println!("Calling deleter to free GPU memory");
-        let deleter = managed
-            .deleter
-            .take()
-            .expect("Deleter function pointer is missing!");
-        deleter(dlpack_ptr);
+        common::take_deleter_and_delete(dlpack_ptr);
         println!("PASS: Memory freed successfully");
     }
 }
@@ -124,12 +102,9 @@ fn test_amplitude_encoding_async_pipeline() {
 fn test_angle_encoding_async_pipeline() {
     println!("Testing angle encoding async pipeline path...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let num_qubits = 4;
@@ -143,15 +118,8 @@ fn test_angle_encoding_async_pipeline() {
     println!("PASS: Angle batch encoding succeeded, DLPack pointer valid");
 
     unsafe {
-        let managed = &mut *dlpack_ptr;
-        assert!(managed.deleter.is_some(), "Deleter must be present");
-
         println!("Calling deleter to free GPU memory");
-        let deleter = managed
-            .deleter
-            .take()
-            .expect("Deleter function pointer is missing!");
-        deleter(dlpack_ptr);
+        common::take_deleter_and_delete(dlpack_ptr);
         println!("PASS: Memory freed successfully");
     }
 }
@@ -161,12 +129,9 @@ fn test_angle_encoding_async_pipeline() {
 fn test_angle_async_alignment_error() {
     println!("Testing angle async pipeline alignment error...");
 
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(device) = common::cuda_device() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let misaligned_data = vec![0.0_f64; 10];
@@ -192,12 +157,9 @@ fn test_angle_async_alignment_error() {
 fn test_batch_dlpack_2d_shape() {
     println!("Testing batch DLPack 2D shape...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     // Create batch data: 3 samples, each with 4 elements (2 qubits)
@@ -216,25 +178,12 @@ fn test_batch_dlpack_2d_shape() {
         "amplitude",
     );
     let dlpack_ptr = result.expect("Batch encoding should succeed");
-    assert!(!dlpack_ptr.is_null(), "DLPack pointer should not be null");
 
     unsafe {
-        let managed = &*dlpack_ptr;
+        let managed = &mut *dlpack_ptr;
         let tensor = &managed.dl_tensor;
 
-        // Verify 2D shape for batch tensor
-        assert_eq!(tensor.ndim, 2, "Batch tensor should be 2D");
-
-        let shape_slice = std::slice::from_raw_parts(tensor.shape, tensor.ndim 
as usize);
-        assert_eq!(
-            shape_slice[0], num_samples as i64,
-            "First dimension should be num_samples"
-        );
-        assert_eq!(
-            shape_slice[1],
-            (1 << num_qubits) as i64,
-            "Second dimension should be 2^num_qubits"
-        );
+        common::assert_dlpack_shape_2d(dlpack_ptr, num_samples as i64, (1 << 
num_qubits) as i64);
 
         let strides_slice = std::slice::from_raw_parts(tensor.strides, 
tensor.ndim as usize);
         let state_len = 1 << num_qubits;
@@ -249,17 +198,15 @@ fn test_batch_dlpack_2d_shape() {
 
         println!(
             "PASS: Batch DLPack tensor has correct 2D shape: [{}, {}]",
-            shape_slice[0], shape_slice[1]
+            num_samples,
+            1 << num_qubits
         );
         println!(
             "PASS: Strides are correct: [{}, {}]",
             strides_slice[0], strides_slice[1]
         );
 
-        // Free memory
-        if let Some(deleter) = managed.deleter {
-            deleter(dlpack_ptr);
-        }
+        common::take_deleter_and_delete(dlpack_ptr);
     }
 }
 
@@ -268,12 +215,9 @@ fn test_batch_dlpack_2d_shape() {
 fn test_single_encode_dlpack_2d_shape() {
     println!("Testing single encode returns 2D shape...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let data = common::create_test_data(16);
@@ -281,21 +225,12 @@ fn test_single_encode_dlpack_2d_shape() {
     assert!(result.is_ok(), "Encoding should succeed");
 
     let dlpack_ptr = result.unwrap();
-    assert!(!dlpack_ptr.is_null(), "DLPack pointer should not be null");
 
     unsafe {
-        let managed = &*dlpack_ptr;
+        let managed = &mut *dlpack_ptr;
         let tensor = &managed.dl_tensor;
 
-        // Verify 2D shape for single encode: [1, 2^num_qubits]
-        assert_eq!(tensor.ndim, 2, "Single encode should be 2D");
-
-        let shape_slice = std::slice::from_raw_parts(tensor.shape, tensor.ndim 
as usize);
-        assert_eq!(
-            shape_slice[0], 1,
-            "First dimension should be 1 for single encode"
-        );
-        assert_eq!(shape_slice[1], 16, "Second dimension should be [2^4]");
+        common::assert_dlpack_shape_2d(dlpack_ptr, 1, 16);
 
         let strides_slice = std::slice::from_raw_parts(tensor.strides, 
tensor.ndim as usize);
         assert_eq!(
@@ -307,15 +242,9 @@ fn test_single_encode_dlpack_2d_shape() {
             "Stride for second dimension should be 1"
         );
 
-        println!(
-            "PASS: Single encode returns 2D shape: [{}, {}]",
-            shape_slice[0], shape_slice[1]
-        );
+        println!("PASS: Single encode returns 2D shape: [{}, {}]", 1, 16);
 
-        // Free memory
-        if let Some(deleter) = managed.deleter {
-            deleter(dlpack_ptr);
-        }
+        common::take_deleter_and_delete(dlpack_ptr);
     }
 }
 
@@ -324,12 +253,9 @@ fn test_single_encode_dlpack_2d_shape() {
 fn test_dlpack_device_id() {
     println!("Testing DLPack device_id propagation...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let data = common::create_test_data(16);
@@ -362,8 +288,6 @@ fn test_dlpack_device_id() {
         );
 
         // Free memory
-        if let Some(deleter) = managed.deleter {
-            deleter(dlpack_ptr);
-        }
+        common::take_deleter_and_delete(dlpack_ptr);
     }
 }
diff --git a/qdp/qdp-core/tests/dlpack.rs b/qdp/qdp-core/tests/gpu_dlpack.rs
similarity index 76%
rename from qdp/qdp-core/tests/dlpack.rs
rename to qdp/qdp-core/tests/gpu_dlpack.rs
index 29cfc74ce..f84bcd38e 100644
--- a/qdp/qdp-core/tests/dlpack.rs
+++ b/qdp/qdp-core/tests/gpu_dlpack.rs
@@ -16,11 +16,14 @@
 
 // DLPack protocol for zero-copy GPU memory sharing with PyTorch
 
+#[path = "common/mod.rs"]
+mod common;
+
 #[cfg(test)]
 mod dlpack_tests {
     use std::ffi::c_void;
 
-    use cudarc::driver::CudaDevice;
+    use super::common;
     use qdp_core::MahoutError;
     use qdp_core::Precision;
     use qdp_core::dlpack::{
@@ -31,7 +34,9 @@ mod dlpack_tests {
 
     #[test]
     fn test_dlpack_batch_shape() {
-        let device = CudaDevice::new(0).unwrap();
+        let Some(device) = common::cuda_device() else {
+            return;
+        };
 
         let num_samples = 4;
         let num_qubits = 2; // 2^2 = 4 elements per sample
@@ -40,62 +45,37 @@ mod dlpack_tests {
                 .expect("Failed to create batch state vector");
 
         let dlpack_ptr = state_vector.to_dlpack();
-        assert!(!dlpack_ptr.is_null());
-
         unsafe {
-            let tensor = &(*dlpack_ptr).dl_tensor;
-
-            // Verify ndim is 2
-            assert_eq!(tensor.ndim, 2, "DLPack tensor should be 2D for batch");
-
-            // Verify shape
-            let shape = std::slice::from_raw_parts(tensor.shape, 2);
-            assert_eq!(shape[0], num_samples as i64, "Batch size mismatch");
-            assert_eq!(shape[1], (1 << num_qubits) as i64, "State size 
mismatch");
-
-            // Clean up using the deleter
-            if let Some(deleter) = (*dlpack_ptr).deleter {
-                deleter(dlpack_ptr);
-            }
-        }
+            common::assert_dlpack_shape_2d_and_delete(
+                dlpack_ptr,
+                num_samples as i64,
+                (1 << num_qubits) as i64,
+            )
+        };
     }
 
     #[test]
     fn test_dlpack_single_shape() {
-        let device = CudaDevice::new(0).unwrap();
+        let Some(device) = common::cuda_device() else {
+            return;
+        };
 
         let num_qubits = 2;
         let state_vector = GpuStateVector::new(&device, num_qubits, 
Precision::Float64)
             .expect("Failed to create state vector");
 
         let dlpack_ptr = state_vector.to_dlpack();
-        assert!(!dlpack_ptr.is_null());
-
         unsafe {
-            let tensor = &(*dlpack_ptr).dl_tensor;
-
-            // Verify ndim is 2 (even for single sample, per the fix)
-            assert_eq!(
-                tensor.ndim, 2,
-                "DLPack tensor should be 2D for single sample"
-            );
-
-            // Verify shape
-            let shape = std::slice::from_raw_parts(tensor.shape, 2);
-            assert_eq!(shape[0], 1, "Batch size should be 1 for single 
sample");
-            assert_eq!(shape[1], (1 << num_qubits) as i64, "State size 
mismatch");
-
-            // Clean up using the deleter
-            if let Some(deleter) = (*dlpack_ptr).deleter {
-                deleter(dlpack_ptr);
-            }
-        }
+            common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, 1, (1 << 
num_qubits) as i64)
+        };
     }
 
     #[test]
     #[cfg(target_os = "linux")]
     fn test_dlpack_single_shape_f32() {
-        let device = CudaDevice::new(0).unwrap();
+        let Some(device) = common::cuda_device() else {
+            return;
+        };
 
         let num_qubits = 2;
         let state_vector = GpuStateVector::new(&device, num_qubits, 
Precision::Float32)
@@ -111,24 +91,17 @@ mod dlpack_tests {
         );
 
         let dlpack_ptr = state_vector.to_dlpack();
-        assert!(!dlpack_ptr.is_null());
-
         unsafe {
-            let tensor = &(*dlpack_ptr).dl_tensor;
-            assert_eq!(tensor.ndim, 2, "DLPack tensor should be 2D");
-            let shape = std::slice::from_raw_parts(tensor.shape, 2);
-            assert_eq!(shape[0], 1);
-            assert_eq!(shape[1], (1 << num_qubits) as i64);
-            if let Some(deleter) = (*dlpack_ptr).deleter {
-                deleter(dlpack_ptr);
-            }
-        }
+            common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, 1, (1 << 
num_qubits) as i64)
+        };
     }
 
     #[test]
     #[cfg(target_os = "linux")]
     fn test_dlpack_batch_shape_f32() {
-        let device = CudaDevice::new(0).unwrap();
+        let Some(device) = common::cuda_device() else {
+            return;
+        };
 
         let num_samples = 3;
         let num_qubits = 2;
@@ -146,18 +119,13 @@ mod dlpack_tests {
         );
 
         let dlpack_ptr = state_vector.to_dlpack();
-        assert!(!dlpack_ptr.is_null());
-
         unsafe {
-            let tensor = &(*dlpack_ptr).dl_tensor;
-            assert_eq!(tensor.ndim, 2, "DLPack tensor should be 2D");
-            let shape = std::slice::from_raw_parts(tensor.shape, 2);
-            assert_eq!(shape[0], num_samples as i64);
-            assert_eq!(shape[1], (1 << num_qubits) as i64);
-            if let Some(deleter) = (*dlpack_ptr).deleter {
-                deleter(dlpack_ptr);
-            }
-        }
+            common::assert_dlpack_shape_2d_and_delete(
+                dlpack_ptr,
+                num_samples as i64,
+                (1 << num_qubits) as i64,
+            )
+        };
     }
 
     /// synchronize_stream(null) is a no-op and returns Ok(()) on all 
platforms.
@@ -176,6 +144,10 @@ mod dlpack_tests {
     #[test]
     #[cfg(target_os = "linux")]
     fn test_synchronize_stream_legacy() {
+        if common::cuda_device().is_none() {
+            return;
+        }
+
         unsafe {
             let result = synchronize_stream(CUDA_STREAM_LEGACY);
             assert!(
diff --git a/qdp/qdp-core/tests/iqp_encoding.rs 
b/qdp/qdp-core/tests/gpu_iqp_encoding.rs
similarity index 89%
rename from qdp/qdp-core/tests/iqp_encoding.rs
rename to qdp/qdp-core/tests/gpu_iqp_encoding.rs
index 7f976d0ad..6ca8e987a 100644
--- a/qdp/qdp-core/tests/iqp_encoding.rs
+++ b/qdp/qdp-core/tests/gpu_iqp_encoding.rs
@@ -16,7 +16,7 @@
 
 // Unit tests for IQP (Instantaneous Quantum Polynomial) encoding
 
-use qdp_core::{MahoutError, QdpEngine};
+use qdp_core::MahoutError;
 
 mod common;
 
@@ -39,9 +39,8 @@ fn iqp_z_data_len(num_qubits: usize) -> usize {
 fn test_iqp_zero_qubits_rejected() {
     println!("Testing IQP zero qubits rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let data = vec![0.5; 1];
@@ -65,9 +64,8 @@ fn test_iqp_zero_qubits_rejected() {
 fn test_iqp_max_qubits_exceeded() {
     println!("Testing IQP max qubits (>30) rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let data = vec![0.5; iqp_full_data_len(31)];
@@ -88,9 +86,8 @@ fn test_iqp_max_qubits_exceeded() {
 fn test_iqp_wrong_data_length() {
     println!("Testing IQP wrong data length rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let num_qubits = 4;
@@ -126,9 +123,8 @@ fn test_iqp_wrong_data_length() {
 fn test_iqp_z_wrong_data_length() {
     println!("Testing IQP-Z wrong data length rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let num_qubits = 4;
@@ -156,9 +152,8 @@ fn test_iqp_z_wrong_data_length() {
 fn test_iqp_nan_value_rejected() {
     println!("Testing IQP NaN value rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let num_qubits = 3;
@@ -185,9 +180,8 @@ fn test_iqp_nan_value_rejected() {
 fn test_iqp_infinity_value_rejected() {
     println!("Testing IQP infinity value rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let num_qubits = 3;
@@ -218,12 +212,9 @@ fn test_iqp_infinity_value_rejected() {
 fn test_iqp_full_encoding_workflow() {
     println!("Testing IQP full encoding workflow...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let num_qubits = 4;
@@ -267,12 +258,9 @@ fn test_iqp_full_encoding_workflow() {
 fn test_iqp_z_encoding_workflow() {
     println!("Testing IQP-Z encoding workflow...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let num_qubits = 5;
@@ -315,12 +303,9 @@ fn test_iqp_z_encoding_workflow() {
 fn test_iqp_single_qubit() {
     println!("Testing IQP single qubit encoding...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     // Single qubit IQP full: 1 parameter (no ZZ terms with only 1 qubit)
@@ -358,12 +343,9 @@ fn test_iqp_single_qubit() {
 fn test_iqp_batch_encoding() {
     println!("Testing IQP batch encoding...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let num_qubits = 3;
@@ -411,12 +393,9 @@ fn test_iqp_batch_encoding() {
 fn test_iqp_z_batch_encoding() {
     println!("Testing IQP-Z batch encoding...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let num_qubits = 4;
@@ -464,9 +443,8 @@ fn test_iqp_z_batch_encoding() {
 fn test_iqp_batch_wrong_sample_size() {
     println!("Testing IQP batch wrong sample_size rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let num_qubits = 3;
@@ -501,9 +479,8 @@ fn test_iqp_batch_wrong_sample_size() {
 fn test_iqp_batch_data_length_mismatch() {
     println!("Testing IQP batch data length mismatch rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let num_qubits = 3;
@@ -533,9 +510,8 @@ fn test_iqp_batch_data_length_mismatch() {
 fn test_iqp_batch_nan_in_sample() {
     println!("Testing IQP batch NaN value rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let num_qubits = 3;
@@ -594,12 +570,9 @@ fn test_iqp_data_length_calculations() {
 fn test_iqp_fwt_threshold_boundary() {
     println!("Testing IQP FWT threshold boundary (n=4, where FWT kicks 
in)...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     // Test at FWT_MIN_QUBITS threshold (n=4)
@@ -640,12 +613,9 @@ fn test_iqp_fwt_threshold_boundary() {
 fn test_iqp_fwt_larger_qubit_counts() {
     println!("Testing IQP FWT with larger qubit counts (n=5,6,7,8)...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     for num_qubits in [5, 6, 7, 8] {
@@ -689,12 +659,9 @@ fn test_iqp_fwt_larger_qubit_counts() {
 fn test_iqp_z_fwt_correctness() {
     println!("Testing IQP-Z FWT correctness for various qubit counts...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     // Test IQP-Z across FWT threshold
@@ -734,12 +701,9 @@ fn test_iqp_z_fwt_correctness() {
 fn test_iqp_fwt_batch_various_sizes() {
     println!("Testing IQP FWT batch encoding with various qubit counts...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     // Test batch encoding across FWT threshold
@@ -787,12 +751,9 @@ fn test_iqp_fwt_batch_various_sizes() {
 fn test_iqp_fwt_zero_parameters_identity() {
     println!("Testing IQP FWT with zero parameters produces |0⟩ state...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     // For FWT-optimized path (n >= 4), zero parameters should still give |0⟩
@@ -833,12 +794,9 @@ fn test_iqp_fwt_zero_parameters_identity() {
 fn test_iqp_encoder_via_factory() {
     println!("Testing IQP encoder creation via get_encoder...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     // Test that "iqp" and "IQP" work (case insensitive)
@@ -873,12 +831,9 @@ fn test_iqp_encoder_via_factory() {
 fn test_iqp_z_encoder_via_factory() {
     println!("Testing IQP-Z encoder creation via get_encoder...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let num_qubits = 3;
diff --git a/qdp/qdp-core/tests/memory_safety.rs 
b/qdp/qdp-core/tests/gpu_memory_safety.rs
similarity index 82%
rename from qdp/qdp-core/tests/memory_safety.rs
rename to qdp/qdp-core/tests/gpu_memory_safety.rs
index 4b6c9aa97..33e937a37 100644
--- a/qdp/qdp-core/tests/memory_safety.rs
+++ b/qdp/qdp-core/tests/gpu_memory_safety.rs
@@ -16,7 +16,7 @@
 
 // Memory safety tests: DLPack lifecycle, RAII, Arc reference counting
 
-use qdp_core::{Precision, QdpEngine};
+use qdp_core::Precision;
 
 mod common;
 
@@ -26,12 +26,9 @@ fn test_memory_pressure() {
     println!("Testing memory pressure (leak detection)");
     println!("Running 100 iterations of encode + free");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let data = common::create_test_data(1024);
@@ -42,12 +39,7 @@ fn test_memory_pressure() {
             .expect("Encoding should succeed");
 
         unsafe {
-            let managed = &mut *ptr;
-            let deleter = managed
-                .deleter
-                .take()
-                .expect("Deleter missing in pressure test!");
-            deleter(ptr);
+            common::take_deleter_and_delete(ptr);
         }
 
         if (i + 1) % 25 == 0 {
@@ -63,9 +55,8 @@ fn test_memory_pressure() {
 fn test_multiple_concurrent_states() {
     println!("Testing multiple concurrent state vectors...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let data1 = common::create_test_data(256);
@@ -81,9 +72,9 @@ fn test_multiple_concurrent_states() {
     // Free in different order to test Arc reference counting
     unsafe {
         println!("Freeing in order: 2, 1, 3");
-        (&mut *ptr2).deleter.take().expect("Deleter missing!")(ptr2);
-        (&mut *ptr1).deleter.take().expect("Deleter missing!")(ptr1);
-        (&mut *ptr3).deleter.take().expect("Deleter missing!")(ptr3);
+        common::take_deleter_and_delete(ptr2);
+        common::take_deleter_and_delete(ptr1);
+        common::take_deleter_and_delete(ptr3);
     }
 
     println!("PASS: All states freed successfully");
@@ -94,9 +85,8 @@ fn test_multiple_concurrent_states() {
 fn test_dlpack_tensor_metadata_default() {
     println!("Testing DLPack tensor metadata...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let data = common::create_test_data(1024);
@@ -136,11 +126,7 @@ fn test_dlpack_tensor_metadata_default() {
             tensor.dtype.code, tensor.dtype.bits
         );
 
-        let deleter = managed
-            .deleter
-            .take()
-            .expect("Deleter missing in metadata test!");
-        deleter(ptr);
+        common::take_deleter_and_delete(ptr);
     }
 }
 
@@ -149,9 +135,8 @@ fn test_dlpack_tensor_metadata_default() {
 fn test_dlpack_tensor_metadata_f64() {
     println!("Testing DLPack tensor metadata...");
 
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        return;
     };
 
     let data = common::create_test_data(1024);
@@ -192,10 +177,6 @@ fn test_dlpack_tensor_metadata_f64() {
             tensor.dtype.code, tensor.dtype.bits
         );
 
-        let deleter = managed
-            .deleter
-            .take()
-            .expect("Deleter missing in metadata test!");
-        deleter(ptr);
+        common::take_deleter_and_delete(ptr);
     }
 }
diff --git a/qdp/qdp-core/tests/gpu_norm_f32.rs 
b/qdp/qdp-core/tests/gpu_norm_f32.rs
index 40be53efe..af1150881 100644
--- a/qdp/qdp-core/tests/gpu_norm_f32.rs
+++ b/qdp/qdp-core/tests/gpu_norm_f32.rs
@@ -21,27 +21,24 @@
 #![cfg(target_os = "linux")]
 
 use approx::assert_relative_eq;
-use cudarc::driver::{CudaDevice, DevicePtr};
+use cudarc::driver::DevicePtr;
 use qdp_core::gpu::encodings::amplitude::AmplitudeEncoder;
 
+mod common;
+
 #[test]
 fn test_calculate_inv_norm_gpu_f32_basic() {
     println!("Testing AmplitudeEncoder::calculate_inv_norm_gpu_f32 (basic 
case)...");
 
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => {
-            println!("SKIP: No CUDA device available");
-            return;
-        }
-    };
-
     // Input: [3.0, 4.0] -> norm = 5.0, inv_norm = 0.2
     let input: Vec<f32> = vec![3.0, 4.0];
     let expected_norm = (3.0_f32.powi(2) + 4.0_f32.powi(2)).sqrt();
     let expected_inv_norm = 1.0_f32 / expected_norm;
 
-    let input_d = device.htod_sync_copy(input.as_slice()).unwrap();
+    let Some((device, input_d)) = common::copy_f32_to_device(input.as_slice()) 
else {
+        println!("SKIP: No CUDA device available");
+        return;
+    };
     let inv = unsafe {
         AmplitudeEncoder::calculate_inv_norm_gpu_f32(
             &device,
@@ -58,16 +55,11 @@ fn test_calculate_inv_norm_gpu_f32_basic() {
 fn test_calculate_inv_norm_gpu_f32_invalid_zero() {
     println!("Testing AmplitudeEncoder::calculate_inv_norm_gpu_f32 with zero 
vector...");
 
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => {
-            println!("SKIP: No CUDA device available");
-            return;
-        }
-    };
-
     let input: Vec<f32> = vec![0.0, 0.0, 0.0];
-    let input_d = device.htod_sync_copy(input.as_slice()).unwrap();
+    let Some((device, input_d)) = common::copy_f32_to_device(input.as_slice()) 
else {
+        println!("SKIP: No CUDA device available");
+        return;
+    };
 
     let result = unsafe {
         AmplitudeEncoder::calculate_inv_norm_gpu_f32(
diff --git a/qdp/qdp-core/tests/gpu_ptr_encoding.rs 
b/qdp/qdp-core/tests/gpu_ptr_encoding.rs
index ef7ffb9d5..470402727 100644
--- a/qdp/qdp-core/tests/gpu_ptr_encoding.rs
+++ b/qdp/qdp-core/tests/gpu_ptr_encoding.rs
@@ -18,11 +18,9 @@
 
 #![cfg(target_os = "linux")]
 
-use std::ffi::c_void;
-use std::sync::Arc;
-
-use cudarc::driver::{CudaDevice, CudaSlice, DevicePtr, DeviceSlice};
+use cudarc::driver::{DevicePtr, DeviceSlice};
 use qdp_core::{MahoutError, Precision, QdpEngine};
+use std::ffi::c_void;
 
 mod common;
 
@@ -39,65 +37,21 @@ fn iqp_z_data_len(num_qubits: usize) -> usize {
 // ---- Helpers for f32 encode_from_gpu_ptr_f32 tests ----
 
 fn engine_f32() -> Option<QdpEngine> {
-    QdpEngine::new_with_precision(0, Precision::Float32).ok()
-}
-
-fn device_and_f32_slice(data: &[f32]) -> Option<(Arc<CudaDevice>, 
CudaSlice<f32>)> {
-    let device = CudaDevice::new(0).ok()?;
-    let slice = device.htod_sync_copy(data).ok()?;
-    Some((device, slice))
-}
-
-fn assert_dlpack_shape_2_4_and_delete(dlpack_ptr: *mut 
qdp_core::dlpack::DLManagedTensor) {
-    assert!(!dlpack_ptr.is_null());
-    unsafe {
-        let tensor = &(*dlpack_ptr).dl_tensor;
-        assert_eq!(tensor.ndim, 2);
-        let shape = std::slice::from_raw_parts(tensor.shape, 2);
-        assert_eq!(shape[0], 1);
-        assert_eq!(shape[1], 4);
-        if let Some(deleter) = (*dlpack_ptr).deleter {
-            deleter(dlpack_ptr);
-        }
-    }
-}
-
-fn assert_dlpack_batch_shape_and_delete(
-    dlpack_ptr: *mut qdp_core::dlpack::DLManagedTensor,
-    num_samples: i64,
-    state_len: i64,
-) {
-    assert!(!dlpack_ptr.is_null());
-    unsafe {
-        let tensor = &(*dlpack_ptr).dl_tensor;
-        assert_eq!(tensor.ndim, 2);
-        let shape = std::slice::from_raw_parts(tensor.shape, 2);
-        assert_eq!(shape[0], num_samples);
-        assert_eq!(shape[1], state_len);
-        if let Some(deleter) = (*dlpack_ptr).deleter {
-            deleter(dlpack_ptr);
-        }
-    }
+    common::qdp_engine_with_precision(Precision::Float32)
 }
 
 // ---- Validation / error-path tests (return before using pointer) ----
 
 #[test]
 fn test_encode_from_gpu_ptr_unknown_method() {
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     // Need valid GPU pointer so we reach method dispatch (validation runs 
first)
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
     let data = common::create_test_data(4);
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        return;
     };
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
 
@@ -117,9 +71,8 @@ fn test_encode_from_gpu_ptr_unknown_method() {
 
 #[test]
 fn test_encode_from_gpu_ptr_amplitude_empty_input() {
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let result = unsafe { engine.encode_from_gpu_ptr(std::ptr::null(), 0, 2, 
"amplitude") };
@@ -135,20 +88,14 @@ fn test_encode_from_gpu_ptr_amplitude_empty_input() {
 
 #[test]
 fn test_encode_from_gpu_ptr_amplitude_input_exceeds_state() {
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     // Need valid GPU pointer so we reach input_len > state_len check 
(validation runs first)
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
     let data = common::create_test_data(10);
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        return;
     };
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
 
@@ -166,20 +113,14 @@ fn 
test_encode_from_gpu_ptr_amplitude_input_exceeds_state() {
 
 #[test]
 fn test_encode_batch_from_gpu_ptr_unknown_method() {
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     // Need valid GPU pointer so we reach method dispatch (validation runs 
first)
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
     let data = common::create_test_data(8);
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        return;
     };
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
 
@@ -199,9 +140,8 @@ fn test_encode_batch_from_gpu_ptr_unknown_method() {
 
 #[test]
 fn test_encode_batch_from_gpu_ptr_amplitude_num_samples_zero() {
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let result =
@@ -218,20 +158,14 @@ fn 
test_encode_batch_from_gpu_ptr_amplitude_num_samples_zero() {
 
 #[test]
 fn test_encode_from_gpu_ptr_basis_input_len_not_one() {
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     // Need valid GPU pointer so we reach basis input_len checks (validation 
runs first)
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
     let indices: Vec<usize> = vec![0, 1, 2];
-    let indices_d = match device.htod_sync_copy(indices.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, indices_d)) = 
common::copy_usize_to_device(indices.as_slice()) else {
+        return;
     };
     let ptr = *indices_d.device_ptr() as *const usize as *const c_void;
 
@@ -262,20 +196,14 @@ fn test_encode_from_gpu_ptr_basis_input_len_not_one() {
 
 #[test]
 fn test_encode_batch_from_gpu_ptr_basis_sample_size_not_one() {
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     // Need valid GPU pointer so we reach basis sample_size check (validation 
runs first)
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
     let indices: Vec<usize> = vec![0, 1];
-    let indices_d = match device.htod_sync_copy(indices.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, indices_d)) = 
common::copy_usize_to_device(indices.as_slice()) else {
+        return;
     };
     let ptr = *indices_d.device_ptr() as *const usize as *const c_void;
 
@@ -297,32 +225,18 @@ fn 
test_encode_batch_from_gpu_ptr_basis_sample_size_not_one() {
 
 #[test]
 fn test_encode_from_gpu_ptr_amplitude_success() {
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let num_qubits = 4;
     let state_len = 1 << num_qubits;
     let data = common::create_test_data(state_len);
 
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => {
-            println!("SKIP: No CUDA device");
-            return;
-        }
-    };
-
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => {
-            println!("SKIP: Failed to copy to device");
-            return;
-        }
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        println!("SKIP: Failed to copy to device");
+        return;
     };
 
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
@@ -336,44 +250,24 @@ fn test_encode_from_gpu_ptr_amplitude_success() {
     assert!(!dlpack_ptr.is_null(), "DLPack pointer should not be null");
 
     unsafe {
-        let managed = &mut *dlpack_ptr;
-        assert!(managed.deleter.is_some(), "Deleter must be present");
-        let deleter = managed
-            .deleter
-            .take()
-            .expect("Deleter function pointer is missing");
-        deleter(dlpack_ptr);
+        common::take_deleter_and_delete(dlpack_ptr);
     }
 }
 
 #[test]
 fn test_encode_from_gpu_ptr_with_stream_amplitude_success() {
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let num_qubits = 3;
     let state_len = 1 << num_qubits;
     let data = common::create_test_data(state_len);
 
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => {
-            println!("SKIP: No CUDA device");
-            return;
-        }
-    };
-
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => {
-            println!("SKIP: Failed to copy to device");
-            return;
-        }
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        println!("SKIP: Failed to copy to device");
+        return;
     };
 
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
@@ -393,20 +287,15 @@ fn 
test_encode_from_gpu_ptr_with_stream_amplitude_success() {
     assert!(!dlpack_ptr.is_null());
 
     unsafe {
-        let managed = &mut *dlpack_ptr;
-        let deleter = managed.deleter.take().expect("Deleter missing");
-        deleter(dlpack_ptr);
+        common::take_deleter_and_delete(dlpack_ptr);
     }
 }
 
 #[test]
 fn test_encode_batch_from_gpu_ptr_amplitude_success() {
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine() else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let num_qubits = 3;
@@ -416,20 +305,9 @@ fn test_encode_batch_from_gpu_ptr_amplitude_success() {
     let total = num_samples * sample_size;
     let data = common::create_test_data(total);
 
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => {
-            println!("SKIP: No CUDA device");
-            return;
-        }
-    };
-
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => {
-            println!("SKIP: Failed to copy to device");
-            return;
-        }
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        println!("SKIP: Failed to copy to device");
+        return;
     };
 
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
@@ -443,41 +321,25 @@ fn test_encode_batch_from_gpu_ptr_amplitude_success() {
     assert!(!dlpack_ptr.is_null());
 
     unsafe {
-        let managed = &mut *dlpack_ptr;
-        let deleter = managed.deleter.take().expect("Deleter missing");
-        deleter(dlpack_ptr);
+        common::take_deleter_and_delete(dlpack_ptr);
     }
 }
 
 #[test]
 fn test_encode_from_gpu_ptr_basis_success() {
     // Basis path uses ptr_f64(); engine must be Float64
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let num_qubits = 3;
     let basis_index: usize = 0;
 
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => {
-            println!("SKIP: No CUDA device");
-            return;
-        }
-    };
-
     let indices: Vec<usize> = vec![basis_index];
-    let indices_d = match device.htod_sync_copy(indices.as_slice()) {
-        Ok(b) => b,
-        Err(_) => {
-            println!("SKIP: Failed to copy to device");
-            return;
-        }
+    let Some((_device, indices_d)) = 
common::copy_usize_to_device(indices.as_slice()) else {
+        println!("SKIP: Failed to copy to device");
+        return;
     };
 
     let ptr = *indices_d.device_ptr() as *const usize as *const c_void;
@@ -491,25 +353,16 @@ fn test_encode_from_gpu_ptr_basis_success() {
     assert!(!dlpack_ptr.is_null());
 
     unsafe {
-        let managed = &mut *dlpack_ptr;
-        assert!(managed.deleter.is_some(), "Deleter must be present");
-        let deleter = managed
-            .deleter
-            .take()
-            .expect("Deleter function pointer is missing");
-        deleter(dlpack_ptr);
+        common::take_deleter_and_delete(dlpack_ptr);
     }
 }
 
 #[test]
 fn test_encode_batch_from_gpu_ptr_basis_success() {
     // Basis path uses ptr_f64(); engine must be Float64
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let num_qubits = 3;
@@ -518,20 +371,9 @@ fn test_encode_batch_from_gpu_ptr_basis_success() {
     let state_len = 1 << num_qubits;
     let basis_indices: Vec<usize> = (0..num_samples).map(|i| i % 
state_len).collect();
 
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => {
-            println!("SKIP: No CUDA device");
-            return;
-        }
-    };
-
-    let indices_d = match device.htod_sync_copy(basis_indices.as_slice()) {
-        Ok(b) => b,
-        Err(_) => {
-            println!("SKIP: Failed to copy to device");
-            return;
-        }
+    let Some((_device, indices_d)) = 
common::copy_usize_to_device(basis_indices.as_slice()) else {
+        println!("SKIP: Failed to copy to device");
+        return;
     };
 
     let ptr = *indices_d.device_ptr() as *const usize as *const c_void;
@@ -545,17 +387,14 @@ fn test_encode_batch_from_gpu_ptr_basis_success() {
     assert!(!dlpack_ptr.is_null());
 
     unsafe {
-        let managed = &mut *dlpack_ptr;
-        let deleter = managed.deleter.take().expect("Deleter missing");
-        deleter(dlpack_ptr);
+        common::take_deleter_and_delete(dlpack_ptr);
     }
 }
 
 #[test]
 fn test_encode_batch_from_gpu_ptr_iqp_success() {
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        return;
     };
     let num_qubits = 2;
     let state_len = 1 << num_qubits;
@@ -563,13 +402,8 @@ fn test_encode_batch_from_gpu_ptr_iqp_success() {
     let num_samples = 3;
     let total = num_samples * sample_size;
     let data: Vec<f64> = (0..total).map(|i| (i as f64) * 0.05).collect();
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        return;
     };
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
     let dlpack_ptr = unsafe {
@@ -578,14 +412,15 @@ fn test_encode_batch_from_gpu_ptr_iqp_success() {
             .expect("encode_batch_from_gpu_ptr iqp should succeed")
     };
     assert!(!dlpack_ptr.is_null());
-    assert_dlpack_batch_shape_and_delete(dlpack_ptr, num_samples as i64, 
state_len as i64);
+    unsafe {
+        common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, num_samples as 
i64, state_len as i64)
+    };
 }
 
 #[test]
 fn test_encode_batch_from_gpu_ptr_iqp_z_success() {
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        return;
     };
     let num_qubits = 2;
     let state_len = 1 << num_qubits;
@@ -593,13 +428,8 @@ fn test_encode_batch_from_gpu_ptr_iqp_z_success() {
     let num_samples = 3;
     let total = num_samples * sample_size;
     let data: Vec<f64> = (0..total).map(|i| (i as f64) * 0.05).collect();
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        return;
     };
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
     let dlpack_ptr = unsafe {
@@ -608,27 +438,23 @@ fn test_encode_batch_from_gpu_ptr_iqp_z_success() {
             .expect("encode_batch_from_gpu_ptr iqp-z should succeed")
     };
     assert!(!dlpack_ptr.is_null());
-    assert_dlpack_batch_shape_and_delete(dlpack_ptr, num_samples as i64, 
state_len as i64);
+    unsafe {
+        common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, num_samples as 
i64, state_len as i64)
+    };
 }
 
 #[test]
 fn test_encode_batch_from_gpu_ptr_iqp_wrong_sample_size() {
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        return;
     };
     let num_qubits = 2;
     let expected_sample_size = iqp_full_data_len(num_qubits);
     let wrong_sample_size = expected_sample_size + 1;
     let num_samples = 2;
     let data = vec![0.1_f64; num_samples * wrong_sample_size];
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        return;
     };
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
     let result = unsafe {
@@ -649,22 +475,16 @@ fn test_encode_batch_from_gpu_ptr_iqp_wrong_sample_size() 
{
 
 #[test]
 fn test_encode_batch_from_gpu_ptr_iqp_z_wrong_sample_size() {
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        return;
     };
     let num_qubits = 2;
     let expected_sample_size = iqp_z_data_len(num_qubits);
     let wrong_sample_size = expected_sample_size + 1;
     let num_samples = 2;
     let data = vec![0.1_f64; num_samples * wrong_sample_size];
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        return;
     };
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
     let result = unsafe {
@@ -685,31 +505,17 @@ fn 
test_encode_batch_from_gpu_ptr_iqp_z_wrong_sample_size() {
 
 #[test]
 fn test_encode_from_gpu_ptr_iqp_z_success() {
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let num_qubits = 2;
     let data = [0.1_f64, -0.2_f64];
 
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => {
-            println!("SKIP: No CUDA device");
-            return;
-        }
-    };
-
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => {
-            println!("SKIP: Failed to copy to device");
-            return;
-        }
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        println!("SKIP: Failed to copy to device");
+        return;
     };
 
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
@@ -719,36 +525,22 @@ fn test_encode_from_gpu_ptr_iqp_z_success() {
             .expect("encode_from_gpu_ptr iqp-z should succeed")
     };
 
-    assert_dlpack_shape_2_4_and_delete(dlpack_ptr);
+    unsafe { common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, 1, 4) };
 }
 
 #[test]
 fn test_encode_from_gpu_ptr_iqp_success() {
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => {
-            println!("SKIP: No GPU available");
-            return;
-        }
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        println!("SKIP: No GPU available");
+        return;
     };
 
     let num_qubits = 2;
     let data = [0.1_f64, -0.2_f64, 0.3_f64];
 
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => {
-            println!("SKIP: No CUDA device");
-            return;
-        }
-    };
-
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => {
-            println!("SKIP: Failed to copy to device");
-            return;
-        }
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        println!("SKIP: Failed to copy to device");
+        return;
     };
 
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
@@ -758,25 +550,19 @@ fn test_encode_from_gpu_ptr_iqp_success() {
             .expect("encode_from_gpu_ptr iqp should succeed")
     };
 
-    assert_dlpack_shape_2_4_and_delete(dlpack_ptr);
+    unsafe { common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, 1, 4) };
 }
 
 #[test]
 fn test_encode_from_gpu_ptr_iqp_wrong_input_len() {
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        return;
     };
     let num_qubits = 2;
     let expected_len = iqp_full_data_len(num_qubits);
     let data = vec![0.1_f64; expected_len];
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        return;
     };
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
 
@@ -797,20 +583,14 @@ fn test_encode_from_gpu_ptr_iqp_wrong_input_len() {
 
 #[test]
 fn test_encode_from_gpu_ptr_iqp_z_wrong_input_len() {
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        return;
     };
     let num_qubits = 2;
     let expected_len = iqp_z_data_len(num_qubits);
     let data = vec![0.1_f64; expected_len];
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        return;
     };
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
 
@@ -826,19 +606,13 @@ fn test_encode_from_gpu_ptr_iqp_z_wrong_input_len() {
 
 #[test]
 fn test_encode_from_gpu_ptr_with_stream_iqp_success() {
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        return;
     };
     let num_qubits = 2;
     let data = [0.1_f64, -0.2_f64, 0.3_f64];
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        return;
     };
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
     let dlpack_ptr = unsafe {
@@ -852,24 +626,18 @@ fn test_encode_from_gpu_ptr_with_stream_iqp_success() {
             )
             .expect("encode_from_gpu_ptr_with_stream iqp")
     };
-    assert_dlpack_shape_2_4_and_delete(dlpack_ptr);
+    unsafe { common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, 1, 4) };
 }
 
 #[test]
 fn test_encode_from_gpu_ptr_with_stream_iqp_z_success() {
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        return;
     };
     let num_qubits = 2;
     let data = [0.1_f64, -0.2_f64];
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        return;
     };
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
     let dlpack_ptr = unsafe {
@@ -883,26 +651,20 @@ fn test_encode_from_gpu_ptr_with_stream_iqp_z_success() {
             )
             .expect("encode_from_gpu_ptr_with_stream iqp-z")
     };
-    assert_dlpack_shape_2_4_and_delete(dlpack_ptr);
+    unsafe { common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, 1, 4) };
 }
 
 #[test]
 fn test_encode_from_gpu_ptr_iqp_three_qubits() {
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        return;
     };
     let num_qubits = 3;
     let state_len = 1 << num_qubits;
     let expected_len = iqp_full_data_len(num_qubits);
     let data: Vec<f64> = (0..expected_len).map(|i| (i as f64) * 0.1).collect();
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        return;
     };
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
     let dlpack_ptr = unsafe {
@@ -925,21 +687,15 @@ fn test_encode_from_gpu_ptr_iqp_three_qubits() {
 
 #[test]
 fn test_encode_from_gpu_ptr_iqp_z_three_qubits() {
-    let engine = match QdpEngine::new_with_precision(0, Precision::Float64) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        return;
     };
     let num_qubits = 3;
     let state_len = 1 << num_qubits;
     let expected_len = iqp_z_data_len(num_qubits);
     let data: Vec<f64> = (0..expected_len).map(|i| (i as f64) * 0.1).collect();
-    let device = match CudaDevice::new(0) {
-        Ok(d) => d,
-        Err(_) => return,
-    };
-    let data_d = match device.htod_sync_copy(data.as_slice()) {
-        Ok(b) => b,
-        Err(_) => return,
+    let Some((_device, data_d)) = common::copy_f64_to_device(data.as_slice()) 
else {
+        return;
     };
     let ptr = *data_d.device_ptr() as *const f64 as *const c_void;
     let dlpack_ptr = unsafe {
@@ -971,7 +727,7 @@ fn test_encode_from_gpu_ptr_f32_success() {
             return;
         }
     };
-    let (_device, input_d) = match device_and_f32_slice(&[1.0, 0.0, 0.0, 0.0]) 
{
+    let (_device, input_d) = match common::copy_f32_to_device(&[1.0, 0.0, 0.0, 
0.0]) {
         Some(t) => t,
         None => {
             println!("SKIP: No CUDA device");
@@ -984,7 +740,7 @@ fn test_encode_from_gpu_ptr_f32_success() {
             .encode_from_gpu_ptr_f32(ptr, input_d.len(), 2)
             .expect("encode_from_gpu_ptr_f32")
     };
-    assert_dlpack_shape_2_4_and_delete(dlpack_ptr);
+    unsafe { common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, 1, 4) };
 }
 
 #[test]
@@ -996,7 +752,7 @@ fn test_encode_from_gpu_ptr_f32_with_stream_success() {
             return;
         }
     };
-    let (_device, input_d) = match device_and_f32_slice(&[1.0, 0.0, 0.0, 0.0]) 
{
+    let (_device, input_d) = match common::copy_f32_to_device(&[1.0, 0.0, 0.0, 
0.0]) {
         Some(t) => t,
         None => {
             println!("SKIP: No CUDA device");
@@ -1008,7 +764,7 @@ fn test_encode_from_gpu_ptr_f32_with_stream_success() {
         engine.encode_from_gpu_ptr_f32_with_stream(ptr, input_d.len(), 2, 
std::ptr::null_mut())
     }
     .expect("encode_from_gpu_ptr_f32_with_stream");
-    assert_dlpack_shape_2_4_and_delete(dlpack_ptr);
+    unsafe { common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, 1, 4) };
 }
 
 #[test]
@@ -1020,7 +776,7 @@ fn 
test_encode_from_gpu_ptr_f32_with_stream_non_default_success() {
             return;
         }
     };
-    let (device, input_d) = match device_and_f32_slice(&[1.0, 0.0, 0.0, 0.0]) {
+    let (device, input_d) = match common::copy_f32_to_device(&[1.0, 0.0, 0.0, 
0.0]) {
         Some(t) => t,
         None => {
             println!("SKIP: No CUDA device");
@@ -1038,19 +794,16 @@ fn 
test_encode_from_gpu_ptr_f32_with_stream_non_default_success() {
             )
             .expect("encode_from_gpu_ptr_f32_with_stream (non-default stream)")
     };
-    assert_dlpack_shape_2_4_and_delete(dlpack_ptr);
+    unsafe { common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, 1, 4) };
 }
 
 #[test]
 fn test_encode_from_gpu_ptr_f32_success_f64_engine() {
-    let engine = match QdpEngine::new_with_precision(0, 
Precision::Float64).ok() {
-        Some(e) => e,
-        None => {
-            println!("SKIP: No GPU");
-            return;
-        }
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        println!("SKIP: No GPU");
+        return;
     };
-    let (_device, input_d) = match device_and_f32_slice(&[1.0, 0.0, 0.0, 0.0]) 
{
+    let (_device, input_d) = match common::copy_f32_to_device(&[1.0, 0.0, 0.0, 
0.0]) {
         Some(t) => t,
         None => {
             println!("SKIP: No CUDA device");
@@ -1063,7 +816,7 @@ fn test_encode_from_gpu_ptr_f32_success_f64_engine() {
             .encode_from_gpu_ptr_f32(ptr, input_d.len(), 2)
             .expect("encode_from_gpu_ptr_f32 (Float64 engine)")
     };
-    assert_dlpack_shape_2_4_and_delete(dlpack_ptr);
+    unsafe { common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, 1, 4) };
 }
 
 #[test]
@@ -1075,7 +828,7 @@ fn test_encode_from_gpu_ptr_f32_empty_input() {
             return;
         }
     };
-    let (_device, input_d) = match device_and_f32_slice(&[1.0]) {
+    let (_device, input_d) = match common::copy_f32_to_device(&[1.0]) {
         Some(t) => t,
         None => {
             println!("SKIP: No CUDA device");
@@ -1117,7 +870,7 @@ fn test_encode_from_gpu_ptr_f32_input_exceeds_state_len() {
             return;
         }
     };
-    let (_device, input_d) = match device_and_f32_slice(&[1.0, 0.0, 0.0, 0.0, 
0.0]) {
+    let (_device, input_d) = match common::copy_f32_to_device(&[1.0, 0.0, 0.0, 
0.0, 0.0]) {
         Some(t) => t,
         None => {
             println!("SKIP: No CUDA device");
@@ -1150,13 +903,14 @@ fn test_encode_batch_from_gpu_ptr_f32_success() {
     };
     let num_samples = 2;
     let sample_size = 4;
-    let (_device, input_d) = match device_and_f32_slice(&[1.0, 0.0, 0.0, 0.0, 
0.5, 0.5, 0.5, 0.5]) {
-        Some(t) => t,
-        None => {
-            println!("SKIP: No CUDA device");
-            return;
-        }
-    };
+    let (_device, input_d) =
+        match common::copy_f32_to_device(&[1.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 
0.5]) {
+            Some(t) => t,
+            None => {
+                println!("SKIP: No CUDA device");
+                return;
+            }
+        };
     let dlpack_ptr = unsafe {
         engine
             .encode_batch_from_gpu_ptr_f32(
@@ -1167,7 +921,13 @@ fn test_encode_batch_from_gpu_ptr_f32_success() {
             )
             .expect("encode_batch_from_gpu_ptr_f32")
     };
-    assert_dlpack_batch_shape_and_delete(dlpack_ptr, num_samples as i64, 
sample_size as i64);
+    unsafe {
+        common::assert_dlpack_shape_2d_and_delete(
+            dlpack_ptr,
+            num_samples as i64,
+            sample_size as i64,
+        )
+    };
 }
 
 #[test]
@@ -1179,13 +939,14 @@ fn 
test_encode_batch_from_gpu_ptr_f32_with_stream_success() {
             return;
         }
     };
-    let (device, input_d) = match device_and_f32_slice(&[1.0, 0.0, 0.0, 0.0, 
0.5, 0.5, 0.5, 0.5]) {
-        Some(t) => t,
-        None => {
-            println!("SKIP: No CUDA device");
-            return;
-        }
-    };
+    let (device, input_d) =
+        match common::copy_f32_to_device(&[1.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 
0.5]) {
+            Some(t) => t,
+            None => {
+                println!("SKIP: No CUDA device");
+                return;
+            }
+        };
     let stream = device.fork_default_stream().expect("fork_default_stream");
     let dlpack_ptr = unsafe {
         engine
@@ -1198,31 +959,29 @@ fn 
test_encode_batch_from_gpu_ptr_f32_with_stream_success() {
             )
             .expect("encode_batch_from_gpu_ptr_f32_with_stream")
     };
-    assert_dlpack_batch_shape_and_delete(dlpack_ptr, 2, 4);
+    unsafe { common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, 2, 4) };
 }
 
 #[test]
 fn test_encode_batch_from_gpu_ptr_f32_success_f64_engine() {
-    let engine = match QdpEngine::new_with_precision(0, 
Precision::Float64).ok() {
-        Some(e) => e,
-        None => {
-            println!("SKIP: No GPU");
-            return;
-        }
-    };
-    let (_device, input_d) = match device_and_f32_slice(&[1.0, 0.0, 0.0, 0.0, 
0.5, 0.5, 0.5, 0.5]) {
-        Some(t) => t,
-        None => {
-            println!("SKIP: No CUDA device");
-            return;
-        }
-    };
+    let Some(engine) = common::qdp_engine_with_precision(Precision::Float64) 
else {
+        println!("SKIP: No GPU");
+        return;
+    };
+    let (_device, input_d) =
+        match common::copy_f32_to_device(&[1.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 
0.5]) {
+            Some(t) => t,
+            None => {
+                println!("SKIP: No CUDA device");
+                return;
+            }
+        };
     let dlpack_ptr = unsafe {
         engine
             .encode_batch_from_gpu_ptr_f32(*input_d.device_ptr() as *const 
f32, 2, 4, 2)
             .expect("encode_batch_from_gpu_ptr_f32 (Float64 engine)")
     };
-    assert_dlpack_batch_shape_and_delete(dlpack_ptr, 2, 4);
+    unsafe { common::assert_dlpack_shape_2d_and_delete(dlpack_ptr, 2, 4) };
 }
 
 #[test]
@@ -1268,7 +1027,7 @@ fn 
test_encode_batch_from_gpu_ptr_f32_sample_size_exceeds_state_len() {
             return;
         }
     };
-    let (_device, input_d) = match device_and_f32_slice(&[1.0; 10]) {
+    let (_device, input_d) = match common::copy_f32_to_device(&[1.0; 10]) {
         Some(t) => t,
         None => {
             println!("SKIP: No CUDA device");
@@ -1299,7 +1058,7 @@ fn 
test_encode_batch_from_gpu_ptr_f32_odd_sample_size_success() {
     let num_samples = 2;
     let sample_size = 3;
     let num_qubits = 2;
-    let (_device, input_d) = match device_and_f32_slice(&[1.0, 2.0, 2.0, 2.0, 
1.0, 2.0]) {
+    let (_device, input_d) = match common::copy_f32_to_device(&[1.0, 2.0, 2.0, 
2.0, 1.0, 2.0]) {
         Some(t) => t,
         None => {
             println!("SKIP: No CUDA device");
@@ -1316,5 +1075,11 @@ fn 
test_encode_batch_from_gpu_ptr_f32_odd_sample_size_success() {
             )
             .expect("encode_batch_from_gpu_ptr_f32 odd sample size")
     };
-    assert_dlpack_batch_shape_and_delete(dlpack_ptr, num_samples as i64, (1 << 
num_qubits) as i64);
+    unsafe {
+        common::assert_dlpack_shape_2d_and_delete(
+            dlpack_ptr,
+            num_samples as i64,
+            (1 << num_qubits) as i64,
+        )
+    };
 }
diff --git a/qdp/qdp-core/tests/validation.rs 
b/qdp/qdp-core/tests/gpu_validation.rs
similarity index 90%
rename from qdp/qdp-core/tests/validation.rs
rename to qdp/qdp-core/tests/gpu_validation.rs
index 7ac25eaf2..d8779f3fc 100644
--- a/qdp/qdp-core/tests/validation.rs
+++ b/qdp/qdp-core/tests/gpu_validation.rs
@@ -16,7 +16,7 @@
 
 // Input validation and error handling tests
 
-use qdp_core::{MahoutError, QdpEngine};
+use qdp_core::MahoutError;
 
 mod common;
 
@@ -25,9 +25,8 @@ mod common;
 fn test_input_validation_invalid_strategy() {
     println!("Testing invalid strategy name rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let data = common::create_test_data(100);
@@ -52,9 +51,8 @@ fn test_input_validation_invalid_strategy() {
 fn test_input_validation_qubit_mismatch() {
     println!("Testing qubit size validation...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let data = common::create_test_data(100);
@@ -83,9 +81,8 @@ fn test_input_validation_qubit_mismatch() {
 fn test_input_validation_zero_qubits() {
     println!("Testing zero qubits rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let data = common::create_test_data(10);
@@ -110,9 +107,8 @@ fn test_input_validation_zero_qubits() {
 fn test_input_validation_max_qubits() {
     println!("Testing maximum qubit limit (30)...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let data = common::create_test_data(100);
@@ -137,9 +133,8 @@ fn test_input_validation_max_qubits() {
 fn test_input_validation_batch_zero_samples() {
     println!("Testing zero num_samples rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let batch_data = vec![1.0, 2.0, 3.0, 4.0];
@@ -163,9 +158,8 @@ fn test_input_validation_batch_zero_samples() {
 fn test_empty_data() {
     println!("Testing empty data rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let data: Vec<f64> = vec![];
@@ -187,9 +181,8 @@ fn test_empty_data() {
 fn test_zero_norm_data() {
     println!("Testing zero-norm data rejection...");
 
-    let engine = match QdpEngine::new(0) {
-        Ok(e) => e,
-        Err(_) => return,
+    let Some(engine) = common::qdp_engine() else {
+        return;
     };
 
     let data = vec![0.0; 128];

Reply via email to