From e06f6ec19ea8adefb2c02cbd834140fe037192ac Mon Sep 17 00:00:00 2001 From: Chia-Hsuan Hsu Date: Mon, 18 May 2026 20:52:15 +0800 Subject: [PATCH 1/4] Add files via upload --- qdp/qdp-kernels/tests/basis_encode.rs | 428 ++++++++++++++++++++++++++ 1 file changed, 428 insertions(+) create mode 100644 qdp/qdp-kernels/tests/basis_encode.rs diff --git a/qdp/qdp-kernels/tests/basis_encode.rs b/qdp/qdp-kernels/tests/basis_encode.rs new file mode 100644 index 0000000000..a0525843ef --- /dev/null +++ b/qdp/qdp-kernels/tests/basis_encode.rs @@ -0,0 +1,428 @@ +// +// 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. + +// Tests for basis encoding CUDA kernels. + +#![allow(unused_unsafe)] + +#[cfg(target_os = "linux")] +use cudarc::driver::{CudaDevice, DevicePtr, DevicePtrMut}; +#[cfg(target_os = "linux")] +use qdp_kernels::{ + CuComplex, CuDoubleComplex, + launch_basis_encode, launch_basis_encode_batch, + launch_basis_encode_f32, launch_basis_encode_batch_f32, +}; + +const EPSILON: f64 = 1e-10; +const EPSILON_F32: f32 = 1e-6; + +#[test] +#[cfg(target_os = "linux")] +fn test_basis_encode_first_index() { + let device = match CudaDevice::new(0) { + Ok(d) => d, + Err(_) => { + println!("SKIP: No CUDA device available"); + return; + } + }; + + let state_len = 4usize; + let mut state_d = device.alloc_zeros::(state_len).unwrap(); + + let result = unsafe { + launch_basis_encode( + 0, + *state_d.device_ptr_mut() as *mut std::ffi::c_void, + state_len, + std::ptr::null_mut(), + ) + }; + assert_eq!(result, 0, "Kernel launch should succeed"); + + let state_h = device.dtoh_sync_copy(&state_d).unwrap(); + assert!((state_h[0].x - 1.0).abs() < EPSILON, "state[0].re should be 1.0"); + assert!(state_h[0].y.abs() < EPSILON, "state[0].im should be 0"); + for i in 1..state_len { + assert!(state_h[i].x.abs() < EPSILON, "state[{i}].re should be 0"); + assert!(state_h[i].y.abs() < EPSILON, "state[{i}].im should be 0"); + } +} + +#[test] +#[cfg(target_os = "linux")] +fn test_basis_encode_last_index() { + let device = match CudaDevice::new(0) { + Ok(d) => d, + Err(_) => { + println!("SKIP: No CUDA device available"); + return; + } + }; + + let state_len = 8usize; + let basis_index = state_len - 1; + let mut state_d = device.alloc_zeros::(state_len).unwrap(); + + let result = unsafe { + launch_basis_encode( + basis_index, + *state_d.device_ptr_mut() as *mut std::ffi::c_void, + state_len, + std::ptr::null_mut(), + ) + }; + assert_eq!(result, 0); + + let state_h = device.dtoh_sync_copy(&state_d).unwrap(); + for i in 0..state_len { + let expected = if i == basis_index { 1.0 } else { 0.0 }; + assert!((state_h[i].x - expected).abs() < EPSILON, "state[{i}].re mismatch"); + assert!(state_h[i].y.abs() < EPSILON, "state[{i}].im should be 0"); + } +} + +#[test] +#[cfg(target_os = "linux")] +fn test_basis_encode_middle_index() { + let device = match CudaDevice::new(0) { + Ok(d) => d, + Err(_) => { + println!("SKIP: No CUDA device available"); + return; + } + }; + + let state_len = 8usize; + let basis_index = 3usize; + let mut state_d = device.alloc_zeros::(state_len).unwrap(); + + let result = unsafe { + launch_basis_encode( + basis_index, + *state_d.device_ptr_mut() as *mut std::ffi::c_void, + state_len, + std::ptr::null_mut(), + ) + }; + assert_eq!(result, 0); + + let state_h = device.dtoh_sync_copy(&state_d).unwrap(); + assert!((state_h[3].x - 1.0).abs() < EPSILON, "state[3].re should be 1.0"); + for i in (0..state_len).filter(|&j| j != 3) { + assert!(state_h[i].x.abs() < EPSILON, "state[{i}].re should be 0"); + } +} + +#[test] +#[cfg(target_os = "linux")] +fn test_basis_encode_rejects_out_of_range_index() { + let device = match CudaDevice::new(0) { + Ok(d) => d, + Err(_) => { + println!("SKIP: No CUDA device available"); + return; + } + }; + + let state_len = 4usize; + let mut state_d = device.alloc_zeros::(state_len).unwrap(); + + let result = unsafe { + launch_basis_encode( + state_len, + *state_d.device_ptr_mut() as *mut std::ffi::c_void, + state_len, + std::ptr::null_mut(), + ) + }; + assert_ne!(result, 0, "Out-of-range index should be rejected"); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_basis_encode_rejects_zero_state_len() { + let device = match CudaDevice::new(0) { + Ok(d) => d, + Err(_) => { + println!("SKIP: No CUDA device available"); + return; + } + }; + + let mut state_d = device.alloc_zeros::(1).unwrap(); + let result = unsafe { + launch_basis_encode( + 0, + *state_d.device_ptr_mut() as *mut std::ffi::c_void, + 0, + std::ptr::null_mut(), + ) + }; + assert_ne!(result, 0, "Zero state_len should be rejected"); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_basis_encode_f32_basic() { + let device = match CudaDevice::new(0) { + Ok(d) => d, + Err(_) => { + println!("SKIP: No CUDA device available"); + return; + } + }; + + let state_len = 4usize; + let basis_index = 2usize; + let mut state_d = device.alloc_zeros::(state_len).unwrap(); + + let result = unsafe { + launch_basis_encode_f32( + basis_index, + *state_d.device_ptr_mut() as *mut std::ffi::c_void, + state_len, + std::ptr::null_mut(), + ) + }; + assert_eq!(result, 0); + + let state_h = device.dtoh_sync_copy(&state_d).unwrap(); + assert!((state_h[2].x - 1.0f32).abs() < EPSILON_F32, "state[2].re should be 1.0"); + assert!(state_h[2].y.abs() < EPSILON_F32, "state[2].im should be 0"); + for i in (0..state_len).filter(|&j| j != 2) { + assert!(state_h[i].x.abs() < EPSILON_F32, "state[{i}].re should be 0"); + } +} + +#[test] +#[cfg(target_os = "linux")] +fn test_basis_encode_f32_rejects_out_of_range() { + let device = match CudaDevice::new(0) { + Ok(d) => d, + Err(_) => { + println!("SKIP: No CUDA device available"); + return; + } + }; + + let state_len = 4usize; + let mut state_d = device.alloc_zeros::(state_len).unwrap(); + + let result = unsafe { + launch_basis_encode_f32( + state_len, + *state_d.device_ptr_mut() as *mut std::ffi::c_void, + state_len, + std::ptr::null_mut(), + ) + }; + assert_ne!(result, 0, "Out-of-range index (f32) should be rejected"); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_basis_encode_batch_basic() { + let device = match CudaDevice::new(0) { + Ok(d) => d, + Err(_) => { + println!("SKIP: No CUDA device available"); + return; + } + }; + + let num_samples = 3usize; + let state_len = 8usize; + let num_qubits = 3u32; + let basis_indices: Vec = vec![0, 3, 1]; + + let indices_d = device.htod_copy(basis_indices.clone()).unwrap(); + let mut state_d = device + .alloc_zeros::(num_samples * state_len) + .unwrap(); + + let result = unsafe { + launch_basis_encode_batch( + *indices_d.device_ptr() as *const usize, + *state_d.device_ptr_mut() as *mut std::ffi::c_void, + num_samples, + state_len, + num_qubits, + std::ptr::null_mut(), + ) + }; + assert_eq!(result, 0, "Batch basis encode should succeed"); + + let state_h = device.dtoh_sync_copy(&state_d).unwrap(); + for (sample_idx, &basis_idx) in basis_indices.iter().enumerate() { + for elem_idx in 0..state_len { + let expected = if elem_idx == basis_idx { 1.0 } else { 0.0 }; + let actual = state_h[sample_idx * state_len + elem_idx]; + assert!( + (actual.x - expected).abs() < EPSILON, + "sample {sample_idx} element {elem_idx}: expected {expected}, got {}", + actual.x + ); + assert!( + actual.y.abs() < EPSILON, + "sample {sample_idx} element {elem_idx}: imaginary should be 0" + ); + } + } +} + +#[test] +#[cfg(target_os = "linux")] +fn test_basis_encode_batch_rejects_zero_samples() { + let device = match CudaDevice::new(0) { + Ok(d) => d, + Err(_) => { + println!("SKIP: No CUDA device available"); + return; + } + }; + + let indices_d = device.alloc_zeros::(1).unwrap(); + let mut state_d = device.alloc_zeros::(4).unwrap(); + + let result = unsafe { + launch_basis_encode_batch( + *indices_d.device_ptr() as *const usize, + *state_d.device_ptr_mut() as *mut std::ffi::c_void, + 0, + 4, + 2, + std::ptr::null_mut(), + ) + }; + assert_ne!(result, 0, "Zero num_samples should be rejected"); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_basis_encode_batch_rejects_zero_state_len() { + let device = match CudaDevice::new(0) { + Ok(d) => d, + Err(_) => { + println!("SKIP: No CUDA device available"); + return; + } + }; + + let indices_d = device.alloc_zeros::(1).unwrap(); + let mut state_d = device.alloc_zeros::(1).unwrap(); + + let result = unsafe { + launch_basis_encode_batch( + *indices_d.device_ptr() as *const usize, + *state_d.device_ptr_mut() as *mut std::ffi::c_void, + 1, + 0, + 0, + std::ptr::null_mut(), + ) + }; + assert_ne!(result, 0, "Zero state_len should be rejected"); +} + +#[test] +#[cfg(target_os = "linux")] +fn test_basis_encode_batch_f32_basic() { + let device = match CudaDevice::new(0) { + Ok(d) => d, + Err(_) => { + println!("SKIP: No CUDA device available"); + return; + } + }; + + let num_samples = 4usize; + let state_len = 4usize; + let num_qubits = 2u32; + let basis_indices: Vec = vec![0, 1, 2, 3]; + + let indices_d = device.htod_copy(basis_indices.clone()).unwrap(); + let mut state_d = device + .alloc_zeros::(num_samples * state_len) + .unwrap(); + + let result = unsafe { + launch_basis_encode_batch_f32( + *indices_d.device_ptr() as *const usize, + *state_d.device_ptr_mut() as *mut std::ffi::c_void, + num_samples, + state_len, + num_qubits, + std::ptr::null_mut(), + ) + }; + assert_eq!(result, 0, "Batch f32 basis encode should succeed"); + + let state_h = device.dtoh_sync_copy(&state_d).unwrap(); + for (sample_idx, &basis_idx) in basis_indices.iter().enumerate() { + for elem_idx in 0..state_len { + let expected = if elem_idx == basis_idx { 1.0f32 } else { 0.0f32 }; + let actual = state_h[sample_idx * state_len + elem_idx]; + assert!( + (actual.x - expected).abs() < EPSILON_F32, + "sample {sample_idx} element {elem_idx}: expected {expected}, got {}", + actual.x + ); + } + } +} + +#[test] +#[cfg(target_os = "linux")] +fn test_basis_encode_batch_f32_rejects_zero_samples() { + let device = match CudaDevice::new(0) { + Ok(d) => d, + Err(_) => { + println!("SKIP: No CUDA device available"); + return; + } + }; + + let indices_d = device.alloc_zeros::(1).unwrap(); + let mut state_d = device.alloc_zeros::(4).unwrap(); + + let result = unsafe { + launch_basis_encode_batch_f32( + *indices_d.device_ptr() as *const usize, + *state_d.device_ptr_mut() as *mut std::ffi::c_void, + 0, + 4, + 2, + std::ptr::null_mut(), + ) + }; + assert_ne!(result, 0, "Zero num_samples (f32 batch) should be rejected"); +} + +#[cfg(not(target_os = "linux"))] +#[test] +fn test_basis_encode_dummy_non_linux() { + let result = unsafe { + qdp_kernels::launch_basis_encode( + 0, + std::ptr::null_mut(), + 0, + std::ptr::null_mut(), + ) + }; + assert_eq!(result, 999, "Non-Linux stub should return 999"); +} \ No newline at end of file From 79684db618eaf090b2235086dc9e78bf1a1f073b Mon Sep 17 00:00:00 2001 From: Chia-Hsuan Hsu Date: Mon, 18 May 2026 22:05:33 +0800 Subject: [PATCH 2/4] Update basis_encode.rs --- qdp/qdp-kernels/tests/basis_encode.rs | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/qdp/qdp-kernels/tests/basis_encode.rs b/qdp/qdp-kernels/tests/basis_encode.rs index a0525843ef..3a4ce92fca 100644 --- a/qdp/qdp-kernels/tests/basis_encode.rs +++ b/qdp/qdp-kernels/tests/basis_encode.rs @@ -57,9 +57,9 @@ fn test_basis_encode_first_index() { let state_h = device.dtoh_sync_copy(&state_d).unwrap(); assert!((state_h[0].x - 1.0).abs() < EPSILON, "state[0].re should be 1.0"); assert!(state_h[0].y.abs() < EPSILON, "state[0].im should be 0"); - for i in 1..state_len { - assert!(state_h[i].x.abs() < EPSILON, "state[{i}].re should be 0"); - assert!(state_h[i].y.abs() < EPSILON, "state[{i}].im should be 0"); + for (i, item) in state_h.iter().enumerate().skip(1) { + assert!(item.x.abs() < EPSILON, "state[{i}].re should be 0"); + assert!(item.y.abs() < EPSILON, "state[{i}].im should be 0"); } } @@ -89,10 +89,10 @@ fn test_basis_encode_last_index() { assert_eq!(result, 0); let state_h = device.dtoh_sync_copy(&state_d).unwrap(); - for i in 0..state_len { + for (i, item) in state_h.iter().enumerate() { let expected = if i == basis_index { 1.0 } else { 0.0 }; - assert!((state_h[i].x - expected).abs() < EPSILON, "state[{i}].re mismatch"); - assert!(state_h[i].y.abs() < EPSILON, "state[{i}].im should be 0"); + assert!((item.x - expected).abs() < EPSILON, "state[{i}].re mismatch"); + assert!(item.y.abs() < EPSILON, "state[{i}].im should be 0"); } } @@ -123,8 +123,8 @@ fn test_basis_encode_middle_index() { let state_h = device.dtoh_sync_copy(&state_d).unwrap(); assert!((state_h[3].x - 1.0).abs() < EPSILON, "state[3].re should be 1.0"); - for i in (0..state_len).filter(|&j| j != 3) { - assert!(state_h[i].x.abs() < EPSILON, "state[{i}].re should be 0"); + for (i, item) in state_h.iter().enumerate().filter(|&(j, _)| j != 3) { + assert!(item.x.abs() < EPSILON, "state[{i}].re should be 0"); } } @@ -204,8 +204,8 @@ fn test_basis_encode_f32_basic() { let state_h = device.dtoh_sync_copy(&state_d).unwrap(); assert!((state_h[2].x - 1.0f32).abs() < EPSILON_F32, "state[2].re should be 1.0"); assert!(state_h[2].y.abs() < EPSILON_F32, "state[2].im should be 0"); - for i in (0..state_len).filter(|&j| j != 2) { - assert!(state_h[i].x.abs() < EPSILON_F32, "state[{i}].re should be 0"); + for (i, item) in state_h.iter().enumerate().filter(|&(j, _)| j != 2) { + assert!(item.x.abs() < EPSILON_F32, "state[{i}].re should be 0"); } } @@ -296,7 +296,7 @@ fn test_basis_encode_batch_rejects_zero_samples() { } }; - let indices_d = device.alloc_zeros::(1).unwrap(); + let indices_d = device.htod_copy(vec![0usize]).unwrap(); let mut state_d = device.alloc_zeros::(4).unwrap(); let result = unsafe { @@ -323,7 +323,7 @@ fn test_basis_encode_batch_rejects_zero_state_len() { } }; - let indices_d = device.alloc_zeros::(1).unwrap(); + let indices_d = device.htod_copy(vec![0usize]).unwrap(); let mut state_d = device.alloc_zeros::(1).unwrap(); let result = unsafe { @@ -397,7 +397,7 @@ fn test_basis_encode_batch_f32_rejects_zero_samples() { } }; - let indices_d = device.alloc_zeros::(1).unwrap(); + let indices_d = device.htod_copy(vec![0usize]).unwrap(); let mut state_d = device.alloc_zeros::(4).unwrap(); let result = unsafe { @@ -425,4 +425,4 @@ fn test_basis_encode_dummy_non_linux() { ) }; assert_eq!(result, 999, "Non-Linux stub should return 999"); -} \ No newline at end of file +} From f4f80475600bd812f2084774343d70f4d1a67ae3 Mon Sep 17 00:00:00 2001 From: Chia-Hsuan Hsu Date: Mon, 18 May 2026 23:25:39 +0800 Subject: [PATCH 3/4] Update basis_encode.rs --- qdp/qdp-kernels/tests/basis_encode.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/qdp/qdp-kernels/tests/basis_encode.rs b/qdp/qdp-kernels/tests/basis_encode.rs index 3a4ce92fca..6ef2eb548c 100644 --- a/qdp/qdp-kernels/tests/basis_encode.rs +++ b/qdp/qdp-kernels/tests/basis_encode.rs @@ -350,10 +350,10 @@ fn test_basis_encode_batch_f32_basic() { } }; - let num_samples = 4usize; - let state_len = 4usize; - let num_qubits = 2u32; - let basis_indices: Vec = vec![0, 1, 2, 3]; + let num_samples = 5usize; + let state_len = 8usize; + let num_qubits = 3u32; + let basis_indices: Vec = vec![0, 7, 3, 7, 0]; let indices_d = device.htod_copy(basis_indices.clone()).unwrap(); let mut state_d = device From b6b7a97753864d58d30ab776e47f3e2a886139b8 Mon Sep 17 00:00:00 2001 From: Chia-Hsuan Hsu Date: Tue, 19 May 2026 12:33:35 +0800 Subject: [PATCH 4/4] Update basis_encode.rs --- qdp/qdp-kernels/tests/basis_encode.rs | 38 +++++++++++++++++---------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/qdp/qdp-kernels/tests/basis_encode.rs b/qdp/qdp-kernels/tests/basis_encode.rs index 6ef2eb548c..9b1b166647 100644 --- a/qdp/qdp-kernels/tests/basis_encode.rs +++ b/qdp/qdp-kernels/tests/basis_encode.rs @@ -22,9 +22,8 @@ use cudarc::driver::{CudaDevice, DevicePtr, DevicePtrMut}; #[cfg(target_os = "linux")] use qdp_kernels::{ - CuComplex, CuDoubleComplex, - launch_basis_encode, launch_basis_encode_batch, - launch_basis_encode_f32, launch_basis_encode_batch_f32, + CuComplex, CuDoubleComplex, launch_basis_encode, launch_basis_encode_batch, + launch_basis_encode_batch_f32, launch_basis_encode_f32, }; const EPSILON: f64 = 1e-10; @@ -55,7 +54,10 @@ fn test_basis_encode_first_index() { assert_eq!(result, 0, "Kernel launch should succeed"); let state_h = device.dtoh_sync_copy(&state_d).unwrap(); - assert!((state_h[0].x - 1.0).abs() < EPSILON, "state[0].re should be 1.0"); + assert!( + (state_h[0].x - 1.0).abs() < EPSILON, + "state[0].re should be 1.0" + ); assert!(state_h[0].y.abs() < EPSILON, "state[0].im should be 0"); for (i, item) in state_h.iter().enumerate().skip(1) { assert!(item.x.abs() < EPSILON, "state[{i}].re should be 0"); @@ -91,7 +93,10 @@ fn test_basis_encode_last_index() { let state_h = device.dtoh_sync_copy(&state_d).unwrap(); for (i, item) in state_h.iter().enumerate() { let expected = if i == basis_index { 1.0 } else { 0.0 }; - assert!((item.x - expected).abs() < EPSILON, "state[{i}].re mismatch"); + assert!( + (item.x - expected).abs() < EPSILON, + "state[{i}].re mismatch" + ); assert!(item.y.abs() < EPSILON, "state[{i}].im should be 0"); } } @@ -122,7 +127,10 @@ fn test_basis_encode_middle_index() { assert_eq!(result, 0); let state_h = device.dtoh_sync_copy(&state_d).unwrap(); - assert!((state_h[3].x - 1.0).abs() < EPSILON, "state[3].re should be 1.0"); + assert!( + (state_h[3].x - 1.0).abs() < EPSILON, + "state[3].re should be 1.0" + ); for (i, item) in state_h.iter().enumerate().filter(|&(j, _)| j != 3) { assert!(item.x.abs() < EPSILON, "state[{i}].re should be 0"); } @@ -202,7 +210,10 @@ fn test_basis_encode_f32_basic() { assert_eq!(result, 0); let state_h = device.dtoh_sync_copy(&state_d).unwrap(); - assert!((state_h[2].x - 1.0f32).abs() < EPSILON_F32, "state[2].re should be 1.0"); + assert!( + (state_h[2].x - 1.0f32).abs() < EPSILON_F32, + "state[2].re should be 1.0" + ); assert!(state_h[2].y.abs() < EPSILON_F32, "state[2].im should be 0"); for (i, item) in state_h.iter().enumerate().filter(|&(j, _)| j != 2) { assert!(item.x.abs() < EPSILON_F32, "state[{i}].re should be 0"); @@ -375,7 +386,11 @@ fn test_basis_encode_batch_f32_basic() { let state_h = device.dtoh_sync_copy(&state_d).unwrap(); for (sample_idx, &basis_idx) in basis_indices.iter().enumerate() { for elem_idx in 0..state_len { - let expected = if elem_idx == basis_idx { 1.0f32 } else { 0.0f32 }; + let expected = if elem_idx == basis_idx { + 1.0f32 + } else { + 0.0f32 + }; let actual = state_h[sample_idx * state_len + elem_idx]; assert!( (actual.x - expected).abs() < EPSILON_F32, @@ -417,12 +432,7 @@ fn test_basis_encode_batch_f32_rejects_zero_samples() { #[test] fn test_basis_encode_dummy_non_linux() { let result = unsafe { - qdp_kernels::launch_basis_encode( - 0, - std::ptr::null_mut(), - 0, - std::ptr::null_mut(), - ) + qdp_kernels::launch_basis_encode(0, std::ptr::null_mut(), 0, std::ptr::null_mut()) }; assert_eq!(result, 999, "Non-Linux stub should return 999"); }