pub mod context;
#[cfg(feature = "autodiff")]
mod analytic;
#[cfg(feature = "autodiff")]
mod contraction;
#[cfg(feature = "autodiff")]
mod diagonal;
#[cfg(feature = "autodiff")]
mod dynamic;
#[cfg(feature = "autodiff")]
mod elementwise;
#[cfg(feature = "autodiff")]
mod indexing;
#[cfg(feature = "autodiff")]
pub(crate) mod registry;
#[cfg(feature = "autodiff")]
mod semiring;
#[cfg(feature = "autodiff")]
mod structural;
#[cfg(feature = "autodiff")]
#[doc(hidden)]
pub mod support;
#[cfg(feature = "autodiff")]
mod zeros;
#[cfg(feature = "autodiff")]
use computegraph::graph::GraphBuilder;
#[cfg(feature = "autodiff")]
use computegraph::types::{LocalValueId, OperationRole, ValueKey, ValueRef};
#[cfg(feature = "autodiff")]
use tidu::{ADRuleError, ADRuleKind, ADRuleResult, PrimitiveBuilder, PrimitiveValue};
#[cfg(feature = "autodiff")]
use crate::ext_op::{linearize_extension_rule, transpose_extension_rule};
#[cfg(feature = "autodiff")]
use crate::std_tensor_op::StdTensorOp;
#[cfg(feature = "autodiff")]
fn missing_primitive_kind(op: &StdTensorOp, rule: ADRuleKind) -> ADRuleError {
ADRuleError::invalid_input(
"tenferro-internal-ops primitive AD dispatch",
rule,
format!("non-extension operation has no primitive kind: {op:?}"),
)
}
#[cfg(feature = "autodiff")]
pub trait PrimitiveRuleBuilder {
fn add_operation(
&mut self,
operation: StdTensorOp,
inputs: Vec<ValueRef<StdTensorOp>>,
role: OperationRole,
) -> Vec<LocalValueId>;
}
#[cfg(feature = "autodiff")]
impl<B> PrimitiveRuleBuilder for B
where
B: PrimitiveBuilder<StdTensorOp> + ?Sized,
{
fn add_operation(
&mut self,
operation: StdTensorOp,
inputs: Vec<ValueRef<StdTensorOp>>,
role: OperationRole,
) -> Vec<LocalValueId> {
let inputs = inputs.into_iter().map(PrimitiveValue::from).collect();
PrimitiveBuilder::add_primitive(self, operation, inputs, role)
}
}
#[cfg(feature = "autodiff")]
impl PrimitiveRuleBuilder for GraphBuilder<StdTensorOp> {
fn add_operation(
&mut self,
operation: StdTensorOp,
inputs: Vec<ValueRef<StdTensorOp>>,
role: OperationRole,
) -> Vec<LocalValueId> {
GraphBuilder::add_operation(self, operation, inputs, role)
}
}
#[cfg(feature = "autodiff")]
pub fn linearize(
op: &StdTensorOp,
builder: &mut dyn PrimitiveRuleBuilder,
primal_in: &[ValueKey<StdTensorOp>],
primal_out: &[ValueKey<StdTensorOp>],
tangent_in: &[Option<LocalValueId>],
ctx: &mut context::ShapeGuardContext,
) -> ADRuleResult<Vec<Option<LocalValueId>>> {
if let StdTensorOp::Extension(ext) = op {
return linearize_extension_rule(
ext.as_ref(),
builder,
primal_in,
primal_out,
tangent_in,
ctx,
);
}
let kind = op
.primitive_kind()
.ok_or_else(|| missing_primitive_kind(op, ADRuleKind::Jvp))?;
let rule = registry::primitive_ad_rule(kind)
.ok_or_else(|| registry::missing_rule(kind, ADRuleKind::Jvp))?;
rule.linearize(op, builder, primal_in, primal_out, tangent_in, ctx)
}
#[cfg(feature = "autodiff")]
pub fn transpose_rule(
op: &StdTensorOp,
builder: &mut impl PrimitiveRuleBuilder,
cotangent_out: &[Option<LocalValueId>],
inputs: &[ValueRef<StdTensorOp>],
mode: &OperationRole,
ctx: &mut context::ShapeGuardContext,
) -> ADRuleResult<Vec<Option<LocalValueId>>> {
if let StdTensorOp::Extension(ext) = op {
let builder_dyn: &mut dyn PrimitiveRuleBuilder = builder;
return transpose_extension_rule(
ext.as_ref(),
builder_dyn,
cotangent_out,
inputs,
mode,
ctx,
);
}
let kind = op
.primitive_kind()
.ok_or_else(|| missing_primitive_kind(op, ADRuleKind::Transpose))?;
let rule = registry::primitive_ad_rule(kind)
.ok_or_else(|| registry::missing_rule(kind, ADRuleKind::Transpose))?;
let builder_dyn: &mut dyn PrimitiveRuleBuilder = builder;
rule.transpose_rule(op, builder_dyn, cotangent_out, inputs, mode, ctx)
}
#[cfg(test)]
mod tests;