Exemplo n.º 1
0
    // NOTE: CPLEX does not provide a function to directly get the dual
    //       multipliers for second order cone constraint.
    //       Example QCPDual.cs illustrates how the dual multipliers for a
    //       quadratic constraint can be computed from that constraint's
    //       slack.
    //       However, for SOCP we can do something simpler: we can read those
    //       multipliers directly from the dual slacks for the
    //       cone head variables. For a second order cone constraint
    //          x[1] >= |(x[2], ..., x[n])|
    //       the dual multiplier is the dual slack value for x[1].
    /// <summary>
    /// Compute dual multipliers for second order cone constraints.
    /// Returns a dictionary with a dual multiplier for each second order cone
    /// constraint.
    /// </summary>
    /// <remarks>
    ///  <c>cplex</c>  is the Cplex instance with the optimal solution.
    ///  <c>vars</c>   is a collection with <em>all</em> variables in the model.
    ///  <c>rngs</c>   is a collection with <em>all</em> constraints in the
    ///                 model.
    ///  <c>dslack</c> is a dictionary in which the function will store the full
    ///                dual slack, provided the argument is not <c>null</c>.
    /// </remarks>
    private static IDictionary <IRange, System.Double> Getsocpconstrmultipliers(Cplex cplex, ICollection <INumVar> vars, ICollection <IRange> rngs, IDictionary <INumVar, System.Double> dslack)
    {
        // Compute full dual slack.
        IDictionary <INumVar, System.Double> dense = new SortedDictionary <INumVar, System.Double>(NumVarComparator);

        foreach (INumVar v in vars)
        {
            dense[v] = cplex.GetReducedCost(v);
        }
        foreach (IRange r in rngs)
        {
            INumExpr e = r.Expr;
            if ((e is IQuadNumExpr) && ((IQuadNumExpr)e).GetQuadEnumerator().MoveNext())
            {
                // Quadratic constraint: pick up dual slack vector
                for (ILinearNumExprEnumerator it = cplex.GetQCDSlack(r).GetLinearEnumerator(); it.MoveNext(); /* nothing */)
                {
                    dense[it.NumVar] = dense[it.NumVar] + it.Value;
                }
            }
        }

        // Find the cone head variables and pick up the dual slacks for them.
        IDictionary <IRange, System.Double> socppi = new SortedDictionary <IRange, System.Double>(RangeComparator);

        foreach (IRange r in rngs)
        {
            INumExpr e = r.Expr;
            if ((e is IQuadNumExpr) && ((IQuadNumExpr)e).GetQuadEnumerator().MoveNext())
            {
                // Quadratic constraint: pick up dual slack vector
                for (IQuadNumExprEnumerator it = ((IQuadNumExpr)e).GetQuadEnumerator(); it.MoveNext(); /* nothing */)
                {
                    if (it.Value < 0)
                    {
                        socppi[r] = dense[it.NumVar1];
                        break;
                    }
                }
            }
        }

        // Fill in the dense slack if the user asked for it.
        if (dslack != null)
        {
            dslack.Clear();
            foreach (INumVar v in dense.Keys)
            {
                dslack[v] = dense[v];
            }
        }

        return(socppi);
    }
Exemplo n.º 2
0
    // Print out the objective function.
    // Note that the quadratic expression in the objective
    // is normalized: i.E., for all i != j, terms
    // c(i,j)*x[i]*x[j] + c(j,i)*x[j]*x[i] is normalized as
    // (c(i,j) + c(j,i)) * x[i]*x[j], or
    // (c(i,j) + c(j,i)) * x[j]*x[i].
    internal static void PrintObjective(IObjective obj)
    {
        System.Console.WriteLine("obj: " + obj);

        // Count the number of linear terms
        // in the objective function.
        int nlinterms = 0;
        ILinearNumExprEnumerator len = ((ILQNumExpr)obj.Expr).GetLinearEnumerator();

        while (len.MoveNext())
        {
            ++nlinterms;
        }

        // Count the number of quadratic terms
        // in the objective function.
        int nquadterms             = 0;
        int nquaddiag              = 0;
        IQuadNumExprEnumerator qen = ((ILQNumExpr)obj.Expr).GetQuadEnumerator();

        while (qen.MoveNext())
        {
            ++nquadterms;
            INumVar var1 = qen.GetNumVar1();
            INumVar var2 = qen.GetNumVar2();
            if (var1.Equals(var2))
            {
                ++nquaddiag;
            }
        }

        System.Console.WriteLine("number of linear terms in the objective             : " + nlinterms);
        System.Console.WriteLine("number of quadratic terms in the objective          : " + nquadterms);
        System.Console.WriteLine("number of diagonal quadratic terms in the objective : " + nquaddiag);
        System.Console.WriteLine();
    }
Exemplo n.º 3
0
    /* Test KKT conditions on the solution.
     * The function returns true if the tested KKT conditions are satisfied
     * and false otherwise.
     */
    private static bool Checkkkt(Cplex cplex,
                                 IObjective obj,
                                 ICollection <INumVar> vars,
                                 ICollection <IRange> rngs,
                                 IDictionary <INumVar, IRange> cone,
                                 double tol)
    {
        System.IO.TextWriter error  = cplex.Output();
        System.IO.TextWriter output = cplex.Output();

        IDictionary <INumVar, System.Double> dslack = new SortedDictionary <INumVar, System.Double>(NumVarComparator);
        IDictionary <INumVar, System.Double> x      = new SortedDictionary <INumVar, System.Double>(NumVarComparator);
        IDictionary <IRange, System.Double>  pi     = new SortedDictionary <IRange, System.Double>(RangeComparator);
        IDictionary <IRange, System.Double>  slack  = new SortedDictionary <IRange, System.Double>(RangeComparator);

        // Read primal and dual solution information.
        foreach (INumVar v in vars)
        {
            x[v] = cplex.GetValue(v);
        }
        pi = Getsocpconstrmultipliers(cplex, vars, rngs, dslack);
        foreach (IRange r in rngs)
        {
            slack[r] = cplex.GetSlack(r);
            INumExpr e = r.Expr;
            if (!(e is IQuadNumExpr) || !((IQuadNumExpr)e).GetQuadEnumerator().MoveNext())
            {
                // Linear constraint: get the dual value
                pi[r] = cplex.GetDual(r);
            }
        }

        // Print out the data we just fetched.
        output.Write("x      = [");
        foreach (INumVar v in vars)
        {
            output.Write(" {0,7:0.000}", x[v]);
        }
        output.WriteLine(" ]");
        output.Write("dslack = [");
        foreach (INumVar v in vars)
        {
            output.Write(" {0,7:0.000}", dslack[v]);
        }
        output.WriteLine(" ]");
        output.Write("pi     = [");
        foreach (IRange r in rngs)
        {
            output.Write(" {0,7:0.000}", pi[r]);
        }
        output.WriteLine(" ]");
        output.Write("slack  = [");
        foreach (IRange r in rngs)
        {
            output.Write(" {0,7:0.000}", slack[r]);
        }
        output.WriteLine(" ]");

        // Test primal feasibility.
        // This example illustrates the use of dual vectors returned by CPLEX
        // to verify dual feasibility, so we do not test primal feasibility
        // here.

        // Test dual feasibility.
        // We must have
        // - for all <= constraints the respective pi value is non-negative,
        // - for all >= constraints the respective pi value is non-positive,
        // - the dslack value for all non-cone variables must be non-negative.
        // Note that we do not support ranged constraints here.
        foreach (INumVar v in vars)
        {
            if (cone[v] == NOT_IN_CONE && dslack[v] < -tol)
            {
                error.WriteLine("Dual multiplier for " + v + " is not feasible: " + dslack[v]);
                return(false);
            }
        }
        foreach (IRange r in rngs)
        {
            if (System.Math.Abs(r.LB - r.UB) <= tol)
            {
                // Nothing to check for equality constraints.
            }
            else if (r.LB > -System.Double.MaxValue && pi[r] > tol)
            {
                error.WriteLine("Dual multiplier " + pi[r] + " for >= constraint");
                error.WriteLine(" " + r);
                error.WriteLine("not feasible.");
                return(false);
            }
            else if (r.UB < System.Double.MaxValue && pi[r] < -tol)
            {
                error.WriteLine("Dual multiplier " + pi[r] + " for <= constraint");
                error.WriteLine(" " + r);
                error.WriteLine("not feasible.");
                return(false);
            }
        }

        // Test complementary slackness.
        // For each constraint either the constraint must have zero slack or
        // the dual multiplier for the constraint must be 0. We must also
        // consider the special case in which a variable is not explicitly
        //  contained in a second order cone constraint.
        foreach (INumVar v in vars)
        {
            if (cone[v] == NOT_IN_CONE)
            {
                if (System.Math.Abs(x[v]) > tol && dslack[v] > tol)
                {
                    error.WriteLine("Invalid complementary slackness for " + v + ":");
                    error.WriteLine(" " + x[v] + " and " + dslack[v]);
                    return(false);
                }
            }
        }
        foreach (IRange r in rngs)
        {
            if (System.Math.Abs(slack[r]) > tol && System.Math.Abs(pi[r]) > tol)
            {
                error.WriteLine("Invalid complementary slackness for");
                error.WriteLine(" " + r);
                error.WriteLine(" " + slack[r] + " and " + pi[r]);
                return(false);
            }
        }

        // Test stationarity.
        // We must have
        //  c - g[i]'(X)*pi[i] = 0
        // where c is the objective function, g[i] is the i-th constraint of the
        // problem, g[i]'(x) is the derivate of g[i] with respect to x and X is the
        // optimal solution.
        // We need to distinguish the following cases:
        // - linear constraints g(x) = ax - b. The derivative of such a
        //   constraint is g'(x) = a.
        // - second order constraints g(x[1],...,x[n]) = -x[1] + |(x[2],...,x[n])|
        //   the derivative of such a constraint is
        //     g'(x) = (-1, x[2]/|(x[2],...,x[n])|, ..., x[n]/|(x[2],...,x[n])|
        //   (here |.| denotes the Euclidean norm).
        // - bound constraints g(x) = -x for variables that are not explicitly
        //   contained in any second order cone constraint. The derivative for
        //   such a constraint is g'(x) = -1.
        // Note that it may happen that the derivative of a second order cone
        // constraint is not defined at the optimal solution X (this happens if
        // X=0). In this case we just skip the stationarity test.
        IDictionary <INumVar, System.Double> sum = new SortedDictionary <INumVar, System.Double>(NumVarComparator);

        foreach (INumVar v in vars)
        {
            sum[v] = 0.0;
        }
        for (ILinearNumExprEnumerator it = ((ILinearNumExpr)cplex.GetObjective().Expr).GetLinearEnumerator(); it.MoveNext(); /* nothing */)
        {
            sum[it.NumVar] = it.Value;
        }

        foreach (INumVar v in vars)
        {
            if (cone[v] == NOT_IN_CONE)
            {
                sum[v] = sum[v] - dslack[v];
            }
        }

        foreach (IRange r in rngs)
        {
            INumExpr e = r.Expr;
            if ((e is IQuadNumExpr) && ((IQuadNumExpr)e).GetQuadEnumerator().MoveNext())
            {
                double norm = 0.0;
                for (IQuadNumExprEnumerator q = ((IQuadNumExpr)e).GetQuadEnumerator(); q.MoveNext(); /* nothing */)
                {
                    if (q.Value > 0)
                    {
                        norm += x[q.NumVar1] * x[q.NumVar1];
                    }
                }
                norm = System.Math.Sqrt(norm);
                if (System.Math.Abs(norm) <= tol)
                {
                    cplex.Warning().WriteLine("Cannot check stationarity at non-differentiable point");
                    return(true);
                }
                for (IQuadNumExprEnumerator q = ((IQuadNumExpr)e).GetQuadEnumerator(); q.MoveNext(); /* nothing */)
                {
                    INumVar v = q.NumVar1;
                    if (q.Value < 0)
                    {
                        sum[v] = sum[v] - pi[r];
                    }
                    else if (q.Value > 0)
                    {
                        sum[v] = sum[v] + pi[r] * x[v] / norm;
                    }
                }
            }
            else if (e is ILinearNumExpr)
            {
                for (ILinearNumExprEnumerator it = ((ILinearNumExpr)e).GetLinearEnumerator(); it.MoveNext(); /* nothing */)
                {
                    INumVar v = it.NumVar;
                    sum[v] = sum[v] - pi[r] * it.Value;
                }
            }
        }

        // Now test that all elements in sum[] are 0.
        foreach (INumVar v in vars)
        {
            if (System.Math.Abs(sum[v]) > tol)
            {
                error.WriteLine("Invalid stationarity " + sum[v] + " for " + v);
                return(false);
            }
        }

        return(true);
    }
Exemplo n.º 4
0
    /* ***************************************************************** *
    *                                                                   *
    *    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.                 *
    *                                                                   *
    * ***************************************************************** */
    /// <summary>
    /// 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.
    /// </summary>
    /// <remarks>
    ///   <c>cplex</c> is the Cplex instance that holds the optimal
    ///                solution.
    ///   <c>xval</c>  is the optimal solution vector.
    ///   <c>tol</c>   is the tolerance used to decide whether we are at
    ///                the cone
    ///   <c>x</c>     is the array of variables in the model.
    ///   <c>qs</c>    is the array of quadratic constraints for which duals
    ///                shall be calculated.
    /// </remarks>
    private static double[] getqconstrmultipliers(Cplex cplex, double[] xval, double tol, INumVar[] x, IRange[] qs)
    {
        double[] qpi = new double[qs.Length];
        // Store solution in map so that we can look up by variable.
        IDictionary <INumVar, System.Double> sol = new Dictionary <INumVar, System.Double>();

        for (int j = 0; j < x.Length; ++j)
        {
            sol.Add(x[j], xval[j]);
        }

        for (int i = 0; i < qs.Length; ++i)
        {
            IDictionary <INumVar, System.Double> dslack = new Dictionary <INumVar, System.Double>();
            for (ILinearNumExprEnumerator it = cplex.GetQCDSlack(qs[i]).GetLinearEnumerator(); it.MoveNext(); /* nothing */)
            {
                dslack[it.NumVar] = it.Value;
            }

            IDictionary <INumVar, System.Double> grad = new Dictionary <INumVar, System.Double>();
            bool conetop = true;
            for (IQuadNumExprEnumerator it = ((ILQNumExpr)qs[i].Expr).GetQuadEnumerator();
                 it.MoveNext(); /* nothing */)
            {
                INumVar x1 = it.NumVar1;
                INumVar x2 = it.NumVar2;
                if (sol[x1] > tol || sol[x2] > tol)
                {
                    conetop = false;
                }
                System.Double old;
                if (!grad.TryGetValue(x1, out old))
                {
                    old = 0.0;
                }
                grad[x1] = old + sol[x2] * it.Value;
                if (!grad.TryGetValue(x2, out old))
                {
                    old = 0.0;
                }
                grad[x2] = old + sol[x1] * it.Value;
            }
            if (conetop)
            {
                throw new System.SystemException("Cannot compute dual multiplier at cone top!");
            }

            // Compute qpi[i] as slack/gradient.
            // We may have several indices to choose from and use the one
            // with largest absolute value in the denominator.
            bool   ok     = false;
            double maxabs = -1.0;
            foreach (INumVar v in x)
            {
                System.Double g;
                if (grad.TryGetValue(v, out g))
                {
                    if (System.Math.Abs(g) > tol)
                    {
                        if (System.Math.Abs(g) > maxabs)
                        {
                            System.Double d;
                            qpi[i] = (dslack.TryGetValue(v, out d) ? d : 0.0) / g;
                            maxabs = System.Math.Abs(g);
                        }
                        ok = true;
                    }
                }
            }
            if (!ok)
            {
                // Dual slack is all 0. qpi[i] can be anything, just set to 0.
                qpi[i] = 0;
            }
        }
        return(qpi);
    }
Exemplo n.º 5
0
    /// <summary>
    /// The example's main function.
    /// </summary>
    public static void Main(string[] args)
    {
        int   retval = 0;
        Cplex cplex  = null;

        try
        {
            cplex = new Cplex();

            /* ***************************************************************** *
            *                                                                   *
            *    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                                                        *
            *  c1: x1 + x2 = 4                                                  *
            *  c2: x1 + x3 >= 3                                                 *
            *  c3: x6 + x7 <= 5                                                 *
            *  c4: -x1 + x7 >= -2                                               *
            *  q1: [ -x1^2 + x2^2 ] <= 0                                        *
            *  q2: [ 4.25x3^2 -2x3*x4 + 4.25x4^2 - 2x4*x5 + 4x5^2  ] + 2 x1 <= 9.0
            *  q3: [ x6^2 - x7^2 ] >= 4                                         *
            * Bounds                                                            *
            *  0 <= x1 <= 3                                                     *
            *  x2 Free                                                          *
            *  0 <= x3 <= 0.5                                                   *
            *  x4 Free                                                          *
            *  x5 Free                                                          *
            *  x7 Free                                                          *
            * End                                                               *
            *                                                                   *
            * ***************************************************************** */

            INumVar[] x = new INumVar[7];
            x[0] = cplex.NumVar(0, 3, "x1");
            x[1] = cplex.NumVar(System.Double.NegativeInfinity, System.Double.PositiveInfinity, "x2");
            x[2] = cplex.NumVar(0, 0.5, "x3");
            x[3] = cplex.NumVar(System.Double.NegativeInfinity, System.Double.PositiveInfinity, "x4");
            x[4] = cplex.NumVar(System.Double.NegativeInfinity, System.Double.PositiveInfinity, "x5");
            x[5] = cplex.NumVar(0, System.Double.PositiveInfinity, "x6");
            x[6] = cplex.NumVar(System.Double.NegativeInfinity, System.Double.PositiveInfinity, "x7");

            IRange[] linear = new IRange[4];
            linear[0] = cplex.AddEq(cplex.Sum(x[0], x[1]), 4.0, "c1");
            linear[1] = cplex.AddGe(cplex.Sum(x[0], x[2]), 3.0, "c2");
            linear[2] = cplex.AddLe(cplex.Sum(x[5], x[6]), 5.0, "c3");
            linear[3] = cplex.AddGe(cplex.Diff(x[6], x[0]), -2.0, "c4");

            IRange[] quad = new IRange[3];
            quad[0] = cplex.AddLe(cplex.Sum(cplex.Prod(-1, x[0], x[0]),
                                            cplex.Prod(x[1], x[1])), 0.0, "q1");
            quad[1] = cplex.AddLe(cplex.Sum(cplex.Prod(4.25, x[2], x[2]),
                                            cplex.Prod(-2, x[2], x[3]),
                                            cplex.Prod(4.25, x[3], x[3]),
                                            cplex.Prod(-2, x[3], x[4]),
                                            cplex.Prod(4, x[4], x[4]),
                                            cplex.Prod(2, x[0])), 9.0, "q2");
            quad[2] = cplex.AddGe(cplex.Sum(cplex.Prod(x[5], x[5]),
                                            cplex.Prod(-1, x[6], x[6])), 4.0, "q3");

            cplex.AddMinimize(cplex.Sum(cplex.Prod(3.0, x[0]),
                                        cplex.Prod(-1.0, x[1]),
                                        cplex.Prod(3.0, x[2]),
                                        cplex.Prod(2.0, x[3]),
                                        cplex.Prod(1.0, x[4]),
                                        cplex.Prod(2.0, x[5]),
                                        cplex.Prod(4.0, x[6])), "obj");

            /* ***************************************************************** *
            *                                                                   *
            *    O P T I M I Z E   P R O B L E M                                *
            *                                                                   *
            * ***************************************************************** */
            cplex.SetParam(Cplex.Param.Barrier.QCPConvergeTol, 1e-10);
            cplex.Solve();

            /* ***************************************************************** *
            *                                                                   *
            *    Q U E R Y   S O L U T I O N                                    *
            *                                                                   *
            * ***************************************************************** */
            double[] xval   = cplex.GetValues(x);
            double[] slack  = cplex.GetSlacks(linear);
            double[] qslack = cplex.GetSlacks(quad);
            double[] cpi    = cplex.GetReducedCosts(x);
            double[] rpi    = cplex.GetDuals(linear);
            double[] qpi    = getqconstrmultipliers(cplex, xval, ZEROTOL, x, quad);
            // Also store solution in a dictionary so that we can look up
            // values by variable and not only by index.
            IDictionary <INumVar, System.Double> xmap = new Dictionary <INumVar, System.Double>();
            for (int j = 0; j < x.Length; ++j)
            {
                xmap.Add(x[j], xval[j]);
            }

            /* ***************************************************************** *
            *                                                                   *
            *    C H E C K   K K T   C O N D I T I O N S                        *
            *                                                                   *
            *    Here we verify that the optimal solution computed by CPLEX     *
            *    (and the qpi[] values computed above) satisfy the KKT          *
            *    conditions.                                                    *
            *                                                                   *
            * ***************************************************************** */

            // Primal feasibility: This example is about duals so we skip this test.

            // Dual feasibility: We must verify
            // - for <= constraints (linear or quadratic) the dual
            //   multiplier is non-positive.
            // - for >= constraints (linear or quadratic) the dual
            //   multiplier is non-negative.
            for (int i = 0; i < linear.Length; ++i)
            {
                if (linear[i].LB <= System.Double.NegativeInfinity)
                {
                    // <= constraint
                    if (rpi[i] > ZEROTOL)
                    {
                        throw new System.SystemException("Dual feasibility test failed for row " + linear[i]
                                                         + ": " + rpi[i]);
                    }
                }
                else if (linear[i].UB >= System.Double.PositiveInfinity)
                {
                    // >= constraint
                    if (rpi[i] < -ZEROTOL)
                    {
                        throw new System.SystemException("Dual feasibility test failed for row " + linear[i]
                                                         + ": " + rpi[i]);
                    }
                }
                else
                {
                    // nothing to do for equality constraints
                }
            }
            for (int i = 0; i < quad.Length; ++i)
            {
                if (quad[i].LB <= System.Double.NegativeInfinity)
                {
                    // <= constraint
                    if (qpi[i] > ZEROTOL)
                    {
                        throw new System.SystemException("Dual feasibility test failed for quad " + quad[i]
                                                         + ": " + qpi[i]);
                    }
                }
                else if (quad[i].UB >= System.Double.PositiveInfinity)
                {
                    // >= constraint
                    if (qpi[i] < -ZEROTOL)
                    {
                        throw new System.SystemException("Dual feasibility test failed for quad " + quad[i]
                                                         + ": " + qpi[i]);
                    }
                }
                else
                {
                    // nothing to do for equality constraints
                }
            }

            // Complementary slackness.
            // For any constraint the product of primal slack and dual multiplier
            // must be 0.
            for (int i = 0; i < linear.Length; ++i)
            {
                if (System.Math.Abs(linear[i].UB - linear[i].LB) > ZEROTOL &&
                    System.Math.Abs(slack[i] * rpi[i]) > ZEROTOL)
                {
                    throw new System.SystemException("Complementary slackness test failed for row " + linear[i]
                                                     + ": " + System.Math.Abs(slack[i] * rpi[i]));
                }
            }
            for (int i = 0; i < quad.Length; ++i)
            {
                if (System.Math.Abs(quad[i].UB - quad[i].LB) > ZEROTOL &&
                    System.Math.Abs(qslack[i] * qpi[i]) > ZEROTOL)
                {
                    throw new System.SystemException("Complementary slackness test failed for quad " + quad[i]
                                                     + ": " + System.Math.Abs(qslack[i] * qpi[i]));
                }
            }
            for (int j = 0; j < x.Length; ++j)
            {
                if (x[j].UB < System.Double.PositiveInfinity)
                {
                    double slk  = x[j].UB - xval[j];
                    double dual = cpi[j] < -ZEROTOL ? cpi[j] : 0.0;
                    if (System.Math.Abs(slk * dual) > ZEROTOL)
                    {
                        throw new System.SystemException("Complementary slackness test failed for column " + x[j]
                                                         + ": " + System.Math.Abs(slk * dual));
                    }
                }
                if (x[j].LB > System.Double.NegativeInfinity)
                {
                    double slk  = xval[j] - x[j].LB;
                    double dual = cpi[j] > ZEROTOL ? cpi[j] : 0.0;
                    if (System.Math.Abs(slk * dual) > ZEROTOL)
                    {
                        throw new System.SystemException("Complementary slackness test failed for column " + x[j]
                                                         + ": " + System.Math.Abs(slk * dual));
                    }
                }
            }

            // Stationarity.
            // The difference between objective function and gradient at optimal
            // solution multiplied by dual multipliers must be 0, i.E., for the
            // optimal solution x
            // 0 == c
            //      - sum(r in rows)  r'(x)*rpi[r]
            //      - sum(q in quads) q'(x)*qpi[q]
            //      - sum(c in cols)  b'(x)*cpi[c]
            // where r' and q' are the derivatives of a row or quadratic constraint,
            // x is the optimal solution and rpi[r] and qpi[q] are the dual
            // multipliers for row r and quadratic constraint q.
            // b' is the derivative of a bound constraint and cpi[c] the dual bound
            // multiplier for column c.
            IDictionary <INumVar, System.Double> kktsum = new Dictionary <INumVar, System.Double>();
            for (int j = 0; j < x.Length; ++j)
            {
                kktsum.Add(x[j], 0.0);
            }

            // Objective function.
            for (ILinearNumExprEnumerator it = ((ILinearNumExpr)cplex.GetObjective().Expr).GetLinearEnumerator();
                 it.MoveNext(); /* nothing */)
            {
                kktsum[it.NumVar] = it.Value;
            }

            // Linear constraints.
            // The derivative of a linear constraint ax - b (<)= 0 is just a.
            for (int i = 0; i < linear.Length; ++i)
            {
                for (ILinearNumExprEnumerator it = ((ILinearNumExpr)linear[i].Expr).GetLinearEnumerator();
                     it.MoveNext(); /* nothing */)
                {
                    kktsum[it.NumVar] = kktsum[it.NumVar] - rpi[i] * it.Value;
                }
            }

            // Quadratic constraints.
            // The derivative of a constraint xQx + ax - b <= 0 is
            // Qx + Q'x + a.
            for (int i = 0; i < quad.Length; ++i)
            {
                for (ILinearNumExprEnumerator it = ((ILinearNumExpr)quad[i].Expr).GetLinearEnumerator();
                     it.MoveNext(); /* nothing */)
                {
                    kktsum[it.NumVar] = kktsum[it.NumVar] - qpi[i] * it.Value;
                }
                for (IQuadNumExprEnumerator it = ((IQuadNumExpr)quad[i].Expr).GetQuadEnumerator();
                     it.MoveNext(); /* nothing */)
                {
                    INumVar v1 = it.NumVar1;
                    INumVar v2 = it.NumVar2;
                    kktsum[v1] = kktsum[v1] - qpi[i] * xmap[v2] * it.Value;
                    kktsum[v2] = kktsum[v2] - qpi[i] * xmap[v1] * it.Value;
                }
            }

            // Bounds.
            // The derivative for lower bounds is -1 and that for upper bounds
            // is 1.
            // CPLEX already returns dj with the appropriate sign so there is
            // no need to distinguish between different bound types here.
            for (int j = 0; j < x.Length; ++j)
            {
                kktsum[x[j]] = kktsum[x[j]] - cpi[j];
            }

            foreach (INumVar v in x)
            {
                if (System.Math.Abs(kktsum[v]) > ZEROTOL)
                {
                    throw new System.SystemException("Stationarity test failed at " + v
                                                     + ": " + System.Math.Abs(kktsum[v]));
                }
            }

            // KKT conditions satisfied. Dump out the optimal solutions and
            // the dual values.
            System.Console.WriteLine("Optimal solution satisfies KKT conditions.");
            System.Console.WriteLine("   x[] = " + arrayToString(xval));
            System.Console.WriteLine(" cpi[] = " + arrayToString(cpi));
            System.Console.WriteLine(" rpi[] = " + arrayToString(rpi));
            System.Console.WriteLine(" qpi[] = " + arrayToString(qpi));
        }
        catch (ILOG.Concert.Exception e)
        {
            System.Console.WriteLine("IloException: " + e.Message);
            System.Console.WriteLine(e.StackTrace);
            retval = -1;
        }
        finally
        {
            if (cplex != null)
            {
                cplex.End();
            }
        }
        System.Environment.Exit(retval);
    }