/* --------------------------------------------------------------------------
* File: QCPDual.java
* Version 12.5
* --------------------------------------------------------------------------
* Licensed Materials - Property of IBM
* 5725-A06 5725-A29 5724-Y48 5724-Y49 5724-Y54 5724-Y55 5655-Y21
* Copyright IBM Corporation 2003, 2012. All Rights Reserved.
*
* US Government Users Restricted Rights - Use, duplication or
* disclosure restricted by GSA ADP Schedule Contract with
* IBM Corp.
* --------------------------------------------------------------------------
*/
/*
* QCPDual.java - Illustrates how to query and analyze dual values of QCPs
*/
import ilog.cplex.*;
import ilog.concert.*;
public final class QCPDual {
/** Tolerance applied when testing values for zero. */
public static final double ZEROTOL = 1e-6;
/** Sparse vector indexed by names.
* The class implements a sparse vector that indexes its non-zero elements
* by names. The keys for such a vector can names IloNumVar or IloRange
* instances.
*/
private static final class SparseVector {
/** The internal map that stores non-zero coefficients. */
private final java.util.Map<String,Double> map = new java.util.TreeMap<String,Double>();
/** Create a new empty vector. */
public SparseVector() {}
/** Create a new vector and initialize it from the data in <code>objs</code>
* and <code>vals</code>.
* The arrays <code>objs</code> and <code>vals</code> are assumed to be
* of equal length and <code>vals[i]</code> is the initial value for
* <code>objs[i]</code>.
*/
public SparseVector(IloAddable[] objs, double[] vals) {
for (int i = 0; i < objs.length; ++i)
map.put(objs[i].getName(), vals[i]);
}
/** Create a new vector and initialize it from a linear expression.
* <code>expr</code> is interpreted as the list of initial non-zero
* coefficients in the sparse vector.
*/
public SparseVector(IloLinearNumExpr expr) {
for (IloLinearNumExprIterator it = expr.linearIterator(); it.hasNext(); /* nothing */) {
IloNumVar v = it.nextNumVar();
map.put(v.getName(), it.getValue());
}
}
/** Get the value for <code>a</code>. */
public double get(IloAddable a) { return get(a.getName()); }
/** Get the value for <code>a</code>. */
public double get(String key) {
Double d = map.get(key);
return d == null ? 0 : d.doubleValue();
}
/** Set the value for <code>a</code> to <code>value</code>. */
public void set(IloAddable a, double value) {
map.put(a.getName(), value);
}
/** Get a string representation of the corresponding dense vector.
* <code>allKeys</code> is the set of all keys that the vector could
* potentially have.
*/
public String toString(IloAddable[] allKeys) {
StringBuffer result = new StringBuffer();
result.append("[");
for (IloAddable a : allKeys)
result.append(String.format(" %7.3f", get(a)));
result.append(" ]");
return result.toString();
}
/** Get the set of keys for which this vector has values. */
public java.util.Collection<String> keys() {
return java.util.Collections.unmodifiableSet(map.keySet());
}
};
/* ***************************************************************** *
* *
* C A L C U L A T E D U A L S F O R Q U A D S *
* *
* CPLEX does not give us the dual multipliers for quadratic *
* constraints directly. This is because they may not be properly *
* defined at the cone top and deciding whether we are at the cone *
* top or not involves (problem specific) tolerance issues. CPLEX *
* instead gives us all the values we need in order to compute the *
* dual multipliers if we are not at the cone top. *
* *
* ***************************************************************** */
/** Calculate dual multipliers for quadratic constraints from dual
* slack vectors and optimal solutions.
* The dual multiplier is essentially the dual slack divided
* by the derivative evaluated at the optimal solution. If the optimal
* solution is 0 then the derivative at this point is not defined (we are
* at the cone top) and we cannot compute a dual multiplier.
* @param cplex The IloCplex instance that holds the optimal solution.
* @param xval The optimal solution vector.
* @param tol The tolerance used to decide whether we are at the cone
* top or not.
* @param qs Array of quadratic constraints for which duals shall
* be calculated.
* @return A {@link SparseVector} with dual multipliers for all quadratic
* constraints in <code>qs</code>.
* @throw IloException if querying data from <code>cplex</code> fails.
* @throw RuntimeException if the optimal solution is at the cone top.
*/
private static SparseVector getqconstrmultipliers(IloCplex cplex, SparseVector xval, double tol, IloRange[] qs) throws IloException
{
SparseVector qpi = new SparseVector();
for (IloRange q : qs) {
SparseVector dslack = new SparseVector(cplex.getQCDSlack(q));
SparseVector grad = new SparseVector();
boolean conetop = true;
for (IloQuadNumExprIterator it = ((IloLQNumExpr)q.getExpr()).quadIterator();
it.hasNext(); /* nothing */)
{
it.next();
IloNumVar x1 = it.getNumVar1();
IloNumVar x2 = it.getNumVar2();
if ( xval.get(x1) > tol || xval.get(x2) > tol )
conetop = false;
grad.set(x1, grad.get(x1) + xval.get(x2) * it.getValue());
grad.set(x2, grad.get(x2) + xval.get(x1) * it.getValue());
}
if ( conetop )
throw new RuntimeException("Cannot compute dual multiplier at cone top!");
// Compute qpi[q] as slack/gradient.
// We may have several indices to choose from and use the one
// with largest absolute value in the denominator.
boolean ok = false;
double maxabs = -1.0;
for (String v : xval.keys()) {
if ( Math.abs(grad.get(v)) > tol ) {
if ( Math.abs(grad.get(v)) > maxabs ) {
qpi.set(q, dslack.get(v) / grad.get(v));
maxabs = Math.abs(grad.get(v));
}
ok = true;
}
}
if ( !ok ) {
// Dual slack is all 0. qpi[q] can be anything, just set to 0.
qpi.set(q, 0.0);
}
}
return qpi;
}
/** The example's main function. */
public static void main(String[] args) {
IloCplex cplex = null;
try {
cplex = new IloCplex();
/* ***************************************************************** *
* *
* S E T U P P R O B L E M *
* *
* We create the following problem: *
* Minimize *
* obj: 3x1 - x2 + 3x3 + 2x4 + x5 + 2x6 + 4x7 *
* Subject To *
- 1
- 2
- 3
前往页