Exemplo n.º 1
0
    def find_best_region_idx(self, area, partition, candidate_regions_idx,
                             attr):
        """

        Parameters
        ----------
        area :
            The area to be moved to one of the regions specified by
            `candidate_regions_idx`.
        partition : `list`
            Each element (of type `set`) represents a region.
        candidate_regions_idx : iterable
            Each element is the index of a region in the `partition` list.
        attr : :class:`numpy.ndarray`
            See the corresponding argument in
            :meth:`fit_from_scipy_sparse_matrix`.

        Returns
        -------
        best_idx : int
            The index of a region (w.r.t. `partition`) which has the smallest
            sum of dissimilarities after area `area` is moved to the region.
        """
        dissim_per_idx = {
            region_idx: sum(
                self.metric(attr[area].reshape(1, -1), attr[area2].reshape(
                    1, -1)) for area2 in partition[region_idx])
            for region_idx in candidate_regions_idx
        }
        minimum_dissim = min(dissim_per_idx.values())
        best_idxs = [
            idx for idx in dissim_per_idx
            if dissim_per_idx[idx] == minimum_dissim
        ]
        return random_element_from(best_idxs)
Exemplo n.º 2
0
    def find_best_area(self, region, candidates, attr):
        """
        Parameters
        ----------
        region : iterable
            Each element represents an area.
        candidates : iterable
            Each element represents an area bordering on region.
        attr : :class:`numpy.ndarray`
            See the corresponding argument in
            :meth:`fit_from_scipy_sparse_matrix`.

        Returns
        -------
        best_area :
            An element of `candidates` with minimal dissimilarity when being
            moved to the region `region`.
        """
        candidates = {
            area: sum(
                self.metric(attr[area].reshape(1, -1), attr[area2].reshape(
                    1, -1)) for area2 in region)
            for area in candidates
        }
        best_candidates = [
            area for area in candidates
            if candidates[area] == min(candidates.values())
        ]
        return random_element_from(best_candidates)
Exemplo n.º 3
0
def test_random_element_from():
    lst = list(range(1000))
    n_pops = 5
    popped = []
    for _ in range(n_pops):
        lst_copy = list(lst)
        popped.append(util.random_element_from(lst_copy))
        assert len(lst_copy) == len(lst)
    assert not_all_elements_equal(popped)
Exemplo n.º 4
0
def test_random_element_from():
    lst = list(range(1000))
    n_pops = 5
    popped = []
    for _ in range(n_pops):
        lst_copy = list(lst)
        popped.append(util.random_element_from(lst_copy))
        assert len(lst_copy) == len(lst)
    assert not_all_elements_equal(popped)
Exemplo n.º 5
0
    def _azp_connected_component(self, adj, initial_clustering, attr):
        """
        Implementation of the basic tabu version of the AZP algorithm (refer
        to [OR1995]_) for a spatially connected set of areas (i.e. for every
        area there is a path to every other area).

        Parameters
        ----------
        adj : :class:`scipy.sparse.csr_matrix`
            Refer to the corresponding argument in
            :meth:`AZP._azp_connected_component`.
        initial_clustering : :class:`numpy.ndarray`
            Refer to the corresponding argument in
            :meth:`AZP._azp_connected_component`.
        attr : :class:`numpy.ndarray`
            Refer to the corresponding argument in
            :meth:`AZP._azp_connected_component`.

        Returns
        -------
        labels : :class:`numpy.ndarray`
            Refer to the return value in :meth:`AZP._azp_connected_component`.
        """
        self.reset_tabu()
        # if there is only one region in the initial solution, just return it.
        distinct_regions = list(np.unique(initial_clustering))
        if len(distinct_regions) == 1:
            return initial_clustering

        #  step 2: make a list of the M regions
        labels = initial_clustering

        visited = []
        stop = False
        while True:
            # added termination condition (not in Openshaw & Rao (1995))
            label_tup = tuple(labels)
            if visited.count(label_tup) >= self.reps_before_termination:
                stop = True

            visited.append(label_tup)

            # step 1 Find the global best move that is not prohibited or tabu.
            # find possible moves (globally)
            best_move = None
            best_objval_diff = float("inf")
            for area in range(labels.shape[0]):
                old_region = labels[area]
                sub_adj = sub_adj_matrix(
                    adj, np.where(labels == old_region)[0], wo_nodes=area)
                # moving the area must not destroy spatial contiguity in donor
                # region and if area is alone in its region, it must stay:
                if is_connected(sub_adj) and count(labels, old_region) > 1:
                    for neigh in neighbors(adj, area):
                        new_region = labels[neigh]
                        if new_region != old_region:
                            possible_move = Move(area, old_region, new_region)
                            if possible_move not in self.tabu:
                                objval_diff = self.objective_func.update(
                                    possible_move.area,
                                    possible_move.new_region, labels, attr)
                                if objval_diff < best_objval_diff:
                                    best_move = possible_move
                                    best_objval_diff = objval_diff
            # step 2: Make this move if it is an improvement or equivalet in
            # value.
            if best_move is not None and best_objval_diff <= 0:
                self._make_move(best_move.area, best_move.new_region, labels)
            else:
                # step 3: if no improving move can be made, then see if a tabu
                # move can be made which improves on the current local best
                # (termed an aspiration move)
                improving_tabus = [
                    move for move in self.tabu
                    if labels[move.area] == move.old_region
                    and self.objective_func.update(move.area, move.new_region,
                                                   labels, attr) < 0
                ]
                if improving_tabus:
                    aspiration_move = random_element_from(improving_tabus)
                    self._make_move(aspiration_move.area,
                                    aspiration_move.new_region, labels)
                else:
                    # step 4: If there is no improving move and no aspirational
                    # move, then make the best move even if it is nonimproving
                    # (that is, results in a worse value of the objective
                    # function).
                    if stop:
                        break
                    if best_move is not None:
                        self._make_move(best_move.area, best_move.new_region,
                                        labels)
        return labels