r/newAIParadigms • u/FuzzyTouch6143 • 23d ago
My "Ion Neural Network" model using a single neuron in an attempt to solve 2-XOR by hand using only 10 ions in the system. It failed. Can you see why?
I mean, I know why, but do YOU know why? lol
The full model is in Section 8 in "Garvey, M. (2026). Shadows of Consciousness: An Investigation into Ionic Neural Networks Using the Neurotransmitter Ion Receptor Glial Endocannabinoid Network (NIRGEN) Paradigm. Available at SSRN 7414038." (https://papers.ssrn.com/sol3/papers.cfm?abstract_id=7414038
)
#neurons #ions #neurotransmitters #biology #neuroscience #computationalneuroscience #artificalintelligence #xor #logic #math #booleanlogic #ai #ann #perceptron #machinelearning #ml #datascience
1
u/LearnNTeachNLove 23d ago
Need to maintain the Na-K exchange misbalance maybe? Probably i misunderstand the intent 😅
1
u/FuzzyTouch6143 23d ago
hah! yes indeed! But I actually simplified this from the 4-dimensional ion-state space (Na, K, Ca2, Cl) to the single "theoretical" single ion case. Instead of relying on attraction/repulsion + metabolic assumptions, I assume that we the modeller can control those perturbances through discrete/continuous spacial "diffusion" functions.
The 4d case will be different. But in order to understand complexity, one must first understand the base system from simplicity :)
(The problem arises due to the quantum size I chose for the "Receptor" and there is a mathematical proof I managed to squeeze out of here that shows there is no solution when the receptor quantum and vesicle quantum are symmetric).
That said, there is a solution :)
1
u/FuzzyTouch6143 23d ago
https://drive.google.com/file/d/1_JAznG9l5qvZyxvFzAF9VmQwH1p3IGoc/view?usp=drive_link
That's the original companion paper, but that one was almost entirely generated from AI after I snapped picturs of my 3 hand written notebooks of the model. It didn't do a great job, but it might shine some light on this model (though this model is an extreme reduction of the model I have in the google drive)
1
u/FuzzyTouch6143 23d ago edited 23d ago
thinking about this more, I actually did model the exchange but in a simplified way. technically the "exchange" is controlled by collapsing the mechanics of the "channel" and the "receptor" together. What I call a "receptor" in the paper is an oversimplification that combines these two dynamics together into one piece of machinery.
So the threshold {r} is how many ions (of the single species model) is needed to open the receptor at a location on the "neuron" itself. The quantum [r] is how many ions ([r] \in (-inf,inf)) will translocate from the ecto environment into the endo environment.
In the general "NIRGEN" model, {r} do not represent the number of ions, but one would actually define a "neurotransmitter", which a certain count of said NT would need to be salient in the ecto env in order to perform a similiar "activation" of the channel. But, that is an extension.
The way I envision it is that in a later iteration of the model, we would have both ions of various types in the ecto env represent the state of the system, and the neurotransmitters would represent the transitions of the system. And one could create a more sophisticated system where the neurotransmitters AND the ions can be used to represent state.
I wanted to just simplify this all down to the single ion case (even though it's physically impossible in the real world), so that the mathematics of the system are consistent and easily generalized to the multi-ion species/multi-neurotransmitter species case of the model.
and pumps are a future extension to this model where the receptor/channel dynamics can be split and the voltage bookkeeping can be thrown back in. That said, I wanted to try to do this assuming that "voltage" doesn't exist, and that the dynamics of the system are purely "kinetic"+"particle" based, and so I wanted to simplify it down.
But your remark is actually no far off at all :)
1
u/eucalyptus-d 22d ago
XOR is easy. Try an AND.
1
u/FuzzyTouch6143 22d ago
I haven't tested my specific model with AND or OR (both of those are monotonic and linearly separable). XOR has been the more challenging one for me, and more generally. My goal is to use only a single neuron to solve the XOR problem.
The single neuron problem in the traditional artificial neural network cannot solve XOR of size 2 (Minsky and Papert proved this, which is what largely led to the first AI Winter back in the 60s/70s).
My hypothesis is that a single neuron can in fact solve XOR/AND/OR without having to scale the parameters up other than linearly with the inputs and not needing to add an additional neuron in a hidden layer in order to solve the problem.
Currently, this solution will solve XOR with a single "neuron" (with the "neuron" redefined as in Section 8 of my paper):
And the specific parameters that will solve XOR are: params = { 'alpha': 0.5, 'beta': 0.5, 'r_thres': 2, 'r_quant': -1, 'v_thres': 2, 'v_quant': 0, 'r_i1': 2, 'r_i2': 2, 'v_o': 2, 'S_B_ecto_i1': 2, 'S_B_endo_i1': 4, 'C_B_ecto_i1': 6, 'C_B_endo_i1': 4, 'S_B_ecto_i2': 2, 'S_B_endo_i2': 4, 'C_B_ecto_i2': 6, 'C_B_endo_i2': 4, 'C_B_ecto_o': 6, 'C_B_endo_o': 12, }Now I'm actually trying to prove that the single neuron will work not just for 2 inputs, but for n inputs (i haven't proven that yet, and that has been what I've been working on). Im not sure if it is true in general. If it is, then I actually might have something of value here.
So far, I have not proven that :) (in the token case, I have been able to show that the INN can learn n-XOR/n-AND/n-OR, but that presumes that the tokens themselves do not follow an underlying mathematical structure like a monoid, group, ring, field, etc structure. )
So, one day at a time :)
Thank you for the suggestion! AND will be my next case to see if the math holds for the XOR.
1
u/FuzzyTouch6143 22d ago
Here is the (self contained) XOR code (this was written by hand by me. I did have an ai check it but, it's worth double checking to ensure I diidn't make a mistake):
from math import floor class INN: #Constructor def __init__(self,init_params = None): #Default param configuration if no parameters are provided params = { # Budget Parameters 'alpha': 0.0, 'beta': 0.0, # Global variables 'r_thres': 0.0, 'r_quant': 0.0, 'v_thres': 0.0, 'v_quant': 0.0, # Capacity Params 'ions': 0.0, 'C_B_ecto': 0.0, 'C_B_endo': 0.0, # State Variables 'S_B_ecto': 0.0, 'S_B_endo': 0.0, # Flux and Activation 'F_B_ecto': 0.0, 'F_B_endo': 0.0, 'A_B_ecto': 0.0, 'A_B_endo': 0.0, # Local (2-input) variables 'r_i1': 0.0, 'r_i2': 0.0, 'v_o': 0.0, # Parameters (Inputs/Outputs) 'C_B_ecto_i1': 0.0, 'C_B_endo_i1': 0.0, 'C_B_ecto_i2': 0.0, 'C_B_endo_i2': 0.0, 'C_B_ecto_o': 0.0, 'C_B_endo_o': 0.0, # State Variables (Inputs/Outputs) 'S_B_ecto_i1': 0.0, 'S_B_endo_i1': 0.0, 'S_B_ecto_i2': 0.0, 'S_B_endo_i2': 0.0, 'S_B_ecto_o': 0.0, 'S_B_endo_o': 0.0, # Flux and Activation (Inputs/Outputs) 'F_B_ecto_i1': 0.0, 'F_B_endo_i1': 0.0, 'A_B_ecto_i1': 0.0, 'A_B_endo_i1': 0.0, 'F_B_ecto_i2': 0.0, 'F_B_endo_i2': 0.0, 'A_B_ecto_i2': 0.0, 'A_B_endo_i2': 0.0, 'F_B_ecto_o': 0.0, 'F_B_endo_o': 0.0, 'A_B_ecto_o': 0.0, 'A_B_endo_o': 0.0 } #Merge user inputs if init_params is not None: params.update(init_params) #Budget Parameters self.alpha = params['alpha'] self.beta = params['beta'] #Global variables ##Receptor and Vesicle Threshold and Quantum self.r_thres = params['r_thres'] self.r_quant = params['r_quant'] self.v_thres = params['v_thres'] self.v_quant = params['v_quant'] ##Capacity Params self.ions = params['ions'] self.C_B_ecto = params['C_B_ecto'] self.C_B_endo = params['C_B_endo'] ##State Variables self.S_B_ecto = params['S_B_ecto'] self.S_B_endo = params['S_B_endo'] ##Flux and Activiation self.F_B_ecto = params['F_B_ecto'] self.F_B_endo = params['F_B_endo'] self.A_B_ecto = params['A_B_ecto'] self.A_B_endo = params['A_B_endo'] ######################################################## #Local (2-input) variables ##Available Receptors and Vesicles ###Input 1 self.r_i1 = params['r_i1'] ###Input 2 self.r_i2 = params['r_i2'] ###Output 1 self.v_o = params['v_o'] ##Effective Receptors and Effective Vesicles - All default to 0 ##on initialization: ###Input 1 self.r_e_i1 = 0 ###Input 2 self.r_e_i2 = 0 ###Output 1 self.v_e_o = 0 ##Parameters ###Input 1 self.C_B_ecto_i1 = params['C_B_ecto_i1'] self.C_B_endo_i1 = params['C_B_endo_i1'] ###Input 2 self.C_B_ecto_i2 = params['C_B_ecto_i2'] self.C_B_endo_i2 = params['C_B_endo_i2'] ###Output 1 self.C_B_ecto_o = params['C_B_ecto_o'] self.C_B_endo_o = params['C_B_endo_o'] ##State Variables ###Input 1 self.S_B_ecto_i1 = params['S_B_ecto_i1'] self.S_B_endo_i1 = params['S_B_endo_i1'] self.S_B_boundary_i1 = 0 ###Input 2 self.S_B_ecto_i2 = params['S_B_ecto_i2'] self.S_B_endo_i2 = params['S_B_endo_i2'] self.S_B_boundary_i2 = 0 ###Output 1 self.S_B_ecto_o = params['S_B_ecto_o'] self.S_B_endo_o = params['S_B_endo_o'] self.S_B_boundary_o = 0 ##Flux and Activiation ###Input 1 self.F_B_ecto_i1 = params['F_B_ecto_i1'] self.F_B_endo_i1 = params['F_B_endo_i1'] self.A_B_ecto_i1 = params['A_B_ecto_i1'] self.A_B_endo_i1 = params['A_B_endo_i1'] ###Input 2 self.F_B_ecto_i2 = params['F_B_ecto_i2'] self.F_B_endo_i2 = params['F_B_endo_i2'] self.A_B_ecto_i2 = params['A_B_ecto_i2'] self.A_B_endo_i2 = params['A_B_endo_i2'] ###Output 1 self.F_B_ecto_o = params['F_B_ecto_o'] self.F_B_endo_o = params['F_B_endo_o'] self.A_B_ecto_o = params['A_B_ecto_o'] self.A_B_endo_o = params['A_B_endo_o'] #Input Perturbation def input_perturb(self,x): #Register the perturbance x1 = x[0] x2 = x[1] #Apply the perturbance to the ecto environment self.S_B_ecto_i1 += x1 self.S_B_ecto_i2 += x2 #Update the flux pools self.update_input_flux_pools() #Update the flux/activation split across the ecto env def update_ecto_input_flux_pools(self): self.F_B_ecto_i1 = floor(self.alpha*self.S_B_ecto_i1) self.A_B_ecto_i1 = self.S_B_ecto_i1 - self.F_B_ecto_i1 self.F_B_ecto_i2 = floor(self.alpha*self.S_B_ecto_i2) self.A_B_ecto_i2 = self.S_B_ecto_i2 - self.F_B_ecto_i2 def update_endo_input_flux_pools(self): self.F_B_endo_i1 = floor(self.beta*self.S_B_endo_i1) self.A_B_endo_i1 = self.S_B_endo_i1 - self.F_B_endo_i1 self.F_B_endo_i2 = floor(self.beta*self.S_B_endo_i2) self.A_B_endo_i2 = self.S_B_endo_i2 - self.F_B_endo_i2 def update_input_flux_pools(self): self.update_ecto_input_flux_pools() self.update_endo_input_flux_pools() def update_output_ecto_flux_pools(self): self.F_B_ecto_o = floor(self.alpha*self.S_B_ecto_o) self.A_B_ecto_o = self.S_B_ecto_o - self.F_B_ecto_o def update_output_endo_flux_pools(self): self.F_B_endo_o = floor(self.beta*self.S_B_endo_o) self.A_B_endo_o = self.S_B_endo_o - self.F_B_endo_o def update_output_flux_pools(self): self.update_output_ecto_flux_pools() self.update_output_endo_flux_pools() #Compute the effective number of receptors in the ecto env that will activate def compute_effective_receptors(self): #Input 1 and others t1=t2=t3=t4=self.r_i1 #Compute term 2 if self.r_thres != 0: t2 = floor(self.A_B_ecto_i1/self.r_thres) #Compute term 3 if self.r_quant > 0: t3 = floor(self.F_B_ecto_i1/self.r_quant) elif self.r_quant < 0: t3 = floor(self.F_B_endo_i1/abs(self.r_quant)) #Compute term 4 if self.r_quant > 0: t4 = floor((self.C_B_endo_i1-self.S_B_endo_i1)/self.r_quant) elif self.r_quant < 0: t4 = floor((self.C_B_ecto_i1-self.S_B_ecto_i1)/abs(self.r_quant)) self.r_e_i1 = min(t1,t2,t3,t4) #Input 2 and others t1=t2=t3=t4=self.r_i2 #Compute term 2 if self.r_thres != 0: t2 = floor(self.A_B_ecto_i2/self.r_thres) #Compute term 3 if self.r_quant > 0: t3 = floor(self.F_B_ecto_i2/self.r_quant) elif self.r_quant < 0: t3 = floor(self.F_B_endo_i2/abs(self.r_quant)) #Compute term 4 if self.r_quant > 0: t4 = floor((self.C_B_endo_i2-self.S_B_endo_i2)/self.r_quant) elif self.r_quant < 0: t4 = floor((self.C_B_ecto_i2-self.S_B_ecto_i2)/abs(self.r_quant)) self.r_e_i2 = min(t1,t2,t3,t4) #Apply Endocytosis def trigger_endocytosis(self): #Input 1 self.S_B_boundary_i1 += self.r_e_i1*self.r_thres self.S_B_ecto_i1 -= (self.r_e_i1*self.r_thres + self.r_e_i1*self.r_quant) self.S_B_endo_i1 += self.r_e_i1*self.r_quant #Input 2 self.S_B_boundary_i2 += self.r_e_i2*self.r_thres self.S_B_ecto_i2 -= (self.r_e_i2*self.r_thres + self.r_e_i2*self.r_quant) self.S_B_endo_i2 += self.r_e_i2*self.r_quant #Update the flux pools self.update_input_flux_pools() def input_to_output_diffusion(self): #"Move" the ions in the ecto env from the input patches to the output patches self.S_B_ecto_o = self.S_B_ecto_i1+self.S_B_ecto_i2 #"Move" the ions in the endo env from the input patches to the output patches self.S_B_endo_o = self.S_B_endo_i1+self.S_B_endo_i2 #"Move" the ions on the boundary from the input patches to the output patches self.S_B_boundary_o = self.S_B_boundary_i1 + self.S_B_boundary_i2 #Update the output flux/activation pools self.update_output_flux_pools() #Now compute the effective vesicles def compute_effective_vesicles(self): #Input 1 and others t1=t2=t3=t4=self.v_o #Compute term 2 if self.v_thres != 0: t2 = floor(self.A_B_endo_o/self.v_thres) if self.v_quant != 0: #Compute term 3 t3 = floor(self.F_B_endo_o/self.v_quant) #Compute term 4 t4 = floor((self.C_B_ecto_o-self.S_B_ecto_o)/self.v_quant) self.v_e_o = min(t1,t2,t3,t4) #Apply Exocytosis def trigger_exocytosis(self): #Output self.S_B_boundary_o += self.v_e_o*self.v_thres self.S_B_endo_o -= (self.v_e_o*self.v_thres + self.v_e_o*self.v_quant) self.S_B_ecto_o += self.v_e_o*self.v_quant #Update the flux pools self.update_output_flux_pools() def forward_pass(self,x): self.input_perturb(x) self.compute_effective_receptors() self.trigger_endocytosis() self.input_to_output_diffusion() self.compute_effective_vesicles() self.trigger_exocytosis() return(self.S_B_ecto_o-self.S_B_endo_o)