Skip to content

Commit 77a4367

Browse files
: v1: value_mesh non materializing ctors (meta-pytorch#1475)
Summary: add a set of `ValueMesh` constructors equivalent to the `RankedValues` initialization paths. `from_single` and `from_default` create compressed single-run meshes without allocating a dense vector, `from_ranges_with_default` builds a compact RLE mesh from sparse overrides on a default value, and `from_dense` constructs from a dense vector and compresses adjacent equal elements. includes tests for correctness, edge cases, and internal structure. completes the construction half of the `ValueMesh` parity plan before adding `ValueOverlay` and merge support. Differential Revision: D84259628
1 parent 013ea71 commit 77a4367

File tree

1 file changed

+263
-0
lines changed

1 file changed

+263
-0
lines changed

hyperactor_mesh/src/v1/value_mesh.rs

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,176 @@ impl<T> ValueMesh<T> {
188188
}
189189
}
190190

191+
impl<T: Clone> ValueMesh<T> {
192+
/// Builds a `ValueMesh` that assigns the single value `s` to
193+
/// every rank in `region`, without materializing a dense
194+
/// `Vec<T>`. The result is stored in compressed (RLE) form as a
195+
/// single run `[0..N)`.
196+
///
197+
/// If `region.num_ranks() == 0`, the mesh contains no runs (and
198+
/// an empty table), regardless of `s`.
199+
pub fn from_single(region: Region, s: T) -> Self {
200+
let n = region.num_ranks();
201+
if n == 0 {
202+
return Self {
203+
region,
204+
rep: Rep::Compressed {
205+
table: Vec::new(),
206+
runs: Vec::new(),
207+
},
208+
};
209+
}
210+
211+
let table = vec![s];
212+
let runs = vec![Run::new(0, n, 0)];
213+
Self {
214+
region,
215+
rep: Rep::Compressed { table, runs },
216+
}
217+
}
218+
}
219+
220+
impl<T: Clone + Default> ValueMesh<T> {
221+
/// Builds a [`ValueMesh`] covering the region, filled with
222+
/// `T::default()`.
223+
///
224+
/// Equivalent to [`ValueMesh::from_single(region,
225+
/// T::default())`].
226+
pub fn from_default(region: Region) -> Self {
227+
ValueMesh::<T>::from_single(region, T::default())
228+
}
229+
}
230+
231+
impl<T: Clone + PartialEq> ValueMesh<T> {
232+
/// Builds a compressed mesh from a default value and a set of
233+
/// disjoint ranges that override the default.
234+
///
235+
/// - `ranges` may be in any order; they must be non-empty,
236+
/// in-bounds, and non-overlapping.
237+
/// - Unspecified ranks are filled with `default`.
238+
/// - Result is stored in RLE form; no dense `Vec<T>` is
239+
/// materialized.
240+
pub fn from_ranges_with_default(
241+
region: Region,
242+
default: T,
243+
mut ranges: Vec<(Range<usize>, T)>,
244+
) -> crate::v1::Result<Self> {
245+
let n = region.num_ranks();
246+
247+
if n == 0 {
248+
return Ok(Self {
249+
region,
250+
rep: Rep::Compressed {
251+
table: Vec::new(),
252+
runs: Vec::new(),
253+
},
254+
});
255+
}
256+
257+
// Validate: non-empty, in-bounds; then sort.
258+
for (r, _) in &ranges {
259+
if r.is_empty() {
260+
return Err(crate::v1::Error::InvalidRankCardinality {
261+
expected: n,
262+
actual: 0,
263+
}); // TODO: this surfaces the error but its not a great fit
264+
}
265+
if r.end > n {
266+
return Err(crate::v1::Error::InvalidRankCardinality {
267+
expected: n,
268+
actual: r.end,
269+
});
270+
}
271+
}
272+
ranges.sort_by_key(|(r, _)| (r.start, r.end));
273+
274+
// Validate: non-overlapping.
275+
for w in ranges.windows(2) {
276+
let (a, _) = &w[0];
277+
let (b, _) = &w[1];
278+
if a.end > b.start {
279+
// Overlap
280+
return Err(crate::v1::Error::InvalidRankCardinality {
281+
expected: n,
282+
actual: b.start, // TODO: this surfaces the error but is a bad fit
283+
});
284+
}
285+
}
286+
287+
// Build table; keep it small by reusing equal values.
288+
fn get_or_insert_id<T: PartialEq + Clone>(table: &mut Vec<T>, v: &T) -> u32 {
289+
if let Some(idx) = table.iter().position(|x| x == v) {
290+
idx as u32
291+
} else {
292+
table.push(v.clone());
293+
(table.len() - 1) as u32
294+
}
295+
}
296+
297+
let mut table: Vec<T> = Vec::with_capacity(1 + ranges.len());
298+
let default_id = get_or_insert_id(&mut table, &default);
299+
300+
let mut runs: Vec<Run> = Vec::with_capacity(1 + 2 * ranges.len());
301+
let mut cursor = 0usize;
302+
303+
for (r, v) in ranges.into_iter() {
304+
// Fill default gap if any.
305+
if cursor < r.start {
306+
runs.push(Run::new(cursor, r.start, default_id));
307+
}
308+
// Override block.
309+
let id = get_or_insert_id(&mut table, &v);
310+
runs.push(Run::new(r.start, r.end, id));
311+
cursor = r.end;
312+
}
313+
314+
// Trailing default tail.
315+
if cursor < n {
316+
runs.push(Run::new(cursor, n, default_id));
317+
}
318+
319+
Ok(Self {
320+
region,
321+
rep: Rep::Compressed { table, runs },
322+
})
323+
}
324+
325+
/// Builds a [`ValueMesh`] from a fully materialized dense vector
326+
/// of per-rank values, then compresses it into run-length–encoded
327+
/// form if possible.
328+
///
329+
/// This constructor is intended for callers that already have one
330+
/// value per rank (e.g. computed or received data) but wish to
331+
/// store it efficiently.
332+
///
333+
/// # Parameters
334+
/// - `region`: The logical region describing the mesh’s shape and
335+
/// rank order.
336+
/// - `values`: A dense vector of values, one per rank in
337+
/// `region`.
338+
///
339+
/// # Returns
340+
/// A [`ValueMesh`] whose internal representation is `Compressed`
341+
/// if any adjacent elements are equal, or `Dense` if no
342+
/// compression was possible.
343+
///
344+
/// # Errors
345+
/// Returns an error if the number of provided `values` does not
346+
/// match the number of ranks in `region`.
347+
///
348+
/// # Examples
349+
/// ```ignore
350+
/// let region: Region = extent!(n = 5).into();
351+
/// let mesh = ValueMesh::from_dense(region, vec![1, 1, 2, 2, 3]).unwrap();
352+
/// // Internally compressed to three runs: [1, 1], [2, 2], [3]
353+
/// ```
354+
pub fn from_dense(region: Region, values: Vec<T>) -> crate::v1::Result<Self> {
355+
let mut vm = Self::new(region, values)?;
356+
vm.compress_adjacent_in_place();
357+
Ok(vm)
358+
}
359+
}
360+
191361
impl<F: Future> ValueMesh<F> {
192362
/// Await all futures in the mesh, yielding a `ValueMesh` of their
193363
/// outputs.
@@ -1414,4 +1584,97 @@ mod tests {
14141584
assert_eq!(range, 0..10);
14151585
assert_eq!(id, 42);
14161586
}
1587+
1588+
#[test]
1589+
fn from_single_builds_single_run() {
1590+
let region: Region = extent!(n = 6).into();
1591+
let vm = ValueMesh::from_single(region.clone(), 7);
1592+
1593+
assert_eq!(vm.region(), &region);
1594+
assert_eq!(vm.values().collect::<Vec<_>>(), vec![7, 7, 7, 7, 7, 7]);
1595+
assert_eq!(vm.get(0), Some(&7));
1596+
assert_eq!(vm.get(5), Some(&7));
1597+
assert_eq!(vm.get(6), None);
1598+
}
1599+
1600+
#[test]
1601+
fn from_default_builds_with_default_value() {
1602+
let region: Region = extent!(n = 6).into();
1603+
let vm = ValueMesh::<i32>::from_default(region.clone());
1604+
1605+
assert_eq!(vm.region(), &region);
1606+
// i32::default() == 0
1607+
assert_eq!(vm.values().collect::<Vec<_>>(), vec![0, 0, 0, 0, 0, 0]);
1608+
assert_eq!(vm.get(0), Some(&0));
1609+
assert_eq!(vm.get(5), Some(&0));
1610+
}
1611+
1612+
#[test]
1613+
fn test_default_vs_single_equivalence() {
1614+
let region: Region = extent!(x = 4).into();
1615+
let d1 = ValueMesh::<i32>::from_default(region.clone());
1616+
let d2 = ValueMesh::from_single(region.clone(), 0);
1617+
assert_eq!(d1, d2);
1618+
}
1619+
1620+
#[test]
1621+
fn build_from_ranges_with_default_basic() {
1622+
let region: Region = extent!(n = 10).into();
1623+
let vm = ValueMesh::from_ranges_with_default(
1624+
region.clone(),
1625+
0, // default
1626+
vec![(2..4, 1), (6..9, 2)],
1627+
)
1628+
.unwrap();
1629+
1630+
assert_eq!(vm.region(), &region);
1631+
assert_eq!(
1632+
vm.values().collect::<Vec<_>>(),
1633+
vec![0, 0, 1, 1, 0, 0, 2, 2, 2, 0]
1634+
);
1635+
1636+
// Internal shape: [0..2)->0, [2..4)->1, [4..6)->0, [6..9)->2,
1637+
// [9..10)->0
1638+
if let Rep::Compressed { table, runs } = &vm.rep {
1639+
// Table is small and de-duplicated.
1640+
assert!(table.len() <= 3);
1641+
assert_eq!(runs.len(), 5);
1642+
} else {
1643+
panic!("expected compressed");
1644+
}
1645+
}
1646+
1647+
#[test]
1648+
fn build_from_ranges_with_default_edge_cases() {
1649+
let region: Region = extent!(n = 5).into();
1650+
1651+
// Full override covers entire region.
1652+
let vm = ValueMesh::from_ranges_with_default(region.clone(), 9, vec![(0..5, 3)]).unwrap();
1653+
assert_eq!(vm.values().collect::<Vec<_>>(), vec![3, 3, 3, 3, 3]);
1654+
1655+
// Adjacent overrides and default gaps.
1656+
let vm = ValueMesh::from_ranges_with_default(region.clone(), 0, vec![(1..2, 7), (2..4, 7)])
1657+
.unwrap();
1658+
assert_eq!(vm.values().collect::<Vec<_>>(), vec![0, 7, 7, 7, 0]);
1659+
1660+
// Empty region.
1661+
let empty_region: Region = extent!(n = 0).into();
1662+
let vm = ValueMesh::from_ranges_with_default(empty_region.clone(), 42, vec![]).unwrap();
1663+
assert_eq!(vm.values().collect::<Vec<_>>(), Vec::<i32>::new());
1664+
}
1665+
1666+
#[test]
1667+
fn from_dense_builds_and_compresses() {
1668+
let region: Region = extent!(n = 6).into();
1669+
let mesh = ValueMesh::from_dense(region.clone(), vec![1, 1, 2, 2, 3, 3]).unwrap();
1670+
1671+
assert_eq!(mesh.region(), &region);
1672+
assert!(matches!(mesh.rep, Rep::Compressed { .. }));
1673+
assert_eq!(mesh.values().collect::<Vec<_>>(), vec![1, 1, 2, 2, 3, 3]);
1674+
1675+
// Spot-check indexing.
1676+
assert_eq!(mesh.get(0), Some(&1));
1677+
assert_eq!(mesh.get(3), Some(&2));
1678+
assert_eq!(mesh.get(5), Some(&3));
1679+
}
14171680
}

0 commit comments

Comments
 (0)