Spaces:
Running
Running
| """ | |
| Bio-Tools: Utilities for the Artificial Life Ecosystem | |
| ------------------------------------------------------ | |
| 1. Genomic Noise: Generates organic 1/f noise vectors for DNA seeding. | |
| 2. Vector Blender: Manually mix two DNA strands. | |
| """ | |
| import numpy as np | |
| # --- STRICT COMPATIBILITY IMPORTS --- | |
| import __main__ | |
| try: | |
| BaseNode = __main__.BaseNode | |
| QtGui = __main__.QtGui | |
| except AttributeError: | |
| from PyQt6 import QtGui | |
| class BaseNode: | |
| def get_blended_input(self, name, mode): return None | |
| class GenomicNoiseNode(BaseNode): | |
| """ | |
| Generates 'Pink Noise' (1/f) vectors. | |
| Biological systems are rarely random; they are correlated. | |
| This creates DNA that looks more 'organic' and less like static. | |
| """ | |
| NODE_CATEGORY = "Artificial Life" | |
| NODE_COLOR = QtGui.QColor(100, 150, 100) # Sage Green | |
| def __init__(self): | |
| super().__init__() | |
| self.node_title = "Genomic Noise" | |
| self.inputs = { | |
| 'volatility': 'signal', # How fast the noise changes | |
| 'roughness': 'signal' # High frequency content | |
| } | |
| self.outputs = { | |
| 'dna_spectrum': 'spectrum', # Vector output | |
| 'value': 'signal' # Single value | |
| } | |
| self.length = 128 | |
| self.state = np.zeros(self.length) | |
| self.smooth_state = np.zeros(self.length) | |
| # Buffers | |
| self.out_spectrum = np.zeros(self.length) | |
| self.out_value = 0.0 | |
| def step(self): | |
| vol = self.get_blended_input('volatility', 'mean') | |
| rough = self.get_blended_input('roughness', 'mean') | |
| if vol is None: vol = 0.1 | |
| if rough is None: rough = 0.5 | |
| # Generate new target noise | |
| target = np.random.randn(self.length) * rough | |
| # Smoothly interpolate (Brownian motion-ish) | |
| self.state = self.state * (1.0 - vol) + target * vol | |
| # Apply smoothing for "structure" | |
| # Simple moving average to simulate correlations | |
| kernel_size = 3 | |
| self.smooth_state = np.convolve(self.state, np.ones(kernel_size)/kernel_size, mode='same') | |
| # Normalize to 0..1 range typically expected by DNA, | |
| # but centered around 0 is also fine for phases. | |
| # Let's keep it raw but bounded slightly | |
| self.out_spectrum = np.clip(self.smooth_state, -2.0, 2.0) | |
| self.out_value = float(np.mean(np.abs(self.out_spectrum))) | |
| def get_output(self, name): | |
| if name == 'dna_spectrum': return self.out_spectrum | |
| if name == 'value': return self.out_value | |
| return None | |
| class VectorMathNode(BaseNode): | |
| """ | |
| Simple math for DNA vectors. | |
| """ | |
| NODE_CATEGORY = "Artificial Life" | |
| NODE_COLOR = QtGui.QColor(100, 100, 150) | |
| def __init__(self): | |
| super().__init__() | |
| self.node_title = "Vector Math" | |
| self.inputs = { | |
| 'vec_a': 'spectrum', | |
| 'vec_b': 'spectrum', | |
| 'op_mode': 'signal' # 0=Add, 1=Sub, 2=Mult | |
| } | |
| self.outputs = { | |
| 'result': 'spectrum' | |
| } | |
| self.out_result = np.zeros(128) | |
| def step(self): | |
| a = self.get_blended_input('vec_a', 'mean') | |
| b = self.get_blended_input('vec_b', 'mean') | |
| mode = self.get_blended_input('op_mode', 'mean') | |
| if a is None: a = np.zeros(128) | |
| if b is None: b = np.zeros(128) | |
| # Resize to match | |
| target_len = max(len(a), len(b)) | |
| if len(a) < target_len: a = np.resize(a, target_len) | |
| if len(b) < target_len: b = np.resize(b, target_len) | |
| if mode is None: mode = 0 | |
| if mode < 0.5: # ADD | |
| res = a + b | |
| elif mode < 1.5: # SUB | |
| res = a - b | |
| else: # MULT | |
| res = a * b | |
| self.out_result = res | |
| def get_output(self, name): | |
| if name == 'result': return self.out_result | |
| return None |