File size: 2,577 Bytes
3bb804c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import __main__
BaseNode = __main__.BaseNode
QtGui = __main__.QtGui

import numpy as np
import cv2

class FitzHughNagumoNode(BaseNode):
    """

    Simulates a FitzHugh-Nagumo neuron model.

    """
    NODE_CATEGORY = "AI / Physics"
    NODE_COLOR = QtGui.QColor(100, 180, 180)

    def __init__(self, a=0.7, b=0.8, tau=12.5):
        super().__init__()
        self.node_title = "FHN Neuron"
        
        self.inputs = {'pain_stimulus': 'signal'}
        self.outputs = {
            'pain_out': 'signal',
            'stability_metric': 'signal'
        }
        
        self.a = float(a)
        self.b = float(b)
        self.tau = float(tau)
        
        self.v = 0.0  # Membrane potential ("pain")
        self.w = 0.0  # Recovery variable ("stability")
        self.dt = 0.1 # Simulation time step

    def step(self):
        # Get input current
        I = self.get_blended_input('pain_stimulus', 'sum') or 0.0
        
        # Model equations (Euler integration)
        dv = self.v - (self.v**3 / 3) - self.w + I
        dw = (self.v + self.a - self.b * self.w) / self.tau
        
        self.v += dv * self.dt
        self.w += dw * self.dt
        
        # Clamp values to prevent explosion
        self.v = np.clip(self.v, -5, 5)
        self.w = np.clip(self.w, -5, 5)

    def get_output(self, port_name):
        if port_name == 'pain_out':
            return self.v
        if port_name == 'stability_metric':
            return self.w
        return None

    def get_display_image(self):
        w, h = 256, 128
        img = np.zeros((h, w, 3), dtype=np.uint8)
        
        # Map v and w to screen
        v_y = int(h/2 - (self.v / 3.0) * (h/2))
        w_y = int(h/2 - (self.w / 3.0) * (h/2))
        
        cv2.circle(img, (w//2, v_y), 8, (0, 255, 255), -1) # 'v' (pain)
        cv2.circle(img, (w//2, w_y), 4, (255, 0, 0), -1)   # 'w' (stability)

        cv2.line(img, (0, h//2), (w, h//2), (50,50,50), 1)
        
        cv2.putText(img, f"Pain (v): {self.v:.2f}", (5, 15), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.4, (0, 255, 255), 1)
        cv2.putText(img, f"Stability (w): {self.w:.2f}", (5, 30), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 0, 0), 1)
        
        return QtGui.QImage(img.data, w, h, w*3, QtGui.QImage.Format.Format_RGB888)

    def get_config_options(self):
        return [
            ("a", "a", self.a, None),
            ("b", "b", self.b, None),
            ("tau", "tau", self.tau, None)
        ]