'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 within 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 parts.
:returns: number of parts with different assignments than
old_replica2part2dev if provided'

def _build_dispersion_graph(self, old_replica2part2dev=None):

    old_replica2part2dev = (old_replica2part2dev or [])
    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 = {}
    for (part_id, dev_ids) in enumerate(six.moves.zip(*self._replica2part2dev)):
        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
            try:
                old_device = old_replica2part2dev[rep_id][part_id]
            except IndexError:
                changed_parts += 1
                continue
            if (old_device != dev['id']):
                changed_parts += 1
        part_at_risk = False
        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
        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
