def fuse(name, loop_chain, tile_size):
    """Apply fusion (and possibly tiling) to a list of :class:`ParLoop` obecjts,
    which we refer to as ``loop_chain``. Return a smaller list of :class:`ParLoop`s
    objects, when some loops may have been fused/tiled. If fusion could not be
    applied, return the original, unmodified ``loop_chain``.

    .. note::
       At the moment, the following features are not supported, in which
       case the unmodified ``loop_chain`` is returned.

        * mixed ``Datasets`` and ``Maps``;
        * extruded ``Sets``

    .. note::
       Tiling cannot be applied if any of the following conditions verifies:

        * a global reduction/write occurs in ``loop_chain``
    """
    # If there is nothing to fuse, just return
    if len(loop_chain) in [0, 1]:
        return loop_chain

    # Search for _Assembly objects since they introduce a synchronization point;
    # that is, loops cannot be fused across an _Assembly object. In that case, try
    # to fuse only the segment of loop chain right before the synchronization point
    remainder = []
    synch_points = [l for l in loop_chain if isinstance(l, Mat._Assembly)]
    if synch_points:
        if len(synch_points) > 1:
            warning("Fusing loops and found more than one synchronization point")
        synch_point = loop_chain.index(synch_points[0])
        remainder, loop_chain = loop_chain[synch_point:], loop_chain[:synch_point]

    # If there is nothing left to fuse (e.g. only _Assembly objects were present), return
    if len(loop_chain) in [0, 1]:
        return loop_chain + remainder

    # If loops in /loop_chain/ are already /fusion/ objects (this could happen
    # when loops had already been fused because in a /loop_chain/ context) or
    # if global reductions are present, return
    if any([isinstance(l, ParLoop) for l in loop_chain]) or \
            any([l._reduced_globals for l in loop_chain]):
        return loop_chain + remainder

    # Loop fusion requires modifying kernels, so ASTs must be present...
    if any([not hasattr(l.kernel, '_ast') or not l.kernel._ast for l in loop_chain]):
        return loop_chain + remainder
    # ...and must not be "fake" ASTs
    if any([isinstance(l.kernel._ast, ast.FlatBlock) for l in loop_chain]):
        return loop_chain + remainder

    # Mixed still not supported
    if any(a._is_mixed for a in flatten([l.args for l in loop_chain])):
        return loop_chain + remainder

    # Extrusion still not supported
    if any([l.is_layered for l in loop_chain]):
        return loop_chain + remainder

    # Check if tiling needs be applied
    mode = 'hard'
    if tile_size > 0:
        mode = 'tile'
        # Loop tiling requires the SLOPE library to be available on the system.
        if slope is None:
            warning("Requested tiling, but couldn't locate SLOPE. Check the PYTHONPATH")
            return loop_chain + remainder
        try:
            os.environ['SLOPE_DIR']
        except KeyError:
            warning("Set the env variable SLOPE_DIR to the location of SLOPE")
            warning("Loops won't be fused, and plain ParLoops will be executed")
            return loop_chain + remainder

    # Get an inspector for fusing this loop_chain, possibly retrieving it from
    # the cache, and obtain the fused ParLoops through the schedule it produces
    inspector = Inspector(name, loop_chain, tile_size)
    schedule = inspector.inspect(mode)
    return schedule(loop_chain) + remainder
dataset/ETHPy150Open OP2/PyOP2/pyop2/fusion.py/fuse/original