diff --git a/skXCS/Classifier.py b/skXCS/Classifier.py index 66cef152..a9965271 100644 --- a/skXCS/Classifier.py +++ b/skXCS/Classifier.py @@ -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) @@ -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 @@ -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) @@ -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 @@ -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: diff --git a/skXCS/ClassifierSet.py b/skXCS/ClassifierSet.py index 3bb6f5ec..2370044a 100644 --- a/skXCS/ClassifierSet.py +++ b/skXCS/ClassifierSet.py @@ -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: @@ -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) @@ -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 @@ -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): @@ -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): @@ -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 @@ -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: @@ -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): @@ -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) diff --git a/skXCS/XCS.py b/skXCS/XCS.py index f5f795f9..9673502d 100644 --- a/skXCS/XCS.py +++ b/skXCS/XCS.py @@ -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 @@ -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 @@ -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: @@ -271,7 +271,7 @@ def checkIsInt(self, num): def checkIsFloat(self,num): try: - n = float(num) + float(num) return True except: return False @@ -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: @@ -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 @@ -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