예제 #1
0
def help(args=None):
    import sys, shlex
    # program name
    try:
        prog = sys.argv[0]
    except Exception:
        prog = getattr(sys, 'executable', 'python')
    # arguments
    if args is None:
        args = sys.argv[1:]
    elif isinstance(args, str):
        args = shlex.split(args)
    else:
        args = [str(a) for a in args]
    # import and initialize
    import petsc4py
    petsc4py.init([prog, '-help'] + args)
    from petsc4py import PETSc
    # help dispatcher
    COMM = PETSc.COMM_SELF
    if 'vec' in args:
        vec = PETSc.Vec().create(comm=COMM)
        vec.setSizes(0)
        vec.setFromOptions()
        vec.destroy()
    if 'mat' in args:
        mat = PETSc.Mat().create(comm=COMM)
        mat.setSizes([0, 0])
        mat.setFromOptions()
        mat.destroy()
    if 'pc' in args:
        pc = PETSc.PC().create(comm=COMM)
        pc.setFromOptions()
        pc.destroy()
    if 'ksp' in args:
        ksp = PETSc.KSP().create(comm=COMM)
        ksp.setFromOptions()
        ksp.destroy()
    if 'snes' in args:
        snes = PETSc.SNES().create(comm=COMM)
        snes.setFromOptions()
        snes.destroy()
    if 'ts' in args:
        ts = PETSc.TS().create(comm=COMM)
        ts.setFromOptions()
        ts.destroy()
    if 'tao' in args:
        tao = PETSc.TAO().create(comm=COMM)
        tao.setFromOptions()
        tao.destroy()
    if 'dmda' in args:
        dmda = PETSc.DMDA().create(comm=COMM)
        dmda.setFromOptions()
        dmda.destroy()
    if 'dmplex' in args:
        dmplex = PETSc.DMPlex().create(comm=COMM)
        dmplex.setFromOptions()
        dmplex.destroy()
예제 #2
0
    def __init__(self, problem, parameters=None, riesz_map=None, prefix=""):

        try:
            from petsc4py import PETSc
        except:
            raise Exception("Could not find petsc4py. Please install it.")
        try:
            TAO = PETSc.TAO
        except:
            raise Exception("Your petsc4py version does not support TAO. Please upgrade to petsc4py >= 3.5.")

        self.PETSc = PETSc

        # Use PETSc forms
        if riesz_map is not None:

            # Handle the case where the user supplied riesz_maps.L2(V)
            if hasattr(riesz_map, "assemble"):
                riesz_map = riesz_map.assemble()

            self.riesz_map = as_backend_type(riesz_map).mat()
        else:
            self.riesz_map = None

        if len(prefix) > 0 and prefix[-1] != "_":
            prefix += "_"

        self.prefix = prefix

        OptimizationSolver.__init__(self, problem, parameters)

        self.tao = PETSc.TAO().create(PETSc.COMM_WORLD)

        self.__build_app_context()
        self.__set_parameters()
        self.__build_tao()
예제 #3
0
 def setUp(self):
     self.tao = PETSc.TAO().create(comm=self.COMM)
예제 #4
0
def tao_pounders(
    criterion_and_derivative,
    x,
    lower_bounds,
    upper_bounds,
    *,
    convergence_absolute_gradient_tolerance=CONVERGENCE_ABSOLUTE_GRADIENT_TOLERANCE,
    convergence_relative_gradient_tolerance=CONVERGENCE_RELATIVE_GRADIENT_TOLERANCE,
    convergence_scaled_gradient_tolerance=CONVERGENCE_SCALED_GRADIENT_TOLERANCE,
    trustregion_initial_radius=None,
    stopping_max_iterations=STOPPING_MAX_ITERATIONS,
):
    r"""Minimize a function using the POUNDERs algorithm.

    For details see :ref:`tao_algorithm`.
    """
    if not IS_PETSC4PY_INSTALLED:
        raise NotImplementedError(
            "The petsc4py package is not installed and required for 'tao_pounders'. If "
            "you are using Linux or MacOS, install the package with 'conda install -c "
            "conda-forge petsc4py. The package is not available on Windows.")

    func = functools.partial(
        criterion_and_derivative,
        task="criterion",
        algorithm_info=POUNDERS_ALGO_INFO,
    )

    x = _initialise_petsc_array(x)
    # We need to know the number of contributions of the criterion value to allocate the
    # array.
    n_errors = len(
        criterion_and_derivative.keywords["first_criterion_evaluation"]
        ["output"]["root_contributions"])
    residuals_out = _initialise_petsc_array(n_errors)

    # Create the solver object.
    tao = PETSc.TAO().create(PETSc.COMM_WORLD)

    # Set the solver type.
    tao.setType("pounders")

    tao.setFromOptions()

    def func_tao(tao, x, resid_out):
        """Evaluate objective and attach result to an petsc object f.

        This is required to use the pounders solver from tao.

        Args:
             tao: The tao object we created for the optimization task.
             x (PETSc.array): Current parameter values.
             f: Petsc object in which we save the current function value.

        """
        resid_out.array = func(x.array)

    # Set the procedure for calculating the objective. This part has to be changed if we
    # want more than pounders.
    tao.setResidual(func_tao, residuals_out)

    if trustregion_initial_radius is None:
        trustregion_initial_radius = calculate_trustregion_initial_radius(x)
    elif trustregion_initial_radius <= 0:
        raise ValueError("The initial trust region radius must be > 0.")
    tao.setInitialTrustRegionRadius(trustregion_initial_radius)

    # Add bounds.
    lower_bounds = _initialise_petsc_array(lower_bounds)
    upper_bounds = _initialise_petsc_array(upper_bounds)
    tao.setVariableBounds(lower_bounds, upper_bounds)

    # Put the starting values into the container and pass them to the optimizer.
    tao.setInitial(x)

    # Obtain tolerances for the convergence criteria. Since we can not create
    # scaled_gradient_tolerance manually we manually set absolute_gradient_tolerance and
    # or relative_gradient_tolerance to zero once a subset of these two is turned off
    # and scaled_gradient_tolerance is still turned on.
    default_gatol = (convergence_absolute_gradient_tolerance
                     if convergence_absolute_gradient_tolerance else -1)
    default_gttol = (convergence_scaled_gradient_tolerance
                     if convergence_scaled_gradient_tolerance else -1)
    default_grtol = (convergence_relative_gradient_tolerance
                     if convergence_relative_gradient_tolerance else -1)
    # Set tolerances for default convergence tests.
    tao.setTolerances(
        gatol=default_gatol,
        grtol=default_grtol,
        gttol=default_gttol,
    )

    # Set user defined convergence tests. Beware that specifying multiple tests could
    # overwrite others or lead to unclear behavior.
    if stopping_max_iterations is not None:
        tao.setConvergenceTest(
            functools.partial(_max_iters, stopping_max_iterations))
    elif (convergence_scaled_gradient_tolerance is False
          and convergence_absolute_gradient_tolerance is False):
        tao.setConvergenceTest(
            functools.partial(_grtol_conv,
                              convergence_relative_gradient_tolerance))
    elif (convergence_relative_gradient_tolerance is False
          and convergence_scaled_gradient_tolerance is False):
        tao.setConvergenceTest(
            functools.partial(_gatol_conv,
                              convergence_absolute_gradient_tolerance))
    elif convergence_scaled_gradient_tolerance is False:
        tao.setConvergenceTest(
            functools.partial(
                _grtol_gatol_conv,
                convergence_relative_gradient_tolerance,
                convergence_absolute_gradient_tolerance,
            ))

    # Run the problem.
    tao.solve()

    results = _process_pounders_results(residuals_out, tao)

    # Destroy petsc objects for memory reasons.
    for obj in [tao, x, residuals_out, lower_bounds, upper_bounds]:
        obj.destroy()

    return results
def run_local_tao(user_specs, comm_queue, x0, f0, child_can_read,
                  parent_can_read):
    """
    Runs a PETSc/TAO local optimization run starting at ``x0``, governed by the
    parameters in ``user_specs``.
    """

    assert isinstance(x0, np.ndarray)

    tao_comm = PETSc.COMM_SELF
    n, = x0.shape
    if f0.shape == ():
        m = 1
    else:
        m, = f0.shape

    # Create starting point, bounds, and tao object
    x = PETSc.Vec().create(tao_comm)
    x.setSizes(n)
    x.setFromOptions()
    x.array = x0
    lb = x.duplicate()
    ub = x.duplicate()
    lb.array = 0 * np.ones(n)
    ub.array = 1 * np.ones(n)
    tao = PETSc.TAO().create(tao_comm)
    tao.setType(user_specs['localopt_method'])

    if user_specs['localopt_method'] == 'pounders':
        f = PETSc.Vec().create(tao_comm)
        f.setSizes(m)
        f.setFromOptions()

        if hasattr(tao, 'setResidual'):
            tao.setResidual(
                lambda tao, x, f: tao_callback_fun_pounders(
                    tao, x, f, comm_queue, child_can_read, parent_can_read,
                    user_specs), f)
        else:
            tao.setSeparableObjective(
                lambda tao, x, f: tao_callback_fun_pounders(
                    tao, x, f, comm_queue, child_can_read, parent_can_read,
                    user_specs), f)
        delta_0 = user_specs['dist_to_bound_multiple'] * np.min(
            [np.min(ub.array - x.array),
             np.min(x.array - lb.array)])
        PETSc.Options().setValue('-tao_pounders_delta', str(delta_0))

    elif user_specs['localopt_method'] == 'nm':
        tao.setObjective(lambda tao, x: tao_callback_fun_nm(
            tao, x, comm_queue, child_can_read, parent_can_read, user_specs))

    elif user_specs['localopt_method'] == 'blmvm':
        g = PETSc.Vec().create(tao_comm)
        g.setSizes(n)
        g.setFromOptions()
        tao.setObjectiveGradient(lambda tao, x, g: tao_callback_fun_grad(
            tao, x, g, comm_queue, child_can_read, parent_can_read, user_specs)
                                 )

    # Set everything for tao before solving
    PETSc.Options().setValue('-tao_max_funcs',
                             str(user_specs.get('run_max_eval', 1000 * n)))
    tao.setFromOptions()
    tao.setVariableBounds((lb, ub))

    tao.setTolerances(grtol=user_specs.get('grtol', 1e-8),
                      gatol=user_specs.get('gatol', 1e-8))
    tao.setInitial(x)

    # print('[Child]: Started my optimization', flush=True)
    tao.solve(x)

    x_opt = tao.getSolution().getArray()
    exit_code = tao.getConvergedReason()

    if exit_code > 0:
        opt_flag = 1
    else:
        # https://www.mcs.anl.gov/petsc/petsc-current/docs/manualpages/Tao/TaoGetConvergedReason.html
        print(
            "[APOSMM] The run started from " + str(x0) +
            " exited with a nonpositive reason. No point from "
            "this run will be ruled as a minimum! APOSMM may start a new run from some point in this run."
        )
        opt_flag = 0

    if user_specs['localopt_method'] == 'pounders':
        f.destroy()
    elif user_specs['localopt_method'] == 'blmvm':
        g.destroy()

    lb.destroy()
    ub.destroy()
    x.destroy()
    tao.destroy()

    finish_queue(x_opt, opt_flag, comm_queue, parent_can_read, user_specs)
예제 #6
0
def minimize_pounders(
    func,
    x,
    len_out,
    bounds=None,
    init_tr=None,
    max_iterations=None,
    gatol=1e-8,
    grtol=1e-8,
    gttol=1e-10,
):
    """Minimize a function using the pounders algortihm.

    Args:
        func (callable): Function that takes a 1d NumPy array and returns a 1d NumPy
            array.
        x (np.ndarray): Contains the start values of the variables of interest
        bounds (list or tuple of lists): Contains the bounds for the variable of
            interest. The first list contains the lower value for each parameter and the
            upper list the upper value. The object has to contain two elements of which
            one represents the upper and the other one the lower bound.
        init_tr (float): Sets the radius for the initial trust region that the optimizer
            employs. If none the algorithm uses 100 as initial  trust region radius.
        max_iterations (int): Alternative Stopping criterion. If set the routine will
            stop after the number of specified iterations or after the step size is
            sufficiently small. If the variable is set the default criteria will all be
            ignored.
        gatol (int): Stop if relative norm of gradient is less than this. If set to
            False the algorithm will not consider gatol.
        grtol (int): Stop if norm of gradient is less than this. If set to False the
            algorithm will not consider grtol.
        gttol (int): Stop if norm of gradient is reduced by this factor. If set to False
            the algorithm will not consider grtol.

    Returns:
        out (dict): Dictionary with the following key-value pairs:

            - `"solution"`: solution vector as `np.ndarray`.
            - `"func_values"`: `np.ndarray` of value of the objective at the solution.
            - `"x"`: `np.ndarray` of the start values.
            - `"conv"`: string indicating the termination reason.
            - `"sol"`: `list` containing ...
              - current iterate as integer.
              - current value of the objective as float
              - current value of the approximated
              - jacobian as float
              - infeasability norm as float
              - step length as float
              - termination reason as int.

    """
    if sys.platform == "win32":
        raise NotImplementedError(
            "The pounders algorithm is not available on Windows.")

    # We want to get containers for the func verctor and the paras.
    size_paras = len(x)
    size_objective = len_out
    paras, crit = _prep_args(size_paras, size_objective)

    # Set the start value.
    paras[:] = x

    def func_tao(tao, paras, f):
        """Evaluate objective and attach result to an petsc object f.

        This is required to use the pounders solver from tao.

        Args:
             tao: The tao object we created for the optimization task.
             paras (np.ndarray): 1d NumPy array of the current values at which we want
                to evaluate the function.
             f: Petsc object in which we save the current function value.
        """
        dev = func(paras.array)
        # Attach to PETSc object.
        f.array = dev

    # Create the solver object.
    tao = PETSc.TAO().create(PETSc.COMM_WORLD)

    # Set the solver type.
    tao.setType("pounders")

    tao.setFromOptions()

    # Set the procedure for calculating the objective. This part has to be changed if we
    # want more than pounders.
    tao.setResidual(func_tao, crit)

    # We try to set user defined convergence tests.
    if init_tr is not None:
        tao.setInitialTrustRegionRadius(init_tr)

    # Change they need to be in a container
    # Set the variable sounds if existing
    if bounds is not None:
        low, up = _prep_args(len(x), len(x))
        low.array = bounds[0]
        up.array = bounds[1]
        tao.setVariableBounds([low, up])

    # Set the container over which we optimize that already contians start values
    tao.setInitial(paras)

    # Obtain tolerances for the convergence criteria. Since we can not create gttol
    # manually we manually set gatol and or grtol to zero once a subset of these two is
    # turned off and gttol is still turned on.
    tol_real = _get_tolerances(gttol, gatol, grtol)

    # Set tolerances for default convergence tests.
    tao.setTolerances(gatol=tol_real["gatol"],
                      gttol=tol_real["gttol"],
                      grtol=tol_real["grtol"])

    # Set user defined convergence tests. Beware that specifiying multiple tests could
    # overwrite others or lead to unclear behavior.
    if max_iterations is not None:
        tao.setConvergenceTest(partial(_max_iters, max_iterations))
    elif gttol is False and gatol is False:
        tao.setConvergenceTest(partial(_grtol_conv, grtol))
    elif grtol is False and gttol is False:
        tao.setConvergenceTest(partial(_gatol_conv, gatol))
    elif gttol is False:
        tao.setConvergenceTest(partial(_grtol_gatol_conv, grtol, gatol))

    # Run the problem.
    tao.solve()

    # Create a dict that contains relevant information.
    out = {}
    out["x"] = paras.array
    out["fun"] = crit.array[-1]
    out["func_values"] = crit.array
    out["start_values"] = x
    out["conv"] = _translate_tao_convergence_reason(tao.getConvergedReason())
    out["sol"] = tao.getSolutionStatus()

    # Destroy petsc objects for memory reasons.
    tao.destroy()
    paras.destroy()
    crit.destroy()

    return out
예제 #7
0
def tao_pounders(
    criterion_and_derivative,
    x,
    lower_bounds,
    upper_bounds,
    *,
    convergence_absolute_gradient_tolerance=CONVERGENCE_ABSOLUTE_GRADIENT_TOLERANCE,
    convergence_relative_gradient_tolerance=CONVERGENCE_RELATIVE_GRADIENT_TOLERANCE,
    convergence_scaled_gradient_tolerance=CONVERGENCE_SCALED_GRADIENT_TOLERANCE,
    trustregion_initial_radius=None,
    stopping_max_iterations=STOPPING_MAX_ITERATIONS,
):
    r"""Minimize a function using the POUNDERs algorithm.

    Do not call this function directly but pass its name "tao_pounders" to estimagic's
    maximize or minimize function as `algorithm` argument. Specify your desired
    arguments as a dictionary and pass them as `algo_options` to minimize or
    maximize.

    POUNDERs (:cite:`Benson2017`, :cite:`Wild2015`, `GitLab repository
    <https://gitlab.com/petsc/petsc/-/tree/master/src/binding/petsc4py/>`_)
    can be a useful tool for economists who estimate structural models using
    indirect inference, because unlike commonly used algorithms such as Nelder-Mead,
    POUNDERs is tailored for minimizing a non-linear sum of squares objective function,
    and therefore may require fewer iterations to arrive at a local optimum than
    Nelder-Mead.

    The criterion function :func:`func` should return a dictionary with the following
    fields:

    1. ``"value"``: The sum of squared (potentially weighted) errors.
    2. ``"root_contributions"``: An array containing the root (weighted) contributions.

    Scaling the problem is necessary such that bounds correspond to the unit hypercube
    :math:`[0, 1]^n`. For unconstrained problems, scale each parameter such that unit
    changes in parameters result in similar order-of-magnitude changes in the criterion
    value(s).

    POUNDERs has several convergence criteria. Let :math:`X` be the current parameter
    vector, :math:`X_0` the initial parameter vector, :math:`g` the gradient, and
    :math:`f` the criterion function.

    ``absolute_gradient_tolerance`` stops the optimization if the norm of the gradient
    falls below :math:`\epsilon`.

    .. math::

        ||g(X)|| < \epsilon

    ``relative_gradient_tolerance`` stops the optimization if the norm of the gradient
    relative to the criterion value falls below :math:`epsilon`.

    .. math::

        ||g(X)|| / |f(X)| < \epsilon

    ``scaled_gradient_tolerance`` stops the optimization if the norm of the gradient is
    lower than some fraction :math:`epsilon` of the norm of the gradient at the initial
    parameters.

    .. math::

        ||g(X)|| / ||g(X0)|| < \epsilon

    Args:
        absolute_gradient_tolerance (float): Stop if relative norm of gradient is less
            than this. If set to False the algorithm will not consider
            absolute_gradient_tolerance.
        relative_gradient_tolerance (float): Stop if norm of gradient is less than this.
            If set to False the algorithm will not consider relative_gradient_tolerance.
        scaled_gradient_tolerance (float): Stop if norm of gradient is reduced by this
            factor. If set to False the algorithm will not consider
            relative_gradient_tolerance.
        trustregion_initial_radius (float): Initial value of the trust region radius.
            It must be :math:`> 0`.
        stopping_max_iterations (int): Alternative Stopping criterion.
            If set the routine will stop after the number of specified iterations or
            after the step size is sufficiently small. If the variable is set the
            default criteria will all be ignored.

    Returns:
        results (dict): Dictionary with processed optimization results.

    """
    if not IS_PETSC4PY_INSTALLED:
        raise NotImplementedError(
            "The petsc4py package is not installed and required for 'tao_pounders'. If "
            "you are using Linux or MacOS, install the package with 'conda install -c "
            "conda-forge petsc4py. The package is not available on Windows.")

    func = functools.partial(
        criterion_and_derivative,
        task="criterion",
        algorithm_info=POUNDERS_ALGO_INFO,
    )

    x = _initialise_petsc_array(x)
    # We need to know the number of contributions of the criterion value to allocate the
    # array.
    n_errors = len(
        criterion_and_derivative.keywords["first_criterion_evaluation"]
        ["output"]["root_contributions"])
    residuals_out = _initialise_petsc_array(n_errors)

    # Create the solver object.
    tao = PETSc.TAO().create(PETSc.COMM_WORLD)

    # Set the solver type.
    tao.setType("pounders")

    tao.setFromOptions()

    def func_tao(tao, x, resid_out):
        """Evaluate objective and attach result to an petsc object f.

        This is required to use the pounders solver from tao.

        Args:
             tao: The tao object we created for the optimization task.
             x (PETSc.array): Current parameter values.
             f: Petsc object in which we save the current function value.

        """
        resid_out.array = func(x.array)

    # Set the procedure for calculating the objective. This part has to be changed if we
    # want more than pounders.
    tao.setResidual(func_tao, residuals_out)

    if trustregion_initial_radius is None:
        trustregion_initial_radius = calculate_trustregion_initial_radius(x)
    elif trustregion_initial_radius <= 0:
        raise ValueError("The initial trust region radius must be > 0.")
    tao.setInitialTrustRegionRadius(trustregion_initial_radius)

    # Add bounds.
    lower_bounds = _initialise_petsc_array(lower_bounds)
    upper_bounds = _initialise_petsc_array(upper_bounds)
    tao.setVariableBounds(lower_bounds, upper_bounds)

    # Put the starting values into the container and pass them to the optimizer.
    tao.setInitial(x)

    # Obtain tolerances for the convergence criteria. Since we can not create
    # scaled_gradient_tolerance manually we manually set absolute_gradient_tolerance and
    # or relative_gradient_tolerance to zero once a subset of these two is turned off
    # and scaled_gradient_tolerance is still turned on.
    default_gatol = (convergence_absolute_gradient_tolerance
                     if convergence_absolute_gradient_tolerance else -1)
    default_gttol = (convergence_scaled_gradient_tolerance
                     if convergence_scaled_gradient_tolerance else -1)
    default_grtol = (convergence_relative_gradient_tolerance
                     if convergence_relative_gradient_tolerance else -1)
    # Set tolerances for default convergence tests.
    tao.setTolerances(
        gatol=default_gatol,
        grtol=default_grtol,
        gttol=default_gttol,
    )

    # Set user defined convergence tests. Beware that specifying multiple tests could
    # overwrite others or lead to unclear behavior.
    if stopping_max_iterations is not None:
        tao.setConvergenceTest(
            functools.partial(_max_iters, stopping_max_iterations))
    elif (convergence_scaled_gradient_tolerance is False
          and convergence_absolute_gradient_tolerance is False):
        tao.setConvergenceTest(
            functools.partial(_grtol_conv,
                              convergence_relative_gradient_tolerance))
    elif (convergence_relative_gradient_tolerance is False
          and convergence_scaled_gradient_tolerance is False):
        tao.setConvergenceTest(
            functools.partial(_gatol_conv,
                              convergence_absolute_gradient_tolerance))
    elif convergence_scaled_gradient_tolerance is False:
        tao.setConvergenceTest(
            functools.partial(
                _grtol_gatol_conv,
                convergence_relative_gradient_tolerance,
                convergence_absolute_gradient_tolerance,
            ))

    # Run the problem.
    tao.solve()

    results = _process_pounders_results(residuals_out, tao)

    # Destroy petsc objects for memory reasons.
    for obj in [tao, x, residuals_out, lower_bounds, upper_bounds]:
        obj.destroy()

    return results
예제 #8
0
def run_local_tao(user_specs, comm_queue, x0, f0, child_can_read,
                  parent_can_read):

    assert isinstance(x0, np.ndarray)

    tao_comm = MPI.COMM_SELF
    n, = x0.shape
    if f0.shape == ():
        m = 1
    else:
        m, = f0.shape

    # Create starting point, bounds, and tao object
    x = PETSc.Vec().create(tao_comm)
    x.setSizes(n)
    x.setFromOptions()
    x.array = x0
    lb = x.duplicate()
    ub = x.duplicate()
    lb.array = 0 * np.ones(n)
    ub.array = 1 * np.ones(n)
    tao = PETSc.TAO().create(tao_comm)
    tao.setType(user_specs['localopt_method'])

    if user_specs['localopt_method'] == 'pounders':
        f = PETSc.Vec().create(tao_comm)
        f.setSizes(m)
        f.setFromOptions()

        if hasattr(tao, 'setResidual'):
            tao.setResidual(
                lambda tao, x, f:
                tao_callback_fun(tao, x, f, comm_queue, child_can_read,
                                 parent_can_read, user_specs), f)
        else:
            tao.setSeparableObjective(
                lambda tao, x, f:
                tao_callback_fun(tao, x, f, comm_queue, child_can_read,
                                 parent_can_read, user_specs), f)

    elif user_specs['localopt_method'] == 'blmvm':
        g = PETSc.Vec().create(tao_comm)
        g.setSizes(n)
        g.setFromOptions()
        tao.setObjectiveGradient(lambda tao, x, g: tao_callback_fun_grad(
            tao, x, g, comm_queue, child_can_read, parent_can_read, user_specs)
                                 )

    delta_0 = user_specs['dist_to_bound_multiple'] * np.min(
        [np.min(ub.array - x.array),
         np.min(x.array - lb.array)])
    PETSc.Options().setValue('-tao_pounders_delta', str(delta_0))

    # Set everything for tao before solving
    # FIXME: Hard-coding 100 as the max funcs as couldn't find any other
    # sensible value.
    PETSc.Options().setValue('-tao_max_funcs', '100')
    tao.setFromOptions()
    tao.setVariableBounds((lb, ub))
    # tao.setObjectiveTolerances(fatol=user_specs['fatol'], frtol=user_specs['frtol'])
    # tao.setGradientTolerances(grtol=user_specs['grtol'], gatol=user_specs['gatol'])
    tao.setTolerances(grtol=user_specs['grtol'], gatol=user_specs['gatol'])
    tao.setInitial(x)

    # print('[Child]: Started my optimization', flush=True)
    tao.solve(x)

    x_opt = tao.getSolution().getArray()
    # exit_code = tao.getConvergedReason()

    # FIXME: Need to do something with the exit codes.
    # print(exit_code)
    # print(tao.view())
    # print(x_opt)

    if user_specs['localopt_method'] == 'pounders':
        f.destroy()
    elif user_specs['localopt_method'] == 'blmvm':
        g.destroy()

    lb.destroy()
    ub.destroy()
    x.destroy()
    tao.destroy()

    # FIXME: Do we need to do something of the final 'x_opt'?
    # print('[Child]: I have converged.', flush=True)
    comm_queue.put(ConvergedMsg(x_opt))
    parent_can_read.set()
예제 #9
0
def set_up_and_run_tao(Run_H, user_specs):
    """ Set up objective and runs PETSc on the comm_self communicator

    Declares the appropriate syntax for our special objective function to read
    through Run_H, sets the parameters and starting points for the run.
    """
    tao_comm = MPI.COMM_SELF
    n = len(user_specs['ub'])

    def pounders_obj_func(tao, X, F, Run_H):
        F.array = look_in_history(X.array_r, Run_H, vector_return=True)
        return F

    def blmvm_obj_func(tao, X, G, Run_H):
        (f, grad) = look_in_history(X.array_r, Run_H)
        G.array = grad
        return f

    # Create starting point, bounds, and tao object
    x = PETSc.Vec().create(tao_comm)
    x.setSizes(n)
    x.setFromOptions()
    x.array = Run_H['x_on_cube'][0]
    lb = x.duplicate()
    ub = x.duplicate()
    lb.array = 0 * np.ones(n)
    ub.array = 1 * np.ones(n)
    tao = PETSc.TAO().create(tao_comm)
    tao.setType(user_specs['localopt_method'])

    if user_specs['localopt_method'] == 'pounders':
        f = PETSc.Vec().create(tao_comm)
        f.setSizes(len(Run_H['fvec'][0]))
        f.setFromOptions()

        delta_0 = user_specs['dist_to_bound_multiple'] * np.min(
            [np.min(ub.array - x.array),
             np.min(x.array - lb.array)])

        PETSc.Options().setValue('-tao_pounders_delta', str(delta_0))

        # PETSc.Options().setValue('-pounders_subsolver_tao_type','bqpip')
        if hasattr(tao, 'setResidual'):
            tao.setResidual(
                lambda tao, x, f: pounders_obj_func(tao, x, f, Run_H), f)
        else:
            tao.setSeparableObjective(
                lambda tao, x, f: pounders_obj_func(tao, x, f, Run_H), f)

    elif user_specs['localopt_method'] == 'blmvm':
        g = PETSc.Vec().create(tao_comm)
        g.setSizes(n)
        g.setFromOptions()
        tao.setObjectiveGradient(
            lambda tao, x, g: blmvm_obj_func(tao, x, g, Run_H))

    # Set everything for tao before solving
    PETSc.Options().setValue('-tao_max_funcs', str(len(Run_H) + 1))
    tao.setFromOptions()
    tao.setVariableBounds((lb, ub))
    # tao.setObjectiveTolerances(fatol=user_specs['fatol'], frtol=user_specs['frtol'])
    # tao.setGradientTolerances(grtol=user_specs['grtol'], gatol=user_specs['gatol'])
    tao.setTolerances(grtol=user_specs['grtol'], gatol=user_specs['gatol'])
    tao.setInitial(x)

    tao.solve(x)

    x_opt = tao.getSolution().getArray()
    exit_code = tao.getConvergedReason()
    # print(exit_code)
    # print(tao.view())
    # print(x_opt)

    if user_specs['localopt_method'] == 'pounders':
        f.destroy()
    if user_specs['localopt_method'] == 'blmvm':
        g.destroy()

    lb.destroy()
    ub.destroy()
    x.destroy()
    tao.destroy()

    return x_opt, exit_code
예제 #10
0
# create Hessian matrix
H = PETSc.Mat().create(PETSc.COMM_SELF)
H.setSizes([user.size, user.size])
H.setFromOptions()
H.setOption(PETSc.Mat.Option.SYMMETRIC, True)
H.setUp()

# pass the following to command line:
#  $ ... -methods nm,lmvm,nls,ntr,cg,blmvm,tron
# to try many methods
methods = OptDB.getString('methods', '')
methods = methods.split(',')
for meth in methods:
    # create TAO Solver
    tao = PETSc.TAO().create(PETSc.COMM_SELF)
    if meth: tao.setType(meth)
    tao.setFromOptions()
    # solve the problem
    tao.setObjectiveGradient(user.formObjGrad)
    tao.setObjective(user.formObjective)
    tao.setGradient(user.formGradient)
    tao.setHessian(user.formHessian, H)
    #app.getKSP().getPC().setFromOptions()
    x.set(0) # zero initial guess
    #tao.setInitial(x)
    tao.solve(x)
    tao.destroy()

## # this is just for testing
## x     = app.getSolution()
예제 #11
0
def minimize_pounders_np(
    func,
    x0,
    bounds,
    gatol=1e-8,
    grtol=1e-8,
    gttol=1e-10,
    init_tr=None,
    max_iterations=None,
    n_errors=None,
):
    """Minimize a function using the Pounders algorithm.

    Pounders can be a useful tool for economists who estimate structural models using
    indirect inference, because unlike commonly used algorithms such as Nelder-Mead,
    Pounders is tailored for minimizing a non-linear sum of squares objective function,
    and therefore may require fewer iterations to arrive at a local optimum than
    Nelder-Mead.

    The criterion function :func:`func` should return an array of the errors NOT an
    array of the squared errors or anything else.

    Args:
        func (callable): Objective function.
        x0 (np.ndarray): Starting values of the parameters.
        bounds (Tuple[np.ndarray]): A tuple containing two NumPy arrays where the first
            corresponds to the lower and the second to the upper bound. Unbounded
            parameters are represented by infinite values. The arrays have the same
            length as the parameter vector.
        gatol (float): Stop if relative norm of gradient is less than this. If set to
            False the algorithm will not consider gatol. Default is 1e-8.
        grtol (float): Stop if norm of gradient is less than this. If set to False the
            algorithm will not consider grtol. Default is 1e-8.
        gttol (float): Stop if norm of gradient is reduced by this factor. If set to
            False the algorithm will not consider grtol. Default is 1e-10.
        init_tr (float): Sets the radius for the initial trust region that the optimizer
            employs. If `None` the algorithm uses 100 as initial  trust region radius.
            Default is `None`.
        max_iterations (int): Alternative Stopping criterion. If set the routine will
            stop after the number of specified iterations or after the step size is
            sufficiently small. If the variable is set the default criteria will all be
            ignored. Default is `None`.
        n_errors (int or None): The number of outputs of `func` are necessary to
            pre-allocate the results array. If the argument is ``None``, evaluate the
            function once. This might be undesirable during dashboard optimizations.

    Returns:
        results (dict): Dictionary with processed optimization results.

    .. _TAO Users Manual:
        https://www.mcs.anl.gov/petsc/petsc-current/docs/tao_manual.pdf
    .. _Solving Derivative-Free Nonlinear Least Squares Problems with POUNDERS:
        https://www.mcs.anl.gov/papers/P5120-0414.pdf

    """
    if sys.platform == "win32":
        raise NotImplementedError("The pounders algorithm is not available on Windows.")

    # We need to know the dimension of the output of the criterion function. Evaluate
    # plain `criterion` to prevent logging.
    if n_errors is None:
        n_errors = len(func(x0))

    # We want to get containers for the func vector and the paras.
    x0 = _initialise_petsc_array(x0)
    residuals_out = _initialise_petsc_array(n_errors)

    # Create the solver object.
    tao = PETSc.TAO().create(PETSc.COMM_WORLD)

    # Set the solver type.
    tao.setType("pounders")

    tao.setFromOptions()

    def func_tao(tao, x, resid_out):
        """Evaluate objective and attach result to an petsc object f.

        This is required to use the pounders solver from tao.

        Args:
             tao: The tao object we created for the optimization task.
             x (PETSc.array): Current parameter values.
             f: Petsc object in which we save the current function value.

        """
        resid_out.array = func(x.array)

    # Set the procedure for calculating the objective. This part has to be changed if we
    # want more than pounders.
    tao.setResidual(func_tao, residuals_out)

    # We try to set user defined convergence tests.
    if init_tr is not None:
        tao.setInitialTrustRegionRadius(init_tr)

    # Add bounds.
    n_params = len(x0.array)
    processed_bounds = []
    for bound in bounds:
        bound = np.full(n_params, bound) if isinstance(bound, (int, float)) else bound
        processed_bounds.append(_initialise_petsc_array(bound))
    tao.setVariableBounds(processed_bounds)

    # Put the starting values into the container and pass them to the optimizer.
    tao.setInitial(x0)

    # Obtain tolerances for the convergence criteria. Since we can not create gttol
    # manually we manually set gatol and or grtol to zero once a subset of these two is
    # turned off and gttol is still turned on.
    default_gatol = gatol if gatol else -1
    default_gttol = gttol if gttol else -1
    default_grtol = grtol if grtol else -1
    # Set tolerances for default convergence tests.
    tao.setTolerances(gatol=default_gatol, gttol=default_gttol, grtol=default_grtol)

    # Set user defined convergence tests. Beware that specifying multiple tests could
    # overwrite others or lead to unclear behavior.
    if max_iterations is not None:
        tao.setConvergenceTest(partial(_max_iters, max_iterations))
    elif gttol is False and gatol is False:
        tao.setConvergenceTest(partial(_grtol_conv, grtol))
    elif grtol is False and gttol is False:
        tao.setConvergenceTest(partial(_gatol_conv, gatol))
    elif gttol is False:
        tao.setConvergenceTest(partial(_grtol_gatol_conv, grtol, gatol))

    # Run the problem.
    tao.solve()

    results = _process_pounders_results(residuals_out, tao)

    # Destroy petsc objects for memory reasons.
    tao.destroy()
    x0.destroy()
    residuals_out.destroy()

    return results
예제 #12
0
def solve(func,
          x,
          len_out,
          bounds=None,
          init_tr=None,
          tol={
              "gatol": 0.00000001,
              "grtol": 0.00000001,
              "gttol": 0.0000000001
          },
          max_iterations=None,
          gatol=True,
          grtol=True,
          gttol=True):
    """
    Args:
        func: function that takes a 1d numpy array and returns a 1d numpy array
        x:np.array that contains the start values of the variables of interest
        bounds: list or tuple of lists containing the bounds for the variable of interest
                The first list contains the lower value for each param and the upper list the upper value
        init_tr: Sets the radius for the initial trust region that the optimizer employs. 
        tol: Sets the tolerance for the three default stopping criteria. The routine will stop once the first is reached.
             One can turn off specific criteria with other args. In this case their value in this dict does not matter.
        max_iterations: Alternative Stopping criterion. If set the routine will stop after the number of specified
                        iterations or after the step size is sufficiently small. If the variable is set the default
                        criteria will all be ignored.
        gatol: Boolean that indicates whether the gatol should be cosnidered.
               Explicit description is in the documentation.
        grtol: Boolean that indicates whether the grtol should be cosnidered.
               Explicit description is in the documentation
        gttol: Boolean that indicates whether the gttol should be cosnidered.
               Explicit description is in the documentation
    Returns:                                                                        
        out: dict with the following key value pairs:
             "solution": solution vector as np.array,
             "func values": np.array of value of the objective at the solution
             "x": np.array of the start values
             "conv": string indicating the termination reason
             "sol": list containing: current iterate as int, current value of the objective as float, current value of
                    the approximated jacobian as float, infeasability norm as float, step length as float and termination
                    reason as int.


    """
    # we want to get containers for the func verctor and the paras
    size_paras = len(x)
    size_objective = len_out
    paras, crit = _prep_args(size_paras, size_objective)

    # Set the start value
    paras[:] = x

    def func_tao(tao, paras, f):
        """
        This function takes an input, calculates the value of the objective and
        attaches it to an petsc object f thereafter.
        func_tao puts the objective in a format that the optimizer requires.
        Args:
             tao: The tao object we created for the optimization task
             paras: 1d np.array of the current values at which we want to evaluate the function.
             f: Petsc object in which we save the current function value
        """
        dev = func(paras.array)
        # Attach to PETSc object
        f.array = dev

    # Create the solver object
    tao = PETSc.TAO().create(PETSc.COMM_WORLD)

    # Set the solver type
    tao.setType('pounders')

    tao.setFromOptions()

    # Set the procedure for calculating the objective
    # This part has to be changed if we want more than pounders
    tao.setResidual(func_tao, crit)

    # We try to set user defined convergence tests
    if init_tr is not None:
        tao.setInitialTrustRegionRadius(init_tr)

    # Change they need to be in a container
    # Set the variable sounds if existing
    if bounds is not None:
        low, up = _prep_args(len(x), len(x))
        low.array = bounds[0]
        up.array = bounds[1]
        tao.setVariableBounds([low, up])

    # Set the container over which we optimize that already contians start values
    tao.setInitial(paras)

    # Obtain tolerances for the convergence criteria
    # Since we can not create gttol manually we manually set gatol and or grtol to zero once a subset of these two is
    # turned off and gttol is still turned on
    tol_real = get_tolerances(tol, gatol, grtol)

    # Set tolerances for default convergence tests
    tao.setTolerances(gatol=tol_real["gatol"],
                      gttol=tol_real["gttol"],
                      grtol=tol_real["grtol"])

    # Set user defined convergence tests. Beware that specifiying multiple tests could overwrite others or lead to
    # unclear behavior.
    if max_iterations is not None:
        tao.setConvergenceTest(partial(max_iters, max_iterations))
    elif gttol is False and gatol is False:
        tao.setConvergenceTest(partial(grtol_conv, tol["grtol"]))
    elif gatol is False and gttol is False:
        tao.setConvergenceTest(partial(gatol_conv, tol["gatol"]))
    elif gttol is False:
        tao.setConvergenceTest(
            partial(grtol_gatol_conv, tol["grtol"], tol["gatol"]))

    # Run the problem
    tao.solve()

    # Create a dict that contains relevant information
    out = dict()
    out["solution"] = paras.array
    out["func_values"] = crit.array
    out["x"] = x
    out["conv"] = conv_reason[tao.getConvergedReason()]
    out["sol"] = tao.getSolutionStatus()

    # Destroy petsc objects for memory reasons
    tao.destroy()
    paras.destroy()
    crit.destroy()

    return out