Beispiel #1
0
    def _setup(self):
        """
        Read input file, store file paths for run
        """

        # If this fails, the program should fail. Input file is required
        # for useful output
        try:
            fin = open(self.configFileName, 'r')
            self.jConf = json.load(fin)
            fin.close()
        except IOError:
            raise NTRTMasterError("Please provide a valid configuration file")

        self.path = self.jConf['resourcePath'] + self.jConf['lowerPath']

        self.a0 = self.jConf['a0']
        self.c0 = self.jConf['c0']
        self.bp = self.jConf['bp']
        self.A = self.jConf['A']
        self.maxStep = self.jConf['maxStep']

        self.k = 1

        try:
            os.makedirs(self.path)
        except OSError:
            if not os.path.isdir(self.path):
                raise NTRTMasterError(
                    "Directed the folder path to an invalid address")
    def __getChildController(self, c1, c2, params):
        """
        Takes two controllers and merges them with a 50/50 chance of selecting
        a parameter from each controller
        """

        if (params['numberOfStates'] > 0):
            c1 = list(c1['neuralParams'])
            c2 = list(c2['neuralParams'])

        cNew = []

        if (len(c1) == 0):
            raise NTRTMasterError("Error in length")
        """
        # Old code for random params
        for i, j in zip(c1, c2):
            # Go to the deepest level of the parameters (see instances in the specification)
            if isinstance(i, collections.Iterable):
                cNew.append(self.__getChildController(i, j, params))
            else:
                # @todo should this be adjustable?
                if (random.random() > 0.5):
                    cNew.append(i)
                else:
                    cNew.append(j)
        """
        crossOver = random.randint(0, len(c1) - 1)

        print(crossOver)

        cNew[0:crossOver] = c1[0:crossOver]
        print(len(cNew))

        cNew[crossOver:len(c2) + 1] = c2[crossOver:len(c2) + 1]
        print(len(cNew))

        if (len(cNew) != len(c1)):
            raise NTRTMasterError("Error in length")

        if (params['numberOfStates'] > 0):
            newNeuro = {}
            newNeuro['neuralParams'] = cNew
            newNeuro['numStates'] = params['numberOfStates']
            newNeuro['numActions'] = params['numberOfOutputs']
            newNeuro['numHidden'] = params['numberHidden']
            return newNeuro
        else:
            return cNew
Beispiel #3
0
    def getNewFile(self, jobNum):
        """
        Handle the generation of a new JSON file with new parameters. Will vary based on the
        learning method used and the config file
        Edit this based on your parameter set
        """

        obj = {}

        for p in self.prefixes:
            if (self.lParams[p + 'Vals']['learning'] == False):
                #TODO This is a hackey solution to running mixed learning and non-learning sets. Update for the potential of non-learning sets
                jobNum_node = 0
            elif (jobNum >= len(self.currentGeneration[p])):
                jobNum_node = 0
                raise NTRTMasterError("Called a bad job number")

            else:
                jobNum_node = jobNum

            obj[p + "Vals"] = self.currentGeneration[p][jobNum_node]

        outFile = self.path + self.jConf['filePrefix'] + "_" + str(
            jobNum) + self.jConf['fileSuffix']

        fout = open(outFile, 'w')

        json.dump(obj, fout, indent=4)

        return self.jConf['filePrefix'] + "_" + str(
            jobNum) + self.jConf['fileSuffix']
Beispiel #4
0
    def startJob(self):
        """
        Override this to start the NTRT instance and pass it the relevant parameters.. This is called
        by NTRTJobMaster when it wants to start this NTRT process.
        """

        logging.info("STARTING job with args %r" % self.args)
        self.pid = os.fork()

        if self.pid == 0:
            # Redirect the stdout output to dev null in the child.
            logPath = self.args['resourcePrefix'] + self.args['path'] + self.args['filename'] + '_log.txt'
            logFile = open(logPath, 'wb')

            # A set of jobs. Currently [0 0] is flat ground, [1 0] is a block field, [0 1] is hilly terrain, and [1 1] is both
            # This will expand in the future.
            terrainMatrix = self.args['terrain']
            # Update this if the subprocess call gets changed
            if len(terrainMatrix[0]) < 4: 
                raise NTRTMasterError("Not enough terrain args!")
            
            # Run through a set of binary job options. Currently handles terrain switches
            for run in terrainMatrix:
                if (len(run)) >= 5:
                    trialLength = run[4]
                else:
                    trialLength = self.args['length']
                #TODO improve error handling here
                subprocess.check_call([self.args['executable'], "-l", self.args['filename'], "-P", self.args['path'], "-s", str(trialLength), "-b", str(run[0]), "-H", str(run[1]), "-a", str(run[2]), "-B", str(run[3]), "-G", str("0")], stdout=logFile)
            sys.exit()
    def __getControllerFromProbability(self, currentGeneration, prob):
        """
        A support function for genetic algorithms that selects a controller
        based on the distribution of probabilities for all of their controllers (their
        contriution to the total score of the generation)
        """
        for c in currentGeneration.itervalues():
            if (c['probability'] < prob):
                break

        return c

        raise NTRTMasterError(
            "Insufficient values to satisfy requested probability")
Beispiel #6
0
    def runTrials(self, st, nt):

        jobList = []

        print("Should run " + str(nt - st) + " jobs")

        numberToWrite = 0
        for p in self.prefixes:
            numberToWrite = max(numberToWrite, len(self.currentGeneration[p]))

        writeOut = min(numberToWrite, self.numTrials)

        if writeOut < nt - 1:
            raise NTRTMasterError("Not writing enough files")

        # We want to write all of the trials for post processing
        for i in range(0, writeOut):

            # MonteCarlo solution. This function could be overridden with something that
            # provides a filename for a pre-existing file
            fileName = self.getNewFile(i)

            for j in self.jConf['terrain']:
                # All args to be passed to subprocess must be strings
                args = {
                    'filename': fileName,
                    'resourcePrefix': self.jConf['resourcePath'],
                    'path': self.jConf['lowerPath'],
                    'executable': self.jConf['executable'],
                    'length': self.jConf['learningParams']['trialLength'],
                    'terrain': j
                }
                if (i <= nt and i >= st):
                    jobList.append(EvolutionJob(args))
                    self.trialTotal += 1

        # Run the jobs
        conSched = ConcurrentScheduler(jobList, self.numProcesses)
        completedJobs = conSched.processJobs()

        # Read scores from files, write to logs
        totalScore = 0
        maxScore = -1000

        for job in completedJobs:
            job.processJobOutput()
            jobVals = job.obj

            scores = jobVals['scores']

            # Iterate through all of the new scores for this file
            for i in scores:
                score = i['distance']

                for p in self.prefixes:
                    if (self.lParams[p + 'Vals']['learning']):
                        jobNum = self.getJobNum(jobVals[p + 'Vals']['paramID'],
                                                p)
                        self.currentGeneration[p][jobNum]['scores'].append(
                            score)

                totalScore += score
                if score > maxScore:
                    maxScore = score

        avgScore = totalScore / float(
            len(completedJobs) * len(self.jConf['terrain']))
        logFile = open('evoLog.txt', 'a')
        logFile.write(
            str(self.trialTotal) + ',' + str(maxScore) + ',' + str(avgScore) +
            '\n')
        logFile.close()
Beispiel #7
0
    def generationGenerator(self, currentGeneration, paramName):
        """
        Master function that takes an existing set of paramters, sorts them by score
        and then returns a new set of parameters based on the specification file
        """

        params = self.jConf["learningParams"][paramName]

        useAvg = params['useAverage']

        nextGeneration = []

        # Are we doing monteCarlo or starting a new trial?
        if (len(currentGeneration) == 0 or params['monteCarlo']):

            # Starting a new trial - load previous results, if any, unless doing monteCarlo
            if (not (params['learning'] and params['monteCarlo'])):
                for i in range(0, params['startingControllers']):
                    inFile = self.path + self.jConf['filePrefix'] + "_" + str(
                        i) + self.jConf['fileSuffix']
                    # We want the IO error if this fails
                    fin = open(inFile, 'r')
                    jControl = json.load(fin)
                    fin.close()
                    controller = {}
                    # Check how controller was generated
                    try:
                        controller['params'] = jControl[paramName]['params']
                    except KeyError:
                        controller['params'] = jControl[paramName]
                    except TypeError:
                        controller['params'] = jControl[paramName]
                    controller['paramID'] = str(self.paramID)
                    controller['scores'] = []
                    nextGeneration.append(controller)

                    self.paramID += 1

            # If no start seed, use random
            for i in range(len(nextGeneration), 1):
                if (i < 0):
                    raise NTRTMasterError(
                        "Number of controllers greater than population size!")

                controller = self.__getNewParams(paramName)
                nextGeneration.append(controller)
                self.paramID += 1

            newX = self.__JSONToArray(params, controller)

        elif (not params['learning']):
            # Not learning, return previous controllers
            nextGeneration = currentGeneration
            newX = np.array([0])

        else:
            # learning, not doing monteCarlo, have a previous generation
            if len(currentGeneration) < 3:
                raise NTRTMasterError("Incorrect input for this stage! " +
                                      str(len(currentGeneration)))

            genSize = len(currentGeneration)

            print(paramName)

            i = 0
            for controller in currentGeneration:
                try:
                    scores = controller['scores']

                    controller['maxScore'] = max(scores)
                    controller['avgScore'] = sum(scores) / float(len(scores))

                except KeyError:

                    self.runTrials(i, i)
                    scores = controller['scores']

                    controller['maxScore'] = max(scores)
                    controller['avgScore'] = sum(scores) / float(len(scores))

                except ValueError:

                    self.runTrials(i, i)
                    scores = controller['scores']

                    controller['maxScore'] = max(scores)
                    controller['avgScore'] = sum(scores) / float(len(scores))

                finally:
                    print(scores)

                    i += 1

            oldX = self.__JSONToArray(params, currentGeneration[genSize - 3])

            if (useAvg):
                newScores = [
                    currentGeneration[genSize - 2]['avgScore'],
                    currentGeneration[genSize - 1]['avgScore']
                ]
            else:
                newScores = [
                    currentGeneration[genSize - 2]['maxScore'],
                    currentGeneration[genSize - 1]['maxScore']
                ]

            gk = self.__getGk(
                self.__getCk(self.k),
                np.array(currentGeneration[genSize - 3]['deltas']), newScores)

            newX = self.__updateParams(oldX, gk, self.__getAk(self.k), params)

            # print(newX)

            newController = self.__arrayToJSON(params, newX)
            self.paramID += 1

            nextGeneration.append(currentGeneration[genSize - 3])
            nextGeneration.append(newController)

            logFile = open('bestScore.txt', 'a')
            logFile.write(
                str(self.k) + ',' + str(nextGeneration[0]['maxScore']) + ',' +
                str(nextGeneration[0]['avgScore']) + '\n')
            logFile.close()

        return [nextGeneration, newX]
    def generationGenerator(self, currentGeneration, paramName):
        """
        Master function that takes an existing set of paramters, sorts them by score
        and then returns a new set of parameters based on the specification file
        """

        numTrials = self.jConf['learningParams']['numTrials']

        params = self.jConf["learningParams"][paramName]

        useAvg = params['useAverage']

        nextGeneration = LastUpdatedOrderedDict()

        # Are we doing monteCarlo or starting a new trial?
        if (len(currentGeneration) == 0 or params['monteCarlo']):

            # Starting a new trial - load previous results, if any, unless doing monteCarlo
            if (not (params['learning'] and params['monteCarlo'])):
                for i in range(0, params['startingControllers']):
                    inFile = self.path + self.jConf['filePrefix'] + "_" + str(
                        i) + self.jConf['fileSuffix']
                    # We want the IO error if this fails
                    fin = open(inFile, 'r')
                    jControl = json.load(fin)
                    fin.close()
                    controller = {}
                    # Check how controller was generated
                    try:
                        controller['params'] = jControl[paramName]['params']
                    except KeyError:
                        controller['params'] = jControl[paramName]
                    except TypeError:
                        controller['params'] = jControl[paramName]
                    controller['paramID'] = str(self.paramID)
                    controller['scores'] = []
                    nextGeneration.__setitem__(controller['paramID'],
                                               controller)

                    self.paramID += 1

            # Fill in remaining population with random parameters
            for i in range(len(nextGeneration), params['populationSize']):
                if (params['monteCarlo']
                        and params['populationSize'] != numTrials):
                    raise NTRTMasterError(
                        "Number of trials must equal population size!")
                if (i < 0):
                    raise NTRTMasterError(
                        "Number of controllers greater than population size!")

                controller = self.__getNewParams(paramName)
                nextGeneration.__setitem__(controller['paramID'], controller)
                self.paramID += 1

        elif (not params['learning']):
            # Not learning, return previous controllers
            nextGeneration = currentGeneration

        else:
            # learning, not doing monteCarlo, have a previous generation

            popSize = len(currentGeneration)

            # order the prior population

            for k, controller in currentGeneration.iteritems():
                scores = controller['scores']
                controller['maxScore'] = max(scores)
                controller['avgScore'] = sum(scores) / float(len(scores))

            if (useAvg):
                key = lambda x: x[1]['avgScore']
            else:
                key = lambda x: x[1]['maxScore']
            sortedGeneration = collections.OrderedDict(
                sorted(currentGeneration.items(), None, key, True))

            scoreDump = open('scoreDump.txt', 'a')
            scoreDump.write(json.dumps(sortedGeneration))
            scoreDump.write('\n')
            scoreDump.close()

            totalScore = 0

            # Get probabilities for children (for mating)

            if (useAvg):

                for controller in sortedGeneration.itervalues():
                    pass

                floor = controller['avgScore']

                for controller in sortedGeneration.itervalues():
                    totalScore += controller['avgScore'] - floor

                first = True
                c1 = {}

                # All scores are the same for some reason, don't divide by zero
                if totalScore == 0.0:
                    totalScore = 1

                for c in sortedGeneration.itervalues():
                    if first:
                        c['probability'] = (c['avgScore'] - floor) / totalScore
                        first = False
                    else:
                        c['probability'] = (c['avgScore'] - floor
                                            ) / totalScore + c1['probability']
                    c1 = c

            else:
                for controller in sortedGeneration.itervalues():
                    pass

                floor = controller['maxScore']

                for controller in sortedGeneration.itervalues():
                    totalScore += controller['maxScore'] - floor

                first = True
                c1 = {}
                # All scores are the same for some reason, don't divide by zero
                if totalScore == 0.0:
                    totalScore = 1
                for c in sortedGeneration.itervalues():
                    if first:
                        c['probability'] = (c['maxScore'] - floor) / totalScore
                        first = False
                    else:
                        c['probability'] = (c['maxScore'] - floor
                                            ) / totalScore + c1['probability']
                    c1 = c

            # How many of the best controllers are we keeping?
            numElites = popSize - (params['numberToMutate'] +
                                   params['numberOfChildren'])

            if (numElites < 0):
                raise NTRTMasterError(
                    "Population slated to grow in size! Please adjust population size, number to mutate and/or number of children"
                )

            # Copy elites to new generation
            count = 0
            for c in sortedGeneration.itervalues():
                # Stop when number of elites has been reached
                if (count >= numElites):
                    break

                nextGeneration.__setitem__(c['paramID'], c)
                if c['params'] == None:
                    raise NTRTMasterError("Found it!")
                count += 1

            # Add 'asexual' mutations to next generation.
            # TODO: make option that assigns mutations to random controllers, rather than just mutating top N
            count = 0
            for c in sortedGeneration.itervalues():
                if (count >= params['numberToMutate']):
                    break
                cNew = {}
                cNew['params'] = self.__mutateParams(c['params'], paramName)
                cNew['paramID'] = str(self.paramID)
                cNew['scores'] = []

                nextGeneration.__setitem__(cNew['paramID'], cNew)
                self.paramID += 1

                if cNew['params'] == None:
                    raise NTRTMasterError("Found it!")

                count += 1

            # Add children to new generation
            for i in range(params['numberOfChildren']):

                c1Prob = random.random()
                c2Prob = random.random()

                c1 = self.__getControllerFromProbability(
                    sortedGeneration, c1Prob)
                c2 = self.__getControllerFromProbability(
                    sortedGeneration, c2Prob)

                while (c1 == c2):
                    c2Prob = random.random()
                    c2 = self.__getControllerFromProbability(
                        sortedGeneration, c2Prob)

                cNew = {}
                cNew['params'] = self.__getChildController(
                    c1['params'], c2['params'], params)

                if (random.random() >= params['childMutationChance']):
                    cNew['params'] = self.__mutateParams(
                        cNew['params'], paramName)

                cNew['paramID'] = str(self.paramID)
                cNew['scores'] = []

                nextGeneration.__setitem__(cNew['paramID'], cNew)
                self.paramID += 1

                if cNew['params'] == None:
                    raise NTRTMasterError("Found it!")

            if (len(nextGeneration) != popSize):
                raise NTRTMasterError(
                    "Failed to generate the correct number of controllers")

            if (params['numberOfStates'] > 0):
                for c in nextGeneration.itervalues():
                    c['params'][
                        'neuralFilename'] = "logs/bestParameters-test_fb-" + c[
                            'paramID'] + ".nnw"
                    self.__writeToNNW(
                        c['params']['neuralParams'],
                        self.path + c['params']['neuralFilename'])

        return nextGeneration