1: #include "aimms/Include.h" 2: #include <iostream> 3: #include <time.h> 4: #include <assert.h> 5: 6: 7: // This example demonstrates the performance difference between three ways to 8: // assign data to a set and to assign a 3 dimensional parameter on 50% of its 9: // domain positions. 10: 11: 12: // the data used to fill the domainsets in this example 13: const char* s_StringLabels[325] = {"ab", "ac", "ad", "ae", "ah", "ai", "al", "am", "an", "ar", "as", "at", "aw", "ax", "ay", "aah", "aam", "abb", "abc", "abo", "abs", "abt", "aby", "ace", "ach", "act", "add", "adj", "ado", "ads", "adv", "adz", "afb", "aft", "aga", "age", "ago", "aha", "ahs", "ahu", "aid", "ail", "aim", "air", "ais", "ait", "ake", "ala", "alb", "ale", "all", "alp", "als", "alt", "ama", "amp", "amt", "amu", "amy", "ana", "and", "ani", "ann", "ano", "ant", "any", "ape", "apo", "app", "apt", "ara", "arc", "are", "arf", "ark", "arm", "ars", "art", "asa", "ash", "ask", "asp", "ass", "ate", "aud", "auf", "auk", "aum", "aux", "ava", "ave", "avg", "awe", "awk", "awl", "awm", "awn", "axe", "aye", "aahs", "abay", "abba", "abbe", "abbr", "abed", "abet", "abib", "abid", "abit", "able", "ably", "abox", "abra", "abut", "abye", "acct", "aced", "aces", "ache", "achy", "acid", "aclu", "acme", "acne", "acre", "acts", "acyl", "adam", "adar", "adaw", "adds", "adit", "ados", "adry", "advt", "adze", "aeon", "aero", "aery", "afar", "afer", "affy", "afro", "agar", "aged", "agen", "ages", "agha", "agin", "agio", "agni", "agog", "agon", "agre", "ague", "ahem", "ahey", "ahoy", "aide", "aids", "aiel", "ails", "aims", "aino", "airs", "airy", "ajar", "ajog", "akin", "alae", "alai", "alan", "alar", "alas", "alba", "albe", "albs", "alco", "alee", "alem", "ales", "alew", "alfa", "alga", "alii", "alit", "alls", "ally", "alma", "alme", "alms", "aloe", "alow", "alps", "also", "alto", "alum", "amah", "ambo", "amel", "amen", "amex", "amia", "amic", "amid", "amir", "amis", "amit", "amma", "ammo", "amok", "amps", "amts", "amyl", "anal", "anan", "anas", "ands", "anes", "anet", "anew", "anil", "anis", "ankh", "anna", "anne", "anno", "anoa", "anon", "ansa", "ansi", "anta", "ante", "anti", "ants", "anus", "apar", "aped", "aper", "apes", "apex", "apis", "apod", "apse", "apus", "aqua", "arab", "arak", "arch", "arco", "arcs", "area", "ares", "aret", "arew", "argo", "aria", "arid", "aril", "arks", "arms", "army", "arna", "arow", "arse", "arts", "arty", "arum", "asap", "asci", "asea", "ashy", "asia", "asks", "asps", "asse", "assn", "asst", "atma", "atmo", "atom", "atop", "atte", "attn", "atty", "atwo", "aube", "auks", "auld", "auln", "aune", "aunt", "aura", "auto", "avdp", "avel", "aver", "aves", "avid", "avie", "avis", "avow", "away", "awed", "awes", "awls", "awns", "awny", "awol", "awry", "axal", "axed", "axel", "axes", "axil", "axis", "axle", "axon", "ayah", "ayen", "ayes", "ayle", "ayme", "ayry", "azym"}; 14: 15: class Performance{ 16: 17: public: 18: Performance(const char* aimmsLocation, const char* project) 19: : m_Location(aimmsLocation) 20: , m_Project(project) 21: , m_Aimms(0) 22: , m_SubSet(0) 23: , m_LargePar(0) 24: , m_StartTime(0) 25: { 26: } 27: 28: 29: public: 30: /** 31: * The convenient way: Use a Tuple object constructed with labels to insert 32: * each data entry. This is inefficient due to the required string 33: * comparisons. 34: */ 35: void convenientAssignLabels(int setsize) { 36: 37: openSessionAndData(); 38: 39: start("Assign string labels using insert and the Tuple class"); 40: 41: double value = 0.0; 42: 43: for (int i = 0; i < setsize; i += 2) { // a density of 50%, skip al odd positions on this dimension 44: for (int j = 0; j < setsize; ++j) { 45: for (int k = 0; k < setsize; ++k) { 46: ++value; 47: m_LargePar->insert(aimms::Tuple(s_StringLabels[i], s_StringLabels[j], s_StringLabels[k]), value); 48: } 49: } 50: } 51: m_Aimms->updateData(); 52: 53: stop(); 54: 55: closeDataAndSession(); 56: } 57: 58: /* 59: * Ordinals, ITuple and setValue: The more efficient way: Use ordinals to 60: * construct an ITuple and add the datapoint with it. Since the SDK uses the 61: * 'natural' Aimms ordering, the ordering of the data is not guaranteed to 62: * be the order in which the set data is supplied. So use ordinals with 63: * care. See also the section on "Set element ordering" in the Execution 64: * Efficiency Cookbook chapter in the Aimms Language Reference. 65: */ 66: void efficientAssignLabels(int setsize) { 67: 68: openSessionAndData(); 69: 70: start("Assign string labels first to the set and use the ITuple interface, ordinals and setValue to assign the values on the parameter"); 71: 72: 73: for (int i = 0; i < setsize; ++i) { 74: m_SubSet->add(s_StringLabels[i]); 75: } 76: 77: m_Aimms->updateData(); // Update the set data to make label lookup faster. 78: 79: aimms::ITuple* tup = m_LargePar->createTuple(); 80: 81: double value = 0.0; 82: try{ 83: for (int i = 1; i <= setsize; i += 2) { // A density of 50%: skip all odd positions of this dimension. 84: 85: // Set each dimension only when it changes. 86: tup->getElement(0)->setOrdinal(i); // Here the getElement method is used to get access to the indivual elements per dimension 87: // The operator [] is defined on ITuple as well. For hints on how to use these, see the language reference. 88: for (int j = 1; j <= setsize; ++j) { 89: tup->getElement(1)->setOrdinal(j); 90: for (int k = 1; k <= setsize; ++k) { 91: tup->getElement(2)->setOrdinal(k); 92: ++value; 93: m_LargePar->setValue(tup, value); 94: } 95: } 96: } 97: m_Aimms->updateData(); 98: } catch( aimms::RuntimeException& ex){ 99: std::cerr<< "Catched a runtime exception: " << ex.what() <<std::endl; 100: m_Aimms->clearBuffers(); 101: } 102: 103: stop(); 104: 105: closeDataAndSession(); 106: } 107: 108: /* 109: * setValues: Efficient and convenient if the data to assign is dense. The 110: * setLabels (for sets) and setValues (for data) methods do allow for dense 111: * allocation of an identifier. The data will be added in the natural 112: * ordering of the domains, thus combining ease of use with efficiency. 113: */ 114: void denseAssignLabels(int setsize) { 115: 116: openSessionAndData(); 117: 118: start("Assign string labels first to the set using setLabels and then use setValues on the IMultiDimData to assign the values to the parameter"); 119: 120: m_SubSet->setLabels(s_StringLabels, setsize); 121: // UpdateData is not neccesary here because after the setLabels method the set is immediately up-to-date. 122: 123: int datasize = setsize * setsize * setsize; 124: double* values = new double[datasize]; 125: 126: // Whether a loop like this is neccesary depends on the source of the values. 127: double value = 0; 128: int pos = 0; 129: for (int i = 0; i < setsize; i += 2) { 130: 131: for (int j = 0; j < setsize; ++j) { 132: for (int k = 0; k < setsize; ++k) { 133: values[pos++] = ++value; 134: } 135: } 136: // Now insert a two dimensional slice with zero's (the default value), because this is a dense assign method but the data is not fully dense. 137: for (int z = 0; z < setsize * setsize; ++z) { 138: values[pos++] = 0; 139: } 140: } 141: 142: m_LargePar->setValues(values, datasize); 143: delete [] values; 144: 145: m_Aimms->updateData(); 146: 147: stop(); 148: 149: closeDataAndSession(); 150: } 151: 152: /* 153: * Slices: Efficient if the data to be assigned is sparse and distributed in 154: * a way slices can be defined. In this example multiple views are created 155: * that are all restricted on the same level on a single element. This 156: * reduces the dimension. 157: */ 158: void sliceAssignLabels( int setsize) { 159: 160: openSessionAndData(); 161: 162: start("Assign string labels first to the set using setLabels and then use setValues on IDataView slices of the IMultiDimData to assign the values to the parameter"); 163: 164: 165: m_SubSet->setLabels(s_StringLabels, setsize); 166: 167: 168: // Create a filter to slice the parameter. 169: aimms::IFilter* filter = m_LargePar->createFilter(); 170: 171: int vieweddatasize = setsize * setsize; 172: double* values = new double[vieweddatasize]; 173: 174: double value = 0; 175: for (int i = 0; i < setsize; i += 2) { 176: // For each position of this dimension to which we will assign data, construct the filter to create a DataView. 177: // Notice the +=2, we only need views on the even positions. 178: filter->restrict(0, s_StringLabels[i]); 179: aimms::IDataView* slice = m_LargePar->openView(filter); 180: 181: int pos = 0; 182: for (int j = 0; j < setsize; ++j) { 183: for (int k = 0; k < setsize; ++k) { 184: values[pos++] = ++value; 185: } 186: } 187: slice->setValues(values, vieweddatasize); 188: } 189: 190: delete [] values; 191: m_Aimms->updateData(); 192: 193: stop(); 194: 195: closeDataAndSession(); 196: } 197: 198: /* 199: * Filtering by call domain: Efficient if the data is sparse and an 200: * appropriate subset is available to filter with. 201: */ 202: void filteredAssignLabels(int setsize) { 203: 204: openSessionAndData(); 205: 206: start("Assign string labels first to the set using setLabels and use setValues on an IDataView filtered by a subset to assign the values on the parameter"); 207: 208: m_SubSet->setLabels(s_StringLabels, setsize); 209: 210: // Open a subset on the domainset to use as a call domain on the level to restrict. 211: aimms::ISetData* subsubset = m_Aimms->openSet("SubSubSet"); 212: aimms::IElement* element = m_SubSet->createElement(); 213: 214: for (int o = 1; o <= setsize; o += 2) { 215: element->setOrdinal(o); 216: subsubset->add(element); 217: } 218: m_Aimms->updateData(); 219: 220: int vieweddatasize = setsize / 2 * setsize * setsize; 221: double* values = new double[vieweddatasize]; 222: 223: // Create a filter to slice the parameter. 224: aimms::IFilter* filter = m_LargePar->createFilter(); 225: filter->restrict(0, subsubset); 226: aimms::IDataView* view = m_LargePar->openView(filter); 227: 228: 229: double value = 0; 230: int pos = 0; 231: for (int i = 0; i < setsize; i += 2) { 232: for (int j = 0; j < setsize; ++j) { 233: for (int k = 0; k < setsize; ++k) { 234: values[pos++] = ++value; 235: } 236: } 237: } 238: view->setValues(values, vieweddatasize); 239: 240: delete [] values; 241: m_Aimms->updateData(); 242: 243: stop(); 244: 245: subsubset->close(); 246: closeDataAndSession(); 247: } 248: 249: 250: 251: private: 252: // Helper functions 253: void openSessionAndData() { 254: m_Aimms = 0; 255: m_Aimms = aimms::openSession(m_Location.c_str(), m_Project.c_str()); 256: if (m_Aimms == 0) { 257: return; 258: } 259: m_SubSet = m_Aimms->openSet("LargeSubSet"); 260: m_LargePar = m_Aimms->openMultiDim("LargePar1"); 261: } 262: 263: void closeDataAndSession() { 264: m_LargePar->close(); 265: m_SubSet->close(); 266: m_Aimms->close(); 267: m_Aimms = 0; 268: } 269: 270: void start(const char* testDescription) { 271: std::cout <<"Start Running " << testDescription << "..." << std::endl; 272: time(&m_StartTime); 273: } 274: 275: void stop() { 276: time_t stop; 277: time(&stop); 278: std::cout<< "... " <<" took " << difftime (stop,m_StartTime) << " seconds." << std::endl << std::endl; 279: } 280: 281: 282: 283: private : 284: std::string m_Location; 285: std::string m_Project; 286: aimms::ISession* m_Aimms; 287: aimms::ISetData* m_SubSet; 288: aimms::IMultiDimData* m_LargePar; 289: time_t m_StartTime; 290: }; 291: 292: 293: 294: 295: 296: int main(int argc, const char* argv[]) 297: { 298: if (argc != 3) { 299: std::cerr << "Invalid number of arguments. usage: <location of AIMMS> <location of project>" << std::endl; 300: return 1; 301: } 302: int setsize = 150; 303: 304: Performance test(argv[1], argv[2]); 305: 306: try { 307: 308: test.convenientAssignLabels(setsize); 309: test.efficientAssignLabels(setsize); 310: test.denseAssignLabels(setsize); 311: test.sliceAssignLabels(setsize); 312: test.filteredAssignLabels(setsize); 313: 314: } catch (aimms::Exception& ex) { 315: std::cerr<< "AIMMS exception:" << ex.what() << std::endl; 316: } catch (std::exception& ex){ 317: std::cerr << "Exception:" << ex.what() << std::endl; 318: } 319: } 320: 321: