Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Increase speed/performance by roughly 10% #3

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 2 additions & 11 deletions skXCS/Classifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,6 @@ def __init__(self,xcs):
self.initTimeStamp = xcs.iterationCount
self.deletionProb = None

pass

def initializeWithParentClassifier(self,classifier):
self.specifiedAttList = copy.deepcopy(classifier.specifiedAttList)
self.condition = copy.deepcopy(classifier.condition)
Expand Down Expand Up @@ -49,8 +47,6 @@ def match(self,state,xcs):
else:
if instanceValue == self.condition[i]:
pass
elif instanceValue == None:
return False
else:
return False
return True
Expand Down Expand Up @@ -176,9 +172,7 @@ def uniformCrossover(self,classifier,xcs):
p_cl_specifiedAttList = copy.deepcopy(classifier.specifiedAttList)

# Make list of attribute references appearing in at least one of the parents.-----------------------------
comboAttList = []
for i in p_self_specifiedAttList:
comboAttList.append(i)
comboAttList = [i for i in p_self_specifiedAttList]
for i in p_cl_specifiedAttList:
if i not in comboAttList:
comboAttList.append(i)
Expand Down Expand Up @@ -235,12 +229,10 @@ def uniformCrossover(self,classifier,xcs):
if tempKey == 2:
self.condition[i_cl1] = [newMin, newMax]
classifier.condition.pop(i_cl2)

classifier.specifiedAttList.remove(attRef)
else:
classifier.condition[i_cl2] = [newMin, newMax]
self.condition.pop(i_cl1)

self.specifiedAttList.remove(attRef)

# Discrete Attribute
Expand Down Expand Up @@ -311,9 +303,8 @@ def mutateAction(self,xcs):
return changed

def getDelProp(self,meanFitness,xcs):
if self.fitness / self.numerosity >= xcs.delta * meanFitness or self.experience < xcs.theta_del:
if self.experience < xcs.theta_del or self.fitness / self.numerosity >= xcs.delta * meanFitness:
deletionVote = self.actionSetSize * self.numerosity

elif self.fitness == 0.0:
deletionVote = self.actionSetSize * self.numerosity * meanFitness / (xcs.init_fit / self.numerosity)
else:
Expand Down
49 changes: 17 additions & 32 deletions skXCS/ClassifierSet.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ def createMatchSet(self,state,xcs):
actionsNotCovered = copy.deepcopy(xcs.env.formatData.phenotypeList)
totalNumActions = len(xcs.env.formatData.phenotypeList)

for i in range(len(self.popSet)):
classifier = self.popSet[i]
for i, classifier in enumerate(self.popSet):
if classifier.match(state,xcs):
self.matchSet.append(i)
if classifier.action in actionsNotCovered:
Expand All @@ -35,7 +34,7 @@ def createMatchSet(self,state,xcs):
action = random.choice(copy.deepcopy(xcs.env.formatData.phenotypeList))
coveredClassifier = Classifier(xcs)
coveredClassifier.initializeWithMatchingStateAndGivenAction(1,state,action,xcs)
self.addClassifierToPopulation(xcs,coveredClassifier,True)
self.addClassifierToPopulation(coveredClassifier,True)
self.matchSet.append(len(self.popSet)-1)
if len(actionsNotCovered) != 0:
actionsNotCovered.remove(action)
Expand All @@ -47,16 +46,16 @@ def createMatchSet(self,state,xcs):
self.popSet[ref].matchCount += 1
xcs.timer.stopTimeMatching()

def getIdenticalClassifier(self,xcs,newClassifier):
def getIdenticalClassifier(self,newClassifier):
for classifier in self.popSet:
if newClassifier.equals(classifier):
return classifier
return None

def addClassifierToPopulation(self,xcs,classifier,isCovering):
def addClassifierToPopulation(self,classifier,isCovering):
oldCl = None
if not isCovering:
oldCl = self.getIdenticalClassifier(xcs,classifier)
oldCl = self.getIdenticalClassifier(classifier)
if oldCl != None:
oldCl.updateNumerosity(1)
self.microPopSize += 1
Expand Down Expand Up @@ -96,18 +95,14 @@ def updateFitnessSet(self,xcs):
accuracySum = 0
accuracies = []

i = 0
for clRef in self.actionSet:
for i, clRef in enumerate(self.actionSet):
classifier = self.popSet[clRef]
accuracies.append(classifier.getAccuracy(xcs))
accuracySum = accuracySum + accuracies[i]*classifier.numerosity
i+=1

i = 0
for clRef in self.actionSet:
for i, clRef in enumerate(self.actionSet):
classifier = self.popSet[clRef]
classifier.updateFitness(accuracySum,accuracies[i],xcs)
i+=1

####Action Set Subsumption####
def do_action_set_subsumption(self,xcs):
Expand Down Expand Up @@ -203,9 +198,9 @@ def insertDiscoveredClassifiers(self,child1,child2,parent1,parent2,xcs):
xcs.timer.stopTimeSubsumption()
else:
if len(child1.specifiedAttList) > 0:
self.addClassifierToPopulation(xcs, child1, False)
self.addClassifierToPopulation(child1, False)
if len(child2.specifiedAttList) > 0:
self.addClassifierToPopulation(xcs, child2, False)
self.addClassifierToPopulation(child2, False)

def subsumeClassifier(self,child,parent1,parent2,xcs):
if parent1.subsumes(child,xcs):
Expand All @@ -218,7 +213,7 @@ def subsumeClassifier(self,child,parent1,parent2,xcs):
xcs.trackingObj.subsumptionCount += 1
else: #No additional [A] subsumption w/ offspring rules
if len(child.specifiedAttList) > 0:
self.addClassifierToPopulation(xcs, child, False)
self.addClassifierToPopulation(child, False)

def getIterStampAverage(self): #Average GA Timestamp
sumCl = 0
Expand Down Expand Up @@ -278,17 +273,15 @@ def deleteFromPopulation(self,xcs):
vote = classifier.getDelProp(meanFitness,xcs)
deletionProbSum += vote
voteList.append(vote)
i = 0
for classifier in self.popSet:
for i, classifier in enumerate(self.popSet):
classifier.deletionProb = voteList[i]/deletionProbSum
i+=1

choicePoint = deletionProbSum * random.random()
newSum = 0
for i in range(len(voteList)):
classifier = self.popSet[i]
newSum = newSum + voteList[i]
for i, vote in enumerate(voteList):
newSum += vote
if newSum > choicePoint:
classifier = self.popSet[i]
classifier.updateNumerosity(-1)
self.microPopSize -= 1
if classifier.numerosity < 1:
Expand All @@ -300,10 +293,7 @@ def deleteFromPopulation(self,xcs):
return

def getFitnessSum(self):
sum = 0
for classifier in self.popSet:
sum += classifier.fitness
return sum
return sum(classifier.fitness for classifier in self.popSet)

####Clear Sets####
def clearSets(self):
Expand All @@ -326,22 +316,17 @@ def getAveGenerality(self,xcs):
aveGenerality = 0
else:
aveGenerality = generalitySum/self.microPopSize

return aveGenerality

def getAttributeSpecificityList(self,xcs): #To be changed for XCS
attributeSpecList = []
for i in range(xcs.env.formatData.numAttributes):
attributeSpecList.append(0)
attributeSpecList = [0] * xcs.env.formatData.numAttributes
for cl in self.popSet:
for ref in cl.specifiedAttList:
attributeSpecList[ref] += cl.numerosity
return attributeSpecList

def getAttributeAccuracyList(self,xcs): #To be changed for XCS
attributeAccList = []
for i in range(xcs.env.formatData.numAttributes):
attributeAccList.append(0.0)
attributeAccList = [0.0] * xcs.env.formatData.numAttributes
for cl in self.popSet:
for ref in cl.specifiedAttList:
attributeAccList[ref] += cl.numerosity * cl.getAccuracy(xcs)
Expand Down
45 changes: 16 additions & 29 deletions skXCS/XCS.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def __init__(self,learning_iterations=10000,N=1000,p_general=0.5,beta=0.2,alpha=
random_state=None,prediction_error_reduction=0.25,fitness_reduction=0.1,reboot_filename=None):

'''
:param learning_iterations: Must be nonnegative integer. The number of explore or exploit learning iterations to run
:param learning_iterations: Must be nonnegative integer. The number of explore or exploit learning iterations to run
:param N: Must be nonnegative integer. Maximum micropopulation size
:param p_general: Must be float from 0 - 1. Probability of generalizing an allele during covering
:param beta: Must be float. Learning Rate for updating statistics
Expand All @@ -38,22 +38,22 @@ def __init__(self,learning_iterations=10000,N=1000,p_general=0.5,beta=0.2,alpha=
:param init_fitness: Must be float. The initial prediction value when generating a new classifier (e.g in covering)
:param p_explore: Must be float from 0 - 1. Probability of doing an explore cycle instead of an exploit cycle
:param theta_matching: Must be nonnegative integer. Number of unique actions that must be represented in the match set (otherwise, covering)
:param do_GA_subsumption: Must be boolean. Do subsumption in GA
:param do_action_set_subsumption: Must be boolean. Do subsumption in [A]
:param max_payoff: Must be float. For single step problems, what the maximum reward for correctness
:param do_GA_subsumption: Must be boolean. Do subsumption in GA
:param do_action_set_subsumption: Must be boolean. Do subsumption in [A]
:param max_payoff: Must be float. For single step problems, what the maximum reward for correctness
:param theta_sub: Must be nonnegative integer. The experience of a classifier required to be a subsumer
:param theta_select: Must be float from 0 - 1. The fraction of the action set to be included in tournament selection
:param discrete_attribute_limit: Must be nonnegative integer OR "c" OR "d". Multipurpose param. If it is a nonnegative integer, discrete_attribute_limit determines the threshold that determines
:param discrete_attribute_limit: Must be nonnegative integer OR "c" OR "d". Multipurpose param. If it is a nonnegative integer, discrete_attribute_limit determines the threshold that determines
if an attribute will be treated as a continuous or discrete attribute. For example, if discrete_attribute_limit == 10, if an attribute has more than 10 unique
values in the dataset, the attribute will be continuous. If the attribute has 10 or less unique values, it will be discrete. Alternatively,
discrete_attribute_limit can take the value of "c" or "d". See next param for this
:param specified_attributes: Must be an ndarray type of nonnegative integer attributeIndices (zero indexed).
:param specified_attributes: Must be an ndarray type of nonnegative integer attributeIndices (zero indexed).
If "c", attributes specified by index in this param will be continuous and the rest will be discrete. If "d", attributes specified by index in this
param will be discrete and the rest will be continuous.
:param random_state: Must be an integer or None. Set a constant random seed value to some integer (in order to obtain reproducible results). Put None if none (for pseudo-random algorithm runs)
:param prediction_error_reduction: Must be float. The reduction of the prediction error when generating an offspring classifier
:param fitness_reduction: Must be float. The reduction of the fitness when generating an offspring classifier
:param reboot_filename: Must be String or None. Filename of model to be rebooted
:param random_state: Must be an integer or None. Set a constant random seed value to some integer (in order to obtain reproducible results). Put None if none (for pseudo-random algorithm runs)
:param prediction_error_reduction: Must be float. The reduction of the prediction error when generating an offspring classifier
:param fitness_reduction: Must be float. The reduction of the fitness when generating an offspring classifier
:param reboot_filename: Must be String or None. Filename of model to be rebooted
'''

#learning_iterations
Expand Down Expand Up @@ -261,7 +261,7 @@ def __init__(self,learning_iterations=10000,N=1000,p_general=0.5,beta=0.2,alpha=

def checkIsInt(self, num):
try:
n = float(num)
float(num) # this unnecessary float cast improves performance!
if num - int(num) == 0:
return True
else:
Expand All @@ -271,7 +271,7 @@ def checkIsInt(self, num):

def checkIsFloat(self,num):
try:
n = float(num)
float(num)
return True
except:
return False
Expand Down Expand Up @@ -357,23 +357,19 @@ def fit(self,X,y):
def runIteration(self,state):
self.trackingObj.resetAll()
shouldExplore = random.random() < self.p_explore
self.population.createMatchSet(state,self)
predictionArray = PredictionArray(self.population,self)
if shouldExplore:
self.population.createMatchSet(state,self)
predictionArray = PredictionArray(self.population,self)
actionWinner = predictionArray.randomActionWinner()
self.population.createActionSet(actionWinner)
reward = self.env.executeAction(actionWinner)
self.population.updateActionSet(reward,self)
self.population.runGA(state,self)
self.population.deletion(self)
else:
self.population.createMatchSet(state, self)
predictionArray = PredictionArray(self.population, self)
actionWinner = predictionArray.bestActionWinner()
self.population.createActionSet(actionWinner)
reward = self.env.executeAction(actionWinner)
self.population.updateActionSet(reward, self)
self.population.deletion(self)

if reward == self.max_payoff:
if len(self.trackedAccuracy) == self.movingAvgCount:
Expand All @@ -383,7 +379,7 @@ def runIteration(self,state):
if len(self.trackedAccuracy) == self.movingAvgCount:
del self.trackedAccuracy[0]
self.trackedAccuracy.append(0)

self.population.deletion(self)
self.trackingObj.avgIterAge = self.iterationCount - self.population.getInitStampAverage()
self.trackingObj.macroPopSize = len(self.population.popSet)
self.trackingObj.microPopSize = self.population.microPopSize
Expand Down Expand Up @@ -659,16 +655,7 @@ def get_final_attribute_accuracy_list(self):
class TempTrackingObj():
#Tracks stats of every iteration (except accuracy, avg generality, and times)
def __init__(self):
self.macroPopSize = 0
self.microPopSize = 0
self.matchSetSize = 0
self.correctSetSize = 0
self.avgIterAge = 0
self.subsumptionCount = 0
self.crossOverCount = 0
self.mutationCount = 0
self.coveringCount = 0
self.deletionCount = 0
self.resetAll()

def resetAll(self):
self.macroPopSize = 0
Expand Down