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_wirelessCongControlTransmissionPowerAssignment;
020import com.net2plan.interfaces.networkDesign.*;
021import com.net2plan.interfaces.simulation.IEventProcessor;
022import com.net2plan.interfaces.simulation.SimEvent;
023import com.net2plan.libraries.NetworkPerformanceMetrics;
024import com.net2plan.libraries.WirelessUtils;
025import com.net2plan.utils.Constants.RoutingType;
026import com.net2plan.utils.*;
027
028import java.io.File;
029import java.util.HashMap;
030import java.util.List;
031import java.util.Map;
032import java.util.Random;
033
034/** 
035 * This module implements a distributed dual-decomposition-based gradient algorithm, for a coordinated adjustment of the traffic to inject by each demand (congestion control), and the transmission power in each link of the underlying wireless network, to maximize the network utility enforcing a fair allocation of the resources.
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_sec11_4_congControlAndBackpressureRouting_dualDecomp.m">{@code fig_sec11_4_congControlAndBackpressureRouting_dualDecomp.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 Bandwidth assignment (BA), Transmission power optimization, Wireless, Capacity assignment (CA), Distributed algorithm, Dual decomposition 
046 * @net2plan.ocnbooksections Section 11.4
047 * @net2plan.inputParameters 
048 * @author Pablo Pavon-Marino
049 */
050@SuppressWarnings("unchecked")
051public class Online_evProc_congControlAndTransmissionPowerAssignmentDualDecomp extends IEventProcessor
052{
053        private Random rng;
054
055        static final int MAC_SIGNALING_WAKEUPTOSENDMESSAGE = 200;
056        static final int MAC_SIGNALING_RECEIVEDMESSAGE = 201;
057        static final int MAC_UPDATE_WAKEUPTOUPDATE = 202;
058
059        static final int CC_SIGNALING_WAKEUPTOSENDMESSAGE = 400;
060        static final int CC_SIGNALING_RECEIVEDMESSAGE = 401;
061        static final int CC_UPDATE_WAKEUPTOUPDATE = 402;
062
063        
064        private InputParameter mac_signaling_isSynchronous = new InputParameter ("mac_signaling_isSynchronous", false , "true if all the distributed agents involved wake up synchronously to send the signaling messages");
065        private InputParameter mac_signaling_averageInterMessageTime = new InputParameter ("mac_signaling_averageInterMessageTime", 1.0 , "Average time between two signaling messages sent by an agent" , 0 , false , Double.MAX_VALUE , true);
066        private InputParameter mac_signaling_maxFluctuationInterMessageTime = new InputParameter ("mac_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 mac_signaling_averageDelay = new InputParameter ("mac_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 mac_signaling_maxFluctuationInDelay = new InputParameter ("mac_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 mac_signaling_signalingLossProbability = new InputParameter ("mac_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 mac_update_isSynchronous = new InputParameter ("mac_update_isSynchronous", false , "true if all the distributed agents involved wake up synchronousely to update its state");
072        private InputParameter mac_update_averageInterUpdateTime = new InputParameter ("mac_update_averageInterUpdateTime", 1.0 , "Average time between two updates of an agent" , 0 , false , Double.MAX_VALUE , true);
073        private InputParameter mac_update_maxFluctuationInterUpdateTime = new InputParameter ("mac_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);
074
075        private InputParameter mac_gradient_maxGradientAbsoluteNoise = new InputParameter ("mac_gradient_maxGradientAbsoluteNoise", 0.01 , "Max value of the added noise to the gradient coordinate in absolute values" , 0 , true , Double.MAX_VALUE , true);
076        private InputParameter mac_gradient_gammaStep = new InputParameter ("mac_gradient_gammaStep", 1.0 , "Gamma step in the gradient algorithm" , 0 , false , Double.MAX_VALUE , true);
077        private InputParameter mac_gradient_heavyBallBetaParameter = new InputParameter ("mac_gradient_heavyBallBetaParameter", 0.0 , "Beta parameter of heavy ball, between 0 and 1. Value 0 means no heavy ball" , 0 , true , 1.0 , true);
078        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);
079
080        private InputParameter mac_pathLossExponent = new InputParameter ("mac_pathLossExponent", 3.0 , "Exponent in the model for propagation losses" , 0 , true , Double.MAX_VALUE , true);
081        private InputParameter mac_worseRiseOverThermal_nu = new InputParameter ("mac_worseRiseOverThermal_nu", 10.0 , "Worse case ratio between interference power and thermal power at any receiver. Used to set the common thermal power in the receivers" , 0 , true , Double.MAX_VALUE , true);
082        private InputParameter mac_interferenceAttenuationFactor_nu = new InputParameter ("mac_interferenceAttenuationFactor_nu", 1.0e6 , "The interference power received in natural units is divided by this to reduce its effect" , 1 , true , Double.MAX_VALUE , true);
083        private InputParameter mac_maxTransmissionPower_logu = new InputParameter ("mac_maxTransmissionPower_logu", 3.0 , "The maximum link transmission power in logarithmic units (e.g. dBm)");
084        private InputParameter mac_minTransmissionPower_logu = new InputParameter ("mac_minTransmissionPower_logu", 0.0 , "The minimum link transmission power in logarithmic units (e.g. dBm)");
085        private InputParameter mac_simulation_maxNumberOfUpdateIntervals = new InputParameter ("mac_simulation_maxNumberOfUpdateIntervals", 2000.0 , "Maximum number of update intervals in average per agent" , 0 , false , Double.MAX_VALUE , true);
086
087        private InputParameter simulation_randomSeed = new InputParameter ("simulation_randomSeed", (long) 1 , "Seed of the random number generator");
088        private InputParameter simulation_outFileNameRoot = new InputParameter ("simulation_outFileNameRoot", "crossLayerCongControlPowerTransmissionAsignmentDualDecomp" , "Root of the file name to be used in the output files. If blank, no output");
089
090        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");
091        private InputParameter cc_signaling_averageInterMessageTime = new InputParameter ("cc_signaling_averageInterMessageTime", 10.0 , "Average time between two signaling messages sent by an agent" , 0 , false , Double.MAX_VALUE , true);
092        private InputParameter cc_signaling_maxFluctuationInterMessageTime = new InputParameter ("cc_signaling_maxFluctuationInterMessageTime", 5.0 , "Max fluctuation in time between two signaling messages sent by an agent" , 0 , true , Double.MAX_VALUE , true);
093        private InputParameter cc_signaling_averageDelay = new InputParameter ("cc_signaling_averageDelay", 30.0 , "Average time between signaling message transmission by an agent and its reception by other or others" , 0 , true , Double.MAX_VALUE , true);
094        private InputParameter cc_signaling_maxFluctuationInDelay = new InputParameter ("cc_signaling_maxFluctuationInDelay", 15.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);
095        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);
096        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");
097        private InputParameter cc_update_averageInterUpdateTime = new InputParameter ("cc_update_averageInterUpdateTime", 10.0 , "Average time between two updates of an agent" , 0 , false , Double.MAX_VALUE , true);
098        private InputParameter cc_update_maxFluctuationInterUpdateTime = new InputParameter ("cc_update_maxFluctuationInterUpdateTime", 5.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);
099
100        private InputParameter cc_gradient_gammaStep = new InputParameter ("cc_gradient_gammaStep", 0.001 , "Gamma step in the gradient algorithm" , 0 , false , Double.MAX_VALUE , true);
101        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);
102
103        private InputParameter cc_minHd = new InputParameter ("cc_minHd", 0.1 , "Minimum traffic assigned to each demand" , 0 , true , Double.MAX_VALUE , true);
104        private InputParameter cc_maxHd = new InputParameter ("cc_maxHd", 1.0E6 , "Maximum traffic assigned to each demand" , 0 , true , Double.MAX_VALUE , true);
105        private InputParameter cc_fairnessFactor = new InputParameter ("cc_fairnessFactor", 1.0 , "Fairness factor in utility function of congestion control" , 0 , true , Double.MAX_VALUE , true);
106        private InputParameter cc_initialLinkPrices = new InputParameter ("cc_initialLinkPrices", 1 , "Initial value of the link weights" , 0 , true , Double.MAX_VALUE , true);
107        private InputParameter cc_simulation_maxNumberOfUpdateIntervals = new InputParameter ("cc_simulation_maxNumberOfUpdateIntervals", 2000.0 , "Maximum number of update intervals in average per agent" , 0 , false , Double.MAX_VALUE , true);
108
109        private Map<String, String> algorithmParameters;
110        private Map<String, String> net2planParameters;
111
112        private DoubleMatrix1D cc_price_e;
113        private DoubleMatrix2D cc_mostUpdatedLinkPriceKnownByDemand_de; 
114        
115        private DoubleMatrix2D mac_g_nu_ee;
116        private DoubleMatrix1D mac_transmissionPower_logu_e; 
117        private DoubleMatrix1D mac_previousTransmissionPower_logu_e; 
118        private double mac_receptionThermalNoise_nu;
119        private DoubleMatrix2D mac_mostUpdatedMe2ValueKnownByLink1_e1e2; 
120                        
121        private TimeTrace traceOf_pi_e;
122        private TimeTrace traceOf_u_e;
123        private TimeTrace traceOf_y_e;
124        private TimeTrace traceOf_p_e;
125        private TimeTrace traceOf_h_d;
126        private TimeTrace traceOf_objFunction;
127
128        private NetPlan currentNetPlan;
129        private NetPlan copyInitialNetPlan;
130        private int E,N,D;
131        
132        @Override
133        public String getDescription()
134        {
135                return "This module implements a distributed dual-decomposition-based gradient algorithm, for a coordinated adjustment of the traffic to inject by each demand (congestion control), and the transmission power in each link of the underlying wireless network, to maximize the network utility enforcing a fair allocation of the resources.";
136        }
137
138        @Override
139        public List<Triple<String, String, String>> getParameters()
140        {
141                /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */
142                return InputParameter.getInformationAllInputParameterFieldsOfObject(this);
143        }
144
145        @Override
146        public void initialize(NetPlan currentNp, Map<String, String> algorithmParameters, Map<String, String> simulationParameters, Map<String, String> net2planParameters)
147        {
148                /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */
149                InputParameter.initializeAllInputParameterFieldsOfObject(this, algorithmParameters);
150
151                this.currentNetPlan = currentNp;
152                this.copyInitialNetPlan = currentNp.copy();
153                this.algorithmParameters = algorithmParameters;
154                this.net2planParameters = net2planParameters;
155                this.N = this.currentNetPlan.getNumberOfNodes();
156                this.E = this.currentNetPlan.getNumberOfLinks();
157                if (currentNp.getNumberOfLayers() != 1) throw new Net2PlanException ("This algorithm works in single layer networks");
158                if (E == 0) throw new Net2PlanException ("The input design should have links");
159
160                this.rng = new Random (simulation_randomSeed.getLong ());
161                
162                /* INITIALIZE INFORMATION COMMON TO MAC AND CONGESTION CONTROL */
163                this.cc_price_e = DoubleFactory1D.dense.make (E , cc_initialLinkPrices.getDouble());
164                
165                /* Remove all routes, and create one with the shortest path in km for each demand */
166                this.currentNetPlan.removeAllDemands();
167                currentNetPlan.setRoutingTypeAllDemands(RoutingType.SOURCE_ROUTING);
168                for (Node n1 : this.currentNetPlan.getNodes())
169                        for (Node n2 : this.currentNetPlan.getNodes())
170                                if (n1 != n2)
171                                        this.currentNetPlan.addDemand(n1, n2, cc_minHd.getDouble(), RoutingType.SOURCE_ROUTING , null);
172                this.currentNetPlan.addRoutesFromCandidatePathList(currentNetPlan.computeUnicastCandidatePathList(currentNetPlan.getVectorLinkLengthInKm() , 1, -1, -1, -1, -1, -1, -1 , null));
173
174                
175                for (Route r : currentNetPlan.getRoutes ()) r.setCarriedTraffic(r.getDemand().getOfferedTraffic() , r.getDemand().getOfferedTraffic());
176                this.D = this.currentNetPlan.getNumberOfDemands();
177                
178                /* Initialize the gains between links, normalizing them so that the maximum gain is one */
179                this.mac_g_nu_ee = WirelessUtils.computeInterferenceMatrixNaturalUnits (currentNetPlan.getLinks () , mac_interferenceAttenuationFactor_nu.getDouble() , mac_pathLossExponent.getDouble());
180                final double maxGainValue = mac_g_nu_ee.getMaxLocation() [0];
181                mac_g_nu_ee.assign (DoubleFunctions.div(maxGainValue));
182                
183                /* Initialize the thermal noise at the receivers, to have a worse case ROT (rise over thermal) */
184                double worseInterferenceReceivedAtMaxPower_nu = WirelessUtils.computeWorseReceiverInterferencePower_nu (mac_maxTransmissionPower_logu.getDouble()  , mac_g_nu_ee);
185
186                /* Adjust the thermal noise in the receivers so that we have a given ROT */
187                this.mac_receptionThermalNoise_nu = worseInterferenceReceivedAtMaxPower_nu / mac_worseRiseOverThermal_nu.getDouble();
188
189                /* Initialize the transmission power variables */
190                this.mac_transmissionPower_logu_e = DoubleFactory1D.dense.make (E , mac_minTransmissionPower_logu.getDouble());
191                this.mac_previousTransmissionPower_logu_e = DoubleFactory1D.dense.make (E , mac_minTransmissionPower_logu.getDouble());
192                        
193                /* Update the netplan object with the resulting capacities */
194                for (Link e : currentNp.getLinks())
195                        e.setCapacity(Math.log(computeSINR_e (e)));
196
197                this.mac_mostUpdatedMe2ValueKnownByLink1_e1e2 = DoubleFactory2D.dense.make (E,E);
198                for (Link e1 : currentNp.getLinks())
199                {
200                        for (Link otherLink : currentNp.getLinks())
201                                if (e1 != otherLink)
202                                        mac_mostUpdatedMe2ValueKnownByLink1_e1e2.set(e1.getIndex () , otherLink.getIndex(), computeMeFactor_e (otherLink));
203                }
204                
205                /* Initially all nodes receive a "wake up to transmit" event, aligned at time zero or y asynchr => randomly chosen */
206                for (Link e : currentNp.getLinks())
207                {
208                        final double signalingTime = (mac_signaling_isSynchronous.getBoolean())? mac_signaling_averageInterMessageTime.getDouble() : Math.max(0 , mac_signaling_averageInterMessageTime.getDouble() + mac_signaling_maxFluctuationInterMessageTime.getDouble() * (rng.nextDouble() - 0.5));
209                        this.scheduleEvent(new SimEvent (signalingTime , SimEvent.DestinationModule.EVENT_PROCESSOR , MAC_SIGNALING_WAKEUPTOSENDMESSAGE , e));
210                        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));
211                        this.scheduleEvent(new SimEvent (updateTime , SimEvent.DestinationModule.EVENT_PROCESSOR , MAC_UPDATE_WAKEUPTOUPDATE , e));
212                }
213                
214                /* INITIALIZATION CONGESTION CONTROL LAYER */
215
216                /* Initialize the information each demand knows of the prices of all the links */
217                this.cc_mostUpdatedLinkPriceKnownByDemand_de = DoubleFactory2D.dense.make (D,E,cc_initialLinkPrices.getDouble());
218                
219                /* Compute the traffic each demand injects, set it in net2plan, and tell routing layer there was an update in this */
220                for (Demand d : currentNetPlan.getDemands())
221                {
222                        final double h_d = this.computeHdFromPrices(d);
223                        d.setOfferedTraffic(h_d);
224                        d.getRoutes ().iterator().next().setCarriedTraffic(h_d , h_d);
225                }
226                
227                /* Initially all nodes receive a "wake up to transmit" event, aligned at time zero or y asynchr => randomly chosen */
228                for (Link e : currentNetPlan.getLinks())
229                {
230                        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));
231                        this.scheduleEvent(new SimEvent (signalingTime , SimEvent.DestinationModule.EVENT_PROCESSOR , CC_SIGNALING_WAKEUPTOSENDMESSAGE , e));
232                }
233                for (Demand d : currentNetPlan.getDemands())
234                {
235                        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));
236                        this.scheduleEvent(new SimEvent (updateTime , SimEvent.DestinationModule.EVENT_PROCESSOR , CC_UPDATE_WAKEUPTOUPDATE , d));
237                }
238
239                /* INITIALIZE STATISTIC VARIABLES FOR BOTH LAYERS */
240                this.traceOf_u_e = new TimeTrace  ();
241                this.traceOf_y_e = new TimeTrace  ();
242                this.traceOf_p_e = new TimeTrace ();
243                this.traceOf_pi_e = new TimeTrace ();
244                this.traceOf_h_d = new TimeTrace (); 
245                this.traceOf_objFunction = new TimeTrace ();
246
247                this.traceOf_u_e.add(0.0, this.currentNetPlan.getVectorLinkCapacity());
248                this.traceOf_y_e.add(0.0, this.currentNetPlan.getVectorLinkCarriedTraffic());
249                this.traceOf_p_e.add(0.0, this.mac_transmissionPower_logu_e.copy ());
250                this.traceOf_pi_e.add(0.0, this.cc_price_e.copy ());
251                this.traceOf_h_d.add(0.0, this.currentNetPlan.getVectorDemandCarriedTraffic());
252                this.traceOf_objFunction.add(0.0 , NetworkPerformanceMetrics.alphaUtility(this.currentNetPlan.getVectorDemandOfferedTraffic() , cc_fairnessFactor.getDouble()));
253                
254        }
255
256        @Override
257        public void processEvent(NetPlan currentNetPlan, SimEvent event)
258        {
259                final double t = event.getEventTime();
260                switch (event.getEventType())
261                {
262                case MAC_SIGNALING_RECEIVEDMESSAGE: // A node broadcasts signaling info to its 1 hop neighbors
263                {
264                        final Pair<Link,Pair<Link,Double>> signalInfo = (Pair<Link,Pair<Link,Double>>) event.getEventObject();
265                        final Link eMe = signalInfo.getFirst();
266                        final Link otherLink = signalInfo.getSecond().getFirst();
267                        final double otherLink_me = signalInfo.getSecond().getSecond();
268                        mac_mostUpdatedMe2ValueKnownByLink1_e1e2.set (eMe.getIndex () , otherLink.getIndex () , otherLink_me);
269                        break;
270                }
271                
272                case MAC_SIGNALING_WAKEUPTOSENDMESSAGE: // A node broadcasts signaling info to its 1 hop neighbors
273                {
274                        final Link eMe = (Link) event.getEventObject();
275
276                        Pair<Link,Double> infoToSignal = Pair.of(eMe , this.computeMeFactor_e(eMe));
277                        if (rng.nextDouble() >= this.mac_signaling_signalingLossProbability.getDouble()) // the signaling may be lost => lost to all nodes
278                                for (Link otherLink : currentNetPlan.getLinks ())
279                                        if (otherLink != eMe)
280                                        {
281                                                final double signalingReceptionTime = t + Math.max(0 , mac_signaling_averageDelay.getDouble() + mac_signaling_maxFluctuationInDelay.getDouble() * (rng.nextDouble() - 0.5));
282                                                this.scheduleEvent(new SimEvent (signalingReceptionTime , SimEvent.DestinationModule.EVENT_PROCESSOR , MAC_SIGNALING_RECEIVEDMESSAGE , Pair.of(otherLink , infoToSignal)));
283                                        }
284
285                        /* Re-schedule when to wake up again */
286                        final double signalingTime = mac_signaling_isSynchronous.getBoolean()? t + mac_signaling_averageInterMessageTime.getDouble() : Math.max(t , t + mac_signaling_averageInterMessageTime.getDouble() + mac_signaling_maxFluctuationInterMessageTime.getDouble() * (rng.nextDouble() - 0.5));
287                        this.scheduleEvent(new SimEvent (signalingTime , SimEvent.DestinationModule.EVENT_PROCESSOR , MAC_SIGNALING_WAKEUPTOSENDMESSAGE , eMe));
288                        break;
289                }
290
291                case MAC_UPDATE_WAKEUPTOUPDATE: 
292                {
293                        final Link eMe = (Link) event.getEventObject(); 
294                        
295                        final double currentTransmissionPower_logu = this.mac_transmissionPower_logu_e.get(eMe.getIndex ());
296                        double gradientThisLink = computeGradient (eMe) + 2*mac_gradient_maxGradientAbsoluteNoise.getDouble()*(rng.nextDouble()-0.5);
297                        double nextTransmissionPower_logu = currentTransmissionPower_logu + this.mac_gradient_gammaStep.getDouble() * gradientThisLink;
298
299                        /* heavy ball */
300                        nextTransmissionPower_logu += this.mac_gradient_heavyBallBetaParameter.getDouble() * (currentTransmissionPower_logu - mac_previousTransmissionPower_logu_e.get(eMe.getIndex ()));
301                        /* projection */
302                        nextTransmissionPower_logu = GradientProjectionUtils.euclideanProjection_boxLike(nextTransmissionPower_logu, this.mac_minTransmissionPower_logu.getDouble(), this.mac_maxTransmissionPower_logu.getDouble());
303
304                        if (mac_gradient_maxGradientCoordinateChange.getDouble() > 0)
305                                nextTransmissionPower_logu = GradientProjectionUtils.scaleDown_maxAbsoluteCoordinateChange (currentTransmissionPower_logu , nextTransmissionPower_logu ,  mac_gradient_maxGradientCoordinateChange.getDouble());
306                        
307                        this.mac_previousTransmissionPower_logu_e.set(eMe.getIndex (), this.mac_transmissionPower_logu_e.get(eMe.getIndex ()));
308                        this.mac_transmissionPower_logu_e.set(eMe.getIndex (), nextTransmissionPower_logu);
309
310                        /* Send event next recomputing time */
311                        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));
312                        this.scheduleEvent(new SimEvent (updateTime , SimEvent.DestinationModule.EVENT_PROCESSOR , MAC_UPDATE_WAKEUPTOUPDATE,  eMe));
313
314                        /* Update in currentNetPlan the capacity values */
315                        for (Link e : this.currentNetPlan.getLinks())
316                                e.setCapacity(Math.log(computeSINR_e (e)));
317
318                        this.traceOf_u_e.add(t, this.currentNetPlan.getVectorLinkCapacity());
319                        this.traceOf_y_e.add(t, this.currentNetPlan.getVectorLinkCarriedTraffic());
320                        this.traceOf_p_e.add(t, this.mac_transmissionPower_logu_e.copy ());
321                        this.traceOf_pi_e.add(t, this.cc_price_e.copy ());
322                        this.traceOf_h_d.add(t, this.currentNetPlan.getVectorDemandCarriedTraffic());
323                        this.traceOf_objFunction.add(t , NetworkPerformanceMetrics.alphaUtility(this.currentNetPlan.getVectorDemandOfferedTraffic() , cc_fairnessFactor.getDouble()));
324
325                        if (t > this.mac_simulation_maxNumberOfUpdateIntervals.getDouble() * this.mac_update_averageInterUpdateTime.getDouble()) { this.endSimulation (); }
326                        break;
327                }
328
329                case CC_SIGNALING_RECEIVEDMESSAGE: // A node receives from an out neighbor the q_nt for any destination
330                {
331                        final Pair<Demand,Pair<Link,Double>> signalInfo = (Pair<Demand,Pair<Link,Double>>) event.getEventObject();
332                        final Demand d = signalInfo.getFirst();
333                        final Pair<Link,Double> receivedInfo_price_e = signalInfo.getSecond();
334                        this.cc_mostUpdatedLinkPriceKnownByDemand_de.set(d.getIndex() , receivedInfo_price_e.getFirst().getIndex () ,receivedInfo_price_e.getSecond());  
335                        break;
336                }
337                
338                case CC_SIGNALING_WAKEUPTOSENDMESSAGE: // A node broadcasts signaling info to its 1 hop neighbors
339                {
340                        final Link eMe = (Link) event.getEventObject();
341
342                        /* Update the new price with the gradient approach */
343                        final double u_e = eMe.getCapacity();
344                        final double y_e = eMe.getCarriedTraffic();
345                        final double old_pie = this.cc_price_e.get(eMe.getIndex());
346                        final double new_pie = Math.max(0, old_pie - this.cc_gradient_gammaStep.getDouble() * (u_e - y_e) + 2*cc_gradient_maxGradientAbsoluteNoise.getDouble()*(rng.nextDouble()-0.5));
347                        this.cc_price_e.set(eMe.getIndex(), new_pie);
348
349                        /* Create the info I will signal */
350                        Pair<Link,Double> infoToSignal = Pair.of(eMe ,  new_pie);
351
352                        /* Send the events of the signaling information messages to all the nodes */
353                        for (Route route : eMe.getTraversingRoutes())
354                        {
355                                if (rng.nextDouble() < this.cc_signaling_signalingLossProbability.getDouble()) continue; // the signaling may be lost => lost to all demands
356                                final Demand d = route.getDemand();
357                                final double signalingReceptionTime = t + Math.max(0 , cc_signaling_averageDelay.getDouble() + cc_signaling_maxFluctuationInDelay.getDouble() * (rng.nextDouble() - 0.5));
358                                this.scheduleEvent(new SimEvent (signalingReceptionTime , SimEvent.DestinationModule.EVENT_PROCESSOR , CC_SIGNALING_RECEIVEDMESSAGE , Pair.of(d , infoToSignal)));
359                        }
360
361                        /* Re-schedule when to wake up again */
362                        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));
363                        this.scheduleEvent(new SimEvent (signalingTime , SimEvent.DestinationModule.EVENT_PROCESSOR , CC_SIGNALING_WAKEUPTOSENDMESSAGE , eMe));
364                        break;
365                }
366
367                case CC_UPDATE_WAKEUPTOUPDATE: // a node updates its p_n, p_e values, using most updated info available
368                {
369                        final Demand dMe = (Demand) event.getEventObject();
370                        
371                        /* compute the new h_d and apply it */
372                        final double new_hd = computeHdFromPrices (dMe);
373                        dMe.setOfferedTraffic(new_hd);
374                        dMe.getRoutes ().iterator().next().setCarriedTraffic(new_hd , new_hd);
375
376                        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));
377                        this.scheduleEvent(new SimEvent (updateTime , SimEvent.DestinationModule.EVENT_PROCESSOR , CC_UPDATE_WAKEUPTOUPDATE,  dMe));
378
379                        this.traceOf_objFunction.add(t, NetworkPerformanceMetrics.alphaUtility(this.currentNetPlan.getVectorDemandOfferedTraffic() , cc_fairnessFactor.getDouble()));
380
381                        if (t > this.cc_simulation_maxNumberOfUpdateIntervals.getDouble() * this.cc_update_averageInterUpdateTime.getDouble()) { this.endSimulation (); }
382                        break;
383                }
384                
385                
386                default: throw new RuntimeException ("Unexpected received event");
387                }
388                
389                
390        }
391
392        public String finish (StringBuilder st , double simTime)
393        {
394                if (simulation_outFileNameRoot.getString().equals("")) return null;
395                this.traceOf_objFunction.printToFile(new File (simulation_outFileNameRoot.getString() + "_objFunc.txt"));
396                this.traceOf_u_e.printToFile(new File (simulation_outFileNameRoot.getString() + "_ue.txt"));
397                this.traceOf_y_e.printToFile(new File (simulation_outFileNameRoot.getString() + "_ye.txt"));
398                this.traceOf_p_e.printToFile(new File (simulation_outFileNameRoot.getString() + "_pe.txt"));
399                this.traceOf_pi_e.printToFile(new File (simulation_outFileNameRoot.getString() + "_pie.txt"));
400                this.traceOf_h_d.printToFile(new File (simulation_outFileNameRoot.getString() + "_hd.txt"));
401
402                Map<String,String> param = new HashMap<String,String> (algorithmParameters);
403                param.put("solverName", "ipopt");
404                param.put("solverLibraryName", "");
405                param.put("maxSolverTimeInSeconds", "-1");
406                new Offline_cba_wirelessCongControlTransmissionPowerAssignment ().executeAlgorithm(copyInitialNetPlan , param , this.net2planParameters);
407                TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_objFunc.txt") , NetworkPerformanceMetrics.alphaUtility(copyInitialNetPlan.getVectorDemandOfferedTraffic() , cc_fairnessFactor.getDouble()));
408                TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_ue.txt") , copyInitialNetPlan.getVectorLinkCapacity());
409                TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_ye.txt") , copyInitialNetPlan.getVectorLinkCarriedTraffic());
410                TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_hd.txt") , copyInitialNetPlan.getVectorDemandCarriedTraffic());
411                TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_pe.txt") , copyInitialNetPlan.getVectorAttributeValues(copyInitialNetPlan.getLinks () , "p_e"));
412                TimeTrace.printToFile(new File (simulation_outFileNameRoot.getString() + "_jom_pie.txt") , copyInitialNetPlan.getVectorAttributeValues(copyInitialNetPlan.getLinks () , "pi_e"));
413                return null;
414        }
415        
416
417        private double computeGradient (Link thisLink)
418        {
419                final DoubleMatrix1D infoIKnow_me = mac_mostUpdatedMe2ValueKnownByLink1_e1e2.viewRow (thisLink.getIndex ());
420                
421                double gradient = cc_price_e.get(thisLink.getIndex ()); 
422                double accumFactor = 0;
423                for (Link epp : this.currentNetPlan.getLinks())
424                        if (epp != thisLink)
425                                accumFactor += this.mac_g_nu_ee.get(thisLink.getIndex (),epp.getIndex ()) * infoIKnow_me.get(epp.getIndex ());
426                accumFactor *= Math.exp(this.mac_transmissionPower_logu_e.get(thisLink.getIndex ()));
427                gradient -= accumFactor;
428                return gradient;
429        }
430
431        private double computeMeFactor_e (Link e)
432        {
433                final double snr_e = Math.exp(e.getCapacity());
434                final double pi_e = this.cc_price_e.get(e.getIndex ());
435                final double g_ee = this.mac_g_nu_ee.get(e.getIndex (),e.getIndex ());
436                return  pi_e * snr_e / (Math.exp(this.mac_transmissionPower_logu_e.get(e.getIndex ())) * g_ee);
437        }
438        
439        private double computeSINR_e (Link e)
440        {
441                final double receivedPower_nu = Math.exp(this.mac_transmissionPower_logu_e.get(e.getIndex())) * this.mac_g_nu_ee.get(e.getIndex (),e.getIndex ());
442                double interferencePower_nu = this.mac_receptionThermalNoise_nu;
443                for (Link eInt : this.currentNetPlan.getLinks ())
444                        if (eInt != e) interferencePower_nu += Math.exp(this.mac_transmissionPower_logu_e.get(eInt.getIndex())) * this.mac_g_nu_ee.get(eInt.getIndex (),e.getIndex ());
445                return receivedPower_nu / interferencePower_nu;
446        }
447
448        private double computeHdFromPrices (Demand d)
449        {
450                DoubleMatrix1D infoIKnow_price_e = this.cc_mostUpdatedLinkPriceKnownByDemand_de.viewRow(d.getIndex());
451
452                /* compute the demand price as weighted sum in the routes of route prices  */
453                double demandWeightedSumLinkPrices = 0;
454                double demandCarriedTraffic = 0; 
455                for (Route r : d.getRoutes ())
456                {
457                        final double h_r = r.getCarriedTraffic();
458                        demandCarriedTraffic += h_r;
459                        for (Link e : r.getSeqLinks())
460                                demandWeightedSumLinkPrices += h_r * infoIKnow_price_e.get(e.getIndex());
461                }
462                //if (Math.abs(demandCarriedTraffic - this.currentNetPlan.getDemandCarriedTraffic(dIdMe)) > 1E-3) throw new RuntimeException ("Not all the traffic is carried");
463                demandWeightedSumLinkPrices /= demandCarriedTraffic;
464
465                /* compute the new h_d */
466                final double new_hd = Math.max(this.cc_minHd.getDouble() , Math.min(this.cc_maxHd.getDouble(), Math.pow(demandWeightedSumLinkPrices, -1/this.cc_fairnessFactor.getDouble())));
467
468                return new_hd;
469        }
470
471}