(ml, presmoother, postsmoother)
Initialize pre and post smoothers.

    Initialize pre- and post- smoothers throughout a MultilevelSolver, with
    the option of having different smoothers at different levels

    For each level of the MultilevelSolver 'ml' (except the coarsest level),
    initialize the .presmoother() and .postsmoother() methods used in the
    multigrid cycle.

    Parameters
    ----------
    ml : pyamg multilevel hierarchy
        Data structure that stores the multigrid hierarchy.
    presmoother : None, string, tuple, list
        presmoother can be (1) the name of a supported smoother, e.g.
        "gauss_seidel", (2) a tuple of the form ('method','opts') where
        'method' is the name of a supported smoother and 'opts' a dict of
        keyword arguments to the smoother, or (3) a list of instances of
        options 1 or 2.  See the Examples section for illustrations of the
        format.

        If presmoother is a list, presmoother[i] determines the smoothing
        strategy for level i.  Else, presmoother defines the same strategy
        for all levels.

        If len(presmoother) < len(ml.levels), then
        presmoother[-1] is used for all remaining levels

        If len(presmoother) > len(ml.levels), then
        the remaining smoothing strategies are ignored

    postsmoother : string, tuple, list
        Defines postsmoother in identical fashion to presmoother

    Returns
    -------
    ml changed in place
    ml.levels[i].presmoother   <===  presmoother[i]
    ml.levels[i].postsmoother  <===  postsmoother[i]
    ml.symmetric_smoothing is marked True/False depending on whether
        the smoothing scheme is symmetric.

    Notes
    -----
    - Parameter 'omega' of the Jacobi, Richardson, and jacobi_ne
      methods is scaled by the spectral radius of the matrix on
      each level.  Therefore 'omega' should be in the interval (0,2).
    - Parameter 'withrho' (default: True) controls whether the omega is
      rescaled by the spectral radius in jacobi, block_jacobi, and jacobi_ne
    - By initializing the smoothers after the hierarchy has been setup, allows
      for "algebraically" directed relaxation, such as strength_based_schwarz,
      which uses only the strong connections of a degree-of-freedom to define
      overlapping regions
    - Available smoother methods::

        gauss_seidel
        block_gauss_seidel
        jacobi
        block_jacobi
        richardson
        sor
        chebyshev
        gauss_seidel_nr
        gauss_seidel_ne
        jacobi_ne
        cg
        gmres
        cgne
        cgnr
        schwarz
        strength_based_schwarz
        None

    Examples
    --------
    >>> from pyamg.gallery import poisson
    >>> from pyamg.aggregation import smoothed_aggregation_solver
    >>> from pyamg.relaxation.smoothing import change_smoothers
    >>> from pyamg.util.linalg import norm
    >>> import numpy as np
    >>> A = poisson((10,10), format='csr')
    >>> b = np.random.rand(A.shape[0],)
    >>> ml = smoothed_aggregation_solver(A, max_coarse=10)
    >>> # Set all levels to use gauss_seidel's defaults
    >>> smoothers = 'gauss_seidel'
    >>> change_smoothers(ml, presmoother=smoothers, postsmoother=smoothers)
    >>> residuals=[]
    >>> x = ml.solve(b, tol=1e-8, residuals=residuals)
    >>> # Set all levels to use three iterations of gauss_seidel's defaults
    >>> smoothers = ('gauss_seidel', {'iterations' : 3})
    >>> change_smoothers(ml, presmoother=smoothers, postsmoother=None)
    >>> residuals=[]
    >>> x = ml.solve(b, tol=1e-8, residuals=residuals)
    >>> # Set level 0 to use gauss_seidel's defaults, and all
    >>> # subsequent levels to use 5 iterations of cgnr
    >>> smoothers = ['gauss_seidel', ('cgnr', {'maxiter' : 5})]
    >>> change_smoothers(ml, presmoother=smoothers, postsmoother=smoothers)
    >>> residuals=[]
    >>> x = ml.solve(b, tol=1e-8, residuals=residuals)
Initialize pre and post smoothers.
Initialize pre and post smoothers .
def change_smoothers(ml, presmoother, postsmoother):
    """Initialize pre and post smoothers.

    Initialize pre- and post- smoothers throughout a MultilevelSolver, with
    the option of having different smoothers at different levels

    For each level of the MultilevelSolver 'ml' (except the coarsest level),
    initialize the .presmoother() and .postsmoother() methods used in the
    multigrid cycle.

    Parameters
    ----------
    ml : pyamg multilevel hierarchy
        Data structure that stores the multigrid hierarchy.
    presmoother : None, string, tuple, list
        presmoother can be (1) the name of a supported smoother, e.g.
        "gauss_seidel", (2) a tuple of the form ('method','opts') where
        'method' is the name of a supported smoother and 'opts' a dict of
        keyword arguments to the smoother, or (3) a list of instances of
        options 1 or 2.  See the Examples section for illustrations of the
        format.

        If presmoother is a list, presmoother[i] determines the smoothing
        strategy for level i.  Else, presmoother defines the same strategy
        for all levels.

        If len(presmoother) < len(ml.levels), then
        presmoother[-1] is used for all remaining levels

        If len(presmoother) > len(ml.levels), then
        the remaining smoothing strategies are ignored

    postsmoother : string, tuple, list
        Defines postsmoother in identical fashion to presmoother

    Returns
    -------
    ml changed in place
    ml.levels[i].presmoother   <===  presmoother[i]
    ml.levels[i].postsmoother  <===  postsmoother[i]
    ml.symmetric_smoothing is marked True/False depending on whether
        the smoothing scheme is symmetric.

    Notes
    -----
    - Parameter 'omega' of the Jacobi, Richardson, and jacobi_ne
      methods is scaled by the spectral radius of the matrix on
      each level.  Therefore 'omega' should be in the interval (0,2).
    - Parameter 'withrho' (default: True) controls whether the omega is
      rescaled by the spectral radius in jacobi, block_jacobi, and jacobi_ne
    - By initializing the smoothers after the hierarchy has been setup, allows
      for "algebraically" directed relaxation, such as strength_based_schwarz,
      which uses only the strong connections of a degree-of-freedom to define
      overlapping regions
    - Available smoother methods::

        gauss_seidel
        block_gauss_seidel
        jacobi
        block_jacobi
        richardson
        sor
        chebyshev
        gauss_seidel_nr
        gauss_seidel_ne
        jacobi_ne
        cg
        gmres
        cgne
        cgnr
        schwarz
        strength_based_schwarz
        None

    Examples
    --------
    >>> from pyamg.gallery import poisson
    >>> from pyamg.aggregation import smoothed_aggregation_solver
    >>> from pyamg.relaxation.smoothing import change_smoothers
    >>> from pyamg.util.linalg import norm
    >>> import numpy as np
    >>> A = poisson((10,10), format='csr')
    >>> b = np.random.rand(A.shape[0],)
    >>> ml = smoothed_aggregation_solver(A, max_coarse=10)
    >>> # Set all levels to use gauss_seidel's defaults
    >>> smoothers = 'gauss_seidel'
    >>> change_smoothers(ml, presmoother=smoothers, postsmoother=smoothers)
    >>> residuals=[]
    >>> x = ml.solve(b, tol=1e-8, residuals=residuals)
    >>> # Set all levels to use three iterations of gauss_seidel's defaults
    >>> smoothers = ('gauss_seidel', {'iterations' : 3})
    >>> change_smoothers(ml, presmoother=smoothers, postsmoother=None)
    >>> residuals=[]
    >>> x = ml.solve(b, tol=1e-8, residuals=residuals)
    >>> # Set level 0 to use gauss_seidel's defaults, and all
    >>> # subsequent levels to use 5 iterations of cgnr
    >>> smoothers = ['gauss_seidel', ('cgnr', {'maxiter' : 5})]
    >>> change_smoothers(ml, presmoother=smoothers, postsmoother=smoothers)
    >>> residuals=[]
    >>> x = ml.solve(b, tol=1e-8, residuals=residuals)

    """
    ml.symmetric_smoothing = True

    # interpret arguments into list
    if isinstance(presmoother, (str, tuple)) or (presmoother is None):
        presmoother = [presmoother]
    elif not isinstance(presmoother, list):
        raise ValueError('Unrecognized presmoother')

    if isinstance(postsmoother, (str, tuple)) or (postsmoother is None):
        postsmoother = [postsmoother]
    elif not isinstance(postsmoother, list):
        raise ValueError('Unrecognized postsmoother')

    # set ml.levels[i].presmoother = presmoother[i],
    #     ml.levels[i].postsmoother = postsmoother[i]
    fn1 = None      # Predefine to keep scope beyond first loop
    fn2 = None
    kwargs1 = {}
    kwargs2 = {}
    min_len = min(len(presmoother), len(postsmoother), len(ml.levels[:-1]))
    # same = (len(presmoother) == len(postsmoother))
    for i in range(0, min_len):
        # unpack presmoother[i]
        fn1, kwargs1 = _unpack_arg(presmoother[i])
        # get function handle
        try:
            setup_presmoother = _setup_call(str(fn1).lower())
        except NameError as e:
            raise NameError(f'Invalid presmoother method: {fn1}') from e

        ml.levels[i].presmoother = setup_presmoother(ml.levels[i], **kwargs1)

        # unpack postsmoother[i]
        fn2, kwargs2 = _unpack_arg(postsmoother[i])
        # get function handle
        try:
            setup_postsmoother = _setup_call(str(fn2).lower())
        except NameError as e:
            raise NameError(f'Invalid postsmoother method: {fn2}') from e
        ml.levels[i].postsmoother = setup_postsmoother(ml.levels[i], **kwargs2)

        # Check if symmetric smoothing scheme
        if 'iterations' in kwargs1:
            it1 = kwargs1['iterations']
        else:
            it1 = DEFAULT_NITER

        if 'iterations' in kwargs2:
            it2 = kwargs2['iterations']
        else:
            it2 = DEFAULT_NITER

        if (fn1 != fn2) or (it1 != it2):
            ml.symmetric_smoothing = False
        elif fn1 not in SYMMETRIC_RELAXATION:
            if 'sweep' in kwargs1:
                sweep1 = kwargs1['sweep']
            else:
                sweep1 = DEFAULT_SWEEP

            if 'sweep' in kwargs2:
                sweep2 = kwargs2['sweep']
            else:
                sweep2 = DEFAULT_SWEEP

            if (sweep1, sweep2) not in (('forward', 'backward'),
                                        ('backward', 'forward'),
                                        ('symmetric', 'symmetric')):
                ml.symmetric_smoothing = False

    if len(presmoother) < len(postsmoother):
        mid_len = min(len(postsmoother), len(ml.levels[:-1]))
        for i in range(min_len, mid_len):
            # Set up presmoother
            ml.levels[i].presmoother = setup_presmoother(ml.levels[i], **kwargs1)

            # unpack postsmoother[i]
            fn2, kwargs2 = _unpack_arg(postsmoother[i])
            # get function handle
            try:
                setup_postsmoother = _setup_call(str(fn2).lower())
            except NameError as e:
                raise NameError(f'Invalid postsmoother method: {fn2}') from e

            ml.levels[i].postsmoother = setup_postsmoother(ml.levels[i], **kwargs2)

            # Check if symmetric smoothing scheme
            if 'iterations' in kwargs1:
                it1 = kwargs1['iterations']
            else:
                it1 = DEFAULT_NITER

            if 'iterations' in kwargs2:
                it2 = kwargs2['iterations']
            else:
                it2 = DEFAULT_NITER

            if (fn1 != fn2) or (it1 != it2):
                ml.symmetric_smoothing = False
            elif fn1 not in SYMMETRIC_RELAXATION:
                if 'sweep' in kwargs1:
                    sweep1 = kwargs1['sweep']
                else:
                    sweep1 = DEFAULT_SWEEP

                if 'sweep' in kwargs2:
                    sweep2 = kwargs2['sweep']
                else:
                    sweep2 = DEFAULT_SWEEP

                if (sweep1, sweep2) not in (('forward', 'backward'),
                                            ('backward', 'forward'),
                                            ('symmetric', 'symmetric')):
                    ml.symmetric_smoothing = False

    elif len(presmoother) > len(postsmoother):
        mid_len = min(len(presmoother), len(ml.levels[:-1]))
        for i in range(min_len, mid_len):
            # unpack presmoother[i]
            fn1, kwargs1 = _unpack_arg(presmoother[i])
            # get function handle
            try:
                setup_presmoother = _setup_call(str(fn1).lower())
            except NameError as e:
                raise NameError(f'Invalid presmoother method: {fn1}') from e

            ml.levels[i].presmoother = setup_presmoother(ml.levels[i], **kwargs1)

            # Set up postsmoother
            ml.levels[i].postsmoother = setup_postsmoother(ml.levels[i], **kwargs2)

            # Check if symmetric smoothing scheme
            if 'iterations' in kwargs1:
                it1 = kwargs1['iterations']
            else:
                it1 = DEFAULT_NITER

            if 'iterations' in kwargs2:
                it2 = kwargs2['iterations']
            else:
                it2 = DEFAULT_NITER

            if (fn1 != fn2) or (it1 != it2):
                ml.symmetric_smoothing = False
            elif fn1 not in SYMMETRIC_RELAXATION:
                if 'sweep' in kwargs1:
                    sweep1 = kwargs1['sweep']
                else:
                    sweep1 = DEFAULT_SWEEP

                if 'sweep' in kwargs2:
                    sweep2 = kwargs2['sweep']
                else:
                    sweep2 = DEFAULT_SWEEP

                if (sweep1, sweep2) not in (('forward', 'backward'),
                                            ('backward', 'forward'),
                                            ('symmetric', 'symmetric')):
                    ml.symmetric_smoothing = False

    else:
        mid_len = min_len

    # Fill in remaining levels
    for i in range(mid_len, len(ml.levels[:-1])):
        ml.levels[i].presmoother = setup_presmoother(ml.levels[i], **kwargs1)
        ml.levels[i].postsmoother = setup_postsmoother(ml.levels[i], **kwargs2)
def change_smoothers ( ml , presmoother , postsmoother ) : ml . symmetric_smoothing = True # interpret arguments into list if isinstance ( presmoother , ( str , tuple ) ) or ( presmoother is None ) : presmoother = [ presmoother ] elif not isinstance ( presmoother , list ) : raise ValueError ( 'Unrecognized presmoother' ) if isinstance ( postsmoother , ( str , tuple ) ) or ( postsmoother is None ) : postsmoother = [ postsmoother ] elif not isinstance ( postsmoother , list ) : raise ValueError ( 'Unrecognized postsmoother' ) # set ml.levels[i].presmoother = presmoother[i], #     ml.levels[i].postsmoother = postsmoother[i] fn1 = None # Predefine to keep scope beyond first loop fn2 = None kwargs1 = { } kwargs2 = { } min_len = min ( len ( presmoother ) , len ( postsmoother ) , len ( ml . levels [ : - 1 ] ) ) # same = (len(presmoother) == len(postsmoother)) for i in range ( 0 , min_len ) : # unpack presmoother[i] fn1 , kwargs1 = _unpack_arg ( presmoother [ i ] ) # get function handle try : setup_presmoother = _setup_call ( str ( fn1 ) . lower ( ) ) except NameError as e : raise NameError ( f'Invalid presmoother method: {fn1}' ) from e ml . levels [ i ] . presmoother = setup_presmoother ( ml . levels [ i ] , * * kwargs1 ) # unpack postsmoother[i] fn2 , kwargs2 = _unpack_arg ( postsmoother [ i ] ) # get function handle try : setup_postsmoother = _setup_call ( str ( fn2 ) . lower ( ) ) except NameError as e : raise NameError ( f'Invalid postsmoother method: {fn2}' ) from e ml . levels [ i ] . postsmoother = setup_postsmoother ( ml . levels [ i ] , * * kwargs2 ) # Check if symmetric smoothing scheme if 'iterations' in kwargs1 : it1 = kwargs1 [ 'iterations' ] else : it1 = DEFAULT_NITER if 'iterations' in kwargs2 : it2 = kwargs2 [ 'iterations' ] else : it2 = DEFAULT_NITER if ( fn1 != fn2 ) or ( it1 != it2 ) : ml . symmetric_smoothing = False elif fn1 not in SYMMETRIC_RELAXATION : if 'sweep' in kwargs1 : sweep1 = kwargs1 [ 'sweep' ] else : sweep1 = DEFAULT_SWEEP if 'sweep' in kwargs2 : sweep2 = kwargs2 [ 'sweep' ] else : sweep2 = DEFAULT_SWEEP if ( sweep1 , sweep2 ) not in ( ( 'forward' , 'backward' ) , ( 'backward' , 'forward' ) , ( 'symmetric' , 'symmetric' ) ) : ml . symmetric_smoothing = False if len ( presmoother ) < len ( postsmoother ) : mid_len = min ( len ( postsmoother ) , len ( ml . levels [ : - 1 ] ) ) for i in range ( min_len , mid_len ) : # Set up presmoother ml . levels [ i ] . presmoother = setup_presmoother ( ml . levels [ i ] , * * kwargs1 ) # unpack postsmoother[i] fn2 , kwargs2 = _unpack_arg ( postsmoother [ i ] ) # get function handle try : setup_postsmoother = _setup_call ( str ( fn2 ) . lower ( ) ) except NameError as e : raise NameError ( f'Invalid postsmoother method: {fn2}' ) from e ml . levels [ i ] . postsmoother = setup_postsmoother ( ml . levels [ i ] , * * kwargs2 ) # Check if symmetric smoothing scheme if 'iterations' in kwargs1 : it1 = kwargs1 [ 'iterations' ] else : it1 = DEFAULT_NITER if 'iterations' in kwargs2 : it2 = kwargs2 [ 'iterations' ] else : it2 = DEFAULT_NITER if ( fn1 != fn2 ) or ( it1 != it2 ) : ml . symmetric_smoothing = False elif fn1 not in SYMMETRIC_RELAXATION : if 'sweep' in kwargs1 : sweep1 = kwargs1 [ 'sweep' ] else : sweep1 = DEFAULT_SWEEP if 'sweep' in kwargs2 : sweep2 = kwargs2 [ 'sweep' ] else : sweep2 = DEFAULT_SWEEP if ( sweep1 , sweep2 ) not in ( ( 'forward' , 'backward' ) , ( 'backward' , 'forward' ) , ( 'symmetric' , 'symmetric' ) ) : ml . symmetric_smoothing = False elif len ( presmoother ) > len ( postsmoother ) : mid_len = min ( len ( presmoother ) , len ( ml . levels [ : - 1 ] ) ) for i in range ( min_len , mid_len ) : # unpack presmoother[i] fn1 , kwargs1 = _unpack_arg ( presmoother [ i ] ) # get function handle try : setup_presmoother = _setup_call ( str ( fn1 ) . lower ( ) ) except NameError as e : raise NameError ( f'Invalid presmoother method: {fn1}' ) from e ml . levels [ i ] . presmoother = setup_presmoother ( ml . levels [ i ] , * * kwargs1 ) # Set up postsmoother ml . levels [ i ] . postsmoother = setup_postsmoother ( ml . levels [ i ] , * * kwargs2 ) # Check if symmetric smoothing scheme if 'iterations' in kwargs1 : it1 = kwargs1 [ 'iterations' ] else : it1 = DEFAULT_NITER if 'iterations' in kwargs2 : it2 = kwargs2 [ 'iterations' ] else : it2 = DEFAULT_NITER if ( fn1 != fn2 ) or ( it1 != it2 ) : ml . symmetric_smoothing = False elif fn1 not in SYMMETRIC_RELAXATION : if 'sweep' in kwargs1 : sweep1 = kwargs1 [ 'sweep' ] else : sweep1 = DEFAULT_SWEEP if 'sweep' in kwargs2 : sweep2 = kwargs2 [ 'sweep' ] else : sweep2 = DEFAULT_SWEEP if ( sweep1 , sweep2 ) not in ( ( 'forward' , 'backward' ) , ( 'backward' , 'forward' ) , ( 'symmetric' , 'symmetric' ) ) : ml . symmetric_smoothing = False else : mid_len = min_len # Fill in remaining levels for i in range ( mid_len , len ( ml . levels [ : - 1 ] ) ) : ml . levels [ i ] . presmoother = setup_presmoother ( ml . levels [ i ] , * * kwargs1 ) ml . levels [ i ] . postsmoother = setup_postsmoother ( ml . levels [ i ] , * * kwargs2 )