def block_combine(arr, nrows, ncols):
    """Combine a list of blocks (m * n) into nrows * ncols 2D matrix.

    Arguments:
        arr {3D np.array} -- A list of blocks in the format:
            arr[# of block][block row size][block column size]
        nrows {int} -- The target row size after combination.
        ncols {int} -- The target column size after combination.

    Returns:
        2D np.array -- Combined matrix.

    Raise:
        ValueError -- The size of `arr` is not equal to `nrows * ncols`.
    """
    if arr.size != nrows * ncols:
        raise ValueError(f'The size of arr ({arr.size}) should be equal to '
                         f'nrows * ncols ({nrows} * {ncols})')

    _, block_nrows, block_ncols = arr.shape

    return (arr.reshape(nrows // block_nrows, -1, block_nrows, block_ncols)
            .swapaxes(1, 2)
            .reshape(nrows, ncols))