/// <summary>
        /// Originally described at https://github.com/stevengj/nlopt/blob/master/src/api/optimize.c#L878
        /// </summary>
        internal static nlopt_result nlopt_optimize_limited(nlopt_opt opt, double[] x, int offset_x, ref double minf,
                                                            int maxeval, double maxtime)
        {
            int          save_maxeval;
            double       save_maxtime;
            nlopt_result ret;

            NLoptOptions.nlopt_unset_errmsg(opt);

            if (opt == null)
            {
                NLoptApi.RETURN_ERR(nlopt_result.NLOPT_INVALID_ARGS, opt, "NULL opt arg");
            }

            save_maxeval = NLoptApi.nlopt_get_maxeval(opt);
            save_maxtime = NLoptApi.nlopt_get_maxtime(opt);

            /* override opt limits if maxeval and/or maxtime are more stringent */
            if (save_maxeval <= 0 || (maxeval > 0 && maxeval < save_maxeval))
            {
                NLoptApi.nlopt_set_maxeval(opt, maxeval);
            }
            if (save_maxtime <= 0 || (maxtime > 0 && maxtime < save_maxtime))
            {
                NLoptApi.nlopt_set_maxtime(opt, maxtime);
            }

            ret = nlopt_optimize(opt, x, offset_x, ref minf);

            NLoptApi.nlopt_set_maxeval(opt, save_maxeval);
            NLoptApi.nlopt_set_maxtime(opt, save_maxtime);

            return(ret);
        }
Beispiel #2
0
        /// <summary>
        /// Note that we implement a hidden feature not in the standard nlopt_minimize_constrained interface: whenever the
        /// constraint function returns NaN, that constraint becomes inactive.
        /// Originally described at
        /// </summary>
        /// <param name="n"></param>
        /// <param name="f"></param>
        /// <param name="f_data"></param>
        /// <param name="m"></param>
        /// <param name="fc"></param>
        /// <param name="lb">Lower bounds. Will not be modified.</param>
        /// <param name="ub">Upper bounds. Will not be modified.</param>
        /// <param name="x">On input: initial guess. On output: minimizer</param>
        /// <param name="minf">
        /// The minimum value of the objective function: <paramref name="minf"/> = <paramref name="f"/>(<paramref name="x"/>).
        /// </param>
        /// <param name="stop"></param>
        /// <param name="dual_opt"></param>
        internal static nlopt_result mma_minimize(int n, nlopt_func f, object[] f_data, int m, nlopt_constraint[] fc,
                                                  double[] lb, double[] ub, double[] x, ref double minf, nlopt_stopping stop, nlopt_opt dual_opt)
        {
            // Translated from https://github.com/stevengj/nlopt/blob/master/src/algs/mma/mma.c#L145
            nlopt_result ret = nlopt_result.NLOPT_SUCCESS;
            double       rho, fcur;

            double[]  xcur, sigma, dfdx, dfdx_cur, xprev, xprevprev;
            double[]  dfcdx, dfcdx_cur;
            double[]  fcval, fcval_cur, rhoc, gcval, y, dual_lb, dual_ub;
            int       i, ifc, j, k = 0;
            dual_data dd;
            bool      feasible;
            double    infeasibility;
            int       mfc;

            m = NLoptStopping.nlopt_count_constraints(mfc = m, fc);
            if (NLoptApi.nlopt_get_dimension(dual_opt) != m)
            {
                NLoptStopping.nlopt_stop_msg(stop,
                                             string.Format("dual optimizer has wrong dimension %d != %d", NLoptApi.nlopt_get_dimension(dual_opt), m));
                return(nlopt_result.NLOPT_INVALID_ARGS);
            }
            try
            {
                // The original source malloc'd a large array sigma and set the rest as offsets.
                sigma     = new double[n];
                dfdx      = new double[n];
                dfdx_cur  = new double[n];
                xcur      = new double[n];
                xprev     = new double[n];
                xprevprev = new double[n];
                fcval     = new double[m];
                fcval_cur = new double[m];
                rhoc      = new double[m];
                gcval     = new double[m];
                dual_lb   = new double[m];
                dual_ub   = new double[m];
                y         = new double[m];
                dfcdx     = new double[m * n];
                dfcdx_cur = new double[m * n];
            }
            catch (OutOfMemoryException)
            {
                return(nlopt_result.NLOPT_OUT_OF_MEMORY);
            }

            dd       = new dual_data();
            dd.n     = n;
            dd.x     = x;
            dd.lb    = lb;
            dd.ub    = ub;
            dd.sigma = sigma;
            dd.dfdx  = dfdx;
            dd.dfcdx = dfcdx;
            dd.fcval = fcval;
            dd.rhoc  = rhoc;
            dd.xcur  = xcur;
            dd.gcval = gcval;

            for (j = 0; j < n; ++j)
            {
                if (NLoptStopping.nlopt_isinf(ub[j]) || NLoptStopping.nlopt_isinf(lb[j]))
                {
                    sigma[j] = 1.0; // arbitrary default
                }
                else
                {
                    sigma[j] = 0.5 * (ub[j] - lb[j]);
                }
            }
            rho = 1.0;
            for (i = 0; i < m; ++i)
            {
                rhoc[i]    = 1.0;
                dual_lb[i] = y[i] = 0.0;
                dual_ub[i] = double.MaxValue; // Originally dual_ub[i] = HUGE_VAL;
            }

            dd.fval = fcur = minf = f(n, x, 0, dfdx, 0, f_data, 0);
            ++stop.nevals_p;
            Array.Copy(x, xcur, n);
            if (NLoptStopping.nlopt_stop_forced(stop))
            {
                ret = nlopt_result.NLOPT_FORCED_STOP;
                return(ret);
            }

            feasible = true; infeasibility = 0;
            for (i = ifc = 0; ifc < mfc; ++ifc)
            {
                NLoptStopping.nlopt_eval_constraint(fcval, i, dfcdx, i * n, fc, ifc, n, x, 0);
                i += fc[ifc].m;
                if (NLoptStopping.nlopt_stop_forced(stop))
                {
                    ret = nlopt_result.NLOPT_FORCED_STOP;
                    return(ret);
                }
            }
            for (i = 0; i < m; ++i)
            {
                feasible = feasible && (fcval[i] <= 0 || NLoptStopping.nlopt_isnan(fcval[i]));
                if (fcval[i] > infeasibility)
                {
                    infeasibility = fcval[i];
                }
            }

            // For non-feasible initial points, set a finite (large) upper-bound on the dual variables.  What this means is that,
            // if no feasible solution is found from the dual problem, it will minimize the dual objective with the unfeasible
            // constraint weighted by 1e40 -- basically, minimizing the unfeasible constraint until it becomes feasible or until
            // we at least obtain a step towards a feasible point. Svanberg suggested a different approach in his 1987 paper,
            // basically introducing additional penalty variables for unfeasible constraints, but this is easier to implement
            // and at least as efficient.

            if (!feasible)
            {
                for (i = 0; i < m; ++i)
                {
                    dual_ub[i] = 1e40;
                }
            }

            NLoptOptions.nlopt_set_min_objective(dual_opt, dual_func, new object[] { dd }, 0);
            NLoptOptions.nlopt_set_lower_bounds(dual_opt, dual_lb);
            NLoptOptions.nlopt_set_upper_bounds(dual_opt, dual_ub);
            NLoptApi.nlopt_set_stopval(dual_opt, double.MinValue);
            NLoptOptions.nlopt_remove_inequality_constraints(dual_opt);
            NLoptOptions.nlopt_remove_equality_constraints(dual_opt);

            while (true) //outer iterations
            {
                double fprev = fcur;
                if (NLoptStopping.nlopt_stop_forced(stop))
                {
                    ret = nlopt_result.NLOPT_FORCED_STOP;
                }
                else if (NLoptStopping.nlopt_stop_evals(stop))
                {
                    ret = nlopt_result.NLOPT_MAXEVAL_REACHED;
                }
                else if (NLoptStopping.nlopt_stop_time(stop))
                {
                    ret = nlopt_result.NLOPT_MAXTIME_REACHED;
                }
                else if (feasible && (minf < stop.minf_max))
                {
                    ret = nlopt_result.NLOPT_MINF_MAX_REACHED;
                }
                if (ret != nlopt_result.NLOPT_SUCCESS)
                {
                    return(ret);
                }
                if (++k > 1)
                {
                    Array.Copy(xprev, xprevprev, n);
                }
                Array.Copy(xcur, xprev, n);

                while (true) //inner iterations
                {
                    double       min_dual = double.NaN;
                    double       infeasibility_cur;
                    bool         feasible_cur, inner_done;
                    int          save_verbose;
                    bool         new_infeasible_constraint;
                    nlopt_result reti;

                    // Solve dual problem
                    dd.rho       = rho; dd.count = 0;
                    save_verbose = mma_verbose;
                    mma_verbose  = 0; // no recursive verbosity
                    reti         = NLoptOptimize.nlopt_optimize_limited(dual_opt, y, 0, ref min_dual, 0,
                                                                        stop.maxtime - (stop.watch.Elapsed.TotalSeconds - stop.start));
                    mma_verbose = save_verbose;
                    if (reti < 0 || reti == nlopt_result.NLOPT_MAXTIME_REACHED)
                    {
                        ret = reti;
                        return(ret);
                    }

                    dual_func(m, y, 0, null, 0, new object[] { dd }, 0); // evaluate final xcur etc.
                    if (mma_verbose == 1)
                    {
                        Console.WriteLine("MMA dual converged in %d iterations to g=%g:", dd.count, dd.gval);
                        for (i = 0; i < Math.Min(mma_verbose, m); ++i)
                        {
                            Console.WriteLine("    MMA y[%u]=%g, gc[%u]=%g\n", i, y[i], i, dd.gcval[i]);
                        }
                    }

                    fcur = f(n, xcur, 0, dfdx_cur, 0, f_data, 0);
                    ++stop.nevals_p;
                    if (NLoptStopping.nlopt_stop_forced(stop))
                    {
                        ret = nlopt_result.NLOPT_FORCED_STOP;
                        return(ret);
                    }
                    feasible_cur = true; infeasibility_cur = 0;
                    new_infeasible_constraint = false;
                    inner_done = dd.gval >= fcur;
                    for (i = ifc = 0; ifc < mfc; ++ifc)
                    {
                        NLoptStopping.nlopt_eval_constraint(fcval_cur, i, dfcdx_cur, i * n, fc, ifc, n, xcur, 0);
                        i += fc[ifc].m;
                        if (NLoptStopping.nlopt_stop_forced(stop))
                        {
                            ret = nlopt_result.NLOPT_FORCED_STOP;
                            return(ret);
                        }
                    }
                    for (i = ifc = 0; ifc < mfc; ++ifc)
                    {
                        int i0 = i, inext = i + fc[ifc].m;
                        for (; i < inext; ++i)
                        {
                            if (!NLoptStopping.nlopt_isnan(fcval_cur[i]))
                            {
                                feasible_cur = feasible_cur && (fcval_cur[i] <= fc[ifc].tol[i - i0]);
                                if (!NLoptStopping.nlopt_isnan(fcval[i]))
                                {
                                    inner_done = inner_done && (dd.gcval[i] >= fcval_cur[i]);
                                }
                                else if (fcval_cur[i] > 0)
                                {
                                    new_infeasible_constraint = true;
                                }
                                if (fcval_cur[i] > infeasibility_cur)
                                {
                                    infeasibility_cur = fcval_cur[i];
                                }
                            }
                        }
                    }

                    if ((fcur < minf && (inner_done || feasible_cur || !feasible)) ||
                        (!feasible && infeasibility_cur < infeasibility))
                    {
                        if (mma_verbose == 1 && !feasible_cur)
                        {
                            Console.WriteLine("MMA - using infeasible point?");
                        }
                        dd.fval       = minf = fcur;
                        infeasibility = infeasibility_cur;
                        Array.Copy(fcval_cur, fcval, m);
                        Array.Copy(xcur, x, n);
                        Array.Copy(dfdx_cur, dfdx, n);
                        Array.Copy(dfcdx_cur, dfcdx, n * m);

                        // once we have reached a feasible solution, the algorithm should never make the solution infeasible
                        // again (if inner_done), although the constraints may be violated slightly by rounding errors etc. so we
                        // must be a little careful about checking feasibility
                        if (infeasibility_cur == 0)
                        {
                            if (!feasible) // reset upper bounds to infin.
                            {
                                for (i = 0; i < m; ++i)
                                {
                                    dual_ub[i] = double.MaxValue;
                                }
                                NLoptOptions.nlopt_set_upper_bounds(dual_opt, dual_ub);
                            }
                            feasible = true;
                        }
                        else if (new_infeasible_constraint)
                        {
                            feasible = false;
                        }
                    }

                    if (NLoptStopping.nlopt_stop_forced(stop))
                    {
                        ret = nlopt_result.NLOPT_FORCED_STOP;
                    }
                    else if (NLoptStopping.nlopt_stop_evals(stop))
                    {
                        ret = nlopt_result.NLOPT_MAXEVAL_REACHED;
                    }
                    else if (NLoptStopping.nlopt_stop_time(stop))
                    {
                        ret = nlopt_result.NLOPT_MAXTIME_REACHED;
                    }
                    else if (feasible && minf < stop.minf_max)
                    {
                        ret = nlopt_result.NLOPT_MINF_MAX_REACHED;
                    }
                    if (ret != nlopt_result.NLOPT_SUCCESS)
                    {
                        return(ret);
                    }

                    if (inner_done)
                    {
                        break;
                    }

                    if (fcur > dd.gval)
                    {
                        rho = Math.Min(10 * rho, 1.1 * (rho + (fcur - dd.gval) / dd.wval));
                    }
                    for (i = 0; i < m; ++i)
                    {
                        if (!NLoptStopping.nlopt_isnan(fcval_cur[i]) && fcval_cur[i] > dd.gcval[i])
                        {
                            rhoc[i] = Math.Min(10 * rhoc[i],
                                               1.1 * (rhoc[i] + (fcval_cur[i] - dd.gcval[i]) / dd.wval));
                        }
                    }

                    if (mma_verbose == 1)
                    {
                        Console.WriteLine("MMA inner iteration: rho -> %g", rho);
                    }
                    for (i = 0; i < Math.Min(mma_verbose, m); ++i)
                    {
                        Console.WriteLine("                 MMA rhoc[%u] -> %g", i, rhoc[i]);
                    }
                }

                if (NLoptStopping.nlopt_stop_ftol(stop, fcur, fprev))
                {
                    ret = nlopt_result.NLOPT_FTOL_REACHED;
                }
                if (NLoptStopping.nlopt_stop_x(stop, xcur, xprev))
                {
                    ret = nlopt_result.NLOPT_XTOL_REACHED;
                }
                if (ret != nlopt_result.NLOPT_SUCCESS)
                {
                    return(ret);
                }

                /* update rho and sigma for iteration k+1 */
                rho = Math.Max(0.1 * rho, MMA_RHOMIN);
                if (mma_verbose == 1)
                {
                    Console.WriteLine("MMA outer iteration: rho -> %g", rho);
                }
                for (i = 0; i < m; ++i)
                {
                    rhoc[i] = Math.Max(0.1 * rhoc[i], MMA_RHOMIN);
                }
                for (i = 0; i < Math.Min(mma_verbose, m); ++i)
                {
                    Console.WriteLine("                 MMA rhoc[%u] -> %g", i, rhoc[i]);
                }

                if (k > 1)
                {
                    for (j = 0; j < n; ++j)
                    {
                        double dx2 = (xcur[j] - xprev[j]) * (xprev[j] - xprevprev[j]);
                        double gam = dx2 < 0 ? 0.7 : (dx2 > 0 ? 1.2 : 1);
                        sigma[j] *= gam;
                        if (!NLoptStopping.nlopt_isinf(ub[j]) && !NLoptStopping.nlopt_isinf(lb[j]))
                        {
                            sigma[j] = Math.Min(sigma[j], 10 * (ub[j] - lb[j]));
                            sigma[j] = Math.Max(sigma[j], 0.01 * (ub[j] - lb[j]));
                        }
                    }
                    for (j = 0; j < Math.Min(mma_verbose, n); ++j)
                    {
                        Console.WriteLine("                 MMA sigma[%u] -> %g\n", j, sigma[j]);
                    }
                }
            }
        }
        /// <summary>
        /// Originally described at https://github.com/stevengj/nlopt/blob/master/src/api/optimize.c#L813
        /// </summary>
        internal static nlopt_result nlopt_optimize(nlopt_opt opt, double[] x, int x_offset, ref double opt_f)
        {
            nlopt_func f;

            object[]      f_data;
            nlopt_precond pre;
            f_max_data    fmd = new f_max_data();
            bool          maximize;
            nlopt_result  ret;

            NLoptOptions.nlopt_unset_errmsg(opt);
            if (opt == null /*|| opt_f == null*/ || opt.f == null)
            {
                NLoptApi.RETURN_ERR(nlopt_result.NLOPT_INVALID_ARGS, opt, "NULL args to nlopt_optimize");
            }
            f      = opt.f;
            f_data = opt.f_data;
            pre    = opt.pre;

            // for maximizing, just minimize the f_max wrapper, which flips the sign of everything
            if ((maximize = opt.maximize))
            {
                fmd.f             = f;
                fmd.f_data        = f_data;
                fmd.pre           = pre;
                opt.f             = f_max;
                opt.f_data        = new object[] { fmd };
                opt.f_data_offset = 0;
                if (opt.pre != null)
                {
                    opt.pre = pre_max;
                }
                opt.stopval  = -opt.stopval;
                opt.maximize = false;
            }

            // possibly eliminate lb == ub dimensions for some algorithms
            {
                nlopt_opt elim_opt = opt;
                if (elimdim_wrapcheck(opt))
                {
                    elim_opt = elimdim_create(opt);
                    if (elim_opt == null)
                    {
                        NLoptOptions.nlopt_set_errmsg(opt, "failure allocating elim_opt");
                        ret = nlopt_result.NLOPT_OUT_OF_MEMORY;
                        goto done;
                    }
                    elimdim_shrink(opt.n, x, x_offset, opt.lb, opt.ub);
                }

                ret = nlopt_optimize_(elim_opt, x, x_offset, ref opt_f);

                if (elim_opt != opt)
                {
                    elimdim_destroy(elim_opt);
                    elimdim_expand(opt.n, x, x_offset, opt.lb, opt.ub);
                }
            }

done:
            if (maximize)  // restore original signs
            {
                opt.maximize = maximize;
                opt.stopval  = -opt.stopval;
                opt.f        = f;
                opt.f_data   = f_data;
                opt.pre      = pre;
                opt_f        = -opt_f;
            }

            return(ret);
        }