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 *******************************************************************************/
011package com.net2plan.examples.ocnbook.offline;
012
013
014import cern.colt.matrix.tdouble.DoubleMatrix1D;
015import com.net2plan.interfaces.networkDesign.IAlgorithm;
016import com.net2plan.interfaces.networkDesign.Link;
017import com.net2plan.interfaces.networkDesign.Net2PlanException;
018import com.net2plan.interfaces.networkDesign.NetPlan;
019import com.net2plan.utils.*;
020import com.net2plan.utils.Constants.RoutingType;
021
022import java.io.File;
023import java.util.*;
024
025/**
026 * Searches for the OSPF link weights that minimize a measure of congestion, using a greedy heuristic
027 * @net2plan.description
028 * @net2plan.keywords IP/OSPF, Flow assignment (FA), Greedy heuristic
029 * @net2plan.ocnbooksections Section 12.6
030 * @net2plan.inputParameters 
031 * @author Pablo Pavon-Marino
032 */
033public class Offline_fa_ospfWeightOptimization_greedy implements IAlgorithm
034{
035        private TimeTrace stat_objFunction;
036        private OSPFHeuristicUtils ospfEngine;
037
038        private InputParameter greedy_initializationType = new InputParameter ("greedy_initializationType", "#select# random" , "The type of initialization of the OSPF link weights");
039        private InputParameter greedy_differenceInWeightToBeNeighbors = new InputParameter ("greedy_differenceInWeightToBeNeighbors", (int) 1 , "Two solutions where all the links have the same weight, but one link where the weight differs in the quantity given by this parameter, are considered neighbors");
040        private InputParameter ospf_maxLinkWeight = new InputParameter ("ospf_maxLinkWeight", (int) 16 , "OSPF link weights are constrained to be integers between 1 and this parameter" , 1 , Integer.MAX_VALUE);
041        private InputParameter algorithm_randomSeed = new InputParameter ("algorithm_randomSeed", (long) 1 , "Seed of the random number generator");
042        private InputParameter algorithm_outputFileNameRoot = new InputParameter ("algorithm_outputFileNameRoot", "ospfWeghtOptimization_greedy" , "Root of the file name to be used in the output files. If blank, no output");
043        private InputParameter algorithm_numSamples = new InputParameter ("algorithm_numSamples", (int) 100 , "Number of repetitions, returns the last, but prints in file all of them" , 1 , Integer.MAX_VALUE);
044        private InputParameter ospf_weightOfMaxUtilizationInObjectiveFunction = new InputParameter ("ospf_weightOfMaxUtilizationInObjectiveFunction", (double) 0.9 , "Objective function is this factor multiplied by maximum link utilization, plus 1 minus this factor by average link utilization" , 0 , true , 1 , true);
045
046        @Override
047        public String executeAlgorithm(NetPlan netPlan, Map<String, String> algorithmParameters, Map<String, String> net2planParameters)
048        {
049                /* Initialize all InputParameter objects defined in this object (this uses Java reflection) */
050                InputParameter.initializeAllInputParameterFieldsOfObject(this, algorithmParameters);
051
052                /* Basic checks */
053                final int N = netPlan.getNumberOfNodes();
054                final int E = netPlan.getNumberOfLinks();
055                if (N == 0) throw new Net2PlanException("The input design must have nodes");
056                netPlan.removeAllUnicastRoutingInformation();
057                netPlan.setRoutingTypeAllDemands (RoutingType.HOP_BY_HOP_ROUTING);
058
059                
060                Random rng = new Random (algorithm_randomSeed.getLong());
061                this.ospfEngine = new OSPFHeuristicUtils(netPlan, ospf_maxLinkWeight.getInt (), ospf_weightOfMaxUtilizationInObjectiveFunction.getDouble(), rng);
062                
063                this.stat_objFunction = new TimeTrace ();
064
065                for (int it = 0 ; it < algorithm_numSamples.getInt () ; it ++)
066                {
067                        Pair<DoubleMatrix1D,Double> p = ospfEngine.getInitialSolution (greedy_initializationType.getString());
068                        DoubleMatrix1D currentSol = p.getFirst();
069                        double currentObjFunction = p.getSecond();
070
071                        /* Create a randomly shuffled sequence of link ids */
072                        List<Link> shuffledLinks = new ArrayList<Link> (netPlan.getLinks());
073                        Collections.shuffle(shuffledLinks);
074
075                        for (Link e : shuffledLinks)
076                        {
077                                Pair<double [],int []> pair = ospfEngine.computeSolutionsVaryingLinkWeight (e , currentSol , null , greedy_differenceInWeightToBeNeighbors.getInt ());
078                                final int indexOfBestWeight = DoubleUtils.maxIndexes (pair.getFirst() , Constants.SearchType.FIRST) [0];
079                                final int bestWeight = pair.getSecond() [indexOfBestWeight];
080                                currentObjFunction = pair.getFirst() [indexOfBestWeight];
081                                currentSol.set(e.getIndex(), (double) bestWeight);
082                        } 
083                        stat_objFunction.add(it , currentObjFunction);
084                }
085                
086                stat_objFunction.printToFile(new File (algorithm_outputFileNameRoot.getString () + "_D" + greedy_differenceInWeightToBeNeighbors.getInt () + "_cong.txt"));
087
088                double minObjectiveFunction = Double.MAX_VALUE;
089                for (Pair<Double,Object> element : stat_objFunction.getList()) minObjectiveFunction = Math.min(minObjectiveFunction, (Double) element.getSecond());
090                return "Ok! Min congestion factor: " + minObjectiveFunction;
091        }
092
093        @Override
094        public String getDescription()
095        {
096                return "Given a set of nodes, links and offered traffic, this algorithm assumes that the nodes are IP routers running the OSPF protocol (applying ECMP rules) for routing it. The algorithm searches for the set of link weights that optimize the routing. In particular, the target is minimizing a congestion metric computed as a function of both the worst-case link utilization and the average link utilization. The algorithm is based on applying a greedy heuristic approach.";      
097        }
098
099        @Override
100        public List<Triple<String, String, String>> getParameters()
101        {
102                /* Returns the parameter information for all the InputParameter objects defined in this object (uses Java reflection) */
103                return InputParameter.getInformationAllInputParameterFieldsOfObject(this);
104        }
105}