Skip to content

Commit

Permalink
Merge branch 'issue-296'
Browse files Browse the repository at this point in the history
  • Loading branch information
maisonobe committed Jan 6, 2024
2 parents 83a12ad + c9061cf commit 8b6b611
Show file tree
Hide file tree
Showing 60 changed files with 5,208 additions and 45 deletions.
1 change: 0 additions & 1 deletion hipparchus-core/src/main/java/org/hipparchus/dfp/Dfp.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@
import org.hipparchus.exception.MathIllegalArgumentException;
import org.hipparchus.exception.MathRuntimeException;
import org.hipparchus.util.FastMath;
import org.hipparchus.util.FieldSinCos;
import org.hipparchus.util.FieldSinhCosh;
import org.hipparchus.util.MathUtils;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@
import org.hipparchus.FieldElement;
import org.hipparchus.exception.LocalizedCoreFormats;
import org.hipparchus.exception.MathRuntimeException;
import org.hipparchus.exception.NullArgumentException;

/**
* Arbitrary precision decimal number.
Expand Down
5 changes: 5 additions & 0 deletions hipparchus-optim/src/changes/changes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ If the output is not quite correct, check for invisible trailing spaces!
<title>Hipparchus Optim Release Notes</title>
</properties>
<body>
<release version="3.1" date="TBC" description="TBC.">
<action dev="luc" type="add" due-to="Francesco Rocca" issue="issues/296">
Added constrained optimization.
</action>
</release>
<release version="3.0" date="2023-10-08" description="This is a major release.">
<action dev="bryan" type="update">
No changes directly in this module. However, lower level Hipparchus modules did change,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ public enum LocalizedOptimFormats implements Localizable {
UNABLE_TO_SOLVE_SINGULAR_PROBLEM("unable to solve: singular problem"),

/** UNBOUNDED_SOLUTION. */
UNBOUNDED_SOLUTION("unbounded solution");
UNBOUNDED_SOLUTION("unbounded solution"),

/** CONSTRAINTS_RANK. */
CONSTRAINTS_RANK("rank of constraints must be lesser than domain dimension, but {0} >= {1}");

/** Source English format. */
private final String sourceFormat;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ public class PowellOptimizer
* @param abs Absolute threshold.
* @param checker Convergence checker.
* @throws MathIllegalArgumentException if {@code abs <= 0}.
* @throws MathIllegalArgumentException if {@code rel < 2 * Math.ulp(1d)}.
* @throws MathIllegalArgumentException if {@code rel < 2 * FastMath.ulp(1d)}.
*/
public PowellOptimizer(double rel,
double abs,
Expand All @@ -106,7 +106,7 @@ public PowellOptimizer(double rel,
* @param lineAbs Absolute threshold for the internal line search optimizer.
* @param checker Convergence checker.
* @throws MathIllegalArgumentException if {@code abs <= 0}.
* @throws MathIllegalArgumentException if {@code rel < 2 * Math.ulp(1d)}.
* @throws MathIllegalArgumentException if {@code rel < 2 * FastMath.ulp(1d)}.
*/
public PowellOptimizer(double rel,
double abs,
Expand Down Expand Up @@ -142,7 +142,7 @@ public PowellOptimizer(double rel,
* @param rel Relative threshold.
* @param abs Absolute threshold.
* @throws MathIllegalArgumentException if {@code abs <= 0}.
* @throws MathIllegalArgumentException if {@code rel < 2 * Math.ulp(1d)}.
* @throws MathIllegalArgumentException if {@code rel < 2 * FastMath.ulp(1d)}.
*/
public PowellOptimizer(double rel,
double abs) {
Expand All @@ -157,7 +157,7 @@ public PowellOptimizer(double rel,
* @param lineRel Relative threshold for the internal line search optimizer.
* @param lineAbs Absolute threshold for the internal line search optimizer.
* @throws MathIllegalArgumentException if {@code abs <= 0}.
* @throws MathIllegalArgumentException if {@code rel < 2 * Math.ulp(1d)}.
* @throws MathIllegalArgumentException if {@code rel < 2 * FastMath.ulp(1d)}.
*/
public PowellOptimizer(double rel,
double abs,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
/*
* Licensed to the Hipparchus project under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The Hipparchus project licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hipparchus.optim.nonlinear.vector.constrained;

import org.hipparchus.linear.RealMatrix;
import org.hipparchus.linear.RealVector;
import org.hipparchus.optim.ConvergenceChecker;
import org.hipparchus.optim.OptimizationData;
import org.hipparchus.util.FastMath;

/** Convergence Checker for ADMM QP Optimizer.
* @since 3.1
*/
public class ADMMQPConvergenceChecker implements ConvergenceChecker<LagrangeSolution>, OptimizationData {

/** Quadratic term matrix. */
private final RealMatrix h;

/** Constraint coefficients matrix. */
private final RealMatrix a;

/** Linear term matrix. */
private final RealVector q;

/** Absolute tolerance for convergence. */
private final double epsAbs;

/** Relative tolerance for convergence. */
private final double epsRel;

/** Convergence indicator. */
private boolean converged;

/** Simple constructor.
* @param h quadratic term matrix
* @param a constraint coefficients matrix
* @param q linear term matrix
* @param epsAbs
* @param epsRel
*/
ADMMQPConvergenceChecker(final RealMatrix h, final RealMatrix a, final RealVector q,
final double epsAbs, final double epsRel) {
this.h = h;
this.a = a;
this.q = q;
this.epsAbs = epsAbs;
this.epsRel = epsRel;
this.converged = false;
}

/** {@inheritDoc} */
@Override
public boolean converged(final int i, final LagrangeSolution previous, final LagrangeSolution current) {
return converged;
}

/** Evaluate convergence.
* @param rp primal residual
* @param rd dual residual
* @param maxPrimal primal vectors max
* @param maxDual dual vectors max
* @return true of convergence has been reached
*/
public boolean converged(final double rp, final double rd, final double maxPrimal, final double maxDual) {
boolean result = false;

if (rp <= epsPrimalDual(maxPrimal) && rd <= epsPrimalDual(maxDual)) {
result = true;
converged = true;
}
return result;
}

/** Compute primal residual.
* @param x primal problem solution
* @param z auxiliary variable
* @return primal residual
*/
public double residualPrime(final RealVector x, final RealVector z) {
return a.operate(x).subtract(z).getLInfNorm();
}

/** Compute dual residual.
* @param x primal problem solution
* @param y dual problem solution
* @return dual residual
*/
public double residualDual(final RealVector x, final RealVector y) {
return q.add(a.transpose().operate(y)).add(h.operate(x)).getLInfNorm();
}

/** Compute primal vectors max.
* @param x primal problem solution
* @param z auxiliary variable
* @return primal vectors max
*/
public double maxPrimal(final RealVector x, final RealVector z) {
return FastMath.max(a.operate(x).getLInfNorm(), z.getLInfNorm());
}

/** Compute dual vectors max.
* @param x primal problem solution
* @param y dual problem solution
* @return dual vectors max
*/
public double maxDual(final RealVector x, final RealVector y) {
return FastMath.max(FastMath.max(h.operate(x).getLInfNorm(),
a.transpose().operate(y).getLInfNorm()),
q.getLInfNorm());
}

/** Combine absolute and relative tolerances.
* @param maxPrimalDual either {@link #maxPrimal(RealVector, RealVector)}
* or {@link #maxDual(RealVector, RealVector)}
* @return global tolerance
*/
private double epsPrimalDual(final double maxPrimalDual) {
return epsAbs + epsRel * maxPrimalDual;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* Licensed to the Hipparchus project under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The Hipparchus project licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.hipparchus.optim.nonlinear.vector.constrained;


import org.hipparchus.linear.ArrayRealVector;
import org.hipparchus.linear.DecompositionSolver;
import org.hipparchus.linear.EigenDecompositionSymmetric;
import org.hipparchus.linear.MatrixUtils;
import org.hipparchus.linear.RealMatrix;
import org.hipparchus.linear.RealVector;
import org.hipparchus.util.FastMath;

/** Alternative Direction Method of Multipliers Solver.
* @since 3.1
*/
public class ADMMQPKKT implements KarushKuhnTuckerSolver<ADMMQPSolution> {

/** Square matrix of weights for quadratic terms. */
private RealMatrix H;

/** Vector of weights for linear terms. */
private RealVector q;

/** constraints coefficients matrix. */
private RealMatrix A;

/** Regularization term sigma for Karush–Kuhn–Tucker solver. */
private double sigma;

/** TBC. */
private RealMatrix R;

/** Inverse of R. */
private RealMatrix Rinv;

/** Lower bound. */
private RealVector lb;

/** Upper bound. */
private RealVector ub;

/** Alpha filter for ADMM iteration. */
private double alpha;

/** Constrained problem KKT matrix. */
private RealMatrix M;

/** Solver for M. */
private DecompositionSolver dsX;

/** Simple constructor.
* <p>
* BEWARE, nothing is initialized here, it is {@link #initialize(RealMatrix, RealMatrix,
* RealVector, int, RealVector, RealVector, double, double, double) initialize} <em>must</em>
* be called before using the instance.
* </p>
*/
ADMMQPKKT() {
// nothing initialized yet!
}

/** {@inheritDoc} */
@Override
public ADMMQPSolution solve(RealVector b1, final RealVector b2) {
RealVector z = dsX.solve(new ArrayRealVector((ArrayRealVector) b1,b2));
return new ADMMQPSolution(z.getSubVector(0,b1.getDimension()), z.getSubVector(b1.getDimension(), b2.getDimension()));
}

public void updateSigmaRho(double newSigma, int me, double rho) {
this.sigma = newSigma;
this.H = H.add(MatrixUtils.createRealIdentityMatrix(H.getColumnDimension()).scalarMultiply(newSigma));
createPenaltyMatrix(me, rho);
M = MatrixUtils.createRealMatrix(H.getRowDimension() + A.getRowDimension(),
H.getRowDimension() + A.getRowDimension());
M.setSubMatrix(H.getData(), 0,0);
M.setSubMatrix(A.getData(), H.getRowDimension(),0);
M.setSubMatrix(A.transpose().getData(), 0, H.getRowDimension());
M.setSubMatrix(Rinv.scalarMultiply(-1.0).getData(), H.getRowDimension(),H.getRowDimension());
dsX = new EigenDecompositionSymmetric(M).getSolver();
}

public void initialize(RealMatrix newH, RealMatrix newA, RealVector newQ,
int me, RealVector newLb, RealVector newUb,
double rho, double newSigma, double newAlpha) {
this.lb = newLb;
this.ub = newUb;
this.alpha = newAlpha;
this.sigma = newSigma;
this.H = newH.add(MatrixUtils.createRealIdentityMatrix(newH.getColumnDimension()).scalarMultiply(newSigma));
this.A = newA.copy();
this.q = newQ.copy();
createPenaltyMatrix(me, rho);

M = MatrixUtils.createRealMatrix(newH.getRowDimension() + newA.getRowDimension(),
newH.getRowDimension() + newA.getRowDimension());
M.setSubMatrix(newH.getData(),0,0);
M.setSubMatrix(newA.getData(),newH.getRowDimension(),0);
M.setSubMatrix(newA.transpose().getData(),0,newH.getRowDimension());
M.setSubMatrix(Rinv.scalarMultiply(-1.0).getData(),newH.getRowDimension(),newH.getRowDimension());
dsX = new EigenDecompositionSymmetric(M).getSolver();
}

private void createPenaltyMatrix(int me, double rho) {
this.R = MatrixUtils.createRealIdentityMatrix(A.getRowDimension());

for (int i = 0; i < R.getRowDimension(); i++) {
if (i < me) {
R.setEntry(i, i, rho * 1000.0);

} else {
R.setEntry(i, i, rho);

}
}
this.Rinv = MatrixUtils.inverse(R);
}

/** {@inheritDoc} */
@Override
public ADMMQPSolution iterate(RealVector... previousSol) {
double onealfa = 1.0 - alpha;
//SAVE OLD VALUE
RealVector xold = previousSol[0].copy();
RealVector yold = previousSol[1].copy();
RealVector zold = previousSol[2].copy();

//UPDATE RIGHT VECTOR
RealVector b1 = previousSol[0].mapMultiply(sigma).subtract(q);
RealVector b2 = previousSol[2].subtract(Rinv.operate(previousSol[1]));

//SOLVE KKT SYSYEM
ADMMQPSolution sol = solve(b1, b2);
RealVector xtilde = sol.getX();
RealVector vtilde = sol.getV();

//UPDATE ZTILDE
RealVector ztilde = zold.add(Rinv.operate(vtilde.subtract(yold)));
//UPDATE X
previousSol[0] = xtilde.mapMultiply(alpha).add((xold.mapMultiply(onealfa)));

//UPDATE Z PARTIAL
RealVector zpartial = ztilde.mapMultiply(alpha).add(zold.mapMultiply(onealfa)).add(Rinv.operate(yold));

//PROJECT ZPARTIAL AND UPDATE Z
for (int j = 0; j < previousSol[2].getDimension(); j++) {
previousSol[2].setEntry(j, FastMath.min(FastMath.max(zpartial.getEntry(j), lb.getEntry(j)), ub.getEntry(j)));
}

//UPDATE Y
RealVector ytilde = ztilde.mapMultiply(alpha).add(zold.mapMultiply(onealfa).subtract(previousSol[2]));
previousSol[1] = yold.add(R.operate(ytilde));

return new ADMMQPSolution(previousSol[0], vtilde, previousSol[1], previousSol[2]);
}

}
Loading

0 comments on commit 8b6b611

Please sign in to comment.