def _diversify_soln(core_soln_column, neighbordict,z, floor_variable,lockSoln):
        #Initialize a local swap space to store n best diversified soln - these do not need to be better 
        div_soln_space = np.empty(sharedSoln.shape)
        div_soln_space[:] = np.inf
        div_variance = np.array([sharedVar[:,core_soln_column]] * sharedVar.shape[1]).T
        workingSoln = np.copy(sharedSoln[0:,core_soln_column])            
        workingVar = np.copy(sharedVar[:,core_soln_column])

        #Iterate through the regions and check all moves, store the 4 best.
        for region in np.unique(workingSoln[1:]):
            members = np.where(workingSoln == region)[0]
            neighbors = []
            for member in members:
                candidates = neighbordict[member-1]#neighbordict is 0 based, member is 1 based
                candidates = [candidate for candidate in candidates if candidate not in members]
                candidates = [candidate for candidate in candidates if candidate not in neighbors]
                neighbors.extend(candidates)
            candidates = []
            
            #Iterate through the neighbors
            for neighbor in neighbors:
                neighborSoln = np.copy(workingSoln[1:]) #Pull a copy of the local working version
                old_membership = neighborSoln[neighbor]#Track where we started to check_floor
                
                neighborSoln[neighbor] = region #Move the neighbor into the new region in the copy
                
                #Check the floor
                floor_check = True 
                if not np.sum(floor_variable[neighborSoln == old_membership]) >= floor:
                    continue
                    if not np.sum(floor_variable[neighborSoln == region]) >= floor:
                        continue                

                #Check contiguity of the swap
                block = np.where(workingSoln[1:] == neighbor)[0].tolist()
                if not check_contiguity(neighbordict, block, neighbor):
                    continue                
                
                #Compute the local variance
                varcopy = np.copy(workingVar)
                varcopy[old_membership] = np.var(z[neighborSoln == old_membership])
                varcopy[region] = np.var(z[neighborSoln == region])
                varcopy[0] = np.sum(varcopy[1:])           
                
                if np.any(div_soln_space[0] == np.inf):
                    column = np.where(np.isinf(div_soln_space[0]) == True)[0][0]
                    div_soln_space[1:,column] = neighborSoln[:]
                    div_soln_space[0:,column][0] = varcopy[0]
                    div_variance[:,column] = varcopy
                else:
                    column = np.argmax(div_soln_space[0])
                    div_soln_space[1:,column] = neighborSoln[:]
                    div_soln_space[0:,column][0] = varcopy[0]
                    div_variance[:,column] = varcopy
        #Write one of the neighbor perturbations to the shared memory space to work on.
        valid = np.where(div_soln_space[0] != np.inf)[0]
        #print sharedVar[:,core_soln_column]
        try:
            selection = randint(0,len(valid)-1)
            with lockSoln:
                sharedSoln[:,core_soln_column] = div_soln_space[:,selection]
            with lockVar:
                sharedVar[:,core_soln_column] = div_variance[:,selection]
                #print sharedVar[:,core_soln_column]
        except:
            pass
            #print div_soln_space[0]
            print "Attempt to diversify failed."
Beispiel #2
0
 def swap(self):
     swapping = True
     swap_iteration = 0
     if self.verbose:
         print 'Initial solution, objective function: ', self.objective_function()
     total_moves = 0
     self.k = len(self.regions)
     changed_regions = [1] * self.k
     nr = range(self.k)
     while swapping:
         moves_made = 0
         regionIds = [r for r in nr if changed_regions[r]]
         np.random.permutation(regionIds)
         changed_regions = [0] * self.k
         swap_iteration += 1
         for seed in regionIds:
             local_swapping = True
             local_attempts = 0
             while local_swapping:
                 local_moves = 0
                 # get neighbors
                 members = self.regions[seed]
                 neighbors = []
                 for member in members:
                     candidates = self.w.neighbors[member]
                     candidates = [candidate for candidate in candidates if candidate not in members]
                     candidates = [candidate for candidate in candidates if candidate not in neighbors]
                     neighbors.extend(candidates)
                 candidates = []
                 for neighbor in neighbors:
                     block = copy.copy(self.regions[self.area2region[
                         neighbor]])
                     if check_contiguity(self.w, block, neighbor):
                         block.remove(neighbor)
                         fv = self.check_floor(block)
                         if fv:
                             candidates.append(neighbor)
                 # find the best local move
                 if not candidates:
                     local_swapping = False
                 else:
                     nc = len(candidates)
                     moves = np.zeros([nc, 1], float)
                     best = None
                     cv = 0.0
                     for area in candidates:
                         current_internal = self.regions[seed]
                         current_outter = self.regions[self.area2region[
                             area]]
                         current = self.objective_function([current_internal, current_outter])
                         new_internal = copy.copy(current_internal)
                         new_outter = copy.copy(current_outter)
                         new_internal.append(area)
                         new_outter.remove(area)
                         new = self.objective_function([new_internal,
                                                        new_outter])
                         change = new - current
                         if change < cv:
                             best = area
                             cv = change
                     if best:
                         # make the move
                         area = best
                         old_region = self.area2region[area]
                         self.regions[old_region].remove(area)
                         self.area2region[area] = seed
                         self.regions[seed].append(area)
                         moves_made += 1
                         changed_regions[seed] = 1
                         changed_regions[old_region] = 1
                     else:
                         # no move improves the solution
                         local_swapping = False
                 local_attempts += 1
                 if self.verbose:
                     print 'swap_iteration: ', swap_iteration, 'moves_made: ', moves_made
                     print 'number of regions: ', len(self.regions)
                     print 'number of changed regions: ', sum(
                         changed_regions)
                     print 'internal region: ', seed, 'local_attempts: ', local_attempts
                     print 'objective function: ', self.objective_function()
                     print 'smallest region size: ',min([len(region) for region in self.regions])
         total_moves += moves_made
         if moves_made == 0:
             swapping = False
             self.swap_iterations = swap_iteration
             self.total_moves = total_moves
         if self.verbose:
             print 'moves_made: ', moves_made
             print 'objective function: ', self.objective_function()
def tabu_search(core, z, neighbordict,numP,w,floor, floor_variable,lockSoln, lockflag,lockVar, maxfailures=50,maxiterations=15):
        
    ##Pseudo constants
    pid = mp.current_process()._identity[0]
    tabu_list = deque(maxlen=sharedupdate[2][core])#What is this core's tabu list length? 
    maxiterations *= cores #Test synchronize cores to exit at the same time.
        
    maxfailures += int(maxfailures*uniform(-1.1, 1.2))#James et. al 2007
    
    def _tabu_check(tabu_list, neighbor, region, old_membership):
        if tabu_list:#If we have a deque with contents
            for tabu_region in(tabu_list):
                if neighbor == tabu_region[0]:
                    #print neighbor, tabu_region[0]
                    if region == tabu_region[1] and old_membership == tabu_region[2]:
                        return False    
    

    def _diversify_soln(core_soln_column, neighbordict,z, floor_variable,lockSoln):
        #Initialize a local swap space to store n best diversified soln - these do not need to be better 
        div_soln_space = np.empty(sharedSoln.shape)
        div_soln_space[:] = np.inf
        div_variance = np.array([sharedVar[:,core_soln_column]] * sharedVar.shape[1]).T
        workingSoln = np.copy(sharedSoln[0:,core_soln_column])            
        workingVar = np.copy(sharedVar[:,core_soln_column])

        #Iterate through the regions and check all moves, store the 4 best.
        for region in np.unique(workingSoln[1:]):
            members = np.where(workingSoln == region)[0]
            neighbors = []
            for member in members:
                candidates = neighbordict[member-1]#neighbordict is 0 based, member is 1 based
                candidates = [candidate for candidate in candidates if candidate not in members]
                candidates = [candidate for candidate in candidates if candidate not in neighbors]
                neighbors.extend(candidates)
            candidates = []
            
            #Iterate through the neighbors
            for neighbor in neighbors:
                neighborSoln = np.copy(workingSoln[1:]) #Pull a copy of the local working version
                old_membership = neighborSoln[neighbor]#Track where we started to check_floor
                
                neighborSoln[neighbor] = region #Move the neighbor into the new region in the copy
                
                #Check the floor
                floor_check = True 
                if not np.sum(floor_variable[neighborSoln == old_membership]) >= floor:
                    continue
                    if not np.sum(floor_variable[neighborSoln == region]) >= floor:
                        continue                

                #Check contiguity of the swap
                block = np.where(workingSoln[1:] == neighbor)[0].tolist()
                if not check_contiguity(neighbordict, block, neighbor):
                    continue                
                
                #Compute the local variance
                varcopy = np.copy(workingVar)
                varcopy[old_membership] = np.var(z[neighborSoln == old_membership])
                varcopy[region] = np.var(z[neighborSoln == region])
                varcopy[0] = np.sum(varcopy[1:])           
                
                if np.any(div_soln_space[0] == np.inf):
                    column = np.where(np.isinf(div_soln_space[0]) == True)[0][0]
                    div_soln_space[1:,column] = neighborSoln[:]
                    div_soln_space[0:,column][0] = varcopy[0]
                    div_variance[:,column] = varcopy
                else:
                    column = np.argmax(div_soln_space[0])
                    div_soln_space[1:,column] = neighborSoln[:]
                    div_soln_space[0:,column][0] = varcopy[0]
                    div_variance[:,column] = varcopy
        #Write one of the neighbor perturbations to the shared memory space to work on.
        valid = np.where(div_soln_space[0] != np.inf)[0]
        #print sharedVar[:,core_soln_column]
        try:
            selection = randint(0,len(valid)-1)
            with lockSoln:
                sharedSoln[:,core_soln_column] = div_soln_space[:,selection]
            with lockVar:
                sharedVar[:,core_soln_column] = div_variance[:,selection]
                #print sharedVar[:,core_soln_column]
        except:
            pass
            #print div_soln_space[0]
            print "Attempt to diversify failed."
 
    ##This shows that we are operating asynchronously.   
    #if core ==2:
        #time.sleep(5)
    
    while sum(sharedupdate[1]) < maxiterations:
        core_soln_column = (core + sharedupdate[1][core])%len(sharedupdate[1])
        
        if sharedupdate[0][core_soln_column] == False:
            _diversify_soln(core_soln_column, neighbordict,z, floor_variable,lockSoln) #Li, et. al (in press - P-Compact_Regions)

        #print "ProcessID %i is processing soln column %i in iteration %i."%(pid, core_soln_column,sharedupdate[1][core]) #Uncomment to see that cores move around the search space    
        failures = 0 #The total local failure counter
        local_best_variance = sharedSoln[:,core_soln_column][0]
        workingSoln = np.copy(sharedSoln[:,core_soln_column])    
        workingVar = np.copy(sharedVar[:,core_soln_column])
        
        while failures <= maxfailures:
            #Select a random starting point in the search space.
            nr = np.unique(workingSoln[1:]) #This is 0 based, ie. region 0 - region 31
            regionIDs = nr
            changed_regions = np.ones(len(nr))
            randstate = RandomState(pid) #To 'unsync' the cores we need to instantiate a random class with a unique seed.
            randstate.shuffle(regionIDs) #shuffle the regions so we start with a random region
            changed_regions[:] = 0
            swap_flag = False #Flag to stop looping prior to max iterations if we are not improving.
            
            #Iterate through the regions, checking potential swaps
            for region in regionIDs:
                members = np.where(workingSoln == region)[0] #get the members of the region
                #Get the neighbors to the members.  Grab only those that could change.
                neighbors = []
                for member in members:
                    candidates = neighbordict[member-1]#neighbordict is 0 based, member is 1 based
                    candidates = [candidate for candidate in candidates if candidate not in members]
                    candidates = [candidate for candidate in candidates if candidate not in neighbors]
                    neighbors.extend(candidates)
                candidates = []
                                
                #Iterate through the neighbors
                for neighbor in neighbors:
                    neighborSoln = np.copy(workingSoln[1:]) #Pull a copy of the local working version
                    old_membership = neighborSoln[neighbor]#Track where we started to check_floor
                    #For whatever reason candidates block is adding other units in the region, ie. we test moving from region 1 to region 1...
                    '''ToDO: Check the candidates code above, something is wrong with it...testing more than necessary'''
                    if old_membership == region:
                        continue
                    
                    #Check the tabu list
                    tabu_move_check = _tabu_check(tabu_list, neighbor, region, old_membership)
                    if tabu_move_check is not None:
                        continue
                    
                    neighborSoln[neighbor] = region #Move the neighbor into the new region in the copy

                    #Check the floor
                    floor_check = True 
                    if not np.sum(floor_variable[neighborSoln == old_membership]) >= floor:
                        continue
                        if not np.sum(floor_variable[neighborSoln == region]) >= floor:
                            continue

                    #Compute the local variance
                    varcopy = np.copy(workingVar)
                    varcopy[old_membership] = np.var(z[neighborSoln== old_membership])
                    varcopy[region] = np.var(z[neighborSoln == region])
                    varcopy[0] = np.sum(varcopy[1:])
                    
                    #Check the locally compute varaince against current working best.
                    if varcopy[0] >= workingSoln[0]:
                        continue
                    
                    #Check contiguity of the swap
                    block = np.where(workingSoln[1:] == neighbor)[0].tolist()
                    if not check_contiguity(neighbordict, block, neighbor):
                        #print "Failed contiguity check"
                        continue
                    
                    #After this we have passed all tests, a check of all possible swaps has yielded a better one.
                    swap_flag = True
                    workingSoln[0] = varcopy[0]
                    workingSoln[1:] = neighborSoln[:]
                    workingVar = varcopy
                    tabu_list.appendleft((neighbor,old_membership,region))
                    
                if swap_flag == False:
                    failures += 1
        
        with lockSoln and lockflag:
            sharedupdate[0][core_soln_column] = 0
            sharedSoln[:,core_soln_column]
            if workingSoln[0] < sharedSoln[:,core_soln_column][0]:
                sharedSoln[:,core_soln_column] = workingSoln[:]
                sharedupdate[0][core_soln_column] = 1 #Set the update flag to true
                if not workingSoln[0] < sharedSoln[0].any():
                    results = set_half_to_best(len(sharedSoln[0]))
                    with lockVar:
                        sharedVar[:,core_soln_column] = workingVar
                        for result in results:
                            sharedVar[:,result] = workingVar
        #Increment the core iteration counter    
        sharedupdate[1][core] += 1
Beispiel #4
0
 def swap(self):
     swapping = True
     swap_iteration = 0
     if self.verbose:
         print 'Initial solution, objective function: ', self.objective_function(
         )
     total_moves = 0
     self.k = len(self.regions)
     changed_regions = [1] * self.k
     nr = range(self.k)
     while swapping:
         moves_made = 0
         regionIds = [r for r in nr if changed_regions[r]]
         np.random.permutation(regionIds)
         changed_regions = [0] * self.k
         swap_iteration += 1
         for seed in regionIds:
             local_swapping = True
             local_attempts = 0
             while local_swapping:
                 local_moves = 0
                 # get neighbors
                 members = self.regions[seed]
                 neighbors = []
                 for member in members:
                     candidates = self.w.neighbors[member]
                     candidates = [
                         candidate for candidate in candidates
                         if candidate not in members
                     ]
                     candidates = [
                         candidate for candidate in candidates
                         if candidate not in neighbors
                     ]
                     neighbors.extend(candidates)
                 candidates = []
                 for neighbor in neighbors:
                     block = copy.copy(
                         self.regions[self.area2region[neighbor]])
                     if check_contiguity(self.w, block, neighbor):
                         block.remove(neighbor)
                         fv = self.check_floor(block)
                         if fv:
                             candidates.append(neighbor)
                 # find the best local move
                 if not candidates:
                     local_swapping = False
                 else:
                     nc = len(candidates)
                     moves = np.zeros([nc, 1], float)
                     best = None
                     cv = 0.0
                     for area in candidates:
                         current_internal = self.regions[seed]
                         current_outter = self.regions[
                             self.area2region[area]]
                         current = self.objective_function(
                             [current_internal, current_outter])
                         new_internal = copy.copy(current_internal)
                         new_outter = copy.copy(current_outter)
                         new_internal.append(area)
                         new_outter.remove(area)
                         new = self.objective_function(
                             [new_internal, new_outter])
                         change = new - current
                         if change < cv:
                             best = area
                             cv = change
                     if best:
                         # make the move
                         area = best
                         old_region = self.area2region[area]
                         self.regions[old_region].remove(area)
                         self.area2region[area] = seed
                         self.regions[seed].append(area)
                         moves_made += 1
                         changed_regions[seed] = 1
                         changed_regions[old_region] = 1
                     else:
                         # no move improves the solution
                         local_swapping = False
                 local_attempts += 1
                 if self.verbose:
                     print 'swap_iteration: ', swap_iteration, 'moves_made: ', moves_made
                     print 'number of regions: ', len(self.regions)
                     print 'number of changed regions: ', sum(
                         changed_regions)
                     print 'internal region: ', seed, 'local_attempts: ', local_attempts
                     print 'objective function: ', self.objective_function()
                     print 'smallest region size: ', min(
                         [len(region) for region in self.regions])
         total_moves += moves_made
         if moves_made == 0:
             swapping = False
             self.swap_iterations = swap_iteration
             self.total_moves = total_moves
         if self.verbose:
             print 'moves_made: ', moves_made
             print 'objective function: ', self.objective_function()
    def swapfunc(self, seed, shared_list, shared_lock, smoves):
        #shared_list[seed].append(self.regions[seed])
        #        file1 = open("/home/vsindhu1/runtimeswap.txt","a")
        psttm = time.time()
        members = shared_list[seed]
        m_made = 0
        neighbors = []
        for member in members:
            candidates = self.w.neighbors[member]
            candidates = [
                candidate for candidate in candidates
                if candidate not in members
            ]
            candidates = [
                candidate for candidate in candidates
                if candidate not in neighbors
            ]
            neighbors.extend(candidates)
        candidates = []
        try:
            for neighbor in neighbors:
                block = copy.copy(shared_list[self.area2region[neighbor]])
                if check_contiguity(self.w, block, neighbor):
                    block.remove(neighbor)
                    fv = self.check_floor(block)
                    if fv:
                        candidates.append(neighbor)
        except:
            pass
        # find the best local move
        if candidates:
            nc = len(candidates)
            moves = np.zeros([nc, 1], float)
            best = None
            cv = 0.0
            try:
                for area in candidates:
                    current_internal = shared_list[seed]
                    current_outter = shared_list[self.area2region[area]]
                    current = self.objective_function(
                        [current_internal, current_outter])
                    new_internal = copy.copy(current_internal)
                    new_outter = copy.copy(current_outter)
                    new_internal.append(area)
                    new_outter.remove(area)
                    new = self.objective_function([new_internal, new_outter])
                    change = new - current
                    if change < cv:
                        best = area
                        cv = change
            except:
                pass
            if best:
                # make the move
                lcksttm = time.time()
                old_region = self.area2region[best]
                lock1 = shared_lock[old_region]
                lock2 = shared_lock[seed]
                if old_region < seed:
                    lock1.acquire()
                    lock2.acquire()
                else:
                    lock2.acquire()
                    lock1.acquire()
#                lck.acquire()
#print("Started")
#print("Best: ", best)
                area = best
                #                old_region = self.area2region[area]
                #                shared_lock[old_region].acquire()
                #print("Area: ", area)
                #print("Old Region: ", old_region)
                #print("A Regions before swap: ", shared_list)
                try:
                    ls = copy.copy(shared_list[self.area2region[neighbor]])
                    fv1 = self.check_floor(ls)
                    if fv1:
                        so_list = shared_list[old_region]
                        so_list.remove(area)
                        shared_list[old_region] = so_list
                        self.area2region[area] = seed
                        ss_list = shared_list[seed]
                        ss_list.append(area)
                        shared_list[seed] = ss_list
                        m_made += 1
                        smoves.value += 1
                except:
                    pass
                #print("A Regions after swap: ", shared_list)
                #changed_regions[seed] = 1
                #changed_regions[old_region] = 1
                #print("Ended")
#                lck.release()
#                shared_lock[old_region].release()
#                shared_lock[seed].release()
#                shared_lock[old_region].acquire()
                if old_region < seed:
                    lock2.release()
                    lock1.release()
                else:
                    lock1.release()
                    lock2.release()


#                file1.write("Total Lock Time: %s\n" % str(seed))
#                file1.write("%s \n" % (time.time() - lcksttm))
#                print("Total Lock Time: ",seed,": ", time.time() - lcksttm)
#        print("Total Process Time: ",seed,": ", time.time() - psttm)
#        print("Number of areas: ",seed,": ",len(shared_list[seed]))
#        file1.write("Total Process Time: %s\n" % str(seed))
#        file1.write("%s \n" % (time.time() - psttm))
#        file1.close()
        return m_made
Beispiel #6
0
    def _diversify_soln(core_soln_column, neighbordict,z, floor_variable,lockSoln):
        '''
        The goal of this function is to diversify a soln that is not improving.
        What about the possability of diversifying a good answer away from 'the soln?'
        I do no think that that should be an issue - we make a randomized greedy swap.  Is one enough?
        
        Rationale: This is a randomized Greedy swap (GRASP), where we store the best n permutations and then randomly select the one we will use.  Originally in Li et. al (in press).  We need to test different values of n to see what the impact is.
        '''
        #print "Diversifying: ", sharedSoln[0]

        #Initialize a local swap space to store n best diversified soln - these do not need to be better 
        div_soln_space = np.ndarray(sharedSoln.shape)
        div_soln_space[:] = float("inf")
        workingcopy = np.copy(sharedSoln[0:,core_soln_column])            

        #Iterate through the regions and check all moves, store the 4 best.
        for region in np.unique(workingcopy[1:]):
            members = np.where(workingcopy == region)[0]
            neighbors = []
            for member in members:
                candidates = neighbordict[member-1]#neighbordict is 0 based, member is 1 based
                candidates = [candidate for candidate in candidates if candidate not in members]
                candidates = [candidate for candidate in candidates if candidate not in neighbors]
                neighbors.extend(candidates)
            candidates = []
            
            #Iterate through the neighbors
            for neighbor in neighbors:
                neighborcopy = np.copy(workingcopy[:]) #Pull a copy of the local working version
                old_membership = neighborcopy[neighbor]#Track where we started to check_floor
                
                neighborcopy[neighbor] = region #Move the neighbor into the new region in the copy
                
                #Here we start to check the swap and see if it is better
                swap_var = objective_function_vec(neighborcopy[1:],z)#Variance of the new swap
                if not swap_var < div_soln_space[0].any():
                    block = np.where(workingcopy[1:] == neighbor)[0]#A list of the members in a region.
                    block=block.tolist() #For current contiguity check
                    if check_contiguity(neighbordict, block, neighbor):#Check contiguity
                        if check_floor(np.where(neighborcopy[1:,]==region)[0], floor_variable, w) and check_floor(np.where(neighborcopy[1:,]==old_membership)[0],floor_variable,w):
                            
                            neighborcopy[0] = swap_var
                            if not np.isinf(div_soln_space[0].any()):
                                div_soln_space[:,np.argmax(div_soln_space[0])] = neighborcopy[:]
                            else:
                                div_soln_space[:,np.argmin(div_soln_space[0])] = neighborcopy[:]
                        else:
                            del neighborcopy
                            #print "Swap failed due to floor_check."
                    else:
                        del neighborcopy
                        #print "Swap failed due to contiguity."
        #It is possible that the perturbation will not generate enough soln to fill the space, 
        # so we need to ignore those columns with variance = infinity.
        
        #Write one of the neighbor perturbations to the shared memory space to work on.
        valid = np.where(div_soln_space[0] != np.inf)[0]
        try:
            selection = randint(0,len(valid)-1)
            with lockSoln:
                sharedSoln[:,core_soln_column] = div_soln_space[:,selection]
                print "Diversified to:", sharedSoln[0]
        except:
            print div_soln_space[0]
            print "Attempt to diversify failed."
Beispiel #7
0
def tabu_search(core, z, neighbordict,numP,w,floor_variable,lockSoln, lockflag, maxfailures=15,maxiterations=10):
    ##Pseudo constants
    pid = mp.current_process()._identity[0]
    tabu_list = deque(maxlen=sharedupdate[2][core])#What is this core's tabu list length? 
    maxiterations *= cores #Test synchronize cores to exit at the same time.
        
    maxfailures += int(maxfailures*uniform(-1.1, 1.2))#James et. al 2007
    
    def _tabu_check(tabu_list, neighbor, region, old_membership):
        if tabu_list:#If we have a deque with contents
            for tabu_region in(tabu_list):
                if neighbor == tabu_region[0]:
                    #print neighbor, tabu_region[0]
                    if region == tabu_region[1] and old_membership == tabu_region[2]:
                        return False    
    

    def _diversify_soln(core_soln_column, neighbordict,z, floor_variable,lockSoln):
        '''
        The goal of this function is to diversify a soln that is not improving.
        What about the possability of diversifying a good answer away from 'the soln?'
        I do no think that that should be an issue - we make a randomized greedy swap.  Is one enough?
        
        Rationale: This is a randomized Greedy swap (GRASP), where we store the best n permutations and then randomly select the one we will use.  Originally in Li et. al (in press).  We need to test different values of n to see what the impact is.
        '''
        #print "Diversifying: ", sharedSoln[0]

        #Initialize a local swap space to store n best diversified soln - these do not need to be better 
        div_soln_space = np.ndarray(sharedSoln.shape)
        div_soln_space[:] = float("inf")
        workingcopy = np.copy(sharedSoln[0:,core_soln_column])            

        #Iterate through the regions and check all moves, store the 4 best.
        for region in np.unique(workingcopy[1:]):
            members = np.where(workingcopy == region)[0]
            neighbors = []
            for member in members:
                candidates = neighbordict[member-1]#neighbordict is 0 based, member is 1 based
                candidates = [candidate for candidate in candidates if candidate not in members]
                candidates = [candidate for candidate in candidates if candidate not in neighbors]
                neighbors.extend(candidates)
            candidates = []
            
            #Iterate through the neighbors
            for neighbor in neighbors:
                neighborcopy = np.copy(workingcopy[:]) #Pull a copy of the local working version
                old_membership = neighborcopy[neighbor]#Track where we started to check_floor
                
                neighborcopy[neighbor] = region #Move the neighbor into the new region in the copy
                
                #Here we start to check the swap and see if it is better
                swap_var = objective_function_vec(neighborcopy[1:],z)#Variance of the new swap
                if not swap_var < div_soln_space[0].any():
                    block = np.where(workingcopy[1:] == neighbor)[0]#A list of the members in a region.
                    block=block.tolist() #For current contiguity check
                    if check_contiguity(neighbordict, block, neighbor):#Check contiguity
                        if check_floor(np.where(neighborcopy[1:,]==region)[0], floor_variable, w) and check_floor(np.where(neighborcopy[1:,]==old_membership)[0],floor_variable,w):
                            
                            neighborcopy[0] = swap_var
                            if not np.isinf(div_soln_space[0].any()):
                                div_soln_space[:,np.argmax(div_soln_space[0])] = neighborcopy[:]
                            else:
                                div_soln_space[:,np.argmin(div_soln_space[0])] = neighborcopy[:]
                        else:
                            del neighborcopy
                            #print "Swap failed due to floor_check."
                    else:
                        del neighborcopy
                        #print "Swap failed due to contiguity."
        #It is possible that the perturbation will not generate enough soln to fill the space, 
        # so we need to ignore those columns with variance = infinity.
        
        #Write one of the neighbor perturbations to the shared memory space to work on.
        valid = np.where(div_soln_space[0] != np.inf)[0]
        try:
            selection = randint(0,len(valid)-1)
            with lockSoln:
                sharedSoln[:,core_soln_column] = div_soln_space[:,selection]
                print "Diversified to:", sharedSoln[0]
        except:
            print div_soln_space[0]
            print "Attempt to diversify failed."
            

        
    ##This shows that we are operating asynchronously.   
    #if core ==2:
        #time.sleep(5)
    
    while sum(sharedupdate[1]) < maxiterations:
        core_soln_column = (core + sharedupdate[1][core])%len(sharedupdate[1]) #This iterates the cores around the search space.
        
        #Check for diversification here and diversify if necessary...
        if sharedupdate[0][core_soln_column] == False:
            _diversify_soln(core_soln_column, neighbordict,z, floor_variable,lockSoln) #Li, et. al (in press - P-Compact_Regions)

        #print "ProcessID %i is processing soln column %i in iteration %i."%(pid, core_soln_column,sharedupdate[1][core]) #Uncomment to see that cores move around the search space    
        failures = 0 #The total iteration counter
        #What are the current best solutions local to this core?
        local_best_variance = sharedSoln[:,core_soln_column][0]
        workingSoln = np.copy(sharedSoln[:,core_soln_column])    
        
        while failures <= maxfailures:#How many total iterations can the core make
      
            #Select a random starting point in the search space.
            nr = np.unique(workingSoln[1:]) #This is 0 based, ie. region 0 - region 31
            regionIDs = nr
            changed_regions = np.ones(len(nr))
            randstate = RandomState(pid) #To 'unsync' the cores we need to instantiate a random class with a unique seed.
            randstate.shuffle(regionIDs) #shuffle the regions so we start with a random region
            changed_regions[:] = 0
            swap_flag = False #Flag to stop looping prior to max iterations if we are not improving.
            
            #Iterate through the regions, checking potential swaps
            for region in regionIDs:
                members = np.where(workingSoln == region)[0] #get the members of the region
                #print region, members
                #Get the neighbors to the members.  Grab only those that could change.
                neighbors = []
                for member in members:
                    candidates = neighbordict[member-1]#neighbordict is 0 based, member is 1 based
                    candidates = [candidate for candidate in candidates if candidate not in members]
                    candidates = [candidate for candidate in candidates if candidate not in neighbors]
                    neighbors.extend(candidates)
                candidates = []
                
                #Iterate through the neighbors
                for neighbor in neighbors:
                    neighborSoln = np.copy(workingSoln[:]) #Pull a copy of the local working version
                    old_membership = neighborSoln[neighbor]#Track where we started to check_floor
                    
                    tabu_move_check =_tabu_check(tabu_list, neighbor, region, old_membership)
                    if tabu_move_check is not None:
                        break
                    
                    neighborSoln[neighbor] = region #Move the neighbor into the new region in the copy
                    
                    #Here we start to check the swap and see if it is better
                    swap_var = objective_function_vec(neighborSoln[1:],z)#Variance of the new swap
                    if swap_var <= local_best_variance:
                        block = np.where(workingSoln[1:] == neighbor)[0]#A list of the members in a region.
                        block=block.tolist() #For current contiguity check
                        if check_contiguity(neighbordict, block, neighbor):#Check contiguity
                            if check_floor(np.where(neighborSoln[1:,]==region)[0], floor_variable, w) and check_floor(np.where(neighborSoln[1:,]==old_membership)[0],floor_variable,w):#What about the floor of the region loosing the member in the original code?
                                #print "Swap made on core %i.  Objective function improved from %f to %f." %(pid, swap_var, local_best_variance)
                                local_best_variance = swap_var#Set the new local best to the swap. We have made a swap that betters the objective function.
                                neighborSoln[0] = swap_var
                                workingSoln[:] = neighborSoln[:]
                                swap_flag = True #We made a swap
                                tabu_list.appendleft((neighbor,old_membership,region))#tuple(polygon_id, oldgroup,newgroup)
                            else:
                                del neighborSoln
                                #print "Swap failed due to floor_check."
                        else:
                            del neighborSoln
                            #print "Swap failed due to contiguity."
                    
            if swap_flag == False:
                #print "Failed to make any swap, incrementing the fail counter."
                failures += 1
            
        #print workingSoln, len(np.unique(workingSoln[1:]))    
        
        with lockflag:
            sharedupdate[0][core_soln_column] = 0 #Set the update flag to false
            #print "Locking update flag to set to false"
        
        with lockSoln:#The lock is released at the end of the with statement
            sharedSoln[:,core_soln_column]#Lock the column of the shared soln we are using.
            if workingSoln[0] < sharedSoln[:,core_soln_column][0]:
                sharedSoln[:,core_soln_column]
                sharedSoln[:,core_soln_column] = workingSoln
                #print "Better soln loaded into sharedSoln: %f." %(workingSoln[0])
                sharedupdate[0][core_soln_column] = 1 #Set the update flag to true
                if not workingSoln[0] < sharedSoln[0].any():
                    set_half_to_best(len(sharedSoln[0]))
                    #print "Setting half the soln to new global best. ", sharedSoln[0]
        #Increment the core iteration counter    
        sharedupdate[1][core] += 1