# The Jets and the Sharks

# Code developed in lab Wednesday, March 29, 2006

#----------------------------------------------------------------------
# global parameters of the model

ALPHA = 0.4       # external input scale factor
BETA = 0.1        # excitation scale factor
GAMMA = 0.1       # inhibition scale factor
MINIMUM = -0.2    # minimum allowable unit activation
MAXIMUM = 1.0     # maximum allowable unit activation
DECAY = 0.1       # activation decay factor
RESTING = -0.1    # resting activation level of a unit

#----------------------------------------------------------------------

class Unit:

    def __init__(self, label):
        self.label = label
        self.activation = RESTING
        self.externalInput = 0
        self.connectionsIn = []
        self.activationChange = 0

    def __str__(self):
        return self.label

    def show(self):
        print "Label: %s" % self.label
        print "Activation: %.4f" % self.activation
        print "External input: %d" % self.externalInput
        print "Incoming connections:"
        for c in self.connectionsIn:
            print c

    def connectTo(self, otherUnit, strength):
        c = Connection(self, otherUnit, strength)
        otherUnit.connectionsIn.append(c)

    def computeActivationChange(self):
        # compute excitation and inhibition coming into this unit
        excitation = 0
        inhibition = 0
        for c in self.connectionsIn:
            v = c.fromUnit
            # only process positively activated units
            if v.activation > 0:
                if c.strength > 0:
                    # excitatory connection
                    excitation = excitation + v.activation * c.strength
                else:
                    # inhibitory connection
                    inhibition = inhibition + v.activation * c.strength
        # compute total incoming activation to this unit
        totalInput = ALPHA*self.externalInput + BETA*excitation + GAMMA*inhibition
        # scale the total input
        if totalInput > 0:
            scaledInput = (MAXIMUM - self.activation) * totalInput
        else:
            scaledInput = (self.activation - MINIMUM) * totalInput
        # compute activation change for this unit
        self.activationChange = scaledInput - DECAY*(self.activation - RESTING)

    def setActivation(self):
        self.activation = self.activation + self.activationChange
        # keep the unit's activation within bounds
        if self.activation > MAXIMUM:
            self.activation = MAXIMUM
        if self.activation < MINIMUM:
            self.activation = MINIMUM

#----------------------------------------------------------------------

class Connection:

    def __init__(self, fromUnit, toUnit, strength):
        self.fromUnit = fromUnit
        self.toUnit = toUnit
        self.strength = strength

    def __str__(self):
        if self.strength > 0:
            sign = "+"
        else:
            sign = "-"
        return "%-10s--[%s]--> %s" % (self.fromUnit, sign, self.toUnit)

#----------------------------------------------------------------------

def connectProperties(instanceUnit, propertyUnits):
    # create mutually excitatory connections between an instance unit
    # and all units in the propertyUnits list
    for p in propertyUnits:
        instanceUnit.connectTo(p, +1)
    for p in propertyUnits:
        p.connectTo(instanceUnit, +1)
    
def connectInhibitory(units):
    # create mutually inhibitory connections between all pairs of
    # units within a group of units
    for u in units:
        for v in units:
            if u != v:
                u.connectTo(v, -1)
                
def oneCycle():
    # perform one complete activation update
    for u in allUnits:
        u.computeActivationChange()
    for u in allUnits:
        u.setActivation()
    jsnet.update()

def run(n):
    # run the network for n time steps
    for i in range(n):
        oneCycle()

def showAct():
    # print out positively activated units
    for u in allUnits:
        if u.activation > 0:
            print "%-10s%.4f" % (u.label, u.activation)        

def clamp(unit):
    # turn on external input to a unit
    unit.externalInput = 1
    print "Clamped %s" % unit.label
    jsnet.redraw()

def unclamp(unit):
    # turn off external input to a unit
    unit.externalInput = 0
    print "Unclamped %s" % unit.label
    jsnet.redraw()

def reset():
    # reset the network to its initial state
    for u in allUnits:
        u.activation = RESTING
        u.externalInput = 0
    print "Network reset"
    jsnet.reset()

#----------------------------------------------------------------------

if __name__ == '__main__':

    jets = Unit("Jets")
    sharks = Unit("Sharks")

    in20s = Unit("in20s")
    in30s = Unit("in30s")
    in40s = Unit("in40s")

    juniorhigh = Unit("J.H.")
    highschool = Unit("H.S.")
    college = Unit("College")

    single = Unit("Single")
    married = Unit("Married")
    divorced = Unit("Divorced")

    burglar = Unit("Burglar")
    bookie = Unit("Bookie")
    pusher = Unit("Pusher")

    nameArt = Unit("Art")
    nameAl = Unit("Al")
    nameSam = Unit("Sam")
    nameClyde = Unit("Clyde")
    nameMike = Unit("Mike")
    nameJim = Unit("Jim")
    nameGreg = Unit("Greg")
    nameJohn = Unit("John")
    nameDoug = Unit("Doug")
    nameLance = Unit("Lance")
    nameGeorge = Unit("George")
    namePete = Unit("Pete")
    nameFred = Unit("Fred")
    nameGene = Unit("Gene")
    nameRalph = Unit("Ralph")
    namePhil = Unit("Phil")
    nameIke = Unit("Ike")
    nameNick = Unit("Nick")
    nameDon = Unit("Don")
    nameNed = Unit("Ned")
    nameKarl = Unit("Karl")
    nameKen = Unit("Ken")
    nameEarl = Unit("Earl")
    nameRick = Unit("Rick")
    nameOl = Unit("Ol")
    nameNeal = Unit("Neal")
    nameDave = Unit("Dave")

    instArt = Unit("(art)")
    instAl = Unit("(al)")
    instSam = Unit("(sam)")
    instClyde = Unit("(clyde)")
    instMike = Unit("(mike)")
    instJim = Unit("(jim)")
    instGreg = Unit("(greg)")
    instJohn = Unit("(john)")
    instDoug = Unit("(doug)")
    instLance = Unit("(lance)")
    instGeorge = Unit("(george)")
    instPete = Unit("(pete)")
    instFred = Unit("(fred)")
    instGene = Unit("(gene)")
    instRalph = Unit("(ralph)")
    instPhil = Unit("(phil)")
    instIke = Unit("(ike)")
    instNick = Unit("(nick)")
    instDon = Unit("(don)")
    instNed = Unit("(ned)")
    instKarl = Unit("(karl)")
    instKen = Unit("(ken)")
    instEarl = Unit("(earl)")
    instRick = Unit("(rick)")
    instOl = Unit("(ol)")
    instNeal = Unit("(neal)")
    instDave = Unit("(dave)")

    gangUnits = [jets, sharks]

    ageUnits = [in20s, in30s, in40s]

    educationUnits = [juniorhigh, highschool, college]

    maritalUnits = [single, married, divorced]

    jobUnits = [pusher, burglar, bookie]

    nameUnits = [nameArt, nameAl, nameSam, nameClyde, nameMike, nameJim,
                 nameGreg, nameJohn, nameDoug, nameLance, nameGeorge, namePete,
                 nameFred, nameGene, nameRalph, namePhil, nameIke, nameNick,
                 nameDon, nameNed, nameKarl, nameKen, nameEarl, nameRick,
                 nameOl, nameNeal, nameDave]

    instUnits = [instArt, instAl, instSam, instClyde, instMike, instJim,
                 instGreg, instJohn, instDoug, instLance, instGeorge, instPete,
                 instFred, instGene, instRalph, instPhil, instIke, instNick,
                 instDon, instNed, instKarl, instKen, instEarl, instRick,
                 instOl, instNeal, instDave]

    allUnits = gangUnits + ageUnits + educationUnits + maritalUnits + jobUnits + \
               nameUnits + instUnits

    connectProperties(instArt, [nameArt, jets, in40s, juniorhigh, single, pusher])
    connectProperties(instAl, [nameAl, jets, in30s, juniorhigh, married, burglar])
    connectProperties(instSam, [nameSam, jets, in20s, college, single, bookie])
    connectProperties(instClyde, [nameClyde, jets, in40s, juniorhigh, single, bookie])
    connectProperties(instMike, [nameMike, jets, in30s, juniorhigh, single, bookie])
    connectProperties(instJim, [nameJim, jets, in20s, juniorhigh, divorced, burglar])
    connectProperties(instGreg, [nameGreg, jets, in20s, highschool, married, pusher])
    connectProperties(instJohn, [nameJohn, jets, in20s, juniorhigh, married, burglar])
    connectProperties(instDoug, [nameDoug, jets, in30s, highschool, single, bookie])
    connectProperties(instLance, [nameLance, jets, in20s, juniorhigh, married, burglar])
    connectProperties(instGeorge, [nameGeorge, jets, in20s, juniorhigh, divorced, burglar])
    connectProperties(instPete, [namePete, jets, in20s, highschool, single, bookie])
    connectProperties(instFred, [nameFred, jets, in20s, highschool, single, pusher])
    connectProperties(instGene, [nameGene, jets, in20s, college, single, pusher])
    connectProperties(instRalph, [nameRalph, jets, in30s, juniorhigh, single, pusher])
    connectProperties(instPhil, [namePhil, sharks, in30s, college, married, pusher])
    connectProperties(instIke, [nameIke, sharks, in30s, juniorhigh, single, bookie])
    connectProperties(instNick, [nameNick, sharks, in30s, highschool, single, pusher])
    connectProperties(instDon, [nameDon, sharks, in30s, college, married, burglar])
    connectProperties(instNed, [nameNed, sharks, in30s, college, married, bookie])
    connectProperties(instKarl, [nameKarl, sharks, in40s, highschool, married, bookie])
    connectProperties(instKen, [nameKen, sharks, in20s, highschool, single, burglar])
    connectProperties(instEarl, [nameEarl, sharks, in40s, highschool, married, burglar])
    connectProperties(instRick, [nameRick, sharks, in30s, highschool, divorced, burglar])
    connectProperties(instOl, [nameOl, sharks, in30s, college, married, pusher])
    connectProperties(instNeal, [nameNeal, sharks, in30s, highschool, single, bookie])
    connectProperties(instDave, [nameDave, sharks, in30s, highschool, divorced, pusher])

    connectInhibitory(gangUnits)
    connectInhibitory(ageUnits)
    connectInhibitory(educationUnits)
    connectInhibitory(maritalUnits)
    connectInhibitory(jobUnits)
    connectInhibitory(nameUnits)
    connectInhibitory(instUnits)

    from jsGraphics import JSNetwork
    jsnet = JSNetwork(allUnits)
