001/******************************************************************************* 002 * Copyright (c) 2017 Pablo Pavon Marino and others. 003 * All rights reserved. This program and the accompanying materials 004 * are made available under the terms of the 2-clause BSD License 005 * which accompanies this distribution, and is available at 006 * https://opensource.org/licenses/BSD-2-Clause 007 * 008 * Contributors: 009 * Pablo Pavon Marino and others - initial API and implementation 010 *******************************************************************************/ 011package com.net2plan.examples.ocnbook.onlineSim; 012 013 014import cern.colt.matrix.tdouble.DoubleMatrix1D; 015import cern.colt.matrix.tdouble.DoubleMatrix2D; 016import cern.colt.matrix.tint.IntFactory3D; 017import cern.colt.matrix.tint.IntMatrix3D; 018import cern.jet.random.tdouble.Poisson; 019import com.jom.OptimizationProblem; 020import com.net2plan.interfaces.networkDesign.*; 021import com.net2plan.interfaces.simulation.IEventProcessor; 022import com.net2plan.interfaces.simulation.SimEvent; 023import com.net2plan.utils.Constants.RoutingType; 024import com.net2plan.utils.InputParameter; 025import com.net2plan.utils.Pair; 026import com.net2plan.utils.TimeTrace; 027import com.net2plan.utils.Triple; 028 029import java.io.File; 030import java.io.FileOutputStream; 031import java.io.PrintStream; 032import java.util.*; 033 034/** 035 * This module implements a distributed dual-gradient based algorithm for adapting the network routing to the one which minimizes the average number of hops, that results in a purely decentralized backpressure scheme. 036 * 037 * Ths event processor is adapted to permit observing the algorithm performances under user-defined conditions, 038 * including asynchronous distributed executions, where signaling can be affected by losses and/or delays, and/or measurement errors. 039 * The time evolution of different metrics can be stored in output files, for later processing. 040 * As an example, see the <a href="../../../../../../graphGeneratorFiles/fig_sec10_3_backpressureRoutingDual.m">{@code fig_sec10_3_backpressureRoutingDual.m}</a> MATLAB file used for generating the graph/s of the case study in the 041 * <a href="http://eu.wiley.com/WileyCDA/WileyTitle/productCd-1119013356.html">book</a> using this algorithm. 042 * 043 * To simulate a network with this module, use the {@code Online_evGen_doNothing} generator. 044 * 045 * @net2plan.keywords Flow assignment (FA), Distributed algorithm, Backpressure routing, Dual gradient algorithm 046 * @net2plan.ocnbooksections Section 10.3 047 * @net2plan.inputParameters 048 * @author Pablo Pavon-Marino 049 */ 050@SuppressWarnings("unchecked") 051public class Online_evProc_backpressureRoutingDual extends IEventProcessor 052{ 053 private static PrintStream getNulFile () { try { return new PrintStream (new FileOutputStream ("NUL") , false); } catch (Exception e) {e.printStackTrace(); throw new RuntimeException ("Not NUL file"); } } 054 private PrintStream log = getNulFile (); //System.err;//new PrintStream (new FileOutputStream ("NUL") , true); 055 056 private Random rng; 057 private int N,E,D; 058 059 private static final int SIGNALING_WAKEUPTOSENDMESSAGE = 300; // A NODE SIGNAL QUEUES TO NEIGHBOR NODES 060 private static final int SIGNALING_RECEIVEDMESSAGE = 301; // A NODE RECEIVED SIGNAL QUEUES TO NEIGHBOR NODES 061 private static final int UPDATE_INITTIMESLOTFORSCHEDULINGPACKETS = 302; // ALL NODES SCHEDULE ONE PACKET PER LINK 062 private static final int UPDATE_STATISTICTRACES = 303; // STAT UPDATE OF N2P 063 064 private InputParameter signaling_isSynchronous = new InputParameter ("signaling_isSynchronous", false , "true if all the distributed agents involved wake up synchronously to send the signaling messages"); 065 private InputParameter signaling_averageInterMessageTime = new InputParameter ("signaling_averageInterMessageTime", 1.0 , "Average time between two signaling messages sent by an agent" , 0 , false , Double.MAX_VALUE , true); 066 private InputParameter signaling_maxFluctuationInterMessageTime = new InputParameter ("signaling_maxFluctuationInterMessageTime", 0.5 , "Max fluctuation in time between two signaling messages sent by an agent" , 0 , true , Double.MAX_VALUE , true); 067 private InputParameter signaling_averageDelay = new InputParameter ("signaling_averageDelay", 0.0 , "Average time between signaling message transmission by an agent and its reception by other or others" , 0 , true , Double.MAX_VALUE , true); 068 private InputParameter signaling_maxFluctuationInDelay = new InputParameter ("signaling_maxFluctuationInDelay", 0.0 , "Max fluctuation in time in the signaling delay, in absolute time values. The signaling delays are sampled from a uniform distribution within the given interval" , 0 , true , Double.MAX_VALUE , true); 069 private InputParameter signaling_signalingLossProbability = new InputParameter ("signaling_signalingLossProbability", 0.05 , "Probability that a signaling message transmitted is lost (not received by other or others involved agents)" , 0 , true , Double.MAX_VALUE , true); 070 071 private InputParameter routing_fixedPacketDurationAndSchedulingInterval = new InputParameter ("routing_fixedPacketDurationAndSchedulingInterval", 1.0 , "Fixed slot size (synchronized all links)" , 0 , false , Double.MAX_VALUE , true); 072 private InputParameter routing_numTrafficUnitsOfOnePacket = new InputParameter ("routing_numTrafficUnitsOfOnePacket", 1.0 , "One packet in one slot time is this traffic" , 0 , false , Double.MAX_VALUE , true); 073 private InputParameter routing_maxNumberOfN2PStatisticsIntervals = new InputParameter ("routing_maxNumberOfN2PStatisticsIntervals", (int) 50 , "Simulation length in number of scheduling intervals" , 1 , Integer.MAX_VALUE); 074 private InputParameter routing_statNumSchedSlotBetweenN2PRecomputing = new InputParameter ("routing_statNumSchedSlotBetweenN2PRecomputing", (int) 100 , "Number of scheduling slots between two N2P update and storing a stat trace" , 1 , Integer.MAX_VALUE); 075 private InputParameter routing_pressureDifferenceThreshold = new InputParameter ("routing_pressureDifferenceThreshold", 1.0 , "Pressure difference parameter (delta P in the book)" , 0 , false , Double.MAX_VALUE , true); 076 077 private InputParameter gradient_gammaStep = new InputParameter ("gradient_gammaStep", 0.01 , "Gamma step in the gradient algorithm" , 0 , false , Double.MAX_VALUE , true); 078 079 private InputParameter simulation_randomSeed = new InputParameter ("simulation_randomSeed", (long) 1 , "Seed of the random number generator"); 080 private InputParameter simulation_outFileNameRoot = new InputParameter ("simulation_outFileNameRoot", "backpressureRoutingDual" , "Root of the file name to be used in the output files. If blank, no output"); 081 082 private NetPlan currentNetPlan; 083 084 private Map<List<Link>,Route> stat_mapSeqLinks2RouteId; 085 private Map<Route,Integer> stat_mapRouteId2CarriedPacketsLastInterval; 086 private int [] stat_accumNumGeneratedPackets_d; 087 private int [] stat_accumNumReceivedPackets_d; 088 private int [][] stat_accumNumQeueusPackets_nd; 089 private double stat_totalOfferedTrafficConstant; 090 091 private TimeTrace stat_traceOf_xp; // traces are taken from netPlan, when it is updated (stat_n2pRecomputingInterval) 092 private TimeTrace stat_traceOf_objFunction; 093 private TimeTrace stat_traceOf_ye; 094 private TimeTrace stat_traceOf_queueSizes; 095 096 private IntMatrix3D ctlMostUpdatedQndKnownByNodeN1_n1nd; // sparse for memory efficiency 097 private int [][] ctlNumPacketsQueue_nd; 098 private Map<Pair<Node,Demand>,LinkedList<PacketInfo>> ctlQueue_nd; 099 100 private NetPlan optNetPlan; 101 private double [][] optQueueSizes_nd; 102 103 @Override 104 public String getDescription() 105 { 106 return "This module implements a distributed dual-gradient based algorithm for adapting the network routing to the one which minimizes the average number of hops, that results in a purely decentralized backpressure scheme."; 107 } 108 109 @Override 110 public List<Triple<String, String, String>> getParameters() 111 { 112 /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */ 113 return InputParameter.getInformationAllInputParameterFieldsOfObject(this); 114 } 115 116 @Override 117 public void initialize(NetPlan currentNetPlan, Map<String, String> algorithmParameters, Map<String, String> simulationParameters, Map<String, String> net2planParameters) 118 { 119 /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */ 120 InputParameter.initializeAllInputParameterFieldsOfObject(this, algorithmParameters); 121 122 if (currentNetPlan.getNumberOfLayers() != 1) throw new Net2PlanException ("This algorithm works in single layer networks"); 123 124 this.rng = new Random (simulation_randomSeed.getLong()); 125 this.currentNetPlan = currentNetPlan; 126 127 /* Update the indexes. The set of nodes, links and demands cannot change during the algorithm */ 128 this.N = this.currentNetPlan.getNumberOfNodes(); 129 this.E = this.currentNetPlan.getNumberOfLinks(); 130 this.D = this.currentNetPlan.getNumberOfDemands(); 131 if ((E == 0) || (D == 0)) throw new Net2PlanException ("The input design should have links and demands"); 132 133 /* Sets the initial routing, with all the traffic balanced equally in all the paths of the demand */ 134 this.stat_mapSeqLinks2RouteId = new HashMap<List<Link>,Route> (); 135 this.stat_mapRouteId2CarriedPacketsLastInterval = new HashMap<Route,Integer> (); 136 137 this.currentNetPlan.removeAllUnicastRoutingInformation(); 138 this.currentNetPlan.setRoutingTypeAllDemands(RoutingType.SOURCE_ROUTING); 139 final Pair<NetPlan,double [][]> pOpt = computeOptimumSolution (false); 140 final Pair<NetPlan,double [][]> pOpt01 = computeOptimumSolution (true); 141 this.optNetPlan = pOpt.getFirst(); 142 this.optQueueSizes_nd = pOpt.getSecond(); 143 /* Add the routes in the optimum solution to the netPlan, with zero traffic. For having teh same IDs later */ 144 this.currentNetPlan.copyFrom(optNetPlan); 145 for (Route r: currentNetPlan.getRoutes()) 146 { 147 r.setCarriedTraffic(0.0 , 0.0); 148 this.stat_mapSeqLinks2RouteId.put(r.getSeqLinks(),r); 149 this.stat_mapRouteId2CarriedPacketsLastInterval.put(r, 0); 150 } 151 this.ctlQueue_nd = new HashMap<Pair<Node,Demand>,LinkedList<PacketInfo>> (); 152 for (Node n: this.currentNetPlan.getNodes()) 153 for (Demand d: this.currentNetPlan.getDemands()) 154 { ctlQueue_nd.put(Pair.of(n, d), new LinkedList<PacketInfo> ()); } 155 156 this.ctlMostUpdatedQndKnownByNodeN1_n1nd = IntFactory3D.sparse.make(N, N, D); 157 this.ctlNumPacketsQueue_nd = new int [N][D]; 158 159 this.stat_accumNumGeneratedPackets_d = new int [D]; 160 this.stat_accumNumReceivedPackets_d = new int [D]; 161 this.stat_accumNumQeueusPackets_nd= new int [N][D]; 162 163 /* Initially all nodes receive a "wake up to transmit" event, aligned at time zero or y asynchr => randomly chosen */ 164 this.scheduleEvent(new SimEvent (routing_statNumSchedSlotBetweenN2PRecomputing.getInt() * routing_fixedPacketDurationAndSchedulingInterval.getDouble(), SimEvent.DestinationModule.EVENT_PROCESSOR , UPDATE_STATISTICTRACES , -1)); 165 this.scheduleEvent(new SimEvent (routing_fixedPacketDurationAndSchedulingInterval.getDouble() , SimEvent.DestinationModule.EVENT_PROCESSOR , UPDATE_INITTIMESLOTFORSCHEDULINGPACKETS , -1)); 166 for (Node n : currentNetPlan.getNodes()) 167 { 168 final double signalingTime = signaling_isSynchronous.getBoolean()? signaling_averageInterMessageTime.getDouble() : Math.max(0 , signaling_averageInterMessageTime.getDouble() + signaling_maxFluctuationInterMessageTime.getDouble() * (rng.nextDouble() - 0.5)); 169 this.scheduleEvent(new SimEvent (signalingTime , SimEvent.DestinationModule.EVENT_PROCESSOR , SIGNALING_WAKEUPTOSENDMESSAGE , n)); 170 } 171 172 /* Intialize the traces */ 173 this.stat_totalOfferedTrafficConstant = this.currentNetPlan.getVectorDemandOfferedTraffic().zSum(); 174 this.stat_traceOf_xp = new TimeTrace (); 175 this.stat_traceOf_ye = new TimeTrace (); 176 this.stat_traceOf_queueSizes = new TimeTrace (); 177 this.stat_traceOf_objFunction = new TimeTrace (); 178 this.stat_traceOf_xp.add(0.0 , netPlanRouteCarriedTrafficMap (this.currentNetPlan)); 179 this.stat_traceOf_ye.add(0.0, this.currentNetPlan.getVectorLinkCarriedTraffic()); 180 this.stat_traceOf_queueSizes.add(0.0, copyOf(this.ctlNumPacketsQueue_nd)); 181 this.stat_traceOf_objFunction.add(0.0, computeObjectiveFucntionFromNetPlan()); 182 183 } 184 185 @Override 186 public void processEvent(NetPlan currentNetPlan, SimEvent event) 187 { 188 final double time = event.getEventTime(); 189 switch (event.getEventType()) 190 { 191 case UPDATE_STATISTICTRACES: 192 { 193 for (Route r : this.currentNetPlan.getRoutes()) 194 { 195 final int numPacketsArrivedDestinationLastInterval = stat_mapRouteId2CarriedPacketsLastInterval.get(r); 196 final double trafficArrivedDestinationLastInterval = numPacketsArrivedDestinationLastInterval * routing_numTrafficUnitsOfOnePacket.getDouble() / (routing_fixedPacketDurationAndSchedulingInterval.getDouble() * routing_statNumSchedSlotBetweenN2PRecomputing.getInt()); 197 r.setCarriedTraffic(trafficArrivedDestinationLastInterval , trafficArrivedDestinationLastInterval); 198 199 /* Set zero in the counter of packets */ 200 stat_mapRouteId2CarriedPacketsLastInterval.put(r,0); 201 } 202 203 /* Update the traces */ 204 this.stat_traceOf_xp.add(time, netPlanRouteCarriedTrafficMap(this.currentNetPlan)); 205 this.stat_traceOf_ye.add(time, this.currentNetPlan.getVectorLinkCarriedTraffic()); 206 this.stat_traceOf_objFunction.add(time, computeObjectiveFucntionFromNetPlan()); 207 final double scaleFactorAccumNumQueuePacketsToAverageQueuedTraffic = this.routing_numTrafficUnitsOfOnePacket.getDouble() / this.routing_statNumSchedSlotBetweenN2PRecomputing.getInt(); 208 /* We store the average queue sizes in traffic units */ 209 this.stat_traceOf_queueSizes.add(time, scaledCopyOf(this.stat_accumNumQeueusPackets_nd , scaleFactorAccumNumQueuePacketsToAverageQueuedTraffic)); 210 this.stat_accumNumQeueusPackets_nd = new int [N][D]; // reset the count 211 212 this.scheduleEvent(new SimEvent (time + routing_fixedPacketDurationAndSchedulingInterval.getDouble() * routing_statNumSchedSlotBetweenN2PRecomputing.getInt() , SimEvent.DestinationModule.EVENT_PROCESSOR , UPDATE_STATISTICTRACES , -1)); 213 214 break; 215 } 216 217 case SIGNALING_RECEIVEDMESSAGE: // A node receives from an out neighbor the q_nd for any demand 218 { 219 final Triple<Node,Node,int []> signalInfo = (Triple<Node,Node,int []>) event.getEventObject(); 220 final Node nMe = signalInfo.getFirst(); 221 final int index_nIdMe = nMe.getIndex (); 222 final Node nNeighbor = signalInfo.getSecond(); 223 final int index_nNeighbor = nNeighbor.getIndex (); 224 final int [] receivedInfo_q_d = signalInfo.getThird(); 225 for (int index_d = 0 ; index_d < D ; index_d ++) 226 ctlMostUpdatedQndKnownByNodeN1_n1nd.set(index_nIdMe, index_nNeighbor, index_d, receivedInfo_q_d[index_d]); 227 break; 228 229 } 230 231 case SIGNALING_WAKEUPTOSENDMESSAGE: // A node broadcasts signaling info to its 1 hop neighbors 232 { 233 final Node nMe = (Node) event.getEventObject(); 234 final int index_nMe = nMe.getIndex (); 235 236 log.println("t=" + time + ": ROUTING_NODEWAKEUPTO_TRANSMITSIGNAL node " + nMe); 237 238 /* Create the info I will signal */ 239 int [] infoToSignal_q_d = Arrays.copyOf(ctlNumPacketsQueue_nd [index_nMe], D); 240 241 /* Send the events of the signaling information messages to incoming neighbors */ 242 for (Node n : nMe.getInNeighbors()) 243 { 244 if (rng.nextDouble() >= this.signaling_signalingLossProbability.getDouble()) // the signaling may be lost => lost to all nodes 245 { 246 final double signalingReceptionTime = time + Math.max(0 , signaling_averageDelay.getDouble() + signaling_maxFluctuationInDelay.getDouble() * (rng.nextDouble() - 0.5)); 247 this.scheduleEvent(new SimEvent (signalingReceptionTime , SimEvent.DestinationModule.EVENT_PROCESSOR , SIGNALING_RECEIVEDMESSAGE , Triple.of(n , nMe , infoToSignal_q_d))); 248 } 249 } 250 251 /* Re-schedule when to wake up again */ 252 final double signalingTime = signaling_isSynchronous.getBoolean()? time + signaling_averageInterMessageTime.getDouble() : Math.max(time , time + signaling_averageInterMessageTime.getDouble() + signaling_maxFluctuationInterMessageTime.getDouble() * (rng.nextDouble() - 0.5)); 253 this.scheduleEvent(new SimEvent (signalingTime , SimEvent.DestinationModule.EVENT_PROCESSOR , SIGNALING_WAKEUPTOSENDMESSAGE , nMe)); 254 break; 255 } 256 257 case UPDATE_INITTIMESLOTFORSCHEDULINGPACKETS: // a node updates its p_n, p_e values, using most updated info available 258 { 259 log.println("t=" + time + ": UPDATE_INITTIMESLOTFORSCHEDULINGPACKETS"); 260 261 /* First, create traffic of the demands. A constant amount */ 262 for (Demand d : this.currentNetPlan.getDemands()) 263 { 264 final double avgNumPackets_d = d.getOfferedTraffic() / routing_numTrafficUnitsOfOnePacket.getDouble(); 265 final int numPackets_d = Poisson.staticNextInt(avgNumPackets_d); 266 final Node a_d = d.getIngressNode(); 267 LinkedList<PacketInfo> q = this.ctlQueue_nd.get(Pair.of(a_d, d)); 268 for (int cont = 0 ; cont < numPackets_d ; cont ++) 269 q.add (new PacketInfo (d , time)); 270 this.ctlNumPacketsQueue_nd [a_d.getIndex ()][d.getIndex ()] += numPackets_d; 271 this.stat_accumNumGeneratedPackets_d [d.getIndex ()] += numPackets_d; 272 } 273 274 /* Forwarding decisions for each link. Each link can transmit as many packets as its capacity divided by trafic per packet factor (floor), during this scheduling */ 275 int [][] numPacketsToForward_ed = new int [E][D]; 276 int [][] numPacketsToForward_nd = new int [N][D]; 277 for (int index_e = 0 ; index_e < E ; index_e ++) 278 { 279 final Link e = currentNetPlan.getLink (index_e); 280 final int maxNumPacketsToTransmit = (int) Math.floor(e.getCapacity() / this.routing_numTrafficUnitsOfOnePacket.getDouble()); 281 final Node a_e = e.getOriginNode(); 282 final Node b_e = e.getDestinationNode(); 283 final int index_ae = a_e.getIndex (); 284 final int index_be = b_e.getIndex (); 285 for (int cont = 0 ; cont < maxNumPacketsToTransmit ; cont ++) 286 { 287 /* Take the queue with higher */ 288 double highestBackpressure = -1; 289 int indexDemandToTransmitPacket = -1; 290 for (int index_d = 0 ; index_d < D ; index_d ++) 291 { 292 final Demand d = currentNetPlan.getDemand (index_d); 293 final int thisQueueSize = ctlNumPacketsQueue_nd [index_ae][index_d]; 294 if (thisQueueSize != ctlQueue_nd.get(Pair.of(a_e, d)).size ()) throw new RuntimeException ("Bad"); 295 final int neighborQueueSize = ctlMostUpdatedQndKnownByNodeN1_n1nd.get(index_ae,index_be,index_d); 296 final double backpressure = gradient_gammaStep.getDouble() * (thisQueueSize - numPacketsToForward_nd[index_ae][index_d] - neighborQueueSize); 297 if (backpressure > highestBackpressure) { indexDemandToTransmitPacket = index_d; highestBackpressure = backpressure; } 298 } 299 if (highestBackpressure <= routing_pressureDifferenceThreshold.getDouble()) break; // no more queues will be empty, not enough backpressures 300 301 numPacketsToForward_ed[index_e][indexDemandToTransmitPacket] ++; 302 numPacketsToForward_nd[index_ae][indexDemandToTransmitPacket] ++; 303 } 304 } 305 /* Apply the forwarding decisions */ 306 for (int index_e = 0 ; index_e < E ; index_e ++) 307 { 308 final Link e = currentNetPlan.getLink (index_e); 309 final Node a_e = e.getOriginNode(); 310 final Node b_e = e.getDestinationNode(); 311 final int index_ae = a_e.getIndex (); 312 final int index_be = b_e.getIndex (); 313 for (int index_d = 0 ; index_d < D ; index_d ++) 314 { 315 final Demand d = currentNetPlan.getDemand (index_d); 316 final int numPackets_ed = numPacketsToForward_ed [index_e][index_d]; 317 318 for (int cont = 0 ; cont < numPackets_ed ; cont ++) 319 { 320 PacketInfo p = this.ctlQueue_nd.get(Pair.of(a_e,d)).pop(); 321 ctlNumPacketsQueue_nd [index_ae][index_d] --; 322 if (this.ctlQueue_nd.get(Pair.of(a_e,d)).size() != ctlNumPacketsQueue_nd [index_ae][index_d]) throw new RuntimeException ("Bad"); 323 p.seqLinks.add(e); // update the seq of links 324 if (b_e == d.getEgressNode()) 325 { 326 /* Packet arrived to destination: update the statistics */ 327 Route route = stat_mapSeqLinks2RouteId.get(p.seqLinks); 328 329 /* If the route does not exist, create it */ 330 if (route == null) 331 { 332 route = this.currentNetPlan.addRoute(d, 0.0 , 0.0 , p.seqLinks, null); 333 stat_mapSeqLinks2RouteId.put(p.seqLinks , route); 334 stat_mapRouteId2CarriedPacketsLastInterval.put(route, 1); 335 } 336 else 337 { 338 stat_mapRouteId2CarriedPacketsLastInterval.put(route, stat_mapRouteId2CarriedPacketsLastInterval.get(route) + 1); 339 this.stat_accumNumReceivedPackets_d [index_d] ++; 340 } 341 } 342 else 343 { 344 /* Not the end node => put it in the next queue */ 345 this.ctlQueue_nd.get(Pair.of(b_e,d)).addLast(p); 346 ctlNumPacketsQueue_nd [index_be][index_d] ++; 347 } 348 } 349 } 350 351 } 352 353 /* Update the statistics of accum number of packets in a queue */ 354 for (int index_n = 0 ; index_n < N ; index_n ++) 355 for (int index_d = 0 ; index_d < D ; index_d ++) 356 stat_accumNumQeueusPackets_nd [index_n][index_d] += this.ctlNumPacketsQueue_nd [index_n][index_d]; 357 358 359 this.scheduleEvent(new SimEvent (time + routing_fixedPacketDurationAndSchedulingInterval.getDouble() , SimEvent.DestinationModule.EVENT_PROCESSOR , UPDATE_INITTIMESLOTFORSCHEDULINGPACKETS , -1)); 360 361 if (time > this.routing_maxNumberOfN2PStatisticsIntervals.getInt() * routing_statNumSchedSlotBetweenN2PRecomputing.getInt() * this.routing_fixedPacketDurationAndSchedulingInterval.getDouble()) { this.endSimulation (); } 362 break; 363 } 364 365 366 default: throw new RuntimeException ("Unexpected received event"); 367 } 368 369 370 } 371 372 public String finish (StringBuilder st , double simTime) 373 { 374 double [] avTrafficGenerated_d = new double [D]; 375 for (int index_d = 0; index_d < D ; index_d ++) 376 avTrafficGenerated_d[index_d] = stat_accumNumGeneratedPackets_d [index_d] * routing_numTrafficUnitsOfOnePacket.getDouble() / simTime; 377 378 /* If no output file, return */ 379 if (simulation_outFileNameRoot.getString().equals("")) return null; 380 381 /* Compute optimum solution and cost */ 382 double optCost = optNetPlan.getVectorLinkCarriedTraffic().zSum() / stat_totalOfferedTrafficConstant; 383 384 TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_objFunc.txt"), optCost); 385 TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_xp.txt"), optNetPlan.getVectorRouteCarriedTraffic()); 386 TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_ye.txt"), optNetPlan.getVectorLinkCarriedTraffic()); 387 TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_qnd.txt"), optQueueSizes_nd); 388 this.stat_traceOf_queueSizes.printToFile(new File (simulation_outFileNameRoot.getString() + "_qnd.txt")); 389 this.stat_traceOf_objFunction.printToFile(new File (simulation_outFileNameRoot.getString() + "_objFunc.txt")); 390 this.stat_traceOf_ye.printToFile(new File (simulation_outFileNameRoot.getString() + "_ye.txt")); 391 for (Pair<Double,Object> sample : this.stat_traceOf_xp.getList()) 392 { 393 Map<Long,Double> x_p = (Map<Long,Double>) sample.getSecond (); 394 for (long r: this.currentNetPlan.getRouteIds()) 395 if (!x_p.containsKey(r)) x_p.put(r , 0.0); 396 } 397 this.stat_traceOf_xp.printToFile(new File (simulation_outFileNameRoot.getString() + "_xp.txt")); 398 return null; 399 } 400 401 private double computeObjectiveFucntionFromNetPlan () 402 { 403 return this.currentNetPlan.getVectorLinkCarriedTraffic().zSum () / this.stat_totalOfferedTrafficConstant; 404 } 405 406 private Pair<NetPlan,double [][]> computeOptimumSolution (boolean xdeVariablesAsFractionsOfTraffic) 407 { 408 /* Modify the map so that it is the pojection where all elements sum h_d, and are non-negative */ 409 final int D = this.currentNetPlan.getNumberOfDemands(); 410 final int E = this.currentNetPlan.getNumberOfLinks(); 411 final DoubleMatrix1D h_d = this.currentNetPlan.getVectorDemandOfferedTraffic(); 412 final DoubleMatrix1D u_e = this.currentNetPlan.getVectorLinkCapacity(); 413 414 OptimizationProblem op = new OptimizationProblem(); 415 if (xdeVariablesAsFractionsOfTraffic) 416 op.addDecisionVariable("x_de", false , new int[] { D , E }, 0 , 1); 417 else 418 op.addDecisionVariable("x_de", false , new int[] { D , E }, 0 , u_e.getMaxLocation() [0]); 419 op.setInputParameter("h_d", h_d , "row"); 420 op.setInputParameter("u_e", u_e , "row"); 421 op.setInputParameter("deltaP", routing_pressureDifferenceThreshold.getDouble()); 422 423 /* Set some input parameters */ 424 if (xdeVariablesAsFractionsOfTraffic) 425 op.setObjectiveFunction("minimize", "deltaP * sum (h_d * x_de)"); 426 else 427 op.setObjectiveFunction("minimize", "deltaP * sum (x_de)"); 428 429 /* VECTORIAL FORM OF THE CONSTRAINTS */ 430 op.setInputParameter("A_ne", this.currentNetPlan.getMatrixNodeLinkIncidence()); 431 op.setInputParameter("A_nd", this.currentNetPlan.getMatrixNodeDemandIncidence()); 432 if (xdeVariablesAsFractionsOfTraffic) 433 { 434 op.addConstraint("A_ne * (x_de') >= A_nd" , "flowConservationConstraints"); // the flow-conservation constraints (NxD constraints) 435 op.addConstraint("h_d * x_de <= u_e"); // the capacity constraints (E constraints) 436 } 437 else 438 { 439 op.addConstraint("A_ne * (x_de') >= A_nd * diag(h_d)" , "flowConservationConstraints"); // the flow-conservation constraints (NxD constraints) 440 op.addConstraint("sum(x_de,1) <= u_e"); // the capacity constraints (E constraints) 441 } 442 443 /* Call the solver to solve the problem */ 444 op.solve("cplex"); 445 446 /* If an optimal solution was not found, quit */ 447 if (!op.solutionIsOptimal ()) throw new RuntimeException ("An optimal solution was not found"); 448 449 /* Retrieve the optimum solutions. Convert the bps into Erlangs */ 450 final DoubleMatrix2D x_de_array = op.getPrimalSolution("x_de").view2D (); 451 final double [][] q_nd_array = (double [][]) op.getMultipliersOfConstraint("flowConservationConstraints").toArray(); 452 453 /* Convert the x_de variables into a set of routes for each demand */ 454 NetPlan np = this.currentNetPlan.copy(); 455 np.removeAllUnicastRoutingInformation(); 456 np.setRoutingFromDemandLinkCarriedTraffic(x_de_array , xdeVariablesAsFractionsOfTraffic , false , null); 457 458 /* Check solution: all traffic is carried, no link oversubscribed */ 459 for (Demand d : np.getDemands()) if (d.getBlockedTraffic() > 1E-3) throw new RuntimeException ("d: " + d + ", hd: " + d.getOfferedTraffic() + ", carried_d: " + d.getCarriedTraffic() + "... Bad"); 460 for (Link e : np.getLinks()) if (e.getCarriedTraffic() > e.getCapacity() + 1E-3) throw new RuntimeException ("Bad"); 461 462 /* Scale q_nd multipliers by 1/gamma factor => they become now the optimum queue sizes in the algorithm */ 463 double [][] optQueueSizes = new double [N][D]; 464 for (int i = 0 ; i < N ; i ++) 465 for (int j = 0 ; j < D ; j ++) 466 optQueueSizes [i][j] = q_nd_array [i][j] / this.gradient_gammaStep.getDouble(); 467 468 return Pair.of(np,optQueueSizes); 469 } 470 471 private Map<Long,Double> netPlanRouteCarriedTrafficMap (NetPlan np) { Map<Long,Double> res = new HashMap<Long,Double> (); for (Route r : np.getRoutes ()) res.put (r.getId () , r.getCarriedTraffic()); return res; } 472 private class PacketInfo 473 { 474 final Demand d; 475 final double packetCreationTime; 476 LinkedList<Link> seqLinks; 477 PacketInfo (Demand d , double creationTime) { this.d = d; this.packetCreationTime = creationTime; this.seqLinks = new LinkedList<Link> (); } 478 } 479 private int [][] copyOf (int [][] a) { int [][] res = new int [a.length][]; for (int cont = 0 ; cont < a.length ; cont ++) res [cont] = Arrays.copyOf(a[cont], a[cont].length); return res; } 480 private double [][] copyOf (double [][] a) { double [][] res = new double [a.length][]; for (int cont = 0 ; cont < a.length ; cont ++) res [cont] = Arrays.copyOf(a[cont], a[cont].length); return res; } 481 private double [][] scaledCopyOf (int [][] a , double mult) { double [][] res = new double [a.length][a[0].length]; for (int cont = 0 ; cont < a.length ; cont ++) for (int cont2 = 0; cont2 < a[cont].length ; cont2 ++) res [cont][cont2] = a[cont][cont2] * mult; return res; } 482}