tenferro_runtime/program/
metadata.rs1use tenferro_ops::dim_expr::DimExpr;
2use tenferro_ops::shape_extent::ShapeExtent;
3use tenferro_tensor::DType;
4
5use super::EffectResourceError;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
9#[non_exhaustive]
10pub enum SemanticProvenanceKind {
11 Builder,
13 Imported,
15 Derived,
17}
18
19#[derive(Clone)]
20pub(crate) struct SemanticProvenance {
21 kind: SemanticProvenanceKind,
22 label: Option<std::sync::Arc<str>>,
23}
24
25impl SemanticProvenance {
26 pub(crate) fn builder(label: Option<&str>) -> Self {
27 Self {
28 kind: SemanticProvenanceKind::Builder,
29 label: label.map(std::sync::Arc::from),
30 }
31 }
32
33 pub(crate) fn view(&self) -> SemanticProvenanceView<'_> {
34 SemanticProvenanceView {
35 kind: self.kind,
36 label: self.label.as_deref(),
37 }
38 }
39}
40
41#[derive(Clone, Copy)]
43pub struct SemanticProvenanceView<'a> {
44 kind: SemanticProvenanceKind,
45 label: Option<&'a str>,
46}
47
48impl<'a> SemanticProvenanceView<'a> {
49 pub const fn kind(self) -> SemanticProvenanceKind {
51 self.kind
52 }
53
54 pub const fn label(self) -> Option<&'a str> {
56 self.label
57 }
58}
59
60impl std::fmt::Debug for SemanticProvenanceView<'_> {
61 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
62 formatter
63 .debug_struct("SemanticProvenanceView")
64 .field("kind", &self.kind)
65 .field("has_label", &self.label.is_some())
66 .finish()
67 }
68}
69
70#[derive(Clone, Debug, PartialEq, Eq, Hash)]
72pub struct ProgramValueMetadata {
73 dtype: DType,
74 shape: Box<[ShapeExtent<DimExpr>]>,
75}
76
77impl ProgramValueMetadata {
78 pub fn new(dtype: DType, shape: impl IntoIterator<Item = DimExpr>) -> Self {
80 Self {
81 dtype,
82 shape: shape.into_iter().map(ShapeExtent::Exact).collect(),
83 }
84 }
85
86 pub fn from_extents(
88 dtype: DType,
89 shape: impl IntoIterator<Item = ShapeExtent<DimExpr>>,
90 ) -> Self {
91 Self {
92 dtype,
93 shape: shape.into_iter().collect(),
94 }
95 }
96
97 pub const fn dtype(&self) -> DType {
99 self.dtype
100 }
101
102 pub fn shape(&self) -> &[ShapeExtent<DimExpr>] {
104 &self.shape
105 }
106
107 pub(crate) fn logical_retained_bytes(&self) -> Option<usize> {
108 checked_sum([
109 self.shape
110 .len()
111 .checked_mul(std::mem::size_of::<ShapeExtent<DimExpr>>())?,
112 checked_sum_options(self.shape.iter().map(shape_extent_logical_retained_bytes))?,
113 ])
114 }
115}
116
117#[derive(Clone, Debug, PartialEq, Eq, Hash)]
119pub struct ProgramInputSpec {
120 metadata: ProgramValueMetadata,
121}
122
123impl ProgramInputSpec {
124 pub fn new(dtype: DType, shape: impl IntoIterator<Item = DimExpr>) -> Self {
126 Self {
127 metadata: ProgramValueMetadata::new(dtype, shape),
128 }
129 }
130
131 pub fn from_metadata(metadata: ProgramValueMetadata) -> Self {
133 Self { metadata }
134 }
135
136 pub const fn metadata(&self) -> &ProgramValueMetadata {
138 &self.metadata
139 }
140}
141
142#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
144#[non_exhaustive]
145pub enum ProgramShapeRelation {
146 Equal,
148 LessEqual,
150 GreaterEqual,
152}
153
154#[derive(Clone, Debug)]
156pub struct ShapeGuard {
157 relation: ProgramShapeRelation,
158 lhs: DimExpr,
159 rhs: DimExpr,
160 source_family: Option<&'static str>,
161}
162
163impl ShapeGuard {
164 pub fn new(relation: ProgramShapeRelation, lhs: DimExpr, rhs: DimExpr) -> Self {
166 Self {
167 relation,
168 lhs,
169 rhs,
170 source_family: None,
171 }
172 }
173
174 pub(crate) fn with_source_family(mut self, family: &'static str) -> Self {
175 self.source_family = Some(family);
176 self
177 }
178
179 pub const fn relation(&self) -> ProgramShapeRelation {
181 self.relation
182 }
183
184 pub const fn lhs(&self) -> &DimExpr {
186 &self.lhs
187 }
188
189 pub const fn rhs(&self) -> &DimExpr {
191 &self.rhs
192 }
193
194 pub const fn source_family(&self) -> Option<&'static str> {
199 self.source_family
200 }
201
202 pub(crate) fn logical_retained_bytes(&self) -> Option<usize> {
203 checked_sum([
204 dim_expr_logical_retained_bytes(&self.lhs)?,
205 dim_expr_logical_retained_bytes(&self.rhs)?,
206 ])
207 }
208}
209
210impl PartialEq for ShapeGuard {
211 fn eq(&self, other: &Self) -> bool {
212 self.relation == other.relation && self.lhs == other.lhs && self.rhs == other.rhs
213 }
214}
215
216impl Eq for ShapeGuard {}
217
218impl std::hash::Hash for ShapeGuard {
219 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
220 self.relation.hash(state);
221 self.lhs.hash(state);
222 self.rhs.hash(state);
223 }
224}
225
226fn shape_extent_logical_retained_bytes(extent: &ShapeExtent<DimExpr>) -> Option<usize> {
227 match extent {
228 ShapeExtent::Exact(expression) | ShapeExtent::UpperBound(expression) => {
229 dim_expr_logical_retained_bytes(expression)
230 }
231 ShapeExtent::Unknown => Some(0),
232 }
233}
234
235fn dim_expr_logical_retained_bytes(expression: &DimExpr) -> Option<usize> {
236 match expression {
237 DimExpr::Const(_) | DimExpr::InputDim { .. } => Some(0),
238 DimExpr::Add(left, right)
239 | DimExpr::Sub(left, right)
240 | DimExpr::Mul(left, right)
241 | DimExpr::FloorDiv(left, right)
242 | DimExpr::Min(left, right)
243 | DimExpr::Max(left, right) => checked_sum([
244 2usize.checked_mul(std::mem::size_of::<DimExpr>())?,
245 dim_expr_logical_retained_bytes(left)?,
246 dim_expr_logical_retained_bytes(right)?,
247 ]),
248 }
249}
250
251fn checked_sum(values: impl IntoIterator<Item = usize>) -> Option<usize> {
252 values
253 .into_iter()
254 .try_fold(0usize, |sum, value| sum.checked_add(value))
255}
256
257fn checked_sum_options(values: impl IntoIterator<Item = Option<usize>>) -> Option<usize> {
258 values
259 .into_iter()
260 .try_fold(0usize, |sum, value| sum.checked_add(value?))
261}
262
263#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
265pub struct EffectResource {
266 family: &'static str,
267 key: u64,
268}
269
270impl EffectResource {
271 pub fn new(family: &'static str, key: u64) -> Result<Self, EffectResourceError> {
278 let version = family.rsplit_once(".v").map(|(_, version)| version);
279 if family.is_empty()
280 || !version.is_some_and(|version| {
281 !version.is_empty() && version.bytes().all(|byte| byte.is_ascii_digit())
282 })
283 {
284 return Err(EffectResourceError::InvalidFamily);
285 }
286 Ok(Self { family, key })
287 }
288
289 pub const fn family(self) -> &'static str {
291 self.family
292 }
293
294 pub const fn key(self) -> u64 {
296 self.key
297 }
298}
299
300#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
302pub enum EffectAccess {
303 Read,
305 Write,
307}
308
309#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
311pub struct Effect {
312 resource: EffectResource,
313 access: EffectAccess,
314}
315
316impl Effect {
317 pub const fn new(resource: EffectResource, access: EffectAccess) -> Self {
319 Self { resource, access }
320 }
321
322 pub const fn resource(self) -> EffectResource {
324 self.resource
325 }
326
327 pub const fn access(self) -> EffectAccess {
329 self.access
330 }
331}
332
333#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
335pub enum AliasKind {
336 Fresh,
338 ViewOf,
340 MustAlias,
342 ExternalAlias,
344}
345
346#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
348pub struct Alias {
349 kind: AliasKind,
350 output: usize,
351 input: Option<usize>,
352 resource: Option<EffectResource>,
353}
354
355impl Alias {
356 pub const fn fresh(output: usize) -> Self {
358 Self {
359 kind: AliasKind::Fresh,
360 output,
361 input: None,
362 resource: None,
363 }
364 }
365
366 pub const fn view_of(output: usize, input: usize) -> Self {
368 Self {
369 kind: AliasKind::ViewOf,
370 output,
371 input: Some(input),
372 resource: None,
373 }
374 }
375
376 pub const fn must_alias(output: usize, input: usize) -> Self {
378 Self {
379 kind: AliasKind::MustAlias,
380 output,
381 input: Some(input),
382 resource: None,
383 }
384 }
385
386 pub const fn external(output: usize, resource: EffectResource) -> Self {
388 Self {
389 kind: AliasKind::ExternalAlias,
390 output,
391 input: None,
392 resource: Some(resource),
393 }
394 }
395
396 pub const fn kind(self) -> AliasKind {
398 self.kind
399 }
400
401 pub const fn output(self) -> usize {
403 self.output
404 }
405
406 pub const fn input(self) -> Option<usize> {
408 self.input
409 }
410
411 pub const fn resource(self) -> Option<EffectResource> {
413 self.resource
414 }
415}
416
417#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
419#[non_exhaustive]
420pub enum SemanticPlacementKind {
421 Any,
423 SameAsInput,
425}
426
427#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
429pub struct SemanticPlacementConstraint {
430 kind: SemanticPlacementKind,
431 input: Option<usize>,
432}
433
434impl SemanticPlacementConstraint {
435 pub const fn any() -> Self {
437 Self {
438 kind: SemanticPlacementKind::Any,
439 input: None,
440 }
441 }
442
443 pub const fn same_as_input(input: usize) -> Self {
445 Self {
446 kind: SemanticPlacementKind::SameAsInput,
447 input: Some(input),
448 }
449 }
450
451 pub const fn kind(self) -> SemanticPlacementKind {
453 self.kind
454 }
455
456 pub const fn input(self) -> Option<usize> {
458 self.input
459 }
460}