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.Demand;
021import com.net2plan.interfaces.networkDesign.IAlgorithm;
022import com.net2plan.interfaces.networkDesign.Net2PlanException;
023import com.net2plan.interfaces.networkDesign.NetPlan;
024import com.net2plan.utils.Constants.RoutingType;
025import com.net2plan.utils.InputParameter;
026import com.net2plan.utils.Triple;
027
028import java.util.List;
029import java.util.Map;
030
031/** 
032 * Solves the congestion control problem using a NUM formulation.
033 * @net2plan.description 
034 * @net2plan.keywords Bandwidth assignment (BA), JOM, NUM, TCP
035 * @net2plan.ocnbooksections Section 6.2, Section 6.3
036 * @net2plan.inputParameters 
037 * @author Pablo Pavon-Marino
038 */
039public class Offline_ba_numFormulations implements IAlgorithm
040{
041        private InputParameter shortestPathType = new InputParameter ("shortestPathType", "#select# hops km" , "Criteria to compute the shortest path. Valid values: 'hops' or 'km'");
042        private InputParameter alphaFairnessFactor = new InputParameter ("alphaFairnessFactor", (double) 2 , "Fairness alpha factor" , 0 , true , Double.MAX_VALUE , true);
043        private InputParameter utilityFunctionType = new InputParameter ("utilityFunctionType", "#select# alphaFairness TCP-Reno TCP-Vegas", "The type of utility function of each demand, in the NUM problem to solve: standard alpha-fairness (alpha parameter is given by alphaFairnessFactor), an utility function suitable for TCP-Reno NUM model (each demand is a TCP connection) and the same for TCP-Vegas");
044        private InputParameter solverName = new InputParameter ("solverName", "#select# ipopt glpk 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");
045        private InputParameter solverLibraryName = new InputParameter ("solverLibraryName", "" , "The solver library full or relative path, to be used by JOM. Leave blank to use JOM default.");
046        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)");
047        
048        @Override
049        public String executeAlgorithm(NetPlan netPlan, Map<String, String> algorithmParameters, Map<String, String> net2planParameters)
050        {
051                /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */
052                InputParameter.initializeAllInputParameterFieldsOfObject(this, algorithmParameters);
053                if (!shortestPathType.getString().equalsIgnoreCase("km") && !shortestPathType.getString().equalsIgnoreCase("hops"))
054                        throw new Net2PlanException("Wrong shortestPathType parameter");
055                
056                /* Initialize variables */
057                final int E = netPlan.getNumberOfLinks();
058                final int D = netPlan.getNumberOfDemands();
059                final double PRECISION_FACTOR = Double.parseDouble(net2planParameters.get("precisionFactor"));
060                if (E == 0 || D == 0) throw new Net2PlanException("This algorithm requires a topology with links and a demand set");
061
062                /* Remove all unicast routed traffic. Any multicast routed traffic is kept */
063                netPlan.removeAllUnicastRoutingInformation();
064                netPlan.setRoutingTypeAllDemands(RoutingType.SOURCE_ROUTING);
065
066                /* Add all the k-shortest candidate routes to the netPlan object carrying no traffic */
067                final DoubleMatrix1D linkCostVector = shortestPathType.getString().equalsIgnoreCase("hops")? DoubleFactory1D.dense.make (E , 1.0) : netPlan.getVectorLinkLengthInKm();
068                
069                netPlan.addRoutesFromCandidatePathList(netPlan.computeUnicastCandidatePathList(linkCostVector , 1 , -1, -1, -1, -1, -1, -1, null)); // one route per demand, so P equals D
070                final int P = netPlan.getNumberOfRoutes(); 
071
072                /* Create the optimization problem object (JOM library) */
073                OptimizationProblem op = new OptimizationProblem();
074
075                /* Set some input parameters to the problem */
076                op.setInputParameter("u_e", netPlan.getVectorLinkSpareCapacity(), "row"); /* for each link, its unused capacity (the one not used by any mulitcast trees) */
077                op.setInputParameter("A_ep", netPlan.getMatrixLink2RouteAssignment()); /* 1 in position (e,p) if link e is traversed by path p, 0 otherwise */
078                op.addDecisionVariable("h_p", false , new int[] { 1, P }, 0, Double.MAX_VALUE); /* the traffic carried by the demand in the macroscopic equilibrium */
079                op.addConstraint("A_ep * h_p' <= u_e'" , "pi_e"); // the capacity constraints (E constraints)
080
081                /* Write the problem formulations */
082                if (utilityFunctionType.getString ().equals ("alphaFairness")) 
083                {
084                        op.setInputParameter("oneMinusAlpha", 1-alphaFairnessFactor.getDouble());
085                        if (alphaFairnessFactor.getDouble() == 1)
086                    op.setObjectiveFunction("maximize", "sum(ln(h_p))");
087                        else if (alphaFairnessFactor.getDouble() == 0)
088                            op.setObjectiveFunction("maximize", "sum(h_p)");
089                        else
090                            op.setObjectiveFunction("maximize", "(1/oneMinusAlpha) * sum(h_p ^ oneMinusAlpha)");
091        
092                }
093                else if (utilityFunctionType.getString ().equals ("TCP-Reno"))
094                {
095                        op.setInputParameter("rtt_p", netPlan.getVectorRoutePropagationDelayInMiliseconds().assign (DoubleFunctions.mult (2)) , "row"); /* round-trip-time in miliseconds for each connection */
096                        op.setObjectiveFunction("maximize", "sum (-1.5 / (rtt_p .* h_p))");
097                }
098                else if (utilityFunctionType.getString ().equals ("TCP-Vegas"))
099                {
100            op.setObjectiveFunction("maximize", "sum(ln(h_p))");
101                }
102                else throw new Net2PlanException ("Unknown optimization target " + utilityFunctionType.getString());
103
104                /* Call the solver to solve the problem */
105                op.solve(solverName.getString (), "solverLibraryName", solverLibraryName.getString () ,  "maxSolverTimeInSeconds" , maxSolverTimeInSeconds.getDouble ());
106
107                /* If no solution is found, quit */
108                if (op.feasibleSolutionDoesNotExist()) throw new Net2PlanException("The problem has no feasible solution");
109                if (op.foundUnboundedSolution()) throw new Net2PlanException ("Found an unbounded solution");
110                if (!op.solutionIsFeasible()) throw new Net2PlanException("A feasible solution was not found");
111                
112                /* Save the solution found in the netPlan object */
113                final DoubleMatrix1D h_p = op.getPrimalSolution("h_p").view1D();
114                netPlan.setVectorRouteCarriedTrafficAndOccupiedLinkCapacities(h_p , h_p);
115                for (Demand d : netPlan.getDemands ()) d.setOfferedTraffic(d.getCarriedTraffic()); // make the offered traffic equal to the carried traffic
116
117//              System.out.println ("Total carried traffic: " + netPlan.getVectorDemandOfferedTraffic().zSum());
118//              System.out.println ("Network utility: " + op.getOptimalCost());
119                
120                return "Ok!: The solution found is guaranteed to be optimal: " + op.solutionIsOptimal() + ". Total carried traffic: " + netPlan.getVectorDemandOfferedTraffic().zSum() + ", Network utility: " + op.getOptimalCost();
121        }
122
123        @Override
124        public String getDescription()
125        {
126                return "Given a network topology, the capacities in the links, and the set of unicast demands, this algorithm assigns the shortest path route to each demand, then optimizes the traffic to inject by each demand (that is, assigns bandwidth), with different optimization targets based on some form of fairness in resource allocation";
127        }
128
129        
130        @Override
131        public List<Triple<String, String, String>> getParameters()
132        {
133                /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */
134                return InputParameter.getInformationAllInputParameterFieldsOfObject(this);
135        }
136}