esa / pagmo_plugins_nonfree

A pagmo affiliated package (https://github.com/esa/pagmo2) adding commercial solvers to the list of solvers/algorithms: SNOPT7, WORHP
GNU General Public License v3.0
10 stars 9 forks source link

Add WORHP ZEN support #22

Open gabor-varga opened 3 years ago

gabor-varga commented 3 years ago

The WORHP library provides additional utilities for doing sensitivity analysis once the optimal and feasible solution is found. This provides a powerful and fast way to estimate the local effects of changing parameters/constraints without having to reoptimise the entire problem.

Since the WORHP library is already wrapped and exposed to python in this project, it would make sense to include this functionality in the worhp class interface. @darioizzo let me know if you think this is feasible. I can start working on it if you approve.

zen_manual.pdf

gabor-varga commented 3 years ago

Based on the manual, I would propose to add zen_update method to the worhp class. The sample code in the PDF relies on an already existing optimisation cone by using the WORHP RC interface, and reusing the OptVar, WorkSpace, Param and Control type variables with the interface below.

void ZenUpdate(OptVar*, Workspace*, Params*, Control*,
               const char *var_pert, double *varnew, const double *dp,
               const double *dr, const double *dq,
               const double *db, const int *order);

At the moment the worhp::evolve is stand-alone in the sense that it manages the lifetime of these objects itself, destroying them before returning. I see two options to implement worhp::zen_update:

  1. zen_update reuses the variables of an optimised solution, and can only be called after evolve. evolve has to make the internal workspace variables available (e.g. class members of worhp instead of local variables of evolve). It would return the update free variables and objective function value (linear or quadratic estimate based on order input).

    struct pop_update
    {
    pagmo::vector_double x;
    double f;
    };
    pop_update zen_update(const pagmo::vector_double &dp, const pagmo::vector_double &dr,
                      const pagmo::vector_double &dq, const pagmo::vector_double &db, int order) const;
  2. zen_update takes a pagmo::population and does most of the work already done in evolve (e.g. wrapping functions). The function might need to check that the solution is a local optimum. It would also return an updated pagmo::population, however if both variables and objective are updated linearly, the objective value will not be consistent with a real evaluation of the problem.

pagmo::population zen_update(pagmo::population pop, const pagmo::vector_double &dp, 
                             const pagmo::vector_double &dr, const pagmo::vector_double &dq,
                             const pagmo::vector_double &db, int order) const;

It would also make sense to scale the linear perturbations (dp, dr, dq, db) with the scalings defined in pagmo. Let me know what do you think about these interfaces, and if you'd prefer a different one.

gabor-varga commented 3 years ago

After some discussion I realised that option 2 needs to evaluate the population for each call of zen_update, even if the problem did not change. To avoid needless objective and constraint function calls, the following methods are proposed:

void zen_init(pagmo::population pop); // maybe const

pagmo::population zen_update(const pagmo::vector_double &dp, const pagmo::vector_double &dr,
                             const pagmo::vector_double &dq, const pagmo::vector_double &db, int order) const;

Where zen_init would initialise internal variables and evaluate the gradients, and zen_update would return the updated population.