├── Makefile ├── README.txt ├── main.cpp ├── mcf.cpp ├── mcf.h ├── mcf_solver ├── pqueue.cpp ├── pqueue.h └── tests ├── README.txt ├── toy1.network ├── toy2.network └── toy3.network /Makefile: -------------------------------------------------------------------------------- 1 | CC=g++ 2 | 3 | CFLAGS=-O3 -Wall 4 | LIBS=-lm -lstdc++ 5 | 6 | CSOURCES=$(wildcard *.cpp) 7 | OBJECTS=$(patsubst %.cpp,%.o,${CSOURCES}) 8 | 9 | TARGETS=mcf_solver 10 | 11 | all: ${TARGETS} 12 | 13 | mcf_solver: ${OBJECTS} 14 | $(CC) $(CFLAGS) $(LIBS) -o $@ ${OBJECTS} 15 | 16 | .cpp.o: 17 | $(CC) $(CFLAGS) -c $< 18 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Author: Cristinel Ababei 2 | August.2009, Fargo ND; version v1.0 (initial) 3 | January.2016, Milwaukee, WI; version v2.0 4 | cristinel.ababei@marquette.edu 5 | 6 | 7 | Synopsis 8 | ======== 9 | This is an efficient C++ implementation of a polynomial time approximation 10 | algorithm to solve multicommodity flow problems [1]. The implementation 11 | follows basically [1] but with the "binary search" in the second part 12 | (while solving the so called "min-cost max concurrent flow" problem) 13 | improved as described in [2]. 14 | This implementation is meant to provide a clean and simple interface (for 15 | easy integration in bigger projects) for specifying the network (or graph) 16 | and the supplies and demands (or commodities). 17 | 18 | 19 | Credits 20 | ======= 21 | This code is an incomplete porting to C++ with "extraction", restructuring, 22 | and clean-up of the MCF code written by Yuanfang Hu, Hongyu Chen, and Yi Zhu 23 | (while at UCSD as students of Chung-Kuan Cheng). Their code is discussed 24 | in [2] and can be downloaded from here: 25 | http://www-cse.ucsd.edu/users/kuan/supercomputer/supercomputer_package.tar 26 | 27 | 28 | Installation 29 | ============ 30 | The latest version of the tool can be downloaded from: 31 | www.dejazzer.com/software.html 32 | To compile it just type: 33 | > make 34 | 35 | 36 | How to use the tool 37 | =================== 38 | Usage: mcf_solver network_file [Options...] 39 | Options: 40 | [-problem_type MCF|MCMCF]. Default is MCMCF. 41 | where: MCF - max multicommodity flow, MCMCF - min-cost max concurrent flow 42 | [-epsilon float]. Default is 0.1. 43 | 44 | Examples: 45 | > mcf_solver tests/toy1.network -problem_type MCF 46 | > mcf_solver tests/toy1.network 47 | > mcf_solver tests/toy2.network -problem_type MCF 48 | > mcf_solver tests/toy2.network 49 | > mcf_solver tests/toy2.network -problem_type MCF -epsilon 0.01 50 | 51 | 52 | Papers 53 | ====== 54 | To better understand the code I suggest you to read the following 55 | (some of the comments refer to some of the equations or things 56 | from some of these papers): 57 | [1] G. Karakostas, "Faster approximation schemes for fractional 58 | multicommodity flow problems," ACM Trans. on Algorithms, 59 | vol. 4, no. 3, March 2008. 60 | Can be downloaded from: http://www.cas.mcmaster.ca/~gk 61 | [2] Yuanfang Hu, Yi Zhu, Hongyu Chen, Ronald L. Graham, and 62 | Chung-Kuan Cheng, "Communication latency aware low power NoC 63 | synthesis," DAC, pp. 574-579, 2006. 64 | [3] Lisa Fleischer, "Approximating fractional multicommodity 65 | flow independent of the number of commodities," FOCS, 1999. 66 | [4] Naveen Garg and Jochen Könemann, "Faster and simpler algorithms 67 | for multicommodity Flow and other fractional packing problems," 68 | 39th Annual Symposium on Foundations of Computer Science, 1998. 69 | [5] C. Ababei, "Efficient congestion-oriented custom Network-on-Chip 70 | topology synthesis," IEEE Int. Conference on Reconfigurable 71 | Computing and FPGAs (ReConFig 2010), Cancun, Mexico, Dec. 2010. 72 | (the portion about MCF only) 73 | 74 | 75 | Things I need yet to do 76 | ======================= 77 | 1. Clean-up more all classes: have all class-variables as "_variable" 78 | and not just "variable"; encapsulate everything: make all class-variables 79 | private and provide access functions; get rid of dubious constants such as 80 | "400" inside shortest_path() routine; 81 | 2. Get rid of all malloc's; for that use vectors for all arrays 82 | 3. Implement MCF::build_network_from_host_application() - this should be 83 | very easy for anyone 84 | 85 | 86 | Things you might want to do 87 | =========================== 88 | 1. If you dig into it and find any bug, please let me know 89 | 2. If you use this code in a research project and want to 90 | include a reference to it, then please use: 91 | [] Cristinel Ababei, C++ implementation of a polynomial time 92 | approximation algorithm, 2009, [Online], Available: 93 | www.dejazzer.com/software.html 94 | 3. If you'll ever hit it big (to be read: make a lot of money :-) ), 95 | and this code helped you in any way, then please consider 96 | donating some to support my research (I need it :-) ) 97 | 98 | 99 | Copyright 100 | ========= 101 | Copyright 2009-present by Cristinel Ababei, cristinel.ababei@marquette.edu 102 | This Copyright notice applies to all files, called hereafter 103 | "The Software". 104 | Permission to use, copy, and modify this software and its 105 | documentation is hereby granted only under the following 106 | terms and conditions. Both the above copyright notice and 107 | this permission notice must appear in all copies of the 108 | software, derivative works or modified versions, and any 109 | portions thereof, and both notices must appear in supporting 110 | documentation. Permission is granted only for non-commercial 111 | use. For commercial use, please contact the author. 112 | This software may be distributed (but not offered for sale 113 | or transferred for compensation) to third parties, provided 114 | such third parties agree to abide by the terms and conditions 115 | of this notice. 116 | The Software is provided "as is", and the authors, 117 | Marquette University, as well as any and 118 | all previous authors (of portions or modified portions of 119 | the software) disclaim all warranties with regard to this 120 | software, including all implied warranties of merchantability 121 | and fitness. In no event shall the authors or NDSU or any and 122 | all previous authors be liable for any special, direct, 123 | indirect, or consequential damages or any damages whatsoever 124 | resulting from loss of use, data or profits, whether in an 125 | action of contract, negligence or other tortious action, 126 | arising out of or in connection with the use or performance 127 | of this software. 128 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include "stdio.h" 2 | #include "stdlib.h" 3 | #include "math.h" 4 | #include 5 | #include "mcf.h" 6 | 7 | using namespace std; 8 | 9 | //////////////////////////////////////////////////////////////////////////////// 10 | // 11 | // launching point; 12 | // 13 | //////////////////////////////////////////////////////////////////////////////// 14 | 15 | int main( int argc, char **argv) 16 | { 17 | struct timeval tv_start, tv_end; 18 | gettimeofday( &tv_start, (struct timezone*) NULL); 19 | 20 | 21 | // (1) run MCF solver; 22 | MCF mcf; 23 | // here you should build your network either by reading it from a 24 | // file (network examples are in /tests) or by populating the 25 | // mcf object directly from the host application (the one inside 26 | // which you plan to call MCF solver possibly multiple times); 27 | mcf.parse_options( argc, argv); 28 | mcf.build_network_from_file(); 29 | //mcf.build_network_from_host_application(); 30 | mcf.run_mcf_solver(); 31 | 32 | 33 | // (2) entertain user; 34 | printf("\n\nFINAL RESULT:"); 35 | if ( mcf.problem_type() == MCMCF_TYPE) { 36 | printf("\nFinal latency L=%lf", mcf.get_L()); 37 | } 38 | mcf.print_network_demands( true); // print_only_edges; 39 | // Note: rounding is not required; you may want or not to use this 40 | // in your application; in my case I do it so that one commodity 41 | // is shipped only via one path (in the context of NoCs I want to 42 | // avoid packet (re)ordering at destinations); be aware: randomization 43 | // "damages" optimality and may violate capacities; 44 | mcf.do_randomized_rounding(); 45 | mcf.print_routing_paths(); 46 | 47 | 48 | // (3) clean up 49 | mcf.free_topology(); 50 | 51 | gettimeofday( &tv_end, (struct timezone*) NULL); 52 | printf("\nRuntime: %ld sec \n", tv_end.tv_sec - tv_start.tv_sec); 53 | 54 | return 1; 55 | } 56 | -------------------------------------------------------------------------------- /mcf.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "pqueue.h" 8 | #include "mcf.h" 9 | 10 | using namespace std; 11 | 12 | //////////////////////////////////////////////////////////////////////////////// 13 | // 14 | // MCF host 15 | // 16 | //////////////////////////////////////////////////////////////////////////////// 17 | 18 | void MCF::initialize( double delta, int flag) 19 | { 20 | // called each time mcf() is called; 21 | int i=0; 22 | 23 | // init dual variables 24 | if ( flag == 0) { // 0: "max concurrent flow" 25 | _phi_latency = 0.0; 26 | } else { // 1 "min-cost max concurrent flow" 27 | _phi_latency = delta / L; // dual variable PHI_d = 1/1000000 28 | } 29 | for ( i = 0; i < no_edge; i++) { 30 | edges[i]._Y_e = delta / edges[i].capacity; 31 | edges[i]._old_Y_e = edges[i]._Y_e; 32 | } 33 | // init edges 34 | for ( i = 0; i < no_edge; i++) { 35 | edges[i].flow = 0.0; 36 | for ( int j = 0; j < no_commodity; j++) { 37 | edges[i]._flows[ j] = 0.0; 38 | } 39 | } 40 | // reset edge flows 41 | for ( i = 0; i < no_edge; i++) { 42 | _temp_edge_flow[i] = 0.0; 43 | } 44 | // init edge "length function" l(e) 45 | for ( i = 0; i < no_edge; i++) { 46 | edges[i].length = 0.0; 47 | edges[i].length += edges[i]._Y_e; 48 | edges[i].length += edges[i].latency * _phi_latency; // 0 for flag=0; 49 | } 50 | // init commodities 51 | for ( i = 0; i < no_commodity; i++) { 52 | _commodities[i].left_demand = _commodities[i].demand; 53 | } 54 | 55 | // reset _total_latency, which will be computed as the summation 56 | // of individual latencies from shortest-path trees for each source 57 | // of commodities; 58 | _total_latency = 0.0; 59 | } 60 | 61 | bool MCF::parse_options( int argc, char **argv) 62 | { 63 | // Note: if this code is to be used within a host code (i.e., not 64 | // as a stand alone tool) then, the stuff in here should be done 65 | // from with the host code; 66 | 67 | // (1) parse command line arguments; 68 | // return false if error 69 | if ( argc == 1) { 70 | printf("\nUsage: mcf_solver network_file [Options...]\n"); 71 | printf("Options:\n"); 72 | printf("\t[-problem_type MCF|MCMCF]. Default is MCMCF.\n"); 73 | printf("\twhere: MCF - max multicommodity flow, MCMCF - min-cost max concurrent flow\n"); 74 | printf("\t[-epsilon float]. Default is 0.1.\n"); 75 | exit(1); 76 | } 77 | // first argument is always the network file; 78 | sprintf( _network_filename, "%s", argv[1]); 79 | _problem_type = MCMCF_TYPE; // default; 80 | _epsilon1 = 0.1; // default; 81 | 82 | int i = 2; 83 | while ( i < argc) { 84 | 85 | if ( strcmp (argv[i],"-problem_type") == 0) { 86 | if (argc <= i+1) { 87 | printf("Error: -problem_type option requires a string parameter.\n"); 88 | exit(1); 89 | } 90 | if (strcmp(argv[i+1], "MCF") == 0) { 91 | _problem_type = MCF_TYPE; 92 | } 93 | else if (strcmp(argv[i+1], "MCMCF") == 0) { 94 | _problem_type = MCMCF_TYPE; 95 | } else { 96 | printf("Error: -problem_type must be MCF or MCMCF.\n"); 97 | exit(1); 98 | } 99 | i += 2; 100 | continue; 101 | } 102 | 103 | if ( strcmp(argv[i], "-epsilon") == 0) { 104 | _epsilon1 = atof(argv[i+1]); 105 | if ( _epsilon1 <= 0 || _epsilon1 >= 1) { 106 | printf("Error: -epsilon option requires a float in (0,1).\n"); 107 | exit(1); 108 | } 109 | i += 2; 110 | continue; 111 | } 112 | } 113 | 114 | return true; 115 | } 116 | 117 | void MCF::init_param() 118 | { 119 | // called one time only from inside build_network_from_file() because 120 | // we need the number of edges of the graph for delta calculation; 121 | // () set latency budget to infinity (inf); 122 | L = 1000000.0; 123 | // () epsilon is now set to default _epsilon1 = 0.1 inside parse_options(); 124 | // or it could be set by user via command line argument; 125 | // () delta is set according to equation 3 from Karakostas paper; 126 | double epsilon = _epsilon1; 127 | _delta = (1/pow(1+epsilon, (1-epsilon)/epsilon))*(pow((1-epsilon)/no_edge, 1/epsilon)); 128 | // () expected number of iterations (or phases) of the outer loop; 129 | // currently it is not used for any purpose; 130 | _scale = log((1+epsilon)/_delta) / log(1+epsilon); 131 | //printf("\nepsilon=%e delta=%e _scale=%e\n",_epsilon1,_delta,_scale); // exit(1); 132 | } 133 | 134 | bool MCF::feasibility_check() 135 | { 136 | // check and see if the routed flows violate capacities; if so, 137 | // then return false: no feasible solution; this is a "stretch"; 138 | // feasibility should be checked differently; 139 | double threshold, violation; 140 | bool printed_warning = false; 141 | for ( int i = 0; i < no_edge; i++) { 142 | if ( edges[i].flow > edges[i].capacity) { 143 | // consider only violations that are greater than 3 * epsilon; 144 | threshold = 3 * _epsilon1 * edges[i].capacity; 145 | violation = (edges[i].flow - edges[i].capacity); 146 | if ( violation > threshold) { 147 | return false; 148 | } else { 149 | // print once only a warning; 150 | if ( !printed_warning) { 151 | printf("\nWarning: Some edges have capacity violation within 3*epsilon"); 152 | printed_warning = true; 153 | } 154 | } 155 | } 156 | } 157 | return true; // solution is ok; 158 | } 159 | 160 | double MCF::compute_D() 161 | { 162 | // "D" is the numerator of dual=D/alpha; see section 6 of Garg paper; 163 | double D = 0.0; 164 | for ( int i = 0; i < no_edge; i++) { 165 | D += edges[i]._Y_e * edges[i].capacity; 166 | } 167 | D += L * _phi_latency; 168 | 169 | return D; 170 | } 171 | 172 | double MCF::compute_alpha() 173 | { 174 | // "alpha" is the denuminator of dual=D/alpha; see section 6 of Garg paper; 175 | int i, j; 176 | double alpha = 0.0; // to return; 177 | for ( i = 0; i < no_node; i++) { 178 | if ( nodes[i].no_comm) { 179 | int *dest_flag = (int*)malloc((no_node)*sizeof(int)); 180 | if ( dest_flag == NULL) { 181 | printf("\nError: Unable to malloc .\n"); exit (1); 182 | } 183 | memset((void*)dest_flag,0,(no_node)*sizeof(int)); 184 | 185 | for ( j = 0; j < nodes[i].no_comm; j++) { 186 | dest_flag[_commodities[nodes[i].comms[j]].dest] = 1; 187 | } 188 | 189 | shortest_paths( nodes[i].id, nodes[i].no_comm, dest_flag); 190 | _rd++; 191 | free( dest_flag); 192 | 193 | for ( j = 0; j < nodes[i].no_comm; j++) { 194 | alpha += _commodities[nodes[i].comms[j]].demand * 195 | nodes[_commodities[nodes[i].comms[j]].dest].dist; 196 | } 197 | } 198 | } 199 | 200 | return alpha; 201 | } 202 | 203 | double MCF::compute_lambda() 204 | { 205 | // compute lambda=MIN(actual flow/demand) among all commodities; 206 | double lambda = DBL_MAX; 207 | 208 | for ( int comm_i = 0; comm_i < no_commodity; comm_i++) { 209 | // for each commodity we take its source node and look 210 | // at its outgoing edges to sum all flow pushed/routed 211 | // for this commodity; 212 | int src_id = _commodities[comm_i].src; // source node; 213 | double routed_flow_this_commodity = 0.0; 214 | for ( int j = 0; j < nodes[src_id].no_edge; j++) { 215 | int edge_id = nodes[src_id].edges[j]; 216 | routed_flow_this_commodity += edges[edge_id]._flows[ comm_i]; 217 | } 218 | double this_lambda = routed_flow_this_commodity / _commodities[comm_i].demand; 219 | if ( this_lambda < lambda) { 220 | lambda = this_lambda; 221 | } 222 | } 223 | 224 | return lambda; 225 | } 226 | 227 | double MCF::check_latency_constraint( int dest) 228 | { 229 | // this is L/c(P) in Fleischer paper (pp. 10), where 230 | // c(P) is is the cost of sending one unit of flow along 231 | // the shortest path: Sum_{e in P}{D(e)}, where D(e) is 232 | // latency of each edge along path; 233 | int t = dest; 234 | double cost_to_send_unit_flow = 0.0; // along the shortest path to this dest; 235 | while ( nodes[t].pre != -1) { 236 | cost_to_send_unit_flow += edges[nodes[t].pre_edge].latency; 237 | t = nodes[t].pre; 238 | } 239 | 240 | return L/cost_to_send_unit_flow; 241 | } 242 | 243 | double MCF::min_capacity( int s) 244 | { 245 | // Note: currently not used; 246 | // find "c" as the minimum capacity of the edges on ALL 247 | // the paths in the shortest paths tree for this source node "s"; 248 | int t = 0; 249 | double min_capacity = 1000000.0; 250 | 251 | _min_rd++; 252 | // start from all dest nodes, traverse shortest path tree; 253 | for ( int i = 0; i < nodes[s].no_comm; i++) { 254 | if ( _commodities[nodes[s].comms[i]].left_demand > 1e-3) { 255 | // pick up this destination and walk backward to sourse "s"; 256 | t = _commodities[nodes[s].comms[i]].dest; 257 | while ( (nodes[t].pre != -1) && (nodes[t].min_visited != _min_rd)) { 258 | int edge_id = nodes[t].pre_edge; 259 | nodes[t].min_visited = _min_rd; 260 | 261 | if ( edges[edge_id].capacity < min_capacity) { 262 | min_capacity = edges[edge_id].capacity; 263 | } 264 | } 265 | } 266 | } 267 | return min_capacity; 268 | } 269 | 270 | double MCF::min_capacity_this_commodity( int dest) 271 | { 272 | // find "c" as the minimum available capacity of the edges on 273 | // the shortest path for this sink node "t"; 274 | double min_avail_capacity = 1000000.0; 275 | 276 | int t = dest; 277 | while ( nodes[t].pre != -1) { 278 | int edge_id = nodes[t].pre_edge; 279 | if ( edges[edge_id].left_capacity < min_avail_capacity) { 280 | min_avail_capacity = edges[edge_id].left_capacity; 281 | } 282 | t = nodes[t].pre; 283 | } 284 | return min_avail_capacity; 285 | } 286 | 287 | void MCF::reset_left_capacities_in_tree( int s) 288 | { 289 | // reset left_capacities of edges in the shortest paths tree to the 290 | // initial capacities; u'(e)=u(e), for any e in tree; 291 | int t = 0; 292 | // start from all dest nodes, traverse shortest path tree; 293 | for ( int i = 0; i < nodes[s].no_comm; i++) { 294 | if ( _commodities[nodes[s].comms[i]].left_demand > 1e-3) { 295 | // pick up this destination and walk backward to sourse "s"; 296 | t = _commodities[nodes[s].comms[i]].dest; 297 | while ( nodes[t].pre != -1) { 298 | int edge_id = nodes[t].pre_edge; 299 | edges[edge_id].left_capacity = edges[edge_id].capacity; 300 | t = nodes[t].pre; 301 | } 302 | } 303 | } 304 | } 305 | 306 | void MCF::route_flow( int t, double routed_amount, int commodity_id) 307 | { 308 | // t is destination to which we route "amount" of commodity; 309 | while ( nodes[t].pre != -1) { 310 | int edge_id = nodes[t].pre_edge; 311 | _temp_edge_flow[edge_id] += routed_amount; 312 | edges[edge_id].left_capacity -= routed_amount; 313 | 314 | // record this routed_amount for this commodity id on the 315 | // corresponding edge also; 316 | assert(commodity_id >= 0 && commodity_id < no_commodity); 317 | edges[ edge_id]._flows[ commodity_id] += routed_amount; 318 | 319 | t = nodes[t].pre; 320 | } 321 | return; 322 | } 323 | 324 | void MCF::update_dual_variables( int s, double epsilon, int flag) 325 | { 326 | // update dual variables; compute l_i_j_s(e), where 327 | // "j" is jth iteration of phase "i", and "s" is the current step; 328 | int i, t; 329 | double old_phi_latency; 330 | double temp_latency = 0.0; 331 | 332 | // (1) accumulate temp_latency along the shortest paths for the 333 | // shortest paths tree for the commodities of this source node; 334 | _min_rd++; 335 | for ( i = 0; i < nodes[s].no_comm; i++) { 336 | t = _commodities[nodes[s].comms[i]].dest; 337 | while ( (nodes[t].pre != -1) && (nodes[t].min_visited != _min_rd)) { 338 | int edge_id = nodes[t].pre_edge; 339 | nodes[t].min_visited = _min_rd; 340 | 341 | temp_latency += _temp_edge_flow[edge_id] * edges[edge_id].latency; 342 | 343 | // update the dual variable Y_e; 344 | edges[edge_id]._old_Y_e = edges[edge_id]._Y_e; 345 | // Note: _temp_edge_flow[edge_id] represents the amount of total 346 | // flow of all commodities that have the same source "s", which is 347 | // pushed thru this edge during this step "s"; 348 | edges[edge_id]._Y_e *= 349 | (1 + epsilon * _temp_edge_flow[edge_id] / edges[edge_id].capacity); 350 | 351 | // walk upstream on shortest path; 352 | t = nodes[t].pre; 353 | } 354 | } 355 | _min_rd++; 356 | // record latency contributed due to total flow pushed thru during 357 | // this step "s"; 358 | _total_latency += temp_latency; 359 | 360 | // (2) update additional dual variable PHI_d; 361 | old_phi_latency = _phi_latency; 362 | _phi_latency *= (1 + epsilon * temp_latency / L); // adjust value from prev. iter; 363 | 364 | // (3) update the "length function"; 365 | for ( i = 0; i < no_edge; i++) { 366 | edges[i].length += (edges[i]._Y_e - edges[i]._old_Y_e); 367 | // the above length function is enough for "max concurrent flow" problem; 368 | // howver, if we solve "min-cost max concurrent flow", then, we must add 369 | // more to the length function; 370 | if ( flag != 0) { // 1 371 | edges[i].length += edges[i].latency * (_phi_latency - old_phi_latency); 372 | } 373 | } 374 | 375 | // (4) add to the flow recorded for each edge the accumulated 376 | // amounts (as sum of f_{i,j,s}^{c_q}) for each commodity routed during 377 | // this iteration, amounts which are reflected by _temp_edge_flow (which 378 | // has values != zero) for edges of shortest path of this iter; 379 | for ( i = 0; i < no_edge; i++) { 380 | edges[i].flow += _temp_edge_flow[i]; 381 | } 382 | 383 | // (5) reset temp storage of pushed flow during this iter; prepare it 384 | // for the next push/iteration; 385 | for ( i = 0; i < no_edge; i++) { 386 | _temp_edge_flow[i] = 0.0; 387 | } 388 | 389 | return; 390 | } 391 | 392 | void MCF::scale_down_linear( float times) 393 | { 394 | // Note: currently not used; 395 | for ( int i = 0; i < no_edge; i++) { 396 | edges[i].length /= times; 397 | edges[i]._Y_e /= times; 398 | } 399 | _phi_latency /= times; 400 | return; 401 | } 402 | 403 | void MCF::scale_down_flows( int phase_count) 404 | { 405 | // scale down final solution; basically averaging over the number 406 | // of phases (iterations of the main big loop of mcf); 407 | int scale = max( 1, phase_count); // this is "t"; 408 | for ( int i = 0; i < no_edge; i ++) { 409 | edges[i].flow /= scale; 410 | for ( int j = 0; j < no_commodity; j ++) { 411 | edges[i]._flows[ j] /= scale; 412 | } 413 | } 414 | } 415 | 416 | double MCF::minimum( double x, double y, double z) 417 | { 418 | double min; 419 | if ( x < y) { 420 | if ( x < z) min = x; 421 | else min = z; 422 | } else { 423 | if ( y < z) min = y; 424 | else min = z; 425 | } 426 | return min; 427 | } 428 | 429 | //////////////////////////////////////////////////////////////////////////////// 430 | // 431 | // MCF actual solver 432 | // 433 | //////////////////////////////////////////////////////////////////////////////// 434 | 435 | int MCF::run_mcf_solver() 436 | { 437 | // it is assumed that the network was already created from file 438 | // or host application; 439 | 440 | 441 | // (1) first, run of MCF solver with the latency constraint 442 | // relaxed to infinity L=1000000 (inf); this is basically 443 | // the "max commodity flow" problem; 444 | // Reminder on MCF flavors: 445 | // -- "max multicommodity flow": total flow summed over all commodities 446 | // is to be maximized; 447 | // -- "max concurrent flow": each commodity si,ti has a demand di; 448 | // objective is to maximize the fraction of the demand that can be shipped 449 | // simultaneously for all commodities; 450 | // -- "min-cost max concurrent flow"; 451 | printf("\nPART 1 - MAX CONCURRENT FLOW (MCF):"); 452 | // flag=0 means that this is a "max commodity flow" run; there is 453 | // no latency constraint/budget; 454 | _lambda_max = mcf( _delta, _epsilon1, 0); // flag=0; 455 | //print_network_demands(true); // exit(1); // debug; 456 | 457 | // early exit if there is no "feasible" solution; 458 | if ( feasibility_check() == false) { 459 | printf("\nWarning: No feasible solution; some edges have capacity "); 460 | printf("\n violation greater than 3*epsilon.\n"); 461 | free_topology(); 462 | exit(1); 463 | } 464 | 465 | // Note: at this time we could simply stop is we were not interested 466 | // in solving this problem such that the minimum latency is also achieved; 467 | // the minimum latency (stored in L) is found via binary search by 468 | // solving repeatedly the so called "min-cost max concurrent flow" problem; 469 | // also note that the solution we have now is most likely different 470 | // from the solution we'll have after the binary search; 471 | // so, if user wants a solution for the problem "max commodity flow" only, 472 | // then stop here; 473 | if ( _problem_type == MCF_TYPE) { 474 | return 1; 475 | } 476 | 477 | 478 | // (2) second, "improved" binary search to refine L; basically we look 479 | // for the minimum latency achievable; during this search mcf is run with 480 | // flag=1, that is as a "min-cost max concurrent flow"; 481 | printf("\n\nPART 2 - BINARY SEARCH FOR L - MIN-COST MAX CONCURRENT FLOW (MCMCF):"); 482 | // maximum latency is as resulted after finding the solution of the 483 | // "max multicommodity flow" problem from PART 1; 484 | _latency_max = _total_latency; // Hu: 1000000; 485 | LL = 0; 486 | UL = _total_latency; // Hu: _latency_max/_lambda_max; 487 | _s = -1; 488 | 489 | int counter = 0; 490 | while ( (UL - LL)/LL > 0.1) { 491 | // (a) set Latency as the middle point between LL and UL; 492 | L = (LL + UL) / 2; 493 | // (b) this call of MCF modifies LL and UL using the 494 | // "interval estimation" technique proposed in Hu paper; 495 | mcf( _delta, _epsilon1, 1); // flag=1; 496 | 497 | // (c) now, if anything goes wrong for some pathological testcase, 498 | // have a brutal exit; this will require debugging; 499 | counter++; 500 | if ( counter >= 512) { 501 | printf("\nError: Binary search of MCMCF took more than 512 iterations."); 502 | printf("\n This is an unusual testcase or the code has a bug.\n"); 503 | free_topology(); 504 | exit(1); 505 | } 506 | } 507 | 508 | //printf("\nLL=%lf, UL=%lf", LL, UL); 509 | //printf("\nFinal latency L=%lf\n", UL); 510 | return 1; 511 | } 512 | 513 | double MCF::mcf( double delta, double epsilon, int flag) 514 | { 515 | // flag: 516 | // 0 -- max concurrent flow; 517 | // 1 -- min-cost max concurrent flow; 518 | 519 | int i,j; 520 | int iter=0; // phase counter: number of iterations of the big main loop; 521 | double lambda=1; // result to be returned; 522 | double D=1, alpha=1, dual=1; 523 | // used to find the amount of flow pushed in each step; 524 | double usable_amount_cap, usable_amount_latency, routed_amount; 525 | // for tracking gap between lambda and dual; 526 | double gap=0.0, old_gap=0.0, old_old_gap=0.0; 527 | 528 | 529 | // () initialization of primal variables (i.e., flows thru all edges) 530 | // and dual valiables PHI_d, Y_e and "length function" l(e) 531 | // of all edges; also resets left_demand to demand for all commodities 532 | // as well as _total_latency; 533 | initialize( delta, flag); 534 | _rd = 1; 535 | for ( i = 0; i < no_node; i++) { 536 | nodes[i].dij_visited = 0; 537 | nodes[i].dij_updated = 0; 538 | nodes[i].min_visited = 0; 539 | } 540 | 541 | 542 | // () the big loop, each run of this loop is a phase; each phase 543 | // has |S| iterations; 544 | while (1) { 545 | 546 | // () in every phase we start with the demand d_j for every commodity; 547 | for ( j = 0; j < no_commodity; j++) { 548 | _commodities[j].left_demand = _commodities[j].demand; 549 | } 550 | 551 | 552 | // () next there are |S| iterations, one for each node that is a 553 | // source for at least a commodity; 554 | for ( i = 0; i < no_node; i++) { 555 | if ( nodes[i].no_comm) { // if this node is source of "r" _commodities; 556 | 557 | int commodities_left = nodes[i].no_comm; 558 | int *dest_flag = (int*)malloc((no_node)*sizeof(int)); 559 | if ( dest_flag == NULL) { 560 | printf("\nError: Unable to malloc .\n"); exit(1); 561 | } 562 | memset((void*)dest_flag,0,(no_node)*sizeof(int)); 563 | // dest_flag is set "1" for nodes that are destinations of _commodities; 564 | for ( j = 0; j < nodes[i].no_comm; j++) { 565 | dest_flag[_commodities[nodes[i].comms[j]].dest] = 1; 566 | } 567 | 568 | 569 | // while there are left commodities to be routed for this node; 570 | // there are a number of steps for current iteration; 571 | int step_count = 0; 572 | while ( commodities_left) { 573 | step_count ++; 574 | 575 | // () compute shortest PATHS tree, where edges have "length(e)"; 576 | // of all paths from this sink to all its destinations; 577 | //print_network_demands( true); // debug; 578 | shortest_paths( nodes[i].id, commodities_left, dest_flag); 579 | 580 | // () reset left_capacities of edges in the tree to the 581 | // initial capacities; u'(e) = u(e), any e in tree; 582 | reset_left_capacities_in_tree( nodes[i].id); 583 | 584 | // () route "f = d(c_q)" units of flow of a given commodity 585 | // and update the flow of each edge: f_e = f_e + f, along its 586 | // shortest path; 587 | bool flow_has_been_routed = false; 588 | for ( j = 0; j < nodes[i].no_comm; j++) { 589 | 590 | // the amount of commodity c_q that has not been routed yet 591 | // at step "s"; 592 | double left_demand = _commodities[nodes[i].comms[j]].left_demand; 593 | 594 | if ( left_demand > 1e-3) { 595 | flow_has_been_routed = true; 596 | //print_backward_shortest_path(_commodities[nodes[i].comms[j]].dest); 597 | 598 | 599 | // available flow amount from bottleneck-edge of shortest path; 600 | // this "c" represents the available minimum capacity of the 601 | // edges on shortest path of this commodity; 602 | usable_amount_cap = min_capacity_this_commodity( 603 | _commodities[nodes[i].comms[j]].dest); 604 | 605 | // available flow amount from latency constraint 606 | if ( flag == 0) { // 0: "max concurrent flow" 607 | usable_amount_latency = 1000000.0; // inf; 608 | } else { // 1: "min-cost max concurrent flow" 609 | // this is L/c(P), where c(P) is is the cost of sending 610 | // one unit of flow along the shortest path: 611 | // Sum_{e in P}{D(e)}, where D(e) is latency of each edge; 612 | usable_amount_latency = check_latency_constraint( 613 | _commodities[nodes[i].comms[j]].dest); 614 | } 615 | 616 | // flow amount to be routed at step "s": f_{i,j,s}^{c_q}; 617 | routed_amount = minimum( 618 | usable_amount_cap, left_demand, usable_amount_latency); 619 | 620 | // update every "_temp_edge_flow" - from dest backward to src 621 | // will be added routed_amount; also update left_capacities 622 | // of edges along the shortest path of this commodity; 623 | route_flow( _commodities[nodes[i].comms[j]].dest, 624 | routed_amount, nodes[i].comms[j]); 625 | 626 | // update commodity amounts to be routed still (i.e., are left); 627 | _commodities[nodes[i].comms[j]].left_demand -= routed_amount; 628 | 629 | if ( _commodities[nodes[i].comms[j]].left_demand <= 1e-3) { 630 | // this commodity is done, set its destination flag to 0; 631 | commodities_left --; 632 | dest_flag[_commodities[nodes[i].comms[j]].dest] = 0; 633 | } 634 | } 635 | }//for ( j = 0; j < nodes[i].no_comm; j++) 636 | 637 | // () update dual variables: Y_e, phi_latency (or PHI_d), 638 | // length(e); 639 | update_dual_variables( nodes[i].id, epsilon, flag); 640 | 641 | _rd++; 642 | if ( !flow_has_been_routed) break; 643 | }//while ( commodities_left) 644 | 645 | 646 | free( dest_flag); 647 | 648 | }//if ( nodes[i].no_comm) 649 | }//for ( i = 0; i < no_node; i++) 650 | 651 | 652 | // () increment phase counter; a phase is an iteration of the big main loop; 653 | iter++; 654 | // additional stopping criterion; 655 | if ( iter >= _scale) break; 656 | //if ( iter >= 80) break; 657 | 658 | // () compute dual and lambda and keep track of the gap between them; 659 | // -- compute dual=D/alpha; 660 | D = compute_D(); 661 | alpha = compute_alpha(); 662 | dual = D / alpha; 663 | 664 | // -- compute lambda; 665 | // Note1: the original code of Hu computed lambda differently; 666 | // this is not in fact lambda in the sense of Karakostas paper, 667 | // but rather an "artificial" variable to make easier its tracking 668 | // towards a value of 1; 669 | //lambda = L / (_total_latency/iter); 670 | // Note2: I now compute it as: lambda=MIN(actual flow/demand) among all commodities; 671 | lambda = compute_lambda(); 672 | lambda /= iter; // consider effect of scaling; 673 | //printf("\n Lambda=%.8f, Dual=D/alpha=%.8f, D=%.8f",lambda,dual,D); 674 | 675 | // -- keep track of gap; 676 | old_old_gap = old_gap; 677 | old_gap = gap; 678 | gap = dual/lambda - 1; 679 | 680 | 681 | // () this implements the "interval estimation"; see Theorem 3 of Hu paper; 682 | if ( flag == 1) { 683 | double UL1 = UL, LL1 = LL; 684 | double d = dual; 685 | //if (d < 1) d = 1; 686 | double s1 = (_latency_max - L)/(_lambda_max - d); 687 | if ( s1 > 0 && ( _s < 0 || _s > s1)) { 688 | _s = s1; 689 | } 690 | if ( _s > 0) { 691 | if ( lambda < 1) { 692 | UL1 = L + (1 - lambda) * _s; 693 | if ( UL1 < UL) UL = UL1; 694 | } 695 | if ( dual > 1) { 696 | LL1 = L - (dual - 1) * _s; 697 | if ( LL1 > LL) LL = LL1; 698 | } 699 | } 700 | if ( lambda > 1) { UL = L; } 701 | if ( dual < 1) { LL = L; } 702 | if ( (UL-LL < 0) || (UL/LL - 1) < 0.01) { break; } 703 | if ( D >= 1) { break; } 704 | } else { // 0 705 | // for "max commodity flow" case, the stopping criterion is 706 | // "D>=1"; see Karakostas paper; 707 | // Note1: original code of Hu used "dual/lambda-1= 1) { break; } 709 | } 710 | 711 | }//while (1) 712 | 713 | 714 | // () scale down the final flows so that the solution is feasible 715 | // (that is, capacities are met); 716 | scale_down_flows( iter); 717 | // also, record final latency, which must consider scaling too; 718 | _total_latency = _total_latency / iter; 719 | 720 | 721 | // () entertain user; 722 | printf("\nlambda = %lf, dual = %lf, [%lf, %lf], L=%lf, iter=%d", 723 | lambda, D/alpha, LL, UL, L, iter); 724 | 725 | return lambda; 726 | } 727 | 728 | //////////////////////////////////////////////////////////////////////////////// 729 | // 730 | // MCF Dijkstra 731 | // 732 | //////////////////////////////////////////////////////////////////////////////// 733 | 734 | void MCF::shortest_paths( int s, int num_commodities, int *dest_flag) 735 | { 736 | // implements Dijkstra's all paths shortest path algorithm; 737 | // num_commodities is the number of commodities that still need 738 | // routing for this source; 739 | 740 | int num_commodities_to_process = num_commodities; 741 | PQDATUM wf, wf1; // WAVEFRONTS; 742 | 743 | PQUEUE pq; 744 | int *pos = (int *)malloc(sizeof(int) * (no_node)); 745 | if ( pos == NULL) { 746 | printf("\nError: Unable to malloc .\n"); exit (1); 747 | } 748 | 749 | pq.pqinit( 400); // 400 is just a constant; 750 | // reset dist of all nodes; 751 | for ( int i = 0; i < no_node; i++) { 752 | nodes[i].dist = DBL_MAX; 753 | } 754 | // source "s" resets; 755 | nodes[s].pre = -1; 756 | nodes[s].pre_edge = -1; 757 | nodes[s].dist = 0.0; 758 | 759 | wf.set_node( s); // sourse "s"; 760 | wf.set_dist( 0.0); 761 | 762 | pq.pqinsert( wf, pos); 763 | 764 | while ( !pq.pqempty()) { 765 | int v, w; 766 | 767 | // retreive the shortest non-visited node; 768 | pq.pqremove( &wf1, pos); 769 | v = wf1.node(); 770 | if ( dest_flag[v]) num_commodities_to_process--; 771 | // break when all shortest paths to all destinations from source "s" 772 | // have been found; 773 | if ( num_commodities_to_process <= 0) break; 774 | 775 | nodes[v].dij_visited = _rd; 776 | for ( int i = 0; i < nodes[v].no_edge; i++) { 777 | w = edges[nodes[v].edges[i]].dest; 778 | if ( nodes[w].dij_visited != _rd) 779 | if ( nodes[w].dij_updated != _rd || 780 | nodes[w].dist > wf1.dist() + edges[nodes[v].edges[i]].length) { 781 | nodes[w].pre = v; 782 | nodes[w].pre_edge = nodes[v].edges[i]; 783 | nodes[w].dist = wf1.dist() + edges[nodes[v].edges[i]].length; 784 | wf.set_node( w); 785 | wf.set_dist( nodes[w].dist); 786 | if (nodes[w].dij_updated != _rd) { 787 | pq.pqinsert( wf, pos); 788 | } else { 789 | pq.pqdeckey( wf, pos); 790 | } 791 | nodes[w].dij_updated = _rd; 792 | } 793 | } 794 | } 795 | pq.pqfree( pos); // clean up; 796 | } 797 | 798 | //////////////////////////////////////////////////////////////////////////////// 799 | // 800 | // MCF network graph 801 | // 802 | //////////////////////////////////////////////////////////////////////////////// 803 | 804 | bool MCF::build_network_from_host_application() 805 | { 806 | // used from inside the host application that hosts also the floorplanner 807 | // and the VNOC1 NoC simulator; 808 | // you should implement this based on how your host application looks like; 809 | // the idea is to populate the MCF object similarly to how I do it inside 810 | // read_network_topology_and_demands(); 811 | 812 | return true; 813 | } 814 | 815 | bool MCF::build_network_from_file( double latency_limit, double rate) 816 | { 817 | // rate is the demand coefficient (injection rate): 0.05, 0.1, 0.15, 0.2, 0.25; 818 | // latency_limit and rate are not used; 819 | 820 | FILE *fp; // file pointer for network file; 821 | 822 | // (1) import the network topology and the demands; 823 | if (( fp = fopen( _network_filename, "r"))) { 824 | read_network_topology_and_demands( fp); 825 | } else { 826 | printf("Error: Can not open file: %s\n", _network_filename); exit(1); 827 | } 828 | 829 | // (2) cleanup; 830 | fclose( fp); 831 | 832 | // (3) one time initialization of parameters (of those not set by 833 | // user via command line arguments); 834 | init_param(); 835 | 836 | return true; 837 | } 838 | 839 | void MCF::read_network_topology_and_demands( FILE *fp, double rate) 840 | { 841 | // Note: I assume that network file is correct; I do not do sanity 842 | // checks for the time being; 843 | // I "made-up" this format for easy parsing; you may want to change 844 | // to fit your application; example of .network format: 845 | // 846 | // 8 <-- num of nodes 847 | // 0 100 300 <-- node id, (x,y) location in um 848 | // 1 100 100 849 | // 2 200 300 850 | // 3 200 100 851 | // 4 300 300 852 | // 5 300 100 853 | // 6 400 300 854 | // 7 400 100 855 | // 12 <-- num of edges 856 | // 0 0 2 10.0 2.00 <-- id, src, des, capacity, delay 857 | // 1 2 4 10.0 2.00 858 | // 2 3 2 10.0 6.00 859 | // 3 1 3 10.0 2.00 860 | // 4 3 5 10.0 2.00 861 | // 5 2 3 10.0 6.00 862 | // 6 4 2 10.0 2.00 863 | // 7 5 3 10.0 2.00 864 | // 8 5 4 10.0 6.00 865 | // 9 4 5 10.0 6.00 866 | // 10 4 6 10.0 2.00 867 | // 11 5 7 10.0 2.00 868 | // 2 <-- num of demands (commodities) 869 | // 0 0 7 0.577004 <-- id src des amount 870 | // 1 1 6 1.777268 871 | 872 | int id, x, y, src, dest; 873 | double delay, capacity; 874 | 875 | // (1) nodes 876 | fscanf(fp,"%d", &no_node); 877 | nodes = (NODE*)malloc(sizeof(NODE) * (no_node)); 878 | if ( nodes == NULL) { 879 | printf("\nError: Unable to malloc .\n"); exit(1); 880 | } 881 | for ( int i = 0; i < no_node; i++) { 882 | fscanf(fp, "%d %d %d", &id, &x, &y); 883 | nodes[i].id = id; 884 | nodes[i].x = x; 885 | nodes[i].y = y; 886 | nodes[i].pre = -1; 887 | nodes[i].dist = DBL_MAX; 888 | nodes[i].no_comm = 0; 889 | nodes[i].comms = NULL; 890 | nodes[i].no_edge = 0; 891 | nodes[i].dij_visited = 0; 892 | nodes[i].dij_updated = 0; 893 | nodes[i].min_visited = 0; 894 | 895 | // here we work with a fixed pre-allocation; not optimal; we should 896 | // allocate only as much as needed; also in this way we have to make 897 | // sure there will be no nodes with a bigger degree than MAX_DEGREE=40; 898 | // TO DO: this must be fixed as it's ugly programming; 899 | nodes[i].edges = (int *)malloc(sizeof(int) * MAX_DEGREE); 900 | if ( nodes[i].edges == NULL) { 901 | printf("\nError: Unable to malloc .\n"); exit(1); 902 | } 903 | } 904 | 905 | // (2) edges 906 | fscanf(fp,"%d", &no_edge); 907 | edges = (EDGE *)malloc(sizeof(EDGE) * (no_edge)); 908 | if ( edges == NULL) { 909 | printf("\nError: Unable to malloc .\n"); exit(1); 910 | } 911 | _temp_edge_flow = (double*)malloc(sizeof(double) * (no_edge)); 912 | if ( _temp_edge_flow == NULL) { 913 | printf("\nError: Unable to malloc <_temp_edge_flow>.\n"); exit(1); 914 | } 915 | for ( int i = 0; i < no_edge; i++) { 916 | fscanf(fp, "%d %d %d %lf %lf",&id, &src,&dest, &capacity, &delay); 917 | edges[i].id = id; 918 | edges[i].src = src; 919 | edges[i].dest = dest; 920 | edges[i].capacity = capacity; 921 | edges[i].left_capacity = capacity; 922 | edges[i].latency = delay; 923 | edges[i].length = 0.0; 924 | 925 | edges[i].flow = 0.0; 926 | edges[i]._flows = NULL; 927 | } 928 | 929 | // (3) record adjacent edges for each node; 930 | for ( int i = 0; i < no_edge; i++) { 931 | int index = edges[i].src; 932 | nodes[index].edges[nodes[index].no_edge] = edges[i].id; 933 | nodes[index].no_edge++; 934 | } 935 | 936 | // (4) read demands/commodities; 937 | double amount; 938 | fscanf(fp,"%d", &no_commodity); 939 | _commodities = (COMMODITY *)malloc(sizeof(COMMODITY) * (no_commodity)); 940 | if ( _commodities == NULL) { 941 | printf("\nError: Unable to malloc <_commodities>.\n"); exit(1); 942 | } 943 | for ( int i = 0; i < no_commodity; i++) { 944 | fscanf(fp,"%d %d %d %lf", &id, &src, &dest, &amount); 945 | _commodities[i].id = id; 946 | _commodities[i].src = src; 947 | _commodities[i].dest = dest; 948 | _commodities[i].demand = amount * rate; // rate = 1 by default; 949 | _commodities[i].left_demand = amount; 950 | 951 | if (nodes[src].comms == NULL) { 952 | nodes[src].comms = (int *)malloc(sizeof(int) * no_node); 953 | if ( nodes[src].comms == NULL) { 954 | printf("\nError: Unable to malloc .\n"); exit(1); 955 | } 956 | } 957 | nodes[src].comms[nodes[src].no_comm] = i; 958 | nodes[src].no_comm++; 959 | } 960 | 961 | // (5) reset; 962 | for ( int i = 0; i < no_edge; i++) { 963 | // Note1: I had to delay this allocation because no_commodity had 964 | // not been set yet; 965 | edges[i]._flows = (double *)malloc(sizeof(double) * (no_commodity)); 966 | if ( edges[i]._flows == NULL) { 967 | printf("\nError: Unable to malloc .\n"); exit(1); 968 | } 969 | for ( int j = 0; j < no_commodity; j++) { 970 | edges[i]._flows[j] = 0.0; 971 | } 972 | } 973 | for ( int i = 0; i < no_node; i++) { 974 | // Note2: same as above; 975 | nodes[i]._preferred_path = (int *)malloc(sizeof(int) * no_commodity); 976 | if ( nodes[i]._preferred_path == NULL) { 977 | printf("\nError: Unable to malloc <_preferred_path>.\n"); exit(1); 978 | } 979 | for ( int j = 0; j < no_commodity; j++) { 980 | nodes[i]._preferred_path[j] = -1; 981 | } 982 | } 983 | 984 | //print_network_demands(); exit(1); // debug; 985 | 986 | return; 987 | } 988 | 989 | void MCF::free_topology() 990 | { 991 | int i=0; 992 | 993 | free( _commodities); 994 | 995 | for ( i = 0; i < no_edge; i++) { 996 | free( edges[i]._flows); 997 | } 998 | free( edges); 999 | free( _temp_edge_flow); 1000 | 1001 | for ( i = 0; i < no_node; i++) { 1002 | free( nodes[i].comms); 1003 | free( nodes[i].edges); 1004 | } 1005 | free( nodes); 1006 | 1007 | return; 1008 | } 1009 | 1010 | //////////////////////////////////////////////////////////////////////////////// 1011 | // 1012 | // debug utils; 1013 | // 1014 | //////////////////////////////////////////////////////////////////////////////// 1015 | 1016 | void MCF::print_network_demands( bool print_only_edges) 1017 | { 1018 | printf("\n\nNetwork and demands:"); 1019 | printf("\nNodes %d",no_node); 1020 | if ( print_only_edges == false) { 1021 | for ( int i = 0; i < no_node; i++) { 1022 | printf("\n %d (%d %d)", nodes[i].id, nodes[i].x, nodes[i].y); 1023 | printf(" num_commodities=%d dist=%lf",nodes[i].no_comm,nodes[i].dist); 1024 | printf("\n "); 1025 | for ( int k = 0; k < nodes[i].no_edge; k++) { 1026 | printf(" %d", nodes[i].edges[k]); 1027 | } 1028 | } 1029 | } 1030 | printf("\nEdges %d",no_edge); 1031 | for ( int i = 0; i < no_edge; i++) { 1032 | //printf("\n %d %d -> %d cap: %.2lf Y_e: %.2lf len: %.2lf flow: %.2lf breakdown:", 1033 | // edges[i].id, edges[i].src, edges[i].dest, 1034 | // edges[i].capacity, edges[i]._Y_e, 1035 | // edges[i].length, edges[i].flow); 1036 | printf("\n %d %d -> %d cap: %.2lf flow: %.2lf breakdown:", 1037 | edges[i].id, edges[i].src, edges[i].dest, 1038 | edges[i].capacity, edges[i].flow); 1039 | for ( int j = 0; j < no_commodity; j++) { 1040 | printf(" %.2lf", edges[i]._flows[ j]); 1041 | } 1042 | } 1043 | if ( print_only_edges == false) { 1044 | printf("\nDemands/commodities %d",no_commodity); 1045 | for ( int i = 0; i < no_commodity; i++) { 1046 | printf("\n %d %d -> %d demand=%lf portion_unsatisfied=%lf", _commodities[i].id, 1047 | _commodities[i].src, _commodities[i].dest, 1048 | _commodities[i].demand, // amount * rate 1049 | _commodities[i].left_demand); // amount 1050 | } 1051 | } 1052 | printf("\n"); 1053 | } 1054 | 1055 | void MCF::print_routing_paths() 1056 | { 1057 | // call only after a call of do_randomized_rounding(); 1058 | printf("\nRandomized rounded paths:"); 1059 | 1060 | for ( int i = 0; i < no_commodity; i++) { 1061 | printf("\nCommodity %d: %d -> %d: ", i, 1062 | _commodities[i].src, _commodities[i].dest); 1063 | 1064 | int src_id = _commodities[i].src; 1065 | while ( src_id != _commodities[i].dest) { 1066 | printf(" %d", src_id); 1067 | src_id = nodes[src_id]._preferred_path[i]; 1068 | } 1069 | printf(" %d", src_id); // dest; 1070 | } 1071 | printf("\n"); 1072 | } 1073 | 1074 | void MCF::print_backward_shortest_path( int dest) 1075 | { 1076 | // debug only; 1077 | int t = dest; 1078 | printf("\n"); 1079 | while ( nodes[t].pre != -1) { 1080 | printf(" %d ->", t); 1081 | t = nodes[t].pre; 1082 | } printf(" %d ", t); 1083 | } 1084 | 1085 | //////////////////////////////////////////////////////////////////////////////// 1086 | // 1087 | // MCF randomized rounding; 1088 | // 1089 | //////////////////////////////////////////////////////////////////////////////// 1090 | 1091 | bool MCF::do_randomized_rounding() 1092 | { 1093 | // after mcf_solver finds a fractional flow solution, we do a 1094 | // randomized rounding to set only one path for each commodity; 1095 | // otherwise the commodities would traverse multiple paths that 1096 | // would translate in path splitting for packets which would require 1097 | // router architecture modification too much and re-ordering of packets 1098 | // at destination; 1099 | 1100 | for ( int i = 0; i < no_commodity; i++) { 1101 | // () for each commodity we start from its source and traverse 1102 | // downstream nodes; at each step we pick up the node that has 1103 | // the largest fraction of this commodity as the preferred path; 1104 | // record this preferred path in the preferred_path array; 1105 | 1106 | int src_id = _commodities[i].src; 1107 | while ( src_id != _commodities[i].dest) { 1108 | // recursively travel from src to dest searching for maximum 1109 | // fractional flow; 1110 | 1111 | int id_max_flow_fraction = -1; 1112 | double max_flow_fraction = 0.0; 1113 | for ( int k = 0; k < nodes[ src_id].no_edge; k++) { 1114 | // for each adjacent edge look at the commodity index "i", 1115 | // and seek the edge id with maximum flow fraction; 1116 | 1117 | int edge_id = nodes[ src_id].edges[k]; 1118 | if ( max_flow_fraction < edges[edge_id]._flows[ i]) { 1119 | max_flow_fraction = edges[edge_id]._flows[ i]; 1120 | id_max_flow_fraction = edge_id; 1121 | } 1122 | } 1123 | assert(id_max_flow_fraction >= 0 & id_max_flow_fraction < no_edge); 1124 | 1125 | // () record the neighbor node id as the downstream node of the 1126 | // preferred path for this commodity; that is, along the edge with 1127 | // max fraction of flow from current node for this commodity; 1128 | nodes[src_id]._preferred_path[i] = edges[id_max_flow_fraction].dest; 1129 | 1130 | // prepare for next iter; 1131 | src_id = nodes[src_id]._preferred_path[i]; 1132 | } 1133 | } 1134 | 1135 | return true; 1136 | } 1137 | -------------------------------------------------------------------------------- /mcf.h: -------------------------------------------------------------------------------- 1 | #ifndef _MCF_H_ 2 | #define _MCF_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | using namespace std; 11 | 12 | #define MAX_DEGREE 40 13 | 14 | // MCF: "max commodity flow"; MCMCF: "min-cost max concurrent flow" 15 | enum PROBLEM_TYPE { MCF_TYPE = 0, MCMCF_TYPE = 1 }; 16 | 17 | //////////////////////////////////////////////////////////////////////////////// 18 | // 19 | // NODE 20 | // 21 | //////////////////////////////////////////////////////////////////////////////// 22 | 23 | class NODE 24 | { 25 | private: 26 | 27 | public: 28 | int id; // start from 0 29 | int x, y; // location coordinates directly in um; 30 | int pre; // parent node in shortest path tree 31 | int pre_edge; // parent edge in shortest path tree 32 | double dist; // distance for shortest path algorithm 33 | int no_comm; // number of destinations for commodities starting from this node 34 | int *comms; // list of commodities starting from the node 35 | int no_edge; // number of edges incident to the node 36 | int *edges; // list of edges incident to the node 37 | int dij_visited; // flag for Dijkstra algo 38 | int dij_updated; // second flag for Dijkstra algo 39 | int min_visited; // flag for searching min c(e) 40 | // _preferred_path has a number of elements equal to the number of 41 | // commodities; each entry stores the next, downstream, node index 42 | // of the preferred unique path of the flow of this commodity from 43 | // src toward des; this is constructed during the randomized rounding; 44 | int *_preferred_path; 45 | public: 46 | NODE() {} 47 | ~NODE() {} 48 | }; 49 | 50 | //////////////////////////////////////////////////////////////////////////////// 51 | // 52 | // EDGE 53 | // 54 | //////////////////////////////////////////////////////////////////////////////// 55 | 56 | class EDGE 57 | { 58 | private: 59 | 60 | public: 61 | int id; // start from 0 62 | int src, dest; // source and destination node id 63 | double latency; // delay of this edge; will play role of cost; 64 | double length; 65 | // _flows has a number of elements equal to the number of demands/ 66 | // commodities with the index being the id of demand; 67 | double *_flows; 68 | double flow; // accumulated total flow; 69 | double capacity; // c_e 70 | double left_capacity; 71 | // dual of capacity; 72 | double _Y_e; 73 | double _old_Y_e; 74 | 75 | public: 76 | EDGE() {} 77 | ~EDGE() {} 78 | 79 | void set_flow_of_commodity( int id, double flow) { _flows[ id] = flow; } 80 | void add_to_flow_of_commodity( int id, double val) { _flows[ id] += val; } 81 | double flow_of_commodity( int id) { return _flows[ id]; } 82 | }; 83 | 84 | //////////////////////////////////////////////////////////////////////////////// 85 | // 86 | // COMMODITY 87 | // 88 | //////////////////////////////////////////////////////////////////////////////// 89 | 90 | class COMMODITY 91 | { 92 | public: 93 | int id; // start from 0 94 | int src, dest; // source and destination 95 | double demand; 96 | double left_demand; 97 | 98 | public: 99 | COMMODITY() {} 100 | ~COMMODITY() {} 101 | }; 102 | 103 | //////////////////////////////////////////////////////////////////////////////// 104 | // 105 | // MCF acts as a host to the graph and methods such as Dijkstra and solver; 106 | // 107 | //////////////////////////////////////////////////////////////////////////////// 108 | 109 | class MCF 110 | { 111 | private: 112 | // Note: "inherited" like this from the original code; I should 113 | // make them vectors; 114 | int no_node; 115 | NODE *nodes; 116 | int no_edge; 117 | EDGE *edges; 118 | int no_cut; 119 | int no_commodity; 120 | COMMODITY *_commodities; 121 | 122 | // primal variables are the actual final flows through edges of graph; 123 | // their values are stored in _flows of EDGE class; 124 | // dual variable PHI_d; the other dual variables are Y_e 125 | // and the "length function" (see Ababei paper); 126 | double _phi_latency; 127 | // L is used to record the minimum latency achievable; utilized as 128 | // budget in the MCF problem formulation; found out via binary search; 129 | double L, LL, UL; // L is latency budget; LL/UP is lower/upper latency; 130 | 131 | // lambda_max is the first lambda that mcf() returns, with initial 132 | // latency_limit relaxed to 1000000 (inf); 133 | double _lambda_max; 134 | double _latency_max; // associated with lambda_max; 135 | double _total_latency; 136 | 137 | // control variables; 138 | double _delta; 139 | double _epsilon1; 140 | double _scale; 141 | // s = [P(lambda_max) - L]/[lambda_max - dual]; see eq. 9 of Hu 142 | // paper; used to implement "interval estimation"; 143 | double _s; 144 | // temp_edge_flow stores how much flow is routed during an iteration; 145 | // it is a sketch array; 146 | double *_temp_edge_flow; 147 | int _rd; 148 | int _min_rd; 149 | public: 150 | // arguments; 151 | PROBLEM_TYPE _problem_type; 152 | char _network_filename[512]; 153 | 154 | public: 155 | MCF() { 156 | no_node = 0; 157 | no_edge = 0; 158 | no_commodity = 0; 159 | 160 | L=0; LL=0; UL=0; 161 | _phi_latency = 0; 162 | _lambda_max = 0; 163 | _latency_max = 0; 164 | _total_latency = 0; 165 | 166 | _delta = 1.0; 167 | _epsilon1 = 0.1; 168 | _scale = 1; 169 | _s = -1; 170 | _min_rd = 0; 171 | _rd = 0; 172 | _temp_edge_flow = 0; 173 | _problem_type = MCMCF_TYPE; 174 | } 175 | ~MCF() {} 176 | 177 | // utils; 178 | double flow_of_commodity_thru_edge( int c_id, int e_id) { 179 | //assert(c_id >= 0 && c_id < no_commodity); 180 | //assert(e_id >= 0 && e_id < no_edge); 181 | return edges[ e_id].flow_of_commodity( c_id); 182 | } 183 | double get_L() const { return L; } 184 | int problem_type() const { return _problem_type; } 185 | 186 | // host; 187 | void initialize( double delta, int flag); 188 | bool parse_options( int argc, char **argv); 189 | bool feasibility_check(); 190 | double compute_D(); 191 | double compute_alpha(); 192 | double compute_lambda(); 193 | void route_flow( int t, double amount, int commodity_id); 194 | double check_latency_constraint( int t); 195 | double min_capacity( int s); 196 | double min_capacity_this_commodity( int dest); 197 | void reset_left_capacities_in_tree( int s); 198 | void update_dual_variables( int s, double epsilon, int flag); 199 | double minimum( double x, double y, double z); 200 | void scale_down_linear( float times); 201 | void scale_down_flows( int phase_count); 202 | 203 | // Dijkstra; 204 | void shortest_paths(int s, int num_commodities, int *dest_flag); 205 | 206 | // MCF solver; 207 | void init_param(); 208 | int run_mcf_solver(); 209 | double mcf( double delta, double epsilon, int flag); 210 | bool do_randomized_rounding(); 211 | 212 | // graph related utilities; 213 | bool build_network_from_file(double latency_limit=1000000, double rate=1); 214 | bool build_network_from_host_application(); 215 | void read_network_topology_and_demands( FILE *fp, double rate=1); 216 | void free_topology(); 217 | void print_network_demands( bool print_only_edges = false); 218 | void print_backward_shortest_path( int t); 219 | void print_routing_paths(); 220 | }; 221 | 222 | #endif 223 | -------------------------------------------------------------------------------- /mcf_solver: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eigenpi/mcf_solver/9bf618449e9ca69ea686e96898e93a485e959f97/mcf_solver -------------------------------------------------------------------------------- /pqueue.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "pqueue.h" 5 | 6 | using namespace std; 7 | 8 | //////////////////////////////////////////////////////////////////////////////// 9 | // 10 | // PQUEUE 11 | // 12 | //////////////////////////////////////////////////////////////////////////////// 13 | 14 | PQUEUE *PQUEUE::pqinit( int n) 15 | { 16 | // initialize the queue; 17 | _d = (PQDATUM *)malloc(sizeof(PQDATUM) * n); 18 | if ( _d == NULL) { 19 | printf("\nError: Unable to realloc .\n"); 20 | exit (1); 21 | } 22 | 23 | _avail = _step = n; 24 | _size = 1; 25 | return ( this); // return a pointer to itself; 26 | } 27 | 28 | int PQUEUE::pqinsert( PQDATUM a_d, int *pos) 29 | { 30 | // insert an item into the queue; 31 | // return 1 if item was inserted; 0 otherwise; 32 | 33 | PQDATUM *tmp; 34 | int i, newsize; 35 | 36 | if ( _size == -1) return 0; // pqueue was not initialized first; 37 | 38 | // (1) allocate more memory if necessary; 39 | if ( _size >= _avail) { 40 | newsize = _size + _step; 41 | tmp = (PQDATUM *)realloc( _d, sizeof(PQDATUM) * newsize); 42 | if ( tmp == NULL) { 43 | printf("\nError: Unable to realloc .\n"); 44 | exit (1); 45 | //return 0; 46 | } 47 | _d = tmp; // redundant; 48 | _avail = newsize; 49 | } 50 | 51 | // (2) insert item; 52 | i = _size++; 53 | while ( i > 1 && get_distance( _d[i / 2]) > get_distance( a_d)) { 54 | _d[i] = _d[i / 2]; 55 | pos[ _d[i].node()] = i; 56 | i /= 2; 57 | } 58 | _d[i] = a_d; 59 | pos[ _d[i].node()] = i; 60 | return 1; 61 | } 62 | 63 | PQDATUM *PQUEUE::pqremove( PQDATUM *a_d, int *pos) 64 | { 65 | // remove the highest-ranking item from the queue; 66 | // a_d: pointer to the PQDATUM variable that will hold the 67 | // datum corresponding to the queue item removed; 68 | // return value: 69 | // >= 0 an item has been removed. The variable that d points 70 | // to now contains the datum associated with the item in question; 71 | // -1 no item could be removed. Either the queue pointer 72 | // provided was NULL, or the queue was empty. The chunk 73 | // of memory that d points to has not been modified. 74 | 75 | PQDATUM tmp; 76 | int i = 1, j; 77 | 78 | if ( _size == -1 || _size == 1) return NULL; 79 | 80 | *a_d = _d[1]; 81 | tmp = _d[ --_size]; 82 | while (i <= _size / 2) { 83 | j = 2 * i; 84 | if ( j < _size && get_distance(_d[j]) > get_distance(_d[j + 1])) { 85 | j++; 86 | } 87 | if ( get_distance(_d[j]) >= get_distance(tmp)) { 88 | break; 89 | } 90 | _d[i] = _d[j]; 91 | pos[ _d[i].node()] = i; 92 | i = j; 93 | } 94 | _d[i] = tmp; 95 | pos[ _d[i].node()] = i; 96 | return a_d; 97 | } 98 | 99 | int PQUEUE::pqdeckey( PQDATUM a_d, int *pos) 100 | { 101 | int i = 0; 102 | 103 | if ( _size == -1) return 0; // pqueue was not initialized first; 104 | 105 | i = pos[ a_d.node()]; 106 | if ( _d[i].node() != a_d.node()) 107 | printf("wrong\n"); 108 | while ( i > 1 && get_distance(_d[i / 2]) > get_distance(a_d)) { 109 | _d[i] = _d[i / 2]; 110 | pos[ _d[i].node()] = i; 111 | i /= 2; 112 | } 113 | _d[i] = a_d; 114 | pos[ _d[i].node()] = i; 115 | return 1; 116 | } 117 | 118 | PQDATUM *PQUEUE::pqpeek( PQDATUM *a_d) 119 | { 120 | // access highest-ranking item without removing it; 121 | // a_d: pointer to the PQDATUM variable that will hold the 122 | // datum corresponding to the highest-ranking item; 123 | // return value: 124 | // >= 0 Success. The variable that d points to now contains 125 | // the datum associated with the highest-ranking item. 126 | // -1 Failure. Either the queue pointer provided was NULL, 127 | // or the queue was empty. The chunk of memory that d 128 | // points to has not been modified. 129 | 130 | if ( _size == -1 || _size == 1) return NULL; 131 | 132 | *a_d = _d[1]; 133 | return a_d; 134 | } 135 | -------------------------------------------------------------------------------- /pqueue.h: -------------------------------------------------------------------------------- 1 | #ifndef _PQUEUE_H_ 2 | #define _PQUEUE_H_ 3 | 4 | #include 5 | 6 | using namespace std; 7 | 8 | //////////////////////////////////////////////////////////////////////////////// 9 | // 10 | // PQUEUE 11 | // 12 | //////////////////////////////////////////////////////////////////////////////// 13 | 14 | class PQDATUM 15 | { 16 | private: 17 | int _node; 18 | double _dist; 19 | public: 20 | PQDATUM() { _node = -1; _dist = -1; } 21 | ~PQDATUM() {} 22 | 23 | int node() { return _node; } 24 | double dist() { return _dist; } 25 | void set_node(int node) { _node = node; } 26 | void set_dist(double dist) { _dist = dist; } 27 | }; 28 | 29 | class PQUEUE 30 | { 31 | private: 32 | int _size, _avail, _step; 33 | PQDATUM *_d; 34 | public: 35 | PQUEUE() { _size = -1; _avail = -1; _step = -1; } 36 | ~PQUEUE() {} 37 | 38 | int size() { return _size; } 39 | int avail() { return _avail; } 40 | int step() { return _step; } 41 | PQDATUM *d() { return _d; } 42 | PQUEUE *pqinit(int n); 43 | int pqinsert( PQDATUM a_d, int *pos); 44 | PQDATUM *pqremove( PQDATUM *a_d, int *pos); 45 | int pqdeckey( PQDATUM a_d, int *pos); 46 | PQDATUM *pqpeek( PQDATUM *a_d); 47 | double get_distance( PQDATUM d) { return ( d.dist()); } 48 | int pqempty() { return ( _size == 1); } 49 | void pqfree( int *pos) { 50 | free( _d); 51 | free( pos); 52 | } 53 | }; 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /tests/README.txt: -------------------------------------------------------------------------------- 1 | I "made-up" this format for easy parsing; you may want to change 2 | to fit your application; example of .network format: 3 | 4 | 8 <-- num of nodes 5 | 0 100 300 <-- node id, (x,y) location in um; location is not used currently for anything; 6 | 1 100 100 7 | 2 200 300 8 | 3 200 100 9 | 4 300 300 10 | 5 300 100 11 | 6 400 300 12 | 7 400 100 13 | 12 <-- num of edges 14 | 0 0 2 10.0 2.00 <-- id, src, des, capacity, delay (as cost) 15 | 1 2 4 10.0 2.00 16 | 2 3 2 10.0 6.00 17 | 3 1 3 10.0 2.00 18 | 4 3 5 10.0 2.00 19 | 5 2 3 10.0 6.00 20 | 6 4 2 10.0 2.00 21 | 7 5 3 10.0 2.00 22 | 8 5 4 10.0 6.00 23 | 9 4 5 10.0 6.00 24 | 10 4 6 10.0 2.00 25 | 11 5 7 10.0 2.00 26 | 2 <-- num of demands (commodities) 27 | 0 0 7 0.577004 <-- id src des amount 28 | 1 1 6 1.777268 29 | -------------------------------------------------------------------------------- /tests/toy1.network: -------------------------------------------------------------------------------- 1 | 6 2 | 0 10 20 3 | 1 20 20 4 | 2 20 10 5 | 3 30 10 6 | 4 30 20 7 | 5 40 10 8 | 6 9 | 0 0 1 10 1 10 | 1 1 2 10 1 11 | 2 2 3 10 1 12 | 3 3 4 10 1 13 | 4 1 4 10 1000 14 | 5 4 5 10 1 15 | 1 16 | 0 0 5 10 17 | -------------------------------------------------------------------------------- /tests/toy2.network: -------------------------------------------------------------------------------- 1 | 4 2 | 0 10 30 3 | 1 10 10 4 | 2 20 30 5 | 3 20 10 6 | 4 7 | 0 0 1 10 1 8 | 1 0 2 10 1 9 | 2 2 3 10 1 10 | 3 1 3 10 1 11 | 2 12 | 0 0 3 10 13 | 1 0 2 10 14 | -------------------------------------------------------------------------------- /tests/toy3.network: -------------------------------------------------------------------------------- 1 | 8 2 | 0 10 10 3 | 1 10 10 4 | 2 10 10 5 | 3 10 10 6 | 4 10 10 7 | 5 10 10 8 | 6 10 10 9 | 7 10 10 10 | 12 11 | 0 0 1 10 0.01 12 | 1 1 2 10 0.01 13 | 2 2 3 10 0.01 14 | 3 3 4 10 0.01 15 | 4 0 5 10 1 16 | 5 5 6 10 1 17 | 6 6 7 10 1 18 | 7 7 4 10 1 19 | 8 1 6 10 1 20 | 9 5 2 10 1 21 | 10 2 7 10 1 22 | 11 6 3 10 1 23 | 1 24 | 0 0 4 10 25 | 26 | --------------------------------------------------------------------------------