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 014package com.net2plan.examples.ocnbook.offline; 015 016import cern.colt.matrix.tdouble.DoubleFactory1D; 017import cern.colt.matrix.tdouble.DoubleMatrix1D; 018import cern.jet.math.tdouble.DoubleFunctions; 019import com.jom.OptimizationProblem; 020import com.net2plan.interfaces.networkDesign.*; 021import com.net2plan.utils.Constants.RoutingType; 022import com.net2plan.utils.InputParameter; 023import com.net2plan.utils.Triple; 024 025import java.util.LinkedList; 026import java.util.List; 027import java.util.Map; 028 029/** 030 * In a network with demands of two QoS, jointly optimizes the demand injected traffic and link capacity assigned to each solving a formulation. 031 * @net2plan.description 032 * @net2plan.keywords Bandwidth assignment (BA), Capacity assignment (CA), JOM, NUM 033 * @net2plan.ocnbooksections Section 11.3, Section 6.2 034 * @net2plan.inputParameters 035 * @author Pablo Pavon-Marino 036 */ 037public class Offline_cba_congControLinkBwSplitTwolQoS implements IAlgorithm 038{ 039 private double PRECISIONFACTOR; 040 private InputParameter solverName = new InputParameter ("solverName", "#select# ipopt", "The solver name to be used by JOM. "); 041 private InputParameter solverLibraryName = new InputParameter ("solverLibraryName", "" , "The solver library full or relative path, to be used by JOM. Leave blank to use JOM default."); 042 private InputParameter maxSolverTimeInSeconds = new InputParameter ("maxSolverTimeInSeconds", (double) -1 , "Maximum time granted to the solver to solve the problem. If this time expires, the solver returns the best solution found so far (if a feasible solution is found)"); 043 private InputParameter cc_control_minHd = new InputParameter ("cc_control_minHd", 0.1 , "Minimum traffic assigned to each demand" , 0 , true , Double.MAX_VALUE , true); 044 private InputParameter cc_control_maxHd = new InputParameter ("cc_control_maxHd", 1e6 , "Maximum traffic assigned to each demand" , 0 , true , Double.MAX_VALUE , true); 045 046 private InputParameter cc_control_fairnessFactor_1 = new InputParameter ("cc_control_fairnessFactor_1", 1.0 , "Fairness factor in utility function of congestion control for demands of class 1" , 0 , true , Double.MAX_VALUE , true); 047 private InputParameter cc_control_fairnessFactor_2 = new InputParameter ("cc_control_fairnessFactor_2", 1.0 , "Fairness factor in utility function of congestion control for demands of class 2" , 0 , true , Double.MAX_VALUE , true); 048 private InputParameter cc_control_weightFairness_1 = new InputParameter ("cc_control_weightFairness_1", 1.0 , "Weight factor in utility function demands type 1" , 0 , true , Double.MAX_VALUE , true); 049 private InputParameter cc_control_weightFairness_2 = new InputParameter ("cc_control_weightFairness_2", 2.0 , "Weight factor in utility function demands type 2" , 0 , true , Double.MAX_VALUE , true); 050 private InputParameter mac_minCapacity_1 = new InputParameter ("mac_minCapacity_1", 0.1 , "Minimum capacity in each link, allocated to traffic of type 1" , 0 , true , Double.MAX_VALUE , true); 051 private InputParameter mac_minCapacity_2 = new InputParameter ("mac_minCapacity_2", 0.1 , "Minimum capacity in each link, allocated to traffic of type 2" , 0 , true , Double.MAX_VALUE , true); 052 053 private int N,E,D; 054 private DoubleMatrix1D demandType; 055 056 @Override 057 public String executeAlgorithm(NetPlan netPlan, Map<String, String> algorithmParameters, Map<String, String> net2planParameters) 058 { 059 /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */ 060 InputParameter.initializeAllInputParameterFieldsOfObject(this, algorithmParameters); 061 062 if (netPlan.getNumberOfLayers() != 1) throw new Net2PlanException ("This algorithm works in single layer networks"); 063 064 this.PRECISIONFACTOR = Double.parseDouble(net2planParameters.get("precisionFactor")); 065 this.N = netPlan.getNumberOfNodes(); 066 this.E = netPlan.getNumberOfLinks(); 067 this.D = 2*N*(N-1); 068 069 /* Remove all demands, then create a demand per input output node pair. One route for it */ 070 netPlan.removeAllDemands(); 071 netPlan.setRoutingTypeAllDemands(RoutingType.SOURCE_ROUTING); 072 List<Integer> arrayIndexesOfDemand1 = new LinkedList<Integer> (); 073 List<Integer> arrayIndexesOfDemand2 = new LinkedList<Integer> (); 074 this.demandType = DoubleFactory1D.dense.make (D); 075 for (Node n1 : netPlan.getNodes()) 076 for (Node n2 : netPlan.getNodes()) 077 if (n1 != n2) 078 { 079 final Demand d1 = netPlan.addDemand(n1, n2, 0.0, RoutingType.SOURCE_ROUTING , null); d1.setAttribute("type" , "1"); demandType.set(d1.getIndex (), 1); 080 final Demand d2 = netPlan.addDemand(n1, n2, 0.0, RoutingType.SOURCE_ROUTING , null); d2.setAttribute("type" , "2"); demandType.set(d2.getIndex (), 2); 081 arrayIndexesOfDemand1.add (d1.getIndex ()); 082 arrayIndexesOfDemand2.add (d2.getIndex ()); 083 } 084 085 /* Remove all routes, and create one with the shortest path in km for each demand */ 086 netPlan.removeAllUnicastRoutingInformation(); 087 netPlan.setRoutingTypeAllDemands(RoutingType.SOURCE_ROUTING); 088 netPlan.addRoutesFromCandidatePathList(netPlan.computeUnicastCandidatePathList(netPlan.getVectorLinkLengthInKm() , 1 , -1, -1, -1, -1, -1, -1, null)); // one route per demand, so P equals D 089 090 091 092 /* Make the formulation */ 093 final int E = netPlan.getNumberOfLinks(); 094 final int D = netPlan.getNumberOfDemands(); 095 final DoubleMatrix1D u_e = netPlan.getVectorLinkCapacity(); 096 OptimizationProblem op = new OptimizationProblem(); 097 098 op.setInputParameter("b1", 1-cc_control_fairnessFactor_1.getDouble()); 099 op.setInputParameter("b2", 1-cc_control_fairnessFactor_2.getDouble()); 100 op.setInputParameter("w1", cc_control_weightFairness_1.getDouble()); 101 op.setInputParameter("w2", cc_control_weightFairness_2.getDouble()); 102 op.setInputParameter("R_de", netPlan.getMatrixDemand2LinkAssignment()); 103 op.setInputParameter("u_e", u_e , "row"); 104 op.setInputParameter("D1", arrayIndexesOfDemand1 , "row"); 105 op.setInputParameter("D2", arrayIndexesOfDemand2 , "row"); 106 107 op.addDecisionVariable("h_d", false , new int[] { 1 , D }, cc_control_minHd.getDouble() , cc_control_maxHd.getDouble()); 108 op.addDecisionVariable("u_1", false , new int[] { 1 , E }, DoubleFactory1D.dense.make (E , mac_minCapacity_1.getDouble()) , u_e); 109 op.addDecisionVariable("u_2", false , new int[] { 1 , E }, DoubleFactory1D.dense.make (E , mac_minCapacity_2.getDouble()) , u_e); 110 111 String objFunc = ""; 112 if (cc_control_fairnessFactor_1.getDouble() == 1) 113 objFunc += "w1*sum(ln(h_d(D1)))"; 114 else 115 objFunc += "(w1/b1) * sum ( h_d(D1)^b1 ) "; 116 if (cc_control_fairnessFactor_2.getDouble() == 1) 117 objFunc += " + w2*sum(ln(h_d(D2)))"; 118 else 119 objFunc += " + (w2/b2) * sum ( h_d(D2)^b2 ) "; 120 121 op.setObjectiveFunction("maximize", objFunc); 122 123 op.addConstraint("h_d(D1) * R_de(D1,all) <= u_1" , "pie_1"); 124 op.addConstraint("h_d(D2) * R_de(D2,all) <= u_2" , "pie_2"); 125 op.addConstraint("u_1 + u_2 <= u_e"); 126 127 /* Call the solver to solve the problem */ 128 op.solve(solverName.getString (), "solverLibraryName", solverLibraryName.getString () , "maxSolverTimeInSeconds" , maxSolverTimeInSeconds.getDouble ()); 129 130 /* If no solution is found, quit */ 131 if (op.feasibleSolutionDoesNotExist()) throw new Net2PlanException("The problem has no feasible solution"); 132 if (op.foundUnboundedSolution()) throw new Net2PlanException ("Found an unbounded solution"); 133 if (!op.solutionIsFeasible()) throw new Net2PlanException("A feasible solution was not found"); 134 135 /* Retrieve the optimum solutions. Convert the bps into Erlangs */ 136 DoubleMatrix1D h_d = op.getPrimalSolution("h_d").view1D (); 137 DoubleMatrix1D u_1 = op.getPrimalSolution("u_1").view1D (); 138 DoubleMatrix1D u_2 = op.getPrimalSolution("u_2").view1D (); 139 DoubleMatrix1D pie_1 = op.getMultipliersOfConstraint("pie_1").assign(DoubleFunctions.abs).view1D (); 140 DoubleMatrix1D pie_2 = op.getMultipliersOfConstraint("pie_2").assign(DoubleFunctions.abs).view1D (); 141 142 /* Set the demand offered and carried traffic */ 143 for (Demand d : netPlan.getDemands ()) 144 { 145 final double thisDemand_hd = h_d.get(d.getIndex ()); 146 d.setOfferedTraffic(thisDemand_hd); 147 d.getRoutes().iterator().next().setCarriedTraffic(thisDemand_hd , thisDemand_hd); 148 } 149 /* Set the link capacity split and multipliers */ 150 for (Link e : netPlan.getLinks()) 151 { 152 e.setAttribute("u_1", "" + u_1.get(e.getIndex ())); 153 e.setAttribute("u_2", "" + u_2.get(e.getIndex ())); 154 e.setAttribute("pie_1", "" + pie_1.get(e.getIndex ())); 155 e.setAttribute("pie_2", "" + pie_2.get(e.getIndex ())); 156 } 157 158 /* Check link capacities */ 159 for (Link e : netPlan.getLinks()) 160 { 161 double traf1 = 0 ; double traf2 = 0; 162 for (Route r : e.getTraversingRoutes()) 163 if (demandType.get(r.getDemand().getIndex ()) == 1) traf1 += r.getCarriedTraffic(); else traf2 += r.getCarriedTraffic(); 164 if (traf1 > u_1.get (e.getIndex ()) + PRECISIONFACTOR) throw new RuntimeException ("Bad"); 165 if (traf2 > u_2.get (e.getIndex ()) + PRECISIONFACTOR) throw new RuntimeException ("Bad"); 166 if (u_1.get(e.getIndex ()) + u_2.get(e.getIndex ()) > e.getCapacity() + PRECISIONFACTOR) throw new RuntimeException ("Bad"); 167 } 168 169 /* Compute the network utility */ 170 double netUtil = 0; 171 for (Demand d : netPlan.getDemands()) 172 { 173 final double thisDemand_hd = h_d.get(d.getIndex ()); 174 if (d.getRoutes ().size() != 1) throw new RuntimeException ("Bad"); 175 if (Math.abs (d.getCarriedTraffic() - d.getOfferedTraffic()) > 1e-3) throw new RuntimeException ("Bad"); 176 if (demandType.get(d.getIndex ()) == 1) 177 netUtil += cc_control_weightFairness_1.getDouble () * ((cc_control_fairnessFactor_1.getDouble () == 1)? Math.log(thisDemand_hd) : 1/(1-cc_control_fairnessFactor_1.getDouble ()) * Math.pow(thisDemand_hd, 1-cc_control_fairnessFactor_1.getDouble ())); 178 else 179 netUtil += cc_control_weightFairness_2.getDouble () * ((cc_control_fairnessFactor_2.getDouble () == 1)? Math.log(thisDemand_hd) : 1/(1-cc_control_fairnessFactor_2.getDouble ()) * Math.pow(thisDemand_hd, 1-cc_control_fairnessFactor_2.getDouble ())); 180 } 181 182 netPlan.setAttribute("netUtility" , "" + netUtil); 183 184 return "Ok! Optimal net utility: " + netUtil; 185 } 186 187 @Override 188 public String getDescription() 189 { 190 return "Given a network with a set of given nodes. The algorithm first creates two demands for each node pair, one of traffic type 1 and one of traffic type 2. The routing of each demand is known. A shortest-path route is assigned to each demand. We assume that, in order to enforce a strict QoS to the two types of traffic, the capacity in each link is split into two parts, one for each traffic type. Then, this algorithm will jointly optimize: (i) the traffic to inject by each demand, and (ii) the capacity in each link assigned to traffic of class 1 and class 2. The solution is found solving a problem formulation with JOM. The optimization target is finding a fair allocation using the NUM model, where the utility functions of demands of type 1 and type 2 are different."; 191 } 192 193 @Override 194 public List<Triple<String, String, String>> getParameters() 195 { 196 /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */ 197 return InputParameter.getInformationAllInputParameterFieldsOfObject(this); 198 } 199}