# Buggy code
def execute(cls, ctx, op):
    def _base_concat(chunk, inputs):
        # auto generated concat when executing a DataFrame, Series or Index
        if chunk.op.output_types[0] == OutputType.dataframe:
            return _auto_concat_dataframe_chunks(chunk, inputs)
        elif chunk.op.output_types[0] == OutputType.series:
            return _auto_concat_series_chunks(chunk, inputs)
        elif chunk.op.output_types[0] == OutputType.index:
            return _auto_concat_index_chunks(chunk, inputs)
        elif chunk.op.output_types[0] == OutputType.categorical:
            return _auto_concat_categorical_chunks(chunk, inputs)
        else:  # pragma: no cover
            raise TypeError(
                "Only DataFrameChunk, SeriesChunk, IndexChunk, "
                "and CategoricalChunk can be automatically concatenated"
            )

    def _auto_concat_dataframe_chunks(chunk, inputs):
        xdf = (
            pd
            if isinstance(inputs[0], (pd.DataFrame, pd.Series)) or cudf is None
            else cudf
        )

        if chunk.op.axis is not None:
            return xdf.concat(inputs, axis=op.axis)

        # auto generated concat when executing a DataFrame
        if len(inputs) == 1:
            ret = inputs[0]
        else:
            max_rows = max(inp.index[0] for inp in chunk.inputs)
            min_rows = min(inp.index[0] for inp in chunk.inputs)
            n_rows = max_rows - min_rows + 1
            n_cols = int(len(inputs) // n_rows)
            assert n_rows * n_cols == len(inputs)

            concats = []
            for i in range(n_rows):
                if n_cols == 1:
                    concats.append(inputs[i])
                else:
                    concat = xdf.concat(
                        [inputs[i * n_cols + j] for j in range(n_cols)], axis=1
                    )
                    concats.append(concat)

            if xdf is pd:
                # The `sort=False` is to suppress a `FutureWarning` of pandas,
                # when the index or column of chunks to concatenate is not aligned,
                # which may happens for certain ops.
                #
                # See also Note [Columns of Left Join] in test_merge_execution.py.
                ret = xdf.concat(concats, sort=False)
            else:
                ret = xdf.concat(concats)
                # cuDF will lost index name when concat two seriess.
                ret.index.name = concats[0].index.name

        if getattr(chunk.index_value, "should_be_monotonic", False):
            ret.sort_index(inplace=True)
        if getattr(chunk.columns_value, "should_be_monotonic", False):
            ret.sort_index(axis=1, inplace=True)
        return ret

    def _auto_concat_series_chunks(chunk, inputs):
        # auto generated concat when executing a Series
        if all(np.isscalar(inp) for inp in inputs):
            return pd.Series(inputs)
        else:
            if len(inputs) == 1:
                concat = inputs[0]
            else:
                xdf = pd if isinstance(inputs[0], pd.Series) or cudf is None else cudf
                if chunk.op.axis is not None:
                    concat = xdf.concat(inputs, axis=chunk.op.axis)
                else:
                    concat = xdf.concat(inputs)
            if getattr(chunk.index_value, "should_be_monotonic", False):
                concat.sort_index(inplace=True)
            return concat

    def _auto_concat_index_chunks(chunk, inputs):
        if len(inputs) == 1:
            xdf = pd if isinstance(inputs[0], pd.Index) or cudf is None else cudf
            concat_df = xdf.DataFrame(index=inputs[0])
        else:
            xdf = pd if isinstance(inputs[0], pd.Index) or cudf is None else cudf
            empty_dfs = [xdf.DataFrame(index=inp) for inp in inputs]
            concat_df = xdf.concat(empty_dfs, axis=0)
        if getattr(chunk.index_value, "should_be_monotonic", False):
            concat_df.sort_index(inplace=True)
        return concat_df.index

    def _auto_concat_categorical_chunks(_, inputs):
        if len(inputs) == 1:  # pragma: no cover
            return inputs[0]
        else:
            # convert categorical into array
            arrays = [np.asarray(inp) for inp in inputs]
            array = np.concatenate(arrays)
            return pd.Categorical(
                array, categories=inputs[0].categories, ordered=inputs[0].ordered
            )

    chunk = op.outputs[0]
    inputs = [ctx[input.key] for input in op.inputs]

    if isinstance(inputs[0], tuple):
        ctx[chunk.key] = tuple(
            _base_concat(chunk, [input[i] for input in inputs])
            for i in range(len(inputs[0]))
        )
    else:
        ctx[chunk.key] = _base_concat(chunk, inputs)


# Traceback (TypeError)
In [1]: import mars.dataframe as md

In [2]: from datetime import datetime

In [3]: s = md.Series([datetime.now(), datetime.now(), datetime.now()], chunk_si
...: ze=2)

In [4]: s.max().execute()
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-4-8891e4a12063> in <module>
----> 1 s.max().execute()

~/Workspace/mars/mars/core.py in execute(self, session, **kw)
638
639         if wait:
--> 640             return run()
641         else:
642             thread_executor = ThreadPoolExecutor(1)

~/Workspace/mars/mars/core.py in run()
634
635         def run():
--> 636             self.data.execute(session, **kw)
637             return self
638

~/Workspace/mars/mars/core.py in execute(self, session, **kw)
374
375         if wait:
--> 376             return run()
377         else:
378             # leverage ThreadPoolExecutor to submit task,

~/Workspace/mars/mars/core.py in run()
369         def run():
370             # no more fetch, thus just fire run
--> 371             session.run(self, **kw)
372             # return Tileable or ExecutableTuple itself
373             return self

~/Workspace/mars/mars/session.py in run(self, *tileables, **kw)
498         tileables = tuple(mt.tensor(t) if not isinstance(t, (Entity, Base)) else t
499                           for t in tileables)
--> 500         result = self._sess.run(*tileables, **kw)
501
502         for t in tileables:

~/Workspace/mars/mars/session.py in run(self, *tileables, **kw)
106             # set number of running cores
107             self.context.set_ncores(kw['n_parallel'])
--> 108             res = self._executor.execute_tileables(tileables, **kw)
109             return res
110

~/Workspace/mars/mars/utils.py in _inner(*args, **kwargs)
437         def _inner(*args, **kwargs):
438             with self:
--> 439                 return func(*args, **kwargs)
440
441         return _inner

~/Workspace/mars/mars/executor.py in execute_tileables(self, tileables, fetch, n_parallel, n_thread, print_progress, mock, compose, name)
879                                    n_parallel=n_parallel or n_thread,
880                                    print_progress=print_progress, mock=mock,
--> 881                                    chunk_result=chunk_result)
882
883                 # update shape of tileable and its chunks whatever it's successful or not

~/Workspace/mars/mars/executor.py in execute_graph(self, graph, keys, n_parallel, print_progress, mock, no_intermediate, compose, retval, chunk_result)
691             print_progress=print_progress, mock=mock, mock_max_memory=self._mock_max_memory,
692             fetch_keys=fetch_keys, no_intermediate=no_intermediate)
--> 693         res = graph_execution.execute(retval)
694         self._mock_max_memory = max(self._mock_max_memory, graph_execution._mock_max_memory)
695         if mock:

~/Workspace/mars/mars/executor.py in execute(self, retval)
572         # wait until all the futures completed
573         for future in executed_futures:
--> 574             future.result()
575
576         if retval:

~/miniconda3/lib/python3.7/concurrent/futures/_base.py in result(self, timeout)
426                 raise CancelledError()
427             elif self._state == FINISHED:
--> 428                 return self.__get_result()
429
430             self._condition.wait(timeout)

~/miniconda3/lib/python3.7/concurrent/futures/_base.py in __get_result(self)
382     def __get_result(self):
383         if self._exception:
--> 384             raise self._exception
385         else:
386             return self._result

~/miniconda3/lib/python3.7/concurrent/futures/thread.py in run(self)
55
56         try:
---> 57             result = self.fn(*self.args, **self.kwargs)
58         except BaseException as exc:
59             self.future.set_exception(exc)

~/Workspace/mars/mars/utils.py in _inner(*args, **kwargs)
437         def _inner(*args, **kwargs):
438             with self:
--> 439                 return func(*args, **kwargs)
440
441         return _inner

~/Workspace/mars/mars/executor.py in _execute_operand(self, op)
444             # so we pass the first operand's first output to Executor.handle
445             first_op = ops[0]
--> 446             self.handle_op(first_op, results, self._mock)
447
448             # update maximal memory usage during execution

~/Workspace/mars/mars/executor.py in handle_op(self, *args, **kw)
376
377     def handle_op(self, *args, **kw):
--> 378         return Executor.handle(*args, **kw)
379
380     def _order_starts(self):

~/Workspace/mars/mars/executor.py in handle(cls, op, results, mock)
642                 # The `UFuncTypeError` was introduced by numpy#12593 since v1.17.0.
643                 try:
--> 644                     return runner(results, op)
645                 except UFuncTypeError as e:
646                     raise TypeError(str(e)).with_traceback(sys.exc_info()[2]) from None

~/Workspace/mars/mars/dataframe/merge/concat.py in execute(cls, ctx, op)
290                                    for i in range(len(inputs[0])))
291         else:
--> 292             ctx[chunk.key] = _base_concat(chunk, inputs)
293
294     @classmethod

~/Workspace/mars/mars/dataframe/merge/concat.py in _base_concat(chunk, inputs)
193                 return _auto_concat_dataframe_chunks(chunk, inputs)
194             elif chunk.op.output_types[0] == OutputType.series:
--> 195                 return _auto_concat_series_chunks(chunk, inputs)
196             elif chunk.op.output_types[0] == OutputType.index:
197                 return _auto_concat_index_chunks(chunk, inputs)

~/Workspace/mars/mars/dataframe/merge/concat.py in _auto_concat_series_chunks(chunk, inputs)
256                         concat = xdf.concat(inputs, axis=chunk.op.axis)
257                     else:
--> 258                         concat = xdf.concat(inputs)
259                 if getattr(chunk.index_value, 'should_be_monotonic', False):
260                     concat.sort_index(inplace=True)

~/miniconda3/lib/python3.7/site-packages/pandas/core/reshape/concat.py in concat(objs, axis, join, ignore_index, keys, levels, names, verify_integrity, sort, copy)
282         verify_integrity=verify_integrity,
283         copy=copy,
--> 284         sort=sort,
285     )
286

~/miniconda3/lib/python3.7/site-packages/pandas/core/reshape/concat.py in __init__(self, objs, axis, join, keys, levels, names, ignore_index, verify_integrity, copy, sort)
357                     "only Series and DataFrame objs are valid"
358                 )
--> 359                 raise TypeError(msg)
360
361             # consolidate

TypeError: cannot concatenate object of type '<class 'pandas._libs.tslibs.timestamps.Timestamp'>'; only Series and DataFrame objs are valid

# Fixed code
def execute(cls, ctx, op):
    def _base_concat(chunk, inputs):
        # auto generated concat when executing a DataFrame, Series or Index
        if chunk.op.output_types[0] == OutputType.dataframe:
            return _auto_concat_dataframe_chunks(chunk, inputs)
        elif chunk.op.output_types[0] == OutputType.series:
            return _auto_concat_series_chunks(chunk, inputs)
        elif chunk.op.output_types[0] == OutputType.index:
            return _auto_concat_index_chunks(chunk, inputs)
        elif chunk.op.output_types[0] == OutputType.categorical:
            return _auto_concat_categorical_chunks(chunk, inputs)
        else:  # pragma: no cover
            raise TypeError(
                "Only DataFrameChunk, SeriesChunk, IndexChunk, "
                "and CategoricalChunk can be automatically concatenated"
            )

    def _auto_concat_dataframe_chunks(chunk, inputs):
        xdf = (
            pd
            if isinstance(inputs[0], (pd.DataFrame, pd.Series)) or cudf is None
            else cudf
        )

        if chunk.op.axis is not None:
            return xdf.concat(inputs, axis=op.axis)

        # auto generated concat when executing a DataFrame
        if len(inputs) == 1:
            ret = inputs[0]
        else:
            max_rows = max(inp.index[0] for inp in chunk.inputs)
            min_rows = min(inp.index[0] for inp in chunk.inputs)
            n_rows = max_rows - min_rows + 1
            n_cols = int(len(inputs) // n_rows)
            assert n_rows * n_cols == len(inputs)

            concats = []
            for i in range(n_rows):
                if n_cols == 1:
                    concats.append(inputs[i])
                else:
                    concat = xdf.concat(
                        [inputs[i * n_cols + j] for j in range(n_cols)], axis=1
                    )
                    concats.append(concat)

            if xdf is pd:
                # The `sort=False` is to suppress a `FutureWarning` of pandas,
                # when the index or column of chunks to concatenate is not aligned,
                # which may happens for certain ops.
                #
                # See also Note [Columns of Left Join] in test_merge_execution.py.
                ret = xdf.concat(concats, sort=False)
            else:
                ret = xdf.concat(concats)
                # cuDF will lost index name when concat two seriess.
                ret.index.name = concats[0].index.name

        if getattr(chunk.index_value, "should_be_monotonic", False):
            ret.sort_index(inplace=True)
        if getattr(chunk.columns_value, "should_be_monotonic", False):
            ret.sort_index(axis=1, inplace=True)
        return ret

    def _auto_concat_series_chunks(chunk, inputs):
        # auto generated concat when executing a Series
        if len(inputs) == 1:
            concat = inputs[0]
        else:
            xdf = pd if isinstance(inputs[0], pd.Series) or cudf is None else cudf
            if chunk.op.axis is not None:
                concat = xdf.concat(inputs, axis=chunk.op.axis)
            else:
                concat = xdf.concat(inputs)
        if getattr(chunk.index_value, "should_be_monotonic", False):
            concat.sort_index(inplace=True)
        return concat

    def _auto_concat_index_chunks(chunk, inputs):
        if len(inputs) == 1:
            xdf = pd if isinstance(inputs[0], pd.Index) or cudf is None else cudf
            concat_df = xdf.DataFrame(index=inputs[0])
        else:
            xdf = pd if isinstance(inputs[0], pd.Index) or cudf is None else cudf
            empty_dfs = [xdf.DataFrame(index=inp) for inp in inputs]
            concat_df = xdf.concat(empty_dfs, axis=0)
        if getattr(chunk.index_value, "should_be_monotonic", False):
            concat_df.sort_index(inplace=True)
        return concat_df.index

    def _auto_concat_categorical_chunks(_, inputs):
        if len(inputs) == 1:  # pragma: no cover
            return inputs[0]
        else:
            # convert categorical into array
            arrays = [np.asarray(inp) for inp in inputs]
            array = np.concatenate(arrays)
            return pd.Categorical(
                array, categories=inputs[0].categories, ordered=inputs[0].ordered
            )

    chunk = op.outputs[0]
    inputs = [ctx[input.key] for input in op.inputs]

    if isinstance(inputs[0], tuple):
        ctx[chunk.key] = tuple(
            _base_concat(chunk, [input[i] for input in inputs])
            for i in range(len(inputs[0]))
        )
    else:
        ctx[chunk.key] = _base_concat(chunk, inputs)