SQPOptimizerGM.java

  1. /*
  2.  * Licensed to the Hipparchus project under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The Hipparchus project licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      https://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  * Unless required by applicable law or agreed to in writing, software
  12.  * distributed under the License is distributed on an "AS IS" BASIS,
  13.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  * See the License for the specific language governing permissions and
  15.  * limitations under the License.
  16.  */
  17. package org.hipparchus.optim.nonlinear.vector.constrained;

  18. import org.hipparchus.linear.Array2DRowRealMatrix;
  19. import org.hipparchus.linear.ArrayRealVector;
  20. import org.hipparchus.linear.EigenDecompositionSymmetric;
  21. import org.hipparchus.linear.MatrixUtils;
  22. import org.hipparchus.linear.RealMatrix;
  23. import org.hipparchus.linear.RealVector;
  24. import org.hipparchus.optim.nonlinear.scalar.ObjectiveFunction;
  25. import org.hipparchus.util.FastMath;

  26. /**
  27.  * Sequential Quadratic Programming Optimizer.
  28.  * <br/>
  29.  * min f(x)
  30.  * <br/>
  31.  * q(x)=b1
  32.  * <br/>
  33.  * h(x)>=b2
  34.  * <br/>
  35.  * Algorithm based on paper:"Some Theoretical properties of an augmented lagrangian merit function
  36.  * (Gill,Murray,Sauders,Wriht,April 1986)"
  37.  * @since 3.1
  38.  */
  39. public class SQPOptimizerGM extends AbstractSQPOptimizer {

  40.     /** Jacobian constraint. */
  41.     private RealMatrix constraintJacob;

  42.     /** Value of the equality constraint. */
  43.     private RealVector equalityEval;

  44.     /** Value of the inequality constraint. */
  45.     private RealVector inequalityEval;

  46.     /** Gradient of the objective function. */
  47.     private RealVector functionGradient;

  48.     /** Hessian of the objective function. */
  49.     private RealMatrix hessian;

  50.     /** {@inheritDoc} */
  51.     @Override
  52.     public LagrangeSolution doOptimize() {
  53.         int me = 0;
  54.         int mi = 0;

  55.         //EQUALITY CONSTRAINT
  56.         if (getEqConstraint() != null) {
  57.             me = getEqConstraint().dimY();
  58.         }
  59.         //INEQUALITY CONSTRAINT
  60.         if (getIqConstraint() != null) {
  61.             mi = getIqConstraint().dimY();
  62.         }

  63.         double alpha;
  64.         double rho;
  65.         RealVector x;
  66.         if (getStartPoint() != null) {
  67.             x = new ArrayRealVector(getStartPoint());
  68.         } else {
  69.             x = new ArrayRealVector(getObj().dim());
  70.         }

  71.         RealVector y = new ArrayRealVector(me + mi, 0.0);
  72.          //INITIAL VALUES
  73.         double functionEval = getObj().value(x);
  74.         functionGradient = getObj().gradient(x);
  75.         double maxGrad = functionGradient.getLInfNorm();

  76.         if (getEqConstraint() != null) {
  77.           equalityEval = getEqConstraint().value(x);
  78.         }
  79.         if (getIqConstraint() != null) {
  80.             inequalityEval = getIqConstraint().value(x);
  81.         }
  82.         constraintJacob = computeJacobianConstraint(x);

  83.         if (getEqConstraint() != null) {
  84.             maxGrad = FastMath.max(maxGrad, equalityEval.getLInfNorm());
  85.         }
  86.         if (getIqConstraint() != null) {
  87.             maxGrad = FastMath.max(maxGrad, inequalityEval.getLInfNorm());
  88.         }

  89.         if (!getSettings().useFunHessian()) {
  90.             hessian= MatrixUtils.createRealIdentityMatrix(x.getDimension()).scalarMultiply(maxGrad);
  91.         } else {
  92.             hessian = getObj().hessian(x);
  93.         }


  94.         rho = 0;

  95.         for (int i = 0; i < getMaxIterations(); i++) {

  96.             iterations.increment();

  97.             alpha = 1.0;

  98.             LagrangeSolution sol1;
  99.             //SOLVE QP
  100.             sol1 = solveQP(x);
  101.             RealVector p = sol1.getX();
  102.             RealVector e = sol1.getLambda().subtract(y);

  103.             RealVector s = calculateSvector(y,rho);
  104.             RealVector se = null;
  105.             RealMatrix jacobi; // NOPMD - PMD detext a false positive here
  106.             RealVector q = null;
  107.             RealVector qe = null;

  108.             //TEST CON SI SOLO PER INEQUALITY AND Q FOR ALL
  109.             if (getEqConstraint() != null) {
  110.                 se = new ArrayRealVector(me);
  111.                 jacobi = constraintJacob.getSubMatrix(0, me - 1, 0, x.getDimension() - 1); // NOPMD - PMD detect a false positive here
  112.                 qe = new ArrayRealVector(me);
  113.             }
  114.             if (getIqConstraint() != null) {
  115.                 jacobi = constraintJacob.getSubMatrix(me, me + mi - 1, 0, x.getDimension() - 1);
  116.                 q = jacobi.operate(p).add(inequalityEval.subtract(getIqConstraint().getLowerBound())).subtract(s);
  117.             }



  118.             //CALCULATE PENALTY GRADIENT
  119.             //
  120.             double penaltyGradient = penaltyFunctionGrad(p, y, s, e, qe, q, rho);
  121.             ArrayRealVector g = new ArrayRealVector(me + mi);
  122.             if (me > 0) {
  123.                 g.setSubVector(0,equalityEval.subtract(getEqConstraint().getLowerBound()));
  124.             }
  125.             if (mi > 0) {
  126.                 g.setSubVector(me, inequalityEval.subtract(getIqConstraint().getLowerBound()).subtract(s));
  127.             }

  128.             double rhoSegnato = 2.0 * e.getNorm() / g.getNorm();

  129.            // rho = rhoSegnato;
  130.             //if (!(penaltyGradient<=-0.5 * p.dotProduct(hessian.operate(p))))
  131.            while (penaltyGradient > -0.5 * p.dotProduct(hessian.operate(p))) {

  132.                  rho = FastMath.max(rhoSegnato,2.0 * rho);

  133.                 penaltyGradient = penaltyFunctionGrad(p, y, s, e, qe, q, rho);
  134.             }
  135.             //LINE SEARCH
  136.             double alfaEval = getObj().value(x.add(p.mapMultiply(alpha)));

  137.             double alphaPenalty;
  138.             RealVector sineq = null;
  139.             RealVector seq = null;
  140.             if (se != null) {
  141.                 seq = se.add(qe.mapMultiply(alpha));
  142.             }
  143.             if (s != null) {
  144.                 sineq = s.add(q.mapMultiply(alpha));
  145.             }

  146.             double currentPenalty = penaltyFunction(functionEval, x, y, se, s, rho);
  147.             alphaPenalty = penaltyFunction(alfaEval,x.add(p.mapMultiply(alpha)),
  148.                                            y.add(e.mapMultiply(alpha)),
  149.                                            seq, sineq, rho);



  150.             int search = 0;
  151.             while ((alphaPenalty -currentPenalty) >= getSettings().getMu() * alpha *  penaltyGradient &&
  152.                    search < getSettings().getMaxLineSearchIteration()) {

  153.                 double alfaStar = -0.5 * alpha * alpha *  penaltyGradient/ (-alpha *  penaltyGradient + alphaPenalty - currentPenalty);

  154.                 alpha =  FastMath.max(getSettings().getB() * alpha, FastMath.min(1.0,alfaStar));
  155.                 alfaEval = getObj().value(x.add(p.mapMultiply(alpha)));
  156.                 if (se != null) {
  157.                     seq = se.add(qe.mapMultiply(alpha));
  158.                 }
  159.                 if (s != null) {
  160.                     sineq = s.add(q.mapMultiply(alpha));
  161.                 }
  162.                 alphaPenalty = penaltyFunction(alfaEval,x.add(p.mapMultiply(alpha)),y.add(e.mapMultiply(alpha)),seq,sineq,rho);

  163.                 search = search + 1;

  164.             }


  165.             if (getSettings().getConvCriteria() == 0) {
  166.                 if (p.mapMultiply(alpha).dotProduct(hessian.operate(p.mapMultiply(alpha))) < getSettings().getEps() * getSettings().getEps()) {
  167.                     break;
  168.                 }
  169.             } else {
  170.                 if (alpha * p.getNorm() <FastMath.sqrt(getSettings().getEps()) * (1 + x.getNorm())) {
  171.                     break;
  172.                 }

  173.             }

  174.             //UPDATE ALL FUNCTION
  175.             RealVector oldGradient = functionGradient;
  176.             RealMatrix oldJacob = constraintJacob;
  177.             RealVector old1 = lagrangianGradX(oldGradient,oldJacob,x,y.add(e.mapMultiply(alpha)));
  178.             functionEval = alfaEval;
  179.             functionGradient = getObj().gradient(x.add(p.mapMultiply(alpha)));
  180.             constraintJacob = computeJacobianConstraint(x.add(p.mapMultiply(alpha)));
  181.             RealVector new1 = lagrangianGradX(functionGradient,constraintJacob,x.add(p.mapMultiply(alpha)),y.add(e.mapMultiply(alpha)));
  182.             hessian = BFGSFormula(hessian, p, alpha, new1, old1);

  183.             if (getEqConstraint() != null) {
  184.                 equalityEval = getEqConstraint().value(x.add(p.mapMultiply(alpha)));
  185.             }
  186.             if (getIqConstraint() != null) {
  187.                 inequalityEval = getIqConstraint().value(x.add(p.mapMultiply(alpha)));
  188.             }

  189.             x = x.add(p.mapMultiply(alpha));
  190.             y = y.add(e.mapMultiply(alpha));
  191.         }

  192.         functionEval = getObj().value(x);
  193.         functionGradient = getObj().gradient(x);

  194.         constraintJacob = computeJacobianConstraint(x);
  195.         return new LagrangeSolution(x, y, functionEval);
  196.     }

  197.     private RealVector calculateSvector(RealVector y, double rho) {
  198.         int me = 0;
  199.         int mi;
  200.         RealVector si = null;
  201.         if (getEqConstraint() != null) {
  202.             me = getEqConstraint().dimY();
  203.         }
  204.         if (getIqConstraint() != null) {
  205.             mi = getIqConstraint().dimY();
  206.             si = new ArrayRealVector(mi);
  207.             RealVector yi = y.getSubVector(me, mi);
  208.             for (int i = 0; i < inequalityEval.getDimension(); i++) {
  209.                 if (rho == 0) {
  210.                     si.setEntry(i, FastMath.max(0, inequalityEval.getEntry(i)));
  211.                 } else {
  212.                     si.setEntry(i, FastMath.max(0, inequalityEval.getEntry(i) - yi.getEntry(i) / rho));
  213.                 }
  214.             }
  215.         }
  216.         return si;
  217.     }

  218.     private double penaltyFunction(double currentF, RealVector x, RealVector y, RealVector se, RealVector s, double rho) {
  219.         // the set of constraints is the same as the previous one but they must be evaluated with the increment

  220.         int me = 0;
  221.         int mi;
  222.         double partial = currentF;

  223.         if (getEqConstraint() != null) {
  224.             me = getEqConstraint().dimY();

  225.             RealVector ye = y.getSubVector(0, me);
  226.             RealVector g = getEqConstraint().value(x).subtract(getEqConstraint().getLowerBound());
  227.             partial += -ye.dotProduct(g.subtract(se)) + 0.5 * rho * (g.subtract(se)).dotProduct(g.subtract(se));
  228.         }

  229.         if (getIqConstraint() != null) {
  230.             mi = getIqConstraint().dimY();

  231.             RealVector yi = y.getSubVector(me, mi);

  232.             RealVector g = getIqConstraint().value(x).subtract(getIqConstraint().getLowerBound());





  233.             partial+= -yi.dotProduct(g.subtract(s)) +0.5 * rho*(g.subtract(s)).dotProduct(g.subtract(s));

  234.         }

  235.         return partial;
  236.     }

  237.     private double penaltyFunctionGrad(RealVector p, RealVector y,RealVector s,RealVector e,RealVector qe, RealVector q,double rho) {

  238.         int me = 0;
  239.         int mi;
  240.         double partial = functionGradient.dotProduct(p);
  241.         if (getEqConstraint() != null) {
  242.             me = getEqConstraint().dimY();

  243.             RealVector ye = y.getSubVector(0, me);
  244.             RealVector ee = e.getSubVector(0, me);
  245.           //  RealVector qe = q.getSubVector(0, me);


  246.             RealVector ge = equalityEval.subtract(getEqConstraint().getLowerBound());
  247.             RealMatrix jacob = constraintJacob.getSubMatrix(0, me - 1, 0, p.getDimension() - 1);
  248.             double term2 = p.dotProduct(jacob.transpose().operate(ye));
  249.             double term3 = p.dotProduct(jacob.transpose().operate(ge))*rho;
  250.             double term4 = ge.dotProduct(ee);
  251.             double term5 = ye.dotProduct(qe);
  252.             double term6 = qe.dotProduct(ge)*rho;
  253.             partial +=-term2 + term3-term4 + term5-term6;
  254.         }

  255.         if (getIqConstraint() != null) {
  256.             mi = getIqConstraint().dimY();

  257.             RealVector yi = y.getSubVector(me, mi);
  258.             RealVector ei = e.getSubVector(me, mi);

  259.             RealVector gi = inequalityEval.subtract(getIqConstraint().getLowerBound());
  260.             RealMatrix jacob = constraintJacob.getSubMatrix(me, me + mi - 1, 0, p.getDimension() - 1);
  261.             double term2 = p.dotProduct(jacob.transpose().operate(yi));
  262.             double term3 = p.dotProduct(jacob.transpose().operate(gi.subtract(s)))*rho;
  263.             double term4 = (gi.subtract(s)).dotProduct(ei);
  264.             double term5 = yi.dotProduct(q);
  265.             double term6 = q.dotProduct(gi.subtract(s))*rho;

  266.             partial +=-term2 + term3-term4 + term5-term6;
  267.         }

  268.         return partial;
  269.     }

  270.     private LagrangeSolution solveQP(RealVector x) {

  271.         RealMatrix H = hessian;
  272.         RealVector g = functionGradient;

  273.         int me = 0;
  274.         int mi = 0;
  275.         if (getEqConstraint() != null) {
  276.             me = getEqConstraint().dimY();
  277.         }
  278.         if (getIqConstraint() != null) {

  279.             mi = getIqConstraint().dimY();

  280.         }

  281.         RealMatrix H1 = new Array2DRowRealMatrix(H.getRowDimension() , H.getRowDimension() );
  282.         H1.setSubMatrix(H.getData(), 0, 0);
  283.         RealVector g1 = new ArrayRealVector(g.getDimension() );
  284.         g1.setSubVector(0, g);

  285.         LinearEqualityConstraint eqc = null;
  286.         if (getEqConstraint() != null) {
  287.             RealMatrix eqJacob = constraintJacob.getSubMatrix(0,me-1,0,x.getDimension()-1);

  288.             RealMatrix Ae = new Array2DRowRealMatrix(me, x.getDimension() );
  289.             RealVector be = new ArrayRealVector(me);
  290.             Ae.setSubMatrix(eqJacob.getData(), 0, 0);



  291.             be.setSubVector(0, getEqConstraint().getLowerBound().subtract(equalityEval));
  292.             eqc = new LinearEqualityConstraint(Ae, be);

  293.         }
  294.         LinearInequalityConstraint iqc = null;

  295.         if (getIqConstraint() != null) {

  296.             RealMatrix iqJacob = constraintJacob.getSubMatrix(me,me + mi-1,0,x.getDimension()-1);

  297.             RealMatrix Ai = new Array2DRowRealMatrix(mi, x.getDimension());
  298.             RealVector bi = new ArrayRealVector(mi);
  299.             Ai.setSubMatrix(iqJacob.getData(), 0, 0);

  300.             bi.setSubVector(0, getIqConstraint().getLowerBound().subtract(inequalityEval));

  301.             iqc = new LinearInequalityConstraint(Ai, bi);

  302.         }

  303.         QuadraticFunction q = new QuadraticFunction(H1, g1, 0);
  304.         LagrangeSolution sol;

  305.         ADMMQPOptimizer solver = new ADMMQPOptimizer();
  306.         sol = solver.optimize(new ObjectiveFunction(q), iqc, eqc);

  307.         return new LagrangeSolution(sol.getX(), sol.getLambda(),0.0);

  308.     }

  309.     private RealMatrix computeJacobianConstraint(RealVector x) {
  310.         int me = 0;
  311.         int mi = 0;
  312.         RealMatrix je = null;
  313.         RealMatrix ji = null;
  314.         if (getEqConstraint() != null) {
  315.             me = getEqConstraint().dimY();
  316.             je = getEqConstraint().jacobian(x);
  317.         }

  318.         if (getIqConstraint() != null) {
  319.             mi = getIqConstraint().dimY();
  320.             ji = getIqConstraint().jacobian(x);
  321.         }

  322.         RealMatrix jacobian = new Array2DRowRealMatrix(me + mi, x.getDimension());
  323.         if (me > 0) {
  324.             jacobian.setSubMatrix(je.getData(), 0, 0);
  325.         }
  326.         if (mi > 0) {
  327.             jacobian.setSubMatrix(ji.getData(), me, 0);
  328.         }

  329.         return jacobian;
  330.     }
  331.     /*
  332.      *DAMPED BFGS FORMULA
  333.      */

  334.     private RealMatrix BFGSFormula(RealMatrix oldH, RealVector dx, double alfa, RealVector newGrad, RealVector oldGrad) {

  335.         RealMatrix oldH1 = oldH;
  336.         RealVector y1 = newGrad.subtract(oldGrad);
  337.         RealVector s = dx.mapMultiply(alfa);

  338.         double theta = 1.0;
  339.         if (s.dotProduct(y1) <= 0.2 * s.dotProduct(oldH.operate(s))) {
  340.             theta = 0.8 * s.dotProduct(oldH.operate(s)) / (s.dotProduct(oldH.operate(s)) - s.dotProduct(y1));
  341.         }

  342.         RealVector y = y1.mapMultiply(theta).add(oldH.operate(s).mapMultiply(1.0 - theta));

  343.         RealMatrix firstTerm = y.outerProduct(y).scalarMultiply(1.0 / s.dotProduct(y));
  344.         RealMatrix secondTerm = oldH1.multiply(s.outerProduct(s)).multiply(oldH);
  345.         double thirtTerm = s.dotProduct(oldH1.operate(s));
  346.         RealMatrix Hnew = oldH1.add(firstTerm).subtract(secondTerm.scalarMultiply(1.0 / thirtTerm));
  347.         //RESET HESSIAN IF NOT POSITIVE DEFINITE
  348.         EigenDecompositionSymmetric dsX = new EigenDecompositionSymmetric(Hnew);
  349.         double min = new ArrayRealVector(dsX.getEigenvalues()).getMinValue();
  350.         if (new ArrayRealVector(dsX.getEigenvalues()).getMinValue() < 0) {

  351.             RealMatrix diag = dsX.getD().
  352.                               add(MatrixUtils.createRealIdentityMatrix(dx.getDimension()).
  353.                                   scalarMultiply(getSettings().getEps() - min));
  354.             Hnew = dsX.getV().multiply(diag.multiply(dsX.getVT()));

  355.         }
  356.         return Hnew;

  357.     }

  358. }