-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathdeep_tree_echo.py
More file actions
1445 lines (1197 loc) · 61.2 KB
/
Copy pathdeep_tree_echo.py
File metadata and controls
1445 lines (1197 loc) · 61.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
import logging
from dataclasses import dataclass, field
from typing import List, Dict, Optional, Any, Tuple
import numpy as np
from collections import deque
from pathlib import Path
from ml_system import MLSystem
from emotional_dynamics import EmotionalDynamics, EmotionalState, CoreEmotion
from differential_emotion_theory import DifferentialEmotionSystem, DETState, DETEmotion, EmotionalScript
# Import standardized Echo components for API compatibility
try:
from echo_component_base import MemoryEchoComponent, EchoConfig, EchoResponse
ECHO_STANDARDIZED_AVAILABLE = True
except ImportError:
MemoryEchoComponent = object
EchoConfig = None
EchoResponse = None
ECHO_STANDARDIZED_AVAILABLE = False
@dataclass
class SpatialContext:
"""Spatial context for 3D environment awareness"""
position: Tuple[float, float, float] = (0.0, 0.0, 0.0) # x, y, z coordinates
orientation: Tuple[float, float, float] = (0.0, 0.0, 0.0) # pitch, yaw, roll
scale: float = 1.0 # Scale factor
depth: float = 1.0 # Depth in 3D space
field_of_view: float = 90.0 # Field of view in degrees
spatial_relations: Dict[str, Any] = field(default_factory=dict) # Relations to other objects
spatial_memory: Dict[str, Any] = field(default_factory=dict) # Memory of spatial configurations
@dataclass
class TreeNode:
content: str
echo_value: float = 0.0
children: List['TreeNode'] = None
parent: Optional['TreeNode'] = None
metadata: Dict[str, Any] = None
emotional_state: np.ndarray = None
det_state: Optional[DETState] = None # Differential Emotion Theory state
spatial_context: Optional[SpatialContext] = None # 3D spatial awareness context
def __post_init__(self):
if self.children is None:
self.children = []
if self.metadata is None:
self.metadata = {}
if self.emotional_state is None:
self.emotional_state = np.array([0.1] * 7) # Default mild emotional state
if self.det_state is None:
self.det_state = None # Will be initialized when needed
if self.spatial_context is None:
self.spatial_context = SpatialContext() # Default spatial context
@dataclass
class MembraneMessage:
"""Message structure for inter-membrane communication"""
source_membrane: str
target_membrane: str
message_type: str
data: Any
timestamp: float = field(default_factory=lambda: __import__('time').time())
priority: int = 1 # 1=low, 2=medium, 3=high, 4=critical
security_level: str = "standard" # "standard", "secure", "encrypted"
class Membrane:
"""Base class for P-System membranes providing computational boundaries"""
def __init__(self, name: str, membrane_type: str, security_level: str = "standard"):
self.name = name
self.membrane_type = membrane_type
self.security_level = security_level
self.logger = logging.getLogger(f"{__name__}.{name}")
self.state = "initialized"
self.resources = {"memory": 0, "cpu": 0, "io": 0}
self.message_queue: deque = deque(maxlen=1000)
self.child_membranes: List['Membrane'] = []
self.parent_membrane: Optional['Membrane'] = None
self.permissions = set()
self.isolation_enabled = True
def send_message(self, target_membrane: str, message_type: str, data: Any,
priority: int = 1, security_level: str = "standard") -> bool:
"""Send message to another membrane"""
if not self._validate_communication(target_membrane, message_type, security_level):
self.logger.warning(f"Communication blocked: {self.name} -> {target_membrane}")
return False
message = MembraneMessage(
source_membrane=self.name,
target_membrane=target_membrane,
message_type=message_type,
data=data,
priority=priority,
security_level=security_level
)
# Find target membrane through parent (simplified routing)
if self.parent_membrane:
return self.parent_membrane._route_message(message)
return False
def receive_message(self, message: MembraneMessage) -> bool:
"""Receive and queue message for processing"""
if not self._validate_incoming_message(message):
self.logger.warning(f"Message rejected: {message.source_membrane} -> {self.name}")
return False
self.message_queue.append(message)
self.logger.debug(f"Message queued: {message.message_type} from {message.source_membrane}")
return True
def process_messages(self) -> List[Any]:
"""Process queued messages"""
results = []
while self.message_queue:
message = self.message_queue.popleft()
try:
result = self._handle_message(message)
results.append(result)
except Exception as e:
self.logger.error(f"Error processing message: {e}")
return results
def add_child_membrane(self, child: 'Membrane'):
"""Add child membrane with proper hierarchy"""
child.parent_membrane = self
self.child_membranes.append(child)
self.logger.info(f"Added child membrane: {child.name}")
def allocate_resources(self, memory: int = 0, cpu: int = 0, io: int = 0) -> bool:
"""Allocate computational resources"""
if self._validate_resource_allocation(memory, cpu, io):
self.resources["memory"] += memory
self.resources["cpu"] += cpu
self.resources["io"] += io
return True
return False
def _validate_communication(self, target: str, msg_type: str, security: str) -> bool:
"""Validate if communication is allowed"""
if not self.isolation_enabled:
return True
# Basic security check - can be extended
return security in ["standard", "secure"] and "communicate" in self.permissions
def _validate_incoming_message(self, message: MembraneMessage) -> bool:
"""Validate incoming message"""
return message.target_membrane == self.name
def _validate_resource_allocation(self, memory: int, cpu: int, io: int) -> bool:
"""Validate resource allocation request"""
# Simple validation - can be extended with quotas
return memory >= 0 and cpu >= 0 and io >= 0
def _handle_message(self, message: MembraneMessage) -> Any:
"""Handle specific message types - to be overridden by subclasses"""
return {"status": "processed", "type": message.message_type}
def _route_message(self, message: MembraneMessage) -> bool:
"""Route message to target membrane"""
# Check self
if message.target_membrane == self.name:
return self.receive_message(message)
# Check children
for child in self.child_membranes:
if child._route_message(message):
return True
return False
class CognitiveMembrane(Membrane):
"""Core processing membrane for cognitive operations"""
def __init__(self, name: str = "cognitive"):
super().__init__(name, "cognitive", "secure")
self.permissions.update({"process", "communicate", "memory_access"})
self.cognitive_state = {
"attention_level": 0.5,
"processing_load": 0.0,
"memory_active": True
}
def _handle_message(self, message: MembraneMessage) -> Any:
"""Handle cognitive processing messages"""
if message.message_type == "process_thought":
return self._process_thought(message.data)
elif message.message_type == "memory_query":
return self._handle_memory_query(message.data)
elif message.message_type == "attention_update":
return self._update_attention(message.data)
return super()._handle_message(message)
def _process_thought(self, thought_data: Any) -> Dict[str, Any]:
"""Process cognitive thought"""
self.cognitive_state["processing_load"] += 0.1
return {
"status": "processed",
"thought_id": id(thought_data),
"processing_time": 0.1,
"attention_level": self.cognitive_state["attention_level"]
}
def _handle_memory_query(self, query_data: Any) -> Dict[str, Any]:
"""Handle memory queries"""
return {
"status": "query_processed",
"results": [],
"query_id": id(query_data)
}
def _update_attention(self, attention_data: Any) -> Dict[str, Any]:
"""Update attention level"""
if isinstance(attention_data, (int, float)):
self.cognitive_state["attention_level"] = max(0.0, min(1.0, attention_data))
return {"attention_level": self.cognitive_state["attention_level"]}
class ExtensionMembrane(Membrane):
"""Plugin container membrane for extensions"""
def __init__(self, name: str = "extension"):
super().__init__(name, "extension", "standard")
self.permissions.update({"communicate", "limited_resources"})
self.loaded_extensions = {}
def load_extension(self, extension_name: str, extension_data: Any) -> bool:
"""Load an extension into the membrane"""
if self._validate_extension(extension_name, extension_data):
self.loaded_extensions[extension_name] = {
"data": extension_data,
"loaded_at": __import__('time').time(),
"active": True
}
self.logger.info(f"Loaded extension: {extension_name}")
return True
return False
def _validate_extension(self, name: str, data: Any) -> bool:
"""Validate extension before loading"""
# Basic validation - can be extended with security checks
return name not in self.loaded_extensions
def _handle_message(self, message: MembraneMessage) -> Any:
"""Handle extension-related messages"""
if message.message_type == "extension_call":
return self._handle_extension_call(message.data)
elif message.message_type == "load_extension":
return self._handle_load_extension(message.data)
return super()._handle_message(message)
def _handle_extension_call(self, call_data: Any) -> Dict[str, Any]:
"""Handle calls to loaded extensions"""
return {"status": "extension_called", "result": None}
def _handle_load_extension(self, load_data: Any) -> Dict[str, Any]:
"""Handle extension loading requests"""
if isinstance(load_data, dict) and "name" in load_data:
success = self.load_extension(load_data["name"], load_data.get("data"))
return {"status": "loaded" if success else "failed", "name": load_data["name"]}
return {"status": "invalid_request"}
class SecurityMembrane(Membrane):
"""Security and validation membrane"""
def __init__(self, name: str = "security"):
super().__init__(name, "security", "encrypted")
self.permissions.update({"validate", "authenticate", "emergency_control"})
self.security_policies = set()
self.threat_level = "low"
def _handle_message(self, message: MembraneMessage) -> Any:
"""Handle security-related messages"""
if message.message_type == "security_check":
return self._perform_security_check(message.data)
elif message.message_type == "threat_assessment":
return self._assess_threat(message.data)
elif message.message_type == "emergency_shutdown":
return self._handle_emergency(message.data)
return super()._handle_message(message)
def _perform_security_check(self, check_data: Any) -> Dict[str, Any]:
"""Perform security validation"""
return {
"status": "security_validated",
"threat_level": self.threat_level,
"timestamp": __import__('time').time()
}
def _assess_threat(self, threat_data: Any) -> Dict[str, Any]:
"""Assess threat level"""
# Simple threat assessment
return {"threat_level": self.threat_level, "action_required": False}
def _handle_emergency(self, emergency_data: Any) -> Dict[str, Any]:
"""Handle emergency situations"""
self.logger.critical("Emergency shutdown initiated")
return {"status": "emergency_handled", "shutdown_initiated": True}
class MembraneManager:
"""Manager for P-System membrane operations"""
def __init__(self):
self.logger = logging.getLogger(f"{__name__}.MembraneManager")
self.membranes: Dict[str, Membrane] = {}
self.root_membrane: Optional[Membrane] = None
self.active = False
def initialize_default_membranes(self):
"""Initialize default membrane hierarchy"""
# Create root membrane
root = Membrane("root", "system", "secure")
root.permissions.update({"full_access", "manage_children"})
# Create main membranes
cognitive = CognitiveMembrane("cognitive")
extension = ExtensionMembrane("extension")
security = SecurityMembrane("security")
# Set up hierarchy
root.add_child_membrane(cognitive)
root.add_child_membrane(extension)
root.add_child_membrane(security)
# Register membranes
self.root_membrane = root
self.membranes = {
"root": root,
"cognitive": cognitive,
"extension": extension,
"security": security
}
self.active = True
self.logger.info("Default membrane hierarchy initialized")
def send_message(self, source: str, target: str, message_type: str, data: Any,
priority: int = 1, security_level: str = "standard") -> bool:
"""Send message between membranes"""
if source in self.membranes:
return self.membranes[source].send_message(
target, message_type, data, priority, security_level
)
return False
def process_all_messages(self) -> Dict[str, List[Any]]:
"""Process messages in all membranes"""
results = {}
for name, membrane in self.membranes.items():
results[name] = membrane.process_messages()
return results
def get_membrane_status(self) -> Dict[str, Dict[str, Any]]:
"""Get status of all membranes"""
status = {}
for name, membrane in self.membranes.items():
status[name] = {
"state": membrane.state,
"resources": membrane.resources,
"message_queue_size": len(membrane.message_queue),
"child_count": len(membrane.child_membranes)
}
return status
class DeepTreeEcho:
def __init__(self, echo_threshold: float = 0.75, max_depth: int = 10, use_julia: bool = True):
self.logger = logging.getLogger(__name__)
self.echo_threshold = echo_threshold
self.max_depth = max_depth
self.root = None
self.ml_system = MLSystem()
# Initialize emotional dynamics system
self.emotional_dynamics = EmotionalDynamics(use_julia=use_julia)
# Initialize differential emotion theory system
self.det_system = DifferentialEmotionSystem(use_julia=use_julia)
# Default emotional configuration
self.default_emotional_state = EmotionalState()
# Spatial awareness parameters
self.spatial_awareness_enabled = True
self.spatial_influence_factor = 0.15 # How much spatial context affects echo values
# Virtual environment representation
self.environment_map = {} # Map of the virtual environment
# Initialize P-System membrane manager
self.membrane_manager = MembraneManager()
self.membrane_manager.initialize_default_membranes()
self.logger.info("P-System membranes initialized")
try:
# Try to import the enhanced sensory motor system with 3D capabilities
from sensory_motor_simple import SensoryMotorSystem
self.sensory_motor = SensoryMotorSystem()
self.logger.info("Enhanced sensory motor system with 3D capabilities loaded")
except ImportError:
# Fall back to standard sensory motor if enhanced version not available
try:
from sensory_motor import SensoryMotorSystem
self.sensory_motor = SensoryMotorSystem()
self.logger.info("Standard sensory motor system loaded")
except ImportError:
self.logger.warning("No sensory motor system available")
self.sensory_motor = None
def create_tree(self, content: str) -> TreeNode:
"""Create initial tree structure from content and analyze emotional content"""
# Extract emotional state from content
initial_emotions = self.emotional_dynamics.content_to_emotion(content)
# Create root node with emotional state
self.root = TreeNode(content=content, emotional_state=initial_emotions)
# Initialize DET state for root node
det_emotions = self.det_system.content_to_det_emotion(content)
self.root.det_state = DETState(det_emotions=det_emotions)
# Initialize spatial context for root node
# Center position, looking forward, standard FOV
self.root.spatial_context = SpatialContext(
position=(0.0, 0.0, 0.0),
orientation=(0.0, 0.0, 0.0),
field_of_view=110.0
)
return self.root
def add_child(self, parent: TreeNode, content: str) -> TreeNode:
"""Add a child node with emotional state based on content"""
# Extract emotional state from content
child_emotions = self.emotional_dynamics.content_to_emotion(content)
# Create child node
child = TreeNode(content=content, parent=parent, emotional_state=child_emotions)
parent.children.append(child)
# Initialize DET state for child node
det_emotions = self.det_system.content_to_det_emotion(content)
child.det_state = DETState(det_emotions=det_emotions)
# Derive spatial context based on parent
if parent.spatial_context:
# Position slightly forward and to the right of parent
relative_pos = (0.5, 0.2, 0.1)
child.spatial_context = SpatialContext(
position=(
parent.spatial_context.position[0] + relative_pos[0],
parent.spatial_context.position[1] + relative_pos[1],
parent.spatial_context.position[2] + relative_pos[2]
),
orientation=parent.spatial_context.orientation,
field_of_view=parent.spatial_context.field_of_view,
depth=parent.spatial_context.depth + 0.1 # Slightly deeper
)
# Update echo values
child.echo_value = self.calculate_echo_value(child)
return child
def add_child_with_spatial_context(self, parent: TreeNode, content: str,
position: Tuple[float, float, float] = None,
orientation: Tuple[float, float, float] = None,
depth: float = None) -> TreeNode:
"""Add a child node with specific spatial positioning"""
# Create basic child first
child = self.add_child(parent, content)
# Update spatial context with provided parameters
if position:
child.spatial_context.position = position
if orientation:
child.spatial_context.orientation = orientation
if depth:
child.spatial_context.depth = depth
# Update echo value with new spatial context
child.echo_value = self.calculate_echo_value(child)
return child
def calculate_echo_value(self, node: TreeNode) -> float:
"""Calculate echo value for a node based on its content, children, emotional state, and spatial context"""
# Base echo from content length and complexity
base_echo = len(node.content) / 1000 # Normalize by 1000 chars
# Add complexity factor
unique_chars = len(set(node.content))
complexity_factor = unique_chars / 128 # Normalize by ASCII range
# Calculate child echoes
child_echo = 0
if node.children:
child_values = [child.echo_value for child in node.children]
child_echo = np.mean(child_values) if child_values else 0
# Incorporate node depth
depth_factor = 1 / (1 + self.get_node_depth(node))
# Incorporate sibling nodes
sibling_echo = 0
if node.parent:
sibling_values = [sibling.echo_value for sibling in node.parent.children if sibling != node]
sibling_echo = np.mean(sibling_values) if sibling_values else 0
# Incorporate historical echo values
historical_echo = node.metadata.get('historical_echo', 0)
# Calculate emotional modifier from core emotions
emotional_modifier = self.emotional_dynamics.emotion_to_echo_modifier(node.emotional_state)
# If DET state is available, incorporate more nuanced emotional influence
det_modifier = 0.0
if node.det_state is not None:
# Get active scripts
active_scripts = node.metadata.get('active_scripts', [])
# Scripts like "Exploration" and "Celebration" enhance echo
for script_name in active_scripts:
if script_name in ["Exploration", "Celebration", "Orientation"]:
det_modifier += 0.1
elif script_name in ["Escape", "Withdrawal", "Atonement"]:
det_modifier -= 0.1
# Add cognitive factors influence
if "valence" in node.det_state.cognitive_factors:
# Positive valence enhances echo
det_modifier += node.det_state.cognitive_factors["valence"] * 0.1
if "arousal" in node.det_state.cognitive_factors:
# High arousal enhances echo
det_modifier += (node.det_state.cognitive_factors["arousal"] - 0.5) * 0.1
# Incorporate spatial context if available and enabled
spatial_modifier = 0.0
if self.spatial_awareness_enabled and node.spatial_context:
# Depth awareness: nodes at optimal depth (not too deep, not too shallow) have higher echo
optimal_depth = 3.0
depth_diff = abs(node.spatial_context.depth - optimal_depth)
spatial_modifier -= depth_diff * 0.03 # Penalize being far from optimal depth
# Field of view: wider FOV gives better awareness
fov_factor = (node.spatial_context.field_of_view - 90) / 90 # Normalized around 90 degrees
spatial_modifier += fov_factor * 0.05
# Position: centrality in the field is preferred
# Calculate distance from origin in the XY plane
distance_from_center = np.sqrt(node.spatial_context.position[0]**2 +
node.spatial_context.position[1]**2)
spatial_modifier -= distance_from_center * 0.02 # Penalize distance from center
# Apply bounds to spatial modifier
spatial_modifier = max(-0.2, min(0.2, spatial_modifier))
# Combine factors with decay
echo_value = (0.4 * base_echo + 0.2 * complexity_factor + 0.1 * child_echo +
0.1 * depth_factor + 0.1 * sibling_echo + 0.1 * historical_echo)
# Apply modifiers
echo_value = min(1.0, max(0.0, echo_value + emotional_modifier + det_modifier +
(spatial_modifier * self.spatial_influence_factor)))
return echo_value
def get_node_depth(self, node: TreeNode) -> int:
"""Calculate the depth of a node in the tree"""
if node is None:
return -1
depth = 0
current = node
while current.parent is not None:
depth += 1
current = current.parent
return depth
def visualize_in_3d_space(self) -> Dict[str, Any]:
"""Generate 3D visualization data for the tree based on spatial context"""
visualization_data = {
'nodes': [],
'edges': [],
'spatial_info': {}
}
if self.root is None:
return visualization_data
# BFS to process all nodes
queue = deque([(self.root, None)]) # (node, parent_id)
node_id = 0
id_map = {} # Maps nodes to their IDs
while queue:
node, parent_id = queue.popleft()
# Assign ID to this node
current_id = node_id
id_map[node] = current_id
node_id += 1
# Get node spatial data
spatial_data = {}
if node.spatial_context:
spatial_data = {
'position': node.spatial_context.position,
'orientation': node.spatial_context.orientation,
'depth': node.spatial_context.depth,
'fov': node.spatial_context.field_of_view,
}
else:
# Default spatial data if not available
level = self.get_node_depth(node)
spatial_data = {
'position': (level * 2, (current_id % 5) * 1.5, 0),
'orientation': (0, 0, 0),
'depth': level,
'fov': 90,
}
# Add node to visualization
node_data = {
'id': current_id,
'content': node.content[:50] + ('...' if len(node.content) > 50 else ''),
'echo_value': node.echo_value,
'spatial': spatial_data,
}
visualization_data['nodes'].append(node_data)
# Add edge if this isn't the root
if parent_id is not None:
edge = {
'source': parent_id,
'target': current_id,
'weight': node.echo_value
}
visualization_data['edges'].append(edge)
# Add children to queue
for child in node.children:
queue.append((child, current_id))
# Add global spatial information
visualization_data['spatial_info'] = {
'bounds': {
'x': [-10, 10],
'y': [-10, 10],
'z': [-10, 10]
},
'optimal_viewing_position': (5, 5, 5),
'echo_threshold': self.echo_threshold,
}
return visualization_data
def update_from_sensory_input(self):
"""Update the tree based on sensory input from the environment"""
if not self.sensory_motor:
self.logger.warning("No sensory motor system available for input")
return False
try:
# Process sensory input
import asyncio
input_data = asyncio.run(self.sensory_motor.process_all())
if input_data.get('status') != 'processed':
self.logger.info(f"Sensory input not processed: {input_data.get('reason', 'unknown reason')}")
return False
# Extract detected objects if available
detected_objects = input_data.get('objects', [])
if detected_objects:
# Update environment map with detected objects
for obj in detected_objects:
obj_id = obj.get('id')
if obj_id:
self.environment_map[obj_id] = {
'class': obj.get('class'),
'position': obj.get('position'),
'depth': obj.get('depth'),
'last_seen': obj.get('last_seen', 0)
}
# Create nodes for significant objects
if self.root:
for obj in detected_objects:
# Only create nodes for high-confidence detections
if obj.get('confidence', 0) > 0.85:
# Create content description
content = f"Detected {obj.get('class')} at depth {obj.get('depth'):.2f}"
# Create position from object data
position = (
obj.get('position', {}).get('x', 0) / 1000, # Scale down for visualization
obj.get('position', {}).get('y', 0) / 1000,
obj.get('depth', 1.0)
)
# Add as child of root with specific spatial context
self.add_child_with_spatial_context(
self.root,
content,
position=position,
depth=obj.get('depth', 1.0)
)
# Process motion data if available
motion_data = input_data.get('motion', {})
if motion_data and motion_data.get('motion_detected'):
motion_content = f"Detected {motion_data.get('motion_count', 0)} motion regions"
motion_child = self.add_child(self.root, motion_content)
motion_child.metadata['motion_regions'] = motion_data.get('motion_regions', [])
return True
except Exception as e:
self.logger.error(f"Error updating from sensory input: {str(e)}")
return False
def apply_spatial_dynamics(self, node: TreeNode = None):
"""Apply spatial dynamics to update tree based on spatial relationships"""
if node is None:
node = self.root
if node is None:
return
# Calculate spatial relationships between this node and its children
for child in node.children:
if node.spatial_context and child.spatial_context:
# Calculate relative position
rel_x = child.spatial_context.position[0] - node.spatial_context.position[0]
rel_y = child.spatial_context.position[1] - node.spatial_context.position[1]
rel_z = child.spatial_context.position[2] - node.spatial_context.position[2]
# Calculate distance
distance = np.sqrt(rel_x**2 + rel_y**2 + rel_z**2)
# Store spatial relationship
child.spatial_context.spatial_relations['parent_distance'] = distance
child.spatial_context.spatial_relations['parent_direction'] = (
rel_x / distance if distance > 0 else 0,
rel_y / distance if distance > 0 else 0,
rel_z / distance if distance > 0 else 0
)
# Update metadata
child.metadata['spatial_distance'] = distance
# Modify echo value based on spatial relationship
# Nodes at optimal distance have higher echo
optimal_distance = 1.0
distance_factor = 1.0 - (abs(distance - optimal_distance) / 2)
distance_factor = max(0.0, min(1.0, distance_factor))
# Apply distance factor to echo value
child.echo_value = (0.8 * child.echo_value) + (0.2 * distance_factor)
# Recursively apply to all children
for child in node.children:
self.apply_spatial_dynamics(child)
def simulate_det_dynamics(self, node: TreeNode, time_span: Tuple[float, float] = (0.0, 5.0)):
"""Apply differential emotion theory simulation to a node"""
if node is None or node.det_state is None:
return
# Simulate cognitive appraisal processes
updated_det_state = self.det_system.simulate_appraisal(node.det_state, time_span)
# Update node DET state
node.det_state = updated_det_state
# Identify active scripts
active_scripts = self.det_system.identify_active_scripts(node.det_state)
node.metadata['active_scripts'] = [script.name for script in active_scripts]
# Extract behavioral responses
responses = self.det_system.extract_behavioral_responses(node.det_state)
node.metadata['behavioral_responses'] = responses
# Map DET emotions back to core emotions for compatibility
core_emotions = self.det_system.map_det_to_core(node.det_state.det_emotions)
node.emotional_state = core_emotions
# Update echo value based on new emotional state
node.echo_value = self.calculate_echo_value(node)
# Update spatial context based on emotional state
self.update_spatial_from_emotion(node)
def update_spatial_from_emotion(self, node: TreeNode):
"""Update spatial context based on emotional state"""
if not node.det_state or not node.spatial_context:
return
# Map joy and interest to increased field of view
joy = node.det_state.det_emotions[DETEmotion.JOY.value]
interest = node.det_state.det_emotions[DETEmotion.INTEREST.value]
# Update field of view based on joy and interest
base_fov = 90.0
fov_modifier = (joy * 0.5 + interest * 0.5) * 40.0 # Up to 40 degree increase
node.spatial_context.field_of_view = min(140.0, base_fov + fov_modifier)
# Fear and anxiety affect depth perception
fear = node.det_state.det_emotions[DETEmotion.FEAR.value]
anxiety = node.det_state.det_emotions[DETEmotion.ANXIETY.value]
# Higher fear/anxiety increases perceived depth (things seem further away)
depth_modifier = (fear * 0.7 + anxiety * 0.3) * 2.0
node.spatial_context.depth += depth_modifier
# Anger and contempt affect orientation (looking down on things)
anger = node.det_state.det_emotions[DETEmotion.ANGER.value]
contempt = node.det_state.det_emotions[DETEmotion.CONTEMPT.value]
current_pitch = node.spatial_context.orientation[0]
pitch_modifier = (anger * 0.4 + contempt * 0.6) * 30.0 # Up to 30 degree change
new_pitch = min(45.0, current_pitch + pitch_modifier)
# Update orientation
node.spatial_context.orientation = (
new_pitch,
node.spatial_context.orientation[1],
node.spatial_context.orientation[2]
)
def inject_echo(self, source_node: TreeNode, target_node: TreeNode, strength: float = 0.5):
"""Inject an echo from source node to target node with given strength"""
if source_node is None or target_node is None:
return
# Only inject echo if source has significant echo value
if source_node.echo_value < self.echo_threshold * 0.5:
return
# Calculate strength based on emotional similarity
emotional_similarity = 0.5
if source_node.emotional_state is not None and target_node.emotional_state is not None:
similarity = np.dot(source_node.emotional_state, target_node.emotional_state)
similarity /= (np.linalg.norm(source_node.emotional_state) * np.linalg.norm(target_node.emotional_state))
emotional_similarity = (similarity + 1.0) / 2.0 # Normalize to [0, 1]
# Apply echo increase
echo_boost = strength * emotional_similarity * source_node.echo_value * 0.3
target_node.echo_value = min(1.0, target_node.echo_value + echo_boost)
# Record injection in metadata
if 'echo_injections' not in target_node.metadata:
target_node.metadata['echo_injections'] = []
target_node.metadata['echo_injections'].append({
'source': source_node.content[:50],
'strength': strength,
'similarity': emotional_similarity,
'boost': echo_boost
})
def propagate_echoes(self):
"""Propagate echo values through the tree structure"""
if self.root is None:
return
# First, update all echo values based on content and metadata
self._update_all_echo_values(self.root)
# Propagate from root down through children
self._propagate_down(self.root)
# Propagate from leaf nodes back up to parents
self._propagate_up(self.root)
# Apply echo decay to all nodes
self._apply_echo_decay(self.root)
# Apply spatial dynamics
if self.spatial_awareness_enabled:
self.apply_spatial_dynamics()
def _update_all_echo_values(self, node: TreeNode):
"""Update echo values for a node and all its children"""
node.echo_value = self.calculate_echo_value(node)
# Recursively update children
for child in node.children:
self._update_all_echo_values(child)
def _propagate_down(self, node: TreeNode, depth: int = 0):
"""Propagate echo from a node down to its children"""
if node is None or depth > self.max_depth:
return
if node.echo_value >= self.echo_threshold:
# Node has significant echo to propagate
for child in node.children:
# Calculate propagation factor based on parent-child relationship
propagation_factor = self._calculate_propagation_factor(node, child)
# Apply echo propagation
echo_propagation = node.echo_value * propagation_factor
child.echo_value = min(1.0, child.echo_value + echo_propagation)
# Record propagation in metadata
if 'echo_propagations' not in child.metadata:
child.metadata['echo_propagations'] = []
child.metadata['echo_propagations'].append({
'direction': 'down',
'from': 'parent',
'factor': propagation_factor,
'value': echo_propagation
})
# Continue propagation to deeper levels
self._propagate_down(child, depth + 1)
def _propagate_up(self, node: TreeNode):
"""Propagate echo from the leaves up to the root"""
if node is None:
return
# First, propagate to all children's subtrees
for child in node.children:
self._propagate_up(child)
# If this is a leaf node or we've already processed all children,
# propagate echo up to parent
if node.parent and node.echo_value >= self.echo_threshold:
# Calculate propagation factor based on child-parent relationship
propagation_factor = self._calculate_propagation_factor(node, node.parent) * 0.7 # Reduced upward propagation
# Apply echo propagation
echo_propagation = node.echo_value * propagation_factor
node.parent.echo_value = min(1.0, node.parent.echo_value + echo_propagation)
# Record propagation in metadata
if 'echo_propagations' not in node.parent.metadata:
node.parent.metadata['echo_propagations'] = []
node.parent.metadata['echo_propagations'].append({
'direction': 'up',
'from': 'child',
'factor': propagation_factor,
'value': echo_propagation
})
def _apply_echo_decay(self, node: TreeNode):
"""Apply decay to echo values"""
if node is None:
return
# Apply decay factor
decay_factor = 0.95 # Retain 95% of echo value
# Store historical echo value
node.metadata['historical_echo'] = node.echo_value
# Apply decay
node.echo_value = node.echo_value * decay_factor
# Recursively apply to all children
for child in node.children:
self._apply_echo_decay(child)
def _calculate_propagation_factor(self, source: TreeNode, target: TreeNode) -> float:
"""Calculate how strongly echo propagates between two nodes"""
# Base propagation factor
base_factor = 0.3
# Adjust based on emotional similarity
emotional_similarity = 0.5
if source.emotional_state is not None and target.emotional_state is not None:
# Calculate cosine similarity between emotional states
similarity = np.dot(source.emotional_state, target.emotional_state)
similarity /= (np.linalg.norm(source.emotional_state) * np.linalg.norm(target.emotional_state))
emotional_similarity = (similarity + 1.0) / 2.0 # Normalize to [0, 1]
# Adjust based on content similarity (simplified)
content_similarity = 0.5
if len(source.content) > 0 and len(target.content) > 0:
# Count shared words as a simple measure
src_words = set(source.content.lower().split())
tgt_words = set(target.content.lower().split())
if len(src_words) > 0 and len(tgt_words) > 0:
shared = len(src_words.intersection(tgt_words))
total = len(src_words.union(tgt_words))
content_similarity = shared / total
# Adjust based on spatial relationship if available
spatial_factor = 0.5
if self.spatial_awareness_enabled and source.spatial_context and target.spatial_context:
# Calculate distance between nodes in 3D space
p1 = source.spatial_context.position
p2 = target.spatial_context.position
distance = np.sqrt((p1[0]-p2[0])**2 + (p1[1]-p2[1])**2 + (p1[2]-p2[2])**2)
# Closer nodes have stronger propagation