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