docs: def to_string(self):
          '''
          API: to_string(self)
          Description:
          This method is based on pydot Graph class with the same name.
          Returns a string representation of the graph in dot language.
          It will return the graph and all its subelements in string form.
          Return:
              String that represents graph in dot language.
          '''
          graph = list()
          processed_edges = {}
          graph.append('%s %s {\n' %(self.graph_type, self.name))
          for a in self.attr:
              if a not in GRAPH_ATTRIBUTES:
                  continue
              val = self.attr[a]
              if val is not None:
                  graph.append( '%s=%s' % (a, quote_if_necessary(val)) )
              else:
                  graph.append(a)
              graph.append( ';\n' )
          # clusters
          for c in self.cluster:
              graph.append('subgraph cluster_%s {\n' %c)
              for a in self.cluster[c]['attrs']:
                  if a=='label':
                      graph.append(a+'='+quote_if_necessary(self.cluster[c]['attrs'][a])+';\n')
                      continue
                  graph.append(a+'='+self.cluster[c]['attrs'][a]+';\n')
              if len(self.cluster[c]['node_attrs'])!=0:
                  graph.append('node [')
              for a in self.cluster[c]['node_attrs']:
                  graph.append(a+'='+self.cluster[c]['node_attrs'][a])
                  graph.append(',')
              if len(self.cluster[c]['node_attrs'])!=0:
                  graph.pop()
                  graph.append('];\n')
              # process cluster nodes
              for n in self.cluster[c]['node_list']:
                  data = self.get_node(n).to_string()
                  graph.append(data + ';\n')
              # process cluster edges
              for n in self.cluster[c]['node_list']:
                  for m in self.cluster[c]['node_list']:
                      if self.check_edge(n,m):
                          data = self.edge_to_string((n,m))
                          graph.append(data + ';\n')
                          processed_edges[(n,m)]=None
              graph.append('}\n')
          # process remaining (non-cluster) nodes
          for n in self.neighbors:
              for c in self.cluster:
                  if n in self.cluster[c]['node_list']:
                      break
              else:
                  data = self.get_node(n).to_string()
                  graph.append(data + ';\n')
          # process edges
          for e in self.edge_attr:
              if e in processed_edges:
                  continue
              data = self.edge_to_string(e)
              graph.append(data + ';\n')
          graph.append( '}\n' )
          return ''.join(graph)