use crate::error::{Error, Result};
use crate::gpu::operations::{GpuMatrix, GpuVector};
use crate::gpu::{GpuError, GpuManager};
#[cfg(cuda_available)]
use cudarc::cublas::CudaBlas;
#[cfg(cuda_available)]
use cudarc::driver::CudaFunction;
#[cfg(cuda_available)]
use cudarc::driver::{CudaContext as CudarcContext, CudaStream};
#[cfg(cuda_available)]
use std::sync::Arc;
#[cfg(cuda_available)]
pub struct PandrsGpuContext {
context: Arc<CudarcContext>,
stream: Arc<CudaStream>,
cublas: Arc<CudaBlas>,
supports_tensor_cores: bool,
}
#[cfg(cuda_available)]
impl PandrsGpuContext {
pub fn new(device_id: i32) -> Result<Self> {
let context = match CudarcContext::new(device_id as usize) {
Ok(ctx) => ctx,
Err(e) => {
return Err(Error::from(GpuError::DeviceError(format!(
"Failed to initialize CUDA context: {}",
e
))))
}
};
let stream = context.default_stream();
let cublas = match CudaBlas::new(stream.clone()) {
Ok(cublas) => Arc::new(cublas),
Err(e) => {
return Err(Error::from(GpuError::DeviceError(format!(
"Failed to initialize cuBLAS: {}",
e
))))
}
};
let supports_tensor_cores = match context.compute_capability() {
Ok((major, _minor)) => major >= 7,
Err(_) => false,
};
Ok(PandrsGpuContext {
context,
stream,
cublas,
supports_tensor_cores,
})
}
pub fn context(&self) -> Arc<CudarcContext> {
self.context.clone()
}
pub fn stream(&self) -> Arc<CudaStream> {
self.stream.clone()
}
pub fn cublas(&self) -> Arc<CudaBlas> {
self.cublas.clone()
}
pub fn supports_tensor_cores(&self) -> bool {
self.supports_tensor_cores
}
pub fn load_kernel(&self, name: &str, _ptx: &str) -> Result<CudaFunction> {
Err(Error::from(GpuError::DeviceError(format!(
"Kernel '{}' not found. PTX loading requires module loading in cudarc 0.19.x.",
name
))))
}
}
pub fn matrix_multiply(a: &GpuMatrix, b: &GpuMatrix, manager: &GpuManager) -> Result<GpuMatrix> {
if a.data.shape()[1] != b.data.shape()[0] {
return Err(Error::DimensionMismatch(format!(
"Incompatible dimensions for matrix multiplication: {:?} and {:?}",
a.data.shape(),
b.data.shape()
)));
}
#[cfg(cuda_available)]
{
let _ = manager;
return Err(Error::from(GpuError::DeviceError(
"GPU matrix multiplication not implemented for cudarc 0.19.x (no cuBLAS GEMM binding)"
.to_string(),
)));
}
#[cfg(not(cuda_available))]
{
let result_data = a.data.dot(&b.data);
Ok(GpuMatrix {
data: result_data,
on_gpu: false,
})
}
}
#[cfg(cuda_available)]
fn elementwise_op(a: &GpuMatrix, b: &GpuMatrix, op_type: &str) -> Result<GpuMatrix> {
if a.data.shape() != b.data.shape() {
return Err(Error::DimensionMismatch(format!(
"Incompatible dimensions for element-wise {}: {:?} and {:?}",
op_type,
a.data.shape(),
b.data.shape()
)));
}
Err(Error::from(GpuError::DeviceError(format!(
"GPU kernel launch not implemented for cudarc 0.19.x (element-wise {})",
op_type
))))
}
pub fn elementwise_add(a: &GpuMatrix, b: &GpuMatrix, manager: &GpuManager) -> Result<GpuMatrix> {
if a.data.shape() != b.data.shape() {
return Err(Error::DimensionMismatch(format!(
"Incompatible dimensions for element-wise addition: {:?} and {:?}",
a.data.shape(),
b.data.shape()
)));
}
#[cfg(cuda_available)]
{
let _ = manager;
return elementwise_op(a, b, "addition");
}
#[cfg(not(cuda_available))]
{
let result_data = &a.data + &b.data;
Ok(GpuMatrix {
data: result_data,
on_gpu: false,
})
}
}
pub fn elementwise_subtract(
a: &GpuMatrix,
b: &GpuMatrix,
manager: &GpuManager,
) -> Result<GpuMatrix> {
if a.data.shape() != b.data.shape() {
return Err(Error::DimensionMismatch(format!(
"Incompatible dimensions for element-wise subtraction: {:?} and {:?}",
a.data.shape(),
b.data.shape()
)));
}
#[cfg(cuda_available)]
{
let _ = manager;
return elementwise_op(a, b, "subtraction");
}
#[cfg(not(cuda_available))]
{
let result_data = &a.data - &b.data;
Ok(GpuMatrix {
data: result_data,
on_gpu: false,
})
}
}
pub fn elementwise_multiply(
a: &GpuMatrix,
b: &GpuMatrix,
manager: &GpuManager,
) -> Result<GpuMatrix> {
if a.data.shape() != b.data.shape() {
return Err(Error::DimensionMismatch(format!(
"Incompatible dimensions for element-wise multiplication: {:?} and {:?}",
a.data.shape(),
b.data.shape()
)));
}
#[cfg(cuda_available)]
{
let _ = manager;
return elementwise_op(a, b, "multiplication");
}
#[cfg(not(cuda_available))]
{
let result_data = &a.data * &b.data;
Ok(GpuMatrix {
data: result_data,
on_gpu: false,
})
}
}
pub fn elementwise_divide(a: &GpuMatrix, b: &GpuMatrix, manager: &GpuManager) -> Result<GpuMatrix> {
if a.data.shape() != b.data.shape() {
return Err(Error::DimensionMismatch(format!(
"Incompatible dimensions for element-wise division: {:?} and {:?}",
a.data.shape(),
b.data.shape()
)));
}
#[cfg(cuda_available)]
{
let _ = manager;
return elementwise_op(a, b, "division");
}
#[cfg(not(cuda_available))]
{
let result_data = &a.data / &b.data;
Ok(GpuMatrix {
data: result_data,
on_gpu: false,
})
}
}
pub fn matrix_sum(a: &GpuMatrix, manager: &GpuManager) -> Result<f64> {
#[cfg(cuda_available)]
{
let _ = (a, manager);
return Err(Error::from(GpuError::DeviceError(
"GPU sum not implemented for cudarc 0.19.x (no cuBLAS binding)".to_string(),
)));
}
#[cfg(not(cuda_available))]
{
Ok(a.data.sum())
}
}
pub fn sort_matrix_rows(_a: &GpuMatrix, _manager: &GpuManager) -> Result<GpuMatrix> {
Err(Error::NotImplemented(
"GPU matrix row sort not implemented (no real CUDA kernel)".into(),
))
}
pub fn vector_dot_product(a: &GpuVector, b: &GpuVector, manager: &GpuManager) -> Result<f64> {
if a.data.len() != b.data.len() {
return Err(Error::DimensionMismatch(format!(
"Incompatible dimensions for dot product: {} and {}",
a.data.len(),
b.data.len()
)));
}
#[cfg(cuda_available)]
{
let _ = manager;
return Err(Error::from(GpuError::DeviceError(
"GPU dot product not implemented for cudarc 0.19.x (no cuBLAS binding)".to_string(),
)));
}
#[cfg(not(cuda_available))]
{
let result = a.data.iter().zip(b.data.iter()).map(|(x, y)| x * y).sum();
Ok(result)
}
}
pub fn vector_add(a: &GpuVector, b: &GpuVector, manager: &GpuManager) -> Result<GpuVector> {
if a.data.len() != b.data.len() {
return Err(Error::DimensionMismatch(format!(
"Incompatible dimensions for vector addition: {} and {}",
a.data.len(),
b.data.len()
)));
}
#[cfg(cuda_available)]
{
let _ = manager;
return Err(Error::from(GpuError::DeviceError(
"GPU vector addition not implemented for cudarc 0.19.x (no cuBLAS binding)".to_string(),
)));
}
#[cfg(not(cuda_available))]
{
let result_data = &a.data + &b.data;
Ok(GpuVector {
data: result_data,
on_gpu: false,
})
}
}