fugue/core/address.rs
1#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/docs/core/address.md"))]
2use std::fmt::{Display, Formatter};
3
4/// A unique identifier for random variables and observation sites in probabilistic models.
5/// Addresses serve as stable names for probabilistic choices, enabling conditioning, inference, and replay.
6/// They are implemented as wrapped strings with ordering and hashing support for use in collections.
7///
8/// Example:
9/// ```rust
10/// use fugue::*;
11/// // Create addresses using the addr! macro
12/// let addr1 = addr!("parameter");
13/// let addr2 = addr!("data", 5);
14/// // Addresses can be compared and used in collections
15/// use std::collections::HashMap;
16/// let mut map = HashMap::new();
17/// map.insert(addr1, 1.0);
18/// map.insert(addr2, 2.0);
19/// ```
20#[derive(Clone, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
21pub struct Address(pub String);
22impl Display for Address {
23 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
24 write!(f, "{}", self.0)
25 }
26}
27
28/// Create an address for naming random variables and observation sites.
29/// This macro provides a convenient way to create `Address` instances with human-readable names and optional indices.
30/// The macro supports two forms:
31///
32/// - `addr!("name")` - Simple named address
33/// - `addr!("name", index)` - Indexed address using "name#index" format
34///
35/// Example:
36/// ```rust
37/// use fugue::*;
38/// // Simple addresses
39/// let mu = addr!("mu");
40/// let sigma = addr!("sigma");
41/// // Indexed addresses for collections
42/// let data_0 = addr!("data", 0);
43/// let data_1 = addr!("data", 1);
44/// // Use in models
45/// let model = sample(addr!("x"), Normal::new(0.0, 1.0).unwrap())
46/// .bind(|x| {
47/// // Index can be dynamic
48/// let i = 42;
49/// sample(addr!("y", i), Normal::new(x, 0.1).unwrap())
50/// });
51/// ```
52#[macro_export]
53macro_rules! addr {
54 ($name:expr) => {
55 $crate::core::address::Address($name.to_string())
56 };
57 ($name:expr, $i:expr) => {
58 $crate::core::address::Address(format!("{}#{}", $name, $i))
59 };
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65 use std::collections::{BTreeSet, HashSet};
66
67 #[test]
68 fn display_formats_inner_string() {
69 let a = Address("alpha".to_string());
70 assert_eq!(a.to_string(), "alpha");
71 }
72
73 #[test]
74 fn addr_macro_basic_and_indexed() {
75 let a = addr!("x");
76 assert_eq!(a.0, "x");
77
78 let b = addr!("x", 3);
79 assert_eq!(b.0, "x#3");
80 }
81
82 #[test]
83 fn equality_hash_and_ordering() {
84 let a1 = Address("x".into());
85 let a2 = Address("x".into());
86 let b = Address("y".into());
87
88 // Eq/Hash
89 let mut set = HashSet::new();
90 set.insert(a1.clone());
91 set.insert(a2.clone());
92 set.insert(b.clone());
93 assert_eq!(set.len(), 2);
94
95 // Ord/PartialOrd via BTreeSet (lexicographic)
96 let mut bset = BTreeSet::new();
97 bset.insert(b);
98 bset.insert(a1);
99 // Expect alphabetical order: "x" comes after "y"? No, "x" < "y"
100 let ordered: Vec<String> = bset.into_iter().map(|a| a.0).collect();
101 assert_eq!(ordered, vec!["x".to_string(), "y".to_string()]);
102 }
103}