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
015
016 
017
018
019
020
021package com.net2plan.examples.ocnbook.offline;
022
023import cern.colt.matrix.tdouble.DoubleMatrix2D;
024import cern.jet.math.tdouble.DoubleFunctions;
025import com.jom.OptimizationProblem;
026import com.net2plan.interfaces.networkDesign.*;
027import com.net2plan.utils.DoubleUtils;
028import com.net2plan.utils.InputParameter;
029import com.net2plan.utils.Triple;
030
031import java.util.List;
032import java.util.Map;
033
034/**
035 * Solves several variants of unicast routing problems, with flow-link formulations
036 * @net2plan.description
037 * @net2plan.keywords JOM, Flow-link formulation, Flow assignment (FA)
038 * @net2plan.ocnbooksections Section 4.3, Section 4.6.3
039 * @net2plan.inputParameters 
040 * @author Pablo Pavon-Marino
041 */
042public class Offline_fa_xdeFormulations implements IAlgorithm
043{
044        private InputParameter nonBifurcatedRouting = new InputParameter ("nonBifurcatedRouting", false , "True if the routing is constrained to be non-bifurcated");
045        private InputParameter optimizationTarget = new InputParameter ("optimizationTarget", "#select# min-av-num-hops minimax-link-utilization maximin-link-idle-capacity min-av-network-delay min-av-network-blocking" , "Type of optimization target. Choose among minimize the average number of hops, minimize the highest link utilization, maximize the lowest link idle capacity, minimize the average end-to-end network delay including queueing (M/M/1 estimation) and propagation delays, and minimize the average network blocking assuming independent Erlang-B blocking in each link, load sharing model");
046        private InputParameter maxLengthInKm = new InputParameter ("maxLengthInKm", (double) -1 , "Paths longer than this are considered not admissible. A non-positive number means this limit does not exist");
047        private InputParameter 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");
048        private InputParameter solverLibraryName = new InputParameter ("solverLibraryName", "", "The solver library full or relative path, to be used by JOM. Leave blank to use JOM default.");
049        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)");
050        private InputParameter binaryRatePerTrafficUnit_bps = new InputParameter ("binaryRatePerTrafficUnit_bps", (double) 1E6 , "Binary rate equivalent to one traffic unit (used only in average network delay minimization formulation)." , 0 , false , Double.MAX_VALUE , true);
051        private InputParameter averagePacketLengthInBytes = new InputParameter ("averagePacketLengthInBytes", (double) 500 , "Average packet length in bytes (used only in average network delay minimization formulation)." , 0 , false , Double.MAX_VALUE , true);
052        
053        @Override
054        public String executeAlgorithm(NetPlan netPlan, Map<String, String> algorithmParameters, Map<String, String> net2planParameters)
055        {
056                /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */
057                InputParameter.initializeAllInputParameterFieldsOfObject(this, algorithmParameters);
058                if (solverName.getString ().equalsIgnoreCase("ipopt") && nonBifurcatedRouting.getBoolean()) throw new Net2PlanException ("With IPOPT solver, the routing cannot be constrained to be non-bifurcated");
059                
060                /* Initialize variables */
061                final int E = netPlan.getNumberOfLinks();
062                final int D = netPlan.getNumberOfDemands();
063                if (E == 0 || D == 0) throw new Net2PlanException("This algorithm requires a topology with links and a demand set");
064
065                /* Remove all unicast routed traffic. Any multicast routed traffic is kept */
066                netPlan.removeAllUnicastRoutingInformation();
067
068                /* Create the optimization problem object (JOM library) */
069                OptimizationProblem op = new OptimizationProblem();
070
071                /* Set some input parameters to the problem */
072                op.setInputParameter("u_e", netPlan.getVectorLinkSpareCapacity(), "row"); /* for each link, its unused capacity (the one not used by any mulitcast trees) */
073                op.setInputParameter("A_nd", netPlan.getMatrixNodeDemandIncidence()); /* 1 in position (n,d) if demand d starts in n, -1 if it ends in n, 0 otherwise */
074                op.setInputParameter("A_ne", netPlan.getMatrixNodeLinkIncidence()); /* 1 in position (n,e) if link e starts in n, -1 if it ends in n, 0 otherwise */
075                op.setInputParameter("h_d", netPlan.getVectorDemandOfferedTraffic(), "row"); /* for each demand, its offered traffic */
076                op.setInputParameter("l_e", netPlan.getVectorLinkLengthInKm(), "row"); /* for each link, its length in km */
077                op.setInputParameter("Lmax", maxLengthInKm.getDouble()); /* for each link, its length in km */
078
079                /* Write the problem formulations */
080                if (optimizationTarget.getString ().equals ("min-av-num-hops")) 
081                {
082                        op.addDecisionVariable("xx_de", nonBifurcatedRouting.getBoolean() , new int[] { D, E }, 0, 1); /* the FRACTION of traffic of demand d that is carried by link e */
083                        op.setObjectiveFunction("minimize", "sum (h_d * xx_de)"); /* sum of the traffic in the links, proportional to the average number of hops  */
084                        op.addConstraint("A_ne * (xx_de') == A_nd"); /* the flow-conservation constraints (NxD constraints) */
085                        op.addConstraint("h_d * xx_de <= u_e"); /* the capacity constraints (E constraints) */
086                        if (maxLengthInKm.getDouble() > 0) 
087                        {
088                                if (nonBifurcatedRouting.getBoolean()) throw new Net2PlanException ("The maximum length constraint can only be applied in this formulation with non-bifurcated routing");
089                                op.addConstraint("xx_de * l_e' <= Lmax"); /* the path traversed by a demand cannot exceed the maximum length in km */
090                        }
091                }
092                else if (optimizationTarget.getString ().equals ("minimax-link-utilization")) 
093                {
094                        op.setInputParameter ("EPSILON" , getMinimumNonZeroTrafficOrCapacityValue (netPlan) / 1000);
095                        op.addDecisionVariable("xx_de", nonBifurcatedRouting.getBoolean() , new int[] { D, E }, 0, 1); /* the FRACTION of traffic of demand d that is carried by link e */
096                        op.addDecisionVariable("rho", false, new int[] { 1, 1 }, 0, 1); /* worse case link utilization */
097                        op.setObjectiveFunction("minimize", "rho + EPSILON * sum (h_d * xx_de)"); // to avoid loops, we sum EPSILON by the traffic carried (EPSILON very small number)
098                        op.addConstraint("A_ne * (xx_de') == A_nd"); /* the flow-conservation constraints (NxD constraints) */
099                        op.addConstraint("h_d * xx_de <= rho * u_e"); /* the traffic in each link cannot exceed its capacity. sets rho as the worse case utilization */
100                        if (maxLengthInKm.getDouble() > 0) 
101                        {
102                                if (nonBifurcatedRouting.getBoolean()) throw new Net2PlanException ("The maximum length constraint can only be applied in this formulation with non-bifurcated routing");
103                                op.addConstraint("xx_de * l_e' <= Lmax"); /* the path traversed by a demand cannot exceed the maximum length in km */
104                        }
105                }
106                else if (optimizationTarget.getString ().equals ("maximin-link-idle-capacity"))
107                {
108                        op.setInputParameter ("EPSILON" , getMinimumNonZeroTrafficOrCapacityValue (netPlan) / 1000);
109                        op.addDecisionVariable("xx_de", nonBifurcatedRouting.getBoolean() , new int[] { D, E }, 0, 1); /* the FRACTION of traffic of demand d that is carried by link e */
110                        op.addDecisionVariable("u", false, new int[] { 1, 1 }, 0, Double.MAX_VALUE); /* worse case link idle capacity */
111                        op.setObjectiveFunction("maximize", "u - (EPSILON * sum (h_d * xx_de))"); // to avoid loops, we sum EPSILON by the traffic carried (EPSILON very small number)
112                        op.addConstraint("A_ne * (xx_de') == A_nd"); /* the flow-conservation constraints (NxD constraints) */
113                        op.addConstraint("h_d * xx_de <= -u + u_e"); /* the traffic in each link cannot exceed its capacity. sets u as the worse case idle capacity */
114                        if (maxLengthInKm.getDouble() > 0) 
115                        {
116                                if (nonBifurcatedRouting.getBoolean()) throw new Net2PlanException ("The maximum length constraint can only be applied in this formulation with non-bifurcated routing");
117                                op.addConstraint("xx_de * l_e' <= Lmax"); /* the path traversed by a demand cannot exceed the maximum length in km */
118                        }
119                }
120                else if (optimizationTarget.getString ().equals ("min-av-network-delay"))
121                {
122                        if (!solverName.getString ().equalsIgnoreCase("ipopt") || nonBifurcatedRouting.getBoolean()) throw new Net2PlanException ("This is a convex non linear model: please use IPOPT solver. The routing cannot be constrained to be non-bifurcated");
123                        if (maxLengthInKm.getDouble() > 0) throw new Net2PlanException ("The maximum length constraint can not be applied in this objective function");
124                        op.setInputParameter("d_e_secs", netPlan.getVectorLinkPropagationDelayInMiliseconds().assign (DoubleFunctions.mult (0.001)) , "row");
125                        op.setInputParameter("L", averagePacketLengthInBytes.getDouble() * 8); /* average packet length in bits */
126                        op.setInputParameter("R", binaryRatePerTrafficUnit_bps.getDouble()); /* binary rate per traffic unit */
127                        op.addDecisionVariable("xx_de", nonBifurcatedRouting.getBoolean() , new int[] { D, E }, 0, 1); /* the FRACTION of traffic of demand d that is carried by link e */
128                        op.addDecisionVariable("y_e", false, new int[] { 1, E }, DoubleUtils.zeros(E), netPlan.getVectorLinkCapacity().toArray()); /* traffic in the links (already limited to the link capacity) */
129                        op.setObjectiveFunction("minimize", "sum( y_e .* (d_e_secs + (L./R) * (1 ./ (u_e - y_e)))  )");
130                        op.addConstraint("A_ne * (xx_de') == A_nd"); /* the flow-conservation constraints (NxD constraints) */
131                        op.addConstraint("h_d * xx_de == y_e"); /* sets y_e as the total traffic in each link */
132                }
133                else if (optimizationTarget.getString ().equals ("min-av-network-blocking"))
134                {
135                        if (!solverName.getString ().equalsIgnoreCase("ipopt") || nonBifurcatedRouting.getBoolean()) throw new Net2PlanException ("This is a convex non linear model: please use IPOPT solver. The routing cannot be constrained to be non-bifurcated");
136                        if (maxLengthInKm.getDouble() > 0) throw new Net2PlanException ("The maximum length constraint can not be applied in this objective function");
137                        op.addDecisionVariable("xx_de", nonBifurcatedRouting.getBoolean() , new int[] { D, E }, 0, 1); /* the FRACTION of traffic of demand d that is carried by link e */
138                        op.addDecisionVariable("y_e", false, new int[] { 1, E }, DoubleUtils.zeros(E), netPlan.getVectorLinkCapacity().toArray()); /* traffic in the links (already limited to the link capacity) */
139                        op.setObjectiveFunction("minimize", "sum(y_e .* erlangB(y_e, u_e))");
140                        op.addConstraint("A_ne * (xx_de') == A_nd"); /* the flow-conservation constraints (NxD constraints) */
141                        op.addConstraint("h_d * xx_de == y_e"); /* sets y_e as the total traffic in each link */
142                }
143                else throw new Net2PlanException ("Unknown optimization target " + optimizationTarget.getString());
144
145                op.solve(solverName.getString (), "solverLibraryName", solverLibraryName.getString () , "maxSolverTimeInSeconds" , maxSolverTimeInSeconds.getDouble ());
146
147                /* If no solution is found, quit */
148                if (op.feasibleSolutionDoesNotExist()) throw new Net2PlanException("The problem has no feasible solution");
149                if (!op.solutionIsFeasible()) throw new Net2PlanException("A feasible solution was not found");
150                
151                /* Save the solution found in the netPlan object */
152                final DoubleMatrix2D xx_de = op.getPrimalSolution("xx_de").view2D();
153                netPlan.setRoutingFromDemandLinkCarriedTraffic(xx_de , true , true , null);
154
155                return "Ok!: The solution found is guaranteed to be optimal: " + op.solutionIsOptimal();
156        }
157
158        @Override
159        public String getDescription()
160        {
161                return "Given a network topology, the capacities in the links, and a set unicast traffic demands, this algorithm permits computing the optimum routing of the traffic (that is, the set of routes carrying the traffic of the demands) solving flow-link formulations (x_{de} variables). Through a set of input parameters, the user can choose among different optimization targets and constraints.";
162        }
163
164        
165        @Override
166        public List<Triple<String, String, String>> getParameters()
167        {
168                /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */
169                return InputParameter.getInformationAllInputParameterFieldsOfObject(this);
170        }
171        
172        private double getMinimumNonZeroTrafficOrCapacityValue (NetPlan netPlan)
173        {
174                double res = Double.MAX_VALUE;
175                for (Demand d : netPlan.getDemands ()) if (d.getOfferedTraffic() > 0) res = Math.min (res , d.getOfferedTraffic());
176                for (Link e : netPlan.getLinks ()) if (e.getCapacity() > 0) res = Math.min (res , e.getCapacity());
177                if (res == Double.MAX_VALUE) throw new Net2PlanException ("Too large offered traffics and link capacities");
178                return res;
179        }
180}