Skip to content

Bio — Biological Computing Substrates

Biological computing interfaces: DNA-based weight storage, gene regulatory network modulation, and neuromodulatory dynamics (dopamine, serotonin, norepinephrine).

DNAEncoder — DNA Data Storage

Maps bitstreams to nucleotide sequences and back. Encoding: pairs of bits → nucleotides (00→A, 01→C, 10→G, 11→T). Decoding includes a configurable mutation rate that simulates sequencing errors.

Parameter Default Meaning
mutation_rate 0.001 Per-nucleotide mutation probability during decode

Odd-length bitstreams are zero-padded to even length.

GeneticRegulatoryLayer — Gene Expression Modulation

Neural activity drives protein production; protein levels modulate neuron thresholds. Implements a first-order ODE: dP/dt = α * spikes - β * P, clipped to [0, 10].

Parameter Default Meaning
n_neurons (required) Number of neurons
production_rate 0.01 Protein production rate (α)
decay_rate 0.005 Protein decay rate (β)

get_threshold_modulators() returns current protein levels — higher protein → higher effective threshold (inhibitory feedback).

NeuromodulatorSystem — Global Emotional System

Three neuromodulators with environmental feedback:

Chemical Baseline Effect
Dopamine (DA) 0.5 Lowers threshold (excitation)
Serotonin (5-HT) 0.5 Reduces noise (stabilization)
Norepinephrine (NE) 0.1 Increases noise + gain (exploration)

update_levels(reward, stress) adjusts chemicals. modulate_neuron(params) returns modified parameters.

Usage

from sc_neurocore.bio import DNAEncoder, GeneticRegulatoryLayer, NeuromodulatorSystem
import numpy as np

# DNA storage roundtrip
enc = DNAEncoder(mutation_rate=0.0)
bits = np.array([1, 0, 0, 1, 1, 1, 0, 0], dtype=np.uint8)
dna = enc.encode(bits)   # "GCTA"
recovered = enc.decode(dna)
assert np.array_equal(bits, recovered)

# Gene regulation
grn = GeneticRegulatoryLayer(n_neurons=100)
for _ in range(50):
    spikes = (np.random.rand(100) < 0.3).astype(float)
    grn.step(spikes)
thresholds = grn.get_threshold_modulators()

# Neuromodulation
nm = NeuromodulatorSystem()
nm.update_levels(reward=0.8, stress=0.2)
params = nm.modulate_neuron({"v_threshold": 1.0, "noise_std": 0.1})

sc_neurocore.bio.dna_storage

DNAEncoder dataclass

Interface for DNA Data Storage. Maps Bitstreams to Nucleotides (A, C, T, G).

Source code in src/sc_neurocore/bio/dna_storage.py
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
@dataclass
class DNAEncoder:
    """
    Interface for DNA Data Storage.
    Maps Bitstreams to Nucleotides (A, C, T, G).
    """

    mutation_rate: float = 0.001

    # Huffman-style mapping
    # 00 -> A, 01 -> C, 10 -> G, 11 -> T
    MAP = {(0, 0): "A", (0, 1): "C", (1, 0): "G", (1, 1): "T"}
    REV_MAP = {"A": (0, 0), "C": (0, 1), "G": (1, 0), "T": (1, 1)}

    def encode(self, bitstream: np.ndarray[Any, Any]) -> str:
        """
        Converts uint8 {0,1} bitstream to DNA string.
        """
        # Ensure even length
        if len(bitstream) % 2 != 0:
            bitstream = np.append(bitstream, 0)

        dna = []
        for i in range(0, len(bitstream), 2):
            pair = (bitstream[i], bitstream[i + 1])
            dna.append(self.MAP[pair])

        return "".join(dna)

    def decode(self, dna_str: str) -> np.ndarray[Any, Any]:
        """
        Converts DNA string back to bitstream.
        """
        bits: list[float] = []
        for char in dna_str:
            # Simulate mutation before decoding
            if np.random.random() < self.mutation_rate:
                char = np.random.choice(["A", "C", "T", "G"])

            pair = self.REV_MAP[char]
            bits.extend(pair)

        return np.array(bits, dtype=np.uint8)

encode(bitstream)

Converts uint8 {0,1} bitstream to DNA string.

Source code in src/sc_neurocore/bio/dna_storage.py
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def encode(self, bitstream: np.ndarray[Any, Any]) -> str:
    """
    Converts uint8 {0,1} bitstream to DNA string.
    """
    # Ensure even length
    if len(bitstream) % 2 != 0:
        bitstream = np.append(bitstream, 0)

    dna = []
    for i in range(0, len(bitstream), 2):
        pair = (bitstream[i], bitstream[i + 1])
        dna.append(self.MAP[pair])

    return "".join(dna)

decode(dna_str)

Converts DNA string back to bitstream.

Source code in src/sc_neurocore/bio/dna_storage.py
42
43
44
45
46
47
48
49
50
51
52
53
54
55
def decode(self, dna_str: str) -> np.ndarray[Any, Any]:
    """
    Converts DNA string back to bitstream.
    """
    bits: list[float] = []
    for char in dna_str:
        # Simulate mutation before decoding
        if np.random.random() < self.mutation_rate:
            char = np.random.choice(["A", "C", "T", "G"])

        pair = self.REV_MAP[char]
        bits.extend(pair)

    return np.array(bits, dtype=np.uint8)

sc_neurocore.bio.grn

GeneticRegulatoryLayer dataclass

Bio-Hybrid Layer. Neural Activity -> Gene Expression (Protein) -> Neural Param Modulation.

Source code in src/sc_neurocore/bio/grn.py
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
@dataclass
class GeneticRegulatoryLayer:
    """
    Bio-Hybrid Layer.
    Neural Activity -> Gene Expression (Protein) -> Neural Param Modulation.
    """

    n_neurons: int
    production_rate: float = 0.01
    decay_rate: float = 0.005

    def __post_init__(self) -> None:
        self.protein_levels = np.zeros(self.n_neurons)

    def step(self, spikes: np.ndarray[Any, Any]) -> None:
        """
        Update protein levels based on spike activity.
        """
        # dP/dt = alpha * spikes - beta * P
        delta = (self.production_rate * spikes) - (self.decay_rate * self.protein_levels)
        self.protein_levels += delta
        self.protein_levels = np.clip(self.protein_levels, 0, 10.0)

    def get_threshold_modulators(self) -> np.ndarray[Any, Any]:
        """
        Protein acts as inhibitor: Higher protein -> Higher threshold.
        """
        return self.protein_levels

step(spikes)

Update protein levels based on spike activity.

Source code in src/sc_neurocore/bio/grn.py
27
28
29
30
31
32
33
34
def step(self, spikes: np.ndarray[Any, Any]) -> None:
    """
    Update protein levels based on spike activity.
    """
    # dP/dt = alpha * spikes - beta * P
    delta = (self.production_rate * spikes) - (self.decay_rate * self.protein_levels)
    self.protein_levels += delta
    self.protein_levels = np.clip(self.protein_levels, 0, 10.0)

get_threshold_modulators()

Protein acts as inhibitor: Higher protein -> Higher threshold.

Source code in src/sc_neurocore/bio/grn.py
36
37
38
39
40
def get_threshold_modulators(self) -> np.ndarray[Any, Any]:
    """
    Protein acts as inhibitor: Higher protein -> Higher threshold.
    """
    return self.protein_levels

sc_neurocore.bio.neuromodulation

NeuromodulatorSystem dataclass

Global Emotional/Chemical System. Modulates neuron parameters based on Dopamine (DA), Serotonin (5HT), Norepinephrine (NE).

Source code in src/sc_neurocore/bio/neuromodulation.py
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
@dataclass
class NeuromodulatorSystem:
    """
    Global Emotional/Chemical System.
    Modulates neuron parameters based on Dopamine (DA), Serotonin (5HT), Norepinephrine (NE).
    """

    da_level: float = 0.5  # Baseline
    ht_level: float = 0.5
    ne_level: float = 0.1

    def update_levels(self, reward: float, stress: float) -> None:
        """
        Adjust chemicals based on environmental feedback.
        """
        # Reward boosts Dopamine
        self.da_level += 0.1 * (reward - self.da_level)

        # Stress boosts Adrenaline (NE) and drops Serotonin (5HT)
        self.ne_level += 0.2 * (stress - self.ne_level)
        self.ht_level -= 0.1 * stress
        self.ht_level = np.clip(self.ht_level, 0.1, 1.0)

    def modulate_neuron(self, neuron_params: dict[str, Any]) -> dict[str, Any]:
        """
        Returns modified parameters for a StochasticLIFNeuron.
        """
        mod_params = neuron_params.copy()

        # Dopamine: Lowers Threshold (Excitation)
        if "v_threshold" in mod_params:
            mod_params["v_threshold"] *= 1.0 - 0.2 * self.da_level

        # 5-HT reduces noise (stabilisation effect)
        if "noise_std" in mod_params:
            mod_params["noise_std"] *= 1.0 - 0.5 * self.ht_level

        # Adrenaline: Increases Noise (Exploration) and Gain
        if "noise_std" in mod_params:
            mod_params["noise_std"] += 0.1 * self.ne_level

        return mod_params

update_levels(reward, stress)

Adjust chemicals based on environmental feedback.

Source code in src/sc_neurocore/bio/neuromodulation.py
24
25
26
27
28
29
30
31
32
33
34
def update_levels(self, reward: float, stress: float) -> None:
    """
    Adjust chemicals based on environmental feedback.
    """
    # Reward boosts Dopamine
    self.da_level += 0.1 * (reward - self.da_level)

    # Stress boosts Adrenaline (NE) and drops Serotonin (5HT)
    self.ne_level += 0.2 * (stress - self.ne_level)
    self.ht_level -= 0.1 * stress
    self.ht_level = np.clip(self.ht_level, 0.1, 1.0)

modulate_neuron(neuron_params)

Returns modified parameters for a StochasticLIFNeuron.

Source code in src/sc_neurocore/bio/neuromodulation.py
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
def modulate_neuron(self, neuron_params: dict[str, Any]) -> dict[str, Any]:
    """
    Returns modified parameters for a StochasticLIFNeuron.
    """
    mod_params = neuron_params.copy()

    # Dopamine: Lowers Threshold (Excitation)
    if "v_threshold" in mod_params:
        mod_params["v_threshold"] *= 1.0 - 0.2 * self.da_level

    # 5-HT reduces noise (stabilisation effect)
    if "noise_std" in mod_params:
        mod_params["noise_std"] *= 1.0 - 0.5 * self.ht_level

    # Adrenaline: Increases Noise (Exploration) and Gain
    if "noise_std" in mod_params:
        mod_params["noise_std"] += 0.1 * self.ne_level

    return mod_params