0

I'm using cplex with C++. Recently, I was looking at the following example. Then I'm confused by the populate function. It is pass-by-value right? But why did the model created in main function updated after executing populate function? Shouldn't it still be empty model?

// -------------------------------------------------------------- -*- C++ -*-
// File: ilolpex1.cpp
// 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 2000, 2012. All Rights Reserved.
//
// US Government Users Restricted Rights - Use, duplication or
// disclosure restricted by GSA ADP Schedule Contract with
// IBM Corp.
// --------------------------------------------------------------------------
//
// ilolpex1.cpp - Entering and optimizing a problem.  Demonstrates different
// methods for creating a problem.  The user has to choose the method
// on the command line:
//
//    ilolpex1  -r     generates the problem by adding rows
//    ilolpex1  -c     generates the problem by adding columns
//    ilolpex1  -n     generates the problem by adding a list of coefficients

#include <ilcplex/ilocplex.h>
ILOSTLBEGIN

static void
   usage (const char *progname),
   populatebyrow     (IloModel model, IloNumVarArray var, IloRangeArray con),
   populatebycolumn  (IloModel model, IloNumVarArray var, IloRangeArray con),
   populatebynonzero (IloModel model, IloNumVarArray var, IloRangeArray con);

int
main (int argc, char **argv)
{
   IloEnv   env;
   try {
      IloModel model(env);

      if (( argc != 2 )                         ||
          ( argv[1][0] != '-' )                 ||
          ( strchr ("rcn", argv[1][1]) == NULL )   ) {
         usage (argv[0]);
         throw(-1);
      }

      IloNumVarArray var(env);
      IloRangeArray con(env);

      switch (argv[1][1]) {
         case 'r':
            populatebyrow (model, var, con);
            break;
         case 'c':
            populatebycolumn (model, var, con);
            break;
         case 'n':
            populatebynonzero (model, var, con);
            break;
      }

      IloCplex cplex(model);
      cplex.exportModel("lpex1.lp");

      // Optimize the problem and obtain solution.
      if ( !cplex.solve() ) {
         env.error() << "Failed to optimize LP" << endl;
         throw(-1);
      }

      IloNumArray vals(env);
      env.out() << "Solution status = " << cplex.getStatus() << endl;
      env.out() << "Solution value  = " << cplex.getObjValue() << endl;
      cplex.getValues(vals, var);
      env.out() << "Values        = " << vals << endl;
      cplex.getSlacks(vals, con);
      env.out() << "Slacks        = " << vals << endl;
      cplex.getDuals(vals, con);
      env.out() << "Duals         = " << vals << endl;
      cplex.getReducedCosts(vals, var);
      env.out() << "Reduced Costs = " << vals << endl;
   }
   catch (IloException& e) {
      cerr << "Concert exception caught: " << e << endl;
   }
   catch (...) {
      cerr << "Unknown exception caught" << endl;
   }

   env.end();

   return 0;
}  // END main


static void usage (const char *progname)
{
   cerr << "Usage: " << progname << " -X" << endl;
   cerr << "   where X is one of the following options:" << endl;
   cerr << "      r          generate problem by row" << endl;
   cerr << "      c          generate problem by column" << endl;
   cerr << "      n          generate problem by nonzero" << endl;
   cerr << " Exiting..." << endl;
} // END usage


// To populate by row, we first create the variables, and then use them to
// create the range constraints and objective.

static void
populatebyrow (IloModel model, IloNumVarArray x, IloRangeArray c)
{
   IloEnv env = model.getEnv();

   x.add(IloNumVar(env, 0.0, 40.0));
   x.add(IloNumVar(env));
   x.add(IloNumVar(env));

   model.add(IloMaximize(env, x[0] + 2 * x[1] + 3 * x[2]));

   c.add( - x[0] +     x[1] + x[2] <= 20);
   c.add(   x[0] - 3 * x[1] + x[2] <= 30);

   x[0].setName("x1");
   x[1].setName("x2");
   x[2].setName("x3");

   c[0].setName("c1");
   c[1].setName("c2");
   model.add(c);

}  // END populatebyrow


// To populate by column, we first create the range constraints and the
// objective, and then create the variables and add them to the ranges and
// objective using column expressions.

static void
populatebycolumn (IloModel model, IloNumVarArray x, IloRangeArray c)
{
   IloEnv env = model.getEnv();

   IloObjective obj = IloMaximize(env);
   c.add(IloRange(env, -IloInfinity, 20.0, "c1"));
   c.add(IloRange(env, -IloInfinity, 30.0, "c2"));

   x.add(IloNumVar(obj(1.0) + c[0](-1.0) + c[1]( 1.0), 0.0, 40.0));
   x.add(IloNumVar(obj(2.0) + c[0]( 1.0) + c[1](-3.0)));
   x.add(IloNumVar(obj(3.0) + c[0]( 1.0) + c[1]( 1.0)));

   x[0].setName("x1");
   x[1].setName("x2");
   x[2].setName("x3");

   model.add(obj);
   model.add(c);

}  // END populatebycolumn


// To populate by nonzero, we first create the rows, then create the
// columns, and then change the nonzeros of the matrix 1 at a time.

static void
populatebynonzero (IloModel model, IloNumVarArray x, IloRangeArray c)
{
   IloEnv env = model.getEnv();

   IloObjective obj = IloMaximize(env);
   c.add(IloRange(env, -IloInfinity, 20.0));
   c.add(IloRange(env, -IloInfinity, 30.0));

   x.add(IloNumVar(env, 0.0, 40.0));
   x.add(IloNumVar(env));
   x.add(IloNumVar(env));

   obj.setLinearCoef(x[0], 1.0);
   obj.setLinearCoef(x[1], 2.0);
   obj.setLinearCoef(x[2], 3.0);

   c[0].setLinearCoef(x[0], -1.0);
   c[0].setLinearCoef(x[1],  1.0);
   c[0].setLinearCoef(x[2],  1.0);
   c[1].setLinearCoef(x[0],  1.0);
   c[1].setLinearCoef(x[1], -3.0);
   c[1].setLinearCoef(x[2],  1.0);

   c[0].setName("c1");
   c[1].setName("c2");

   x[0].setName("x1");
   x[1].setName("x2");
   x[2].setName("x3");

   model.add(obj);
   model.add(c);
}  // END populatebynonzero
Yu Hao
  • 119,891
  • 44
  • 235
  • 294
Fugui
  • 91
  • 9
  • 7
    I wonder if any IBM employees monitor this board... Hmmm. Eh. belay that. I say maybe, since *we have no idea what the types of those parameters are*. They certainly look like object types. They could be reference types I suppose, but since you have the header and we don't go look at the typedefs and see what `IloModel`, `IloNumVarArray`, and `IloRangeArray` are. – WhozCraig Oct 31 '13 at 03:37
  • 3
    My guess is that `IloModel` is a class wrapper around a handle, providing convenient calls to what is probably an underlying C API. – paddy Oct 31 '13 at 03:50
  • @paddy I think likewise. It makes the most sense. Wrap an API object around a pointer and pass the object around by-value, always getting the same pointer with each copy. – WhozCraig Oct 31 '13 at 03:51

3 Answers3

1

See the ilocplex.h, probably IloModel is defined as pointer. Or "a handle" as the manual states.

I cannot find ilocplex.h easily using google. But that's how I suppose it works:

struct ModelObj {
   int data1, data2, data3;
}

struct Model {
   ModelObj* obj;
}

Now you can pass Model as value, but still obj will point to the reference.

exebook
  • 32,014
  • 33
  • 141
  • 226
  • Its definitely not a pointer type, or `model.add(obj);` would not compile. Its something else. – WhozCraig Oct 31 '13 at 03:45
  • yes but one of it's member definetly is a pointer then. Similar to refrence counter classes. – exebook Oct 31 '13 at 03:46
  • And using the given call, `model.add(obj)`, you get at that "one of its member" of `model` ... how? – WhozCraig Oct 31 '13 at 03:50
  • Normally `obj` is hidden from you, you only deal with `model`. That's how reference counting works. All data copy is made when necessary, in other cases only reference is copied. – exebook Oct 31 '13 at 03:52
  • I *think* you're trying to describe what paddy commented on in general chat, and if so, I concur. If not, I've no idea what you're talking about. but I think it looks like you're saying the same thing. – WhozCraig Oct 31 '13 at 03:53
  • @WhozCraig and exebook, I don't see why any of this even matters without more info this is all just speculation – aaronman Oct 31 '13 at 03:53
  • @aaronman It is? Ahh. ok. because I wondered whether I made that evident in the first comment in general-chat. I guess not. My bad. – WhozCraig Oct 31 '13 at 03:56
  • @WhozCraig didn't read the general chat, my bad, I'm just looking at this post cause I think it's hilarious someone posted code with a scary IBM copyright on it – aaronman Oct 31 '13 at 03:57
  • @exebook after rereading this a few times, you're definitely saying the same thing paddy did (or actually he said the same thing you did, since you posted first). Its a good deduction, and worthy of a +1. On that we could all be wildly wrong, but I'm kinda doubting it =P – WhozCraig Oct 31 '13 at 04:00
1

Yup, these classes are really "handle" classes - really just some syntactic sugar wrapped around a good old pointer. The reasons are that it is easier to make the code look cleaner that way, and it allows the library to maintain better control over what is going on than just allowing pointers everywhere, and makes it easier to do stuff like making sure objects get allocated on a custom heap for example, to get better performance through using specialised memory management that knows a bit more about the types of objects it must manage. I've been using CPLEX for many projects since the late 90s and taught many courses with it too. If done right it can make the code much cleaner. Just when you implement your own custom extensions you sometimes need to write both the implementation class and a handle class, which can be confusing to start with...

TimChippingtonDerrick
  • 2,042
  • 1
  • 12
  • 13
-1

Please check:

  1. Whether your header file have customized copy constructor?

  2. Is the class IloModel is a type based on pointer? Like exebook have mentioned?

detail explaination of the customized copy constructor

whether your code have a customized copy constructor, especially when the class have some data field create in heap. Please check your code for the definition of class IloModel.

  • If no customized copy construction, the model class should be passed by value.

  • If a customized copy constructor is defined, and only reference is used, pass by reference is possible.

Here is a piece of code which could show you how this type of constructor works:

#include <string>
#include <iostream>

using namespace std;

class person {
  public:
  std::string _name;
  int* _age_ptr; 

  person (const std::string& name, int age) {
    _name = name;
    _age_ptr = new int(age); 
  }

  person(const person& that) {  // The customized copy constructor
    _name = that._name;
    _age_ptr = that._age_ptr;
    cout << "run copy constructor" << endl;
  }

  void print() {
    cout << "name:" << _name <<", age:" << *_age_ptr << endl;
  }

};

person update_age( person that) {
  cout << "Set age tp 70" << endl;
  *that._age_ptr = 70;
  return that;
}

int main() {
  person a("Jim", 60);
  cout <<"Before update, a" << endl;
  a.print(); // Output: name:Jim, age 60
  cout << endl;
  person b = update_age(a);
  cout <<"After update, a" << endl;
  a.print(); // HERE Output: name:Jim, age 70
  cout << endl;
  cout <<"After update, b" << endl;                                                                       
  b.print();
  return 0;
}

For more information about copy constructor, here is a useful link: What is the copy-and-swap idiom?

Community
  • 1
  • 1
Kun Ling
  • 2,211
  • 14
  • 22
  • 1
    What do you mean, the model class *"should"* be passed by value? In any case, a customized copy constructor is not the only possibility and your answer seems to suggest that it is. See comments by WhozCraig and paddy, for examples of other possibilities. – JBentley Oct 31 '13 at 03:52
  • I understood your answer without the code example - my comment above still stands. In order for your answer to be valid, you should amend it to make clear that this is just one possibility, rather than saying, *"it depends on whether your code have a customized copy constructor"* - it doesn't depend on this - there are other ways it can work. – JBentley Oct 31 '13 at 04:14
  • yeah, have modified my answer to be precise. – Kun Ling Oct 31 '13 at 04:21