Ejemplo n.º 1
0
    def __evaluate(self, newObj, assignments, candidateForest, cluster):
        if (self.DEBUG_MESSAGES):
            print("=============================")
            print("Evaluate Method Started...")
            print(
                "Current Partial Objective: %s || Candidate Partial Objective %s"
                % (newObj, candidateForest.getTotalCost()))

        if (newObj - candidateForest.getTotalCost() <= self.EPS):

            newSolution = Forest()
            newSolution.buildForestFromArray(
                self.subproblemSolutionForest.getAssignmentsArray(),
                self.facilities, self.customers)
            partialSolution = Forest()
            partialSolution.buildForestFromDict(
                Util.getDictSolutionFromMIP(assignments), self.facilities,
                self.customers)
            currentForestObj = self.subproblemSolutionForest.getTotalCost()

            newFacilities = set()
            previousFacilities = set()

            for tree in candidateForest.getTrees().values():
                newSolution.removeTree(tree.getRoot().index)
                previousFacilities.add(tree.getRoot().index)

            for tree in partialSolution.getTrees().values():
                newSolution.addTree(Tree(tree.getRoot()))
                newFacilities.add(tree.getRoot().index)
                for node in tree.getNodes().values():
                    newSolution.getTrees().get(
                        tree.getRoot().index).addNode(node)

            #Facilities that were in the solution, but was not even selected as candidates
            clusterIntersection = set([
                tree.getRoot().index
                for tree in self.subproblemSolutionForest.getTrees().values()
            ]).intersection(cluster)
            notInterestingFacilities = clusterIntersection.difference(
                previousFacilities)

            for facilityIndex in notInterestingFacilities:
                newSolution.removeTree(facilityIndex)

            newSolution.updateStatistics()

            previousCandidates = list(
                previousFacilities.difference(
                    newFacilities.intersection(previousFacilities)))

            if (len(previousCandidates) == 0):
                previousCandidates = list(newFacilities)

            previousCandidates.extend(list(notInterestingFacilities))

            print("Current Objective: %s || Candidate Objective: %s" %
                  (currentForestObj, newSolution.getTotalCost()))

            if (self.params["improvementType"] == ImprovementType.Best):
                if (Util.truncate(
                        Util.truncate(newSolution.getTotalCost(), 10) -
                        Util.truncate(
                            self.subproblemSolutionForest.getTotalCost(), 10),
                        10) <= self.EPS):
                    if (self.DEBUG_MESSAGES):
                        print("NEW SOLUTION FOUND!")
                    self.subproblemSolutionForest.buildForestFromArray(
                        newSolution.getAssignmentsArray(), self.facilities,
                        self.customers)

                    newForestObj = self.subproblemSolutionForest.getTotalCost()

                    self.__updateFrequency(list(newFacilities),
                                           self.ASSIGNMENT_REWARD)

                    previousCandidates = list(
                        previousFacilities.difference(
                            newFacilities.intersection(previousFacilities)))

                    if (len(previousCandidates) == 0):
                        previousCandidates = list(newFacilities)

                    previousCandidates.extend(list(notInterestingFacilities))

                    reward = Util.truncate(
                        float(len(newFacilities)) /
                        float(len(previousCandidates)), 3)

                    self.__updateFrequency(previousCandidates, reward)

                    if (self.DEBUG_MESSAGES):
                        print("Previous Objective: %s || New Objective: %s" %
                              (currentForestObj, newForestObj))
                        print("Partial Solution")
                        partial = ""
                        partial = '%.2f' % self.subproblemSolutionForest.getTotalCost(
                        ) + ' ' + str(0) + '\n'
                        partial += ' '.join(
                            map(
                                str,
                                self.subproblemSolutionForest.
                                getAssignmentsArray()))
                        print(partial)
                else:
                    candidates = [
                        tree.getRoot().index
                        for tree in candidateForest.getTrees().values()
                    ]
                    #reward = (Util.truncate(float(candidateForest.getTreesCount()/self.facilitiesCount),3))*self.ASSIGNMENT_REWARD
                    reward = self.NO_ASSIGNMENT_REWARD
                    self.__updateFrequency(candidates, reward)

            elif self.params["improvementType"] == ImprovementType.First:
                self.subproblemSolutionForest.buildForestFromArray(
                    newSolution.getAssignmentsArray(), self.facilities,
                    self.customers)

                newForestObj = self.subproblemSolutionForest.getTotalCost()

                self.__updateFrequency(list(newFacilities),
                                       self.ASSIGNMENT_REWARD)

                previousCandidates = list(
                    previousFacilities.difference(
                        newFacilities.intersection(previousFacilities)))

                if (len(previousCandidates) == 0):
                    previousCandidates = list(newFacilities)

                previousCandidates.extend(list(notInterestingFacilities))

                reward = Util.truncate(
                    float(len(newFacilities)) / float(len(previousCandidates)),
                    3)

                self.__updateFrequency(previousCandidates, reward)

                if (self.DEBUG_MESSAGES):
                    print("Previous Objective: %s || New Objective: %s" %
                          (currentForestObj, newForestObj))
                    print("Partial Solution")
                    partial = ""
                    partial = '%.2f' % self.subproblemSolutionForest.getTotalCost(
                    ) + ' ' + str(0) + '\n'
                    partial += ' '.join(
                        map(
                            str,
                            self.subproblemSolutionForest.getAssignmentsArray(
                            )))
                    print(partial)

                candidates = [
                    tree.getRoot().index
                    for tree in candidateForest.getTrees().values()
                ]
                #reward = (Util.truncate(float(candidateForest.getTreesCount()/self.facilitiesCount),3))*self.ASSIGNMENT_REWARD
                reward = self.NO_ASSIGNMENT_REWARD
                self.__updateFrequency(candidates, reward)

        if (self.DEBUG_MESSAGES):
            print("Evaluate Method Finished...")
            print("=============================")
Ejemplo n.º 2
0
class LNS:

    EPS = 1.e-10
    DEBUG_MESSAGES = False
    ASSIGNMENT_REWARD = 1
    INITIAL_REWARD = 0.5
    NO_ASSIGNMENT_REWARD = 0.25
    MAX_PROBLEM_SIZE = 25000

    def __init__(self, facilities, customers, params):
        self.facilities = dict(
            zip([facility.index for facility in facilities],
                [facility for facility in facilities]))
        self.customers = customers
        self.subproblemSolutionForest = Forest()
        self.currentIteration = 0
        self.mip = MIP(facilities, customers,
                       "Instance_%s_%s" % (len(facilities), len(customers)),
                       params["mipSolver"])
        self.facilitiesCount = len(facilities)
        self.quantiles = []
        self.params = params
        self.totalDemand = sum([customer.demand for customer in customers])
        self.currentObjectiveFunction = 0
        self.currentSolutionAssignment = []

    def __InitializeProblem(self, facilities, customers):
        self.currentIteration = 0
        self.clusterAreas = Preprocessing.getClusters(
            facilities.values(), self.params["quantile_intervals"])
        if (self.params["initialSolutionFunction"] ==
                InitialSolutionFunction.Radius):
            Preprocessing.getDistanceQuantiles(
                facilities, self.params["quantile_intervals"])
            self.subproblemSolutionForest.buildForestFromArray(
                Util.formatSolutionFromMIP(
                    Preprocessing.getRadiusDistanceInitialSolution(
                        facilities, customers, self.clusterAreas.get(0))),
                self.facilities, self.customers)
        else:
            self.subproblemSolutionForest.buildForestFromArray(
                Util.formatSolutionFromMIP(
                    Preprocessing.getNearestNeighbourInitialSolution(
                        facilities, customers,
                        self.params["initialSolutionFunction"])),
                self.facilities, self.customers)

        for facility in facilities.keys():
            self.facilities[facility] = self.facilities[facility]._replace(
                frequency=self.INITIAL_REWARD)

    def __getQuantiles(self):
        firstQuantile = Util.truncate(1.0 / float(len(self.clusterAreas)), 10)

        quantileIntervals = len(self.clusterAreas)

        quantile = firstQuantile

        for interval in range(0, quantileIntervals - 1):
            self.quantiles.append(quantile)
            quantile = Util.truncate(quantile + firstQuantile, 10)

        self.quantiles.append(Util.truncate(quantile - firstQuantile, 10))

    def __getCandidateFacilities(self,
                                 cluster,
                                 demand,
                                 threshold,
                                 fulfillDemand=True):

        freqs = [self.facilities[i].frequency for i in cluster]
        candidateIndexes = Util.filterbyThreshold(freqs, threshold,
                                                  self.currentIteration + 1)
        result = [cluster[i] for i in candidateIndexes]

        candidatesCapacity = 0
        for index in result:
            candidatesCapacity = candidatesCapacity + self.facilities[
                index].capacity

        print("Demand: %s || Capacity: %s" % (demand, candidatesCapacity))

        if (candidatesCapacity < demand):
            if fulfillDemand:
                remainingFacilitiesIndex = set(cluster).difference(
                    set(cluster).intersection(set(result)))
                remainingFacilities = [
                    self.facilities[i] for i in remainingFacilitiesIndex
                ]
                remainingFacilities.sort(key=lambda x: x.cost_per_capacity,
                                         reverse=True)
                print("Demand above Capacity")
                for facility in remainingFacilities:
                    result.append(facility.index)
                    candidatesCapacity = candidatesCapacity + facility.capacity
                    if (candidatesCapacity >= demand):
                        print("Demand: %s || New Capacity: %s" %
                              (demand, candidatesCapacity))
                        break
            else:
                return None

        return result

    def __updateFrequency(self, facilities, reward):

        for index in facilities:
            if (index not in self.facilities.keys()):
                input("key does not exists!")
            freq = self.facilities[index].frequency + reward
            self.facilities[index] = self.facilities[index]._replace(
                frequency=freq)

    def __destroy(self, cluster):

        if (self.DEBUG_MESSAGES):
            print("=============================")
            print("Destroy Method Started...")

        clusterDemand = 0
        for facilityIndex in cluster:
            if (facilityIndex
                    in self.subproblemSolutionForest.getTrees().keys()):
                for node in self.subproblemSolutionForest.getTrees().get(
                        facilityIndex).getNodes().values():
                    clusterDemand = clusterDemand + node.demand

        #candidateFacilities = self.__getCandidateFacilities(cluster,clusterDemand,Util.truncate(self.quantiles[self.currentIteration],5))
        candidateFacilities = copy.deepcopy(cluster)
        print("Current Forest: %s/%s - Candidate Facilities: %s/%s" %
              (self.subproblemSolutionForest.getTreesCount(),
               self.subproblemSolutionForest.getTotalNodes(),
               len(candidateFacilities), len(cluster)))

        reassignmentCandidates = Forest()

        for facilityIndex in candidateFacilities:
            if (facilityIndex not in reassignmentCandidates.getTrees().keys()):
                reassignmentCandidates.addTree(
                    Tree(self.facilities[facilityIndex]))

        for facilityIndex in cluster:
            if (facilityIndex
                    in self.subproblemSolutionForest.getTrees().keys()):
                for node in self.subproblemSolutionForest.getTrees().get(
                        facilityIndex).getNodes().values():
                    reassignmentCandidates.getTrees().get(
                        candidateFacilities[0]).addNode(node)

        reassignmentCandidates.updateStatistics()

        if (self.DEBUG_MESSAGES):
            print("Facilities: %s - Customers %s" %
                  (reassignmentCandidates.getTreesCount(),
                   reassignmentCandidates.getTotalNodes()))
            print("Destroy Method Finished...")
            print("=============================")

        return reassignmentCandidates

    def __repair(self, candidatesFacility, candidatesCustomer):
        if (self.DEBUG_MESSAGES):
            print("=============================")
            print("Repair Method Started...")
        self.mip.clear()
        self.mip.initialize(
            candidatesFacility, candidatesCustomer, "Instance_%s_%s" %
            (len(candidatesFacility), len(candidatesCustomer)),
            self.params["mipSolver"])
        obj, assignments, status = self.mip.optimize(
            self.params["mipTimeLimit"])

        if (self.DEBUG_MESSAGES):
            print("Repair Method Finished...")
            print("=============================")

        return obj, assignments, status

    def __evaluate(self, newObj, assignments, candidateForest, cluster):
        if (self.DEBUG_MESSAGES):
            print("=============================")
            print("Evaluate Method Started...")
            print(
                "Current Partial Objective: %s || Candidate Partial Objective %s"
                % (newObj, candidateForest.getTotalCost()))

        if (newObj - candidateForest.getTotalCost() <= self.EPS):

            newSolution = Forest()
            newSolution.buildForestFromArray(
                self.subproblemSolutionForest.getAssignmentsArray(),
                self.facilities, self.customers)
            partialSolution = Forest()
            partialSolution.buildForestFromDict(
                Util.getDictSolutionFromMIP(assignments), self.facilities,
                self.customers)
            currentForestObj = self.subproblemSolutionForest.getTotalCost()

            newFacilities = set()
            previousFacilities = set()

            for tree in candidateForest.getTrees().values():
                newSolution.removeTree(tree.getRoot().index)
                previousFacilities.add(tree.getRoot().index)

            for tree in partialSolution.getTrees().values():
                newSolution.addTree(Tree(tree.getRoot()))
                newFacilities.add(tree.getRoot().index)
                for node in tree.getNodes().values():
                    newSolution.getTrees().get(
                        tree.getRoot().index).addNode(node)

            #Facilities that were in the solution, but was not even selected as candidates
            clusterIntersection = set([
                tree.getRoot().index
                for tree in self.subproblemSolutionForest.getTrees().values()
            ]).intersection(cluster)
            notInterestingFacilities = clusterIntersection.difference(
                previousFacilities)

            for facilityIndex in notInterestingFacilities:
                newSolution.removeTree(facilityIndex)

            newSolution.updateStatistics()

            previousCandidates = list(
                previousFacilities.difference(
                    newFacilities.intersection(previousFacilities)))

            if (len(previousCandidates) == 0):
                previousCandidates = list(newFacilities)

            previousCandidates.extend(list(notInterestingFacilities))

            print("Current Objective: %s || Candidate Objective: %s" %
                  (currentForestObj, newSolution.getTotalCost()))

            if (self.params["improvementType"] == ImprovementType.Best):
                if (Util.truncate(
                        Util.truncate(newSolution.getTotalCost(), 10) -
                        Util.truncate(
                            self.subproblemSolutionForest.getTotalCost(), 10),
                        10) <= self.EPS):
                    if (self.DEBUG_MESSAGES):
                        print("NEW SOLUTION FOUND!")
                    self.subproblemSolutionForest.buildForestFromArray(
                        newSolution.getAssignmentsArray(), self.facilities,
                        self.customers)

                    newForestObj = self.subproblemSolutionForest.getTotalCost()

                    self.__updateFrequency(list(newFacilities),
                                           self.ASSIGNMENT_REWARD)

                    previousCandidates = list(
                        previousFacilities.difference(
                            newFacilities.intersection(previousFacilities)))

                    if (len(previousCandidates) == 0):
                        previousCandidates = list(newFacilities)

                    previousCandidates.extend(list(notInterestingFacilities))

                    reward = Util.truncate(
                        float(len(newFacilities)) /
                        float(len(previousCandidates)), 3)

                    self.__updateFrequency(previousCandidates, reward)

                    if (self.DEBUG_MESSAGES):
                        print("Previous Objective: %s || New Objective: %s" %
                              (currentForestObj, newForestObj))
                        print("Partial Solution")
                        partial = ""
                        partial = '%.2f' % self.subproblemSolutionForest.getTotalCost(
                        ) + ' ' + str(0) + '\n'
                        partial += ' '.join(
                            map(
                                str,
                                self.subproblemSolutionForest.
                                getAssignmentsArray()))
                        print(partial)
                else:
                    candidates = [
                        tree.getRoot().index
                        for tree in candidateForest.getTrees().values()
                    ]
                    #reward = (Util.truncate(float(candidateForest.getTreesCount()/self.facilitiesCount),3))*self.ASSIGNMENT_REWARD
                    reward = self.NO_ASSIGNMENT_REWARD
                    self.__updateFrequency(candidates, reward)

            elif self.params["improvementType"] == ImprovementType.First:
                self.subproblemSolutionForest.buildForestFromArray(
                    newSolution.getAssignmentsArray(), self.facilities,
                    self.customers)

                newForestObj = self.subproblemSolutionForest.getTotalCost()

                self.__updateFrequency(list(newFacilities),
                                       self.ASSIGNMENT_REWARD)

                previousCandidates = list(
                    previousFacilities.difference(
                        newFacilities.intersection(previousFacilities)))

                if (len(previousCandidates) == 0):
                    previousCandidates = list(newFacilities)

                previousCandidates.extend(list(notInterestingFacilities))

                reward = Util.truncate(
                    float(len(newFacilities)) / float(len(previousCandidates)),
                    3)

                self.__updateFrequency(previousCandidates, reward)

                if (self.DEBUG_MESSAGES):
                    print("Previous Objective: %s || New Objective: %s" %
                          (currentForestObj, newForestObj))
                    print("Partial Solution")
                    partial = ""
                    partial = '%.2f' % self.subproblemSolutionForest.getTotalCost(
                    ) + ' ' + str(0) + '\n'
                    partial += ' '.join(
                        map(
                            str,
                            self.subproblemSolutionForest.getAssignmentsArray(
                            )))
                    print(partial)

                candidates = [
                    tree.getRoot().index
                    for tree in candidateForest.getTrees().values()
                ]
                #reward = (Util.truncate(float(candidateForest.getTreesCount()/self.facilitiesCount),3))*self.ASSIGNMENT_REWARD
                reward = self.NO_ASSIGNMENT_REWARD
                self.__updateFrequency(candidates, reward)

        if (self.DEBUG_MESSAGES):
            print("Evaluate Method Finished...")
            print("=============================")

    def optimize(self):
        start = time.time()
        clock = Clock()
        clock.setStart(start)
        if (self.DEBUG_MESSAGES):
            print("=============================")
            print("LNS Optimize Method Started...")

        customerSubset = copy.deepcopy(self.customers)
        facilitySubet = copy.deepcopy(self.facilities)
        self.__InitializeProblem(facilitySubet, customerSubset)
        self.__getQuantiles()
        self.currentObjectiveFunction = self.subproblemSolutionForest.getTotalCost(
        )
        self.currentSolutionAssignment = self.subproblemSolutionForest.getAssignmentsArray(
        )
        initialQuantiles = copy.deepcopy(self.quantiles)
        quantileSize = len(initialQuantiles)
        quantilesCount = 0
        customerCount = len(self.customers)
        noImprovementIterations = 0
        while True:
            if (clock.isTimeOver(time.time(),
                                 self.params["executionTimeLimit"])):
                break

            iterationsCount = len(self.clusterAreas)
            for iteration in range(0, iterationsCount):
                self.currentIteration = iteration
                clustersCount = 0
                clustersSize = len(self.clusterAreas.get(iteration))
                for cluster in self.clusterAreas.get(iteration).values():
                    clustersCount = clustersCount + 1
                    print("Iteration: %s/%s || Instance: %s_%s" %
                          (quantilesCount + 1, quantileSize,
                           self.facilitiesCount, customerCount))
                    print("Subproblem: %s/%s" %
                          (self.currentIteration + 1, iterationsCount))
                    candidateForest = self.__destroy(cluster)
                    if (candidateForest.getTreesCount() *
                            candidateForest.getTotalNodes() >
                            self.MAX_PROBLEM_SIZE):
                        print(
                            "Problem instance is larger than limit. Skipping..."
                        )
                        candidateFacilities = [
                            tree.getRoot()
                            for tree in candidateForest.getTrees().values()
                        ]
                        self.__updateFrequency(
                            dict([(facility.index, facility)
                                  for facility in candidateFacilities]),
                            self.NO_ASSIGNMENT_REWARD)
                        continue

                    cFacilities, cCustomers = candidateForest.getData()

                    print(
                        "Current Cluster: %s/%s || Facilities: %s || Customers Assigned: %s"
                        % (clustersCount, clustersSize,
                           candidateForest.getTreesCount(),
                           candidateForest.getTotalNodes()))
                    if (candidateForest.getTotalNodes() == 0):
                        if (self.DEBUG_MESSAGES):
                            print("No Customers Assigned... Continue")
                        continue

                    obj, assignment, status = self.__repair(
                        cFacilities, cCustomers)

                    if (status == 'optimal'):
                        self.__evaluate(obj, assignment, candidateForest,
                                        cluster)
                    else:
                        print("No Optimal Solution Found for this instance")
                        candidateFacilities = [
                            tree.getRoot()
                            for tree in candidateForest.getTrees().values()
                        ]
                        self.__updateFrequency(
                            dict([(facility.index, facility)
                                  for facility in candidateFacilities]),
                            self.ASSIGNMENT_REWARD)

                    print("Subproblem Forest: %s/%s" %
                          (self.subproblemSolutionForest.getTreesCount(),
                           self.subproblemSolutionForest.getTotalNodes()))
                    print("Subproblem Objective Funciton: %s" %
                          self.subproblemSolutionForest.getTotalCost())
                    print("Current Objective Function: %s" %
                          self.currentObjectiveFunction)

                if (self.DEBUG_MESSAGES):
                    print("Partial Solution")
                    partial = ""
                    partial = '%.2f' % self.subproblemSolutionForest.getTotalCost(
                    ) + ' ' + str(0) + '\n'
                    partial += ' '.join(
                        map(
                            str,
                            self.subproblemSolutionForest.getAssignmentsArray(
                            )))
                    print(partial)

            if (self.currentObjectiveFunction >=
                    self.subproblemSolutionForest.getTotalCost()):
                self.currentObjectiveFunction = self.subproblemSolutionForest.getTotalCost(
                )
                self.currentSolutionAssignment = self.subproblemSolutionForest.getAssignmentsArray(
                )
            else:
                noImprovementIterations = noImprovementIterations + 1

            print("====================================================")
            print("CURRENT OBJECTIVE FUNCTION: %s" %
                  self.currentObjectiveFunction)
            print("====================================================")
            if (quantilesCount >= quantileSize):
                print("Maximum Iteration Count Reached! Stopping...")
                break

            if (noImprovementIterations >
                    self.params["noImprovementIterationLimit"]):
                print("No improvement limit reached! Stopping the search...")
                break

            ##filtrar as facilities mais interessantes e jogar no facility subset
            candidates = [
                facility.index for facility in facilitySubet.values()
            ]
            lastCandidateCount = len(candidates)
            while quantilesCount < quantileSize and len(
                    candidates) == lastCandidateCount:
                candidates = self.__getCandidateFacilities(
                    candidates, self.totalDemand,
                    Util.truncate(initialQuantiles[quantilesCount], 5), False)
                quantilesCount = quantilesCount + 1

            if (candidates is None or len(candidates) == 0
                    or len(candidates) == lastCandidateCount):
                break

            facilitySubet = dict(
                zip([index for index in candidates],
                    [facilitySubet[index] for index in candidates]))
            self.facilities = facilitySubet
            self.__InitializeProblem(facilitySubet, customerSubset)
            self.__getQuantiles()

        return self.currentObjectiveFunction, self.currentSolutionAssignment