Search code examples
arraysoptimizationmultidimensional-arraydynamic-arrays

CPLEX Creating arrays (matrix) that depend on decision variables


I would really appreciate some help on the following.

Having n origins and n destinations and the related n x n matrix of costs (integers) as input, I have to choose k origins and k destinations (k<n) such that they minimize the linear sum assignment problem* that results from the new k x k matrix of costs. We have 3 decision variables: the origins selected, the destinations selected, and finally the selected edges (costs, that is, the solution of the assignment problem).

I am not able to define the condition that creates the k x k matrix of costs, based on the selection of origins and destinations, so it serves as input for the assignment problem. I tried the following code:

int n=...;
int k= ...;

range origins = 1..n;
range destin = 1..n;
range k_origins =1..k;
range k_destin = 1..k;

int costs[origins][destin]=...;

dvar boolean x[origins];
dvar boolean y[destin];
dvar boolean z[k_origins][k_destin];

///First I tried to create k x k costs matrix directly:
//dexpr int k_costs[k_origins][k_destin]= costs[i: x[i]==1][j: y[j]==1]; //ERROR: Syntax error: unexpected ":"

//Then I tried to extract first the indexes and then create the k x k matrix, but there is always an error:
    //dexpr int array_origins[k_origins] =  [i: x[i]==1| i in origins]; //ERROR: Boolean[{int}] type cannot be used for dexpr int[k_origins]
    //dexpr {int} array_origins =  {i| i in origins : x[i]==1}; //ERROR: {int} type cannot be used with decision expressions

dexpr int k_costs[i in k_origins][j in k_destin]= costs[i][j]* (x[i]==1)*(y[j]==1);

dexpr float totalcost = sum(i in k_origins, j in k_destin)
    k_costs[i][j] * z[i][j];

minimize totalcost ;
        
subject to
{
sum(i in origins)x[i]==k;
sum(j in destin)y[j]==k;
 
forall(j in k_destin) sum(i in k_origins)z[i][j] == 1;
forall(i in k_origins) sum(j in k_destin)z[i][j] == 1;
};

Any idea or tip is welcome! Thank you

*A brief description of the assignment problem can be found here https://en.wikipedia.org/wiki/Assignment_problem


Solution

  • In https://www.linkedin.com/pulse/how-opl-alex-fleischer/

    How to use a decision variable as an index with CPLEX ?

    range r=1..5;
    
    float value[r]=[2,3,4.5,1,0];
    dvar int i in 1..5;
    
    maximize sum(k in r) value[k]*(k==i);
    subject to
    {
    
    }
    
    execute
    {
    writeln("i=",i);
    }
    

    or if with decision expressions

    range r=1..5;
    
    float value[r]=[2,3,4.5,1,0];
    dvar int i in 1..5;
    
    dexpr float cost=sum(k in r) value[k]*(k==i);
    
    minimize cost;
    subject to
    {
    
    }
    
    execute
    {
    writeln("i=",i);
    }
    

    With your example, you could use CPOptimizer:

    using CP;
    
    int n=3;
    int k= 2;
    
    range origins = 1..n;
    range destin = 1..n;
    range k_origins =1..k;
    range k_destin = 1..k;
    
    int costs[o in origins][d in destin]=ftoi(abs(o-d)) mod 2;
    
    dvar boolean x[origins];
    dvar boolean y[destin];
    dvar boolean z[k_origins][k_destin];
    
    ///First I tried to create k x k costs matrix directly:
    //dexpr int k_costs[k_origins][k_destin]= costs[i: x[i]==1][j: y[j]==1]; //ERROR: Syntax error: unexpected ":"
    
    //Then I tried to extract first the indexes and then create the k x k matrix, but there is always an error:
        //dexpr int array_origins[k_origins] =  [i: x[i]==1| i in origins]; //ERROR: Boolean[{int}] type cannot be used for dexpr int[k_origins]
        //dexpr {int} array_origins =  {i| i in origins : x[i]==1}; //ERROR: {int} type cannot be used with decision expressions
    
    dexpr int k_costs[i in k_origins][j in k_destin]= costs[i][j]* (x[i]==1)*(y[j]==1);
    
    dexpr float totalcost = sum(i in k_origins, j in k_destin:i in origins && j in destin)
        k_costs[i][j] * z[i][j];
    
    minimize totalcost ;
            
    subject to
    {
    sum(i in origins)x[i]==k;
    sum(j in destin)y[j]==k;
     
    forall(j in k_destin) sum(i in k_origins)z[i][j] == 1;
    forall(i in k_origins) sum(j in k_destin)z[i][j] == 1;
    };
    

    or if you want to stay with MIP

    int n=3;
    int k= 2;
    
    range origins = 1..n;
    range destin = 1..n;
    range k_origins =1..k;
    range k_destin = 1..k;
    
    int costs[o in origins][d in destin]=ftoi(abs(o-d)) mod 2;
    
    dvar boolean x[origins];
    dvar boolean y[destin];
    dvar boolean z[k_origins][k_destin];
    
    ///First I tried to create k x k costs matrix directly:
    //dexpr int k_costs[k_origins][k_destin]= costs[i: x[i]==1][j: y[j]==1]; //ERROR: Syntax error: unexpected ":"
    
    //Then I tried to extract first the indexes and then create the k x k matrix, but there is always an error:
        //dexpr int array_origins[k_origins] =  [i: x[i]==1| i in origins]; //ERROR: Boolean[{int}] type cannot be used for dexpr int[k_origins]
        //dexpr {int} array_origins =  {i| i in origins : x[i]==1}; //ERROR: {int} type cannot be used with decision expressions
    
    dexpr int k_costs[i in k_origins][j in k_destin]= costs[i][j]* ((x[i]==1) &&(y[j]==1));
    dvar int k_coststimesz[i in k_origins][j in k_destin];
    
    dexpr float totalcost = sum(i in k_origins, j in k_destin:i in origins && j in destin)
        k_coststimesz[i][j];
    
    minimize totalcost ;
            
    subject to
    {
    forall(i in k_origins, j in k_destin)  (z[i][j]==0) => (k_coststimesz[i][j]==0);
    forall(i in k_origins, j in k_destin)  (z[i][j]==1) => (k_coststimesz[i][j]==k_costs[i][j]);
      
    sum(i in origins)x[i]==k;
    sum(j in destin)y[j]==k;
     
    forall(j in k_destin) sum(i in k_origins)z[i][j] == 1;
    forall(i in k_origins) sum(j in k_destin)z[i][j] == 1;
    };
    

    works fine