1pub mod context;
8
9#[cfg(feature = "autodiff")]
10mod analytic;
11#[cfg(feature = "autodiff")]
12mod contraction;
13#[cfg(feature = "autodiff")]
14mod diagonal;
15#[cfg(feature = "autodiff")]
16mod dynamic;
17#[cfg(feature = "autodiff")]
18mod elementwise;
19#[cfg(feature = "autodiff")]
20mod indexing;
21#[cfg(feature = "autodiff")]
22pub(crate) mod registry;
23#[cfg(feature = "autodiff")]
24mod semiring;
25#[cfg(feature = "autodiff")]
26mod structural;
27#[cfg(feature = "autodiff")]
28#[doc(hidden)]
29pub mod support;
30#[cfg(feature = "autodiff")]
31#[doc(hidden)]
32pub mod transpose_input;
33#[cfg(feature = "autodiff")]
34mod zeros;
35
36#[cfg(feature = "autodiff")]
37use computegraph::graph::GraphBuilder;
38#[cfg(feature = "autodiff")]
39use computegraph::types::{LocalValueId, OperationRole, ValueKey, ValueRef};
40#[cfg(feature = "autodiff")]
41use tidu::{
42 ADRuleError, ADRuleKind, ADRuleResult, PrimitiveBuilder, PrimitiveTransposeInput,
43 PrimitiveValue,
44};
45
46#[cfg(feature = "autodiff")]
47use crate::ext_op::{linearize_extension_rule, transpose_extension_rule};
48#[cfg(feature = "autodiff")]
49use crate::std_tensor_op::StdTensorOp;
50
51#[cfg(feature = "autodiff")]
52fn missing_primitive_kind(op: &StdTensorOp, rule: ADRuleKind) -> ADRuleError {
53 ADRuleError::invalid_input(
54 "tenferro-internal-ops primitive AD dispatch",
55 rule,
56 format!("non-extension operation has no primitive kind: {op:?}"),
57 )
58}
59
60#[cfg(feature = "autodiff")]
82pub trait PrimitiveRuleBuilder {
83 fn add_operation(
85 &mut self,
86 operation: StdTensorOp,
87 inputs: Vec<ValueRef<StdTensorOp>>,
88 role: OperationRole,
89 ) -> Vec<LocalValueId>;
90}
91
92#[cfg(feature = "autodiff")]
93impl<B> PrimitiveRuleBuilder for B
94where
95 B: PrimitiveBuilder<StdTensorOp> + ?Sized,
96{
97 fn add_operation(
98 &mut self,
99 operation: StdTensorOp,
100 inputs: Vec<ValueRef<StdTensorOp>>,
101 role: OperationRole,
102 ) -> Vec<LocalValueId> {
103 let inputs = inputs.into_iter().map(PrimitiveValue::from).collect();
104 PrimitiveBuilder::add_primitive(self, operation, inputs, role)
105 }
106}
107
108#[cfg(feature = "autodiff")]
109impl PrimitiveRuleBuilder for GraphBuilder<StdTensorOp> {
110 fn add_operation(
111 &mut self,
112 operation: StdTensorOp,
113 inputs: Vec<ValueRef<StdTensorOp>>,
114 role: OperationRole,
115 ) -> Vec<LocalValueId> {
116 GraphBuilder::add_operation(self, operation, inputs, role)
117 }
118}
119
120#[cfg(feature = "autodiff")]
128pub fn linearize(
129 op: &StdTensorOp,
130 builder: &mut dyn PrimitiveRuleBuilder,
131 primal_in: &[ValueKey<StdTensorOp>],
132 primal_out: &[ValueKey<StdTensorOp>],
133 tangent_in: &[Option<LocalValueId>],
134 ctx: &mut context::ShapeGuardContext,
135) -> ADRuleResult<Vec<Option<LocalValueId>>> {
136 if let StdTensorOp::Extension(ext) = op {
137 return linearize_extension_rule(
138 ext.as_ref(),
139 builder,
140 primal_in,
141 primal_out,
142 tangent_in,
143 ctx,
144 );
145 }
146
147 let kind = op
148 .primitive_kind()
149 .ok_or_else(|| missing_primitive_kind(op, ADRuleKind::Jvp))?;
150 let rule = registry::primitive_ad_rule(kind)
151 .ok_or_else(|| registry::missing_rule(kind, ADRuleKind::Jvp))?;
152 rule.linearize(op, builder, primal_in, primal_out, tangent_in, ctx)
153}
154
155#[cfg(feature = "autodiff")]
162pub fn transpose_rule(
163 op: &StdTensorOp,
164 builder: &mut impl PrimitiveRuleBuilder,
165 cotangent_out: &[Option<LocalValueId>],
166 inputs: &[PrimitiveTransposeInput<StdTensorOp>],
167 mode: &OperationRole,
168 ctx: &mut context::ShapeGuardContext,
169) -> ADRuleResult<Vec<Option<LocalValueId>>> {
170 if let StdTensorOp::Extension(ext) = op {
171 let builder_dyn: &mut dyn PrimitiveRuleBuilder = builder;
172 return transpose_extension_rule(
173 ext.as_ref(),
174 builder_dyn,
175 cotangent_out,
176 inputs,
177 mode,
178 ctx,
179 );
180 }
181
182 let transpose_inputs = inputs
183 .iter()
184 .map(transpose_input::TransposeInputRef::new)
185 .collect::<Vec<_>>();
186 let kind = op
187 .primitive_kind()
188 .ok_or_else(|| missing_primitive_kind(op, ADRuleKind::Transpose))?;
189 let rule = registry::primitive_ad_rule(kind)
190 .ok_or_else(|| registry::missing_rule(kind, ADRuleKind::Transpose))?;
191 let builder_dyn: &mut dyn PrimitiveRuleBuilder = builder;
192 rule.transpose_rule(op, builder_dyn, cotangent_out, &transpose_inputs, mode, ctx)
193}
194
195#[cfg(test)]
196mod tests;