1use crate::coherence::CoherenceMeasure;
2use crate::error::SheafError;
3use crate::laplacian::SheafLaplacian;
4use crate::section::GlobalSection;
5use crate::sheaf::{CellularSheaf, SheafBuilder};
6
7#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
9pub struct AgentBelief {
10 pub id: String,
12 pub belief_vector: Vec<f64>,
14 pub confidence: f64,
16}
17
18impl AgentBelief {
19 pub fn new(id: impl Into<String>, belief_vector: Vec<f64>, confidence: f64) -> Self {
20 Self {
21 id: id.into(),
22 belief_vector,
23 confidence: confidence.clamp(0.0, 1.0),
24 }
25 }
26}
27
28#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
33pub struct AgentSheaf {
34 pub agents: Vec<AgentBelief>,
36 pub sheaf: CellularSheaf,
38}
39
40impl AgentSheaf {
41 pub fn complete(agents: Vec<AgentBelief>) -> Result<Self, SheafError> {
44 if agents.is_empty() {
45 return Err(SheafError::EmptySheaf);
46 }
47 let stalk_dim = agents[0].belief_vector.len();
48 for a in &agents {
49 if a.belief_vector.len() != stalk_dim {
50 return Err(SheafError::BeliefDimensionMismatch {
51 agent: a.id.clone(),
52 expected: stalk_dim,
53 got: a.belief_vector.len(),
54 });
55 }
56 }
57 let n = agents.len();
58 let sheaf = CellularSheaf::complete(n, stalk_dim)?;
59 Ok(Self { agents, sheaf })
60 }
61
62 pub fn path(agents: Vec<AgentBelief>) -> Result<Self, SheafError> {
64 if agents.is_empty() {
65 return Err(SheafError::EmptySheaf);
66 }
67 let stalk_dim = agents[0].belief_vector.len();
68 for a in &agents {
69 if a.belief_vector.len() != stalk_dim {
70 return Err(SheafError::BeliefDimensionMismatch {
71 agent: a.id.clone(),
72 expected: stalk_dim,
73 got: a.belief_vector.len(),
74 });
75 }
76 }
77 let n = agents.len();
78 let sheaf = if n == 1 {
79 CellularSheaf::constant(1, stalk_dim)?
80 } else {
81 CellularSheaf::path(n, stalk_dim)?
82 };
83 Ok(Self { agents, sheaf })
84 }
85
86 pub fn with_edges(agents: Vec<AgentBelief>, edges: &[(usize, usize)]) -> Result<Self, SheafError> {
88 if agents.is_empty() {
89 return Err(SheafError::EmptySheaf);
90 }
91 let stalk_dim = agents[0].belief_vector.len();
92 for a in &agents {
93 if a.belief_vector.len() != stalk_dim {
94 return Err(SheafError::BeliefDimensionMismatch {
95 agent: a.id.clone(),
96 expected: stalk_dim,
97 got: a.belief_vector.len(),
98 });
99 }
100 }
101
102 let identity = {
103 let mut m = vec![vec![0.0; stalk_dim]; stalk_dim];
104 for (i, row) in m.iter_mut().enumerate() {
105 row[i] = 1.0;
106 }
107 m
108 };
109
110 let mut builder = SheafBuilder::default();
111 for _ in &agents {
112 builder = builder.add_node(stalk_dim);
113 }
114 for &(i, j) in edges {
115 builder = builder.add_edge(i, j, identity.clone());
116 }
117 let sheaf = builder.build()?;
118
119 Ok(Self { agents, sheaf })
120 }
121
122 pub fn flat_beliefs(&self) -> Vec<f64> {
124 self.agents.iter().flat_map(|a| a.belief_vector.iter().copied()).collect()
125 }
126
127 pub fn laplacian(&self) -> Result<SheafLaplacian, SheafError> {
129 SheafLaplacian::from_sheaf(&self.sheaf)
130 }
131
132 pub fn coherence(&self, max_iter: usize, tol: f64) -> Result<CoherenceMeasure, SheafError> {
134 let flat = self.flat_beliefs();
135 CoherenceMeasure::from_flat(&self.sheaf, &flat, max_iter, tol)
136 }
137
138 pub fn global_section(&self, tol: f64) -> Result<GlobalSection, SheafError> {
140 let values: Vec<Vec<f64>> = self.agents.iter().map(|a| a.belief_vector.clone()).collect();
141 GlobalSection::new(&self.sheaf, values, tol)
142 }
143
144 pub fn len(&self) -> usize {
146 self.agents.len()
147 }
148
149 pub fn is_empty(&self) -> bool {
151 self.agents.is_empty()
152 }
153}