Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
719bafb
feat(qdp): introduce batch throughput optimization scaffolding for TC
aloha1357 Jun 7, 2026
c3d0ed8
feat(qdp): introduce batch throughput optimization scaffolding for TC
aloha1357 Jun 7, 2026
cfc8493
feat(qdp): introduce shared memory fused FWT for small qubit counts
aloha1357 Jun 7, 2026
b1a32e7
feat(qdp): restructure FWT into Kronecker decomposition blocked archi…
aloha1357 Jun 7, 2026
62c249b
chore: remove PR1 agent comments, trim kernel docs, add PR4 benchmark
aloha1357 Jun 8, 2026
507661c
feat(qdp): implement Matrix-Free Implicit Hadamard Tensor Core engine
aloha1357 Jun 7, 2026
c249cc0
chore(qdp): clean up agent comments and add independent PR5 benchmark…
aloha1357 Jun 8, 2026
806a419
test(qdp): PR5 Ozaki TC benchmark, encode_batch_tc API, and GPU unit …
aloha1357 Jun 10, 2026
44b2a9b
feat(qdp): hook AdaptiveOzakiEngine for mixed-precision graded-ring T…
aloha1357 Jun 7, 2026
eccb253
PR6: Implement Tensor Core acceleration for IQP encoding with operato…
aloha1357 Jun 8, 2026
3b402ac
test(qdp): add PR6 TC benchmark, e2e encode-path option, and unit tests
aloha1357 Jun 10, 2026
543586a
fix(qdp): remove duplicate encode_batch_tc PyO3 binding after rebase
aloha1357 Jun 10, 2026
8ce65bd
feat(qdp): PR7 E2E TC integration — kernel correctness and Mahout pip…
aloha1357 Jun 10, 2026
1e7459a
fix(qdp): PR7 N>12 Kronecker correctness — transpose fallback, E2E ve…
aloha1357 Jun 11, 2026
ac78832
chore(qdp): remove dev-only PR4-6 micro-benchmarks and reports from c…
aloha1357 Jun 11, 2026
6497bbb
fix(qdp): complete TC path reviewer remediation
aloha1357 Jul 1, 2026
7b5dd74
fix(qdp): restore WSL validation and clean pre-commit issues
aloha1357 Jul 5, 2026
6b4d853
ci: replace disallowed rust-toolchain action in python testing
aloha1357 Jul 5, 2026
e86b5c5
Revert "ci: replace disallowed rust-toolchain action in python testing"
aloha1357 Jul 6, 2026
ba31b0b
ci: replace disallowed rust-toolchain action in python testing
Jul 6, 2026
7d68598
Merge upstream/main into pr2-batch-throughput-opt
aloha1357 Jul 6, 2026
dc33ea2
fix: resolve AttributeError for cuda_available fallback in testing
Jul 6, 2026
89b881c
test:
Jul 10, 2026
3ebff9e
test(qdp): add pytest.mark.gpu to test_iqp_tc_path to fix CI
aloha1357 Jul 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions .github/workflows/python-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,17 @@ jobs:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6

- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # stable
shell: bash
run: |
if ! command -v rustup >/dev/null 2>&1; then
curl https://sh.rustup.rs -sSf | sh -s -- -y --profile minimal --default-toolchain stable
else
rustup toolchain install stable --profile minimal
rustup default stable
fi
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
"$HOME/.cargo/bin/rustc" --version
"$HOME/.cargo/bin/cargo" --version

- name: Cargo check (no-CUDA stubs)
working-directory: qdp
Expand All @@ -62,7 +72,7 @@ jobs:
# GPU fidelity cases self-skip when the probe finds no functional GPU
# (stub build: kernel launch returns Err; no driver: cudarc panics and
# catch_unwind treats it as skip). Reader-level assertions still run.
# Scoped to these two binaries other gpu_*.rs tests lack probe-skip.
# Scoped to these two binaries -- other gpu_*.rs tests lack probe-skip.
- name: Cargo test (f32 Parquet CPU smoke)
working-directory: qdp
env:
Expand Down
108 changes: 108 additions & 0 deletions qdp/qdp-core/src/gpu/encodings/iqp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,114 @@ impl IqpEncoder {
num_qubits
}
}

/// Encode multiple IQP samples using Matrix-Free Implicit Hadamard Tensor Core engine
#[cfg(target_os = "linux")]
pub fn encode_batch_tc(
&self,
device: &Arc<CudaDevice>,
batch_data: &[f64],
num_samples: usize,
sample_size: usize,
num_qubits: usize,
) -> Result<GpuStateVector> {
validate_qubit_count(num_qubits)?;
let expected_len = self.expected_data_len(num_qubits);

if sample_size != expected_len {
return Err(MahoutError::InvalidInput(format!(
"Batch sample size {} does not match required parameters {}",
sample_size, expected_len
)));
}

if batch_data.len() != num_samples * sample_size {
return Err(MahoutError::InvalidInput(format!(
"Batch data length {} does not match num_samples {} * sample_size {}",
batch_data.len(),
num_samples,
sample_size
)));
}

for (i, &val) in batch_data.iter().enumerate() {
if !val.is_finite() {
let sample_idx = i / sample_size;
let param_idx = i % sample_size;
return Err(MahoutError::InvalidInput(format!(
"Sample {} parameter {} must be finite, got {}",
sample_idx, param_idx, val
)));
}
}

let state_len = 1 << num_qubits;

let batch_state_vector = {
crate::profile_scope!("GPU::AllocBatchTC");
GpuStateVector::new_batch(device, num_samples, num_qubits, Precision::Float64)?
};

let input_bytes = std::mem::size_of_val(batch_data);
let data_gpu = {
crate::profile_scope!("GPU::H2D_BatchIqpDataTC");
device.htod_sync_copy(batch_data).map_err(|e| {
map_allocation_error(input_bytes, "IQP TC batch upload", Some(num_qubits), e)
})?
};

let state_ptr = batch_state_vector.ptr_f64().ok_or_else(|| {
MahoutError::InvalidInput(
"Batch state vector precision mismatch (expected float64 buffer)".to_string(),
)
})?;

{
crate::profile_scope!("GPU::BatchKernelLaunchTC");
let ret = unsafe {
qdp_kernels::launch_iqp_encode_tc(
*data_gpu.device_ptr() as *const f64,
state_ptr as *mut c_void,
num_samples,
state_len,
num_qubits as u32,
if self.enable_zz { 1 } else { 0 },
(*device.cu_stream()) as *mut c_void,
)
};

if ret != 0 {
return Err(MahoutError::KernelLaunch(format!(
"Batch IQP TC encoding kernel failed: {} ({})",
ret,
cuda_error_to_string(ret)
)));
}
}

{
crate::profile_scope!("GPU::SynchronizeTC");
device
.synchronize()
.map_err(|e| MahoutError::Cuda(format!("Sync failed: {:?}", e)))?;
}

Ok(batch_state_vector)
}

#[cfg(not(target_os = "linux"))]
pub fn encode_batch_tc(
&self,
_device: &Arc<CudaDevice>,
_batch_data: &[f64],
_num_samples: usize,
_sample_size: usize,
_num_qubits: usize,
) -> Result<GpuStateVector> {
Err(MahoutError::Cuda(
"CUDA unavailable (non-Linux stub)".to_string(),
))
}
}

impl QuantumEncoder for IqpEncoder {
Expand Down
34 changes: 34 additions & 0 deletions qdp/qdp-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,40 @@ impl QdpEngine {
self.encode_batch_for_pipeline(batch_data, num_samples, sample_size, num_qubits, encoding)
}

/// Encode a batch of IQP samples via the Tensor Core / Kronecker FWT path.
#[cfg(target_os = "linux")]
pub fn encode_batch_tc(
&self,
batch_data: &[f64],
num_samples: usize,
sample_size: usize,
num_qubits: usize,
encoding_method: &str,
) -> Result<*mut DLManagedTensor> {
crate::profile_scope!("Mahout::EncodeBatchTC");

let encoding = Encoding::from_str_ci(encoding_method)?;
let encoder = match encoding {
Encoding::Iqp => gpu::encodings::IqpEncoder::full(),
Encoding::IqpZ => gpu::encodings::IqpEncoder::z_only(),
_ => {
return Err(MahoutError::InvalidInput(format!(
"encode_batch_tc supports iqp and iqp-z only, got {}",
encoding_method
)));
}
};
let state_vector = encoder.encode_batch_tc(
&self.device,
batch_data,
num_samples,
sample_size,
num_qubits,
)?;

Ok(state_vector.to_dlpack())
}

/// Same as [`encode_batch`](Self::encode_batch) with a resolved [`Encoding`] (no string parse).
pub(crate) fn encode_batch_for_pipeline(
&self,
Expand Down
9 changes: 6 additions & 3 deletions qdp/qdp-kernels/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
use std::env;
use std::process::Command;

const DEFAULT_CUBIN_ARCHES: &[&str] = &["75", "80", "86", "89", "90", "100", "120"];
const DEFAULT_PTX_CANDIDATES: &[&str] = &["120", "100", "90", "89", "86", "80", "75"];
const LEGACY_FALLBACK_ARCHES: &[&str] = &["75", "80", "86"];
const DEFAULT_CUBIN_ARCHES: &[&str] = &["80", "86", "89", "90", "100", "120"];
const DEFAULT_PTX_CANDIDATES: &[&str] = &["120", "100", "90", "89", "86", "80"];
const LEGACY_FALLBACK_ARCHES: &[&str] = &["80", "86"];

fn add_sm_target(build: &mut cc::Build, arch: &str) {
build.flag("-gencode");
Expand Down Expand Up @@ -164,6 +164,7 @@ fn main() {
println!("cargo:rerun-if-changed=src/angle.cu");
println!("cargo:rerun-if-changed=src/validation.cu");
println!("cargo:rerun-if-changed=src/iqp.cu");
println!("cargo:rerun-if-changed=src/iqp_tc.cu");
println!("cargo:rerun-if-changed=src/phase.cu");
println!("cargo:rerun-if-env-changed=QDP_NO_CUDA");
println!("cargo:rerun-if-env-changed=QDP_CUDA_ARCH_LIST");
Expand Down Expand Up @@ -232,6 +233,8 @@ fn main() {
.file("src/angle.cu")
.file("src/validation.cu")
.file("src/iqp.cu")
.file("src/iqp_tc.cu")
.file("src/ImplicitHadamardOzaki.cu")
.file("src/phase.cu")
.compile("kernels");
}
Loading