def test_grow_exogenous_regions_queen(self): """Number of regions is exogenous, aka given (Wqueen)""" am = AreaManager(self.Wqueen, self.Y) for regions in xrange(1, max_num_regions): rm = RegionMaker(am, pRegions=regions) self.assertEqual(regions, len(rm.region2Area)) self.assertTrue(am.checkFeasibility(rm.returnRegions()))
def test_azp_gives_at_least_same_obj_func(self): instance = self.map_instance aream = AreaManager(instance.Wrook, instance.Y) rm = RegionMaker(aream, into_regions) ob_before = rm.objInfo rm.AZPImproving() ob_after = rm.objInfo self.assertTrue(ob_before >= ob_after)
def test_false_possible_queen_region_movement(self): """ An infeasible region removal yields an uncontiguos region. Using WQueen. """ am = AreaManager(self.layer.Wqueen, self.layer.Y) rm = RegionMaker(am, initialSolution = self.possible_queen_solution) """ Removing area 47 from region 1 leaves area 58 as an island""" feasible = rm.checkFeasibility(1, 47, rm.region2Area) assert not feasible
def test_true_possible_queen_region_movement(self): """ A region removal yields in fact a feasible solution using queen contiguity. """ am = AreaManager(self.layer.Wqueen, self.layer.Y) rm = RegionMaker(am, initialSolution = self.possible_queen_solution) """ Removing area 49 from region 0 leaves areas attached by a point""" feasible = rm.checkFeasibility(0, 49, rm.region2Area) assert feasible
def test_false_possible_rook_region_movement(self): """ Removing an area that breaks the contiguity constraint. """ am = AreaManager(self.layer.Wrook, self.layer.Y) rm = RegionMaker(am, initialSolution = self.possible_rook_solution) """ Removing area 47 from region 1 leaves area 48 as an island""" feasible = rm.checkFeasibility(1, 47, rm.region2Area) assert not feasible
def test_true_possible_rook_region_movement(self): """ A possible region change is in fact a feasible movement using rook contiguity. """ am = AreaManager(self.layer.Wrook, self.layer.Y) rm = RegionMaker(am, initialSolution = self.possible_rook_solution) """ Removing area 29 from region 1 is an allowed movement""" feasible = rm.checkFeasibility(1, 29, rm.region2Area) assert feasible
def test_azprtabu_gives_at_least_same_obj_func(self): instance = self.map_instance aream = AreaManager(instance.Wrook, instance.Y) rm = RegionMaker(aream, into_regions) convTabu = len(instance.Y) / into_regions ob_before = rm.objInfo rm.reactiveTabuMove(convTabu) ob_after = rm.objInfo self.assertTrue(ob_before >= ob_after)
def test_azpsa_gives_at_least_same_obj_func(self): instance = self.map_instance aream = AreaManager(instance.Wrook, instance.Y) rm = RegionMaker(aream, into_regions) alpha = 0.85 maxiterations = 3 ob_before = rm.objInfo rm.AZPSA(alpha, maxiterations) ob_after = rm.objInfo self.assertTrue(ob_before >= ob_after)
def test_azptabu_gives_at_least_same_obj_func(self): instance = self.map_instance aream = AreaManager(instance.Wrook, instance.Y) rm = RegionMaker(aream, into_regions) convTabu = max(10, len(instance.Y) / into_regions) tabuLength = 10 ob_before = rm.objInfo rm.AZPTabuMove(tabuLength=tabuLength, convTabu=convTabu) ob_after = rm.objInfo self.assertTrue(ob_before >= ob_after)
def test_impossible_solution_configuration_on_rook(self): """ Using the Wrook, test for a impossible region assignment. """ """ Possible solution: 0011 0101 """ possible_solution = [0, 0, 1, 1, 0, 1, 0, 1] am = AreaManager(self.Wrook, self.Y) result = am.checkFeasibility(possible_solution) self.assertFalse(result)
def test_possible_solution_configuration_on_rook(self): """ Using the Wrook, test for a possible region assignment """ """ Possible solution: 0112 0002 """ possible_solution = [0, 1, 1, 2, 0, 0, 0, 2] am = AreaManager(self.Wrook, self.Y) result = am.checkFeasibility(possible_solution) self.assertTrue(result)
def test_impossible_solution_configuration_on_queen(self): """ Using the Wqueen, test for an impossible region assignment """ """ Impossible solution: 0110 1010 """ possible_solution = [0, 1, 1, 0, 1, 0, 1, 0] am = AreaManager(self.Wqueen, self.Y) result = am.checkFeasibility(possible_solution) self.assertFalse(result)
def test_randomness_on_same_process(self): """This tests that running multiple instances of the region maker on the same script (process) will """ am = AreaManager(self.Wrook, self.Y) regions = [] for _reg in xrange(1, max_num_regions): rm = RegionMaker(am, pRegions=max_num_regions) self.assertEqual(max_num_regions, len(rm.region2Area)) self.assertTrue(am.checkFeasibility(rm.returnRegions())) regions.append(rm.returnRegions()) for regioni in xrange(len(regions) - 1): for regionj in xrange(regioni + 1, len(regions)): self.assertNotEqual(regions[regioni], regions[regionj])
def test_possible_solution_configuration_on_rook_w_one_region(self): """ Using the Wrook, test for a possible region assignment where only one region exists. """ """ Possible solution: 0000 0000 """ possible_solution = [0, 0, 0, 0, 0, 0, 0, 0] am = AreaManager(self.Wrook, self.Y) result = am.checkFeasibility(possible_solution) self.assertTrue(result)
def test_possible_solution_configuration_on_rook_w_empty_region(self): """ Using the Wrook, test for a impossible region assignment where one area is missing. """ """ Possible solution: 0022 0202 """ possible_solution = [0, 0, 2, 2, 0, 2, 0, 2] am = AreaManager(self.Wrook, self.Y) result = am.checkFeasibility(possible_solution) self.assertFalse(result)
def execArisel(y, w, pRegions, inits=3, initialSolution=[], convTabu=0, tabuLength=10): """Automatic Rationalization with Initial Seed Location ARiSeL, proposed by [Duque_Church2004]_ , aggregates N areas into P spatially contiguous regions while minimizing intra-regional heterogeneity (measured as the within-cluster sum of squares from each area to the attribute centroid of its cluster). This algorithm is a modification of Openshaw's AZP-tabu [Openshaw_Rao1995]_. In ARISeL the construction of a initial feasible solution is repeated several times (inits) before running Tabu Search algorithm [Glover1977]_. Duque and Church argue that: - constructing and initial feasible solution is computationally less expensive than performing local search. - local search by moving bordering areas between region do not allow an extensive search in the solution space and it is computationally expensive. Based on those two ideas, the authors propose to generate as many different initial feasible solutions and run Tabu search on the best initial solution obtained so far. The initial solution follows a "growing regions" strategy. It starts with a initial set of seeds (as many seed as regions) selected using the K-means++ algorithm. From those seeds, other neighbouring areas are assigned to its closest (in attribute space) growing region. This strategy has proven better results. :: Layer.cluster('arisel', vars, regions, <wType>, <std>, <inits>, <initialSolution>, <convTabu>, <tabuLength>, <dissolve>, <dataOperations>) :keyword vars: Area attribute(s) (e.g. ['SAR1','SAR2']) :type vars: list :keyword regions: Number of regions :type regions: integer :keyword wType: Type of first-order contiguity-based spatial matrix: 'rook' or 'queen'. Default value wType = 'rook'. :type wType: string :keyword std: If = 1, then the variables will be standardized. :type std: binary :keyword inits: number of initial feasible solutions to be constructed before applying Tabu Search. :type inits: integer. Default value inits = 5. :keyword initialSolution: List with a initial solution vector. It is useful when the user wants a solution that is not very different from a preexisting solution (e.g. municipalities,districts, etc.). Note that the number of regions will be the same as the number of regions in the initial feasible solution (regardless the value you assign to parameter "regions"). IMPORTANT: make sure you are entering a feasible solution and according to the W matrix you selected, otherwise the algorithm will not converge. :type initialSolution: list :keyword convTabu: Stop the search after convTabu nonimproving moves (nonimproving moves are those moves that do not improve the current solution. Note that "improving moves" are different to "aspirational moves"). If convTabu=0 the algorithm will stop after Int(M/N) nonimproving moves. Default value convTabu = 0. :type convTabu: integer :keyword tabuLength: Number of times a reverse move is prohibited. Default value *tabuLength = 10*. :type tabuLength: integer :keyword dissolve: If = 1, then you will get a "child" instance of the layer that contains the new regions. Default value *dissolve = 0*. **Note:**. Each child layer is saved in the attribute *layer.results*. The first algorithm that you run with *dissolve=1* will have a child layer in *layer.results[0]*; the second algorithm that you run with *dissolve=1* will be in *layer.results[1]*, and so on. You can export a child as a shapefile with *layer.result[<1,2,3..>].exportArcData('filename')* :type dissolve: binary :keyword dataOperations: Dictionary which maps a variable to a list of operations to run on it. The dissolved layer will contains in it's data all the variables specified in this dictionary. Be sure to check the input layer's fieldNames before use this utility. :type dataOperations: dictionary The dictionary structure must be as showed bellow. >>> X = {} >>> X[variableName1] = [function1, function2,....] >>> X[variableName2] = [function1, function2,....] Where functions are strings wich represents the name of the functions to be used on the given variableName. Functions could be,'sum','mean','min','max','meanDesv','stdDesv','med', 'mode','range','first','last','numberOfAreas. By deffault just ID variable is added to the dissolved map. """ lenY = len(y) start = 0.0 time2 = 0.0 print("Running original Arisel algorithm") print("Number of areas: ", lenY) if initialSolution: print("Number of regions: ", len(np.unique(initialSolution))) pRegions = len(set(initialSolution)) else: print("Number of regions: ", pRegions) if pRegions >= lenY: message = "\n WARNING: You are aggregating "+str(lenY)+" into"+\ str(pRegions)+" regions!!. The number of regions must be an integer"+\ " number lower than the number of areas being aggregated" raise Exception(message) if convTabu <= 0: convTabu = old_div(lenY, pRegions) # convTabu = 230*numpy.sqrt(pRegions) distanceType = "EuclideanSquared" distanceStat = "Centroid" objectiveFunctionType = "SS" selectionType = "Minimum" am = AreaManager(w, y, distanceType) extendedMemory = ExtendedMemory() pool = Pool(processes=cpu_count()) procs = [] start = tm.time() for dummy in range(inits): ans = pool.apply_async(constructPossible, [ am, pRegions, initialSolution, distanceType, distanceStat, selectionType, objectiveFunctionType ]) procs.append(ans) results = [] for p in procs: results.append(p.get()) tmp_ans = extendedMemory for rm in results: if rm.objInfo < tmp_ans.objInfo: tmp_ans = rm rm = tmp_ans extendedMemory.updateExtendedMemory(rm) rm.recoverFromExtendedMemory(extendedMemory) print("INITIAL SOLUTION: ", rm.returnRegions(), "\nINITIAL OF: ", rm.objInfo) rm.tabuMove(tabuLength=tabuLength, convTabu=convTabu) time2 = tm.time() - start Sol = rm.regions Of = rm.objInfo print("FINAL SOLUTION: ", Sol, "\nFINAL OF: ", Of) output = { "objectiveFunction": Of, "runningTime": time2, "algorithm": "arisel", "regions": len(Sol), "r2a": Sol, "distanceType": distanceType, "distanceStat": distanceStat, "selectionType": selectionType, "ObjectiveFuncionType": objectiveFunctionType } return output
def test_grow_exogenous_regions_with_initial_solution(self): """Number of regions is exogenous, aka given, and an initial solution""" am = AreaManager(self.Wqueen, self.Y) rm = RegionMaker(am) self.assertIsNotNone(rm)
def _final_regions_are_contiguous_in_instance(instance): exp_name = instance.fieldNames[-1] clustering_results = instance.outputCluster[exp_name] final_region_assignment = clustering_results['r2a'] am = AreaManager(instance.Wrook, instance.Y) return am.checkFeasibility(final_region_assignment)
def test_grow_endogenous_range_regions(self): """Number of regions is endogenous with a range value""" am = AreaManager(self.Wqueen, self.Y) rm = RegionMaker(am) self.assertIsNotNone(rm)