1use crate::actors::prelude::{Driver, OperatorKind, TriadDriver, VNode};
6use crate::frag::FragTonnetz;
7use crate::types::{NodeCapabilities, NodeMessage};
8use rshyper::{EdgeId, VertexId};
9use rstmt::nrt::{LPR, Triad};
10use rstmt::{Aspn as Note, PitchMod};
11use std::collections::{HashMap, HashSet};
12
13impl<D> FragTonnetz<D>
14where
15 D: Driver<Triad>,
16{
17 pub fn add_triad(&mut self, triad: Triad) -> crate::Result<EdgeId> {
19 let mut vertex_ids = Vec::with_capacity(3);
21
22 for note_class in triad.notes() {
23 if let Some(id) = self.find_vertex_by_pitch(note_class) {
25 vertex_ids.push(id);
26 } else {
27 let note = Note::new(note_class, triad.octave());
29 let vertex_id = self.add_note(note)?;
30 vertex_ids.push(vertex_id);
31 }
32 }
33
34 let edge_id = self.add_edge(vertex_ids)?;
36
37 let plant = D::from_headspace(triad);
39 let vnode = VNode::from_driver(plant);
40
41 self.partitions_mut().insert(edge_id, vnode);
43
44 Ok(edge_id)
45 }
46 pub fn add_transformation(&mut self, edge_id: EdgeId) -> crate::Result<()> {
48 if let Some(source_node) = self.get_vnode(&edge_id) {
49 let source_triad = *source_node.driver().headspace();
50 let mut edge_transformations = HashMap::new();
51
52 for transform in [LPR::Leading, LPR::Parallel, LPR::Relative] {
54 let target_triad = source_triad.transform(transform);
55 if let Some(target_edge) = self.find_edge_by_notes(target_triad.notes()) {
57 edge_transformations.insert(transform, target_edge);
58 }
59 }
60
61 if !edge_transformations.is_empty() {
63 self.transformations.insert(edge_id, edge_transformations);
64 }
65
66 for (&other_edge, other_node) in &self.partitions {
68 if other_edge == edge_id {
69 continue;
70 }
71
72 let other_triad = *other_node.driver().headspace();
73
74 for transform in LPR::iter() {
75 let transformed = other_triad.transform(transform);
76 if transformed.notes() == source_triad.notes() {
77 self.transformations
79 .entry(other_edge)
80 .or_default()
81 .insert(transform, edge_id);
82 }
83 }
84 }
85
86 Ok(())
87 } else {
88 Err(crate::RuntimeError::NodeNotFound)
89 }
90 }
91 pub fn balance_resources(&mut self) -> crate::Result<()> {
93 for node in self.partitions_mut().values_mut() {
99 node.optimize_memory(1000)?;
100 }
101
102 Ok(())
103 }
104 pub fn batch_transform_sequential(
106 &mut self,
107 operations: Vec<(EdgeId, Vec<LPR>)>,
108 ) -> crate::Result<()>
109 where
110 D: TriadDriver,
111 {
112 for (edge_id, transforms) in operations {
113 let node = self.get_vnode_mut(&edge_id).expect("Node not found");
114 node.transform_batch(&transforms)?;
115 }
116 Ok(())
117 }
118 pub fn calculate_node_capabilities(&self) -> HashMap<EdgeId, NodeCapabilities> {
120 self.partitions
121 .iter()
122 .map(|(&id, node)| {
123 (
124 id,
125 NodeCapabilities {
126 memory_usage: node.store().size(),
127 compute_capability: if node.has_surface_network() { 10 } else { 5 },
128 learning_capability: node.has_surface_network(),
129 feature_count: node.total_features(),
130 },
131 )
132 })
133 .collect()
134 }
135 pub fn calculate_resource_usage(&self) -> (usize, usize) {
137 let memory_usage = self
139 .partitions()
140 .values()
141 .map(|node| node.total_features())
142 .sum();
143
144 let compute_usage = self.total_partitions() * 10 + self.total_edges();
146
147 (memory_usage, compute_usage)
148 }
149 pub fn collect_proposed_transformations(&self) -> Vec<(EdgeId, LPR)> {
151 let mut proposals = Vec::new();
152
153 for (&edge_id, node) in self.partitions() {
154 if let OperatorKind::Agent = node.kind() {
155 if let Some(transform) = node.propose_transformation() {
156 proposals.push((edge_id, transform));
157 }
158 }
159 }
160
161 proposals
162 }
163 pub fn compute_transformations(&mut self) {
165 self.transformations_mut().clear();
167
168 let edges: Vec<EdgeId> = self.partitions().keys().cloned().collect();
170
171 for &source_edge in &edges {
172 let mut edge_transformations = HashMap::new();
173
174 if let Some(source_node) = self.get_vnode(&source_edge) {
175 let source_triad = *source_node.driver().headspace();
176
177 for transform in [LPR::Leading, LPR::Parallel, LPR::Relative] {
179 let target_triad = source_triad.transform(transform);
180
181 if let Some(target_edge) = self.find_edge_by_notes(target_triad.notes()) {
183 edge_transformations.insert(transform, target_edge);
184 }
185 }
186 }
187
188 if !edge_transformations.is_empty() {
190 self.transformations
191 .insert(source_edge, edge_transformations);
192 }
193 }
194 }
195 pub fn coordinate_learning(&mut self) -> crate::Result<()> {
197 if self.partitions().len() <= 1 {
199 return Ok(());
200 }
201
202 let knowledge_nodes: Vec<EdgeId> = self
205 .partitions
206 .iter()
207 .filter_map(|(&id, node)| {
208 if node.has_surface_network() && node.total_features() > 0 {
209 Some(id)
210 } else {
211 None
212 }
213 })
214 .collect();
215
216 if knowledge_nodes.len() <= 1 {
217 return Ok(());
218 }
219
220 let total_transfers = core::cmp::min(50, knowledge_nodes.len() * 3);
223 let mut transfers_done = 0;
224
225 for source_id in &knowledge_nodes {
227 if transfers_done >= total_transfers {
228 break;
229 }
230
231 let patterns = match self
233 .get_vnode(source_id)
234 .and_then(|node| node.extract_knowledge_patterns().ok())
235 {
236 Some(p) if !p.is_empty() => p,
237 _ => continue,
238 };
239
240 let max_targets = std::cmp::min(3, knowledge_nodes.len() - 1);
242 let mut targets_for_this_source = 0;
243
244 for target_id in &knowledge_nodes {
246 if source_id == target_id || targets_for_this_source >= max_targets {
247 continue;
248 }
249
250 if let Some(target) = self.get_vnode_mut(target_id) {
252 if let Ok(_) = target.integrate_external_knowledge(&patterns, source_id) {
253 targets_for_this_source += 1;
254 transfers_done += 1;
255 }
256 }
257
258 if transfers_done >= total_transfers {
259 break;
260 }
261 }
262 }
263
264 if transfers_done > 0 {
266 for (_, node) in self.partitions.iter_mut().take(5) {
268 node.store_mut()
269 .record_event("knowledge_coordination", Some(vec![knowledge_nodes.len()]));
270 }
271 }
272
273 Ok(())
274 }
275 pub fn create_node(&mut self, triad: Triad) -> crate::Result<EdgeId> {
277 if let Some(edge_id) = self.find_edge_by_notes(triad.notes()) {
279 return Ok(edge_id); }
281
282 self.add_triad(triad)
284 }
285 pub fn execute_transformation_path(&mut self, path: &[(EdgeId, LPR)]) -> crate::Result<()>
287 where
288 D: TriadDriver,
289 {
290 for &(node_id, transform) in path {
291 self.send_command(node_id, transform)?;
292 }
293 Ok(())
294 }
295 pub fn find_edge_by_notes(&self, notes: [usize; 3]) -> Option<EdgeId> {
297 self.partitions.iter().find_map(|(&edge_id, vnode)| {
298 if vnode.get_notes() == notes {
299 Some(edge_id)
300 } else {
301 None
302 }
303 })
304 }
305 pub fn get_vnode(&self, id: &EdgeId) -> Option<&VNode<D>> {
307 self.partitions().get(id)
308 }
309 pub fn get_vnode_mut(&mut self, id: &EdgeId) -> Option<&mut VNode<D>> {
311 self.partitions_mut().get_mut(id)
312 }
313 pub fn find_node_by_name(&self, name: &str) -> Option<&VNode<D>> {
315 self.partitions()
316 .values()
317 .find(|node| node.store().get_property("name").is_some_and(|n| n == name))
318 }
319 pub fn find_optimal_node_for(&self, work_type: &str) -> Option<EdgeId> {
321 let capabilities = self.calculate_node_capabilities();
322
323 match work_type {
324 "learning" => capabilities
325 .iter()
326 .filter(|(_, cap)| cap.learning_capability)
327 .min_by_key(|(_, cap)| cap.memory_usage)
328 .map(|(&id, _)| id),
329
330 "pattern_recognition" => capabilities
331 .iter()
332 .max_by_key(|(_, cap)| cap.feature_count)
333 .map(|(&id, _)| id),
334
335 "transformation" => capabilities
336 .iter()
337 .min_by_key(|(_, cap)| cap.memory_usage)
338 .map(|(&id, _)| id),
339
340 _ => None,
341 }
342 }
343 pub fn find_related_nodes(&self, node_id: EdgeId) -> Vec<EdgeId> {
345 let mut related = HashSet::new();
346
347 if let Some(transforms) = self.transformations().get(&node_id) {
349 for &target_id in transforms.values() {
350 related.insert(target_id);
351 }
352 }
353
354 for (&source, transforms) in self.transformations() {
356 for &target in transforms.values() {
357 if target == node_id {
358 related.insert(source);
359 }
360 }
361 }
362
363 related.into_iter().collect()
365 }
366 pub fn find_nodes_with_operator(&self, mode: OperatorKind) -> Vec<EdgeId> {
368 self.partitions()
369 .iter()
370 .filter_map(
371 |(&id, node)| {
372 if node.kind() == mode { Some(id) } else { None }
373 },
374 )
375 .collect()
376 }
377 pub fn find_nodes_with_pitch(&self, pitch: usize) -> Vec<EdgeId> {
379 self.partitions()
380 .iter()
381 .filter_map(|(&id, node)| {
382 if node.get_notes().contains(&(pitch % 12)) {
383 Some(id)
384 } else {
385 None
386 }
387 })
388 .collect()
389 }
390 pub fn find_transformation_path(
392 &self,
393 source_id: EdgeId,
394 target_id: EdgeId,
395 ) -> Option<Vec<(EdgeId, LPR)>> {
396 if !self.partitions.contains_key(&source_id) || !self.partitions.contains_key(&target_id) {
398 return None;
399 }
400
401 use std::collections::VecDeque;
403
404 let mut queue = VecDeque::new();
405 let mut visited = HashSet::new();
406 let mut came_from: HashMap<EdgeId, (EdgeId, LPR)> = HashMap::new();
407
408 queue.push_back(source_id);
409 visited.insert(source_id);
410
411 while let Some(current) = queue.pop_front() {
412 if current == target_id {
413 let mut path = Vec::new();
415 let mut node_id = current;
416
417 while let Some(&(prev_id, transform)) = came_from.get(&node_id) {
418 path.push((prev_id, transform));
419 node_id = prev_id;
420 }
421
422 path.reverse();
423 return Some(path);
424 }
425
426 if let Some(transforms) = self.transformations.get(¤t) {
428 for (&transform, &next) in transforms {
429 if !visited.contains(&next) {
430 visited.insert(next);
431 came_from.insert(next, (current, transform));
432 queue.push_back(next);
433 }
434 }
435 }
436 }
437
438 None }
440 pub fn find_vertex_by_pitch(&self, pitch: usize) -> Option<VertexId> {
442 self.graph()
443 .nodes()
444 .iter()
445 .find(|(_, node)| node.weight().class() == pitch.pmod())
446 .map(|(id, _)| *id)
447 }
448 pub fn get_all_edge_loads(&self) -> HashMap<EdgeId, f32> {
450 self.partitions()
451 .iter()
452 .map(|(&id, node)| {
453 let feature_count = node.total_features() as f32;
455 let has_surface = if node.has_surface_network() { 1.5 } else { 1.0 };
456 let load = (feature_count / 1000.0 * has_surface).min(1.0);
457 (id, load)
458 })
459 .collect()
460 }
461 pub fn get_transformations(&self, index: &EdgeId) -> HashMap<LPR, EdgeId> {
463 self.transformations()
464 .get(index)
465 .cloned()
466 .unwrap_or_default()
467 }
468 pub fn initialize_with_all_triads(&mut self) -> crate::Result<()> {
470 for root in 0..12 {
472 self.add_triad(Triad::major(root))?;
473 self.add_triad(Triad::minor(root))?;
474 self.add_triad(Triad::diminished(root))?;
475 self.add_triad(Triad::augmented(root))?;
476 }
477
478 self.compute_transformations();
480
481 Ok(())
482 }
483 pub fn mode_distribution(&self) -> HashMap<OperatorKind, f32> {
485 use strum::IntoEnumIterator;
486 let mut distribution = HashMap::new();
487
488 for mode in OperatorKind::iter() {
489 distribution.insert(mode, 0.0);
490 }
491
492 let total = self.partitions().len() as f32;
493 if total == 0.0 {
494 return distribution;
495 }
496
497 for node in self.partitions().values() {
498 let mode = node.kind();
499 *distribution.entry(mode).or_insert(0.0) += 1.0;
500 }
501
502 for count in distribution.values_mut() {
504 *count /= total;
505 }
506
507 distribution
508 }
509 pub fn optimize_for_real_time(&mut self) -> crate::Result<()>
511 where
512 D: TriadDriver,
513 {
514 self.optimize_operator_distribution()?;
516
517 self.balance_resources()?;
519
520 self.compute_transformations();
522
523 self.share_patterns()?;
525
526 self.coordinate_learning()?;
528
529 Ok(())
530 }
531 pub fn optimize_memory(&mut self, max_features: usize) -> crate::Result<()> {
533 for node in self.partitions_mut().values_mut() {
534 let _stats = node.optimize_memory(max_features)?;
535 }
536 Ok(())
537 }
538 pub fn optimize_operator_distribution(&mut self) -> crate::Result<()> {
540 let mut operator_counts = HashMap::new();
542
543 for node in self.partitions().values() {
544 let kind = node.kind();
545 *operator_counts.entry(kind).or_insert(0) += 1;
546 }
547
548 if !operator_counts.contains_key(&OperatorKind::Observer) {
551 if let Some((&id, _)) = self.partitions().iter().next() {
553 self.set_node_operator(id, OperatorKind::Observer)?;
554 }
555 }
556
557 if !operator_counts.contains_key(&OperatorKind::Agent) {
558 if let Some((&id, _)) = self.partitions().iter().nth(1) {
560 self.set_node_operator(id, OperatorKind::Agent)?;
561 }
562 }
563
564 Ok(())
565 }
566 pub fn optimal_voice_leading_distance(&self, src: &EdgeId, dest: &EdgeId) -> Option<usize> {
568 let source_node = self.get_vnode(src)?;
569 let target_node = self.get_vnode(dest)?;
570
571 let source_notes = source_node.get_notes();
572 let target_notes = target_node.get_notes();
573
574 fn permutations<T: Copy>(arr: &[T]) -> Vec<Vec<T>> {
576 if arr.len() <= 1 {
577 return vec![arr.to_vec()];
578 }
579
580 let mut result = Vec::new();
581 for (i, &item) in arr.iter().enumerate() {
582 let mut sub_arr = arr.to_vec();
583 sub_arr.remove(i);
584
585 for mut perm in permutations(&sub_arr) {
586 perm.insert(0, item);
587 result.push(perm);
588 }
589 }
590
591 result
592 }
593
594 let target_perms = permutations(&target_notes);
596
597 target_perms
599 .iter()
600 .map(|perm| {
601 source_notes
602 .iter()
603 .zip(perm)
604 .map(|(&s, &t)| {
605 let dist = ((t as isize - s as isize).abs()).pmod() as usize;
606 std::cmp::min(dist, 12 - dist)
607 })
608 .sum()
609 })
610 .min()
611 }
612 pub fn process_message(&mut self, message: &NodeMessage) -> crate::Result<()>
614 where
615 D: TriadDriver,
616 {
617 match message {
618 NodeMessage::TransformRequest {
619 source,
620 target,
621 transform,
622 } => {
623 if !self.partitions.contains_key(source) {
625 return Err(crate::RuntimeError::NodeNotFound);
626 }
627
628 self.send_command(*target, *transform)
630 }
631
632 NodeMessage::StateSync { source, .. } => {
633 if !self.partitions.contains_key(source) {
636 return Err(crate::RuntimeError::NodeNotFound);
637 }
638 Ok(())
639 }
640
641 NodeMessage::PatternShare {
642 source, pattern, ..
643 } => {
644 if !self.partitions.contains_key(source) {
646 return Err(crate::RuntimeError::NodeNotFound);
647 }
648
649 for (&id, node) in self.partitions_mut() {
651 if id != *source {
652 let _ = node.learn_transformation_sequence(pattern);
653 }
654 }
655
656 Ok(())
657 }
658 }
659 }
660 pub fn process_stream<I>(&mut self, stream: I) -> crate::Result<Vec<(EdgeId, Vec<LPR>)>>
662 where
663 I: Iterator<Item = (Triad, Vec<LPR>)>,
664 D: TriadDriver,
665 {
666 let mut results = Vec::new();
667
668 for (target_triad, transforms) in stream {
669 let edge_id = match self.find_edge_by_notes(target_triad.notes()) {
671 Some(id) => id,
672 None => self.add_triad(target_triad)?,
673 };
674
675 if let Some(node) = self.get_vnode_mut(&edge_id) {
677 node.transform_batch(&transforms)?;
678
679 results.push((edge_id, transforms));
681 }
682 }
683
684 Ok(results)
685 }
686 pub fn process_transformation_stream<I>(&mut self, stream: I) -> crate::Result<()>
688 where
689 I: Iterator<Item = (EdgeId, LPR)>,
690 D: TriadDriver,
691 {
692 let optimal_batch_size = 10; let mut current_batch = Vec::with_capacity(optimal_batch_size);
696 let mut current_edge = None;
697
698 for (edge_id, transform) in stream {
699 if current_edge != Some(edge_id) && !current_batch.is_empty() {
701 if let Some(edge) = current_edge {
702 if let Some(node) = self.get_vnode_mut(&edge) {
703 node.transform_batch(¤t_batch)?;
704 }
705 }
706 current_batch.clear();
707 }
708
709 current_edge = Some(edge_id);
711 current_batch.push(transform);
712
713 if current_batch.len() >= optimal_batch_size {
715 if let Some(node) = self.get_vnode_mut(&edge_id) {
716 node.transform_batch(¤t_batch)?;
717 }
718 current_batch.clear();
719 }
720 }
721
722 if let Some(edge_id) = current_edge {
724 if !current_batch.is_empty() {
725 if let Some(node) = self.get_vnode_mut(&edge_id) {
726 node.transform_batch(¤t_batch)?;
727 }
728 }
729 }
730
731 Ok(())
732 }
733 pub fn send_command(&mut self, node_id: EdgeId, transform: LPR) -> crate::Result<()>
735 where
736 D: TriadDriver,
737 {
738 if let Some(node) = self.get_vnode_mut(&node_id) {
739 node.transform(transform).map_err(|err| err.into())
740 } else {
741 Err(crate::RuntimeError::NodeNotFound)
742 }
743 }
744 pub fn set_all_nodes_mode(&mut self, mode: OperatorKind) -> crate::Result<()> {
746 for node in self.partitions_mut().values_mut() {
747 node.set_operator_by_kind(mode);
748 }
749 Ok(())
750 }
751 pub fn set_node_operator(&mut self, node_id: EdgeId, mode: OperatorKind) -> crate::Result<()> {
753 if let Some(node) = self.get_vnode_mut(&node_id) {
754 node.set_operator_by_kind(mode);
755 Ok(())
756 } else {
757 Err(crate::RuntimeError::NodeNotFound)
758 }
759 }
760 pub fn share_patterns(&mut self) -> crate::Result<()> {
762 let mut all_patterns = Vec::new();
764
765 for (&edge_id, node) in &self.partitions {
766 let node_patterns = node
768 .store()
769 .patterns()
770 .iter()
771 .map(|p| (edge_id, p.sequence().clone(), *p.importance()))
772 .collect::<Vec<_>>();
773
774 all_patterns.extend(node_patterns);
775 }
776
777 all_patterns
779 .sort_by(|(_, _, a), (_, _, b)| b.partial_cmp(a).unwrap_or(std::cmp::Ordering::Equal));
780
781 let top_patterns = all_patterns.into_iter().take(10);
783
784 for (source_id, pattern, ..) in top_patterns {
785 for (&target_id, target_node) in self.partitions_mut() {
786 if target_id != source_id {
787 let lpr_pattern = pattern
789 .clone()
790 .into_iter()
791 .filter_map(|p| match p {
792 0 => Some(LPR::Leading),
793 1 => Some(LPR::Parallel),
794 2 => Some(LPR::Relative),
795 _ => None,
796 })
797 .collect::<Vec<_>>();
798
799 let _ = target_node.learn_transformation_sequence(&lpr_pattern);
801 }
802 }
803 }
804
805 Ok(())
806 }
807 pub fn split_for_distributed_processing(&self) -> Vec<(EdgeId, Vec<LPR>)> {
809 let _capabilities = self.calculate_node_capabilities();
811
812 let mut workloads = Vec::new();
814
815 for id in self.partitions.keys() {
816 if let Some(node) = self.get_vnode(id) {
818 if node.kind() == OperatorKind::Agent {
819 if let Some(transform) = node.propose_transformation() {
821 workloads.push((*id, vec![transform]));
822 }
823 }
824 }
825 }
826
827 workloads
828 }
829 pub fn voice_leading_distance(&self, from: &EdgeId, to: &EdgeId) -> Option<usize> {
831 let from_node = self.get_vnode(from)?;
833 let to_node = self.get_vnode(to)?;
834
835 let from_triad = from_node.headspace();
836 let to_triad = to_node.headspace();
837
838 let from_notes = from_triad.notes();
840 let to_notes = to_triad.notes();
841
842 let mut distance = 0;
844
845 for from_note in from_notes {
847 let mut min_dist = 12; for to_note in to_notes {
849 let direct_dist = ((to_note as isize - from_note as isize).abs()).pmod() as usize;
851 min_dist = min_dist.min(direct_dist);
852 }
853 distance += min_dist;
854 }
855
856 Some(distance)
857 }
858}