use tenferro_ops::dim_expr::DimExpr;
use tenferro_ops::shape_extent::ShapeExtent;
use tenferro_tensor::DType;
use super::EffectResourceError;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SemanticProvenanceKind {
Builder,
Imported,
Derived,
}
#[derive(Clone)]
pub(crate) struct SemanticProvenance {
kind: SemanticProvenanceKind,
label: Option<std::sync::Arc<str>>,
}
impl SemanticProvenance {
pub(crate) fn builder(label: Option<&str>) -> Self {
Self {
kind: SemanticProvenanceKind::Builder,
label: label.map(std::sync::Arc::from),
}
}
pub(crate) fn view(&self) -> SemanticProvenanceView<'_> {
SemanticProvenanceView {
kind: self.kind,
label: self.label.as_deref(),
}
}
}
#[derive(Clone, Copy)]
pub struct SemanticProvenanceView<'a> {
kind: SemanticProvenanceKind,
label: Option<&'a str>,
}
impl<'a> SemanticProvenanceView<'a> {
pub const fn kind(self) -> SemanticProvenanceKind {
self.kind
}
pub const fn label(self) -> Option<&'a str> {
self.label
}
}
impl std::fmt::Debug for SemanticProvenanceView<'_> {
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter
.debug_struct("SemanticProvenanceView")
.field("kind", &self.kind)
.field("has_label", &self.label.is_some())
.finish()
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ProgramValueMetadata {
dtype: DType,
shape: Box<[ShapeExtent<DimExpr>]>,
}
impl ProgramValueMetadata {
pub fn new(dtype: DType, shape: impl IntoIterator<Item = DimExpr>) -> Self {
Self {
dtype,
shape: shape.into_iter().map(ShapeExtent::Exact).collect(),
}
}
pub fn from_extents(
dtype: DType,
shape: impl IntoIterator<Item = ShapeExtent<DimExpr>>,
) -> Self {
Self {
dtype,
shape: shape.into_iter().collect(),
}
}
pub const fn dtype(&self) -> DType {
self.dtype
}
pub fn shape(&self) -> &[ShapeExtent<DimExpr>] {
&self.shape
}
pub(crate) fn logical_retained_bytes(&self) -> Option<usize> {
checked_sum([
self.shape
.len()
.checked_mul(std::mem::size_of::<ShapeExtent<DimExpr>>())?,
checked_sum_options(self.shape.iter().map(shape_extent_logical_retained_bytes))?,
])
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ProgramInputSpec {
metadata: ProgramValueMetadata,
}
impl ProgramInputSpec {
pub fn new(dtype: DType, shape: impl IntoIterator<Item = DimExpr>) -> Self {
Self {
metadata: ProgramValueMetadata::new(dtype, shape),
}
}
pub fn from_metadata(metadata: ProgramValueMetadata) -> Self {
Self { metadata }
}
pub const fn metadata(&self) -> &ProgramValueMetadata {
&self.metadata
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum ProgramShapeRelation {
Equal,
LessEqual,
GreaterEqual,
}
#[derive(Clone, Debug)]
pub struct ShapeGuard {
relation: ProgramShapeRelation,
lhs: DimExpr,
rhs: DimExpr,
source_family: Option<&'static str>,
}
impl ShapeGuard {
pub fn new(relation: ProgramShapeRelation, lhs: DimExpr, rhs: DimExpr) -> Self {
Self {
relation,
lhs,
rhs,
source_family: None,
}
}
pub(crate) fn with_source_family(mut self, family: &'static str) -> Self {
self.source_family = Some(family);
self
}
pub const fn relation(&self) -> ProgramShapeRelation {
self.relation
}
pub const fn lhs(&self) -> &DimExpr {
&self.lhs
}
pub const fn rhs(&self) -> &DimExpr {
&self.rhs
}
pub const fn source_family(&self) -> Option<&'static str> {
self.source_family
}
pub(crate) fn logical_retained_bytes(&self) -> Option<usize> {
checked_sum([
dim_expr_logical_retained_bytes(&self.lhs)?,
dim_expr_logical_retained_bytes(&self.rhs)?,
])
}
}
impl PartialEq for ShapeGuard {
fn eq(&self, other: &Self) -> bool {
self.relation == other.relation && self.lhs == other.lhs && self.rhs == other.rhs
}
}
impl Eq for ShapeGuard {}
impl std::hash::Hash for ShapeGuard {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.relation.hash(state);
self.lhs.hash(state);
self.rhs.hash(state);
}
}
fn shape_extent_logical_retained_bytes(extent: &ShapeExtent<DimExpr>) -> Option<usize> {
match extent {
ShapeExtent::Exact(expression) | ShapeExtent::UpperBound(expression) => {
dim_expr_logical_retained_bytes(expression)
}
ShapeExtent::Unknown => Some(0),
}
}
fn dim_expr_logical_retained_bytes(expression: &DimExpr) -> Option<usize> {
match expression {
DimExpr::Const(_) | DimExpr::InputDim { .. } => Some(0),
DimExpr::Add(left, right)
| DimExpr::Sub(left, right)
| DimExpr::Mul(left, right)
| DimExpr::FloorDiv(left, right)
| DimExpr::Min(left, right)
| DimExpr::Max(left, right) => checked_sum([
2usize.checked_mul(std::mem::size_of::<DimExpr>())?,
dim_expr_logical_retained_bytes(left)?,
dim_expr_logical_retained_bytes(right)?,
]),
}
}
fn checked_sum(values: impl IntoIterator<Item = usize>) -> Option<usize> {
values
.into_iter()
.try_fold(0usize, |sum, value| sum.checked_add(value))
}
fn checked_sum_options(values: impl IntoIterator<Item = Option<usize>>) -> Option<usize> {
values
.into_iter()
.try_fold(0usize, |sum, value| sum.checked_add(value?))
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct EffectResource {
family: &'static str,
key: u64,
}
impl EffectResource {
pub fn new(family: &'static str, key: u64) -> Result<Self, EffectResourceError> {
let version = family.rsplit_once(".v").map(|(_, version)| version);
if family.is_empty()
|| !version.is_some_and(|version| {
!version.is_empty() && version.bytes().all(|byte| byte.is_ascii_digit())
})
{
return Err(EffectResourceError::InvalidFamily);
}
Ok(Self { family, key })
}
pub const fn family(self) -> &'static str {
self.family
}
pub const fn key(self) -> u64 {
self.key
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EffectAccess {
Read,
Write,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Effect {
resource: EffectResource,
access: EffectAccess,
}
impl Effect {
pub const fn new(resource: EffectResource, access: EffectAccess) -> Self {
Self { resource, access }
}
pub const fn resource(self) -> EffectResource {
self.resource
}
pub const fn access(self) -> EffectAccess {
self.access
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AliasKind {
Fresh,
ViewOf,
MustAlias,
ExternalAlias,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Alias {
kind: AliasKind,
output: usize,
input: Option<usize>,
resource: Option<EffectResource>,
}
impl Alias {
pub const fn fresh(output: usize) -> Self {
Self {
kind: AliasKind::Fresh,
output,
input: None,
resource: None,
}
}
pub const fn view_of(output: usize, input: usize) -> Self {
Self {
kind: AliasKind::ViewOf,
output,
input: Some(input),
resource: None,
}
}
pub const fn must_alias(output: usize, input: usize) -> Self {
Self {
kind: AliasKind::MustAlias,
output,
input: Some(input),
resource: None,
}
}
pub const fn external(output: usize, resource: EffectResource) -> Self {
Self {
kind: AliasKind::ExternalAlias,
output,
input: None,
resource: Some(resource),
}
}
pub const fn kind(self) -> AliasKind {
self.kind
}
pub const fn output(self) -> usize {
self.output
}
pub const fn input(self) -> Option<usize> {
self.input
}
pub const fn resource(self) -> Option<EffectResource> {
self.resource
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SemanticPlacementKind {
Any,
SameAsInput,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct SemanticPlacementConstraint {
kind: SemanticPlacementKind,
input: Option<usize>,
}
impl SemanticPlacementConstraint {
pub const fn any() -> Self {
Self {
kind: SemanticPlacementKind::Any,
input: None,
}
}
pub const fn same_as_input(input: usize) -> Self {
Self {
kind: SemanticPlacementKind::SameAsInput,
input: Some(input),
}
}
pub const fn kind(self) -> SemanticPlacementKind {
self.kind
}
pub const fn input(self) -> Option<usize> {
self.input
}
}