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 *******************************************************************************/
011
012
013
014
015package com.net2plan.examples.ocnbook.offline;
016
017import cern.colt.matrix.tdouble.DoubleFactory1D;
018import cern.colt.matrix.tdouble.DoubleFactory2D;
019import cern.colt.matrix.tdouble.DoubleMatrix1D;
020import cern.colt.matrix.tdouble.DoubleMatrix2D;
021import cern.jet.math.tdouble.DoubleFunctions;
022import com.jom.DoubleMatrixND;
023import com.jom.OptimizationProblem;
024import com.net2plan.interfaces.networkDesign.*;
025import com.net2plan.utils.Constants.RoutingType;
026import com.net2plan.utils.Constants.SearchType;
027import com.net2plan.utils.*;
028
029import java.io.File;
030import java.io.PrintWriter;
031import java.util.*;
032import java.util.Map.Entry;
033
034/**
035 * This algorithm is devoted to solve the several network planning problems in an optical WDM network (fiber placement, RWA, under different recovery schemes), appearing in the case study in the book section mentioned below. 
036 * @net2plan.description 
037 * @net2plan.keywords WDM, Topology assignment (TA), Flow assignment (FA), GRASP, JOM
038 * @net2plan.ocnbooksections Section 12.10
039 * @net2plan.inputParameters 
040 * @author Pablo Pavon-Marino
041 */
042public class Offline_tcfa_wdmPhysicalDesign_graspAndILP implements IAlgorithm
043{
044        /* Stat */
045        private double stat_totalCost , stat_costinks , stat_costCirc , stat_costNodes;
046        private int stat_numLinks , stat_numCirc , stat_numNodesDeg2 , stat_numNodesDeg3;
047        private int stat_numItGRASP , stat_numLSIterationsReducingCost , stat_numSolverCalls , stat_numSolverCallsOk;
048        private double stat_totalTimeSecs , stat_totalTimeCreateProgramsSecs , stat_totalTimeSolverSecs;
049        
050        /* Parameters */
051        private DoubleMatrix1D linkCost_e;
052        private Random rng;
053        private NetPlan netPlan;
054        
055        /* Control */
056        private int Efm, N, nSRGs , D , R; 
057        private DoubleMatrix1D numCircH_d; 
058        private DoubleMatrix2D A_er , A_dr , A_rs , A_se , Aout_ne , Ain_ne, Abid_dd , Abid_rr , Abid_ee;
059        private Map<Demand,Demand> opposite_d;
060        private Map<Route,Route> opposite_r;
061        private int [][] routeList_d; // for each demand, the array of route ids assigned to it
062        private int [][] traversedLinks_r; // for each demand, the array of route ids assigned to it
063
064        
065        private InputParameter algorithm_randomSeed = new InputParameter ("algorithm_randomSeed", (long) 1 , "Seed of the random number generator");
066        private InputParameter algorithm_outputFileNameRoot = new InputParameter ("algorithm_outputFileNameRoot", "tcfaWDM_graspAndILP" , "Root of the file name to be used in the output files. If blank, no output");
067        private InputParameter algorithm_maxExecutionTimeInSeconds = new InputParameter ("algorithm_maxExecutionTimeInSeconds", (double) 300 , "Algorithm maximum running time in seconds" , 0 , false , Double.MAX_VALUE , true);
068        private InputParameter tcfa_circuitCost = new InputParameter ("tcfa_circuitCost", (double) 3.0 , "Cost of each circuit" , 0 , true , Double.MAX_VALUE , true);
069        private InputParameter tcfa_circuitCapacity_Gbps = new InputParameter ("tcfa_circuitCapacity_Gbps", (double) 10.0 , "Capacity of an optical circuit" , 0 , true , Double.MAX_VALUE , true);
070        private InputParameter tcfa_srgType = new InputParameter ("tcfa_srgType" , "#select# perBidirectionalLinkBundle noFailure", "Determines how the SRGs are initialized. The design must be tolerant to single SRG failures");
071        private InputParameter tcfa_srgMttfPer1000Km_hrs = new InputParameter ("tcfa_srgMttfPer1000Km_hrs" , (double) 4380 , "Mean Time To Fail (MTTF) of the SRGs defined is this value multiplied by the link length divided by 1000",  0 , false , Double.MAX_VALUE , true);
072        private InputParameter tcfa_srgMttr_hrs = new InputParameter ("tcfa_srgMttr_hrs" , (double) 24 , "Mean Time To Repair (MTTR) of the SRGs",  0 , true , Double.MAX_VALUE , true);
073        private InputParameter tcfa_recoveryType = new InputParameter ("tcfa_recoveryType" , "#select# 1+1 shared restoration", "Determines the type of network recovery mechanism in the network.");
074        private InputParameter tcfa_maxPathLengthInKm = new InputParameter ("tcfa_maxPathLengthInKm" , (double) 10000 , "Maximum length that a lightpath can have",  0 , true , Double.MAX_VALUE , true);
075        private InputParameter tcfa_maxPathNumberOfHops = new InputParameter ("tcfa_maxPathNumberOfHops" , (int) 4 , "Maximum number of hops that a lightpath can make",  1 , Integer.MAX_VALUE);
076        private InputParameter tcfa_maxNumberPathsPerDemand = new InputParameter ("tcfa_maxNumberPathsPerDemand" , (int) 200 , "Maximum number of paths in the candidate path list, for each demand",  1 , Integer.MAX_VALUE);
077        private InputParameter tcfa_linkCapacity_numCirc = new InputParameter ("tcfa_linkCapacity_numCirc" , (int) 80 , "Number of wavelengths available per fiber",  1 , Integer.MAX_VALUE);
078        private InputParameter tcfa_linkCostPerKm = new InputParameter ("tcfa_linkCostPerKm" , (double) 0.4 , "Cost of one km of optical fiber",  0 , true  , Double.MAX_VALUE , true);
079        private InputParameter tcfa_costNodeDegree2 = new InputParameter ("tcfa_costNodeDegree2" , (double) 4.0 , "Cost of one OADM of degree lower or equal than two",  0 , true  , Double.MAX_VALUE , true);
080        private InputParameter tcfa_costNodeDegreeHigherThan2 = new InputParameter ("tcfa_costNodeDegreeHigherThan2" , (double) 4.0 , "Cost of one OADM of degree higher than two",  0 , true  , Double.MAX_VALUE , true);
081        private InputParameter tcfa_networkDiameterKmToNormalize = new InputParameter ("tcfa_networkDiameterKmToNormalize" , (double) 4500 , "The link lengths are normalized so the longest link has this value",  0 , true  , Double.MAX_VALUE , true);
082        private InputParameter tcfa_maxNumIterations = new InputParameter ("tcfa_maxNumIterations", (int) 10000 , "Maximum number of iterations" , 1 , Integer.MAX_VALUE);
083        private InputParameter algorithm_solverName = new InputParameter ("solverName", "#select# glpk ipopt xpress cplex", "The solver name to be used by JOM. GLPK and IPOPT are free, XPRESS and CPLEX commercial. GLPK, XPRESS and CPLEX solve linear problems w/w.o integer contraints. IPOPT is can solve nonlinear problems (if convex, returns global optimum), but cannot handle integer constraints");
084        private InputParameter algorithm_solverLibraryName = new InputParameter ("solverLibraryName", "", "The solver library full or relative path, to be used by JOM. Leave blank to use JOM default.");
085        private InputParameter algorithm_maxSolverTimeInSeconds = new InputParameter ("maxSolverTimeInSeconds", (double) 2 , "Maximum time granted to the solver in to solve each problem instance. If this time expires, the solver returns the best solution found so far (if a feasible solution is found)");
086        private InputParameter tcfa_bidirectionalLinks = new InputParameter ("tcfa_bidirectionalLinks", true , "If true, the topology of fibers deployed is constrained to be bidirectional (the same number of fibers in both directions)");
087
088        @Override
089        public String executeAlgorithm(NetPlan netPlan, Map<String, String> algorithmParameters, Map<String, String> net2planParameters)
090        {
091                /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */
092                InputParameter.initializeAllInputParameterFieldsOfObject(this, algorithmParameters);
093                if (netPlan.getNumberOfNodes() == 0) throw new Net2PlanException ("The input design has no nodes");
094                
095                
096                try{ 
097                this.netPlan = netPlan;
098                /* Initializations */
099
100                netPlan.removeAllLinks(); netPlan.setRoutingTypeAllDemands(RoutingType.SOURCE_ROUTING);
101                this.N = netPlan.getNumberOfNodes();
102                this.rng = new Random (algorithm_randomSeed.getLong ());
103                
104                /* Initialize links */
105                for (Node n1 : netPlan.getNodes()) for (Node n2 : netPlan.getNodes()) if (n1 != n2) netPlan.addLink(n1, n2, 0.01, netPlan.getNodePairEuclideanDistance(n1, n2), 200000 , null);
106                final double linkLengthKmFromEuclideanDistanceFactor = tcfa_networkDiameterKmToNormalize.getDouble() / netPlan.getVectorLinkLengthInKm().getMaxLocation() [0];
107                for (Link e : netPlan.getLinks()) e.setLengthInKm(e.getLengthInKm() * linkLengthKmFromEuclideanDistanceFactor);
108                this.Efm = netPlan.getNumberOfLinks(); if (Efm != N*(N-1)) throw new RuntimeException ("Bad, Efm" + Efm + ", N: " + N);
109                this.linkCost_e = DoubleFactory1D.dense.make(Efm); for (Link e : netPlan.getLinks ()) linkCost_e.set(e.getIndex () , tcfa_linkCostPerKm.getDouble() * e.getLengthInKm());
110                
111                /* If there is no traffic, create uniform traffic matrix */
112                if (netPlan.getNumberOfDemands() == 0)
113                        for (Node n1 : netPlan.getNodes()) for (Node n2 : netPlan.getNodes()) if (n1 != n2) netPlan.addDemand(n1, n2, tcfa_circuitCapacity_Gbps.getDouble(), RoutingType.SOURCE_ROUTING , null);
114
115                
116                /* Convert the demand offered traffic in the upper multiple of circuit capacity */
117                DoubleMatrix2D trafficMatrix = netPlan.getMatrixNode2NodeOfferedTraffic();
118                netPlan.removeAllDemands();
119                for (Node n1 : netPlan.getNodes()) for (Node n2 : netPlan.getNodes()) if (n1.getIndex () > n2.getIndex ())
120                {
121                        final double maxTrafficBidir = Math.max(trafficMatrix.get(n1.getIndex (), n2.getIndex ()), trafficMatrix.get(n2.getIndex (), n1.getIndex ()));
122                        netPlan.addDemand(n1, n2, tcfa_circuitCapacity_Gbps.getDouble() * Math.ceil(maxTrafficBidir / tcfa_circuitCapacity_Gbps.getDouble()) , RoutingType.SOURCE_ROUTING , null); 
123                        netPlan.addDemand(n2, n1, tcfa_circuitCapacity_Gbps.getDouble() * Math.ceil(maxTrafficBidir / tcfa_circuitCapacity_Gbps.getDouble()) , RoutingType.SOURCE_ROUTING , null); 
124                }
125                        
126                /* If 1+1 then decouple demands in one per channel */
127                if (tcfa_recoveryType.getString ().equals("1+1"))
128                {
129                        NetPlan npcopy = netPlan.copy();
130                        netPlan.removeAllDemands();
131                        for (Demand d : npcopy.getDemands())
132                        {
133                                final int numChannels = (int) Math.round (d.getOfferedTraffic() / tcfa_circuitCapacity_Gbps.getDouble ());
134                                for (int cont = 0 ; cont < numChannels ; cont ++) netPlan.addDemand(netPlan.getNodeFromId (d.getIngressNode().getId ()), netPlan.getNodeFromId (d.getEgressNode().getId ()), tcfa_circuitCapacity_Gbps.getDouble () , RoutingType.SOURCE_ROUTING , d.getAttributes());
135                        }
136                }
137
138                /* Initialize routes and demands to make it bidirectional */
139                Pair<Map<Demand,Demand>,Map<Route,Route>> pair = initializeNetPlanLinksBidirDemandsAndRoutes (netPlan);
140                this.opposite_d = pair.getFirst();
141                this.opposite_r = pair.getSecond();
142
143                /* Initialize demand info */
144                this.D = netPlan.getNumberOfDemands();
145                this.numCircH_d = netPlan.getVectorDemandOfferedTraffic().assign(DoubleFunctions.div(this.tcfa_circuitCapacity_Gbps.getDouble ())).assign(DoubleFunctions.rint);
146                //System.out.println("Total number of circuits to establish: " + numCircH_d.zSum());
147                
148                //if (1==1) throw new RuntimeException ("Stop");
149                this.R = netPlan.getNumberOfRoutes();
150                //System.out.println ("numCircH_d: " + numCircH_d);
151
152                /* Initialize SRG information */
153                initializeSRGs(netPlan);
154                this.nSRGs = netPlan.getNumberOfSRGs();
155                this.routeList_d = new int [D][]; for (Demand d : netPlan.getDemands ()) routeList_d [d.getIndex ()] = getIndexes (d.getRoutes ());
156                this.traversedLinks_r = new int [R][]; for (Route r : netPlan.getRoutes ()) traversedLinks_r [r.getIndex ()] = getIndexes (r.getSeqLinks());
157                
158                
159                /* Initialize aux arrays for speed-up */
160                this.A_dr = netPlan.getMatrixDemand2RouteAssignment();
161                for (Demand d : netPlan.getDemands ()) if (d.getRoutes().isEmpty()) throw new Net2PlanException ("A demand has no assigned routes");
162                this.A_er = netPlan.getMatrixLink2RouteAssignment();
163                this.A_se = DoubleFactory2D.dense.make(1 + nSRGs , Efm , 1.0); // 1 if link OK, 0 if fails
164                for (int contSRG = 0 ; contSRG < nSRGs ; contSRG ++)
165                        for (Link e : netPlan.getSRG (contSRG).getLinksAllLayers())
166                                A_se.set(contSRG+1 , e.getIndex () , 0.0);
167                this.A_rs = DoubleFactory2D.dense.make(R , 1 + nSRGs , 1.0); // 1 if link OK, 0 if fails
168                for (Route r : netPlan.getRoutes ()) for (Link e : r.getSeqLinks()) for (SharedRiskGroup srg : e.getSRGs())
169                                        A_rs.set(r.getIndex (),1+srg.getIndex () , 0.0);
170
171                /* Check if the problem may have a solution: demands have at least one path in any failure state  */
172                if (A_dr.zMult(A_rs, null).getMinLocation() [0] == 0) { System.out.println("A_dr.zMult(A_rs, null): " + A_dr.zMult(A_rs, null)); throw new Net2PlanException ("Some demands cannot be routed in some failure state. We need more paths! (e.g. extend the path reach)"); }
173
174                this.Aout_ne = netPlan.getMatrixNodeLinkOutgoingIncidence();
175                this.Ain_ne = netPlan.getMatrixNodeLinkIncomingIncidence(); 
176                this.Abid_dd = DoubleFactory2D.sparse.make(D,D); for (Entry<Demand,Demand> entry : opposite_d.entrySet()) { Abid_dd.set(entry.getKey().getIndex (), entry.getValue().getIndex (), 1.0); Abid_dd.set(entry.getValue().getIndex (), entry.getKey().getIndex (), 1.0); } 
177                this.Abid_rr = DoubleFactory2D.sparse.make(R,R); for (Entry<Route,Route> entry : opposite_r.entrySet()) { Abid_rr.set(entry.getKey().getIndex (), entry.getValue().getIndex (), 1.0); Abid_rr.set(entry.getValue().getIndex (), entry.getKey().getIndex (), 1.0); } 
178                this.Abid_ee = DoubleFactory2D.sparse.make(Efm,Efm); for (Link e : netPlan.getLinks ()) { Abid_ee.set(e.getIndex (), oppositeLink(e).getIndex (), 1.0);  Abid_ee.set(oppositeLink(e).getIndex (), e.getIndex (), 1.0); }
179                
180                final long algorithmInitialtime = System.nanoTime();
181                final long algorithmEndtime = algorithmInitialtime + (long) (algorithm_maxExecutionTimeInSeconds.getDouble() * 1E9);
182
183                /* Up stage */
184                ArrayList<Integer> shuffledDemands = new ArrayList<Integer> (D); for (int d = 0 ; d < D ; d ++) shuffledDemands.add(d);
185                Collections.shuffle(shuffledDemands , rng);
186                if (tcfa_recoveryType.getString ().equals("shared"))
187                {
188                        DoubleMatrix1D best_xr = null; DoubleMatrix1D best_pe = null; double bestCost = Double.MAX_VALUE;
189                        int iterationCounter = 0;
190                        while ((System.nanoTime() < algorithmEndtime) && (iterationCounter < tcfa_maxNumIterations.getInt ()))
191                        {
192                                DoubleMatrix1D thisIt_xr = greedyAndLocalSearch_shared(algorithmEndtime , netPlan);
193                                DoubleMatrix1D thisIt_pe = A_er.zMult(thisIt_xr, null); thisIt_pe.assign(DoubleFunctions.div(tcfa_linkCapacity_numCirc.getInt ())).assign(DoubleFunctions.ceil);
194                                double thisItCost = computeCost_shared(thisIt_xr, thisIt_pe);
195                                if (thisItCost < bestCost) { best_xr = thisIt_xr; best_pe = thisIt_pe; bestCost = thisItCost; }
196                                iterationCounter ++;
197                        }
198                        stat_numItGRASP = iterationCounter;
199                        for (Route r : netPlan.getRoutes ()) r.setCarriedTraffic(tcfa_circuitCapacity_Gbps.getDouble () * best_xr.get(r.getIndex ()) , tcfa_circuitCapacity_Gbps.getDouble () * best_xr.get(r.getIndex ()));
200                        for (Link e : netPlan.getLinks ()) e.setCapacity(tcfa_linkCapacity_numCirc.getInt () * tcfa_circuitCapacity_Gbps.getDouble () * best_pe.get(e.getIndex()));
201                } else if (tcfa_recoveryType.getString ().equals("1+1"))
202                {
203                        DoubleMatrix1D best_xr = null; DoubleMatrix1D best_x2r = null; DoubleMatrix1D best_pe = null; double bestCost = Double.MAX_VALUE;
204                        int iterationCounter = 0;
205                        while ((System.nanoTime() < algorithmEndtime) && (iterationCounter < tcfa_maxNumIterations.getInt ()))
206                        {
207                                Pair<DoubleMatrix1D,DoubleMatrix1D> thisItSolution = greedyAndLocalSearch_11(algorithmEndtime , netPlan);
208                                DoubleMatrix1D thisIt_xr = thisItSolution.getFirst();   
209                                DoubleMatrix1D thisIt_x2r = thisItSolution.getSecond();
210                                DoubleMatrix1D thisIt_pe = computePe_11(thisIt_xr, thisIt_x2r);
211                                double thisItCost = computeCost_11(thisIt_xr, thisIt_x2r , thisIt_pe);
212                                if (thisItCost < bestCost) { best_xr = thisIt_xr; best_x2r = thisIt_x2r; best_pe = thisIt_pe; bestCost = thisItCost; }
213                                iterationCounter ++;
214                        }
215                        stat_numItGRASP = iterationCounter;
216                        for (Demand d : netPlan.getDemands ())
217                        {
218                                if (Math.abs(best_xr.viewSelection(routeList_d [d.getIndex ()]).zSum() - 1) > 1E-3) throw new RuntimeException ("Bad");
219                                if (Math.abs(best_x2r.viewSelection(routeList_d [d.getIndex ()]).zSum() - 1) > 1E-3) throw new RuntimeException ("Bad");
220                                Route primaryRoute = null; Route backupRoute = null;  
221                                for (Route r : d.getRoutes ()) if (Math.abs(best_xr.get(r.getIndex ()) - 1) <= 1e-3) { primaryRoute = r; primaryRoute.setCarriedTraffic(tcfa_circuitCapacity_Gbps.getDouble () , tcfa_circuitCapacity_Gbps.getDouble ()); break; }
222                                for (Route r : d.getRoutes ()) if (Math.abs(best_x2r.get(r.getIndex ()) - 1) <= 1e-3) { backupRoute = r; backupRoute.setCarriedTraffic(0 , tcfa_circuitCapacity_Gbps.getDouble ()); break; }
223                                primaryRoute.addBackupRoute(backupRoute);
224                        }
225                        for (Link e : netPlan.getLinks ()) e.setCapacity(tcfa_linkCapacity_numCirc.getInt () * tcfa_circuitCapacity_Gbps.getDouble () * best_pe.get(e.getIndex()));
226                
227                } else if (tcfa_recoveryType.getString ().equals("restoration"))
228                {
229                        DoubleMatrix2D best_xrs = null; DoubleMatrix1D best_pe = null; double bestCost = Double.MAX_VALUE;
230                        int iterationCounter = 0;
231                        while ((System.nanoTime() < algorithmEndtime) && (iterationCounter < tcfa_maxNumIterations.getInt ()))
232                        {
233                                DoubleMatrix2D thisIt_xrs = greedyAndLocalSearch_restoration(algorithmEndtime , netPlan);
234                                DoubleMatrix1D thisIt_pe = computePe_restoration (thisIt_xrs); 
235                                double thisItCost = computeCost_shared(thisIt_xrs.viewColumn(0), thisIt_pe);
236                                if (thisItCost < bestCost) { best_xrs = thisIt_xrs; best_pe = thisIt_pe; bestCost = thisItCost; }
237                                iterationCounter ++;
238                        }
239                        stat_numItGRASP = iterationCounter;
240                        for (Route r : netPlan.getRoutes ()) r.setCarriedTraffic(tcfa_circuitCapacity_Gbps.getDouble () * best_xrs.get(r.getIndex (),0) , tcfa_circuitCapacity_Gbps.getDouble () * best_xrs.get(r.getIndex (),0));
241                        for (Link e : netPlan.getLinks ()) e.setCapacity(tcfa_linkCapacity_numCirc.getInt () * tcfa_circuitCapacity_Gbps.getDouble () * best_pe.get(e.getIndex()));
242                } else throw new RuntimeException ("Bad");              
243                
244                /* Remove unused routes and links */
245                for (Route r : new HashSet<Route>(netPlan.getRoutes())) if (!r.isBackupRoute() && r.getCarriedTraffic() == 0) r.remove();
246                for (Link e : new HashSet<Link>(netPlan.getLinks())) if (e.getCapacity() == 0) e.remove();
247                
248                Quadruple<Double,Double,Double,Double> q = computeCost (netPlan);
249                stat_totalCost = q.getFirst();
250                stat_costinks = q.getSecond();
251                stat_costNodes = q.getThird();
252                stat_costCirc = q.getFourth();
253                stat_numLinks = (int) (netPlan.getVectorLinkCapacity().zSum () / (tcfa_linkCapacity_numCirc.getInt () * tcfa_circuitCapacity_Gbps.getDouble ())); 
254                stat_numCirc = (int) Math.round(netPlan.getVectorDemandCarriedTraffic().zSum() / tcfa_circuitCapacity_Gbps.getDouble ()); if (tcfa_recoveryType.getString ().equals("1+1")) stat_numCirc *=2; 
255                for (Node n : netPlan.getNodes()) if (n.getOutgoingLinks().size() > 2) stat_numNodesDeg3 ++; else stat_numNodesDeg2 ++;
256                stat_totalTimeSecs = (System.nanoTime() - algorithmInitialtime)*1e-9;
257                checkSolution (netPlan);
258
259                double averageLinkUtilization = netPlan.getVectorLinkUtilization().zSum() / stat_numLinks;
260                final String fileNameStem = algorithm_outputFileNameRoot.getString() + "_" + tcfa_srgType.getString() + "_c" + tcfa_circuitCapacity_Gbps.getDouble () + "_t" + algorithm_maxExecutionTimeInSeconds.getDouble() + "_r" + tcfa_recoveryType.getString ();  
261                try 
262                {
263                        PrintWriter pw = new PrintWriter (new File (fileNameStem + "_allResults.txt"));
264                        pw.println(stat_totalCost );
265                        pw.println( stat_costinks );
266                        pw.println( stat_costNodes );
267                        pw.println( stat_costCirc );
268                        pw.println( stat_numLinks );
269                        pw.println( stat_numCirc );
270                        pw.println( stat_numNodesDeg2 );
271                        pw.println( stat_numNodesDeg3 );
272                        pw.println( stat_numItGRASP );
273                        pw.println( stat_numLSIterationsReducingCost );
274                        pw.println( stat_numSolverCalls );
275                        pw.println( stat_numSolverCallsOk );
276                        pw.println( stat_totalTimeSecs );
277                        pw.println( stat_totalTimeCreateProgramsSecs );
278                        pw.println( stat_totalTimeSolverSecs );
279                        pw.println( averageLinkUtilization );
280                        pw.close ();
281                } catch (Exception e) { e.printStackTrace(); throw new RuntimeException ("Not possible to write in File " + fileNameStem  + "_allResults.txt"); } 
282
283
284                netPlan.setAttribute("stat_totalCost", ""+stat_totalCost);
285                netPlan.setAttribute("stat_costinks", ""+stat_costinks);
286                netPlan.setAttribute("stat_costNodes", ""+stat_costNodes);
287                netPlan.setAttribute("stat_costCirc", ""+stat_costCirc);
288                netPlan.setAttribute("stat_numLinks", ""+stat_numLinks);
289                netPlan.setAttribute("stat_numCirc", ""+stat_numCirc);
290                netPlan.setAttribute("stat_numNodesDeg2", ""+stat_numNodesDeg2);
291                netPlan.setAttribute("stat_numNodesDeg3", ""+stat_numNodesDeg3);
292                netPlan.setAttribute("stat_numItGRASP", ""+stat_numItGRASP);
293                netPlan.setAttribute("stat_numLSIterationsReducingCost", ""+stat_numLSIterationsReducingCost);
294                netPlan.setAttribute("stat_numSolverCalls", ""+stat_numSolverCalls);
295                netPlan.setAttribute("stat_numSolverCallsOk", ""+stat_numSolverCallsOk);
296                netPlan.setAttribute("stat_totalTimeSecs", ""+stat_totalTimeSecs);
297                netPlan.setAttribute("stat_totalTimeCreateProgramsSecs", ""+stat_totalTimeCreateProgramsSecs);
298                netPlan.setAttribute("stat_totalTimeSolverSecs", ""+stat_totalTimeSolverSecs);
299
300                netPlan.saveToFile(new File (fileNameStem + ".n2p"));
301
302//              if (netPlan.getDemandTotalBlockedTraffic() > 1E-3) throw new RuntimeException ("Bad");
303//              if (netPlan.getLinksOversubscribed().size() > 0) throw new RuntimeException ("Bad");
304                //System.out.println("JOM sol: cost: " + stat_totalCost + ", num links: "+ stat_numLinks);
305
306                return "Ok! cost: " + stat_totalCost + ", num links: "+ stat_numLinks;
307
308                } catch (Exception e) { e.printStackTrace(); throw new RuntimeException ("BAD OUT"); }  
309
310        }
311
312        @Override
313        public String getDescription()
314        {
315                return "This algorithm is devoted to solve the several network planning problems appearing in the case study in Section 12.10 of the book mentioned below. The context is the planning of a WDM optical network, given a set of node locations, and a set of end-to-end optical connections (lightpaths) to establish. The algorithm should decide on the fibers to deploy, and the routing of the lightpaths on the fibers, minimizing the total network cost. The cost model includes the fiber renting cost (paid to a dark fiber provider), OADM cost (separating between OADMs of degree two, and degree higher than two), and the cost of the transponders. The network must be tolerant to a set of user-defined failures (defined by SRGs). The algorithm permits choosing between two recovery types: 1+1, and shared protection. In the latter, the number of lightpaths between two nodes that survive to any of the failures is at least the number of optical connections requested for that node pair. For solving the problem, the algorithm uses a GRASP heuristic, to govern the iterative search that is performed using small ILPs solved with JOM in each iteration.";
316        }
317
318        @Override
319        public List<Triple<String, String, String>> getParameters()
320        {
321                /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */
322                return InputParameter.getInformationAllInputParameterFieldsOfObject(this);
323        }
324
325        private Quadruple<Double,Double,Double,Double> computeCost (NetPlan  np)
326        {
327                double costLinks = 0;
328                for (Link e : np.getLinks ())
329                        costLinks += linkCost_e.get(e.getIndex ()) * e.getCapacity() / (tcfa_linkCapacity_numCirc.getInt () * tcfa_circuitCapacity_Gbps.getDouble ());
330                
331                double costCircuits = 0;
332                for (Route r : np.getRoutes ()) // sums both lps and 1+1 if there are any
333                {
334                        final double lineRate = r.isBackupRoute()? r.getRoutesIAmBackup().iterator().next().getCarriedTrafficInNoFailureState() : r.getCarriedTrafficInNoFailureState();
335                        costCircuits += tcfa_circuitCost.getDouble () * (lineRate / tcfa_circuitCapacity_Gbps.getDouble ());
336                }
337                double costNodes = 0;
338                for (Node n : np.getNodes())
339                {
340                        final int nodeDegree = (int) Math.max(n.getOutgoingLinks().size(), n.getIncomingLinks().size()); 
341                        costNodes += (nodeDegree <= 2)? tcfa_costNodeDegree2.getDouble () : tcfa_costNodeDegreeHigherThan2.getDouble ();
342                }
343                final double totalCost = costLinks+  costCircuits + costNodes;
344
345                System.out.println("Total cost: " + totalCost + ", links %: " + (costLinks/totalCost) + ", circ %: " + (costCircuits/totalCost) + ", nodes %: " + (costNodes/totalCost));
346                
347                System.out.println("-- From np cost total :" + totalCost + ", links: " + costLinks + ", nodes: " + costNodes + ", circ: " + costCircuits);
348
349                return Quadruple.of(totalCost,costLinks,costNodes,costCircuits);
350        }
351        
352        private double computeCost_shared (DoubleMatrix1D x_r , DoubleMatrix1D p_e)
353        {
354                double costLinks = p_e.zDotProduct(linkCost_e);
355                final double costCircuits = tcfa_circuitCost.getDouble () * x_r.zSum();
356                double costNodes = 0;
357                DoubleMatrix1D degree_n = Aout_ne.zMult(p_e, null);
358                for (int n = 0; n < N ; n ++)
359                        costNodes += (degree_n.get(n) <= 2)? tcfa_costNodeDegree2.getDouble () : tcfa_costNodeDegreeHigherThan2.getDouble ();
360                final double totalCost = costLinks+  costCircuits + costNodes;
361                //System.out.println("-- shared cost total :" + totalCost + ", links: " + costLinks + ", nodes: " + costNodes + ", circ: " + costCircuits);
362                return totalCost;
363        }
364        private double computeCost_11 (DoubleMatrix1D x_r , DoubleMatrix1D x2_r , DoubleMatrix1D p_e)
365        {
366                double costLinks = p_e.zDotProduct(linkCost_e);
367                final double costCircuits = tcfa_circuitCost.getDouble () * (x_r.zSum() + x2_r.zSum());
368                double costNodes = 0;
369                DoubleMatrix1D degree_n = Aout_ne.zMult(p_e, null);
370                for (int n = 0; n < N ; n ++)
371                        costNodes += (degree_n.get(n) <= 2)? tcfa_costNodeDegree2.getDouble () : tcfa_costNodeDegreeHigherThan2.getDouble ();
372                final double totalCost = costLinks+  costCircuits + costNodes;
373                //System.out.println("-- shared cost total :" + totalCost + ", links: " + costLinks + ", nodes: " + costNodes + ", circ: " + costCircuits);
374                return totalCost;
375        }
376
377        private static Link oppositeLink (Link e) { return e.getNetPlan ().getNodePairLinks(e.getDestinationNode(), e.getOriginNode() , false).iterator().next(); }
378        
379        private Pair<Map<Demand,Demand>,Map<Route,Route>> initializeNetPlanLinksBidirDemandsAndRoutes (NetPlan np)
380        {
381                /* Remove lower half demands from np */
382                np.removeAllRoutes(); 
383                for (Node n1 : np.getNodes()) for (Node n2 : np.getNodes()) if (n1.getIndex () > n2.getIndex ()) for (Demand d : np.getNodePairDemands(n1, n2,false)) d.remove ();
384                np.addRoutesFromCandidatePathList(netPlan.computeUnicastCandidatePathList(null , tcfa_maxNumberPathsPerDemand.getInt(), tcfa_maxPathLengthInKm.getDouble(), tcfa_maxPathNumberOfHops.getInt(), -1, -1, -1, -1 , null));
385                
386                /* Add symmetric demands and routes */
387                Map<Demand,Demand> opposite_d = new HashMap<Demand,Demand> ();
388                Map<Route,Route> opposite_r = new HashMap<Route,Route> ();
389                for (Demand d : new HashSet<Demand> (np.getDemands()))
390                {
391                        final Demand opDemand = np.addDemand(d.getEgressNode(), d.getIngressNode(), d.getOfferedTraffic(), RoutingType.SOURCE_ROUTING , null);
392                        opposite_d.put(d,opDemand);
393                        opposite_d.put(opDemand,d);
394                        for (Route r : new HashSet<Route> (d.getRoutes ()))
395                        {
396                                final Route oppRoute = np.addRoute(opDemand, r.getCarriedTraffic(), r.getOccupiedCapacity() , oppositeSeqLinks (r.getSeqLinks()), null);
397                                opposite_r.put(r,oppRoute); opposite_r.put(oppRoute,r);
398                        }
399                }
400                return Pair.of(opposite_d,opposite_r);
401        }
402
403        private void initializeSRGs (NetPlan np)
404        {
405                np.removeAllSRGs();
406                if (tcfa_srgType.getString ().equals("perBidirectionalLinkBundle"))
407                {
408                        for (Node n1 : np.getNodes()) for (Node n2 : np.getNodes()) if (n1.getIndex () < n2.getIndex())
409                        {
410                                final double linkLengthKm = np.getNodePairLinks(n1, n2,false).iterator().next().getLengthInKm();
411                                final SharedRiskGroup srg = np.addSRG(tcfa_srgMttfPer1000Km_hrs.getDouble () * linkLengthKm / 1000, tcfa_srgMttr_hrs.getDouble (), null);
412                                for (Link e : np.getNodePairLinks(n1, n2,true)) srg.addLink(e); 
413                        }
414                }
415                else if (tcfa_srgType.getString ().equals("noFailure"))
416                {
417                } else throw new Net2PlanException ("Wrong SRG type");
418        }
419        
420        private List<Link> oppositeSeqLinks  (List<Link> s)
421        {
422                LinkedList<Link> oppList = new LinkedList<Link> ();
423                for (Link e : s) oppList.addFirst(oppositeLink(e));
424                return (List<Link>) oppList;
425        }
426
427        private void checkSolution (NetPlan np)
428        {
429                if (np.getDemandsBlocked().size() != 0)
430                {
431                        System.out.println("np.getDemandsBlocked(): " + np.getDemandsBlocked());
432                        System.out.println("np.getDemandOfferedTrafficMap(): " + np.getVectorDemandOfferedTraffic());
433                        System.out.println("np.getDemandBlockedTrafficMap(): " + np.getVectorDemandBlockedTraffic());
434                        throw new RuntimeException ("Bad");
435                }
436                if (np.getLinksOversubscribed().size() != 0)
437                {
438                        System.out.println("np.getLinksOversubscribed(): " + np.getLinksOversubscribed());
439                        System.out.println("np.getLinkCapacityMap(): " + np.getVectorLinkCapacity());
440                        System.out.println("np.getLinkCarriedTrafficMap(): " + np.getVectorLinkCarriedTraffic());
441                        throw new RuntimeException ("Bad");
442                }
443                
444                /* Check bidirectional topology */
445                for (Node n1 : np.getNodes()) 
446                        for (Node n2 : np.getNodes()) 
447                                if (n1.getIndex () > n2.getIndex ()) 
448                                {
449                                        Set<Link> thisLinks = np.getNodePairLinks(n1, n2,false);
450                                        Set<Link> oppLinks = np.getNodePairLinks(n2, n1,false);
451                                        if (thisLinks.size() > 1) throw new RuntimeException ("Bad");
452                                        if (oppLinks.size() > 1) throw new RuntimeException ("Bad");
453                                        if (tcfa_bidirectionalLinks.getBoolean())
454                                        {
455                                                if (thisLinks.size() != oppLinks.size()) throw new RuntimeException ("Bad");
456                                                if (thisLinks.size() > 0)
457                                                        if (thisLinks.iterator().next().getCapacity() != oppLinks.iterator().next().getCapacity() ) throw new RuntimeException ("Bad");
458                                        }
459                                }
460
461                /* Check bidirectional routes */
462                for (Route r : np.getRoutes())
463                {
464                        final Route oppRoute = opposite_r.get(r);
465                        if (!np.getRoutes().contains(oppRoute)) throw new RuntimeException ("Bad");
466                        if (r.getCarriedTraffic() != oppRoute.getCarriedTraffic()) throw new RuntimeException ("Bad");
467                }
468
469                if (tcfa_recoveryType.getString ().equals("1+1"))
470                { // one prot segment, and link disjoint
471                        for (Route r : np.getRoutesAreNotBackup())
472                        {
473                                if (r.getBackupRoutes().size() != 1) throw new RuntimeException ("Bad: " + r.getBackupRoutes().size());
474                                final Route backupRoute = r.getBackupRoutes().get(0);
475                                List<Link> seqLinks = new ArrayList<Link> (r.getSeqLinks());
476                                seqLinks.retainAll(backupRoute.getSeqLinks());
477                                if (!seqLinks.isEmpty()) 
478                                {
479                                        System.out.println ("Route : "+  r + " links: " + r.getSeqLinks() + ", backup route " + backupRoute + " links: " + backupRoute.getSeqLinks() + ", route carried traffic: " + r.getCarriedTraffic() + ", backup occupied capacity: " + backupRoute.getOccupiedCapacity());
480                                        throw new RuntimeException ("Bad");
481                                }
482                        }
483                }
484                
485        }
486        
487        private DoubleMatrix1D jomStep_shared (Set<Integer> demandsChange , DoubleMatrix1D maxPe , DoubleMatrix1D  x_r)
488        {
489                final long initTimeBeforeCreatingProgram = System.nanoTime();
490                if (demandsChange.isEmpty()) throw new RuntimeException ("Bad");
491                final int [] dChange = IntUtils.toArray(demandsChange);
492                int [] rChange = new int [0]; for (int d : dChange) rChange = IntUtils.concatenate(rChange,  routeList_d [d]);
493                final int Rrest = rChange.length;
494                x_r.viewSelection(rChange).assign(0);
495                DoubleMatrix2D A_dRestrRest = A_dr.viewSelection(dChange, rChange);
496                DoubleMatrix2D A_erRest = A_er.viewSelection(null, rChange);
497                DoubleMatrix2D A_rRests = A_rs.viewSelection(rChange,null);
498                DoubleMatrix1D occupiedWithoutChangingR_e = A_er.zMult (x_r,null);
499                DoubleMatrix2D Abid_rRestrRest = DoubleFactory2D.sparse.make(Rrest,Rrest);
500                for (int rRest = 0 ; rRest < Rrest ; rRest ++) 
501                { 
502                        final int r = rChange [rRest]; 
503                        final int opp_r = opposite_r.get(netPlan.getRoute(r)).getIndex ();
504                        final int [] oppRrest = IntUtils.find(rChange, opp_r, SearchType.ALL);
505                        if (oppRrest.length != 1) throw new RuntimeException ("Bad");
506                        Abid_rRestrRest.set(rRest , oppRrest [0] , 1.0); Abid_rRestrRest.set(oppRrest [0], rRest , 1);
507                }
508                OptimizationProblem op = new OptimizationProblem();
509                op.setInputParameter("R",R);
510                op.setInputParameter("U",tcfa_linkCapacity_numCirc.getInt ());
511                op.setInputParameter("c_e", linkCost_e.toArray() , "row");
512                op.setInputParameter("c",  tcfa_circuitCost.getDouble ());
513                op.setInputParameter("d3Cost",  tcfa_costNodeDegreeHigherThan2.getDouble ());
514                op.setInputParameter("onesS",  DoubleUtils.ones(1+nSRGs) , "row");
515                op.setInputParameter("onesE",  DoubleUtils.ones(Efm) , "row");
516                op.setInputParameter("A_dRestrRest", new DoubleMatrixND (A_dRestrRest));
517                op.setInputParameter("h_dRest", numCircH_d.viewSelection(dChange).toArray(), "row");
518                op.setInputParameter("A_rRests", new DoubleMatrixND (A_rRests));
519                op.setInputParameter("A_erRest", new DoubleMatrixND (A_erRest));
520                op.setInputParameter("Aout_ne", new DoubleMatrixND (Aout_ne));
521                op.setInputParameter("Abid_rRestrRest", new DoubleMatrixND (Abid_rRestrRest));
522                op.setInputParameter("occup_e", occupiedWithoutChangingR_e.toArray(),  "row");
523                
524                op.addDecisionVariable("d3_n", true, new int[] { 1, N }, 0, 1); 
525                op.addDecisionVariable("p_e", true, new int[] { 1, Efm }, DoubleUtils.zeros(Efm) , (maxPe == null)? DoubleUtils.constantArray(Efm, Double.MAX_VALUE) : maxPe.toArray());
526                op.addDecisionVariable("x_rRest", true, new int[] { 1, Rrest}, 0 ,  Double.MAX_VALUE); /* 1 if there is a link from node i to node j, 0 otherwise */
527
528                op.setObjectiveFunction("minimize", "sum(c_e .* p_e) + c * sum (x_rRest) + d3Cost * sum(d3_n)");
529                op.addConstraint("A_dRestrRest * diag(x_rRest) * A_rRests >= h_dRest' * onesS"); /* the flow-conservation constraints (NxD constraints) */
530                op.addConstraint("A_erRest * x_rRest' + occup_e' <= U * p_e'"); /* the capacity constraints (E constraints) */
531                op.addConstraint("Aout_ne * p_e' <= 2 + R * d3_n'"); /* the capacity constraints (E constraints) */
532                op.addConstraint("x_rRest == x_rRest * Abid_rRestrRest");
533                if (tcfa_bidirectionalLinks.getBoolean()) { op.setInputParameter("Abid_ee", new DoubleMatrixND (Abid_ee)); op.addConstraint("p_e == p_e * Abid_ee"); } 
534
535                stat_totalTimeCreateProgramsSecs += eTime (initTimeBeforeCreatingProgram); 
536                                
537                double solverTimeAllowed = algorithm_maxSolverTimeInSeconds.getDouble();
538                do 
539                {
540                        final long t = System.nanoTime(); 
541                        try 
542                        { 
543                                stat_numSolverCalls ++; op.solve(algorithm_solverName.getString() , "solverLibraryName", algorithm_solverLibraryName.getString () ,  "maxSolverTimeInSeconds" , solverTimeAllowed);
544                        } catch (Exception e) { System.out.println ("-- EXTRA TIME FOR SOLVER, NOW: " + solverTimeAllowed); solverTimeAllowed += 2; stat_totalTimeSolverSecs += eTime(t);  continue;} 
545                        stat_totalTimeSolverSecs += eTime(t);  
546                        if (solverTimeAllowed != algorithm_maxSolverTimeInSeconds.getDouble()) System.out.println ("-- EXTRA TIME FOR SOLVER, NOW: " + solverTimeAllowed);
547                        solverTimeAllowed *= 2; // duplicate the time if not feasible solution is found
548                } while (!op.solutionIsFeasible());             
549                stat_numSolverCallsOk ++;
550                final double [] new_x_rRest = op.getPrimalSolution("x_rRest").to1DArray();
551                x_r.viewSelection(rChange).assign(new_x_rRest);
552                return x_r;
553        }
554
555        private Pair<DoubleMatrix1D,DoubleMatrix1D> jomStep_11 (Set<Integer> demandsChange ,  DoubleMatrix1D maxPe , DoubleMatrix1D x_r , DoubleMatrix1D x2_r)
556        {
557//              System.out.println ("demandsChange :" + demandsChange);
558                final long initTimeBeforeCreatingProgram = System.nanoTime();
559
560                if (demandsChange.isEmpty()) throw new RuntimeException ("Bad");
561                final int [] dChange = IntUtils.toArray(demandsChange);
562//              System.out.println ("dChange :" + Arrays.toString(dChange));
563                int [] rChange = new int [0]; for (int d : dChange) rChange = IntUtils.concatenate(rChange,  routeList_d [d]);
564                final int Rrest = rChange.length;
565                final int Drest = dChange.length;
566                x_r.viewSelection(rChange).assign(0);
567                x2_r.viewSelection(rChange).assign(0);
568                DoubleMatrix2D A_dRestrRest = A_dr.viewSelection(dChange, rChange);
569                DoubleMatrix2D A_erRest = A_er.viewSelection(null, rChange);
570                DoubleMatrix2D A_rRests = A_rs.viewSelection(rChange,null);
571                DoubleMatrix1D occupiedWithoutChangingR_e = A_er.zMult (x_r,null);
572                DoubleMatrix1D backup_occupiedWithoutChangingR_e = A_er.zMult (x2_r,null);
573                for (int e = 0;e < Efm; e ++) occupiedWithoutChangingR_e.set(e, occupiedWithoutChangingR_e.get(e) + backup_occupiedWithoutChangingR_e.get(e));
574                DoubleMatrix2D Abid_rRestrRest = DoubleFactory2D.sparse.make(Rrest,Rrest);
575                for (int rRest = 0 ; rRest < Rrest ; rRest ++) 
576                { 
577                        final int r = rChange [rRest]; final int opp_r = opposite_r.get(netPlan.getRoute (r)).getIndex ();
578                        final int [] oppRrest = IntUtils.find(rChange, opp_r, SearchType.ALL); if (oppRrest.length != 1) throw new RuntimeException ("Bad");
579                        Abid_rRestrRest.set(rRest , oppRrest [0] , 1.0); Abid_rRestrRest.set(oppRrest [0], rRest , 1);
580                }
581                
582                OptimizationProblem op = new OptimizationProblem();
583                op.setInputParameter("R",R);
584                op.setInputParameter("U",tcfa_linkCapacity_numCirc.getInt ());
585                op.setInputParameter("c_e", linkCost_e.toArray() , "row");
586                op.setInputParameter("c",  tcfa_circuitCost.getDouble ());
587                op.setInputParameter("d3Cost",  tcfa_costNodeDegreeHigherThan2.getDouble ());
588                op.setInputParameter("onesS",  DoubleUtils.ones(1+nSRGs) , "row");
589                op.setInputParameter("onesE",  DoubleUtils.ones(Efm) , "row");
590                op.setInputParameter("A_dRestrRest", new DoubleMatrixND (A_dRestrRest));
591                op.setInputParameter("h_dRest", numCircH_d.viewSelection(dChange).toArray(), "row");
592                op.setInputParameter("A_rRests", new DoubleMatrixND (A_rRests));
593                op.setInputParameter("A_erRest", new DoubleMatrixND (A_erRest));
594                op.setInputParameter("Aout_ne", new DoubleMatrixND (Aout_ne));
595                op.setInputParameter("Abid_rRestrRest", new DoubleMatrixND (Abid_rRestrRest));
596                op.setInputParameter("occup_e", occupiedWithoutChangingR_e.toArray(),  "row");
597                
598                op.addDecisionVariable("d3_n", true, new int[] { 1, N }, 0, 1); 
599                op.addDecisionVariable("p_e", true, new int[] { 1, Efm }, DoubleUtils.zeros(Efm) , (maxPe == null)? DoubleUtils.constantArray(Efm, Double.MAX_VALUE) : maxPe.toArray());
600                op.addDecisionVariable("x_rRest", true, new int[] { 1, Rrest}, 0 ,  1); 
601                op.addDecisionVariable("x2_rRest", true, new int[] { 1, Rrest}, 0 ,  1); 
602
603                op.setObjectiveFunction("minimize", "sum(c_e .* p_e) + d3Cost * sum(d3_n)");
604                op.addConstraint("A_dRestrRest * x_rRest' == h_dRest'"); /* the flow-conservation constraints (NxD constraints) */
605                op.addConstraint("A_dRestrRest * x2_rRest' == h_dRest'"); /* the flow-conservation constraints (NxD constraints) */
606                op.addConstraint("A_erRest * (x_rRest+x2_rRest)' + occup_e' <= U * p_e'"); /* the capacity constraints (E constraints) */
607                op.addConstraint("Aout_ne * p_e' <= 2 + R*d3_n'"); /* the capacity constraints (E constraints) */
608                op.addConstraint("A_dRestrRest * diag(x_rRest+x2_rRest) * A_erRest' <= 1"); 
609                op.addConstraint("x_rRest == x_rRest * Abid_rRestrRest"); 
610                op.addConstraint("x2_rRest == x2_rRest * Abid_rRestrRest"); 
611                if (tcfa_bidirectionalLinks.getBoolean()) { op.setInputParameter("Abid_ee", new DoubleMatrixND (Abid_ee)); op.addConstraint("p_e == p_e * Abid_ee"); } 
612
613//              System.out.println("Abid_rr sum filas: " + Abid_rRestrRest.zMult(DoubleFactory1D.dense.make(Rrest,1), null));
614//              System.out.println("Abid_rr sum cols: " +  Abid_rRestrRest.zMult(DoubleFactory1D.dense.make(Rrest,1), null , 1 , 0 , true));
615//              System.out.println("Abid_rr non zeros: " +  Abid_rRestrRest.zSum());
616                stat_totalTimeCreateProgramsSecs += eTime (initTimeBeforeCreatingProgram); 
617                
618                double solverTimeAllowed = algorithm_maxSolverTimeInSeconds.getDouble ();
619                do 
620                { 
621                        final long t = System.nanoTime(); 
622                        try 
623                        {
624                                stat_numSolverCalls ++; op.solve(algorithm_solverName.getString() , "solverLibraryName", algorithm_solverLibraryName.getString () ,  "maxSolverTimeInSeconds" , solverTimeAllowed);
625                        } catch (Exception e) { System.out.println ("-- EXTRA TIME FOR SOLVER, NOW: " + solverTimeAllowed); solverTimeAllowed += 2; stat_totalTimeSolverSecs += eTime(t);  continue; }
626                        if (solverTimeAllowed != algorithm_maxSolverTimeInSeconds.getDouble ()) System.out.println ("-- EXTRA TIME FOR SOLVER, NOW: " + solverTimeAllowed);
627                        stat_totalTimeSolverSecs += eTime(t);  
628                        solverTimeAllowed *= 2; // duplicate the time if not feasible solution is found
629                        //System.out.println ("Time to create program: " + (1E-9*(t-initTimeBeforeCreatingProgram)) + ", solver time " + ((System.nanoTime()-t)*1E-9) + " s ; ");
630                } while (!op.solutionIsFeasible());             
631                stat_numSolverCallsOk ++; 
632                final double[] new_x_rRest = op.getPrimalSolution("x_rRest").to1DArray();
633                final double[] new_x2_rRest = op.getPrimalSolution("x2_rRest").to1DArray();
634
635                x_r.viewSelection(rChange).assign(new_x_rRest);
636                x2_r.viewSelection(rChange).assign(new_x2_rRest);
637                return Pair.of(x_r,x2_r);
638        }
639
640        private DoubleMatrix2D jomStep_restoration (Set<Integer> demandsChange ,  DoubleMatrix1D maxPe , DoubleMatrix2D  x_rs)
641        {
642                final long initTimeBeforeCreatingProgram = System.nanoTime();
643                if (demandsChange.isEmpty()) throw new RuntimeException ("Bad");
644                final int [] dChange = IntUtils.toArray(demandsChange);
645                int [] rChange = new int [0]; for (int d : dChange) rChange = IntUtils.concatenate(rChange,  routeList_d [d]);
646                final int Rrest = rChange.length;
647                final int Drest = dChange.length;
648                x_rs.viewSelection(rChange,null).assign(0);
649//              DoubleMatrix2D A_dRestrRest = A_dr.viewSelection(dChange, rChange);
650                DoubleMatrix2D A_dRestrRest = DoubleFactory2D.sparse.make(Drest,Rrest);
651                for (int rRest = 0 ; rRest < Rrest ; rRest ++) 
652                { 
653                        final int r = rChange [rRest]; final int dRest [] = IntUtils.find(dChange, netPlan.getRoute (r).getDemand().getIndex () , SearchType.ALL);
654                        if (dRest.length  != 1) throw new RuntimeException ("Bad");
655                        A_dRestrRest.set(dRest [0], rRest , 1.0);
656                }
657                
658                DoubleMatrix2D A_erRest = A_er.viewSelection(null, rChange);
659                DoubleMatrix2D A_rRests = A_rs.viewSelection(rChange,null);
660                DoubleMatrix2D occupiedWithoutChangingR_es = A_er.zMult (x_rs,null);
661                DoubleMatrix2D Abid_rRestrRest = DoubleFactory2D.sparse.make(Rrest,Rrest);
662                for (int rRest = 0 ; rRest < Rrest ; rRest ++) 
663                { 
664                        final int r = rChange [rRest]; final int opp_r = opposite_r.get(netPlan.getRoute (r)).getIndex ();
665                        final int [] oppRrest = IntUtils.find(rChange, opp_r, SearchType.ALL);
666                        if (oppRrest.length != 1) throw new RuntimeException ("Bad");
667                        Abid_rRestrRest.set(rRest , oppRrest [0] , 1.0); Abid_rRestrRest.set(oppRrest [0], rRest , 1);
668                }
669                
670                OptimizationProblem op = new OptimizationProblem();
671                op.setInputParameter("R",R);
672                op.setInputParameter("U",tcfa_linkCapacity_numCirc.getInt ());
673                op.setInputParameter("c_e", linkCost_e.toArray() , "row");
674                op.setInputParameter("c",  tcfa_circuitCost.getDouble ());
675                op.setInputParameter("d3Cost",  tcfa_costNodeDegreeHigherThan2.getDouble ());
676                op.setInputParameter("onesS",  DoubleUtils.ones(1+nSRGs) , "row");
677                op.setInputParameter("onesE",  DoubleUtils.ones(Efm) , "row");
678                op.setInputParameter("A_dRestrRest", new DoubleMatrixND (A_dRestrRest));
679                op.setInputParameter("h_dRest", numCircH_d.viewSelection(dChange).toArray(), "row");
680                op.setInputParameter("A_rRests", new DoubleMatrixND (A_rRests));
681                op.setInputParameter("A_erRest", new DoubleMatrixND (A_erRest));
682                op.setInputParameter("Aout_ne", new DoubleMatrixND (Aout_ne));
683                op.setInputParameter("Abid_rRestrRest", new DoubleMatrixND (Abid_rRestrRest));
684                op.setInputParameter("occup_es", occupiedWithoutChangingR_es.toArray());
685                
686                op.addDecisionVariable("d3_n", true, new int[] { 1, N }, 0, 1); 
687                op.addDecisionVariable("p_e", true, new int[] { 1, Efm }, DoubleUtils.zeros(Efm) , (maxPe == null)? DoubleUtils.constantArray(Efm, Double.MAX_VALUE) : maxPe.toArray());
688                op.addDecisionVariable("x_rRests", true, new int[] { Rrest, 1+nSRGs}, 0 , Double.MAX_VALUE); /* 1 if there is a link from node i to node j, 0 otherwise */
689
690                op.setObjectiveFunction("minimize", "sum(c_e .* p_e) + d3Cost * sum(d3_n)");
691                op.addConstraint("A_dRestrRest * x_rRests(all,0) == h_dRest'"); /* the flow-conservation constraints (NxD constraints) */
692                op.addConstraint("A_dRestrRest * (x_rRests .* A_rRests) >= h_dRest' * onesS"); /* the flow-conservation constraints (NxD constraints) */
693                op.addConstraint("A_erRest * x_rRests + occup_es <= U * p_e' * onesS"); /* the capacity constraints (E constraints) */
694                op.addConstraint("Aout_ne * p_e' <= 2 + R*d3_n'"); /* the capacity constraints (E constraints) */
695                op.addConstraint("x_rRests == Abid_rRestrRest * x_rRests"); 
696                op.addConstraint("x_rRests >= diag(x_rRests(all,0)) * A_rRests"); // if not affected by failure, do not change 
697                if (tcfa_bidirectionalLinks.getBoolean()) { op.setInputParameter("Abid_ee", new DoubleMatrixND (Abid_ee)); op.addConstraint("p_e == p_e * Abid_ee"); } 
698
699                stat_totalTimeCreateProgramsSecs += eTime (initTimeBeforeCreatingProgram); 
700
701                double solverTimeAllowed = algorithm_maxSolverTimeInSeconds.getDouble ();
702                do 
703                { 
704                        final long t = System.nanoTime(); 
705                        try { stat_numSolverCalls ++; op.solve(algorithm_solverName.getString() , "solverLibraryName", algorithm_solverLibraryName.getString () ,  "maxSolverTimeInSeconds" , solverTimeAllowed); } catch (Exception e) { System.out.println ("-- EXTRA TIME FOR SOLVER, NOW: " + solverTimeAllowed); solverTimeAllowed += 2; stat_totalTimeSolverSecs += eTime(t);  continue;} 
706                        if (solverTimeAllowed != algorithm_maxSolverTimeInSeconds.getDouble ()) System.out.println ("-- EXTRA TIME FOR SOLVER, NOW: " + solverTimeAllowed);
707                        stat_totalTimeSolverSecs += eTime(t);  
708                        solverTimeAllowed *= 2; // duplicate the time if not feasible solution is found
709                        //System.out.println ("Time to create program: " + (1E-9*(t-initTimeBeforeCreatingProgram)) + ", solver time " + ((System.nanoTime()-t)*1E-9) + " s ; ");
710                } while (!op.solutionIsFeasible());             
711                stat_numSolverCallsOk ++; 
712                final DoubleMatrix2D new_x_rRests = op.getPrimalSolution("x_rRests").view2D();
713                final DoubleMatrix1D new_pe = DoubleFactory1D.dense.make(op.getPrimalSolution("p_e").to1DArray());
714                x_rs.viewSelection(rChange, null).assign(new_x_rRests);
715                return x_rs;
716        }
717
718        private DoubleMatrix1D greedyAndLocalSearch_shared (long algorithmEndtime , NetPlan np)
719        {
720                final long initGreedy = System.nanoTime();
721                ArrayList<Pair<Node,Node>> shuffledNodePairs = new ArrayList<Pair<Node,Node>> (N*(N-1)/2); 
722                for (Node n1 : np.getNodes ()) for (Node n2 : np.getNodes ()) if (n1.getIndex () < n2.getIndex ()) shuffledNodePairs.add(Pair.of(n1, n2));
723                Collections.shuffle(shuffledNodePairs , rng);
724                DoubleMatrix1D current_xr = DoubleFactory1D.dense.make(R);
725                for (Pair<Node,Node> nodePair : shuffledNodePairs)
726                {
727                        Set<Integer> thisDemand = new HashSet<Integer> ();
728                        for (Demand d : np.getNodePairDemands(nodePair.getFirst(), nodePair.getSecond() , true))
729                                thisDemand.add(d.getIndex());
730//                      System.out.println("thsiDemand: " + thisDemand);
731                        if (jomStep_shared(thisDemand , null , current_xr) == null) throw new RuntimeException ("Returned null");
732                }
733                DoubleMatrix1D current_pe = A_er.zMult(current_xr, null); current_pe.assign(DoubleFunctions.div(tcfa_linkCapacity_numCirc.getInt ())).assign(DoubleFunctions.ceil);
734                double current_cost = computeCost_shared(current_xr, current_pe); 
735                //DoubleMatrix1D best_xr = current_xr.copy(); DoubleMatrix1D best_pe = current_pe.copy(); double bestCost = ;
736                
737                //System.out.println("---- Greedy solution Time " + (1E-9*(System.nanoTime() - initGreedy)) +",  Cost : "+  current_cost + ", num links: " + current_pe.zSum());
738
739                while (System.nanoTime() < algorithmEndtime) 
740                {
741                        //DoubleMatrix1D bestNeighbor_xr = null; DoubleMatrix1D bestNeighbor_pe = null; double bestNeighborCost = Double.MAX_VALUE; Pair<Long,Long> bestNeighborNodePair = null;
742                        Collections.shuffle(shuffledNodePairs ,rng);
743                        final long initLSIteration = System.nanoTime();
744
745                        boolean improvedSolution = false;
746                        for (Pair<Node,Node> nodePair : shuffledNodePairs)
747                        {
748//                              System.out.println("Pair: " + nodePair + ". Tabu list: " + tabuList_n1n2 + ", IS TABU: " + tabuList_n1n2.contains(nodePair));
749                                Set<Integer> demandsToChange = new HashSet<Integer> (); 
750                                for (Demand d : np.getNodePairDemands(nodePair.getFirst(), nodePair.getSecond() , true))
751                                        demandsToChange.add(d.getIndex ()); 
752                                DoubleMatrix1D thisNeighbor_xr = current_xr.copy(); // do not modify the original one
753                                if (jomStep_shared(demandsToChange , null , thisNeighbor_xr) == null) throw new RuntimeException ("Returned null");
754                                DoubleMatrix1D thisNeighbor_pe = A_er.zMult(thisNeighbor_xr, null); thisNeighbor_pe.assign(DoubleFunctions.div(tcfa_linkCapacity_numCirc.getInt ())).assign(DoubleFunctions.ceil);
755                                final double thisNeighborCost = computeCost_shared (thisNeighbor_xr , thisNeighbor_pe);
756                                if (thisNeighborCost < current_cost)
757                                { 
758                                        current_xr = thisNeighbor_xr; current_pe = thisNeighbor_pe; current_cost = thisNeighborCost; improvedSolution = true; 
759                                        System.out.println("NEW BEST COST: " + current_cost + ", num links: " + current_pe.zSum()); 
760                                        break; 
761                                }
762                        }                       
763                        
764                        //System.out.println("- Local search TIME: " + ((System.nanoTime()-initLSIteration)*1e-9) + ", current cost: " + current_cost + ", num links: " + current_pe.zSum() + ", IMPROVED: " + improvedSolution);
765
766                        if (!improvedSolution) break; else stat_numLSIterationsReducingCost ++; // end grasp iteration 
767                }
768                
769                return current_xr;
770        }
771
772        private Pair<DoubleMatrix1D,DoubleMatrix1D> greedyAndLocalSearch_11 (long algorithmEndtime , NetPlan np)
773        {
774                final long initGreedy = System.nanoTime();
775                ArrayList<Pair<Node,Node>> shuffledNodePairs = new ArrayList<Pair<Node,Node>> (N*(N-1)/2); 
776                for (Node n1 : np.getNodes ()) for (Node n2 : np.getNodes ()) if (n1.getIndex () < n2.getIndex ()) shuffledNodePairs.add(Pair.of(n1, n2));
777                Collections.shuffle(shuffledNodePairs , rng);
778                DoubleMatrix1D current_xr = DoubleFactory1D.dense.make(R);
779                DoubleMatrix1D current_x2r = DoubleFactory1D.dense.make(R);
780                for (Pair<Node,Node> nodePair : shuffledNodePairs)
781                {
782                        Set<Integer> thisDemand = new HashSet<Integer> ();
783                        for (Demand d : np.getNodePairDemands(nodePair.getFirst(), nodePair.getSecond() , true))
784                                thisDemand.add(d.getIndex());
785                        final long t = System.nanoTime(); if (jomStep_11(thisDemand , null , current_xr , current_x2r) == null) throw new RuntimeException ("Returned null");
786                        //System.out.println("Node pair " + nodePair + " time increasing it: " + (1E-9*(System.nanoTime()-t)) + ", num demands: " + thisDemand.size());
787                }
788                DoubleMatrix1D current_pe = computePe_11 (current_xr , current_xr);
789                double current_cost = computeCost_11(current_xr, current_x2r , current_pe); 
790
791                //System.out.println("---- Greedy solution Time " + (1E-9*(System.nanoTime() - initGreedy)) +",  Cost : "+  current_cost + ", num links: " + current_pe.zSum());
792
793                while (System.nanoTime() < algorithmEndtime) 
794                {
795                        Collections.shuffle(shuffledNodePairs ,rng);
796                        final long initLSIteration = System.nanoTime();
797
798                        boolean improvedSolution = false;
799                        for (Pair<Node,Node> nodePair : shuffledNodePairs)
800                        {
801                                Set<Integer> demandsToChange = new HashSet<Integer> (); 
802                                for (Demand d : np.getNodePairDemands(nodePair.getFirst(), nodePair.getSecond() , true))
803                                        demandsToChange.add(d.getIndex()); 
804                                DoubleMatrix1D thisNeighbor_xr = current_xr.copy(); // do not modify the original one
805                                DoubleMatrix1D thisNeighbor_x2r = current_x2r.copy(); // do not modify the original one
806                                if (jomStep_11(demandsToChange , null , thisNeighbor_xr , thisNeighbor_x2r) == null) throw new RuntimeException ("Returned null");
807                                DoubleMatrix1D thisNeighbor_pe = computePe_11(thisNeighbor_xr, thisNeighbor_x2r);
808                                final double thisNeighborCost = computeCost_11 (thisNeighbor_xr , thisNeighbor_x2r , thisNeighbor_pe);
809                                if (thisNeighborCost < current_cost) 
810                                { 
811                                        current_xr = thisNeighbor_xr; current_x2r = thisNeighbor_x2r; current_pe = thisNeighbor_pe; current_cost = thisNeighborCost; improvedSolution = true; 
812                                        System.out.println("NEW BEST COST: " + current_cost + ", num links: " + current_pe.zSum()); 
813                                        break;
814                                }
815                        }                       
816                        
817                        //System.out.println("- Local search TIME: " + ((System.nanoTime()-initLSIteration)*1e-9) + ", current cost: " + current_cost + ", num links: " + current_pe.zSum() + ", IMPROVED: " + improvedSolution);
818
819                        if (!improvedSolution) break; else stat_numLSIterationsReducingCost ++; // end grasp iteration 
820                }
821                
822                return Pair.of(current_xr , current_x2r);
823        }
824        private DoubleMatrix2D greedyAndLocalSearch_restoration (long algorithmEndtime , NetPlan np)
825        {
826                final long initGreedy = System.nanoTime();
827                ArrayList<Pair<Node,Node>> shuffledNodePairs = new ArrayList<Pair<Node,Node>> (N*(N-1)/2); 
828                for (Node n1 : np.getNodes ()) for (Node n2 : np.getNodes ()) if (n1.getIndex () < n2.getIndex()) shuffledNodePairs.add(Pair.of(n1, n2));
829                Collections.shuffle(shuffledNodePairs , rng);
830                DoubleMatrix2D current_xrs = DoubleFactory2D.dense.make(R,1+nSRGs);
831                for (Pair<Node,Node> nodePair : shuffledNodePairs)
832                {
833                        Set<Integer> thisDemand = new HashSet<Integer> ();
834                        for (Demand d : np.getNodePairDemands(nodePair.getFirst(), nodePair.getSecond(),true))
835                                thisDemand.add(d.getIndex ());
836//                      System.out.println("thsiDemand: " + thisDemand);
837                        if (jomStep_restoration(thisDemand , null , current_xrs) == null) throw new RuntimeException ("Returned null");
838                }
839                DoubleMatrix1D current_pe = computePe_restoration(current_xrs);
840                double current_cost = computeCost_shared(current_xrs.viewColumn(0), current_pe); 
841                
842                //System.out.println("---- Greedy solution Time " + (1E-9*(System.nanoTime() - initGreedy)) +",  Cost : "+  current_cost + ", num links: " + current_pe.zSum() + ", SRGs: " + nSRGs);
843
844                while (System.nanoTime() < algorithmEndtime) 
845                {
846                        Collections.shuffle(shuffledNodePairs ,rng);
847                        final long initLSIteration = System.nanoTime();
848
849                        boolean improvedSolution = false;
850                        for (Pair<Node,Node> nodePair : shuffledNodePairs)
851                        {
852                                Set<Integer> demandsToChange = new HashSet<Integer> (); 
853                                for (Demand d : np.getNodePairDemands(nodePair.getFirst(), nodePair.getSecond() , true))
854                                        demandsToChange.add(d.getIndex()); 
855                                DoubleMatrix2D thisNeighbor_xrs = current_xrs.copy(); // do not modify the original one
856                                if (jomStep_restoration(demandsToChange , null , thisNeighbor_xrs) == null) throw new RuntimeException ("Returned null");
857                                DoubleMatrix1D thisNeighbor_pe = computePe_restoration(thisNeighbor_xrs);
858                                final double thisNeighborCost = computeCost_shared (thisNeighbor_xrs.viewColumn(0) , thisNeighbor_pe);
859                                if (thisNeighborCost < current_cost) 
860                                { 
861                                        current_xrs = thisNeighbor_xrs; current_pe = thisNeighbor_pe; current_cost = thisNeighborCost; improvedSolution = true; 
862                                        System.out.println("NEW BEST COST: " + current_cost + ", num links: " + current_pe.zSum()); 
863                                        break; 
864                                }
865                        }                       
866                        
867                        //System.out.println("- Local search TIME: " + ((System.nanoTime()-initLSIteration)*1e-9) + ", current cost: " + current_cost + ", num links: " + current_pe.zSum() + ", IMPROVED: " + improvedSolution);
868
869                        if (!improvedSolution) break; else stat_numLSIterationsReducingCost ++; // end grasp iteration 
870                }
871                
872                return current_xrs;
873        }
874        
875        private DoubleMatrix1D computePe_11 (DoubleMatrix1D x_r , DoubleMatrix1D x2_r)
876        {
877                DoubleMatrix1D p_e = A_er.zMult(x_r, null);
878                DoubleMatrix1D backup_occup_e = A_er.zMult(x2_r, null);
879                for (int e = 0 ; e < Efm ; e ++) p_e.set(e , p_e.get(e) + backup_occup_e.get(e));
880                p_e.assign(DoubleFunctions.div(tcfa_linkCapacity_numCirc.getInt ())).assign(DoubleFunctions.ceil);
881                return p_e;
882        }
883        private DoubleMatrix1D computePe_restoration (DoubleMatrix2D x_rs)
884        {
885                DoubleMatrix2D p_es = A_er.zMult(x_rs, null); DoubleMatrix1D p_e = DoubleFactory1D.dense.make(Efm); 
886                for (int e = 0; e < Efm ; e ++) p_e.set(e , p_es.viewRow(e).getMaxLocation() [0]); // the maximum per state s
887                p_e.assign(DoubleFunctions.div(tcfa_linkCapacity_numCirc.getInt ())).assign(DoubleFunctions.ceil);
888                return p_e;
889        }
890        
891        private double eTime (long init) { return 1E-9*(System.nanoTime() - init); }
892        
893        private static int [] getIndexes (Collection<? extends NetworkElement> col) { final int [] res = new int [col.size ()]; int cont = 0; for (NetworkElement el : col) res [cont++] = el.getIndex (); return res; }
894        
895        
896}