Decision Optimization

Decision Optimization

Delivers prescriptive analytics capabilities and decision intelligence to improve decision-making.

 View Only
  • 1.  CSV reader - 3 and more dimensional arrays

    Posted Wed April 11, 2012 09:25 PM

    Originally posted by: FANS_Nader_Al_Theeb


    Hello

    Sometimes we have parameter with three and more indices. I need to create a .csv file, read it, and store it in IloNumArrayArrayArray.

    For Example, I have the following empty 3D Array


    IloNumArrayArrayArray dcot(env,3); // indices: c, o, and t
    for(int c = 0; c < 3; c++)
    {
    dcot[c] = IloNumArrayArray (env, 5);
    for(int o=0; o<5; o++)
    {
    dcot[c][o] = IloNumArray (env, 4); } }

    I need to fill the (dcot) array with values by reading a csv file using IloCsvReader function, then store the values in dcot

    if that possible, how the .csv file will look like ( what the number of rows and columns), and what the CPLEX - C++ code to do that.

    if it is not possible, is there any other way fill the dcot array with values.

    Thanks
    #CPLEXOptimizers
    #DecisionOptimization


  • 2.  Re: CSV reader - 3 and more dimensional arrays

    Posted Thu April 12, 2012 08:15 AM

    Originally posted by: SystemAdmin


    Since a CSV file encodes a table it only supports 2-dimensional arrays.
    What you could do is define a table like that:
    c | o | t | value
    --+---+---+-------
    0 | 0 | 0 |  1
    0 | 0 | 1 |  2
    0 | 0 | 3 |  3
    0 | 1 | 0 |  4
    0 | 1 | 1 |  5
    0 | 1 | 3 |  6
    1 | 0 | 0 |  7
    1 | 0 | 1 |  8
    1 | 0 | 3 |  9
    1 | 1 | 0 | 10
    1 | 1 | 1 | 11
    1 | 1 | 3 | 12
    

    The first three columns give the index (c,o,t) into array dcot, the fourth column gives the index at this position. Such a file could be easily written and read back using IloCsvReader. On input, you can process the file line by line, extract the three indices and then set the value at the appropriate position.
    #CPLEXOptimizers
    #DecisionOptimization


  • 3.  Re: CSV reader - 3 and more dimensional arrays

    Posted Fri April 13, 2012 05:22 PM

    Originally posted by: FANS_Nader_Al_Theeb


    Thank you Daniel, but can you help me more of how doing that using loop

    for example after reading the file, do I need to use three for loops and at the last one adding
    dcot[c][o][t] = what???

    or there is other choice

    Thanks a lot
    #CPLEXOptimizers
    #DecisionOptimization


  • 4.  Re: CSV reader - 3 and more dimensional arrays

    Posted Sat April 14, 2012 05:20 AM

    Originally posted by: SystemAdmin


    Exactly. You have to use nested loop and assign the values in the innermost loop. Here is an example code for that:
    
    #include <ilcplex/ilocplex.h> #include <ilconcert/ilocsvreader.h>   typedef IloArray<IloNumArray> Array2; typedef IloArray<Array2> Array3;   
    
    void readArray (Array3 result, 
    
    char 
    
    const *file) 
    { result.clear(); IloCsvReader reader;   
    
    try 
    { reader = IloCsvReader(result.getEnv(), file); IloInt 
    
    const lines = reader.getNumberOfItems();   
    // Pass 1: Read all the multi-indices to figure out the dimensions 
    //         of the resulting array. IloInt maxidx[3] = 
    { 0, 0, 0 
    }; 
    
    for (IloInt i = 0; i < lines; ++i) 
    { IloCsvLine 
    
    const line = reader.getLineByNumber(i); 
    
    for (
    
    int j = 0; j < 3; ++j) 
    { IloInt idx = line.getIntByPosition(j); 
    
    if ( idx > maxidx[j] ) maxidx[j] = idx; 
    } 
    }   
    // Initialize the result array with all 0. 
    
    for (IloInt i = 0; i <= maxidx[0]; ++i) 
    { result.add(Array2(result.getEnv())); 
    
    for (IloInt j = 0; j <= maxidx[1]; ++j) 
    { result[i].add(IloNumArray(result.getEnv(), maxidx[2] + 1)); 
    } 
    }   
    // Pass 2: Read the values and place them at the correct positions 
    //         of the result array. 
    
    for (IloInt i = 0; i < lines; ++i) 
    { IloCsvLine 
    
    const line = reader.getLineByNumber(i); result[line.getIntByPosition(0)][line.getIntByPosition(1)][line.getIntByPosition(2)] = line.getFloatByPosition(3); 
    }   reader.end(); 
    } 
    
    catch (...) 
    { reader.end(); 
    
    throw; 
    } 
    }   
    
    int main(
    
    int argc, 
    
    char **argv) 
    { 
    
    for (
    
    int a = 1; a < argc; ++a) 
    { 
    
    char 
    
    const *filename = argv[a]; 
    
    try 
    { IloEnv env; Array3 array(env); readArray(array, filename); 
    
    for (IloInt i = 0; i < array.getSize(); ++i) 
    { 
    
    for (IloInt j = 0; j < array[i].getSize(); ++j) 
    { 
    
    for (IloInt k = 0; k < array[i][j].getSize(); ++k) std::cout << i << 
    ", " << j << 
    ", " << k << 
    ": " << array[i][j][k] << std::endl; 
    } 
    } env.end(); 
    } 
    
    catch (IloException& e) 
    { std::cerr << e << std::endl; 
    } 
    } 
    }
    


    Since I thought that this might be useful for other people as well I also coded up a template version of the above code. The template version has the advantage that it easily extends to any dimension, so that you do not have to re-implement function readArray for every dimension you want to support. Here is the template version of the code:
    
    #include <ilcplex/ilocplex.h> #include <ilconcert/ilocsvreader.h>   typedef IloArray<IloNumArray> Array2; typedef IloArray<Array2> Array3;   namespace CsvUtil 
    { 
    // Unfortunately IloArray does not define a value_type member. 
    // So we have to roll our own to be able to get the type of data 
    // that is stored in an IloArray. 
    // The ValueType template is specialized for each type of interest 
    // here. If you want to read a 4-dimensional array then you will need a 
    //    template struct ValueType<Array4> { typedef Array3 value_type; }; 
    // and so on for each new dimension you want to support. template<typename X> struct ValueType 
    { typedef 
    
    void value_type; 
    }; template struct ValueType<IloNumArray> 
    { typedef IloNum value_type; 
    }; template struct ValueType<Array2> 
    { typedef IloNumArray value_type; 
    }; template struct ValueType<Array3> 
    { typedef Array2 value_type; 
    };   
    // Function template to read a value from a IloCsvLine. 
    // Depending on the type we want to read (IloInt, IloNum, ...) we need 
    // to call a different function. This is achieved by providing explicit 
    // specializations for each type we support. template<typename T> T readValue(IloCsvLine 
    
    const &l, IloInt idx, T const&); template IloNum readValue(IloCsvLine 
    
    const &l, IloInt idx, IloNum const&) 
    { 
    
    return l.getFloatByPosition(idx); 
    } template IloInt readValue(IloCsvLine 
    
    const &l, IloInt idx, IloInt const&) 
    { 
    
    return l.getIntByPosition(idx); 
    }   
    // Template to set a value for a given multi-index. 
    // Since we don't want to hard-code the dimension of the multi-index 
    // we use recursive templates. The default templates just indexes into 
    // the "current" array and recursively invokes itself. 
    // The template that terminates the recursion just reads the value to 
    // be set from the line and assigns it to the place specified by the 
    // multi-index. template<IloInt L,typename T,IloInt I> struct setValue 
    { 
    
    static 
    
    void set(T& t, IloCsvLine const& l) 
    { setValue<L,typename ValueType<T>::value_type,I-1>::set(t[l.getIntByPosition(L-I)], l); 
    } 
    }; 
    // Specialization to terminate recursion. template<IloInt L,typename T> struct setValue<L,T,0> 
    { 
    
    static 
    
    void set(T& t, IloCsvLine const& l) 
    { t = readValue<T>(l, L, T(0));
    //l.getFloatByPosition(L); 
    } 
    };   
    // Template to initialize a multi-dimensional array. 
    // Again we use recursive templates to avoid hard-coding the actual 
    // dimension. template<IloInt L,typename T,IloInt I> struct initValue 
    { 
    
    static 
    
    void init(T& t, IloInt 
    
    const *maxidx) 
    { 
    
    for (IloInt i = 0; i <= maxidx[L - I]; ++i) 
    { t.add(typename ValueType<T>::value_type(t.getEnv())); initValue<L,typename ValueType<T>::value_type,I-1>::init(t[i], maxidx); 
    } 
    } 
    }; 
    // Specialization to terminate recursion. template<IloInt L,typename T> struct initValue<L,T,1> 
    { 
    
    static 
    
    void init(T& t, IloInt 
    
    const *maxidx) 
    { 
    
    for (IloInt i = 0; i <= maxidx[L - 1]; ++i) t.add(typename ValueType<T>::value_type(0)); 
    } 
    };   
    // Function to read a multi-dimensional array from a .csv file. 
    // The file is supposed to be in the following format (for 3 dimensions): 
    //   i0,j0,k0,v0 
    //   i1,j1,k1,v1 
    //   i2,j2,k2,v2 
    // which will result in 
    //   array[i0][j0][k0] = v0 
    //   array[i1][j1][k1] = v1 
    //   array[i2][j2][k2] = v2 
    // The template parameter L specifies the dimension of the array to be 
    // read. Note that it is probably possible to deduce L from the actual 
    // type of the array parameter but I did not bother with doing that. template<IloInt L,typename T> 
    
    void readArrayN (T& array, 
    
    char 
    
    const *file) 
    { array.clear(); IloCsvReader reader;   
    
    try 
    { reader = IloCsvReader(array.getEnv(), file); IloInt 
    
    const lines = reader.getNumberOfItems();   
    // Pass 1: Read all the multi-indices to figure out the dimensions 
    //         of the resulting array. IloInt maxidx[L]; 
    
    for (IloInt i = 0; i < L; ++i) maxidx[i] = 0;   
    
    for (IloInt i = 0; i < lines; ++i) 
    { IloCsvLine 
    
    const line = reader.getLineByNumber(i); 
    
    for (IloInt j = 0; j < L; ++j) 
    { IloInt 
    
    const idx = line.getIntByPosition(j); 
    
    if ( idx > maxidx[j] ) maxidx[j] = idx; 
    } 
    }   
    // Initialize the array to all 0. initValue<L,T,L>::init(array, maxidx);   
    // Pass 2: Read the values and place them at the correct positions 
    //         of the result array. 
    
    for (IloInt i = 0; i < lines; ++i) 
    { setValue<L,T,L>::set(array, reader.getLineByNumber(i)); 
    }   reader.end(); 
    } 
    
    catch (...) 
    { reader.end(); 
    
    throw; 
    } 
    } 
    }   
    
    int main(
    
    int argc, 
    
    char **argv) 
    { 
    
    for (
    
    int a = 1; a < argc; ++a) 
    { 
    
    char 
    
    const *filename = argv[a]; 
    
    try 
    { IloEnv env; Array3 array(env); CsvUtil::readArrayN<3>(array, filename); 
    
    for (IloInt i = 0; i < array.getSize(); ++i) 
    { 
    
    for (IloInt j = 0; j < array[i].getSize(); ++j) 
    { 
    
    for (IloInt k = 0; k < array[i][j].getSize(); ++k) std::cout << i << 
    ", " << j << 
    ", " << k << 
    ": " << array[i][j][k] << std::endl; 
    } 
    } env.end(); 
    } 
    
    catch (IloException& e) 
    { std::cerr << e << std::endl; 
    } 
    } 
    }
    

    To read a 2-dimensional array, just use CsvUtil::readArrayN<2>, for a 4-dimensional array use CsvUtil::readArrayN<4> and so on.

    I hope this helps.
    #CPLEXOptimizers
    #DecisionOptimization


  • 5.  Re: CSV reader - 3 and more dimensional arrays

    Posted Sat April 14, 2012 07:30 PM

    Originally posted by: FANS_Nader_Al_Theeb


    Hi, Thank you Daniel for your effort. But I need more help and explanations, still I cant get the values, I have the following problems:

    In first part of your replay

    I copy the void function (from void to throw;}}) and paste it in my code before the main, then I copy the for loops and paste it in the main function as following:


    //Array3 dcot(env);
    // readArray(dcot, "DCoOT.csv");
    //
    // for (IloInt i = 0; i < dcot.getSize(); ++i) {
    // for (IloInt j = 0; j < dcot[i].getSize(); ++j) {
    // for (IloInt k = 0; k < dcot[i][j].getSize(); ++k)
    // std::cout << i << ", " << j << ", " << k << ": "
    // << dcot[i][j][k] << std::endl;
    // }
    // }


    and change array word to dcot, when I ran the program, I don't receive any syntax error but the program stop and prints "ERROR" word, the last thing I got is the things just before these for loops.

    In the 2nd part of your replay, could you please how we can use the template. In other words, do I need to construct .h file and include it, or just put that code before my main().

    Thanks a lot
    #CPLEXOptimizers
    #DecisionOptimization


  • 6.  Re: CSV reader - 3 and more dimensional arrays

    Posted Mon April 16, 2012 03:47 AM

    Originally posted by: SystemAdmin


    Concerning the "Error": Do you handle exceptions properly? Do you have a try-catch-block in your main() function. If not, maybe try this:
    
    
    
    int main(
    
    int argc, 
    
    char **argv) 
    { 
    
    try 
    { 
    // your code here 
    } 
    
    catch (IloException& e) 
    { std::cerr << 
    "IloException: " << e << std::endl; 
    
    return -1; 
    } 
    
    catch (...) 
    { std::cerr << 
    "Unknown exception!" << std::endl; 
    
    return -1; 
    } 
    
    return 0; 
    }
    

    Does this give you more information? It could also be helpful to run the program through a debugger and see if that tells you more.

    Concerning the template: This is just a standard template. If you only need it in one place then just put it into the implementation file in which you need it. If you need it in more than one place then put it into a header file and include that header file where necessary. In my example I just put the template before the main() function since the template was used only in this single place.
    #CPLEXOptimizers
    #DecisionOptimization