use crate::{
DType, Error, Matrix, OpAttribute, Result,
ml::autograd,
runtime::{BufferBinding, ComputeDispatch, KernelId, PushConstant, SemanticDispatch},
};
pub fn gelu(input: &Matrix) -> Result<Matrix> {
let contract = crate::core::operation::ml::GELU;
let operation = contract.name();
let engine = input.engine_handle();
if input.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
input.dtype().token()
)));
}
let element_count = u32::try_from(input.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let output = Matrix::allocate(
engine,
input.shape().to_vec(),
input.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(input.storage()),
BufferBinding::write(output.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlGeluF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlGeluF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[input],
outputs: &[&output],
attributes: &[],
},
)?;
}
autograd::record_gelu(input, &output)?;
Ok(output)
}
pub(in crate::ml) fn gelu_backward(
saved_input: &Matrix,
output_gradient: &Matrix,
) -> Result<Matrix> {
let contract = crate::core::operation::ml::GELU_BACKWARD;
let operation = contract.name();
let engine = saved_input.engine_handle();
for matrix in &[saved_input, output_gradient] {
if matrix.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
matrix.dtype().token()
)));
}
if !engine.same_as(matrix.engine_handle()) {
return Err(Error::invalid_argument(format!(
"{operation} inputs must belong to the same engine"
)));
}
}
if saved_input.shape() != output_gradient.shape() {
return Err(Error::invalid_argument(format!(
"{operation} requires matching shapes"
)));
}
let element_count = u32::try_from(saved_input.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let gradient = Matrix::allocate(
engine,
saved_input.shape().to_vec(),
saved_input.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(saved_input.storage()),
BufferBinding::read(output_gradient.storage()),
BufferBinding::write(gradient.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlGeluBackwardF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlGeluBackwardF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[saved_input, output_gradient],
outputs: &[&gradient],
attributes: &[],
},
)?;
}
Ok(gradient)
}
pub fn silu(input: &Matrix) -> Result<Matrix> {
let contract = crate::core::operation::ml::SILU;
let operation = contract.name();
let engine = input.engine_handle();
if input.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
input.dtype().token()
)));
}
let element_count = u32::try_from(input.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let output = Matrix::allocate(
engine,
input.shape().to_vec(),
input.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(input.storage()),
BufferBinding::write(output.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlSiluF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlSiluF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[input],
outputs: &[&output],
attributes: &[],
},
)?;
}
autograd::record_silu(input, &output)?;
Ok(output)
}
pub(in crate::ml) fn silu_backward(
saved_input: &Matrix,
output_gradient: &Matrix,
) -> Result<Matrix> {
let contract = crate::core::operation::ml::SILU_BACKWARD;
let operation = contract.name();
let engine = saved_input.engine_handle();
for matrix in &[saved_input, output_gradient] {
if matrix.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
matrix.dtype().token()
)));
}
if !engine.same_as(matrix.engine_handle()) {
return Err(Error::invalid_argument(format!(
"{operation} inputs must belong to the same engine"
)));
}
}
if saved_input.shape() != output_gradient.shape() {
return Err(Error::invalid_argument(format!(
"{operation} requires matching shapes"
)));
}
let element_count = u32::try_from(saved_input.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let gradient = Matrix::allocate(
engine,
saved_input.shape().to_vec(),
saved_input.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(saved_input.storage()),
BufferBinding::read(output_gradient.storage()),
BufferBinding::write(gradient.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlSiluBackwardF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlSiluBackwardF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[saved_input, output_gradient],
outputs: &[&gradient],
attributes: &[],
},
)?;
}
Ok(gradient)
}
pub fn relu(input: &Matrix) -> Result<Matrix> {
let contract = crate::core::operation::ml::RELU;
let operation = contract.name();
let engine = input.engine_handle();
if input.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
input.dtype().token()
)));
}
let element_count = u32::try_from(input.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let output = Matrix::allocate(
engine,
input.shape().to_vec(),
input.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(input.storage()),
BufferBinding::write(output.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlReluF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlReluF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[input],
outputs: &[&output],
attributes: &[],
},
)?;
}
autograd::record_relu(input, &output, &output)?;
Ok(output)
}
pub(in crate::ml) fn relu_backward(
saved_output: &Matrix,
output_gradient: &Matrix,
) -> Result<Matrix> {
let contract = crate::core::operation::ml::RELU_BACKWARD;
let operation = contract.name();
let engine = saved_output.engine_handle();
for matrix in &[saved_output, output_gradient] {
if matrix.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
matrix.dtype().token()
)));
}
if !engine.same_as(matrix.engine_handle()) {
return Err(Error::invalid_argument(format!(
"{operation} inputs must belong to the same engine"
)));
}
}
if saved_output.shape() != output_gradient.shape() {
return Err(Error::invalid_argument(format!(
"{operation} requires matching shapes"
)));
}
let element_count = u32::try_from(saved_output.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let gradient = Matrix::allocate(
engine,
saved_output.shape().to_vec(),
saved_output.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(saved_output.storage()),
BufferBinding::read(output_gradient.storage()),
BufferBinding::write(gradient.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlReluBackwardF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlReluBackwardF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[saved_output, output_gradient],
outputs: &[&gradient],
attributes: &[],
},
)?;
}
Ok(gradient)
}
pub fn tanh(input: &Matrix) -> Result<Matrix> {
let contract = crate::core::operation::ml::TANH;
let operation = contract.name();
let engine = input.engine_handle();
if input.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
input.dtype().token()
)));
}
let element_count = u32::try_from(input.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let output = Matrix::allocate(
engine,
input.shape().to_vec(),
input.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(input.storage()),
BufferBinding::write(output.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlTanhF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlTanhF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[input],
outputs: &[&output],
attributes: &[],
},
)?;
}
autograd::record_tanh(input, &output, &output)?;
Ok(output)
}
pub(in crate::ml) fn tanh_backward(
saved_output: &Matrix,
output_gradient: &Matrix,
) -> Result<Matrix> {
let contract = crate::core::operation::ml::TANH_BACKWARD;
let operation = contract.name();
let engine = saved_output.engine_handle();
for matrix in &[saved_output, output_gradient] {
if matrix.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
matrix.dtype().token()
)));
}
if !engine.same_as(matrix.engine_handle()) {
return Err(Error::invalid_argument(format!(
"{operation} inputs must belong to the same engine"
)));
}
}
if saved_output.shape() != output_gradient.shape() {
return Err(Error::invalid_argument(format!(
"{operation} requires matching shapes"
)));
}
let element_count = u32::try_from(saved_output.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let gradient = Matrix::allocate(
engine,
saved_output.shape().to_vec(),
saved_output.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(saved_output.storage()),
BufferBinding::read(output_gradient.storage()),
BufferBinding::write(gradient.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlTanhBackwardF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlTanhBackwardF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[saved_output, output_gradient],
outputs: &[&gradient],
attributes: &[],
},
)?;
}
Ok(gradient)
}
pub fn sigmoid(input: &Matrix) -> Result<Matrix> {
let contract = crate::core::operation::ml::SIGMOID;
let operation = contract.name();
let engine = input.engine_handle();
if input.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
input.dtype().token()
)));
}
let element_count = u32::try_from(input.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let output = Matrix::allocate(
engine,
input.shape().to_vec(),
input.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(input.storage()),
BufferBinding::write(output.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlSigmoidF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlSigmoidF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[input],
outputs: &[&output],
attributes: &[],
},
)?;
}
autograd::record_sigmoid(input, &output, &output)?;
Ok(output)
}
pub(in crate::ml) fn sigmoid_backward(
saved_output: &Matrix,
output_gradient: &Matrix,
) -> Result<Matrix> {
let contract = crate::core::operation::ml::SIGMOID_BACKWARD;
let operation = contract.name();
let engine = saved_output.engine_handle();
for matrix in &[saved_output, output_gradient] {
if matrix.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
matrix.dtype().token()
)));
}
if !engine.same_as(matrix.engine_handle()) {
return Err(Error::invalid_argument(format!(
"{operation} inputs must belong to the same engine"
)));
}
}
if saved_output.shape() != output_gradient.shape() {
return Err(Error::invalid_argument(format!(
"{operation} requires matching shapes"
)));
}
let element_count = u32::try_from(saved_output.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let gradient = Matrix::allocate(
engine,
saved_output.shape().to_vec(),
saved_output.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(saved_output.storage()),
BufferBinding::read(output_gradient.storage()),
BufferBinding::write(gradient.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlSigmoidBackwardF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlSigmoidBackwardF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[saved_output, output_gradient],
outputs: &[&gradient],
attributes: &[],
},
)?;
}
Ok(gradient)
}
pub fn leaky_relu(input: &Matrix, alpha: f32) -> Result<Matrix> {
let contract = crate::core::operation::ml::LEAKY_RELU;
let operation = contract.name();
let engine = input.engine_handle();
if input.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
input.dtype().token()
)));
}
let element_count = u32::try_from(input.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let output = Matrix::allocate(
engine,
input.shape().to_vec(),
input.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(input.storage()),
BufferBinding::write(output.storage()),
];
let attributes = [OpAttribute::Float {
name: "alpha".into(),
value: f64::from(alpha),
}];
let push_constants = [PushConstant::U32(element_count), PushConstant::F32(alpha)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlLeakyReluF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlLeakyReluF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[input],
outputs: &[&output],
attributes: &attributes,
},
)?;
}
autograd::record_leaky_relu(input, alpha, &output)?;
Ok(output)
}
pub(in crate::ml) fn leaky_relu_backward(
saved_input: &Matrix,
output_gradient: &Matrix,
alpha: f32,
) -> Result<Matrix> {
let contract = crate::core::operation::ml::LEAKY_RELU_BACKWARD;
let operation = contract.name();
let engine = saved_input.engine_handle();
for matrix in &[saved_input, output_gradient] {
if matrix.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
matrix.dtype().token()
)));
}
if !engine.same_as(matrix.engine_handle()) {
return Err(Error::invalid_argument(format!(
"{operation} inputs must belong to the same engine"
)));
}
}
if saved_input.shape() != output_gradient.shape() {
return Err(Error::invalid_argument(format!(
"{operation} requires matching shapes"
)));
}
let element_count = u32::try_from(saved_input.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let gradient = Matrix::allocate(
engine,
saved_input.shape().to_vec(),
saved_input.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(saved_input.storage()),
BufferBinding::read(output_gradient.storage()),
BufferBinding::write(gradient.storage()),
];
let attributes = [OpAttribute::Float {
name: "alpha".into(),
value: f64::from(alpha),
}];
let push_constants = [PushConstant::U32(element_count), PushConstant::F32(alpha)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlLeakyReluBackwardF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlLeakyReluBackwardF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[saved_input, output_gradient],
outputs: &[&gradient],
attributes: &attributes,
},
)?;
}
Ok(gradient)
}
pub fn elu(input: &Matrix, alpha: f32) -> Result<Matrix> {
let contract = crate::core::operation::ml::ELU;
let operation = contract.name();
let engine = input.engine_handle();
if input.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
input.dtype().token()
)));
}
let element_count = u32::try_from(input.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let output = Matrix::allocate(
engine,
input.shape().to_vec(),
input.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(input.storage()),
BufferBinding::write(output.storage()),
];
let attributes = [OpAttribute::Float {
name: "alpha".into(),
value: f64::from(alpha),
}];
let push_constants = [PushConstant::U32(element_count), PushConstant::F32(alpha)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlEluF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlEluF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[input],
outputs: &[&output],
attributes: &attributes,
},
)?;
}
autograd::record_elu(input, &output, alpha, &output)?;
Ok(output)
}
pub(in crate::ml) fn elu_backward(
saved_output: &Matrix,
output_gradient: &Matrix,
alpha: f32,
) -> Result<Matrix> {
let contract = crate::core::operation::ml::ELU_BACKWARD;
let operation = contract.name();
let engine = saved_output.engine_handle();
for matrix in &[saved_output, output_gradient] {
if matrix.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
matrix.dtype().token()
)));
}
if !engine.same_as(matrix.engine_handle()) {
return Err(Error::invalid_argument(format!(
"{operation} inputs must belong to the same engine"
)));
}
}
if saved_output.shape() != output_gradient.shape() {
return Err(Error::invalid_argument(format!(
"{operation} requires matching shapes"
)));
}
let element_count = u32::try_from(saved_output.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let gradient = Matrix::allocate(
engine,
saved_output.shape().to_vec(),
saved_output.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(saved_output.storage()),
BufferBinding::read(output_gradient.storage()),
BufferBinding::write(gradient.storage()),
];
let attributes = [OpAttribute::Float {
name: "alpha".into(),
value: f64::from(alpha),
}];
let push_constants = [PushConstant::U32(element_count), PushConstant::F32(alpha)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlEluBackwardF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlEluBackwardF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[saved_output, output_gradient],
outputs: &[&gradient],
attributes: &attributes,
},
)?;
}
Ok(gradient)
}
pub fn mish(input: &Matrix) -> Result<Matrix> {
let contract = crate::core::operation::ml::MISH;
let operation = contract.name();
let engine = input.engine_handle();
if input.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
input.dtype().token()
)));
}
let element_count = u32::try_from(input.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let output = Matrix::allocate(
engine,
input.shape().to_vec(),
input.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(input.storage()),
BufferBinding::write(output.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlMishF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlMishF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[input],
outputs: &[&output],
attributes: &[],
},
)?;
}
autograd::record_mish(input, &output)?;
Ok(output)
}
pub(in crate::ml) fn mish_backward(
saved_input: &Matrix,
output_gradient: &Matrix,
) -> Result<Matrix> {
let contract = crate::core::operation::ml::MISH_BACKWARD;
let operation = contract.name();
let engine = saved_input.engine_handle();
for matrix in &[saved_input, output_gradient] {
if matrix.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
matrix.dtype().token()
)));
}
if !engine.same_as(matrix.engine_handle()) {
return Err(Error::invalid_argument(format!(
"{operation} inputs must belong to the same engine"
)));
}
}
if saved_input.shape() != output_gradient.shape() {
return Err(Error::invalid_argument(format!(
"{operation} requires matching shapes"
)));
}
let element_count = u32::try_from(saved_input.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let gradient = Matrix::allocate(
engine,
saved_input.shape().to_vec(),
saved_input.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(saved_input.storage()),
BufferBinding::read(output_gradient.storage()),
BufferBinding::write(gradient.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlMishBackwardF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlMishBackwardF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[saved_input, output_gradient],
outputs: &[&gradient],
attributes: &[],
},
)?;
}
Ok(gradient)
}
pub fn softplus(input: &Matrix) -> Result<Matrix> {
let contract = crate::core::operation::ml::SOFTPLUS;
let operation = contract.name();
let engine = input.engine_handle();
if input.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
input.dtype().token()
)));
}
let element_count = u32::try_from(input.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let output = Matrix::allocate(
engine,
input.shape().to_vec(),
input.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(input.storage()),
BufferBinding::write(output.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlSoftplusF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlSoftplusF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[input],
outputs: &[&output],
attributes: &[],
},
)?;
}
autograd::record_softplus(input, &output, &output)?;
Ok(output)
}
pub(in crate::ml) fn softplus_backward(
saved_output: &Matrix,
output_gradient: &Matrix,
) -> Result<Matrix> {
let contract = crate::core::operation::ml::SOFTPLUS_BACKWARD;
let operation = contract.name();
let engine = saved_output.engine_handle();
for matrix in &[saved_output, output_gradient] {
if matrix.dtype() != DType::F32 {
return Err(Error::invalid_argument(format!(
"{operation} requires F32 matrices; found {}",
matrix.dtype().token()
)));
}
if !engine.same_as(matrix.engine_handle()) {
return Err(Error::invalid_argument(format!(
"{operation} inputs must belong to the same engine"
)));
}
}
if saved_output.shape() != output_gradient.shape() {
return Err(Error::invalid_argument(format!(
"{operation} requires matching shapes"
)));
}
let element_count = u32::try_from(saved_output.num_elements())
.map_err(|_| Error::invalid_argument(format!("{operation} element count exceeds u32")))?;
let gradient = Matrix::allocate(
engine,
saved_output.shape().to_vec(),
saved_output.num_elements(),
DType::F32,
)?;
if element_count != 0 {
let buffers = [
BufferBinding::read(saved_output.storage()),
BufferBinding::read(output_gradient.storage()),
BufferBinding::write(gradient.storage()),
];
let push_constants = [PushConstant::U32(element_count)];
engine.record_semantic(
ComputeDispatch {
kernel: KernelId::MlSoftplusBackwardF32,
buffers: &buffers,
push_constants: &push_constants,
workgroups: KernelId::MlSoftplusBackwardF32.linear_workgroups(element_count),
},
SemanticDispatch {
contract,
inputs: &[saved_output, output_gradient],
outputs: &[&gradient],
attributes: &[],
},
)?;
}
Ok(gradient)
}