diff --git a/brain/README.txt b/brain/README.txt index f7490e0..f47f391 100644 --- a/brain/README.txt +++ b/brain/README.txt @@ -18,17 +18,29 @@ when creating a synapsis: [] >>> n01.receivers == [s0102] True - >>> n01.state.value - 1.0 - >>> n02.state.value - 1.0 + >>> n01.getState() + + >>> n02.getState() + -When we trigger a neuron, all its receivers get triggered so that the -receivers' state is updated: +When we change the state of a neuron and notify it, all its receiver synapses +get triggered so that the receiver neurons' states are updated: - >>> n01.trigger() - >>> n01.state.value - 1.0 - >>> n02.state.value - 2.0 + >>> from cybertools.brain.state import State + >>> n01.setState(State(1.0)) + >>> n01.getState() + + >>> n01.notify() + >>> n02.getState() + +To allow for concurrent (thread-safe) access to the brain all changes to +the neurons' states is under the control of a transaction. If we end the +current transaction all state changes will be forgotton: + + >>> from cybertools.brain.transaction import endTransaction + >>> endTransaction() + >>> n01.getState() + + >>> n02.getState() + diff --git a/brain/interfaces.py b/brain/interfaces.py index 7329e70..59371ca 100644 --- a/brain/interfaces.py +++ b/brain/interfaces.py @@ -34,24 +34,32 @@ class ISynapsis(Interface): transition = Attribute("A transition changes the sender neuron's state.") + def trigger(transaction=None): + """ Recalculate the receiver neuron's state by executing the + synapse's transition using the state of the sender neuron. + """ + class INeuron(Interface): - state = Attribute("The current state of the neuron") - senders = Attribute("The sender synapses") receivers = Attribute("The receiver synapses") - def trigger(): + def setState(state, transaction=None): + """ Set the neuron's state. + """ + + def getState(transaction=None): + """ Return the neuron's state. + """ + + def notify(transaction=None): """ Notifies the neuron that something has happened. This method - executes all transitions from all sender synapses in order to - calculate the neuron's new state; - then it should call the trigger() method on all downstream - (receiver)neurons. + calls the trigger() method on all downstream (receiver) synapses. In addition it may perform side effects like changing transition properties of adjacent synapses or even create new synapses or neurons; this side effects should happen before - triggering the receiver neurons. + triggering the receiver synapses. """ @@ -62,7 +70,12 @@ class IState(Interface): class ITransition(Interface): - def execute(state): - """ Transform state to a new state value and return it. + def execute(transaction=None): + """ Transform the receiver's state to a new state value and return it. """ + +class ITransaction(Interface): + """ A transaction that keeps track of the neurons' states. + """ + diff --git a/brain/neuron.py b/brain/neuron.py index e3f8209..6b076d4 100644 --- a/brain/neuron.py +++ b/brain/neuron.py @@ -19,12 +19,13 @@ """ A simple basic implementation of Neuron and Synapsis. -$Id: type.py 1129 2006-03-19 09:46:08Z helmutm $ +$Id$ """ from zope.interface import implements from cybertools.brain.interfaces import INeuron, ISynapsis from cybertools.brain.state import State, Transition +from cybertools.brain.transaction import getTransaction class Synapsis(object): @@ -33,10 +34,6 @@ class Synapsis(object): implements(ISynapsis) - sender = None - reciever = None - transition = None - def __init__(self, sender, receiver): self.sender = sender sender.receivers.append(self) @@ -44,28 +41,33 @@ class Synapsis(object): receiver.senders.append(self) self.transition = Transition(self) + def trigger(self, transaction=None): + receiver = self.receiver + receiver.setState(self.transition.execute(transaction), transaction) + receiver.notify(transaction) + class Neuron(object): implements(INeuron) - state = None - def __init__(self): self.senders = [] self.receivers = [] self.state = State() - self.active = False - def trigger(self): - if self.active: # avoid cycles - return - self.active = True - state = self.state - for s in self.senders: - state = s.transition.execute(state) - self.state = state + def setState(self, state, transaction=None): + transaction = getTransaction(transaction) + transaction.setState(self, state) + + def getState(self, transaction=None): + if transaction is None: + transaction = getTransaction(create=False) + if transaction is None: + return self.state + return transaction.getState(self) + + def notify(self, transaction=None): for r in self.receivers: - r.receiver.trigger() - self.active = False + r.trigger(transaction) diff --git a/brain/state.py b/brain/state.py index 726fb4f..065714b 100644 --- a/brain/state.py +++ b/brain/state.py @@ -32,9 +32,12 @@ class State(object): implements(IState) - def __init__(self, value=1.0): + def __init__(self, value=0.0): self.value = value + def __repr__(self): + return '' % self.value + class Transition(object): @@ -44,7 +47,9 @@ class Transition(object): self.synapsis = synapsis self.factor = factor - def execute(self, state): - return State(state.value + self.synapsis.sender.state.value * self.factor) + def execute(self, transaction=None): + oldState = self.synapsis.receiver.getState(transaction) + senderState = self.synapsis.sender.getState(transaction) + return State(oldState.value + senderState.value * self.factor) diff --git a/brain/transaction.py b/brain/transaction.py new file mode 100644 index 0000000..5962c6a --- /dev/null +++ b/brain/transaction.py @@ -0,0 +1,63 @@ +# +# Copyright (c) 2006 Helmut Merz helmutm@cy55.de +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +""" +Transaction management. + +$Id$ +""" + +from zope.interface import implements +from cybertools.brain.interfaces import ITransaction + + +class Transaction(object): + + implements(ITransaction) + + def __init__(self): + self.states = {} + + def setState(self, neuron, state): + self.states[neuron] = state + + def getState(self, neuron): + return self.states.get(neuron, neuron.state) + + +transactions = [] + +def getTransaction(transaction=None, create=True): + if transaction is None: + if transactions: + transaction = transactions[0] + elif create: + transaction = Transaction() + transactions.append(transaction) + else: + return None + return transaction + +def endTransaction(transaction=None): + if transaction is None: + if transactions: + del transactions[0] + else: + if transaction in transactions: + del transactions[transactions.index(transaction)] +