# Buggy code
def calc_operand_assignments(self, op_keys, input_chunk_metas=None):
    """
    Decide target worker for given chunks.

    :param op_keys: keys of operands to assign
    :param input_chunk_metas: chunk metas for graph-level inputs, grouped by initial chunks
    :type input_chunk_metas: dict[str, dict[str, mars.scheduler.chunkmeta.WorkerMeta]]
    :return: dict mapping operand keys into worker endpoints
    """
    graph = self._graph
    op_states = self._op_states
    cur_assigns = self._fixed_assigns.copy()

    key_to_chunks = defaultdict(list)
    for n in graph:
        key_to_chunks[n.op.key].append(n)

    descendant_readies = set()
    op_keys = set(op_keys)
    chunks_to_assign = [key_to_chunks[k][0] for k in op_keys]

    if any(graph.count_predecessors(c) for c in chunks_to_assign):
        graph = graph.copy()
        for c in graph:
            if c.op.key not in op_keys:
                continue
            for pred in graph.predecessors(c):
                graph.remove_edge(pred, c)

    assigned_counts = defaultdict(lambda: 0)
    worker_op_keys = defaultdict(set)
    if cur_assigns:
        for op_key, state in op_states.items():
            if (
                op_key not in op_keys
                and state == OperandState.READY
                and op_key in cur_assigns
            ):
                descendant_readies.add(op_key)
                assigned_counts[cur_assigns[op_key]] += 1

    # calculate the number of nodes to be assigned to every worker
    # given number of workers and existing assignments
    pre_worker_quotas = self._calc_worker_assign_limits(
        len(chunks_to_assign) + len(descendant_readies), assigned_counts
    )

    # pre-assign nodes given pre-determined transfer sizes
    if not input_chunk_metas:
        worker_quotas = pre_worker_quotas
    else:
        for op_key, worker in self._iter_assignments_by_transfer_sizes(
            pre_worker_quotas, input_chunk_metas
        ):
            if op_key in cur_assigns:
                continue
            assigned_counts[worker] += 1
            cur_assigns[op_key] = worker
            worker_op_keys[worker].add(op_key)

        worker_quotas = self._calc_worker_assign_limits(
            len(chunks_to_assign) + len(descendant_readies), assigned_counts
        )

    if cur_assigns:
        # calculate ranges of nodes already assigned
        for op_key, worker in self._iter_successor_assigns(cur_assigns):
            cur_assigns[op_key] = worker
            worker_op_keys[worker].add(op_key)

    logger.debug("Worker assign quotas: %r", worker_quotas)

    # calculate expected descendant count (spread range) of
    # every worker and subtract assigned number from it
    average_spread_range = len(graph) * 1.0 / len(self._worker_slots)
    spread_ranges = defaultdict(lambda: average_spread_range)
    for worker in cur_assigns.values():
        spread_ranges[worker] -= 1

    logger.debug("Scan spread ranges: %r", dict(spread_ranges))

    # assign pass 1: assign from fixed groups
    sorted_workers = sorted(
        worker_op_keys, reverse=True, key=lambda k: len(worker_op_keys[k])
    )
    for worker in sorted_workers:
        start_chunks = reduce(
            operator.add, (key_to_chunks[op_key] for op_key in worker_op_keys[worker])
        )
        self._assign_by_bfs(
            start_chunks,
            worker,
            worker_quotas,
            spread_ranges,
            op_keys,
            cur_assigns,
            graph=graph,
        )

    # assign pass 2: assign from other nodes to be assigned
    sorted_candidates = [v for v in chunks_to_assign]
    while max(worker_quotas.values()):
        worker = max(worker_quotas, key=lambda k: worker_quotas[k])
        cur = sorted_candidates.pop()
        while cur.op.key in cur_assigns:
            cur = sorted_candidates.pop()
        self._assign_by_bfs(
            cur, worker, worker_quotas, spread_ranges, op_keys, cur_assigns, graph=graph
        )

    return dict((n.op.key, cur_assigns[n.op.key]) for n in chunks_to_assign)


# Traceback (ValueError)
500 GET /worker?endpoint
Traceback (most recent call last):
File "/opt/conda/lib/python3.6/site-packages/tornado/web.py", line 1592, in _execute
result = yield result
File "/opt/conda/lib/python3.6/site-packages/tornado/gen.py", line 1133, in run
value = future.result()
File "/opt/conda/lib/python3.6/site-packages/tornado/gen.py", line 1147, in run
yielded = self.gen.send(value)
File "/opt/conda/lib/python3.6/site-packages/bokeh/server/views/doc_handler.py", line 27, in get
template_variables=session.document.template_variables)
File "/opt/conda/lib/python3.6/site-packages/bokeh/embed/server.py", line 230, in server_html_page_for_session
template=template, template_variables=template_variables)
File "/opt/conda/lib/python3.6/site-packages/bokeh/embed/elements.py", line 133, in html_page_for_render_items
html = template.render(context)
File "/opt/conda/lib/python3.6/site-packages/jinja2/asyncsupport.py", line 76, in render
return original_render(self, *args, **kwargs)
File "/opt/conda/lib/python3.6/site-packages/jinja2/environment.py", line 1008, in render
return self.environment.handle_exception(exc_info, True)
File "/opt/conda/lib/python3.6/site-packages/jinja2/environment.py", line 780, in handle_exception
reraise(exc_type, exc_value, tb)
File "/opt/conda/lib/python3.6/site-packages/jinja2/_compat.py", line 37, in reraise
raise value.with_traceback(tb)
File "/home/admin/work/public-mars-0.2.0b1-cupid.zip/mars/web/templates/worker_detail.html", line 1, in top-level template code
{% extends "base.html" %}
File "/home/admin/work/public-mars-0.2.0b1-cupid.zip/mars/web/templates/base.html", line 65, in top-level template code
{% block body %}{% endblock %}
File "/home/admin/work/public-mars-0.2.0b1-cupid.zip/mars/web/templates/worker_detail.html", line 252, in block "body"
<td>{{ stats['min_est_finish_time'] | format_ts }}</td>
File "/home/admin/work/public-mars-0.2.0b1-cupid.zip/mars/web/server.py", line 49, in format_ts
return datetime.fromtimestamp(value).strftime('%Y-%m-%d %H:%M:%S')
ValueError: Invalid value NaN (not a number)
500 GET /worker?endpoint=10.101.216.148:53630 (10.101.202.142) 137.45ms

# Fixed code
def calc_operand_assignments(self, op_keys, input_chunk_metas=None):
    """
    Decide target worker for given chunks.

    :param op_keys: keys of operands to assign
    :param input_chunk_metas: chunk metas for graph-level inputs, grouped by initial chunks
    :type input_chunk_metas: dict[str, dict[str, mars.scheduler.chunkmeta.WorkerMeta]]
    :return: dict mapping operand keys into worker endpoints
    """
    graph = self._graph
    op_states = self._op_states
    cur_assigns = OrderedDict(self._fixed_assigns)

    key_to_chunks = defaultdict(list)
    for n in graph:
        key_to_chunks[n.op.key].append(n)

    descendant_readies = set()
    op_keys = set(op_keys)
    chunks_to_assign = [key_to_chunks[k][0] for k in op_keys]

    if any(graph.count_predecessors(c) for c in chunks_to_assign):
        graph = graph.copy()
        for c in graph:
            if c.op.key not in op_keys:
                continue
            for pred in graph.predecessors(c):
                graph.remove_edge(pred, c)

    assigned_counts = defaultdict(lambda: 0)
    worker_op_keys = defaultdict(set)
    if cur_assigns:
        for op_key, state in op_states.items():
            if (
                op_key not in op_keys
                and state == OperandState.READY
                and op_key in cur_assigns
            ):
                descendant_readies.add(op_key)
                assigned_counts[cur_assigns[op_key]] += 1

    # calculate the number of nodes to be assigned to every worker
    # given number of workers and existing assignments
    pre_worker_quotas = self._calc_worker_assign_limits(
        len(chunks_to_assign) + len(descendant_readies), assigned_counts
    )

    # pre-assign nodes given pre-determined transfer sizes
    if not input_chunk_metas:
        worker_quotas = pre_worker_quotas
    else:
        for op_key, worker in self._iter_assignments_by_transfer_sizes(
            pre_worker_quotas, input_chunk_metas
        ):
            if op_key in cur_assigns:
                continue
            assigned_counts[worker] += 1
            cur_assigns[op_key] = worker
            worker_op_keys[worker].add(op_key)

        worker_quotas = self._calc_worker_assign_limits(
            len(chunks_to_assign) + len(descendant_readies), assigned_counts
        )

    if cur_assigns:
        # calculate ranges of nodes already assigned
        for op_key, worker in self._iter_successor_assigns(cur_assigns):
            cur_assigns[op_key] = worker
            worker_op_keys[worker].add(op_key)

    logger.debug("Worker assign quotas: %r", worker_quotas)

    # calculate expected descendant count (spread range) of
    # every worker and subtract assigned number from it
    average_spread_range = len(graph) * 1.0 / len(self._worker_slots)
    spread_ranges = defaultdict(lambda: average_spread_range)
    for worker in cur_assigns.values():
        spread_ranges[worker] -= 1

    logger.debug("Scan spread ranges: %r", dict(spread_ranges))

    # assign pass 1: assign from fixed groups
    sorted_workers = sorted(
        worker_op_keys, reverse=True, key=lambda k: len(worker_op_keys[k])
    )
    for worker in sorted_workers:
        start_chunks = reduce(
            operator.add, (key_to_chunks[op_key] for op_key in worker_op_keys[worker])
        )
        self._assign_by_bfs(
            start_chunks,
            worker,
            worker_quotas,
            spread_ranges,
            op_keys,
            cur_assigns,
            graph=graph,
        )

    # assign pass 2: assign from other nodes to be assigned
    sorted_candidates = [v for v in chunks_to_assign]
    while max(worker_quotas.values()):
        worker = max(worker_quotas, key=lambda k: worker_quotas[k])
        cur = sorted_candidates.pop()
        while cur.op.key in cur_assigns:
            cur = sorted_candidates.pop()
        self._assign_by_bfs(
            cur, worker, worker_quotas, spread_ranges, op_keys, cur_assigns, graph=graph
        )

    keys_to_assign = set(n.op.key for n in chunks_to_assign)
    return OrderedDict((k, v) for k, v in cur_assigns.items() if k in keys_to_assign)