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}