use std::sync::Arc;
use tenferro_runtime::program::FrozenProgram;
use tenferro_runtime::{CacheStats, Result, TracedTensor};
use crate::semantic_extension::{SemanticExtensionRegistryError, SemanticExtensionRuleSet};
use crate::semantic_transform::{
semantic_jvp, semantic_vjp, SemanticAdProgram, SemanticAdTransformError,
};
use crate::transform_cache::{
AdTransformCache, AdTransformCacheLimits, SemanticAdTransformCacheKey,
};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub struct AdContextCacheStats {
pub ad_transforms: CacheStats,
}
#[derive(Clone, Debug)]
pub struct AdContext {
semantic_extension_rules: SemanticExtensionRuleSet,
ad_transform_cache: Arc<AdTransformCache>,
}
impl AdContext {
pub fn builder() -> AdContextBuilder {
AdContextBuilder::default()
}
pub(crate) fn with_rules_and_transform_cache(
semantic_extension_rules: SemanticExtensionRuleSet,
ad_transform_cache: Arc<AdTransformCache>,
) -> Self {
Self {
semantic_extension_rules,
ad_transform_cache,
}
}
pub fn semantic_extension_rules(&self) -> &SemanticExtensionRuleSet {
&self.semantic_extension_rules
}
pub fn jvp_program(
&self,
input: &FrozenProgram,
active_inputs: &[bool],
) -> std::result::Result<SemanticAdProgram, SemanticAdTransformError> {
let key = SemanticAdTransformCacheKey::jvp(input, active_inputs);
if let Some(cached) = self
.ad_transform_cache
.get_semantic(&key, input)
.map_err(SemanticAdTransformError::Cache)?
{
return cached
.as_ref()
.with_input_prefix_bindings_from(input)
.map_err(SemanticAdTransformError::from);
}
let transformed = semantic_jvp(input, active_inputs, &self.semantic_extension_rules)?;
self.ad_transform_cache
.put_semantic(key, input, Arc::new(transformed.clone()))
.map_err(SemanticAdTransformError::Cache)?;
Ok(transformed)
}
pub fn vjp_program(
&self,
input: &FrozenProgram,
active_inputs: &[bool],
active_outputs: &[bool],
) -> std::result::Result<SemanticAdProgram, SemanticAdTransformError> {
let key = SemanticAdTransformCacheKey::vjp(input, active_inputs, active_outputs);
if let Some(cached) = self
.ad_transform_cache
.get_semantic(&key, input)
.map_err(SemanticAdTransformError::Cache)?
{
return cached
.as_ref()
.with_input_prefix_bindings_from(input)
.map_err(SemanticAdTransformError::from);
}
let transformed = semantic_vjp(
input,
active_inputs,
active_outputs,
&self.semantic_extension_rules,
)?;
self.ad_transform_cache
.put_semantic(key, input, Arc::new(transformed.clone()))
.map_err(SemanticAdTransformError::Cache)?;
Ok(transformed)
}
pub(crate) fn ad_transform_cache(&self) -> Arc<AdTransformCache> {
Arc::clone(&self.ad_transform_cache)
}
pub fn ad_transform_cache_limits(&self) -> Result<AdTransformCacheLimits> {
self.ad_transform_cache.limits()
}
pub fn set_ad_transform_cache_limits(&self, limits: AdTransformCacheLimits) -> Result<()> {
self.ad_transform_cache.set_limits(limits)
}
pub fn clear_ad_transform_caches(&self) -> Result<()> {
self.ad_transform_cache.clear()
}
pub fn ad_transform_cache_stats(&self) -> Result<CacheStats> {
self.ad_transform_cache.stats()
}
pub fn clear_caches(&self) -> Result<()> {
self.clear_ad_transform_caches()
}
pub fn cache_stats(&self) -> Result<AdContextCacheStats> {
Ok(AdContextCacheStats {
ad_transforms: self.ad_transform_cache_stats()?,
})
}
pub fn grad(&self, output: &TracedTensor, wrt: &TracedTensor) -> Result<TracedTensor> {
crate::traced::grad_with_rules_and_cache(
output,
wrt,
&self.semantic_extension_rules,
Some(self.ad_transform_cache.as_ref()),
)
}
pub fn grad_optional(
&self,
output: &TracedTensor,
wrt: &TracedTensor,
) -> Result<Option<TracedTensor>> {
crate::traced::grad_optional_with_rules_and_cache(
output,
wrt,
&self.semantic_extension_rules,
Some(self.ad_transform_cache.as_ref()),
)
}
pub fn jvp(
&self,
output: &TracedTensor,
wrt: &TracedTensor,
tangent: &TracedTensor,
) -> Result<TracedTensor> {
crate::traced::jvp_with_rules_and_cache(
output,
wrt,
tangent,
&self.semantic_extension_rules,
Some(self.ad_transform_cache.as_ref()),
)
}
pub fn jvp_optional(
&self,
output: &TracedTensor,
wrt: &TracedTensor,
tangent: &TracedTensor,
) -> Result<Option<TracedTensor>> {
crate::traced::jvp_optional_with_rules_and_cache(
output,
wrt,
tangent,
&self.semantic_extension_rules,
Some(self.ad_transform_cache.as_ref()),
)
}
pub fn vjp(
&self,
output: &TracedTensor,
wrt: &TracedTensor,
cotangent: &TracedTensor,
) -> Result<TracedTensor> {
crate::traced::vjp_with_rules_and_cache(
output,
wrt,
cotangent,
&self.semantic_extension_rules,
Some(self.ad_transform_cache.as_ref()),
)
}
pub fn vjp_optional(
&self,
output: &TracedTensor,
wrt: &TracedTensor,
cotangent: &TracedTensor,
) -> Result<Option<TracedTensor>> {
crate::traced::vjp_optional_with_rules_and_cache(
output,
wrt,
cotangent,
&self.semantic_extension_rules,
Some(self.ad_transform_cache.as_ref()),
)
}
}
#[derive(Clone, Debug, Default)]
pub struct AdContextBuilder {
semantic_extension_rules: SemanticExtensionRuleSet,
}
impl AdContextBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn with_semantic_extension_rules(
mut self,
rules: SemanticExtensionRuleSet,
) -> std::result::Result<Self, SemanticExtensionRegistryError> {
self.semantic_extension_rules.merge(rules)?;
Ok(self)
}
pub fn build(self) -> std::result::Result<AdContext, std::convert::Infallible> {
Ok(AdContext {
semantic_extension_rules: self.semantic_extension_rules,
ad_transform_cache: Arc::new(AdTransformCache::new()),
})
}
}