001package com.net2plan.examples.general.offline;
002/*******************************************************************************
003 * Copyright (c) 2017 Pablo Pavon Marino and others.
004 * All rights reserved. This program and the accompanying materials
005 * are made available under the terms of the 2-clause BSD License 
006 * which accompanies this distribution, and is available at
007 * https://opensource.org/licenses/BSD-2-Clause
008 *
009 * Contributors:
010 *     Pablo Pavon Marino and others - initial API and implementation
011 *******************************************************************************/
012
013
014import cern.colt.matrix.tdouble.DoubleFactory2D;
015import cern.colt.matrix.tdouble.DoubleMatrix2D;
016import com.net2plan.interfaces.networkDesign.*;
017import com.net2plan.libraries.WDMUtils;
018import com.net2plan.utils.Constants.OrderingType;
019import com.net2plan.utils.Constants.RoutingType;
020import com.net2plan.utils.*;
021
022import java.util.*;
023
024/**
025 * Algorithm based on an heuristic solving the Routing, Spectrum, Modulation Assignment (RSMA) problem with regenerator placement, 
026 * in flexi (elastic) or fixed grid optical WDM networks, with or without fault tolerance and/or latency requisites.
027 * 
028 * <p>The input design typically has two layers (IP and WDM layers), according to the typical conventions in {@link com.net2plan.libraries.WDMUtils WDMUtils}. 
029 * If the design has one single layer, it is first converted into a two-layer design: WDM layer taking the links (fibers) with no demands, 
030 * IP layer taking the traffic demands, without IP links. Any previous routes are removed.</p>
031 * <p>The WDM layer is compatible with {@link com.net2plan.libraries.WDMUtils WDMUtils} Net2Plan 
032 * library usual assumptions:</p>
033 * <ul>
034 * <li>Each network node is assumed to be an Optical Add/Drop Multiplexer WDM node</li>
035 * <li>Each network link at WDM layer is assumed to be an optical fiber.</li>
036 * <li>The spectrum in the fibers is assumed to be composed of a number of frequency slots. 
037 * In fixed-grid network, each frequency slot would correspond to a wavelength channel. 
038 * In flexi-grid networks, it is just a frequency slot, and lightpaths can occupy more than one. In any case, 
039 * two lightpaths that use overlapping frequency slots cannot traverse the same fiber, since their signals would mix.</li>
040 * <li>No traffic demands initially exist at the WDM layer. In this algorithms, each lightpath is associated to a WDM demand </li>
041 * <li> Each traffic demand at the IP layer is a need to transmit an amount of Gbps between two nodes. 
042 * A demand traffic can be carried using one or more lightpaths.</li>
043 * </ul>
044 * 
045 * <p>Each lightpath (primary or backup) produced by the design is returned as a {@code Route} object.</p>
046 * 
047 * <p>Each lightpath starts and ends in a transponder. The user is able to define a set of available transpoder types, so 
048 * the design can create lightpaths using any combination of them. The information user-defined per transponder is:</p>
049 * <ul>
050 * <li>Line rate in Gbps (typically 10, 40, 100 in fixed-grid cases, and other multiples in flexi-grid networks).</li> 
051 * <li>Cost</li> 
052 * <li>Number of frequency slots occupied (for a given line rate, this depends on the modulation the transponder uses)</li> 
053 * <li>Optical reach in km: Maximum allowed length in km of the lightpaths with this transponder. 
054 * Higher distances can be reached using signal regenerators.</li> 
055 * <li>Cost of a regenerator for this transponder. Regenerators can be placed at intermdiate nodes of the lightpath route, 
056 * regenerate its optical signal, and then permit extending its reach. We consider that regenerators cannot change the 
057 * frequency slots occupied by the lightpath (that is, they are not capable of wavelength conversion)</li> 
058 * </ul>
059 * 
060 * <p>We assume that all the fibers use the same wavelength grid, composed of a user-defined number of frequency slots. 
061 * The user can also select a limit in the maximum propagation delay of a lightpath. </p>
062 * <p>The output design consists in the set of lightpaths to establish, in the 1+1 case also with a 1+1 lightpath each. 
063 * Each lightpath is characterized by the transponder type used (which sets its line rate and number of occupied slots in the 
064 * traversed fibers), the particular set of contiguous frequency slots occupied, and the set of signal regeneration points (if any). 
065 * This information is stored in the {@code Route} object using the regular methods in WDMUTils, 
066 * and can be retrieved in the same form (e.g. by a report showing the WDM network information). 
067 * If a feasible solution is not found (one where all the demands are satisfied with the given constraints), a message is shown.</p>.  
068 * 
069 * <h2>Failure tolerance</h2>
070 * <p>The user can choose among three possibilities for designing the network:</p>
071 * <ul>
072 * <li>No failure tolerant: The lightpaths established should be enough to carry the traffic of all the demands when no failure 
073 * occurs in the network, but any losses are accepted if the network suffers failures in links or nodes.</li>
074 * <li>Tolerant to single-SRG (Shared Risk Group) failures with static lightpaths: All the traffic demands should be satisfied using 
075 * lightpaths, so that under any single-SRG failure (SRGs are taken from the input design), the surviving lightpaths are enough 
076 * to carry the 100% of the traffic. Note that lightpaths are static, in the sense that they are not rerouted when affected by a 
077 * failure (they just also fail), and the design should just overprovision the number of lightpaths to establish with that in mind.</li>
078 * <li>1+1 SRG-disjoint protection: This is another form to provide single-SRG failure tolerance. Each lightpath is backed up by 
079 * a SRG-disjoint lightpath. The backup lightpath uses the same type of transponder 
080 * as the primary (and thus the same line rate, an occupies the same number of slots), its path is SRG-disjoint, and the particular 
081 * set of slots occupied can be different. </li>
082 * </ul>
083 * <h2>Use cases</h2>
084 * <p>This algorithm is quite general, and fits a number of use cases designing WDM networks, for instance:</p>
085 * <ul>
086 * <li>Single line rate, fixed grid networks: Then, one single type of transponder will be available, which occupies one frequency slot</li>
087 * <li>Mixed-Line Rate fixed-grid networks: In this case, several transponders can be available at different line rates and with different optical 
088 * reaches. However, all of them occupy one slot (one wavelength channel)</li>
089 * <li>Single line rate, flexi-grid networks using varying-modulation transponders: Several transponders are available (or the same 
090 * transponder with varying configurations), all of them with the same line rate, but thanks to the different usable modulations 
091 * they can have different optical reaches and/or number of occupied slots.</li>
092 * <li>Multiple line rate, flexi-grid networks using Bandwidth Variable Transponders: Here, it is possible to use different transponders 
093 * with different line rates, e.g. to reflect more sophisticated transponders which can have different configurations, varying its line rate, 
094 * optical reach, and number of occupied slots.</li>
095 * <li>...</li>
096 * </ul>
097 * <h2>Some details of the algorithm</h2>
098 * <p>The algorithm is based on a heuristic. Initially, at most {@code k} paths are selected for each demand and transponder type. 
099 * Then, in each iteration, the algorithm first orders the demands in descending order according to the traffic pending 
100 * to be carried (if single-SRG failure tolerance is chosen, this is the average among all the 
101 * states -no failure and single SRG failure). Then, all the transponder types and possible routes (or SRG-disjoint 1+1 pairs in the 
102 * 1+1 case) are attempted for that demand, using a first-fit approach for the slots. If an RSA is found for more than one 
103 * transponder and route, the one chosen is first, the one with best performance metric, and among them, the first transponder 
104 * according to the order in which the user put it in the input parameter, and among them the shortest one in km . 
105 * The performance metric used is the amount of extra traffic carried if the lightpath is established, divided by the lightpath cost, 
106 * summing the transponder cost, and the cost of the signal regenerators if any.</p>
107 * <p>The details of the algorithm will be provided in a publication currently under elaboration.</p>
108 * 
109 * @net2plan.keywords WDM
110 * @net2plan.inputParameters 
111 * @author Pablo Pavon-Marino
112 */
113@SuppressWarnings("unchecked")
114public class Offline_ipOverWdm_routingSpectrumAndModulationAssignmentHeuristicNotGrooming implements IAlgorithm
115{
116        private InputParameter k = new InputParameter ("k", (int) 5 , "Maximum number of admissible paths per input-output node pair" , 1 , Integer.MAX_VALUE);
117        private InputParameter numFrequencySlotsPerFiber = new InputParameter ("numFrequencySlotsPerFiber", (int) 40 , "Number of wavelengths per link" , 1, Integer.MAX_VALUE);
118        private InputParameter transponderTypesInfo = new InputParameter ("transponderTypesInfo", "10 1 1 9600 1" , "Transpoder types separated by \";\" . Each type is characterized by the space-separated values: (i) Line rate in Gbps, (ii) cost of the transponder, (iii) number of slots occupied in each traversed fiber, (iv) optical reach in km (a non-positive number means no reach limit), (v) cost of the optical signal regenerator (regenerators do NOT make wavelength conversion ; if negative, regeneration is not possible).");
119        private InputParameter ipLayerIndex = new InputParameter ("ipLayerIndex", (int) 1 , "Index of the IP layer (-1 means default layer)");
120        private InputParameter wdmLayerIndex = new InputParameter ("wdmLayerIndex", (int) 0 , "Index of the WDM layer (-1 means default layer)");
121        private InputParameter networkRecoveryType = new InputParameter ("networkRecoveryType", "#select# not-fault-tolerant single-srg-tolerant-static-lp 1+1-srg-disjoint-lps" , "Establish if the design should be tolerant or not to single SRG failures (SRGs are as defined in the input NetPlan). First option is that the design should not be fault tolerant, the second means that failed lightpaths are not recovered, but an overprovisioned should be made so enough lightpaths survive to carry all the traffic in every failure. The third means that each lightpath is 1+1 protceted by a SRG-disjoint one, that uses the same transponder");
122        private InputParameter maxPropagationDelayMs = new InputParameter ("maxPropagationDelayMs", (double) -1 , "Maximum allowed propagation time of a lighptath in miliseconds. If non-positive, no limit is assumed");
123        
124        private NetPlan netPlan;
125        private SortedMap<Pair<Node,Node>,List<List<Link>>> cpl;
126        private SortedMap<Pair<Node,Node>,List<Pair<List<Link>,List<Link>>>> cpl11;
127        private NetworkLayer wdmLayer, ipLayer;
128        private WDMUtils.TransponderTypesInfo tpInfo;
129        private int N, Ewdm, Dip, S, T;
130        private boolean singleSRGToleranceNot11Type;
131        private DoubleMatrix2D frequencySlot2FiberOccupancy_se;
132        private Demand.IntendedRecoveryType recoveryTypeNewLps;
133        
134        @Override
135        public String executeAlgorithm(NetPlan netPlan, Map<String, String> algorithmParameters, Map<String, String> net2planParameters)
136        {
137                /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */
138                InputParameter.initializeAllInputParameterFieldsOfObject(this, algorithmParameters);
139
140                this.netPlan = netPlan;
141                
142                /* Create a two-layer IP over WDM design if the input is single layer */
143                if (netPlan.getNumberOfLayers() == 1)
144                {
145                        this.wdmLayer = netPlan.getNetworkLayerDefault();
146                        this.netPlan.setDemandTrafficUnitsName("Gbps");
147                        this.netPlan.setLinkCapacityUnitsName("Frequency slots");
148                        this.ipLayer = netPlan.addLayer("IP" , "IP layer" , "Gbps" , "Gbps" , null , null);
149                        for (Demand wdmDemand : netPlan.getDemands(wdmLayer))
150                                netPlan.addDemand(wdmDemand.getIngressNode(), wdmDemand.getEgressNode() , wdmDemand.getOfferedTraffic() , RoutingType.HOP_BY_HOP_ROUTING , wdmDemand.getAttributes() , ipLayer);
151                }
152                else
153                {
154                        this.wdmLayer = wdmLayerIndex.getInt () == -1? netPlan.getNetworkLayerDefault() : netPlan.getNetworkLayer(wdmLayerIndex.getInt ());
155                        this.ipLayer = ipLayerIndex.getInt () == -1? netPlan.getNetworkLayerDefault() : netPlan.getNetworkLayer(ipLayerIndex.getInt ());
156                }
157
158                /* Basic checks */
159                this.N = netPlan.getNumberOfNodes();
160                this.Ewdm = netPlan.getNumberOfLinks(wdmLayer);
161                this.Dip = netPlan.getNumberOfDemands(ipLayer);
162                this.S = numFrequencySlotsPerFiber.getInt();
163                if (N == 0 || Ewdm == 0 || Dip == 0) throw new Net2PlanException("This algorithm requires a topology with links and a demand set");
164                this.singleSRGToleranceNot11Type = networkRecoveryType.getString().equals("single-srg-tolerant-static-lp");
165                
166                if (singleSRGToleranceNot11Type && (netPlan.getNumberOfSRGs() == 0)) throw new Net2PlanException ("No SRGs are defined, so there is no reason to use the single-SRG failure-tolerant design option");
167                
168                if (networkRecoveryType.getString().equals("not-fault-tolerant") || networkRecoveryType.getString().equals("single-srg-tolerant-static-lp"))
169                        recoveryTypeNewLps = Demand.IntendedRecoveryType.NONE;
170                else if (networkRecoveryType.getString().equals("1+1-srg-disjoint-lps"))
171                        recoveryTypeNewLps = Demand.IntendedRecoveryType.PROTECTION_REVERT;
172                else throw new Net2PlanException ("Wrong input parameters");
173                
174                /* Store transpoder info */
175                WDMUtils.setFibersNumFrequencySlots(netPlan, S, wdmLayer);
176                this.tpInfo = new WDMUtils.TransponderTypesInfo(transponderTypesInfo.getString());
177                this.T = tpInfo.getNumTypes();
178
179                /* Remove all routes in current netPlan object. Initialize link capacities and attributes, and demand offered traffic */
180                /* WDM and IP layer are in source routing type */
181                netPlan.removeAllLinks (ipLayer);
182                netPlan.removeAllDemands (wdmLayer);
183                netPlan.removeAllMulticastDemands(wdmLayer);
184
185                /* Initialize the slot occupancy */
186                this.frequencySlot2FiberOccupancy_se = DoubleFactory2D.dense.make(S , Ewdm); 
187
188                /* Compute the candidate path list of possible paths */
189                this.cpl = netPlan.computeUnicastCandidatePathList(netPlan.getVectorLinkLengthInKm(wdmLayer) , k.getInt(), tpInfo.getMaxOpticalReachKm() , -1, maxPropagationDelayMs.getDouble(), -1, -1, -1 , null , wdmLayer);
190                this.cpl11 = networkRecoveryType.getString().equals("1+1-srg-disjoint-lps")? NetPlan.computeUnicastCandidate11PathList(cpl,0) : null;
191                
192                /* Compute the CPL, adding the routes */
193                /* 1+1 case: as many routes as 1+1 valid pairs (then, the same sequence of links can be in more than one Route).  */
194                /* rest of the cases: each sequence of links appears at most once */
195                Map<Link,Double> linkLengthMap = new HashMap<Link,Double> (); for (Link e : netPlan.getLinks(wdmLayer)) linkLengthMap.put(e , e.getLengthInKm());
196                final int maximumNumberOfPaths = T*k.getInt()*Dip;
197                List<Integer> transponderType_p = new ArrayList<Integer> (maximumNumberOfPaths);
198                List<Double> cost_p = new ArrayList<Double> (maximumNumberOfPaths); 
199                List<Double> lineRate_p = new ArrayList<Double> (maximumNumberOfPaths); 
200                List<Integer> numSlots_p = new ArrayList<Integer> (maximumNumberOfPaths);
201                List<List<Link>> seqLinks_p = new ArrayList<List<Link>> (maximumNumberOfPaths);
202                List<List<Link>> seqLinks2_p = cpl11 == null? null : new ArrayList<List<Link>> (maximumNumberOfPaths);
203                List<int []> regPositions_p = new ArrayList<int []> (maximumNumberOfPaths);
204                List<int []> regPositions2_p = new ArrayList<int []> (maximumNumberOfPaths);
205                List<Demand> ipDemand_p = new ArrayList<Demand> (maximumNumberOfPaths);
206                Map<Demand,List<Integer>> ipDemand2WDMPathListMap = new HashMap<Demand,List<Integer>> (); 
207                for (Demand ipDemand : netPlan.getDemands(ipLayer))
208                {
209                        final Pair<Node,Node> nodePair = Pair.of(ipDemand.getIngressNode() , ipDemand.getEgressNode());
210                        boolean atLeastOnePathOrPathPair = false;
211                        List<Integer> pathListThisDemand = new LinkedList<Integer> ();
212                        ipDemand2WDMPathListMap.put(ipDemand , pathListThisDemand);
213                        for (int t = 0 ; t < T ; t ++)
214                        {
215                                final boolean isRegenerable = tpInfo.isOpticalRegenerationPossible(t);
216                                for (Object sp : cpl11 != null? cpl11.get(nodePair) : cpl.get(nodePair))
217                                {
218                                        List<Link> firstPath = (cpl11 == null)? ((List<Link>) sp) : ((Pair<List<Link>,List<Link>>) sp).getFirst(); 
219                                        List<Link> secondPath = (cpl11 == null)? null : ((Pair<List<Link>,List<Link>>) sp).getSecond (); 
220                                        if (!isRegenerable && (getLengthInKm(firstPath) > tpInfo.getOpticalReachKm(t))) break;
221                                        if (secondPath != null) if (!isRegenerable && (getLengthInKm(secondPath) > tpInfo.getOpticalReachKm(t))) break;
222
223                                        final int [] regPositions1 = isRegenerable? WDMUtils.computeRegeneratorPositions(firstPath , tpInfo.getOpticalReachKm(t)) : new int [firstPath.size()];
224                                        final int [] regPositions2 = cpl11 == null? null : isRegenerable? WDMUtils.computeRegeneratorPositions(secondPath , tpInfo.getOpticalReachKm(t)) : new int [secondPath.size()];
225                                        final int numRegeneratorsNeeded = !isRegenerable? 0 : (int) IntUtils.sum(regPositions1) + (secondPath == null? 0 : (int) IntUtils.sum(regPositions2)) ;
226                                        final double costOfLightpathOr11Pair = tpInfo.getCost(t) * (cpl11 != null? 2 : 1) + (tpInfo.getRegeneratorCost(t) * numRegeneratorsNeeded);
227
228                                        final int pathIndex = cost_p.size();
229                                        cost_p.add (costOfLightpathOr11Pair);
230                                        transponderType_p.add (t);
231                                        lineRate_p.add(tpInfo.getLineRateGbps(t));
232                                        numSlots_p.add(tpInfo.getNumSlots(t));
233                                        ipDemand_p.add(ipDemand);
234                                        seqLinks_p.add(firstPath);
235                                        regPositions_p.add(regPositions1);
236                                        pathListThisDemand.add(pathIndex);
237                                        if (cpl11 != null) { seqLinks2_p.add(secondPath); regPositions2_p.add(regPositions2); }
238                                        atLeastOnePathOrPathPair = true;
239                                }
240                        }
241                        if (!atLeastOnePathOrPathPair) throw new Net2PlanException ("There are no possible routes (or 1+1 pairs) for a demand (" + ipDemand + "). The topology may be not connected enough, or the optical reach may be too small");
242                }
243                final int P = transponderType_p.size(); // one per potential sequence of links (or 1+1 pairs of sequences) and transponder
244
245                /* Main algorithm loop. Take one demand at a time, in a HLDA loop (ordered by average blocked traffic). 
246                 * In each demand, try all possible path-transponder pairs, and take the best according to the performance metric:
247                 * avExtraTrafficCarried/transponderCost */
248                boolean atLeastOneLpAdded = false;
249                Set<Integer> ipDemandIndexesNotToTry = new HashSet<Integer> ();
250                double totalCost = 0;
251                do 
252                {
253                        double [] b_d = getVectorIPDemandAverageAllStatesBlockedTraffic ();
254                        int [] ipDemandIndexes = DoubleUtils.sortIndexes(b_d , OrderingType.DESCENDING);
255                        atLeastOneLpAdded = false;
256                        for (int ipDemandIndex : ipDemandIndexes)
257                        {
258                                /* Not to try a demand if already fully satisfied or we tried and could not add a lp to it */
259                                if (ipDemandIndexesNotToTry.contains(ipDemandIndex)) continue;
260
261                                final Demand ipDemand = netPlan.getDemand(ipDemandIndex , ipLayer);
262                                ipDemand.setRoutingType(RoutingType.SOURCE_ROUTING);
263
264                                /* If the demand is already fully satisfied, skip it */
265                                if (isIpDemandFullySatisfied(ipDemand)) { ipDemandIndexesNotToTry.add(ipDemandIndex); continue; } 
266                                
267                                /* Try all the possible routes and all the possible transpoder types. Take the solution with the best 
268                                 * performance metric (average extra carried traffic / transponder cost) */
269                                WDMUtils.RSA best_rsa = null;
270                                WDMUtils.RSA best_rsa2 = null;
271                                double best_performanceMetric = 0;
272                                int best_pathIndex = -1;
273                                for (int pathIndex : ipDemand2WDMPathListMap.get (ipDemand))
274                                {
275                                        List<Link> firstPath = seqLinks_p.get(pathIndex);
276                                        List<Link> secondPath = cpl11 == null? null : seqLinks2_p.get(pathIndex);
277                                        Pair<Integer,Integer> slotIds = null;
278                                        int slotId = -1;
279                                        if (cpl11 == null)
280                                                slotId = WDMUtils.spectrumAssignment_firstFit(firstPath , frequencySlot2FiberOccupancy_se , numSlots_p.get(pathIndex));
281                                        else
282                                                slotIds = WDMUtils.spectrumAssignment_firstFitTwoRoutes(firstPath, secondPath, frequencySlot2FiberOccupancy_se , numSlots_p.get(pathIndex));
283                                        
284                                        /* Check if the path (or 1+1 path pair) is not feasible */
285                                        if (cpl11 == null) if (slotId == -1) continue;
286                                        if (cpl11 != null) if (slotIds == null) continue;
287                                        
288                                        /* If the performance metric is better than existing, this is the best choice */
289                                        final double extraCarriedTraffic = getAverageAllStatesExtraCarriedTrafficAfterPotentialAllocation (ipDemand , lineRate_p.get(pathIndex) , seqLinks_p.get(pathIndex));
290                                        final double performanceIndicator = extraCarriedTraffic / cost_p.get(pathIndex); 
291                                        if (performanceIndicator > best_performanceMetric)
292                                        {
293                                                best_performanceMetric = performanceIndicator;
294                                                best_rsa = new WDMUtils.RSA(firstPath , cpl11 != null? slotIds.getFirst() : slotId , numSlots_p.get(pathIndex) , regPositions_p.get(pathIndex));
295                                                best_rsa2 = cpl11 == null? null : new WDMUtils.RSA(secondPath , slotIds.getSecond() , numSlots_p.get(pathIndex) , regPositions2_p.get(pathIndex));
296                                                best_pathIndex = pathIndex;
297                                        }
298                                }
299
300                                
301                                /* No lp could be added to this demand, try with the next */
302                                if (best_pathIndex == -1) { ipDemandIndexesNotToTry.add(ipDemand.getIndex()); continue; }
303                                
304                                /* Add the lightpath to the design */
305                                atLeastOneLpAdded = true;
306                                totalCost += cost_p.get(best_pathIndex);
307                                final Demand newWDMDemand = netPlan.addDemand(best_rsa.ingressNode , best_rsa.egressNode , lineRate_p.get(best_pathIndex) , RoutingType.SOURCE_ROUTING , null , wdmLayer);
308                                newWDMDemand.setIntendedRecoveryType(recoveryTypeNewLps);
309                                final Route lp = WDMUtils.addLightpath(newWDMDemand , best_rsa , lineRate_p.get(best_pathIndex));
310                                final Link ipLink = newWDMDemand.coupleToNewLinkCreated(ipLayer);
311                                final double ipTrafficToCarry = Math.min(lineRate_p.get(best_pathIndex) , ipDemand.getBlockedTraffic());
312                                netPlan.addRoute(ipDemand , ipTrafficToCarry , ipTrafficToCarry , Arrays.asList(ipLink), null);
313                                WDMUtils.allocateResources(best_rsa , frequencySlot2FiberOccupancy_se , null);
314                                if (cpl11 != null)
315                                {
316                                        final Route lpBackup = WDMUtils.addLightpath(newWDMDemand , best_rsa2 , 0);
317                                        WDMUtils.allocateResources(best_rsa2 , frequencySlot2FiberOccupancy_se , null);
318                                        lp.addBackupRoute(lpBackup);
319                                }
320                                break;
321                        }
322                        
323                } while (atLeastOneLpAdded);
324                
325                WDMUtils.checkResourceAllocationClashing(netPlan,true,true,wdmLayer);
326
327                String outMessage = "Total cost: " + totalCost + ". Num lps (not including 1+1 backup if any) " + netPlan.getNumberOfRoutes(wdmLayer);
328                //System.out.println (outMessage);
329                return "Ok! " + outMessage;
330        }
331
332        private static double getLengthInKm (Collection<Link> r) { double res = 0; for (Link e : r) res += e.getLengthInKm(); return res; }
333        
334        @Override
335        public String getDescription()
336        {
337                return "Algorithm based on an ILP solving the Routing, Spectrum, Modulation Assignment (RSMA) problem with regenerator placement, in flexi (elastic) or fixed grid optical WDM networks, with or without fault tolerance, latency and/or lightpath bidirectionality requisites (see the Javadoc for details).";
338        }
339
340        @Override
341        public List<Triple<String, String, String>> getParameters()
342        {
343                /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */
344                return InputParameter.getInformationAllInputParameterFieldsOfObject(this);
345        }
346        
347        /* A vector with the blocked traffic for each demand (in the single-SRG failure tolerance, is averaged for each state) */
348        private double [] getVectorIPDemandAverageAllStatesBlockedTraffic ()
349        {
350                double [] res = new double [Dip];
351                for (Demand ipDemand : netPlan.getDemands(ipLayer))
352                {
353                        res [ipDemand.getIndex()] = ipDemand.getBlockedTraffic();
354                        if (singleSRGToleranceNot11Type)
355                        {
356                                for (SharedRiskGroup srg : netPlan.getSRGs())
357                                {
358                                        Set<Route> affectedRoutes = srg.getAffectedRoutes(wdmLayer);
359                                        double carriedTrafficThisFailure = 0; 
360                                        for (Route ipRoute : ipDemand.getRoutes())
361                                        {
362                                                final Set<Route> wdmRoutes = ipRoute.getSeqLinks().get(0).getCoupledDemand().getRoutes();
363                                                for (Route r : wdmRoutes)
364                                                        if (!affectedRoutes.contains(r)) carriedTrafficThisFailure += r.getCarriedTraffic();
365                                        }
366                                        res [ipDemand.getIndex()] += Math.max(0 , ipDemand.getOfferedTraffic() - carriedTrafficThisFailure);
367                                }
368                        }
369                        res [ipDemand.getIndex()] /= (singleSRGToleranceNot11Type? (netPlan.getNumberOfSRGs() + 1) : 1);
370                }
371                return res;
372        }
373        
374        /* The average for all the states (no failure, and potentially one per SRG if single-SRG failure tolerance option is chosen), 
375         * of all the blocked traffic */
376        private double getAverageAllStatesExtraCarriedTrafficAfterPotentialAllocation (Demand ipDemand , double lineRateGbps , List<Link> seqLinksIfSingleSRGToleranceIsNeeded)
377        {
378                double extraCarriedTraffic = Math.min(ipDemand.getBlockedTraffic() , lineRateGbps);
379                if (singleSRGToleranceNot11Type)
380                {
381                        for (SharedRiskGroup srg : netPlan.getSRGs())
382                        {
383                                if (srg.affectsAnyOf(seqLinksIfSingleSRGToleranceIsNeeded)) continue; // no extra carried traffic
384                                Set<Route> affectedRoutes = srg.getAffectedRoutes(wdmLayer);
385                                double carriedTrafficThisFailure = 0; 
386                                for (Route ipRoute : ipDemand.getRoutes())
387                                {
388                                        Set<Route> wdmRoutes = ipRoute.getSeqLinks().get(0).getCoupledDemand().getRoutes();
389                                        for (Route r : wdmRoutes)
390                                                if (!affectedRoutes.contains(r)) carriedTrafficThisFailure += r.getCarriedTraffic();
391                                }
392                                extraCarriedTraffic += Math.min(lineRateGbps , Math.max(0 , ipDemand.getOfferedTraffic() - carriedTrafficThisFailure));
393                        }
394                }
395                
396                return extraCarriedTraffic / (singleSRGToleranceNot11Type? (netPlan.getNumberOfSRGs() + 1) : 1);
397        }
398
399        /* True if the demand is fully satisfied (in the single-SRG failure case, also in each SRG) */
400        private boolean isIpDemandFullySatisfied (Demand d)
401        {
402                if (d.getBlockedTraffic() > 1e-3) return false;
403                if (singleSRGToleranceNot11Type)
404                {
405                        for (SharedRiskGroup srg : netPlan.getSRGs())
406                        {
407                                Set<Route> affectedRoutes = srg.getAffectedRoutes(wdmLayer);
408                                double carriedTrafficThisFailure = 0;
409                                for (Route ipRoute : d.getRoutes())
410                                {
411                                        final Set<Route> lps = ipRoute.getSeqLinks().get(0).getCoupledDemand().getRoutes();  
412                                        for (Route wdmRoute : lps) if (!affectedRoutes.contains(wdmRoute)) carriedTrafficThisFailure += wdmRoute.getCarriedTraffic();
413                                }
414                                if (carriedTrafficThisFailure + 1e-3 < d.getOfferedTraffic()) return false;
415                        }
416                }
417                return true;
418        }
419        
420}