<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Prisonization Agent-Based Model
Midterm Checkpoint
Step1:  Features class
Step2:  Agent class
Step3:  Grid class
Step4:  Parameters
Step5:  Initialization
Step6:  Model run
Step7:  Model output
<ASSISTANT_TASK:>
Python Code:
# Import necesary modules
%matplotlib inline 
import random
import time
import matplotlib.pyplot as plt
import numpy
# import seaborn; seaborn.set()
# Helper function for getting the current time in seconds
millis = lambda: int(round(time.time()*1000))
Explanation: Prisonization Agent-Based Model
Midterm Checkpoint: March 25, 2016
Erin Lane
Course ID: CMPLXSYS 530
Course Title: Computer Modeling of Complex Systems
Term: Winter, 2016
Goal
In this project, I explore the relationship between density of formerly incarcerated individuals within a neighborhood and likelihood that prison culture becomes embedded into a neighborhood’s culture ("prisonization"), using an adaptation of Axelrod’s model of cultural dissemination (1997). 
Justification
Through the rise of mass incarceration over the last thirty years, American communities contain more people than ever before who have experienced incarceration. Many low-income and minority communities, disproportionately affected by tough-on-crime legislation, have particularly high concentrations of people returning home from jails and prisons. Prisons, as total institutions, tend to develop their own subcultures as their members are forced to alter their conceptions of self (Goffman 1961). Whether this prison subculture spreads to communities receiving large numbers of former inmates is unknown. 
Econometric approaches for estimating effects of incarceration on neighborhoods are generally not feasible due to multidirectional causality. An agent-based modeling approach to understanding this relationship provides bottom-up insight into understanding how influences at the individual level can cause community-level changes in culture. Through the agent-based model I am developing, I can explore the shape of the relationship between density of previously incarcerated individuals in a neighbhorhood and the likelihood that the overall neighborhood culture changes as a result of cultural traits picked up by individuals during incarceration. 
Outcome measures
The outcome of interest in this model is the shape of the relationship between initial prisionization levels and equilibrium prisonization levels. A linear or concave relationship between these two levels indicate that there are no compounding effects of high-density incarceration on neighbhorhood culture, whereas a convex relationship indicates that prison culture can spread to the wider community once incarceration reaches some tipping point. 
Model outline
1. Space
The space is a two-dimensional grid, where the edges wrap around to form a torus. 
2. Agents
The agents are individuals that reside on the grid. Each space on the grid contains an agent. Agents all have a vector of cultural traits, of which prisonized is one. 
3. Classes
The model contains three classes:
* An Agent class related to agents in the model
* A Features class related to cultural traits possessed by agents
* A Grid class related to the space in which the agents reside
4. Parameters
The model consists of four parameters 
* gridSize: Grid length and width
* prisPct: The initial level of prisonization 
* traitCounts: A vector with length indicating the number of cultural features, where each element contains the number possible values for each feature. The first element of the vector represents prisonization and will always be 2, indicating that there are two possible values for prisonized
* contagion: The likelihood that a trait will be passed from a neighbor to an agent, given that the agent and the neighbor have at least one trait in common
5. Model summary
The model is initialized by creating a gridSize by gridSize grid, and creating  agents to populate the grid. Each agent is randomly assigned a vector indicating that agent's cultural features. prisPct percent of agents in the model are randomly designated as having previously been prisonized as a result of incarceration at the start of the model. 
To begin a single step of the model, an agent is picked at random. Next, one of that agent’s four neighbors (north, south, east, and west) is randomly chosen. Each of the agent’s features, including prisonization, is compared to those if its neighbor. If the agent has any features in common with its neighbor, the agent “interacts” with its neighbor, and takes on one of the neighbor’s randomly chosen non-shared features (including prisonization) with probability contagion. 
Steps are repeated until the model reaches equilibrium. This occurs when all agents have either all features or no features in common with all of their neighbors, thus no further cultural contagion is possible. According to the Axelrod model, this should lead to some number of groups that are culturally homogenous. 
Once the model reaches equilibrium, I will calculate equilibrium proportion of agents who are prisonized. 
6. Parameter sweep
I will choose fixed values for gridSize, traitCounts, and contagion, and then sweep through values between 0 and 1 of prisPct, with increments of 0.05. For each value of prisPct, I will run the model 1,000 times. I will then collect the model parameters and the equilibrium proportion of prisonized agents and explore the relationship between initial and equilibrium prisonization levels. 
Prisonization contagion simulator
Imports
End of explanation
'''
Features class:
    Static vars:
        * traitCounts - an array with length equal to the feature count. Each
            element holds an integer representing the number of possible traits
            for the corresponding feature.
    Object vars:
        * curTraits - an array with length equal to the feature count. Each
            element holds the current trait value for the corresponding feature.
            For the purposes of this model, curTraits[0] represets the binary
            trait for the prisionization feature.
    Static methods:
        * init(traitCounts, prisPct) - sets the feature count, traitCounts,
            and initial relative prisionization.
    Object methods:
        * randomizeTraits - sets random traits for each feature in the object
        * setTrait - sets the trait of a selected feature.
'''
class Features(object):
    # Initialize the feature count and the trait ranges for those features
    @staticmethod
    def init(traitCounts):
        Features.count = len(traitCounts)
        Features.traitCounts = traitCounts
    def __init__(self):
        # Initialize an empty array with a location for each current trait
        self.curTraits = [0 for i in range(Features.count)]
        self.randomizeTraits()
        self.setTrait(0,0)
    def randomizeTraits(self):
        for i in range(1, Features.count):
            self.curTraits[i] = random.randint(0, self.traitCounts[i]-1)
    def setTrait(self, which, val):
        self.curTraits[which] = val
        
Explanation: Features class
End of explanation
'''
Agent class:
    Object vars:
        * grid - a reference to the grid in which the agent is located
        * row - row of the grid in which the agent is located
        * col - column of the grid in which the agent is located
        * features - the features object
    Object methods:
        * printTraits - prints to console the current traits for this agent
        * influencePossible - returns a boolean value indicating whether the
            agent could be influenced by any of its neighbors
        * isInfluenced(neighbor) - returns a boolean value indicating whether
            a new interaction with a given neighbor causes the agent to be
            influenced
        * isPrisonized - returns a boolean value indicating whether the agent
            is currently prisionized
        * similarity(neighbor) - returns a similarity index from 0.0 to 1.0
            indicating the agent's cultural similarity to the given neighbor
        * differingTraits(neighbor) - returns an array containing values of the
            features for which the agent and the given neighbor do not share
            the same trait
        * inheritTrait(neighbor) - causes the agent to inherit a randomly
            selected feature trait from the given neighbor
        * executeModel - selects a random neighbor, tests whether the agent
            is influenced, and if it is, causes the agent to inherit a trait
            from that neighbor
'''
class Agent(object):
    
    def __init__(self, row, col, grid):
        self.grid = grid
        self.row = row
        self.col = col
        self.features = Features()
    def printTraits(self):
        print self.features.curTraits
    def influencePossible(self):
        # Get all neighbors
        r = self.row
        c = self.col
        
        neighbors = [grid.getAgent((r+1) % self.grid.size, c), grid.getAgent((r-1) % self.grid.size, c), \
            grid.getAgent(r, (c-1) % self.grid.size), grid.getAgent(r, (c+1) % self.grid.size)]                
                          
        # Influence is possible if similarity to any neighbor is between 0 and 1
        for i in range(len(neighbors)):
            similarity = self.similarity(neighbors[i])
            if similarity > 0 and similarity < 1:
                return True
        return False
    
    def isInfluenced(self, neighbor):
        sim = self.similarity(neighbor)
        if sim ==1 or sim ==0:
            return False
        elif contagion > random.random:
            return True 
    
    def isPrisonized(self):
        return True if self.features.curTraits[0] == 1 else False
    def similarity(self, neighbor):
        matchingTraits = 0
        for x in range (Features.count):
            if self.features.curTraits[x] == neighbor.features.curTraits[x]:
                matchingTraits += 1
        return float(matchingTraits) / Features.count
    def differingTraits(self, neighbor):
        diffTraits = []
        for x in range (Features.count):
            if self.features.curTraits[x] != neighbor.features.curTraits[x]:
                diffTraits.append(x)
        return diffTraits
    def inheritTrait(self, neighbor):
        which = random.choice(self.differingTraits(neighbor))
        self.features.curTraits[which] = neighbor.features.curTraits[which]
    def executeModel(self):
        # Pick a neighbor location
        # I changed this to use NSEW neighbors, and to wrap around the grid
        if random.random > .5:    
            row = (self.row + random.choice([1, -1])) % self.grid.size
            col = self.col
        else:
            row = self.row
            col = (self.col + random.choice([1, -1])) % self.grid.size
        # Retrieve neighbor
        neighbor = self.grid.getAgent(row, col)
        # If the agent is influenced, inherit a trait from neighbor
        if self.isInfluenced(neighbor):
            self.inheritTrait(neighbor)
Explanation: Agent class
End of explanation
'''
Grid class:
    Object vars:
        * size - the height / width of the grid
        * agents - a 2D matrix where each element contains an agent
        * getLocationCount - returns the total number of elements in the
            agents matrix
    Object methods:
        * addAgent(row, col) - adds a new agent at the specified matrix location
        * getAgent(row, col) - returns the agent object from the specified
            matrix location
        * getPrisPortion - returns a value from 0.0 to 1.0 representing the
            portion of the total grid population that is currently prisionized
        * isAtEquilibrium - returns a boolean value indicating whether the
            grid object is currently at equilibrium (this occurs when every
            agent in the grid either completly shares the culture of all its
            neighbors or shares no culture with its neighbors)
'''
class Grid(object):
    def __init__(self, size):
        self.agents = [[0 for x in range(size)] for x in range(size)]
        self.size = size
    def getLocationCount(self):
        count = self.size * self.size
        return count
    def addAgent(self, row, col):
        self.agents[row][col] = Agent(row, col, self)
    def getAgent(self, row, col):
        # If the requested location is off the edge of the grid, wrap
        # around to the other edge
        if row < 0:
            row = grid.size - 1
        elif row > grid.size - 1:
            row = 0
        if col < 0:
            col = grid.size - 1
        elif col > grid.size - 1:
            col = 0
        return self.agents[row][col]
    def getPrisPortion(self):
        prisPop = 0
        for x in range(self.size):
            for y in range(self.size):
                if self.getAgent(x, y).isPrisonized():
                    prisPop += 1
        return float(prisPop) / self.getLocationCount()
    def isAtEquilibrium(self):
        for x in range(self.size):
            for y in range(self.size):
                if(self.getAgent(x, y).influencePossible()):
                    return False
        return True
Explanation: Grid class
End of explanation
# Parameters
gridSize = 10
prisPct = 0.2
contagion = 1
 
'''
Set the possible trait counts for the features. In this case, prisionization
is binary while the other features have some other abitrary number of traits
associated with them.
'''
traitCounts = [2,3,3,3]
'''
Initialize the features class with the number of different traits for each of
the features and the initial relative prisionization rate.
'''
Features.init(traitCounts)
Explanation: Parameters
End of explanation
# Initialize the grid size, add agents
grid = Grid(gridSize)
for x in range(grid.size):
    for y in range(grid.size):
        grid.addAgent(x, y)
# Assign initial prisionization 
x = round(prisPct*grid.getLocationCount())
loopCount = 0 
while x > 0:
    loopCount += 1
    row = random.randint(0,grid.size)
    col = random.randint(0,grid.size)
    agent = grid.getAgent(row, col)
    if agent.isPrisonized() == False:
        agent.features.setTrait(0,1)
        x -= 1
        
# Report starting state
print "Starting prisionization: " + str(grid.getPrisPortion())
print "Loops: " + str(loopCount)
Explanation: Initialization
End of explanation
# Run the model
iteration = 0
running = True
startTime = millis()
while running:
    # Select a random agent for this model step, then execute the model it
    thisAgent = grid.getAgent(random.randint(0, (grid.size - 1)), \
        random.randint(0, (grid.size - 1)))
    thisAgent.executeModel()
    iteration += 1
    # Only check for equilibrium once in a while to save time
    if iteration % 10 == 0:
        if grid.isAtEquilibrium():
            running = False
Explanation: Model run
End of explanation
# Report model results
print "Completion time: " + str((millis() - startTime)) + " milliseconds"
print "Model reached equilibrium after " + str(iteration) + " increments"
print "Ending prisionization: " + str(grid.getPrisPortion())
for col in range(grid.size):
    for row in range(grid.size):
        agent = grid.getAgent(row, col)
        print "(" + str(row) + ", " + str(col) + ") " + str(agent.features.curTraits)
Explanation: Model output
End of explanation
<END_TASK>