Skip to main content

ariadnetor_tensor/block_sparse/
qn_index.rs

1//! `QNIndex<S>`: per-leg quantum-number index with sorted sector blocks.
2
3use super::Direction;
4use crate::sector::Sector;
5
6/// Quantum-number index for one tensor leg.
7///
8/// Maps each sector to a block dimension, with a direction for flux computation.
9///
10/// # Invariants (enforced by constructor)
11///
12/// - `blocks` is sorted by sector (`Ord`)
13/// - No duplicate sectors
14/// - Every block dimension is > 0
15#[derive(Clone, Debug)]
16pub struct QNIndex<S: Sector> {
17    /// Sector → block dimension pairs, sorted by sector, no duplicates.
18    blocks: Vec<(S, usize)>,
19    /// Leg direction.
20    direction: Direction,
21}
22
23impl<S: Sector> QNIndex<S> {
24    /// Create a new QN index.
25    ///
26    /// `blocks` is sorted by sector. Panics if any block dimension is zero
27    /// or if duplicate sectors are present.
28    pub fn new(mut blocks: Vec<(S, usize)>, direction: Direction) -> Self {
29        blocks.sort_by(|a, b| a.0.cmp(&b.0));
30
31        for (i, (sector, dim)) in blocks.iter().enumerate() {
32            assert!(
33                *dim > 0,
34                "QNIndex: block dimension must be > 0 for sector {sector:?}"
35            );
36            if i > 0 {
37                assert!(
38                    blocks[i - 1].0 != *sector,
39                    "QNIndex: duplicate sector {sector:?}"
40                );
41            }
42        }
43
44        Self { blocks, direction }
45    }
46
47    /// Sector–dimension pairs (sorted by sector).
48    pub fn blocks(&self) -> &[(S, usize)] {
49        &self.blocks
50    }
51
52    /// Leg direction.
53    pub fn direction(&self) -> Direction {
54        self.direction
55    }
56
57    /// Number of distinct sectors (blocks) in this index.
58    pub fn num_blocks(&self) -> usize {
59        self.blocks.len()
60    }
61
62    /// Total dimension (sum of all block dimensions).
63    pub fn total_dim(&self) -> usize {
64        self.blocks.iter().map(|(_, d)| d).sum()
65    }
66
67    /// Block dimension for a given block index.
68    ///
69    /// Panics if `idx >= self.num_blocks()`.
70    pub fn block_dim(&self, idx: usize) -> usize {
71        self.blocks[idx].1
72    }
73
74    /// Sector for a given block index.
75    ///
76    /// Panics if `idx >= self.num_blocks()`.
77    pub fn sector(&self, idx: usize) -> &S {
78        &self.blocks[idx].0
79    }
80}