<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def generate_correlated_data(dim = 2000, num_active = 40, num_samples = 1000, num_cells_per_cluster_size = [2000]*8, cluster_sizes = range(2, 10)):
 """ Generates a set of data drawn from a uniform distribution, but with bits clustered to force correlation between neurons. Clusters are randomly chosen to form an activation pattern, in such a way as to maintain sparsity. The number of clusters of each size is defined by num_cells_per_cluster_size, as cluster_size*num_clusters = num_cells. This parameter can be thought of as indicating how thoroughly the clusters "cover" the space. Typical values are 1-3 times the total dimension. These are specificied independently for each cluster size, to allow for more variation. """


  # Chooses clusters to
  clusters = []
  cells = set(range(dim))

  # Associate cluster sizes with how many cells they should have, then generate
  # clusters.
  for size, num_cells in zip(cluster_sizes, num_cells_per_cluster_size):

    # Must have (num_cells/size) clusters in order to have num_cells cells
    # across all clusters with this size.
    for i in range(int(1.*num_cells/size)):
      cluster = tuple(numpy.random.choice(dim, size, replace = False))
      clusters.append(cluster)

  # Now generate a list of num_samples SDRs
  datapoints = []
  for sample in range(num_samples):

    # This conditional is necessary in the case that very, very few clusters are
    # created, as otherwise numpy.random.choice can attempt to choose more
    # clusters than is possible.  Typically this case will not occur, but it is
    # possible if we are attempting to generate extremely correlated data.
    if len(clusters) > num_active/2:
      chosen_clusters = numpy.random.choice(len(clusters),
          num_active/2, replace = False)
      current_clusters = [clusters[i] for i in chosen_clusters]
    else:
      current_clusters = clusters

    # Pick clusters until doing so would exceed our target num_active.
    current_cells = set()
    for cluster in current_clusters:
      if len(current_cells) + len(cluster) < num_active:
        current_cells |= set(cluster)
      else:
        break

    # Add random cells to the SDR if we still don't have enough active.
    if len(current_cells) < num_active:
      possible_cells = cells - current_cells
      new_cells = numpy.random.choice(tuple(possible_cells),
          num_active - len(current_cells), replace = False)
      current_cells |= set(new_cells)
    datapoints.append(list(current_cells))

  data = SM32()
  data.reshape(num_samples, dim)
  for sample, datapoint in enumerate(datapoints):
    for i in datapoint:
      data[sample, i] = 1.
  return data