ADMMQPModifiedRuizEquilibrium.java

/*
 * 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.MatrixUtils;
import org.hipparchus.linear.RealMatrix;
import org.hipparchus.linear.RealVector;
import org.hipparchus.util.FastMath;

/** TBD.
 * @since 3.1
 */
public class ADMMQPModifiedRuizEquilibrium {

    /** Minimum scaling value. */
    private static final double MIN_SCALING = 1.0e-6;

    /** Maximum scaling value. */
    private static final double MAX_SCALING = 1.0e+6;

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

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

    /** Constraints coefficients matrix. */
    private final RealMatrix A;

    /** TBC. */
    private RealMatrix D;

    /** TBC. */
    private RealMatrix E;

    /** TBC. */
    private double c;

    /** Inverse of D. */
    private RealMatrix Dinv;

    /** Inverse of E. */
    private RealMatrix Einv;

    /** Inverse of c. */
    private double cinv;

    /** Simple constructor
     * @param H square matrix of weights for quadratic terms
     * @param A constraints coefficients matrix
     * @param q vector of weights for linear terms
     */
    public ADMMQPModifiedRuizEquilibrium(RealMatrix H, RealMatrix A, RealVector q) {
        this.H  = H;
        this.A  = A;
        this.q  = q;
    }

    /** Normalize matrices.
     * @param epsilon TBD
     * @param maxIteration TBD
     */
    public void normalize(double epsilon, int maxIteration) {

        int iteration = 0;
        this.c = 1.0;
        RealVector gammaD = new ArrayRealVector(H.getRowDimension());
        RealVector gammaE = new ArrayRealVector(A.getRowDimension());

        double lambda;
        RealVector q1 = q.copy();
        RealMatrix H1 = H.copy();
        RealMatrix A1 = A.copy();
        RealMatrix diagD;
        RealMatrix diagE;
        RealMatrix SD = MatrixUtils.createRealIdentityMatrix(H.getRowDimension());
        RealMatrix SE = MatrixUtils.createRealIdentityMatrix(A.getRowDimension());
        RealVector H1norm = new ArrayRealVector(H1.getColumnDimension());
        while (iteration < maxIteration) {
       // while (1.0 - gamma.getLInfNorm() > epsilon && iteration < maxIteration) {

            for (int i = 0; i < H1.getColumnDimension(); i++) {
                double norma = (new ArrayRealVector(H1.getColumn(i), A1.getColumn(i))).getLInfNorm();
                gammaD.setEntry(i, norma);
            }

            for (int i = 0; i < A1.getRowDimension(); i++) {
                double norma = A1.getRowVector(i).getLInfNorm();
                gammaE.setEntry(i, norma);
            }

            gammaD = limitScaling(gammaD);
            gammaE = limitScaling(gammaE);

            for (int i = 0; i < gammaD.getDimension(); i++) {
                gammaD.setEntry(i, 1.0 / FastMath.sqrt(gammaD.getEntry(i)));
            }

            for (int i = 0; i < gammaE.getDimension(); i++) {
                gammaE.setEntry(i, 1.0 / FastMath.sqrt(gammaE.getEntry(i)));
            }

            diagD = MatrixUtils.createRealDiagonalMatrix(gammaD.toArray());
            diagE = MatrixUtils.createRealDiagonalMatrix(gammaE.toArray());

            H1 = diagD.multiply(H1.copy()).multiply(diagD.copy());
            q1 = diagD.operate(q1.copy());
            A1 = diagE.multiply(A1.copy()).multiply(diagD.copy());

            for (int i = 0; i < H1.getRowDimension(); i++) {
                double norma = (new ArrayRealVector(H1.getRow(i))).getLInfNorm();
                H1norm.setEntry(i, norma);
            }
            double qnorm = q1.getLInfNorm();
            if (qnorm == 0) {
                qnorm = 1.0;
            }
            qnorm = limitScaling(new ArrayRealVector(1, qnorm)).getEntry(0);
            double mean = 0;
            for (int i = 0; i < H1norm.getDimension(); i++) {
                mean+=H1norm.getEntry(i) / H1norm.getDimension();
            }

            lambda = 1.0 / limitScaling(new ArrayRealVector(1, FastMath.max(mean, qnorm))).getEntry(0);

            H1 = H1.copy().scalarMultiply(lambda);
            q1 = q1.copy().mapMultiply(lambda);
            c *= lambda;
            SD = diagD.multiply(SD.copy());
            SE = diagE.multiply(SE.copy());
            iteration += 1;
        }
        this.E    = SE.copy();
        this.D    = SD.copy();
        this.Einv = MatrixUtils.inverse(SE);
        this.Dinv = MatrixUtils.inverse(SD);
        this.cinv = 1.0 / c;

    }

    /** Get scaled square matrix of weights for quadratic terms.
     * @return scaled square matrix of weights for quadratic terms
     */
    public RealMatrix getScaledH() {
        return D.multiply(H).multiply(D).scalarMultiply(c);
    }

    /** Get scaled constraints coefficients matrix.
     * @return scaled constraints coefficients matrix
     */
    public RealMatrix getScaledA() {
        return E.multiply(A).multiply(D);
    }

    /** Get scaled vector of weights for linear terms.
     * @return scaled vector of weights for linear terms
     */
    public RealVector getScaledQ() {
        return D.operate(q.mapMultiply(c));
    }

    /** Get scaled upper bound
     * @param lb1 unscaled lower bound
     * @return scaled lower bound
     */
    public RealVector getScaledLUb(RealVector lb1) {
        RealVector scaledLUb = new ArrayRealVector(lb1.getDimension());
        for (int i = 0; i < lb1.getDimension(); i++) {
            if (lb1.getEntry(i) != Double.POSITIVE_INFINITY) {
                scaledLUb.setEntry(i, E.getEntry(i, i) * lb1.getEntry(i));
            } else {
                scaledLUb.setEntry(i, Double.POSITIVE_INFINITY);
            }
        }
        return scaledLUb;
    }

    /** Unscale solution vector.
     * @param x scaled solution vector
     * @return unscaled solution vector
     */
    public RealVector unscaleX(RealVector x) {
        return D.operate(x);
    }

    /** Unscale Y vector.
     * @param y scaled Y vector
     * @return unscaled Y vector
     */
    public RealVector unscaleY(RealVector y) {
        return E.operate(y).mapMultiply(cinv);
    }

    /** Unscale Z vector.
     * @param z scaled Z vector
     * @return unscaled Z vector
     */
    public RealVector unscaleZ(RealVector z) {
        return Einv.operate(z);
    }

    /** Scale solution vector.
     * @param x unscaled solution vector
     * @return scaled solution vector
     */
    RealVector scaleX(RealVector x) {
        return Dinv.operate(x);
    }

    /** Limit scaling.
     * @param v vector to limit
     * @return rescaled vector, taking scaling limits into account
     */
    private RealVector limitScaling(RealVector v) {

        RealVector result = new ArrayRealVector(v.getDimension());
        for (int i = 0; i < v.getDimension(); i++) {
            result.setEntry(i,  v.getEntry(i) < MIN_SCALING ? 1.0 : v.getEntry(i));
            result.setEntry(i,  v.getEntry(i) > MAX_SCALING ? MAX_SCALING : v.getEntry(i));
        }

        return result;

    }

}