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}