Skip to main content

comnum_spec/blocks/schema/
fact.rs

1//! The ports of a block: the [`Fact`]s of a frame, the check of a link, and the
2//! check of a block while it runs.
3
4use crate::blocks::{Block, BlockInfo, Frame};
5use comnum_core::rng::Rng;
6use comnum_core::{Error, Result};
7use std::borrow::Borrow;
8
9/// What the [`Frame`] of a trial holds at some point of a link.
10#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
11pub enum Fact {
12    /// Transmitted bits (the labels of the symbols, or the coded bits before `map`).
13    Bits,
14    /// Transmitted symbols.
15    Symbols,
16    /// The constellation of the transmitted symbols.
17    Constellation,
18    /// Samples.
19    Samples,
20    /// A noise variance: of the channel, declared by the receiver or per symbol.
21    Noise,
22    /// The fading of the trial (channel state information).
23    Csi,
24    /// The information bits of a coded link.
25    Payload,
26    /// Bit LLRs.
27    Llr,
28    /// Decoded information bits.
29    Decoded,
30    /// An OFDM frame between `ofdm_tx` and `ofdm_rx`.
31    Ofdm,
32    /// A WDM grid between `wdm_tx` and `wdm_rx`.
33    Wdm,
34    /// The reference sequence of a measured capture, not yet aligned.
35    Capture,
36}
37
38impl Fact {
39    /// Every fact.
40    pub const ALL: [Fact; 12] = [
41        Fact::Bits,
42        Fact::Symbols,
43        Fact::Constellation,
44        Fact::Samples,
45        Fact::Noise,
46        Fact::Csi,
47        Fact::Payload,
48        Fact::Llr,
49        Fact::Decoded,
50        Fact::Ofdm,
51        Fact::Wdm,
52        Fact::Capture,
53    ];
54
55    /// The name of the fact (`noise`, `llr`…).
56    pub fn name(self) -> &'static str {
57        match self {
58            Fact::Bits => "bits",
59            Fact::Symbols => "symbols",
60            Fact::Constellation => "constellation",
61            Fact::Samples => "samples",
62            Fact::Noise => "noise",
63            Fact::Csi => "csi",
64            Fact::Payload => "payload",
65            Fact::Llr => "llr",
66            Fact::Decoded => "decoded",
67            Fact::Ofdm => "ofdm",
68            Fact::Wdm => "wdm",
69            Fact::Capture => "capture",
70        }
71    }
72
73    /// Whether the frame holds the fact.
74    pub fn holds(self, s: &Frame) -> bool {
75        match self {
76            Fact::Bits => !s.bits.is_empty(),
77            Fact::Symbols => !s.symbols.is_empty(),
78            Fact::Constellation => s.constellation.is_some(),
79            Fact::Samples => !s.samples.is_empty(),
80            Fact::Noise => {
81                s.receiver.noise_var.is_some() || s.channel.noise_var.is_some() || !s.receiver.symbol_noise.is_empty()
82            }
83            Fact::Csi => s.channel.csi.is_some(),
84            Fact::Payload => !s.coding.payload.is_empty(),
85            Fact::Llr => !s.coding.llr.is_empty(),
86            Fact::Decoded => !s.coding.decoded.is_empty(),
87            Fact::Ofdm => s.in_flight.ofdm.is_some(),
88            Fact::Wdm => s.in_flight.wdm.is_some(),
89            Fact::Capture => !s.capture.reference.is_empty(),
90        }
91    }
92}
93
94/// Check the ports of a link: every block finds the facts it needs among those that
95/// the blocks before it give.
96///
97/// # Errors
98/// [`Error::Config`] naming the first block that misses a fact, and the blocks that give
99/// it.
100pub fn check_link<B: Borrow<BlockInfo>>(kinds: &[&str], registry: &[B]) -> Result<()> {
101    let registry: Vec<&BlockInfo> = registry.iter().map(Borrow::borrow).collect();
102    let mut have: Vec<Fact> = Vec::new();
103    for (i, k) in kinds.iter().enumerate() {
104        let Some(b) = registry.iter().find(|b| b.name == *k) else { continue };
105        if let Some(f) = b.needs.iter().find(|f| !have.contains(f)) {
106            let givers: Vec<&str> = registry.iter().filter(|g| g.gives.contains(f)).map(|g| g.name).collect();
107            return Err(Error::Config(format!(
108                "link block {} ({k}) needs {}, which no block before it gives: add one of {givers:?} before it",
109                i + 1,
110                f.name()
111            )));
112        }
113        have.extend(b.gives.iter().copied());
114    }
115    Ok(())
116}
117
118/// A block that checks its declaration while it runs (debug builds): the facts it
119/// gains must be declared in `gives` (what it needs is checked before the link runs,
120/// by [`check_link`]).
121#[cfg_attr(not(debug_assertions), allow(dead_code, reason = "built in debug builds only"))]
122pub(crate) struct Checked {
123    pub(crate) inner: Box<dyn Block>,
124    pub(crate) kind: String,
125    pub(crate) gives: &'static [Fact],
126}
127
128impl Block for Checked {
129    fn apply(&self, s: &mut Frame, rng: &mut Rng) -> Result<()> {
130        let before: Vec<Fact> = Fact::ALL.into_iter().filter(|f| f.holds(s)).collect();
131        self.inner.apply(s, rng)?;
132        if let Some(f) = Fact::ALL.into_iter().find(|f| f.holds(s) && !before.contains(f) && !self.gives.contains(f)) {
133            panic!("block {:?} gave {}, which it does not declare in gives", self.kind, f.name());
134        }
135        Ok(())
136    }
137
138    fn needs_training(&self) -> bool {
139        self.inner.needs_training()
140    }
141
142    fn train(&mut self, s: &Frame, rng: &mut Rng) -> Result<()> {
143        self.inner.train(s, rng)
144    }
145}