====================================
Distributed Graphs in ScotchPy
====================================

Distributed graphs are implemented in ScotchPy with the DGraph class.

The DGraph class
################



** WARNING: the DGraph class and related tests only work if mpi4py is installed. **


.. class:: DGraph(init=True)

    The distributed graph constructor itself calls libscotch's ``dgraphAlloc`` function, and
    usually ``dgraphInit`` is called too.

    :param init:
        If True, the graph allocation is followed by the init.
        If not, the :meth:`~DGraph.init` method needs to be called before
        :meth:`~DGraph.build` or :meth:`~DGraph.load`.

    The graph object stores the arguments given to the :meth:`~DGraph.build`
    method as integers and numpy arrays. They are accessible even when the graph
    has been constructed with :meth:`~DGraph.load`. They default to None if the
    argument was not given.

    |

    .. method:: init()

        This routine Initializes the fields of the DGraph object instance.
        Called by a class constructor when ``init=True`` (by default).
        Mandatory before calling either :meth:`~DGraph.build` or :meth:`~DGraph.load`, or after calling :meth:`~DGraph.exit` in order to re-use the same DGraph instance.

    |

    .. method:: exit()

        This routine cleans-up all fields of the instance. Is the opposite of the :meth:`~DGraph.init` routine.
        Called automatically when the object is destroyed.
        Calling explicitly this method is only required when an :meth:`~DGraph.init` call follows.

    |

    .. method:: free()

        This routine resets the instance fields so that it can hold a new DGraph topology.
        Is equivalent to calling :meth:`~DGraph.exit` and :meth:`~DGraph.init` successively.

    |

    .. method:: build(baseval=None, vertglbnbr=None, vertlocnbr=None, vertlocmax=None, vertgstnbr=None, vertloctab=None, vendloctab=None, veloloctab=None, vlblloctab=None, edgeglbnbr=None, edgelocnbr=None, edgelocsiz=None, edgeloctab=None, edgegsttab=None, edloloctab=None)

        This routine builds the distributed graph itself using the given values.
        Arguments should be given as ints and ideally numpy arrays of ints, although any iterable would work.
        Mutating these arrays is not recommended as it may cause runtime errors
        while Scotch handles them.

        Some arguments may be omitted, but :
            - ``baseval`` must be given if the graph is not compact.
            - if ``vertloctab`` has no size, ``baseval``, ``vertlocnbr`` and
              ``edgelocnbr`` must be given.

    :param baseval: DGraph base value for index arrays.
    :type baseval: Integer
    :param vertglbnbr: The global number of vertices.
    :type vertglbnbr: Integer
    :param vertlocnbr: The number of local vertices.
    :type vertlocnbr: Integer
    :param vertlocmax: The maximum number of local vertices.
    :type vertlocmax: Integer
    :param vertgstnbr: The number of ghost vertices.
    :type vertgstnbr: Integer
    :param vertloctab: The local adjacency index array (**Required**).
    :type vertloctab: Any iterable of integers ideally numpy array
    :param vendloctab: The adjacency end index array of size vertlocnbr if disjoint from vertloctab (**Optional**).
    :type vendloctab: Any iterable of integers ideally numpy array
    :param veloloctab: The local vertex load array of size vertlocnbr (**Optional**).
    :type veloloctab: Any iterable of integers ideally numpy array
    :param vlblloctab: The vertex label array of size vertlocnbr (**Optional**).
    :type vlblloctab: Any iterable of integers ideally numpy array
    :param edgeglbnbr: The global number of edges.
    :type edgeglbnbr: Integer
    :param edgelocnbr: The number of local edges.
    :type edgelocnbr: Integer
    :param edgelocsiz: The size of the local edge array.
    :type edgelocsiz: Integer
    :param edgeloctab: The local adjacency array of size edgelocsiz (*more if not compact*) (**Required**).
    :type edgeloctab: Any iterable of integers ideally numpy array
    :param edgegsttab: The global adjacency array of size edgelocsiz (*more if not compact*) (**Required**).
    :type edgegsttab: Any iterable of integers ideally numpy array
    :param edloloctab: The local arc load array of size edgelocsiz (**Optional**).
    :type edloloctab: Any iterable of integers ideally numpy array

    |

    .. method:: build_grid_3d(baseval, dimxval, dimyval, dimzval, incrval, flagval)

        This routine fills the distributed graph with the description of a 3D grid of dimensions ``dimxval``, ``dimyval`` and ``dimzval``, using ``baseval`` as the base value for vertex and edge indices.

    :param baseval: The base value for index arrays.
    :type baseval: Integer
    :param dimxval: The number of vertices in the X dimension.
    :type dimxval: Integer
    :param dimyval: The number of vertices in the Y dimension.
    :type dimyval: Integer
    :param dimzval: The number of vertices in the Z dimension.
    :type dimzval: Integer
    :param incrval: The increment value for vertex indices.
    :type incrval: Integer
    :param flagval: The flagval value is a combination of the following integer values,
                    that may be added or bitwise-ored:

         - ``1``  : 26-neighbor mesh (defaults at 6-neighbor mesh).
         - ``2``  : torus (defaults at mesh)
         - ``4``  : weighted vertices (defaults at no weights).
         - ``8``  : weighted edges (ddefaults at no weights).

    :type flagval: Integer

    |

    .. method:: load(stream, baseval=-1, flagval=0)

        This routine builds the distributed graph with :meth:`~DGraph.build` from data included in the given file or stream.

    :param stream: Input file or stream to read from.
    :type stream: Either a file object (result of an ``open()``, don't forget to close
        it) or a filename, as a string or as a bytes object (such as
        ``b"file/name"``)

    :param baseval: The graph base value for index arrays(0 or 1),
                    takes the baseval value from the ``stream`` data if -1.
    :type baseval: Integer
    :param flagval: The flagval value is a combination of the following integer values,
                    that may be added or bitwise-ored:

                    - ``0`` : Keep vertex and edge weights if they are present in the stream data.
                    - ``1`` : Remove vertex weights. The graph read will have all of its vertex weights set to one, regardless of what is specified in the stream data.
                    - ``2`` : Remove edge weights. The graph read will have all of its edge weights set to one, regardless of what is specified in the stream data.

    :type flagval: Integer

    |

    **The following routines can't be called before the graph is properly built either with
    DGraph.build or with DGraph.load:**

    |

    .. method:: save(stream)

        This routine saves the distributed graph to the given file or stream in the Scotch graph format.

    :param stream: input file or stream to read from.
    :type stream: Either a file object (result of an ``open()``, don't forget to close
                    it) or a filename, as a string or as a bytes object (such as
                    ``b"file/name"``)

    |

    .. method:: check()

        Runs a self-test to check data consistency after the
        :meth:`~DGraph.build` or :meth:`~DGraph.load` method is called, raises a :class:`LibraryError` if not.

    |

    .. method:: data(as_dict=None)

        This routine returns the values taken by the :meth:`~DGraph.build` method. The arrays
        are returned as numpy arrays.

    :param as_dict:
        If True, the requested data is returned as a dict.
        If not, it is returned as a 15-element tuple.
    :type as_dict: Boolean
    :returns: Values in this order: (``baseval``, ``vertglbnbr``, ``vertlocnbr``, ``vertlocmax``,
        ``vertgstnbr``, ``vertloctab``, ``vendloctab``, ``veloloctab``, ``vlblloctab``,
        ``edgeglbnbr``, ``edgelocnbr``, ``edgelocsiz``, ``edgeloctab``, ``edgegsttab``,
        ``edloloctab``).
    :rtype: Tuple or Dict

    |

    .. method:: size(as_dict=None)

        This routine returns the values of ``edgeglbnbr``, ``edgelocnbr``, ``vertlocnbr`` and ``vertglbnbr`` taken by the :meth:`~DGraph.build` method.

    :param as_dict:
        If True, the requested data is returned as a dict.
        If not, it is returned as a 4-element tuple.
    :type as_dict: Boolean
    :returns: Values in this order: (``edgeglbnbr``, ``edgelocnbr``, ``vertlocnbr``, ``vertglbnbr``).
    :rtype: Tuple or Dict

    |

    .. method:: part(partnbr, strat=Strat(init=True), partloctab=None)

        This routine computes a vertex-separated partition, into ``partnbr`` parts, of the graph itself, with respect to the given strategy.

    :param partnbr: The number of parts in the partitioning.
    :type partnbr: Integer
    :param strat: Strategy to be used for the partitioning.
    :type strat: :class:`Strat`
    :param partloctab: The local partition array. If given as a list or numpy array of
            integers, it is updated with the returned values.
    :type partloctab: Any kind of iterable or a numpy array

    |

    .. method:: induce_part(orgpartloctab, indpartval, indvertlocnbr, indgraf)

        This routine induces a partition of the distributed graph itself from the given partition array ``orgpartloctab`` to the induced distributed graph ``indgraf`` with respect to the given induced partition value ``indpartval`` and the number of local vertices ``indvertlocnbr``.

    :param orgpartloctab: The original partition array.
    :type orgpartloctab: Any kind of iterable or a numpy array
    :param indpartval: The induced partition value.
    :type indpartval: Integer
    :param indvertlocnbr: The number of local vertices.
    :type indvertlocnbr: Integer
    :param indgraf: The induced graph.
    :type indgraf: :class:`DGraph`

    |


    .. method:: map(arch, strat=Strat(init=True), partloctab=None)

        This routine computes a mapping of the distributed graph itself onto the given target architecture ``arch`` , with respect to the given strategy ``strat``.

    :param arch: The target architecture.
    :type arch: :class:`Arch`
    :param strat: Strategy to be used for the mapping.
    :type strat: :class:`Strat`
    :param partloctab: The partition array. If given as a list or numpy array of
            integers, it is updated with the returned values.
    :type partloctab: Any kind of iterable or a numpy array

    |

    .. method:: map_init(mapping, arch, partloctab=None)

        This routine initializes an API opaque mapping with respect to the distributed graph itself and the given parameters.

    :param mapping: The mapping to be initialized.
    :type mapping: :class:`Mapping`
    :param arch: The target architecture.
    :type arch: :class:`Arch`
    :param partloctab: The partition array. If given as a list or numpy array of
            integers, it is updated with the returned values.
    :type partloctab: Any kind of iterable or a numpy array

    |

    .. method:: map_exit(mapping)

        This routine frees an API mapping instance.

    :param mapping: The mapping to be freed.
    :type mapping: :class:`Mapping`

    |

    .. method:: map_view(mapping, stream)

        This routine writes mapping statistics (load of target processors, number of neigh-
        boring domains, average dilation and expansion, edge cut size, distribution of
        edge dilations) to the given stream.

    :param mapping: The mapping to be viewed.
    :type mapping: :class:`Mapping`
    :param stream: Input file or stream to write to.
    :type stream: Either a file object (result of an ``open()``, don't forget to close
                  it) or a filename, as a string or as a bytes object (such as
                  ``b"file/name"``)

    |

    .. method:: map_compute(mapping, strat)

        This routine computes a mapping of the API mapping structure ``mapping`` with
        respect to the given strategy ``strat`` .

    :param mapping: The mapping to be computed.
    :type mapping: :class:`Mapping`
    :param strat: Strategy to be used for the mapping .
    :type strat: :class:`Strat`

    |

    .. method:: map_save(mapping, stream)

        This routine saves the contents of the given user mapping ``mapping`` to the given
        stream ``stream``.

    :param mapping: The mapping to be saved.
    :type mapping: :class:`Mapping`
    :param stream: Input file or stream to write to.
    :type stream: Either a file object (result of an ``open()``, don't forget to close
                  it) or a filename, as a string or as a bytes object (such as
                  ``b"file/name"``)

    |

    .. method:: map_view(mapping, stream)

        This routine writes mapping statistics (load of target processors, number of neigh-
        boring domains, average dilation and expansion, edge cut size, distribution of
        edge dilations) to the given stream.

    :param mapping: The mapping to be displayed.
    :type mapping: :class:`Mapping`
    :param stream: Input file or stream to write to.
    :type stream: Either a file object (result of an ``open()``, don't forget to close
                  it) or a filename, as a string or as a bytes object (such as
                  ``b"file/name"``)

    .. method:: order_init(ordering)

        This routine initializes an ordering instance.

    :param ordering: Ordering to be initialized.
    :type ordering: :class:`Ordering`


    .. method:: order_exit(ordering)

        This routine frees an ordering instance.

    :param ordering: Ordering to be freed.
    :type ordering: :class:`Ordering`

    |

    .. method:: order_compute(ordering, strat)

        This routine computes a block ordering of the distributed graph itself with respect to the given strategy ``strat``. Stores the result in the given ordering instance ``ordering``.

    :param ordering: Ordering to be computed.
    :type ordering: :class:`Ordering`
    :param strat: Strategy to be used for the ordering.
    :type strat: :class:`Strat`

    |

    .. method:: order_save(ordering, stream)

        This routine saves the contents of the given ordering instance ``ordering`` to the given stream ``stream``.

    :param ordering: Ordering to be saved.
    :type ordering: :class:`Ordering`
    :param stream: Input file or stream to write to.
    :type stream: Either a file object (result of an ``open()``, don't forget to close
                  it) or a filename, as a string or as a bytes object (such as
                  ``b"file/name"``)

    |

    .. method:: order_cblk_dist(ordering)

        This routine returns the global number of distributed elimination tree (super-)nodes possessed by the given distributed ordering instance ``ordering``.

    :param ordering: Ordering to be used.
    :type ordering: :class:`Ordering`

    .. method:: order_save_map(ordering, stream)

        This routine saves the contents of the given ordering instance ``ordering`` to the given stream ``stream`` in the Scotch mapping format.

    :param ordering: Ordering to be saved.
    :type ordering: :class:`Ordering`
    :param stream: Input file or stream to write to.
    :type stream: Either a file object (result of an ``open()``, don't forget to close
                  it) or a filename, as a string or as a bytes object (such as
                  ``b"file/name"``)

    |

    .. method:: order_save_tree(ordering, stream)

        This routine saves the contents of the given ordering instance ``ordering`` to the given stream ``stream`` in the tree output format format.

    :param ordering: Ordering to be saved.
    :type ordering: :class:`Ordering`
    :param stream: Input file or stream to write to.
    :type stream: Either a file object (result of an ``open()``, don't forget to close
                  it) or a filename, as a string or as a bytes object (such as
                  ``b"file/name"``)

    |

    .. method:: order_perm(ordering, permloctab=None)

        This routine fills the distributed direct permutation array ``permloctab`` with the inverse permutation of the ordering array ``ordering``.

    :param ordering: Ordering to be used.
    :type ordering: :class:`Ordering`
    :param permloctab: The permutation array or size vertlocnbr. If given as a list or numpy array of
            integers, it is updated with the returned values.
    :type permloctab: Any kind of iterable or a numpy array

    |

    .. method:: order_tree_dist(ordering, treeglbtab=None, sizeglbtab=None)

        This routine fills on all processes the arrays representing the distributed part of the elimination tree associated with the given ordering instance ``ordering``.

    :param ordering: Ordering to be used.
    :type ordering: :class:`Ordering`
    :param treeglbtab: The global elimination tree array. If given as a list or numpy array of
            integers, it is updated with the returned values.
    :type treeglbtab: Any kind of iterable or a numpy array
    :param sizeglbtab: The global size array. If given as a list or numpy array of
            integers, it is updated with the returned values.
    :type sizeglbtab: Any kind of iterable or a numpy array

    |

    .. method:: centralized_order_exit(cordering)

        This routine frees a centralized ordering instance.

    :param cordering: Centralized ordering to be freed.
    :type cordering: :class:`Ordering`

    |

    .. method:: centralized_order_init(cordering, permtab=None, peritab=None, cblknbr=None, rangtab=None, treetab=None)

        This routine fills the centralized ordering instance ``cordering`` with the given parameters.

    :param cordering: Centralized ordering to be filled.
    :type cordering: :class:`Ordering`
    :param permtab: The ordering permutation array.
    :type permtab: Any kind of iterable or a numpy array
    :param peritab: The inverse ordering permutation array.
    :type peritab: Any kind of iterable or a numpy array
    :param cblknbr: The number of column blocks.
    :type cblknbr: Integer
    :param rangtab: The column block span array.
    :type rangtab: Any kind of iterable or a numpy array
    :param treetab: The separators tree array.
    :type treetab: Any kind of iterable or a numpy array

    |

    .. method:: grow(seedlocnbr, seedloctab, distmax, partgsttab=None)

        This routine grows areas of the distributed graph itself from a set of colored seeds provided on each process.

    :param seedlocnbr: The number of local vertices in the seed.
    :type seedlocnbr: Integer
    :param seedloctab: The local vertex array of the seed.
    :type seedloctab: Any kind of iterable or a numpy array
    :param distval: The distance value.
    :type distval: Integer
    :param partgsttab: The ghost partition array. If given as a list or numpy array of
            integers, it is updated with the returned values.
    :type partgsttab: Any kind of iterable or a numpy array

    |

    .. method:: coarsen(coarnbr, coarrat, flagval, coargraf, multloctab=None)

        This routine coarsens the graph itself with respect to the given parameters.
        The coarsened distributed graph is created only if it comprises more than ``coarnbr`` vertices, or if the coarsening ratio is lower than ``coarrat``.
        Valid coarsening ratio values range from 0.5 (in the case of a perfect matching) to 1.0 (if no vertex could be coarsened). Classical threshold values range from 0.7 to 0.8.
        Raises :class:`CannotCoarsen` if not because of threshold parameters,
        raises :class:`LibraryError` otherwise.

    :param coarnbr: Number of vertices in the coarsened graph.
    :type coarvertnbr: Integer
    :param coarrat: Coarsening ratio.
    :type coarrat: Floating-point number
    :param flagval: The flagval flag specifies the type of coarsening
    :type flagval: Integer
    :param coargraf: Another :class:`DGraph` instance, which gets modified.
    :type coargraf: :class:`DGraph`
    :param multloctab: The vertex-to-vertex multiplicity array. If given as a list or numpy array of
            integers, it is updated with the returned values.
    :type coarmulttab: Any kind of iterable or a numpy array

    |

    .. method:: stat(as_dict=False)

        This routine gives various stats about the distributed graph:

        - ``velomin``, ``velomax``, ``velosum``, ``veloavg``, ``velodlt``, are the minimum vertex load, the maximum vertex load, the sum of all vertex loads, the average vertex load and the variance of the vertex degrees, respectively.

        - ``degrmin``, ``degrmax``, ``degravg``, ``degrdlt``, are the minimum vertex degree, the maximum vertex degree, the average vertex degree and the variance of the vertex degrees, respectively.

        - ``edlomin``, ``edlomax``, ``edlosum``, ``edloavg``, ``edlodlt`` are the minimum edge load, the maximum edge load, the sum of all edge loads, the average edge load and the variance of the edge loads, respectively.


        :param as_dict:
            If True, the requested data is returned as a dict.
            If not, it is returned as a 14-element tuple.
        :type as_dict: Boolean
        :returns: various stats about the distributed graph, in this specific order :(``velomin``, ``velomax``, ``velosum``, ``veloavg``,
            ``velodlt``, ``degrmin``, ``degrmax``, ``degravg``, ``degrdlt``,
            ``edlomin``, ``edlomax``, ``edlosum``, ``edloavg``, ``edlodlt``).
        :rtype: Tuple or Dict

    |

    .. method:: coarsen_vert_loc_max(flagval)

        This routine computes an upper bound on the local number of coarse vertices that can be created by the coarsening process.

    :param flagval: The flagval flag specifies the type of coarsening.
    :type flagval: Integer
    :returns: The upper bound on the local number of coarse vertices.
    :rtype: Integer

    |

    .. method:: gather(cgrf)

        This routine gathers the contents of the distributed graph itself into the centralized graph ``cgrf``.

    :param cgrf: The centralized graph to be filled.
    :type cgrf: :class:`Graph`

    |

    .. method:: scatter(cgrf)

        This routine scatters the contents of the centralized graph ``cgrf`` into the distributed graph itself.

    :param cgrf: The centralized graph to be scattered.
    :type cgrf: :class:`Graph`

    |

    .. method:: redist(partloctab, permgsttab, vertlocdlt, edgelocdlt, redgraf)

        This routine initializes and fills the redistributed graph ``redgraf`` with a new distributed graph made from the original graph itself and the given parameters.

    :param partloctab: The partition array.
    :type partloctab: Any kind of iterable or a numpy array
    :param permgsttab: The global permutation array.
    :type permgsttab: Any kind of iterable or a numpy array
    :param vertlocdlt: The local vertex weight array.
    :type vertlocdlt: Any kind of iterable or a numpy array
    :param edgelocdlt: The local edge weight array.
    :type edgelocdlt: Any kind of iterable or a numpy array
    :param redgraf: The redistributed graph.
    :type redgraf: :class:`DGraph`

    |

    .. method:: ghst()

        This routine fills the ``edgegsttab`` array of the distributed graph itself with the local and ghost vertex indices corresponding to the global vertex indices contained in the ``edgeloctab`` array.

    |

    .. method:: halo(datatab, typeval)

        This routine propagates the data borne by local vertices to all of the corresponding halo vertices located on neighboring processes.

    :param datatab: The data array to be propagated of size ``vertgstnbr`` at least.
    :type datatab: Any kind of iterable or a numpy array
    :param typeval: The type of data to be propagated.
    :type typeval: Integer

    |

    .. method:: halo_async(datatab, typeval, requ)

        This routine propagates the data borne by local vertices to all of the corresponding halo vertices located on neighboring processes, asynchronously.

    :param datatab: The data array to be propagated of size ``vertgstnbr`` at least.
    :type datatab: Any kind of iterable or a numpy array
    :param typeval: The type of data to be propagated.
    :type typeval: Integer
    :param requ: HaloReq data structure.
    :type requ: :class:`HaloReq` instance

    |

    .. method:: halo_wait(requ)

        This routine waits for the completion of the asynchronous halo exchange process.

    :param requ: HaloReq data structure.
    :type requ: :class:`HaloReq` instance

    |

**Various functions are defined as wrappers of DGraph operations:**

    |

.. function:: dgraph_alloc()

    Recreates the ``dgraphAlloc`` function, returning a non-initialized
    :class:`DGraph` instance. It will need the :meth:`~DGraph.init` method to be
    called before the instance be built or loaded upon.

    |

.. function:: build_dgraph(*args, **kwargs)

    Returns a properly built graph instance. Positional and keyword arguments
    need to be given in the same fashion as :meth:`DGraph.build` takes them.

    |

.. function:: load_dgraph(*args, **kwargs)

    Returns a distributed graph instance loaded from the given file or stream. Positional
    arguments need to be given in the same fashion as :meth:`DGraph.load` takes
    them.

    |

There is no explicit equivalent to Scotch's ``memFree`` function, since its call
is included in the class destructor. To delete the ``graf`` variable and free
the related graph structure before the local scope expires, simply use::

    del graf

However, due to how Python's garbage-collector works, the ``memFree`` function
may not be called immediately.

Good practices
##############

All in all, the Python and ScotchPy good practices tend not to use the
:meth:`~DGraph.init` and :meth:`~DGraph.exit` methods and the :class:`DGraph`
constructor. They return unfinished and *per se* unusable objects, since they
are included for compatibility with the C version of PT-Scotch. The use of the
:func:`build_dgraph` or :func:`load_dgraph` functions is therefore advised.
