Skip to main content

bloq_ir/ir/
id.rs

1use std::fmt;
2
3use bloq_circuit::DetectorParity;
4
5pub(super) fn pool_index(index: usize) -> u32 {
6    u32::try_from(index).expect("append-only pool id space exhausted")
7}
8
9pub(super) fn deserialize_pool<'de, D, T>(
10    deserializer: D,
11) -> Result<Vec<std::sync::Arc<T>>, D::Error>
12where
13    D: serde::Deserializer<'de>,
14    T: serde::Deserialize<'de>,
15{
16    let entries = <Vec<std::sync::Arc<T>> as serde::Deserialize>::deserialize(deserializer)?;
17    if let Some(last) = entries.len().checked_sub(1) {
18        u32::try_from(last)
19            .map_err(|_| serde::de::Error::custom("append-only pool id space exhausted"))?;
20    }
21    Ok(entries)
22}
23
24/// A node's slot index in its own graph level (a [`crate::Bloq`] top level or
25/// one region [`crate::SubGraph`] body) — ids are level-local, not program-global.
26///
27/// Identity contract: an id is valid until its node is
28/// removed; removal may recycle the id for a later `add_node`. Holders of ids
29/// across mutating passes must re-resolve them — in particular,
30/// The `fuse_single_use_computes` optimization removes nodes and is the
31/// documented last structural pass before external ids are retained.
32#[derive(
33    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
34)]
35pub struct BloqNodeId(pub u32);
36
37/// A template's index into the program's shared [`crate::BloqTemplatePool`].
38#[derive(
39    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
40)]
41pub struct TemplateId(pub u32);
42
43/// A detector bundle's append-only index in a program.
44#[derive(
45    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
46)]
47pub struct DetectorBundleId(pub u32);
48
49/// A program-global id for one placement of a template: ids are unique
50/// across every graph level, not level-local like [`BloqNodeId`].
51#[derive(
52    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
53)]
54pub struct TemplateInstanceId(pub u32);
55
56/// One measurement within a template instance, named by the instance and the
57/// template-local measurement id
58#[derive(
59    Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
60)]
61pub struct InstanceMeasurement {
62    /// Owning template instance.
63    pub instance: TemplateInstanceId,
64    /// Template-local measurement id.
65    pub measurement: u32,
66}
67
68impl fmt::Display for InstanceMeasurement {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        write!(f, "i{}:m{}", self.instance.0, self.measurement)
71    }
72}
73
74/// A detector parity in template-local measurement-id space.
75pub type TemplateDetectorParity = DetectorParity<u32>;
76/// A measurement-only detector parity in instance space ([`InstanceMeasurement`]).
77/// Validation rejects the generic parity type's loop-state terms (WF-6).
78pub type NodeDetectorParity = DetectorParity<InstanceMeasurement>;
79
80#[cfg(test)]
81mod tests {
82    #[test]
83    fn pool_indices_never_alias_after_exhaustion() {
84        assert_eq!(super::pool_index(u32::MAX as usize), u32::MAX);
85        if let Some(exhausted) = (u32::MAX as usize).checked_add(1) {
86            std::panic::catch_unwind(|| super::pool_index(exhausted))
87                .expect_err("an exhausted append-only pool must refuse insertion");
88        }
89    }
90}