Ejemplo n.º 1
0
def reverse_info(ds):
    dsn = []
    for dp in ds:
        dsn.append(
            OptimizeResult(f=np.flipud(dp.f),
                           x=np.flipud(dp.x),
                           g=np.flipud(dp.g)))
    return dsn
Ejemplo n.º 2
0
 def optimize_objective_on_Line_segment(self, start_point, end_point, iteration, blended_point, previous_end,
         ratio=0.5, limit=1000):
     f = self.objective
     if self.blend and not np.allclose(blended_point, end_point):
         start_point = blended_point
     vertex_ratio = self.vertex_ratio
     if vertex_ratio is not None and previous_end is not None:
         # mix the current and previous end points
         mixed_end = end_point + vertex_ratio * (previous_end - end_point)
         if not np.allclose(mixed_end, start_point) and f(mixed_end) < f(end_point):
             end_point = mixed_end
     left = start_point
     right = end_point
     f_left = f(left)
     f_right = f(right)
     count = 0
     done = False
     convex_ok = True
     while convex_ok and not done:
         count += 1
         middle = left + ratio * (right - left)
         f_middle = f(middle)
         if f_middle <= f_right:
             right = middle
             f_right = f_middle
         elif f_middle > f_left:
             convex_ok = False
         done = np.allclose(left, right) or f_right < f_left
         if count > limit:
             break
     if not done and self.scalar_fallback:
         return self.optimize_objective_on_Line_segment0(start_point, end_point, iteration, blended_point, previous_end)
     res = OptimizeResult()
     res.success = done
     res.start = start_point
     res.end = end_point
     res.x = right
     if not done:
         if not convex_ok:
             res.message = "objective is not convex on line segment"
         else:
             res.message = "limit reached seeking improvement on line segment"
     else:
         res.message = "improvement found"
     return (right, res, done)
Ejemplo n.º 3
0
def _minimize_neldermead(func,
                         x0,
                         args=(),
                         callback=None,
                         xtol=1e-4,
                         ftol=1e-4,
                         maxiter=None,
                         maxfev=None,
                         disp=False,
                         return_all=False,
                         **unknown_options):
    """
    Minimization of scalar function of one or more variables using the
    Nelder-Mead algorithm.

    Options
    -------
    disp : bool
        Set to True to print convergence messages.
    xtol : float
        Relative error in solution `xopt` acceptable for convergence.
    ftol : float
        Relative error in ``fun(xopt)`` acceptable for convergence.
    maxiter : int
        Maximum number of iterations to perform.
    maxfev : int
        Maximum number of function evaluations to make.

    """
    # _check_unknown_options(unknown_options)
    maxfun = maxfev
    retall = return_all

    fcalls, func = wrap_function(func, args)
    x0 = asfarray(x0).flatten()
    N = len(x0)
    if maxiter is None:
        maxiter = N * 200
    if maxfun is None:
        maxfun = N * 200

    rho = 1
    chi = 2
    psi = 0.5
    sigma = 0.5
    one2np1 = list(range(1, N + 1))

    sim = numpy.zeros((N + 1, N), dtype=x0.dtype)
    fsim = numpy.zeros((N + 1, ), float)
    sim[0] = x0
    if retall:
        allvecs = [sim[0]]
    fsim[0] = func(x0)
    nonzdelt = 0.05
    zdelt = 0.00025
    for k in range(0, N):
        y = numpy.array(x0, copy=True)
        if y[k] != 0:
            y[k] = (1 + nonzdelt) * y[k]
        else:
            y[k] = zdelt

        sim[k + 1] = y
        f = func(y)
        fsim[k + 1] = f

    ind = numpy.argsort(fsim)
    fsim = numpy.take(fsim, ind, 0)
    # sort so sim[0,:] has the lowest function value
    sim = numpy.take(sim, ind, 0)

    iterations = 1

    while (fcalls[0] < maxfun and iterations < maxiter):
        if (numpy.max(numpy.ravel(numpy.abs(sim[1:] - sim[0]))) <= xtol
                and numpy.max(numpy.abs(fsim[0] - fsim[1:])) <= ftol):
            break

        xbar = numpy.add.reduce(sim[:-1], 0) / N
        xr = (1 + rho) * xbar - rho * sim[-1]
        fxr = func(xr)
        doshrink = 0

        if fxr < fsim[0]:
            xe = (1 + rho * chi) * xbar - rho * chi * sim[-1]
            fxe = func(xe)

            if fxe < fxr:
                sim[-1] = xe
                fsim[-1] = fxe
            else:
                sim[-1] = xr
                fsim[-1] = fxr
        else:  # fsim[0] <= fxr
            if fxr < fsim[-2]:
                sim[-1] = xr
                fsim[-1] = fxr
            else:  # fxr >= fsim[-2]
                # Perform contraction
                if fxr < fsim[-1]:
                    xc = (1 + psi * rho) * xbar - psi * rho * sim[-1]
                    fxc = func(xc)

                    if fxc <= fxr:
                        sim[-1] = xc
                        fsim[-1] = fxc
                    else:
                        doshrink = 1
                else:
                    # Perform an inside contraction
                    xcc = (1 - psi) * xbar + psi * sim[-1]
                    fxcc = func(xcc)

                    if fxcc < fsim[-1]:
                        sim[-1] = xcc
                        fsim[-1] = fxcc
                    else:
                        doshrink = 1

                if doshrink:
                    for j in one2np1:
                        sim[j] = sim[0] + sigma * (sim[j] - sim[0])
                        fsim[j] = func(sim[j])

        ind = numpy.argsort(fsim)
        sim = numpy.take(sim, ind, 0)
        fsim = numpy.take(fsim, ind, 0)
        if callback is not None:
            callback(sim[0])
        iterations += 1
        if retall:
            allvecs.append(sim[0])

    x = sim[0]
    fval = numpy.min(fsim)
    warnflag = 0

    if fcalls[0] >= maxfun:
        warnflag = 1
        msg = _status_message['maxfev']
        if disp:
            print('Warning: ' + msg)
    elif iterations >= maxiter:
        warnflag = 2
        msg = _status_message['maxiter']
        if disp:
            print('Warning: ' + msg)
    else:
        msg = _status_message['success']
        if disp:
            print(msg)
            print("         Current function value: %f" % fval)
            print("         Iterations: %d" % iterations)
            print("         Function evaluations: %d" % fcalls[0])

    result = OptimizeResult(fun=fval,
                            nit=iterations,
                            nfev=fcalls[0],
                            status=warnflag,
                            success=(warnflag == 0),
                            message=msg,
                            x=x,
                            final_simplex=(sim, fsim))
    if retall:
        result['allvecs'] = allvecs
    return result
Ejemplo n.º 4
0
def _minimize_slsqp(func,
                    x0,
                    args=(),
                    jac=None,
                    bounds=None,
                    constraints=(),
                    maxiter=100,
                    ftol=1.0E-6,
                    iprint=1,
                    disp=False,
                    eps=_epsilon,
                    callback=None,
                    **unknown_options):
    """
    Minimize a scalar function of one or more variables using Sequential
    Least SQuares Programming (SLSQP).
    Options
    -------
    ftol : float
        Precision goal for the value of f in the stopping criterion.
    eps : float
        Step size used for numerical approximation of the jacobian.
    disp : bool
        Set to True to print convergence messages. If False,
        `verbosity` is ignored and set to 0.
    maxiter : int
        Maximum number of iterations.
    """
    _check_unknown_options(unknown_options)
    fprime = jac
    iter = maxiter
    acc = ftol
    epsilon = eps

    if not disp:
        iprint = 0

    # Constraints are triaged per type into a dictionnary of tuples
    if isinstance(constraints, dict):
        constraints = (constraints, )

    cons = {'eq': (), 'ineq': ()}
    for ic, con in enumerate(constraints):
        # check type
        try:
            ctype = con['type'].lower()
        except KeyError:
            raise KeyError('Constraint %d has no type defined.' % ic)
        except TypeError:
            raise TypeError('Constraints must be defined using a '
                            'dictionary.')
        except AttributeError:
            raise TypeError("Constraint's type must be a string.")
        else:
            if ctype not in ['eq', 'ineq']:
                raise ValueError("Unknown constraint type '%s'." % con['type'])

        # check function
        if 'fun' not in con:
            raise ValueError('Constraint %d has no function defined.' % ic)

        # check jacobian
        cjac = con.get('jac')
        if cjac is None:
            # approximate jacobian function.  The factory function is needed
            # to keep a reference to `fun`, see gh-4240.
            def cjac_factory(fun):
                def cjac(x, *args):
                    return approx_jacobian(x, fun, epsilon, *args)

                return cjac

            cjac = cjac_factory(con['fun'])

        # update constraints' dictionary
        cons[ctype] += ({
            'fun': con['fun'],
            'jac': cjac,
            'args': con.get('args', ())
        }, )

    exit_modes = {
        -1: "Gradient evaluation required (g & a)",
        0: "Optimization terminated successfully.",
        1: "Function evaluation required (f & c)",
        2: "More equality constraints than independent variables",
        3: "More than 3*n iterations in LSQ subproblem",
        4: "Inequality constraints incompatible",
        5: "Singular matrix E in LSQ subproblem",
        6: "Singular matrix C in LSQ subproblem",
        7: "Rank-deficient equality constraint subproblem HFTI",
        8: "Positive directional derivative for linesearch",
        9: "Iteration limit exceeded"
    }

    # Wrap func
    feval, func = wrap_function(func, args)

    # Wrap fprime, if provided, or approx_jacobian if not
    if fprime:
        geval, fprime = wrap_function(fprime, args)
    else:
        geval, fprime = wrap_function(approx_jacobian, (func, epsilon))

    # Transform x0 into an array.
    x = asfarray(x0).flatten()

    # Set the parameters that SLSQP will need
    # meq, mieq: number of equality and inequality constraints
    meq = sum(
        map(len, [atleast_1d(c['fun'](x, *c['args'])) for c in cons['eq']]))
    mieq = sum(
        map(len, [atleast_1d(c['fun'](x, *c['args'])) for c in cons['ineq']]))
    # m = The total number of constraints
    m = meq + mieq
    # la = The number of constraints, or 1 if there are no constraints
    la = array([1, m]).max()
    # n = The number of independent variables
    n = len(x)

    # Define the workspaces for SLSQP
    n1 = n + 1
    mineq = m - meq + n1 + n1
    len_w = (3*n1+m)*(n1+1)+(n1-meq+1)*(mineq+2) + 2*mineq+(n1+mineq)*(n1-meq) \
            + 2*meq + n1 + ((n+1)*n)//2 + 2*m + 3*n + 3*n1 + 1
    len_jw = mineq
    w = zeros(len_w)
    jw = zeros(len_jw)

    # Decompose bounds into xl and xu
    if bounds is None or len(bounds) == 0:
        xl = np.empty(n, dtype=float)
        xu = np.empty(n, dtype=float)
        xl.fill(np.nan)
        xu.fill(np.nan)
    else:
        bnds = array(bounds, float)
        if bnds.shape[0] != n:
            raise IndexError('SLSQP Error: the length of bounds is not '
                             'compatible with that of x0.')

        with np.errstate(invalid='ignore'):
            bnderr = bnds[:, 0] > bnds[:, 1]

        if bnderr.any():
            raise ValueError('SLSQP Error: lb > ub in bounds %s.' %
                             ', '.join(str(b) for b in bnderr))
        xl, xu = bnds[:, 0], bnds[:, 1]

        # Mark infinite bounds with nans; the Fortran code understands this
        infbnd = ~isfinite(bnds)
        xl[infbnd[:, 0]] = np.nan
        xu[infbnd[:, 1]] = np.nan

    # Clip initial guess to bounds (SLSQP may fail with bounds-infeasible initial point)
    have_bound = np.isfinite(xl)
    x[have_bound] = np.clip(x[have_bound], xl[have_bound], np.inf)
    have_bound = np.isfinite(xu)
    x[have_bound] = np.clip(x[have_bound], -np.inf, xu[have_bound])

    # Initialize the iteration counter and the mode value
    mode = array(0, int)
    acc = array(acc, float)
    majiter = array(iter, int)
    majiter_prev = 0

    # Print the header if iprint >= 2
    if iprint >= 2:
        print("%5s %5s %16s %16s" % ("NIT", "FC", "OBJFUN", "GNORM"))

    while 1:

        if mode == 0 or mode == 1:  # objective and constraint evaluation requird

            # Compute objective function
            fx = func(x)
            try:
                fx = float(np.asarray(fx))
            except (TypeError, ValueError):
                raise ValueError("Objective function must return a scalar")
            # Compute the constraints
            if cons['eq']:
                c_eq = concatenate([
                    atleast_1d(con['fun'](x, *con['args']))
                    for con in cons['eq']
                ])
            else:
                c_eq = zeros(0)
            if cons['ineq']:
                c_ieq = concatenate([
                    atleast_1d(con['fun'](x, *con['args']))
                    for con in cons['ineq']
                ])
            else:
                c_ieq = zeros(0)

            # Now combine c_eq and c_ieq into a single matrix
            c = concatenate((c_eq, c_ieq))

        if mode == 0 or mode == -1:  # gradient evaluation required

            # Compute the derivatives of the objective function
            # For some reason SLSQP wants g dimensioned to n+1
            g = append(fprime(x), 0.0)

            # Compute the normals of the constraints
            if cons['eq']:
                a_eq = vstack(
                    [con['jac'](x, *con['args']) for con in cons['eq']])
            else:  # no equality constraint
                a_eq = zeros((meq, n))

            if cons['ineq']:
                a_ieq = vstack(
                    [con['jac'](x, *con['args']) for con in cons['ineq']])
            else:  # no inequality constraint
                a_ieq = zeros((mieq, n))

            # Now combine a_eq and a_ieq into a single a matrix
            if m == 0:  # no constraints
                a = zeros((la, n))
            else:
                a = vstack((a_eq, a_ieq))
            a = concatenate((a, zeros([la, 1])), 1)

        # Call SLSQP
        slsqp(m, meq, x, xl, xu, fx, c, g, a, acc, majiter, mode, w, jw)

        # call callback if major iteration has incremented
        if callback is not None and majiter > majiter_prev:
            callback(x)

        # Print the status of the current iterate if iprint > 2 and the
        # major iteration has incremented
        if iprint >= 2 and majiter > majiter_prev:
            print("%5i %5i % 16.6E % 16.6E" %
                  (majiter, feval[0], fx, linalg.norm(g)))

        # If exit mode is not -1 or 1, slsqp has completed
        if abs(mode) != 1:
            break

        majiter_prev = int(majiter)

    # Optimization loop complete.  Print status if requested
    if iprint >= 1:
        print(exit_modes[int(mode)] + "    (Exit mode " + str(mode) + ')')
        print("            Current function value:", fx)
        print("            Iterations:", majiter)
        print("            Function evaluations:", feval[0])
        print("            Gradient evaluations:", geval[0])

    return OptimizeResult(x=x,
                          fun=fx,
                          jac=g[:-1],
                          nit=int(majiter),
                          nfev=feval[0],
                          njev=geval[0],
                          status=int(mode),
                          message=exit_modes[int(mode)],
                          success=(mode == 0))
Ejemplo n.º 5
0
def _root_hybr(func,
               x0,
               args=(),
               jac=None,
               col_deriv=0,
               xtol=1.49012e-08,
               maxfev=0,
               band=None,
               eps=None,
               factor=100,
               diag=None,
               **unknown_options):
    """
    Find the roots of a multivariate function using MINPACK's hybrd and
    hybrj routines (modified Powell method).

    Options
    -------
    col_deriv : bool
        Specify whether the Jacobian function computes derivatives down
        the columns (faster, because there is no transpose operation).
    xtol : float
        The calculation will terminate if the relative error between two
        consecutive iterates is at most `xtol`.
    maxfev : int
        The maximum number of calls to the function. If zero, then
        ``100*(N+1)`` is the maximum where N is the number of elements
        in `x0`.
    band : tuple
        If set to a two-sequence containing the number of sub- and
        super-diagonals within the band of the Jacobi matrix, the
        Jacobi matrix is considered banded (only for ``fprime=None``).
    eps : float
        A suitable step length for the forward-difference
        approximation of the Jacobian (for ``fprime=None``). If
        `eps` is less than the machine precision, it is assumed
        that the relative errors in the functions are of the order of
        the machine precision.
    factor : float
        A parameter determining the initial step bound
        (``factor * || diag * x||``).  Should be in the interval
        ``(0.1, 100)``.
    diag : sequence
        N positive entries that serve as a scale factors for the
        variables.

    """
    _check_unknown_options(unknown_options)
    epsfcn = eps

    x0 = asarray(x0).flatten()
    n = len(x0)
    if not isinstance(args, tuple):
        args = (args, )
    shape, dtype = _check_func('fsolve', 'func', func, x0, args, n, (n, ))
    if epsfcn is None:
        epsfcn = finfo(dtype).eps
    Dfun = jac
    if Dfun is None:
        if band is None:
            ml, mu = -10, -10
        else:
            ml, mu = band[:2]
        if maxfev == 0:
            maxfev = 200 * (n + 1)
        retval = _minpack._hybrd(func, x0, args, 1, xtol, maxfev, ml, mu,
                                 epsfcn, factor, diag)
    else:
        _check_func('fsolve', 'fprime', Dfun, x0, args, n, (n, n))
        if (maxfev == 0):
            maxfev = 100 * (n + 1)
        retval = _minpack._hybrj(func, Dfun, x0, args, 1, col_deriv, xtol,
                                 maxfev, factor, diag)

    x, status = retval[0], retval[-1]

    errors = {
        0:
        "Improper input parameters were entered.",
        1:
        "The solution converged.",
        2:
        "The number of calls to function has "
        "reached maxfev = %d." % maxfev,
        3:
        "xtol=%f is too small, no further improvement "
        "in the approximate\n  solution "
        "is possible." % xtol,
        4:
        "The iteration is not making good progress, as measured "
        "by the \n  improvement from the last five "
        "Jacobian evaluations.",
        5:
        "The iteration is not making good progress, "
        "as measured by the \n  improvement from the last "
        "ten iterations.",
        'unknown':
        "An error occurred."
    }

    info = retval[1]
    info['fun'] = info.pop('fvec')
    sol = OptimizeResult(x=x, success=(status == 1), status=status)
    sol.update(info)
    try:
        sol['message'] = errors[status]
    except KeyError:
        sol['message'] = errors['unknown']

    return sol
Ejemplo n.º 6
0
    def solve(self, maximize=False):
        """
        Minimize the objective function f starting from x0.

        :return: x: the optimized parameters
        """
        start_time = time.time()

        if self.verbose:
            if maximize:
                solving = 'Maximizing'
            else:
                solving = 'Minimizing'

            self._write(
                "{} the problem using a {} IOA with the {} direction.\n".
                format(solving, self.alg_type.to_str(), self.dir.to_str()))

        xk = np.array(self.x0)
        self.optimized = False

        # Return values set to Nothing
        self.xs = []
        self.epochs = []
        self.fs = []
        self.fs_full = []
        self.batches = []

        if self.seed != -1:
            np.random.seed(self.seed)

        self.ep = 0
        self.it = 0

        # Initialize the multiplicative factor for the direction
        self.dir.prep_mult_factor(maximize)

        # Initialize the algorithm
        self.alg_type.init_solve()

        # Initialize the Hessian to an identity matrix for BFGS
        # Nothing for the other directions
        Bk = self.dir.init_hessian(self.x0)

        while self.ep < self.max_epochs:

            # Get the function, its gradient and the Hessian
            f, fprime, grad_hess = self.dir.compute_func_and_derivatives(
                self.alg_type.batch, self.alg_type.full_size)

            fk = f(xk)
            gk, Bk = grad_hess(xk, Bk)

            # Add the return values to the arrays
            self.xs.append(xk)
            self.fs.append(fk)
            self.epochs.append(self.ep)
            self.batches.append(self.alg_type.batch)

            sc = self.stop_crit(xk, fk, gk)

            if 0 < sc <= self.thresh and self.alg_type.batch == self.alg_type.full_size:
                if self.verbose:
                    self._write("Algorithm Optimized!\n")
                    self._write("  x* = [{}]\n".format(", ".join(
                        format(x, ".3f") for x in xk)))
                    self._write("  f(x*) = {:.3f}\n".format(fk))

                self.optimized = True
                break

            if self.verbose:
                self._write("Epoch {}:\n".format(self.ep))
                self._write("  xk = [{}]\n".format(", ".join(
                    format(x, ".3f") for x in xk)))
                self._write("  f(xk) = {:.3f}\n".format(fk))
                self._write("  ||gk|| = {:.3E}\n".format(np.linalg.norm(gk)))
                self._write("  stop_crit = {:.3E}\n".format(sc))

            # Get the new value for x_k using either a LineSearch or a TrustRegion algorithm
            xk_new = self.alg_type.update_xk(xk, fk, gk, Bk, f, fprime,
                                             self.dir, self.fs)

            # Update the hessian; only used by BFGS.
            # Return the same Bk for the other directions
            Bk = self.dir.upd_hessian(xk, xk_new, f, fprime, Bk, gk)

            # Make sure xk is still in bounds
            xk = back_to_bounds(xk_new, self.bounds)

            # Update the batch size if we're using an ABS algorithm
            self.dir.batch_changed = self.alg_type.update_batch(
                self.it, fk / self.alg_type.batch)

            # Update the Hybrid direction if needed (does nothing for all other directions)
            self.dir.update_dir(self.alg_type.batch, self.alg_type.full_size)

            if self.verbose:
                self._write('\n')

            # Update the number of epochs
            self.ep += self.alg_type.batch / self.alg_type.full_size
            self.it += 1

        status = 'Algorithm optimized'
        if not self.optimized:
            status = 'Optimum not reached'

        if self.verbose and not self.optimized:

            self._write("Algorithm not fully optimized!\n")
            self._write("  x_n = [{}]\n".format(", ".join(
                format(x, ".3f") for x in xk)))
            self._write("  f(x_n) = {:.3f}\n".format(fk))

        self.opti_time = time.time() - start_time

        # Compute the function value, the gradient and the Hessian one last time.
        fk, gk, Bk = self.dir.compute_final_LL_and_derivatives(
            xk, hessian=self.compute_final_hessian)

        dct = {
            'x': xk,
            'success': self.optimized,
            'status': status,
            'fun': fk,
            'jac': gk,
            'nit': self.it,
            'nep': self.ep,
            'stop_crit': sc,
            'opti_time': self.opti_time
        }

        if self.compute_final_hessian:
            dct['hess'] = Bk

        return OptimizeResult(dct)
Ejemplo n.º 7
0
def minimize_scalar_bounded_alt(func,
                                bounds,
                                xatol=1e-5,
                                maxiter=500,
                                **extra):
    # Adapted from
    # https://github.com/scipy/scipy/blob/v1.5.2/scipy/optimize/optimize.py
    maxfun = maxiter
    x1, x2 = bounds
    assert x1 <= x2

    flag = 0

    sqrt_eps = sqrt(2.2e-16)
    golden_mean = 0.5 * (3.0 - sqrt(5.0))
    a, b = x1, x2
    fulc = a + golden_mean * (b - a)
    nfc, xf = fulc, fulc
    rat = e = 0.0
    x = xf
    fx = func(x)
    num = 1
    fu = inf

    ffulc = fnfc = fx
    xm = 0.5 * (a + b)
    tol1 = sqrt_eps * abs(xf) + xatol / 3.0
    tol2 = 2.0 * tol1

    while abs(xf - xm) > (tol2 - 0.5 * (b - a)):
        golden = 1
        # Check for parabolic fit
        if abs(e) > tol1:
            golden = 0
            r = (xf - nfc) * (fx - ffulc)
            q = (xf - fulc) * (fx - fnfc)
            p = (xf - fulc) * q - (xf - nfc) * r
            q = 2.0 * (q - r)
            if q > 0.0:
                p = -p
            q = abs(q)
            r = e
            e = rat

            # Check for acceptability of parabola
            if ((abs(p) < abs(0.5 * q * r)) and (p > q * (a - xf))
                    and (p < q * (b - xf))):
                rat = (p + 0.0) / q
                x = xf + rat

                if ((x - a) < tol2) or ((b - x) < tol2):
                    si = copysign(1, xm - xf) + ((xm - xf) == 0)
                    rat = tol1 * si
            else:  # do a golden-section step
                golden = 1

        if golden:  # do a golden-section step
            if xf >= xm:
                e = a - xf
            else:
                e = b - xf
            rat = golden_mean * e

        si = copysign(1, rat) + (rat == 0)
        x = xf + si * max(abs(rat), tol1)
        fu = func(x)
        num += 1

        if fu <= fx:
            if x >= xf:
                a = xf
            else:
                b = xf
            fulc, ffulc = nfc, fnfc
            nfc, fnfc = xf, fx
            xf, fx = x, fu
        else:
            if x < xf:
                a = x
            else:
                b = x
            if (fu <= fnfc) or (nfc == xf):
                fulc, ffulc = nfc, fnfc
                nfc, fnfc = x, fu
            elif (fu <= ffulc) or (fulc == xf) or (fulc == nfc):
                fulc, ffulc = x, fu

        xm = 0.5 * (a + b)
        tol1 = sqrt_eps * abs(xf) + xatol / 3.0
        tol2 = 2.0 * tol1

        if num >= maxfun:
            flag = 1
            break

    if isnan(xf) or isnan(fx) or isnan(fu):
        flag = 2

    fval = fx

    result = OptimizeResult(fun=fval,
                            status=flag,
                            success=(flag == 0),
                            message={
                                0: 'Solution found.',
                                1: 'Maximum number of function calls '
                                'reached.',
                                2: _status_message['nan']
                            }.get(flag, ''),
                            x=xf,
                            nfev=num)

    return result
Ejemplo n.º 8
0
def _minimize_lbfgsb_timeup(
                     fun, x0, args=(), jac=None, bounds=None,
                     disp=None, maxcor=10, ftol=2.2204460492503131e-09,
                     gtol=1e-5, eps=1e-8, maxfun=15000, maxiter=15000,
                     iprint=-1, callback=None, maxls=20,
                     t0=None, timeup=float("inf"),
                     **unknown_options): # JFF: added time-up check
    """
    Minimize a scalar function of one or more variables using the L-BFGS-B
    algorithm.

    Options
    -------
    disp : bool
       Set to True to print convergence messages.
    maxcor : int
        The maximum number of variable metric corrections used to
        define the limited memory matrix. (The limited memory BFGS
        method does not store the full hessian but uses this many terms
        in an approximation to it.)
    factr : float
        The iteration stops when ``(f^k -
        f^{k+1})/max{|f^k|,|f^{k+1}|,1} <= factr * eps``, where ``eps``
        is the machine precision, which is automatically generated by
        the code. Typical values for `factr` are: 1e12 for low
        accuracy; 1e7 for moderate accuracy; 10.0 for extremely high
        accuracy.
    ftol : float
        The iteration stops when ``(f^k -
        f^{k+1})/max{|f^k|,|f^{k+1}|,1} <= ftol``.
    gtol : float
        The iteration will stop when ``max{|proj g_i | i = 1, ..., n}
        <= gtol`` where ``pg_i`` is the i-th component of the
        projected gradient.
    eps : float
        Step size used for numerical approximation of the jacobian.
    disp : int
        Set to True to print convergence messages.
    maxfun : int
        Maximum number of function evaluations.
    maxiter : int
        Maximum number of iterations.
    maxls : int, optional
        Maximum number of line search steps (per iteration). Default is 20.

    """
    _check_unknown_options(unknown_options)
    m = maxcor
    epsilon = eps
    pgtol = gtol
    factr = ftol / np.finfo(float).eps

    x0 = asarray(x0).ravel()
    n, = x0.shape

    if bounds is None:
        bounds = [(None, None)] * n
    if len(bounds) != n:
        raise ValueError('length of x0 != length of bounds')
    # unbounded variables must use None, not +-inf, for optimizer to work properly
    bounds = [(None if l == -np.inf else l, None if u == np.inf else u) for l, u in bounds]

    if disp is not None:
        if disp == 0:
            iprint = -1
        else:
            iprint = disp

    n_function_evals, fun = wrap_function(fun, ())
    if jac is None:
        def func_and_grad(x):
            f = fun(x, *args)
            g = _approx_fprime_helper(x, fun, epsilon, args=args, f0=f)
            return f, g
    else:
        def func_and_grad(x):
            f = fun(x, *args)
            g = jac(x, *args)
            return f, g

    nbd = zeros(n, int32)
    low_bnd = zeros(n, float64)
    upper_bnd = zeros(n, float64)
    bounds_map = {(None, None): 0,
                  (1, None): 1,
                  (1, 1): 2,
                  (None, 1): 3}
    for i in range(0, n):
        l, u = bounds[i]
        if l is not None:
            low_bnd[i] = l
            l = 1
        if u is not None:
            upper_bnd[i] = u
            u = 1
        nbd[i] = bounds_map[l, u]

    if not maxls > 0:
        raise ValueError('maxls must be positive.')

    x = array(x0, float64)
    f = array(0.0, float64)
    g = zeros((n,), float64)
    wa = zeros(2*m*n + 5*n + 11*m*m + 8*m, float64)
    iwa = zeros(3*n, int32)
    task = zeros(1, 'S60')
    csave = zeros(1, 'S60')
    lsave = zeros(4, int32)
    isave = zeros(44, int32)
    dsave = zeros(29, float64)

    task[:] = 'START'

    n_iterations = 0
    if t0 is None:
        t0 = time.time()

    time_profile.predicted_inner_loop_func2_duration = 0.0
    while 1:
        # x, f, g, wa, iwa, task, csave, lsave, isave, dsave = \
        _lbfgsb.setulb(m, x, low_bnd, upper_bnd, nbd, f, g, factr,
                       pgtol, wa, iwa, task, iprint, csave, lsave,
                       isave, dsave, maxls)
        task_str = task.tostring()

        # begin EB
        curr_time = time.time()
        predicted_inner_loop_func2_duration = (curr_time + 
            time_profile.maximize_inner_time_profile.mean +
            time_profile.func2_time_profile.mean - t0)
        if predicted_inner_loop_func2_duration > timeup: # JFF: added time-up check
            task[:] = ('STOP: PREDICTED COMPUTATION TIME EXCEEDS LIMIT')
            break
        # end EB
        if task_str.startswith(b'FG'):
            # The minimization routine wants f and g at the current x.
            # Note that interruptions due to maxfun are postponed
            # until the completion of the current minimization iteration.
            # Overwrite f and g:
            f, g = func_and_grad(x)
        elif task_str.startswith(b'NEW_X'):
            # new iteration
            if n_iterations > maxiter:
                task[:] = 'STOP: TOTAL NO. of ITERATIONS EXCEEDS LIMIT'
            elif n_function_evals[0] > maxfun:
                task[:] = ('STOP: TOTAL NO. of f AND g EVALUATIONS '
                           'EXCEEDS LIMIT')
            else:
                n_iterations += 1
                if callback is not None:
                    callback(x)
        else:
            break

        time_profile.predicted_inner_loop_func2_duration = predicted_inner_loop_func2_duration

    task_str = task.tostring().strip(b'\x00').strip()
    if task_str.startswith(b'CONV'):
        warnflag = 0
    elif n_function_evals[0] > maxfun:
        warnflag = 1
    elif n_iterations > maxiter:
        warnflag = 1
    else:
        warnflag = 2

    # These two portions of the workspace are described in the mainlb
    # subroutine in lbfgsb.f. See line 363.
    s = wa[0: m*n].reshape(m, n)
    y = wa[m*n: 2*m*n].reshape(m, n)

    # See lbfgsb.f line 160 for this portion of the workspace.
    # isave(31) = the total number of BFGS updates prior the current iteration;
    n_bfgs_updates = isave[30]

    n_corrs = min(n_bfgs_updates, maxcor)
    hess_inv = LbfgsInvHessProduct(s[:n_corrs], y[:n_corrs])

    return OptimizeResult(fun=f, jac=g, nfev=n_function_evals[0],
                          nit=n_iterations, status=warnflag, message=task_str,
                          x=x, success=(warnflag == 0), hess_inv=hess_inv)
Ejemplo n.º 9
0
    def solve(self, maximize=False):
        """
        Minimize the objective function f starting from x0.

        :return: x: the optimized parameters
        """
        start_time = time.clock()

        if self.verbose:
            if maximize:
                solving = 'Maximizing'
            else:
                solving = 'Minimizing'

            self._write("{} the problem using Newton Method\n".format(solving))

        xk = np.asarray(self.x0).flatten()

        self.ep = 0
        self.it = 0
        I = np.eye(len(self.x0))
        Bk = I

        mult = 1
        if maximize:
            mult = -1

        if self.biogeme is None:
            fprime = lambda x: mult * self.grad(x)
        else:
            fprime = lambda x: mult * self.biogeme.calculateLikelihoodAndDerivatives(
                x, hessian=False)[1]

        f = lambda x: mult * self.func(x)

        self.xs = []
        self.epochs = []
        self.fs = []

        while self.ep < self.nbr_epochs:

            fk = f(xk)
            gk = fprime(xk)

            self.xs.append(xk)
            self.fs.append(fk)
            self.epochs.append(self.ep)

            if self.verbose:
                self._write("Epoch {}:\n".format(self.ep))
                self._write("  xk = [{}]\n".format(", ".join(
                    format(x, ".3f") for x in xk)))
                self._write("  f(xk) = {:.3f}\n".format(fk))

            step = -np.dot(Bk, gk)

            if self.it > 0:
                old_old_fval = self.fs[-2]
            else:
                old_old_fval = self.fs[-1] + np.linalg.norm(gk) / 2

            alpha = ls_wolfe12(f, fprime, xk, step, gk, self.fs[-1],
                               old_old_fval)

            xkp1 = xk + alpha * step
            sk = xkp1 - xk
            xk = xkp1

            gkp1 = fprime(xkp1)
            yk = gkp1 - gk
            gk = gkp1

            gnorm = np.linalg.norm(gk)
            snorm = np.linalg.norm(step)

            if self.verbose:
                self._write("  ||gk|| = {:.3E}\n".format(gnorm))
                self._write("  ||step|| = {:.3E}\n".format(
                    np.linalg.norm(step)))
                self._write("  alpha = {:.3E}\n".format(alpha))

            if (gnorm <= self.thresh) or (snorm <= self.thresh):
                self.status = 'Optimum reached!'
                if self.verbose:
                    self._write("Algorithm Optimized!\n")
                    self._write("  x* = [{}]\n".format(", ".join(
                        format(x, ".3f") for x in xk)))
                    self._write("  f(x*) = {:.3f}\n".format(fk))

                self.optimized = True
                break

            try:  # this was handled in numeric, let it remains for more safety
                rhok = 1.0 / (np.dot(yk, sk))
            except ZeroDivisionError:
                rhok = 1000.0
            if np.isinf(rhok):  # this is patch for numpy
                rhok = 1000.0
            A1 = I - sk[:, np.newaxis] * yk[np.newaxis, :] * rhok
            A2 = I - yk[:, np.newaxis] * sk[np.newaxis, :] * rhok
            Bk = np.dot(A1, np.dot(
                Bk, A2)) + (rhok * sk[:, np.newaxis] * sk[np.newaxis, :])

            if self.verbose:
                self._write('\n')

            if self.bounds is not None:
                tmp = []
                for i, x in enumerate(xk):
                    if self.bounds[i][1] is not None and x > self.bounds[i][1]:
                        tmp.append(self.bounds[i][1])
                    elif self.bounds[i][
                            0] is not None and x < self.bounds[i][0]:
                        tmp.append(self.bounds[i][0])
                    else:
                        tmp.append(x)
                xk = tmp

            self.it += 1
            self.ep += 1

        if not self.optimized:
            self.status = 'Optimum not reached'

        if self.verbose and not self.optimized:
            self._write("Algorithm not fully optimized!\n")
            self._write("  x_n = [{}]\n".format(", ".join(
                format(x, ".3f") for x in xk)))
            self._write("  f(x_n) = {:.3f}\n".format(fk))

        self.f = mult * f(xk)
        self.x = xk
        gk = fprime(xk)

        self.opti_time = time.clock() - start_time

        dct = {
            'x': self.x,
            'success': self.optimized,
            'status': self.status,
            'fun': self.f,
            'jac': gk,
            'hess': Bk,
            'nit': self.it,
            'nep': self.ep,
            'opti_time': self.opti_time
        }

        return OptimizeResult(dct)
Ejemplo n.º 10
0
def _minimize_trust_region(fun,
                           x0,
                           subproblem=None,
                           initial_trust_radius=1.,
                           max_trust_radius=1000.,
                           eta=0.15,
                           gtol=1e-4,
                           max_iter=None,
                           disp=False,
                           return_all=False,
                           callback=None):
    """
    Minimization of scalar function of one or more variables using a
    trust-region algorithm.

    Options for the trust-region algorithm are:
        initial_trust_radius : float
            Initial trust radius.
        max_trust_radius : float
            Never propose steps that are longer than this value.
        eta : float
            Trust region related acceptance stringency for proposed steps.
        gtol : float
            Gradient norm must be less than `gtol`
            before successful termination.
        max_iter : int
            Maximum number of iterations to perform.
        disp : bool
            If True, print convergence message.

    This function is called by :func:`torchmin.minimize`.
    It is not supposed to be called directly.
    """
    if subproblem is None:
        raise ValueError('A subproblem solving strategy is required for '
                         'trust-region methods')
    if not (0 <= eta < 0.25):
        raise Exception('invalid acceptance stringency')
    if max_trust_radius <= 0:
        raise Exception('the max trust radius must be positive')
    if initial_trust_radius <= 0:
        raise ValueError('the initial trust radius must be positive')
    if initial_trust_radius >= max_trust_radius:
        raise ValueError('the initial trust radius must be less than the '
                         'max trust radius')

    # Input check/pre-process
    disp = int(disp)
    if max_iter is None:
        max_iter = x0.numel() * 200

    # Construct scalar objective function
    hessp = subproblem.hess_prod
    sf = ScalarFunction(fun, x0.shape, hessp=hessp, hess=not hessp)
    closure = sf.closure

    # init the search status
    warnflag = 1  # maximum iterations flag
    k = 0

    # initialize the search
    trust_radius = torch.as_tensor(initial_trust_radius,
                                   dtype=x0.dtype,
                                   device=x0.device)
    x = x0.detach().flatten()
    if return_all:
        allvecs = [x]

    # initial subproblem
    m = subproblem(x, closure)

    # search for the function min
    # do not even start if the gradient is small enough
    while k < max_iter:

        # Solve the sub-problem.
        # This gives us the proposed step relative to the current position
        # and it tells us whether the proposed step
        # has reached the trust region boundary or not.
        try:
            p, hits_boundary = m.solve(trust_radius)
        except RuntimeError as exc:
            # TODO: catch general linalg error like np.linalg.linalg.LinAlgError
            if 'singular' in exc.args[0]:
                warnflag = 3
                break
            else:
                raise

        # calculate the predicted value at the proposed point
        predicted_value = m(p)

        # define the local approximation at the proposed point
        x_proposed = x + p
        m_proposed = subproblem(x_proposed, closure)

        # evaluate the ratio defined in equation (4.4)
        actual_reduction = m.fun - m_proposed.fun
        predicted_reduction = m.fun - predicted_value
        if predicted_reduction <= 0:
            warnflag = 2
            break
        rho = actual_reduction / predicted_reduction

        # update the trust radius according to the actual/predicted ratio
        if rho < 0.25:
            trust_radius = trust_radius.mul(0.25)
        elif rho > 0.75 and hits_boundary:
            trust_radius = torch.clamp(2 * trust_radius, max=max_trust_radius)

        # if the ratio is high enough then accept the proposed step
        if rho > eta:
            x = x_proposed
            m = m_proposed

        # append the best guess, call back, increment the iteration count
        if return_all:
            allvecs.append(x.clone())
        if callback is not None:
            callback(x.clone())
        k += 1

        # verbosity check
        if disp > 1:
            print('iter %d - fval: %0.4f' % (k, m.fun))

        # check if the gradient is small enough to stop
        if m.jac_mag < gtol:
            warnflag = 0
            break

    # print some stuff if requested
    if disp:
        msg = status_messages[warnflag]
        if warnflag != 0:
            msg = 'Warning: ' + msg
        print(msg)
        print("         Current function value: %f" % m.fun)
        print("         Iterations: %d" % k)
        print("         Function evaluations: %d" % sf.nfev)
        # print("         Gradient evaluations: %d" % sf.ngev)
        # print("         Hessian evaluations: %d" % (sf.nhev + nhessp[0]))

    result = OptimizeResult(x=x.view_as(x0),
                            fun=m.fun,
                            grad=m.jac.view_as(x0),
                            success=(warnflag == 0),
                            status=warnflag,
                            nfev=sf.nfev,
                            nit=k,
                            message=status_messages[warnflag])

    if not subproblem.hess_prod:
        result['hess'] = m.hess.view(*x0.shape, *x0.shape)

    if return_all:
        result['allvecs'] = allvecs

    return result
Ejemplo n.º 11
0
def _minimize_lbfgsb_multi(fun, x0, args=(), jac=None, bounds=None,
                           disp=None, maxcor=10, ftol=2.2204460492503131e-09,
                           gtol=1e-5, eps=1e-8, maxfun=15000, maxiter=15000,
                           iprint=-1, callback=None, maxls=20, **unknown_options):
    """
    Minimize a scalar function of one or more variables using the L-BFGS-B
    algorithm.

    Options
    -------
    disp : bool
       Set to True to print convergence messages.
    maxcor : int
        The maximum number of variable metric corrections used to
        define the limited memory matrix. (The limited memory BFGS
        method does not store the full hessian but uses this many terms
        in an approximation to it.)
    ftol : float
        The iteration stops when ``(f^k -
        f^{k+1})/max{|f^k|,|f^{k+1}|,1} <= ftol``.
    gtol : float
        The iteration will stop when ``max{|proj g_i | i = 1, ..., n}
        <= gtol`` where ``pg_i`` is the i-th component of the
        projected gradient.
    eps : float
        Step size used for numerical approximation of the jacobian.
    disp : int
        Set to True to print convergence messages.
    maxfun : int
        Maximum number of function evaluations.
    maxiter : int
        Maximum number of iterations.
    maxls : int, optional
        Maximum number of line search steps (per iteration). Default is 20.

    Notes
    -----
    The option `ftol` is exposed via the `scipy.optimize.minimize` interface,
    but calling `scipy.optimize.fmin_l_bfgs_b` directly exposes `factr`. The
    relationship between the two is ``ftol = factr * numpy.finfo(float).eps``.
    I.e., `factr` multiplies the default machine floating-point precision to
    arrive at `ftol`.

    """
    _check_unknown_options(unknown_options)
    m = maxcor
    epsilon = eps
    pgtol = gtol
    factr = ftol / np.finfo(float).eps

    k,n = x0.shape

    # x0 = [asarray(x).ravel() for x in x0]
    # n, = x0.shape

    if bounds is None:
        bounds = [(None, None)] * n

    if len(bounds) != n:
        raise ValueError('length of x0 != length of bounds')
    # unbounded variables must use None, not +-inf, for optimizer to work properly
    bounds = [(None if l == -np.inf else l, None if u == np.inf else u) for l, u in bounds]

    if disp is not None:
        if disp == 0:
            iprint = -1
        else:
            iprint = disp

    if jac is None:
        def func_and_grad(x):
            # f = fun(x, *args)
            f, g = _approx_fprime_helper(x, fun, epsilon, args=args)
            return f, g
    else:
        def func_and_grad(x):
            f = fun(x, *args)
            g = jac(x, *args)
            return f, g

    nbd = zeros(n, int32)
    low_bnd = zeros(n, float64)
    upper_bnd = zeros(n, float64)
    bounds_map = {(None, None): 0,
                  (1, None): 1,
                  (1, 1): 2,
                  (None, 1): 3}
    for i in range(0, n):
        l, u = bounds[i]
        if l is not None:
            low_bnd[i] = l
            l = 1
        if u is not None:
            upper_bnd[i] = u
            u = 1
        nbd[i] = bounds_map[l, u]

    if not maxls > 0:
        raise ValueError('maxls must be positive.')

    # x = [array(x0_, float64) for x0_ in x0]
    X = x0.copy()

    f = [array(0.0, float64) for _ in range(k)]
    g = [zeros((n,), float64) for _ in range(k)]
    wa = [zeros(2*m*n + 5*n + 11*m*m + 8*m, float64) for _ in range(k)]
    iwa = [zeros(3*n, int32) for _ in range(k)]
    task = [zeros(1, 'S60') for _ in range(k)]
    csave = [zeros(1, 'S60') for _ in range(k)]
    lsave = [zeros(4, int32) for _ in range(k)]
    isave = [zeros(44, int32) for _ in range(k)]
    dsave = [zeros(29, float64) for _ in range(k)]
    n_function_evals = 0

    for i in range(k):
        task[i][:] = 'START'

    n_iterations = [0 for _ in range(k)]

    k_running = [i for i in range(k)]
    while 1:
        # x, f, g, wa, iwa, task, csave, lsave, isave, dsave = \

        request_index = []

        # run all instance until they request a new point
        # X contains only points for the remaining instances
        for i in k_running:
            while 1:
                _lbfgsb.setulb(m, X[i], low_bnd, upper_bnd, nbd, f[i], g[i], factr,
                           pgtol, wa[i], iwa[i], task[i], iprint, csave[i], lsave[i],
                           isave[i], dsave[i], maxls)
                task_str = task[i].tostring()

                if task_str.startswith(b'FG'):
                    # The minimization routine wants f and g at the current x.
                    # Note that interruptions due to maxfun are postponed
                    # until the completion of the current minimization iteration.
                    # Overwrite f and g:
                    request_index.append(i)
                    break

                elif task_str.startswith(b'NEW_X'):
                    # new iteration
                    if n_iterations[i] > maxiter:
                        task[i][:] = 'STOP: TOTAL NO. of ITERATIONS EXCEEDS LIMIT'
                        # break
                    elif n_function_evals > maxfun:
                        task[i][:] = ('STOP: TOTAL NO. of f AND g EVALUATIONS '
                                      'EXCEEDS LIMIT')
                        # break
                    else:
                        n_iterations[i] += 1
                        # if callback is not None:
                        #     callback(x)
                else:
                    break


        k_running = request_index
        if len(k_running) == 0:
            break


        F,G = func_and_grad(X[request_index])
        n_function_evals += 1

        for ff,gg,i in zip(F,G,k_running):
            f[i] = ff
            g[i] = gg

    warnflag = []
    task_str = [t.tostring().strip(b'\x00').strip() for t in task]
    for i,t in enumerate(task_str):
        if t.startswith(b'CONV'):
            warnflag.append(0)
        elif n_function_evals > maxfun:
            warnflag.append(1)
        elif n_iterations[i] > maxiter:
            warnflag.append(2)
        else:
            warnflag.append(3)

    # These two portions of the workspace are described in the mainlb
    # subroutine in lbfgsb.f. See line 363.
    # s = [wa[i][0: m*n].reshape(m, n) for i in range(k)]
    # y = [wa[i][m*n: 2*m*n].reshape(m, n) for i in range(k)]

    # See lbfgsb.f line 160 for this portion of the workspace.
    # isave(31) = the total number of BFGS updates prior the current iteration;
    # n_bfgs_updates = isave[30]

    # n_corrs = min(n_bfgs_updates, maxcor)
    # hess_inv = LbfgsInvHessProduct(s[:n_corrs], y[:n_corrs])

    return OptimizeResult(fun=f, jac=g, nfev=n_function_evals,
                          nit=n_iterations, status=warnflag, message=task_str,
                          x=X, success=(sum(warnflag) == 0))
Ejemplo n.º 12
0
def _minimize_lbfgsb(
    fun,
    x0,
    bounds=None,
    args=(),
    kwargs={},
    jac=None,
    callback=None,
    tol={"abs": 1e-05, "rel": 1e-08},
    norm=np.Inf,
    maxiter=None,
    disp=False,
    return_all=False,
    **unknown_options
):
    """
    Minimization of scalar function of one or more variables using the
    BHHH algorithm.

    Options
    -------
    disp : bool
        Set to True to print convergence messages.
    maxiter : int
        Maximum number of iterations to perform.
    tol : dict
        Absolute and relative tolerance values.
    norm : float
        Order of norm (Inf is max, -Inf is min).

    """
    _check_unknown_options(unknown_options)

    def f(x0):
        return fun(x0, *args, **kwargs)

    fprime = jac
    # epsilon = eps Add functionality
    retall = return_all
    k = 0
    ns = 0
    nsmax = 5
    N = len(x0)

    x0 = np.asarray(x0).flatten()
    if x0.ndim == 0:
        x0.shape = (1,)

    if bounds is None:
        bounds = np.array([np.inf] * N * 2).reshape((2, N))
        bounds[0, :] = -bounds[0, :]
    if bounds.shape[1] != N:
        raise ValueError("length of x0 != length of bounds")

    low = bounds[0, :]
    up = bounds[1, :]
    x0 = np.clip(x0, low, up)

    if maxiter is None:
        maxiter = len(x0) * 200

    if not callable(fprime):

        def myfprime(x0):
            return approx_derivative(f, x0, args=args, kwargs=kwargs)

    else:
        myfprime = fprime

    # Setup for iteration
    old_fval = f(x0)

    gf0 = myfprime(x0)
    gfk = gf0
    norm_pg0 = vecnorm(x0 - np.clip(x0 - gf0, low, up), ord=norm)

    xk = x0
    norm_pgk = norm_pg0

    sstore = np.zeros((maxiter, N))
    ystore = sstore.copy()

    if retall:
        allvecs = [x0]
    warnflag = 0

    # Calculate indices ofactive and inative set using projected gradient
    epsilon = min(np.min(up - low) / 2, norm_pgk)
    activeset = np.logical_or(xk - low <= epsilon, up - xk <= epsilon)
    inactiveset = np.logical_not(activeset)

    for _ in range(maxiter):  # for loop instead.

        # Check tolerance of gradient norm
        if norm_pgk <= tol["abs"] + tol["rel"] * norm_pg0:
            break

        pk = -gfk
        pk = bfgsrecb(ns, sstore, ystore, pk, activeset)
        gfk_active = gfk.copy()
        gfk_active[inactiveset] = 0
        pk = -gfk_active + pk

        # Sets the initial step guess to dx ~ 1
        old_old_fval = old_fval + np.linalg.norm(gfk) / 2

        try:
            alpha_k, fc, gc, old_fval, old_old_fval, gfkp1 = _line_search_wolfe12(
                f,
                myfprime,
                xk,
                pk,
                gfk,
                old_fval,
                old_old_fval,
                amin=1e-100,
                amax=1e100,
            )
        except _LineSearchError:
            # Line search failed to find a better solution.
            warnflag = 2
            break

        xkp1 = np.clip(xk + alpha_k * pk, low, up)

        if retall:
            allvecs.append(xkp1)

        yk = myfprime(xkp1) - gfk
        sk = xkp1 - xk
        xk = xkp1
        gfk = myfprime(xkp1)

        norm_pgk = vecnorm(xk - np.clip(xk - gfk, low, up), ord=norm)

        # Calculate indices ofactive and inative set using projected gradient
        epsilon = min(np.min(up - low) / 2, norm_pgk)
        activeset = np.logical_or(xk - low <= epsilon, up - xk <= epsilon)
        inactiveset = np.logical_not(activeset)

        yk[activeset] = 0
        sk[activeset] = 0

        # reset storage
        ytsk = yk.dot(sk)
        if ytsk <= 0:
            ns = 0
        if ns == nsmax:
            print("ns reached maximum size")
            ns = 0
        elif ytsk > 0:
            ns += 1
            alpha0 = ytsk ** 0.5
            sstore[ns - 1, :] = sk / alpha0
            ystore[ns - 1, :] = yk / alpha0

        k += 1

        if callback is not None:
            callback(xk)

        if np.isinf(old_fval):
            # We correctly found +-Inf as optimal value, or something went
            # wrong.
            warnflag = 2
            break
        if np.isnan(xk).any():
            warnflag = 3
            break

    fval = old_fval

    if warnflag == 2:
        msg = _status_message["pr_loss"]
    elif k >= maxiter:
        warnflag = 1
        msg = _status_message["maxiter"]
    elif np.isnan(fval) or np.isnan(xk).any():
        warnflag = 3
        msg = _status_message["nan"]
    else:
        msg = _status_message["success"]

    if disp:
        print("{}{}".format("Warning: " if warnflag != 0 else "", msg))
        print("         Current function value: %f" % fval)
        print("         Iterations: %d" % k)

    result = OptimizeResult(
        fun=fval,
        jac=gfk,
        status=warnflag,
        success=(warnflag == 0),
        message=msg,
        x=xk,
        nit=k,
    )
    if retall:
        result["allvecs"] = allvecs
    return result
Ejemplo n.º 13
0
    def solve(self, maximize=False):
        """
        Minimize the objective function f starting from x0.

        :return: x: the optimized parameters
        """
        start_time = time.clock()

        if self.verbose:
            if maximize:
                solving = 'Maximizing'
            else:
                solving = 'Minimizing'

            self._write("{} the problem using BFGS Method\n".format(solving))

        xk = np.asarray(self.x0).flatten()

        self.ep = 0
        self.it = 0
        I = np.eye(len(self.x0))
        Bk = I

        mult = 1
        if maximize:
            mult = -1

        self.xs = []
        self.epochs = []
        self.fs = []
        self.fs_full = []
        self.batches = []

        if self.batch == -1:
            self.batch = self.full_size

        if self.seed != -1:
            np.random.seed(self.seed)

        while self.ep < self.nbr_epochs:

            if self.biogeme is None:

                idx = np.random.choice(self.full_size, self.batch, replace=False)

                if self.batch == -1:
                    fprime = lambda x: mult*self.grad(x)
                    f = lambda x: mult*self.func(x)
                else:
                    fprime = lambda x: mult*self.grad(x, idx)
                    f = lambda x: mult*self.func(x, idx)

                full_f = lambda x: self.func(x)
            else:

                sample = self.biogeme.database.data.sample(n=self.batch, replace=False)

                if self.batch == -1:
                    fprime = lambda x: mult*self.biogeme.calculateLikelihoodAndDerivatives(x, hessian=False)[1]
                    f = lambda x: mult*self.func(x)
                else:
                    def fprime(x):
                        self.biogeme.theC.setData(sample)
                        tmp = back_to_bounds(x, self.bounds)
                        return mult*self.biogeme.calculateLikelihoodAndDerivatives(tmp, hessian=False)[1]

                    def f(x):
                        self.biogeme.theC.setData(sample)
                        tmp = back_to_bounds(x, self.bounds)
                        return mult*self.func(tmp)

                def full_f(x):
                    self.biogeme.theC.setData(self.biogeme.database.data)
                    return self.func(x)

            fk = f(xk)
            fk_full = full_f(xk)
            gk = fprime(xk)

            self.xs.append(xk)
            self.fs.append(fk)
            self.fs_full.append(fk_full)
            self.epochs.append(self.ep)
            self.batches.append(self.batch)

            if self.verbose:
                self._write("Epoch {}:\n".format(self.ep))
                self._write("  xk = [{}]\n".format(", ".join(format(x, ".3f") for x in xk)))
                self._write("  f(xk) = {:.3f}\n".format(fk_full))

            step = -np.dot(Bk, gk)

            if self.it > 0:
                old_old_fval = self.fs[-2]
            else:
                old_old_fval = self.fs[-1] + np.linalg.norm(gk) / 2

            alpha = ls_wolfe12(f, fprime, xk, step, gk, self.fs[-1], old_old_fval)

            xkp1 = xk + alpha * step
            sk = xkp1 - xk
            xk = xkp1

            xk = back_to_bounds(xk, self.bounds)

            gkp1 = fprime(xk)
            yk = gkp1 - gk
            gk = gkp1

            gnorm = np.linalg.norm(gk)
            snorm = np.linalg.norm(step)

            if self.verbose:
                self._write("  ||gk|| = {:.3E}\n".format(gnorm))
                self._write("  ||step|| = {:.3E}\n".format(np.linalg.norm(step)))
                self._write("  alpha = {:.3E}\n".format(alpha))

            if (gnorm <= self.thresh) or (snorm <= self.thresh):
                self.status = 'Optimum reached!'
                if self.verbose:
                    self._write("Algorithm Optimized!\n")
                    self._write("  x* = [{}]\n".format(", ".join(format(x, ".3f") for x in xk)))
                    self._write("  f(x*) = {:.3f}\n".format(fk))

                self.optimized = True
                break

            try:  # this was handled in numeric, let it remaines for more safety
                rhok = 1.0 / (np.dot(yk, sk))
            except ZeroDivisionError:
                rhok = 1000.0
            if np.isinf(rhok):  # this is patch for numpy
                rhok = 1000.0
            A1 = I - sk[:, np.newaxis] * yk[np.newaxis, :] * rhok
            A2 = I - yk[:, np.newaxis] * sk[np.newaxis, :] * rhok
            Bk = np.dot(A1, np.dot(Bk, A2)) + (rhok * sk[:, np.newaxis] *
                                                     sk[np.newaxis, :])

            self.batch = self.abs.upd(self.it, fk_full, self.batch)

            if self.verbose:
                self._write('\n')

            if self.batch == -1:
                self.ep += 1
            else:
                self.ep += self.batch/self.full_size

            self.it += 1

        if not self.optimized:
            self.status = 'Optimum not reached'

        if self.verbose and not self.optimized:
            self._write("Algorithm not fully optimized!\n")
            self._write("  x_n = [{}]\n".format(", ".join(format(x, ".3f") for x in xk)))
            self._write("  f(x_n) = {:.3f}\n".format(fk))

        self.f = mult*f(xk)
        self.x = xk
        gk = fprime(xk)

        self.opti_time = time.clock() - start_time

        dct = {'x': self.x,
               'success': self.optimized,
               'status': self.status,
               'fun': self.f,
               'jac': gk,
               'hess': Bk,
               'nit': self.it,
               'nep': self.ep,
               'opti_time': self.opti_time}

        return OptimizeResult(dct)
Ejemplo n.º 14
0
def _minimize_bhhh(fun,
                   x0,
                   bounds=None,
                   args=(),
                   jac=None,
                   callback=None,
                   tol={
                       "abs": 1e-05,
                       "rel": 1e-08
                   },
                   norm=np.Inf,
                   maxiter=None,
                   disp=False,
                   return_all=False,
                   **unknown_options):
    """
    Minimization of scalar function of one or more variables using the
    BHHH algorithm.

    Options
    -------
    disp : bool
        Set to True to print convergence messages.
    maxiter : int
        Maximum number of iterations to perform.
    tol : dict
        Absolute and relative tolerance values.
    norm : float
        Order of norm (Inf is max, -Inf is min).

    """

    _check_unknown_options(unknown_options)

    f = fun
    fprime = jac
    retall = return_all
    k = 0
    N = len(x0)

    x0 = np.asarray(x0).flatten()
    if x0.ndim == 0:
        x0.shape = (1, )

    if bounds is None:
        bounds = np.array([np.inf] * N * 2).reshape((2, N))
        bounds[0, :] = -bounds[0, :]
    if bounds.shape[1] != N:
        raise ValueError("length of x0 != length of bounds")

    low = bounds[0, :]
    up = bounds[1, :]
    x0 = np.clip(x0, low, up)

    if maxiter is None:
        maxiter = len(x0) * 200

    # Need the aggregate functions to take only x0 as an argument
    func_calls, agg_fun = wrap_function_agg(f, args)

    if not callable(fprime):
        grad_calls, myfprime = wrap_function_num_dev(f, args)
    else:
        grad_calls, myfprime = wrap_function(fprime, args)

    def agg_fprime(x0):
        return myfprime(x0).sum(axis=0)

    # Setup for iteration
    old_fval = agg_fun(x0)

    gf0 = agg_fprime(x0)
    norm_pg0 = vecnorm(x0 - np.clip(x0 - gf0, low, up), ord=norm)

    xk = x0
    norm_pgk = norm_pg0

    if retall:
        allvecs = [x0]
    warnflag = 0

    for _ in range(maxiter):  # for loop instead.

        # Individual
        gfk_obs = myfprime(xk)

        # Aggregate fprime. Might replace by simply summing up gfk_obs
        gfk = gfk_obs.sum(axis=0)
        norm_pgk = vecnorm(xk - np.clip(xk - gfk, low, up), ord=norm)

        # Check tolerance of gradient norm
        if norm_pgk <= tol["abs"] + tol["rel"] * norm_pg0:
            break

        # Sets the initial step guess to dx ~ 1
        old_old_fval = old_fval + np.linalg.norm(gfk) / 2

        # Calculate BHHH hessian and step
        Hk = np.dot(gfk_obs.T, gfk_obs)
        Bk = np.linalg.inv(Hk)
        pk = np.empty(N)
        pk = -np.dot(Bk, gfk)

        try:
            alpha_k, fc, gc, old_fval, old_old_fval, gfkp1 = _line_search_wolfe12(
                agg_fun,
                agg_fprime,
                xk,
                pk,
                gfk,
                old_fval,
                old_old_fval,
                amin=1e-100,
                amax=1e100,
            )
        except _LineSearchError:
            # Line search failed to find a better solution.
            warnflag = 2
            break

        xkp1 = np.clip(xk + alpha_k * pk, low, up)
        if retall:
            allvecs.append(xkp1)
        xk = xkp1
        if callback is not None:
            callback(xk)
        k += 1

        if np.isinf(old_fval):
            # We correctly found +-Inf as optimal value, or something went
            # wrong.
            warnflag = 2
            break

    fval = old_fval

    if warnflag == 2:
        msg = _status_message["pr_loss"]
    elif k >= maxiter:
        warnflag = 1
        msg = _status_message["maxiter"]
    elif np.isnan(fval) or np.isnan(xk).any():
        warnflag = 3
        msg = _status_message["nan"]
    else:
        msg = _status_message["success"]

    if disp:
        print("{}{}".format("Warning: " if warnflag != 0 else "", msg))
        print("         Current function value: %f" % fval)
        print("         Iterations: %d" % k)

    result = OptimizeResult(
        fun=fval,
        jac=gfk,
        hess_inv=Bk,
        nfev=func_calls[0],
        njev=grad_calls[0],
        status=warnflag,
        success=(warnflag == 0),
        message=msg,
        x=xk,
        nit=k,
    )
    if retall:
        result["allvecs"] = allvecs
    return result