PerceptionLabPortable / app /nodes /bionoisetools.py
Aluode's picture
Upload folder using huggingface_hub
3bb804c verified
"""
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