<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def Bipartite(graph, resolution_parameter_01, resolution_parameter_0 = 0, resolution_parameter_1 = 0, degree_as_node_size=False, types='type', **kwargs):
 """ Create three layers for bipartite partitions. This creates three layers for bipartite partition necessary for detecting communities in bipartite networks. These three layers should be passed to :func:`Optimiser.optimise_partition_multiplex` with ``layer_weights=[1,-1,-1]``. Parameters graph : :class:`ig.Graph` Graph to define the bipartite partitions on. resolution_parameter_01 : double Resolution parameter for in between two classes. resolution_parameter_0 : double Resolution parameter for class 0. resolution_parameter_1 : double Resolution parameter for class 1. degree_as_node_size : boolean If ``True`` use degree as node size instead of 1, to mimic modularity, see `Notes <#notes-bipartite>`_. types : vertex attribute or list Indicator of the class for each vertex. If not 0, 1, it is automatically converted. **kwargs Additional arguments passed on to default constructor of :class:`CPMVertexPartition`. .. _notes-bipartite: Notes ----- For bipartite networks, we would like to be able to set three different resolution parameters: one for within each class :math:`\\gamma_0, \\gamma_1`, and one for the links between classes, :math:`\\gamma_{01}`. Then the formulation would be .. math:: Q = \\sum_{ij} [A_{ij} - (\\gamma_0\\delta(s_i,0) + \\gamma_1\\delta(s_i,1)) \\delta(s_i,s_j) - \\gamma_{01}(1 - \\delta(s_i, s_j)) ]\\delta(\\sigma_i, \\sigma_j) In terms of communities this is .. math:: Q = \\sum_c (e_c - \\gamma_{01} 2 n_c(0) n_c(1) - \\gamma_0 n^2_c(0) - \\gamma_1 n^2_c(1)) where :math:`n_c(0)` is the number of nodes in community :math:`c` of class 0 (and similarly for 1) and :math:`e_c` is the number of edges within community :math:`c`. We denote by :math:`n_c = n_c(0) + n_c(1)` the total number of nodes in community :math:`c`. We achieve this by creating three layers : (1) all nodes have ``node_size = 1`` and all relevant links; (2) only nodes of class 0 have ``node_size = 1`` and no links; (3) only nodes of class 1 have ``node_size = 1`` and no links. If we add the first with resolution parameter :math:`\\gamma_{01}`, and the others with resolution parameters :math:`\\gamma_{01} - \\gamma_0` and :math:`\\gamma_{01} - \\gamma_1`, but the latter two with a layer weight of -1 while the first layer has layer weight 1, we obtain the following: .. math:: Q &= \\sum_c (e_c - \\gamma_{01} n_c^2) -\\sum_c (- (\\gamma_{01} - \\gamma_0) n_c(0)^2) -\\sum_c (- (\\gamma_{01} - \\gamma_1) n_c(1)^2) \\\\ &= \\sum_c [e_c - \\gamma_{01} 2 n_c(0) n_c(1) - \\gamma_{01} n_c(0)^2 - \\gamma_{01} n_c(1)^2) + ( \\gamma_{01} - \\gamma_0) n_c(0)^2 + ( \\gamma_{01} - \\gamma_1) n_c(1)^2 ] \\\\ &= \\sum_c [e_c - \\gamma_{01} 2 n_c(0) n_c(1) - \\gamma_{0} n_c(0)^2 - \\gamma_{1} n_c(1)^2] Although the derivation above is using :math:`n_c^2`, implicitly assuming a direct graph with self-loops, similar derivations can be made for undirected graphs using :math:`\\binom{n_c}{2}`, but the notation is then somewhat more convoluted. If we set node sizes equal to the degree, we get something similar to modularity, except that the resolution parameter should still be divided by :math:`2m`. In particular, in general (i.e. not specifically for bipartite graph) if ``node_sizes=G.degree()`` we then obtain .. math:: Q = \\sum_{ij} A_{ij} - \\gamma k_i k_j In the case of bipartite graphs something similar is obtained, but then correctly adapted (as long as the resolution parameter is also appropriately rescaled). .. note:: This function is not suited for directed graphs in the case of using the degree as node sizes. """


    if types is not None:
      if isinstance(types, str):
        types = graph.vs[types]
      else:
        # Make sure it is a list
        types = list(types)

    if set(types) != set([0, 1]):
      new_type = _ig.UniqueIdGenerator()
      types = [new_type[t] for t in types]

    if set(types) != set([0, 1]):
      raise ValueError("More than one type specified.")

    if degree_as_node_size:
      if (graph.is_directed()):
        raise ValueError("This method is not suitable for directed graphs " +
                         "when using degree as node sizes.")
      node_sizes = graph.degree()
    else:
      node_sizes = [1]*graph.vcount()

    partition_01 = CPMVertexPartition(graph,
                     node_sizes=node_sizes,
                     resolution_parameter=resolution_parameter_01,
                     **kwargs)
    H_0 = graph.subgraph_edges([], delete_vertices=False)
    partition_0 = CPMVertexPartition(H_0, weights=None,
                     node_sizes=[s if t == 0 else 0
                                 for v, s, t in zip(graph.vs,node_sizes,types)],
                     resolution_parameter=resolution_parameter_01 - resolution_parameter_0,
                     **kwargs)
    H_1 = graph.subgraph_edges([], delete_vertices=False)
    partition_1 = CPMVertexPartition(H_1, weights=None,
                     node_sizes=[s if t == 1 else 0
                                 for v, s, t in zip(graph.vs,node_sizes,types)],
                     resolution_parameter=resolution_parameter_01 - resolution_parameter_1,
                     **kwargs)
    return partition_01, partition_0, partition_1