1use serde_json::Value;
19use std::collections::{HashMap, HashSet};
20
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
23pub enum ChangeType {
24 Parameter,
28
29 Metadata,
33
34 Structural,
38
39 Hybrid,
41}
42
43pub struct CorticalChangeClassifier;
45
46impl CorticalChangeClassifier {
47 pub fn structural_changes() -> HashSet<&'static str> {
52 [
53 "cortical_dimensions",
55 "cortical_dimensions_per_device",
56 "dimensions",
57 "per_voxel_neuron_cnt",
59 "cortical_neuron_per_vox_count",
60 "neuron_density",
61 "neurons_per_voxel",
62 "cortical_type",
64 "area_type",
65 "cortical_mapping_dst",
67 "group_id",
69 "sub_group_id",
70 "region_id",
71 "brain_region_id",
72 "parent_region_id",
73 ]
74 .iter()
75 .copied()
76 .collect()
77 }
78
79 pub fn metadata_changes() -> HashSet<&'static str> {
84 [
85 "cortical_name",
86 "name",
87 "visible",
88 "coordinate_2d",
90 "coordinates_2d",
91 "coordinates_3d",
92 "coordinate_3d",
93 "coordinates",
94 "position",
95 "visualization_voxel_granularity",
97 "coding_signage",
99 "coding_behavior",
100 "coding_type",
101 "new_cortical_id",
102 ]
103 .iter()
104 .copied()
105 .collect()
106 }
107
108 pub fn parameter_changes() -> HashSet<&'static str> {
113 [
114 "firing_threshold",
116 "neuron_fire_threshold",
117 "firing_threshold_limit",
118 "neuron_firing_threshold_limit",
119 "firing_threshold_increment",
121 "neuron_fire_threshold_increment",
122 "firing_threshold_increment_x",
123 "firing_threshold_increment_y",
124 "firing_threshold_increment_z",
125 "refractory_period",
127 "neuron_refractory_period",
128 "refrac",
129 "leak_coefficient",
131 "neuron_leak_coefficient",
132 "leak",
133 "consecutive_fire_cnt_max",
136 "neuron_consecutive_fire_count",
137 "consecutive_fire_count",
138 "snooze_length",
140 "neuron_snooze_period",
141 "snooze_period",
142 "neuron_excitability",
144 "degeneration",
146 "neuron_degeneracy_coefficient",
147 "postsynaptic_current",
149 "neuron_post_synaptic_potential",
150 "postsynaptic_current_max",
151 "neuron_post_synaptic_potential_max",
152 "longterm_mem_threshold",
154 "neuron_longterm_mem_threshold",
155 "lifespan_growth_rate",
156 "neuron_lifespan_growth_rate",
157 "init_lifespan",
158 "neuron_init_lifespan",
159 "temporal_depth",
160 "mp_charge_accumulation",
162 "neuron_mp_charge_accumulation",
163 "mp_driven_psp",
164 "neuron_mp_driven_psp",
165 "psp_uniform_distribution",
168 "neuron_psp_uniform_distribution",
169 "plasticity_constant",
171 "burst_engine_active",
173 ]
174 .iter()
175 .copied()
176 .collect()
177 }
178
179 pub fn special_parameters() -> HashSet<&'static str> {
184 [
185 "leak_variability",
186 "neuron_leak_variability",
187 "is_mem_type",
188 "dev_count",
189 "synapse_attractivity",
190 "visualization",
191 "location_generation_type",
192 ]
193 .iter()
194 .copied()
195 .collect()
196 }
197
198 pub fn classify_changes(changes: &HashMap<String, Value>) -> ChangeType {
200 let structural = Self::structural_changes();
201 let parameters = Self::parameter_changes();
202 let metadata = Self::metadata_changes();
203 let special = Self::special_parameters();
204
205 let has_structural = changes.keys().any(|k| structural.contains(k.as_str()));
206 let has_parameters = changes.keys().any(|k| parameters.contains(k.as_str()));
207 let has_metadata = changes.keys().any(|k| metadata.contains(k.as_str()));
208 let has_special = changes.keys().any(|k| special.contains(k.as_str()));
209
210 let change_count = [has_structural, has_parameters, has_metadata, has_special]
212 .iter()
213 .filter(|&&x| x)
214 .count();
215
216 if change_count > 1 {
217 ChangeType::Hybrid
218 } else if has_structural || has_special {
219 ChangeType::Structural
221 } else if has_parameters {
222 ChangeType::Parameter
223 } else if has_metadata {
224 ChangeType::Metadata
225 } else {
226 tracing::warn!("Unknown change types detected: {:?}", changes.keys());
228 ChangeType::Structural
229 }
230 }
231
232 pub fn separate_changes_by_type(
234 changes: &HashMap<String, Value>,
235 ) -> HashMap<ChangeType, HashMap<String, Value>> {
236 let structural = Self::structural_changes();
237 let parameters = Self::parameter_changes();
238 let metadata = Self::metadata_changes();
239 let special = Self::special_parameters();
240
241 let mut separated = HashMap::new();
242 separated.insert(ChangeType::Structural, HashMap::new());
243 separated.insert(ChangeType::Parameter, HashMap::new());
244 separated.insert(ChangeType::Metadata, HashMap::new());
245
246 for (key, value) in changes {
247 if structural.contains(key.as_str()) || special.contains(key.as_str()) {
248 separated
249 .get_mut(&ChangeType::Structural)
250 .unwrap()
251 .insert(key.clone(), value.clone());
252 } else if parameters.contains(key.as_str()) {
253 separated
254 .get_mut(&ChangeType::Parameter)
255 .unwrap()
256 .insert(key.clone(), value.clone());
257 } else if metadata.contains(key.as_str()) {
258 separated
259 .get_mut(&ChangeType::Metadata)
260 .unwrap()
261 .insert(key.clone(), value.clone());
262 } else {
263 separated
265 .get_mut(&ChangeType::Structural)
266 .unwrap()
267 .insert(key.clone(), value.clone());
268 }
269 }
270
271 separated
272 }
273
274 pub fn log_classification_result(changes: &HashMap<String, Value>, change_type: ChangeType) {
276 let change_summary: Vec<String> = changes
277 .iter()
278 .map(|(k, v)| format!("{}={}", k, v))
279 .collect();
280
281 tracing::info!(
282 "[CHANGE-CLASSIFIER] Type: {:?} | Changes: {}",
283 change_type,
284 change_summary.join(", ")
285 );
286
287 match change_type {
288 ChangeType::Parameter => {
289 tracing::info!(
290 "[OPTIMIZATION] Fast parameter update path selected - avoiding synapse rebuild"
291 );
292 }
293 ChangeType::Metadata => {
294 tracing::info!("[OPTIMIZATION] Metadata-only update - minimal processing required");
295 }
296 ChangeType::Structural => {
297 tracing::info!("[STRUCTURAL] Synapse rebuild required for this change");
298 }
299 ChangeType::Hybrid => {
300 tracing::info!(
301 "[HYBRID] Mixed changes - using optimized combination of update paths"
302 );
303 }
304 }
305 }
306}