class_docstr: Constructs and holds the fitting of the diagram into a grid.
  
  Explanation
  ===========
  
  The mission of this class is to analyse the structure of the
  supplied diagram and to place its objects on a grid such that,
  when the objects and the morphisms are actually drawn, the diagram
  would be "readable", in the sense that there will not be many
  intersections of moprhisms.  This class does not perform any
  actual drawing.  It does strive nevertheless to offer sufficient
  metadata to draw a diagram.
  
  Consider the following simple diagram.
  
  >>> from sympy.categories import Object, NamedMorphism
  >>> from sympy.categories import Diagram, DiagramGrid
  >>> from sympy import pprint
  >>> A = Object("A")
  >>> B = Object("B")
  >>> C = Object("C")
  >>> f = NamedMorphism(A, B, "f")
  >>> g = NamedMorphism(B, C, "g")
  >>> diagram = Diagram([f, g])
  
  The simplest way to have a diagram laid out is the following:
  
  >>> grid = DiagramGrid(diagram)
  >>> (grid.width, grid.height)
  (2, 2)
  >>> pprint(grid)
  A  B
  <BLANKLINE>
     C
  
  Sometimes one sees the diagram as consisting of logical groups.
  One can advise ``DiagramGrid`` as to such groups by employing the
  ``groups`` keyword argument.
  
  Consider the following diagram:
  
  >>> D = Object("D")
  >>> f = NamedMorphism(A, B, "f")
  >>> g = NamedMorphism(B, C, "g")
  >>> h = NamedMorphism(D, A, "h")
  >>> k = NamedMorphism(D, B, "k")
  >>> diagram = Diagram([f, g, h, k])
  
  Lay it out with generic layout:
  
  >>> grid = DiagramGrid(diagram)
  >>> pprint(grid)
  A  B  D
  <BLANKLINE>
     C
  
  Now, we can group the objects `A` and `D` to have them near one
  another:
  
  >>> grid = DiagramGrid(diagram, groups=[[A, D], B, C])
  >>> pprint(grid)
  B     C
  <BLANKLINE>
  A  D
  
  Note how the positioning of the other objects changes.
  
  Further indications can be supplied to the constructor of
  :class:`DiagramGrid` using keyword arguments.  The currently
  supported hints are explained in the following paragraphs.
  
  :class:`DiagramGrid` does not automatically guess which layout
  would suit the supplied diagram better.  Consider, for example,
  the following linear diagram:
  
  >>> E = Object("E")
  >>> f = NamedMorphism(A, B, "f")
  >>> g = NamedMorphism(B, C, "g")
  >>> h = NamedMorphism(C, D, "h")
  >>> i = NamedMorphism(D, E, "i")
  >>> diagram = Diagram([f, g, h, i])
  
  When laid out with the generic layout, it does not get to look
  linear:
  
  >>> grid = DiagramGrid(diagram)
  >>> pprint(grid)
  A  B
  <BLANKLINE>
     C  D
  <BLANKLINE>
        E
  
  To get it laid out in a line, use ``layout="sequential"``:
  
  >>> grid = DiagramGrid(diagram, layout="sequential")
  >>> pprint(grid)
  A  B  C  D  E
  
  One may sometimes need to transpose the resulting layout.  While
  this can always be done by hand, :class:`DiagramGrid` provides a
  hint for that purpose:
  
  >>> grid = DiagramGrid(diagram, layout="sequential", transpose=True)
  >>> pprint(grid)
  A
  <BLANKLINE>
  B
  <BLANKLINE>
  C
  <BLANKLINE>
  D
  <BLANKLINE>
  E
  
  Separate hints can also be provided for each group.  For an
  example, refer to ``tests/test_drawing.py``, and see the different
  ways in which the five lemma [FiveLemma] can be laid out.
  
  See Also
  ========
  
  Diagram
  
  References
  ==========
  
  .. [FiveLemma] https://en.wikipedia.org/wiki/Five_lemma
class_docstr_tok: ["Constructs","and","holds","the","fitting","of","the","diagram","into","a","grid",".","Explanation","===========","The","mission","of","this","class","is","to","analyse","the","structure","of","the","supplied","diagram","and","to","place","its","objects","on","a","grid","such","that",",","when","the","objects","and","the","morphisms","are","actually","drawn",",","the","diagram","would","be","``","readable","''",",","in","the","sense","that","there","will","not","be","many","intersections","of","moprhisms",".","This","class","does","not","perform","any","actual","drawing",".","It","does","strive","nevertheless","to","offer","sufficient","metadata","to","draw","a","diagram",".","Consider","the","following","simple","diagram",".",">",">",">","from","sympy.categories","import","Object",",","NamedMorphism",">",">",">","from","sympy.categories","import","Diagram",",","DiagramGrid",">",">",">","from","sympy","import","pprint",">",">",">","A","=","Object","(","``","A","''",")",">",">",">","B","=","Object","(","``","B","''",")",">",">",">","C","=","Object","(","``","C","''",")",">",">",">","f","=","NamedMorphism","(","A",",","B",",","``","f","''",")",">",">",">","g","=","NamedMorphism","(","B",",","C",",","``","g","''",")",">",">",">","diagram","=","Diagram","(","[","f",",","g","]",")","The","simplest","way","to","have","a","diagram","laid","out","is","the","following",":",">",">",">","grid","=","DiagramGrid","(","diagram",")",">",">",">","(","grid.width",",","grid.height",")","(","2",",","2",")",">",">",">","pprint","(","grid",")","A","B","<","BLANKLINE",">","C","Sometimes","one","sees","the","diagram","as","consisting","of","logical","groups",".","One","can","advise","``","DiagramGrid","``","as","to","such","groups","by","employing","the","``","groups","``","keyword","argument",".","Consider","the","following","diagram",":",">",">",">","D","=","Object","(","``","D","''",")",">",">",">","f","=","NamedMorphism","(","A",",","B",",","``","f","''",")",">",">",">","g","=","NamedMorphism","(","B",",","C",",","``","g","''",")",">",">",">","h","=","NamedMorphism","(","D",",","A",",","``","h","''",")",">",">",">","k","=","NamedMorphism","(","D",",","B",",","``","k","''",")",">",">",">","diagram","=","Diagram","(","[","f",",","g",",","h",",","k","]",")","Lay","it","out","with","generic","layout",":",">",">",">","grid","=","DiagramGrid","(","diagram",")",">",">",">","pprint","(","grid",")","A","B","D","<","BLANKLINE",">","C","Now",",","we","can","group","the","objects","`","A","`","and","`","D","`","to","have","them","near","one","another",":",">",">",">","grid","=","DiagramGrid","(","diagram",",","groups=","[","[","A",",","D","]",",","B",",","C","]",")",">",">",">","pprint","(","grid",")","B","C","<","BLANKLINE",">","A","D","Note","how","the","positioning","of","the","other","objects","changes",".","Further","indications","can","be","supplied","to","the","constructor","of",":","class",":","`","DiagramGrid","`","using","keyword","arguments",".","The","currently","supported","hints","are","explained","in","the","following","paragraphs",".",":","class",":","`","DiagramGrid","`","does","not","automatically","guess","which","layout","would","suit","the","supplied","diagram","better",".","Consider",",","for","example",",","the","following","linear","diagram",":",">",">",">","E","=","Object","(","``","E","''",")",">",">",">","f","=","NamedMorphism","(","A",",","B",",","``","f","''",")",">",">",">","g","=","NamedMorphism","(","B",",","C",",","``","g","''",")",">",">",">","h","=","NamedMorphism","(","C",",","D",",","``","h","''",")",">",">",">","i","=","NamedMorphism","(","D",",","E",",","``","i","''",")",">",">",">","diagram","=","Diagram","(","[","f",",","g",",","h",",","i","]",")","When","laid","out","with","the","generic","layout",",","it","does","not","get","to","look","linear",":",">",">",">","grid","=","DiagramGrid","(","diagram",")",">",">",">","pprint","(","grid",")","A","B","<","BLANKLINE",">","C","D","<","BLANKLINE",">","E","To","get","it","laid","out","in","a","line",",","use","``","layout=","''","sequential","''","``",":",">",">",">","grid","=","DiagramGrid","(","diagram",",","layout=","''","sequential","''",")",">",">",">","pprint","(","grid",")","A","B","C","D","E","One","may","sometimes","need","to","transpose","the","resulting","layout",".","While","this","can","always","be","done","by","hand",",",":","class",":","`","DiagramGrid","`","provides","a","hint","for","that","purpose",":",">",">",">","grid","=","DiagramGrid","(","diagram",",","layout=","''","sequential","''",",","transpose=True",")",">",">",">","pprint","(","grid",")","A","<","BLANKLINE",">","B","<","BLANKLINE",">","C","<","BLANKLINE",">","D","<","BLANKLINE",">","E","Separate","hints","can","also","be","provided","for","each","group",".","For","an","example",",","refer","to","``","tests\/test_drawing.py","``",",","and","see","the","different","ways","in","which","the","five","lemma","[","FiveLemma","]","can","be","laid","out",".","See","Also","========","Diagram","References","==========","..","[","FiveLemma","]","https",":","\/\/en.wikipedia.org\/wiki\/Five_lemma"]
docstr: Given the slightly preprocessed morphisms of the diagram,
  produces a grid laid out according to ``groups``.
  
  If a group has hints, it is laid out with those hints only,
  without any influence from ``hints``.  Otherwise, it is laid
  out with ``hints``.
docstr_tok: ["Given","the","slightly","preprocessed","morphisms","of","the","diagram",",","produces","a","grid","laid","out","according","to","``","groups","``",".","If","a","group","has","hints",",","it","is","laid","out","with","those","hints","only",",","without","any","influence","from","``","hints","``",".","Otherwise",",","it","is","laid","out","with","``","hints","``","."]
code: def _handle_groups(diagram, groups, merged_morphisms, hints):
      """
      Given the slightly preprocessed morphisms of the diagram,
      produces a grid laid out according to ``groups``.
  
      If a group has hints, it is laid out with those hints only,
      without any influence from ``hints``.  Otherwise, it is laid
      out with ``hints``.
      """
  
      def lay_out_group(group, local_hints):
          """
          If ``group`` is a set of objects, uses a ``DiagramGrid``
          to lay it out and returns the grid.  Otherwise returns the
          object (i.e., ``group``).  If ``local_hints`` is not
          empty, it is supplied to ``DiagramGrid`` as the dictionary
          of hints.  Otherwise, the ``hints`` argument of
          ``_handle_groups`` is used.
          """
          if isinstance(group, FiniteSet):
              # Set up the corresponding object-to-group
              # mappings.
              for obj in group:
                  obj_groups[obj] = group
  
              # Lay out the current group.
              if local_hints:
                  groups_grids[group] = DiagramGrid(
                      diagram.subdiagram_from_objects(group),
                      **local_hints,
                  )
              else:
                  groups_grids[group] = DiagramGrid(
                      diagram.subdiagram_from_objects(group), **hints
                  )
          else:
              obj_groups[group] = group
  
      def group_to_finiteset(group):
          """
          Converts ``group`` to a :class:``FiniteSet`` if it is an
          iterable.
          """
          if iterable(group):
              return FiniteSet(*group)
          else:
              return group
  
      obj_groups = {}
      groups_grids = {}
  
      # We would like to support various containers to represent
      # groups.  To achieve that, before laying each group out, it
      # should be converted to a FiniteSet, because that is what the
      # following code expects.
  
      if isinstance(groups, (dict, Dict)):
          finiteset_groups = {}
          for group, local_hints in groups.items():
              finiteset_group = group_to_finiteset(group)
              finiteset_groups[finiteset_group] = local_hints
              lay_out_group(group, local_hints)
          groups = finiteset_groups
      else:
          finiteset_groups = []
          for group in groups:
              finiteset_group = group_to_finiteset(group)
              finiteset_groups.append(finiteset_group)
              lay_out_group(finiteset_group, None)
          groups = finiteset_groups
  
      new_morphisms = []
      for morphism in merged_morphisms:
          dom = obj_groups[morphism.domain]
          cod = obj_groups[morphism.codomain]
          # Note that we are not really interested in morphisms
          # which do not employ two different groups, because
          # these do not influence the layout.
          if dom != cod:
              # These are essentially unnamed morphisms; they are
              # not going to mess in the final layout.  By giving
              # them the same names, we avoid unnecessary
              # duplicates.
              new_morphisms.append(NamedMorphism(dom, cod, "dummy"))
  
      # Lay out the new diagram.  Since these are dummy morphisms,
      # properties and conclusions are irrelevant.
      top_grid = DiagramGrid(Diagram(new_morphisms))
  
      # We now have to substitute the groups with the corresponding
      # grids, laid out at the beginning of this function.  Compute
      # the size of each row and column in the grid, so that all
      # nested grids fit.
  
      def group_size(group):
          """
          For the supplied group (or object, eventually), returns
          the size of the cell that will hold this group (object).
          """
          if group in groups_grids:
              grid = groups_grids[group]
              return (grid.height, grid.width)
          else:
              return (1, 1)
  
      row_heights = [
          max(
              group_size(top_grid[i, j])[0]
              for j in range(top_grid.width)
          )
          for i in range(top_grid.height)
      ]
  
      column_widths = [
          max(
              group_size(top_grid[i, j])[1]
              for i in range(top_grid.height)
          )
          for j in range(top_grid.width)
      ]
  
      grid = _GrowableGrid(sum(column_widths), sum(row_heights))
  
      real_row = 0
      real_column = 0
      for logical_row in range(top_grid.height):
          for logical_column in range(top_grid.width):
              obj = top_grid[logical_row, logical_column]
  
              if obj in groups_grids:
                  # This is a group.  Copy the corresponding grid in
                  # place.
                  local_grid = groups_grids[obj]
                  for i in range(local_grid.height):
                      for j in range(local_grid.width):
                          grid[
                              real_row + i, real_column + j
                          ] = local_grid[i, j]
              else:
                  # This is an object.  Just put it there.
                  grid[real_row, real_column] = obj
  
              real_column += column_widths[logical_column]
          real_column = 0
          real_row += row_heights[logical_row]
  
      return grid
  
imports: from sympy.categories import CompositeMorphism, IdentityMorphism, NamedMorphism, Diagram
  from sympy.core import Dict, Symbol, default_sort_key
  from sympy.printing.latex import latex
  from sympy.sets import FiniteSet
  from sympy.utilities.iterables import iterable
  from sympy.utilities.decorator import doctest_depends_on
  from itertools import chain
label_desc: Use image node_id 21 for calling the DiagramGrid obj's underlying member method code with example usage: obj._handle_groups(diagram, groups, merged_morphisms, hints) and returns: grid, FiniteSet, group, grid,  grid, int,  int