def _build_dispersion_graph(self, old_replica2part2dev=None):
        """
        Build a dict of all tiers in the cluster to a list of the number of
        parts with a replica count at each index.  The values of the dict will
        be lists of length the maximum whole replica + 1 so that the
        graph[tier][3] is the number of parts with in the tier with 3 replicas
        and graph [tier][0] is the number of parts not assigned in this tier.

        i.e.
        {
            <tier>: [
                <number_of_parts_with_0_replicas>,
                <number_of_parts_with_1_replicas>,
                ...
                <number_of_parts_with_n_replicas>,
                ],
            ...
        }

        :param old_replica2part2dev: if called from rebalance, the
            old_replica2part2dev can be used to count moved moved parts.

        :returns: number of parts with different assignments than
            old_replica2part2dev if provided
        """

        # Since we're going to loop over every replica of every part we'll
        # also count up changed_parts if old_replica2part2dev is passed in
        old_replica2part2dev = old_replica2part2dev or []
        # Compare the partition allocation before and after the rebalance
        # Only changed device ids are taken into account; devices might be
        # "touched" during the rebalance, but actually not really moved
        changed_parts = 0

        int_replicas = int(math.ceil(self.replicas))
        max_allowed_replicas = self._build_max_replicas_by_tier()
        parts_at_risk = 0

        dispersion_graph = {}
        # go over all the devices holding each replica part by part
        for part_id, dev_ids in enumerate(
                six.moves.zip(*self._replica2part2dev)):
            # count the number of replicas of this part for each tier of each
            # device, some devices may have overlapping tiers!
            replicas_at_tier = defaultdict(int)
            for rep_id, dev in enumerate(iter(
                    self.devs[dev_id] for dev_id in dev_ids)):
                for tier in (dev.get('tiers') or tiers_for_dev(dev)):
                    replicas_at_tier[tier] += 1
                # IndexErrors will be raised if the replicas are increased or
                # decreased, and that actually means the partition has changed
                try:
                    old_device = old_replica2part2dev[rep_id][part_id]
                except __HOLE__:
                    changed_parts += 1
                    continue

                if old_device != dev['id']:
                    changed_parts += 1
            part_at_risk = False
            # update running totals for each tiers' number of parts with a
            # given replica count
            for tier, replicas in replicas_at_tier.items():
                if tier not in dispersion_graph:
                    dispersion_graph[tier] = [self.parts] + [0] * int_replicas
                dispersion_graph[tier][0] -= 1
                dispersion_graph[tier][replicas] += 1
                if replicas > max_allowed_replicas[tier]:
                    part_at_risk = True
            # this part may be at risk in multiple tiers, but we only count it
            # as at_risk once
            if part_at_risk:
                parts_at_risk += 1
        self._dispersion_graph = dispersion_graph
        self.dispersion = 100.0 * parts_at_risk / self.parts
        return changed_parts
dataset/ETHPy150Open openstack/swift/swift/common/ring/builder.py/RingBuilder._build_dispersion_graph