View Javadoc
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  
19  
20  import org.hipparchus.linear.ArrayRealVector;
21  import org.hipparchus.linear.DecompositionSolver;
22  import org.hipparchus.linear.EigenDecompositionSymmetric;
23  import org.hipparchus.linear.MatrixUtils;
24  import org.hipparchus.linear.RealMatrix;
25  import org.hipparchus.linear.RealVector;
26  import org.hipparchus.util.FastMath;
27  
28  /** Alternative Direction Method of Multipliers Solver.
29   * @since 3.1
30   */
31  public class ADMMQPKKT implements KarushKuhnTuckerSolver<ADMMQPSolution> {
32  
33      /** Square matrix of weights for quadratic terms. */
34      private RealMatrix H;
35  
36      /** Vector of weights for linear terms. */
37      private RealVector q;
38  
39      /** Constraints coefficients matrix. */
40      private RealMatrix A;
41  
42      /** Regularization term sigma for Karush–Kuhn–Tucker solver. */
43      private double sigma;
44  
45      /** TBC. */
46      private RealMatrix R;
47  
48      /** Inverse of R. */
49      private RealMatrix Rinv;
50  
51      /** Lower bound. */
52      private RealVector lb;
53  
54      /** Upper bound. */
55      private RealVector ub;
56  
57      /** Alpha filter for ADMM iteration. */
58      private double alpha;
59  
60      /** Constrained problem KKT matrix. */
61      private RealMatrix M;
62  
63      /** Solver for M. */
64      private DecompositionSolver dsX;
65  
66      /** Simple constructor.
67       * <p>
68       * BEWARE, nothing is initialized here, it is {@link #initialize(RealMatrix, RealMatrix,
69       * RealVector, int, RealVector, RealVector, double, double, double) initialize} <em>must</em>
70       * be called before using the instance.
71       * </p>
72       */
73      ADMMQPKKT() {
74          // nothing initialized yet!
75      }
76  
77      /** {@inheritDoc} */
78      @Override
79      public ADMMQPSolution solve(RealVector b1, final RealVector b2) {
80          RealVector z = dsX.solve(new ArrayRealVector((ArrayRealVector) b1,b2));
81          return new ADMMQPSolution(z.getSubVector(0,b1.getDimension()), z.getSubVector(b1.getDimension(), b2.getDimension()));
82      }
83  
84      /** Update steps
85       * @param newSigma new regularization term sigma for Karush–Kuhn–Tucker solver
86       * @param me number of equality constraints
87       * @param rho new step size
88       */
89      public void updateSigmaRho(double newSigma, int me, double rho) {
90          this.sigma = newSigma;
91          this.H = H.add(MatrixUtils.createRealIdentityMatrix(H.getColumnDimension()).scalarMultiply(newSigma));
92          createPenaltyMatrix(me, rho);
93          M =  MatrixUtils.createRealMatrix(H.getRowDimension() + A.getRowDimension(),
94                                            H.getRowDimension() + A.getRowDimension());
95          M.setSubMatrix(H.getData(), 0,0);
96          M.setSubMatrix(A.getData(), H.getRowDimension(),0);
97          M.setSubMatrix(A.transpose().getData(), 0, H.getRowDimension());
98          M.setSubMatrix(Rinv.scalarMultiply(-1.0).getData(), H.getRowDimension(),H.getRowDimension());
99          dsX = new EigenDecompositionSymmetric(M).getSolver();
100     }
101 
102     /** Initialize problem
103      * @param newH square matrix of weights for quadratic term
104      * @param newA constraints coefficients matrix
105      * @param newQ TBD
106      * @param me number of equality constraints
107      * @param newLb lower bound
108      * @param newUb upper bound
109      * @param rho step size
110      * @param newSigma regularization term sigma for Karush–Kuhn–Tucker solver
111      * @param newAlpha alpha filter for ADMM iteration
112      */
113     public void initialize(RealMatrix newH, RealMatrix newA, RealVector newQ,
114                            int me, RealVector newLb, RealVector newUb,
115                            double rho, double newSigma, double newAlpha) {
116         this.lb = newLb;
117         this.ub = newUb;
118         this.alpha = newAlpha;
119         this.sigma = newSigma;
120         this.H = newH.add(MatrixUtils.createRealIdentityMatrix(newH.getColumnDimension()).scalarMultiply(newSigma));
121         this.A = newA.copy();
122         this.q = newQ.copy();
123         createPenaltyMatrix(me, rho);
124 
125         M =  MatrixUtils.createRealMatrix(newH.getRowDimension() + newA.getRowDimension(),
126                                           newH.getRowDimension() + newA.getRowDimension());
127         M.setSubMatrix(newH.getData(),0,0);
128         M.setSubMatrix(newA.getData(),newH.getRowDimension(),0);
129         M.setSubMatrix(newA.transpose().getData(),0,newH.getRowDimension());
130         M.setSubMatrix(Rinv.scalarMultiply(-1.0).getData(),newH.getRowDimension(),newH.getRowDimension());
131         dsX = new EigenDecompositionSymmetric(M).getSolver();
132     }
133 
134     private void createPenaltyMatrix(int me, double rho) {
135         this.R = MatrixUtils.createRealIdentityMatrix(A.getRowDimension());
136 
137         for (int i = 0; i < R.getRowDimension(); i++) {
138             if (i < me) {
139                 R.setEntry(i, i, rho * 1000.0);
140 
141             } else {
142                 R.setEntry(i, i, rho);
143 
144             }
145         }
146         this.Rinv = MatrixUtils.inverse(R);
147     }
148 
149     /** {@inheritDoc} */
150     @Override
151     public ADMMQPSolution iterate(RealVector... previousSol) {
152         double onealfa = 1.0 - alpha;
153         //SAVE OLD VALUE
154         RealVector xold = previousSol[0].copy();
155         RealVector yold = previousSol[1].copy();
156         RealVector zold = previousSol[2].copy();
157 
158         //UPDATE RIGHT VECTOR
159         RealVector b1 = previousSol[0].mapMultiply(sigma).subtract(q);
160         RealVector b2 = previousSol[2].subtract(Rinv.operate(previousSol[1]));
161 
162         //SOLVE KKT SYSYEM
163         ADMMQPSolution sol = solve(b1, b2);
164         RealVector xtilde = sol.getX();
165         RealVector vtilde = sol.getV();
166 
167         //UPDATE ZTILDE
168         RealVector ztilde = zold.add(Rinv.operate(vtilde.subtract(yold)));
169         //UPDATE X
170         previousSol[0] = xtilde.mapMultiply(alpha).add(xold.mapMultiply(onealfa));
171 
172         //UPDATE Z PARTIAL
173         RealVector zpartial = ztilde.mapMultiply(alpha).add(zold.mapMultiply(onealfa)).add(Rinv.operate(yold));
174 
175         //PROJECT ZPARTIAL AND UPDATE Z
176         for (int j = 0; j < previousSol[2].getDimension(); j++) {
177             previousSol[2].setEntry(j, FastMath.min(FastMath.max(zpartial.getEntry(j), lb.getEntry(j)), ub.getEntry(j)));
178         }
179 
180         //UPDATE Y
181         RealVector ytilde = ztilde.mapMultiply(alpha).add(zold.mapMultiply(onealfa).subtract(previousSol[2]));
182         previousSol[1] = yold.add(R.operate(ytilde));
183 
184         return new ADMMQPSolution(previousSol[0], vtilde, previousSol[1], previousSol[2]);
185     }
186 
187 }