예제 #1
0
파일: test_log.py 프로젝트: xiaoyao79/sfepy
    def test_log_create(self):
        from sfepy.base.log import Log

        log = Log([['x^3']],
                  plot_kwargs=[{
                      'color': 'b',
                      'ls': '',
                      'marker': 'o'
                  }],
                  yscales=['linear'],
                  xlabels=['x'],
                  ylabels=['a cubic function'],
                  is_plot=False,
                  aggregate=0,
                  sleep=0.0,
                  log_filename=self.log_filename,
                  formats=[['{:.5e}']])

        for x in nm.linspace(0, 1, 11):
            log(x**3, x=[x])
            if nm.allclose(x, 0.5):
                log.plot_vlines([0], color='g', linewidth=2)

        log(finished=True)

        return True
예제 #2
0
def main():
    cwd = os.path.split(os.path.join(os.getcwd(), __file__))[0]

    log = Log((['sin(x) + i sin(x**2)', 'cos(x)'], ['exp(x)']),
              yscales=['linear', 'log'],
              xlabels=['angle', None],
              ylabels=[None, 'a function'],
              aggregate=1000,
              sleep=0.05,
              log_filename=os.path.join(cwd, 'live_plot.log'))
    log2 = Log([['x^3']],
               yscales=['linear'],
               xlabels=['x'],
               ylabels=['a cubic function'],
               aggregate=1000,
               sleep=0.05,
               log_filename=os.path.join(cwd, 'live_plot2.log'),
               formats=[['{:.5e}']])

    added = 0
    for x in nm.linspace(0, 4.0 * nm.pi, 200):
        output('x: ', x)

        if x < (2.0 * nm.pi):
            log(nm.sin(x) + 1j * nm.sin(x**2),
                nm.cos(x),
                nm.exp(x),
                x=[x, None])

        else:
            if added:
                log(nm.sin(x) + 1j * nm.sin(x**2),
                    nm.cos(x),
                    nm.exp(x),
                    x**2,
                    x=[x, None, x])
            else:
                log.plot_vlines(color='r', linewidth=2)
                log.add_group(['x^2'],
                              yscale='linear',
                              xlabel='new x',
                              ylabel='square',
                              formats=['%+g'])
            added += 1

        if (added == 20) or (added == 50):
            log.plot_vlines([2], color='g', linewidth=2)

        log2(x * x * x, x=[x])

    print(log)
    print(log2)
    pause()

    log(finished=True)
    log2(finished=True)
예제 #3
0
def main():
    cwd = os.path.split(os.path.join(os.getcwd(), __file__))[0]

    log = Log(
        (["sin(x)", "cos(x)"], ["exp(x)"]),
        yscales=["linear", "log"],
        xlabels=["angle", None],
        ylabels=[None, "a function"],
        log_filename=os.path.join(cwd, "live_plot.log"),
    )
    log2 = Log(
        [["x^3"]],
        yscales=["linear"],
        xlabels=["x"],
        ylabels=["a cubic function"],
        log_filename=os.path.join(cwd, "live_plot2.log"),
    )

    added = 0
    for x in nm.linspace(0, 4.0 * nm.pi, 200):
        output("x: ", x)

        if x < (2.0 * nm.pi):
            log(nm.sin(x), nm.cos(x), nm.exp(x), x=[x, None])

        else:
            if added:
                log(nm.sin(x), nm.cos(x), nm.exp(x), x ** 2, x=[x, None, x])
            else:
                log.plot_vlines(color="r", linewidth=2)
                log.add_group(["x^2"], "linear", "new x", "square", formats=["%+g"])
            added += 1

        if (added == 20) or (added == 50):
            log.plot_vlines([2], color="g", linewidth=2)

        log2(x * x * x, x=[x])

    print log
    print log2
    pause()

    log(finished=True)
    log2(finished=True)
예제 #4
0
파일: live_plot.py 프로젝트: rc/sfepy
def main():
    cwd = os.path.split(os.path.join(os.getcwd(), __file__))[0]

    log = Log((['sin(x) + i sin(x**2)', 'cos(x)'], ['exp(x)']),
              yscales=['linear', 'log'],
              xlabels=['angle', None], ylabels=[None, 'a function'],
              log_filename=os.path.join(cwd, 'live_plot.log'))
    log2 = Log([['x^3']],
               yscales=['linear'],
               xlabels=['x'], ylabels=['a cubic function'],
               aggregate=50, sleep=0.05,
               log_filename=os.path.join(cwd, 'live_plot2.log'),
               formats=[['{:.5e}']])

    added = 0
    for x in nm.linspace(0, 4.0 * nm.pi, 200):
        output('x: ', x)

        if x < (2.0 * nm.pi):
            log(nm.sin(x)+1j*nm.sin(x**2), nm.cos(x), nm.exp(x), x=[x, None])

        else:
            if added:
                log(nm.sin(x)+1j*nm.sin(x**2), nm.cos(x), nm.exp(x), x**2,
                    x=[x, None, x])
            else:
                log.plot_vlines(color='r', linewidth=2)
                log.add_group(['x^2'], yscale='linear', xlabel='new x',
                              ylabel='square', formats=['%+g'])
            added += 1

        if (added == 20) or (added == 50):
            log.plot_vlines([2], color='g', linewidth=2)

        log2(x*x*x, x=[x])

    print(log)
    print(log2)
    pause()

    log(finished=True)
    log2(finished=True)
예제 #5
0
파일: oseen.py 프로젝트: snilek/sfepy
class Oseen( NonlinearSolver ):
    name = 'nls.oseen'

    @staticmethod
    def process_conf(conf, kwargs):
        """
        Missing items are set to default values.

        Example configuration, all items::

            solver_1 = {
                'name' : 'oseen',
                'kind' : 'nls.oseen',

                'needs_problem_instance' : True,
                'stabil_mat' : 'stabil',

                'adimensionalize' : False,
                'check_navier_stokes_rezidual' : False,

                'i_max'      : 10,
                'eps_a'      : 1e-8,
                'eps_r'      : 1.0,
                'macheps'    : 1e-16,
                'lin_red'    : 1e-2, # Linear system error < (eps_a * lin_red).
                'is_plot'    : False,
                'log'        : {'text' : 'oseen_log.txt',
                                'plot' : 'oseen_log.png'},
            }
        """
        get = make_get_conf(conf, kwargs)
        common = NonlinearSolver.process_conf(conf)

        # Compulsory.
        needs_problem_instance = get('needs_problem_instance', True)
        if not needs_problem_instance:
            msg = 'set solver option "needs_problem_instance" to True!'
            raise ValueError(msg)

        stabil_mat = get('stabil_mat', None, 'missing "stabil_mat" in options!')

        # With defaults.
        adimensionalize = get('adimensionalize', False)
        if adimensionalize:
            raise NotImplementedError

        check = get('check_navier_stokes_rezidual', False)

        log = get_logging_conf(conf)
        log = Struct(name='log_conf', **log)
        is_any_log = (log.text is not None) or (log.plot is not None)

        return Struct(needs_problem_instance=needs_problem_instance,
                      stabil_mat=stabil_mat,
                      adimensionalize=adimensionalize,
                      check_navier_stokes_rezidual=check,
                      i_max=get('i_max', 1),
                      eps_a=get('eps_a', 1e-10),
                      eps_r=get('eps_r', 1.0),
                      macheps=get('macheps', nm.finfo(nm.float64).eps),
                      lin_red=get('lin_red', 1.0),
                      lin_precision=get('lin_precision', None),
                      is_plot=get('is_plot', False),
                      log=log,
                      is_any_log=is_any_log) + common

    def __init__( self, conf, **kwargs ):
        NonlinearSolver.__init__( self, conf, **kwargs )

        conf = self.conf
        if conf.is_any_log:
            self.log = Log([[r'$||r||$'], ['iteration'],
                            [r'$\gamma$', r'$\max(\delta)$', r'$\max(\tau)$']],
                           xlabels=['', '', 'all iterations'],
                           ylabels=[r'$||r||$', 'iteration', 'stabilization'],
                           yscales=['log', 'linear', 'log'],
                           is_plot=conf.log.plot is not None,
                           log_filename=conf.log.text,
                           formats=[['%.8e'], ['%d'],
                                    ['%.8e', '%.8e', '%.8e']])

        else:
            self.log = None

    def __call__( self, vec_x0, conf = None, fun = None, fun_grad = None,
                  lin_solver = None, status = None, problem = None ):
        """
        Oseen solver is problem-specific - it requires a Problem instance.
        """
        import sfepy.base.plotutils as plu

        conf = get_default( conf, self.conf )
        fun = get_default( fun, self.fun )
        fun_grad = get_default( fun_grad, self.fun_grad )
        lin_solver = get_default( lin_solver, self.lin_solver )
        status = get_default( status, self.status )
        problem = get_default( problem, self.problem )

        if problem is None:
            msg = 'set solver option "needs_problem_instance" to True!'
            raise ValueError(msg)

        time_stats = {}

        stabil = problem.get_materials()[conf.stabil_mat]
        ns, ii = stabil.function.function.get_maps()

        variables = problem.get_variables()
        update_var = variables.set_data_from_state
        make_full_vec = variables.make_full_vec

        print 'problem size:'
        print '    velocity: %s' % ii['us']
        print '    pressure: %s' % ii['ps']

        vec_x = vec_x0.copy()
        vec_x_prev = vec_x0.copy()
        vec_dx = None

        if self.log is not None:
            self.log.plot_vlines(color='r', linewidth=1.0)

        err0 = -1.0
        it = 0
        while 1:
            vec_x_prev_f = make_full_vec( vec_x_prev )
            update_var( ns['b'], vec_x_prev_f, ns['u'] )

            vec_b = vec_x_prev_f[ii['u']]
            b_norm = nla.norm( vec_b, nm.inf )
            print '|b|_max: %.12e' % b_norm

            vec_x_f = make_full_vec( vec_x )
            vec_u = vec_x_f[ii['u']]
            u_norm = nla.norm( vec_u, nm.inf )
            print '|u|_max: %.2e' % u_norm

            stabil.function.set_extra_args(b_norm=b_norm)
            stabil.time_update(None, problem.equations, mode='force',
                               problem=problem)
            max_pars = stabil.reduce_on_datas( lambda a, b: max( a, b.max() ) )
            print 'stabilization parameters:'
            print '                   gamma: %.12e' % max_pars[ns['gamma']]
            print '            max( delta ): %.12e' % max_pars[ns['delta']]
            print '              max( tau ): %.12e' % max_pars[ns['tau']]

            if (not are_close( b_norm, 1.0 )) and conf.adimensionalize:
                adimensionalize = True
            else:
                adimensionalize = False

            tt = time.clock()
            try:
                vec_r = fun( vec_x )
            except ValueError:
                ok = False
            else:
                ok = True
            time_stats['rezidual'] = time.clock() - tt
            if ok:
                err = nla.norm( vec_r )
                if it == 0:
                    err0 = err;
                else:
                    err += nla.norm( vec_dx )
            else: # Failure.
                output( 'rezidual computation failed for iter %d!' % it )
                raise RuntimeError( 'giving up...' )

            if self.log is not None:
                self.log(err, it,
                         max_pars[ns['gamma']], max_pars[ns['delta']],
                         max_pars[ns['tau']])

            condition = conv_test( conf, it, err, err0 )
            if condition >= 0:
                break

            if adimensionalize:
                output( 'adimensionalizing' )
                ## mat.viscosity = viscosity / b_norm
                ## vec_r[indx_us] /= b_norm

            tt = time.clock()
            try:
                mtx_a = fun_grad( vec_x )
            except ValueError:
                ok = False
            else:
                ok = True
            time_stats['matrix'] = time.clock() - tt
            if not ok:
                raise RuntimeError( 'giving up...' )

            tt = time.clock() 
            vec_dx = lin_solver(vec_r, x0=vec_x, mtx=mtx_a)
            time_stats['solve'] = time.clock() - tt

            vec_e = mtx_a * vec_dx - vec_r
            lerr = nla.norm( vec_e )
            if lerr > (conf.eps_a * conf.lin_red):
                output( 'linear system not solved! (err = %e)' % lerr )

            if adimensionalize:
                output( 'restoring pressure...' )
                ## vec_dx[indx_ps] *= b_norm

            dx_norm = nla.norm( vec_dx )
            output( '||dx||: %.2e' % dx_norm )

            for kv in time_stats.iteritems():
                output( '%10s: %7.2f [s]' % kv )

            vec_x_prev = vec_x.copy()
            vec_x -= vec_dx

            if conf.is_plot:
                plu.plt.ion()
                plu.plt.gcf().clear()
                plu.plt.subplot( 2, 2, 1 )
                plu.plt.plot( vec_x_prev )
                plu.plt.ylabel( r'$x_{i-1}$' )
                plu.plt.subplot( 2, 2, 2 )
                plu.plt.plot( vec_r )
                plu.plt.ylabel( r'$r$' )
                plu.plt.subplot( 2, 2, 4 )
                plu.plt.plot( vec_dx )
                plu.plt.ylabel( r'$\_delta x$' )
                plu.plt.subplot( 2, 2, 3 )
                plu.plt.plot( vec_x )
                plu.plt.ylabel( r'$x_i$' )
                plu.plt.draw()
                plu.plt.ioff()
                pause()

            it += 1

        if conf.check_navier_stokes_rezidual:

            t1 = '+ dw_div_grad.%s.%s( %s.viscosity, %s, %s )' \
                 % (ns['i2'], ns['omega'], ns['fluid'], ns['v'], ns['u'])
##             t2 = '+ dw_lin_convect.%s( %s, %s, %s )' % (ns['omega'],
##                                                         ns['v'], b_name, ns['u'])
            t2 = '+ dw_convect.%s.%s( %s, %s )' % (ns['i2'], ns['omega'],
                                                   ns['v'], ns['u'])
            t3 = '- dw_stokes.%s.%s( %s, %s )' % (ns['i1'], ns['omega'],
                                                  ns['v'], ns['p'])
            t4 = 'dw_stokes.%s.%s( %s, %s )' % (ns['i1'], ns['omega'],
                                                ns['u'], ns['q'])
            equations = {
                'balance' : ' '.join( (t1, t2, t3) ),
                'incompressibility' : t4,
            }
            problem.set_equations( equations )
            try:
                vec_rns0 = fun( vec_x0 )
                vec_rns = fun( vec_x )
            except ValueError:
                ok = False
            else:
                ok = True
            if not ok:
                print 'Navier-Stokes rezidual computation failed!'
                err_ns = err_ns0 = None
            else:
                err_ns0 = nla.norm( vec_rns0 )
                err_ns = nla.norm( vec_rns )
            print 'Navier-Stokes rezidual0: %.8e' % err_ns0
            print 'Navier-Stokes rezidual : %.8e' % err_ns
            print 'b - u: %.8e' % nla.norm( vec_b - vec_u )
            print condition
    ##         print vec_rns - vec_rns1
            plu.plt.ion()
            plu.plt.gcf().clear()
            plu.plt.plot( vec_rns )
    ##         plu.plt.gcf().clear()
    ##         plu.plt.plot( vec_rns1 )
            plu.plt.draw()
            plu.plt.ioff()
            pause()
        else:
            err_ns = None

        if status is not None:
            status['time_stats'] = time_stats
            status['err0'] = err0
            status['err'] = err
            status['err_ns'] = err_ns
            status['condition'] = condition

        if conf.log.plot is not None:
            if self.log is not None:
                self.log(save_figure=conf.log.plot)
                
        return vec_x
예제 #6
0
파일: nls.py 프로젝트: cheon7886/sfepy
class Newton(NonlinearSolver):
    r"""
    Solves a nonlinear system :math:`f(x) = 0` using the Newton method with
    backtracking line-search, starting with an initial guess :math:`x^0`.
    """
    name = 'nls.newton'

    __metaclass__ = SolverMeta

    _parameters = [
        ('i_max', 'int', 1, False,
         'The maximum number of iterations.'),
        ('eps_a', 'float', 1e-10, False,
         'The absolute tolerance for the residual, i.e. :math:`||f(x^i)||`.'),
        ('eps_r', 'float', 1.0, False,
         """The relative tolerance for the residual, i.e. :math:`||f(x^i)|| /
            ||f(x^0)||`."""),
        ('eps_mode', "'and' or 'or'", 'and', False,
         """The logical operator to use for combining the absolute and relative
            tolerances."""),
        ('macheps', 'float', nm.finfo(nm.float64).eps, False,
         'The float considered to be machine "zero".'),
        ('lin_red', 'float', 1.0, False,
         """The linear system solution error should be smaller than (`eps_a` *
            `lin_red`), otherwise a warning is printed."""),
        ('lin_precision', 'float or None', None, False,
         """If not None, the linear system solution tolerances are set in each
            nonlinear iteration relative to the current residual norm by the
            `lin_precision` factor. Ignored for direct linear solvers."""),
        ('ls_on', 'float', 0.99999, False,
         """Start the backtracking line-search by reducing the step, if
            :math:`||f(x^i)|| / ||f(x^{i-1})||` is larger than `ls_on`."""),
        ('ls_red', '0.0 < float < 1.0', 0.1, False,
         'The step reduction factor in case of correct residual assembling.'),
        ('ls_red_warp', '0.0 < float < 1.0', 0.001, False,
         """The step reduction factor in case of failed residual assembling
            (e.g. the "warp violation" error caused by a negative volume
            element resulting from too large deformations)."""),
        ('ls_min', '0.0 < float < 1.0', 1e-5, False,
         'The minimum step reduction factor.'),
        ('give_up_warp', 'bool', False, False,
         'If True, abort on the "warp violation" error.'),
        ('check', '0, 1 or 2', 0, False,
         """If >= 1, check the tangent matrix using finite differences.  If 2,
            plot the resulting sparsity patterns."""),
        ('delta', 'float', 1e-6, False,
         r"""If `check >= 1`, the finite difference matrix is taken as
            :math:`A_{ij} = \frac{f_i(x_j + \delta) - f_i(x_j - \delta)}{2
            \delta}`."""),
        ('log', 'dict or None', None, False,
         """If not None, log the convergence according to the configuration in
            the following form: ``{'text' : 'log.txt', 'plot' : 'log.pdf'}``.
            Each of the dict items can be None."""),
        ('is_linear', 'bool', False, False,
         'If True, the problem is considered to be linear.'),
    ]

    def __init__(self, conf, **kwargs):
        NonlinearSolver.__init__(self, conf, **kwargs)

        conf = self.conf

        log = get_logging_conf(conf)
        conf.log = log = Struct(name='log_conf', **log)
        conf.is_any_log = (log.text is not None) or (log.plot is not None)

        if conf.is_any_log:
            self.log = Log([[r'$||r||$'], ['iteration']],
                           xlabels=['', 'all iterations'],
                           ylabels=[r'$||r||$', 'iteration'],
                           yscales=['log', 'linear'],
                           is_plot=conf.log.plot is not None,
                           log_filename=conf.log.text,
                           formats=[['%.8e'], ['%d']])

        else:
            self.log = None

    def __call__(self, vec_x0, conf=None, fun=None, fun_grad=None,
                 lin_solver=None, iter_hook=None, status=None):
        """
        Nonlinear system solver call.

        Solves a nonlinear system :math:`f(x) = 0` using the Newton method with
        backtracking line-search, starting with an initial guess :math:`x^0`.

        Parameters
        ----------
        vec_x0 : array
            The initial guess vector :math:`x_0`.
        conf : Struct instance, optional
            The solver configuration parameters,
        fun : function, optional
            The function :math:`f(x)` whose zero is sought - the residual.
        fun_grad : function, optional
            The gradient of :math:`f(x)` - the tangent matrix.
        lin_solver : LinearSolver instance, optional
            The linear solver for each nonlinear iteration.
        iter_hook : function, optional
            User-supplied function to call before each iteration.
        status : dict-like, optional
            The user-supplied object to hold convergence statistics.

        Notes
        -----
        * The optional parameters except `iter_hook` and `status` need
          to be given either here or upon `Newton` construction.
        * Setting `conf.is_linear == True` means a pre-assembled and possibly
          pre-solved matrix. This is mostly useful for linear time-dependent
          problems.
        """
        conf = get_default(conf, self.conf)
        fun = get_default(fun, self.fun)
        fun_grad = get_default(fun_grad, self.fun_grad)
        lin_solver = get_default(lin_solver, self.lin_solver)
        iter_hook = get_default(iter_hook, self.iter_hook)
        status = get_default(status, self.status)

        ls_eps_a, ls_eps_r = lin_solver.get_tolerance()
        eps_a = get_default(ls_eps_a, 1.0)
        eps_r = get_default(ls_eps_r, 1.0)
        lin_red = conf.eps_a * conf.lin_red

        time_stats = {}

        vec_x = vec_x0.copy()
        vec_x_last = vec_x0.copy()
        vec_dx = None

        if self.log is not None:
            self.log.plot_vlines(color='r', linewidth=1.0)

        err = err0 = -1.0
        err_last = -1.0
        it = 0
        while 1:
            if iter_hook is not None:
                iter_hook(self, vec_x, it, err, err0)

            ls = 1.0
            vec_dx0 = vec_dx;
            while 1:
                tt = time.clock()

                try:
                    vec_r = fun(vec_x)

                except ValueError:
                    if (it == 0) or (ls < conf.ls_min):
                        output('giving up!')
                        raise

                    else:
                        ok = False

                else:
                    ok = True

                time_stats['rezidual'] = time.clock() - tt
                if ok:
                    try:
                        err = nla.norm(vec_r)
                    except:
                        output('infs or nans in the residual:', vec_r)
                        output(nm.isfinite(vec_r).all())
                        debug()

                    if self.log is not None:
                        self.log(err, it)

                    if it == 0:
                        err0 = err;
                        break
                    if err < (err_last * conf.ls_on): break
                    red = conf.ls_red;
                    output('linesearch: iter %d, (%.5e < %.5e) (new ls: %e)'
                           % (it, err, err_last * conf.ls_on, red * ls))
                else: # Failure.
                    if conf.give_up_warp:
                        output('giving up!')
                        break

                    red = conf.ls_red_warp;
                    output('rezidual computation failed for iter %d'
                           ' (new ls: %e)!' % (it, red * ls))

                if ls < conf.ls_min:
                    output('linesearch failed, continuing anyway')
                    break

                ls *= red;

                vec_dx = ls * vec_dx0;
                vec_x = vec_x_last.copy() - vec_dx
            # End residual loop.

            if self.log is not None:
                self.log.plot_vlines([1], color='g', linewidth=0.5)

            err_last = err;
            vec_x_last = vec_x.copy()

            condition = conv_test(conf, it, err, err0)
            if condition >= 0:
                break

            if (not ok) and conf.give_up_warp:
                condition = 2
                break

            tt = time.clock()
            if not conf.is_linear:
                mtx_a = fun_grad(vec_x)

            else:
                mtx_a = fun_grad('linear')

            time_stats['matrix'] = time.clock() - tt

            if conf.check:
                tt = time.clock()
                wt = check_tangent_matrix(conf, vec_x, fun, fun_grad)
                time_stats['check'] = time.clock() - tt - wt

            if conf.lin_precision is not None:
                if ls_eps_a is not None:
                    eps_a = max(err * conf.lin_precision, ls_eps_a)

                elif ls_eps_r is not None:
                    eps_r = max(conf.lin_precision, ls_eps_r)

                lin_red = max(eps_a, err * eps_r)

            if conf.verbose:
                output('solving linear system...')

            tt = time.clock()
            vec_dx = lin_solver(vec_r, x0=vec_x,
                                eps_a=eps_a, eps_r=eps_r, mtx=mtx_a)
            time_stats['solve'] = time.clock() - tt

            if conf.verbose:
                output('...done')

            for kv in time_stats.iteritems():
                output('%10s: %7.2f [s]' % kv)

            vec_e = mtx_a * vec_dx - vec_r
            lerr = nla.norm(vec_e)
            if lerr > lin_red:
                output('warning: linear system solution precision is lower')
                output('then the value set in solver options! (err = %e < %e)'
                       % (lerr, lin_red))

            vec_x -= vec_dx
            it += 1

        if status is not None:
            status['time_stats'] = time_stats
            status['err0'] = err0
            status['err'] = err
            status['n_iter'] = it
            status['condition'] = condition

        if conf.log.plot is not None:
            if self.log is not None:
                self.log(save_figure=conf.log.plot)

        return vec_x
예제 #7
0
파일: nls.py 프로젝트: lokik/sfepy
class Newton(NonlinearSolver):
    r"""
    Solves a nonlinear system :math:`f(x) = 0` using the Newton method with
    backtracking line-search, starting with an initial guess :math:`x^0`.
    """
    name = 'nls.newton'

    __metaclass__ = SolverMeta

    _parameters = [
        ('i_max', 'int', 1, False, 'The maximum number of iterations.'),
        ('eps_a', 'float', 1e-10, False,
         'The absolute tolerance for the residual, i.e. :math:`||f(x^i)||`.'),
        ('eps_r', 'float', 1.0, False,
         """The relative tolerance for the residual, i.e. :math:`||f(x^i)|| /
            ||f(x^0)||`."""),
        ('eps_mode', "'and' or 'or'", 'and', False,
         """The logical operator to use for combining the absolute and relative
            tolerances."""),
        ('macheps', 'float', nm.finfo(nm.float64).eps, False,
         'The float considered to be machine "zero".'),
        ('lin_red', 'float', 1.0, False,
         """The linear system solution error should be smaller than (`eps_a` *
            `lin_red`), otherwise a warning is printed."""),
        ('lin_precision', 'float or None', None, False,
         """If not None, the linear system solution tolerances are set in each
            nonlinear iteration relative to the current residual norm by the
            `lin_precision` factor. Ignored for direct linear solvers."""),
        ('ls_on', 'float', 0.99999, False,
         """Start the backtracking line-search by reducing the step, if
            :math:`||f(x^i)|| / ||f(x^{i-1})||` is larger than `ls_on`."""),
        ('ls_red', '0.0 < float < 1.0', 0.1, False,
         'The step reduction factor in case of correct residual assembling.'),
        ('ls_red_warp', '0.0 < float < 1.0', 0.001, False,
         """The step reduction factor in case of failed residual assembling
            (e.g. the "warp violation" error caused by a negative volume
            element resulting from too large deformations)."""),
        ('ls_min', '0.0 < float < 1.0', 1e-5, False,
         'The minimum step reduction factor.'),
        ('give_up_warp', 'bool', False, False,
         'If True, abort on the "warp violation" error.'),
        ('check', '0, 1 or 2', 0, False,
         """If >= 1, check the tangent matrix using finite differences.  If 2,
            plot the resulting sparsity patterns."""),
        ('delta', 'float', 1e-6, False,
         r"""If `check >= 1`, the finite difference matrix is taken as
            :math:`A_{ij} = \frac{f_i(x_j + \delta) - f_i(x_j - \delta)}{2
            \delta}`."""),
        ('log', 'dict or None', None, False,
         """If not None, log the convergence according to the configuration in
            the following form: ``{'text' : 'log.txt', 'plot' : 'log.pdf'}``.
            Each of the dict items can be None."""),
        ('is_linear', 'bool', False, False,
         'If True, the problem is considered to be linear.'),
    ]

    def __init__(self, conf, **kwargs):
        NonlinearSolver.__init__(self, conf, **kwargs)

        conf = self.conf

        log = get_logging_conf(conf)
        conf.log = log = Struct(name='log_conf', **log)
        conf.is_any_log = (log.text is not None) or (log.plot is not None)

        if conf.is_any_log:
            self.log = Log([[r'$||r||$'], ['iteration']],
                           xlabels=['', 'all iterations'],
                           ylabels=[r'$||r||$', 'iteration'],
                           yscales=['log', 'linear'],
                           is_plot=conf.log.plot is not None,
                           log_filename=conf.log.text,
                           formats=[['%.8e'], ['%d']])

        else:
            self.log = None

    def __call__(self,
                 vec_x0,
                 conf=None,
                 fun=None,
                 fun_grad=None,
                 lin_solver=None,
                 iter_hook=None,
                 status=None):
        """
        Nonlinear system solver call.

        Solves a nonlinear system :math:`f(x) = 0` using the Newton method with
        backtracking line-search, starting with an initial guess :math:`x^0`.

        Parameters
        ----------
        vec_x0 : array
            The initial guess vector :math:`x_0`.
        conf : Struct instance, optional
            The solver configuration parameters,
        fun : function, optional
            The function :math:`f(x)` whose zero is sought - the residual.
        fun_grad : function, optional
            The gradient of :math:`f(x)` - the tangent matrix.
        lin_solver : LinearSolver instance, optional
            The linear solver for each nonlinear iteration.
        iter_hook : function, optional
            User-supplied function to call before each iteration.
        status : dict-like, optional
            The user-supplied object to hold convergence statistics.

        Notes
        -----
        * The optional parameters except `iter_hook` and `status` need
          to be given either here or upon `Newton` construction.
        * Setting `conf.is_linear == True` means a pre-assembled and possibly
          pre-solved matrix. This is mostly useful for linear time-dependent
          problems.
        """
        conf = get_default(conf, self.conf)
        fun = get_default(fun, self.fun)
        fun_grad = get_default(fun_grad, self.fun_grad)
        lin_solver = get_default(lin_solver, self.lin_solver)
        iter_hook = get_default(iter_hook, self.iter_hook)
        status = get_default(status, self.status)

        ls_eps_a, ls_eps_r = lin_solver.get_tolerance()
        eps_a = get_default(ls_eps_a, 1.0)
        eps_r = get_default(ls_eps_r, 1.0)
        lin_red = conf.eps_a * conf.lin_red

        time_stats_keys = ['residual', 'matrix', 'solve']
        time_stats = {key: 0.0 for key in time_stats_keys}

        vec_x = vec_x0.copy()
        vec_x_last = vec_x0.copy()
        vec_dx = None

        if self.log is not None:
            self.log.plot_vlines(color='r', linewidth=1.0)

        err = err0 = -1.0
        err_last = -1.0
        it = 0
        ls_status = {}
        ls_n_iter = 0
        while 1:
            if iter_hook is not None:
                iter_hook(self, vec_x, it, err, err0)

            ls = 1.0
            vec_dx0 = vec_dx
            while 1:
                tt = time.clock()

                try:
                    vec_r = fun(vec_x)

                except ValueError:
                    if (it == 0) or (ls < conf.ls_min):
                        output('giving up!')
                        raise

                    else:
                        ok = False

                else:
                    ok = True

                time_stats['residual'] = time.clock() - tt
                if ok:
                    try:
                        err = nla.norm(vec_r)
                    except:
                        output('infs or nans in the residual:', vec_r)
                        output(nm.isfinite(vec_r).all())
                        debug()

                    if self.log is not None:
                        self.log(err, it)

                    if it == 0:
                        err0 = err
                        break
                    if err < (err_last * conf.ls_on): break
                    red = conf.ls_red
                    output('linesearch: iter %d, (%.5e < %.5e) (new ls: %e)' %
                           (it, err, err_last * conf.ls_on, red * ls))
                else:  # Failure.
                    if conf.give_up_warp:
                        output('giving up!')
                        break

                    red = conf.ls_red_warp
                    output('residual computation failed for iter %d'
                           ' (new ls: %e)!' % (it, red * ls))

                if ls < conf.ls_min:
                    output('linesearch failed, continuing anyway')
                    break

                ls *= red

                vec_dx = ls * vec_dx0
                vec_x = vec_x_last.copy() - vec_dx
            # End residual loop.

            if self.log is not None:
                self.log.plot_vlines([1], color='g', linewidth=0.5)

            err_last = err
            vec_x_last = vec_x.copy()

            condition = conv_test(conf, it, err, err0)
            if condition >= 0:
                break

            if (not ok) and conf.give_up_warp:
                condition = 2
                break

            tt = time.clock()
            if not conf.is_linear:
                mtx_a = fun_grad(vec_x)

            else:
                mtx_a = fun_grad('linear')

            time_stats['matrix'] = time.clock() - tt

            if conf.check:
                tt = time.clock()
                wt = check_tangent_matrix(conf, vec_x, fun, fun_grad)
                time_stats['check'] = time.clock() - tt - wt

            if conf.lin_precision is not None:
                if ls_eps_a is not None:
                    eps_a = max(err * conf.lin_precision, ls_eps_a)

                elif ls_eps_r is not None:
                    eps_r = max(conf.lin_precision, ls_eps_r)

                lin_red = max(eps_a, err * eps_r)

            if conf.verbose:
                output('solving linear system...')

            tt = time.clock()
            vec_dx = lin_solver(vec_r,
                                x0=vec_x,
                                eps_a=eps_a,
                                eps_r=eps_r,
                                mtx=mtx_a,
                                status=ls_status)
            ls_n_iter += ls_status['n_iter']
            time_stats['solve'] = time.clock() - tt

            if conf.verbose:
                output('...done')

            for key in time_stats_keys:
                output('%10s: %7.2f [s]' % (key, time_stats[key]))

            vec_e = mtx_a * vec_dx - vec_r
            lerr = nla.norm(vec_e)
            if lerr > lin_red:
                output('warning: linear system solution precision is lower')
                output(
                    'then the value set in solver options! (err = %e < %e)' %
                    (lerr, lin_red))

            vec_x -= vec_dx
            it += 1

        if status is not None:
            status['time_stats'] = time_stats
            status['err0'] = err0
            status['err'] = err
            status['n_iter'] = it
            status['ls_n_iter'] = ls_n_iter if ls_n_iter >= 0 else -1
            status['condition'] = condition

        if conf.log.plot is not None:
            if self.log is not None:
                self.log(save_figure=conf.log.plot)

        return vec_x
예제 #8
0
class FMinSteepestDescent( OptimizationSolver ):
    name = 'opt.fmin_sd'

    def process_conf( conf ):
        """
        Missing items are set to default values.
        
        Example configuration, all items::
        
            solver_0 = {
                'name'      : 'fmin_sd',
                'kind'      : 'opt.fmin_sd',

                'i_max'      : 10,
                'eps_rd'     : 1e-5, # Relative delta of objective function
                'eps_of'     : 1e-4,
                'eps_ofg'    : 1e-8,
                'norm'      : nm.Inf,
                'ls'        : True, # Linesearch.
                'ls_method'  : 'backtracking', # 'backtracking' or 'full'
                'ls0'       : 0.25,
                'ls_red'     : 0.5,
                'ls_red_warp' : 0.1,
                'ls_on'      : 0.99999,
                'ls_min'     : 1e-5,
                'check'     : 0,
                'delta'     : 1e-6,
                'output'    : None, # 'itc'
                'log'       : {'text' : 'output/log.txt',
                               'plot' : 'output/log.png'},
                'yscales'   : ['linear', 'log', 'log', 'linear'],
            }
        """
        get = conf.get_default_attr

        i_max = get( 'i_max', 10 )
        eps_rd = get( 'eps_rd', 1e-5 )
        eps_of = get( 'eps_of', 1e-4 )
        eps_ofg = get( 'eps_ofg', 1e-8 )
        norm = get( 'norm', nm.Inf )
        ls = get( 'ls', True )
        ls_method = get( 'ls_method', 'backtracking' )
        ls0 = get( 'ls0', 0.25 )
        ls_red = get( 'ls_red', 0.5 )
        ls_red_warp = get( 'ls_red_warp', 0.1 )
        ls_on = get( 'ls_on', 0.99999 )
        ls_min = get( 'ls_min', 1e-5 )
        check = get( 'check', 0 )
        delta = get( 'delta', 1e-6)
        output = get( 'output', None )
        yscales = get( 'yscales', ['linear', 'log', 'log', 'linear'] )

        log = get_logging_conf(conf)
        log = Struct(name='log_conf', **log)
        is_any_log = (log.text is not None) or (log.plot is not None)

        common = OptimizationSolver.process_conf( conf )
        return Struct( **locals() ) + common
    process_conf = staticmethod( process_conf )

    ##
    # 17.10.2007, c
    def __init__( self, conf, **kwargs ):
        OptimizationSolver.__init__( self, conf, **kwargs )

        conf = self.conf
        if conf.is_any_log:
            self.log = Log([[r'$||\Psi||$'], [r'$||\nabla \Psi||$'],
                            [r'$\alpha$'], ['iteration']],
                           xlabels=['', '', 'all iterations', 'all iterations'],
                           yscales=conf.yscales,
                           is_plot=conf.log.plot is not None,
                           log_filename=conf.log.text,
                           formats=[['%.8e'], ['%.3e'], ['%.3e'], ['%d']])
        else:
            self.log = None

    ##
    # 19.04.2006, c
    # 20.04.2006
    # 21.04.2006
    # 26.04.2006
    # 06.06.2006
    # 07.06.2006
    # 04.09.2006
    # 21.03.2007
    # 17.10.2007, from fmin_sd()
    def __call__( self, x0, conf = None, obj_fun = None, obj_fun_grad = None,
                  status = None, obj_args = None ):
#    def fmin_sd( conf, x0, fn_of, fn_ofg, args = () ):

        conf = get_default( conf, self.conf )
        obj_fun = get_default( obj_fun, self.obj_fun )
        obj_fun_grad = get_default( obj_fun_grad, self.obj_fun_grad )
        status = get_default( status, self.status )
        obj_args = get_default( obj_args, self.obj_args )

        if conf.output:
            globals()['output'] = conf.output

        output( 'entering optimization loop...' )

        nc_of, tt_of, fn_of = wrap_function( obj_fun, obj_args )
        nc_ofg, tt_ofg, fn_ofg = wrap_function( obj_fun_grad, obj_args )

        time_stats = {'of' : tt_of, 'ofg': tt_ofg, 'check' : []}

        ofg = None

        it = 0
        xit = x0.copy()
        while 1:

            of = fn_of( xit )

            if it == 0:
                of0 = ofit0 = of_prev = of
                of_prev_prev = of + 5000.0

            if ofg is None:
                ofg = fn_ofg( xit )

            if conf.check:
                tt = time.clock()
                check_gradient( xit, ofg, fn_of, conf.delta, conf.check )
                time_stats['check'].append( time.clock() - tt )

            ofg_norm = nla.norm( ofg, conf.norm )

            ret = conv_test( conf, it, of, ofit0, ofg_norm )
            if ret >= 0:
                break
            ofit0 = of

            ##
            # Backtrack (on errors).
            alpha = conf.ls0
            can_ls = True
            while 1:
                xit2 = xit - alpha * ofg
                aux = fn_of( xit2 )

                if self.log is not None:
                    self.log(of, ofg_norm, alpha, it)

                if aux is None:
                    alpha *= conf.ls_red_warp
                    can_ls = False
                    output( 'warp: reducing step (%f)' % alpha )
                elif conf.ls and conf.ls_method == 'backtracking':
                    if aux < of * conf.ls_on: break
                    alpha *= conf.ls_red
                    output( 'backtracking: reducing step (%f)' % alpha )
                else:
                    of_prev_prev = of_prev
                    of_prev = aux
                    break

                if alpha < conf.ls_min:
                    if aux is None:
                        raise RuntimeError, 'giving up...'
                    output( 'linesearch failed, continuing anyway' )
                    break

            # These values are modified by the line search, even if it fails
            of_prev_bak = of_prev
            of_prev_prev_bak = of_prev_prev

            if conf.ls and can_ls and conf.ls_method == 'full':
                output( 'full linesearch...' )
                alpha, fc, gc, of_prev, of_prev_prev, ofg1 = \
                       linesearch.line_search(fn_of,fn_ofg,xit,
                                              -ofg,ofg,of_prev,of_prev_prev,
                                              c2=0.4)
                if alpha is None:  # line search failed -- use different one.
                    alpha, fc, gc, of_prev, of_prev_prev, ofg1 = \
                           sopt.line_search(fn_of,fn_ofg,xit,
                                            -ofg,ofg,of_prev_bak,
                                            of_prev_prev_bak)
                    if alpha is None or alpha == 0:
                        # This line search also failed to find a better solution.
                        ret = 3
                        break
                output( ' -> alpha: %.8e' % alpha )
            else:
                if conf.ls_method == 'full':
                    output( 'full linesearch off (%s and %s)' % (conf.ls,
                                                                 can_ls) )
                ofg1 = None

            if self.log is not None:
                self.log.plot_vlines(color='g', linewidth=0.5)

            xit = xit - alpha * ofg
            if ofg1 is None:
                ofg = None
            else:
                ofg = ofg1.copy()

            for key, val in time_stats.iteritems():
                if len( val ):
                    output( '%10s: %7.2f [s]' % (key, val[-1]) )

            it = it + 1

        output( 'status:               %d' % ret )
        output( 'initial value:        %.8e' % of0 )
        output( 'current value:        %.8e' % of )
        output( 'iterations:           %d' % it )
        output( 'function evaluations: %d in %.2f [s]' \
              % (nc_of[0], nm.sum( time_stats['of'] ) ) )
        output( 'gradient evaluations: %d in %.2f [s]' \
              % (nc_ofg[0], nm.sum( time_stats['ofg'] ) ) )

        if self.log is not None:
            self.log(of, ofg_norm, alpha, it)

            if conf.log.plot is not None:
                self.log(save_figure=conf.log.plot,
                         finished=True)
            else:
                self.log(finished=True)
                
        if status is not None:
            status['log'] = self.log
            status['status'] = status
            status['of0'] = of0
            status['of'] = of
            status['it'] = it
            status['nc_of'] = nc_of[0]
            status['nc_ofg'] = nc_ofg[0]
            status['time_stats'] = time_stats

        return xit
예제 #9
0
파일: oseen.py 프로젝트: LeiDai/sfepy
class Oseen(NonlinearSolver):
    name = 'nls.oseen'

    @staticmethod
    def process_conf(conf, kwargs):
        """
        Missing items are set to default values.

        Example configuration, all items::

            solver_1 = {
                'name' : 'oseen',
                'kind' : 'nls.oseen',

                'needs_problem_instance' : True,
                'stabil_mat' : 'stabil',

                'adimensionalize' : False,
                'check_navier_stokes_rezidual' : False,

                'i_max'      : 10,
                'eps_a'      : 1e-8,
                'eps_r'      : 1.0,
                'macheps'    : 1e-16,
                'lin_red'    : 1e-2, # Linear system error < (eps_a * lin_red).
                'log'        : {'text' : 'oseen_log.txt',
                                'plot' : 'oseen_log.png'},
            }
        """
        get = make_get_conf(conf, kwargs)
        common = NonlinearSolver.process_conf(conf)

        # Compulsory.
        needs_problem_instance = get('needs_problem_instance', True)
        if not needs_problem_instance:
            msg = 'set solver option "needs_problem_instance" to True!'
            raise ValueError(msg)

        stabil_mat = get('stabil_mat', None,
                         'missing "stabil_mat" in options!')

        # With defaults.
        adimensionalize = get('adimensionalize', False)
        if adimensionalize:
            raise NotImplementedError

        check = get('check_navier_stokes_rezidual', False)

        log = get_logging_conf(conf)
        log = Struct(name='log_conf', **log)
        is_any_log = (log.text is not None) or (log.plot is not None)

        return Struct(needs_problem_instance=needs_problem_instance,
                      stabil_mat=stabil_mat,
                      adimensionalize=adimensionalize,
                      check_navier_stokes_rezidual=check,
                      i_max=get('i_max', 1),
                      eps_a=get('eps_a', 1e-10),
                      eps_r=get('eps_r', 1.0),
                      macheps=get('macheps',
                                  nm.finfo(nm.float64).eps),
                      lin_red=get('lin_red', 1.0),
                      lin_precision=get('lin_precision', None),
                      log=log,
                      is_any_log=is_any_log) + common

    def __init__(self, conf, **kwargs):
        NonlinearSolver.__init__(self, conf, **kwargs)

        conf = self.conf
        if conf.is_any_log:
            self.log = Log([[r'$||r||$'], ['iteration'],
                            [r'$\gamma$', r'$\max(\delta)$', r'$\max(\tau)$']],
                           xlabels=['', '', 'all iterations'],
                           ylabels=[r'$||r||$', 'iteration', 'stabilization'],
                           yscales=['log', 'linear', 'log'],
                           log_filename=conf.log.text,
                           formats=[['%.8e'], ['%d'], ['%.8e', '%.8e',
                                                       '%.8e']])

        else:
            self.log = None

    def __call__(self,
                 vec_x0,
                 conf=None,
                 fun=None,
                 fun_grad=None,
                 lin_solver=None,
                 status=None,
                 problem=None):
        """
        Oseen solver is problem-specific - it requires a Problem instance.
        """
        conf = get_default(conf, self.conf)
        fun = get_default(fun, self.fun)
        fun_grad = get_default(fun_grad, self.fun_grad)
        lin_solver = get_default(lin_solver, self.lin_solver)
        status = get_default(status, self.status)
        problem = get_default(problem, self.problem)

        if problem is None:
            msg = 'set solver option "needs_problem_instance" to True!'
            raise ValueError(msg)

        time_stats = {}

        stabil = problem.get_materials()[conf.stabil_mat]
        ns, ii = stabil.function.function.get_maps()

        variables = problem.get_variables()
        update_var = variables.set_data_from_state
        make_full_vec = variables.make_full_vec

        print 'problem size:'
        print '    velocity: %s' % ii['us']
        print '    pressure: %s' % ii['ps']

        vec_x = vec_x0.copy()
        vec_x_prev = vec_x0.copy()
        vec_dx = None

        if self.log is not None:
            self.log.plot_vlines(color='r', linewidth=1.0)

        err0 = -1.0
        it = 0
        while 1:
            vec_x_prev_f = make_full_vec(vec_x_prev)
            update_var(ns['b'], vec_x_prev_f, ns['u'])

            vec_b = vec_x_prev_f[ii['u']]
            b_norm = nla.norm(vec_b, nm.inf)
            print '|b|_max: %.12e' % b_norm

            vec_x_f = make_full_vec(vec_x)
            vec_u = vec_x_f[ii['u']]
            u_norm = nla.norm(vec_u, nm.inf)
            print '|u|_max: %.2e' % u_norm

            stabil.function.set_extra_args(b_norm=b_norm)
            stabil.time_update(None,
                               problem.equations,
                               mode='force',
                               problem=problem)
            max_pars = stabil.reduce_on_datas(lambda a, b: max(a, b.max()))
            print 'stabilization parameters:'
            print '                   gamma: %.12e' % max_pars[ns['gamma']]
            print '            max( delta ): %.12e' % max_pars[ns['delta']]
            print '              max( tau ): %.12e' % max_pars[ns['tau']]

            if (not are_close(b_norm, 1.0)) and conf.adimensionalize:
                adimensionalize = True
            else:
                adimensionalize = False

            tt = time.clock()
            try:
                vec_r = fun(vec_x)
            except ValueError:
                ok = False
            else:
                ok = True
            time_stats['rezidual'] = time.clock() - tt
            if ok:
                err = nla.norm(vec_r)
                if it == 0:
                    err0 = err
                else:
                    err += nla.norm(vec_dx)
            else:  # Failure.
                output('rezidual computation failed for iter %d!' % it)
                raise RuntimeError('giving up...')

            if self.log is not None:
                self.log(err, it, max_pars[ns['gamma']], max_pars[ns['delta']],
                         max_pars[ns['tau']])

            condition = conv_test(conf, it, err, err0)
            if condition >= 0:
                break

            if adimensionalize:
                output('adimensionalizing')
                ## mat.viscosity = viscosity / b_norm
                ## vec_r[indx_us] /= b_norm

            tt = time.clock()
            try:
                mtx_a = fun_grad(vec_x)
            except ValueError:
                ok = False
            else:
                ok = True
            time_stats['matrix'] = time.clock() - tt
            if not ok:
                raise RuntimeError('giving up...')

            tt = time.clock()
            vec_dx = lin_solver(vec_r, x0=vec_x, mtx=mtx_a)
            time_stats['solve'] = time.clock() - tt

            vec_e = mtx_a * vec_dx - vec_r
            lerr = nla.norm(vec_e)
            if lerr > (conf.eps_a * conf.lin_red):
                output('linear system not solved! (err = %e)' % lerr)

            if adimensionalize:
                output('restoring pressure...')
                ## vec_dx[indx_ps] *= b_norm

            dx_norm = nla.norm(vec_dx)
            output('||dx||: %.2e' % dx_norm)

            for kv in time_stats.iteritems():
                output('%10s: %7.2f [s]' % kv)

            vec_x_prev = vec_x.copy()
            vec_x -= vec_dx
            it += 1

        if conf.check_navier_stokes_rezidual:

            t1 = '+ dw_div_grad.%s.%s( %s.viscosity, %s, %s )' \
                 % (ns['i2'], ns['omega'], ns['fluid'], ns['v'], ns['u'])
            ##             t2 = '+ dw_lin_convect.%s( %s, %s, %s )' % (ns['omega'],
            ##                                                         ns['v'], b_name, ns['u'])
            t2 = '+ dw_convect.%s.%s( %s, %s )' % (ns['i2'], ns['omega'],
                                                   ns['v'], ns['u'])
            t3 = '- dw_stokes.%s.%s( %s, %s )' % (ns['i1'], ns['omega'],
                                                  ns['v'], ns['p'])
            t4 = 'dw_stokes.%s.%s( %s, %s )' % (ns['i1'], ns['omega'], ns['u'],
                                                ns['q'])
            equations = {
                'balance': ' '.join((t1, t2, t3)),
                'incompressibility': t4,
            }
            problem.set_equations(equations)
            try:
                vec_rns0 = fun(vec_x0)
                vec_rns = fun(vec_x)
            except ValueError:
                ok = False
            else:
                ok = True
            if not ok:
                print 'Navier-Stokes rezidual computation failed!'
                err_ns = err_ns0 = None
            else:
                err_ns0 = nla.norm(vec_rns0)
                err_ns = nla.norm(vec_rns)
            print 'Navier-Stokes rezidual0: %.8e' % err_ns0
            print 'Navier-Stokes rezidual : %.8e' % err_ns
            print 'b - u: %.8e' % nla.norm(vec_b - vec_u)
            print condition

        else:
            err_ns = None

        if status is not None:
            status['time_stats'] = time_stats
            status['err0'] = err0
            status['err'] = err
            status['err_ns'] = err_ns
            status['condition'] = condition

        if conf.log.plot is not None:
            if self.log is not None:
                self.log(save_figure=conf.log.plot)

        return vec_x
예제 #10
0
파일: nls.py 프로젝트: olivierverdier/sfepy
class Newton( NonlinearSolver ):
    name = 'nls.newton'

    def process_conf( conf ):
        """
        Missing items are set to default values for a linear problem.
        
        Example configuration, all items::
        
            solver_1 = {
                'name' : 'newton',
                'kind' : 'nls.newton',

                'i_max'      : 2,
                'eps_a'      : 1e-8,
                'eps_r'      : 1e-2,
                'macheps'   : 1e-16,
                'lin_red'    : 1e-2, # Linear system error < (eps_a * lin_red).
                'ls_red'     : 0.1,
                'ls_red_warp' : 0.001,
                'ls_on'      : 0.99999,
                'ls_min'     : 1e-5,
                'check'     : 0,
                'delta'     : 1e-6,
                'is_plot'    : False,
                'log'        : None,
                 # 'nonlinear' or 'linear' (ignore i_max)
                'problem'   : 'nonlinear',
            }
        """
        get = conf.get_default_attr

        i_max = get( 'i_max', 1 )
        eps_a = get( 'eps_a', 1e-10 )
        eps_r = get( 'eps_r', 1.0 )
        macheps = get( 'macheps', nm.finfo( nm.float64 ).eps )
        lin_red = get( 'lin_red', 1.0 )
        ls_red = get( 'ls_red', 0.1 )
        ls_red_warp = get( 'ls_red_warp', 0.001 )
        ls_on = get( 'ls_on', 0.99999 )
        ls_min = get( 'ls_min', 1e-5 )
        check = get( 'check', 0 )
        delta = get( 'delta', 1e-6)
        is_plot = get( 'is_plot', False )
        problem = get( 'problem', 'nonlinear' )

        log = get_logging_conf(conf)
        log = Struct(name='log_conf', **log)
        is_any_log = (log.text is not None) or (log.plot is not None)

        common = NonlinearSolver.process_conf( conf )
        return Struct( **locals() ) + common
    process_conf = staticmethod( process_conf )

    def __init__(self, conf, **kwargs):
        NonlinearSolver.__init__( self, conf, **kwargs )

        conf = self.conf
        if conf.is_any_log:
            self.log = Log([[r'$||r||$'], ['iteration']],
                           xlabels=['', 'all iterations'],
                           ylabels=[r'$||r||$', 'iteration'],
                           yscales=['log', 'linear'],
                           is_plot=conf.log.plot is not None,
                           log_filename=conf.log.text,
                           formats=[['%.8e'], ['%d']])

        else:
            self.log = None

    ##
    # c: 02.12.2005, r: 04.04.2008
    # 10.10.2007, from newton()
    def __call__( self, vec_x0, conf = None, fun = None, fun_grad = None,
                  lin_solver = None, status = None ):
        """setting conf.problem == 'linear' means 1 iteration and no rezidual
        check!
        """
        import sfepy.base.plotutils as plu
        conf = get_default( conf, self.conf )
        fun = get_default( fun, self.fun )
        fun_grad = get_default( fun_grad, self.fun_grad )
        lin_solver = get_default( lin_solver, self.lin_solver )
        status = get_default( status, self.status )

        time_stats = {}

        vec_x = vec_x0.copy()
        vec_x_last = vec_x0.copy()
        vec_dx = None

        if self.log is not None:
            self.log.plot_vlines(color='r', linewidth=1.0)

        err0 = -1.0
        err_last = -1.0
        it = 0
        while 1:

            ls = 1.0
            vec_dx0 = vec_dx;
            while 1:
                tt = time.clock()

                try:
                    vec_r = fun( vec_x )

                except ValueError:
                    if (it == 0) or (ls < conf.ls_min):
                        output('giving up!')
                        raise

                else:
                    ok = True

                time_stats['rezidual'] = time.clock() - tt
                if ok:
                    try:
                        err = nla.norm( vec_r )
                    except:
                        output( 'infs or nans in the residual:', vec_r )
                        output( nm.isfinite( vec_r ).all() )
                        debug()

                    if self.log is not None:
                        self.log(err, it)

                    if it == 0:
                        err0 = err;
                        break
                    if err < (err_last * conf.ls_on): break
                    red = conf.ls_red;
                    output( 'linesearch: iter %d, (%.5e < %.5e) (new ls: %e)'\
                            % (it, err, err_last * conf.ls_on, red * ls) )
                else: # Failure.
                    red = conf.ls_red_warp;
                    output(  'rezidual computation failed for iter %d'
                             ' (new ls: %e)!' % (it, red * ls) )

                if ls < conf.ls_min:
                    output( 'linesearch failed, continuing anyway' )
                    break

                ls *= red;

                vec_dx = ls * vec_dx0;
                vec_x = vec_x_last.copy() - vec_dx
            # End residual loop.

            if self.log is not None:
                self.log.plot_vlines([1], color='g', linewidth=0.5)

            err_last = err;
            vec_x_last = vec_x.copy()

            condition = conv_test( conf, it, err, err0 )
            if condition >= 0:
                break

            tt = time.clock()
            if conf.problem == 'nonlinear':
                mtx_a = fun_grad(vec_x)

            else:
                mtx_a = fun_grad( 'linear' )

            time_stats['matrix'] = time.clock() - tt

            if conf.check:
                tt = time.clock()
                wt = check_tangent_matrix( conf, vec_x, fun, fun_grad )
                time_stats['check'] = time.clock() - tt - wt

            tt = time.clock()
            vec_dx = lin_solver( vec_r, mtx = mtx_a )
            time_stats['solve'] = time.clock() - tt

            for kv in time_stats.iteritems():
                output( '%10s: %7.2f [s]' % kv )

            vec_e = mtx_a * vec_dx - vec_r
            lerr = nla.norm( vec_e )
            if lerr > (conf.eps_a * conf.lin_red):
                output( 'linear system not solved! (err = %e)' % lerr )

            vec_x -= vec_dx

            if conf.is_plot:
                plu.pylab.ion()
                plu.pylab.gcf().clear()
                plu.pylab.subplot( 2, 2, 1 )
                plu.pylab.plot( vec_x_last )
                plu.pylab.ylabel( r'$x_{i-1}$' )
                plu.pylab.subplot( 2, 2, 2 )
                plu.pylab.plot( vec_r )
                plu.pylab.ylabel( r'$r$' )
                plu.pylab.subplot( 2, 2, 4 )
                plu.pylab.plot( vec_dx )
                plu.pylab.ylabel( r'$\_delta x$' )
                plu.pylab.subplot( 2, 2, 3 )
                plu.pylab.plot( vec_x )
                plu.pylab.ylabel( r'$x_i$' )
                plu.pylab.draw()
                plu.pylab.ioff()
                pause()

            it += 1

        if status is not None:
            status['time_stats'] = time_stats
            status['err0'] = err0
            status['err'] = err
            status['condition'] = condition

        if conf.log.plot is not None:
            if self.log is not None:
                self.log(save_figure=conf.log.plot)

        return vec_x
예제 #11
0
파일: optimize.py 프로젝트: uberstig/sfepy
class FMinSteepestDescent(OptimizationSolver):
    """
    Steepest descent optimization solver.
    """
    name = 'opt.fmin_sd'

    __metaclass__ = SolverMeta

    _parameters = [
        ('i_max', 'int', 10, False, 'The maximum number of iterations.'),
        ('eps_rd', 'float', 1e-5, False,
         'The relative delta of the objective function.'),
        ('eps_of', 'float', 1e-4, False,
         'The tolerance for the objective function.'),
        ('eps_ofg', 'float', 1e-8, False,
         'The tolerance for the objective function gradient.'),
        ('norm', 'numpy norm', nm.Inf, False, 'The norm to be used.'),
        ('ls', 'bool', True, False, 'If True, use a line-search.'),
        ('ls_method', "{'backtracking', 'full'}", 'backtracking', False,
         'The line-search method.'),
        ('ls_on', 'float', 0.99999, False,
         """Start the backtracking line-search by reducing the step, if
            :math:`||f(x^i)|| / ||f(x^{i-1})||` is larger than `ls_on`."""),
        ('ls0', '0.0 < float < 1.0', 1.0, False, 'The initial step.'),
        ('ls_red', '0.0 < float < 1.0', 0.5, False,
         'The step reduction factor in case of correct residual assembling.'),
        ('ls_red_warp', '0.0 < float < 1.0', 0.1, False,
         """The step reduction factor in case of failed residual assembling
            (e.g. the "warp violation" error caused by a negative volume
            element resulting from too large deformations)."""),
        ('ls_min', '0.0 < float < 1.0', 1e-5, False,
         'The minimum step reduction factor.'),
        ('check', '0, 1 or 2', 0, False,
         """If >= 1, check the tangent matrix using finite differences.  If 2,
            plot the resulting sparsity patterns."""),
        ('delta', 'float', 1e-6, False,
         r"""If `check >= 1`, the finite difference matrix is taken as
            :math:`A_{ij} = \frac{f_i(x_j + \delta) - f_i(x_j - \delta)}{2
            \delta}`."""),
        ('output', 'function', None, False,
         """If given, use it instead of :func:`output()
            <sfepy.base.base.output()>` function."""),
        ('yscales', 'list of str', ['linear', 'log', 'log', 'linear'], False,
         'The list of four convergence log subplot scales.'),
        ('log', 'dict or None', None, False,
         """If not None, log the convergence according to the configuration in
            the following form: ``{'text' : 'log.txt', 'plot' : 'log.pdf'}``.
            Each of the dict items can be None."""),
    ]

    def __init__(self, conf, **kwargs):
        OptimizationSolver.__init__(self, conf, **kwargs)

        conf = self.conf

        log = get_logging_conf(conf)
        conf.log = log = Struct(name='log_conf', **log)
        conf.is_any_log = (log.text is not None) or (log.plot is not None)

        if conf.is_any_log:
            self.log = Log(
                [[r'$||\Psi||$'], [r'$||\nabla \Psi||$'], [r'$\alpha$'],
                 ['iteration']],
                xlabels=['', '', 'all iterations', 'all iterations'],
                yscales=conf.yscales,
                is_plot=conf.log.plot is not None,
                log_filename=conf.log.text,
                formats=[['%.8e'], ['%.3e'], ['%.3e'], ['%d']])
        else:
            self.log = None

    def __call__(self,
                 x0,
                 conf=None,
                 obj_fun=None,
                 obj_fun_grad=None,
                 status=None,
                 obj_args=None):
        conf = get_default(conf, self.conf)
        obj_fun = get_default(obj_fun, self.obj_fun)
        obj_fun_grad = get_default(obj_fun_grad, self.obj_fun_grad)
        status = get_default(status, self.status)
        obj_args = get_default(obj_args, self.obj_args)

        if conf.output:
            globals()['output'] = conf.output

        output('entering optimization loop...')

        nc_of, tt_of, fn_of = wrap_function(obj_fun, obj_args)
        nc_ofg, tt_ofg, fn_ofg = wrap_function(obj_fun_grad, obj_args)

        time_stats = {'of': tt_of, 'ofg': tt_ofg, 'check': []}

        ofg = None

        it = 0
        xit = x0.copy()
        while 1:

            of = fn_of(xit)

            if it == 0:
                of0 = ofit0 = of_prev = of
                of_prev_prev = of + 5000.0

            if ofg is None:
                ofg = fn_ofg(xit)

            if conf.check:
                tt = time.clock()
                check_gradient(xit, ofg, fn_of, conf.delta, conf.check)
                time_stats['check'].append(time.clock() - tt)

            ofg_norm = nla.norm(ofg, conf.norm)

            ret = conv_test(conf, it, of, ofit0, ofg_norm)
            if ret >= 0:
                break
            ofit0 = of

            ##
            # Backtrack (on errors).
            alpha = conf.ls0
            can_ls = True
            while 1:
                xit2 = xit - alpha * ofg
                aux = fn_of(xit2)

                if self.log is not None:
                    self.log(of, ofg_norm, alpha, it)

                if aux is None:
                    alpha *= conf.ls_red_warp
                    can_ls = False
                    output('warp: reducing step (%f)' % alpha)
                elif conf.ls and conf.ls_method == 'backtracking':
                    if aux < of * conf.ls_on: break
                    alpha *= conf.ls_red
                    output('backtracking: reducing step (%f)' % alpha)
                else:
                    of_prev_prev = of_prev
                    of_prev = aux
                    break

                if alpha < conf.ls_min:
                    if aux is None:
                        raise RuntimeError, 'giving up...'
                    output('linesearch failed, continuing anyway')
                    break

            # These values are modified by the line search, even if it fails
            of_prev_bak = of_prev
            of_prev_prev_bak = of_prev_prev

            if conf.ls and can_ls and conf.ls_method == 'full':
                output('full linesearch...')
                alpha, fc, gc, of_prev, of_prev_prev, ofg1 = \
                    linesearch.line_search(fn_of,fn_ofg,xit,
                                           -ofg,ofg,of_prev,of_prev_prev,
                                           c2=0.4)
                if alpha is None:  # line search failed -- use different one.
                    alpha, fc, gc, of_prev, of_prev_prev, ofg1 = \
                        sopt.line_search(fn_of,fn_ofg,xit,
                                         -ofg,ofg,of_prev_bak,
                                         of_prev_prev_bak)
                    if alpha is None or alpha == 0:
                        # This line search also failed to find a better
                        # solution.
                        ret = 3
                        break
                output(' -> alpha: %.8e' % alpha)
            else:
                if conf.ls_method == 'full':
                    output('full linesearch off (%s and %s)' %
                           (conf.ls, can_ls))
                ofg1 = None

            if self.log is not None:
                self.log.plot_vlines(color='g', linewidth=0.5)

            xit = xit - alpha * ofg
            if ofg1 is None:
                ofg = None
            else:
                ofg = ofg1.copy()

            for key, val in time_stats.iteritems():
                if len(val):
                    output('%10s: %7.2f [s]' % (key, val[-1]))

            it = it + 1

        output('status:               %d' % ret)
        output('initial value:        %.8e' % of0)
        output('current value:        %.8e' % of)
        output('iterations:           %d' % it)
        output('function evaluations: %d in %.2f [s]' %
               (nc_of[0], nm.sum(time_stats['of'])))
        output('gradient evaluations: %d in %.2f [s]' %
               (nc_ofg[0], nm.sum(time_stats['ofg'])))

        if self.log is not None:
            self.log(of, ofg_norm, alpha, it)

            if conf.log.plot is not None:
                self.log(save_figure=conf.log.plot, finished=True)

            else:
                self.log(finished=True)

        if status is not None:
            status['log'] = self.log
            status['status'] = status
            status['of0'] = of0
            status['of'] = of
            status['it'] = it
            status['nc_of'] = nc_of[0]
            status['nc_ofg'] = nc_ofg[0]
            status['time_stats'] = time_stats

        return xit
예제 #12
0
파일: nls.py 프로젝트: taldcroft/sfepy
class Newton(NonlinearSolver):
    name = "nls.newton"

    @staticmethod
    def process_conf(conf, kwargs):
        """
        Missing items are set to default values for a linear problem.

        Example configuration, all items::

            solver_1 = {
                'name' : 'newton',
                'kind' : 'nls.newton',

                'i_max' : 2,
                'eps_a' : 1e-8,
                'eps_r' : 1e-2,
                'macheps' : 1e-16,
                'lin_red' : 1e-2, # Linear system error < (eps_a * lin_red).
                'lin_precision' : None,
                'ls_red' : 0.1,
                'ls_red_warp' : 0.001,
                'ls_on' : 0.99999,
                'ls_min' : 1e-5,
                'give_up_warp' : False,
                'check' : 0,
                'delta' : 1e-6,
                'is_plot' : False,
                'log' : None, # 'nonlinear' or 'linear' (ignore i_max)
                'problem' : 'nonlinear',
            }
        """
        get = make_get_conf(conf, kwargs)
        common = NonlinearSolver.process_conf(conf)

        log = get_logging_conf(conf)
        log = Struct(name="log_conf", **log)
        is_any_log = (log.text is not None) or (log.plot is not None)

        return (
            Struct(
                i_max=get("i_max", 1),
                eps_a=get("eps_a", 1e-10),
                eps_r=get("eps_r", 1.0),
                macheps=get("macheps", nm.finfo(nm.float64).eps),
                lin_red=get("lin_red", 1.0),
                lin_precision=get("lin_precision", None),
                ls_red=get("ls_red", 0.1),
                ls_red_warp=get("ls_red_warp", 0.001),
                ls_on=get("ls_on", 0.99999),
                ls_min=get("ls_min", 1e-5),
                give_up_warp=get("give_up_warp", False),
                check=get("check", 0),
                delta=get("delta", 1e-6),
                is_plot=get("is_plot", False),
                problem=get("problem", "nonlinear"),
                log=log,
                is_any_log=is_any_log,
            )
            + common
        )

    def __init__(self, conf, **kwargs):
        NonlinearSolver.__init__(self, conf, **kwargs)

        conf = self.conf
        if conf.is_any_log:
            self.log = Log(
                [[r"$||r||$"], ["iteration"]],
                xlabels=["", "all iterations"],
                ylabels=[r"$||r||$", "iteration"],
                yscales=["log", "linear"],
                is_plot=conf.log.plot is not None,
                log_filename=conf.log.text,
                formats=[["%.8e"], ["%d"]],
            )

        else:
            self.log = None

    def __call__(self, vec_x0, conf=None, fun=None, fun_grad=None, lin_solver=None, iter_hook=None, status=None):
        """
        Nonlinear system solver call.

        Solves :math:`f(x) = 0` by the Newton method with backtracking
        linesearch.

        Parameters
        ----------
        vec_x0 : array
            The initial guess vector :math:`x_0`.
        conf : Struct instance, optional
            The solver configuration parameters,
        fun : function, optional
            The function :math:`f(x)` whose zero is sought - the residual.
        fun_grad : function, optional
            The gradient of :math:`f(x)` - the tangent matrix.
        lin_solver : LinearSolver instance, optional
            The linear solver for each nonlinear iteration.
        iter_hook : function, optional
            User-supplied function to call before each iteration.
        status : dict-like, optional
            The user-supplied object to hold convergence statistics.

        Notes
        -----
        * The optional parameters except `iter_hook` and `status` need
          to be given either here or upon `Newton` construction.
        * Setting `conf.problem == 'linear'` means 1 iteration and no
          rezidual check!
        """
        import sfepy.base.plotutils as plu

        conf = get_default(conf, self.conf)
        fun = get_default(fun, self.fun)
        fun_grad = get_default(fun_grad, self.fun_grad)
        lin_solver = get_default(lin_solver, self.lin_solver)
        iter_hook = get_default(iter_hook, self.iter_hook)
        status = get_default(status, self.status)

        ls_eps_a, ls_eps_r = lin_solver.get_tolerance()
        eps_a = get_default(ls_eps_a, 1.0)
        eps_r = get_default(ls_eps_r, 1.0)
        lin_red = conf.eps_a * conf.lin_red

        time_stats = {}

        vec_x = vec_x0.copy()
        vec_x_last = vec_x0.copy()
        vec_dx = None

        if self.log is not None:
            self.log.plot_vlines(color="r", linewidth=1.0)

        err = err0 = -1.0
        err_last = -1.0
        it = 0
        while 1:
            if iter_hook is not None:
                iter_hook(self, vec_x, it, err, err0)

            ls = 1.0
            vec_dx0 = vec_dx
            while 1:
                tt = time.clock()

                try:
                    vec_r = fun(vec_x)

                except ValueError:
                    if (it == 0) or (ls < conf.ls_min):
                        output("giving up!")
                        raise

                    else:
                        ok = False

                else:
                    ok = True

                time_stats["rezidual"] = time.clock() - tt
                if ok:
                    try:
                        err = nla.norm(vec_r)
                    except:
                        output("infs or nans in the residual:", vec_r)
                        output(nm.isfinite(vec_r).all())
                        debug()

                    if self.log is not None:
                        self.log(err, it)

                    if it == 0:
                        err0 = err
                        break
                    if err < (err_last * conf.ls_on):
                        break
                    red = conf.ls_red
                    output(
                        "linesearch: iter %d, (%.5e < %.5e) (new ls: %e)" % (it, err, err_last * conf.ls_on, red * ls)
                    )
                else:  # Failure.
                    if conf.give_up_warp:
                        output("giving up!")
                        break

                    red = conf.ls_red_warp
                    output("rezidual computation failed for iter %d" " (new ls: %e)!" % (it, red * ls))

                if ls < conf.ls_min:
                    output("linesearch failed, continuing anyway")
                    break

                ls *= red

                vec_dx = ls * vec_dx0
                vec_x = vec_x_last.copy() - vec_dx
            # End residual loop.

            if self.log is not None:
                self.log.plot_vlines([1], color="g", linewidth=0.5)

            err_last = err
            vec_x_last = vec_x.copy()

            condition = conv_test(conf, it, err, err0)
            if condition >= 0:
                break

            if (not ok) and conf.give_up_warp:
                condition = 2
                break

            tt = time.clock()
            if conf.problem == "nonlinear":
                mtx_a = fun_grad(vec_x)

            else:
                mtx_a = fun_grad("linear")

            time_stats["matrix"] = time.clock() - tt

            if conf.check:
                tt = time.clock()
                wt = check_tangent_matrix(conf, vec_x, fun, fun_grad)
                time_stats["check"] = time.clock() - tt - wt

            if conf.lin_precision is not None:
                if ls_eps_a is not None:
                    eps_a = max(err * conf.lin_precision, ls_eps_a)

                elif ls_eps_r is not None:
                    eps_r = max(conf.lin_precision, ls_eps_r)

                lin_red = max(eps_a, err * eps_r)

            if conf.verbose:
                output("solving linear system...")

            tt = time.clock()
            vec_dx = lin_solver(vec_r, x0=vec_x, eps_a=eps_a, eps_r=eps_r, mtx=mtx_a)
            time_stats["solve"] = time.clock() - tt

            if conf.verbose:
                output("...done")

            for kv in time_stats.iteritems():
                output("%10s: %7.2f [s]" % kv)

            vec_e = mtx_a * vec_dx - vec_r
            lerr = nla.norm(vec_e)
            if lerr > lin_red:
                output("linear system not solved! (err = %e < %e)" % (lerr, lin_red))

            vec_x -= vec_dx

            if conf.is_plot:
                plu.plt.ion()
                plu.plt.gcf().clear()
                plu.plt.subplot(2, 2, 1)
                plu.plt.plot(vec_x_last)
                plu.plt.ylabel(r"$x_{i-1}$")
                plu.plt.subplot(2, 2, 2)
                plu.plt.plot(vec_r)
                plu.plt.ylabel(r"$r$")
                plu.plt.subplot(2, 2, 4)
                plu.plt.plot(vec_dx)
                plu.plt.ylabel(r"$\_delta x$")
                plu.plt.subplot(2, 2, 3)
                plu.plt.plot(vec_x)
                plu.plt.ylabel(r"$x_i$")
                plu.plt.draw()
                plu.plt.ioff()
                pause()

            it += 1

        if status is not None:
            status["time_stats"] = time_stats
            status["err0"] = err0
            status["err"] = err
            status["n_iter"] = it
            status["condition"] = condition

        if conf.log.plot is not None:
            if self.log is not None:
                self.log(save_figure=conf.log.plot)

        return vec_x
예제 #13
0
파일: oseen.py 프로젝트: Nasrollah/sfepy
class Oseen(NonlinearSolver):
    """
    The Oseen solver for Navier-Stokes equations.
    """
    name = 'nls.oseen'

    __metaclass__ = SolverMeta

    _parameters = [
        ('stabil_mat', 'str', None, True,
         'The name of stabilization material.'),
        ('adimensionalize', 'bool', False, False,
         'If True, adimensionalize the problem (not implemented!).'),
        ('check_navier_stokes_rezidual', 'bool', False, False,
         'If True, check the Navier-Stokes rezidual after the nonlinear loop.'),
        ('i_max', 'int', 1, False,
         'The maximum number of iterations.'),
        ('eps_a', 'float', 1e-10, False,
         'The absolute tolerance for the residual, i.e. :math:`||f(x^i)||`.'),
        ('eps_r', 'float', 1.0, False,
         """The relative tolerance for the residual, i.e. :math:`||f(x^i)|| /
            ||f(x^0)||`."""),
        ('macheps', 'float', nm.finfo(nm.float64).eps, False,
         'The float considered to be machine "zero".'),
        ('lin_red', 'float', 1.0, False,
         """The linear system solution error should be smaller than (`eps_a` *
            `lin_red`), otherwise a warning is printed."""),
        ('lin_precision', 'float or None', None, False,
         """If not None, the linear system solution tolerances are set in each
            nonlinear iteration relative to the current residual norm by the
            `lin_precision` factor. Ignored for direct linear solvers."""),
    ]

    def __init__(self, conf, problem, **kwargs):
        NonlinearSolver.__init__(self, conf, **kwargs)

        conf = self.conf

        log = get_logging_conf(conf)
        conf.log = log = Struct(name='log_conf', **log)
        conf.is_any_log = (log.text is not None) or (log.plot is not None)

        conf.problem = problem

        conf = self.conf
        if conf.is_any_log:
            self.log = Log([[r'$||r||$'], ['iteration'],
                            [r'$\gamma$', r'$\max(\delta)$', r'$\max(\tau)$']],
                           xlabels=['', '', 'all iterations'],
                           ylabels=[r'$||r||$', 'iteration', 'stabilization'],
                           yscales=['log', 'linear', 'log'],
                           is_plot=conf.log.plot is not None,
                           log_filename=conf.log.text,
                           formats=[['%.8e'], ['%d'],
                                    ['%.8e', '%.8e', '%.8e']])

        else:
            self.log = None

    def __call__(self, vec_x0, conf=None, fun=None, fun_grad=None,
                 lin_solver=None, status=None, problem=None):
        """
        Oseen solver is problem-specific - it requires a Problem instance.
        """
        conf = get_default(conf, self.conf)
        fun = get_default(fun, self.fun)
        fun_grad = get_default(fun_grad, self.fun_grad)
        lin_solver = get_default(lin_solver, self.lin_solver)
        status = get_default(status, self.status)
        problem = get_default(problem, conf.problem,
                              '`problem` parameter needs to be set!')

        time_stats = {}

        stabil = problem.get_materials()[conf.stabil_mat]
        ns, ii = stabil.function.function.get_maps()

        variables = problem.get_variables()
        update_var = variables.set_data_from_state
        make_full_vec = variables.make_full_vec

        output('problem size:')
        output('    velocity: %s' % ii['us'])
        output('    pressure: %s' % ii['ps'])

        vec_x = vec_x0.copy()
        vec_x_prev = vec_x0.copy()
        vec_dx = None

        if self.log is not None:
            self.log.plot_vlines(color='r', linewidth=1.0)

        err0 = -1.0
        it = 0
        while 1:
            vec_x_prev_f = make_full_vec(vec_x_prev)
            update_var(ns['b'], vec_x_prev_f, ns['u'])

            vec_b = vec_x_prev_f[ii['u']]
            b_norm = nla.norm(vec_b, nm.inf)
            output('|b|_max: %.12e' % b_norm)

            vec_x_f = make_full_vec(vec_x)
            vec_u = vec_x_f[ii['u']]
            u_norm = nla.norm(vec_u, nm.inf)
            output('|u|_max: %.2e' % u_norm)

            stabil.function.set_extra_args(b_norm=b_norm)
            stabil.time_update(None, problem.equations, mode='force',
                               problem=problem)
            max_pars = stabil.reduce_on_datas(lambda a, b: max(a, b.max()))
            output('stabilization parameters:')
            output('                   gamma: %.12e' % max_pars[ns['gamma']])
            output('            max(delta): %.12e' % max_pars[ns['delta']])
            output('              max(tau): %.12e' % max_pars[ns['tau']])

            if (not are_close(b_norm, 1.0)) and conf.adimensionalize:
                adimensionalize = True
            else:
                adimensionalize = False

            tt = time.clock()
            try:
                vec_r = fun(vec_x)
            except ValueError:
                ok = False
            else:
                ok = True
            time_stats['rezidual'] = time.clock() - tt
            if ok:
                err = nla.norm(vec_r)
                if it == 0:
                    err0 = err;
                else:
                    err += nla.norm(vec_dx)
            else: # Failure.
                output('rezidual computation failed for iter %d!' % it)
                raise RuntimeError('giving up...')

            if self.log is not None:
                self.log(err, it,
                         max_pars[ns['gamma']], max_pars[ns['delta']],
                         max_pars[ns['tau']])

            condition = conv_test(conf, it, err, err0)
            if condition >= 0:
                break

            if adimensionalize:
                output('adimensionalizing')
                ## mat.viscosity = viscosity / b_norm
                ## vec_r[indx_us] /= b_norm

            tt = time.clock()
            try:
                mtx_a = fun_grad(vec_x)
            except ValueError:
                ok = False
            else:
                ok = True
            time_stats['matrix'] = time.clock() - tt
            if not ok:
                raise RuntimeError('giving up...')

            tt = time.clock()
            vec_dx = lin_solver(vec_r, x0=vec_x, mtx=mtx_a)
            time_stats['solve'] = time.clock() - tt

            vec_e = mtx_a * vec_dx - vec_r
            lerr = nla.norm(vec_e)
            if lerr > (conf.eps_a * conf.lin_red):
                output('linear system not solved! (err = %e)' % lerr)

            if adimensionalize:
                output('restoring pressure...')
                ## vec_dx[indx_ps] *= b_norm

            dx_norm = nla.norm(vec_dx)
            output('||dx||: %.2e' % dx_norm)

            for kv in six.iteritems(time_stats):
                output('%10s: %7.2f [s]' % kv)

            vec_x_prev = vec_x.copy()
            vec_x -= vec_dx
            it += 1

        if conf.check_navier_stokes_rezidual:

            t1 = '+ dw_div_grad.%s.%s(%s.viscosity, %s, %s)' \
                 % (ns['i2'], ns['omega'], ns['fluid'], ns['v'], ns['u'])
            # t2 = '+ dw_lin_convect.%s(%s, %s, %s)' % (ns['omega'],
            #                                           ns['v'], b_name, ns['u'])
            t2 = '+ dw_convect.%s.%s(%s, %s)' % (ns['i2'], ns['omega'],
                                                 ns['v'], ns['u'])
            t3 = '- dw_stokes.%s.%s(%s, %s)' % (ns['i1'], ns['omega'],
                                                ns['v'], ns['p'])
            t4 = 'dw_stokes.%s.%s(%s, %s)' % (ns['i1'], ns['omega'],
                                              ns['u'], ns['q'])
            equations = {
                'balance' : ' '.join((t1, t2, t3)),
                'incompressibility' : t4,
            }
            problem.set_equations(equations)
            try:
                vec_rns0 = fun(vec_x0)
                vec_rns = fun(vec_x)
            except ValueError:
                ok = False
            else:
                ok = True
            if not ok:
                output('Navier-Stokes rezidual computation failed!')
                err_ns = err_ns0 = None
            else:
                err_ns0 = nla.norm(vec_rns0)
                err_ns = nla.norm(vec_rns)
            output('Navier-Stokes rezidual0: %.8e' % err_ns0)
            output('Navier-Stokes rezidual : %.8e' % err_ns)
            output('b - u: %.8e' % nla.norm(vec_b - vec_u))
            output(condition)

        else:
            err_ns = None

        if status is not None:
            status['time_stats'] = time_stats
            status['err0'] = err0
            status['err'] = err
            status['err_ns'] = err_ns
            status['condition'] = condition

        if conf.log.plot is not None:
            if self.log is not None:
                self.log(save_figure=conf.log.plot)

        return vec_x
예제 #14
0
파일: optimize.py 프로젝트: Gkdnz/sfepy
class FMinSteepestDescent(OptimizationSolver):
    """
    Steepest descent optimization solver.
    """
    name = 'opt.fmin_sd'

    __metaclass__ = SolverMeta

    _parameters = [
        ('i_max', 'int', 10, False,
         'The maximum number of iterations.'),
        ('eps_rd', 'float', 1e-5, False,
         'The relative delta of the objective function.'),
        ('eps_of', 'float', 1e-4, False,
         'The tolerance for the objective function.'),
        ('eps_ofg', 'float', 1e-8, False,
         'The tolerance for the objective function gradient.'),
        ('norm', 'numpy norm', nm.Inf, False,
         'The norm to be used.'),
        ('ls', 'bool', True, False,
         'If True, use a line-search.'),
        ('ls_method', "{'backtracking', 'full'}", 'backtracking', False,
         'The line-search method.'),
        ('ls_on', 'float', 0.99999, False,
         """Start the backtracking line-search by reducing the step, if
            :math:`||f(x^i)|| / ||f(x^{i-1})||` is larger than `ls_on`."""),
        ('ls0', '0.0 < float < 1.0', 1.0, False,
         'The initial step.'),
        ('ls_red', '0.0 < float < 1.0', 0.5, False,
         'The step reduction factor in case of correct residual assembling.'),
        ('ls_red_warp', '0.0 < float < 1.0', 0.1, False,
         """The step reduction factor in case of failed residual assembling
            (e.g. the "warp violation" error caused by a negative volume
            element resulting from too large deformations)."""),
        ('ls_min', '0.0 < float < 1.0', 1e-5, False,
         'The minimum step reduction factor.'),
        ('check', '0, 1 or 2', 0, False,
         """If >= 1, check the tangent matrix using finite differences.  If 2,
            plot the resulting sparsity patterns."""),
        ('delta', 'float', 1e-6, False,
         r"""If `check >= 1`, the finite difference matrix is taken as
            :math:`A_{ij} = \frac{f_i(x_j + \delta) - f_i(x_j - \delta)}{2
            \delta}`."""),
        ('output', 'function', None, False,
         """If given, use it instead of :func:`output()
            <sfepy.base.base.output()>` function."""),
        ('yscales', 'list of str', ['linear', 'log', 'log', 'linear'], False,
         'The list of four convergence log subplot scales.'),
        ('log', 'dict or None', None, False,
         """If not None, log the convergence according to the configuration in
            the following form: ``{'text' : 'log.txt', 'plot' : 'log.pdf'}``.
            Each of the dict items can be None."""),
    ]

    def __init__(self, conf, **kwargs):
        OptimizationSolver.__init__(self, conf, **kwargs)

        conf = self.conf

        log = get_logging_conf(conf)
        conf.log = log = Struct(name='log_conf', **log)
        conf.is_any_log = (log.text is not None) or (log.plot is not None)

        if conf.is_any_log:
            self.log = Log([[r'$||\Psi||$'], [r'$||\nabla \Psi||$'],
                            [r'$\alpha$'], ['iteration']],
                           xlabels=['', '', 'all iterations', 'all iterations'],
                           yscales=conf.yscales,
                           is_plot=conf.log.plot is not None,
                           log_filename=conf.log.text,
                           formats=[['%.8e'], ['%.3e'], ['%.3e'], ['%d']])
        else:
            self.log = None

    def __call__(self, x0, conf=None, obj_fun=None, obj_fun_grad=None,
                 status=None, obj_args=None):
        conf = get_default(conf, self.conf)
        obj_fun = get_default(obj_fun, self.obj_fun)
        obj_fun_grad = get_default(obj_fun_grad, self.obj_fun_grad)
        status = get_default(status, self.status)
        obj_args = get_default(obj_args, self.obj_args)

        if conf.output:
            globals()['output'] = conf.output

        output('entering optimization loop...')

        nc_of, tt_of, fn_of = wrap_function(obj_fun, obj_args)
        nc_ofg, tt_ofg, fn_ofg = wrap_function(obj_fun_grad, obj_args)

        time_stats = {'of' : tt_of, 'ofg': tt_ofg, 'check' : []}

        ofg = None

        it = 0
        xit = x0.copy()
        while 1:

            of = fn_of(xit)

            if it == 0:
                of0 = ofit0 = of_prev = of
                of_prev_prev = of + 5000.0

            if ofg is None:
                ofg = fn_ofg(xit)

            if conf.check:
                tt = time.clock()
                check_gradient(xit, ofg, fn_of, conf.delta, conf.check)
                time_stats['check'].append(time.clock() - tt)

            ofg_norm = nla.norm(ofg, conf.norm)

            ret = conv_test(conf, it, of, ofit0, ofg_norm)
            if ret >= 0:
                break
            ofit0 = of

            ##
            # Backtrack (on errors).
            alpha = conf.ls0
            can_ls = True
            while 1:
                xit2 = xit - alpha * ofg
                aux = fn_of(xit2)

                if self.log is not None:
                    self.log(of, ofg_norm, alpha, it)

                if aux is None:
                    alpha *= conf.ls_red_warp
                    can_ls = False
                    output('warp: reducing step (%f)' % alpha)
                elif conf.ls and conf.ls_method == 'backtracking':
                    if aux < of * conf.ls_on: break
                    alpha *= conf.ls_red
                    output('backtracking: reducing step (%f)' % alpha)
                else:
                    of_prev_prev = of_prev
                    of_prev = aux
                    break

                if alpha < conf.ls_min:
                    if aux is None:
                        raise RuntimeError, 'giving up...'
                    output('linesearch failed, continuing anyway')
                    break

            # These values are modified by the line search, even if it fails
            of_prev_bak = of_prev
            of_prev_prev_bak = of_prev_prev

            if conf.ls and can_ls and conf.ls_method == 'full':
                output('full linesearch...')
                alpha, fc, gc, of_prev, of_prev_prev, ofg1 = \
                    linesearch.line_search(fn_of,fn_ofg,xit,
                                           -ofg,ofg,of_prev,of_prev_prev,
                                           c2=0.4)
                if alpha is None:  # line search failed -- use different one.
                    alpha, fc, gc, of_prev, of_prev_prev, ofg1 = \
                        sopt.line_search(fn_of,fn_ofg,xit,
                                         -ofg,ofg,of_prev_bak,
                                         of_prev_prev_bak)
                    if alpha is None or alpha == 0:
                        # This line search also failed to find a better
                        # solution.
                        ret = 3
                        break
                output(' -> alpha: %.8e' % alpha)
            else:
                if conf.ls_method == 'full':
                    output('full linesearch off (%s and %s)'
                           % (conf.ls, can_ls))
                ofg1 = None

            if self.log is not None:
                self.log.plot_vlines(color='g', linewidth=0.5)

            xit = xit - alpha * ofg
            if ofg1 is None:
                ofg = None
            else:
                ofg = ofg1.copy()

            for key, val in time_stats.iteritems():
                if len(val):
                    output('%10s: %7.2f [s]' % (key, val[-1]))

            it = it + 1

        output('status:               %d' % ret)
        output('initial value:        %.8e' % of0)
        output('current value:        %.8e' % of)
        output('iterations:           %d' % it)
        output('function evaluations: %d in %.2f [s]'
               % (nc_of[0], nm.sum(time_stats['of'])))
        output('gradient evaluations: %d in %.2f [s]'
               % (nc_ofg[0], nm.sum(time_stats['ofg'])))

        if self.log is not None:
            self.log(of, ofg_norm, alpha, it)

            if conf.log.plot is not None:
                self.log(save_figure=conf.log.plot, finished=True)

            else:
                self.log(finished=True)

        if status is not None:
            status['log'] = self.log
            status['status'] = status
            status['of0'] = of0
            status['of'] = of
            status['it'] = it
            status['nc_of'] = nc_of[0]
            status['nc_ofg'] = nc_ofg[0]
            status['time_stats'] = time_stats

        return xit
예제 #15
0
class Oseen(NonlinearSolver):
    """
    The Oseen solver for Navier-Stokes equations.
    """
    name = 'nls.oseen'

    _parameters = [
        ('stabil_mat', 'str', None, True,
         'The name of stabilization material.'),
        ('adimensionalize', 'bool', False, False,
         'If True, adimensionalize the problem (not implemented!).'),
        ('check_navier_stokes_residual', 'bool', False, False,
         'If True, check the Navier-Stokes residual after the nonlinear loop.'
         ),
        ('i_max', 'int', 1, False, 'The maximum number of iterations.'),
        ('eps_a', 'float', 1e-10, False,
         'The absolute tolerance for the residual, i.e. :math:`||f(x^i)||`.'),
        ('eps_r', 'float', 1.0, False,
         """The relative tolerance for the residual, i.e. :math:`||f(x^i)|| /
            ||f(x^0)||`."""),
        ('macheps', 'float', nm.finfo(nm.float64).eps, False,
         'The float considered to be machine "zero".'),
        ('lin_red', 'float', 1.0, False,
         """The linear system solution error should be smaller than (`eps_a` *
            `lin_red`), otherwise a warning is printed."""),
        ('lin_precision', 'float or None', None, False,
         """If not None, the linear system solution tolerances are set in each
            nonlinear iteration relative to the current residual norm by the
            `lin_precision` factor. Ignored for direct linear solvers."""),
    ]

    def __init__(self, conf, context=None, **kwargs):
        NonlinearSolver.__init__(self, conf, context=context, **kwargs)

        conf = self.conf

        log = get_logging_conf(conf)
        conf.log = log = Struct(name='log_conf', **log)
        conf.is_any_log = (log.text is not None) or (log.plot is not None)

        conf.problem = context

        conf = self.conf
        if conf.is_any_log:
            self.log = Log([[r'$||r||$'], ['iteration'],
                            [r'$\gamma$', r'$\max(\delta)$', r'$\max(\tau)$']],
                           xlabels=['', '', 'all iterations'],
                           ylabels=[r'$||r||$', 'iteration', 'stabilization'],
                           yscales=['log', 'linear', 'log'],
                           is_plot=conf.log.plot is not None,
                           log_filename=conf.log.text,
                           formats=[['%.8e'], ['%d'], ['%.8e', '%.8e',
                                                       '%.8e']])

        else:
            self.log = None

    def __call__(self,
                 vec_x0,
                 conf=None,
                 fun=None,
                 fun_grad=None,
                 lin_solver=None,
                 status=None,
                 problem=None):
        """
        Oseen solver is problem-specific - it requires a Problem instance.
        """
        conf = get_default(conf, self.conf)
        fun = get_default(fun, self.fun)
        fun_grad = get_default(fun_grad, self.fun_grad)
        lin_solver = get_default(lin_solver, self.lin_solver)
        status = get_default(status, self.status)
        problem = get_default(problem, conf.problem,
                              '`problem` parameter needs to be set!')

        timer = Timer()
        time_stats = {}

        stabil = problem.get_materials()[conf.stabil_mat]
        ns, ii = stabil.function.function.get_maps()

        variables = problem.get_variables()
        update_var = variables.set_from_state
        make_full_vec = variables.make_full_vec

        output('problem size:')
        output('    velocity: %s' % ii['us'])
        output('    pressure: %s' % ii['ps'])

        vec_x = vec_x0.copy()
        vec_x_prev = vec_x0.copy()
        vec_dx = None

        if self.log is not None:
            self.log.plot_vlines(color='r', linewidth=1.0)

        err0 = -1.0
        it = 0
        while 1:
            vec_x_prev_f = make_full_vec(vec_x_prev)
            update_var(ns['b'], vec_x_prev_f, ns['u'])

            vec_b = vec_x_prev_f[ii['u']]
            b_norm = nla.norm(vec_b, nm.inf)
            output('|b|_max: %.12e' % b_norm)

            vec_x_f = make_full_vec(vec_x)
            vec_u = vec_x_f[ii['u']]
            u_norm = nla.norm(vec_u, nm.inf)
            output('|u|_max: %.2e' % u_norm)

            stabil.function.set_extra_args(b_norm=b_norm)
            stabil.time_update(None,
                               problem.equations,
                               mode='force',
                               problem=problem)
            max_pars = stabil.reduce_on_datas(lambda a, b: max(a, b.max()))
            output('stabilization parameters:')
            output('                   gamma: %.12e' % max_pars[ns['gamma']])
            output('            max(delta): %.12e' % max_pars[ns['delta']])
            output('              max(tau): %.12e' % max_pars[ns['tau']])

            if (not are_close(b_norm, 1.0)) and conf.adimensionalize:
                adimensionalize = True
            else:
                adimensionalize = False

            timer.start()
            try:
                vec_r = fun(vec_x)
            except ValueError:
                ok = False
            else:
                ok = True
            time_stats['residual'] = timer.stop()
            if ok:
                err = nla.norm(vec_r)
                if it == 0:
                    err0 = err
                else:
                    err += nla.norm(vec_dx)
            else:  # Failure.
                output('residual computation failed for iter %d!' % it)
                raise RuntimeError('giving up...')

            if self.log is not None:
                self.log(err, it, max_pars[ns['gamma']], max_pars[ns['delta']],
                         max_pars[ns['tau']])

            condition = conv_test(conf, it, err, err0)
            if condition >= 0:
                break

            if adimensionalize:
                output('adimensionalizing')
                ## mat.viscosity = viscosity / b_norm
                ## vec_r[indx_us] /= b_norm

            timer.start()
            try:
                mtx_a = fun_grad(vec_x)
            except ValueError:
                ok = False
            else:
                ok = True
            time_stats['matrix'] = timer.stop()
            if not ok:
                raise RuntimeError('giving up...')

            timer.start()
            vec_dx = lin_solver(vec_r, x0=vec_x, mtx=mtx_a)
            time_stats['solve'] = timer.stop()

            vec_e = mtx_a * vec_dx - vec_r
            lerr = nla.norm(vec_e)
            if lerr > (conf.eps_a * conf.lin_red):
                output('linear system not solved! (err = %e)' % lerr)

            if adimensionalize:
                output('restoring pressure...')
                ## vec_dx[indx_ps] *= b_norm

            dx_norm = nla.norm(vec_dx)
            output('||dx||: %.2e' % dx_norm)

            for kv in six.iteritems(time_stats):
                output('%10s: %7.2f [s]' % kv)

            vec_x_prev = vec_x.copy()
            vec_x -= vec_dx
            it += 1

        if conf.check_navier_stokes_residual:

            t1 = '+ dw_div_grad.%s.%s(%s.viscosity, %s, %s)' \
                 % (ns['i2'], ns['omega'], ns['fluid'], ns['v'], ns['u'])
            # t2 = '+ dw_lin_convect.%s(%s, %s, %s)' % (ns['omega'],
            #                                           ns['v'], b_name, ns['u'])
            t2 = '+ dw_convect.%s.%s(%s, %s)' % (ns['i2'], ns['omega'],
                                                 ns['v'], ns['u'])
            t3 = '- dw_stokes.%s.%s(%s, %s)' % (ns['i1'], ns['omega'], ns['v'],
                                                ns['p'])
            t4 = 'dw_stokes.%s.%s(%s, %s)' % (ns['i1'], ns['omega'], ns['u'],
                                              ns['q'])
            equations = {
                'balance': ' '.join((t1, t2, t3)),
                'incompressibility': t4,
            }
            problem.set_equations(equations)
            try:
                vec_rns0 = fun(vec_x0)
                vec_rns = fun(vec_x)
            except ValueError:
                ok = False
            else:
                ok = True
            if not ok:
                output('Navier-Stokes residual computation failed!')
                err_ns = err_ns0 = None
            else:
                err_ns0 = nla.norm(vec_rns0)
                err_ns = nla.norm(vec_rns)
            output('Navier-Stokes residual0: %.8e' % err_ns0)
            output('Navier-Stokes residual : %.8e' % err_ns)
            output('b - u: %.8e' % nla.norm(vec_b - vec_u))
            output(condition)

        else:
            err_ns = None

        if status is not None:
            status['time_stats'] = time_stats
            status['err0'] = err0
            status['err'] = err
            status['err_ns'] = err_ns
            status['condition'] = condition

        if conf.log.plot is not None:
            if self.log is not None:
                self.log(save_figure=conf.log.plot)

        return vec_x
예제 #16
0
class Newton(NonlinearSolver):
    r"""
    Solves a nonlinear system :math:`f(x) = 0` using the Newton method with
    backtracking line-search, starting with an initial guess :math:`x^0`.

    For common configuration parameters, see :class:`Solver
    <sfepy.solvers.solvers.Solver>`.

    Parameters
    ----------
    i_max : int
        The maximum number of iterations.
    eps_a : float
        The absolute tolerance for the residual, i.e. :math:`||f(x^i)||`.
    eps_r : float
        The relative tolerance for the residual, i.e. :math:`||f(x^i)|| /
        ||f(x^0)||`.
    macheps : float
        The float considered to be machine "zero".
    lin_red : float
        The linear system solution error should be smaller than (`eps_a` *
        `lin_red`), otherwise a warning is printed.
    lin_precision : float or None
        If not None, the linear system solution tolerances are set in each
        nonlinear iteration relative to the current residual norm by the
        `lin_precision` factor. Ignored for direct linear solvers.
    ls_on : float
        Start the backtracking line-search by reducing the step, if
        :math:`||f(x^i)|| / ||f(x^{i-1})||` is larger than `ls_on`.
    ls_red : 0.0 < float < 1.0
        The step reduction factor in case of correct residual assembling.
    ls_red_warp : 0.0 < float < 1.0
        The step reduction factor in case of failed residual assembling
        (e.g. the "warp violation" error caused by a negative volume element
        resulting from too large deformations).
    ls_min : 0.0 < float < 1.0
        The minimum step reduction factor.
    give_up_warp : bool
        If True, abort on the "warp violation" error.
    check : 0, 1 or 2
        If >= 1, check the tangent matrix using finite differences. If 2, plot
        the resulting sparsity patterns.
    delta : float
        If `check >= 1`, the finite difference matrix is taken as :math:`A_{ij}
        = \frac{f_i(x_j + \delta) - f_i(x_j - \delta)}{2 \delta}`.
    is_plot : False
        If True, plot the solution and residual in each step.
    log : dict or None
        If not None, log the convergence according to the configuration in the
        following form::

            {'text' : 'log.txt', 'plot' : 'log.pdf'}

        Each of the dict items can be None.
    problem : 'nonlinear' or 'linear'
        Specifies if the problem is linear or non-linear.
    """
    name = 'nls.newton'

    @staticmethod
    def process_conf(conf, kwargs):
        """
        Missing items are set to default values for a linear problem.

        Example configuration, all items::

            solver_1 = {
                'name' : 'newton',
                'kind' : 'nls.newton',

                'i_max' : 2,
                'eps_a' : 1e-8,
                'eps_r' : 1e-2,
                'macheps' : 1e-16,
                'lin_red' : 1e-2, # Linear system error < (eps_a * lin_red).
                'lin_precision' : None,
                'ls_on' : 0.99999,
                'ls_red' : 0.1,
                'ls_red_warp' : 0.001,
                'ls_min' : 1e-5,
                'give_up_warp' : False,
                'check' : 0,
                'delta' : 1e-6,
                'is_plot' : False,
                'log' : None, # 'nonlinear' or 'linear' (ignore i_max)
                'problem' : 'nonlinear',
            }
        """
        get = make_get_conf(conf, kwargs)
        common = NonlinearSolver.process_conf(conf)

        log = get_logging_conf(conf)
        log = Struct(name='log_conf', **log)
        is_any_log = (log.text is not None) or (log.plot is not None)

        return Struct(i_max=get('i_max', 1),
                      eps_a=get('eps_a', 1e-10),
                      eps_r=get('eps_r', 1.0),
                      macheps=get('macheps', nm.finfo(nm.float64).eps),
                      lin_red=get('lin_red', 1.0),
                      lin_precision=get('lin_precision', None),
                      ls_red=get('ls_red', 0.1),
                      ls_red_warp=get('ls_red_warp', 0.001),
                      ls_on=get('ls_on', 0.99999),
                      ls_min=get('ls_min', 1e-5),
                      give_up_warp=get('give_up_warp', False),
                      check=get('check', 0),
                      delta=get('delta', 1e-6),
                      is_plot=get('is_plot', False),
                      problem=get('problem', 'nonlinear'),
                      log=log,
                      is_any_log=is_any_log) + common

    def __init__(self, conf, **kwargs):
        NonlinearSolver.__init__( self, conf, **kwargs )

        conf = self.conf
        if conf.is_any_log:
            self.log = Log([[r'$||r||$'], ['iteration']],
                           xlabels=['', 'all iterations'],
                           ylabels=[r'$||r||$', 'iteration'],
                           yscales=['log', 'linear'],
                           is_plot=conf.log.plot is not None,
                           log_filename=conf.log.text,
                           formats=[['%.8e'], ['%d']])

        else:
            self.log = None

    def __call__(self, vec_x0, conf=None, fun=None, fun_grad=None,
                 lin_solver=None, iter_hook=None, status=None):
        """
        Nonlinear system solver call.

        Solves a nonlinear system :math:`f(x) = 0` using the Newton method with
        backtracking line-search, starting with an initial guess :math:`x^0`.

        Parameters
        ----------
        vec_x0 : array
            The initial guess vector :math:`x_0`.
        conf : Struct instance, optional
            The solver configuration parameters,
        fun : function, optional
            The function :math:`f(x)` whose zero is sought - the residual.
        fun_grad : function, optional
            The gradient of :math:`f(x)` - the tangent matrix.
        lin_solver : LinearSolver instance, optional
            The linear solver for each nonlinear iteration.
        iter_hook : function, optional
            User-supplied function to call before each iteration.
        status : dict-like, optional
            The user-supplied object to hold convergence statistics.

        Notes
        -----
        * The optional parameters except `iter_hook` and `status` need
          to be given either here or upon `Newton` construction.
        * Setting `conf.problem == 'linear'` means a pre-assembled and possibly
          pre-solved matrix. This is mostly useful for linear time-dependent
          problems.
        """
        import sfepy.base.plotutils as plu
        conf = get_default( conf, self.conf )
        fun = get_default( fun, self.fun )
        fun_grad = get_default( fun_grad, self.fun_grad )
        lin_solver = get_default( lin_solver, self.lin_solver )
        iter_hook = get_default(iter_hook, self.iter_hook)
        status = get_default( status, self.status )

        ls_eps_a, ls_eps_r = lin_solver.get_tolerance()
        eps_a = get_default(ls_eps_a, 1.0)
        eps_r = get_default(ls_eps_r, 1.0)
        lin_red = conf.eps_a * conf.lin_red

        time_stats = {}

        vec_x = vec_x0.copy()
        vec_x_last = vec_x0.copy()
        vec_dx = None

        if self.log is not None:
            self.log.plot_vlines(color='r', linewidth=1.0)

        err = err0 = -1.0
        err_last = -1.0
        it = 0
        while 1:
            if iter_hook is not None:
                iter_hook(self, vec_x, it, err, err0)

            ls = 1.0
            vec_dx0 = vec_dx;
            while 1:
                tt = time.clock()

                try:
                    vec_r = fun( vec_x )

                except ValueError:
                    if (it == 0) or (ls < conf.ls_min):
                        output('giving up!')
                        raise

                    else:
                        ok = False

                else:
                    ok = True

                time_stats['rezidual'] = time.clock() - tt
                if ok:
                    try:
                        err = nla.norm( vec_r )
                    except:
                        output( 'infs or nans in the residual:', vec_r )
                        output( nm.isfinite( vec_r ).all() )
                        debug()

                    if self.log is not None:
                        self.log(err, it)

                    if it == 0:
                        err0 = err;
                        break
                    if err < (err_last * conf.ls_on): break
                    red = conf.ls_red;
                    output( 'linesearch: iter %d, (%.5e < %.5e) (new ls: %e)'\
                            % (it, err, err_last * conf.ls_on, red * ls) )
                else: # Failure.
                    if conf.give_up_warp:
                        output('giving up!')
                        break

                    red = conf.ls_red_warp;
                    output(  'rezidual computation failed for iter %d'
                             ' (new ls: %e)!' % (it, red * ls) )

                if ls < conf.ls_min:
                    output( 'linesearch failed, continuing anyway' )
                    break

                ls *= red;

                vec_dx = ls * vec_dx0;
                vec_x = vec_x_last.copy() - vec_dx
            # End residual loop.

            if self.log is not None:
                self.log.plot_vlines([1], color='g', linewidth=0.5)

            err_last = err;
            vec_x_last = vec_x.copy()

            condition = conv_test( conf, it, err, err0 )
            if condition >= 0:
                break

            if (not ok) and conf.give_up_warp:
                condition = 2
                break

            tt = time.clock()
            if conf.problem == 'nonlinear':
                mtx_a = fun_grad(vec_x)

            else:
                mtx_a = fun_grad( 'linear' )

            time_stats['matrix'] = time.clock() - tt

            if conf.check:
                tt = time.clock()
                wt = check_tangent_matrix( conf, vec_x, fun, fun_grad )
                time_stats['check'] = time.clock() - tt - wt

            if conf.lin_precision is not None:
                if ls_eps_a is not None:
                    eps_a = max(err * conf.lin_precision, ls_eps_a)

                elif ls_eps_r is not None:
                    eps_r = max(conf.lin_precision, ls_eps_r)

                lin_red = max(eps_a, err * eps_r)

            if conf.verbose:
                output('solving linear system...')

            tt = time.clock()
            vec_dx = lin_solver(vec_r, x0=vec_x,
                                eps_a=eps_a, eps_r=eps_r, mtx=mtx_a)
            time_stats['solve'] = time.clock() - tt

            if conf.verbose:
                output('...done')

            for kv in time_stats.iteritems():
                output( '%10s: %7.2f [s]' % kv )

            vec_e = mtx_a * vec_dx - vec_r
            lerr = nla.norm( vec_e )
            if lerr > lin_red:
                output('linear system not solved! (err = %e < %e)'
                       % (lerr, lin_red))

            vec_x -= vec_dx

            if conf.is_plot:
                plu.plt.ion()
                plu.plt.gcf().clear()
                plu.plt.subplot( 2, 2, 1 )
                plu.plt.plot( vec_x_last )
                plu.plt.ylabel( r'$x_{i-1}$' )
                plu.plt.subplot( 2, 2, 2 )
                plu.plt.plot( vec_r )
                plu.plt.ylabel( r'$r$' )
                plu.plt.subplot( 2, 2, 4 )
                plu.plt.plot( vec_dx )
                plu.plt.ylabel( r'$\_delta x$' )
                plu.plt.subplot( 2, 2, 3 )
                plu.plt.plot( vec_x )
                plu.plt.ylabel( r'$x_i$' )
                plu.plt.draw()
                plu.plt.ioff()
                pause()

            it += 1

        if status is not None:
            status['time_stats'] = time_stats
            status['err0'] = err0
            status['err'] = err
            status['n_iter'] = it
            status['condition'] = condition

        if conf.log.plot is not None:
            if self.log is not None:
                self.log(save_figure=conf.log.plot)

        return vec_x
예제 #17
0
class FMinSteepestDescent(OptimizationSolver):
    name = 'opt.fmin_sd'

    @staticmethod
    def process_conf(conf, kwargs):
        """
        Missing items are set to default values.

        Example configuration, all items::

            solver_0 = {
                'name'      : 'fmin_sd',
                'kind'      : 'opt.fmin_sd',

                'i_max'      : 10,
                'eps_rd'     : 1e-5, # Relative delta of objective function
                'eps_of'     : 1e-4,
                'eps_ofg'    : 1e-8,
                'norm'      : nm.Inf,
                'ls'        : True, # Linesearch.
                'ls_method'  : 'backtracking', # 'backtracking' or 'full'
                'ls0'       : 0.25,
                'ls_red'     : 0.5,
                'ls_red_warp' : 0.1,
                'ls_on'      : 0.99999,
                'ls_min'     : 1e-5,
                'check'     : 0,
                'delta'     : 1e-6,
                'output'    : None, # 'itc'
                'log'       : {'text' : 'output/log.txt',
                               'plot' : 'output/log.png'},
                'yscales'   : ['linear', 'log', 'log', 'linear'],
            }
        """
        get = make_get_conf(conf, kwargs)
        common = OptimizationSolver.process_conf(conf)

        log = get_logging_conf(conf)
        log = Struct(name='log_conf', **log)
        is_any_log = (log.text is not None) or (log.plot is not None)

        return Struct(i_max=get('i_max', 10),
                      eps_rd=get('eps_rd', 1e-5),
                      eps_of=get('eps_of', 1e-4),
                      eps_ofg=get('eps_ofg', 1e-8),
                      norm=get('norm', nm.Inf),
                      ls=get('ls', True),
                      ls_method=get('ls_method', 'backtracking'),
                      ls0=get('ls0', 0.25),
                      ls_red=get('ls_red', 0.5),
                      ls_red_warp=get('ls_red_warp', 0.1),
                      ls_on=get('ls_on', 0.99999),
                      ls_min=get('ls_min', 1e-5),
                      check=get('check', 0),
                      delta=get('delta', 1e-6),
                      output=get('output', None),
                      yscales=get('yscales',
                                  ['linear', 'log', 'log', 'linear']),
                      log=log,
                      is_any_log=is_any_log) + common

    ##
    # 17.10.2007, c
    def __init__(self, conf, **kwargs):
        OptimizationSolver.__init__(self, conf, **kwargs)

        conf = self.conf
        if conf.is_any_log:
            self.log = Log(
                [[r'$||\Psi||$'], [r'$||\nabla \Psi||$'], [r'$\alpha$'],
                 ['iteration']],
                xlabels=['', '', 'all iterations', 'all iterations'],
                yscales=conf.yscales,
                is_plot=conf.log.plot is not None,
                log_filename=conf.log.text,
                formats=[['%.8e'], ['%.3e'], ['%.3e'], ['%d']])
        else:
            self.log = None

    ##
    # 19.04.2006, c
    # 20.04.2006
    # 21.04.2006
    # 26.04.2006
    # 06.06.2006
    # 07.06.2006
    # 04.09.2006
    # 21.03.2007
    # 17.10.2007, from fmin_sd()
    def __call__(self,
                 x0,
                 conf=None,
                 obj_fun=None,
                 obj_fun_grad=None,
                 status=None,
                 obj_args=None):
        #    def fmin_sd( conf, x0, fn_of, fn_ofg, args = () ):

        conf = get_default(conf, self.conf)
        obj_fun = get_default(obj_fun, self.obj_fun)
        obj_fun_grad = get_default(obj_fun_grad, self.obj_fun_grad)
        status = get_default(status, self.status)
        obj_args = get_default(obj_args, self.obj_args)

        if conf.output:
            globals()['output'] = conf.output

        output('entering optimization loop...')

        nc_of, tt_of, fn_of = wrap_function(obj_fun, obj_args)
        nc_ofg, tt_ofg, fn_ofg = wrap_function(obj_fun_grad, obj_args)

        time_stats = {'of': tt_of, 'ofg': tt_ofg, 'check': []}

        ofg = None

        it = 0
        xit = x0.copy()
        while 1:

            of = fn_of(xit)

            if it == 0:
                of0 = ofit0 = of_prev = of
                of_prev_prev = of + 5000.0

            if ofg is None:
                ofg = fn_ofg(xit)

            if conf.check:
                tt = time.clock()
                check_gradient(xit, ofg, fn_of, conf.delta, conf.check)
                time_stats['check'].append(time.clock() - tt)

            ofg_norm = nla.norm(ofg, conf.norm)

            ret = conv_test(conf, it, of, ofit0, ofg_norm)
            if ret >= 0:
                break
            ofit0 = of

            ##
            # Backtrack (on errors).
            alpha = conf.ls0
            can_ls = True
            while 1:
                xit2 = xit - alpha * ofg
                aux = fn_of(xit2)

                if self.log is not None:
                    self.log(of, ofg_norm, alpha, it)

                if aux is None:
                    alpha *= conf.ls_red_warp
                    can_ls = False
                    output('warp: reducing step (%f)' % alpha)
                elif conf.ls and conf.ls_method == 'backtracking':
                    if aux < of * conf.ls_on: break
                    alpha *= conf.ls_red
                    output('backtracking: reducing step (%f)' % alpha)
                else:
                    of_prev_prev = of_prev
                    of_prev = aux
                    break

                if alpha < conf.ls_min:
                    if aux is None:
                        raise RuntimeError, 'giving up...'
                    output('linesearch failed, continuing anyway')
                    break

            # These values are modified by the line search, even if it fails
            of_prev_bak = of_prev
            of_prev_prev_bak = of_prev_prev

            if conf.ls and can_ls and conf.ls_method == 'full':
                output('full linesearch...')
                alpha, fc, gc, of_prev, of_prev_prev, ofg1 = \
                       linesearch.line_search(fn_of,fn_ofg,xit,
                                              -ofg,ofg,of_prev,of_prev_prev,
                                              c2=0.4)
                if alpha is None:  # line search failed -- use different one.
                    alpha, fc, gc, of_prev, of_prev_prev, ofg1 = \
                           sopt.line_search(fn_of,fn_ofg,xit,
                                            -ofg,ofg,of_prev_bak,
                                            of_prev_prev_bak)
                    if alpha is None or alpha == 0:
                        # This line search also failed to find a better solution.
                        ret = 3
                        break
                output(' -> alpha: %.8e' % alpha)
            else:
                if conf.ls_method == 'full':
                    output('full linesearch off (%s and %s)' %
                           (conf.ls, can_ls))
                ofg1 = None

            if self.log is not None:
                self.log.plot_vlines(color='g', linewidth=0.5)

            xit = xit - alpha * ofg
            if ofg1 is None:
                ofg = None
            else:
                ofg = ofg1.copy()

            for key, val in time_stats.iteritems():
                if len(val):
                    output('%10s: %7.2f [s]' % (key, val[-1]))

            it = it + 1

        output('status:               %d' % ret)
        output('initial value:        %.8e' % of0)
        output('current value:        %.8e' % of)
        output('iterations:           %d' % it)
        output( 'function evaluations: %d in %.2f [s]' \
              % (nc_of[0], nm.sum( time_stats['of'] ) ) )
        output( 'gradient evaluations: %d in %.2f [s]' \
              % (nc_ofg[0], nm.sum( time_stats['ofg'] ) ) )

        if self.log is not None:
            self.log(of, ofg_norm, alpha, it)

            if conf.log.plot is not None:
                self.log(save_figure=conf.log.plot, finished=True)
            else:
                self.log(finished=True)

        if status is not None:
            status['log'] = self.log
            status['status'] = status
            status['of0'] = of0
            status['of'] = of
            status['it'] = it
            status['nc_of'] = nc_of[0]
            status['nc_ofg'] = nc_ofg[0]
            status['time_stats'] = time_stats

        return xit