Skip to main content

ariadnetor_tensor/dense/
storage.rs

1//! `DenseStorage<T>`: pure-data half of the dense tensor split.
2//!
3//! Carries only the flat element buffer. Shape and memory order live
4//! on the paired [`DenseLayout`](crate::DenseLayout); the wrapper
5//! [`DenseTensorData<T>`](crate::DenseTensorData) joins the two.
6//!
7//! `T: Scalar`-bound scalar-only data operations (`norm`,
8//! `norm_frobenius`, `normalize`) live here because their bodies
9//! touch only `data`; they require no shape, no memory order, and no
10//! compute backend. Symmetric with the BSp side.
11
12use std::ops::Mul;
13use std::sync::Arc;
14
15use aligned_vec::{AVec, ConstAlign};
16use num_traits::{Float, One, Zero};
17
18use super::Align64;
19use crate::{Storage, StorageFor};
20
21/// Pure-data half of the dense tensor split.
22///
23/// Holds a 64-byte-aligned flat buffer with Arc-based shared
24/// ownership (Copy-on-Write via [`Arc::make_mut`]). Shape and memory
25/// order are not carried here — they live on
26/// [`DenseLayout`](crate::DenseLayout). For the full
27/// storage + layout bundle, use
28/// [`DenseTensorData<T>`](crate::DenseTensorData).
29///
30/// # Type Parameters
31///
32/// * `T` - Element type (default: f64)
33pub struct DenseStorage<T = f64> {
34    data: Arc<AVec<T, Align64>>,
35}
36
37// Manual Clone impl: Arc<AVec<T, _>> is Clone regardless of T.
38// #[derive(Clone)] would unnecessarily require T: Clone.
39impl<T> Clone for DenseStorage<T> {
40    fn clone(&self) -> Self {
41        Self {
42            data: Arc::clone(&self.data),
43        }
44    }
45}
46
47impl<T> DenseStorage<T> {
48    /// Construct from a `Vec<T>`, internally rebuilding into a
49    /// 64-byte-aligned buffer.
50    pub fn new(data: Vec<T>) -> Self {
51        let len = data.len();
52        let mut aligned: AVec<T, ConstAlign<64>> = AVec::with_capacity(64, len);
53        for elem in data {
54            aligned.push(elem);
55        }
56        Self {
57            data: Arc::new(aligned),
58        }
59    }
60
61    /// Construct from an already-aligned `AVec` (zero-copy).
62    pub(crate) fn from_aligned(data: AVec<T, ConstAlign<64>>) -> Self {
63        Self {
64            data: Arc::new(data),
65        }
66    }
67
68    /// Get a reference to the underlying contiguous data.
69    pub fn data(&self) -> &[T] {
70        &self.data[..]
71    }
72
73    /// Get a mutable reference to the underlying data (triggers CoW
74    /// if shared).
75    pub fn data_mut(&mut self) -> &mut [T]
76    where
77        T: Clone,
78    {
79        Arc::make_mut(&mut self.data).as_mut_slice()
80    }
81
82    /// Iterate over all stored elements in flat (storage) order.
83    pub fn iter(&self) -> std::slice::Iter<'_, T> {
84        self.data[..].iter()
85    }
86
87    /// Get pointer to the underlying data for FFI.
88    pub fn as_ptr(&self) -> *const T {
89        self.data.as_ptr()
90    }
91
92    /// Get mutable pointer to the underlying data for FFI (triggers
93    /// CoW if shared).
94    pub fn as_mut_ptr(&mut self) -> *mut T
95    where
96        T: Clone,
97    {
98        Arc::make_mut(&mut self.data).as_mut_ptr()
99    }
100}
101
102impl<T> Storage for DenseStorage<T> {
103    type Element = T;
104
105    fn flat_len(&self) -> usize {
106        self.data.len()
107    }
108}
109
110impl<T> StorageFor<crate::DenseLayout> for DenseStorage<T> {}
111
112// ---------------------------------------------------------------------------
113// Length-preserving data operations
114//
115// Bodies only touch `data`, so they live on the storage half.
116// Mirrors the BSp storage pattern.
117// ---------------------------------------------------------------------------
118
119impl<T> DenseStorage<T>
120where
121    T: Clone,
122{
123    /// Fill every stored element with a constant value (triggers CoW
124    /// if shared).
125    pub(crate) fn fill(&mut self, value: T) {
126        Arc::make_mut(&mut self.data).as_mut_slice().fill(value);
127    }
128
129    /// Apply a function to each stored element in place (triggers CoW
130    /// if shared).
131    ///
132    /// Element ordering follows storage layout; the closure sees raw
133    /// flat positions without coordinate context.
134    pub(crate) fn map_mut<F>(&mut self, f: F)
135    where
136        F: Fn(&T) -> T,
137    {
138        let data = Arc::make_mut(&mut self.data).as_mut_slice();
139        for x in data.iter_mut() {
140            *x = f(x);
141        }
142    }
143
144    /// Scale every stored element by a scalar factor in place
145    /// (triggers CoW if shared).
146    pub(crate) fn scale<S>(&mut self, factor: S)
147    where
148        T: Mul<S, Output = T>,
149        S: Clone,
150    {
151        let data = Arc::make_mut(&mut self.data).as_mut_slice();
152        for elem in data.iter_mut() {
153            *elem = elem.clone() * factor.clone();
154        }
155    }
156}
157
158// ---------------------------------------------------------------------------
159// Scalar-only data operations
160//
161// Symmetric with `BlockSparseStorage`: norm-related operations read
162// only the flat buffer (no shape, no layout-internal metadata).
163// ---------------------------------------------------------------------------
164
165impl<T> DenseStorage<T>
166where
167    T: ariadnetor_core::Scalar,
168{
169    /// Squared Frobenius norm: Σ |element|².
170    fn norm_squared(&self) -> T::Real {
171        self.data
172            .iter()
173            .map(|&x| {
174                let a = x.abs();
175                a * a
176            })
177            .fold(T::Real::zero(), |acc, x| acc + x)
178    }
179
180    /// Frobenius norm: √(Σ |element|²).
181    pub(crate) fn norm_frobenius(&self) -> T::Real {
182        self.norm_squared().sqrt()
183    }
184
185    /// Frobenius norm (alias for [`norm_frobenius`](Self::norm_frobenius)).
186    pub(crate) fn norm(&self) -> T::Real {
187        self.norm_frobenius()
188    }
189
190    /// Normalize to unit Frobenius norm (in-place).
191    ///
192    /// Returns the norm before normalization. Panics if the tensor has
193    /// zero norm.
194    pub(crate) fn normalize(&mut self) -> T::Real {
195        let norm = self.norm_frobenius();
196        assert!(norm != T::Real::zero(), "Cannot normalize zero tensor");
197        let inv_norm = T::Real::one() / norm;
198        let data = Arc::make_mut(&mut self.data);
199        for elem in data.iter_mut() {
200            *elem = elem.scale_real(inv_norm);
201        }
202        norm
203    }
204}