Skip to main content

tenferro_runtime/program/
bindings.rs

1use std::fmt;
2use std::sync::Arc;
3
4use tenferro_tensor::Tensor;
5
6use super::value::ProgramBuilderNonce;
7use super::{BindingKey, ProgramValue};
8
9pub(crate) struct PendingBinding {
10    pub(crate) key: BindingKey,
11    pub(crate) input: ProgramValue,
12    pub(crate) tensor: Arc<Tensor>,
13}
14
15struct ProgramBinding {
16    key: BindingKey,
17    tensor: Arc<Tensor>,
18}
19
20/// Immutable tensor defaults and large constants kept outside semantic structure.
21#[derive(Clone)]
22pub struct ProgramBindings {
23    owner: ProgramBuilderNonce,
24    entries: Arc<[ProgramBinding]>,
25}
26
27impl ProgramBindings {
28    pub(crate) fn freeze(owner: ProgramBuilderNonce, pending: Vec<PendingBinding>) -> Self {
29        let mut entries: Vec<_> = pending
30            .into_iter()
31            .map(|binding| ProgramBinding {
32                key: binding.key,
33                tensor: binding.tensor,
34            })
35            .collect();
36        entries.sort_unstable_by_key(|entry| entry.key.slot);
37        Self {
38            owner,
39            entries: entries.into(),
40        }
41    }
42
43    pub(crate) fn belongs_to(&self, owner: ProgramBuilderNonce) -> bool {
44        self.owner == owner
45    }
46
47    pub(crate) fn tensor_for_input(&self, input: ProgramValue) -> Option<Arc<Tensor>> {
48        self.tensor_ref_for_input(input).map(Arc::clone)
49    }
50
51    pub(crate) fn tensor_ref_for_input(&self, input: ProgramValue) -> Option<&Arc<Tensor>> {
52        if input.owner != self.owner {
53            return None;
54        }
55        self.entries
56            .binary_search_by_key(&input.slot, |entry| entry.key.slot)
57            .ok()
58            .and_then(|index| self.entries.get(index))
59            .map(|entry| &entry.tensor)
60    }
61
62    pub(crate) fn bound_inputs(&self) -> impl ExactSizeIterator<Item = ProgramValue> + '_ {
63        self.entries
64            .iter()
65            .map(|entry| ProgramValue::new(entry.key.slot, self.owner))
66    }
67
68    #[allow(dead_code, reason = "wired into GraphCompiler in Phase 3 A3")]
69    pub(crate) fn preserves_all(&self, source: &Self) -> bool {
70        if self.entries.len() < source.entries.len() {
71            return false;
72        }
73        let mut matched = vec![false; self.entries.len()];
74        source.entries.iter().all(|source_entry| {
75            self.entries
76                .iter()
77                .enumerate()
78                .find(|(index, entry)| {
79                    !matched[*index] && Arc::ptr_eq(&entry.tensor, &source_entry.tensor)
80                })
81                .map(|(index, _)| {
82                    matched[index] = true;
83                })
84                .is_some()
85        })
86    }
87
88    #[allow(dead_code, reason = "wired into GraphCompiler in Phase 3 A3")]
89    pub(crate) fn cache_exact_eq(&self, other: &Self) -> bool {
90        self.entries.len() == other.entries.len()
91            && self
92                .entries
93                .iter()
94                .zip(other.entries.iter())
95                .all(|(left, right)| {
96                    left.key.slot == right.key.slot && Arc::ptr_eq(&left.tensor, &right.tensor)
97                })
98    }
99
100    /// Return the number of tensor bindings.
101    pub fn len(&self) -> usize {
102        self.entries.len()
103    }
104
105    /// Return whether no tensor bindings are present.
106    pub fn is_empty(&self) -> bool {
107        self.entries.is_empty()
108    }
109
110    /// Borrow a tensor by opaque binding key.
111    pub fn get(&self, key: BindingKey) -> Option<&Tensor> {
112        if key.owner != self.owner {
113            return None;
114        }
115        self.entries
116            .binary_search_by_key(&key.slot, |entry| entry.key.slot)
117            .ok()
118            .and_then(|index| self.entries.get(index))
119            .filter(|entry| entry.key == key)
120            .map(|entry| entry.tensor.as_ref())
121    }
122
123    /// Iterate over ordered binding keys and borrowed tensors.
124    pub fn iter(&self) -> impl ExactSizeIterator<Item = (BindingKey, &Tensor)> + '_ {
125        self.entries
126            .iter()
127            .map(|entry| (entry.key, entry.tensor.as_ref()))
128    }
129}
130
131impl fmt::Debug for ProgramBindings {
132    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
133        formatter
134            .debug_struct("ProgramBindings")
135            .field("len", &self.entries.len())
136            .finish()
137    }
138}