def block_adjoint(block_form):
    assert isinstance(block_form, (array, list, BlockForm2))
    if isinstance(block_form, (array, list)):
        input_type = array
        (block_form, block_function_space, block_form_rank) = _block_form_preprocessing(block_form)
        assert block_form_rank == 2
        N = len(block_form)
        M = len(block_form[0])
        block_adjoint_function_space = [block_function_space[1], block_function_space[0]]
    else:
        input_type = BlockForm2
        N = block_form.block_size(0)
        M = block_form.block_size(1)
        block_adjoint_function_space = [block_form.block_function_spaces(1), block_form.block_function_spaces(0)]
    block_test_function_adjoint = BlockTestFunction(block_adjoint_function_space[0])
    block_trial_function_adjoint = BlockTrialFunction(block_adjoint_function_space[1])
    block_adjoint_form = empty((M, N), dtype=object)
    for I in range(N):
        for J in range(M):
            assert isinstance(block_form[I, J], Form) or _is_zero(block_form[I, J])
            if isinstance(block_form[I, J], Form):
                block_adjoint_form[J, I] = adjoint(block_form[I, J], (block_test_function_adjoint[J], block_trial_function_adjoint[I]))
            elif _is_zero(block_form[I, J]):
                block_adjoint_form[J, I] = 0
            else:
                raise TypeError("Invalid form")
    if input_type is array:
        return block_adjoint_form
    elif input_type is BlockForm2:
        return BlockForm2(block_adjoint_form, block_function_space=block_adjoint_function_space)
def block_derivative(F, u, du):
    assert isinstance(F, (array, list, BlockForm1))
    if isinstance(F, (array, list)):
        input_type = array
        (F, block_V, block_form_rank) = _block_form_preprocessing(F)
        assert block_form_rank == 1
    else:
        input_type = BlockForm1
        block_V = F.block_function_spaces()
    assert len(block_V) == 1
    block_V = block_V[0]
    
    # Compute the derivative
    assert len(F) == len(u) == len(du)
    J = empty((len(F), len(u)), dtype=object)
    for i in range(len(F)):
        if not _is_zero(F[i]):
            for j in range(len(u)):
                J[i, j] = derivative(F[i], u[j], du[j])
        else:
            J[i, :] = 0
    if input_type is array:
        return J
    elif input_type is BlockForm1:
        return BlockForm2(J, block_function_space=[du.block_function_space(), block_V])
def block_restrict(block_input, block_function_sub_space):
    assert isinstance(
        block_input,
        (array, list, BlockDirichletBC, BlockForm1, BlockForm2, BlockFunction))
    if isinstance(block_input, (array, list, BlockForm1, BlockForm2)):
        if isinstance(block_input, (array, list)):
            (block_form, _,
             block_form_rank) = _block_form_preprocessing(block_input)
        elif isinstance(block_input, BlockForm2):
            block_form = block_input
            block_form_rank = 2
        elif isinstance(block_input, BlockForm1):
            block_form = block_input
            block_form_rank = 1
        if block_form_rank == 2:
            assert isinstance(block_function_sub_space, list)
            assert len(block_function_sub_space) == 2
            assert isinstance(block_function_sub_space[0], BlockFunctionSpace)
            assert isinstance(block_function_sub_space[1], BlockFunctionSpace)
            N_sub_space = block_function_sub_space[0].num_sub_spaces()
            M_sub_space = block_function_sub_space[1].num_sub_spaces()
            sub_block_form = zeros((N_sub_space, M_sub_space), dtype=object)
            for I_sub_space in range(N_sub_space):
                I_space = _sub_component_to_component(
                    block_function_sub_space[0], I_sub_space)
                for J_sub_space in range(M_sub_space):
                    J_space = _sub_component_to_component(
                        block_function_sub_space[1], J_sub_space)
                    sub_block_form[I_sub_space,
                                   J_sub_space] = block_form[I_space, J_space]
            return BlockForm2(sub_block_form, block_function_sub_space)
        elif block_form_rank == 1:
            assert isinstance(block_function_sub_space,
                              (BlockFunctionSpace, list))
            if isinstance(block_function_sub_space, BlockFunctionSpace):
                block_function_sub_space = [block_function_sub_space]
            assert len(block_function_sub_space) == 1
            assert isinstance(block_function_sub_space[0], BlockFunctionSpace)
            N_sub_space = block_function_sub_space[0].num_sub_spaces()
            sub_block_form = zeros((N_sub_space, ), dtype=object)
            for I_sub_space in range(N_sub_space):
                I_space = _sub_component_to_component(
                    block_function_sub_space[0], I_sub_space)
                sub_block_form[I_sub_space] = block_form[I_space]
            return BlockForm1(sub_block_form, block_function_sub_space)
    elif isinstance(block_input, BlockFunction):
        assert isinstance(block_function_sub_space, (BlockFunctionSpace, list))
        if isinstance(block_function_sub_space, list):
            assert len(block_function_sub_space) == 1
            block_function_sub_space = block_function_sub_space[0]
        assert isinstance(block_function_sub_space, BlockFunctionSpace)
        N_sub_space = block_function_sub_space.num_sub_spaces()
        sub_functions = list()
        for I_sub_space in range(N_sub_space):
            I_space = _sub_component_to_component(block_function_sub_space,
                                                  I_sub_space)
            sub_functions.append(block_input[I_space])
        return BlockFunction(block_function_sub_space, sub_functions)
    elif isinstance(block_input, BlockDirichletBC):
        assert isinstance(block_function_sub_space, (BlockFunctionSpace, list))
        if isinstance(block_function_sub_space, list):
            assert len(block_function_sub_space) == 1
            block_function_sub_space = block_function_sub_space[0]
        assert isinstance(block_function_sub_space, BlockFunctionSpace)
        N_sub_space = block_function_sub_space.num_sub_spaces()
        sub_bcs = list()
        for I_sub_space in range(N_sub_space):
            I_space = _sub_component_to_component(block_function_sub_space,
                                                  I_sub_space)
            sub_bcs.append(block_input[I_space])
        return BlockDirichletBC(sub_bcs, block_function_sub_space)
    else:
        raise AssertionError("Invalid arguments to block_restrict")