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.DoubleFactory1D;
015import cern.colt.matrix.tdouble.DoubleFactory2D;
016import cern.colt.matrix.tdouble.DoubleMatrix1D;
017import cern.colt.matrix.tdouble.DoubleMatrix2D;
018import cern.jet.math.tdouble.DoubleFunctions;
019import com.net2plan.examples.ocnbook.offline.Offline_cba_congControLinkBwSplitTwolQoS;
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.*;
025
026import java.io.File;
027import java.util.HashMap;
028import java.util.List;
029import java.util.Map;
030import java.util.Random;
031
032/** 
033 * This module implements a distributed primal-decomposition-based gradient algorithm, for a coordinated adjustment of the congestion control of two types of demands (with different utility functions), and the fraction of each link capacity to grant to the traffic of each type, to maximize the network utility enforcing a fair allocation of the resources.
034 *
035 * Ths event processor is adapted to permit observing the algorithm performances under user-defined conditions, 
036 * including asynchronous distributed executions, where signaling can be affected by losses and/or delays, and/or measurement errors. 
037 * The time evolution of different metrics can be stored in output files, for later processing. 
038 * As an example, see the <a href="../../../../../../graphGeneratorFiles/fig_sec11_3_congControlAndQoSLinkCap_primalDecomp.m">{@code fig_sec11_3_congControlAndQoSLinkCap_primalDecomp.m}</a> MATLAB file used for generating the graph/s of the case study in the 
039 * <a href="http://eu.wiley.com/WileyCDA/WileyTitle/productCd-1119013356.html">book</a> using this algorithm.
040 * 
041 * To simulate a network with this module, use the {@code Online_evGen_doNothing} generator.
042 * 
043 * @net2plan.keywords Bandwidth assignment (BA), Capacity assignment (CA), Distributed algorithm, Primal decomposition 
044 * @net2plan.ocnbooksections Section 11.3
045 * @net2plan.inputParameters 
046 * @author Pablo Pavon-Marino
047 */
048@SuppressWarnings("unchecked")
049public class Online_evProc_congControlAndQoSTwoClassesPrimalDecomp extends IEventProcessor
050{
051        private double PRECISIONFACTOR;
052        private Random rng;
053
054        private int N,E,D1,D2,D;
055        private Map<String,String> algorithmParameters , net2PlanParameters;
056        
057        private static final int MAC_UPDATE_WAKEUPTOUPDATE = 202;
058        private static final int CC_SIGNALING_WAKEUPTOSENDMESSAGE = 400;
059        private static final int CC_SIGNALING_RECEIVEDMESSAGE = 401;
060        private static final int CC_UPDATE_WAKEUPTOUPDATE = 402;
061
062        private InputParameter mac_update_isSynchronous = new InputParameter ("mac_update_isSynchronous", false , "true if all the distributed agents involved wake up synchronousely to update its state");
063        private InputParameter mac_update_averageInterUpdateTime = new InputParameter ("mac_update_averageInterUpdateTime", 50.0 , "Average time between two updates of an agent" , 0 , false , Double.MAX_VALUE , true);
064        private InputParameter mac_update_maxFluctuationInterUpdateTime = new InputParameter ("mac_update_maxFluctuationInterUpdateTime", 10.0 , "Max fluctuation in time in the update interval of an agent, in absolute time values. The update intervals are sampled from a uniform distribution within the given interval" , 0 , true , Double.MAX_VALUE , true);
065        private InputParameter mac_gradient_gammaStep = new InputParameter ("mac_gradient_gammaStep", 5.0 , "Gamma step in the gradient algorithm" , 0 , false , Double.MAX_VALUE , true);
066        private InputParameter mac_gradient_maxGradientCoordinateChange = new InputParameter ("mac_gradient_maxGradientCoordinateChange", 1000.0 , "Maximum change in an iteration of a gradient coordinate" , 0 , false , Double.MAX_VALUE , true);
067
068        private InputParameter mac_simulation_maxNumberOfUpdateIntervals = new InputParameter ("mac_simulation_maxNumberOfUpdateIntervals", 600.0 , "Maximum number of update intervals in average per agent" , 0 , false , Double.MAX_VALUE , true);
069        private InputParameter simulation_randomSeed = new InputParameter ("simulation_randomSeed", (long) 1 , "Seed of the random number generator");
070        private InputParameter simulation_outFileNameRoot = new InputParameter ("simulation_outFileNameRoot", "crossLayerCongControlTwoQoSPrimalDecomp" , "Root of the file name to be used in the output files. If blank, no output");
071        
072        private InputParameter cc_signaling_isSynchronous = new InputParameter ("cc_signaling_isSynchronous", false , "true if all the distributed agents involved wake up synchronously to send the signaling messages");
073        private InputParameter cc_signaling_averageInterMessageTime = new InputParameter ("cc_signaling_averageInterMessageTime", 1.0 , "Average time between two signaling messages sent by an agent" , 0 , false , Double.MAX_VALUE , true);
074        private InputParameter cc_signaling_maxFluctuationInterMessageTime = new InputParameter ("cc_signaling_maxFluctuationInterMessageTime", 0.5 , "Max fluctuation in time between two signaling messages sent by an agent" , 0 , true , Double.MAX_VALUE , true);
075        private InputParameter cc_signaling_averageDelay = new InputParameter ("cc_signaling_averageDelay", 3.0 , "Average time between signaling message transmission by an agent and its reception by other or others" , 0 , true , Double.MAX_VALUE , true);
076        private InputParameter cc_signaling_maxFluctuationInDelay = new InputParameter ("cc_signaling_maxFluctuationInDelay", 0.5 , "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);
077        private InputParameter cc_signaling_signalingLossProbability = new InputParameter ("cc_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);
078
079        private InputParameter cc_update_isSynchronous = new InputParameter ("cc_update_isSynchronous", false , "true if all the distributed agents involved wake up synchronousely to update its state");
080        private InputParameter cc_update_averageInterUpdateTime = new InputParameter ("cc_update_averageInterUpdateTime", 1.0 , "Average time between two updates of an agent" , 0 , false , Double.MAX_VALUE , true);
081        private InputParameter cc_update_maxFluctuationInterUpdateTime = new InputParameter ("cc_update_maxFluctuationInterUpdateTime", 0.5 , "Max fluctuation in time in the update interval of an agent, in absolute time values. The update intervals are sampled from a uniform distribution within the given interval" , 0 , true , Double.MAX_VALUE , true);
082
083        private InputParameter cc_gradient_gammaStep = new InputParameter ("cc_gradient_gammaStep", 0.001 , "Gamma step in the gradient algorithm" , 0 , false , Double.MAX_VALUE , true);
084        private InputParameter cc_gradient_maxGradientAbsoluteNoise = new InputParameter ("cc_gradient_maxGradientAbsoluteNoise", 0.0 , "Max value of the added noise to the gradient coordinate in absolute values" , 0 , true , Double.MAX_VALUE , true);
085
086        private InputParameter cc_simulation_maxNumberOfUpdateIntervals = new InputParameter ("cc_simulation_maxNumberOfUpdateIntervals", 600.0 , "Maximum number of update intervals in average per agent" , 0 , false , Double.MAX_VALUE , true);
087        private InputParameter cc_control_minHd = new InputParameter ("cc_control_minHd", 0.1 , "Minimum traffic assigned to each demand" , 0 , true , Double.MAX_VALUE , true);
088        private InputParameter cc_control_maxHd = new InputParameter ("cc_control_maxHd", 1e6 , "Maximum traffic assigned to each demand" , 0 , true , Double.MAX_VALUE , true);
089        private InputParameter cc_control_initialiLinkPrices = new InputParameter ("cc_control_initialiLinkPrices", 1.0 , "Link prices in the algorithm initialization" , 0 , true , Double.MAX_VALUE , true);
090
091        private InputParameter cc_control_fairnessFactor_1 = new InputParameter ("cc_control_fairnessFactor_1", 1.0 , "Fairness factor in utility function of congestion control for demands of class 1" , 0 , true , Double.MAX_VALUE , true);
092        private InputParameter cc_control_fairnessFactor_2 = new InputParameter ("cc_control_fairnessFactor_2", 1.0 , "Fairness factor in utility function of congestion control for demands of class 2" , 0 , true , Double.MAX_VALUE , true);
093        private InputParameter cc_control_weightFairness_1 = new InputParameter ("cc_control_weightFairness_1", 1.0 , "Weight factor in utility function demands type 1" , 0 , true , Double.MAX_VALUE , true);
094        private InputParameter cc_control_weightFairness_2 = new InputParameter ("cc_control_weightFairness_2", 2.0 , "Weight factor in utility function demands type 2" , 0 , true , Double.MAX_VALUE , true);
095        private InputParameter mac_minCapacity_1 = new InputParameter ("mac_minCapacity_1", 0.1 , "Minimum capacity in each link, allocated to traffic of type 1" , 0 , true , Double.MAX_VALUE , true);
096        private InputParameter mac_minCapacity_2 = new InputParameter ("mac_minCapacity_2", 0.1 , "Minimum capacity in each link, allocated to traffic of type 2" , 0 , true , Double.MAX_VALUE , true);
097
098        private DoubleMatrix1D demandType;
099        private DoubleMatrix1D congControl_price1_e;
100        private DoubleMatrix1D congControl_price2_e;
101        private DoubleMatrix2D control_mostUpdatedLinkPriceKnownDemand_de;
102        private DoubleMatrix1D mac_u1; 
103        private DoubleMatrix1D mac_u2; 
104        
105        private TimeTrace traceOf_pi1_e;
106        private TimeTrace traceOf_pi2_e;
107        private TimeTrace traceOf_u1_e;
108        private TimeTrace traceOf_u2_e;
109        private TimeTrace traceOf_y1_e;
110        private TimeTrace traceOf_y2_e;
111        private TimeTrace traceOf_h_d1;
112        private TimeTrace traceOf_h_d2;
113        private TimeTrace traceOf_objFunction;
114
115        private NetPlan currentNetPlan , copyInitialNetPlan;
116        
117        
118        @Override
119        public String getDescription()
120        {
121                return "This module implements a distributed primal-decomposition-based gradient algorithm, for a coordinated adjustment of the congestion control of two types of demands (with different utility functions), and the fraction of each link capacity to grant to the traffic of each type, to maximize the network utility enforcing a fair allocation of the resources.";
122        }
123
124        @Override
125        public List<Triple<String, String, String>> getParameters()
126        {
127                /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */
128                return InputParameter.getInformationAllInputParameterFieldsOfObject(this);
129        }
130
131        @Override
132        public void initialize(NetPlan currentNp, Map<String, String> algorithmParameters, Map<String, String> simulationParameters, Map<String, String> net2planParameters)
133        {
134                /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */
135                InputParameter.initializeAllInputParameterFieldsOfObject(this, algorithmParameters);
136
137                this.currentNetPlan = currentNp;
138                this.algorithmParameters = algorithmParameters;
139                this.net2PlanParameters = net2planParameters;
140                if (currentNetPlan.getNumberOfLayers() != 1) throw new Net2PlanException ("This algorithm works in single layer networks");
141
142                /* INITIALIZE VARIABLES COMMON FOR BOTH LAYERS */
143                this.copyInitialNetPlan = currentNp.copy();
144                if (currentNetPlan.getVectorLinkCapacity().getMaxLocation() [0]< mac_minCapacity_1.getDouble() + mac_minCapacity_2.getDouble()) throw new Net2PlanException ("Minimum link capacities for each class are too high for the available link capacities");
145                
146                this.PRECISIONFACTOR = Double.parseDouble(net2planParameters.get("precisionFactor"));
147                
148                /* READ INPUT PARAMETERS BOTH LAYERS */
149                this.rng = new Random (simulation_randomSeed.getLong());
150                this.N = this.currentNetPlan.getNumberOfNodes();
151                this.E = this.currentNetPlan.getNumberOfLinks();
152                if (E == 0) throw new Net2PlanException ("The input design should have links");
153                this.D1 = N*(N-1);
154                this.D2 = N*(N-1);
155                this.D = D1+D2;
156                
157                /* Initialize the demands and routers per demand */
158                this.currentNetPlan.removeAllDemands();
159                this.currentNetPlan.setRoutingTypeAllDemands(RoutingType.SOURCE_ROUTING);
160                this.demandType = DoubleFactory1D.dense.make (D1+D2);
161                for (Node n1 : this.currentNetPlan.getNodes())
162                        for (Node n2 : this.currentNetPlan.getNodes())
163                                if (n1 != n2) 
164                                { 
165                                        final Demand d1 = this.currentNetPlan.addDemand(n1, n2, 0.0, RoutingType.SOURCE_ROUTING , null); d1.setAttribute("type" , "1"); demandType.set(d1.getIndex (), 1); 
166                                        final Demand d2 = this.currentNetPlan.addDemand(n1, n2, 0.0, RoutingType.SOURCE_ROUTING , null); d1.setAttribute("type" , "2"); demandType.set(d2.getIndex (), 2); 
167                                }
168                
169                /* Remove all routes, and create one with the shortest path in km for each demand */
170                currentNetPlan.removeAllUnicastRoutingInformation();
171                currentNetPlan.setRoutingTypeAllDemands(RoutingType.SOURCE_ROUTING);
172                this.currentNetPlan.addRoutesFromCandidatePathList(currentNetPlan.computeUnicastCandidatePathList(currentNetPlan.getVectorLinkLengthInKm() , 1, -1, -1, -1, -1, -1, -1 , null));
173
174                
175                for (Route r : currentNp.getRoutes ()) r.setCarriedTraffic(cc_control_minHd.getDouble() , cc_control_minHd.getDouble());
176                
177                /* INITIALIZE control information  */
178                this.congControl_price1_e = DoubleFactory1D.dense.make (E , cc_control_initialiLinkPrices.getDouble());
179                this.congControl_price2_e = DoubleFactory1D.dense.make (E , cc_control_initialiLinkPrices.getDouble());
180                
181                /* Initialize the information each demand knows of the prices of all the links */
182                this.control_mostUpdatedLinkPriceKnownDemand_de = DoubleFactory2D.dense.make (D,E,cc_control_initialiLinkPrices.getDouble());
183
184                this.mac_u1 = DoubleFactory1D.dense.make (E , mac_minCapacity_1.getDouble());
185                this.mac_u2 = currentNetPlan.getVectorLinkCapacity().copy ().assign(DoubleFunctions.minus (mac_minCapacity_1.getDouble()));
186                
187                /* Each demand adjusts its rate to the pi_e */
188                /* Compute the traffic each demand injects, set it in net2plan, and tell routing layer there was an update in this */
189                for (Demand d : currentNetPlan.getDemands())
190                {
191                        final double h_d = this.computeHdFromPrices(d);
192                        d.setOfferedTraffic(h_d);
193                        d.getRoutes().iterator().next ().setCarriedTraffic(h_d , h_d);
194                }
195                
196                /* Initially all nodes receive a "wake up to transmit" event, aligned at time zero or y asynchr => randomly chosen */
197                for (Link e : currentNp.getLinks())
198                {
199                        final double updateTime = (mac_update_isSynchronous.getBoolean())? mac_update_averageInterUpdateTime.getDouble() : Math.max(0 , mac_update_averageInterUpdateTime.getDouble() + mac_update_maxFluctuationInterUpdateTime.getDouble() * (rng.nextDouble() - 0.5));
200                        this.scheduleEvent(new SimEvent (updateTime , SimEvent.DestinationModule.EVENT_PROCESSOR , MAC_UPDATE_WAKEUPTOUPDATE , e));
201                }
202                
203                /* INITIALIZATION CONGESTION CONTROL LAYER */
204                /* Initially all nodes receive a "wake up to transmit" event, aligned at time zero or y asynchr => randomly chosen */
205                for (Link e : currentNp.getLinks())
206                {
207                        final double signalingTime = (cc_signaling_isSynchronous.getBoolean())? cc_signaling_averageInterMessageTime.getDouble() : Math.max(0 , cc_signaling_averageInterMessageTime.getDouble() + cc_signaling_maxFluctuationInterMessageTime.getDouble() * (rng.nextDouble() - 0.5));
208                        this.scheduleEvent(new SimEvent (signalingTime , SimEvent.DestinationModule.EVENT_PROCESSOR , CC_SIGNALING_WAKEUPTOSENDMESSAGE , e));
209                }
210                for (Demand d : currentNetPlan.getDemands())
211                {
212                        final double updateTime = (cc_update_isSynchronous.getBoolean())? cc_update_averageInterUpdateTime.getDouble() : Math.max(0 , cc_update_averageInterUpdateTime.getDouble() + cc_update_maxFluctuationInterUpdateTime.getDouble() * (rng.nextDouble() - 0.5));
213                        this.scheduleEvent(new SimEvent (updateTime , SimEvent.DestinationModule.EVENT_PROCESSOR , CC_UPDATE_WAKEUPTOUPDATE , d));
214                }
215                
216                /* INITIALIZE STATISTIC VARIABLES FOR BOTH LAYERS */
217                this.traceOf_pi1_e = new TimeTrace  ();
218                this.traceOf_pi2_e = new TimeTrace  ();
219                this.traceOf_u1_e = new TimeTrace ();
220                this.traceOf_u2_e = new TimeTrace ();
221                this.traceOf_y1_e = new TimeTrace (); 
222                this.traceOf_y2_e = new TimeTrace (); 
223                this.traceOf_h_d1 = new TimeTrace (); 
224                this.traceOf_h_d2 = new TimeTrace (); 
225                this.traceOf_objFunction = new TimeTrace ();
226
227                
228                this.traceOf_pi1_e.add(0.0, this.congControl_price1_e.copy ());
229                this.traceOf_pi2_e.add(0.0, this.congControl_price2_e.copy ());
230                this.traceOf_u1_e.add(0.0, this.mac_u1.copy ());
231                this.traceOf_u2_e.add(0.0, this.mac_u2.copy ());
232                final Triple<Double,Pair<DoubleMatrix1D,DoubleMatrix1D>,Pair<DoubleMatrix1D,DoubleMatrix1D>> objFunctionInfo = computeObjectiveFunctionFromNetPlan(this.currentNetPlan);
233                this.traceOf_objFunction.add(0.0 , objFunctionInfo.getFirst());
234                this.traceOf_h_d1.add(0.0, objFunctionInfo.getSecond().getFirst().copy ());
235                this.traceOf_h_d2.add(0.0, objFunctionInfo.getSecond().getSecond().copy ());
236                this.traceOf_y1_e.add(0.0, objFunctionInfo.getThird().getFirst().copy ());
237                this.traceOf_y2_e.add(0.0, objFunctionInfo.getThird().getSecond().copy ());
238        }
239
240        @Override
241        public void processEvent(NetPlan currentNetPlan, SimEvent event)
242        {
243                final double t = event.getEventTime();
244                switch (event.getEventType())
245                {
246                case MAC_UPDATE_WAKEUPTOUPDATE: 
247                {
248                        final Link eMe = (Link) event.getEventObject();
249                        final double current_u1 = mac_u1.get(eMe.getIndex ());
250                        final double current_u2 = mac_u2.get(eMe.getIndex ());
251                        final double gradient_1 = congControl_price1_e.get(eMe.getIndex ());
252                        final double gradient_2 = congControl_price2_e.get(eMe.getIndex ());
253                        final double nextBeforeProjection_u1 = current_u1 + this.mac_gradient_gammaStep.getDouble() * gradient_1;
254                        final double nextBeforeProjection_u2 = current_u2 + this.mac_gradient_gammaStep.getDouble() * gradient_2;
255                        DoubleMatrix1D initialSolution = DoubleFactory1D.dense.make (new double [] { current_u1 , current_u2 });
256                        DoubleMatrix1D inOut = DoubleFactory1D.dense.make (new double [] { nextBeforeProjection_u1 , nextBeforeProjection_u2 });
257                        DoubleMatrix1D uMin = DoubleFactory1D.dense.make (new double [] { mac_minCapacity_1.getDouble() , mac_minCapacity_2.getDouble() });
258                        GradientProjectionUtils.euclideanProjection_sumInequality (inOut , null , uMin , eMe.getCapacity());
259                        if (mac_gradient_maxGradientCoordinateChange.getDouble() > 0)
260                                GradientProjectionUtils.scaleDown_maxAbsoluteCoordinateChange (initialSolution , inOut , null , mac_gradient_maxGradientCoordinateChange.getDouble());
261
262                        this.mac_u1.set(eMe.getIndex (), inOut.get(0));
263                        this.mac_u2.set(eMe.getIndex (), inOut.get(1));
264                        if (mac_u1.get(eMe.getIndex ()) + mac_u2.get(eMe.getIndex ()) > eMe.getCapacity() + PRECISIONFACTOR) throw new RuntimeException ("Bad");
265                        
266                        final double updateTime = mac_update_isSynchronous.getBoolean()? t + mac_update_averageInterUpdateTime.getDouble() : Math.max(t , t + mac_update_averageInterUpdateTime.getDouble() + mac_update_maxFluctuationInterUpdateTime.getDouble() * (rng.nextDouble() - 0.5));
267                        this.scheduleEvent(new SimEvent (updateTime , SimEvent.DestinationModule.EVENT_PROCESSOR , MAC_UPDATE_WAKEUPTOUPDATE,  eMe));
268
269                        this.traceOf_u1_e.add(t, this.mac_u1.copy ());
270                        this.traceOf_u2_e.add(t, this.mac_u2.copy ());
271
272                        if (t > this.mac_simulation_maxNumberOfUpdateIntervals.getDouble() * this.mac_update_averageInterUpdateTime.getDouble()) { this.endSimulation (); }
273
274                        break;
275                }
276
277                case CC_SIGNALING_RECEIVEDMESSAGE: // A node receives from an out neighbor the q_nt for any destination
278                {
279                        final Pair<Demand,Triple<Link,Double,Double>> signalInfo = (Pair<Demand,Triple<Link,Double,Double>>) event.getEventObject();
280                        final Demand d = signalInfo.getFirst();
281                        final int type = (int) demandType.get(d.getIndex ());
282                        final Link e = signalInfo.getSecond().getFirst();
283                        final double pi1 = signalInfo.getSecond().getSecond();
284                        final double pi2 = signalInfo.getSecond().getThird();
285                        this.control_mostUpdatedLinkPriceKnownDemand_de.set(d.getIndex () , e.getIndex () , (type == 1)? pi1 : pi2);  
286                        break;
287                }
288                
289                case CC_SIGNALING_WAKEUPTOSENDMESSAGE: // A node broadcasts signaling info to its 1 hop neighbors
290                {
291                        final Link eMe = (Link) event.getEventObject();
292
293                        /* Update the new price with the gradient approach */
294                        Triple<Double,Pair<DoubleMatrix1D,DoubleMatrix1D>,Pair<DoubleMatrix1D,DoubleMatrix1D>>  triple = computeObjectiveFunctionFromNetPlan (this.currentNetPlan);
295                        final double u1 = this.mac_u1.get(eMe.getIndex ());
296                        final double u2 = this.mac_u2.get(eMe.getIndex ());
297                        final double y1 = triple.getThird().getFirst().get(eMe.getIndex ());
298                        final double y2 = triple.getThird().getSecond().get(eMe.getIndex ());
299                        final double old_pie_1 = this.congControl_price1_e.get(eMe.getIndex ());
300                        final double old_pie_2 = this.congControl_price2_e.get(eMe.getIndex ());
301                        final double new_pie_1 = Math.max(0, old_pie_1 - this.cc_gradient_gammaStep.getDouble() * (u1 - y1) + 2*cc_gradient_maxGradientAbsoluteNoise.getDouble()*(rng.nextDouble()-0.5));
302                        final double new_pie_2 = Math.max(0, old_pie_2 - this.cc_gradient_gammaStep.getDouble() * (u2 - y2) + 2*cc_gradient_maxGradientAbsoluteNoise.getDouble()*(rng.nextDouble()-0.5));
303                        this.congControl_price1_e.set(eMe.getIndex (), new_pie_1);
304                        this.congControl_price2_e.set(eMe.getIndex (), new_pie_2);
305                        
306                        this.traceOf_pi1_e.add(t, this.congControl_price1_e.copy ());
307                        this.traceOf_pi2_e.add(t, this.congControl_price2_e.copy ());
308
309                        /* Create the info I will signal */
310                        Triple<Link,Double,Double> infoToSignal = Triple.of(eMe ,  new_pie_1 , new_pie_2);
311
312                        /* Send the events of the signaling information messages to all the nodes */
313                        for (Route route : eMe.getTraversingRoutes())
314                        {
315                                if (rng.nextDouble() < this.cc_signaling_signalingLossProbability.getDouble()) continue; // the signaling may be lost => lost to all demands
316                                final Demand d = route.getDemand ();
317                                final double signalingReceptionTime = t + Math.max(0 , cc_signaling_averageDelay.getDouble() + cc_signaling_maxFluctuationInDelay.getDouble() * (rng.nextDouble() - 0.5));
318                                this.scheduleEvent(new SimEvent (signalingReceptionTime , SimEvent.DestinationModule.EVENT_PROCESSOR , CC_SIGNALING_RECEIVEDMESSAGE , Pair.of(d , infoToSignal)));
319                        }
320                        
321                        /* Re-schedule when to wake up again */
322                        final double signalingTime = cc_signaling_isSynchronous.getBoolean()? t + cc_signaling_averageInterMessageTime.getDouble() : Math.max(t , t + cc_signaling_averageInterMessageTime.getDouble() + cc_signaling_maxFluctuationInterMessageTime.getDouble() * (rng.nextDouble() - 0.5));
323                        this.scheduleEvent(new SimEvent (signalingTime , SimEvent.DestinationModule.EVENT_PROCESSOR , CC_SIGNALING_WAKEUPTOSENDMESSAGE , eMe));
324                        break;
325                }
326
327                case CC_UPDATE_WAKEUPTOUPDATE: 
328                {
329                        final Demand dMe = (Demand) event.getEventObject();
330                        final double new_hd = computeHdFromPrices (dMe);
331                        
332                        dMe.setOfferedTraffic(new_hd);
333                        dMe.getRoutes ().iterator().next().setCarriedTraffic(new_hd , new_hd);
334                        
335                        final Triple<Double,Pair<DoubleMatrix1D,DoubleMatrix1D>,Pair<DoubleMatrix1D,DoubleMatrix1D>> objFunctionInfo = computeObjectiveFunctionFromNetPlan(this.currentNetPlan);
336                        this.traceOf_objFunction.add(t , objFunctionInfo.getFirst());
337                        this.traceOf_h_d1.add(t, objFunctionInfo.getSecond().getFirst().copy ());
338                        this.traceOf_h_d2.add(t, objFunctionInfo.getSecond().getSecond().copy ());
339                        this.traceOf_y1_e.add(t, objFunctionInfo.getThird().getFirst().copy ());
340                        this.traceOf_y2_e.add(t, objFunctionInfo.getThird().getSecond().copy ());
341
342                        final double updateTime = cc_update_isSynchronous.getBoolean()? t + cc_update_averageInterUpdateTime.getDouble() : Math.max(t , t + cc_update_averageInterUpdateTime.getDouble() + cc_update_maxFluctuationInterUpdateTime.getDouble() * (rng.nextDouble() - 0.5));
343                        this.scheduleEvent(new SimEvent (updateTime , SimEvent.DestinationModule.EVENT_PROCESSOR , CC_UPDATE_WAKEUPTOUPDATE,  dMe));
344
345                        if (t > this.cc_simulation_maxNumberOfUpdateIntervals.getDouble() * this.cc_update_averageInterUpdateTime.getDouble()) { this.endSimulation (); }
346
347                        break;
348                }
349                
350                
351                default: throw new RuntimeException ("Unexpected received event");
352                }
353                
354                
355        }
356
357        public String finish (StringBuilder st , double simTime)
358        {
359                if (simulation_outFileNameRoot.getString().equals("")) return null;
360                traceOf_h_d1.printToFile(new File (simulation_outFileNameRoot.getString() + "_hd1.txt"));
361                traceOf_h_d2.printToFile(new File (simulation_outFileNameRoot.getString() + "_hd2.txt"));
362                traceOf_u1_e.printToFile(new File (simulation_outFileNameRoot.getString() + "_ue1.txt"));
363                traceOf_u2_e.printToFile(new File (simulation_outFileNameRoot.getString() + "_ue2.txt"));
364                traceOf_objFunction.printToFile(new File (simulation_outFileNameRoot.getString() + "_objFunc.txt"));
365
366                Map<String,String> param = new HashMap<String,String> (algorithmParameters);
367                param.put("solverName", "ipopt");
368                param.put("solverLibraryName", "");
369                param.put("maxSolverTimeInSeconds", "-1");
370                new Offline_cba_congControLinkBwSplitTwolQoS().executeAlgorithm(copyInitialNetPlan , param , this.net2PlanParameters);
371                DoubleMatrix1D jom_hd1 = DoubleFactory1D.dense.make (D1);
372                DoubleMatrix1D jom_hd2 = DoubleFactory1D.dense.make (D2);
373                DoubleMatrix1D jom_ue1 = DoubleFactory1D.dense.make (E);
374                DoubleMatrix1D jom_ue2 = DoubleFactory1D.dense.make (E);
375                int counter_1 = 0; int counter_2 = 0;
376                for (Demand d : copyInitialNetPlan.getDemands ())
377                        if (d.getAttribute("type").equals ("1")) jom_hd1.set (counter_1 ++ , d.getOfferedTraffic()); else jom_hd2.set (counter_2 ++ , d.getOfferedTraffic());
378                for (Link e : copyInitialNetPlan.getLinks ()) { jom_ue1.set (e.getIndex () , Double.parseDouble (e.getAttribute("u_1"))); jom_ue2.set (e.getIndex () , Double.parseDouble (e.getAttribute("u_2"))); }
379                final double jomObjcFunc = Double.parseDouble (copyInitialNetPlan.getAttribute("netUtility"));
380                TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_hd1.txt"), jom_hd1);
381                TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_hd2.txt"), jom_hd2);
382                TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_ue1.txt"), jom_ue1);
383                TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_ue2.txt"), jom_ue2);
384                TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_objFunc.txt"), jomObjcFunc);
385                return null;
386        }
387        
388
389        private Triple<Double,Pair<DoubleMatrix1D,DoubleMatrix1D>,Pair<DoubleMatrix1D,DoubleMatrix1D>>  computeObjectiveFunctionFromNetPlan (NetPlan np)
390        {
391                DoubleMatrix1D y1 = DoubleFactory1D.dense.make (E);
392                DoubleMatrix1D y2 = DoubleFactory1D.dense.make (E);
393                DoubleMatrix1D h1 = DoubleFactory1D.dense.make (D1);
394                DoubleMatrix1D h2 = DoubleFactory1D.dense.make (D2);
395                double obj = 0;
396                int counter_d1 = 0; int counter_d2 = 0;
397                for (Demand d : np.getDemands())
398                {
399                        final double h_d = d.getCarriedTraffic();
400                        final int type = (int) this.demandType.get(d.getIndex());
401                        final List<Link> seqLinks = d.getRoutes ().iterator().next().getSeqLinks();
402                        for (Link e : seqLinks)
403                        {
404                                final int index_e = e.getIndex ();
405                                if (type == 1) y1.set(index_e , y1.get(index_e) + h_d); else y2.set(index_e , y2.get(index_e) + h_d); 
406                        }
407                        if (type == 1) 
408                        { 
409                                h1.set (counter_d1 , h_d);
410                                obj += (this.cc_control_fairnessFactor_1.getDouble() == 1)? this.cc_control_weightFairness_1.getDouble() * Math.log(h_d) : this.cc_control_weightFairness_1.getDouble() *  Math.pow(h_d, 1-this.cc_control_fairnessFactor_1.getDouble()) / (1-this.cc_control_fairnessFactor_1.getDouble());
411                                counter_d1 ++; 
412                        } else 
413                        { 
414                                h2.set (counter_d2 , h_d);
415                                obj += (this.cc_control_fairnessFactor_2.getDouble() == 1)? this.cc_control_weightFairness_2.getDouble() * Math.log(h_d) : this.cc_control_weightFairness_2.getDouble() *  Math.pow(h_d, 1-this.cc_control_fairnessFactor_2.getDouble()) / (1-this.cc_control_fairnessFactor_2.getDouble());
416                                counter_d2 ++; 
417                        } 
418                }
419                if ((counter_d1 != D1) || (counter_d2 != D2)) throw new RuntimeException ("Bad");
420                return Triple.of(obj, Pair.of(h1, h2),Pair.of(y1, y2));
421        }
422
423
424        private double computeHdFromPrices (Demand d)
425        {
426                DoubleMatrix1D infoIKnow_price_e = this.control_mostUpdatedLinkPriceKnownDemand_de.viewRow(d.getIndex ());
427
428                /* compute the demand price as weighted sum in the routes of route prices  */
429                double demandWeightedSumLinkPrices = 0;
430                double demandCarriedTraffic = 0; 
431                for (Route r : d.getRoutes ())
432                {
433                        final double h_r = r.getCarriedTraffic();
434                        demandCarriedTraffic += h_r;
435                        for (Link e : r.getSeqLinks())
436                                demandWeightedSumLinkPrices += h_r * infoIKnow_price_e.get(e.getIndex ());
437                }
438                //if (Math.abs(demandCarriedTraffic - this.currentNetPlan.getDemandCarriedTraffic(dIdMe)) > 1E-3) throw new RuntimeException ("Not all the traffic is carried");
439                demandWeightedSumLinkPrices /= demandCarriedTraffic;
440
441                /* compute the new h_d */
442                final double alpha = (demandType.get(d.getIndex ()) == 1)? this.cc_control_fairnessFactor_1.getDouble() : this.cc_control_fairnessFactor_2.getDouble();
443                final double weight = (demandType.get(d.getIndex ()) == 1)? this.cc_control_weightFairness_1.getDouble() : this.cc_control_weightFairness_2.getDouble();
444                final double new_hd = Math.max(this.cc_control_minHd.getDouble() , Math.min(this.cc_control_maxHd.getDouble(), Math.pow(demandWeightedSumLinkPrices/weight, -1/alpha)));
445
446                return new_hd;
447        }
448
449}